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 (w->isFocusEnabled() || (label && label->buddy()) || dynamic_cast<QGroupBox*>(w))
00316 {
00317 QString content;
00318 QVariant variant;
00319 int tprop = w->metaObject()->findProperty("text", true);
00320 if (tprop != -1) {
00321 const QMetaProperty* p = w->metaObject()->property( tprop, true );
00322 if ( p && p->isValid() )
00323 w->qt_property( tprop, 1, &variant );
00324 else
00325 tprop = -1;
00326 }
00327
00328 if (tprop == -1) {
00329 tprop = w->metaObject()->findProperty("title", true);
00330 if (tprop != -1) {
00331 const QMetaProperty* p = w->metaObject()->property( tprop, true );
00332 if ( p && p->isValid() )
00333 w->qt_property( tprop, 1, &variant );
00334 }
00335 }
00336
00337 if (variant.isValid())
00338 content = variant.toString();
00339
00340 if (!content.isEmpty())
00341 {
00342 Item *i = new Item;
00343 i->m_widget = w;
00344
00345
00346 int weight = KAccelManagerAlgorithm::DEFAULT_WEIGHT;
00347 if (dynamic_cast<QPushButton*>(w) || dynamic_cast<QCheckBox*>(w) || dynamic_cast<QRadioButton*>(w) || dynamic_cast<QLabel*>(w))
00348 weight = KAccelManagerAlgorithm::ACTION_ELEMENT_WEIGHT;
00349
00350
00351 if (dynamic_cast<QGroupBox*>(w))
00352 weight = KAccelManagerAlgorithm::GROUP_BOX_WEIGHT;
00353
00354
00355 if (w->inherits("KDialogBaseButton"))
00356 weight += KAccelManagerAlgorithm::DIALOG_BUTTON_EXTRA_WEIGHT;
00357
00358 i->m_content = KAccelString(content, weight);
00359 item->addChild(i);
00360 }
00361 }
00362 traverseChildren(w, item);
00363 }
00364
00365 void KAcceleratorManagerPrivate::manageTabBar(QTabBar *bar, Item *item)
00366 {
00367 for (int i=0; i<bar->count(); i++)
00368 {
00369 QString content = bar->tabAt(i)->text();
00370 if (content.isEmpty())
00371 continue;
00372
00373 Item *it = new Item;
00374 item->addChild(it);
00375 it->m_widget = bar;
00376 it->m_index = i;
00377 it->m_content = KAccelString(content);
00378 }
00379 }
00380
00381
00382 void KAcceleratorManagerPrivate::manageMenuBar(QMenuBar *mbar, Item *item)
00383 {
00384 QMenuItem *mitem;
00385 QString s;
00386
00387 for (uint i=0; i<mbar->count(); ++i)
00388 {
00389 mitem = mbar->findItem(mbar->idAt(i));
00390 if (!mitem)
00391 continue;
00392
00393
00394 if (mitem->isSeparator())
00395 continue;
00396
00397 s = mitem->text();
00398 if (!s.isEmpty())
00399 {
00400 Item *it = new Item;
00401 item->addChild(it);
00402 it->m_content =
00403 KAccelString(s,
00404
00405 KAccelManagerAlgorithm::MENU_TITLE_WEIGHT);
00406
00407 it->m_widget = mbar;
00408 it->m_index = i;
00409 }
00410
00411
00412 if (mitem->popup())
00413 KPopupAccelManager::manage(mitem->popup());
00414 }
00415 }
00416
00417
00418
00419
00420
00421
00422
00423
00424
00425
00426 void KAcceleratorManager::manage(QWidget *widget)
00427 {
00428 KAcceleratorManager::manage(widget, false);
00429 }
00430
00431 void KAcceleratorManager::manage(QWidget *widget, bool programmers_mode)
00432 {
00433 KAcceleratorManagerPrivate::changed_string = QString::null;
00434 KAcceleratorManagerPrivate::added_string = QString::null;
00435 KAcceleratorManagerPrivate::removed_string = QString::null;
00436 KAcceleratorManagerPrivate::programmers_mode = programmers_mode;
00437 KAcceleratorManagerPrivate::manage(widget);
00438 }
00439
00440 void KAcceleratorManager::last_manage(QString &added, QString &changed, QString &removed)
00441 {
00442 added = KAcceleratorManagerPrivate::added_string;
00443 changed = KAcceleratorManagerPrivate::changed_string;
00444 removed = KAcceleratorManagerPrivate::removed_string;
00445 }
00446
00447
00448
00449
00450
00451
00452
00453
00454 KAccelString::KAccelString(const QString &input, int initialWeight)
00455 : m_pureText(input), m_origText(input), m_weight()
00456 {
00457 if (m_pureText.contains('\t'))
00458 m_pureText = m_pureText.left(m_pureText.find('\t'));
00459 m_orig_accel = m_pureText.find("(!)&");
00460 m_pureText.replace(m_orig_accel, 4, "");
00461 m_orig_accel = m_pureText.find("(&&)");
00462 if (m_orig_accel != -1)
00463 m_pureText.replace(m_orig_accel, 4, "&");
00464 m_orig_accel = m_accel = stripAccelerator(m_pureText);
00465
00466 kdDebug(125) << input << " " << m_orig_accel << " " << m_accel << " " << m_pureText << endl;
00467 if (initialWeight == -1)
00468 initialWeight = KAccelManagerAlgorithm::DEFAULT_WEIGHT;
00469
00470 calculateWeights(initialWeight);
00471
00472
00473 }
00474
00475
00476 QString KAccelString::accelerated() const
00477 {
00478 QString result = m_origText;
00479 if (result.isEmpty())
00480 return result;
00481
00482 if (KAcceleratorManagerPrivate::programmers_mode)
00483 {
00484 int oa = m_orig_accel;
00485
00486 if (m_accel >= 0) {
00487 if (m_accel != m_orig_accel) {
00488 result.insert(m_accel, "(!)&");
00489 if (m_accel < m_orig_accel)
00490 oa += 4;
00491 } else {
00492 result.insert(m_accel, "&");
00493 if (m_accel < m_orig_accel)
00494 oa++;
00495 }
00496 }
00497
00498 if (m_accel != m_orig_accel && m_orig_accel >= 0)
00499 result.insert(oa, "(&&)");
00500 } else {
00501 if (m_accel >= 0 && m_orig_accel != m_accel) {
00502 result.remove(m_orig_accel, 1);
00503 result.insert(m_accel, "&");
00504 }
00505 }
00506 return result;
00507 }
00508
00509
00510 QChar KAccelString::accelerator() const
00511 {
00512 if ((m_accel < 0) || (m_accel > (int)m_pureText.length()))
00513 return QChar();
00514
00515 return m_pureText[m_accel].lower();
00516 }
00517
00518
00519 void KAccelString::calculateWeights(int initialWeight)
00520 {
00521 m_weight.resize(m_pureText.length());
00522
00523 uint pos = 0;
00524 bool start_character = true;
00525
00526 while (pos<m_pureText.length())
00527 {
00528 QChar c = m_pureText[pos];
00529
00530 int weight = initialWeight+1;
00531
00532
00533 if (pos == 0)
00534 weight += KAccelManagerAlgorithm::FIRST_CHARACTER_EXTRA_WEIGHT;
00535
00536
00537 if (start_character)
00538 {
00539 weight += KAccelManagerAlgorithm::WORD_BEGINNING_EXTRA_WEIGHT;
00540 start_character = false;
00541 }
00542
00543
00544 if (pos < 50)
00545 weight += (50-pos);
00546
00547
00548 if ((int)pos == accel()) {
00549 weight += KAccelManagerAlgorithm::WANTED_ACCEL_EXTRA_WEIGHT;
00550
00551 if (KAcceleratorManagerPrivate::standardName(m_origText)) {
00552 weight += KAccelManagerAlgorithm::STANDARD_ACCEL;
00553 }
00554 }
00555
00556
00557 if (!c.isLetterOrNumber())
00558 {
00559 weight = 0;
00560 start_character = true;
00561 }
00562
00563 m_weight[pos] = weight;
00564
00565 ++pos;
00566 }
00567 }
00568
00569
00570 int KAccelString::stripAccelerator(QString &text)
00571 {
00572
00573 int p = 0;
00574
00575 while (p >= 0)
00576 {
00577 p = text.find('&', p)+1;
00578
00579 if (p <= 0 || p >= (int)text.length())
00580 return -1;
00581
00582 if (text[p] != '&')
00583 {
00584 QChar c = text[p];
00585 if (c.isPrint())
00586 {
00587 text.remove(p-1,1);
00588 return p-1;
00589 }
00590 }
00591
00592 p++;
00593 }
00594
00595 return -1;
00596 }
00597
00598
00599 int KAccelString::maxWeight(int &index, const QString &used)
00600 {
00601 int max = 0;
00602 index = -1;
00603
00604 for (uint pos=0; pos<m_pureText.length(); ++pos)
00605 if (used.find(m_pureText[pos], 0, FALSE) == -1 && m_pureText[pos].latin1() != 0)
00606 if (m_weight[pos] > max)
00607 {
00608 max = m_weight[pos];
00609 index = pos;
00610 }
00611
00612 return max;
00613 }
00614
00615
00616 void KAccelString::dump()
00617 {
00618 QString s;
00619 for (uint i=0; i<m_weight.count(); ++i)
00620 s += QString("%1(%2) ").arg(pure()[i]).arg(m_weight[i]);
00621 kdDebug() << "s " << s << endl;
00622 }
00623
00624
00625
00626
00627
00628
00629
00630
00631
00632
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 void KAccelManagerAlgorithm::findAccelerators(KAccelStringList &result, QString &used)
00659 {
00660 KAccelStringList accel_strings = result;
00661
00662
00663 for (KAccelStringList::Iterator it = result.begin(); it != result.end(); ++it)
00664 (*it).setAccel(-1);
00665
00666
00667 for (uint cnt=0; cnt<accel_strings.count(); ++cnt)
00668 {
00669 int max = 0, index = -1, accel = -1;
00670
00671
00672 for (uint i=0; i<accel_strings.count(); ++i)
00673 {
00674 int a;
00675 int m = accel_strings[i].maxWeight(a, used);
00676 if (m>max)
00677 {
00678 max = m;
00679 index = i;
00680 accel = a;
00681 }
00682 }
00683
00684
00685 if (index < 0)
00686 return;
00687
00688
00689 if (accel >= 0)
00690 {
00691 result[index].setAccel(accel);
00692 used.append(result[index].accelerator());
00693 }
00694
00695
00696 accel_strings[index] = KAccelString();
00697 }
00698 }
00699
00700
00701
00702
00703
00704
00705
00706
00707 KPopupAccelManager::KPopupAccelManager(QPopupMenu *popup)
00708 : QObject(popup), m_popup(popup), m_count(-1)
00709 {
00710 aboutToShow();
00711 connect(popup, SIGNAL(aboutToShow()), SLOT(aboutToShow()));
00712 }
00713
00714
00715 void KPopupAccelManager::aboutToShow()
00716 {
00717
00718
00719
00720
00721
00722 if (m_count != (int)m_popup->count())
00723 {
00724 findMenuEntries(m_entries);
00725 calculateAccelerators();
00726 m_count = m_popup->count();
00727 }
00728 else
00729 {
00730 KAccelStringList entries;
00731 findMenuEntries(entries);
00732 if (entries != m_entries)
00733 {
00734 m_entries = entries;
00735 calculateAccelerators();
00736 }
00737 }
00738 }
00739
00740
00741 void KPopupAccelManager::calculateAccelerators()
00742 {
00743
00744 QString used;
00745 KAccelManagerAlgorithm::findAccelerators(m_entries, used);
00746
00747
00748 setMenuEntries(m_entries);
00749 }
00750
00751
00752 void KPopupAccelManager::findMenuEntries(KAccelStringList &list)
00753 {
00754 QMenuItem *mitem;
00755 QString s;
00756
00757 list.clear();
00758
00759
00760 for (uint i=0; i<m_popup->count(); i++)
00761 {
00762 mitem = m_popup->findItem(m_popup->idAt(i));
00763 if (mitem->isSeparator())
00764 continue;
00765
00766 s = mitem->text();
00767
00768
00769 int weight = 50;
00770 if (s.contains('\t'))
00771 weight = 0;
00772
00773 list.append(KAccelString(s, weight));
00774
00775
00776 if (mitem->popup())
00777 KPopupAccelManager::manage(mitem->popup());
00778 }
00779 }
00780
00781
00782 void KPopupAccelManager::setMenuEntries(const KAccelStringList &list)
00783 {
00784 QMenuItem *mitem;
00785
00786 uint cnt = 0;
00787 for (uint i=0; i<m_popup->count(); i++)
00788 {
00789 mitem = m_popup->findItem(m_popup->idAt(i));
00790 if (mitem->isSeparator())
00791 continue;
00792
00793 if (KAcceleratorManagerPrivate::checkChange(list[cnt]))
00794 mitem->setText(list[cnt].accelerated());
00795 cnt++;
00796 }
00797 }
00798
00799
00800 void KPopupAccelManager::manage(QPopupMenu *popup)
00801 {
00802
00803 if (popup->child(0, "KPopupAccelManager", false) == 0 )
00804 new KPopupAccelManager(popup);
00805 }
00806
00807
00808 #include "kaccelmanager_private.moc"