• Skip to content
  • Skip to link menu
KDE 3.5 API Reference
  • KDE API Reference
  • API Reference
  • Sitemap
  • Contact Us
 

Kate

docwordcompletion.cpp

Go to the documentation of this file.
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., 51 Franklin Street, Fifth Floor,
00014     Boston, MA 02110-1301, 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   int directionalPos;   // be able to insert "" at the correct time
00136 };
00137 
00138 DocWordCompletionPluginView::DocWordCompletionPluginView( uint treshold, bool autopopup, KTextEditor::View *view, const char *name )
00139   : QObject( view, name ),
00140     KXMLGUIClient( view ),
00141     m_view( view ),
00142     d( new DocWordCompletionPluginViewPrivate )
00143 {
00144   d->treshold = treshold;
00145   view->insertChildClient( this );
00146   setInstance( KGenericFactory<DocWordCompletionPlugin>::instance() );
00147 
00148   (void) new KAction( i18n("Reuse Word Above"), CTRL+Key_8, this,
00149     SLOT(completeBackwards()), actionCollection(), "doccomplete_bw" );
00150   (void) new KAction( i18n("Reuse Word Below"), CTRL+Key_9, this,
00151     SLOT(completeForwards()), actionCollection(), "doccomplete_fw" );
00152   (void) new KAction( i18n("Pop Up Completion List"), 0, this,
00153     SLOT(popupCompletionList()), actionCollection(), "doccomplete_pu" );
00154   (void) new KAction( i18n("Shell Completion"), 0, this,
00155     SLOT(shellComplete()), actionCollection(), "doccomplete_sh" );
00156   d->autopopup = new KToggleAction( i18n("Automatic Completion Popup"), 0, this,
00157     SLOT(toggleAutoPopup()), actionCollection(), "enable_autopopup" );
00158 
00159   d->autopopup->setChecked( autopopup );
00160   toggleAutoPopup();
00161 
00162   setXMLFile("docwordcompletionui.rc");
00163 
00164   KTextEditor::VariableInterface *vi = KTextEditor::variableInterface( view->document() );
00165   if ( vi )
00166   {
00167     QString e = vi->variable("wordcompletion-autopopup");
00168     if ( ! e.isEmpty() )
00169       d->autopopup->setEnabled( e == "true" );
00170 
00171     connect( view->document(), SIGNAL(variableChanged(const QString &, const QString &)),
00172              this, SLOT(slotVariableChanged(const QString &, const QString &)) );
00173   }
00174 }
00175 
00176 void DocWordCompletionPluginView::settreshold( uint t )
00177 {
00178   d->treshold = t;
00179 }
00180 
00181 void DocWordCompletionPluginView::completeBackwards()
00182 {
00183   complete( false );
00184 }
00185 
00186 void DocWordCompletionPluginView::completeForwards()
00187 {
00188   complete();
00189 }
00190 
00191 // Pop up the editors completion list if applicable
00192 void DocWordCompletionPluginView::popupCompletionList( QString w )
00193 {
00194   if ( w.isEmpty() )
00195     w = word();
00196   if ( w.isEmpty() )
00197     return;
00198 
00199   KTextEditor::CodeCompletionInterface *cci = codeCompletionInterface( m_view );
00200   cci->showCompletionBox( allMatches( w ), w.length() );
00201 }
00202 
00203 void DocWordCompletionPluginView::toggleAutoPopup()
00204 {
00205   if ( d->autopopup->isChecked() ) {
00206     if ( ! connect( m_view->document(), SIGNAL(charactersInteractivelyInserted(int ,int ,const QString&)),
00207          this, SLOT(autoPopupCompletionList()) ))
00208     {
00209       connect( m_view->document(), SIGNAL(textChanged()), this, SLOT(autoPopupCompletionList()) );
00210     }
00211   } else {
00212     disconnect( m_view->document(), SIGNAL(textChanged()), this, SLOT(autoPopupCompletionList()) );
00213     disconnect( m_view->document(), SIGNAL(charactersInteractivelyInserted(int ,int ,const QString&)),
00214                 this, SLOT(autoPopupCompletionList()) );
00215 
00216   }
00217 }
00218 
00219 // for autopopup FIXME - don't pop up if reuse word is inserting
00220 void DocWordCompletionPluginView::autoPopupCompletionList()
00221 {
00222   if ( ! m_view->hasFocus() ) return;
00223   QString w = word();
00224   if ( w.length() >= d->treshold )
00225   {
00226       popupCompletionList( w );
00227   }
00228 }
00229 
00230 // Contributed by <brain@hdsnet.hu>
00231 void DocWordCompletionPluginView::shellComplete()
00232 {
00233     // setup
00234   KTextEditor::EditInterface * ei = KTextEditor::editInterface(m_view->document());
00235     // find the word we are typing
00236   uint cline, ccol;
00237   viewCursorInterface(m_view)->cursorPositionReal(&cline, &ccol);
00238   QString wrd = word();
00239   if (wrd.isEmpty())
00240     return;
00241 
00242   QValueList < KTextEditor::CompletionEntry > matches = allMatches(wrd);
00243   if (matches.size() == 0)
00244     return;
00245   QString partial = findLongestUnique(matches);
00246   if (partial.length() == wrd.length())
00247   {
00248     KTextEditor::CodeCompletionInterface * cci = codeCompletionInterface(m_view);
00249     cci->showCompletionBox(matches, wrd.length());
00250   }
00251   else
00252   {
00253     partial.remove(0, wrd.length());
00254     ei->insertText(cline, ccol, partial);
00255   }
00256 }
00257 
00258 // Do one completion, searching in the desired direction,
00259 // if possible
00260 void DocWordCompletionPluginView::complete( bool fw )
00261 {
00262   // setup
00263   KTextEditor::EditInterface *ei = KTextEditor::editInterface( m_view->document() );
00264   // find the word we are typing
00265   uint cline, ccol;
00266   viewCursorInterface( m_view )->cursorPositionReal( &cline, &ccol );
00267   QString wrd = word();
00268   if ( wrd.isEmpty() )
00269     return;
00270 
00271   int inc = fw ? 1 : -1;
00272 
00273   /* IF the current line is equal to the previous line
00274      AND the position - the length of the last inserted string
00275           is equal to the old position
00276      AND the lastinsertedlength last characters of the word is
00277           equal to the last inserted string
00278           */
00279   if ( cline == d-> cline &&
00280           ccol - d->lilen == d->ccol &&
00281           wrd.endsWith( d->lastIns ) )
00282   {
00283     // this is a repeted activation
00284 
00285     // if we are back to where we started, reset.
00286     if ( ( fw && d->directionalPos == -1 ) ||
00287          ( !fw && d->directionalPos == 1 ) )
00288     {
00289       if ( d->lilen )
00290         ei->removeText( d->cline, d->ccol, d->cline, d->ccol + d->lilen );
00291 
00292       d->lastIns = "";
00293       d->lilen = 0;
00294       d->line = d->cline;
00295       d->col = d->ccol;
00296       d->directionalPos = 0;
00297 
00298       return;
00299     }
00300 
00301     if ( fw )
00302       d->col += d->lilen;
00303 
00304     ccol = d->ccol;
00305     wrd = d->last;
00306 
00307     d->directionalPos += inc;
00308   }
00309   else
00310   {
00311     d->cline = cline;
00312     d->ccol = ccol;
00313     d->last = wrd;
00314     d->lastIns = "";
00315     d->line = cline;
00316     d->col = ccol - wrd.length();
00317     d->lilen = 0;
00318     d->directionalPos = inc;
00319   }
00320 
00321   d->re.setPattern( "\\b" + wrd + "(\\w+)" );
00322   int pos ( 0 );
00323   QString ln = ei->textLine( d->line );
00324 
00325   while ( true )
00326   {
00327     pos = fw ?
00328       d->re.search( ln, d->col ) :
00329       d->re.searchRev( ln, d->col );
00330 
00331     if ( pos > -1 ) // we matched a word
00332     {
00333       QString m = d->re.cap( 1 );
00334       if ( m != d->lastIns )
00335       {
00336         // we got good a match! replace text and return.
00337         if ( d->lilen )
00338           ei->removeText( d->cline, d->ccol, d->cline, d->ccol + d->lilen );
00339         ei->insertText( d->cline, d->ccol, m );
00340 
00341         d->lastIns = m;
00342         d->lilen = m.length();
00343         d->col = pos; // for next try
00344 
00345         return;
00346       }
00347 
00348       // equal to last one, continue
00349       else
00350       {
00351         d->col = pos; // for next try
00352 
00353         if ( fw )
00354           d->col += d->re.matchedLength();
00355 
00356         else
00357         {
00358           if ( pos == 0 )
00359           {
00360             if ( d->line > 0 )
00361             {
00362               d->line += inc;
00363               ln = ei->textLine( d->line );
00364               d->col = ln.length();
00365             }
00366             else
00367             {
00368               KNotifyClient::beep();
00369               return;
00370             }
00371           }
00372 
00373           else
00374             d->col--;
00375         }
00376       }
00377     }
00378 
00379     else  // no match
00380     {
00381       if ( (! fw && d->line == 0 ) || ( fw && d->line >= (uint)ei->numLines() ) )
00382       {
00383         KNotifyClient::beep();
00384         return;
00385       }
00386 
00387       d->line += inc;
00388 
00389       ln = ei->textLine( d->line );
00390       d->col = fw ? 0 : ln.length();
00391     }
00392   } // while true
00393 }
00394 
00395 // Contributed by <brain@hdsnet.hu>
00396 QString DocWordCompletionPluginView::findLongestUnique(const QValueList < KTextEditor::CompletionEntry > &matches)
00397 {
00398   QString partial = matches.front().text;
00399   QValueList < KTextEditor::CompletionEntry >::const_iterator i = matches.begin();
00400   for (++i; i != matches.end(); ++i)
00401   {
00402     if (!(*i).text.startsWith(partial))
00403     {
00404       while(partial.length() > 0)
00405       {
00406         partial.remove(partial.length() - 1, 1);
00407         if ((*i).text.startsWith(partial))
00408         {
00409           break;
00410         }
00411       }
00412       if (partial.length() == 0)
00413         return QString();
00414     }
00415   }
00416 
00417   return partial;
00418 }
00419 
00420 // Return the string to complete (the letters behind the cursor)
00421 QString DocWordCompletionPluginView::word()
00422 {
00423   uint cline, ccol;
00424   viewCursorInterface( m_view )->cursorPositionReal( &cline, &ccol );
00425   if ( ! ccol ) return QString::null; // no word
00426   KTextEditor::EditInterface *ei = KTextEditor::editInterface( m_view->document() );
00427   d->re.setPattern( "\\b(\\w+)$" );
00428   if ( d->re.searchRev(
00429         ei->text( cline, 0, cline, ccol )
00430         ) < 0 )
00431     return QString::null; // no word
00432   return d->re.cap( 1 );
00433 }
00434 
00435 // Scan throught the entire document for possible completions,
00436 // ignoring any dublets
00437 QValueList<KTextEditor::CompletionEntry> DocWordCompletionPluginView::allMatches( const QString &word )
00438 {
00439   QValueList<KTextEditor::CompletionEntry> l;
00440   uint i( 0 );
00441   int pos( 0 );
00442   d->re.setPattern( "\\b("+word+"\\w+)" );
00443   QString s, m;
00444   KTextEditor::EditInterface *ei = KTextEditor::editInterface( m_view->document() );
00445   QDict<int> seen; // maybe slow with > 17 matches
00446   int sawit(1);    // to ref for the dict
00447   uint cline, ccol;// needed to avoid constructing a word at cursor position
00448   viewCursorInterface( m_view )->cursorPositionReal( &cline, &ccol );
00449 
00450   while( i < ei->numLines() )
00451   {
00452     s = ei->textLine( i );
00453     pos = 0;
00454     while ( pos >= 0 )
00455     {
00456       pos = d->re.search( s, pos );
00457       if ( pos >= 0 )
00458       {
00459         // do not construct a new word!
00460         if ( i == cline && pos + word.length() == ccol )
00461         {
00462           pos += word.length();
00463           continue;
00464         }
00465 
00466         m = d->re.cap( 1 );
00467         if ( ! seen[ m ] ) {
00468           seen.insert( m, &sawit );
00469           KTextEditor::CompletionEntry e;
00470           e.text = m;
00471           l.append( e );
00472         }
00473         pos += d->re.matchedLength();
00474       }
00475     }
00476     i++;
00477   }
00478   return l;
00479 }
00480 
00481 void DocWordCompletionPluginView::slotVariableChanged( const QString &var, const QString &val )
00482 {
00483   if ( var == "wordcompletion-autopopup" )
00484     d->autopopup->setEnabled( val == "true" );
00485   else if ( var == "wordcompletion-treshold" )
00486     d->treshold = val.toInt();
00487 }
00488 //END
00489 
00490 //BEGIN DocWordCompletionConfigPage
00491 DocWordCompletionConfigPage::DocWordCompletionConfigPage( DocWordCompletionPlugin *completion, QWidget *parent, const char *name )
00492   : KTextEditor::ConfigPage( parent, name )
00493   , m_completion( completion )
00494 {
00495   QVBoxLayout *lo = new QVBoxLayout( this );
00496   lo->setSpacing( KDialog::spacingHint() );
00497 
00498   cbAutoPopup = new QCheckBox( i18n("Automatically &show completion list"), this );
00499   lo->addWidget( cbAutoPopup );
00500 
00501   QHBox *hb = new QHBox( this );
00502   hb->setSpacing( KDialog::spacingHint() );
00503   lo->addWidget( hb );
00504   QLabel *l = new QLabel( i18n(
00505       "Translators: This is the first part of two strings wich will comprise the "
00506       "sentence 'Show completions when a word is at least N characters'. The first "
00507       "part is on the right side of the N, which is represented by a spinbox "
00508       "widget, followed by the second part: 'characters long'. Characters is a "
00509       "ingeger number between and including 1 and 30. Feel free to leave the "
00510       "second part of the sentence blank if it suits your language better. ",
00511       "Show completions &when a word is at least"), hb );
00512   sbAutoPopup = new QSpinBox( 1, 30, 1, hb );
00513   l->setBuddy( sbAutoPopup );
00514   lSbRight = new QLabel( i18n(
00515       "This is the second part of two strings that will comprise teh sentence "
00516       "'Show completions when a word is at least N characters'",
00517       "characters long."), hb );
00518 
00519   QWhatsThis::add( cbAutoPopup, i18n(
00520       "Enable the automatic completion list popup as default. The popup can "
00521       "be disabled on a view basis from the 'Tools' menu.") );
00522   QWhatsThis::add( sbAutoPopup, i18n(
00523       "Define the length a word should have before the completion list "
00524       "is displayed.") );
00525 
00526   cbAutoPopup->setChecked( m_completion->autoPopupEnabled() );
00527   sbAutoPopup->setValue( m_completion->treshold() );
00528 
00529   lo->addStretch();
00530 }
00531 
00532 void DocWordCompletionConfigPage::apply()
00533 {
00534   m_completion->setAutoPopupEnabled( cbAutoPopup->isChecked() );
00535   m_completion->setTreshold( sbAutoPopup->value() );
00536   m_completion->writeConfig();
00537 }
00538 
00539 void DocWordCompletionConfigPage::reset()
00540 {
00541   cbAutoPopup->setChecked( m_completion->autoPopupEnabled() );
00542   sbAutoPopup->setValue( m_completion->treshold() );
00543 }
00544 
00545 void DocWordCompletionConfigPage::defaults()
00546 {
00547   cbAutoPopup->setChecked( true );
00548   sbAutoPopup->setValue( 3 );
00549 }
00550 
00551 //END DocWordCompletionConfigPage
00552 
00553 #include "docwordcompletion.moc"
00554 // kate: space-indent on; indent-width 2; replace-tabs on; mixed-indent off;

Kate

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

API Reference

Skip menu "API Reference"
  • dcop
  • DNSSD
  • interfaces
  • Kate
  • kconf_update
  • KDECore
  • KDED
  • kdefx
  • KDEsu
  • kdeui
  • KDocTools
  • KHTML
  • KImgIO
  • KInit
  • kio
  • kioslave
  • KJS
  • KNewStuff
  • KParts
  • KUtils
Generated for API Reference by doxygen 1.5.9
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