| # kdecore Library API Documentation
Overview | Class Hierarchy | Classes | Classes (annotated) | Members | Source Files

# kcheckaccelerators.cpp

    00001 /* This file is part of the KDE libraries
    00002     Copyright (C) 1997 Matthias Kalle Dalheimer (kalle@kde.org)
    00003     Copyright (C) 1998, 1999, 2000 KDE Team
    00004
    00005     This library is free software; you can redistribute it and/or
    00006     modify it under the terms of the GNU Library General Public
    00007     License as published by the Free Software Foundation; either
    00008     version 2 of the License, or (at your option) any later version.
    00009
    00010     This library is distributed in the hope that it will be useful,
    00011     but WITHOUT ANY WARRANTY; without even the implied warranty of
    00012     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    00013     Library General Public License for more details.
    00014
    00015     You should have received a copy of the GNU Library General Public License
    00016     along with this library; see the file COPYING.LIB.  If not, write to
    00017     the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    00018     Boston, MA 02111-1307, USA.
    00019         */
    00020
    00021 // $Id: kcheckaccelerators.cpp,v 1.5 2002/10/19 14:55:33 coolo Exp $
    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  HOWTO:
    00050
    00051  This class allows translators (and application developers) to check for accelerator
    00052  conflicts in menu and widgets. Put the following in your kdeglobals (or the config
    00053  file for the application you're testing):
    00054
    00055  [Development]
    00056  CheckAccelerators=F12
    00057  AutoCheckAccelerators=false
    00058  AlwaysShowCheckAccelerators=false
    00059
    00060  The checking can be either manual or automatic. To perform manual check, press
    00061  the keyboard shortcut set to 'CheckAccelerators' (here F12). If automatic checking
    00062  is enabled by setting 'AutoCheckAccelerators' to true, check will be performed every
    00063  time the GUI changes. It's possible that in certain cases the check will be
    00064  done also when no visible changes in the GUI happen or the check won't be done
    00065  even if the GUI changed (in the latter case, use manual check ). Automatic
    00066  checks can be anytime disabled by the checkbox in the dialog presenting
    00067  the results of the check. If you set 'AlwaysShowCheckAccelerators' to true,
    00068  the dialog will be shown even if the automatic check didn't find any conflicts,
    00069  and all submenus will be shown, even those without conflicts.
    00070
    00071  The dialog first lists the name of the window, then all results for all menus
    00072  (if the window has a menubar) and then result for all controls in the active
    00073  window (if there are any checkboxes etc.). For every submenu and all controls
    00074  there are shown all conflicts grouped by accelerator, and a list of all used
    00075  accelerators.
    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 ); // 100 ms
    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; // search also after '&&'
    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             // Skip widgets that are children of non-visible objects
    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()) // don't list the menubar itself, it's already handled
    00280                 continue;           //  together with other controls, do only submenus here
    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     // dlg will be destroyed before returning
    00354 }
    00355
    00356 #include "kcheckaccelerators.moc"

This file is part of the documentation for kdelibs Version 3.1.5.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Wed Jan 28 12:46:20 2004 by doxygen 1.3.4 written by Dimitri van Heesch, © 1997-2001