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
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 if (m_accel >= 0)
00415 result.insert(m_accel, "&");
00416 }
00417
00418 return result;
00419 }
00420
00421
00422 QChar KAccelString::accelerator() const
00423 {
00424 if ((m_accel < 0) || (m_accel > (int)m_pureText.length()))
00425 return QChar();
00426
00427 return m_pureText[m_accel].lower();
00428 }
00429
00430
00431 void KAccelString::calculateWeights(int initialWeight)
00432 {
00433 m_weight.resize(m_pureText.length());
00434
00435 uint pos = 0;
00436 bool start_character = true;
00437
00438 while (pos<m_pureText.length())
00439 {
00440 QChar c = m_pureText[pos];
00441
00442 int weight = initialWeight+1;
00443
00444
00445 if (pos == 0)
00446 weight += KAccelManagerAlgorithm::FIRST_CHARACTER_EXTRA_WEIGHT;
00447
00448
00449 if (start_character)
00450 {
00451 weight += KAccelManagerAlgorithm::WORD_BEGINNING_EXTRA_WEIGHT;
00452 start_character = false;
00453 }
00454
00455
00456 if (pos < 50)
00457 weight += (50-pos);
00458
00459
00460 if ((int)pos == accel())
00461 weight += KAccelManagerAlgorithm::WANTED_ACCEL_EXTRA_WEIGHT;
00462
00463
00464 if (!c.isLetterOrNumber())
00465 {
00466 weight = 0;
00467 start_character = true;
00468 }
00469
00470 m_weight[pos] = weight;
00471
00472 ++pos;
00473 }
00474 }
00475
00476
00477 int KAccelString::stripAccelerator(QString &text)
00478 {
00479
00480 int p = 0;
00481
00482 while (p >= 0)
00483 {
00484 p = text.find('&', p)+1;
00485
00486 if (p <= 0 || p >= (int)text.length())
00487 return -1;
00488
00489 if (text[p] != '&')
00490 {
00491 QChar c = text[p];
00492 if (c.isPrint())
00493 {
00494 text.remove(p-1,1);
00495 return p-1;
00496 }
00497 }
00498
00499 p++;
00500 }
00501
00502 return -1;
00503 }
00504
00505
00506 int KAccelString::maxWeight(int &index, const QString &used)
00507 {
00508 int max = 0;
00509 index = -1;
00510
00511 for (uint pos=0; pos<m_pureText.length(); ++pos)
00512 if (!used.contains(m_pureText[pos].lower()))
00513 if (m_weight[pos] > max)
00514 {
00515 max = m_weight[pos];
00516 index = pos;
00517 }
00518
00519 return max;
00520 }
00521
00522
00523 void KAccelString::dump()
00524 {
00525 QString s;
00526 for (uint i=0; i<m_weight.count(); ++i)
00527 s += QString("%1(%2)").arg(pure()[i]).arg(m_weight[i]);
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
00563
00564 void KAccelManagerAlgorithm::findAccelerators(KAccelStringList &result, QString &used)
00565 {
00566 KAccelStringList accel_strings = result;
00567
00568
00569 for (KAccelStringList::Iterator it = result.begin(); it != result.end(); ++it)
00570 (*it).setAccel(-1);
00571
00572
00573 for (uint cnt=0; cnt<accel_strings.count(); ++cnt)
00574 {
00575 int max = 0, index = -1, accel = -1;
00576
00577
00578 for (uint i=0; i<accel_strings.count(); ++i)
00579 {
00580 int a;
00581 int m = accel_strings[i].maxWeight(a, used);
00582 if (m>max)
00583 {
00584 max = m;
00585 index = i;
00586 accel = a;
00587 }
00588 }
00589
00590
00591 if (index < 0)
00592 return;
00593
00594
00595 if (accel >= 0)
00596 {
00597 result[index].setAccel(accel);
00598 used.append(result[index].accelerator());
00599 }
00600
00601
00602 accel_strings[index] = KAccelString();
00603 }
00604 }
00605
00606
00607
00608
00609
00610
00611
00612
00613 KPopupAccelManager::KPopupAccelManager(QPopupMenu *popup)
00614 : QObject(popup), m_popup(popup), m_count(-1)
00615 {
00616 connect(popup, SIGNAL(aboutToShow()), SLOT(aboutToShow()));
00617 }
00618
00619
00620 void KPopupAccelManager::aboutToShow()
00621 {
00622
00623
00624
00625
00626
00627 if (m_count != (int)m_popup->count())
00628 {
00629 findMenuEntries(m_entries);
00630 calculateAccelerators();
00631 m_count = m_popup->count();
00632 }
00633 else
00634 {
00635 KAccelStringList entries;
00636 findMenuEntries(entries);
00637 if (entries != m_entries)
00638 {
00639 m_entries = entries;
00640 calculateAccelerators();
00641 }
00642 }
00643 }
00644
00645
00646 void KPopupAccelManager::calculateAccelerators()
00647 {
00648
00649 QString used;
00650 KAccelManagerAlgorithm::findAccelerators(m_entries, used);
00651
00652
00653 setMenuEntries(m_entries);
00654 }
00655
00656
00657 void KPopupAccelManager::findMenuEntries(KAccelStringList &list)
00658 {
00659 QMenuItem *mitem;
00660 QString s;
00661
00662 list.clear();
00663
00664
00665 for (uint i=0; i<m_popup->count(); i++)
00666 {
00667 mitem = m_popup->findItem(m_popup->idAt(i));
00668 if (mitem->isSeparator())
00669 continue;
00670
00671 s = mitem->text();
00672
00673
00674 int weight = 50;
00675 if (s.contains('\t'))
00676 weight = 0;
00677
00678 if (KAcceleratorManagerPrivate::standardName(s))
00679 weight += 300;
00680
00681 list.append(KAccelString(s, weight));
00682 }
00683 }
00684
00685
00686 void KPopupAccelManager::setMenuEntries(const KAccelStringList &list)
00687 {
00688 QMenuItem *mitem;
00689
00690 uint cnt = 0;
00691 for (uint i=0; i<m_popup->count(); i++)
00692 {
00693 mitem = m_popup->findItem(m_popup->idAt(i));
00694 if (mitem->isSeparator())
00695 continue;
00696
00697 mitem->setText(list[cnt++].accelerated());
00698 }
00699 }
00700
00701
00702 void KPopupAccelManager::manage(QPopupMenu *popup)
00703 {
00704
00705 if (popup->child(0, "KPopupAccelManager", false) == 0 )
00706 new KPopupAccelManager(popup);
00707 }
00708
00709
00710 #include "kaccelmanager_private.moc"