Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_action_manager.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2013 Sven Langkamp <sven.langkamp@gmail.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
8
9#include <QList>
10#include <kactioncollection.h>
11
12#include <kis_icon.h>
13#include "KisPart.h"
14#include "kis_action.h"
15#include "KisViewManager.h"
20#include "kis_layer.h"
21#include "KisDocument.h"
22#include "kis_clipboard.h"
24#include "kis_config.h"
25#include <KisPortingUtils.h>
26
27#include <QMenu>
28#include "QFile"
29#include <QDomDocument>
30#include <QDomElement>
31
32class Q_DECL_HIDDEN KisActionManager::Private {
33
34public:
36
38 {
39 qDeleteAll(uiRegistry.values());
40 }
41
42 KisViewManager* viewManager {nullptr};
43 KisKActionCollection *actionCollection {nullptr};
44
48
49};
50
52 : d(new Private)
53{
54 d->viewManager = viewManager;
55 d->actionCollection = actionCollection;
56
57 connect(d->actionCollection,
58 SIGNAL(inserted(QAction*)), SLOT(slotActionAddedToCollection(QAction*)));
59}
60
62{
63
64#if 0
65 if ((d->actions.size() > 0)) {
66
67 QDomDocument doc;
68 QDomElement e = doc.createElement("Actions");
69 e.setAttribute("version", "2");
70 doc.appendChild(e);
71
72 Q_FOREACH (KisAction *action, d->actions) {
73 QDomElement a = doc.createElement("Action");
74 a.setAttribute("name", action->objectName());
75
76 // But seriously, XML is the worst format ever designed
77 auto addElement = [&](QString title, QString content) {
78 QDomElement newNode = doc.createElement(title);
79 QDomText newText = doc.createTextNode(content);
80 newNode.appendChild(newText);
81 a.appendChild(newNode);
82 };
83
84 addElement("icon", action->icon().name());
85 addElement("text", action->text());
86 addElement("whatsThis" , action->whatsThis());
87 addElement("toolTip" , action->toolTip());
88 addElement("iconText" , action->iconText());
89 addElement("shortcut" , action->shortcut().toString());
90 addElement("activationFlags" , QString::number(action->activationFlags(),2));
91 addElement("activationConditions" , QString::number(action->activationConditions(),2));
92 addElement("defaultShortcut" , action->defaultShortcut().toString());
93 addElement("isCheckable" , QString((action->isChecked() ? "true" : "false")));
94 addElement("statusTip", action->statusTip());
95 e.appendChild(a);
96 }
97 QFile f("ActionManager.action");
98 f.open(QFile::WriteOnly);
99 f.write(doc.toString().toUtf8());
100 f.close();
101
102 }
103#endif
104 delete d;
105}
106
108{
109 Q_UNUSED(imageView);
110}
111
113{
120 KisActionRegistry::instance()->updateShortcut(action->objectName(), action);
121}
122
123void KisActionManager::addAction(const QString& name, KisAction* action)
124{
125 Q_ASSERT(!name.isEmpty());
126 Q_ASSERT(action);
127 Q_ASSERT(d->viewManager);
128 Q_ASSERT(d->actionCollection);
129
130 d->actionCollection->addAction(name, action);
131 action->setParent(d->actionCollection);
132
133 d->actions.append(action);
134 action->setActionManager(this);
135}
136
138{
139 d->actions.removeOne(action);
140
141 if (!action->objectName().isEmpty()) {
142 KIS_ASSERT_RECOVER_RETURN(d->actionCollection);
143 d->actionCollection->takeAction(action);
144 }
145}
146
147KisAction *KisActionManager::actionByName(const QString &name) const
148{
149 Q_FOREACH (KisAction *action, d->actions) {
150 if (action->objectName() == name) {
151 return action;
152 }
153 }
154 return 0;
155}
156
157
159{
160 KisAction *a = actionByName(name); // Check if the action already exists
161
162 if (a) {
163 dbgAction << name << "already exists";
164 return a;
165 }
166
167 // There is some tension here. KisActionManager is supposed to be in control
168 // of global actions, but these actions are supposed to be duplicated. We
169 // will add them to the KisActionRegistry for the time being so we can get
170 // properly categorized shortcuts.
171 a = new KisAction();
172
174
175 // Add extra properties
176 actionRegistry->propertizeAction(name, a);
177 bool ok; // We will skip this check
178 int activationFlags = actionRegistry->getActionProperty(name, "activationFlags").toInt(&ok, 2);
179 int activationConditions = actionRegistry->getActionProperty(name, "activationConditions").toInt(&ok, 2);
180 a->setActivationFlags((KisAction::ActivationFlags) activationFlags);
181 a->setActivationConditions((KisAction::ActivationConditions) activationConditions);
182
183 addAction(name, a);
184 return a;
185}
186
188{
189 //TODO other flags
190 KisAction::ActivationFlags flags;
191
192 KisImageWSP image;
193 KisNodeSP node;
194 KisLayerSP layer;
195 KisSelectionManager *selectionManager = 0;
196
197 KisAction::ActivationConditions conditions = KisAction::NO_CONDITION;
198
199 if (d->viewManager) {
200 node = d->viewManager->activeNode();
201 selectionManager = d->viewManager->selectionManager();
202
203 // if there are no views, that means no document is open.
204 // we cannot have nodes (selections), devices, or documents without a view
205 if ( d->viewManager->viewCount() > 0 )
206 {
207 image = d->viewManager->image();
209
210 if (image && image->animationInterface()->hasAnimation()) {
212 }
213
214 KisDocument *document = d->viewManager->document();
215 if (document && document->isReadWrite()) {
217 }
218
219 if (d->viewManager->viewCount() > 1) {
221 }
222
223 if (d->viewManager->document() && d->viewManager->document()->isModified()) {
225 }
226
227 if (d->viewManager->activeDevice()) {
229 }
230 }
231
232 if (d->viewManager->selectionEditable()) {
233 conditions |= KisAction::SELECTION_EDITABLE;
234 }
235
236 }
237
238 // is there a selection/mask?
239 // you have to have at least one view (document) open for this to be true
240 if (node) {
241
242 // if a node exists, we know there is an active layer as well
243 flags |= KisAction::ACTIVE_NODE;
244
245 layer = qobject_cast<KisLayer*>(node.data());
246 if (layer) {
248 }
249
250 if (node->inherits("KisTransparencyMask")) {
252 }
253
254
255 if (layer && layer->inherits("KisShapeLayer")) {
257 }
258
259 if (KisClipboard::instance()->hasLayers()) {
261 }
262
263 if (selectionManager) {
264
265 if (selectionManager->canReselectDeactivatedSelection()) {
267 }
268
269 if (selectionManager->havePixelsSelected()) {
271 }
272
273 if (selectionManager->haveShapesSelected()) {
275 }
276
277 if (selectionManager->haveAnySelectionWithPixels()) {
279 }
280
281 if (selectionManager->haveShapeSelectionWithShapes()) {
283 }
284
285 if (selectionManager->haveRasterSelectionWithPixels()) {
287 }
288
289 if (selectionManager->havePixelsInClipboard()) {
291 }
292
293 if (selectionManager->haveShapesInClipboard()) {
295 }
296 }
297
298 if (node->isEditable(false)) {
300 }
301
302 if (node->hasEditablePaintDevice()) {
304 }
305 }
306
307 KisConfig cfg(true);
308 if (cfg.useOpenGL()) {
309 conditions |= KisAction::OPENGL_ENABLED;
310 }
311
312 // loop through all actions in action manager and determine what should be enabled
313 Q_FOREACH (QPointer<KisAction> action, d->actions) {
314 bool enable;
315 if (action) {
316 if (action->activationFlags() == KisAction::NONE) {
317 enable = true;
318 }
319 else {
320 enable = action->activationFlags() & flags; // combine action flags with updateGUI flags
321 }
322
323 enable = enable && (int)(action->activationConditions() & conditions) == (int)action->activationConditions();
324
325 if (node && enable) {
326 Q_FOREACH (const QString &type, action->excludedNodeTypes()) {
327 if (node->inherits(type.toLatin1())) {
328 enable = false;
329 break;
330 }
331 }
332 }
333
334 action->setActionEnabled(enable);
335 }
336 }
337}
338
339KisAction *KisActionManager::createStandardAction(KStandardAction::StandardAction actionType, const QObject *receiver, const char *member)
340{
341 QAction *standardAction = KStandardAction::create(actionType, receiver, member, 0);
342 KisAction *action = new KisAction(standardAction->icon(), standardAction->text());
343
344 const QList<QKeySequence> defaultShortcuts = standardAction->property("defaultShortcuts").value<QList<QKeySequence> >();
345 const QKeySequence defaultShortcut = defaultShortcuts.isEmpty() ? QKeySequence() : defaultShortcuts.at(0);
346 action->setDefaultShortcut(standardAction->shortcut());
347#ifdef Q_OS_WIN
348 if (actionType == KStandardAction::SaveAs && defaultShortcuts.isEmpty()) {
349 action->setShortcut(QKeySequence("CTRL+SHIFT+S"));
350 }
351#endif
352 action->setCheckable(standardAction->isCheckable());
353 if (action->isCheckable()) {
354 action->setChecked(standardAction->isChecked());
355 }
356 action->setMenuRole(standardAction->menuRole());
357 action->setText(standardAction->text());
358 action->setToolTip(standardAction->toolTip());
359
360 if (receiver && member) {
361 if (actionType == KStandardAction::OpenRecent) {
362 QObject::connect(action, SIGNAL(urlSelected(QUrl)), receiver, member);
363 }
364 else if (actionType == KStandardAction::ConfigureToolbars) {
365 QObject::connect(action, SIGNAL(triggered(bool)), receiver, member, Qt::QueuedConnection);
366 }
367 else {
368 QObject::connect(action, SIGNAL(triggered(bool)), receiver, member);
369 }
370 }
371
373 actionRegistry->propertizeAction(standardAction->objectName(), action);
374
375 addAction(standardAction->objectName(), action);
376 delete standardAction;
377 return action;
378}
379
380void KisActionManager::safePopulateMenu(QMenu *menu, const QString &actionId, KisActionManager *actionManager)
381{
382 KIS_SAFE_ASSERT_RECOVER_RETURN(actionManager);
383
384 KisAction *action = actionManager->actionByName(actionId);
386
387 menu->addAction(action);
388}
389
391{
392 d->uiRegistry.add(factory);
393}
394
396{
397 d->operationRegistry.add(operation);
398}
399
400void KisActionManager::runOperation(const QString& id)
401{
403
404 KisOperationUIFactory* uiFactory = d->uiRegistry.get(id);
405 if (uiFactory) {
406 bool gotConfig = uiFactory->fetchConfiguration(d->viewManager, config);
407 if (!gotConfig) {
408 return;
409 }
410 }
411
413}
414
416{
417 KisOperation* operation = d->operationRegistry.get(config->id());
418 Q_ASSERT(operation);
419 operation->runFromXML(d->viewManager, *config);
420}
421
423{
424 QFile data("actions.txt");
425 if (data.open(QFile::WriteOnly | QFile::Truncate)) {
426 QTextStream out(&data);
428
429 Q_FOREACH (KisAction* action, d->actions) {
430 KisAction::ActivationFlags flags = action->activationFlags();
431 out << "-------- " << action->text() << " --------\n";
432 out << "Action will activate on: \n";
433
434 if (flags & KisAction::ACTIVE_IMAGE) {
435 out << " Active image\n";
436 }
437 if (flags & KisAction::MULTIPLE_IMAGES) {
438 out << " More than one image open\n";
439 }
441 out << " Active image modified\n";
442 }
443 if (flags & KisAction::ACTIVE_DEVICE) {
444 out << " Active device\n";
445 }
446 if (flags & KisAction::ACTIVE_LAYER) {
447 out << " Active layer\n";
448 }
450 out << " Active transparency mask\n";
451 }
452 if (flags & KisAction::ACTIVE_NODE) {
453 out << " Active node\n";
454 }
455 if (flags & KisAction::ACTIVE_SHAPE_LAYER) {
456 out << " Active shape layer\n";
457 }
458 if (flags & KisAction::PIXELS_SELECTED) {
459 out << " Pixels selected\n";
460 }
461 if (flags & KisAction::SHAPES_SELECTED) {
462 out << " Shapes selected\n";
463 }
465 out << " Any selection with pixels\n";
466 }
467 if (flags & KisAction::PIXELS_IN_CLIPBOARD) {
468 out << " Pixels in clipboard\n";
469 }
470 if (flags & KisAction::SHAPES_IN_CLIPBOARD) {
471 out << " Shape in clipboard\n";
472 }
473 if (flags & KisAction::IMAGE_HAS_ANIMATION) {
474 out << " Image has animation\n";
475 }
476
477 out << "\n\n";
478 out << "Action will only activate if the following conditions are met: \n";
479 KisAction::ActivationConditions conditions = action->activationConditions();
480 if ((int)conditions == 0) {
481 out << " -\n";
482 }
483 if (conditions & KisAction::ACTIVE_NODE_EDITABLE) {
484 out << " Active Node editable\n";
485 }
487 out << " Active Node has editable paint device\n";
488 }
489 if (conditions & KisAction::SELECTION_EDITABLE) {
490 out << " Selection is editable\n";
491 }
492 if (conditions & KisAction::OPENGL_ENABLED) {
493 out << " OpenGL is enabled\n";
494 }
495 out << "\n\n";
496 }
497 }
498}
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))
A KisActionManager class keeps track of KisActions. These actions are always associated with the GUI....
void setView(QPointer< KisView > imageView)
KisOperationRegistry operationRegistry
void runOperation(const QString &id)
KisAction * createAction(const QString &name)
KisAction * actionByName(const QString &name) const
KisActionManager(KisViewManager *viewManager, KisKActionCollection *actionCollection)
QList< QPointer< KisAction > > actions
void takeAction(KisAction *action)
KisViewManager * viewManager
void slotActionAddedToCollection(QAction *action)
KisKActionCollection * actionCollection
void registerOperationUIFactory(KisOperationUIFactory *factory)
static void safePopulateMenu(QMenu *menu, const QString &actionId, KisActionManager *actionManager)
KoGenericRegistry< KisOperationUIFactory * > uiRegistry
void addAction(const QString &name, KisAction *action)
void runOperationFromConfiguration(KisOperationConfigurationSP config)
KisAction * createStandardAction(KStandardAction::StandardAction, const QObject *receiver, const char *member)
void registerOperation(KisOperation *operation)
void updateShortcut(const QString &name, QAction *ac)
bool propertizeAction(const QString &name, QAction *a)
static KisActionRegistry * instance()
QString getActionProperty(const QString &name, const QString &property)
void setActivationFlags(ActivationFlags flags)
void setActivationConditions(ActivationConditions conditions)
@ PIXELS_IN_CLIPBOARD
Activate if the clipboard contains pixels.
Definition kis_action.h:54
@ NONE
Always activate.
Definition kis_action.h:42
@ SHAPES_IN_CLIPBOARD
Activate if the clipboard contains vector data.
Definition kis_action.h:55
@ CURRENT_IMAGE_MODIFIED
Activate if the current image is modified.
Definition kis_action.h:45
@ ACTIVE_IMAGE
Activate if there is at least one image.
Definition kis_action.h:43
@ ACTIVE_NODE
Activate if there's an active node (layer or mask)
Definition kis_action.h:46
@ SHAPE_SELECTION_WITH_SHAPES
Activate there is a vector selection active.
Definition kis_action.h:59
@ IMAGE_IS_WRITABLE
Activate KisDocument::isReadWrite() is active.
Definition kis_action.h:62
@ ANY_SELECTION_WITH_PIXELS
???
Definition kis_action.h:53
@ ACTIVE_TRANSPARENCY_MASK
Activate if the current node is a transparency mask.
Definition kis_action.h:49
@ IMAGE_HAS_ANIMATION
Activate if the image has an animation.
Definition kis_action.h:58
@ SHAPES_SELECTED
Activate if any vector shape is selected.
Definition kis_action.h:52
@ PIXELS_SELECTED
Activate if any pixels are selected (with any kind of selection)
Definition kis_action.h:51
@ ACTIVE_SHAPE_LAYER
Activate if the current node is a vector layer.
Definition kis_action.h:50
@ IMAGE_CAN_RESELECT
Activate there is a deselected selection in the image.
Definition kis_action.h:61
@ PIXEL_SELECTION_WITH_PIXELS
Activate there is a raster selection active.
Definition kis_action.h:60
@ LAYERS_IN_CLIPBOARD
???
Definition kis_action.h:57
@ ACTIVE_LAYER
Activate if the current node is a layer (vector or pixel)
Definition kis_action.h:48
@ MULTIPLE_IMAGES
Activate if there is more than one image open.
Definition kis_action.h:44
@ ACTIVE_DEVICE
Activate if the active node has a paint device, i.e. there are pixels to be modified.
Definition kis_action.h:47
@ ACTIVE_NODE_EDITABLE_PAINT_DEVICE
Definition kis_action.h:69
@ OPENGL_ENABLED
Definition kis_action.h:71
@ SELECTION_EDITABLE
Definition kis_action.h:70
@ ACTIVE_NODE_EDITABLE
Definition kis_action.h:68
void setActionManager(KisActionManager *actionManager)
ActivationFlags activationFlags()
void setDefaultShortcut(const QKeySequence &shortcut)
ActivationConditions activationConditions()
QKeySequence defaultShortcut() const
static KisClipboard * instance()
bool useOpenGL(bool defaultValue=false) const
KisImageAnimationInterface * animationInterface() const
A container for a set of QAction objects.
virtual bool fetchConfiguration(KisViewManager *view, KisOperationConfigurationSP configuration)=0
virtual void runFromXML(KisViewManager *view, const KisOperationConfiguration &config)
bool haveAnySelectionWithPixels()
Checks if the current selection is editable and has some pixels selected in the pixel selection.
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128
#define KIS_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:75
#define dbgAction
Definition kis_debug.h:58
QAction * create(StandardAction id, const QObject *recvr, const char *slot, QObject *parent)
void setUtf8OnStream(QTextStream &stream)
bool isEditable(bool checkVisibility=true) const
bool hasEditablePaintDevice() const