File indexing completed on 2024-04-14 03:57:12

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 1998 Mark Donohoe <donohoe@kde.org>
0004     SPDX-FileCopyrightText: 1997 Nicolas Hadacek <hadacek@kde.org>
0005     SPDX-FileCopyrightText: 1998 Matthias Ettrich <ettrich@kde.org>
0006     SPDX-FileCopyrightText: 2001 Ellis Whitehead <ellis@kde.org>
0007     SPDX-FileCopyrightText: 2006 Hamish Rodda <rodda@kde.org>
0008     SPDX-FileCopyrightText: 2007 Roberto Raggi <roberto@kdevelop.org>
0009     SPDX-FileCopyrightText: 2007 Andreas Hartmetz <ahartmetz@gmail.com>
0010     SPDX-FileCopyrightText: 2008 Michael Jansen <kde@michael-jansen.biz>
0011 
0012     SPDX-License-Identifier: LGPL-2.0-or-later
0013 */
0014 #include "config-xmlgui.h"
0015 
0016 #include "debug.h"
0017 #include "kshortcutsdialog_p.h"
0018 
0019 #include <QAction>
0020 #include <QTreeWidgetItem>
0021 
0022 #if HAVE_GLOBALACCEL
0023 #include <KGlobalAccel>
0024 #endif
0025 
0026 KShortcutsEditorItem::KShortcutsEditorItem(QTreeWidgetItem *parent, QAction *action)
0027     : QTreeWidgetItem(parent, ActionItem)
0028     , m_action(action)
0029     , m_isNameBold(false)
0030     , m_oldLocalShortcut(nullptr)
0031     , m_oldGlobalShortcut(nullptr)
0032 {
0033     // Filtering message requested by translators (scripting).
0034     m_id = m_action->objectName();
0035     m_actionNameInTable = i18nc("@item:intable Action name in shortcuts configuration", "%1", KLocalizedString::removeAcceleratorMarker(m_action->text()));
0036     if (m_actionNameInTable.isEmpty()) {
0037         qCWarning(DEBUG_KXMLGUI) << "Action without text!" << m_action->objectName();
0038         m_actionNameInTable = m_id;
0039     }
0040 
0041     m_collator.setNumericMode(true);
0042     m_collator.setCaseSensitivity(Qt::CaseSensitive);
0043 }
0044 
0045 KShortcutsEditorItem::~KShortcutsEditorItem()
0046 {
0047     delete m_oldLocalShortcut;
0048     delete m_oldGlobalShortcut;
0049 }
0050 
0051 bool KShortcutsEditorItem::isModified() const
0052 {
0053     return m_oldLocalShortcut || m_oldGlobalShortcut;
0054 }
0055 
0056 QVariant KShortcutsEditorItem::data(int column, int role) const
0057 {
0058     switch (role) {
0059     case Qt::DisplayRole:
0060         switch (column) {
0061         case Name:
0062             return m_actionNameInTable;
0063         case Id:
0064             return m_id;
0065         case LocalPrimary:
0066         case LocalAlternate:
0067         case GlobalPrimary:
0068         case GlobalAlternate:
0069             return keySequence(column);
0070         default:
0071             break;
0072         }
0073         break;
0074     case Qt::DecorationRole:
0075         if (column == Name) {
0076             return m_action->icon();
0077         } else {
0078             return QIcon();
0079         }
0080     case Qt::WhatsThisRole:
0081         return m_action->whatsThis();
0082     case Qt::ToolTipRole:
0083         // There is no such thing as a QAction::description(). So we have
0084         // nothing to display here.
0085         return QVariant();
0086     case Qt::FontRole:
0087         if (column == Name && m_isNameBold) {
0088             QFont modifiedFont = treeWidget()->font();
0089             modifiedFont.setBold(true);
0090             return modifiedFont;
0091         }
0092         break;
0093     case KExtendableItemDelegate::ShowExtensionIndicatorRole:
0094         switch (column) {
0095         case Name:
0096             return false;
0097         case LocalPrimary:
0098         case LocalAlternate:
0099             return !m_action->property("isShortcutConfigurable").isValid() //
0100                 || m_action->property("isShortcutConfigurable").toBool();
0101 #if HAVE_GLOBALACCEL
0102         case GlobalPrimary:
0103         case GlobalAlternate:
0104             if (!KGlobalAccel::self()->hasShortcut(m_action)) {
0105                 return false;
0106             }
0107             return true;
0108 #endif
0109         default:
0110             return false;
0111         }
0112     // the following are custom roles, defined in this source file only
0113     case ShortcutRole:
0114         switch (column) {
0115         case LocalPrimary:
0116         case LocalAlternate:
0117         case GlobalPrimary:
0118         case GlobalAlternate:
0119             return keySequence(column);
0120         default:
0121             // Column not valid for this role
0122             Q_ASSERT(false);
0123             return QVariant();
0124         }
0125 
0126     case DefaultShortcutRole: {
0127         QList<QKeySequence> defaultShortcuts = m_action->property("defaultShortcuts").value<QList<QKeySequence>>();
0128 #if HAVE_GLOBALACCEL
0129         QList<QKeySequence> defaultGlobalShortcuts = KGlobalAccel::self()->defaultShortcut(m_action);
0130 #endif
0131 
0132         switch (column) {
0133         case LocalPrimary:
0134             return primarySequence(defaultShortcuts);
0135         case LocalAlternate:
0136             return alternateSequence(defaultShortcuts);
0137 #if HAVE_GLOBALACCEL
0138         case GlobalPrimary:
0139             return primarySequence(defaultGlobalShortcuts);
0140         case GlobalAlternate:
0141             return alternateSequence(defaultGlobalShortcuts);
0142 #endif
0143         default:
0144             // Column not valid for this role
0145             Q_ASSERT(false);
0146             return QVariant();
0147         }
0148     }
0149     case ObjectRole:
0150         return QVariant::fromValue(static_cast<QObject *>(m_action));
0151 
0152     default:
0153         break;
0154     }
0155 
0156     return QVariant();
0157 }
0158 
0159 bool KShortcutsEditorItem::operator<(const QTreeWidgetItem &other) const
0160 {
0161     const int column = treeWidget() ? treeWidget()->sortColumn() : 0;
0162     return m_collator.compare(text(column), other.text(column)) < 0;
0163 }
0164 
0165 QKeySequence KShortcutsEditorItem::keySequence(uint column) const
0166 {
0167     auto shortcuts = [this]() {
0168         return m_action->shortcuts();
0169     };
0170 
0171 #if HAVE_GLOBALACCEL
0172     auto globalShortcuts = [this]() {
0173         return KGlobalAccel::self()->shortcut(m_action);
0174     };
0175 #endif
0176 
0177     switch (column) {
0178     case LocalPrimary:
0179         return primarySequence(shortcuts());
0180     case LocalAlternate:
0181         return alternateSequence(shortcuts());
0182 #if HAVE_GLOBALACCEL
0183     case GlobalPrimary:
0184         return primarySequence(globalShortcuts());
0185     case GlobalAlternate:
0186         return alternateSequence(globalShortcuts());
0187 #endif
0188     default:
0189         return QKeySequence();
0190     }
0191 }
0192 
0193 void KShortcutsEditorItem::setKeySequence(uint column, const QKeySequence &seq)
0194 {
0195     QList<QKeySequence> ks;
0196 #if HAVE_GLOBALACCEL
0197     if (column == GlobalPrimary || column == GlobalAlternate) {
0198         ks = KGlobalAccel::self()->shortcut(m_action);
0199         if (!m_oldGlobalShortcut) {
0200             m_oldGlobalShortcut = new QList<QKeySequence>(ks);
0201         }
0202     } else
0203 #endif
0204     {
0205         ks = m_action->shortcuts();
0206         if (!m_oldLocalShortcut) {
0207             m_oldLocalShortcut = new QList<QKeySequence>(ks);
0208         }
0209     }
0210 
0211     if (column == LocalAlternate || column == GlobalAlternate) {
0212         if (ks.isEmpty()) {
0213             ks << QKeySequence();
0214         }
0215 
0216         if (ks.size() <= 1) {
0217             ks << seq;
0218         } else {
0219             ks[1] = seq;
0220         }
0221     } else {
0222         if (ks.isEmpty()) {
0223             ks << seq;
0224         } else {
0225             ks[0] = seq;
0226         }
0227     }
0228 
0229     // avoid also setting the default shortcut - what we are setting here is custom by definition
0230 #if HAVE_GLOBALACCEL
0231     if (column == GlobalPrimary || column == GlobalAlternate) {
0232         KGlobalAccel::self()->setShortcut(m_action, ks, KGlobalAccel::NoAutoloading);
0233 
0234     } else
0235 #endif
0236     {
0237         m_action->setShortcuts(ks);
0238     }
0239 
0240     updateModified();
0241 }
0242 
0243 // our definition of modified is "modified since the chooser was shown".
0244 void KShortcutsEditorItem::updateModified()
0245 {
0246     if (m_oldLocalShortcut && *m_oldLocalShortcut == m_action->shortcuts()) {
0247         delete m_oldLocalShortcut;
0248         m_oldLocalShortcut = nullptr;
0249     }
0250 #if HAVE_GLOBALACCEL
0251     if (m_oldGlobalShortcut && *m_oldGlobalShortcut == KGlobalAccel::self()->shortcut(m_action)) {
0252         delete m_oldGlobalShortcut;
0253         m_oldGlobalShortcut = nullptr;
0254     }
0255 #endif
0256 }
0257 
0258 bool KShortcutsEditorItem::isModified(uint column) const
0259 {
0260     switch (column) {
0261     case Name:
0262         return false;
0263     case LocalPrimary:
0264     case LocalAlternate:
0265         if (!m_oldLocalShortcut) {
0266             return false;
0267         }
0268         if (column == LocalPrimary) {
0269             return primarySequence(*m_oldLocalShortcut) != primarySequence(m_action->shortcuts());
0270         } else {
0271             return alternateSequence(*m_oldLocalShortcut) != alternateSequence(m_action->shortcuts());
0272         }
0273 #if HAVE_GLOBALACCEL
0274     case GlobalPrimary:
0275     case GlobalAlternate:
0276         if (!m_oldGlobalShortcut) {
0277             return false;
0278         }
0279         if (column == GlobalPrimary) {
0280             return primarySequence(*m_oldGlobalShortcut) != primarySequence(KGlobalAccel::self()->shortcut(m_action));
0281         } else {
0282             return alternateSequence(*m_oldGlobalShortcut) != alternateSequence(KGlobalAccel::self()->shortcut(m_action));
0283         }
0284 #endif
0285     default:
0286         return false;
0287     }
0288 }
0289 
0290 void KShortcutsEditorItem::undo()
0291 {
0292     if (m_oldLocalShortcut) {
0293         // We only ever reset the active Shortcut
0294         m_action->setShortcuts(*m_oldLocalShortcut);
0295     }
0296 
0297 #if HAVE_GLOBALACCEL
0298     if (m_oldGlobalShortcut) {
0299         KGlobalAccel::self()->setShortcut(m_action, *m_oldGlobalShortcut, KGlobalAccel::NoAutoloading);
0300     }
0301 #endif
0302 
0303     updateModified();
0304 }
0305 
0306 void KShortcutsEditorItem::commit()
0307 {
0308     delete m_oldLocalShortcut;
0309     m_oldLocalShortcut = nullptr;
0310     delete m_oldGlobalShortcut;
0311     m_oldGlobalShortcut = nullptr;
0312 }