00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
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
00057
00058
00059
00060 K_EXPORT_COMPONENT_FACTORY( ktexteditor_docwordcompletion, KGenericFactory<DocWordCompletionPlugin>( "ktexteditor_docwordcompletion" ) )
00061 DocWordCompletionPlugin::DocWordCompletionPlugin( QObject *parent,
00062 const char* name,
00063 const QStringList& )
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
00118 QPixmap DocWordCompletionPlugin::configPagePixmap( uint, int size ) const
00119 {
00120 return UserIcon( "kte_wordcompletion", size );
00121 }
00122
00123
00124
00125 struct DocWordCompletionPluginViewPrivate
00126 {
00127 uint line, col;
00128 uint cline, ccol;
00129 uint lilen;
00130 QString last;
00131 QString lastIns;
00132 QRegExp re;
00133 KToggleAction *autopopup;
00134 uint treshold;
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
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
00209 void DocWordCompletionPluginView::autoPopupCompletionList()
00210 {
00211 QString w = word();
00212 if ( w.length() == d->treshold )
00213 {
00214 popupCompletionList( w );
00215 }
00216 }
00217
00218
00219
00220 void DocWordCompletionPluginView::complete( bool fw )
00221 {
00222
00223 KTextEditor::EditInterface *ei = KTextEditor::editInterface( m_view->document() );
00224
00225 uint cline, ccol;
00226 viewCursorInterface( m_view )->cursorPosition( &cline, &ccol );
00227 QString wrd = word();
00228 if ( wrd.isEmpty() ) return;
00229
00230
00231
00232
00233
00234
00235
00236 if ( cline == d-> cline &&
00237 ccol - d->lilen == d->ccol &&
00238 wrd.endsWith( d->lastIns ) )
00239 {
00240
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 )
00270 {
00271 QString m = d->re.cap( 1 );
00272 if ( m != d->lastIns )
00273 {
00274
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;
00282
00283 if ( fw )
00284 d->col += m.length();
00285
00286 return;
00287 }
00288
00289
00290 else
00291 {
00292 d->col = pos;
00293 if ( fw )
00294 d->col += m.length();
00295 else
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
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 }
00338 }
00339
00340
00341 QString DocWordCompletionPluginView::word()
00342 {
00343 uint cline, ccol;
00344 viewCursorInterface( m_view )->cursorPositionReal( &cline, &ccol );
00345 if ( ! ccol ) return QString::null;
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;
00352 return d->re.cap( 1 );
00353 }
00354
00355
00356
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;
00366 int sawit(1);
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
00400
00401
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
00452
00453 #include "docwordcompletion.moc"