• Skip to content
  • Skip to link menu
KDE 4.6 API Reference
  • KDE API Reference
  • kdelibs
  • KDE Home
  • Contact Us
 

KDEUI

kfind.cpp

Go to the documentation of this file.
00001 /*
00002     Copyright (C) 2001, S.R.Haque <srhaque@iee.org>.
00003     Copyright (C) 2002, David Faure <david@mandrakesoft.com>
00004     Copyright (C) 2004, Arend van Beelen jr. <arend@auton.nl>
00005     This file is part of the KDE project
00006 
00007     This library is free software; you can redistribute it and/or
00008     modify it under the terms of the GNU Library General Public
00009     License version 2, as published by the Free Software Foundation.
00010 
00011     This library is distributed in the hope that it will be useful,
00012     but WITHOUT ANY WARRANTY; without even the implied warranty of
00013     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014     Library General Public License for more details.
00015 
00016     You should have received a copy of the GNU Library General Public License
00017     along with this library; see the file COPYING.LIB.  If not, write to
00018     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00019     Boston, MA 02110-1301, USA.
00020 */
00021 
00022 #include "kfind.h"
00023 #include "kfind_p.h"
00024 #include "kfinddialog.h"
00025 
00026 #include <klocale.h>
00027 #include <kmessagebox.h>
00028 #include <kdebug.h>
00029 
00030 #include <QtGui/QLabel>
00031 #include <QtCore/QRegExp>
00032 #include <QtCore/QHash>
00033 #include <QTextDocument>
00034 
00035 // #define DEBUG_FIND
00036 
00037 static const int INDEX_NOMATCH = -1;
00038 
00039 class KFindNextDialog : public KDialog
00040 {
00041 public:
00042     KFindNextDialog(const QString &pattern, QWidget *parent);
00043 };
00044 
00045 // Create the dialog.
00046 KFindNextDialog::KFindNextDialog(const QString &pattern, QWidget *parent) :
00047     KDialog(parent)
00048 {
00049     setModal( false );
00050     setCaption( i18n("Find Next") );
00051     setButtons( User1 | Close );
00052     setButtonGuiItem( User1, KStandardGuiItem::find() );
00053     setDefaultButton( User1 );
00054 
00055     setMainWidget( new QLabel( i18n("<qt>Find next occurrence of '<b>%1</b>'?</qt>", pattern), this ) );
00056 }
00057 
00059 
00060 
00061 KFind::KFind( const QString &pattern, long options, QWidget *parent )
00062     : QObject( parent ),
00063     d(new KFind::Private(this))
00064 {
00065     d->options = options;
00066     d->init( pattern );
00067 }
00068 
00069 KFind::KFind( const QString &pattern, long options, QWidget *parent, QWidget *findDialog )
00070     : QObject( parent ),
00071     d(new KFind::Private(this))
00072 {
00073     d->findDialog = findDialog;
00074     d->options = options;
00075     d->init( pattern );
00076 }
00077 
00078 void KFind::Private::init( const QString& _pattern )
00079 {
00080     matches = 0;
00081     pattern = _pattern;
00082     dialog = 0;
00083     dialogClosed = false;
00084     index = INDEX_NOMATCH;
00085     lastResult = NoMatch;
00086     regExp = 0;
00087     q->setOptions( options ); // create d->regExp with the right options
00088 }
00089 
00090 KFind::~KFind()
00091 {
00092     delete d;
00093     kDebug() ;
00094 }
00095 
00096 bool KFind::needData() const
00097 {
00098     // always true when d->text is empty.
00099     if (d->options & KFind::FindBackwards)
00100         // d->index==-1 and d->lastResult==Match means we haven't answered nomatch yet
00101         // This is important in the "replace with a prompt" case.
00102         return ( d->index < 0 && d->lastResult != Match );
00103     else
00104         // "index over length" test removed: we want to get a nomatch before we set data again
00105         // This is important in the "replace with a prompt" case.
00106         return d->index == INDEX_NOMATCH;
00107 }
00108 
00109 void KFind::setData( const QString& data, int startPos )
00110 {
00111     setData( -1, data, startPos );
00112 }
00113 
00114 void KFind::setData( int id, const QString& data, int startPos )
00115 {
00116     // cache the data for incremental find
00117     if ( d->options & KFind::FindIncremental )
00118     {
00119         if ( id != -1 )
00120             d->customIds = true;
00121         else
00122             id = d->currentId + 1;
00123 
00124         Q_ASSERT( id <= d->data.size() );
00125 
00126         if ( id == d->data.size() )
00127             d->data.append( Private::Data(id, data, true) );
00128         else
00129             d->data.replace( id, Private::Data(id, data, true) );
00130         Q_ASSERT( d->data.at(id).text == data );
00131     }
00132 
00133     if ( !(d->options & KFind::FindIncremental) || needData() )
00134     {
00135         d->text = data;
00136 
00137         if ( startPos != -1 )
00138             d->index = startPos;
00139         else if (d->options & KFind::FindBackwards)
00140             d->index = d->text.length();
00141         else
00142             d->index = 0;
00143 #ifdef DEBUG_FIND
00144         kDebug() << "setData: '" << d->text << "' d->index=" << d->index;
00145 #endif
00146         Q_ASSERT( d->index != INDEX_NOMATCH );
00147         d->lastResult = NoMatch;
00148 
00149         d->currentId = id;
00150     }
00151 }
00152 
00153 KDialog* KFind::findNextDialog( bool create )
00154 {
00155     if ( !d->dialog && create )
00156     {
00157         d->dialog = new KFindNextDialog( d->pattern, parentWidget() );
00158         connect( d->dialog, SIGNAL( user1Clicked() ), this, SLOT( _k_slotFindNext() ) );
00159         connect( d->dialog, SIGNAL( finished() ), this, SLOT( _k_slotDialogClosed() ) );
00160     }
00161     return d->dialog;
00162 }
00163 
00164 KFind::Result KFind::find()
00165 {
00166     Q_ASSERT( d->index != INDEX_NOMATCH || d->patternChanged );
00167 
00168     if ( d->lastResult == Match && !d->patternChanged )
00169     {
00170         // Move on before looking for the next match, _if_ we just found a match
00171         if (d->options & KFind::FindBackwards) {
00172             d->index--;
00173             if ( d->index == -1 ) // don't call KFind::find with -1, it has a special meaning
00174             {
00175                 d->lastResult = NoMatch;
00176                 return NoMatch;
00177             }
00178         } else
00179             d->index++;
00180     }
00181     d->patternChanged = false;
00182 
00183     if ( d->options & KFind::FindIncremental )
00184     {
00185         // if the current pattern is shorter than the matchedPattern we can
00186         // probably look up the match in the incrementalPath
00187         if ( d->pattern.length() < d->matchedPattern.length() )
00188         {
00189             Private::Match match;
00190             if ( !d->pattern.isEmpty() )
00191                 match = d->incrementalPath.value( d->pattern );
00192             else if ( d->emptyMatch )
00193                 match = *d->emptyMatch;
00194             QString previousPattern (d->matchedPattern);
00195             d->matchedPattern = d->pattern;
00196             if ( !match.isNull() )
00197             {
00198                 bool clean = true;
00199 
00200                 // find the first result backwards on the path that isn't dirty
00201                 while ( d->data.at(match.dataId).dirty == true &&
00202                         !d->pattern.isEmpty() )
00203                 {
00204                     d->pattern.truncate( d->pattern.length() - 1 );
00205 
00206                     match = d->incrementalPath.value( d->pattern );
00207 
00208                     clean = false;
00209                 }
00210 
00211                 // remove all matches that lie after the current match
00212                 while ( d->pattern.length() < previousPattern.length() )
00213                 {
00214                     d->incrementalPath.remove(previousPattern);
00215                     previousPattern.truncate(previousPattern.length() - 1);
00216                 }
00217 
00218                 // set the current text, index, etc. to the found match
00219                 d->text = d->data.at(match.dataId).text;
00220                 d->index = match.index;
00221                 d->matchedLength = match.matchedLength;
00222                 d->currentId = match.dataId;
00223 
00224                 // if the result is clean we can return it now
00225                 if ( clean )
00226                 {
00227                     if ( d->customIds )
00228                         emit highlight(d->currentId, d->index, d->matchedLength);
00229                     else
00230                         emit highlight(d->text, d->index, d->matchedLength);
00231 
00232                     d->lastResult = Match;
00233                     d->matchedPattern = d->pattern;
00234                     return Match;
00235                 }
00236             }
00237             // if we couldn't look up the match, the new pattern isn't a
00238             // substring of the matchedPattern, so we start a new search
00239             else
00240             {
00241                 d->startNewIncrementalSearch();
00242             }
00243         }
00244         // if the new pattern is longer than the matchedPattern we might be
00245         // able to proceed from the last search
00246         else if ( d->pattern.length() > d->matchedPattern.length() )
00247         {
00248             // continue from the previous pattern
00249             if ( d->pattern.startsWith(d->matchedPattern) )
00250             {
00251                 // we can't proceed from the previous position if the previous
00252                 // position already failed
00253                 if ( d->index == INDEX_NOMATCH )
00254                     return NoMatch;
00255 
00256                 QString temp (d->pattern);
00257                 d->pattern.truncate(d->matchedPattern.length() + 1);
00258                 d->matchedPattern = temp;
00259             }
00260             // start a new search
00261             else
00262             {
00263                 d->startNewIncrementalSearch();
00264             }
00265         }
00266         // if the new pattern is as long as the matchedPattern, we reset if
00267         // they are not equal
00268         else if ( d->pattern != d->matchedPattern )
00269         {
00270              d->startNewIncrementalSearch();
00271         }
00272     }
00273 
00274 #ifdef DEBUG_FIND
00275     kDebug() << "d->index=" << d->index;
00276 #endif
00277     do
00278     {
00279         // if we have multiple data blocks in our cache, walk through these
00280         // blocks till we either searched all blocks or we find a match
00281         do
00282         {
00283             // Find the next candidate match.
00284             if ( d->options & KFind::RegularExpression )
00285                 d->index = KFind::find(d->text, *d->regExp, d->index, d->options, &d->matchedLength);
00286             else
00287                 d->index = KFind::find(d->text, d->pattern, d->index, d->options, &d->matchedLength);
00288 
00289             if ( d->options & KFind::FindIncremental )
00290                 d->data[d->currentId].dirty = false;
00291 
00292             if (d->index == -1 && d->currentId < d->data.count() - 1) {
00293                 d->text = d->data.at(++d->currentId).text;
00294 
00295                 if ( d->options & KFind::FindBackwards )
00296                     d->index = d->text.length();
00297                 else
00298                     d->index = 0;
00299             } else {
00300                 break;
00301             }
00302         } while ( !(d->options & KFind::RegularExpression) );
00303 
00304         if ( d->index != -1 )
00305         {
00306             // Flexibility: the app can add more rules to validate a possible match
00307             if ( validateMatch( d->text, d->index, d->matchedLength ) )
00308             {
00309                 bool done = true;
00310 
00311                 if ( d->options & KFind::FindIncremental )
00312                 {
00313                     if ( d->pattern.isEmpty() ) {
00314                         delete d->emptyMatch;
00315                         d->emptyMatch = new Private::Match( d->currentId, d->index, d->matchedLength );
00316                     } else
00317                         d->incrementalPath.insert(d->pattern, Private::Match(d->currentId, d->index, d->matchedLength));
00318 
00319                     if ( d->pattern.length() < d->matchedPattern.length() )
00320                     {
00321                         d->pattern += d->matchedPattern.mid(d->pattern.length(), 1);
00322                         done = false;
00323                     }
00324                 }
00325 
00326                 if ( done )
00327                 {
00328                     d->matches++;
00329                     // Tell the world about the match we found, in case someone wants to
00330                     // highlight it.
00331                     if ( d->customIds )
00332                         emit highlight(d->currentId, d->index, d->matchedLength);
00333                     else
00334                         emit highlight(d->text, d->index, d->matchedLength);
00335 
00336                     if ( !d->dialogClosed )
00337                         findNextDialog(true)->show();
00338 
00339 #ifdef DEBUG_FIND
00340                     kDebug() << "Match. Next d->index=" << d->index;
00341 #endif
00342                     d->lastResult = Match;
00343                     return Match;
00344                 }
00345             }
00346             else // Skip match
00347             {
00348                 if (d->options & KFind::FindBackwards)
00349                     d->index--;
00350                 else
00351                     d->index++;
00352             }
00353         }
00354         else
00355         {
00356             if ( d->options & KFind::FindIncremental )
00357             {
00358                 QString temp (d->pattern);
00359                 temp.truncate(temp.length() - 1);
00360                 d->pattern = d->matchedPattern;
00361                 d->matchedPattern = temp;
00362             }
00363 
00364             d->index = INDEX_NOMATCH;
00365         }
00366     }
00367     while (d->index != INDEX_NOMATCH);
00368 
00369 #ifdef DEBUG_FIND
00370     kDebug() << "NoMatch. d->index=" << d->index;
00371 #endif
00372     d->lastResult = NoMatch;
00373     return NoMatch;
00374 }
00375 
00376 void KFind::Private::startNewIncrementalSearch()
00377 {
00378     Private::Match *match = emptyMatch;
00379     if(match == 0)
00380     {
00381         text.clear();
00382         index = 0;
00383         currentId = 0;
00384     }
00385     else
00386     {
00387         text = data.at(match->dataId).text;
00388         index = match->index;
00389         currentId = match->dataId;
00390     }
00391     matchedLength = 0;
00392     incrementalPath.clear();
00393     delete emptyMatch; emptyMatch = 0;
00394     matchedPattern = pattern;
00395     pattern.clear();
00396 }
00397 
00398 static bool isInWord(QChar ch)
00399 {
00400     return ch.isLetter() || ch.isDigit() || ch == '_';
00401 }
00402 
00403 static bool isWholeWords(const QString &text, int starts, int matchedLength)
00404 {
00405     if (starts == 0 || !isInWord(text.at(starts-1)))
00406     {
00407         const int ends = starts + matchedLength;
00408         if (ends == text.length() || !isInWord(text.at(ends))) {
00409             return true;
00410         }
00411     }
00412     return false;
00413 }
00414 
00415 static bool matchOk(const QString& text, int index, int matchedLength, long options)
00416 {
00417     if (options & KFind::WholeWordsOnly) {
00418         // Is the match delimited correctly?
00419         if (isWholeWords(text, index, matchedLength))
00420             return true;
00421     } else {
00422         // Non-whole-word search: this match is good
00423         return true;
00424     }
00425     return false;
00426 }
00427 
00428 // static
00429 int KFind::find(const QString &text, const QString &pattern, int index, long options, int *matchedLength)
00430 {
00431     // Handle regular expressions in the appropriate way.
00432     if (options & KFind::RegularExpression)
00433     {
00434         Qt::CaseSensitivity caseSensitive = (options & KFind::CaseSensitive) ? Qt::CaseSensitive : Qt::CaseInsensitive;
00435         QRegExp regExp(pattern, caseSensitive);
00436 
00437         return find(text, regExp, index, options, matchedLength);
00438     }
00439 
00440     // In Qt4 QString("aaaaaa").lastIndexOf("a",6) returns -1; we need
00441     // to start at text.length() - pattern.length() to give a valid index to QString.
00442     if (options & KFind::FindBackwards) {
00443         index = qMin( qMax(0, text.length() - pattern.length()), index );
00444     }
00445 
00446     Qt::CaseSensitivity caseSensitive = (options & KFind::CaseSensitive) ? Qt::CaseSensitive : Qt::CaseInsensitive;
00447 
00448     if (options & KFind::FindBackwards) {
00449         // Backward search, until the beginning of the line...
00450         while (index >= 0) {
00451             // ...find the next match.
00452             index = text.lastIndexOf(pattern, index, caseSensitive);
00453             if (index == -1)
00454                 break;
00455 
00456             if (matchOk(text, index, pattern.length(), options))
00457                 break;
00458             index--;
00459             kDebug() << "decrementing:" << index;
00460         }
00461     } else {
00462         // Forward search, until the end of the line...
00463         while (index <= text.length())
00464         {
00465             // ...find the next match.
00466             index = text.indexOf(pattern, index, caseSensitive);
00467             if (index == -1)
00468                 break;
00469 
00470             if (matchOk(text, index, pattern.length(), options))
00471                 break;
00472             index++;
00473         }
00474         if (index > text.length()) { // end of line
00475             kDebug() << "at" << index << "-> not found";
00476             index = -1; // not found
00477         }
00478     }
00479     if (index <= -1)
00480         *matchedLength = 0;
00481     else
00482         *matchedLength = pattern.length();
00483     return index;
00484 }
00485 
00486 // Core method for the regexp-based find
00487 static int doFind(const QString &text, const QRegExp &pattern, int index, long options, int *matchedLength)
00488 {
00489     if (options & KFind::FindBackwards) {
00490         // Backward search, until the beginning of the line...
00491         while (index >= 0) {
00492             // ...find the next match.
00493             index = text.lastIndexOf(pattern, index);
00494             if (index == -1)
00495                 break;
00496 
00497             /*int pos =*/ pattern.indexIn( text.mid(index) );
00498             *matchedLength = pattern.matchedLength();
00499             if (matchOk(text, index, *matchedLength, options))
00500                 break;
00501             index--;
00502         }
00503     } else {
00504         // Forward search, until the end of the line...
00505         while (index <= text.length()) {
00506             // ...find the next match.
00507             index = text.indexOf(pattern, index);
00508             if (index == -1)
00509                 break;
00510 
00511             /*int pos =*/ pattern.indexIn( text.mid(index) );
00512             *matchedLength = pattern.matchedLength();
00513             if (matchOk(text, index, *matchedLength, options))
00514                 break;
00515             index++;
00516         }
00517         if (index > text.length()) { // end of line
00518             index = -1; // not found
00519         }
00520     }
00521     if (index == -1)
00522         *matchedLength = 0;
00523     return index;
00524 }
00525 
00526 // Since QRegExp doesn't support multiline searches (the equivalent of perl's /m)
00527 // we have to cut the text into lines if the pattern starts with ^ or ends with $.
00528 static int lineBasedFind(const QString &text, const QRegExp &pattern, int index, long options, int *matchedLength)
00529 {
00530     const QStringList lines = text.split('\n');
00531     int offset = 0;
00532     // Use "index" to find the first line we should start from
00533     int startLineNumber = 0;
00534     for (; startLineNumber < lines.count(); ++startLineNumber) {
00535         const QString line = lines.at(startLineNumber);
00536         if (index < offset + line.length()) {
00537             break;
00538         }
00539         offset += line.length() + 1 /*newline*/;
00540     }
00541 
00542     if (options & KFind::FindBackwards) {
00543 
00544         if (startLineNumber == lines.count()) {
00545             // We went too far, go back to the last line
00546             --startLineNumber;
00547             offset -= lines.at(startLineNumber).length() + 1;
00548         }
00549 
00550         for (int lineNumber = startLineNumber; lineNumber >= 0; --lineNumber) {
00551             const QString line = lines.at(lineNumber);
00552             const int ret = doFind(line, pattern, lineNumber == startLineNumber ? index - offset : line.length(), options, matchedLength);
00553             if (ret > -1)
00554                 return ret + offset;
00555             offset -= line.length() + 1 /*newline*/;
00556         }
00557 
00558     } else {
00559         for (int lineNumber = startLineNumber; lineNumber < lines.count(); ++lineNumber) {
00560             const QString line = lines.at(lineNumber);
00561             const int ret = doFind(line, pattern, lineNumber == startLineNumber ? (index - offset) : 0, options, matchedLength);
00562             if (ret > -1) {
00563                 return ret + offset;
00564             }
00565             offset += line.length() + 1 /*newline*/;
00566         }
00567     }
00568     return -1;
00569 }
00570 
00571 // static
00572 int KFind::find(const QString &text, const QRegExp &pattern, int index, long options, int *matchedLength)
00573 {
00574     if (pattern.pattern().startsWith('^') || pattern.pattern().endsWith('$')) {
00575         return lineBasedFind(text, pattern, index, options, matchedLength);
00576     }
00577 
00578     return doFind(text, pattern, index, options, matchedLength);
00579 }
00580 
00581 void KFind::Private::_k_slotFindNext()
00582 {
00583     emit q->findNext();
00584 }
00585 
00586 void KFind::Private::_k_slotDialogClosed()
00587 {
00588 #ifdef DEBUG_FIND
00589     kDebug() << " Begin";
00590 #endif
00591     emit q->dialogClosed();
00592     dialogClosed = true;
00593 #ifdef DEBUG_FIND
00594     kDebug() << " End";
00595 #endif
00596 
00597 }
00598 
00599 void KFind::displayFinalDialog() const
00600 {
00601     QString message;
00602     if ( numMatches() )
00603         message = i18np( "1 match found.", "%1 matches found.", numMatches() );
00604     else
00605         message = i18n("<qt>No matches found for '<b>%1</b>'.</qt>", Qt::escape(d->pattern));
00606     KMessageBox::information(dialogsParent(), message);
00607 }
00608 
00609 bool KFind::shouldRestart( bool forceAsking, bool showNumMatches ) const
00610 {
00611     // Only ask if we did a "find from cursor", otherwise it's pointless.
00612     // Well, unless the user can modify the document during a search operation,
00613     // hence the force boolean.
00614     if ( !forceAsking && (d->options & KFind::FromCursor) == 0 )
00615     {
00616         displayFinalDialog();
00617         return false;
00618     }
00619     QString message;
00620     if ( showNumMatches )
00621     {
00622         if ( numMatches() )
00623             message = i18np( "1 match found.", "%1 matches found.", numMatches() );
00624         else
00625             message = i18n("No matches found for '<b>%1</b>'.", Qt::escape(d->pattern));
00626     }
00627     else
00628     {
00629         if ( d->options & KFind::FindBackwards )
00630             message = i18n( "Beginning of document reached." );
00631         else
00632             message = i18n( "End of document reached." );
00633     }
00634 
00635     message += "<br><br>"; // can't be in the i18n() of the first if() because of the plural form.
00636     // Hope this word puzzle is ok, it's a different sentence
00637     message +=
00638         ( d->options & KFind::FindBackwards ) ?
00639         i18n("Continue from the end?")
00640         : i18n("Continue from the beginning?");
00641 
00642     int ret = KMessageBox::questionYesNo( dialogsParent(), "<qt>"+message+"</qt>",
00643                                           QString(), KStandardGuiItem::cont(), KStandardGuiItem::stop() );
00644     bool yes = ( ret == KMessageBox::Yes );
00645     if ( yes )
00646         const_cast<KFind*>(this)->d->options &= ~KFind::FromCursor; // clear FromCursor option
00647     return yes;
00648 }
00649 
00650 long KFind::options() const
00651 {
00652     return d->options;
00653 }
00654 
00655 void KFind::setOptions( long options )
00656 {
00657     d->options = options;
00658 
00659     delete d->regExp;
00660     if (d->options & KFind::RegularExpression) {
00661         Qt::CaseSensitivity caseSensitive = (d->options & KFind::CaseSensitive) ? Qt::CaseSensitive : Qt::CaseInsensitive;
00662         d->regExp = new QRegExp(d->pattern, caseSensitive);
00663     } else
00664         d->regExp = 0;
00665 }
00666 
00667 void KFind::closeFindNextDialog()
00668 {
00669     if (d->dialog) {
00670         d->dialog->deleteLater();
00671         d->dialog = 0;
00672     }
00673     d->dialogClosed = true;
00674 }
00675 
00676 int KFind::index() const
00677 {
00678     return d->index;
00679 }
00680 
00681 QString KFind::pattern() const
00682 {
00683     return d->pattern;
00684 }
00685 
00686 void KFind::setPattern( const QString& pattern )
00687 {
00688     if ( d->options & KFind::FindIncremental && d->pattern != pattern )
00689         d->patternChanged = true;
00690 
00691     d->pattern = pattern;
00692     setOptions( options() ); // rebuild d->regExp if necessary
00693 }
00694 
00695 int KFind::numMatches() const
00696 {
00697     return d->matches;
00698 }
00699 
00700 void KFind::resetCounts()
00701 {
00702     d->matches = 0;
00703 }
00704 
00705 bool KFind::validateMatch( const QString &, int, int )
00706 {
00707     return true;
00708 }
00709 
00710 QWidget* KFind::parentWidget() const
00711 {
00712     return (QWidget *)parent();
00713 }
00714 
00715 QWidget* KFind::dialogsParent() const
00716 {
00717     // If the find dialog is still up, it should get the focus when closing a message box
00718     // Otherwise, maybe the "find next?" dialog is up
00719     // Otherwise, the "view" is the parent.
00720     return d->findDialog ? (QWidget*)d->findDialog : ( d->dialog ? d->dialog : parentWidget() );
00721 }
00722 
00723 #include "kfind.moc"

KDEUI

Skip menu "KDEUI"
  • Main Page
  • Modules
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.7.3
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal