From kfm-devel Thu Sep 25 07:29:24 2003 From: George Staikos Date: Thu, 25 Sep 2003 07:29:24 +0000 To: kfm-devel Subject: [PATCH] Textarea search, and spelling->KStdAction X-MARC-Message: https://marc.info/?l=kfm-devel&m=106447509110770 MIME-Version: 1 Content-Type: multipart/mixed; boundary="--Boundary-00=_Ulpc/hBWJ3Eio/p" --Boundary-00=_Ulpc/hBWJ3Eio/p Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Content-Disposition: inline Attached is a patch that does two things: 1) Converts the spell check menu item to a standard action. This way if it gets an accelerator in the future, it will "just work". 2) Most importantly, I implemented: http://ask.slashdot.org/article.pl?sid=03/09/24/201231&mode=thread&tid=126&tid=185&tid=95 It isn't *perfect* yet which is why I did not apply this patch. I would like some testing, and perhaps some help. This patch doesn't support regexps at all, and I think the "find" is a bit clunky still (there is no "Find Next" and trying to find again finds the same hit over again). In general it works though. Oh if someone has a slashdot account and cares, feel free to post a link to this patch to show them that 49 (at this point) Slashdotters can talk about a feature and form a committee in the same time that one person can implement it. Reminds me of a joke about lightbulbs... :) -- George Staikos KDE Developer http://www.kde.org/ Staikos Computing Services Inc. http://www.staikos.net/ --Boundary-00=_Ulpc/hBWJ3Eio/p Content-Type: text/x-diff; charset="us-ascii"; name="textarea-search.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="textarea-search.patch" Index: render_form.cpp =================================================================== RCS file: /home/kde/kdelibs/khtml/rendering/render_form.cpp,v retrieving revision 1.235 diff -u -3 -p -r1.235 render_form.cpp --- render_form.cpp 9 Sep 2003 16:31:55 -0000 1.235 +++ render_form.cpp 25 Sep 2003 07:17:12 -0000 @@ -23,13 +23,17 @@ * $Id: render_form.cpp,v 1.235 2003/09/09 16:31:55 pletourn Exp $ */ -#include -#include -#include #include #include -#include +#include +#include +#include #include +#include +#include +#include +#include +#include #include @@ -289,15 +293,22 @@ LineEditWidget::LineEditWidget(DOM::HTML : KLineEdit(parent), m_input(input), m_view(view), m_spell(0) { setMouseTracking(true); + KActionCollection *ac = new KActionCollection(this); + m_spellAction = KStdAction::spelling( this, SLOT( slotCheckSpelling() ), ac ); } LineEditWidget::~LineEditWidget() { delete m_spell; + m_spell = 0L; } void LineEditWidget::slotCheckSpelling() { + if ( text().isEmpty() ) { + return; + } + delete m_spell; m_spell = new KSpell( this, i18n( "Spell Checking" ), this, SLOT( slotSpellCheckReady( KSpell *) ), 0, true, true); @@ -346,29 +357,34 @@ void LineEditWidget::slotSpellCheckDone( QPopupMenu *LineEditWidget::createPopupMenu() { QPopupMenu *popup = KLineEdit::createPopupMenu(); - if ( !popup ) + + if ( !popup ) { return 0L; + } + connect( popup, SIGNAL( activated( int ) ), this, SLOT( extendedMenuActivated( int ) ) ); if (m_input->autoComplete()) { popup->insertSeparator(); int id = popup->insertItem( i18n("Clear &History"), ClearHistory ); - if (!completionObject()) - popup->setItemEnabled( id, false); + if (!completionObject()) { + popup->setItemEnabled( id, false ); + } } if (echoMode() == QLineEdit::Normal && !isReadOnly()) { popup->insertSeparator(); - int id = popup->insertItem( SmallIcon( "spellcheck" ), i18n( "Check Spelling" ), this, SLOT( slotCheckSpelling() ) ); - if( text().isEmpty() ) - popup->setItemEnabled( id, false ); + + m_spellAction->plug(popup); + m_spellAction->setEnabled( !text().isEmpty() ); } return popup; } + void LineEditWidget::extendedMenuActivated( int id) { switch ( id ) @@ -1248,7 +1264,7 @@ void RenderSelect::updateSelection() // ------------------------------------------------------------------------- TextAreaWidget::TextAreaWidget(int wrap, QWidget* parent) - : KTextEdit(parent) + : KTextEdit(parent), m_findDlg(0), m_repDlg(0) { setCheckSpellingEnabled( true ); @@ -1266,7 +1282,191 @@ TextAreaWidget::TextAreaWidget(int wrap, setTextFormat(QTextEdit::PlainText); setAutoMask(true); setMouseTracking(true); + + KActionCollection *ac = new KActionCollection(this); + m_findAction = KStdAction::find( this, SLOT( slotFind() ), ac ); + m_replaceAction = KStdAction::replace( this, SLOT( slotReplace() ), ac ); +} + + +TextAreaWidget::~TextAreaWidget() +{ + delete m_repDlg; + m_repDlg = 0L; + delete m_findDlg; + m_findDlg = 0L; +} + + +QPopupMenu *TextAreaWidget::createPopupMenu(const QPoint& pos) +{ + QPopupMenu *popup = KTextEdit::createPopupMenu(pos); + + if ( !popup ) { + return 0L; + } + + if (!isReadOnly()) { + popup->insertSeparator(); + + m_findAction->plug(popup); + m_findAction->setEnabled( !text().isEmpty() ); + + m_replaceAction->plug(popup); + m_replaceAction->setEnabled( !text().isEmpty() ); + } + + return popup; +} + + +void TextAreaWidget::slotFindNext() +{ + int index = 0, para = 0; + bool replace = m_repDlg && sender() == m_repDlg; + KFindDialog *dlg = 0L; + + if (replace) { + dlg = m_repDlg; + } else { + dlg = m_findDlg; + } + + if (!dlg) { + // Should really assert() + return; + } + + QString pattern = dlg->pattern(); + long options = dlg->options(); + + if (pattern.isEmpty()) { + dlg->hide(); + return; + } + + if (options & KFindDialog::FromCursor) { + getCursorPosition(¶, &index); + if (options & KFindDialog::FindBackwards) { + if (index == 0 && para == 0) { + dlg->hide(); + return; + } else { + moveCursor(MoveBackward, false); + } + } else { + if (para == paragraphs() - 1 && index == paragraphLength(paragraphs() - 1) - 1) { + dlg->hide(); + return; + } else { + moveCursor(MoveForward, false); + } + } + } else if (options & KFindDialog::FindBackwards) { + para = paragraphs() - 1; + index = paragraphLength(para) - 1; + } + + //kdDebug() << "Searching for " << pattern << " at " << para << " " + // << index << ", case: " + // << bool(options & KFindDialog::CaseSensitive) + // << ", whole words: " + // << bool(options & KFindDialog::WholeWordsOnly) + // << ", backwards: " + // << bool(options & KFindDialog::FindBackwards) << endl; + + bool rc = find(pattern, + options & KFindDialog::CaseSensitive, + options & KFindDialog::WholeWordsOnly, + !(options & KFindDialog::FindBackwards), + ¶, + &index); + + if (rc) { + setCursorPosition(para, index); + ensureCursorVisible(); + dlg->setOptions(options | KFindDialog::FromCursor); + dlg->hide(); + if (replace) { + bool prompt = m_repDlg->options() & KReplaceDialog::PromptOnReplace; + while (rc) { + int epara = 0, eindex = 0; + int forwardOffset = 0; + + getSelection(¶, &index, &epara, &eindex); + + QString replacement = m_repDlg->replacement(); + if (prompt) { + int rc = KMessageBox::questionYesNo(this, i18n("Replace '%1' with '%2'?").arg(pattern).arg(replacement)); + if (rc == KMessageBox::Yes) { + removeSelectedText(); + insertAt(replacement, para, index); + forwardOffset = replacement.length(); + } else { + forwardOffset = 1; + } + } else { + removeSelectedText(); + insertAt(replacement, para, index); + forwardOffset = replacement.length(); + } + + if (options & KFindDialog::FindBackwards) { + if (para == 0 && index == 0) { + break; + } + setCursorPosition(para, index); + for (int i = 0; i < pattern.length(); ++i) { + moveCursor(MoveBackward, false); + } + } else { + setCursorPosition(para, index + forwardOffset); + } + + rc = find(pattern, + options & KFindDialog::CaseSensitive, + options & KFindDialog::WholeWordsOnly, + !(options & KFindDialog::FindBackwards), + 0, 0); + } + } + } else { + dlg->hide(); + return; + } } + + +void TextAreaWidget::slotFind() +{ + if( text().isEmpty() ) // saves having to track the text changes + return; + + if ( m_findDlg ) { + KWin::setActiveWindow( m_findDlg->winId() ); + } else { + m_findDlg = new KFindDialog(false, this, "KHTML Text Area Find Dialog"); + connect( m_findDlg, SIGNAL(okClicked()), this, SLOT(slotFindNext()) ); + } + m_findDlg->show(); +} + + +void TextAreaWidget::slotReplace() +{ + if( text().isEmpty() ) // saves having to track the text changes + return; + + if ( m_repDlg ) { + KWin::setActiveWindow( m_repDlg->winId() ); + } else { + m_repDlg = new KReplaceDialog(this, "KHTMLText Area Replace Dialog", 0, + QStringList(), QStringList(), false); + connect( m_repDlg, SIGNAL(okClicked()), this, SLOT(slotFindNext()) ); + } + m_repDlg->show(); +} + bool TextAreaWidget::event( QEvent *e ) { Index: render_form.h =================================================================== RCS file: /home/kde/kdelibs/khtml/rendering/render_form.h,v retrieving revision 1.99 diff -u -3 -p -r1.99 render_form.h --- render_form.h 7 Sep 2003 20:52:28 -0000 1.99 +++ render_form.h 25 Sep 2003 07:17:13 -0000 @@ -47,6 +47,9 @@ class QListboxItem; class KHTMLPartBrowserExtension; class KSpell; +class KFindDialog; +class KReplaceDialog; +class KAction; namespace DOM { class HTMLFormElementImpl; @@ -260,6 +263,7 @@ private: DOM::HTMLInputElementImpl* m_input; KHTMLView* m_view; KSpell *m_spell; + KAction *m_spellAction; }; // ------------------------------------------------------------------------- @@ -397,15 +401,25 @@ protected slots: }; // ------------------------------------------------------------------------- - class TextAreaWidget : public KTextEdit { Q_OBJECT public: TextAreaWidget(int wrap, QWidget* parent); + virtual ~TextAreaWidget(); protected: virtual bool event (QEvent *e ); + virtual QPopupMenu *createPopupMenu(const QPoint& pos); +private slots: + void slotFind(); + void slotFindNext(); + void slotReplace(); +private: + KFindDialog *m_findDlg; + KReplaceDialog *m_repDlg; + KAction *m_findAction; + KAction *m_replaceAction; }; --Boundary-00=_Ulpc/hBWJ3Eio/p--