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