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"