kdecore Library API Documentation

kaccelmanager.cpp

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., 59 Temple Place - Suite 330,
00017     Boston, MA 02111-1307, USA.
00018 */
00019 
00020 
00021 #include <qapplication.h>
00022 #include <qcheckbox.h>
00023 #include <qcombobox.h>
00024 #include <qgroupbox.h>
00025 #include <qlabel.h>
00026 #include <qlineedit.h>
00027 #include <qmenubar.h>
00028 #include <qmemarray.h>
00029 #include <qmetaobject.h>
00030 #include <qmainwindow.h>
00031 #include <qobjectlist.h>
00032 #include <qpopupmenu.h>
00033 #include <qptrlist.h>
00034 #include <qpushbutton.h>
00035 #include <qradiobutton.h>
00036 #include <qspinbox.h>
00037 #include <qtabbar.h>
00038 #include <qtextview.h>
00039 #include <qwidget.h>
00040 #include <qwidgetstack.h>
00041 
00042 #include <kstdaction.h>
00043 #include <kstaticdeleter.h>
00044 #include <kdebug.h>
00045 
00046 
00047 #include "kaccelmanager_private.h"
00048 #include "../kdeui/kstdaction_p.h"
00049 
00050 #include "kaccelmanager.h"
00051 
00052 // Default control weight
00053 const int KAccelManagerAlgorithm::DEFAULT_WEIGHT = 50;
00054 // Additional weight for first character in string
00055 const int KAccelManagerAlgorithm::FIRST_CHARACTER_EXTRA_WEIGHT = 50;
00056 // Additional weight for the beginning of a word
00057 const int KAccelManagerAlgorithm::WORD_BEGINNING_EXTRA_WEIGHT = 50;
00058 // Additional weight for the dialog buttons (large, we basically never want these reassigned)
00059 const int KAccelManagerAlgorithm::DIALOG_BUTTON_EXTRA_WEIGHT = 300;
00060 // Additional weight for a 'wanted' accelerator
00061 const int KAccelManagerAlgorithm::WANTED_ACCEL_EXTRA_WEIGHT = 150;
00062 // Default weight for an 'action' widget (ie, pushbuttons)
00063 const int KAccelManagerAlgorithm::ACTION_ELEMENT_WEIGHT = 50;
00064 // Default weight for group boxes (low priority)
00065 const int KAccelManagerAlgorithm::GROUP_BOX_WEIGHT = 0;
00066 // Default weight for menu titles
00067 const int KAccelManagerAlgorithm::MENU_TITLE_WEIGHT = 250;
00068 // Additional weight for KDE standard accelerators
00069 const int KAccelManagerAlgorithm::STANDARD_ACCEL = 300;
00070 
00071 /*********************************************************************
00072 
00073  class Item - helper class containing widget information
00074 
00075  This class stores information about the widgets the need accelerators,
00076  as well as about their relationship.
00077 
00078  *********************************************************************/
00079 
00080 
00081 
00082 /*********************************************************************
00083 
00084  class KAcceleratorManagerPrivate - internal helper class
00085 
00086  This class does all the work to find accelerators for a hierarchy of
00087  widgets.
00088 
00089  *********************************************************************/
00090 
00091 
00092 class KAcceleratorManagerPrivate
00093 {
00094 public:
00095 
00096     static void manage(QWidget *widget);
00097     static bool programmers_mode;
00098     static bool standardName(const QString &str);
00099 
00100     static bool checkChange(const KAccelString &as)  {
00101         QString t2 = as.accelerated();
00102         QString t1 = as.originalText();
00103         if (t1 != t2)
00104         {
00105             if (as.accel() == -1)  {
00106                 removed_string  += "<tr><td>" + t1 + "</td></tr>";
00107             } else if (as.originalAccel() == -1) {
00108                 added_string += "<tr><td>" + t2 + "</td></tr>";
00109             } else {
00110                 changed_string += "<tr><td>" + t1 + "</td>";
00111                 changed_string += "<td>" + t2 + "</td></tr>";
00112             }
00113             return true;
00114         }
00115         return false;
00116     }
00117     static QString changed_string;
00118     static QString added_string;
00119     static QString removed_string;
00120 
00121 private:
00122   class Item;
00123 public:
00124   typedef QPtrList<Item> ItemList;
00125 
00126 private:
00127   static void traverseChildren(QWidget *widget, Item *item);
00128 
00129   static void manageWidget(QWidget *widget, Item *item);
00130   static void manageMenuBar(QMenuBar *mbar, Item *item);
00131   static void manageTabBar(QTabBar *bar, Item *item);
00132 
00133   static void calculateAccelerators(Item *item, QString &used);
00134 
00135   class Item
00136   {
00137   public:
00138 
00139     Item() : m_widget(0), m_children(0), m_index(-1) {};
00140     ~Item();
00141 
00142     void addChild(Item *item);
00143 
00144     QWidget       *m_widget;
00145     KAccelString  m_content;
00146     ItemList      *m_children;
00147     int           m_index;
00148 
00149   };
00150 };
00151 
00152 
00153 bool KAcceleratorManagerPrivate::programmers_mode = false;
00154 QString KAcceleratorManagerPrivate::changed_string;
00155 QString KAcceleratorManagerPrivate::added_string;
00156 QString KAcceleratorManagerPrivate::removed_string;
00157 static QStringList *kaccmp_sns = 0;
00158 static KStaticDeleter<QStringList> kaccmp_sns_d;
00159 
00160 bool KAcceleratorManagerPrivate::standardName(const QString &str)
00161 {
00162     if (!kaccmp_sns)
00163         kaccmp_sns_d.setObject(kaccmp_sns, new QStringList(KStdAction::internal_stdNames()));
00164         return kaccmp_sns->contains(str);
00165 }
00166 
00167 KAcceleratorManagerPrivate::Item::~Item()
00168 {
00169     delete m_children;
00170 }
00171 
00172 
00173 void KAcceleratorManagerPrivate::Item::addChild(Item *item)
00174 {
00175     if (!m_children) {
00176         m_children = new ItemList;
00177     m_children->setAutoDelete(true);
00178     }
00179 
00180     m_children->append(item);
00181 }
00182 
00183 void KAcceleratorManagerPrivate::manage(QWidget *widget)
00184 {
00185     if (dynamic_cast<QPopupMenu*>(widget))
00186     {
00187         // create a popup accel manager that can deal with dynamic menus
00188         KPopupAccelManager::manage(static_cast<QPopupMenu*>(widget));
00189         return;
00190     }
00191 
00192     Item *root = new Item;
00193 
00194     manageWidget(widget, root);
00195 
00196     QString used;
00197     calculateAccelerators(root, used);
00198     delete root;
00199 }
00200 
00201 
00202 void KAcceleratorManagerPrivate::calculateAccelerators(Item *item, QString &used)
00203 {
00204     if (!item->m_children)
00205         return;
00206 
00207     // collect the contents
00208     KAccelStringList contents;
00209     for (Item *it = item->m_children->first(); it != 0;
00210          it = item->m_children->next())
00211     {
00212         contents << it->m_content;
00213     }
00214 
00215     // find the right accelerators
00216     KAccelManagerAlgorithm::findAccelerators(contents, used);
00217 
00218     // write them back into the widgets
00219     int cnt = -1;
00220     for (Item *it = item->m_children->first(); it != 0;
00221          it = item->m_children->next())
00222     {
00223         cnt++;
00224 
00225         QTabBar *tabBar = dynamic_cast<QTabBar*>(it->m_widget);
00226         if (tabBar)
00227         {
00228             if (checkChange(contents[cnt]))
00229                 tabBar->tabAt(it->m_index)->setText(contents[cnt].accelerated());
00230             continue;
00231         }
00232         QMenuBar *menuBar = dynamic_cast<QMenuBar*>(it->m_widget);
00233         if (menuBar)
00234         {
00235             if (it->m_index >= 0)
00236             {
00237                 QMenuItem *mitem = menuBar->findItem(menuBar->idAt(it->m_index));
00238                 if (mitem)
00239                 {
00240                     checkChange(contents[cnt]);
00241                     mitem->setText(contents[cnt].accelerated());
00242                 }
00243                 continue;
00244             }
00245         }
00246         int tprop = it->m_widget->metaObject()->findProperty("text", true);
00247         if (tprop != -1)  {
00248             if (checkChange(contents[cnt]))
00249                 it->m_widget->setProperty("text", contents[cnt].accelerated());
00250         } else {
00251             tprop = it->m_widget->metaObject()->findProperty("title", true);
00252             if (tprop != -1 && checkChange(contents[cnt]))
00253                 it->m_widget->setProperty("title", contents[cnt].accelerated());
00254         }
00255     }
00256 
00257     // calculate the accelerators for the children
00258     for (Item *it = item->m_children->first(); it != 0;
00259          it = item->m_children->next())
00260     {
00261         if (it->m_widget && it->m_widget->isVisibleTo( item->m_widget ))
00262             calculateAccelerators(it, used);
00263     }
00264 }
00265 
00266 
00267 void KAcceleratorManagerPrivate::traverseChildren(QWidget *widget, Item *item)
00268 {
00269   QObjectList *childList = widget->queryList("QWidget", 0, false, false);
00270   for ( QObject *it = childList->first(); it; it = childList->next() )
00271   {
00272     QWidget *w = static_cast<QWidget*>(it);
00273 
00274     if ( !w->isVisibleTo( widget ) )
00275         continue;
00276 
00277     manageWidget(w, item);
00278   }
00279   delete childList;
00280 }
00281 
00282 void KAcceleratorManagerPrivate::manageWidget(QWidget *w, Item *item)
00283 {
00284   // first treat the special cases
00285 
00286   QTabBar *tabBar = dynamic_cast<QTabBar*>(w);
00287   if (tabBar)
00288   {
00289       manageTabBar(tabBar, item);
00290       return;
00291   }
00292 
00293   QPopupMenu *popupMenu = dynamic_cast<QPopupMenu*>(w);
00294   if (popupMenu)
00295   {
00296       // create a popup accel manager that can deal with dynamic menus
00297       KPopupAccelManager::manage(popupMenu);
00298       return;
00299   }
00300 
00301   QMenuBar *menuBar = dynamic_cast<QMenuBar*>(w);
00302   if (menuBar)
00303   {
00304       manageMenuBar(menuBar, item);
00305       return;
00306   }
00307 
00308   if (dynamic_cast<QComboBox*>(w) || dynamic_cast<QLineEdit*>(w) ||
00309       dynamic_cast<QTextEdit*>(w) || dynamic_cast<QTextView*>(w) ||
00310       dynamic_cast<QSpinBox*>(w))
00311       return;
00312 
00313   // now treat 'ordinary' widgets
00314   QLabel *label =  dynamic_cast<QLabel*>(w);
00315   if ( label  ) { 
00316      if ( !label->buddy() ) 
00317           label = 0; 
00318       else { 
00319           if ( label->textFormat() == Qt::RichText || 
00320                ( label->textFormat() == Qt::AutoText && 
00321                  QStyleSheet::mightBeRichText( label->text() ) ) ) 
00322               label = 0; 
00323       } 
00324   } 
00325  
00326   if (w->isFocusEnabled() || label || dynamic_cast<QGroupBox*>(w) || dynamic_cast<QRadioButton*>( w ))    
00327   {
00328     QString content;
00329     QVariant variant;
00330     int tprop = w->metaObject()->findProperty("text", true);
00331     if (tprop != -1)  {
00332         const QMetaProperty* p = w->metaObject()->property( tprop, true );
00333         if ( p && p->isValid() )
00334             w->qt_property( tprop, 1, &variant );
00335         else
00336             tprop = -1;
00337     }
00338 
00339     if (tprop == -1)  {
00340         tprop = w->metaObject()->findProperty("title", true);
00341         if (tprop != -1)  {
00342             const QMetaProperty* p = w->metaObject()->property( tprop, true );
00343             if ( p && p->isValid() )
00344                 w->qt_property( tprop, 1, &variant );
00345         }
00346     }
00347 
00348     if (variant.isValid())
00349         content = variant.toString();
00350 
00351     if (!content.isEmpty())
00352     {
00353         Item *i = new Item;
00354         i->m_widget = w;
00355 
00356         // put some more weight on the usual action elements
00357         int weight = KAccelManagerAlgorithm::DEFAULT_WEIGHT;
00358         if (dynamic_cast<QPushButton*>(w) || dynamic_cast<QCheckBox*>(w) || dynamic_cast<QRadioButton*>(w) || dynamic_cast<QLabel*>(w))
00359             weight = KAccelManagerAlgorithm::ACTION_ELEMENT_WEIGHT;
00360 
00361         // don't put weight on group boxes, as usually the contents are more important
00362         if (dynamic_cast<QGroupBox*>(w))
00363             weight = KAccelManagerAlgorithm::GROUP_BOX_WEIGHT;
00364 
00365         // put a lot of extra weight on the KDialogBaseButton's
00366         if (w->inherits("KDialogBaseButton"))
00367             weight += KAccelManagerAlgorithm::DIALOG_BUTTON_EXTRA_WEIGHT;
00368 
00369         i->m_content = KAccelString(content, weight);
00370         item->addChild(i);
00371     }
00372   }
00373   traverseChildren(w, item);
00374 }
00375 
00376 void KAcceleratorManagerPrivate::manageTabBar(QTabBar *bar, Item *item)
00377 {
00378   for (int i=0; i<bar->count(); i++)
00379   {
00380     QString content = bar->tabAt(i)->text();
00381     if (content.isEmpty())
00382       continue;
00383 
00384     Item *it = new Item;
00385     item->addChild(it);
00386     it->m_widget = bar;
00387     it->m_index = i;
00388     it->m_content = KAccelString(content);
00389   }
00390 }
00391 
00392 
00393 void KAcceleratorManagerPrivate::manageMenuBar(QMenuBar *mbar, Item *item)
00394 {
00395     QMenuItem *mitem;
00396     QString s;
00397 
00398     for (uint i=0; i<mbar->count(); ++i)
00399     {
00400         mitem = mbar->findItem(mbar->idAt(i));
00401         if (!mitem)
00402             continue;
00403 
00404         // nothing to do for separators
00405         if (mitem->isSeparator())
00406             continue;
00407 
00408         s = mitem->text();
00409         if (!s.isEmpty())
00410         {
00411             Item *it = new Item;
00412             item->addChild(it);
00413             it->m_content =
00414                 KAccelString(s,
00415                              // menu titles are important, so raise the weight
00416                              KAccelManagerAlgorithm::MENU_TITLE_WEIGHT);
00417 
00418             it->m_widget = mbar;
00419             it->m_index = i;
00420         }
00421 
00422         // have a look at the popup as well, if present
00423         if (mitem->popup())
00424             KPopupAccelManager::manage(mitem->popup());
00425     }
00426 }
00427 
00428 
00429 /*********************************************************************
00430 
00431  class KAcceleratorManager - main entry point
00432 
00433  This class is just here to provide a clean public API...
00434 
00435  *********************************************************************/
00436 
00437 void KAcceleratorManager::manage(QWidget *widget)
00438 {
00439     KAcceleratorManager::manage(widget, false);
00440 }
00441 
00442 void KAcceleratorManager::manage(QWidget *widget, bool programmers_mode)
00443 {
00444     KAcceleratorManagerPrivate::changed_string = QString::null;
00445     KAcceleratorManagerPrivate::added_string = QString::null;
00446     KAcceleratorManagerPrivate::removed_string = QString::null;
00447     KAcceleratorManagerPrivate::programmers_mode = programmers_mode;
00448     KAcceleratorManagerPrivate::manage(widget);
00449 }
00450 
00451 void KAcceleratorManager::last_manage(QString &added,  QString &changed, QString &removed)
00452 {
00453     added = KAcceleratorManagerPrivate::added_string;
00454     changed = KAcceleratorManagerPrivate::changed_string;
00455     removed = KAcceleratorManagerPrivate::removed_string;
00456 }
00457 
00458 
00459 /*********************************************************************
00460 
00461  class KAccelString - a string with weighted characters
00462 
00463  *********************************************************************/
00464 
00465 KAccelString::KAccelString(const QString &input, int initialWeight)
00466   : m_pureText(input), m_origText(input), m_weight()
00467 {
00468     if (m_pureText.contains('\t'))
00469         m_pureText = m_pureText.left(m_pureText.find('\t'));
00470     m_orig_accel = m_pureText.find("(!)&");
00471     m_pureText.replace(m_orig_accel, 4, "");
00472     m_orig_accel = m_pureText.find("(&&)");
00473     if (m_orig_accel != -1)
00474         m_pureText.replace(m_orig_accel, 4, "&");
00475     m_orig_accel = m_accel = stripAccelerator(m_pureText);
00476 
00477     kdDebug(125) << input << " " << m_orig_accel << " " << m_accel << " " << m_pureText << endl;
00478     if (initialWeight == -1)
00479         initialWeight = KAccelManagerAlgorithm::DEFAULT_WEIGHT;
00480 
00481     calculateWeights(initialWeight);
00482 
00483     // dump();
00484 }
00485 
00486 
00487 QString KAccelString::accelerated() const
00488 {
00489   QString result = m_origText;
00490   if (result.isEmpty())
00491       return result;
00492 
00493   if (KAcceleratorManagerPrivate::programmers_mode)
00494   {
00495       int oa = m_orig_accel;
00496 
00497       if (m_accel >= 0) {
00498           if (m_accel != m_orig_accel) {
00499               result.insert(m_accel, "(!)&");
00500               if (m_accel < m_orig_accel)
00501                   oa += 4;
00502           } else {
00503               result.insert(m_accel, "&");
00504               if (m_accel < m_orig_accel)
00505                   oa++;
00506           }
00507       }
00508 
00509       if (m_accel != m_orig_accel && m_orig_accel >= 0)
00510           result.insert(oa, "(&&)");
00511   } else {
00512       if (m_accel >= 0 && m_orig_accel != m_accel) {
00513           result.remove(m_orig_accel, 1);
00514           result.insert(m_accel, "&");
00515       }
00516   }
00517   return result;
00518 }
00519 
00520 
00521 QChar KAccelString::accelerator() const
00522 {
00523   if ((m_accel < 0) || (m_accel > (int)m_pureText.length()))
00524     return QChar();
00525 
00526   return m_pureText[m_accel].lower();
00527 }
00528 
00529 
00530 void KAccelString::calculateWeights(int initialWeight)
00531 {
00532   m_weight.resize(m_pureText.length());
00533 
00534   uint pos = 0;
00535   bool start_character = true;
00536 
00537   while (pos<m_pureText.length())
00538   {
00539     QChar c = m_pureText[pos];
00540 
00541     int weight = initialWeight+1;
00542 
00543     // add special weight to first character
00544     if (pos == 0)
00545       weight += KAccelManagerAlgorithm::FIRST_CHARACTER_EXTRA_WEIGHT;
00546 
00547     // add weight to word beginnings
00548     if (start_character)
00549     {
00550       weight += KAccelManagerAlgorithm::WORD_BEGINNING_EXTRA_WEIGHT;
00551       start_character = false;
00552     }
00553 
00554     // add decreasing weight to left characters
00555     if (pos < 50)
00556       weight += (50-pos);
00557 
00558     // try to preserve the wanted accelarators
00559     if ((int)pos == accel()) {
00560         weight += KAccelManagerAlgorithm::WANTED_ACCEL_EXTRA_WEIGHT;
00561         // kdDebug() << "wanted " << m_pureText << " " << KAcceleratorManagerPrivate::standardName(m_origText) << endl;
00562         if (KAcceleratorManagerPrivate::standardName(m_origText))  {
00563             weight += KAccelManagerAlgorithm::STANDARD_ACCEL;
00564         }
00565     }
00566 
00567     // skip non typeable characters
00568     if (!c.isLetterOrNumber())
00569     {
00570       weight = 0;
00571       start_character = true;
00572     }
00573 
00574     m_weight[pos] = weight;
00575 
00576     ++pos;
00577   }
00578 }
00579 
00580 
00581 int KAccelString::stripAccelerator(QString &text)
00582 {
00583   // Note: this code is derived from QAccel::shortcutKey
00584   int p = 0;
00585 
00586   while (p >= 0)
00587   {
00588     p = text.find('&', p)+1;
00589 
00590     if (p <= 0 || p >= (int)text.length())
00591       return -1;
00592 
00593     if (text[p] != '&')
00594     {
00595       QChar c = text[p];
00596       if (c.isPrint())
00597       {
00598         text.remove(p-1,1);
00599     return p-1;
00600       }
00601     }
00602 
00603     p++;
00604   }
00605 
00606   return -1;
00607 }
00608 
00609 
00610 int KAccelString::maxWeight(int &index, const QString &used)
00611 {
00612   int max = 0;
00613   index = -1;
00614 
00615   for (uint pos=0; pos<m_pureText.length(); ++pos)
00616     if (used.find(m_pureText[pos], 0, FALSE) == -1 && m_pureText[pos].latin1() != 0)
00617       if (m_weight[pos] > max)
00618       {
00619         max = m_weight[pos];
00620     index = pos;
00621       }
00622 
00623   return max;
00624 }
00625 
00626 
00627 void KAccelString::dump()
00628 {
00629   QString s;
00630   for (uint i=0; i<m_weight.count(); ++i)
00631     s += QString("%1(%2) ").arg(pure()[i]).arg(m_weight[i]);
00632   kdDebug() << "s " << s << endl;
00633 }
00634 
00635 
00636 /*********************************************************************
00637 
00638  findAccelerators - the algorithm determining the new accelerators
00639 
00640  The algorithm is very crude:
00641 
00642    * each character in each widget text is assigned a weight
00643    * the character with the highest weight over all is picked
00644    * that widget is removed from the list
00645    * the weights are recalculated
00646    * the process is repeated until no more accelerators can be found
00647 
00648  The algorithm has some advantages:
00649 
00650    * it favors 'nice' accelerators (first characters in a word, etc.)
00651    * it is quite fast, O(N²)
00652    * it is easy to understand :-)
00653 
00654  The disadvantages:
00655 
00656    * it does not try to find as many accelerators as possible
00657 
00658  TODO:
00659 
00660  * The result is always correct, but not neccesarily optimal. Perhaps
00661    it would be a good idea to add another algorithm with higher complexity
00662    that gets used when this one fails, i.e. leaves widgets without
00663    accelerators.
00664 
00665  * The weights probably need some tweaking so they make more sense.
00666 
00667  *********************************************************************/
00668 
00669 void KAccelManagerAlgorithm::findAccelerators(KAccelStringList &result, QString &used)
00670 {
00671   KAccelStringList accel_strings = result;
00672 
00673   // initally remove all accelerators
00674   for (KAccelStringList::Iterator it = result.begin(); it != result.end(); ++it)
00675     (*it).setAccel(-1);
00676 
00677   // pick the highest bids
00678   for (uint cnt=0; cnt<accel_strings.count(); ++cnt)
00679   {
00680     int max = 0, index = -1, accel = -1;
00681 
00682     // find maximum weight
00683     for (uint i=0; i<accel_strings.count(); ++i)
00684     {
00685       int a;
00686       int m = accel_strings[i].maxWeight(a, used);
00687       if (m>max)
00688       {
00689         max = m;
00690         index = i;
00691         accel = a;
00692       }
00693     }
00694 
00695     // stop if no more accelerators can be found
00696     if (index < 0)
00697       return;
00698 
00699     // insert the accelerator
00700     if (accel >= 0)
00701     {
00702       result[index].setAccel(accel);
00703       used.append(result[index].accelerator());
00704     }
00705 
00706     // make sure we don't visit this one again
00707     accel_strings[index] = KAccelString();
00708   }
00709 }
00710 
00711 
00712 /*********************************************************************
00713 
00714  class KPopupAccelManager - managing QPopupMenu widgets dynamically
00715 
00716  *********************************************************************/
00717 
00718 KPopupAccelManager::KPopupAccelManager(QPopupMenu *popup)
00719   : QObject(popup), m_popup(popup), m_count(-1)
00720 {
00721     aboutToShow(); // do one check and then connect to show
00722     connect(popup, SIGNAL(aboutToShow()), SLOT(aboutToShow()));
00723 }
00724 
00725 
00726 void KPopupAccelManager::aboutToShow()
00727 {
00728   // Note: we try to be smart and avoid recalculating the accelerators
00729   // whenever possible. Unfortunately, there is no way to know if an
00730   // item has been added or removed, so we can not do much more than
00731   // to compare the items each time the menu is shown :-(
00732 
00733   if (m_count != (int)m_popup->count())
00734   {
00735     findMenuEntries(m_entries);
00736     calculateAccelerators();
00737     m_count = m_popup->count();
00738   }
00739   else
00740   {
00741     KAccelStringList entries;
00742     findMenuEntries(entries);
00743     if (entries != m_entries)
00744     {
00745       m_entries = entries;
00746       calculateAccelerators();
00747     }
00748   }
00749 }
00750 
00751 
00752 void KPopupAccelManager::calculateAccelerators()
00753 {
00754   // find the new accelerators
00755   QString used;
00756   KAccelManagerAlgorithm::findAccelerators(m_entries, used);
00757 
00758   // change the menu entries
00759   setMenuEntries(m_entries);
00760 }
00761 
00762 
00763 void KPopupAccelManager::findMenuEntries(KAccelStringList &list)
00764 {
00765   QMenuItem *mitem;
00766   QString s;
00767 
00768   list.clear();
00769 
00770   // read out the menu entries
00771   for (uint i=0; i<m_popup->count(); i++)
00772   {
00773     mitem = m_popup->findItem(m_popup->idAt(i));
00774     if (mitem->isSeparator())
00775       continue;
00776 
00777     s = mitem->text();
00778 
00779     // in full menus, look at entries with global accelerators last
00780     int weight = 50;
00781     if (s.contains('\t'))
00782         weight = 0;
00783 
00784     list.append(KAccelString(s, weight));
00785 
00786     // have a look at the popup as well, if present
00787     if (mitem->popup())
00788         KPopupAccelManager::manage(mitem->popup());
00789   }
00790 }
00791 
00792 
00793 void KPopupAccelManager::setMenuEntries(const KAccelStringList &list)
00794 {
00795   QMenuItem *mitem;
00796 
00797   uint cnt = 0;
00798   for (uint i=0; i<m_popup->count(); i++)
00799   {
00800     mitem = m_popup->findItem(m_popup->idAt(i));
00801     if (mitem->isSeparator())
00802       continue;
00803 
00804     if (KAcceleratorManagerPrivate::checkChange(list[cnt]))
00805         mitem->setText(list[cnt].accelerated());
00806     cnt++;
00807   }
00808 }
00809 
00810 
00811 void KPopupAccelManager::manage(QPopupMenu *popup)
00812 {
00813   // don't add more than one manager to a popup
00814   if (popup->child(0, "KPopupAccelManager", false) == 0 )
00815     new KPopupAccelManager(popup);
00816 }
00817 
00818 
00819 #include "kaccelmanager_private.moc"
KDE Logo
This file is part of the documentation for kdecore Library Version 3.3.90.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Wed Mar 30 10:09:37 2005 by doxygen 1.3.9.1 written by Dimitri van Heesch, © 1997-2003