• Skip to content
  • Skip to link menu
KDE 4.6 API Reference
  • KDE API Reference
  • kdelibs
  • KDE Home
  • Contact Us
 

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"

KDEUI

Skip menu "KDEUI"
  • Main Page
  • Modules
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.7.3
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal