File indexing completed on 2024-05-12 16:40:01

0001 /* This file is part of the KDE project
0002    Copyright (C) 2004-2016 Jarosław Staniek <staniek@kde.org>
0003 
0004    This library is free software; you can redistribute it and/or
0005    modify it under the terms of the GNU Library General Public
0006    License as published by the Free Software Foundation; either
0007    version 2 of the License, or (at your option) any later version.
0008 
0009    This library is distributed in the hope that it will be useful,
0010    but WITHOUT ANY WARRANTY; without even the implied warranty of
0011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012    Library General Public License for more details.
0013 
0014    You should have received a copy of the GNU Library General Public License
0015    along with this library; see the file COPYING.LIB.  If not, write to
0016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0017  * Boston, MA 02110-1301, USA.
0018 */
0019 
0020 #include "kexifinddialog.h"
0021 #include <kexiutils/utils.h>
0022 
0023 #include <KStandardGuiItem>
0024 #include <KStandardAction>
0025 #include <KComboBox>
0026 #include <KConfigGroup>
0027 #include <KSharedConfig>
0028 #include <KLocalizedString>
0029 
0030 #include <QDebug>
0031 #include <QDialog>
0032 #include <QCheckBox>
0033 #include <QLayout>
0034 #include <QList>
0035 #include <QShortcut>
0036 #include <QPointer>
0037 #include <QAction>
0038 #include <QDesktopWidget>
0039 #include <QKeyEvent>
0040 
0041 #include <kexi_global.h>
0042 
0043 //! @internal
0044 class Q_DECL_HIDDEN KexiFindDialog::Private
0045 {
0046 public:
0047     Private() :
0048         confGroup(KSharedConfig::openConfig()->group("FindDialog"))
0049     {
0050     }
0051     ~Private() {
0052         qDeleteAll(shortcuts);
0053         shortcuts.clear();
0054     }
0055     //! Connects action \a action with appropriate signal \a member
0056     //! and optionally adds shortcut that will receive shortcut for \a action
0057     //! at global scope of the dialog \a parent.
0058     void setActionAndShortcut(QAction *action, QWidget* parent, const char* member) {
0059 //! @todo KEXI3 not tested: setActionAndShortcut::setActionAndShortcut()
0060         if (!action)
0061             return;
0062         QObject::connect(parent, member, action, SLOT(trigger()));
0063         if (action->shortcut().isEmpty())
0064             return;
0065         // we want to handle dialog-wide shortcut as well
0066         if (!action->shortcut().isEmpty()) {
0067             QShortcut *shortcut = new QShortcut(action->shortcut(), parent, member);
0068             shortcuts.append(shortcut);
0069         }
0070     }
0071 
0072     QStringList lookInColumnNames;
0073     QStringList lookInColumnCaptions;
0074     QString objectName; //!< for caption
0075     QPointer<QAction> findnextAction;
0076     QPointer<QAction> findprevAction;
0077     QPointer<QAction> replaceAction;
0078     QPointer<QAction> replaceallAction;
0079     QList<QShortcut*> shortcuts;
0080     KConfigGroup confGroup;
0081     bool replaceMode;
0082 };
0083 
0084 //------------------------------------------
0085 
0086 KexiFindDialog::KexiFindDialog(QWidget* parent)
0087         : QDialog(parent,
0088                   Qt::Dialog | Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::Tool
0089                   | Qt::WindowCloseButtonHint)
0090         , d(new Private())
0091 {
0092     setObjectName("KexiFindDialog");
0093     setupUi(this);
0094     m_search->setCurrentIndex(
0095         (int)KexiSearchAndReplaceViewInterface::Options::SearchDown);
0096     layout()->setMargin(KexiUtils::marginHint());
0097     layout()->setSpacing(KexiUtils::spacingHint());
0098     QAction *a = KStandardAction::findNext(0, 0, 0);
0099     m_btnFind->setText(a->text());
0100     m_btnFind->setIcon(a->icon());
0101     delete a;
0102     connect(m_btnFind, SIGNAL(clicked()), this, SIGNAL(findNext()));
0103     connect(m_btnReplace, SIGNAL(clicked()), this, SIGNAL(replaceNext()));
0104     connect(m_btnReplaceAll, SIGNAL(clicked()), this, SIGNAL(replaceAll()));
0105     connect(m_textToFind, SIGNAL(activated(int)), this, SLOT(addToFindHistory()));
0106     connect(m_btnFind, SIGNAL(clicked()), this, SLOT(addToFindHistory()));
0107     connect(m_textToReplace, SIGNAL(activated(int)), this, SLOT(addToReplaceHistory()));
0108     connect(m_btnReplace, SIGNAL(clicked()), this, SLOT(addToReplaceHistory()));
0109     connect(m_btnReplaceAll, SIGNAL(clicked()), this, SLOT(addToReplaceHistory()));
0110     // clear message after the text is changed
0111     connect(m_textToFind, SIGNAL(editTextChanged(QString)), this, SLOT(updateMessage(QString)));
0112     connect(m_textToReplace, SIGNAL(editTextChanged(QString)), this, SLOT(updateMessage(QString)));
0113 
0114     d->replaceMode = true; //to force updating by setReplaceMode()
0115     setReplaceMode(false);
0116 
0117     setLookInColumnList(QStringList(), QStringList());
0118 
0119     QRect g = d->confGroup.readEntry("Geometry", QRect());
0120     updateGeometry();
0121 #ifndef Q_OS_WIN // not needed for Windows: this kind of dialog is centered by default
0122     if (g.isNull()) {
0123         g = geometry();
0124         g.moveCenter((parentWidget() ? parentWidget()->geometry()
0125                                      : QApplication::desktop()->availableGeometry(this)).center());
0126     }
0127 #endif
0128     if (!g.isNull()) {
0129         setGeometry(g);
0130     }
0131 }
0132 
0133 KexiFindDialog::~KexiFindDialog()
0134 {
0135     d->confGroup.writeEntry("Geometry", geometry());
0136     delete d;
0137 }
0138 
0139 void KexiFindDialog::setActions(QAction *findnext, QAction *findprev,
0140                                 QAction *replace, QAction *replaceall)
0141 {
0142     d->findnextAction = findnext;
0143     d->findprevAction = findprev;
0144     d->replaceAction = replace;
0145     d->replaceallAction = replaceall;
0146     qDeleteAll(d->shortcuts);
0147     d->setActionAndShortcut(d->findnextAction, this, SIGNAL(findNext()));
0148     d->setActionAndShortcut(d->findprevAction, this, SIGNAL(findPrevious()));
0149     d->setActionAndShortcut(d->replaceAction, this, SIGNAL(replaceNext()));
0150     d->setActionAndShortcut(d->replaceallAction, this, SIGNAL(replaceAll()));
0151 }
0152 
0153 QStringList KexiFindDialog::lookInColumnNames() const
0154 {
0155     return d->lookInColumnNames;
0156 }
0157 
0158 QStringList KexiFindDialog::lookInColumnCaptions() const
0159 {
0160     return d->lookInColumnCaptions;
0161 }
0162 
0163 QString KexiFindDialog::currentLookInColumnName() const
0164 {
0165     int index = m_lookIn->currentIndex();
0166     if (index <= 0 || index >= (int)d->lookInColumnNames.count())
0167         return QString();
0168     else if (index == 1)
0169         return "(field)";
0170     return d->lookInColumnNames[index - 1/*"(All fields)"*/ - 1/*"(Current field)"*/];
0171 }
0172 
0173 QVariant KexiFindDialog::valueToFind() const
0174 {
0175     return m_textToFind->currentText();
0176 }
0177 
0178 QVariant KexiFindDialog::valueToReplaceWith() const
0179 {
0180     return m_textToReplace->currentText();
0181 }
0182 
0183 void KexiFindDialog::setLookInColumnList(const QStringList& columnNames,
0184         const QStringList& columnCaptions)
0185 {
0186     d->lookInColumnNames = columnNames;
0187     d->lookInColumnCaptions = columnCaptions;
0188     m_lookIn->clear();
0189     m_lookIn->addItem(xi18n("(All fields)"));
0190     m_lookIn->addItem(xi18n("(Current field)"));
0191     m_lookIn->addItems(d->lookInColumnCaptions);
0192 }
0193 
0194 void KexiFindDialog::setCurrentLookInColumnName(const QString& columnName)
0195 {
0196     int index;
0197     if (columnName.isEmpty())
0198         index = 0;
0199     else if (columnName == "(field)")
0200         index = 1;
0201     else {
0202         index = d->lookInColumnNames.indexOf(columnName);
0203         if (index == -1) {
0204             qWarning() << QString(
0205                 "KexiFindDialog::setCurrentLookInColumn(%1) column name not found on the list")
0206             .arg(columnName);
0207             return;
0208         }
0209         index = index + 1/*"(All fields)"*/ + 1/*"(Current field)"*/;
0210     }
0211     m_lookIn->setCurrentIndex(index);
0212 }
0213 
0214 void KexiFindDialog::setReplaceMode(bool set)
0215 {
0216     if (d->replaceMode == set)
0217         return;
0218     d->replaceMode = set;
0219     if (d->replaceMode) {
0220         m_promptOnReplace->show();
0221         m_replaceLbl->show();
0222         m_textToReplace->show();
0223         m_btnReplace->show();
0224         m_btnReplaceAll->show();
0225     } else {
0226         m_promptOnReplace->hide();
0227         m_replaceLbl->hide();
0228         m_textToReplace->hide();
0229         m_btnReplace->hide();
0230         m_btnReplaceAll->hide();
0231         resize(width(), height() - 30);
0232     }
0233     setObjectNameForCaption(d->objectName);
0234     updateGeometry();
0235 }
0236 
0237 void KexiFindDialog::setObjectNameForCaption(const QString& name)
0238 {
0239     d->objectName = name;
0240     if (d->replaceMode) {
0241         if (name.isEmpty())
0242             setWindowTitle(xi18nc("@title:window", "Replace"));
0243         else
0244             setWindowTitle(xi18nc("@title:window", "Replace in <resource>%1</resource>", name));
0245     } else {
0246         if (name.isEmpty())
0247             setWindowTitle(xi18nc("@title:window", "Find"));
0248         else
0249             setWindowTitle(xi18nc("@title:window", "Find in <resource>%1</resource>", name));
0250     }
0251 }
0252 
0253 void KexiFindDialog::setButtonsEnabled(bool enable)
0254 {
0255     m_btnFind->setEnabled(enable);
0256     m_btnReplace->setEnabled(enable);
0257     m_btnReplaceAll->setEnabled(enable);
0258     if (!enable)
0259         setObjectNameForCaption(QString());
0260 }
0261 
0262 void KexiFindDialog::setMessage(const QString& message)
0263 {
0264     m_messageLabel->setText(message);
0265 }
0266 
0267 void KexiFindDialog::updateMessage(bool found)
0268 {
0269     if (found)
0270         setMessage(QString());
0271     else
0272         setMessage(xi18n("The search item was not found"));
0273 }
0274 
0275 void KexiFindDialog::addToFindHistory()
0276 {
0277     m_textToFind->addToHistory(m_textToFind->currentText());
0278 }
0279 
0280 void KexiFindDialog::addToReplaceHistory()
0281 {
0282     m_textToReplace->addToHistory(m_textToReplace->currentText());
0283 }
0284 
0285 void KexiFindDialog::show()
0286 {
0287     m_textToFind->setFocus();
0288     QDialog::show();
0289 }
0290 
0291 KexiSearchAndReplaceViewInterface::Options KexiFindDialog::options() const
0292 {
0293     KexiSearchAndReplaceViewInterface::Options options;
0294     if (m_lookIn->currentIndex() <= 0) //"(All fields)"
0295         options.columnNumber = KexiSearchAndReplaceViewInterface::Options::AllColumns;
0296     else if (m_lookIn->currentIndex() == 1) //"(Current field)"
0297         options.columnNumber = KexiSearchAndReplaceViewInterface::Options::CurrentColumn;
0298     else
0299         options.columnNumber = m_lookIn->currentIndex()  - 1/*"(All fields)"*/ - 1/*"(Current field)"*/;
0300     options.textMatching
0301         = (KexiSearchAndReplaceViewInterface::Options::TextMatching)m_match->currentIndex();
0302     options.searchDirection
0303         = (KexiSearchAndReplaceViewInterface::Options::SearchDirection)m_search->currentIndex();
0304     options.caseSensitive = m_caseSensitive->isChecked();
0305     options.wholeWordsOnly = m_wholeWords->isChecked();
0306     options.promptOnReplace = m_promptOnReplace->isChecked();
0307     return options;
0308 }
0309 
0310 bool KexiFindDialog::event(QEvent *e)
0311 {
0312     if (e->type() == QEvent::ShortcutOverride && static_cast<QKeyEvent*>(e)->key() == Qt::Key_Escape
0313         && static_cast<QKeyEvent*>(e)->modifiers() == Qt::NoModifier)
0314     {
0315         reject();
0316         return true;
0317     }
0318     return QDialog::event(e);
0319 }