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