00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #define INCLUDE_MENUITEM_DEF
00024 #include <qmenudata.h>
00025
00026 #include "config.h"
00027
00028 #include "kcheckaccelerators.h"
00029
00030 #include <qpopupmenu.h>
00031 #include <qapplication.h>
00032 #include <qdialog.h>
00033 #include <qlayout.h>
00034 #include <qtextview.h>
00035 #include <qobjectlist.h>
00036 #include <qmenubar.h>
00037 #include <qtabbar.h>
00038 #include <qpushbutton.h>
00039 #include <qmetaobject.h>
00040 #include <qcheckbox.h>
00041
00042 #include <kconfig.h>
00043 #include <kglobal.h>
00044 #include <kshortcut.h>
00045 #include <klocale.h>
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078 KCheckAccelerators::KCheckAccelerators( QObject* parent )
00079 : QObject( parent, "kapp_accel_filter" ), block( false )
00080 {
00081 parent->installEventFilter( this );
00082 KConfigGroupSaver saver( KGlobal::config(), "Development" );
00083 QString sKey = KGlobal::config()->readEntry( "CheckAccelerators" ).stripWhiteSpace();
00084 if( !sKey.isEmpty() ) {
00085 KShortcut cuts( sKey );
00086 if( cuts.count() > 0 )
00087 key = cuts.seq(0).qt();
00088 }
00089 alwaysShow = KGlobal::config()->readBoolEntry( "AlwaysShowCheckAccelerators", false );
00090 autoCheck = KGlobal::config()->readBoolEntry( "AutoCheckAccelerators", true );
00091 connect( &autoCheckTimer, SIGNAL( timeout()), SLOT( autoCheckSlot()));
00092 }
00093
00094 bool KCheckAccelerators::eventFilter( QObject * , QEvent * e) {
00095 if ( block )
00096 return false;
00097 if ( e->type() == QEvent::Accel ) {
00098 if ( ( static_cast<QKeyEvent *>(e) )->key() == key ) {
00099 block = true;
00100 checkAccelerators( false );
00101 block = false;
00102 ( static_cast<QKeyEvent *>(e) )->accept();
00103 return true;
00104 }
00105 }
00106 if( autoCheck
00107 && ( e->type() == QEvent::ChildInserted || e->type() == QEvent::ChildRemoved )) {
00108 autoCheckTimer.start( 100, true );
00109 }
00110 return false;
00111 }
00112
00113 void KCheckAccelerators::autoCheckSlot()
00114 {
00115 if( block || QWidget::mouseGrabber() || QWidget::keyboardGrabber() || QApplication::activePopupWidget()) {
00116 autoCheckTimer.start( 100, true );
00117 return;
00118 }
00119 block = true;
00120 checkAccelerators( true );
00121 block = false;
00122 }
00123
00124 void KCheckAccelerators::findAccel( const QString& item, const QString &txt, AccelMap &accels ) {
00125 QChar c;
00126 int search_pos = 0;
00127 for(;;) {
00128 int i = txt.find( "&", search_pos );
00129 if ( i == -1 )
00130 return;
00131 c = txt[ i + 1 ];
00132 if ( !c.isNull() && c != '&')
00133 break;
00134 search_pos = i + 2;
00135 }
00136 c = c.lower();
00137 AccelMap::Iterator it = accels.find( c );
00138 AccelInfo info;
00139 info.item = item;
00140 info.string = txt;
00141 if ( it == accels.end() ) {
00142 AccelInfoList list;
00143 list.append( info );
00144 accels.insert( c, list );
00145 } else {
00146 AccelInfoList &list = it.data();
00147 list.append( info );
00148 }
00149 }
00150
00151 void KCheckAccelerators::checkMenuData( const QString& prefix, QMenuData* m ) {
00152 AccelMap accels;
00153 QMenuItem* mi;
00154 int i;
00155 QString s;
00156 for ( i = 0; i < (int) m->count(); i++ ) {
00157 mi = m->findItem( m->idAt( i ) );
00158 s = mi->text();
00159 if ( s.contains( '\t' ) )
00160 s = s.left( s.find( '\t' ) );
00161 findAccel( prefix.isEmpty() ? s : prefix + "/" + s, s, accels );
00162 }
00163
00164 menuAccels[ prefix ] = accels;
00165
00166 for ( i = 0; i < (int) m->count(); i++ ) {
00167 mi = m->findItem( m->idAt( i ) );
00168 if ( mi->popup() ) {
00169 s = mi->text();
00170 if ( s.contains( '\t' ) )
00171 s = s.left( s.find( '\t' ) );
00172 checkMenuData( prefix.isEmpty() ? s : prefix + "/" + s, mi->popup());
00173 }
00174 }
00175 }
00176
00177 void KCheckAccelerators::checkMenuData( QMenuData* m ) {
00178 checkMenuData( "", m );
00179 }
00180
00181 void KCheckAccelerators::checkAccelerators( bool automatic ) {
00182 QWidget* actWin = qApp->activeWindow();
00183 if ( !actWin )
00184 return;
00185 QMap<QChar, AccelInfoList > accels;
00186 QObjectList *l = actWin->queryList( "QWidget" );
00187 if ( !l )
00188 return;
00189 QMenuBar* mbar = 0;
00190 QObject *p;
00191 for ( QObject *o = l->first(); o; o = l->next() ) {
00192 if ( ( static_cast<QWidget *>(o) )->isVisibleTo( actWin ) ) {
00193 QWidget *w = static_cast<QWidget *>(o);
00194 if ( w->inherits( "QMenuBar" ) ) {
00195 mbar = static_cast<QMenuBar *>(w);
00196 for( unsigned int i = 0;
00197 i < mbar->count();
00198 ++i )
00199 findAccel( w->className(), mbar->text( mbar->idAt( i )), accels );
00200 }
00201 if (w->inherits("QLineEdit") || w->inherits("QComboBox") || w->inherits("QTextEdit") || w->inherits("QTextView") )
00202 continue;
00203
00204
00205 p = w->parent();
00206 while ( p && p->inherits("QWidget") && static_cast<QWidget *>(p)->isVisible() )
00207 p = p->parent();
00208 if ( p )
00209 continue;
00210
00211 QMetaObject *mo = w->metaObject();
00212 const QMetaProperty* text = mo->property( mo->findProperty( "text", TRUE ), TRUE );
00213 const QMetaProperty* title = mo->property( mo->findProperty( "title", TRUE ), TRUE );
00214 if ( text )
00215 findAccel( w->className(), w->property( "text" ).toString(), accels );
00216 if ( title )
00217 findAccel( w->className(), w->property( "title" ).toString(), accels );
00218
00219 if ( w->inherits( "QTabBar" ) ) {
00220 QTabBar *tbar = static_cast<QTabBar *>(w);
00221 for ( int i = 0; i < tbar->count(); i++ )
00222 findAccel( tbar->className(), tbar->tabAt( i )->text(), accels );
00223 }
00224 }
00225 }
00226 delete l;
00227
00228 QString s;
00229
00230 bool was_clash = false;
00231 int num_clashes = 0;
00232 QString used;
00233 for ( QMap<QChar,AccelInfoList>::Iterator it = accels.begin(); it != accels.end(); ++it ) {
00234 AccelInfoList list = it.data();
00235 if( used.isEmpty())
00236 used = it.key();
00237 else {
00238 used += ", ";
00239 used += it.key();
00240 }
00241
00242 if ( list.count() <= 1 )
00243 continue;
00244
00245 if ( ++num_clashes == 1 ) {
00246 s += "<table border>";
00247 s += "<tr><th>" + i18n( "Accel" ) + "</th><th>" + i18n( "String" ) + "</th><th>" + i18n( "Widget" ) + "</th></tr>";
00248 }
00249 AccelInfoList::Iterator ait = list.begin();
00250 s += "<tr><td rowspan=" + QString::number( list.count() ) + "><large><b>" + it.key() + "</b></large></td>";
00251 s += "<td>";
00252 s += (*ait).string;
00253 s += "</td><td>";
00254 s += (*ait).item;
00255 s += "</td></tr>";
00256
00257 for ( ait++; ait != list.end(); ++ait ) {
00258 s += "<tr><td>";
00259 s += (*ait).string;
00260 s += "</td><td>";
00261 s += (*ait).item;
00262 s += "</td></tr>";
00263 }
00264 }
00265 if ( num_clashes ) {
00266 s += "</table>";
00267 s.prepend( "<h3>" + i18n( "One clash detected", "%n clashes detected", num_clashes ) + "</h3>" );
00268 } else {
00269 s += "<h3>" + i18n( "No clashes detected" ) + "</h3>";
00270 }
00271
00272 s += "<h3>" + i18n( "Used accelerators:" ) + "</h3> " + ( used.isEmpty() ? i18n( "None" ) : used );
00273 was_clash |= ( num_clashes > 0 );
00274
00275 if ( mbar ) {
00276 checkMenuData( mbar );
00277 QString s2;
00278 for( QMap<QString,AccelMap>::Iterator mit = menuAccels.begin(); mit != menuAccels.end(); ++mit ) {
00279 if( mit.key().isEmpty())
00280 continue;
00281 num_clashes = 0;
00282 used = "";
00283 QString m;
00284 for ( AccelMap::Iterator it = (*mit).begin(); it != (*mit).end(); ++it ) {
00285 AccelInfoList list = it.data();
00286 if( used.isEmpty())
00287 used = it.key();
00288 else {
00289 used += ", ";
00290 used += it.key();
00291 }
00292 if ( list.count() <= 1 )
00293 continue;
00294
00295 if ( ++num_clashes == 1 ) {
00296 m += "<table border>";
00297 m += "<tr><th>" + i18n( "Accel" ) + "</th><th>" + i18n( "Menu Item" ) + "</th></tr>";
00298 }
00299 AccelInfoList::Iterator ait = list.begin();
00300 m += "<tr><td rowspan=" + QString::number( list.count() ) + "><large><b>" + it.key() + "</b></large></td>";
00301 m += "<td>";
00302 m += (*ait).item;
00303 m += "</td></tr>";
00304
00305 for ( ait++; ait != list.end(); ++ait ) {
00306 m += "<tr><td>";
00307 m += (*ait).item;
00308 m += "</td></tr>";
00309 }
00310 }
00311 if ( num_clashes ) {
00312 m += "</table>";
00313 m.prepend( "<h3>" + i18n( "One clash detected", "%n clashes detected", num_clashes ) + "</h3>" );
00314 }
00315
00316 if( num_clashes || alwaysShow ) {
00317 m.prepend( "<h2>" + i18n( "Submenu" ) + " " + mit.key() + "</h2>" );
00318 m += "<h3>" + i18n( "Used accelerators:" ) + "</h3> " + ( used.isEmpty() ? i18n( "None" ) : used );
00319 }
00320 s2 += m;
00321 was_clash |= ( num_clashes > 0 );
00322 }
00323
00324 if( s2.isEmpty())
00325 s2 = "<h3>" + i18n( "No clashes detected" ) + "</h3>";
00326 s2.prepend( "<h2>" + i18n( "Menu" ) + "</h2>" );
00327 s2 += "<h2>" + i18n( "Other control elements" ) + "</h2>";
00328 s.prepend( s2 );
00329 }
00330
00331 if( automatic && !alwaysShow && !was_clash )
00332 return;
00333
00334 s.prepend( QString("<h2><em>") + actWin->caption() + "<em></h2>" );
00335
00336 QDialog dlg( actWin, "kapp_accel_check_dlg", true );
00337 dlg.setCaption( i18n( "Dr. Klash' Accelerator Diagnosis" ));
00338 dlg.resize( 500, 460 );
00339 QVBoxLayout* layout = new QVBoxLayout( &dlg, 11, 6 );
00340 layout->setAutoAdd( TRUE );
00341 QTextView* view = new QTextView( &dlg );
00342 QCheckBox* disableAutoCheck = NULL;
00343 if( automatic )
00344 disableAutoCheck = new QCheckBox( i18n( "&Disable automatic checking" ), &dlg );
00345 QPushButton* btnClose = new QPushButton( i18n( "&Close" ), &dlg );
00346 btnClose->setDefault( true );
00347 connect( btnClose, SIGNAL( clicked() ), &dlg, SLOT( close() ) );
00348 view->setText( s );
00349 view->setFocus();
00350 dlg.exec();
00351 if( disableAutoCheck != NULL && disableAutoCheck->isChecked())
00352 autoCheck = false;
00353
00354 }
00355
00356 #include "kcheckaccelerators.moc"