00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "kacceleratormanager.h"
00021
00022 #include <QtGui/QApplication>
00023 #include <QtGui/QCheckBox>
00024 #include <QtGui/QComboBox>
00025 #include <QtGui/QGroupBox>
00026 #include <QtGui/QLabel>
00027 #include <QtGui/QLineEdit>
00028 #include <QtGui/QMenuBar>
00029 #include <QtGui/qmenudata.h>
00030 #include <QtCore/QMetaClassInfo>
00031 #include <QtCore/QObject>
00032 #include <QList>
00033 #include <QtGui/QPushButton>
00034 #include <QtGui/QRadioButton>
00035 #include <QtGui/QDoubleSpinBox>
00036 #include <QtGui/QTabBar>
00037 #include <QtGui/QTextEdit>
00038 #include <QtGui/QWidget>
00039 #include <QStackedWidget>
00040 #include <QDockWidget>
00041 #include <QTextDocument>
00042
00043 #include <kstandardaction.h>
00044 #include <kdebug.h>
00045 #include <kdeversion.h>
00046 #include <kglobal.h>
00047
00048 #include "kacceleratormanager_private.h"
00049 #include <kstandardaction_p.h>
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073 class KAcceleratorManagerPrivate
00074 {
00075 public:
00076
00077 static void manage(QWidget *widget);
00078 static bool programmers_mode;
00079 static bool standardName(const QString &str);
00080
00081 static bool checkChange(const KAccelString &as) {
00082 QString t2 = as.accelerated();
00083 QString t1 = as.originalText();
00084 if (t1 != t2)
00085 {
00086 if (as.accel() == -1) {
00087 removed_string += "<tr><td>" + Qt::escape(t1) + "</td></tr>";
00088 } else if (as.originalAccel() == -1) {
00089 added_string += "<tr><td>" + Qt::escape(t2) + "</td></tr>";
00090 } else {
00091 changed_string += "<tr><td>" + Qt::escape(t1) + "</td>";
00092 changed_string += "<td>" + Qt::escape(t2) + "</td></tr>";
00093 }
00094 return true;
00095 }
00096 return false;
00097 }
00098 static QString changed_string;
00099 static QString added_string;
00100 static QString removed_string;
00101 static QMap<QWidget *, int> ignored_widgets;
00102
00103 private:
00104 class Item;
00105 public:
00106 typedef QList<Item *> ItemList;
00107
00108 private:
00109 static void traverseChildren(QWidget *widget, Item *item);
00110
00111 static void manageWidget(QWidget *widget, Item *item);
00112 static void manageMenuBar(QMenuBar *mbar, Item *item);
00113 static void manageTabBar(QTabBar *bar, Item *item);
00114 static void manageDockWidget(QDockWidget *dock, Item *item);
00115
00116 static void calculateAccelerators(Item *item, QString &used);
00117
00118 class Item
00119 {
00120 public:
00121
00122 Item() : m_widget(0), m_children(0), m_index(-1) {}
00123 ~Item();
00124
00125 void addChild(Item *item);
00126
00127 QWidget *m_widget;
00128 KAccelString m_content;
00129 ItemList *m_children;
00130 int m_index;
00131
00132 };
00133 };
00134
00135
00136 bool KAcceleratorManagerPrivate::programmers_mode = false;
00137 QString KAcceleratorManagerPrivate::changed_string;
00138 QString KAcceleratorManagerPrivate::added_string;
00139 QString KAcceleratorManagerPrivate::removed_string;
00140 K_GLOBAL_STATIC_WITH_ARGS(QStringList, kaccmp_sns, (KStandardAction::internal_stdNames()))
00141 QMap<QWidget*, int> KAcceleratorManagerPrivate::ignored_widgets;
00142
00143 bool KAcceleratorManagerPrivate::standardName(const QString &str)
00144 {
00145 return kaccmp_sns->contains(str);
00146 }
00147
00148 KAcceleratorManagerPrivate::Item::~Item()
00149 {
00150 if (m_children)
00151 while (!m_children->isEmpty())
00152 delete m_children->takeFirst();
00153
00154 delete m_children;
00155 }
00156
00157
00158 void KAcceleratorManagerPrivate::Item::addChild(Item *item)
00159 {
00160 if (!m_children) {
00161 m_children = new ItemList;
00162 }
00163
00164 m_children->append(item);
00165 }
00166
00167 void KAcceleratorManagerPrivate::manage(QWidget *widget)
00168 {
00169 if (!widget)
00170 {
00171 kDebug(131) << "null pointer given to manage";
00172 return;
00173 }
00174
00175 if (qobject_cast<QMenu*>(widget))
00176 {
00177
00178 KPopupAccelManager::manage(static_cast<QMenu*>(widget));
00179 return;
00180 }
00181
00182 Item *root = new Item;
00183
00184 manageWidget(widget, root);
00185
00186 QString used;
00187 calculateAccelerators(root, used);
00188 delete root;
00189 }
00190
00191
00192 void KAcceleratorManagerPrivate::calculateAccelerators(Item *item, QString &used)
00193 {
00194 if (!item->m_children)
00195 return;
00196
00197
00198 KAccelStringList contents;
00199 foreach(Item *it, *item->m_children)
00200 {
00201 contents << it->m_content;
00202 }
00203
00204
00205 KAccelManagerAlgorithm::findAccelerators(contents, used);
00206
00207
00208 int cnt = -1;
00209 foreach(Item *it, *item->m_children)
00210 {
00211 cnt++;
00212
00213 QDockWidget *dock = qobject_cast<QDockWidget*>(it->m_widget);
00214 if (dock)
00215 {
00216 if (checkChange(contents[cnt]))
00217 dock->setWindowTitle(contents[cnt].accelerated());
00218 continue;
00219 }
00220 QTabBar *tabBar = qobject_cast<QTabBar*>(it->m_widget);
00221 if (tabBar)
00222 {
00223 if (checkChange(contents[cnt]))
00224 tabBar->setTabText(it->m_index, contents[cnt].accelerated());
00225 continue;
00226 }
00227 QMenuBar *menuBar = qobject_cast<QMenuBar*>(it->m_widget);
00228 if (menuBar)
00229 {
00230 if (it->m_index >= 0)
00231 {
00232 QAction *maction = menuBar->actions()[it->m_index];
00233 if (maction)
00234 {
00235 checkChange(contents[cnt]);
00236 maction->setText(contents[cnt].accelerated());
00237 }
00238 continue;
00239 }
00240 }
00241
00242 if ( qobject_cast<QGroupBox*>( it->m_widget ) )
00243 continue;
00244
00245 int tprop = it->m_widget->metaObject()->indexOfProperty("text");
00246 if (tprop != -1) {
00247 if (checkChange(contents[cnt]))
00248 it->m_widget->setProperty("text", contents[cnt].accelerated());
00249 } else {
00250 tprop = it->m_widget->metaObject()->indexOfProperty("title");
00251 if (tprop != -1 && checkChange(contents[cnt]))
00252 it->m_widget->setProperty("title", contents[cnt].accelerated());
00253 }
00254 }
00255
00256
00257 foreach(Item *it, *item->m_children)
00258 {
00259 if (it->m_widget && it->m_widget->isVisibleTo( item->m_widget ) )
00260 calculateAccelerators(it, used);
00261 }
00262 }
00263
00264
00265 void KAcceleratorManagerPrivate::traverseChildren(QWidget *widget, Item *item)
00266 {
00267 QList<QWidget*> childList = widget->findChildren<QWidget*>();
00268 foreach ( QWidget *w , childList ) {
00269
00270 if(qobject_cast<QWidget *>(w->parent()) != widget) continue;
00271
00272 if ( !w->isVisibleTo( widget ) || (w->isTopLevel() && qobject_cast<QMenu*>(w) == NULL) )
00273 continue;
00274
00275 if ( KAcceleratorManagerPrivate::ignored_widgets.contains( w ) )
00276 continue;
00277
00278 manageWidget(w, item);
00279 }
00280 }
00281
00282 void KAcceleratorManagerPrivate::manageWidget(QWidget *w, Item *item)
00283 {
00284
00285
00286 QTabBar *tabBar = qobject_cast<QTabBar*>(w);
00287 if (tabBar)
00288 {
00289 manageTabBar(tabBar, item);
00290 return;
00291 }
00292
00293 QStackedWidget *wds = qobject_cast<QStackedWidget*>( w );
00294 if ( wds )
00295 {
00296 QWidgetStackAccelManager::manage( wds );
00297
00298 }
00299
00300 QDockWidget *dock = qobject_cast<QDockWidget*>( w );
00301 if ( dock )
00302 {
00303
00304 manageDockWidget(dock, item);
00305 }
00306
00307
00308 QMenu *popupMenu = qobject_cast<QMenu*>(w);
00309 if (popupMenu)
00310 {
00311
00312 KPopupAccelManager::manage(popupMenu);
00313 return;
00314 }
00315
00316 QStackedWidget *wdst = qobject_cast<QStackedWidget*>( w );
00317 if ( wdst )
00318 {
00319 QWidgetStackAccelManager::manage( wdst );
00320
00321 }
00322
00323 QMenuBar *menuBar = qobject_cast<QMenuBar*>(w);
00324 if (menuBar)
00325 {
00326 manageMenuBar(menuBar, item);
00327 return;
00328 }
00329
00330 if (qobject_cast<QComboBox*>(w) || qobject_cast<QLineEdit*>(w) ||
00331 w->inherits("Q3TextEdit") ||
00332 qobject_cast<QTextEdit*>(w) ||
00333 qobject_cast<QAbstractSpinBox*>(w) || w->inherits( "KMultiTabBar" ) )
00334 return;
00335
00336
00337 QLabel *label = qobject_cast<QLabel*>(w);
00338 if ( label ) {
00339 if ( !label->buddy() )
00340 return;
00341 else {
00342 if ( label->textFormat() == Qt::RichText ||
00343 ( label->textFormat() == Qt::AutoText &&
00344 Qt::mightBeRichText( label->text() ) ) )
00345 return;
00346 }
00347 }
00348
00349 if (w->focusPolicy() != Qt::NoFocus || label || qobject_cast<QGroupBox*>(w) || qobject_cast<QRadioButton*>( w ))
00350 {
00351 QString content;
00352 QVariant variant;
00353 int tprop = w->metaObject()->indexOfProperty("text");
00354 if (tprop != -1) {
00355 QMetaProperty p = w->metaObject()->property( tprop );
00356 if ( p.isValid() )
00357 variant = p.read (w);
00358 else
00359 tprop = -1;
00360 }
00361
00362 if (tprop == -1) {
00363 tprop = w->metaObject()->indexOfProperty("title");
00364 if (tprop != -1) {
00365 QMetaProperty p = w->metaObject()->property( tprop );
00366 if ( p.isValid() )
00367 variant = p.read (w);
00368 }
00369 }
00370
00371 if (variant.isValid())
00372 content = variant.toString();
00373
00374 if (!content.isEmpty())
00375 {
00376 Item *i = new Item;
00377 i->m_widget = w;
00378
00379
00380 int weight = KAccelManagerAlgorithm::DEFAULT_WEIGHT;
00381 if (qobject_cast<QPushButton*>(w) || qobject_cast<QCheckBox*>(w) || qobject_cast<QRadioButton*>(w) || qobject_cast<QLabel*>(w))
00382 weight = KAccelManagerAlgorithm::ACTION_ELEMENT_WEIGHT;
00383
00384
00385 if (qobject_cast<QGroupBox*>(w))
00386 weight = KAccelManagerAlgorithm::GROUP_BOX_WEIGHT;
00387 i->m_content = KAccelString(content, weight);
00388 item->addChild(i);
00389 }
00390 }
00391 traverseChildren(w, item);
00392 }
00393
00394 void KAcceleratorManagerPrivate::manageTabBar(QTabBar *bar, Item *item)
00395 {
00396 for (int i=0; i<bar->count(); i++)
00397 {
00398 QString content = bar->tabText(i);
00399 if (content.isEmpty())
00400 continue;
00401
00402 Item *it = new Item;
00403 item->addChild(it);
00404 it->m_widget = bar;
00405 it->m_index = i;
00406 it->m_content = KAccelString(content);
00407 }
00408 }
00409
00410 void KAcceleratorManagerPrivate::manageDockWidget(QDockWidget *dock, Item *item)
00411 {
00412 QString content = dock->windowTitle();
00413 if (content.isEmpty())
00414 return;
00415
00416 Item *it = new Item;
00417 item->addChild(it);
00418 it->m_widget = dock;
00419 it->m_content = KAccelString(content);
00420 }
00421
00422
00423 void KAcceleratorManagerPrivate::manageMenuBar(QMenuBar *mbar, Item *item)
00424 {
00425 QAction *maction;
00426 QString s;
00427
00428 for (int i=0; i<mbar->actions().count(); ++i)
00429 {
00430 maction = mbar->actions()[i];
00431 if (!maction)
00432 continue;
00433
00434
00435 if (maction->isSeparator())
00436 continue;
00437
00438 s = maction->text();
00439 if (!s.isEmpty())
00440 {
00441 Item *it = new Item;
00442 item->addChild(it);
00443 it->m_content =
00444 KAccelString(s,
00445
00446 KAccelManagerAlgorithm::MENU_TITLE_WEIGHT);
00447
00448 it->m_widget = mbar;
00449 it->m_index = i;
00450 }
00451
00452
00453 if (maction->menu())
00454 KPopupAccelManager::manage(maction->menu());
00455 }
00456 }
00457
00458
00459
00460
00461
00462
00463
00464
00465
00466
00467 void KAcceleratorManager::manage(QWidget *widget, bool programmers_mode)
00468 {
00469 KAcceleratorManagerPrivate::changed_string.clear();
00470 KAcceleratorManagerPrivate::added_string.clear();
00471 KAcceleratorManagerPrivate::removed_string.clear();
00472 KAcceleratorManagerPrivate::programmers_mode = programmers_mode;
00473 KAcceleratorManagerPrivate::manage(widget);
00474 }
00475
00476 void KAcceleratorManager::last_manage(QString &added, QString &changed, QString &removed)
00477 {
00478 added = KAcceleratorManagerPrivate::added_string;
00479 changed = KAcceleratorManagerPrivate::changed_string;
00480 removed = KAcceleratorManagerPrivate::removed_string;
00481 }
00482
00483
00484
00485
00486
00487
00488
00489
00490 KAccelString::KAccelString(const QString &input, int initialWeight)
00491 : m_pureText(input), m_weight()
00492 {
00493 m_orig_accel = m_pureText.indexOf("(!)&");
00494 if (m_orig_accel != -1)
00495 m_pureText.remove(m_orig_accel, 4);
00496
00497 m_orig_accel = m_pureText.indexOf("(&&)");
00498 if (m_orig_accel != -1)
00499 m_pureText.replace(m_orig_accel, 4, "&");
00500
00501 m_origText = m_pureText;
00502
00503 if (m_pureText.contains('\t'))
00504 m_pureText = m_pureText.left(m_pureText.indexOf('\t'));
00505
00506 m_orig_accel = m_accel = stripAccelerator(m_pureText);
00507
00508 if (initialWeight == -1)
00509 initialWeight = KAccelManagerAlgorithm::DEFAULT_WEIGHT;
00510
00511 calculateWeights(initialWeight);
00512
00513
00514 }
00515
00516
00517 QString KAccelString::accelerated() const
00518 {
00519 QString result = m_origText;
00520 if (result.isEmpty())
00521 return result;
00522
00523 if (KAcceleratorManagerPrivate::programmers_mode)
00524 {
00525 if (m_accel != m_orig_accel) {
00526 int oa = m_orig_accel;
00527
00528 if (m_accel >= 0) {
00529 result.insert(m_accel, "(!)&");
00530 if (m_accel < m_orig_accel)
00531 oa += 4;
00532 }
00533 if (m_orig_accel >= 0)
00534 result.replace(oa, 1, "(&&)");
00535 }
00536 } else {
00537 if (m_accel >= 0 && m_orig_accel != m_accel) {
00538 if (m_orig_accel != -1)
00539 result.remove(m_orig_accel, 1);
00540 result.insert(m_accel, "&");
00541 }
00542 }
00543 return result;
00544 }
00545
00546
00547 QChar KAccelString::accelerator() const
00548 {
00549 if ((m_accel < 0) || (m_accel > (int)m_pureText.length()))
00550 return QChar();
00551
00552 return m_pureText[m_accel].toLower();
00553 }
00554
00555
00556 void KAccelString::calculateWeights(int initialWeight)
00557 {
00558 m_weight.resize(m_pureText.length());
00559
00560 int pos = 0;
00561 bool start_character = true;
00562
00563 while (pos<m_pureText.length())
00564 {
00565 QChar c = m_pureText[pos];
00566
00567 int weight = initialWeight+1;
00568
00569
00570 if (pos == 0)
00571 weight += KAccelManagerAlgorithm::FIRST_CHARACTER_EXTRA_WEIGHT;
00572
00573
00574 if (start_character)
00575 {
00576 weight += KAccelManagerAlgorithm::WORD_BEGINNING_EXTRA_WEIGHT;
00577 start_character = false;
00578 }
00579
00580
00581 if (pos < 50)
00582 weight += (50-pos);
00583
00584
00585 if ((int)pos == accel()) {
00586 weight += KAccelManagerAlgorithm::WANTED_ACCEL_EXTRA_WEIGHT;
00587
00588 if (KAcceleratorManagerPrivate::standardName(m_origText)) {
00589 weight += KAccelManagerAlgorithm::STANDARD_ACCEL;
00590 }
00591 }
00592
00593
00594 if (!c.isLetterOrNumber())
00595 {
00596 weight = 0;
00597 start_character = true;
00598 }
00599
00600 m_weight[pos] = weight;
00601
00602 ++pos;
00603 }
00604 }
00605
00606
00607 int KAccelString::stripAccelerator(QString &text)
00608 {
00609
00610 int p = 0;
00611
00612 while (p >= 0)
00613 {
00614 p = text.indexOf('&', p)+1;
00615
00616 if (p <= 0 || p >= (int)text.length())
00617 return -1;
00618
00619 if (text[p] != '&')
00620 {
00621 QChar c = text[p];
00622 if (c.isPrint())
00623 {
00624 text.remove(p-1,1);
00625 return p-1;
00626 }
00627 }
00628
00629 p++;
00630 }
00631
00632 return -1;
00633 }
00634
00635
00636 int KAccelString::maxWeight(int &index, const QString &used) const
00637 {
00638 int max = 0;
00639 index = -1;
00640
00641 for (int pos=0; pos<m_pureText.length(); ++pos)
00642 if (used.indexOf(m_pureText[pos], 0, Qt::CaseInsensitive) == -1 && m_pureText[pos].toLatin1() != 0)
00643 if (m_weight[pos] > max)
00644 {
00645 max = m_weight[pos];
00646 index = pos;
00647 }
00648
00649 return max;
00650 }
00651
00652
00653 void KAccelString::dump()
00654 {
00655 QString s;
00656 for (int i=0; i<m_weight.count(); ++i)
00657 s += QString("%1(%2) ").arg(pure()[i]).arg(m_weight[i]);
00658 kDebug() << "s " << s;
00659 }
00660
00661
00662
00663
00664
00665
00666
00667
00668
00669
00670
00671
00672
00673
00674
00675
00676
00677
00678
00679
00680
00681
00682
00683
00684
00685
00686
00687
00688
00689
00690
00691
00692
00693
00694
00695 void KAccelManagerAlgorithm::findAccelerators(KAccelStringList &result, QString &used)
00696 {
00697 KAccelStringList accel_strings = result;
00698
00699
00700 for (KAccelStringList::Iterator it = result.begin(); it != result.end(); ++it) {
00701 (*it).setAccel(-1);
00702 }
00703
00704
00705 for (int cnt=0; cnt<accel_strings.count(); ++cnt)
00706 {
00707 int max = 0, index = -1, accel = -1;
00708
00709
00710 for (int i=0; i<accel_strings.count(); ++i)
00711 {
00712 int a;
00713 int m = accel_strings[i].maxWeight(a, used);
00714 if (m>max)
00715 {
00716 max = m;
00717 index = i;
00718 accel = a;
00719 }
00720 }
00721
00722
00723 if (index < 0)
00724 return;
00725
00726
00727 if (accel >= 0)
00728 {
00729 result[index].setAccel(accel);
00730 used.append(result[index].accelerator());
00731 }
00732
00733
00734 accel_strings[index] = KAccelString();
00735 }
00736 }
00737
00738
00739
00740
00741
00742
00743
00744
00745 KPopupAccelManager::KPopupAccelManager(QMenu *popup)
00746 : QObject(popup), m_popup(popup), m_count(-1)
00747 {
00748 aboutToShow();
00749 connect(popup, SIGNAL(aboutToShow()), SLOT(aboutToShow()));
00750 }
00751
00752
00753 void KPopupAccelManager::aboutToShow()
00754 {
00755
00756
00757
00758
00759
00760 if (m_count != (int)m_popup->actions().count())
00761 {
00762 findMenuEntries(m_entries);
00763 calculateAccelerators();
00764 m_count = m_popup->actions().count();
00765 }
00766 else
00767 {
00768 KAccelStringList entries;
00769 findMenuEntries(entries);
00770 if (entries != m_entries)
00771 {
00772 m_entries = entries;
00773 calculateAccelerators();
00774 }
00775 }
00776 }
00777
00778
00779 void KPopupAccelManager::calculateAccelerators()
00780 {
00781
00782 QString used;
00783 KAccelManagerAlgorithm::findAccelerators(m_entries, used);
00784
00785
00786 setMenuEntries(m_entries);
00787 }
00788
00789
00790 void KPopupAccelManager::findMenuEntries(KAccelStringList &list)
00791 {
00792 QAction *maction;
00793 QString s;
00794
00795 list.clear();
00796
00797
00798 for (int i=0; i<m_popup->actions().count(); i++)
00799 {
00800 maction = m_popup->actions()[i];
00801 if (maction->isSeparator())
00802 continue;
00803
00804 s = maction->text();
00805
00806
00807 int weight = 50;
00808 if (s.contains('\t'))
00809 weight = 0;
00810
00811 list.append(KAccelString(s, weight));
00812
00813
00814 if (maction->menu())
00815 KPopupAccelManager::manage(maction->menu());
00816 }
00817 }
00818
00819
00820 void KPopupAccelManager::setMenuEntries(const KAccelStringList &list)
00821 {
00822 QAction *maction;
00823
00824 uint cnt = 0;
00825 for (int i=0; i<m_popup->actions().count(); i++)
00826 {
00827 maction = m_popup->actions()[i];
00828 if (maction->isSeparator())
00829 continue;
00830
00831 if (KAcceleratorManagerPrivate::checkChange(list[cnt]))
00832 maction->setText(list[cnt].accelerated());
00833 cnt++;
00834 }
00835 }
00836
00837
00838 void KPopupAccelManager::manage(QMenu *popup)
00839 {
00840
00841 if (popup->findChild<KPopupAccelManager*>(QString()) == 0 )
00842 new KPopupAccelManager(popup);
00843 }
00844
00845 void QWidgetStackAccelManager::manage( QStackedWidget *stack )
00846 {
00847 if ( stack->findChild<QWidgetStackAccelManager*>(QString()) == 0 )
00848 new QWidgetStackAccelManager( stack );
00849 }
00850
00851 QWidgetStackAccelManager::QWidgetStackAccelManager(QStackedWidget *stack)
00852 : QObject(stack), m_stack(stack)
00853 {
00854 currentChanged(stack->currentIndex());
00855 connect(stack, SIGNAL(currentChanged(int)), SLOT(currentChanged(int)));
00856 }
00857
00858 bool QWidgetStackAccelManager::eventFilter ( QObject * watched, QEvent * e )
00859 {
00860 if ( e->type() == QEvent::Show && qApp->activeWindow() ) {
00861 KAcceleratorManager::manage( qApp->activeWindow() );
00862 watched->removeEventFilter( this );
00863 }
00864 return false;
00865 }
00866
00867 void QWidgetStackAccelManager::currentChanged(int child)
00868 {
00869 if (child < 0 || child >= static_cast<QStackedWidget*>(parent())->count())
00870 {
00871 kDebug(131) << "invalid index provided";
00872 return;
00873 }
00874
00875 static_cast<QStackedWidget*>(parent())->widget(child)->installEventFilter( this );
00876 }
00877
00878 void KAcceleratorManager::setNoAccel( QWidget *widget )
00879 {
00880 KAcceleratorManagerPrivate::ignored_widgets[widget] = 1;
00881 }
00882
00883 #include "kacceleratormanager_private.moc"