File indexing completed on 2024-04-28 04:21:25

0001 // SPDX-FileCopyrightText: 2023 Jesper K. Pedersen <jesper.pedersen@kdab.com>
0002 //
0003 // SPDX-License-Identifier: GPL-2.0-or-later
0004 
0005 #include "AnnotationHandler.h"
0006 #include "Logging.h"
0007 #include "SelectCategoryAndValue.h"
0008 
0009 #include <kpabase/SettingsData.h>
0010 
0011 #include <KConfigGroup>
0012 #include <KLocalizedString>
0013 #include <KSharedConfig>
0014 #include <QDebug>
0015 #include <QKeyEvent>
0016 #include <QLocale>
0017 #include <qnamespace.h>
0018 
0019 namespace Viewer
0020 {
0021 
0022 AnnotationHandler::AnnotationHandler(QObject *parent)
0023     : QObject(parent)
0024 {
0025     loadSettings();
0026 }
0027 
0028 namespace
0029 {
0030     bool isKeyboardLetter(const QString &txt)
0031     {
0032         if (txt.isEmpty())
0033             return false;
0034 
0035 #if 0
0036         // See https://www.kdab.com/a-little-hidden-gem-qstringiterator/ for details
0037         // Unfortunately this requires a private header, which our CI doesn't like
0038         QStringIterator i(txt);
0039         char32_t ch = i.next();
0040         return QChar::isLetter(ch);
0041 #else
0042         return QLocale().toUpper(txt) != txt;
0043 #endif
0044     }
0045 };
0046 
0047 bool AnnotationHandler::handle(QKeyEvent *event)
0048 {
0049     const auto key = event->text().toLower();
0050 
0051     if (key.isEmpty() || !isKeyboardLetter(key) || (event->modifiers() != Qt::KeyboardModifiers() && event->modifiers() != Qt::KeyboardModifiers(Qt::ShiftModifier)))
0052         return false;
0053 
0054     if (!m_assignments.contains(key) || event->modifiers().testFlag(Qt::ShiftModifier)) {
0055         const bool assigned = assignKey(key);
0056         if (!assigned)
0057             return false;
0058     }
0059     const auto match = m_assignments.value(key);
0060 
0061     Q_EMIT requestToggleCategory(match.category, match.value);
0062 
0063     return true;
0064 }
0065 
0066 bool AnnotationHandler::assignKey(const QString &key)
0067 {
0068     SelectCategoryAndValue dialog(i18nc("@title", "Assign Macro"), i18n("Select item for macro key <b>%1</b>", key), m_assignments);
0069     connect(&dialog, &SelectCategoryAndValue::helpRequest, this, &AnnotationHandler::requestHelp);
0070     connect(&dialog, &SelectCategoryAndValue::keyRemovalRequested, this, &AnnotationHandler::clearKey);
0071 
0072     auto result = dialog.exec();
0073     if (result == QDialog::Rejected)
0074         return false;
0075 
0076     m_assignments[key] = { dialog.category(), dialog.value() };
0077     qCDebug(ViewerLog) << "Added macro assignment of key" << key << "to:" << m_assignments.value(key);
0078     saveSettings();
0079     return true;
0080 }
0081 
0082 void AnnotationHandler::clearKey(const QString &key)
0083 {
0084     qCDebug(ViewerLog) << "Removed macro assignment of key" << key << "to:" << m_assignments.value(key);
0085     m_assignments.remove(key);
0086     saveSettings();
0087 }
0088 
0089 namespace
0090 {
0091     KConfigGroup configGroup()
0092     {
0093         const auto section = Settings::SettingsData::instance()->groupForDatabase("viewer keybindings");
0094         return KSharedConfig::openConfig(QString::fromLatin1("kphotoalbumrc"))->group(section);
0095     }
0096 }
0097 
0098 void AnnotationHandler::saveSettings()
0099 {
0100 
0101     KConfigGroup group = configGroup();
0102     // delete group so that removed keys are removed:
0103     group.deleteGroup();
0104     for (auto it = m_assignments.cbegin(); it != m_assignments.cend(); ++it) {
0105         auto subgroup = group.group(it.key());
0106         const auto item = it.value();
0107         subgroup.writeEntry("category", item.category);
0108         subgroup.writeEntry("value", item.value);
0109     }
0110     group.sync();
0111 }
0112 
0113 void AnnotationHandler::loadSettings()
0114 {
0115     KConfigGroup group = configGroup();
0116     const QStringList keys = group.groupList();
0117     for (const QString &key : keys) {
0118         auto subgroup = group.group(key);
0119         m_assignments[key] = { subgroup.readEntry("category"), subgroup.readEntry("value") };
0120     }
0121 }
0122 
0123 bool AnnotationHandler::askForTagAndInsert()
0124 {
0125     SelectCategoryAndValue dialog(i18n("Tag Item"), i18n("Select tag for image"), m_assignments);
0126     connect(&dialog, &SelectCategoryAndValue::helpRequest, this, &AnnotationHandler::requestHelp);
0127 
0128     auto result = dialog.exec();
0129     if (result == QDialog::Rejected)
0130         return false;
0131     Q_EMIT requestToggleCategory(dialog.category(), dialog.value());
0132     return true;
0133 }
0134 
0135 AnnotationHandler::Assignments AnnotationHandler::assignments() const
0136 {
0137     return m_assignments;
0138 }
0139 
0140 QDebug operator<<(QDebug debug, const AnnotationHandler::Assignment &a)
0141 {
0142     QDebugStateSaver saveState(debug);
0143     debug.nospace().noquote() << "\"" << a.category << "/" << a.value << "\"";
0144     return debug;
0145 }
0146 
0147 } // namespace Viewer