00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
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 ) :
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
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
00146 m_patterns = 0L;
00147 m_placeholders = 0L;
00148
00149
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
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
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
00231
00232 m_selectedText->setChecked( hasSelection );
00233 slotSelectedTextToggled( hasSelection );
00234 }
00235
00236 void KoFindDialog::slotSelectedTextToggled(bool selec)
00237 {
00238
00239 m_fromCursor->setEnabled( !selec );
00240 if ( selec )
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
00255
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
00280
00281
00282 };
00283 int i;
00284
00285
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
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
00307
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
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
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
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
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
00367 KoFind::KoFind(const QString &pattern, long options, QWidget *parent) :
00368 KDialogBase(parent, __FILE__, false,
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
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
00427
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
00441 return !m_cancelled;
00442 }
00443
00444 int KoFind::find(const QString &text, const QString &pattern, int index, long options, int *matchedLength)
00445 {
00446
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
00461 while (index >= 0)
00462 {
00463
00464 index = text.findRev(pattern, index, caseSensitive);
00465 if (index == -1)
00466 break;
00467
00468
00469 *matchedLength = pattern.length();
00470 if (isWholeWords(text, index, *matchedLength))
00471 break;
00472 index--;
00473 }
00474 }
00475 else
00476 {
00477
00478 while (index < (int)text.length())
00479 {
00480
00481 index = text.find(pattern, index, caseSensitive);
00482 if (index == -1)
00483 break;
00484
00485
00486 *matchedLength = pattern.length();
00487 if (isWholeWords(text, index, *matchedLength))
00488 break;
00489 index++;
00490 }
00491 if (index >= (int)text.length())
00492 index = -1;
00493 }
00494 }
00495 else
00496 {
00497
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
00521 while (index >= 0)
00522 {
00523
00524 index = text.findRev(pattern, index);
00525 if (index == -1)
00526 break;
00527
00528
00529
00530 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
00540 while (index < (int)text.length())
00541 {
00542
00543 index = text.find(pattern, index);
00544 if (index == -1)
00545 break;
00546
00547
00548
00549 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())
00556 index = -1;
00557 }
00558 }
00559 else
00560 {
00561
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
00573 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
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"