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 <tqcheckbox.h>
00023 #include <tqcursor.h>
00024 #include <tqgroupbox.h>
00025 #include <tqlabel.h>
00026 #include <tqlayout.h>
00027 #include <tqpopupmenu.h>
00028 #include <tqpushbutton.h>
00029 #include <tqregexp.h>
00030 #include <kcombobox.h>
00031 #include <kdebug.h>
00032 #include <tdelocale.h>
00033 #include <tdemessagebox.h>
00034 #include <assert.h>
00035 #include <tqwhatsthis.h>
00036
00037 #include <kregexpeditorinterface.h>
00038 #include <tdeparts/componentfactory.h>
00039
00040 class KFindDialog::KFindDialogPrivate
00041 {
00042 public:
00043 KFindDialogPrivate() : m_regexpDialog(0),
00044 m_regexpDialogQueryDone(false),
00045 m_enabled(WholeWordsOnly | FromCursor | SelectedText | CaseSensitive | FindBackwards | RegularExpression), m_initialShowDone(false) {}
00046 TQDialog* m_regexpDialog;
00047 bool m_regexpDialogQueryDone;
00048 long m_enabled;
00049 bool m_initialShowDone;
00050 TQStringList findStrings;
00051 TQString pattern;
00052 };
00053
00054 KFindDialog::KFindDialog(TQWidget *parent, const char *name, long options, const TQStringList &findStrings, bool hasSelection) :
00055 KDialogBase(parent, name, true, i18n("Find Text"), Ok | Cancel, Ok),
00056 m_findExtension (0),
00057 m_replaceExtension (0)
00058 {
00059 d = new KFindDialogPrivate;
00060 init(false, findStrings, hasSelection);
00061 setOptions(options);
00062 setButtonCancel( KStdGuiItem::close() );
00063 }
00064
00065 KFindDialog::KFindDialog(bool modal, TQWidget *parent, const char *name, long options, const TQStringList &findStrings, bool hasSelection) :
00066 KDialogBase(parent, name, modal, i18n("Find Text"), Ok | Cancel, Ok),
00067 m_findExtension (0),
00068 m_replaceExtension (0)
00069 {
00070 d = new KFindDialogPrivate;
00071 init(false, findStrings, hasSelection);
00072 setOptions(options);
00073 setButtonCancel( KStdGuiItem::close() );
00074 }
00075
00076 KFindDialog::KFindDialog(TQWidget *parent, const char *name, bool ) :
00077 KDialogBase(parent, name, true, i18n("Replace Text"), Ok | Cancel, Ok),
00078 m_findExtension (0),
00079 m_replaceExtension (0)
00080 {
00081 d = new KFindDialogPrivate;
00082 setButtonCancel( KStdGuiItem::close() );
00083 }
00084
00085 KFindDialog::~KFindDialog()
00086 {
00087 delete d;
00088 }
00089
00090 TQWidget *KFindDialog::findExtension()
00091 {
00092 if (!m_findExtension)
00093 {
00094 m_findExtension = new TQWidget(m_findGrp);
00095 m_findLayout->addMultiCellWidget(m_findExtension, 3, 3, 0, 1);
00096 }
00097
00098 return m_findExtension;
00099 }
00100
00101 TQStringList KFindDialog::findHistory() const
00102 {
00103 return m_find->historyItems();
00104 }
00105
00106 void KFindDialog::init(bool forReplace, const TQStringList &findStrings, bool hasSelection)
00107 {
00108 TQVBoxLayout *topLayout;
00109 TQGridLayout *optionsLayout;
00110
00111
00112 TQWidget *page = new TQWidget(this);
00113 setMainWidget(page);
00114
00115 topLayout = new TQVBoxLayout(page);
00116 topLayout->setSpacing( KDialog::spacingHint() );
00117 topLayout->setMargin( 0 );
00118
00119 m_findGrp = new TQGroupBox(0, Qt::Vertical, i18n("Find"), page);
00120 m_findGrp->layout()->setSpacing( KDialog::spacingHint() );
00121
00122 m_findLayout = new TQGridLayout(m_findGrp->layout());
00123 m_findLayout->setSpacing( KDialog::spacingHint() );
00124
00125
00126 m_findLabel = new TQLabel(i18n("&Text to find:"), m_findGrp);
00127 m_find = new KHistoryCombo(true, m_findGrp);
00128 m_find->setMaxCount(10);
00129 m_find->setDuplicatesEnabled(false);
00130 m_regExp = new TQCheckBox(i18n("Regular e&xpression"), m_findGrp);
00131 m_regExpItem = new TQPushButton(i18n("&Edit..."), m_findGrp);
00132 m_regExpItem->setEnabled(false);
00133
00134 m_findLayout->addWidget(m_findLabel, 0, 0);
00135 m_findLayout->addMultiCellWidget(m_find, 1, 1, 0, 1);
00136 m_findLayout->addWidget(m_regExp, 2, 0);
00137 m_findLayout->addWidget(m_regExpItem, 2, 1);
00138 topLayout->addWidget(m_findGrp);
00139
00140 m_replaceGrp = new TQGroupBox(0, Qt::Vertical, i18n("Replace With"), page);
00141 m_replaceGrp->layout()->setSpacing( KDialog::spacingHint() );
00142
00143 m_replaceLayout = new TQGridLayout(m_replaceGrp->layout());
00144 m_replaceLayout->setSpacing( KDialog::spacingHint() );
00145
00146
00147 m_replaceLabel = new TQLabel(i18n("Replace&ment text:"), m_replaceGrp);
00148 m_replace = new KHistoryCombo(true, m_replaceGrp);
00149 m_replace->setMaxCount(10);
00150 m_replace->setDuplicatesEnabled(false);
00151 m_backRef = new TQCheckBox(i18n("Use p&laceholders"), m_replaceGrp);
00152 m_backRef->setEnabled(m_regExp->isChecked());
00153 m_backRefItem = new TQPushButton(i18n("Insert Place&holder"), m_replaceGrp);
00154 m_backRefItem->setEnabled(m_regExp->isChecked() && m_backRef->isChecked());
00155
00156 m_replaceLayout->addWidget(m_replaceLabel, 0, 0);
00157 m_replaceLayout->addMultiCellWidget(m_replace, 1, 1, 0, 1);
00158 m_replaceLayout->addWidget(m_backRef, 2, 0);
00159 m_replaceLayout->addWidget(m_backRefItem, 2, 1);
00160 topLayout->addWidget(m_replaceGrp);
00161
00162 m_optionGrp = new TQGroupBox(0, Qt::Vertical, i18n("Options"), page);
00163 m_optionGrp->layout()->setSpacing(KDialog::spacingHint());
00164
00165 optionsLayout = new TQGridLayout(m_optionGrp->layout());
00166 optionsLayout->setSpacing( KDialog::spacingHint() );
00167
00168
00169 m_caseSensitive = new TQCheckBox(i18n("C&ase sensitive"), m_optionGrp);
00170 m_wholeWordsOnly = new TQCheckBox(i18n("&Whole words only"), m_optionGrp);
00171 m_fromCursor = new TQCheckBox(i18n("From c&ursor"), m_optionGrp);
00172 m_findBackwards = new TQCheckBox(i18n("Find &backwards"), m_optionGrp);
00173 m_selectedText = new TQCheckBox(i18n("&Selected text"), m_optionGrp);
00174 setHasSelection( hasSelection );
00175
00176
00177 m_selectedText->setChecked( hasSelection );
00178 slotSelectedTextToggled( hasSelection );
00179
00180 m_promptOnReplace = new TQCheckBox(i18n("&Prompt on replace"), m_optionGrp);
00181 m_promptOnReplace->setChecked( true );
00182
00183 optionsLayout->addWidget(m_caseSensitive, 0, 0);
00184 optionsLayout->addWidget(m_wholeWordsOnly, 1, 0);
00185 optionsLayout->addWidget(m_fromCursor, 2, 0);
00186 optionsLayout->addWidget(m_findBackwards, 0, 1);
00187 optionsLayout->addWidget(m_selectedText, 1, 1);
00188 optionsLayout->addWidget(m_promptOnReplace, 2, 1);
00189 topLayout->addWidget(m_optionGrp);
00190
00191
00192 m_patterns = 0L;
00193 m_placeholders = 0L;
00194
00195
00196 connect(m_selectedText, TQT_SIGNAL(toggled(bool)), this, TQT_SLOT(slotSelectedTextToggled(bool)));
00197 connect(m_regExp, TQT_SIGNAL(toggled(bool)), this, TQT_SLOT(slotRegexCheckBoxToggled(bool)));
00198 connect(m_backRef, TQT_SIGNAL(toggled(bool)), this, TQT_SLOT(slotPlaceholdersCheckBoxToggled(bool)));
00199 connect(m_regExpItem, TQT_SIGNAL(clicked()), this, TQT_SLOT(showPatterns()));
00200 connect(m_backRefItem, TQT_SIGNAL(clicked()), this, TQT_SLOT(showPlaceholders()));
00201
00202 connect(m_find, TQT_SIGNAL(textChanged ( const TQString & )),this, TQT_SLOT(textSearchChanged( const TQString & )));
00203
00204
00205 setTabOrder(m_find, m_regExp);
00206 setTabOrder(m_regExp, m_regExpItem);
00207 setTabOrder(m_regExpItem, m_replace);
00208 setTabOrder(m_replace, m_backRef);
00209 setTabOrder(m_backRef, m_backRefItem);
00210 setTabOrder(m_backRefItem, m_caseSensitive);
00211 setTabOrder(m_caseSensitive, m_wholeWordsOnly);
00212 setTabOrder(m_wholeWordsOnly, m_fromCursor);
00213 setTabOrder(m_fromCursor, m_findBackwards);
00214 setTabOrder(m_findBackwards, m_selectedText);
00215 setTabOrder(m_selectedText, m_promptOnReplace);
00216
00217
00218 m_findLabel->setBuddy(m_find);
00219 m_replaceLabel->setBuddy(m_replace);
00220
00221 if (!forReplace)
00222 {
00223 m_promptOnReplace->hide();
00224 m_replaceGrp->hide();
00225 }
00226
00227 d->findStrings = findStrings;
00228 m_find->setFocus();
00229 enableButtonOK( !pattern().isEmpty() );
00230 if (forReplace)
00231 {
00232 setButtonOK(KGuiItem( i18n("&Replace"), TQString::null,
00233 i18n("Start replace"),
00234 i18n("<qt>If you press the <b>Replace</b> button, the text you entered "
00235 "above is searched for within the document and any occurrence is "
00236 "replaced with the replacement text.</qt>")));
00237 }
00238 else
00239 {
00240 setButtonOK(KGuiItem( i18n("&Find"), "edit-find",
00241 i18n("Start searching"),
00242 i18n("<qt>If you press the <b>Find</b> button, the text you entered "
00243 "above is searched for within the document.</qt>")));
00244 }
00245
00246
00247 TQWhatsThis::add ( m_find, i18n(
00248 "Enter a pattern to search for, or select a previous pattern from "
00249 "the list.") );
00250 TQWhatsThis::add ( m_regExp, i18n(
00251 "If enabled, search for a regular expression.") );
00252 TQWhatsThis::add ( m_regExpItem, i18n(
00253 "Click here to edit your regular expression using a graphical editor.") );
00254 TQWhatsThis::add ( m_replace, i18n(
00255 "Enter a replacement string, or select a previous one from the list.") );
00256 TQWhatsThis::add( m_backRef, i18n(
00257 "<qt>When regular expressions are enabled, you can select part of the searched text by "
00258 "enclosing it within parenthesis. Placeholders allow you to insert such text in the "
00259 "replacement string, similar to how backreferences are used in sed. When enabled, "
00260 "any occurrence of <code><b>\\N</b></code> (where <code><b>N</b></code> "
00261 "is a integer number, e.g. \\1, \\2, ...), will be replaced with "
00262 "the corresponding capture (\"parenthesized substring\") from the "
00263 "pattern.<p>To include a literal <code><b>\\N</b></code> in your "
00264 "replacement, put an extra backslash in front of it, like "
00265 "<code><b>\\\\N</b></code>.</qt>") );
00266 TQWhatsThis::add ( m_backRefItem, i18n(
00267 "Click for a menu of available captures.") );
00268 TQWhatsThis::add ( m_wholeWordsOnly, i18n(
00269 "Require word boundaries in both ends of a match to succeed.") );
00270 TQWhatsThis::add ( m_fromCursor, i18n(
00271 "Start searching at the current cursor location rather than at the top.") );
00272 TQWhatsThis::add ( m_selectedText, i18n(
00273 "Only search within the current selection.") );
00274 TQWhatsThis::add ( m_caseSensitive, i18n(
00275 "Perform a case sensitive search: entering the pattern "
00276 "'Joe' will not match 'joe' or 'JOE', only 'Joe'.") );
00277 TQWhatsThis::add ( m_findBackwards, i18n(
00278 "Search backwards.") );
00279 TQWhatsThis::add ( m_promptOnReplace, i18n(
00280 "Ask before replacing each match found.") );
00281 }
00282
00283 void KFindDialog::textSearchChanged(const TQString & text)
00284 {
00285 enableButtonOK( !text.isEmpty() );
00286 }
00287
00288 void KFindDialog::slotRegexCheckBoxToggled(bool checked)
00289 {
00290 m_regExpItem->setEnabled(checked);
00291 m_backRef->setEnabled(checked);
00292 m_backRefItem->setEnabled(checked && m_backRef->isChecked());
00293 }
00294
00295 void KFindDialog::slotPlaceholdersCheckBoxToggled(bool checked)
00296 {
00297 m_backRefItem->setEnabled(checked && m_regExp->isChecked());
00298 }
00299
00300 void KFindDialog::showEvent( TQShowEvent *e )
00301 {
00302 if ( !d->m_initialShowDone )
00303 {
00304 d->m_initialShowDone = true;
00305 kdDebug() << "showEvent\n";
00306 if (!d->findStrings.isEmpty())
00307 setFindHistory(d->findStrings);
00308 d->findStrings = TQStringList();
00309 if (!d->pattern.isEmpty()) {
00310 m_find->lineEdit()->setText( d->pattern );
00311 m_find->lineEdit()->selectAll();
00312 d->pattern = TQString::null;
00313 }
00314 }
00315 KDialogBase::showEvent(e);
00316 }
00317
00318 long KFindDialog::options() const
00319 {
00320 long options = 0;
00321
00322 if (m_caseSensitive->isChecked())
00323 options |= CaseSensitive;
00324 if (m_wholeWordsOnly->isChecked())
00325 options |= WholeWordsOnly;
00326 if (m_fromCursor->isChecked())
00327 options |= FromCursor;
00328 if (m_findBackwards->isChecked())
00329 options |= FindBackwards;
00330 if (m_selectedText->isChecked())
00331 options |= SelectedText;
00332 if (m_regExp->isChecked())
00333 options |= RegularExpression;
00334 return options;
00335 }
00336
00337 TQString KFindDialog::pattern() const
00338 {
00339 return m_find->currentText();
00340 }
00341
00342 void KFindDialog::setPattern (const TQString &pattern)
00343 {
00344 m_find->lineEdit()->setText( pattern );
00345 m_find->lineEdit()->selectAll();
00346 d->pattern = pattern;
00347 kdDebug() << "setPattern " << pattern<<endl;
00348 }
00349
00350 void KFindDialog::setFindHistory(const TQStringList &strings)
00351 {
00352 if (strings.count() > 0)
00353 {
00354 m_find->setHistoryItems(strings, true);
00355 m_find->lineEdit()->setText( strings.first() );
00356 m_find->lineEdit()->selectAll();
00357 }
00358 else
00359 m_find->clearHistory();
00360 }
00361
00362 void KFindDialog::setHasSelection(bool hasSelection)
00363 {
00364 if (hasSelection) d->m_enabled |= SelectedText;
00365 else d->m_enabled &= ~SelectedText;
00366 m_selectedText->setEnabled( hasSelection );
00367 if ( !hasSelection )
00368 {
00369 m_selectedText->setChecked( false );
00370 slotSelectedTextToggled( hasSelection );
00371 }
00372 }
00373
00374 void KFindDialog::slotSelectedTextToggled(bool selec)
00375 {
00376
00377 m_fromCursor->setEnabled( !selec && (d->m_enabled & FromCursor) );
00378 if ( selec )
00379 m_fromCursor->setChecked( false );
00380 }
00381
00382 void KFindDialog::setHasCursor(bool hasCursor)
00383 {
00384 if (hasCursor) d->m_enabled |= FromCursor;
00385 else d->m_enabled &= ~FromCursor;
00386 m_fromCursor->setEnabled( hasCursor );
00387 m_fromCursor->setChecked( hasCursor && (options() & FromCursor) );
00388 }
00389
00390 void KFindDialog::setSupportsBackwardsFind( bool supports )
00391 {
00392
00393 if (supports) d->m_enabled |= FindBackwards;
00394 else d->m_enabled &= ~FindBackwards;
00395 m_findBackwards->setEnabled( supports );
00396 m_findBackwards->setChecked( supports && (options() & FindBackwards) );
00397 }
00398
00399 void KFindDialog::setSupportsCaseSensitiveFind( bool supports )
00400 {
00401
00402 if (supports) d->m_enabled |= CaseSensitive;
00403 else d->m_enabled &= ~CaseSensitive;
00404 m_caseSensitive->setEnabled( supports );
00405 m_caseSensitive->setChecked( supports && (options() & CaseSensitive) );
00406 }
00407
00408 void KFindDialog::setSupportsWholeWordsFind( bool supports )
00409 {
00410
00411 if (supports) d->m_enabled |= WholeWordsOnly;
00412 else d->m_enabled &= ~WholeWordsOnly;
00413 m_wholeWordsOnly->setEnabled( supports );
00414 m_wholeWordsOnly->setChecked( supports && (options() & WholeWordsOnly) );
00415 }
00416
00417 void KFindDialog::setSupportsRegularExpressionFind( bool supports )
00418 {
00419
00420 if (supports) d->m_enabled |= RegularExpression;
00421 else d->m_enabled &= ~RegularExpression;
00422 m_regExp->setEnabled( supports );
00423 m_regExp->setChecked( supports && (options() & RegularExpression) );
00424 }
00425
00426 void KFindDialog::setOptions(long options)
00427 {
00428 m_caseSensitive->setChecked((d->m_enabled & CaseSensitive) && (options & CaseSensitive));
00429 m_wholeWordsOnly->setChecked((d->m_enabled & WholeWordsOnly) && (options & WholeWordsOnly));
00430 m_fromCursor->setChecked((d->m_enabled & FromCursor) && (options & FromCursor));
00431 m_findBackwards->setChecked((d->m_enabled & FindBackwards) && (options & FindBackwards));
00432 m_selectedText->setChecked((d->m_enabled & SelectedText) && (options & SelectedText));
00433 m_regExp->setChecked((d->m_enabled & RegularExpression) && (options & RegularExpression));
00434 }
00435
00436
00437
00438 void KFindDialog::showPatterns()
00439 {
00440 if ( !d->m_regexpDialogQueryDone )
00441 {
00442 d->m_regexpDialog = KParts::ComponentFactory::createInstanceFromQuery<TQDialog>( "KRegExpEditor/KRegExpEditor", TQString(), TQT_TQOBJECT(this) );
00443 d->m_regexpDialogQueryDone = true;
00444 }
00445
00446 if ( d->m_regexpDialog )
00447 {
00448 KRegExpEditorInterface *iface = tqt_dynamic_cast<KRegExpEditorInterface *>( d->m_regexpDialog );
00449 assert( iface );
00450
00451 iface->setRegExp( pattern() );
00452 if ( d->m_regexpDialog->exec() == TQDialog::Accepted )
00453 setPattern( iface->regExp() );
00454 }
00455 else
00456 {
00457 typedef struct
00458 {
00459 const char *description;
00460 const char *regExp;
00461 int cursorAdjustment;
00462 } term;
00463 static const term items[] =
00464 {
00465 { I18N_NOOP("Any Character"), ".", 0 },
00466 { I18N_NOOP("Start of Line"), "^", 0 },
00467 { I18N_NOOP("End of Line"), "$", 0 },
00468 { I18N_NOOP("Set of Characters"), "[]", -1 },
00469 { I18N_NOOP("Repeats, Zero or More Times"), "*", 0 },
00470 { I18N_NOOP("Repeats, One or More Times"), "+", 0 },
00471 { I18N_NOOP("Optional"), "?", 0 },
00472 { I18N_NOOP("Escape"), "\\", 0 },
00473 { I18N_NOOP("TAB"), "\\t", 0 },
00474 { I18N_NOOP("Newline"), "\\n", 0 },
00475 { I18N_NOOP("Carriage Return"), "\\r", 0 },
00476 { I18N_NOOP("White Space"), "\\s", 0 },
00477 { I18N_NOOP("Digit"), "\\d", 0 },
00478 };
00479 int i;
00480
00481
00482 if (!m_patterns)
00483 {
00484 m_patterns = new TQPopupMenu(this);
00485 for (i = 0; (unsigned)i < sizeof(items) / sizeof(items[0]); i++)
00486 {
00487 m_patterns->insertItem(i18n(items[i].description), i, i);
00488 }
00489 }
00490
00491
00492 i = m_patterns->exec(m_regExpItem->mapToGlobal(m_regExpItem->rect().bottomLeft()));
00493 if (i != -1)
00494 {
00495 TQLineEdit *editor = m_find->lineEdit();
00496
00497 editor->insert(items[i].regExp);
00498 editor->setCursorPosition(editor->cursorPosition() + items[i].cursorAdjustment);
00499 }
00500 }
00501 }
00502
00503
00504
00505 void KFindDialog::showPlaceholders()
00506 {
00507
00508 if (!m_placeholders)
00509 {
00510 m_placeholders = new TQPopupMenu(this);
00511 connect( m_placeholders, TQT_SIGNAL(aboutToShow()), this, TQT_SLOT(slotPlaceholdersAboutToShow()) );
00512 }
00513
00514
00515 int i = m_placeholders->exec(m_backRefItem->mapToGlobal(m_backRefItem->rect().bottomLeft()));
00516 if (i != -1)
00517 {
00518 TQLineEdit *editor = m_replace->lineEdit();
00519 editor->insert( TQString("\\%1").arg( i ) );
00520 }
00521 }
00522
00523 void KFindDialog::slotPlaceholdersAboutToShow()
00524 {
00525 m_placeholders->clear();
00526 m_placeholders->insertItem( i18n("Complete Match"), 0 );
00527
00528 TQRegExp r( pattern() );
00529 uint n = r.numCaptures();
00530 for ( uint i=0; i < n; i++ )
00531 m_placeholders->insertItem( i18n("Captured Text (%1)").arg( i+1 ), i+1 );
00532 }
00533
00534 void KFindDialog::slotOk()
00535 {
00536
00537 if (pattern().isEmpty())
00538 {
00539 KMessageBox::error(this, i18n("You must enter some text to search for."));
00540 return;
00541 }
00542
00543 if (m_regExp->isChecked())
00544 {
00545
00546 TQRegExp regExp(pattern());
00547
00548 if (!regExp.isValid())
00549 {
00550 KMessageBox::error(this, i18n("Invalid regular expression."));
00551 return;
00552 }
00553 }
00554 m_find->addToHistory(pattern());
00555 emit okClicked();
00556 if ( testWFlags( WShowModal ) )
00557 accept();
00558 }
00559
00560 #include "kfinddialog.moc"