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