~ubuntu-branches/ubuntu/gutsy/kde4libs/gutsy

« back to all changes in this revision

Viewing changes to kdecore/kacceleratormanager.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Jonathan Riddell
  • Date: 2007-02-21 11:00:12 UTC
  • mfrom: (1.1.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20070221110012-6kw8khr9knv6lmg1
Tags: 3.80.3-0ubuntu1
New upstream unstable release

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*  This file is part of the KDE project
2
 
    Copyright (C) 2002 Matthias Hölzer-Klüpfel <mhk@kde.org>
3
 
 
4
 
    This library is free software; you can redistribute it and/or
5
 
    modify it under the terms of the GNU Library General Public
6
 
    License as published by the Free Software Foundation; either
7
 
    version 2 of the License, or (at your option) any later version.
8
 
 
9
 
    This library is distributed in the hope that it will be useful,
10
 
    but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
 
    Library General Public License for more details.
13
 
 
14
 
    You should have received a copy of the GNU Library General Public License
15
 
    along with this library; see the file COPYING.LIB.  If not, write to
16
 
    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17
 
    Boston, MA 02110-1301, USA.
18
 
*/
19
 
 
20
 
#include "kacceleratormanager.h"
21
 
 
22
 
#include <qapplication.h>
23
 
#include <qcheckbox.h>
24
 
#include <qcombobox.h>
25
 
#include <q3groupbox.h>
26
 
#include <qlabel.h>
27
 
#include <qlineedit.h>
28
 
#include <qmenubar.h>
29
 
#include <qmenudata.h>
30
 
#include <qmetaobject.h>
31
 
#include <qobject.h>
32
 
#include <QList>
33
 
#include <qpushbutton.h>
34
 
#include <qradiobutton.h>
35
 
#include <qspinbox.h>
36
 
#include <qtabbar.h>
37
 
#include <q3textview.h>
38
 
#include <qwidget.h>
39
 
#include <QStackedWidget>
40
 
#include <QTextDocument>
41
 
 
42
 
#include <kstdaction.h>
43
 
#include <kstaticdeleter.h>
44
 
#include <kdebug.h>
45
 
 
46
 
 
47
 
#include "kacceleratormanager_private.h"
48
 
#include "../kdeui/kstdaction_p.h"
49
 
 
50
 
 
51
 
/*********************************************************************
52
 
 
53
 
 class Item - helper class containing widget information
54
 
 
55
 
 This class stores information about the widgets the need accelerators,
56
 
 as well as about their relationship.
57
 
 
58
 
 *********************************************************************/
59
 
 
60
 
 
61
 
 
62
 
/*********************************************************************
63
 
 
64
 
 class KAcceleratorManagerPrivate - internal helper class
65
 
 
66
 
 This class does all the work to find accelerators for a hierarchy of
67
 
 widgets.
68
 
 
69
 
 *********************************************************************/
70
 
 
71
 
 
72
 
class KAcceleratorManagerPrivate
73
 
{
74
 
public:
75
 
 
76
 
    static void manage(QWidget *widget);
77
 
    static bool programmers_mode;
78
 
    static bool standardName(const QString &str);
79
 
 
80
 
    static bool checkChange(const KAccelString &as)  {
81
 
        QString t2 = as.accelerated();
82
 
        QString t1 = as.originalText();
83
 
        if (t1 != t2)
84
 
        {
85
 
            if (as.accel() == -1)  {
86
 
                removed_string  += "<tr><td>" + Qt::escape(t1) + "</td></tr>";
87
 
            } else if (as.originalAccel() == -1) {
88
 
                added_string += "<tr><td>" + Qt::escape(t2) + "</td></tr>";
89
 
            } else {
90
 
                changed_string += "<tr><td>" + Qt::escape(t1) + "</td>";
91
 
                changed_string += "<td>" + Qt::escape(t2) + "</td></tr>";
92
 
            }
93
 
            return true;
94
 
        }
95
 
        return false;
96
 
    }
97
 
    static QString changed_string;
98
 
    static QString added_string;
99
 
    static QString removed_string;
100
 
    static QMap<QWidget *, int> ignored_widgets;
101
 
 
102
 
private:
103
 
  class Item;
104
 
public:
105
 
  typedef QList<Item *> ItemList;
106
 
 
107
 
private:
108
 
  static void traverseChildren(QWidget *widget, Item *item);
109
 
 
110
 
  static void manageWidget(QWidget *widget, Item *item);
111
 
  static void manageMenuBar(QMenuBar *mbar, Item *item);
112
 
  static void manageTabBar(QTabBar *bar, Item *item);
113
 
 
114
 
  static void calculateAccelerators(Item *item, QString &used);
115
 
 
116
 
  class Item
117
 
  {
118
 
  public:
119
 
 
120
 
    Item() : m_widget(0), m_children(0), m_index(-1) {};
121
 
    ~Item();
122
 
 
123
 
    void addChild(Item *item);
124
 
 
125
 
    QWidget       *m_widget;
126
 
    KAccelString  m_content;
127
 
    ItemList      *m_children;
128
 
    int           m_index;
129
 
 
130
 
  };
131
 
};
132
 
 
133
 
 
134
 
bool KAcceleratorManagerPrivate::programmers_mode = false;
135
 
QString KAcceleratorManagerPrivate::changed_string;
136
 
QString KAcceleratorManagerPrivate::added_string;
137
 
QString KAcceleratorManagerPrivate::removed_string;
138
 
static QStringList *kaccmp_sns = 0;
139
 
static KStaticDeleter<QStringList> kaccmp_sns_d;
140
 
QMap<QWidget*, int> KAcceleratorManagerPrivate::ignored_widgets;
141
 
 
142
 
bool KAcceleratorManagerPrivate::standardName(const QString &str)
143
 
{
144
 
    if (!kaccmp_sns)
145
 
        kaccmp_sns_d.setObject(kaccmp_sns, new QStringList(KStdAction::internal_stdNames()));
146
 
        return kaccmp_sns->contains(str);
147
 
}
148
 
 
149
 
KAcceleratorManagerPrivate::Item::~Item()
150
 
{
151
 
    if (m_children)
152
 
        while (!m_children->isEmpty())
153
 
            delete m_children->takeFirst();
154
 
 
155
 
    delete m_children;
156
 
}
157
 
 
158
 
 
159
 
void KAcceleratorManagerPrivate::Item::addChild(Item *item)
160
 
{
161
 
    if (!m_children) {
162
 
        m_children = new ItemList;
163
 
    }
164
 
 
165
 
    m_children->append(item);
166
 
}
167
 
 
168
 
void KAcceleratorManagerPrivate::manage(QWidget *widget)
169
 
{
170
 
    if (!widget)
171
 
    {
172
 
        kDebug(131) << "null pointer given to manage" << endl;
173
 
        return;
174
 
    }
175
 
 
176
 
    if (qobject_cast<QMenu*>(widget))
177
 
    {
178
 
        // create a popup accel manager that can deal with dynamic menus
179
 
        KPopupAccelManager::manage(static_cast<QMenu*>(widget));
180
 
        return;
181
 
    }
182
 
 
183
 
    Item *root = new Item;
184
 
 
185
 
    manageWidget(widget, root);
186
 
 
187
 
    QString used;
188
 
    calculateAccelerators(root, used);
189
 
    delete root;
190
 
}
191
 
 
192
 
 
193
 
void KAcceleratorManagerPrivate::calculateAccelerators(Item *item, QString &used)
194
 
{
195
 
    if (!item->m_children)
196
 
        return;
197
 
 
198
 
    // collect the contents
199
 
    KAccelStringList contents;
200
 
    foreach(Item *it, *item->m_children)
201
 
    {
202
 
        contents << it->m_content;
203
 
    }
204
 
 
205
 
    // find the right accelerators
206
 
    KAccelManagerAlgorithm::findAccelerators(contents, used);
207
 
 
208
 
    // write them back into the widgets
209
 
    int cnt = -1;
210
 
    foreach(Item *it, *item->m_children)
211
 
    {
212
 
        cnt++;
213
 
 
214
 
        QTabBar *tabBar = qobject_cast<QTabBar*>(it->m_widget);
215
 
        if (tabBar)
216
 
        {
217
 
            if (checkChange(contents[cnt]))
218
 
                tabBar->setTabText(it->m_index, contents[cnt].accelerated());
219
 
            continue;
220
 
        }
221
 
        QMenuBar *menuBar = qobject_cast<QMenuBar*>(it->m_widget);
222
 
        if (menuBar)
223
 
        {
224
 
            if (it->m_index >= 0)
225
 
            {
226
 
                QAction *maction = menuBar->actions()[it->m_index];
227
 
                if (maction)
228
 
                {
229
 
                    checkChange(contents[cnt]);
230
 
                    maction->setText(contents[cnt].accelerated());
231
 
                }
232
 
                continue;
233
 
            }
234
 
        }
235
 
        // we possibly reserved an accel, but we won't set it as it looks silly
236
 
        if ( qobject_cast<Q3GroupBox*>( it->m_widget ) )
237
 
             continue;
238
 
 
239
 
        kDebug(131) << "write " << cnt << " " << it->m_widget->metaObject()->className() << " " <<contents[cnt].accelerated() << endl;
240
 
 
241
 
        int tprop = it->m_widget->metaObject()->indexOfProperty("text");
242
 
        if (tprop != -1)  {
243
 
            if (checkChange(contents[cnt]))
244
 
                it->m_widget->setProperty("text", contents[cnt].accelerated());
245
 
        } else {
246
 
            tprop = it->m_widget->metaObject()->indexOfProperty("title");
247
 
            if (tprop != -1 && checkChange(contents[cnt]))
248
 
                it->m_widget->setProperty("title", contents[cnt].accelerated());
249
 
        }
250
 
    }
251
 
 
252
 
    // calculate the accelerators for the children
253
 
    foreach(Item *it, *item->m_children)
254
 
    {
255
 
        if (it->m_widget)
256
 
            kDebug(131) << "children " << it->m_widget->metaObject()->className() << endl;
257
 
        if (it->m_widget && it->m_widget->isVisibleTo( item->m_widget ) )
258
 
            calculateAccelerators(it, used);
259
 
    }
260
 
}
261
 
 
262
 
 
263
 
void KAcceleratorManagerPrivate::traverseChildren(QWidget *widget, Item *item)
264
 
{
265
 
  QList<QWidget*> childList = widget->findChildren<QWidget*>();
266
 
  foreach ( QWidget *w , childList ) {
267
 
    // Ignore unless we have the direct parent
268
 
    if(qobject_cast<QWidget *>(w->parent()) != widget) continue;
269
 
 
270
 
    if ( !w->isVisibleTo( widget ) || w->isTopLevel() )
271
 
        continue;
272
 
 
273
 
    if ( KAcceleratorManagerPrivate::ignored_widgets.find( w ) != KAcceleratorManagerPrivate::ignored_widgets.end() )
274
 
        continue;
275
 
 
276
 
    manageWidget(w, item);
277
 
  }
278
 
}
279
 
 
280
 
void KAcceleratorManagerPrivate::manageWidget(QWidget *w, Item *item)
281
 
{
282
 
  // first treat the special cases
283
 
 
284
 
  QTabBar *tabBar = qobject_cast<QTabBar*>(w);
285
 
  if (tabBar)
286
 
  {
287
 
      manageTabBar(tabBar, item);
288
 
      return;
289
 
  }
290
 
 
291
 
  QStackedWidget *wds = qobject_cast<QStackedWidget*>( w );
292
 
  if ( wds )
293
 
  {
294
 
      QWidgetStackAccelManager::manage( wds );
295
 
      // return;
296
 
  }
297
 
 
298
 
  QMenu *popupMenu = qobject_cast<QMenu*>(w);
299
 
  if (popupMenu)
300
 
  {
301
 
      // create a popup accel manager that can deal with dynamic menus
302
 
      KPopupAccelManager::manage(popupMenu);
303
 
      return;
304
 
  }
305
 
 
306
 
  QStackedWidget *wdst = qobject_cast<QStackedWidget*>( w );
307
 
  if ( wdst )
308
 
  {
309
 
      QWidgetStackAccelManager::manage( wdst );
310
 
      // return;
311
 
  }
312
 
 
313
 
  QMenuBar *menuBar = qobject_cast<QMenuBar*>(w);
314
 
  if (menuBar)
315
 
  {
316
 
      manageMenuBar(menuBar, item);
317
 
      return;
318
 
  }
319
 
 
320
 
  if (qobject_cast<QComboBox*>(w) || qobject_cast<QLineEdit*>(w) ||
321
 
      qobject_cast<Q3TextEdit*>(w) || qobject_cast<Q3TextView*>(w) ||
322
 
      qobject_cast<QSpinBox*>(w) || w->inherits( "KMultiTabBar" ) )
323
 
      return;
324
 
 
325
 
  // now treat 'ordinary' widgets
326
 
  QLabel *label =  qobject_cast<QLabel*>(w);
327
 
  if ( label  ) {
328
 
      if ( !label->buddy() )
329
 
          return;
330
 
      else {
331
 
          kDebug() << "label-text " << label->textFormat() << endl;
332
 
          if ( label->textFormat() == Qt::RichText ||
333
 
               ( label->textFormat() == Qt::AutoText &&
334
 
                 Qt::mightBeRichText( label->text() ) ) )
335
 
              return;
336
 
      }
337
 
  }
338
 
 
339
 
  if (w->focusPolicy() != Qt::NoFocus || label || qobject_cast<Q3GroupBox*>(w) || qobject_cast<QRadioButton*>( w ))
340
 
  {
341
 
    QString content;
342
 
    QVariant variant;
343
 
    int tprop = w->metaObject()->indexOfProperty("text");
344
 
    if (tprop != -1)  {
345
 
        QMetaProperty p = w->metaObject()->property( tprop );
346
 
        if ( p.isValid() )
347
 
            variant = p.read (w);
348
 
        else
349
 
            tprop = -1;
350
 
    }
351
 
 
352
 
    if (tprop == -1)  {
353
 
        tprop = w->metaObject()->indexOfProperty("title");
354
 
        if (tprop != -1)  {
355
 
            QMetaProperty p = w->metaObject()->property( tprop );
356
 
            if ( p.isValid() )
357
 
                variant = p.read (w);
358
 
        }
359
 
    }
360
 
 
361
 
    if (variant.isValid())
362
 
        content = variant.toString();
363
 
 
364
 
    if (!content.isEmpty())
365
 
    {
366
 
        Item *i = new Item;
367
 
        i->m_widget = w;
368
 
 
369
 
        // put some more weight on the usual action elements
370
 
        int weight = KAccelManagerAlgorithm::DEFAULT_WEIGHT;
371
 
        if (qobject_cast<QPushButton*>(w) || qobject_cast<QCheckBox*>(w) || qobject_cast<QRadioButton*>(w) || qobject_cast<QLabel*>(w))
372
 
            weight = KAccelManagerAlgorithm::ACTION_ELEMENT_WEIGHT;
373
 
 
374
 
        // don't put weight on group boxes, as usually the contents are more important
375
 
        if (qobject_cast<Q3GroupBox*>(w))
376
 
            weight = KAccelManagerAlgorithm::GROUP_BOX_WEIGHT;
377
 
 
378
 
        // put a lot of extra weight on the KDialogBaseButton's
379
 
        if (w->inherits("KDialogBaseButton"))
380
 
            weight += KAccelManagerAlgorithm::DIALOG_BUTTON_EXTRA_WEIGHT;
381
 
 
382
 
        i->m_content = KAccelString(content, weight);
383
 
        item->addChild(i);
384
 
    }
385
 
  }
386
 
  traverseChildren(w, item);
387
 
}
388
 
 
389
 
void KAcceleratorManagerPrivate::manageTabBar(QTabBar *bar, Item *item)
390
 
{
391
 
  for (int i=0; i<bar->count(); i++)
392
 
  {
393
 
    QString content = bar->tabText(i);
394
 
    if (content.isEmpty())
395
 
      continue;
396
 
 
397
 
    Item *it = new Item;
398
 
    item->addChild(it);
399
 
    it->m_widget = bar;
400
 
    it->m_index = i;
401
 
    it->m_content = KAccelString(content);
402
 
  }
403
 
}
404
 
 
405
 
void KAcceleratorManagerPrivate::manageMenuBar(QMenuBar *mbar, Item *item)
406
 
{
407
 
    QAction *maction;
408
 
    QString s;
409
 
 
410
 
    for (int i=0; i<mbar->actions().count(); ++i)
411
 
    {
412
 
        maction = mbar->actions()[i];
413
 
        if (!maction)
414
 
            continue;
415
 
 
416
 
        // nothing to do for separators
417
 
        if (maction->isSeparator())
418
 
            continue;
419
 
 
420
 
        s = maction->text();
421
 
        if (!s.isEmpty())
422
 
        {
423
 
            Item *it = new Item;
424
 
            item->addChild(it);
425
 
            it->m_content =
426
 
                KAccelString(s,
427
 
                             // menu titles are important, so raise the weight
428
 
                             KAccelManagerAlgorithm::MENU_TITLE_WEIGHT);
429
 
 
430
 
            it->m_widget = mbar;
431
 
            it->m_index = i;
432
 
        }
433
 
 
434
 
        // have a look at the popup as well, if present
435
 
        if (maction->menu())
436
 
            KPopupAccelManager::manage(maction->menu());
437
 
    }
438
 
}
439
 
 
440
 
 
441
 
/*********************************************************************
442
 
 
443
 
 class KAcceleratorManager - main entry point
444
 
 
445
 
 This class is just here to provide a clean public API...
446
 
 
447
 
 *********************************************************************/
448
 
 
449
 
void KAcceleratorManager::manage(QWidget *widget, bool programmers_mode)
450
 
{
451
 
    kDebug(131) << "KAcceleratorManager::manage\n";
452
 
    KAcceleratorManagerPrivate::changed_string.clear();
453
 
    KAcceleratorManagerPrivate::added_string.clear();
454
 
    KAcceleratorManagerPrivate::removed_string.clear();
455
 
    KAcceleratorManagerPrivate::programmers_mode = programmers_mode;
456
 
    KAcceleratorManagerPrivate::manage(widget);
457
 
}
458
 
 
459
 
void KAcceleratorManager::last_manage(QString &added,  QString &changed, QString &removed)
460
 
{
461
 
    added = KAcceleratorManagerPrivate::added_string;
462
 
    changed = KAcceleratorManagerPrivate::changed_string;
463
 
    removed = KAcceleratorManagerPrivate::removed_string;
464
 
}
465
 
 
466
 
 
467
 
/*********************************************************************
468
 
 
469
 
 class KAccelString - a string with weighted characters
470
 
 
471
 
 *********************************************************************/
472
 
 
473
 
KAccelString::KAccelString(const QString &input, int initialWeight)
474
 
  : m_pureText(input), m_weight()
475
 
{
476
 
    m_orig_accel = m_pureText.indexOf("(!)&");
477
 
    if (m_orig_accel != -1)
478
 
        m_pureText.remove(m_orig_accel, 4);
479
 
 
480
 
    m_orig_accel = m_pureText.indexOf("(&&)");
481
 
    if (m_orig_accel != -1)
482
 
        m_pureText.replace(m_orig_accel, 4, "&");
483
 
 
484
 
    m_origText = m_pureText;
485
 
 
486
 
    if (m_pureText.contains('\t'))
487
 
        m_pureText = m_pureText.left(m_pureText.indexOf('\t'));
488
 
 
489
 
    m_orig_accel = m_accel = stripAccelerator(m_pureText);
490
 
 
491
 
    kDebug(131) << input << " " << m_orig_accel << " " << m_accel << " " << m_pureText << endl;
492
 
    if (initialWeight == -1)
493
 
        initialWeight = KAccelManagerAlgorithm::DEFAULT_WEIGHT;
494
 
 
495
 
    calculateWeights(initialWeight);
496
 
 
497
 
    // dump();
498
 
}
499
 
 
500
 
 
501
 
QString KAccelString::accelerated() const
502
 
{
503
 
  QString result = m_origText;
504
 
  if (result.isEmpty())
505
 
      return result;
506
 
 
507
 
  if (KAcceleratorManagerPrivate::programmers_mode)
508
 
  {
509
 
    if (m_accel != m_orig_accel) {
510
 
      int oa = m_orig_accel;
511
 
 
512
 
      if (m_accel >= 0) {
513
 
              result.insert(m_accel, "(!)&");
514
 
              if (m_accel < m_orig_accel)
515
 
                  oa += 4;
516
 
      }
517
 
      if (m_orig_accel >= 0)
518
 
          result.replace(oa, 1, "(&&)");
519
 
    }
520
 
  } else {
521
 
      if (m_accel >= 0 && m_orig_accel != m_accel) {
522
 
          if (m_orig_accel != -1)
523
 
              result.remove(m_orig_accel, 1);
524
 
          result.insert(m_accel, "&");
525
 
      }
526
 
  }
527
 
  return result;
528
 
}
529
 
 
530
 
 
531
 
QChar KAccelString::accelerator() const
532
 
{
533
 
  if ((m_accel < 0) || (m_accel > (int)m_pureText.length()))
534
 
    return QChar();
535
 
 
536
 
  return m_pureText[m_accel].toLower();
537
 
}
538
 
 
539
 
 
540
 
void KAccelString::calculateWeights(int initialWeight)
541
 
{
542
 
  m_weight.resize(m_pureText.length());
543
 
 
544
 
  int pos = 0;
545
 
  bool start_character = true;
546
 
 
547
 
  while (pos<m_pureText.length())
548
 
  {
549
 
    QChar c = m_pureText[pos];
550
 
 
551
 
    int weight = initialWeight+1;
552
 
 
553
 
    // add special weight to first character
554
 
    if (pos == 0)
555
 
      weight += KAccelManagerAlgorithm::FIRST_CHARACTER_EXTRA_WEIGHT;
556
 
 
557
 
    // add weight to word beginnings
558
 
    if (start_character)
559
 
    {
560
 
      weight += KAccelManagerAlgorithm::WORD_BEGINNING_EXTRA_WEIGHT;
561
 
      start_character = false;
562
 
    }
563
 
 
564
 
    // add decreasing weight to left characters
565
 
    if (pos < 50)
566
 
      weight += (50-pos);
567
 
 
568
 
    // try to preserve the wanted accelarators
569
 
    if ((int)pos == accel()) {
570
 
        weight += KAccelManagerAlgorithm::WANTED_ACCEL_EXTRA_WEIGHT;
571
 
        // kDebug(131) << "wanted " << m_pureText << " " << KAcceleratorManagerPrivate::standardName(m_origText) << endl;
572
 
        if (KAcceleratorManagerPrivate::standardName(m_origText))  {
573
 
            weight += KAccelManagerAlgorithm::STANDARD_ACCEL;
574
 
        }
575
 
    }
576
 
 
577
 
    // skip non typeable characters
578
 
    if (!c.isLetterOrNumber())
579
 
    {
580
 
      weight = 0;
581
 
      start_character = true;
582
 
    }
583
 
 
584
 
    m_weight[pos] = weight;
585
 
 
586
 
    ++pos;
587
 
  }
588
 
}
589
 
 
590
 
 
591
 
int KAccelString::stripAccelerator(QString &text)
592
 
{
593
 
  // Note: this code is derived from QAccel::shortcutKey
594
 
  int p = 0;
595
 
 
596
 
  while (p >= 0)
597
 
  {
598
 
    p = text.indexOf('&', p)+1;
599
 
 
600
 
    if (p <= 0 || p >= (int)text.length())
601
 
      return -1;
602
 
 
603
 
    if (text[p] != '&')
604
 
    {
605
 
      QChar c = text[p];
606
 
      if (c.isPrint())
607
 
      {
608
 
        text.remove(p-1,1);
609
 
        return p-1;
610
 
      }
611
 
    }
612
 
 
613
 
    p++;
614
 
  }
615
 
 
616
 
  return -1;
617
 
}
618
 
 
619
 
 
620
 
int KAccelString::maxWeight(int &index, const QString &used)
621
 
{
622
 
  int max = 0;
623
 
  index = -1;
624
 
 
625
 
  for (int pos=0; pos<m_pureText.length(); ++pos)
626
 
    if (used.indexOf(m_pureText[pos], 0, Qt::CaseInsensitive) == -1 && m_pureText[pos].toLatin1() != 0)
627
 
      if (m_weight[pos] > max)
628
 
      {
629
 
        max = m_weight[pos];
630
 
        index = pos;
631
 
      }
632
 
 
633
 
  return max;
634
 
}
635
 
 
636
 
 
637
 
void KAccelString::dump()
638
 
{
639
 
  QString s;
640
 
  for (int i=0; i<m_weight.count(); ++i)
641
 
    s += QString("%1(%2) ").arg(pure()[i]).arg(m_weight[i]);
642
 
  kDebug() << "s " << s << endl;
643
 
}
644
 
 
645
 
 
646
 
/*********************************************************************
647
 
 
648
 
 findAccelerators - the algorithm determining the new accelerators
649
 
 
650
 
 The algorithm is very crude:
651
 
 
652
 
   * each character in each widget text is assigned a weight
653
 
   * the character with the highest weight over all is picked
654
 
   * that widget is removed from the list
655
 
   * the weights are recalculated
656
 
   * the process is repeated until no more accelerators can be found
657
 
 
658
 
 The algorithm has some advantages:
659
 
 
660
 
   * it favors 'nice' accelerators (first characters in a word, etc.)
661
 
   * it is quite fast, O(N²)
662
 
   * it is easy to understand :-)
663
 
 
664
 
 The disadvantages:
665
 
 
666
 
   * it does not try to find as many accelerators as possible
667
 
 
668
 
 TODO:
669
 
 
670
 
 * The result is always correct, but not neccesarily optimal. Perhaps
671
 
   it would be a good idea to add another algorithm with higher complexity
672
 
   that gets used when this one fails, i.e. leaves widgets without
673
 
   accelerators.
674
 
 
675
 
 * The weights probably need some tweaking so they make more sense.
676
 
 
677
 
 *********************************************************************/
678
 
 
679
 
void KAccelManagerAlgorithm::findAccelerators(KAccelStringList &result, QString &used)
680
 
{
681
 
    kDebug(131) << "findAccelerators\n";
682
 
  KAccelStringList accel_strings = result;
683
 
 
684
 
  // initally remove all accelerators
685
 
  for (KAccelStringList::Iterator it = result.begin(); it != result.end(); ++it) {
686
 
      kDebug(131) << "reset " << ( *it ).pure() << endl;
687
 
    (*it).setAccel(-1);
688
 
  }
689
 
 
690
 
  // pick the highest bids
691
 
  for (int cnt=0; cnt<accel_strings.count(); ++cnt)
692
 
  {
693
 
      kDebug(131) << "cnt " << accel_strings[cnt].pure() << endl;
694
 
    int max = 0, index = -1, accel = -1;
695
 
 
696
 
    // find maximum weight
697
 
    for (int i=0; i<accel_strings.count(); ++i)
698
 
    {
699
 
      int a;
700
 
      int m = accel_strings[i].maxWeight(a, used);
701
 
      if (m>max)
702
 
      {
703
 
        max = m;
704
 
        index = i;
705
 
        accel = a;
706
 
      }
707
 
    }
708
 
 
709
 
    // stop if no more accelerators can be found
710
 
    if (index < 0)
711
 
      return;
712
 
 
713
 
    // insert the accelerator
714
 
    if (accel >= 0)
715
 
    {
716
 
      result[index].setAccel(accel);
717
 
      used.append(result[index].accelerator());
718
 
    }
719
 
 
720
 
    // make sure we don't visit this one again
721
 
    accel_strings[index] = KAccelString();
722
 
  }
723
 
}
724
 
 
725
 
 
726
 
/*********************************************************************
727
 
 
728
 
 class KPopupAccelManager - managing QPopupMenu widgets dynamically
729
 
 
730
 
 *********************************************************************/
731
 
 
732
 
KPopupAccelManager::KPopupAccelManager(QMenu *popup)
733
 
  : QObject(popup), m_popup(popup), m_count(-1)
734
 
{
735
 
    aboutToShow(); // do one check and then connect to show
736
 
    connect(popup, SIGNAL(aboutToShow()), SLOT(aboutToShow()));
737
 
}
738
 
 
739
 
 
740
 
void KPopupAccelManager::aboutToShow()
741
 
{
742
 
  // Note: we try to be smart and avoid recalculating the accelerators
743
 
  // whenever possible. Unfortunately, there is no way to know if an
744
 
 // item has been added or removed, so we can not do much more than
745
 
  // to compare the items each time the menu is shown :-(
746
 
 
747
 
  if (m_count != (int)m_popup->actions().count())
748
 
  {
749
 
    findMenuEntries(m_entries);
750
 
    calculateAccelerators();
751
 
    m_count = m_popup->actions().count();
752
 
  }
753
 
  else
754
 
  {
755
 
    KAccelStringList entries;
756
 
    findMenuEntries(entries);
757
 
    if (entries != m_entries)
758
 
    {
759
 
      m_entries = entries;
760
 
      calculateAccelerators();
761
 
    }
762
 
  }
763
 
}
764
 
 
765
 
 
766
 
void KPopupAccelManager::calculateAccelerators()
767
 
{
768
 
  // find the new accelerators
769
 
  QString used;
770
 
  KAccelManagerAlgorithm::findAccelerators(m_entries, used);
771
 
 
772
 
  // change the menu entries
773
 
  setMenuEntries(m_entries);
774
 
}
775
 
 
776
 
 
777
 
void KPopupAccelManager::findMenuEntries(KAccelStringList &list)
778
 
{
779
 
  QAction *maction;
780
 
  QString s;
781
 
 
782
 
  list.clear();
783
 
 
784
 
  // read out the menu entries
785
 
  for (int i=0; i<m_popup->actions().count(); i++)
786
 
  {
787
 
    maction = m_popup->actions()[i];
788
 
    if (maction->isSeparator())
789
 
      continue;
790
 
 
791
 
    s = maction->text();
792
 
 
793
 
    // in full menus, look at entries with global accelerators last
794
 
    int weight = 50;
795
 
    if (s.contains('\t'))
796
 
        weight = 0;
797
 
 
798
 
    list.append(KAccelString(s, weight));
799
 
 
800
 
    // have a look at the popup as well, if present
801
 
    if (maction->menu())
802
 
        KPopupAccelManager::manage(maction->menu());
803
 
  }
804
 
}
805
 
 
806
 
 
807
 
void KPopupAccelManager::setMenuEntries(const KAccelStringList &list)
808
 
{
809
 
  QAction *maction;
810
 
 
811
 
  uint cnt = 0;
812
 
  for (int i=0; i<m_popup->actions().count(); i++)
813
 
  {
814
 
    maction = m_popup->actions()[i];
815
 
    if (maction->isSeparator())
816
 
      continue;
817
 
 
818
 
    if (KAcceleratorManagerPrivate::checkChange(list[cnt]))
819
 
        maction->setText(list[cnt].accelerated());
820
 
    cnt++;
821
 
  }
822
 
}
823
 
 
824
 
 
825
 
void KPopupAccelManager::manage(QMenu *popup)
826
 
{
827
 
  // don't add more than one manager to a popup
828
 
  if (popup->findChild<KPopupAccelManager*>(QString()) == 0 )
829
 
    new KPopupAccelManager(popup);
830
 
}
831
 
 
832
 
void QWidgetStackAccelManager::manage( QStackedWidget *stack )
833
 
{
834
 
    if ( stack->findChild<QWidgetStackAccelManager*>(QString()) == 0 )
835
 
        new QWidgetStackAccelManager( stack );
836
 
}
837
 
 
838
 
QWidgetStackAccelManager::QWidgetStackAccelManager(QStackedWidget *stack)
839
 
  : QObject(stack), m_stack(stack)
840
 
{
841
 
    currentChanged(stack->currentIndex()); // do one check and then connect to show
842
 
    connect(stack, SIGNAL(currentChanged(int)), SLOT(currentChanged(int)));
843
 
}
844
 
 
845
 
bool QWidgetStackAccelManager::eventFilter ( QObject * watched, QEvent * e )
846
 
{
847
 
    if ( e->type() == QEvent::Show && qApp->activeWindow() ) {
848
 
        KAcceleratorManager::manage( qApp->activeWindow() );
849
 
        watched->removeEventFilter( this );
850
 
    }
851
 
    return false;
852
 
}
853
 
 
854
 
void QWidgetStackAccelManager::currentChanged(int child)
855
 
{
856
 
    if (child < 0 || child >= static_cast<QStackedWidget*>(parent())->count())
857
 
    {
858
 
        kDebug(131) << k_funcinfo << "invalid index provided" << endl;
859
 
        return;
860
 
    }
861
 
 
862
 
    static_cast<QStackedWidget*>(parent())->widget(child)->installEventFilter( this );
863
 
}
864
 
 
865
 
void KAcceleratorManager::setNoAccel( QWidget *widget )
866
 
{
867
 
    KAcceleratorManagerPrivate::ignored_widgets[widget] = 1;
868
 
}
869
 
 
870
 
#include "kacceleratormanager_private.moc"