kate Library: docwordcompletion.cpp Source File (kate Library)

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

# docwordcompletion.cpp

    00001 /*
    00002     This library is free software; you can redistribute it and/or
    00003     modify it under the terms of the GNU Library General Public
    00004     License version 2 as published by the Free Software Foundation.
    00005
    00006     This library is distributed in the hope that it will be useful,
    00007     but WITHOUT ANY WARRANTY; without even the implied warranty of
    00008     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    00009     Library General Public License for more details.
    00010
    00011     You should have received a copy of the GNU Library General Public License
    00012     along with this library; see the file COPYING.LIB.  If not, write to
    00013     the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    00014     Boston, MA 02111-1307, USA.
    00015
    00016     ---
    00017     file: docwordcompletion.cpp
    00018
    00019     KTextEditor plugin to autocompletion with document words.
    00020     Copyright Anders Lund <anders.lund@lund.tdcadsl.dk>, 2003
    00021
    00022     The following completion methods are supported:
    00023     * Completion with bigger matching words in
    00024       either direction (backward/forward).
    00025     * NOT YET Pop up a list of all bigger matching words in document
    00026
    00027 */
    00028 //BEGIN includes
    00029 #include "docwordcompletion.h"
    00030
    00031 #include <ktexteditor/document.h>
    00032 #include <ktexteditor/viewcursorinterface.h>
    00033 #include <ktexteditor/editinterface.h>
    00034 #include <ktexteditor/variableinterface.h>
    00035
    00036 #include <kapplication.h>
    00037 #include <kconfig.h>
    00038 #include <kdialog.h>
    00039 #include <kgenericfactory.h>
    00040 #include <klocale.h>
    00041 #include <kaction.h>
    00042 #include <knotifyclient.h>
    00043 #include <kparts/part.h>
    00044 #include <kiconloader.h>
    00045
    00046 #include <qregexp.h>
    00047 #include <qstring.h>
    00048 #include <qdict.h>
    00049 #include <qspinbox.h>
    00050 #include <qlabel.h>
    00051 #include <qlayout.h>
    00052 #include <qhbox.h>
    00053 #include <qwhatsthis.h>
    00054 #include <qcheckbox.h>
    00055
    00056 // #include <kdebug.h>
    00057 //END
    00058
    00059 //BEGIN DocWordCompletionPlugin
    00060 K_EXPORT_COMPONENT_FACTORY( ktexteditor_docwordcompletion, KGenericFactory<DocWordCompletionPlugin>( "ktexteditor_docwordcompletion" ) )
    00061 DocWordCompletionPlugin::DocWordCompletionPlugin( QObject *parent,
    00062                             const char* name,
    00063                             const QStringList& /*args*/ )
    00064     : KTextEditor::Plugin ( (KTextEditor::Document*) parent, name )
    00065 {
    00066   readConfig();
    00067 }
    00068
    00069 void DocWordCompletionPlugin::readConfig()
    00070 {
    00071   KConfig *config = kapp->config();
    00072   config->setGroup( "DocWordCompletion Plugin" );
    00073   m_treshold = config->readNumEntry( "treshold", 3 );
    00074   m_autopopup = config->readBoolEntry( "autopopup", true );
    00075 }
    00076
    00077 void DocWordCompletionPlugin::writeConfig()
    00078 {
    00079   KConfig *config = kapp->config();
    00080   config->setGroup("DocWordCompletion Plugin");
    00081   config->writeEntry("autopopup", m_autopopup );
    00082   config->writeEntry("treshold", m_treshold );
    00083 }
    00084
    00085 void DocWordCompletionPlugin::addView(KTextEditor::View *view)
    00086 {
    00087   DocWordCompletionPluginView *nview = new DocWordCompletionPluginView (m_treshold, m_autopopup, view, "Document word completion");
    00088   m_views.append (nview);
    00089 }
    00090
    00091 void DocWordCompletionPlugin::removeView(KTextEditor::View *view)
    00092 {
    00093   for (uint z=0; z < m_views.count(); z++)
    00094     if (m_views.at(z)->parentClient() == view)
    00095     {
    00096        DocWordCompletionPluginView *nview = m_views.at(z);
    00097        m_views.remove (nview);
    00098        delete nview;
    00099     }
    00100 }
    00101
    00102 KTextEditor::ConfigPage* DocWordCompletionPlugin::configPage( uint, QWidget *parent, const char *name )
    00103 {
    00104   return new DocWordCompletionConfigPage( this, parent, name );
    00105 }
    00106
    00107 QString DocWordCompletionPlugin::configPageName( uint ) const
    00108 {
    00109   return i18n("Word Completion Plugin");
    00110 }
    00111
    00112 QString DocWordCompletionPlugin::configPageFullName( uint ) const
    00113 {
    00114   return i18n("Configure the Word Completion Plugin");
    00115 }
    00116
    00117 // FIXME provide sucn a icon
    00118        QPixmap DocWordCompletionPlugin::configPagePixmap( uint, int size ) const
    00119 {
    00120   return UserIcon( "kte_wordcompletion", size );
    00121 }
    00122 //END
    00123
    00124 //BEGIN DocWordCompletionPluginView
    00125 struct DocWordCompletionPluginViewPrivate
    00126 {
    00127   uint line, col;       // start position of last match (where to search from)
    00128   uint cline, ccol;     // cursor position
    00129   uint lilen;           // length of last insertion
    00130   QString last;         // last word we were trying to match
    00131   QString lastIns;      // latest applied completion
    00132   QRegExp re;           // hrm
    00133   KToggleAction *autopopup; // for accessing state
    00134   uint treshold;         // the required length of a word before popping up the completion list automatically
    00135 };
    00136
    00137 DocWordCompletionPluginView::DocWordCompletionPluginView( uint treshold, bool autopopup, KTextEditor::View *view, const char *name )
    00138   : QObject( view, name ),
    00139     KXMLGUIClient( view ),
    00140     m_view( view ),
    00141     d( new DocWordCompletionPluginViewPrivate )
    00142 {
    00143   d->treshold = treshold;
    00144   view->insertChildClient( this );
    00145   setInstance( KGenericFactory<DocWordCompletionPlugin>::instance() );
    00146
    00147   (void) new KAction( i18n("Reuse Word Above"), CTRL+Key_8, this,
    00148     SLOT(completeBackwards()), actionCollection(), "doccomplete_bw" );
    00149   (void) new KAction( i18n("Reuse Word Below"), CTRL+Key_9, this,
    00150     SLOT(completeForwards()), actionCollection(), "doccomplete_fw" );
    00151   (void) new KAction( i18n("Pop Up Completion List"), 0, this,
    00152     SLOT(popupCompletionList()), actionCollection(), "doccomplete_pu" );
    00153   d->autopopup = new KToggleAction( i18n("Automatic Completion Popup"), 0, this,
    00154     SLOT(toggleAutoPopup()), actionCollection(), "enable_autopopup" );
    00155
    00156   d->autopopup->setChecked( autopopup );
    00157   toggleAutoPopup();
    00158
    00159   setXMLFile("docwordcompletionui.rc");
    00160
    00161   KTextEditor::VariableInterface *vi = KTextEditor::variableInterface( view->document() );
    00162   if ( vi )
    00163   {
    00164     QString e = vi->variable("wordcompletion-autopopup");
    00165     if ( ! e.isEmpty() )
    00166       d->autopopup->setEnabled( e == "true" );
    00167
    00168     connect( view->document(), SIGNAL(variableChanged(const QString &, const QString &)),
    00169              this, SLOT(slotVariableChanged(const QString &, const QString &)) );
    00170   }
    00171 }
    00172
    00173 void DocWordCompletionPluginView::settreshold( uint t )
    00174 {
    00175   d->treshold = t;
    00176 }
    00177
    00178 void DocWordCompletionPluginView::completeBackwards()
    00179 {
    00180   complete( false );
    00181 }
    00182
    00183 void DocWordCompletionPluginView::completeForwards()
    00184 {
    00185   complete();
    00186 }
    00187
    00188 // Pop up the editors completion list if applicable
    00189 void DocWordCompletionPluginView::popupCompletionList( QString w )
    00190 {
    00191   if ( w.isEmpty() )
    00192     w = word();
    00193   if ( w.isEmpty() )
    00194     return;
    00195
    00196   KTextEditor::CodeCompletionInterface *cci = codeCompletionInterface( m_view );
    00197   cci->showCompletionBox( allMatches( w ), w.length() );
    00198 }
    00199
    00200 void DocWordCompletionPluginView::toggleAutoPopup()
    00201 {
    00202   if ( d->autopopup->isChecked() ) {
    00203     if ( ! connect( m_view->document(), SIGNAL(charactersInteractivelyInserted(int ,int ,const QString&)),
    00204          this, SLOT(autoPopupCompletionList()) ))
    00205     {
    00206       connect( m_view->document(), SIGNAL(textChanged()), this, SLOT(autoPopupCompletionList()) );
    00207     }
    00208   } else {
    00209     disconnect( m_view->document(), SIGNAL(textChanged()), this, SLOT(autoPopupCompletionList()) );
    00210     disconnect( m_view->document(), SIGNAL(charactersInteractivelyInserted(int ,int ,const QString&)),
    00211                 this, SLOT(autoPopupCompletionList()) );
    00212
    00213   }
    00214 }
    00215
    00216 // for autopopup FIXME - don't pop up if reuse word is inserting
    00217 void DocWordCompletionPluginView::autoPopupCompletionList()
    00218 {
    00219   if ( ! m_view->hasFocus() ) return;
    00220   QString w = word();
    00221   if ( w.length() >= d->treshold )
    00222   {
    00223       popupCompletionList( w );
    00224   }
    00225 }
    00226
    00227 // Do one completion, searching in the desired direction,
    00228 // if possible
    00229 void DocWordCompletionPluginView::complete( bool fw )
    00230 {
    00231   // setup
    00232   KTextEditor::EditInterface *ei = KTextEditor::editInterface( m_view->document() );
    00233   // find the word we are typing
    00234   uint cline, ccol;
    00235   viewCursorInterface( m_view )->cursorPositionReal( &cline, &ccol );
    00236   QString wrd = word();
    00237   if ( wrd.isEmpty() ) return;
    00238
    00239   /* IF the current line is equal to the previous line
    00240      AND the position - the length of the last inserted string
    00241           is equal to the old position
    00242      AND the lastinsertedlength last characters of the word is
    00243           equal to the last inserted string
    00244           */
    00245   if ( cline == d-> cline &&
    00246           ccol - d->lilen == d->ccol &&
    00247           wrd.endsWith( d->lastIns ) )
    00248   {
    00249     // this is a repeted activation
    00250     ccol = d->ccol;
    00251     wrd = d->last;
    00252   }
    00253   else
    00254   {
    00255     d->cline = cline;
    00256     d->ccol = ccol;
    00257     d->last = wrd;
    00258     d->lastIns = QString::null;
    00259     d->line = d->cline;
    00260     d->col = d->ccol - wrd.length();
    00261     d->lilen = 0;
    00262   }
    00263
    00264   d->re.setPattern( "\\b" + wrd + "(\\w+)" );
    00265   int inc = fw ? 1 : -1;
    00266   int pos ( 0 );
    00267   QString ln = ei->textLine( d->line );
    00268
    00269   if ( ! fw )
    00270     ln = ln.mid( 0, d->col );
    00271
    00272   while ( true )
    00273   {
    00274     pos = fw ?
    00275       d->re.search( ln, d->col ) :
    00276       d->re.searchRev( ln, d->col );
    00277
    00278     if ( pos > -1 ) // we matched a word
    00279     {
    00280       QString m = d->re.cap( 1 );
    00281       if ( m != d->lastIns )
    00282       {
    00283         // we got good a match! replace text and return.
    00284         if ( d->lilen )
    00285           ei->removeText( d->cline, d->ccol, d->cline, d->ccol + d->lilen );
    00286         ei->insertText( d->cline, d->ccol, m );
    00287
    00288         d->lastIns = m;
    00289         d->lilen = m.length();
    00290         d->col = pos; // for next try
    00291
    00292         if ( fw )
    00293           d->col += m.length();
    00294
    00295         return;
    00296       }
    00297
    00298       // equal to last one, continue
    00299       else
    00300       {
    00301         d->col = pos; // for next try
    00302         if ( fw )
    00303           d->col += m.length();
    00304         else // FIXME figure out if all of that is really nessecary
    00305         {
    00306           if ( pos == 0 )
    00307           {
    00308             if ( d->line > 0 )
    00309             {
    00310               d->line += inc;
    00311               ln = ei->textLine( d->line );
    00312               d->col = ln.length();
    00313             }
    00314             else
    00315             {
    00316               KNotifyClient::beep();
    00317               return;
    00318             }
    00319           }
    00320           else
    00321             d->col--;
    00322         }
    00323       }
    00324     }
    00325
    00326     else  // no match
    00327     {
    00328       if ( ! fw && d->line == 0)
    00329       {
    00330         KNotifyClient::beep();
    00331         return;
    00332       }
    00333       else if ( fw && d->line >= ei->numLines() )
    00334       {
    00335         KNotifyClient::beep();
    00336         return;
    00337       }
    00338
    00339       d->line += inc;
    00340       if ( fw )
    00341         d->col++;
    00342
    00343       ln = ei->textLine( d->line );
    00344       d->col = fw ? 0 : ln.length();
    00345     }
    00346   } // while true
    00347 }
    00348
    00349 // Return the string to complete (the letters behind the cursor)
    00350 QString DocWordCompletionPluginView::word()
    00351 {
    00352   uint cline, ccol;
    00353   viewCursorInterface( m_view )->cursorPositionReal( &cline, &ccol );
    00354   if ( ! ccol ) return QString::null; // no word
    00355   KTextEditor::EditInterface *ei = KTextEditor::editInterface( m_view->document() );
    00356   d->re.setPattern( "\\b(\\w+)$" );
    00357   if ( d->re.searchRev(
    00358         ei->text( cline, 0, cline, ccol )
    00359         ) < 0 )
    00360     return QString::null; // no word
    00361   return d->re.cap( 1 );
    00362 }
    00363
    00364 // Scan throught the entire document for possible completions,
    00365 // ignoring any dublets
    00366 QValueList<KTextEditor::CompletionEntry> DocWordCompletionPluginView::allMatches( const QString &word )
    00367 {
    00368   QValueList<KTextEditor::CompletionEntry> l;
    00369   uint i( 0 );
    00370   int pos( 0 );
    00371   d->re.setPattern( "\\b("+word+"\\w+)" );
    00372   QString s, m;
    00373   KTextEditor::EditInterface *ei = KTextEditor::editInterface( m_view->document() );
    00374   QDict<int> seen; // maybe slow with > 17 matches
    00375   int sawit(1);    // to ref for the dict
    00376
    00377   while( i < ei->numLines() )
    00378   {
    00379     s = ei->textLine( i );
    00380     pos = 0;
    00381     while ( pos >= 0 )
    00382     {
    00383       pos = d->re.search( s, pos );
    00384       if ( pos >= 0 )
    00385       {
    00386         m = d->re.cap( 1 );
    00387         if ( ! seen[ m ] ) {
    00388           seen.insert( m, &sawit );
    00389           KTextEditor::CompletionEntry e;
    00390           e.text = m;
    00391           l.append( e );
    00392         }
    00393         pos += d->re.matchedLength();
    00394       }
    00395     }
    00396     i++;
    00397   }
    00398   return l;
    00399 }
    00400
    00401 void DocWordCompletionPluginView::slotVariableChanged( const QString &var, const QString &val )
    00402 {
    00403   if ( var == "wordcompletion-autopopup" )
    00404     d->autopopup->setEnabled( val == "true" );
    00405   else if ( var == "wordcompletion-treshold" )
    00406     d->treshold = val.toInt();
    00407 }
    00408 //END
    00409
    00410 //BEGIN DocWordCompletionConfigPage
    00411 DocWordCompletionConfigPage::DocWordCompletionConfigPage( DocWordCompletionPlugin *completion, QWidget *parent, const char *name )
    00412   : KTextEditor::ConfigPage( parent, name )
    00413   , m_completion( completion )
    00414 {
    00415   QVBoxLayout *lo = new QVBoxLayout( this );
    00416   lo->setSpacing( KDialog::spacingHint() );
    00417
    00418   cbAutoPopup = new QCheckBox( i18n("Automatically &show completion list"), this );
    00419   lo->addWidget( cbAutoPopup );
    00420
    00421   QHBox *hb = new QHBox( this );
    00422   hb->setSpacing( KDialog::spacingHint() );
    00423   lo->addWidget( hb );
    00424   QLabel *l = new QLabel( i18n(
    00425       "Translators: This is the first part of two strings wich will comprise the "
    00426       "sentence 'Show completions when a word is at least N characters'. The first "
    00427       "part is on the right side of the N, which is represented by a spinbox "
    00428       "widget, followed by the second part: 'characters long'. Characters is a "
    00429       "ingeger number between and including 1 and 30. Feel free to leave the "
    00430       "second part of the sentence blank if it suits your language better. ",
    00431       "Show completions &when a word is at least"), hb );
    00432   sbAutoPopup = new QSpinBox( 1, 30, 1, hb );
    00433   l->setBuddy( sbAutoPopup );
    00434   lSbRight = new QLabel( i18n(
    00435       "This is the second part of two strings that will comprise teh sentence "
    00436       "'Show completions when a word is at least N characters'",
    00437       "characters long."), hb );
    00438
    00439   QWhatsThis::add( cbAutoPopup, i18n(
    00440       "Enable the automatic completion list popup as default. The popup can "
    00441       "be disabled on a view basis from the 'Tools' menu.") );
    00442   QWhatsThis::add( sbAutoPopup, i18n(
    00443       "Define the length a word should have before the completion list "
    00444       "is displayed.") );
    00445
    00446   cbAutoPopup->setChecked( m_completion->autoPopupEnabled() );
    00447   sbAutoPopup->setValue( m_completion->treshold() );
    00448
    00449   lo->addStretch();
    00450 }
    00451
    00452 void DocWordCompletionConfigPage::apply()
    00453 {
    00454   m_completion->setAutoPopupEnabled( cbAutoPopup->isChecked() );
    00455   m_completion->setTreshold( sbAutoPopup->value() );
    00456   m_completion->writeConfig();
    00457 }
    00458
    00459 void DocWordCompletionConfigPage::reset()
    00460 {
    00461   cbAutoPopup->setChecked( m_completion->autoPopupEnabled() );
    00462   sbAutoPopup->setValue( m_completion->treshold() );
    00463 }
    00464
    00465 void DocWordCompletionConfigPage::defaults()
    00466 {
    00467   cbAutoPopup->setChecked( true );
    00468   sbAutoPopup->setValue( 3 );
    00469 }
    00470
    00471 //END DocWordCompletionConfigPage
    00472
    00473 #include "docwordcompletion.moc"

This file is part of the documentation for kate Library Version 3.4.1.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sat Jun 11 20:36:29 2005 by doxygen 1.4.1 written by Dimitri van Heesch, © 1997-2003