00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
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
00053 const int KAccelManagerAlgorithm::DEFAULT_WEIGHT = 50;
00054
00055 const int KAccelManagerAlgorithm::FIRST_CHARACTER_EXTRA_WEIGHT = 50;
00056
00057 const int KAccelManagerAlgorithm::WORD_BEGINNING_EXTRA_WEIGHT = 50;
00058
00059 const int KAccelManagerAlgorithm::DIALOG_BUTTON_EXTRA_WEIGHT = 300;
00060
00061 const int KAccelManagerAlgorithm::WANTED_ACCEL_EXTRA_WEIGHT = 150;
00062
00063 const int KAccelManagerAlgorithm::ACTION_ELEMENT_WEIGHT = 50;
00064
00065 const int KAccelManagerAlgorithm::GROUP_BOX_WEIGHT = 0;
00066
00067 const int KAccelManagerAlgorithm::MENU_TITLE_WEIGHT = 250;
00068
00069 const int KAccelManagerAlgorithm::STANDARD_ACCEL = 300;
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
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
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
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
00216 KAccelManagerAlgorithm::findAccelerators(contents, used);
00217
00218
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
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
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
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
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
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
00362 if (dynamic_cast<QGroupBox*>(w))
00363 weight = KAccelManagerAlgorithm::GROUP_BOX_WEIGHT;
00364
00365
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
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
00416 KAccelManagerAlgorithm::MENU_TITLE_WEIGHT);
00417
00418 it->m_widget = mbar;
00419 it->m_index = i;
00420 }
00421
00422
00423 if (mitem->popup())
00424 KPopupAccelManager::manage(mitem->popup());
00425 }
00426 }
00427
00428
00429
00430
00431
00432
00433
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
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
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
00544 if (pos == 0)
00545 weight += KAccelManagerAlgorithm::FIRST_CHARACTER_EXTRA_WEIGHT;
00546
00547
00548 if (start_character)
00549 {
00550 weight += KAccelManagerAlgorithm::WORD_BEGINNING_EXTRA_WEIGHT;
00551 start_character = false;
00552 }
00553
00554
00555 if (pos < 50)
00556 weight += (50-pos);
00557
00558
00559 if ((int)pos == accel()) {
00560 weight += KAccelManagerAlgorithm::WANTED_ACCEL_EXTRA_WEIGHT;
00561
00562 if (KAcceleratorManagerPrivate::standardName(m_origText)) {
00563 weight += KAccelManagerAlgorithm::STANDARD_ACCEL;
00564 }
00565 }
00566
00567
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
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
00639
00640
00641
00642
00643
00644
00645
00646
00647
00648
00649
00650
00651
00652
00653
00654
00655
00656
00657
00658
00659
00660
00661
00662
00663
00664
00665
00666
00667
00668
00669 void KAccelManagerAlgorithm::findAccelerators(KAccelStringList &result, QString &used)
00670 {
00671 KAccelStringList accel_strings = result;
00672
00673
00674 for (KAccelStringList::Iterator it = result.begin(); it != result.end(); ++it)
00675 (*it).setAccel(-1);
00676
00677
00678 for (uint cnt=0; cnt<accel_strings.count(); ++cnt)
00679 {
00680 int max = 0, index = -1, accel = -1;
00681
00682
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
00696 if (index < 0)
00697 return;
00698
00699
00700 if (accel >= 0)
00701 {
00702 result[index].setAccel(accel);
00703 used.append(result[index].accelerator());
00704 }
00705
00706
00707 accel_strings[index] = KAccelString();
00708 }
00709 }
00710
00711
00712
00713
00714
00715
00716
00717
00718 KPopupAccelManager::KPopupAccelManager(QPopupMenu *popup)
00719 : QObject(popup), m_popup(popup), m_count(-1)
00720 {
00721 aboutToShow();
00722 connect(popup, SIGNAL(aboutToShow()), SLOT(aboutToShow()));
00723 }
00724
00725
00726 void KPopupAccelManager::aboutToShow()
00727 {
00728
00729
00730
00731
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
00755 QString used;
00756 KAccelManagerAlgorithm::findAccelerators(m_entries, used);
00757
00758
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
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
00780 int weight = 50;
00781 if (s.contains('\t'))
00782 weight = 0;
00783
00784 list.append(KAccelString(s, weight));
00785
00786
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
00814 if (popup->child(0, "KPopupAccelManager", false) == 0 )
00815 new KPopupAccelManager(popup);
00816 }
00817
00818
00819 #include "kaccelmanager_private.moc"