diff options
Diffstat (limited to 'kutils/kfinddialog.cpp')
-rw-r--r-- | kutils/kfinddialog.cpp | 544 |
1 files changed, 544 insertions, 0 deletions
diff --git a/kutils/kfinddialog.cpp b/kutils/kfinddialog.cpp new file mode 100644 index 000000000..7a18111a3 --- /dev/null +++ b/kutils/kfinddialog.cpp @@ -0,0 +1,544 @@ +/* + Copyright (C) 2001, S.R.Haque <srhaque@iee.org>. + Copyright (C) 2002, David Faure <david@mandrakesoft.com> + This file is part of the KDE project + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2, as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kfinddialog.h" +#include <qcheckbox.h> +#include <qcursor.h> +#include <qgroupbox.h> +#include <qlabel.h> +#include <qlayout.h> +#include <qpopupmenu.h> +#include <qpushbutton.h> +#include <qregexp.h> +#include <kcombobox.h> +#include <kdebug.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <assert.h> +#include <qwhatsthis.h> + +#include <kregexpeditorinterface.h> +#include <kparts/componentfactory.h> + +class KFindDialog::KFindDialogPrivate +{ +public: + KFindDialogPrivate() : m_regexpDialog(0), + m_regexpDialogQueryDone(false), + m_enabled(WholeWordsOnly | FromCursor | SelectedText | CaseSensitive | FindBackwards | RegularExpression), m_initialShowDone(false) {} + QDialog* m_regexpDialog; + bool m_regexpDialogQueryDone; + long m_enabled; // uses Options to define which search options are enabled + bool m_initialShowDone; + QStringList findStrings; + QString pattern; +}; + +KFindDialog::KFindDialog(QWidget *parent, const char *name, long options, const QStringList &findStrings, bool hasSelection) : + KDialogBase(parent, name, true, i18n("Find Text"), Ok | Cancel, Ok), + m_findExtension (0), + m_replaceExtension (0) +{ + d = new KFindDialogPrivate; + init(false, findStrings, hasSelection); + setOptions(options); + setButtonCancel( KStdGuiItem::close() ); +} + +KFindDialog::KFindDialog(bool modal, QWidget *parent, const char *name, long options, const QStringList &findStrings, bool hasSelection) : + KDialogBase(parent, name, modal, i18n("Find Text"), Ok | Cancel, Ok), + m_findExtension (0), + m_replaceExtension (0) +{ + d = new KFindDialogPrivate; + init(false, findStrings, hasSelection); + setOptions(options); + setButtonCancel( KStdGuiItem::close() ); +} + +KFindDialog::KFindDialog(QWidget *parent, const char *name, bool /*forReplace*/) : + KDialogBase(parent, name, true, i18n("Replace Text"), Ok | Cancel, Ok), + m_findExtension (0), + m_replaceExtension (0) +{ + d = new KFindDialogPrivate; + setButtonCancel( KStdGuiItem::close() ); +} + +KFindDialog::~KFindDialog() +{ + delete d; +} + +QWidget *KFindDialog::findExtension() +{ + if (!m_findExtension) + { + m_findExtension = new QWidget(m_findGrp); + m_findLayout->addMultiCellWidget(m_findExtension, 3, 3, 0, 1); + } + + return m_findExtension; +} + +QStringList KFindDialog::findHistory() const +{ + return m_find->historyItems(); +} + +void KFindDialog::init(bool forReplace, const QStringList &findStrings, bool hasSelection) +{ + QVBoxLayout *topLayout; + QGridLayout *optionsLayout; + + // Create common parts of dialog. + QWidget *page = new QWidget(this); + setMainWidget(page); + + topLayout = new QVBoxLayout(page); + topLayout->setSpacing( KDialog::spacingHint() ); + topLayout->setMargin( 0 ); + + m_findGrp = new QGroupBox(0, Qt::Vertical, i18n("Find"), page); + m_findGrp->layout()->setSpacing( KDialog::spacingHint() ); + // m_findGrp->layout()->setMargin( KDialog::marginHint() ); + m_findLayout = new QGridLayout(m_findGrp->layout()); + m_findLayout->setSpacing( KDialog::spacingHint() ); + // m_findLayout->setMargin( KDialog::marginHint() ); + + m_findLabel = new QLabel(i18n("&Text to find:"), m_findGrp); + m_find = new KHistoryCombo(true, m_findGrp); + m_find->setMaxCount(10); + m_find->setDuplicatesEnabled(false); + m_regExp = new QCheckBox(i18n("Regular e&xpression"), m_findGrp); + m_regExpItem = new QPushButton(i18n("&Edit..."), m_findGrp); + m_regExpItem->setEnabled(false); + + m_findLayout->addWidget(m_findLabel, 0, 0); + m_findLayout->addMultiCellWidget(m_find, 1, 1, 0, 1); + m_findLayout->addWidget(m_regExp, 2, 0); + m_findLayout->addWidget(m_regExpItem, 2, 1); + topLayout->addWidget(m_findGrp); + + m_replaceGrp = new QGroupBox(0, Qt::Vertical, i18n("Replace With"), page); + m_replaceGrp->layout()->setSpacing( KDialog::spacingHint() ); + // m_replaceGrp->layout()->setMargin( KDialog::marginHint() ); + m_replaceLayout = new QGridLayout(m_replaceGrp->layout()); + m_replaceLayout->setSpacing( KDialog::spacingHint() ); +// m_replaceLayout->setMargin( KDialog::marginHint() ); + + m_replaceLabel = new QLabel(i18n("Replace&ment text:"), m_replaceGrp); + m_replace = new KHistoryCombo(true, m_replaceGrp); + m_replace->setMaxCount(10); + m_replace->setDuplicatesEnabled(false); + m_backRef = new QCheckBox(i18n("Use p&laceholders"), m_replaceGrp); + m_backRefItem = new QPushButton(i18n("Insert Place&holder"), m_replaceGrp); + m_backRefItem->setEnabled(false); + + m_replaceLayout->addWidget(m_replaceLabel, 0, 0); + m_replaceLayout->addMultiCellWidget(m_replace, 1, 1, 0, 1); + m_replaceLayout->addWidget(m_backRef, 2, 0); + m_replaceLayout->addWidget(m_backRefItem, 2, 1); + topLayout->addWidget(m_replaceGrp); + + m_optionGrp = new QGroupBox(0, Qt::Vertical, i18n("Options"), page); + m_optionGrp->layout()->setSpacing(KDialog::spacingHint()); + // m_optionGrp->layout()->setMargin(KDialog::marginHint()); + optionsLayout = new QGridLayout(m_optionGrp->layout()); + optionsLayout->setSpacing( KDialog::spacingHint() ); + // optionsLayout->setMargin( KDialog::marginHint() ); + + m_caseSensitive = new QCheckBox(i18n("C&ase sensitive"), m_optionGrp); + m_wholeWordsOnly = new QCheckBox(i18n("&Whole words only"), m_optionGrp); + m_fromCursor = new QCheckBox(i18n("From c&ursor"), m_optionGrp); + m_findBackwards = new QCheckBox(i18n("Find &backwards"), m_optionGrp); + m_selectedText = new QCheckBox(i18n("&Selected text"), m_optionGrp); + setHasSelection( hasSelection ); + // If we have a selection, we make 'find in selection' default + // and if we don't, then the option has to be unchecked, obviously. + m_selectedText->setChecked( hasSelection ); + slotSelectedTextToggled( hasSelection ); + + m_promptOnReplace = new QCheckBox(i18n("&Prompt on replace"), m_optionGrp); + m_promptOnReplace->setChecked( true ); + + optionsLayout->addWidget(m_caseSensitive, 0, 0); + optionsLayout->addWidget(m_wholeWordsOnly, 1, 0); + optionsLayout->addWidget(m_fromCursor, 2, 0); + optionsLayout->addWidget(m_findBackwards, 0, 1); + optionsLayout->addWidget(m_selectedText, 1, 1); + optionsLayout->addWidget(m_promptOnReplace, 2, 1); + topLayout->addWidget(m_optionGrp); + + // We delay creation of these until needed. + m_patterns = 0L; + m_placeholders = 0L; + + // signals and slots connections + connect(m_selectedText, SIGNAL(toggled(bool)), this, SLOT(slotSelectedTextToggled(bool))); + connect(m_regExp, SIGNAL(toggled(bool)), m_regExpItem, SLOT(setEnabled(bool))); + connect(m_backRef, SIGNAL(toggled(bool)), m_backRefItem, SLOT(setEnabled(bool))); + connect(m_regExpItem, SIGNAL(clicked()), this, SLOT(showPatterns())); + connect(m_backRefItem, SIGNAL(clicked()), this, SLOT(showPlaceholders())); + + connect(m_find, SIGNAL(textChanged ( const QString & )),this, SLOT(textSearchChanged( const QString & ))); + + // tab order + setTabOrder(m_find, m_regExp); + setTabOrder(m_regExp, m_regExpItem); + setTabOrder(m_regExpItem, m_replace); + setTabOrder(m_replace, m_backRef); + setTabOrder(m_backRef, m_backRefItem); + setTabOrder(m_backRefItem, m_caseSensitive); + setTabOrder(m_caseSensitive, m_wholeWordsOnly); + setTabOrder(m_wholeWordsOnly, m_fromCursor); + setTabOrder(m_fromCursor, m_findBackwards); + setTabOrder(m_findBackwards, m_selectedText); + setTabOrder(m_selectedText, m_promptOnReplace); + + // buddies + m_findLabel->setBuddy(m_find); + m_replaceLabel->setBuddy(m_replace); + + if (!forReplace) + { + m_promptOnReplace->hide(); + m_replaceGrp->hide(); + } + + d->findStrings = findStrings; + m_find->setFocus(); + enableButtonOK( !pattern().isEmpty() ); + if (forReplace) + { + setButtonOK(KGuiItem( i18n("&Replace"), QString::null, + i18n("Start replace"), + i18n("<qt>If you press the <b>Replace</b> button, the text you entered " + "above is searched for within the document and any occurrence is " + "replaced with the replacement text.</qt>"))); + } + else + { + setButtonOK(KGuiItem( i18n("&Find"), "find", + i18n("Start searching"), + i18n("<qt>If you press the <b>Find</b> button, the text you entered " + "above is searched for within the document.</qt>"))); + } + + // QWhatsthis texts + QWhatsThis::add ( m_find, i18n( + "Enter a pattern to search for, or select a previous pattern from " + "the list.") ); + QWhatsThis::add ( m_regExp, i18n( + "If enabled, search for a regular expression.") ); + QWhatsThis::add ( m_regExpItem, i18n( + "Click here to edit your regular expression using a graphical editor.") ); + QWhatsThis::add ( m_replace, i18n( + "Enter a replacement string, or select a previous one from the list.") ); + QWhatsThis::add( m_backRef, i18n( + "<qt>If enabled, any occurrence of <code><b>\\N</b></code>, where " + "<code><b>N</b></code> is a integer number, will be replaced with " + "the corresponding capture (\"parenthesized substring\") from the " + "pattern.<p>To include (a literal <code><b>\\N</b></code> in your " + "replacement, put an extra backslash in front of it, like " + "<code><b>\\\\N</b></code>.</qt>") ); + QWhatsThis::add ( m_backRefItem, i18n( + "Click for a menu of available captures.") ); + QWhatsThis::add ( m_wholeWordsOnly, i18n( + "Require word boundaries in both ends of a match to succeed.") ); + QWhatsThis::add ( m_fromCursor, i18n( + "Start searching at the current cursor location rather than at the top.") ); + QWhatsThis::add ( m_selectedText, i18n( + "Only search within the current selection.") ); + QWhatsThis::add ( m_caseSensitive, i18n( + "Perform a case sensitive search: entering the pattern " + "'Joe' will not match 'joe' or 'JOE', only 'Joe'.") ); + QWhatsThis::add ( m_findBackwards, i18n( + "Search backwards.") ); + QWhatsThis::add ( m_promptOnReplace, i18n( + "Ask before replacing each match found.") ); +} + +void KFindDialog::textSearchChanged( const QString & text) +{ + enableButtonOK( !text.isEmpty() ); +} + +void KFindDialog::showEvent( QShowEvent *e ) +{ + if ( !d->m_initialShowDone ) + { + d->m_initialShowDone = true; // only once + kdDebug() << "showEvent\n"; + if (!d->findStrings.isEmpty()) + setFindHistory(d->findStrings); + d->findStrings = QStringList(); + if (!d->pattern.isEmpty()) { + m_find->lineEdit()->setText( d->pattern ); + m_find->lineEdit()->selectAll(); + d->pattern = QString::null; + } + } + KDialogBase::showEvent(e); +} + +long KFindDialog::options() const +{ + long options = 0; + + if (m_caseSensitive->isChecked()) + options |= CaseSensitive; + if (m_wholeWordsOnly->isChecked()) + options |= WholeWordsOnly; + if (m_fromCursor->isChecked()) + options |= FromCursor; + if (m_findBackwards->isChecked()) + options |= FindBackwards; + if (m_selectedText->isChecked()) + options |= SelectedText; + if (m_regExp->isChecked()) + options |= RegularExpression; + return options; +} + +QString KFindDialog::pattern() const +{ + return m_find->currentText(); +} + +void KFindDialog::setPattern (const QString &pattern) +{ + m_find->lineEdit()->setText( pattern ); + m_find->lineEdit()->selectAll(); + d->pattern = pattern; + kdDebug() << "setPattern " << pattern<<endl; +} + +void KFindDialog::setFindHistory(const QStringList &strings) +{ + if (strings.count() > 0) + { + m_find->setHistoryItems(strings, true); + m_find->lineEdit()->setText( strings.first() ); + m_find->lineEdit()->selectAll(); + } + else + m_find->clearHistory(); +} + +void KFindDialog::setHasSelection(bool hasSelection) +{ + if (hasSelection) d->m_enabled |= SelectedText; + else d->m_enabled &= ~SelectedText; + m_selectedText->setEnabled( hasSelection ); + if ( !hasSelection ) + { + m_selectedText->setChecked( false ); + slotSelectedTextToggled( hasSelection ); + } +} + +void KFindDialog::slotSelectedTextToggled(bool selec) +{ + // From cursor doesn't make sense if we have a selection + m_fromCursor->setEnabled( !selec && (d->m_enabled & FromCursor) ); + if ( selec ) // uncheck if disabled + m_fromCursor->setChecked( false ); +} + +void KFindDialog::setHasCursor(bool hasCursor) +{ + if (hasCursor) d->m_enabled |= FromCursor; + else d->m_enabled &= ~FromCursor; + m_fromCursor->setEnabled( hasCursor ); + m_fromCursor->setChecked( hasCursor && (options() & FromCursor) ); +} + +void KFindDialog::setSupportsBackwardsFind( bool supports ) +{ + // ########## Shouldn't this hide the checkbox instead? + if (supports) d->m_enabled |= FindBackwards; + else d->m_enabled &= ~FindBackwards; + m_findBackwards->setEnabled( supports ); + m_findBackwards->setChecked( supports && (options() & FindBackwards) ); +} + +void KFindDialog::setSupportsCaseSensitiveFind( bool supports ) +{ + // ########## This should hide the checkbox instead + if (supports) d->m_enabled |= CaseSensitive; + else d->m_enabled &= ~CaseSensitive; + m_caseSensitive->setEnabled( supports ); + m_caseSensitive->setChecked( supports && (options() & CaseSensitive) ); +} + +void KFindDialog::setSupportsWholeWordsFind( bool supports ) +{ + // ########## This should hide the checkbox instead + if (supports) d->m_enabled |= WholeWordsOnly; + else d->m_enabled &= ~WholeWordsOnly; + m_wholeWordsOnly->setEnabled( supports ); + m_wholeWordsOnly->setChecked( supports && (options() & WholeWordsOnly) ); +} + +void KFindDialog::setSupportsRegularExpressionFind( bool supports ) +{ + // ########## This should hide the checkbox instead + if (supports) d->m_enabled |= RegularExpression; + else d->m_enabled &= ~RegularExpression; + m_regExp->setEnabled( supports ); + m_regExp->setChecked( supports && (options() & RegularExpression) ); +} + +void KFindDialog::setOptions(long options) +{ + m_caseSensitive->setChecked((d->m_enabled & CaseSensitive) && (options & CaseSensitive)); + m_wholeWordsOnly->setChecked((d->m_enabled & WholeWordsOnly) && (options & WholeWordsOnly)); + m_fromCursor->setChecked((d->m_enabled & FromCursor) && (options & FromCursor)); + m_findBackwards->setChecked((d->m_enabled & FindBackwards) && (options & FindBackwards)); + m_selectedText->setChecked((d->m_enabled & SelectedText) && (options & SelectedText)); + m_regExp->setChecked((d->m_enabled & RegularExpression) && (options & RegularExpression)); +} + +// Create a popup menu with a list of regular expression terms, to help the user +// compose a regular expression search pattern. +void KFindDialog::showPatterns() +{ + if ( !d->m_regexpDialogQueryDone ) + { + d->m_regexpDialog = KParts::ComponentFactory::createInstanceFromQuery<QDialog>( "KRegExpEditor/KRegExpEditor", QString::null, this ); + d->m_regexpDialogQueryDone = true; + } + + if ( d->m_regexpDialog ) + { + KRegExpEditorInterface *iface = static_cast<KRegExpEditorInterface *>( d->m_regexpDialog->qt_cast( "KRegExpEditorInterface" ) ); + assert( iface ); + + iface->setRegExp( pattern() ); + if ( d->m_regexpDialog->exec() == QDialog::Accepted ) + setPattern( iface->regExp() ); + } + else // No complete regexp-editor available, bring up the old popupmenu + { + typedef struct + { + const char *description; + const char *regExp; + int cursorAdjustment; + } term; + static const term items[] = + { + { I18N_NOOP("Any Character"), ".", 0 }, + { I18N_NOOP("Start of Line"), "^", 0 }, + { I18N_NOOP("End of Line"), "$", 0 }, + { I18N_NOOP("Set of Characters"), "[]", -1 }, + { I18N_NOOP("Repeats, Zero or More Times"), "*", 0 }, + { I18N_NOOP("Repeats, One or More Times"), "+", 0 }, + { I18N_NOOP("Optional"), "?", 0 }, + { I18N_NOOP("Escape"), "\\", 0 }, + { I18N_NOOP("TAB"), "\\t", 0 }, + { I18N_NOOP("Newline"), "\\n", 0 }, + { I18N_NOOP("Carriage Return"), "\\r", 0 }, + { I18N_NOOP("White Space"), "\\s", 0 }, + { I18N_NOOP("Digit"), "\\d", 0 }, + }; + int i; + + // Populate the popup menu. + if (!m_patterns) + { + m_patterns = new QPopupMenu(this); + for (i = 0; (unsigned)i < sizeof(items) / sizeof(items[0]); i++) + { + m_patterns->insertItem(i18n(items[i].description), i, i); + } + } + + // Insert the selection into the edit control. + i = m_patterns->exec(m_regExpItem->mapToGlobal(m_regExpItem->rect().bottomLeft())); + if (i != -1) + { + QLineEdit *editor = m_find->lineEdit(); + + editor->insert(items[i].regExp); + editor->setCursorPosition(editor->cursorPosition() + items[i].cursorAdjustment); + } + } +} + +// Create a popup menu with a list of backreference terms, to help the user +// compose a regular expression replacement pattern. +void KFindDialog::showPlaceholders() +{ + // Populate the popup menu. + if (!m_placeholders) + { + m_placeholders = new QPopupMenu(this); + connect( m_placeholders, SIGNAL(aboutToShow()), this, SLOT(slotPlaceholdersAboutToShow()) ); + } + + // Insert the selection into the edit control. + int i = m_placeholders->exec(m_backRefItem->mapToGlobal(m_backRefItem->rect().bottomLeft())); + if (i != -1) + { + QLineEdit *editor = m_replace->lineEdit(); + editor->insert( QString("\\%1").arg( i ) ); + } +} + +void KFindDialog::slotPlaceholdersAboutToShow() +{ + m_placeholders->clear(); + m_placeholders->insertItem( i18n("Complete Match"), 0 ); + + QRegExp r( pattern() ); + uint n = r.numCaptures(); + for ( uint i=0; i < n; i++ ) + m_placeholders->insertItem( i18n("Captured Text (%1)").arg( i+1 ), i+1 ); +} + +void KFindDialog::slotOk() +{ + // Nothing to find? + if (pattern().isEmpty()) + { + KMessageBox::error(this, i18n("You must enter some text to search for.")); + return; + } + + if (m_regExp->isChecked()) + { + // Check for a valid regular expression. + QRegExp regExp(pattern()); + + if (!regExp.isValid()) + { + KMessageBox::error(this, i18n("Invalid regular expression.")); + return; + } + } + m_find->addToHistory(pattern()); + emit okClicked(); + if ( testWFlags( WShowModal ) ) + accept(); +} +// kate: space-indent on; indent-width 4; replace-tabs on; +#include "kfinddialog.moc" |