00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include "kfinddialog.h"
00022 #include "kfinddialog_p.h"
00023
00024 #include <QtGui/QCheckBox>
00025 #include <QtGui/QCursor>
00026 #include <QtGui/QGroupBox>
00027 #include <QtGui/QLabel>
00028 #include <QtGui/QLayout>
00029 #include <QtGui/QLineEdit>
00030 #include <QtGui/QMenu>
00031 #include <QtGui/QPushButton>
00032 #include <QtCore/QRegExp>
00033 #include <kcombobox.h>
00034 #include <khistorycombobox.h>
00035 #include <kdebug.h>
00036 #include <klocale.h>
00037 #include <kmessagebox.h>
00038 #include <assert.h>
00039 #include <kfind.h>
00040 #include <kregexpeditorinterface.h>
00041 #include <kservicetypetrader.h>
00042
00043 KFindDialog::KFindDialog(QWidget *parent, long options, const QStringList &findStrings, bool hasSelection, bool replaceDialog)
00044 : KDialog(parent),
00045 d(new KFindDialogPrivate(this))
00046 {
00047 setCaption( i18n("Find Text") );
00048 setButtons( Ok | Cancel );
00049 setDefaultButton( Ok );
00050
00051 d->init(replaceDialog, findStrings, hasSelection);
00052 setOptions(options);
00053 setButtonGuiItem( KDialog::Cancel, KStandardGuiItem::close() );
00054 }
00055
00056 KFindDialog::~KFindDialog()
00057 {
00058 delete d;
00059 }
00060
00061 QWidget *KFindDialog::findExtension() const
00062 {
00063 if (!d->findExtension)
00064 {
00065 d->findExtension = new QWidget(d->findGrp);
00066 d->findLayout->addWidget(d->findExtension, 3, 0, 1, 2);
00067 }
00068
00069 return d->findExtension;
00070 }
00071
00072 QStringList KFindDialog::findHistory() const
00073 {
00074 return d->find->historyItems();
00075 }
00076
00077 void KFindDialog::KFindDialogPrivate::init(bool forReplace, const QStringList &_findStrings, bool hasSelection)
00078 {
00079 QVBoxLayout *topLayout;
00080 QGridLayout *optionsLayout;
00081
00082
00083 QWidget *page = new QWidget(q);
00084 q->setMainWidget(page);
00085
00086 topLayout = new QVBoxLayout(page);
00087 topLayout->setMargin( 0 );
00088
00089 findGrp = new QGroupBox(i18nc("@title:group", "Find"), page);
00090 findLayout = new QGridLayout(findGrp);
00091
00092 QLabel *findLabel = new QLabel(i18n("&Text to find:"), findGrp);
00093 find = new KHistoryComboBox(findGrp);
00094 find->setMaxCount(10);
00095 find->setDuplicatesEnabled(false);
00096 regExp = new QCheckBox(i18n("Regular e&xpression"), findGrp);
00097 regExpItem = new QPushButton(i18n("&Edit..."), findGrp);
00098 regExpItem->setEnabled(false);
00099
00100 findLayout->addWidget(findLabel, 0, 0);
00101 findLayout->addWidget(find, 1, 0, 1, 2);
00102 findLayout->addWidget(regExp, 2, 0);
00103 findLayout->addWidget(regExpItem, 2, 1);
00104 topLayout->addWidget(findGrp);
00105
00106 replaceGrp = new QGroupBox( i18n("Replace With"), page);
00107 replaceLayout = new QGridLayout(replaceGrp);
00108
00109 QLabel *replaceLabel = new QLabel(i18n("Replace&ment text:"), replaceGrp);
00110 replace = new KHistoryComboBox(replaceGrp);
00111 replace->setMaxCount(10);
00112 replace->setDuplicatesEnabled(false);
00113 backRef = new QCheckBox(i18n("Use p&laceholders"), replaceGrp);
00114 backRefItem = new QPushButton(i18n("Insert Place&holder"), replaceGrp);
00115 backRefItem->setEnabled(false);
00116
00117 replaceLayout->addWidget(replaceLabel, 0, 0);
00118 replaceLayout->addWidget(replace, 1, 0, 1, 2);
00119 replaceLayout->addWidget(backRef, 2, 0);
00120 replaceLayout->addWidget(backRefItem, 2, 1);
00121 topLayout->addWidget(replaceGrp);
00122
00123 QGroupBox *optionGrp = new QGroupBox(i18n("Options"), page);
00124 optionsLayout = new QGridLayout(optionGrp);
00125
00126 caseSensitive = new QCheckBox(i18n("C&ase sensitive"), optionGrp);
00127 wholeWordsOnly = new QCheckBox(i18n("&Whole words only"), optionGrp);
00128 fromCursor = new QCheckBox(i18n("From c&ursor"), optionGrp);
00129 findBackwards = new QCheckBox(i18n("Find &backwards"), optionGrp);
00130 selectedText = new QCheckBox(i18n("&Selected text"), optionGrp);
00131 q->setHasSelection( hasSelection );
00132
00133
00134 selectedText->setChecked( hasSelection );
00135 _k_slotSelectedTextToggled( hasSelection );
00136
00137 promptOnReplace = new QCheckBox(i18n("&Prompt on replace"), optionGrp);
00138 promptOnReplace->setChecked( true );
00139
00140 optionsLayout->addWidget(caseSensitive, 0, 0);
00141 optionsLayout->addWidget(wholeWordsOnly, 1, 0);
00142 optionsLayout->addWidget(fromCursor, 2, 0);
00143 optionsLayout->addWidget(findBackwards, 0, 1);
00144 optionsLayout->addWidget(selectedText, 1, 1);
00145 optionsLayout->addWidget(promptOnReplace, 2, 1);
00146 topLayout->addWidget(optionGrp);
00147
00148
00149 patterns = 0;
00150 placeholders = 0;
00151
00152
00153 q->connect(selectedText, SIGNAL(toggled(bool)), q, SLOT(_k_slotSelectedTextToggled(bool)));
00154 q->connect(regExp, SIGNAL(toggled(bool)), regExpItem, SLOT(setEnabled(bool)));
00155 q->connect(backRef, SIGNAL(toggled(bool)), backRefItem, SLOT(setEnabled(bool)));
00156 q->connect(regExpItem, SIGNAL(clicked()), q, SLOT(_k_showPatterns()));
00157 q->connect(backRefItem, SIGNAL(clicked()), q, SLOT(_k_showPlaceholders()));
00158
00159 q->connect(find, SIGNAL(editTextChanged(const QString &)), q, SLOT(_k_textSearchChanged(const QString &)));
00160
00161 q->connect(regExp, SIGNAL(toggled(bool)), q, SIGNAL(optionsChanged()));
00162 q->connect(backRef, SIGNAL(toggled(bool)), q, SIGNAL(optionsChanged()));
00163 q->connect(caseSensitive, SIGNAL(toggled(bool)), q, SIGNAL(optionsChanged()));
00164 q->connect(wholeWordsOnly, SIGNAL(toggled(bool)), q, SIGNAL(optionsChanged()));
00165 q->connect(fromCursor, SIGNAL(toggled(bool)), q, SIGNAL(optionsChanged()));
00166 q->connect(findBackwards, SIGNAL(toggled(bool)), q, SIGNAL(optionsChanged()));
00167 q->connect(selectedText, SIGNAL(toggled(bool)), q, SIGNAL(optionsChanged()));
00168 q->connect(promptOnReplace, SIGNAL(toggled(bool)), q, SIGNAL(optionsChanged()));
00169
00170
00171 q->setTabOrder(find, regExp);
00172 q->setTabOrder(regExp, regExpItem);
00173 q->setTabOrder(regExpItem, replace);
00174 q->setTabOrder(replace, backRef);
00175 q->setTabOrder(backRef, backRefItem);
00176 q->setTabOrder(backRefItem, caseSensitive);
00177 q->setTabOrder(caseSensitive, wholeWordsOnly);
00178 q->setTabOrder(wholeWordsOnly, fromCursor);
00179 q->setTabOrder(fromCursor, findBackwards);
00180 q->setTabOrder(findBackwards, selectedText);
00181 q->setTabOrder(selectedText, promptOnReplace);
00182
00183
00184 findLabel->setBuddy(find);
00185 replaceLabel->setBuddy(replace);
00186
00187 if (!forReplace)
00188 {
00189 promptOnReplace->hide();
00190 replaceGrp->hide();
00191 }
00192
00193 findStrings = _findStrings;
00194 find->setFocus();
00195 q->enableButtonOk( !q->pattern().isEmpty() );
00196 if (forReplace)
00197 {
00198 q->setButtonGuiItem( KDialog::Ok, KGuiItem( i18n("&Replace"), QString(),
00199 i18n("Start replace"),
00200 i18n("<qt>If you press the <b>Replace</b> button, the text you entered "
00201 "above is searched for within the document and any occurrence is "
00202 "replaced with the replacement text.</qt>")));
00203 }
00204 else
00205 {
00206 q->setButtonGuiItem( KDialog::Ok, KGuiItem( i18n("&Find"), "edit-find",
00207 i18n("Start searching"),
00208 i18n("<qt>If you press the <b>Find</b> button, the text you entered "
00209 "above is searched for within the document.</qt>")));
00210 }
00211
00212
00213 find->setWhatsThis(i18n(
00214 "Enter a pattern to search for, or select a previous pattern from "
00215 "the list.") );
00216 regExp->setWhatsThis(i18n(
00217 "If enabled, search for a regular expression.") );
00218 regExpItem->setWhatsThis(i18n(
00219 "Click here to edit your regular expression using a graphical editor.") );
00220 replace->setWhatsThis(i18n(
00221 "Enter a replacement string, or select a previous one from the list.") );
00222 backRef->setWhatsThis(i18n(
00223 "<qt>If enabled, any occurrence of <code><b>\\N</b></code>, where "
00224 "<code><b>N</b></code> is an integer number, will be replaced with "
00225 "the corresponding capture (\"parenthesized substring\") from the "
00226 "pattern.<p>To include (a literal <code><b>\\N</b></code> in your "
00227 "replacement, put an extra backslash in front of it, like "
00228 "<code><b>\\\\N</b></code>.</p></qt>") );
00229 backRefItem->setWhatsThis(i18n(
00230 "Click for a menu of available captures.") );
00231 wholeWordsOnly->setWhatsThis(i18n(
00232 "Require word boundaries in both ends of a match to succeed.") );
00233 fromCursor->setWhatsThis(i18n(
00234 "Start searching at the current cursor location rather than at the top.") );
00235 selectedText->setWhatsThis(i18n(
00236 "Only search within the current selection.") );
00237 caseSensitive->setWhatsThis(i18n(
00238 "Perform a case sensitive search: entering the pattern "
00239 "'Joe' will not match 'joe' or 'JOE', only 'Joe'.") );
00240 findBackwards->setWhatsThis(i18n(
00241 "Search backwards.") );
00242 promptOnReplace->setWhatsThis(i18n(
00243 "Ask before replacing each match found.") );
00244
00245 q->connect(q, SIGNAL(okClicked()), q, SLOT(_k_slotOk()));
00246 _k_textSearchChanged(find->lineEdit()->text());
00247 }
00248
00249 void KFindDialog::KFindDialogPrivate::_k_textSearchChanged( const QString & text)
00250 {
00251 q->enableButtonOk( !text.isEmpty() );
00252 }
00253
00254 void KFindDialog::showEvent( QShowEvent *e )
00255 {
00256 if ( !d->initialShowDone )
00257 {
00258 d->initialShowDone = true;
00259 kDebug() << "showEvent\n";
00260 if (!d->findStrings.isEmpty())
00261 setFindHistory(d->findStrings);
00262 d->findStrings = QStringList();
00263 if (!d->pattern.isEmpty()) {
00264 d->find->lineEdit()->setText( d->pattern );
00265 d->find->lineEdit()->selectAll();
00266 d->pattern.clear();
00267 }
00268 }
00269 KDialog::showEvent(e);
00270 }
00271
00272 long KFindDialog::options() const
00273 {
00274 long options = 0;
00275
00276 if (d->caseSensitive->isChecked())
00277 options |= KFind::CaseSensitive;
00278 if (d->wholeWordsOnly->isChecked())
00279 options |= KFind::WholeWordsOnly;
00280 if (d->fromCursor->isChecked())
00281 options |= KFind::FromCursor;
00282 if (d->findBackwards->isChecked())
00283 options |= KFind::FindBackwards;
00284 if (d->selectedText->isChecked())
00285 options |= KFind::SelectedText;
00286 if (d->regExp->isChecked())
00287 options |= KFind::RegularExpression;
00288 return options;
00289 }
00290
00291 QString KFindDialog::pattern() const
00292 {
00293 return d->find->currentText();
00294 }
00295
00296 void KFindDialog::setPattern (const QString &pattern)
00297 {
00298 d->find->lineEdit()->setText( pattern );
00299 d->find->lineEdit()->selectAll();
00300 d->pattern = pattern;
00301 kDebug() << "setPattern " << pattern;
00302 }
00303
00304 void KFindDialog::setFindHistory(const QStringList &strings)
00305 {
00306 if (strings.count() > 0)
00307 {
00308 d->find->setHistoryItems(strings, true);
00309 d->find->lineEdit()->setText( strings.first() );
00310 d->find->lineEdit()->selectAll();
00311 }
00312 else
00313 d->find->clearHistory();
00314 }
00315
00316 void KFindDialog::setHasSelection(bool hasSelection)
00317 {
00318 if (hasSelection) d->enabled |= KFind::SelectedText;
00319 else d->enabled &= ~KFind::SelectedText;
00320 d->selectedText->setEnabled( hasSelection );
00321 if ( !hasSelection )
00322 {
00323 d->selectedText->setChecked( false );
00324 d->_k_slotSelectedTextToggled( hasSelection );
00325 }
00326 }
00327
00328 void KFindDialog::KFindDialogPrivate::_k_slotSelectedTextToggled(bool selec)
00329 {
00330
00331 fromCursor->setEnabled( !selec && (enabled & KFind::FromCursor) );
00332 if ( selec )
00333 fromCursor->setChecked( false );
00334 }
00335
00336 void KFindDialog::setHasCursor(bool hasCursor)
00337 {
00338 if (hasCursor) d->enabled |= KFind::FromCursor;
00339 else d->enabled &= ~KFind::FromCursor;
00340 d->fromCursor->setEnabled( hasCursor );
00341 d->fromCursor->setChecked( hasCursor && (options() & KFind::FromCursor) );
00342 }
00343
00344 void KFindDialog::setSupportsBackwardsFind( bool supports )
00345 {
00346
00347 if (supports) d->enabled |= KFind::FindBackwards;
00348 else d->enabled &= ~KFind::FindBackwards;
00349 d->findBackwards->setEnabled( supports );
00350 d->findBackwards->setChecked( supports && (options() & KFind::FindBackwards) );
00351 }
00352
00353 void KFindDialog::setSupportsCaseSensitiveFind( bool supports )
00354 {
00355
00356 if (supports) d->enabled |= KFind::CaseSensitive;
00357 else d->enabled &= ~KFind::CaseSensitive;
00358 d->caseSensitive->setEnabled( supports );
00359 d->caseSensitive->setChecked( supports && (options() & KFind::CaseSensitive) );
00360 }
00361
00362 void KFindDialog::setSupportsWholeWordsFind( bool supports )
00363 {
00364
00365 if (supports) d->enabled |= KFind::WholeWordsOnly;
00366 else d->enabled &= ~KFind::WholeWordsOnly;
00367 d->wholeWordsOnly->setEnabled( supports );
00368 d->wholeWordsOnly->setChecked( supports && (options() & KFind::WholeWordsOnly) );
00369 }
00370
00371 void KFindDialog::setSupportsRegularExpressionFind( bool supports )
00372 {
00373 if (supports) d->enabled |= KFind::RegularExpression;
00374 else d->enabled &= ~KFind::RegularExpression;
00375 d->regExp->setEnabled( supports );
00376 d->regExp->setChecked( supports && (options() & KFind::RegularExpression) );
00377 if( !supports)
00378 {
00379 d->regExpItem->hide();
00380 d->regExp->hide();
00381 }
00382 else
00383 {
00384 d->regExpItem->show();
00385 d->regExp->show();
00386 }
00387 }
00388
00389 void KFindDialog::setOptions(long options)
00390 {
00391 d->caseSensitive->setChecked((d->enabled & KFind::CaseSensitive) && (options & KFind::CaseSensitive));
00392 d->wholeWordsOnly->setChecked((d->enabled & KFind::WholeWordsOnly) && (options & KFind::WholeWordsOnly));
00393 d->fromCursor->setChecked((d->enabled & KFind::FromCursor) && (options & KFind::FromCursor));
00394 d->findBackwards->setChecked((d->enabled & KFind::FindBackwards) && (options & KFind::FindBackwards));
00395 d->selectedText->setChecked((d->enabled & KFind::SelectedText) && (options & KFind::SelectedText));
00396 d->regExp->setChecked((d->enabled & KFind::RegularExpression) && (options & KFind::RegularExpression));
00397 }
00398
00399
00400
00401 void KFindDialog::KFindDialogPrivate::_k_showPatterns()
00402 {
00403 if ( !regexpDialogQueryDone )
00404 {
00405 regexpDialog = KServiceTypeTrader::createInstanceFromQuery<QDialog>( "KRegExpEditor/KRegExpEditor", QString(), q );
00406 regexpDialogQueryDone = true;
00407 }
00408
00409 if ( regexpDialog )
00410 {
00411 KRegExpEditorInterface *iface = qobject_cast<KRegExpEditorInterface*>( regexpDialog );
00412 assert( iface );
00413
00414 iface->setRegExp( q->pattern() );
00415 if ( regexpDialog->exec() == QDialog::Accepted )
00416 q->setPattern( iface->regExp() );
00417 }
00418 else
00419 {
00420 typedef struct
00421 {
00422 const char *description;
00423 const char *regExp;
00424 int cursorAdjustment;
00425 } term;
00426 static const term items[] =
00427 {
00428 { I18N_NOOP("Any Character"), ".", 0 },
00429 { I18N_NOOP("Start of Line"), "^", 0 },
00430 { I18N_NOOP("End of Line"), "$", 0 },
00431 { I18N_NOOP("Set of Characters"), "[]", -1 },
00432 { I18N_NOOP("Repeats, Zero or More Times"), "*", 0 },
00433 { I18N_NOOP("Repeats, One or More Times"), "+", 0 },
00434 { I18N_NOOP("Optional"), "?", 0 },
00435 { I18N_NOOP("Escape"), "\\", 0 },
00436 { I18N_NOOP("TAB"), "\\t", 0 },
00437 { I18N_NOOP("Newline"), "\\n", 0 },
00438 { I18N_NOOP("Carriage Return"), "\\r", 0 },
00439 { I18N_NOOP("White Space"), "\\s", 0 },
00440 { I18N_NOOP("Digit"), "\\d", 0 },
00441 };
00442
00443
00444 class RegExpAction : public QAction
00445 {
00446 public:
00447 RegExpAction( QObject *parent, const QString &text, const QString ®Exp, int cursor )
00448 : QAction( text, parent ), mText( text ), mRegExp( regExp ), mCursor( cursor )
00449 {
00450 }
00451
00452 QString text() const { return mText; }
00453 QString regExp() const { return mRegExp; }
00454 int cursor() const { return mCursor; }
00455
00456 private:
00457 QString mText;
00458 QString mRegExp;
00459 int mCursor;
00460 };
00461
00462 int i;
00463
00464
00465 if (!patterns)
00466 {
00467 patterns = new QMenu(q);
00468 for (i = 0; (unsigned)i < sizeof(items) / sizeof(items[0]); i++)
00469 {
00470 patterns->addAction(new RegExpAction(patterns, i18n(items[i].description),
00471 items[i].regExp,
00472 items[i].cursorAdjustment));
00473 }
00474 }
00475
00476
00477 QAction *action = patterns->exec(regExpItem->mapToGlobal(regExpItem->rect().bottomLeft()));
00478 if (action)
00479 {
00480 RegExpAction *regExpAction = static_cast<RegExpAction*>( action );
00481 if ( regExpAction ) {
00482 QLineEdit *editor = find->lineEdit();
00483
00484 editor->insert(regExpAction->regExp());
00485 editor->setCursorPosition(editor->cursorPosition() + regExpAction->cursor());
00486 }
00487 }
00488 }
00489 }
00490
00491 class PlaceHolderAction : public QAction
00492 {
00493 public:
00494 PlaceHolderAction( QObject *parent, const QString &text, int id )
00495 : QAction( text, parent ), mText( text ), mId( id )
00496 {
00497 }
00498
00499 QString text() const { return mText; }
00500 int id() const { return mId; }
00501
00502 private:
00503 QString mText;
00504 int mId;
00505 };
00506
00507
00508
00509 void KFindDialog::KFindDialogPrivate::_k_showPlaceholders()
00510 {
00511
00512 if (!placeholders)
00513 {
00514 placeholders = new QMenu(q);
00515 q->connect( placeholders, SIGNAL(aboutToShow()), q, SLOT(_k_slotPlaceholdersAboutToShow()) );
00516 }
00517
00518
00519 QAction *action = placeholders->exec(backRefItem->mapToGlobal(backRefItem->rect().bottomLeft()));
00520 if (action)
00521 {
00522 PlaceHolderAction *placeHolderAction = static_cast<PlaceHolderAction*>(action);
00523 if (placeHolderAction) {
00524 QLineEdit *editor = replace->lineEdit();
00525 editor->insert( QString("\\%1").arg( placeHolderAction->id() ) );
00526 }
00527 }
00528 }
00529
00530 void KFindDialog::KFindDialogPrivate::_k_slotPlaceholdersAboutToShow()
00531 {
00532 placeholders->clear();
00533 placeholders->addAction( new PlaceHolderAction(placeholders, i18n("Complete Match"), 0));
00534
00535 QRegExp r( q->pattern() );
00536 uint n = r.numCaptures();
00537 for ( uint i=0; i < n; i++ )
00538 placeholders->addAction( new PlaceHolderAction(placeholders, i18n("Captured Text (%1)", i+1 ), i+1 ) );
00539 }
00540
00541 void KFindDialog::KFindDialogPrivate::_k_slotOk()
00542 {
00543
00544 if (q->pattern().isEmpty())
00545 {
00546 KMessageBox::error(q, i18n("You must enter some text to search for."));
00547 return;
00548 }
00549
00550 if (regExp->isChecked())
00551 {
00552
00553 QRegExp _regExp(q->pattern());
00554
00555 if (!_regExp.isValid())
00556 {
00557 KMessageBox::error(q, i18n("Invalid regular expression."));
00558 return;
00559 }
00560 }
00561 find->addToHistory(q->pattern());
00562 if ( q->windowModality() != Qt::NonModal )
00563 q->accept();
00564 }
00565
00566 #include "kfinddialog.moc"