File indexing completed on 2024-05-12 04:06:25
0001 /* 0002 SPDX-FileCopyrightText: 2010 Stefan Majewsky <majewsky@gmx.net> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "triggermapper.h" 0008 #include "constraintinteractor.h" 0009 #include "interactors.h" 0010 0011 #include <QKeyEvent> 0012 #include <QMouseEvent> 0013 #include <QWheelEvent> 0014 #include <KConfig> 0015 #include <KConfigGroup> 0016 #include <KSharedConfig> 0017 0018 Palapeli::TriggerMapper* Palapeli::TriggerMapper::instance() 0019 { 0020 static Palapeli::TriggerMapper instance; 0021 return &instance; 0022 } 0023 0024 QMap<QByteArray, Palapeli::Interactor*> Palapeli::TriggerMapper::createInteractors(QGraphicsView* view) 0025 { 0026 QMap<QByteArray, Palapeli::Interactor*> result; 0027 result["MovePiece"] = new Palapeli::MovePieceInteractor(view); 0028 result["SelectPiece"] = new Palapeli::SelectPieceInteractor(view); 0029 result["TeleportPiece"] = new Palapeli::TeleportPieceInteractor(view); 0030 result["MoveViewport"] = new Palapeli::MoveViewportInteractor(view); 0031 result["ToggleCloseUp"] = new Palapeli::ToggleCloseUpInteractor(view); 0032 result["ZoomViewport"] = new Palapeli::ZoomViewportInteractor(view); 0033 result["ScrollViewportHoriz"] = new Palapeli::ScrollViewportInteractor(Qt::Horizontal, view); 0034 result["ScrollViewportVert"] = new Palapeli::ScrollViewportInteractor(Qt::Vertical, view); 0035 result["RubberBand"] = new Palapeli::RubberBandInteractor(view); 0036 result["Constraints"] = new Palapeli::ConstraintInteractor(view); 0037 result["ToggleConstraints"] = new Palapeli::ToggleConstraintInteractor(view); 0038 return result; 0039 } 0040 0041 QMap<QByteArray, Palapeli::Trigger> Palapeli::TriggerMapper::defaultAssociations() 0042 { 0043 QMap<QByteArray, Palapeli::Trigger> result; 0044 result.insert("MovePiece", Palapeli::Trigger("LeftButton;NoModifier")); 0045 result.insert("SelectPiece", Palapeli::Trigger("LeftButton;ControlModifier")); 0046 result.insert("TeleportPiece", Palapeli::Trigger("LeftButton;ShiftModifier")); 0047 result.insert("MoveViewport", Palapeli::Trigger("RightButton;NoModifier")); 0048 result.insert("ToggleCloseUp", Palapeli::Trigger("MidButton;NoModifier")); 0049 result.insert("ZoomViewport", Palapeli::Trigger("wheel:Vertical;NoModifier")); 0050 result.insert("RubberBand", Palapeli::Trigger("LeftButton;NoModifier")); 0051 result.insert("Constraints", Palapeli::Trigger("LeftButton;NoModifier")); 0052 return result; 0053 } 0054 0055 Palapeli::TriggerMapper::TriggerMapper() 0056 { 0057 //initialize quasi-static data 0058 m_keyModifierMap[Qt::Key_Shift] = Qt::ShiftModifier; 0059 m_keyModifierMap[Qt::Key_Control] = Qt::ControlModifier; 0060 m_keyModifierMap[Qt::Key_Alt] = Qt::AltModifier; 0061 m_keyModifierMap[Qt::Key_Meta] = Qt::MetaModifier; 0062 //initialize dynamic data 0063 readSettings(); 0064 } 0065 0066 QMap<QByteArray, Palapeli::Trigger> Palapeli::TriggerMapper::associations() const 0067 { 0068 return m_associations; 0069 } 0070 0071 void Palapeli::TriggerMapper::readSettings() 0072 { 0073 m_associations.clear(); 0074 m_associations = Palapeli::TriggerMapper::defaultAssociations(); 0075 //read config 0076 KConfigGroup group(KSharedConfig::openConfig(), QStringLiteral("Mouse Interaction")); 0077 const QStringList configKeys = group.keyList(); 0078 for (const QString& configKey : configKeys) 0079 { 0080 const QByteArray interactorKey = configKey.toLatin1(); 0081 const QList<QByteArray> triggers = group.readEntry(configKey, QList<QByteArray>()); 0082 for (const Palapeli::Trigger trigger : triggers) //implicit casts FTW 0083 if (trigger.isValid()) { 0084 // Remove default and insert config value(s). 0085 m_associations.insert(interactorKey, trigger); 0086 } 0087 } 0088 //announce update to InteractorManagers 0089 Q_EMIT associationsChanged(); 0090 } 0091 0092 void Palapeli::TriggerMapper::setAssociations(const QMap<QByteArray, Palapeli::Trigger>& associations) 0093 { 0094 m_associations = associations; 0095 //announce update to InteractorManagers 0096 Q_EMIT associationsChanged(); 0097 //assemble trigger serializations 0098 QMap<QByteArray, QList<QByteArray> > triggerSerializations; 0099 { 0100 QMap<QByteArray, Palapeli::Trigger>::const_iterator it1 = m_associations.constBegin(), it2 = m_associations.constEnd(); 0101 for (; it1 != it2; ++it1) 0102 triggerSerializations[it1.key()] << it1.value().serialized(); 0103 } 0104 //clear config 0105 KConfigGroup group(KSharedConfig::openConfig(), QStringLiteral("Mouse Interaction")); 0106 const auto keys = group.keyList(); 0107 for (const QString& key : keys) 0108 group.deleteEntry(key); 0109 //write config (in a way that supports multiple triggers for one interactor) 0110 QMap<QByteArray, QList<QByteArray> >::const_iterator it1 = triggerSerializations.constBegin(), it2 = triggerSerializations.constEnd(); 0111 for (; it1 != it2; ++it1) 0112 group.writeEntry(it1.key().data(), it1.value()); 0113 KSharedConfig::openConfig()->sync(); 0114 } 0115 0116 Palapeli::EventProcessingFlags Palapeli::TriggerMapper::testTrigger(const QByteArray& interactor, QWheelEvent* event) const 0117 { 0118 Palapeli::EventProcessingFlags result; 0119 QMap<QByteArray, Palapeli::Trigger>::const_iterator it1 = m_associations.begin(), it2 = m_associations.end(); 0120 for (; it1 != it2; ++it1) 0121 if (it1.key() == interactor) 0122 result |= testTrigger(it1.value(), event); 0123 return result; 0124 } 0125 0126 Palapeli::EventContext Palapeli::TriggerMapper::testTrigger(const QByteArray& interactor, QMouseEvent* event) const 0127 { 0128 Palapeli::EventContext result; 0129 QMap<QByteArray, Palapeli::Trigger>::const_iterator it1 = m_associations.begin(), it2 = m_associations.end(); 0130 for (; it1 != it2; ++it1) 0131 if (it1.key() == interactor) 0132 { 0133 const Palapeli::EventProcessingFlags flags = testTrigger(it1.value(), event); 0134 result.flags |= flags; 0135 if (flags & Palapeli::EventMatches) 0136 result.triggeringButtons |= it1.value().button(); 0137 } 0138 return result; 0139 } 0140 0141 Palapeli::EventContext Palapeli::TriggerMapper::testTrigger(const QByteArray& interactor, QKeyEvent* event, Qt::MouseButtons buttons) const 0142 { 0143 Palapeli::EventContext result; 0144 QMap<QByteArray, Palapeli::Trigger>::const_iterator it1 = m_associations.begin(), it2 = m_associations.end(); 0145 for (; it1 != it2; ++it1) 0146 if (it1.key() == interactor) 0147 { 0148 result.flags |= testTrigger(it1.value(), event, buttons); 0149 result.triggeringButtons |= it1.value().button(); 0150 } 0151 return result; 0152 } 0153 0154 Palapeli::EventProcessingFlags Palapeli::TriggerMapper::testTrigger(const Palapeli::Trigger& trigger, QWheelEvent* event) const 0155 { 0156 if (trigger.isValid()) 0157 { 0158 const bool testModifiers = trigger.modifiers() == Qt::NoModifier || trigger.modifiers() == event->modifiers(); 0159 const bool checkDirection = trigger.wheelDirection() != 0; 0160 const QPoint angleDelta = event->angleDelta(); 0161 const Qt::Orientation orientation = (qAbs(angleDelta.x()) > qAbs(angleDelta.y()) ? Qt::Horizontal : Qt::Vertical); 0162 const bool testDirection = (trigger.wheelDirection() == orientation); 0163 if (testModifiers && checkDirection && testDirection) 0164 { 0165 if (trigger.modifiers() == event->modifiers()) 0166 return Palapeli::EventMatchesExactly; 0167 else 0168 return Palapeli::EventMatches; 0169 } 0170 } 0171 //if execution comes to this point, trigger does not match 0172 return {}; 0173 } 0174 0175 Palapeli::EventProcessingFlags Palapeli::TriggerMapper::testTrigger(const Palapeli::Trigger& trigger, QMouseEvent* event) const 0176 { 0177 if (trigger.isValid()) 0178 { 0179 const bool testModifiers = trigger.modifiers() == Qt::NoModifier || trigger.modifiers() == event->modifiers(); 0180 const Palapeli::EventProcessingFlags positiveResult = (trigger.modifiers() == event->modifiers()) ? Palapeli::EventMatchesExactly : Palapeli::EventMatches; 0181 const bool checkDirection = trigger.wheelDirection() == 0; 0182 if (testModifiers && checkDirection) 0183 { 0184 if (trigger.button() == Qt::NoButton) 0185 //trigger matches 0186 return positiveResult; 0187 const bool checkButtons = (event->button() | event->buttons()) & trigger.button(); 0188 if (checkButtons) 0189 { 0190 //trigger matches - construct result 0191 Palapeli::EventProcessingFlags result = positiveResult; 0192 if (event->button() == trigger.button()) 0193 { 0194 if (event->type() == QEvent::MouseButtonPress) 0195 result |= Palapeli::EventStartsInteraction; 0196 if (event->type() == QEvent::MouseButtonRelease) 0197 result |= Palapeli::EventConcludesInteraction; 0198 } 0199 return result; 0200 } 0201 } 0202 } 0203 //if execution comes to this point, trigger does not match 0204 return {}; 0205 } 0206 0207 Palapeli::EventProcessingFlags Palapeli::TriggerMapper::testTrigger(const Palapeli::Trigger& trigger, QKeyEvent* event, Qt::MouseButtons buttons) const 0208 { 0209 if (trigger.isValid()) 0210 { 0211 //read modifiers 0212 const Qt::KeyboardModifier keyModifier = m_keyModifierMap.value((Qt::Key) event->key(), Qt::NoModifier); 0213 const Qt::KeyboardModifiers modifiers = keyModifier | event->modifiers(); 0214 //checking 0215 const bool testModifiers = trigger.modifiers() == Qt::NoModifier || trigger.modifiers() == modifiers; 0216 const Palapeli::EventProcessingFlags positiveResult = trigger.modifiers() == event->modifiers() ? Palapeli::EventMatchesExactly : Palapeli::EventMatches; 0217 const bool checkDirection = trigger.wheelDirection() == 0; 0218 const bool checkButton = (trigger.button() & buttons) || trigger.button() == Qt::NoButton; 0219 if (testModifiers && checkDirection && checkButton) 0220 { 0221 //trigger matches - construct result 0222 Palapeli::EventProcessingFlags result = positiveResult; 0223 if (keyModifier != Qt::NoModifier) 0224 { 0225 if (event->type() == QEvent::KeyPress) 0226 result |= Palapeli::EventStartsInteraction; 0227 if (event->type() == QEvent::KeyRelease) 0228 result |= Palapeli::EventConcludesInteraction; 0229 } 0230 return result; 0231 } 0232 } 0233 //if execution comes to this point, trigger does not match 0234 return {}; 0235 } 0236 0237 #include "moc_triggermapper.cpp"