00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include <qlabel.h>
00020 #include <qregexp.h>
00021 #include <qstyle.h>
00022 #include <qpopupmenu.h>
00023 #include <kgenericfactory.h>
00024 #include <klocale.h>
00025 #include <kaction.h>
00026 #include <kcombobox.h>
00027 #include <kconfig.h>
00028 #include <kdebug.h>
00029
00030 #include "ISearchPlugin.h"
00031 #include "ISearchPlugin.moc"
00032
00033 K_EXPORT_COMPONENT_FACTORY( ktexteditor_isearch, KGenericFactory<ISearchPlugin>( "ktexteditor_isearch" ) )
00034
00035 ISearchPluginView::ISearchPluginView( KTextEditor::View *view )
00036 : QObject ( view ), KXMLGUIClient (view)
00037 , m_view( 0L )
00038 , m_doc( 0L )
00039 , m_searchIF( 0L )
00040 , m_cursorIF( 0L )
00041 , m_selectIF( 0L )
00042
00043 , m_searchForwardAction( 0L )
00044 , m_searchBackwardAction( 0L )
00045 , m_label( 0L )
00046 , m_combo( 0L )
00047 , m_lastString( "" )
00048 , m_searchBackward( false )
00049 , m_caseSensitive( false )
00050 , m_fromBeginning( false )
00051 , m_regExp( false )
00052 , m_autoWrap( false )
00053 , m_wrapped( false )
00054 , m_startLine( 0 )
00055 , m_startCol( 0 )
00056 , m_searchLine( 0 )
00057 , m_searchCol( 0 )
00058 , m_foundLine( 0 )
00059 , m_foundCol( 0 )
00060 , m_matchLen( 0 )
00061 , m_toolBarWasHidden( false )
00062 {
00063 view->insertChildClient (this);
00064
00065 setInstance( KGenericFactory<ISearchPlugin>::instance() );
00066
00067 m_searchForwardAction = new KAction(
00068 i18n("Search Incrementally"), CTRL+ALT+Key_F,
00069 this, SLOT(slotSearchForwardAction()),
00070 actionCollection(), "edit_isearch" );
00071 m_searchBackwardAction = new KAction(
00072 i18n("Search Incrementally Backwards"), CTRL+ALT+SHIFT+Key_F,
00073 this, SLOT(slotSearchBackwardAction()),
00074 actionCollection(), "edit_isearch_reverse" );
00075
00076 m_label = new QLabel( i18n("I-Search:"), 0L, "kde toolbar widget" );
00077 KWidgetAction* labelAction = new KWidgetAction(
00078 m_label,
00079 i18n("I-Search:"), 0, 0, 0,
00080 actionCollection(), "isearch_label" );
00081 labelAction->setShortcutConfigurable( false );
00082
00083 m_combo = new KHistoryCombo();
00084 m_combo->setDuplicatesEnabled( false );
00085 m_combo->setMaximumWidth( 300 );
00086 m_combo->lineEdit()->installEventFilter( this );
00087 connect( m_combo, SIGNAL(textChanged(const QString&)),
00088 this, SLOT(slotTextChanged(const QString&)) );
00089 connect( m_combo, SIGNAL(returnPressed(const QString&)),
00090 this, SLOT(slotReturnPressed(const QString&)) );
00091 connect( m_combo, SIGNAL(aboutToShowContextMenu(QPopupMenu*)),
00092 this, SLOT(slotAddContextMenuItems(QPopupMenu*)) );
00093 m_comboAction = new KWidgetAction(
00094 m_combo,
00095 i18n("Search"), 0, 0, 0,
00096 actionCollection(), "isearch_combo" );
00097 m_comboAction->setAutoSized( true );
00098 m_comboAction->setShortcutConfigurable( false );
00099
00100 KActionMenu* optionMenu = new KActionMenu(
00101 i18n("Search Options"), "configure",
00102 actionCollection(), "isearch_options" );
00103 optionMenu->setDelayed( false );
00104
00105 KToggleAction* action = new KToggleAction(
00106 i18n("Case Sensitive"), KShortcut(),
00107 actionCollection(), "isearch_case_sensitive" );
00108 action->setShortcutConfigurable( false );
00109 connect( action, SIGNAL(toggled(bool)),
00110 this, SLOT(setCaseSensitive(bool)) );
00111 action->setChecked( m_caseSensitive );
00112 optionMenu->insert( action );
00113
00114 action = new KToggleAction(
00115 i18n("From Beginning"), KShortcut(),
00116 actionCollection(), "isearch_from_beginning" );
00117 action->setShortcutConfigurable( false );
00118 connect( action, SIGNAL(toggled(bool)),
00119 this, SLOT(setFromBeginning(bool)) );
00120 action->setChecked( m_fromBeginning );
00121 optionMenu->insert( action );
00122
00123 action = new KToggleAction(
00124 i18n("Regular Expression"), KShortcut(),
00125 actionCollection(), "isearch_reg_exp" );
00126 action->setShortcutConfigurable( false );
00127 connect( action, SIGNAL(toggled(bool)),
00128 this, SLOT(setRegExp(bool)) );
00129 action->setChecked( m_regExp );
00130 optionMenu->insert( action );
00131
00132
00133
00134
00135
00136
00137
00138
00139
00140
00141
00142 setXMLFile( "ktexteditor_isearchui.rc" );
00143 }
00144
00145 ISearchPluginView::~ISearchPluginView()
00146 {
00147 writeConfig();
00148 m_combo->lineEdit()->removeEventFilter( this );
00149 delete m_combo;
00150 delete m_label;
00151 }
00152
00153 void ISearchPluginView::setView( KTextEditor::View* view )
00154 {
00155 m_view = view;
00156 m_doc = m_view->document();
00157 m_searchIF = KTextEditor::searchInterface ( m_doc );
00158 m_cursorIF = KTextEditor::viewCursorInterface ( m_view );
00159 m_selectIF = KTextEditor::selectionInterface ( m_doc );
00160 if( !m_doc || !m_cursorIF || !m_selectIF ) {
00161 m_view = 0L;
00162 m_doc = 0L;
00163 m_searchIF = 0L;
00164 m_cursorIF = 0L;
00165 m_selectIF = 0L;
00166 }
00167
00168 readConfig();
00169 }
00170
00171 void ISearchPluginView::readConfig()
00172 {
00173
00174 }
00175
00176 void ISearchPluginView::writeConfig()
00177 {
00178
00179 }
00180
00181 void ISearchPluginView::setCaseSensitive( bool caseSensitive )
00182 {
00183 m_caseSensitive = caseSensitive;
00184 }
00185
00186 void ISearchPluginView::setFromBeginning( bool fromBeginning )
00187 {
00188 m_fromBeginning = fromBeginning;
00189
00190 if( m_fromBeginning ) {
00191 m_searchLine = m_searchCol = 0;
00192 }
00193 }
00194
00195 void ISearchPluginView::setRegExp( bool regExp )
00196 {
00197 m_regExp = regExp;
00198 }
00199
00200 void ISearchPluginView::setAutoWrap( bool autoWrap )
00201 {
00202 m_autoWrap = autoWrap;
00203 }
00204
00205 bool ISearchPluginView::eventFilter( QObject* o, QEvent* e )
00206 {
00207 if( o != m_combo->lineEdit() )
00208 return false;
00209
00210 if( e->type() == QEvent::FocusIn ) {
00211 QFocusEvent* focusEvent = (QFocusEvent*)e;
00212 if( focusEvent->reason() == QFocusEvent::ActiveWindow ||
00213 focusEvent->reason() == QFocusEvent::Popup )
00214 return false;
00215 startSearch();
00216 }
00217
00218 if( e->type() == QEvent::FocusOut ) {
00219 QFocusEvent* focusEvent = (QFocusEvent*)e;
00220 if( focusEvent->reason() == QFocusEvent::ActiveWindow ||
00221 focusEvent->reason() == QFocusEvent::Popup )
00222 return false;
00223 endSearch();
00224 }
00225
00226 if( e->type() == QEvent::KeyPress ) {
00227 QKeyEvent *keyEvent = (QKeyEvent*)e;
00228 if( keyEvent->key() == Qt::Key_Escape )
00229 quitToView( QString::null );
00230 }
00231
00232 return false;
00233 }
00234
00235
00236 void ISearchPluginView::updateLabelText(
00237 bool failing , bool reverse ,
00238 bool wrapped , bool overwrapped )
00239 {
00240 QString text;
00241
00242
00243 if( !failing && !reverse && !wrapped && !overwrapped ) {
00244 text = i18n("Incremental Search", "I-Search:");
00245
00246 } else if ( failing && !reverse && !wrapped && !overwrapped ) {
00247 text = i18n("Incremental Search found no match", "Failing I-Search:");
00248
00249 } else if ( !failing && reverse && !wrapped && !overwrapped ) {
00250 text = i18n("Incremental Search in the reverse direction", "I-Search Backward:");
00251
00252 } else if ( failing && reverse && !wrapped && !overwrapped ) {
00253 text = i18n("Failing I-Search Backward:");
00254
00255 } else if ( !failing && !reverse && wrapped && !overwrapped ) {
00256 text = i18n("Incremental Search has passed the end of the document", "Wrapped I-Search:");
00257
00258 } else if ( failing && !reverse && wrapped && !overwrapped ) {
00259 text = i18n("Failing Wrapped I-Search:");
00260
00261 } else if ( !failing && reverse && wrapped && !overwrapped ) {
00262 text = i18n("Wrapped I-Search Backward:");
00263
00264 } else if ( failing && reverse && wrapped && !overwrapped ) {
00265 text = i18n("Failing Wrapped I-Search Backward:");
00266
00267 } else if ( !failing && !reverse && overwrapped ) {
00268 text = i18n("Incremental Search has passed both the end of the document "
00269 "and the original starting position", "Overwrapped I-Search:");
00270
00271 } else if ( failing && !reverse && overwrapped ) {
00272 text = i18n("Failing Overwrapped I-Search:");
00273
00274 } else if ( !failing && reverse && overwrapped ) {
00275 text = i18n("Overwrapped I-Search Backwards:");
00276
00277 } else if ( failing && reverse && overwrapped ) {
00278 text = i18n("Failing Overwrapped I-Search Backward:");
00279 } else {
00280 text = i18n("Error: unknown i-search state!");
00281 }
00282 m_label->setText( text );
00283 }
00284
00285 void ISearchPluginView::slotSearchForwardAction()
00286 {
00287 slotSearchAction( false );
00288 }
00289
00290 void ISearchPluginView::slotSearchBackwardAction()
00291 {
00292 slotSearchAction( true );
00293 }
00294
00295 void ISearchPluginView::slotSearchAction( bool reverse )
00296 {
00297 if( !m_combo->hasFocus() ) {
00298 if( m_comboAction->container(0) && m_comboAction->container(0)->isHidden() ) {
00299 m_toolBarWasHidden = true;
00300 m_comboAction->container(0)->setHidden( false );
00301 } else {
00302 m_toolBarWasHidden = false;
00303 }
00304 m_combo->setFocus();
00305 } else {
00306 nextMatch( reverse );
00307 }
00308 }
00309
00310 void ISearchPluginView::nextMatch( bool reverse )
00311 {
00312 QString text = m_combo->currentText();
00313 if( text.isEmpty() )
00314 return;
00315 if( state != MatchSearch ) {
00316
00317 if( !reverse ) {
00318 m_searchLine = m_foundLine;
00319 m_searchCol = m_foundCol + m_matchLen;
00320 } else {
00321 m_searchLine = m_foundLine;
00322 m_searchCol = m_foundCol;
00323 }
00324 state = MatchSearch;
00325 }
00326
00327 bool found = iSearch( m_searchLine, m_searchCol, text, reverse, m_autoWrap );
00328 if( found ) {
00329 m_searchLine = m_foundLine;
00330 m_searchCol = m_foundCol + m_matchLen;
00331 } else {
00332 m_wrapped = true;
00333 m_searchLine = m_searchCol = 0;
00334 }
00335 }
00336
00337 void ISearchPluginView::startSearch()
00338 {
00339 if( !m_view ) return;
00340
00341 m_searchForwardAction->setText( i18n("Next Incremental Search Match") );
00342 m_searchBackwardAction->setText( i18n("Previous Incremental Search Match") );
00343
00344 m_wrapped = false;
00345
00346 if( m_fromBeginning ) {
00347 m_startLine = m_startCol = 0;
00348 } else {
00349 m_cursorIF->cursorPositionReal( &m_startLine, &m_startCol );
00350 }
00351 m_searchLine = m_startLine;
00352 m_searchCol = m_startCol;
00353
00354 updateLabelText( false, m_searchBackward );
00355
00356 m_combo->blockSignals( true );
00357
00358 QString text = m_selectIF->selection();
00359 if( text.isEmpty() )
00360 text = m_lastString;
00361 m_combo->setCurrentText( text );
00362
00363 m_combo->blockSignals( false );
00364 m_combo->lineEdit()->selectAll();
00365
00366
00367 }
00368
00369 void ISearchPluginView::endSearch()
00370 {
00371 m_searchForwardAction->setText( i18n("Search Incrementally") );
00372 m_searchBackwardAction->setText( i18n("Search Incrementally Backwards") );
00373
00374 updateLabelText();
00375
00376 if( m_toolBarWasHidden && m_comboAction->containerCount() > 0 ) {
00377 m_comboAction->container(0)->setHidden( true );
00378 }
00379 }
00380
00381 void ISearchPluginView::quitToView( const QString &text )
00382 {
00383 if( !text.isNull() && !text.isEmpty() ) {
00384 m_combo->addToHistory( text );
00385 m_lastString = text;
00386 }
00387
00388 if( m_view ) {
00389 m_view->setFocus();
00390 }
00391 }
00392
00393 void ISearchPluginView::slotTextChanged( const QString& text )
00394 {
00395 state = TextSearch;
00396
00397 if( text.isEmpty() )
00398 return;
00399
00400 iSearch( m_searchLine, m_searchCol, text, m_searchBackward, m_autoWrap );
00401 }
00402
00403 void ISearchPluginView::slotReturnPressed( const QString& text )
00404 {
00405 quitToView( text );
00406 }
00407
00408 void ISearchPluginView::slotAddContextMenuItems( QPopupMenu *menu )
00409 {
00410 if( menu ) {
00411 menu->insertSeparator();
00412 menu->insertItem( i18n("Case Sensitive"), this,
00413 SLOT(setCaseSensitive(bool)));
00414 menu->insertItem( i18n("From Beginning"), this,
00415 SLOT(setFromBeginning(bool)));
00416 menu->insertItem( i18n("Regular Expression"), this,
00417 SLOT(setRegExp(bool)));
00418
00419
00420 }
00421 }
00422
00423 bool ISearchPluginView::iSearch(
00424 uint startLine, uint startCol,
00425 const QString& text, bool reverse,
00426 bool autoWrap )
00427 {
00428 if( !m_view ) return false;
00429
00430
00431 bool found = false;
00432 if( !m_regExp ) {
00433 found = m_searchIF->searchText( startLine,
00434 startCol,
00435 text,
00436 &m_foundLine,
00437 &m_foundCol,
00438 &m_matchLen,
00439 m_caseSensitive,
00440 reverse );
00441 } else {
00442 found = m_searchIF->searchText( startLine,
00443 startCol,
00444 QRegExp( text ),
00445 &m_foundLine,
00446 &m_foundCol,
00447 &m_matchLen,
00448 reverse );
00449 }
00450 if( found ) {
00451
00452
00453 m_cursorIF->setCursorPositionReal( m_foundLine, m_foundCol + m_matchLen );
00454 m_selectIF->setSelection( m_foundLine, m_foundCol, m_foundLine, m_foundCol + m_matchLen );
00455 } else if ( autoWrap ) {
00456 m_wrapped = true;
00457 found = iSearch( 0, 0, text, reverse, false );
00458 }
00459
00460 bool overwrapped = ( m_wrapped &&
00461 ((m_foundLine > m_startLine ) ||
00462 (m_foundLine == m_startLine && m_foundCol >= m_startCol)) );
00463
00464 updateLabelText( !found, reverse, m_wrapped, overwrapped );
00465 return found;
00466 }
00467
00468 ISearchPlugin::ISearchPlugin( QObject *parent, const char* name, const QStringList& )
00469 : KTextEditor::Plugin ( (KTextEditor::Document*) parent, name )
00470 {
00471 }
00472
00473 ISearchPlugin::~ISearchPlugin()
00474 {
00475 }
00476
00477 void ISearchPlugin::addView(KTextEditor::View *view)
00478 {
00479 ISearchPluginView *nview = new ISearchPluginView (view);
00480 nview->setView (view);
00481 m_views.append (nview);
00482 }
00483
00484 void ISearchPlugin::removeView(KTextEditor::View *view)
00485 {
00486 for (uint z=0; z < m_views.count(); z++)
00487 {
00488 if (m_views.at(z)->parentClient() == view)
00489 {
00490 ISearchPluginView *nview = m_views.at(z);
00491 m_views.remove (nview);
00492 delete nview;
00493 }
00494 }
00495 }