kate Library API Documentation

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 Behind"), CTRL+Key_H, this,
00148     SLOT(completeBackwards()), actionCollection(), "doccomplete_bw" );
00149   (void) new KAction( i18n("Reuse Word Ahead"), CTRL+Key_J, this,
00150     SLOT(completeForwards()), actionCollection(), "doccomplete_fw" );
00151   (void) new KAction( i18n("Pop Up Completion List"), CTRL+Key_Y, 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     connect( m_view->document(), SIGNAL(textChanged()), this, SLOT(autoPopupCompletionList()) );
00204   else
00205     disconnect( m_view->document(), SIGNAL(textChanged()), this, SLOT(autoPopupCompletionList()) );
00206 }
00207 
00208 // for autopopup FIXME - don't pop up if reuse word is inserting
00209 void DocWordCompletionPluginView::autoPopupCompletionList()
00210 {
00211   QString w = word();
00212   if ( w.length() == d->treshold )
00213   {
00214       popupCompletionList( w );
00215   }
00216 }
00217 
00218 // Do one completion, searching in the desired direction,
00219 // if possible
00220 void DocWordCompletionPluginView::complete( bool fw )
00221 {
00222   // setup
00223   KTextEditor::EditInterface *ei = KTextEditor::editInterface( m_view->document() );
00224   // find the word we are typing
00225   uint cline, ccol;
00226   viewCursorInterface( m_view )->cursorPosition( &cline, &ccol );
00227   QString wrd = word();
00228   if ( wrd.isEmpty() ) return;
00229 
00230   /* IF the current line is equal to the previous line
00231      AND the position - the length of the last inserted string
00232           is equal to the old position
00233      AND the lastinsertedlength last characters of the word is
00234           equal to the last inserted string
00235           */
00236   if ( cline == d-> cline &&
00237           ccol - d->lilen == d->ccol &&
00238           wrd.endsWith( d->lastIns ) )
00239   {
00240     // this is a repeted activation
00241     ccol = d->ccol;
00242     wrd = d->last;
00243   }
00244   else
00245   {
00246     d->cline = cline;
00247     d->ccol = ccol;
00248     d->last = wrd;
00249     d->lastIns = QString::null;
00250     d->line = d->cline;
00251     d->col = d->ccol - wrd.length();
00252     d->lilen = 0;
00253   }
00254 
00255   d->re.setPattern( "\\b" + wrd + "(\\w+)" );
00256   int inc = fw ? 1 : -1;
00257   int pos ( 0 );
00258   QString ln = ei->textLine( d->line );
00259 
00260   if ( ! fw )
00261     ln = ln.mid( 0, d->col );
00262 
00263   while ( true )
00264   {
00265     pos = fw ?
00266       d->re.search( ln, d->col ) :
00267       d->re.searchRev( ln, d->col );
00268 
00269     if ( pos > -1 ) // we matched a word
00270     {
00271       QString m = d->re.cap( 1 );
00272       if ( m != d->lastIns )
00273       {
00274         // we got good a match! replace text and return.
00275         if ( d->lilen )
00276           ei->removeText( d->cline, d->ccol, d->cline, d->ccol + d->lilen );
00277         ei->insertText( d->cline, d->ccol, m );
00278 
00279         d->lastIns = m;
00280         d->lilen = m.length();
00281         d->col = pos; // for next try
00282 
00283         if ( fw )
00284           d->col += m.length();
00285 
00286         return;
00287       }
00288 
00289       // equal to last one, continue
00290       else
00291       {
00292         d->col = pos; // for next try
00293         if ( fw )
00294           d->col += m.length();
00295         else // FIXME figure out if all of that is really nessecary
00296         {
00297           if ( pos == 0 )
00298           {
00299             if ( d->line > 0 )
00300             {
00301               d->line += inc;
00302               ln = ei->textLine( d->line );
00303               d->col = ln.length();
00304             }
00305             else
00306             {
00307               KNotifyClient::beep();
00308               return;
00309             }
00310           }
00311           else
00312             d->col--;
00313         }
00314       }
00315     }
00316 
00317     else  // no match
00318     {
00319       if ( ! fw && d->line == 0)
00320       {
00321         KNotifyClient::beep();
00322         return;
00323       }
00324       else if ( fw && d->line >= ei->numLines() )
00325       {
00326         KNotifyClient::beep();
00327         return;
00328       }
00329 
00330       d->line += inc;
00331       if ( fw )
00332         d->col++;
00333 
00334       ln = ei->textLine( d->line );
00335       d->col = fw ? 0 : ln.length();
00336     }
00337   } // while true
00338 }
00339 
00340 // Return the string to complete (the letters behind the cursor)
00341 QString DocWordCompletionPluginView::word()
00342 {
00343   uint cline, ccol;
00344   viewCursorInterface( m_view )->cursorPositionReal( &cline, &ccol );
00345   if ( ! ccol ) return QString::null; // no word
00346   KTextEditor::EditInterface *ei = KTextEditor::editInterface( m_view->document() );
00347   d->re.setPattern( "\\b(\\w+)$" );
00348   if ( d->re.searchRev(
00349         ei->text( cline, 0, cline, ccol )
00350         ) < 0 )
00351     return QString::null; // no word
00352   return d->re.cap( 1 );
00353 }
00354 
00355 // Scan throught the entire document for possible completions,
00356 // ignoring any dublets
00357 QValueList<KTextEditor::CompletionEntry> DocWordCompletionPluginView::allMatches( const QString &word )
00358 {
00359   QValueList<KTextEditor::CompletionEntry> l;
00360   uint i( 0 );
00361   int pos( 0 );
00362   d->re.setPattern( "\\b("+word+"\\w+)" );
00363   QString s, m;
00364   KTextEditor::EditInterface *ei = KTextEditor::editInterface( m_view->document() );
00365   QDict<int> seen; // maybe slow with > 17 matches
00366   int sawit(1);    // to ref for the dict
00367 
00368   while( i < ei->numLines() )
00369   {
00370     s = ei->textLine( i );
00371     pos = 0;
00372     while ( pos >= 0 )
00373     {
00374       pos = d->re.search( s, pos );
00375       if ( pos >= 0 )
00376       {
00377         m = d->re.cap( 1 );
00378         if ( ! seen[ m ] ) {
00379           seen.insert( m, &sawit );
00380           KTextEditor::CompletionEntry e;
00381           e.text = m;
00382           l.append( e );
00383         }
00384         pos += d->re.matchedLength();
00385       }
00386     }
00387     i++;
00388   }
00389   return l;
00390 }
00391 
00392 void DocWordCompletionPluginView::slotVariableChanged( const QString &var, const QString &val )
00393 {
00394   if ( var == "wordcompletion-autopopup" )
00395     d->autopopup->setEnabled( val == "true" );
00396   else if ( var == "wordcompletion-treshold" )
00397     d->treshold = val.toInt();
00398 }
00399 //END
00400 
00401 //BEGIN DocWordCompletionConfigPage
00402 DocWordCompletionConfigPage::DocWordCompletionConfigPage( DocWordCompletionPlugin *completion, QWidget *parent, const char *name )
00403   : KTextEditor::ConfigPage( parent, name )
00404   , m_completion( completion )
00405 {
00406   QVBoxLayout *lo = new QVBoxLayout( this );
00407   lo->setSpacing( KDialog::spacingHint() );
00408 
00409   cbAutoPopup = new QCheckBox( i18n("Automatically show completion list"), this );
00410   lo->addWidget( cbAutoPopup );
00411 
00412   QHBox *hb = new QHBox( this );
00413   hb->setSpacing( KDialog::spacingHint() );
00414   lo->addWidget( hb );
00415   new QLabel( i18n("when the word is"), hb );
00416   sbAutoPopup = new QSpinBox( 1, 30, 1, hb );
00417   new QLabel( i18n("characters long."), hb );
00418 
00419   QWhatsThis::add( cbAutoPopup, i18n(
00420       "Enable the automatic completion list popup as default. The popup can "
00421       "be disabled on a view basis from the 'Tools' menu.") );
00422   QWhatsThis::add( sbAutoPopup, i18n(
00423       "Define the length a word should have before the completion list "
00424       "is displayed.") );
00425 
00426   cbAutoPopup->setChecked( m_completion->autoPopupEnabled() );
00427   sbAutoPopup->setValue( m_completion->treshold() );
00428 
00429   lo->addStretch();
00430 }
00431 
00432 void DocWordCompletionConfigPage::apply()
00433 {
00434   m_completion->setAutoPopupEnabled( cbAutoPopup->isChecked() );
00435   m_completion->setTreshold( sbAutoPopup->value() );
00436   m_completion->writeConfig();
00437 }
00438 
00439 void DocWordCompletionConfigPage::reset()
00440 {
00441   cbAutoPopup->setChecked( m_completion->autoPopupEnabled() );
00442   sbAutoPopup->setValue( m_completion->treshold() );
00443 }
00444 
00445 void DocWordCompletionConfigPage::defaults()
00446 {
00447   cbAutoPopup->setChecked( true );
00448   sbAutoPopup->setValue( 3 );
00449 }
00450 
00451 //END DocWordCompletionConfigPage
00452 
00453 #include "docwordcompletion.moc"
KDE Logo
This file is part of the documentation for kate Library Version 3.3.1.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Fri Feb 18 15:11:55 2005 by doxygen 1.3.9.1 written by Dimitri van Heesch, © 1997-2003