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