KDEUI
kacceleratormanager.cpp
Go to the documentation of this file.
00001 /* This file is part of the KDE project 00002 Copyright (C) 2002 Matthias Hölzer-Klüpfel <mhk@kde.org> 00003 00004 This library is free software; you can redistribute it and/or 00005 modify it under the terms of the GNU Library General Public 00006 License as published by the Free Software Foundation; either 00007 version 2 of the License, or (at your option) any later version. 00008 00009 This library is distributed in the hope that it will be useful, 00010 but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00012 Library General Public License for more details. 00013 00014 You should have received a copy of the GNU Library General Public License 00015 along with this library; see the file COPYING.LIB. If not, write to 00016 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00017 Boston, MA 02110-1301, USA. 00018 */ 00019 00020 #include "kacceleratormanager.h" 00021 00022 #include <QtGui/QApplication> 00023 #include <QtGui/QMainWindow> 00024 #include <QtGui/QCheckBox> 00025 #include <QtGui/QComboBox> 00026 #include <QtGui/QGroupBox> 00027 #include <QtGui/QLabel> 00028 #include <QtGui/QLineEdit> 00029 #include <QtGui/QMenuBar> 00030 #include <QtGui/qmenudata.h> 00031 #include <QtCore/QMetaClassInfo> 00032 #include <QtCore/QObject> 00033 #include <QList> 00034 #include <QtGui/QPushButton> 00035 #include <QtGui/QRadioButton> 00036 #include <QtGui/QDoubleSpinBox> 00037 #include <QtGui/QTabBar> 00038 #include <QtGui/QTextEdit> 00039 #include <QtGui/QWidget> 00040 #include <QStackedWidget> 00041 #include <QDockWidget> 00042 #include <QTextDocument> 00043 00044 #include <kstandardaction.h> 00045 #include <kdebug.h> 00046 #include <kdeversion.h> 00047 #include <kglobal.h> 00048 00049 #include "kacceleratormanager_private.h" 00050 #include <kstandardaction_p.h> 00051 00052 00053 /********************************************************************* 00054 00055 class Item - helper class containing widget information 00056 00057 This class stores information about the widgets the need accelerators, 00058 as well as about their relationship. 00059 00060 *********************************************************************/ 00061 00062 00063 00064 /********************************************************************* 00065 00066 class KAcceleratorManagerPrivate - internal helper class 00067 00068 This class does all the work to find accelerators for a hierarchy of 00069 widgets. 00070 00071 *********************************************************************/ 00072 00073 00074 class KAcceleratorManagerPrivate 00075 { 00076 public: 00077 00078 static void manage(QWidget *widget); 00079 static bool programmers_mode; 00080 static bool standardName(const QString &str); 00081 00082 static bool checkChange(const KAccelString &as) { 00083 QString t2 = as.accelerated(); 00084 QString t1 = as.originalText(); 00085 if (t1 != t2) 00086 { 00087 if (as.accel() == -1) { 00088 removed_string += "<tr><td>" + Qt::escape(t1) + "</td></tr>"; 00089 } else if (as.originalAccel() == -1) { 00090 added_string += "<tr><td>" + Qt::escape(t2) + "</td></tr>"; 00091 } else { 00092 changed_string += "<tr><td>" + Qt::escape(t1) + "</td>"; 00093 changed_string += "<td>" + Qt::escape(t2) + "</td></tr>"; 00094 } 00095 return true; 00096 } 00097 return false; 00098 } 00099 static QString changed_string; 00100 static QString added_string; 00101 static QString removed_string; 00102 static QMap<QWidget *, int> ignored_widgets; 00103 00104 private: 00105 class Item; 00106 public: 00107 typedef QList<Item *> ItemList; 00108 00109 private: 00110 static void traverseChildren(QWidget *widget, Item *item); 00111 00112 static void manageWidget(QWidget *widget, Item *item); 00113 static void manageMenuBar(QMenuBar *mbar, Item *item); 00114 static void manageTabBar(QTabBar *bar, Item *item); 00115 static void manageDockWidget(QDockWidget *dock, Item *item); 00116 00117 static void calculateAccelerators(Item *item, QString &used); 00118 00119 class Item 00120 { 00121 public: 00122 00123 Item() : m_widget(0), m_children(0), m_index(-1) {} 00124 ~Item(); 00125 00126 void addChild(Item *item); 00127 00128 QWidget *m_widget; 00129 KAccelString m_content; 00130 ItemList *m_children; 00131 int m_index; 00132 00133 }; 00134 }; 00135 00136 00137 bool KAcceleratorManagerPrivate::programmers_mode = false; 00138 QString KAcceleratorManagerPrivate::changed_string; 00139 QString KAcceleratorManagerPrivate::added_string; 00140 QString KAcceleratorManagerPrivate::removed_string; 00141 K_GLOBAL_STATIC_WITH_ARGS(QStringList, kaccmp_sns, (KStandardAction::internal_stdNames())) 00142 QMap<QWidget*, int> KAcceleratorManagerPrivate::ignored_widgets; 00143 00144 bool KAcceleratorManagerPrivate::standardName(const QString &str) 00145 { 00146 return kaccmp_sns->contains(str); 00147 } 00148 00149 KAcceleratorManagerPrivate::Item::~Item() 00150 { 00151 if (m_children) 00152 while (!m_children->isEmpty()) 00153 delete m_children->takeFirst(); 00154 00155 delete m_children; 00156 } 00157 00158 00159 void KAcceleratorManagerPrivate::Item::addChild(Item *item) 00160 { 00161 if (!m_children) { 00162 m_children = new ItemList; 00163 } 00164 00165 m_children->append(item); 00166 } 00167 00168 void KAcceleratorManagerPrivate::manage(QWidget *widget) 00169 { 00170 if (!widget) 00171 { 00172 kDebug(240) << "null pointer given to manage"; 00173 return; 00174 } 00175 00176 if (KAcceleratorManagerPrivate::ignored_widgets.contains(widget)) { 00177 return; 00178 } 00179 00180 if (qobject_cast<QMenu*>(widget)) 00181 { 00182 // create a popup accel manager that can deal with dynamic menus 00183 KPopupAccelManager::manage(static_cast<QMenu*>(widget)); 00184 return; 00185 } 00186 00187 Item *root = new Item; 00188 00189 manageWidget(widget, root); 00190 00191 QString used; 00192 calculateAccelerators(root, used); 00193 delete root; 00194 } 00195 00196 00197 void KAcceleratorManagerPrivate::calculateAccelerators(Item *item, QString &used) 00198 { 00199 if (!item->m_children) 00200 return; 00201 00202 // collect the contents 00203 KAccelStringList contents; 00204 foreach(Item *it, *item->m_children) 00205 { 00206 contents << it->m_content; 00207 } 00208 00209 // find the right accelerators 00210 KAccelManagerAlgorithm::findAccelerators(contents, used); 00211 00212 // write them back into the widgets 00213 int cnt = -1; 00214 foreach(Item *it, *item->m_children) 00215 { 00216 cnt++; 00217 00218 QDockWidget *dock = qobject_cast<QDockWidget*>(it->m_widget); 00219 if (dock) 00220 { 00221 if (checkChange(contents[cnt])) 00222 dock->setWindowTitle(contents[cnt].accelerated()); 00223 continue; 00224 } 00225 QTabBar *tabBar = qobject_cast<QTabBar*>(it->m_widget); 00226 if (tabBar) 00227 { 00228 if (checkChange(contents[cnt])) 00229 tabBar->setTabText(it->m_index, contents[cnt].accelerated()); 00230 continue; 00231 } 00232 QMenuBar *menuBar = qobject_cast<QMenuBar*>(it->m_widget); 00233 if (menuBar) 00234 { 00235 if (it->m_index >= 0) 00236 { 00237 QAction *maction = menuBar->actions()[it->m_index]; 00238 if (maction) 00239 { 00240 checkChange(contents[cnt]); 00241 maction->setText(contents[cnt].accelerated()); 00242 } 00243 continue; 00244 } 00245 } 00246 00247 // we possibly reserved an accel, but we won't set it as it looks silly 00248 QGroupBox *groupBox = qobject_cast<QGroupBox*>(it->m_widget); 00249 if (groupBox && !groupBox->isCheckable()) 00250 continue; 00251 00252 int tprop = it->m_widget->metaObject()->indexOfProperty("text"); 00253 if (tprop != -1) { 00254 if (checkChange(contents[cnt])) 00255 it->m_widget->setProperty("text", contents[cnt].accelerated()); 00256 } else { 00257 tprop = it->m_widget->metaObject()->indexOfProperty("title"); 00258 if (tprop != -1 && checkChange(contents[cnt])) 00259 it->m_widget->setProperty("title", contents[cnt].accelerated()); 00260 } 00261 } 00262 00263 // calculate the accelerators for the children 00264 foreach(Item *it, *item->m_children) 00265 { 00266 if (it->m_widget && it->m_widget->isVisibleTo( item->m_widget ) ) 00267 calculateAccelerators(it, used); 00268 } 00269 } 00270 00271 00272 void KAcceleratorManagerPrivate::traverseChildren(QWidget *widget, Item *item) 00273 { 00274 QList<QWidget*> childList = widget->findChildren<QWidget*>(); 00275 foreach ( QWidget *w , childList ) { 00276 // Ignore unless we have the direct parent 00277 if(qobject_cast<QWidget *>(w->parent()) != widget) continue; 00278 00279 if ( !w->isVisibleTo( widget ) || (w->isTopLevel() && qobject_cast<QMenu*>(w) == NULL) ) 00280 continue; 00281 00282 if ( KAcceleratorManagerPrivate::ignored_widgets.contains( w ) ) 00283 continue; 00284 00285 manageWidget(w, item); 00286 } 00287 } 00288 00289 void KAcceleratorManagerPrivate::manageWidget(QWidget *w, Item *item) 00290 { 00291 // first treat the special cases 00292 00293 QTabBar *tabBar = qobject_cast<QTabBar*>(w); 00294 if (tabBar) 00295 { 00296 manageTabBar(tabBar, item); 00297 return; 00298 } 00299 00300 QStackedWidget *wds = qobject_cast<QStackedWidget*>( w ); 00301 if ( wds ) 00302 { 00303 QWidgetStackAccelManager::manage( wds ); 00304 // return; 00305 } 00306 00307 QDockWidget *dock = qobject_cast<QDockWidget*>( w ); 00308 if ( dock ) 00309 { 00310 //QWidgetStackAccelManager::manage( wds ); 00311 manageDockWidget(dock, item); 00312 } 00313 00314 00315 QMenu *popupMenu = qobject_cast<QMenu*>(w); 00316 if (popupMenu) 00317 { 00318 // create a popup accel manager that can deal with dynamic menus 00319 KPopupAccelManager::manage(popupMenu); 00320 return; 00321 } 00322 00323 QStackedWidget *wdst = qobject_cast<QStackedWidget*>( w ); 00324 if ( wdst ) 00325 { 00326 QWidgetStackAccelManager::manage( wdst ); 00327 // return; 00328 } 00329 00330 QMenuBar *menuBar = qobject_cast<QMenuBar*>(w); 00331 if (menuBar) 00332 { 00333 manageMenuBar(menuBar, item); 00334 return; 00335 } 00336 00337 if (qobject_cast<QComboBox*>(w) || qobject_cast<QLineEdit*>(w) || 00338 w->inherits("Q3TextEdit") || 00339 qobject_cast<QTextEdit*>(w) || 00340 qobject_cast<QAbstractSpinBox*>(w) || w->inherits( "KMultiTabBar" ) ) 00341 return; 00342 00343 if ( w->inherits("KUrlRequester") ) { 00344 traverseChildren(w, item); 00345 return; 00346 } 00347 00348 // now treat 'ordinary' widgets 00349 QLabel *label = qobject_cast<QLabel*>(w); 00350 if ( label ) { 00351 if ( !label->buddy() ) 00352 return; 00353 else { 00354 if ( label->textFormat() == Qt::RichText || 00355 ( label->textFormat() == Qt::AutoText && 00356 Qt::mightBeRichText( label->text() ) ) ) 00357 return; 00358 } 00359 } 00360 00361 if (w->focusPolicy() != Qt::NoFocus || label || qobject_cast<QGroupBox*>(w) || qobject_cast<QRadioButton*>( w )) 00362 { 00363 QString content; 00364 QVariant variant; 00365 int tprop = w->metaObject()->indexOfProperty("text"); 00366 if (tprop != -1) { 00367 QMetaProperty p = w->metaObject()->property( tprop ); 00368 if ( p.isValid() && p.isWritable() ) 00369 variant = p.read (w); 00370 else 00371 tprop = -1; 00372 } 00373 00374 if (tprop == -1) { 00375 tprop = w->metaObject()->indexOfProperty("title"); 00376 if (tprop != -1) { 00377 QMetaProperty p = w->metaObject()->property( tprop ); 00378 if ( p.isValid() && p.isWritable() ) 00379 variant = p.read (w); 00380 } 00381 } 00382 00383 if (variant.isValid()) 00384 content = variant.toString(); 00385 00386 if (!content.isEmpty()) 00387 { 00388 Item *i = new Item; 00389 i->m_widget = w; 00390 00391 // put some more weight on the usual action elements 00392 int weight = KAccelManagerAlgorithm::DEFAULT_WEIGHT; 00393 if (qobject_cast<QPushButton*>(w) || qobject_cast<QCheckBox*>(w) || qobject_cast<QRadioButton*>(w) || qobject_cast<QLabel*>(w)) 00394 weight = KAccelManagerAlgorithm::ACTION_ELEMENT_WEIGHT; 00395 00396 // don't put weight on non-checkable group boxes, 00397 // as usually the contents are more important 00398 QGroupBox *groupBox = qobject_cast<QGroupBox*>(w); 00399 if (groupBox) 00400 { 00401 if (groupBox->isCheckable()) 00402 weight = KAccelManagerAlgorithm::CHECKABLE_GROUP_BOX_WEIGHT; 00403 else 00404 weight = KAccelManagerAlgorithm::GROUP_BOX_WEIGHT; 00405 } 00406 00407 i->m_content = KAccelString(content, weight); 00408 item->addChild(i); 00409 } 00410 } 00411 traverseChildren(w, item); 00412 } 00413 00414 void KAcceleratorManagerPrivate::manageTabBar(QTabBar *bar, Item *item) 00415 { 00416 // ignore QTabBar for QDockWidgets, because QDockWidget on its title change 00417 // also updates its tabbar entry, so on the next run of KCheckAccelerators 00418 // this looks like a conflict and triggers a new reset of the shortcuts -> endless loop 00419 QWidget* parentWidget = bar->parentWidget(); 00420 if( parentWidget ) 00421 { 00422 QMainWindow* mainWindow = qobject_cast<QMainWindow*>(parentWidget); 00423 // TODO: find better hints that this is a QTabBar for QDockWidgets 00424 if( mainWindow ) // && (mainWindow->layout()->indexOf(bar) != -1)) QMainWindowLayout lacks proper support 00425 return; 00426 } 00427 00428 for (int i=0; i<bar->count(); i++) 00429 { 00430 QString content = bar->tabText(i); 00431 if (content.isEmpty()) 00432 continue; 00433 00434 Item *it = new Item; 00435 item->addChild(it); 00436 it->m_widget = bar; 00437 it->m_index = i; 00438 it->m_content = KAccelString(content); 00439 } 00440 } 00441 00442 void KAcceleratorManagerPrivate::manageDockWidget(QDockWidget *dock, Item *item) 00443 { 00444 // As of Qt 4.4.3 setting a shortcut to a QDockWidget has no effect, 00445 // because a QDockWidget does not grab it, even while displaying an underscore 00446 // in the title for the given shortcut letter. 00447 // Still it is useful to set the shortcut, because if QDockWidgets are tabbed, 00448 // the tab automatically gets the same text as the QDockWidget title, including the shortcut. 00449 // And for the QTabBar the shortcut does work, it gets grabbed as usual. 00450 // Having the QDockWidget without a shortcut and resetting the tab text with a title including 00451 // the shortcut does not work, the tab text is instantly reverted to the QDockWidget title 00452 // (see also manageTabBar()). 00453 // All in all QDockWidgets and shortcuts are a little broken for now. 00454 QString content = dock->windowTitle(); 00455 if (content.isEmpty()) 00456 return; 00457 00458 Item *it = new Item; 00459 item->addChild(it); 00460 it->m_widget = dock; 00461 it->m_content = KAccelString(content, KAccelManagerAlgorithm::STANDARD_ACCEL); 00462 } 00463 00464 00465 void KAcceleratorManagerPrivate::manageMenuBar(QMenuBar *mbar, Item *item) 00466 { 00467 QAction *maction; 00468 QString s; 00469 00470 for (int i=0; i<mbar->actions().count(); ++i) 00471 { 00472 maction = mbar->actions()[i]; 00473 if (!maction) 00474 continue; 00475 00476 // nothing to do for separators 00477 if (maction->isSeparator()) 00478 continue; 00479 00480 s = maction->text(); 00481 if (!s.isEmpty()) 00482 { 00483 Item *it = new Item; 00484 item->addChild(it); 00485 it->m_content = 00486 KAccelString(s, 00487 // menu titles are important, so raise the weight 00488 KAccelManagerAlgorithm::MENU_TITLE_WEIGHT); 00489 00490 it->m_widget = mbar; 00491 it->m_index = i; 00492 } 00493 00494 // have a look at the popup as well, if present 00495 if (maction->menu()) 00496 KPopupAccelManager::manage(maction->menu()); 00497 } 00498 } 00499 00500 00501 /********************************************************************* 00502 00503 class KAcceleratorManager - main entry point 00504 00505 This class is just here to provide a clean public API... 00506 00507 *********************************************************************/ 00508 00509 void KAcceleratorManager::manage(QWidget *widget, bool programmers_mode) 00510 { 00511 KAcceleratorManagerPrivate::changed_string.clear(); 00512 KAcceleratorManagerPrivate::added_string.clear(); 00513 KAcceleratorManagerPrivate::removed_string.clear(); 00514 KAcceleratorManagerPrivate::programmers_mode = programmers_mode; 00515 KAcceleratorManagerPrivate::manage(widget); 00516 } 00517 00518 void KAcceleratorManager::last_manage(QString &added, QString &changed, QString &removed) 00519 { 00520 added = KAcceleratorManagerPrivate::added_string; 00521 changed = KAcceleratorManagerPrivate::changed_string; 00522 removed = KAcceleratorManagerPrivate::removed_string; 00523 } 00524 00525 00526 /********************************************************************* 00527 00528 class KAccelString - a string with weighted characters 00529 00530 *********************************************************************/ 00531 00532 KAccelString::KAccelString(const QString &input, int initialWeight) 00533 : m_pureText(input), m_weight() 00534 { 00535 m_orig_accel = m_pureText.indexOf("(!)&"); 00536 if (m_orig_accel != -1) 00537 m_pureText.remove(m_orig_accel, 4); 00538 00539 m_orig_accel = m_pureText.indexOf("(&&)"); 00540 if (m_orig_accel != -1) 00541 m_pureText.replace(m_orig_accel, 4, "&"); 00542 00543 m_origText = m_pureText; 00544 00545 if (m_pureText.contains('\t')) 00546 m_pureText = m_pureText.left(m_pureText.indexOf('\t')); 00547 00548 m_orig_accel = m_accel = stripAccelerator(m_pureText); 00549 00550 if (initialWeight == -1) 00551 initialWeight = KAccelManagerAlgorithm::DEFAULT_WEIGHT; 00552 00553 calculateWeights(initialWeight); 00554 00555 // dump(); 00556 } 00557 00558 00559 QString KAccelString::accelerated() const 00560 { 00561 QString result = m_origText; 00562 if (result.isEmpty()) 00563 return result; 00564 00565 if (KAcceleratorManagerPrivate::programmers_mode) 00566 { 00567 if (m_accel != m_orig_accel) { 00568 int oa = m_orig_accel; 00569 00570 if (m_accel >= 0) { 00571 result.insert(m_accel, "(!)&"); 00572 if (m_accel < m_orig_accel) 00573 oa += 4; 00574 } 00575 if (m_orig_accel >= 0) 00576 result.replace(oa, 1, "(&&)"); 00577 } 00578 } else { 00579 if (m_accel >= 0 && m_orig_accel != m_accel) { 00580 if (m_orig_accel != -1) 00581 result.remove(m_orig_accel, 1); 00582 result.insert(m_accel, "&"); 00583 } 00584 } 00585 return result; 00586 } 00587 00588 00589 QChar KAccelString::accelerator() const 00590 { 00591 if ((m_accel < 0) || (m_accel > (int)m_pureText.length())) 00592 return QChar(); 00593 00594 return m_pureText[m_accel].toLower(); 00595 } 00596 00597 00598 void KAccelString::calculateWeights(int initialWeight) 00599 { 00600 m_weight.resize(m_pureText.length()); 00601 00602 int pos = 0; 00603 bool start_character = true; 00604 00605 while (pos<m_pureText.length()) 00606 { 00607 QChar c = m_pureText[pos]; 00608 00609 int weight = initialWeight+1; 00610 00611 // add special weight to first character 00612 if (pos == 0) 00613 weight += KAccelManagerAlgorithm::FIRST_CHARACTER_EXTRA_WEIGHT; 00614 00615 // add weight to word beginnings 00616 if (start_character) 00617 { 00618 weight += KAccelManagerAlgorithm::WORD_BEGINNING_EXTRA_WEIGHT; 00619 start_character = false; 00620 } 00621 00622 // add decreasing weight to left characters 00623 if (pos < 50) 00624 weight += (50-pos); 00625 00626 // try to preserve the wanted accelarators 00627 if ((int)pos == accel()) { 00628 weight += KAccelManagerAlgorithm::WANTED_ACCEL_EXTRA_WEIGHT; 00629 // kDebug(240) << "wanted " << m_pureText << " " << KAcceleratorManagerPrivate::standardName(m_origText); 00630 if (KAcceleratorManagerPrivate::standardName(m_origText)) { 00631 weight += KAccelManagerAlgorithm::STANDARD_ACCEL; 00632 } 00633 } 00634 00635 // skip non typeable characters 00636 if (!c.isLetterOrNumber()) 00637 { 00638 weight = 0; 00639 start_character = true; 00640 } 00641 00642 m_weight[pos] = weight; 00643 00644 ++pos; 00645 } 00646 } 00647 00648 00649 int KAccelString::stripAccelerator(QString &text) 00650 { 00651 // Note: this code is derived from QAccel::shortcutKey 00652 int p = 0; 00653 00654 while (p >= 0) 00655 { 00656 p = text.indexOf('&', p)+1; 00657 00658 if (p <= 0 || p >= (int)text.length()) 00659 return -1; 00660 00661 if (text[p] != '&') 00662 { 00663 QChar c = text[p]; 00664 if (c.isPrint()) 00665 { 00666 text.remove(p-1,1); 00667 return p-1; 00668 } 00669 } 00670 00671 p++; 00672 } 00673 00674 return -1; 00675 } 00676 00677 00678 int KAccelString::maxWeight(int &index, const QString &used) const 00679 { 00680 int max = 0; 00681 index = -1; 00682 00683 for (int pos=0; pos<m_pureText.length(); ++pos) 00684 if (used.indexOf(m_pureText[pos], 0, Qt::CaseInsensitive) == -1 && m_pureText[pos].toLatin1() != 0) 00685 if (m_weight[pos] > max) 00686 { 00687 max = m_weight[pos]; 00688 index = pos; 00689 } 00690 00691 return max; 00692 } 00693 00694 00695 void KAccelString::dump() 00696 { 00697 QString s; 00698 for (int i=0; i<m_weight.count(); ++i) 00699 s += QString("%1(%2) ").arg(pure()[i]).arg(m_weight[i]); 00700 kDebug() << "s " << s; 00701 } 00702 00703 00704 /********************************************************************* 00705 00706 findAccelerators - the algorithm determining the new accelerators 00707 00708 The algorithm is very crude: 00709 00710 * each character in each widget text is assigned a weight 00711 * the character with the highest weight over all is picked 00712 * that widget is removed from the list 00713 * the weights are recalculated 00714 * the process is repeated until no more accelerators can be found 00715 00716 The algorithm has some advantages: 00717 00718 * it favors 'nice' accelerators (first characters in a word, etc.) 00719 * it is quite fast, O(N²) 00720 * it is easy to understand :-) 00721 00722 The disadvantages: 00723 00724 * it does not try to find as many accelerators as possible 00725 00726 TODO: 00727 00728 * The result is always correct, but not necessarily optimal. Perhaps 00729 it would be a good idea to add another algorithm with higher complexity 00730 that gets used when this one fails, i.e. leaves widgets without 00731 accelerators. 00732 00733 * The weights probably need some tweaking so they make more sense. 00734 00735 *********************************************************************/ 00736 00737 void KAccelManagerAlgorithm::findAccelerators(KAccelStringList &result, QString &used) 00738 { 00739 KAccelStringList accel_strings = result; 00740 00741 // initially remove all accelerators 00742 for (KAccelStringList::Iterator it = result.begin(); it != result.end(); ++it) { 00743 (*it).setAccel(-1); 00744 } 00745 00746 // pick the highest bids 00747 for (int cnt=0; cnt<accel_strings.count(); ++cnt) 00748 { 00749 int max = 0, index = -1, accel = -1; 00750 00751 // find maximum weight 00752 for (int i=0; i<accel_strings.count(); ++i) 00753 { 00754 int a; 00755 int m = accel_strings[i].maxWeight(a, used); 00756 if (m>max) 00757 { 00758 max = m; 00759 index = i; 00760 accel = a; 00761 } 00762 } 00763 00764 // stop if no more accelerators can be found 00765 if (index < 0) 00766 return; 00767 00768 // insert the accelerator 00769 if (accel >= 0) 00770 { 00771 result[index].setAccel(accel); 00772 used.append(result[index].accelerator()); 00773 } 00774 00775 // make sure we don't visit this one again 00776 accel_strings[index] = KAccelString(); 00777 } 00778 } 00779 00780 00781 /********************************************************************* 00782 00783 class KPopupAccelManager - managing QPopupMenu widgets dynamically 00784 00785 *********************************************************************/ 00786 00787 KPopupAccelManager::KPopupAccelManager(QMenu *popup) 00788 : QObject(popup), m_popup(popup), m_count(-1) 00789 { 00790 aboutToShow(); // do one check and then connect to show 00791 connect(popup, SIGNAL(aboutToShow()), SLOT(aboutToShow())); 00792 } 00793 00794 00795 void KPopupAccelManager::aboutToShow() 00796 { 00797 // Note: we try to be smart and avoid recalculating the accelerators 00798 // whenever possible. Unfortunately, there is no way to know if an 00799 // item has been added or removed, so we can not do much more than 00800 // to compare the items each time the menu is shown :-( 00801 00802 if (m_count != (int)m_popup->actions().count()) 00803 { 00804 findMenuEntries(m_entries); 00805 calculateAccelerators(); 00806 m_count = m_popup->actions().count(); 00807 } 00808 else 00809 { 00810 KAccelStringList entries; 00811 findMenuEntries(entries); 00812 if (entries != m_entries) 00813 { 00814 m_entries = entries; 00815 calculateAccelerators(); 00816 } 00817 } 00818 } 00819 00820 00821 void KPopupAccelManager::calculateAccelerators() 00822 { 00823 // find the new accelerators 00824 QString used; 00825 KAccelManagerAlgorithm::findAccelerators(m_entries, used); 00826 00827 // change the menu entries 00828 setMenuEntries(m_entries); 00829 } 00830 00831 00832 void KPopupAccelManager::findMenuEntries(KAccelStringList &list) 00833 { 00834 QString s; 00835 00836 list.clear(); 00837 00838 // read out the menu entries 00839 foreach (QAction *maction, m_popup->actions()) 00840 { 00841 if (maction->isSeparator()) 00842 continue; 00843 00844 s = maction->text(); 00845 00846 // in full menus, look at entries with global accelerators last 00847 int weight = 50; 00848 if (s.contains('\t')) 00849 weight = 0; 00850 00851 list.append(KAccelString(s, weight)); 00852 00853 // have a look at the popup as well, if present 00854 if (maction->menu()) 00855 KPopupAccelManager::manage(maction->menu()); 00856 } 00857 } 00858 00859 00860 void KPopupAccelManager::setMenuEntries(const KAccelStringList &list) 00861 { 00862 uint cnt = 0; 00863 foreach (QAction *maction, m_popup->actions()) 00864 { 00865 if (maction->isSeparator()) 00866 continue; 00867 00868 if (KAcceleratorManagerPrivate::checkChange(list[cnt])) 00869 maction->setText(list[cnt].accelerated()); 00870 cnt++; 00871 } 00872 } 00873 00874 00875 void KPopupAccelManager::manage(QMenu *popup) 00876 { 00877 // don't add more than one manager to a popup 00878 if (popup->findChild<KPopupAccelManager*>(QString()) == 0 ) 00879 new KPopupAccelManager(popup); 00880 } 00881 00882 void QWidgetStackAccelManager::manage( QStackedWidget *stack ) 00883 { 00884 if ( stack->findChild<QWidgetStackAccelManager*>(QString()) == 0 ) 00885 new QWidgetStackAccelManager( stack ); 00886 } 00887 00888 QWidgetStackAccelManager::QWidgetStackAccelManager(QStackedWidget *stack) 00889 : QObject(stack), m_stack(stack) 00890 { 00891 currentChanged(stack->currentIndex()); // do one check and then connect to show 00892 connect(stack, SIGNAL(currentChanged(int)), SLOT(currentChanged(int))); 00893 } 00894 00895 bool QWidgetStackAccelManager::eventFilter ( QObject * watched, QEvent * e ) 00896 { 00897 if ( e->type() == QEvent::Show && qApp->activeWindow() ) { 00898 KAcceleratorManager::manage( qApp->activeWindow() ); 00899 watched->removeEventFilter( this ); 00900 } 00901 return false; 00902 } 00903 00904 void QWidgetStackAccelManager::currentChanged(int child) 00905 { 00906 if (child < 0 || child >= static_cast<QStackedWidget*>(parent())->count()) 00907 { 00908 kDebug(240) << "invalid index provided"; 00909 return; 00910 } 00911 00912 static_cast<QStackedWidget*>(parent())->widget(child)->installEventFilter( this ); 00913 } 00914 00915 void KAcceleratorManager::setNoAccel( QWidget *widget ) 00916 { 00917 KAcceleratorManagerPrivate::ignored_widgets[widget] = 1; 00918 } 00919 00920 #include "kacceleratormanager_private.moc"
KDE 4.6 API Reference