Changeset View
Changeset View
Standalone View
Standalone View
libs/widgetutils/kis_action_registry.cpp
Show All 13 Lines | |||||
14 | * You should have received a copy of the GNU General Public License | 14 | * You should have received a copy of the GNU General Public License | ||
15 | * along with this program; if not, write to the Free Software | 15 | * along with this program; if not, write to the Free Software | ||
16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | 16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
17 | */ | 17 | */ | ||
18 | 18 | | |||
19 | 19 | | |||
20 | #include <QString> | 20 | #include <QString> | ||
21 | #include <QHash> | 21 | #include <QHash> | ||
22 | #include <utility> | | |||
23 | #include <KSharedConfig> | | |||
24 | #include <QGlobalStatic> | 22 | #include <QGlobalStatic> | ||
25 | #include <QDomElement> | | |||
26 | #include <kis_debug.h> | | |||
27 | #include <tuple> | | |||
28 | #include "kis_debug.h" | | |||
29 | #include <KoResourcePaths.h> | | |||
30 | #include <QFile> | 23 | #include <QFile> | ||
24 | #include <QDomElement> | ||||
25 | #include <KSharedConfig> | ||||
26 | #include <klocalizedstring.h> | ||||
27 | #include <KisShortcutsDialog.h> | ||||
31 | 28 | | |||
29 | #include "kis_debug.h" | ||||
30 | #include "KoResourcePaths.h" | ||||
31 | #include "kis_icon_utils.h" | ||||
32 | #include "kactioncollection.h" | ||||
32 | 33 | | |||
33 | 34 | | |||
34 | #include "kis_action_registry.h" | 35 | #include "kis_action_registry.h" | ||
35 | 36 | | |||
36 | 37 | | |||
37 | namespace { | 38 | namespace { | ||
38 | 39 | | |||
39 | struct actionInfoItem { | 40 | struct actionInfoItem { | ||
Show All 9 Lines | |||||
49 | }; | 50 | }; | ||
50 | 51 | | |||
51 | 52 | | |||
52 | 53 | | |||
53 | class Q_DECL_HIDDEN KisActionRegistry::Private | 54 | class Q_DECL_HIDDEN KisActionRegistry::Private | ||
54 | { | 55 | { | ||
55 | public: | 56 | public: | ||
56 | 57 | | |||
58 | Private(KisActionRegistry *_q) : q(_q) {}; | ||||
59 | | ||||
57 | /** | 60 | /** | ||
58 | * We associate three pieces of information with each shortcut name. The | 61 | * We associate three pieces of information with each shortcut name. The | ||
59 | * first piece of information is a QDomElement, containing the raw data from | 62 | * first piece of information is a QDomElement, containing the raw data from | ||
60 | * the .action XML file. The second and third are QKeySequences, the first | 63 | * the .action XML file. The second and third are QKeySequences, the first | ||
61 | * of which is the default shortcut, the last of which is any custom | 64 | * of which is the default shortcut, the last of which is any custom | ||
62 | * shortcut. | 65 | * shortcut. | ||
63 | * | 66 | * | ||
64 | * QHash is most efficient as long as the action name keys are kept relatively short. | 67 | * QHash is most efficient as long as the action name keys are kept relatively short. | ||
65 | */ | 68 | */ | ||
66 | QHash<QString, actionInfoItem> actionInfoList; | 69 | QHash<QString, actionInfoItem> actionInfoList; | ||
67 | KSharedConfigPtr cfg{0}; | 70 | KSharedConfigPtr cfg{0}; | ||
68 | void loadActions(); | 71 | void loadActionFiles(); | ||
72 | void loadActionCollections(); | ||||
69 | actionInfoItem actionInfo(QString name) { | 73 | actionInfoItem actionInfo(QString name) { | ||
70 | return actionInfoList.value(name, emptyActionInfo); | 74 | return actionInfoList.value(name, emptyActionInfo); | ||
71 | }; | 75 | }; | ||
76 | | ||||
77 | KisActionRegistry *q; | ||||
78 | KActionCollection * defaultActionCollection; | ||||
79 | QHash<QString, KActionCollection*> actionCollections; | ||||
72 | }; | 80 | }; | ||
73 | 81 | | |||
74 | 82 | | |||
75 | Q_GLOBAL_STATIC(KisActionRegistry, s_instance); | 83 | Q_GLOBAL_STATIC(KisActionRegistry, s_instance); | ||
76 | 84 | | |||
77 | KisActionRegistry *KisActionRegistry::instance() | 85 | KisActionRegistry *KisActionRegistry::instance() | ||
78 | { | 86 | { | ||
79 | return s_instance; | 87 | return s_instance; | ||
80 | }; | 88 | }; | ||
81 | 89 | | |||
82 | 90 | | |||
83 | KisActionRegistry::KisActionRegistry() | 91 | KisActionRegistry::KisActionRegistry() | ||
84 | : d(new KisActionRegistry::Private()) | 92 | : d(new KisActionRegistry::Private(this)) | ||
85 | { | 93 | { | ||
86 | d->cfg = KSharedConfig::openConfig("kritashortcutsrc"); | 94 | d->cfg = KSharedConfig::openConfig("krbitashortcutsrc"); | ||
87 | d->loadActions(); | 95 | d->loadActionFiles(); | ||
96 | | ||||
97 | // Should change to , then translate | ||||
98 | d->defaultActionCollection = new KActionCollection(this, "Krita"); | ||||
99 | d->actionCollections.insert("Krita", d->defaultActionCollection); | ||||
100 | | ||||
88 | } | 101 | } | ||
89 | 102 | | |||
90 | // No this isn't the most efficient logic, but it's nice and readable. | 103 | // No this isn't the most efficient logic, but it's nice and readable. | ||
91 | QKeySequence KisActionRegistry::getPreferredShortcut(QString name) | 104 | QKeySequence KisActionRegistry::getPreferredShortcut(QString name) | ||
92 | { | 105 | { | ||
93 | QKeySequence customShortcut = getCustomShortcut(name); | 106 | QKeySequence customShortcut = getCustomShortcut(name); | ||
94 | 107 | | |||
95 | if (customShortcut.isEmpty()) { | 108 | if (customShortcut.isEmpty()) { | ||
Show All 19 Lines | 127 | { | |||
115 | return d->actionInfoList.keys(); | 128 | return d->actionInfoList.keys(); | ||
116 | }; | 129 | }; | ||
117 | 130 | | |||
118 | QDomElement KisActionRegistry::getActionXml(QString name) | 131 | QDomElement KisActionRegistry::getActionXml(QString name) | ||
119 | { | 132 | { | ||
120 | return d->actionInfo(name).xmlData; | 133 | return d->actionInfo(name).xmlData; | ||
121 | }; | 134 | }; | ||
122 | 135 | | |||
123 | void KisActionRegistry::Private::loadActions() | 136 | KActionCollection * KisActionRegistry::getDefaultCollection() | ||
137 | { | ||||
138 | return d->defaultActionCollection; | ||||
139 | }; | ||||
140 | | ||||
141 | void KisActionRegistry::addAction(QString name, QAction *a, QString category) | ||||
142 | { | ||||
143 | KActionCollection *ac; | ||||
144 | if (d->actionCollections.contains(category)) { | ||||
145 | ac = d->actionCollections.value(category); | ||||
146 | } else { | ||||
147 | ac = new KActionCollection(this, category); | ||||
148 | d->actionCollections.insert(category, ac); | ||||
149 | dbgAction << "Adding a new KActionCollection - " << category; | ||||
150 | } | ||||
151 | | ||||
152 | if (!ac->action(name)) { | ||||
153 | ac->addAction(name, a); | ||||
154 | } | ||||
155 | else { | ||||
156 | dbgAction << "duplicate action" << name << a << "in collection" << ac->componentName(); | ||||
157 | } | ||||
158 | | ||||
159 | // TODO: look into the loading/saving mechanism | ||||
160 | ac->readSettings(); | ||||
161 | }; | ||||
162 | | ||||
163 | | ||||
164 | | ||||
165 | QAction * KisActionRegistry::makeQAction(QString name, QObject *parent, QString category) | ||||
166 | { | ||||
167 | | ||||
168 | QAction * a = new QAction(parent); | ||||
169 | if (!d->actionInfoList.contains(name)) { | ||||
170 | dbgAction << "Warning: requested data for unknown action" << name; | ||||
171 | return a; | ||||
172 | } | ||||
173 | | ||||
174 | propertizeAction(name, a); | ||||
175 | addAction(name, a, category); | ||||
176 | | ||||
177 | return a; | ||||
178 | }; | ||||
179 | | ||||
180 | | ||||
181 | void KisActionRegistry::configureShortcuts(KActionCollection *ac) | ||||
182 | { | ||||
183 | | ||||
184 | KisShortcutsDialog dlg; | ||||
185 | dlg.addCollection(ac); | ||||
186 | for (auto i = d->actionCollections.constBegin(); i != d->actionCollections.constEnd(); i++ ) { | ||||
187 | dlg.addCollection(i.value(), i.key()); | ||||
188 | } | ||||
189 | | ||||
190 | dlg.configure(); // Show the dialog. | ||||
191 | } | ||||
192 | | ||||
193 | | ||||
194 | | ||||
195 | bool KisActionRegistry::propertizeAction(QString name, QAction * a) | ||||
196 | { | ||||
197 | | ||||
198 | QStringList actionNames = allActions(); | ||||
199 | QDomElement actionXml = getActionXml(name); | ||||
200 | | ||||
201 | | ||||
202 | // Convenience macros to extract text of a child node. | ||||
203 | auto getChildContent = [=](QString node){return actionXml.firstChildElement(node).text();}; | ||||
204 | // i18n requires converting format from QString. | ||||
205 | auto getChildContent_i18n = [=](QString node) { | ||||
206 | if (getChildContent(node).isEmpty()) { | ||||
207 | dbgAction << "Found empty string to translate for property" << node; | ||||
208 | return QString(); | ||||
209 | } | ||||
210 | return i18n(getChildContent(node).toUtf8().constData()); | ||||
211 | }; | ||||
212 | | ||||
213 | | ||||
214 | QString icon = getChildContent("icon"); | ||||
215 | QString text = getChildContent("text"); | ||||
216 | | ||||
217 | // Note: these fields in the .action definitions are marked for translation. | ||||
218 | QString whatsthis = getChildContent_i18n("whatsThis"); | ||||
219 | QString toolTip = getChildContent_i18n("toolTip"); | ||||
220 | QString statusTip = getChildContent_i18n("statusTip"); | ||||
221 | QString iconText = getChildContent_i18n("iconText"); | ||||
222 | | ||||
223 | bool isCheckable = getChildContent("isCheckable") == QString("true"); | ||||
224 | QKeySequence shortcut = QKeySequence(getChildContent("shortcut")); | ||||
225 | QKeySequence defaultShortcut = QKeySequence(getChildContent("defaultShortcut")); | ||||
226 | | ||||
227 | | ||||
228 | a->setObjectName(name); // This is helpful!! | ||||
229 | a->setIcon(KisIconUtils::loadIcon(icon.toLatin1())); | ||||
230 | a->setText(text); | ||||
231 | a->setObjectName(name); | ||||
232 | a->setWhatsThis(whatsthis); | ||||
233 | a->setToolTip(toolTip); | ||||
234 | a->setStatusTip(statusTip); | ||||
235 | a->setIconText(iconText); | ||||
236 | a->setShortcut(shortcut); | ||||
237 | a->setCheckable(isCheckable); | ||||
238 | | ||||
239 | | ||||
240 | // XXX: this totally duplicates KisAction::setDefaultShortcut | ||||
241 | QList<QKeySequence> listifiedShortcut; | ||||
242 | listifiedShortcut.append(shortcut); | ||||
243 | setProperty("defaultShortcuts", qVariantFromValue(listifiedShortcut)); | ||||
244 | | ||||
245 | | ||||
246 | | ||||
247 | | ||||
248 | // TODO: check for colliding shortcuts, or make sure it happens smartly inside kactioncollection | ||||
249 | // | ||||
250 | // Ultimately we want to have more than one KActionCollection, so we can | ||||
251 | // have things like Ctrl+I be italics in the text editor widget, while not | ||||
252 | // complaining about conflicts elsewhere. Right now, we use only one | ||||
253 | // collection, and we don't make things like the text editor configurable, | ||||
254 | // so duplicate shortcuts are handled mostly automatically by the shortcut | ||||
255 | // editor. | ||||
256 | // | ||||
257 | // QMap<QKeySequence, QAction*> existingShortcuts; | ||||
258 | // foreach(QAction* action, actionCollection->actions()) { | ||||
259 | // if(action->shortcut() == QKeySequence(0)) { | ||||
260 | // continue; | ||||
261 | // } | ||||
262 | // if (existingShortcuts.contains(action->shortcut())) { | ||||
263 | // dbgAction << QString("Actions %1 and %2 have the same shortcut: %3") \ | ||||
264 | // .arg(action->text()) \ | ||||
265 | // .arg(existingShortcuts[action->shortcut()]->text()) \ | ||||
266 | // .arg(action->shortcut()); | ||||
267 | // } | ||||
268 | // else { | ||||
269 | // existingShortcuts[action->shortcut()] = action; | ||||
270 | // } | ||||
271 | // } | ||||
272 | | ||||
273 | | ||||
274 | return true; | ||||
275 | } | ||||
276 | | ||||
277 | | ||||
278 | | ||||
279 | | ||||
280 | | ||||
281 | void KisActionRegistry::Private::loadActionFiles() | ||||
124 | { | 282 | { | ||
125 | 283 | | |||
126 | KoResourcePaths::addResourceType("kis_actions", "data", "krita/actions"); | 284 | KoResourcePaths::addResourceType("kis_actions", "data", "krita/actions"); | ||
127 | auto searchType = KoResourcePaths::Recursive | KoResourcePaths::NoDuplicates; | 285 | auto searchType = KoResourcePaths::Recursive | KoResourcePaths::NoDuplicates; | ||
128 | QStringList actionDefinitions = | 286 | QStringList actionDefinitions = | ||
129 | KoResourcePaths::findAllResources("kis_actions", "*.action", searchType); | 287 | KoResourcePaths::findAllResources("kis_actions", "*.action", searchType); | ||
130 | 288 | | |||
131 | // Extract actions all XML .action files. | 289 | // Extract actions all XML .action files. | ||
▲ Show 20 Lines • Show All 41 Lines • Show Last 20 Lines |