• Skip to content
  • Skip to link menu
KDE 4.3 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • 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(131) << "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         // we possibly reserved an accel, but we won't set it as it looks silly
00247         if ( qobject_cast<QGroupBox*>( it->m_widget ) )
00248              continue;
00249 
00250         int tprop = it->m_widget->metaObject()->indexOfProperty("text");
00251         if (tprop != -1)  {
00252             if (checkChange(contents[cnt]))
00253                 it->m_widget->setProperty("text", contents[cnt].accelerated());
00254         } else {
00255             tprop = it->m_widget->metaObject()->indexOfProperty("title");
00256             if (tprop != -1 && checkChange(contents[cnt]))
00257                 it->m_widget->setProperty("title", contents[cnt].accelerated());
00258         }
00259     }
00260 
00261     // calculate the accelerators for the children
00262     foreach(Item *it, *item->m_children)
00263     {
00264         if (it->m_widget && it->m_widget->isVisibleTo( item->m_widget ) )
00265             calculateAccelerators(it, used);
00266     }
00267 }
00268 
00269 
00270 void KAcceleratorManagerPrivate::traverseChildren(QWidget *widget, Item *item)
00271 {
00272   QList<QWidget*> childList = widget->findChildren<QWidget*>();
00273   foreach ( QWidget *w , childList ) {
00274     // Ignore unless we have the direct parent
00275     if(qobject_cast<QWidget *>(w->parent()) != widget) continue;
00276 
00277     if ( !w->isVisibleTo( widget ) || (w->isTopLevel() && qobject_cast<QMenu*>(w) == NULL) )
00278         continue;
00279 
00280     if ( KAcceleratorManagerPrivate::ignored_widgets.contains( w ) )
00281         continue;
00282 
00283     manageWidget(w, item);
00284   }
00285 }
00286 
00287 void KAcceleratorManagerPrivate::manageWidget(QWidget *w, Item *item)
00288 {
00289   // first treat the special cases
00290 
00291   QTabBar *tabBar = qobject_cast<QTabBar*>(w);
00292   if (tabBar)
00293   {
00294       manageTabBar(tabBar, item);
00295       return;
00296   }
00297 
00298   QStackedWidget *wds = qobject_cast<QStackedWidget*>( w );
00299   if ( wds )
00300   {
00301       QWidgetStackAccelManager::manage( wds );
00302       // return;
00303   }
00304 
00305   QDockWidget *dock = qobject_cast<QDockWidget*>( w );
00306   if ( dock )
00307   {
00308       //QWidgetStackAccelManager::manage( wds );
00309       manageDockWidget(dock, item);
00310   }
00311 
00312 
00313   QMenu *popupMenu = qobject_cast<QMenu*>(w);
00314   if (popupMenu)
00315   {
00316       // create a popup accel manager that can deal with dynamic menus
00317       KPopupAccelManager::manage(popupMenu);
00318       return;
00319   }
00320 
00321   QStackedWidget *wdst = qobject_cast<QStackedWidget*>( w );
00322   if ( wdst )
00323   {
00324       QWidgetStackAccelManager::manage( wdst );
00325       // return;
00326   }
00327 
00328   QMenuBar *menuBar = qobject_cast<QMenuBar*>(w);
00329   if (menuBar)
00330   {
00331       manageMenuBar(menuBar, item);
00332       return;
00333   }
00334 
00335   if (qobject_cast<QComboBox*>(w) || qobject_cast<QLineEdit*>(w) ||
00336       w->inherits("Q3TextEdit") ||
00337       qobject_cast<QTextEdit*>(w) ||
00338       qobject_cast<QAbstractSpinBox*>(w) || w->inherits( "KMultiTabBar" ) )
00339       return;
00340 
00341   if ( w->inherits("KUrlRequester") ) {
00342     traverseChildren(w, item);
00343     return;
00344   }
00345 
00346   // now treat 'ordinary' widgets
00347   QLabel *label =  qobject_cast<QLabel*>(w);
00348   if ( label  ) {
00349       if ( !label->buddy() )
00350           return;
00351       else {
00352           if ( label->textFormat() == Qt::RichText ||
00353                ( label->textFormat() == Qt::AutoText &&
00354                  Qt::mightBeRichText( label->text() ) ) )
00355               return;
00356       }
00357   }
00358 
00359   if (w->focusPolicy() != Qt::NoFocus || label || qobject_cast<QGroupBox*>(w) || qobject_cast<QRadioButton*>( w ))
00360   {
00361     QString content;
00362     QVariant variant;
00363     int tprop = w->metaObject()->indexOfProperty("text");
00364     if (tprop != -1)  {
00365         QMetaProperty p = w->metaObject()->property( tprop );
00366         if ( p.isValid() )
00367             variant = p.read (w);
00368         else
00369             tprop = -1;
00370     }
00371 
00372     if (tprop == -1)  {
00373         tprop = w->metaObject()->indexOfProperty("title");
00374         if (tprop != -1)  {
00375             QMetaProperty p = w->metaObject()->property( tprop );
00376             if ( p.isValid() )
00377                 variant = p.read (w);
00378         }
00379     }
00380 
00381     if (variant.isValid())
00382         content = variant.toString();
00383 
00384     if (!content.isEmpty())
00385     {
00386         Item *i = new Item;
00387         i->m_widget = w;
00388 
00389         // put some more weight on the usual action elements
00390         int weight = KAccelManagerAlgorithm::DEFAULT_WEIGHT;
00391         if (qobject_cast<QPushButton*>(w) || qobject_cast<QCheckBox*>(w) || qobject_cast<QRadioButton*>(w) || qobject_cast<QLabel*>(w))
00392             weight = KAccelManagerAlgorithm::ACTION_ELEMENT_WEIGHT;
00393 
00394         // don't put weight on group boxes, as usually the contents are more important
00395         if (qobject_cast<QGroupBox*>(w))
00396             weight = KAccelManagerAlgorithm::GROUP_BOX_WEIGHT;
00397         i->m_content = KAccelString(content, weight);
00398         item->addChild(i);
00399     }
00400   }
00401   traverseChildren(w, item);
00402 }
00403 
00404 void KAcceleratorManagerPrivate::manageTabBar(QTabBar *bar, Item *item)
00405 {
00406   // ignore QTabBar for QDockWidgets, because QDockWidget on its title change
00407   // also updates its tabbar entry, so on the next run of KCheckAccelerators
00408   // this looks like a conflict and triggers a new reset of the shortcuts -> endless loop
00409   QWidget* parentWidget = bar->parentWidget();
00410   if( parentWidget )
00411   {
00412     QMainWindow* mainWindow = qobject_cast<QMainWindow*>(parentWidget);
00413     // TODO: find better hints that this is a QTabBar for QDockWidgets
00414     if( mainWindow ) // && (mainWindow->layout()->indexOf(bar) != -1)) QMainWindowLayout lacks proper support 
00415       return;
00416   }
00417 
00418   for (int i=0; i<bar->count(); i++)
00419   {
00420     QString content = bar->tabText(i);
00421     if (content.isEmpty())
00422       continue;
00423 
00424     Item *it = new Item;
00425     item->addChild(it);
00426     it->m_widget = bar;
00427     it->m_index = i;
00428     it->m_content = KAccelString(content);
00429   }
00430 }
00431 
00432 void KAcceleratorManagerPrivate::manageDockWidget(QDockWidget *dock, Item *item)
00433 {
00434     // As of Qt 4.4.3 setting a shortcut to a QDockWidget has no effect,
00435     // because a QDockWidget does not grab it, even while displaying an underscore
00436     // in the title for the given shortcut letter.
00437     // Still it is useful to set the shortcut, because if QDockWidgets are tabbed,
00438     // the tab automatically gets the same text as the QDockWidget title, including the shortcut.
00439     // And for the QTabBar the shortcut does work, it gets grabbed as usual.
00440     // Having the QDockWidget without a shortcut and resetting the tab text with a title including
00441     // the shortcut does not work, the tab text is instantly reverted to the QDockWidget title
00442     // (see also manageTabBar()).
00443     // All in all QDockWidgets and shortcuts are a little broken for now.
00444     QString content = dock->windowTitle();
00445     if (content.isEmpty())
00446         return;
00447 
00448     Item *it = new Item;
00449     item->addChild(it);
00450     it->m_widget = dock;
00451     it->m_content = KAccelString(content, KAccelManagerAlgorithm::STANDARD_ACCEL);
00452 }
00453 
00454 
00455 void KAcceleratorManagerPrivate::manageMenuBar(QMenuBar *mbar, Item *item)
00456 {
00457     QAction *maction;
00458     QString s;
00459 
00460     for (int i=0; i<mbar->actions().count(); ++i)
00461     {
00462         maction = mbar->actions()[i];
00463         if (!maction)
00464             continue;
00465 
00466         // nothing to do for separators
00467         if (maction->isSeparator())
00468             continue;
00469 
00470         s = maction->text();
00471         if (!s.isEmpty())
00472         {
00473             Item *it = new Item;
00474             item->addChild(it);
00475             it->m_content =
00476                 KAccelString(s,
00477                              // menu titles are important, so raise the weight
00478                              KAccelManagerAlgorithm::MENU_TITLE_WEIGHT);
00479 
00480             it->m_widget = mbar;
00481             it->m_index = i;
00482         }
00483 
00484         // have a look at the popup as well, if present
00485         if (maction->menu())
00486             KPopupAccelManager::manage(maction->menu());
00487     }
00488 }
00489 
00490 
00491 /*********************************************************************
00492 
00493  class KAcceleratorManager - main entry point
00494 
00495  This class is just here to provide a clean public API...
00496 
00497  *********************************************************************/
00498 
00499 void KAcceleratorManager::manage(QWidget *widget, bool programmers_mode)
00500 {
00501     KAcceleratorManagerPrivate::changed_string.clear();
00502     KAcceleratorManagerPrivate::added_string.clear();
00503     KAcceleratorManagerPrivate::removed_string.clear();
00504     KAcceleratorManagerPrivate::programmers_mode = programmers_mode;
00505     KAcceleratorManagerPrivate::manage(widget);
00506 }
00507 
00508 void KAcceleratorManager::last_manage(QString &added,  QString &changed, QString &removed)
00509 {
00510     added = KAcceleratorManagerPrivate::added_string;
00511     changed = KAcceleratorManagerPrivate::changed_string;
00512     removed = KAcceleratorManagerPrivate::removed_string;
00513 }
00514 
00515 
00516 /*********************************************************************
00517 
00518  class KAccelString - a string with weighted characters
00519 
00520  *********************************************************************/
00521 
00522 KAccelString::KAccelString(const QString &input, int initialWeight)
00523   : m_pureText(input), m_weight()
00524 {
00525     m_orig_accel = m_pureText.indexOf("(!)&");
00526     if (m_orig_accel != -1)
00527     m_pureText.remove(m_orig_accel, 4);
00528 
00529     m_orig_accel = m_pureText.indexOf("(&&)");
00530     if (m_orig_accel != -1)
00531         m_pureText.replace(m_orig_accel, 4, "&");
00532 
00533     m_origText = m_pureText;
00534 
00535     if (m_pureText.contains('\t'))
00536         m_pureText = m_pureText.left(m_pureText.indexOf('\t'));
00537 
00538     m_orig_accel = m_accel = stripAccelerator(m_pureText);
00539 
00540     if (initialWeight == -1)
00541         initialWeight = KAccelManagerAlgorithm::DEFAULT_WEIGHT;
00542 
00543     calculateWeights(initialWeight);
00544 
00545     // dump();
00546 }
00547 
00548 
00549 QString KAccelString::accelerated() const
00550 {
00551   QString result = m_origText;
00552   if (result.isEmpty())
00553       return result;
00554 
00555   if (KAcceleratorManagerPrivate::programmers_mode)
00556   {
00557     if (m_accel != m_orig_accel) {
00558       int oa = m_orig_accel;
00559 
00560       if (m_accel >= 0) {
00561               result.insert(m_accel, "(!)&");
00562               if (m_accel < m_orig_accel)
00563                   oa += 4;
00564       }
00565       if (m_orig_accel >= 0)
00566       result.replace(oa, 1, "(&&)");
00567     }
00568   } else {
00569       if (m_accel >= 0 && m_orig_accel != m_accel) {
00570           if (m_orig_accel != -1)
00571               result.remove(m_orig_accel, 1);
00572           result.insert(m_accel, "&");
00573       }
00574   }
00575   return result;
00576 }
00577 
00578 
00579 QChar KAccelString::accelerator() const
00580 {
00581   if ((m_accel < 0) || (m_accel > (int)m_pureText.length()))
00582     return QChar();
00583 
00584   return m_pureText[m_accel].toLower();
00585 }
00586 
00587 
00588 void KAccelString::calculateWeights(int initialWeight)
00589 {
00590   m_weight.resize(m_pureText.length());
00591 
00592   int pos = 0;
00593   bool start_character = true;
00594 
00595   while (pos<m_pureText.length())
00596   {
00597     QChar c = m_pureText[pos];
00598 
00599     int weight = initialWeight+1;
00600 
00601     // add special weight to first character
00602     if (pos == 0)
00603       weight += KAccelManagerAlgorithm::FIRST_CHARACTER_EXTRA_WEIGHT;
00604 
00605     // add weight to word beginnings
00606     if (start_character)
00607     {
00608       weight += KAccelManagerAlgorithm::WORD_BEGINNING_EXTRA_WEIGHT;
00609       start_character = false;
00610     }
00611 
00612     // add decreasing weight to left characters
00613     if (pos < 50)
00614       weight += (50-pos);
00615 
00616     // try to preserve the wanted accelarators
00617     if ((int)pos == accel()) {
00618         weight += KAccelManagerAlgorithm::WANTED_ACCEL_EXTRA_WEIGHT;
00619         // kDebug(131) << "wanted " << m_pureText << " " << KAcceleratorManagerPrivate::standardName(m_origText);
00620         if (KAcceleratorManagerPrivate::standardName(m_origText))  {
00621             weight += KAccelManagerAlgorithm::STANDARD_ACCEL;
00622         }
00623     }
00624 
00625     // skip non typeable characters
00626     if (!c.isLetterOrNumber())
00627     {
00628       weight = 0;
00629       start_character = true;
00630     }
00631 
00632     m_weight[pos] = weight;
00633 
00634     ++pos;
00635   }
00636 }
00637 
00638 
00639 int KAccelString::stripAccelerator(QString &text)
00640 {
00641   // Note: this code is derived from QAccel::shortcutKey
00642   int p = 0;
00643 
00644   while (p >= 0)
00645   {
00646     p = text.indexOf('&', p)+1;
00647 
00648     if (p <= 0 || p >= (int)text.length())
00649       return -1;
00650 
00651     if (text[p] != '&')
00652     {
00653       QChar c = text[p];
00654       if (c.isPrint())
00655       {
00656         text.remove(p-1,1);
00657     return p-1;
00658       }
00659     }
00660 
00661     p++;
00662   }
00663 
00664   return -1;
00665 }
00666 
00667 
00668 int KAccelString::maxWeight(int &index, const QString &used) const
00669 {
00670   int max = 0;
00671   index = -1;
00672 
00673   for (int pos=0; pos<m_pureText.length(); ++pos)
00674     if (used.indexOf(m_pureText[pos], 0, Qt::CaseInsensitive) == -1 && m_pureText[pos].toLatin1() != 0)
00675       if (m_weight[pos] > max)
00676       {
00677         max = m_weight[pos];
00678     index = pos;
00679       }
00680 
00681   return max;
00682 }
00683 
00684 
00685 void KAccelString::dump()
00686 {
00687   QString s;
00688   for (int i=0; i<m_weight.count(); ++i)
00689     s += QString("%1(%2) ").arg(pure()[i]).arg(m_weight[i]);
00690   kDebug() << "s " << s;
00691 }
00692 
00693 
00694 /*********************************************************************
00695 
00696  findAccelerators - the algorithm determining the new accelerators
00697 
00698  The algorithm is very crude:
00699 
00700    * each character in each widget text is assigned a weight
00701    * the character with the highest weight over all is picked
00702    * that widget is removed from the list
00703    * the weights are recalculated
00704    * the process is repeated until no more accelerators can be found
00705 
00706  The algorithm has some advantages:
00707 
00708    * it favors 'nice' accelerators (first characters in a word, etc.)
00709    * it is quite fast, O(N²)
00710    * it is easy to understand :-)
00711 
00712  The disadvantages:
00713 
00714    * it does not try to find as many accelerators as possible
00715 
00716  TODO:
00717 
00718  * The result is always correct, but not neccesarily optimal. Perhaps
00719    it would be a good idea to add another algorithm with higher complexity
00720    that gets used when this one fails, i.e. leaves widgets without
00721    accelerators.
00722 
00723  * The weights probably need some tweaking so they make more sense.
00724 
00725  *********************************************************************/
00726 
00727 void KAccelManagerAlgorithm::findAccelerators(KAccelStringList &result, QString &used)
00728 {
00729   KAccelStringList accel_strings = result;
00730 
00731   // initally remove all accelerators
00732   for (KAccelStringList::Iterator it = result.begin(); it != result.end(); ++it) {
00733     (*it).setAccel(-1);
00734   }
00735 
00736   // pick the highest bids
00737   for (int cnt=0; cnt<accel_strings.count(); ++cnt)
00738   {
00739     int max = 0, index = -1, accel = -1;
00740 
00741     // find maximum weight
00742     for (int i=0; i<accel_strings.count(); ++i)
00743     {
00744       int a;
00745       int m = accel_strings[i].maxWeight(a, used);
00746       if (m>max)
00747       {
00748         max = m;
00749         index = i;
00750         accel = a;
00751       }
00752     }
00753 
00754     // stop if no more accelerators can be found
00755     if (index < 0)
00756       return;
00757 
00758     // insert the accelerator
00759     if (accel >= 0)
00760     {
00761       result[index].setAccel(accel);
00762       used.append(result[index].accelerator());
00763     }
00764 
00765     // make sure we don't visit this one again
00766     accel_strings[index] = KAccelString();
00767   }
00768 }
00769 
00770 
00771 /*********************************************************************
00772 
00773  class KPopupAccelManager - managing QPopupMenu widgets dynamically
00774 
00775  *********************************************************************/
00776 
00777 KPopupAccelManager::KPopupAccelManager(QMenu *popup)
00778   : QObject(popup), m_popup(popup), m_count(-1)
00779 {
00780     aboutToShow(); // do one check and then connect to show
00781     connect(popup, SIGNAL(aboutToShow()), SLOT(aboutToShow()));
00782 }
00783 
00784 
00785 void KPopupAccelManager::aboutToShow()
00786 {
00787   // Note: we try to be smart and avoid recalculating the accelerators
00788   // whenever possible. Unfortunately, there is no way to know if an
00789  // item has been added or removed, so we can not do much more than
00790   // to compare the items each time the menu is shown :-(
00791 
00792   if (m_count != (int)m_popup->actions().count())
00793   {
00794     findMenuEntries(m_entries);
00795     calculateAccelerators();
00796     m_count = m_popup->actions().count();
00797   }
00798   else
00799   {
00800     KAccelStringList entries;
00801     findMenuEntries(entries);
00802     if (entries != m_entries)
00803     {
00804       m_entries = entries;
00805       calculateAccelerators();
00806     }
00807   }
00808 }
00809 
00810 
00811 void KPopupAccelManager::calculateAccelerators()
00812 {
00813   // find the new accelerators
00814   QString used;
00815   KAccelManagerAlgorithm::findAccelerators(m_entries, used);
00816 
00817   // change the menu entries
00818   setMenuEntries(m_entries);
00819 }
00820 
00821 
00822 void KPopupAccelManager::findMenuEntries(KAccelStringList &list)
00823 {
00824   QString s;
00825 
00826   list.clear();
00827 
00828   // read out the menu entries
00829   foreach (QAction *maction, m_popup->actions())
00830   {
00831     if (maction->isSeparator())
00832       continue;
00833 
00834     s = maction->text();
00835 
00836     // in full menus, look at entries with global accelerators last
00837     int weight = 50;
00838     if (s.contains('\t'))
00839         weight = 0;
00840 
00841     list.append(KAccelString(s, weight));
00842 
00843     // have a look at the popup as well, if present
00844     if (maction->menu())
00845         KPopupAccelManager::manage(maction->menu());
00846   }
00847 }
00848 
00849 
00850 void KPopupAccelManager::setMenuEntries(const KAccelStringList &list)
00851 {
00852   uint cnt = 0;
00853   foreach (QAction *maction, m_popup->actions())
00854   {
00855     if (maction->isSeparator())
00856       continue;
00857 
00858     if (KAcceleratorManagerPrivate::checkChange(list[cnt]))
00859         maction->setText(list[cnt].accelerated());
00860     cnt++;
00861   }
00862 }
00863 
00864 
00865 void KPopupAccelManager::manage(QMenu *popup)
00866 {
00867   // don't add more than one manager to a popup
00868   if (popup->findChild<KPopupAccelManager*>(QString()) == 0 )
00869     new KPopupAccelManager(popup);
00870 }
00871 
00872 void QWidgetStackAccelManager::manage( QStackedWidget *stack )
00873 {
00874     if ( stack->findChild<QWidgetStackAccelManager*>(QString()) == 0 )
00875         new QWidgetStackAccelManager( stack );
00876 }
00877 
00878 QWidgetStackAccelManager::QWidgetStackAccelManager(QStackedWidget *stack)
00879   : QObject(stack), m_stack(stack)
00880 {
00881     currentChanged(stack->currentIndex()); // do one check and then connect to show
00882     connect(stack, SIGNAL(currentChanged(int)), SLOT(currentChanged(int)));
00883 }
00884 
00885 bool QWidgetStackAccelManager::eventFilter ( QObject * watched, QEvent * e )
00886 {
00887     if ( e->type() == QEvent::Show && qApp->activeWindow() ) {
00888         KAcceleratorManager::manage( qApp->activeWindow() );
00889         watched->removeEventFilter( this );
00890     }
00891     return false;
00892 }
00893 
00894 void QWidgetStackAccelManager::currentChanged(int child)
00895 {
00896     if (child < 0 || child >= static_cast<QStackedWidget*>(parent())->count())
00897     {
00898         kDebug(131) << "invalid index provided";
00899         return;
00900     }
00901 
00902     static_cast<QStackedWidget*>(parent())->widget(child)->installEventFilter( this );
00903 }
00904 
00905 void KAcceleratorManager::setNoAccel( QWidget *widget )
00906 {
00907     KAcceleratorManagerPrivate::ignored_widgets[widget] = 1;
00908 }
00909 
00910 #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
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.6.1
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