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"