File indexing completed on 2024-12-22 04:12:36

0001 /*
0002  * This file is part of the KDE project
0003  * SPDX-FileCopyrightText: 2013 Arjen Hiemstra <ahiemstra@heimr.nl>
0004  *
0005  * SPDX-License-Identifier: GPL-2.0-or-later
0006  */
0007 
0008 #include "kis_input_configuration_page.h"
0009 #include "ui_kis_input_configuration_page.h"
0010 
0011 #include <QDebug>
0012 #include <QDir>
0013 #include <QMap>
0014 
0015 #include "input/kis_input_profile_manager.h"
0016 #include "input/kis_input_profile.h"
0017 #include "input/kis_shortcut_configuration.h"
0018 #include "input/kis_abstract_input_action.h"
0019 #include "kis_edit_profiles_dialog.h"
0020 #include "kis_input_profile_model.h"
0021 #include "kis_input_configuration_page_item.h"
0022 #include <KoResourcePaths.h>
0023 
0024 
0025 #include "kis_icon_utils.h"
0026 
0027 struct KisInputConfigurationPage::Private {
0028     QMap<KisAbstractInputAction *, KisInputConfigurationPageItem*> actionInputConfigurationMap;
0029 };
0030 
0031 KisInputConfigurationPage::KisInputConfigurationPage(QWidget *parent, Qt::WindowFlags f)
0032     : QWidget(parent, f)
0033     , m_d(new Private)
0034 {
0035     ui = new Ui::KisInputConfigurationPage;
0036     ui->setupUi(this);
0037 
0038     ui->profileComboBox->setModel(new KisInputProfileModel(ui->profileComboBox));
0039     updateSelectedProfile();
0040     connect(ui->profileComboBox, SIGNAL(currentIndexChanged(QString)), SLOT(changeCurrentProfile(QString)));
0041 
0042     ui->editProfilesButton->setIcon(KisIconUtils::loadIcon("document-edit"));
0043 
0044     connect(ui->editProfilesButton, SIGNAL(clicked(bool)), SLOT(editProfilesButtonClicked()));
0045     connect(KisInputProfileManager::instance(), SIGNAL(profilesChanged()), SLOT(updateSelectedProfile()));
0046     connect(KisInputProfileManager::instance(),
0047             &KisInputProfileManager::currentProfileChanged,
0048             this,
0049             &KisInputConfigurationPage::checkForConflicts);
0050 
0051     QList<KisAbstractInputAction *> actions = KisInputProfileManager::instance()->actions();
0052     Q_FOREACH(KisAbstractInputAction * action, actions) {
0053         KisInputConfigurationPageItem *item = new KisInputConfigurationPageItem(this);
0054         item->setAction(action);
0055         ui->configurationItemsArea->addWidget(item);
0056         m_d->actionInputConfigurationMap.insert(action, item);
0057         connect(item,
0058                 &KisInputConfigurationPageItem::inputConfigurationChanged,
0059                 this,
0060                 &KisInputConfigurationPage::checkForConflicts,
0061                 Qt::UniqueConnection);
0062     }
0063     checkForConflicts();
0064     ui->configurationItemsArea->addStretch(20); // ensures listed input are on top
0065 
0066     QScroller *scroller = KisKineticScroller::createPreconfiguredScroller(ui->scrollArea);
0067     if (scroller) {
0068         connect(scroller, SIGNAL(stateChanged(QScroller::State)),
0069                 this, SLOT(slotScrollerStateChanged(QScroller::State)));
0070     }
0071 }
0072 
0073 KisInputConfigurationPage::~KisInputConfigurationPage() = default;
0074 
0075 void KisInputConfigurationPage::saveChanges()
0076 {
0077     KisInputProfileManager::instance()->saveProfiles();
0078 }
0079 
0080 void KisInputConfigurationPage::revertChanges()
0081 {
0082     KisInputProfileManager::instance()->loadProfiles();
0083 }
0084 
0085 void KisInputConfigurationPage::checkForConflicts()
0086 {
0087     // reset all of them first.
0088     Q_FOREACH (KisInputConfigurationPageItem *pageItem, m_d->actionInputConfigurationMap.values()) {
0089         pageItem->setWarningEnabled(false);
0090     }
0091     const QList<KisShortcutConfiguration *> conflictingShortcuts =
0092         KisInputProfileManager::instance()->getConflictingShortcuts(
0093             KisInputProfileManager::instance()->currentProfile());
0094 
0095     if (conflictingShortcuts.isEmpty()) {
0096         return;
0097     }
0098 
0099     struct ShortcutInfo {
0100         QVector<KisShortcutConfiguration *> shortcutObjects;
0101         QStringList actionTexts;
0102     };
0103     QMap<QString, ShortcutInfo> conflictingShortcutsMap;
0104 
0105     Q_FOREACH (KisShortcutConfiguration *shortcut, conflictingShortcuts) {
0106         if (shortcut->action()) {
0107             if (m_d->actionInputConfigurationMap.contains(shortcut->action())) {
0108                 ShortcutInfo &shortcutInfo = conflictingShortcutsMap[shortcut->getInputText()];
0109                 shortcutInfo.shortcutObjects.append(shortcut);
0110                 const QString subActionName = shortcut->action()->shortcutIndexes().key(shortcut->mode());
0111                 shortcutInfo.actionTexts.append(shortcut->action()->name() + " → " + subActionName);
0112             } else {
0113                 qWarning() << "KisInputConfigurationPageItem does not exist for the specified action:"
0114                            << shortcut->action()->name();
0115             }
0116         } else {
0117             qWarning() << "Action not set for the given shortcut.";
0118         }
0119     }
0120 
0121     QMap<KisAbstractInputAction *, QSet<QString>> infoTexts;
0122     Q_FOREACH (const ShortcutInfo &shortcutInfo, conflictingShortcutsMap) {
0123         const QString text = "<b>" + shortcutInfo.shortcutObjects.first()->getInputText() + "</b>" + "<br>&nbsp;&nbsp;"
0124             + shortcutInfo.actionTexts.join("<br>&nbsp;&nbsp;");
0125         Q_FOREACH (KisShortcutConfiguration *shortcut, shortcutInfo.shortcutObjects) {
0126             infoTexts[shortcut->action()] << text;
0127         }
0128     }
0129 
0130     QMapIterator<KisAbstractInputAction *, QSet<QString>> it(infoTexts);
0131     while (it.hasNext()) {
0132         it.next();
0133         QSetIterator<QString> it2(it.value());
0134         if (!it2.hasNext()) {
0135             continue;
0136         }
0137         QString text = "<ul> <li>" + it2.next() + "</li>";
0138         while (it2.hasNext()) {
0139             text += "<li>" + it2.next() + "</li>";
0140         }
0141         text += "</ul>";
0142         m_d->actionInputConfigurationMap[it.key()]->setWarningEnabled(true, text);
0143     }
0144 }
0145 
0146 void KisInputConfigurationPage::setDefaults()
0147 {
0148     QDir profileDir(KoResourcePaths::saveLocation("data", "input/", false));
0149 
0150     if (profileDir.exists()) {
0151         QStringList entries = profileDir.entryList(QStringList() << "*.profile", QDir::Files | QDir::NoDotAndDotDot);
0152         Q_FOREACH(const QString & file, entries) {
0153             profileDir.remove(file);
0154         }
0155 
0156         KisInputProfileManager::instance()->loadProfiles();
0157     }
0158 }
0159 
0160 void KisInputConfigurationPage::editProfilesButtonClicked()
0161 {
0162     KisEditProfilesDialog dialog;
0163     dialog.exec();
0164 }
0165 
0166 void KisInputConfigurationPage::updateSelectedProfile()
0167 {
0168     if (KisInputProfileManager::instance()->currentProfile()) {
0169         ui->profileComboBox->setCurrentItem(KisInputProfileManager::instance()->currentProfile()->name());
0170     }
0171 }
0172 
0173 void KisInputConfigurationPage::changeCurrentProfile(const QString &newProfile)
0174 {
0175     KisInputProfileManager::instance()->setCurrentProfile(KisInputProfileManager::instance()->profile(newProfile));
0176 }