KDevelop API Documentation

editors/qeditor/koFind.cpp

Go to the documentation of this file.
00001 /* 00002 Copyright (C) 2001, S.R.Haque <srhaque@iee.org>. 00003 This file is part of the KDE project 00004 00005 This library is free software; you can redistribute it and/or 00006 modify it under the terms of the GNU Library General Public 00007 License as published by the Free Software Foundation; either 00008 version 2 of the License, or (at your option) any later version. 00009 00010 This library is distributed in the hope that it will be useful, 00011 but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00013 Library General Public License for more details. 00014 00015 You should have received a copy of the GNU Library General Public License 00016 along with this library; see the file COPYING.LIB. If not, write to 00017 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 00018 Boston, MA 02111-1307, USA. 00019 */ 00020 00021 #include <qcheckbox.h> 00022 #include <qcursor.h> 00023 #include <qgroupbox.h> 00024 #include <qlabel.h> 00025 #include <qlayout.h> 00026 #include <qpopupmenu.h> 00027 #include <qpushbutton.h> 00028 #include <qregexp.h> 00029 #include <kapplication.h> 00030 #include <kdebug.h> 00031 #include <kcombobox.h> 00032 #include <klocale.h> 00033 #include "koFind.h" 00034 #include <kmessagebox.h> 00035 00036 KoFindDialog::KoFindDialog(QWidget *parent, const char *name, long options, const QStringList &findStrings, bool hasSelection) : 00037 KDialogBase(parent, name, true, i18n("Find Text"), Ok | Cancel, Ok) 00038 { 00039 init(false, findStrings, hasSelection); 00040 setOptions(options); 00041 } 00042 00043 KoFindDialog::KoFindDialog(QWidget *parent, const char *name, bool /*forReplace*/) : 00044 KDialogBase(parent, name, true, i18n("Replace Text"), Ok | Cancel, Ok) 00045 { 00046 } 00047 00048 KoFindDialog::~KoFindDialog() 00049 { 00050 } 00051 00052 QWidget *KoFindDialog::findExtension() 00053 { 00054 return m_findExtension; 00055 } 00056 00057 QStringList KoFindDialog::findHistory() const 00058 { 00059 return m_find->historyItems(); 00060 } 00061 00062 void KoFindDialog::init(bool forReplace, const QStringList &findStrings, bool hasSelection) 00063 { 00064 QVBoxLayout *topLayout; 00065 QGridLayout *optionsLayout; 00066 00067 // Create common parts of dialog. 00068 QWidget *page = new QWidget(this); 00069 setMainWidget(page); 00070 00071 topLayout = new QVBoxLayout(page); 00072 topLayout->setSpacing( KDialog::spacingHint() ); 00073 topLayout->setMargin( KDialog::marginHint() ); 00074 00075 m_findGrp = new QGroupBox(0, Qt::Vertical, i18n("Find"), page); 00076 m_findGrp->layout()->setSpacing(KDialog::spacingHint()); 00077 m_findGrp->layout()->setMargin(KDialog::marginHint()); 00078 m_findLayout = new QGridLayout(m_findGrp->layout()); 00079 m_findLayout->setSpacing( KDialog::spacingHint() ); 00080 m_findLayout->setMargin( KDialog::marginHint() ); 00081 00082 m_findLabel = new QLabel(i18n("&Text to find:"), m_findGrp); 00083 m_find = new KHistoryCombo(true, m_findGrp); 00084 m_find->setMaxCount(10); 00085 m_find->setDuplicatesEnabled(false); 00086 m_regExp = new QCheckBox(i18n("Use patterns"), m_findGrp); 00087 m_regExpItem = new QPushButton(i18n("Insert Pattern"), m_findGrp); 00088 m_regExpItem->setEnabled(false); 00089 m_findExtension = new QWidget(m_findGrp); 00090 00091 m_findLayout->addWidget(m_findLabel, 0, 0); 00092 m_findLayout->addMultiCellWidget(m_find, 1, 1, 0, 1); 00093 m_findLayout->addWidget(m_regExp, 2, 0); 00094 m_findLayout->addWidget(m_regExpItem, 2, 1); 00095 m_findLayout->addMultiCellWidget(m_findExtension, 3, 3, 0, 1); 00096 topLayout->addWidget(m_findGrp); 00097 00098 m_replaceGrp = new QGroupBox(0, Qt::Vertical, i18n("Replace With"), page); 00099 m_replaceGrp->layout()->setSpacing(KDialog::spacingHint()); 00100 m_replaceGrp->layout()->setMargin(KDialog::marginHint()); 00101 m_replaceLayout = new QGridLayout(m_replaceGrp->layout()); 00102 m_replaceLayout->setSpacing( KDialog::spacingHint() ); 00103 m_replaceLayout->setMargin( KDialog::marginHint() ); 00104 00105 m_replaceLabel = new QLabel(i18n("&Replacement text:"), m_replaceGrp); 00106 m_replace = new KHistoryCombo(true, m_replaceGrp); 00107 m_replace->setMaxCount(10); 00108 m_replace->setDuplicatesEnabled(false); 00109 m_backRef = new QCheckBox(i18n("&Use placeholders"), m_replaceGrp); 00110 m_backRefItem = new QPushButton(i18n("Insert Placeholder"), m_replaceGrp); 00111 m_backRefItem->setEnabled(false); 00112 m_replaceExtension = new QWidget(m_replaceGrp); 00113 00114 m_replaceLayout->addWidget(m_replaceLabel, 0, 0); 00115 m_replaceLayout->addMultiCellWidget(m_replace, 1, 1, 0, 1); 00116 m_replaceLayout->addWidget(m_backRef, 2, 0); 00117 m_replaceLayout->addWidget(m_backRefItem, 2, 1); 00118 m_replaceLayout->addMultiCellWidget(m_replaceExtension, 3, 3, 0, 1); 00119 topLayout->addWidget(m_replaceGrp); 00120 00121 m_optionGrp = new QGroupBox(0, Qt::Vertical, i18n("Options"), page); 00122 m_optionGrp->layout()->setSpacing(KDialog::spacingHint()); 00123 m_optionGrp->layout()->setMargin(KDialog::marginHint()); 00124 optionsLayout = new QGridLayout(m_optionGrp->layout()); 00125 optionsLayout->setSpacing( KDialog::spacingHint() ); 00126 optionsLayout->setMargin( KDialog::marginHint() ); 00127 00128 m_caseSensitive = new QCheckBox(i18n("C&ase sensitive"), m_optionGrp); 00129 m_wholeWordsOnly = new QCheckBox(i18n("&Whole words only"), m_optionGrp); 00130 m_fromCursor = new QCheckBox(i18n("&From cursor"), m_optionGrp); 00131 m_findBackwards = new QCheckBox(i18n("Find &backwards"), m_optionGrp); 00132 m_selectedText = new QCheckBox(i18n("&Selected text"), m_optionGrp); 00133 setHasSelection( hasSelection ); 00134 m_promptOnReplace = new QCheckBox(i18n("&Prompt on replace"), m_optionGrp); 00135 m_promptOnReplace->setChecked( true ); 00136 00137 optionsLayout->addWidget(m_caseSensitive, 0, 0); 00138 optionsLayout->addWidget(m_wholeWordsOnly, 1, 0); 00139 optionsLayout->addWidget(m_fromCursor, 2, 0); 00140 optionsLayout->addWidget(m_findBackwards, 0, 1); 00141 optionsLayout->addWidget(m_selectedText, 1, 1); 00142 optionsLayout->addWidget(m_promptOnReplace, 2, 1); 00143 topLayout->addWidget(m_optionGrp); 00144 00145 // We delay creation of these until needed. 00146 m_patterns = 0L; 00147 m_placeholders = 0L; 00148 00149 // signals and slots connections 00150 connect(m_selectedText, SIGNAL(toggled(bool)), this, SLOT(slotSelectedTextToggled(bool))); 00151 connect(m_regExp, SIGNAL(toggled(bool)), m_regExpItem, SLOT(setEnabled(bool))); 00152 connect(m_backRef, SIGNAL(toggled(bool)), m_backRefItem, SLOT(setEnabled(bool))); 00153 connect(m_regExpItem, SIGNAL(pressed()), this, SLOT(showPatterns())); 00154 connect(m_backRefItem, SIGNAL(pressed()), this, SLOT(showPlaceholders())); 00155 00156 connect(m_find, SIGNAL(textChanged ( const QString & )),this, SLOT(textSearchChanged( const QString & ))); 00157 00158 // tab order 00159 setTabOrder(m_find, m_regExp); 00160 setTabOrder(m_regExp, m_regExpItem); 00161 setTabOrder(m_regExpItem, m_replace); 00162 setTabOrder(m_replace, m_backRef); 00163 setTabOrder(m_backRef, m_backRefItem); 00164 setTabOrder(m_backRefItem, m_caseSensitive); 00165 setTabOrder(m_caseSensitive, m_wholeWordsOnly); 00166 setTabOrder(m_wholeWordsOnly, m_fromCursor); 00167 setTabOrder(m_fromCursor, m_findBackwards); 00168 setTabOrder(m_findBackwards, m_selectedText); 00169 setTabOrder(m_selectedText, m_promptOnReplace); 00170 00171 // buddies 00172 m_findLabel->setBuddy(m_find); 00173 m_replaceLabel->setBuddy(m_replace); 00174 00175 if (!forReplace) 00176 { 00177 m_promptOnReplace->hide(); 00178 m_replaceGrp->hide(); 00179 } 00180 setFindHistory(findStrings); 00181 m_find->setFocus(); 00182 enableButtonOK( !pattern().isEmpty() ); 00183 00184 } 00185 00186 void KoFindDialog::textSearchChanged( const QString & text) 00187 { 00188 enableButtonOK( !text.isEmpty() ); 00189 } 00190 00191 long KoFindDialog::options() const 00192 { 00193 long options = 0; 00194 00195 if (m_caseSensitive->isChecked()) 00196 options |= CaseSensitive; 00197 if (m_wholeWordsOnly->isChecked()) 00198 options |= WholeWordsOnly; 00199 if (m_fromCursor->isChecked()) 00200 options |= FromCursor; 00201 if (m_findBackwards->isChecked()) 00202 options |= FindBackwards; 00203 if (m_selectedText->isChecked()) 00204 options |= SelectedText; 00205 if (m_regExp->isChecked()) 00206 options |= RegularExpression; 00207 return options; 00208 } 00209 00210 QString KoFindDialog::pattern() const 00211 { 00212 return m_find->currentText(); 00213 } 00214 00215 void KoFindDialog::setFindHistory(const QStringList &strings) 00216 { 00217 if (strings.count() > 0) 00218 { 00219 m_find->setHistoryItems(strings, true); 00220 m_find->lineEdit()->setText( strings.first() ); 00221 m_find->lineEdit()->selectAll(); 00222 } 00223 else 00224 m_find->clearHistory(); 00225 } 00226 00227 void KoFindDialog::setHasSelection(bool hasSelection) 00228 { 00229 m_selectedText->setEnabled( hasSelection ); 00230 // If we have a selection, we make 'find in selection' default 00231 // and if we don't, then the option has to be unchecked, obviously. 00232 m_selectedText->setChecked( hasSelection ); 00233 slotSelectedTextToggled( hasSelection ); 00234 } 00235 00236 void KoFindDialog::slotSelectedTextToggled(bool selec) 00237 { 00238 // From cursor doesn't make sense if we have a selection 00239 m_fromCursor->setEnabled( !selec ); 00240 if ( selec ) // uncheck if disabled 00241 m_fromCursor->setChecked( false ); 00242 } 00243 00244 void KoFindDialog::setOptions(long options) 00245 { 00246 m_caseSensitive->setChecked(options & CaseSensitive); 00247 m_wholeWordsOnly->setChecked(options & WholeWordsOnly); 00248 m_fromCursor->setChecked(options & FromCursor); 00249 m_findBackwards->setChecked(options & FindBackwards); 00250 m_selectedText->setChecked(options & SelectedText); 00251 m_regExp->setChecked(options & RegularExpression); 00252 } 00253 00254 // Create a popup menu with a list of regular expression terms, to help the user 00255 // compose a regular expression search pattern. 00256 void KoFindDialog::showPatterns() 00257 { 00258 typedef struct 00259 { 00260 const char *description; 00261 const char *regExp; 00262 int cursorAdjustment; 00263 } term; 00264 static const term items[] = 00265 { 00266 { I18N_NOOP("Any Character"), ".", 0 }, 00267 { I18N_NOOP("Start of Line"), "^", 0 }, 00268 { I18N_NOOP("End of Line"), "$", 0 }, 00269 { I18N_NOOP("Set of Characters"), "[]", -1 }, 00270 { I18N_NOOP("Repeats, Zero or More Times"), "*", 0 }, 00271 { I18N_NOOP("Repeats, One or More Times"), "+", 0 }, 00272 { I18N_NOOP("Optional"), "?", 0 }, 00273 { I18N_NOOP("Escape"), "\\", 0 }, 00274 { I18N_NOOP("TAB"), "\\t", 0 }, 00275 { I18N_NOOP("Newline"), "\\n", 0 }, 00276 { I18N_NOOP("Carriage Return"), "\\r", 0 }, 00277 { I18N_NOOP("White Space"), "\\s", 0 }, 00278 { I18N_NOOP("Digit"), "\\d", 0 }, 00279 // Those don't work 00280 //{ I18N_NOOP("Unicode Point"), "\x0000", 0 }, 00281 //{ I18N_NOOP("ASCII/Latin-1 Character"), "\000", 0 } 00282 }; 00283 int i; 00284 00285 // Populate the popup menu. 00286 if (!m_patterns) 00287 { 00288 m_patterns = new QPopupMenu(this); 00289 for (i = 0; (unsigned)i < sizeof(items) / sizeof(items[0]); i++) 00290 { 00291 m_patterns->insertItem(i18n(items[i].description), i, i); 00292 } 00293 } 00294 00295 // Insert the selection into the edit control. 00296 i = m_patterns->exec(QCursor::pos()); 00297 if (i != -1) 00298 { 00299 QLineEdit *editor = m_find->lineEdit(); 00300 00301 editor->insert(items[i].regExp); 00302 editor->setCursorPosition(editor->cursorPosition() + items[i].cursorAdjustment); 00303 } 00304 } 00305 00306 // Create a popup menu with a list of backreference terms, to help the user 00307 // compose a regular expression replacement pattern. 00308 void KoFindDialog::showPlaceholders() 00309 { 00310 typedef struct 00311 { 00312 const char *description; 00313 const char *backRef; 00314 } term; 00315 static const term items[] = 00316 { 00317 { I18N_NOOP("Complete text found"), "/0" }, 00318 }; 00319 int i; 00320 00321 // Populate the popup menu. 00322 if (!m_placeholders) 00323 { 00324 m_placeholders = new QPopupMenu(this); 00325 for (i = 0; (unsigned)i < sizeof(items) / sizeof(items[0]); i++) 00326 { 00327 m_placeholders->insertItem(i18n(items[i].description), i, i); 00328 } 00329 } 00330 00331 // Insert the selection into the edit control. 00332 i = m_placeholders->exec(QCursor::pos()); 00333 if (i != -1) 00334 { 00335 QLineEdit *editor = m_replace->lineEdit(); 00336 00337 editor->insert(items[i].backRef); 00338 } 00339 } 00340 00341 void KoFindDialog::slotOk() 00342 { 00343 // Nothing to find? 00344 if (pattern().isEmpty()) 00345 { 00346 KMessageBox::error(this, i18n("You must enter some text to search for.")); 00347 return; 00348 } 00349 00350 if (m_regExp->isChecked()) 00351 { 00352 // Check for a valid regular expression. 00353 QRegExp regExp(pattern()); 00354 00355 if (!regExp.isValid()) 00356 { 00357 KMessageBox::error(this, i18n("Invalid regular expression.")); 00358 return; 00359 } 00360 } 00361 m_find->addToHistory(pattern()); 00362 emit okClicked(); 00363 accept(); 00364 } 00365 00366 // Create the dialog. 00367 KoFind::KoFind(const QString &pattern, long options, QWidget *parent) : 00368 KDialogBase(parent, __FILE__, false, // non-modal! 00369 i18n("Find"), 00370 User1 | Close, 00371 User1, 00372 false, 00373 KStdGuiItem::yes()) 00374 { 00375 setMainWidget( new QLabel( i18n("Find next '%1'").arg(pattern), this ) ); 00376 00377 m_cancelled = false; 00378 m_options = options; 00379 m_parent = parent; 00380 m_matches = 0; 00381 if (m_options & KoFindDialog::RegularExpression) 00382 m_regExp = new QRegExp(pattern, m_options & KoFindDialog::CaseSensitive); 00383 else 00384 m_pattern = pattern; 00385 resize(minimumSize()); 00386 } 00387 00388 KoFind::~KoFind() 00389 { 00390 if (!m_matches && !m_cancelled) 00391 KMessageBox::information(m_parent, i18n("No match was found.")); 00392 } 00393 00394 void KoFind::slotClose() 00395 { 00396 m_cancelled = true; 00397 kapp->exit_loop(); 00398 } 00399 00400 void KoFind::abort() 00401 { 00402 slotClose(); 00403 } 00404 00405 bool KoFind::find(const QString &text, const QRect &expose) 00406 { 00407 if (m_options & KoFindDialog::FindBackwards) 00408 { 00409 m_index = text.length(); 00410 } 00411 else 00412 { 00413 m_index = 0; 00414 } 00415 m_text = text; 00416 m_expose = expose; 00417 do 00418 { 00419 // Find the next match. 00420 if (m_options & KoFindDialog::RegularExpression) 00421 m_index = KoFind::find(m_text, *m_regExp, m_index, m_options, &m_matchedLength); 00422 else 00423 m_index = KoFind::find(m_text, m_pattern, m_index, m_options, &m_matchedLength); 00424 if (m_index != -1) 00425 { 00426 // Tell the world about the match we found, in case someone wants to 00427 // highlight it. 00428 if ( validateMatch( m_text, m_index, m_matchedLength )) 00429 { 00430 emit highlight(m_text, m_index, m_matchedLength, m_expose); 00431 show(); 00432 kapp->enter_loop(); 00433 } 00434 else 00435 m_index = m_index+m_matchedLength; 00436 } 00437 } 00438 while ((m_index != -1) && !m_cancelled); 00439 00440 // Should the user continue? 00441 return !m_cancelled; 00442 } 00443 00444 int KoFind::find(const QString &text, const QString &pattern, int index, long options, int *matchedLength) 00445 { 00446 // Handle regular expressions in the appropriate way. 00447 if (options & KoFindDialog::RegularExpression) 00448 { 00449 QRegExp regExp(pattern, options & KoFindDialog::CaseSensitive); 00450 00451 return find(text, regExp, index, options, matchedLength); 00452 } 00453 00454 bool caseSensitive = (options & KoFindDialog::CaseSensitive); 00455 00456 if (options & KoFindDialog::WholeWordsOnly) 00457 { 00458 if (options & KoFindDialog::FindBackwards) 00459 { 00460 // Backward search, until the beginning of the line... 00461 while (index >= 0) 00462 { 00463 // ...find the next match. 00464 index = text.findRev(pattern, index, caseSensitive); 00465 if (index == -1) 00466 break; 00467 00468 // Is the match delimited correctly? 00469 *matchedLength = pattern.length(); 00470 if (isWholeWords(text, index, *matchedLength)) 00471 break; 00472 index--; 00473 } 00474 } 00475 else 00476 { 00477 // Forward search, until the end of the line... 00478 while (index < (int)text.length()) 00479 { 00480 // ...find the next match. 00481 index = text.find(pattern, index, caseSensitive); 00482 if (index == -1) 00483 break; 00484 00485 // Is the match delimited correctly? 00486 *matchedLength = pattern.length(); 00487 if (isWholeWords(text, index, *matchedLength)) 00488 break; 00489 index++; 00490 } 00491 if (index >= (int)text.length()) // end of line 00492 index = -1; // not found 00493 } 00494 } 00495 else 00496 { 00497 // Non-whole-word search. 00498 if (options & KoFindDialog::FindBackwards) 00499 { 00500 index = text.findRev(pattern, index, caseSensitive); 00501 } 00502 else 00503 { 00504 index = text.find(pattern, index, caseSensitive); 00505 } 00506 if (index != -1) 00507 { 00508 *matchedLength = pattern.length(); 00509 } 00510 } 00511 return index; 00512 } 00513 00514 int KoFind::find(const QString &text, const QRegExp &pattern, int index, long options, int *matchedLength) 00515 { 00516 if (options & KoFindDialog::WholeWordsOnly) 00517 { 00518 if (options & KoFindDialog::FindBackwards) 00519 { 00520 // Backward search, until the beginning of the line... 00521 while (index >= 0) 00522 { 00523 // ...find the next match. 00524 index = text.findRev(pattern, index); 00525 if (index == -1) 00526 break; 00527 00528 // Is the match delimited correctly? 00529 //pattern.match(text, index, matchedLength, false); 00530 /*int pos =*/ pattern.search( text.mid(index) ); 00531 *matchedLength = pattern.matchedLength(); 00532 if (isWholeWords(text, index, *matchedLength)) 00533 break; 00534 index--; 00535 } 00536 } 00537 else 00538 { 00539 // Forward search, until the end of the line... 00540 while (index < (int)text.length()) 00541 { 00542 // ...find the next match. 00543 index = text.find(pattern, index); 00544 if (index == -1) 00545 break; 00546 00547 // Is the match delimited correctly? 00548 //pattern.match(text, index, matchedLength, false); 00549 /*int pos =*/ pattern.search( text.mid(index) ); 00550 *matchedLength = pattern.matchedLength(); 00551 if (isWholeWords(text, index, *matchedLength)) 00552 break; 00553 index++; 00554 } 00555 if (index >= (int)text.length()) // end of line 00556 index = -1; // not found 00557 } 00558 } 00559 else 00560 { 00561 // Non-whole-word search. 00562 if (options & KoFindDialog::FindBackwards) 00563 { 00564 index = text.findRev(pattern, index); 00565 } 00566 else 00567 { 00568 index = text.find(pattern, index); 00569 } 00570 if (index != -1) 00571 { 00572 //pattern.match(text, index, matchedLength, false); 00573 /*int pos =*/ pattern.search( text.mid(index) ); 00574 *matchedLength = pattern.matchedLength(); 00575 } 00576 } 00577 return index; 00578 } 00579 00580 bool KoFind::isInWord(QChar ch) 00581 { 00582 return ch.isLetter() || ch.isDigit() || ch == '_'; 00583 } 00584 00585 bool KoFind::isWholeWords(const QString &text, int starts, int matchedLength) 00586 { 00587 if ((starts == 0) || (!isInWord(text[starts - 1]))) 00588 { 00589 int ends = starts + matchedLength; 00590 00591 if ((ends == (int)text.length()) || (!isInWord(text[ends]))) 00592 return true; 00593 } 00594 return false; 00595 } 00596 00597 // Yes. 00598 void KoFind::slotUser1() 00599 { 00600 m_matches++; 00601 if (m_options & KoFindDialog::FindBackwards) 00602 m_index--; 00603 else 00604 m_index++; 00605 kapp->exit_loop(); 00606 } 00607 00608 #include "koFind.moc"
KDE Logo
This file is part of the documentation for KDevelop Version 3.0.4.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Tue Oct 19 08:01:38 2004 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003