File indexing completed on 2024-05-12 15:42:13

0001 /*
0002     SPDX-FileCopyrightText: 2000 Malte Starostik <malte@kde.org>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "searchproviderdlg.h"
0008 #include "searchprovider.h"
0009 
0010 #include <QClipboard>
0011 
0012 #include <KLocalizedString>
0013 #include <KMessageBox>
0014 #include <QApplication>
0015 #include <QTextCodec>
0016 #include <QVBoxLayout>
0017 
0018 SearchProviderDialog::SearchProviderDialog(SearchProvider *provider, QList<SearchProvider *> &providers, QWidget *parent)
0019     : QDialog(parent)
0020     , m_provider(provider)
0021 {
0022     setModal(true);
0023 
0024     m_buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
0025     connect(m_buttons, &QDialogButtonBox::accepted, this, &SearchProviderDialog::accept);
0026     connect(m_buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
0027 
0028     QWidget *mainWidget = new QWidget(this);
0029     m_dlg.setupUi(mainWidget);
0030 
0031     QVBoxLayout *layout = new QVBoxLayout(this);
0032     layout->addWidget(mainWidget);
0033     layout->addWidget(m_buttons);
0034 
0035     m_dlg.leQuery->setMinimumWidth(m_dlg.leQuery->fontMetrics().averageCharWidth() * 50);
0036 
0037     connect(m_dlg.leName, &QLineEdit::textChanged, this, &SearchProviderDialog::slotChanged);
0038     connect(m_dlg.leQuery, &QLineEdit::textChanged, this, &SearchProviderDialog::slotChanged);
0039     connect(m_dlg.leShortcut, &QLineEdit::textChanged, this, &SearchProviderDialog::slotChanged);
0040     connect(m_dlg.leShortcut, &QLineEdit::textChanged, this, &SearchProviderDialog::shortcutsChanged);
0041     connect(m_dlg.pbPaste, &QAbstractButton::clicked, this, &SearchProviderDialog::pastePlaceholder);
0042 
0043     // Data init
0044     m_providers = providers;
0045     QStringList charsets;
0046     for (const auto &codec : QTextCodec::availableCodecs()) {
0047         charsets << QString::fromUtf8(codec);
0048     }
0049     charsets.prepend(i18nc("@item:inlistbox The default character set", "Default"));
0050     m_dlg.cbCharset->addItems(charsets);
0051     if (m_provider) {
0052         setWindowTitle(i18n("Modify Web Shortcut"));
0053         m_dlg.leName->setText(m_provider->name());
0054         m_dlg.leQuery->setText(m_provider->query());
0055         m_dlg.leShortcut->setText(m_provider->keys().join(QLatin1Char(',')));
0056         m_dlg.cbCharset->setCurrentIndex(m_provider->charset().isEmpty() ? 0 : charsets.indexOf(m_provider->charset()));
0057         m_dlg.leName->setEnabled(false);
0058         m_dlg.leQuery->setFocus();
0059     } else {
0060         setWindowTitle(i18n("New Web Shortcut"));
0061         m_dlg.leName->setFocus();
0062 
0063         // If the clipboard contains a url copy it to the query lineedit
0064         const QClipboard *clipboard = QApplication::clipboard();
0065         const QString url = clipboard->text();
0066 
0067         if (!QUrl(url).host().isEmpty()) {
0068             m_dlg.leQuery->setText(url);
0069         }
0070 
0071         m_buttons->button(QDialogButtonBox::Ok)->setEnabled(false);
0072     }
0073 }
0074 
0075 void SearchProviderDialog::slotChanged()
0076 {
0077     m_buttons->button(QDialogButtonBox::Ok)
0078         ->setEnabled(!(m_dlg.leName->text().isEmpty() || m_dlg.leShortcut->text().isEmpty() || m_dlg.leQuery->text().isEmpty())
0079                      && m_dlg.noteLabel->text().isEmpty());
0080 }
0081 
0082 // Check if the user wants to assign shorthands that are already assigned to
0083 // another search provider. Invoked on every change to the shortcuts field.
0084 void SearchProviderDialog::shortcutsChanged(const QString &newShorthands)
0085 {
0086     // Convert all spaces to commas. A shorthand should be a single word.
0087     // Assume that the user wanted to enter an alternative shorthand and hit
0088     // space instead of the comma key. Save cursor position beforehand because
0089     // setText() will reset it to the end, which is not what we want when
0090     // backspacing something in the middle.
0091     int savedCursorPosition = m_dlg.leShortcut->cursorPosition();
0092     QString normalizedShorthands = QString(newShorthands).replace(QLatin1Char(' '), QLatin1Char(','));
0093     m_dlg.leShortcut->setText(normalizedShorthands);
0094     m_dlg.leShortcut->setCursorPosition(savedCursorPosition);
0095 
0096     QHash<QString, const SearchProvider *> contenders;
0097     const QStringList normList = normalizedShorthands.split(QLatin1Char(','));
0098     const QSet<QString> shorthands(normList.begin(), normList.end());
0099 
0100     auto findProvider = [this](const QString &shorthand) {
0101         return std::find_if(m_providers.cbegin(), m_providers.cend(), [this, &shorthand](const SearchProvider *provider) {
0102             return provider != m_provider && provider->keys().contains(shorthand);
0103         });
0104     };
0105     // Look at each shorthand the user entered and wade through the search
0106     // provider list in search of a conflicting shorthand. Do not continue
0107     // search after finding one, because shorthands should be assigned only
0108     // once. Act like data inconsistencies regarding this don't exist (should
0109     // probably be handled on load).
0110     for (const QString &shorthand : shorthands) {
0111         auto it = findProvider(shorthand);
0112         if (it != m_providers.cend()) {
0113             contenders.insert(shorthand, *it);
0114         }
0115     }
0116 
0117     const int contendersSize = contenders.size();
0118     if (contendersSize != 0) {
0119         if (contendersSize == 1) {
0120             auto it = contenders.cbegin();
0121             m_dlg.noteLabel->setText(i18n("The shortcut \"%1\" is already assigned to \"%2\". Please choose a different one.", it.key(), it.value()->name()));
0122         } else {
0123             QStringList contenderList;
0124             contenderList.reserve(contendersSize);
0125             for (auto it = contenders.cbegin(); it != contenders.cend(); ++it) {
0126                 contenderList.append(i18nc("- web short cut (e.g. gg): what it refers to (e.g. Google)", "- %1: \"%2\"", it.key(), it.value()->name()));
0127             }
0128 
0129             m_dlg.noteLabel->setText(
0130                 i18n("The following shortcuts are already assigned. Please choose different ones.\n%1", contenderList.join(QLatin1Char('\n'))));
0131         }
0132         m_buttons->button(QDialogButtonBox::Ok)->setEnabled(false);
0133     } else {
0134         m_dlg.noteLabel->clear();
0135     }
0136     slotChanged();
0137 }
0138 
0139 void SearchProviderDialog::accept()
0140 {
0141     if ((m_dlg.leQuery->text().indexOf(QLatin1String("\\{")) == -1)
0142         && KMessageBox::warningContinueCancel(nullptr,
0143                                               i18n("The Shortcut URL does not contain a \\{...} placeholder for the user query.\n"
0144                                                    "This means that the same page is always going to be visited, "
0145                                                    "regardless of the text typed in with the shortcut."),
0146                                               QString(),
0147                                               KGuiItem(i18n("Keep It")))
0148             == KMessageBox::Cancel) {
0149         return;
0150     }
0151 
0152     if (!m_provider) {
0153         m_provider = new SearchProvider;
0154     }
0155 
0156     const QString name = m_dlg.leName->text().trimmed();
0157     const QString query = m_dlg.leQuery->text().trimmed();
0158     QStringList keys = m_dlg.leShortcut->text().trimmed().toLower().split(QLatin1Char(','), Qt::SkipEmptyParts);
0159     keys.removeDuplicates(); // #169801. Remove duplicates...
0160     const QString charset = (m_dlg.cbCharset->currentIndex() ? m_dlg.cbCharset->currentText().trimmed() : QString());
0161 
0162     m_provider->setDirty((name != m_provider->name() || query != m_provider->query() || keys != m_provider->keys() || charset != m_provider->charset()));
0163     m_provider->setName(name);
0164     m_provider->setQuery(query);
0165     m_provider->setKeys(keys);
0166     m_provider->setCharset(charset);
0167     QDialog::accept();
0168 }
0169 
0170 void SearchProviderDialog::pastePlaceholder()
0171 {
0172     m_dlg.leQuery->insert(QStringLiteral("\\{@}"));
0173     m_dlg.leQuery->setFocus();
0174 }
0175 
0176 #include "moc_searchproviderdlg.cpp"