Krita Source Code Documentation
Loading...
Searching...
No Matches
KoToolManager.cpp
Go to the documentation of this file.
1/* This file is part of the KDE project
2 *
3 * SPDX-FileCopyrightText: 2005-2010 Boudewijn Rempt <boud@valdyas.org>
4 * SPDX-FileCopyrightText: 2006-2008 Thomas Zander <zander@kde.org>
5 * SPDX-FileCopyrightText: 2006 Thorsten Zachmann <zachmann@kde.org>
6 * SPDX-FileCopyrightText: 2008 Jan Hambrecht <jaham@gmx.net>
7 *
8 * SPDX-License-Identifier: LGPL-2.0-or-later
9 */
10// flake
11#include "KoToolManager.h"
12#include "KoToolManager_p.h"
13#include "KoToolRegistry.h"
14#include "KoToolProxy.h"
15#include "KoToolProxy_p.h"
16#include "KoSelection.h"
17#include "KoCanvasController.h"
18#include "KoShape.h"
19#include "KoShapeLayer.h"
20#include "KoShapeRegistry.h"
21#include "KoShapeManager.h"
23#include "KoCanvasBase.h"
24#include "KoPointerEvent.h"
25#include "tools/KoZoomTool.h"
26#include "kis_action_registry.h"
27#include "KoToolFactoryBase.h"
28#include "kis_assert.h"
30
32
33// Qt + kde
34#include <QWidget>
35#include <QEvent>
36#include <QWheelEvent>
37#include <QMouseEvent>
38#include <QPaintEvent>
39#include <QTabletEvent>
40#include <QVBoxLayout>
41#include <QStringList>
42#include <QApplication>
43#include <kactioncollection.h>
44#include <kactioncategory.h>
45#include <FlakeDebug.h>
46
47#include <QAction>
48#include <klocalizedstring.h>
49#include <QKeySequence>
50#include <QStack>
51#include <QLabel>
52#include <QGlobalStatic>
53
55
56
58{
59public:
61 : activeTool(0),
62 canvas(cc),
63 inputDevice(id),
64 dummyToolWidget(0),
65 dummyToolLabel(0)
66 {
67 }
68
70 {
71 // the dummy tool widget does not necessarily have a parent and we create it, so we delete it.
72 delete dummyToolWidget;
73 }
74
76 {
77 toolActions.clear();
78 disabledGlobalActions.clear();
79
80 KisKActionCollection *windowActionCollection = canvas->actionCollection();
81
82 if (!windowActionCollection) {
83 qWarning() << "We haven't got an action collection";
84 return;
85 }
86
87 QStringList globalActions;
88
89 QMap<QKeySequence, QStringList> shortcutMap;
90
91// qDebug() << "................... activating tool" << activeToolId;
92
93 Q_FOREACH(QAction *action, windowActionCollection->actions()) {
94
95 if (action->property("tool_action").isValid()) {
96 QStringList tools = action->property("tool_action").toStringList();
97
98 if (KoToolRegistry::instance()->keys().contains(action->objectName())) {
99 //qDebug() << "This action needs to be enabled!";
100 action->setEnabled(true);
101 toolActions << action->objectName();
102 }
103 else {
104 if (tools.contains(activeToolId) || action->property("always_enabled").toBool()) {
105 //qDebug() << "\t\tenabling";
106 action->setEnabled(true);
107 toolActions << action->objectName();
108 }
109 else {
110 //qDebug() << "\t\tDISabling";
111 action->setDisabled(true);
112 }
113 }
114 }
115 else {
116 globalActions << action->objectName();
117 }
118
119 Q_FOREACH(QKeySequence keySequence, action->shortcuts()) {
120 // After loading a custom shortcut profile, shortcuts can be defined as an empty string, which is not an empty shortcut
121 if (keySequence.toString() != "") {
122 if (shortcutMap.contains(keySequence)) {
123 shortcutMap[keySequence].append(action->objectName());
124 }
125 else {
126 shortcutMap[keySequence] = QStringList() << action->objectName();
127 }
128 }
129 }
130 }
131
132 // Make sure the tool's actions override the global actions that aren't associated with the tool.
133 Q_FOREACH(const QKeySequence &k, shortcutMap.keys()) {
134 if (shortcutMap[k].size() > 1) {
135 QStringList actions = shortcutMap[k];
136 //qDebug() << k << actions;
137 bool toolActionFound = false;
138 Q_FOREACH(const QString &action, actions) {
139 if (toolActions.contains(action)) {
140 toolActionFound = true;
141 }
142 }
143 Q_FOREACH(const QString &action, actions) {
144 if (toolActionFound && globalActions.contains(action)) {
145 //qDebug() << "\tdisabling global action" << action;
146 windowActionCollection->action(action)->setEnabled(false);
147 disabledGlobalActions << action;
148 }
149 }
150 //qDebug() << k << shortcutMap[k];
151 }
152 }
153
154 windowActionCollection->readSettings(); // The shortcuts might have been configured in the meantime.
155 }
156
158 {
159 if (!activeTool)
160 return;
161
162 //qDebug() << "............... deactivating previous tool because activating" << activeToolId;
163
164 KisKActionCollection *windowActionCollection = canvas->actionCollection();
165
166 Q_FOREACH(const QString &action, toolActions) {
167 //qDebug() << "disabling" << action;
168 windowActionCollection->action(action)->setDisabled(true);
169 }
170 Q_FOREACH(const QString &action, disabledGlobalActions) {
171 //qDebug() << "enabling" << action;
172 windowActionCollection->action(action)->setEnabled(true);
173 }
174 }
175
176 KoToolBase *activeTool; // active Tool
177 QString activeToolId; // the id of the active Tool
178 QString activationShapeId; // the shape-type (KoShape::shapeId()) the activeTool 'belongs' to.
179 QHash<QString, KoToolBase*> allTools; // all the tools that are created for this canvas.
180 QList<KoToolBase*> mostRecentTools; // ordered unique list of tools starting from the most recently used, except for the active tool.
183 QWidget *dummyToolWidget; // the widget shown in the toolDocker.
187};
188
189
190// ******** KoToolManager **********
192 : QObject(),
193 d(new Private(this))
194{
195 connect(QApplication::instance(), SIGNAL(focusChanged(QWidget*,QWidget*)),
196 this, SLOT(movedFocus(QWidget*,QWidget*)));
197}
198
200{
201 delete d;
202}
203
205{
206 return d->toolActionList;
207}
208
210{
211 if (d->canvasses.contains(controller)) {
212 d->switchTool(d->canvasses.value(controller).first()->activeToolId);
213 }
214}
215
217{
218 return d->inputDevice;
219}
220
222{
223 d->setup();
224}
225
227{
228 Q_ASSERT(controller);
229 if (d->canvasses.contains(controller))
230 return;
231 d->setup();
232 d->attachCanvas(controller);
233 connect(controller->proxyObject, SIGNAL(destroyed(QObject*)), this, SLOT(attemptCanvasControllerRemoval(QObject*)));
234 connect(controller->proxyObject, SIGNAL(canvasRemoved(KoCanvasController*)), this, SLOT(detachCanvas(KoCanvasController*)));
235 connect(controller->proxyObject, SIGNAL(canvasSet(KoCanvasController*)), this, SLOT(attachCanvas(KoCanvasController*)));
236}
237
239{
240 Q_ASSERT(controller);
241 disconnect(controller->proxyObject, SIGNAL(canvasRemoved(KoCanvasController*)), this, SLOT(detachCanvas(KoCanvasController*)));
242 disconnect(controller->proxyObject, SIGNAL(canvasSet(KoCanvasController*)), this, SLOT(attachCanvas(KoCanvasController*)));
243 d->detachCanvas(controller);
244}
245
247{
248 KoCanvasControllerProxyObject* controllerActual = qobject_cast<KoCanvasControllerProxyObject*>(controller);
249 if (controllerActual) {
250 removeCanvasController(controllerActual->canvasController());
251 }
252}
253
255{
256 d->switchTool(id);
257}
258
260{
261 if (!d->canvasData) return;
262 d->switchInputDevice(id);
263}
264
266{
267 if (!d->canvasData) return;
268 if (d->canvasData->mostRecentTools.isEmpty()) return;
269 d->switchTool(d->canvasData->mostRecentTools.first()->toolId());
270}
271
272KoToolBase *KoToolManager::toolById(KoCanvasBase *canvas, const QString &id) const
273{
274 Q_ASSERT(canvas);
275 Q_FOREACH (KoCanvasController *controller, d->canvasses.keys()) {
276 if (controller->canvas() == canvas)
277 return d->canvasData->allTools.value(id);
278 }
279 return 0;
280}
281
283{
284 if (! d->canvasData) return 0;
285 return d->canvasData->canvas;
286}
287
289{
290 QSet<QString> shapeTypes;
291 Q_FOREACH (KoShape *shape, shapes) {
292 shapeTypes << shape->shapeId();
293 }
294 //KritaUtils::makeContainerUnique(types);
295
296 QString toolType = KoInteractionTool_ID;
297 int prio = INT_MAX;
298 Q_FOREACH (KoToolAction *helper, d->toolActionList) {
299 if (helper->priority() >= prio)
300 continue;
301
302 bool toolWillWork = false;
303 foreach (const QString &type, shapeTypes) {
304 if (helper->toolFactory()->activationShapeId().split(',').contains(type)) {
305 toolWillWork = true;
306 break;
307 }
308 }
309
310 if (toolWillWork) {
311 toolType = helper->id();
312 prio = helper->priority();
313 }
314 }
315 return toolType;
316}
317
319{
320 KIS_ASSERT_RECOVER_RETURN(d->canvasData);
321
322 // make a full reconnect cycle for the currently active tool
323 d->disconnectActiveTool();
324 d->connectActiveTool();
325 d->postSwitchTool();
326}
327
329{
330 Q_FOREACH (const QList<CanvasData*> &canvasDataList, d->canvasses) {
331 Q_FOREACH (CanvasData *canvasData, canvasDataList) {
332 Q_FOREACH (KoToolBase *tool, canvasData->allTools) {
334 }
335 }
336 }
337}
338
340{
341 return s_instance;
342}
343
345{
346 if (!d->canvasData) return QString();
347 return d->canvasData->activeToolId;
348}
349
351{
352 tool->setConverter(converter);
353}
354
356{
357 tool->setAbstractResource(abstractResource);
358}
359
360
365
366
367/**** KoToolManager::Private ****/
368
369KoToolManager::Private::Private(KoToolManager *qq)
370 : q(qq),
371 canvasData(0),
372 layerExplicitlyDisabled(false)
373{
374}
375
376KoToolManager::Private::~Private()
377{
378 qDeleteAll(toolActionList);
379}
380
381// helper method.
382CanvasData *KoToolManager::Private::createCanvasData(KoCanvasController *controller, const KoInputDevice &device)
383{
384 QHash<QString, KoToolBase*> toolsHash;
385 Q_FOREACH (KoToolAction *toolAction, toolActionList) {
386 KoToolBase* tool = createTool(controller, toolAction);
387 if (tool) { // only if a real tool was created
388 toolsHash.insert(tool->toolId(), tool);
389 Q_EMIT q->createOpacityResource(tool->isOpacityPresetMode(), tool);
390 }
391 }
392
393 CanvasData *cd = new CanvasData(controller, device);
394 cd->allTools = toolsHash;
395 return cd;
396}
397
398KoToolBase *KoToolManager::Private::createTool(KoCanvasController *controller, KoToolAction *toolAction)
399{
400 QHash<QString, KoToolBase*> origHash;
401
402 if (canvasses.contains(controller)) {
403 origHash = canvasses.value(controller).first()->allTools;
404 }
405
406 if (origHash.contains(toolAction->id())) {
407 return origHash.value(toolAction->id());
408 }
409
410 debugFlake << "Creating tool" << toolAction->id() << ". Activated on:" << toolAction->visibilityCode() << ", prio:" << toolAction->priority();
411
412 KoToolBase *tool = toolAction->toolFactory()->createTool(controller->canvas());
413 if (tool) {
414 tool->setFactory(toolAction->toolFactory());
415 tool->setObjectName(toolAction->id());
416 }
417
418 KoZoomTool *zoomTool = dynamic_cast<KoZoomTool*>(tool);
419 if (zoomTool) {
420 zoomTool->setCanvasController(controller);
421 }
422
423 return tool;
424}
425
426void KoToolManager::Private::setup()
427{
428 if (toolActionList.size() > 0)
429 return;
430
433 Q_FOREACH (const QString & id, registry->keys()) {
434 toolActionList.append(new KoToolAction(registry->value(id)));
435 }
436}
437
438void KoToolManager::Private::connectActiveTool()
439{
440 if (canvasData->activeTool) {
441 connect(canvasData->activeTool, SIGNAL(cursorChanged(QCursor)),
442 q, SLOT(updateCursor(QCursor)));
443 connect(canvasData->activeTool, SIGNAL(activateTool(QString)),
444 q, SLOT(switchToolRequested(QString)));
445 connect(canvasData->activeTool, SIGNAL(statusTextChanged(QString)),
446 q, SIGNAL(changedStatusText(QString)));
447 connect(canvasData->activeTool, SIGNAL(textModeChanged(bool)),
448 q, SIGNAL(textModeChanged(bool)));
449
450 {
451 KoCanvasResourceProvider *resourceManager = canvasData->canvas->canvas()->resourceManager();
452
453 const QHash<int, KoAbstractCanvasResourceInterfaceSP> abstractResources =
454 canvasData->activeTool->toolAbstractResources();
455 const QHash<int, KoDerivedResourceConverterSP> converters = canvasData->activeTool->toolConverters();
456 for (KoAbstractCanvasResourceInterfaceSP abstractResource : abstractResources) {
457 resourceManager->setAbstractResource(abstractResource);
458 }
459 for (KoDerivedResourceConverterSP converter : converters) {
460 resourceManager->addDerivedResourceConverter(converter);
461 }
462 }
463 }
464
465 // we expect the tool to Q_EMIT a cursor on activation.
466 updateCursor(Qt::BlankCursor);
467}
468
469
470
471void KoToolManager::Private::disconnectActiveTool()
472{
473 if (canvasData->activeTool) {
474 {
475 KoCanvasResourceProvider *resourceManager = canvasData->canvas->canvas()->resourceManager();
476
477 const QList<int> abstractKeys = canvasData->activeTool->toolAbstractResources().keys();
478 const QList<int> derivedKeys = canvasData->activeTool->toolConverters().keys();
479 for (int key : abstractKeys) {
480 if (resourceManager->hasAbstractResource(key))
481 resourceManager->removeAbstractResource(key);
482 }
483 for (int key : derivedKeys) {
484 if (resourceManager->hasDerivedResourceConverter(key))
485 resourceManager->removeDerivedResourceConverter(key);
486 }
487 }
488
489 canvasData->deactivateToolActions();
490 // repaint the decorations before we deactivate the tool as it might deleted
491 // data needed for the repaint
492 Q_EMIT q->aboutToChangeTool(canvasData->canvas);
493 canvasData->activeTool->deactivate();
494 disconnect(canvasData->activeTool, SIGNAL(cursorChanged(QCursor)),
495 q, SLOT(updateCursor(QCursor)));
496 disconnect(canvasData->activeTool, SIGNAL(activateTool(QString)),
497 q, SLOT(switchToolRequested(QString)));
498 disconnect(canvasData->activeTool, SIGNAL(statusTextChanged(QString)),
499 q, SIGNAL(changedStatusText(QString)));
500 disconnect(canvasData->activeTool, SIGNAL(textModeChanged(bool)),
501 q, SIGNAL(textModeChanged(bool)));
502 }
503
504 // Q_EMIT a empty status text to clear status text from last active tool
505 Q_EMIT q->changedStatusText(QString());
506}
507
508void KoToolManager::Private::switchTool(const QString &id)
509{
510 if (!canvasData) return;
511
512 canvasData->activeToolId = id;
513 KoToolBase *tool = canvasData->allTools.value(id);
514 if (! tool) {
515 return;
516 }
517
518 canvasData->activationShapeId = tool->factory()->activationShapeId();
519
520 if (canvasData->activeTool == tool && tool->toolId() != KoInteractionTool_ID)
521 return;
522
523 disconnectActiveTool();
524
525 if (canvasData->activeTool) {
526 canvasData->mostRecentTools.prepend(canvasData->activeTool);
527 }
528 canvasData->activeTool = tool;
529 canvasData->mostRecentTools.removeOne(tool);
530
531 connectActiveTool();
532 postSwitchTool();
533}
534
535void KoToolManager::Private::postSwitchTool()
536{
537#ifndef NDEBUG
538 int canvasCount = 1;
539 Q_FOREACH (QList<CanvasData*> list, canvasses) {
540 bool first = true;
541 Q_FOREACH (CanvasData *data, list) {
542 if (first) {
543 debugFlake << "Canvas" << canvasCount++;
544 }
545 debugFlake << " +- Tool:" << data->activeToolId << (data == canvasData ? " *" : "");
546 first = false;
547 }
548 }
549#endif
550 Q_ASSERT(canvasData);
551 if (!canvasData) return;
552
553 QSet<KoShape*> shapesToOperateOn;
554 if (canvasData->activeTool
555 && canvasData->activeTool->canvas()
556 && canvasData->activeTool->canvas()->shapeManager()) {
557 KoSelection *selection = canvasData->activeTool->canvas()->shapeManager()->selection();
558 Q_ASSERT(selection);
559 QList<KoShape *> shapesDelegatesList = selection->selectedEditableShapesAndDelegates();
560 if (!shapesDelegatesList.isEmpty()) {
561 shapesToOperateOn = QSet<KoShape*>(shapesDelegatesList.begin(),
562 shapesDelegatesList.end());
563 }
564 }
565
566 if (canvasData->canvas->canvas()) {
567 // Caller of postSwitchTool expect this to be called to update the selected tool
568 updateToolForProxy();
569
570 // Activate the actions for the currently active tool
571 //
572 // We should do that **before** calling tool->activate(),
573 // because the tool may have its own logic on activation
574 // of the actions.
575 canvasData->activateToolActions();
576
577 canvasData->activeTool->activate(shapesToOperateOn);
578 } else {
579
580 // Activate the actions for the currently active tool
581 //
582 // We should do that **before** calling tool->activate(),
583 // because the tool may have its own logic on activation
584 // of the actions.
585 canvasData->activateToolActions();
586
587 canvasData->activeTool->activate(shapesToOperateOn);
588 }
589
590 QList<QPointer<QWidget> > optionWidgetList = canvasData->activeTool->optionWidgets();
591 if (optionWidgetList.empty()) { // no option widget.
592 QWidget *toolWidget;
593 QString title = canvasData->activeTool->factory()->toolTip();
594 toolWidget = canvasData->dummyToolWidget;
595 if (toolWidget == 0) {
596 toolWidget = new QWidget();
597 toolWidget->setObjectName("DummyToolWidget");
598 QVBoxLayout *layout = new QVBoxLayout(toolWidget);
599 layout->setContentsMargins(3, 3, 3, 3);
600 canvasData->dummyToolLabel = new QLabel(toolWidget);
601 layout->addWidget(canvasData->dummyToolLabel);
602 layout->addItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::Expanding));
603 canvasData->dummyToolWidget = toolWidget;
604 }
605 canvasData->dummyToolLabel->setText(i18n("Active tool: %1", title));
606 optionWidgetList.append(toolWidget);
607 }
608
609
610 Q_EMIT q->changedTool(canvasData->canvas);
611
612 Q_EMIT q->toolOptionWidgetsChanged(canvasData->canvas, optionWidgetList);
613}
614
615
616void KoToolManager::Private::switchCanvasData(CanvasData *cd)
617{
618 Q_ASSERT(cd);
619
620 KoCanvasBase *oldCanvas = 0;
621 KoInputDevice oldInputDevice;
622
623 if (canvasData) {
624 oldCanvas = canvasData->canvas->canvas();
625 oldInputDevice = canvasData->inputDevice;
626
627 if (canvasData->activeTool) {
628 disconnectActiveTool();
629 }
630
631 KoToolProxy *proxy = proxies.value(oldCanvas);
632 Q_ASSERT(proxy);
633 proxy->setActiveTool(0);
634 }
635
636 canvasData = cd;
637 inputDevice = canvasData->inputDevice;
638
639 if (canvasData->activeTool) {
640 connectActiveTool();
641 postSwitchTool();
642 }
643
644 if (oldInputDevice != canvasData->inputDevice) {
645 Q_EMIT q->inputDeviceChanged(canvasData->inputDevice);
646 }
647
648 if (oldCanvas != canvasData->canvas->canvas()) {
649 Q_EMIT q->changedCanvas(canvasData->canvas->canvas());
650 }
651}
652
653void KoToolManager::Private::detachCanvas(KoCanvasController *controller)
654{
655 Q_ASSERT(controller);
656 // check if we are removing the active canvas controller
657 if (canvasData && canvasData->canvas == controller) {
658 KoCanvasController *newCanvas = 0;
659 // try to find another canvas controller beside the one we are removing
660 Q_FOREACH (KoCanvasController* canvas, canvasses.keys()) {
661 if (canvas != controller) {
662 // yay found one
663 newCanvas = canvas;
664 break;
665 }
666 }
667 if (newCanvas) {
668 switchCanvasData(canvasses.value(newCanvas).first());
669 } else {
670 disconnectActiveTool();
671 Q_EMIT q->toolOptionWidgetsChanged(controller, QList<QPointer<QWidget> >());
672 // as a last resort just set a blank one
673 canvasData = 0;
674 }
675 }
676
677 KoToolProxy *proxy = proxies.value(controller->canvas());
678 if (proxy)
679 proxy->setActiveTool(0);
680
682 Q_FOREACH (CanvasData *canvasData, canvasses.value(controller)) {
683 Q_FOREACH (KoToolBase *tool, canvasData->allTools) {
684 if (! tools.contains(tool)) {
685 tools.append(tool);
686 }
687 }
688 delete canvasData;
689 }
690 Q_FOREACH (KoToolBase *tool, tools) {
691 delete tool;
692 }
693 canvasses.remove(controller);
694 Q_EMIT q->changedCanvas(canvasData ? canvasData->canvas->canvas() : 0);
695}
696
697void KoToolManager::Private::attachCanvas(KoCanvasController *controller)
698{
699 Q_ASSERT(controller);
700 CanvasData *cd = createCanvasData(controller, KoInputDevice::mouse());
701
702 // switch to new canvas as the active one.
703 switchCanvasData(cd);
704
705 inputDevice = cd->inputDevice;
706 QList<CanvasData*> canvasses_;
707 canvasses_.append(cd);
708 canvasses[controller] = canvasses_;
709
710 KoToolProxy *tp = proxies[controller->canvas()];
711 if (tp)
712 tp->priv()->setCanvasController(controller);
713
714 if (cd->activeTool == 0) {
715 // no active tool, so we activate the highest priority main tool
716 int highestPriority = INT_MAX;
717 KoToolAction * helper = 0;
718 Q_FOREACH (KoToolAction * th, toolActionList) {
719 if (th->section() == ToolBoxSection::Main) {
720 if (th->priority() < highestPriority) {
721 highestPriority = qMin(highestPriority, th->priority());
722 helper = th;
723 }
724 }
725 }
726 if (helper)
727 switchTool(helper->id());
728 }
729
730 Connector *connector = new Connector(controller->canvas()->shapeManager());
731 connect(connector, SIGNAL(selectionChanged(QList<KoShape*>)), q,
732 SLOT(selectionChanged(QList<KoShape*>)));
733 connect(controller->canvas()->selectedShapesProxy(),
734 SIGNAL(currentLayerChanged(const KoShapeLayer*)),
735 q, SLOT(currentLayerChanged(const KoShapeLayer*)));
736
737 Q_EMIT q->changedCanvas(canvasData ? canvasData->canvas->canvas() : 0);
738}
739
740void KoToolManager::Private::movedFocus(QWidget *from, QWidget *to)
741{
742 Q_UNUSED(from);
743 // no canvas anyway or no focus set anyway?
744 if (!canvasData || to == 0) {
745 return;
746 }
747
748 // focus returned to current canvas?
749 if (to == canvasData->canvas->canvas()->canvasWidget()) {
750 // nothing to do
751 return;
752 }
753
754 // if the 'to' is one of our canvasWidgets, then switch.
755
756 // for code simplicity the current canvas will be checked again,
757 // but would have been caught already in the lines above, so no issue
758 KoCanvasController *newCanvas = 0;
759 Q_FOREACH (KoCanvasController* canvas, canvasses.keys()) {
760 if (canvas->canvas()->canvasWidget() == to) {
761 newCanvas = canvas;
762 break;
763 }
764 }
765
766 // none of our canvasWidgets got focus?
767 if (newCanvas == 0) {
768 return;
769 }
770
771 // switch to canvasdata matching inputdevice used last with this app instance
772 Q_FOREACH (CanvasData *data, canvasses.value(newCanvas)) {
773 if (data->inputDevice == inputDevice) {
774 switchCanvasData(data);
775 return;
776 }
777 }
778 // if no such inputDevice for this canvas, then simply fallback to first one
779 switchCanvasData(canvasses.value(newCanvas).first());
780}
781
782void KoToolManager::Private::updateCursor(const QCursor &cursor)
783{
784 Q_ASSERT(canvasData);
785 Q_ASSERT(canvasData->canvas);
786 Q_ASSERT(canvasData->canvas->canvas());
787 canvasData->canvas->canvas()->setCursor(cursor);
788}
789
790void KoToolManager::Private::selectionChanged(const QList<KoShape*> &shapes)
791{
792 QList<QString> types;
793 Q_FOREACH (KoShape *shape, shapes) {
794 QSet<KoShape*> delegates = shape->toolDelegates();
795 if (delegates.isEmpty()) { // no delegates, just the orig shape
796 delegates << shape;
797 }
798
799 foreach (KoShape *shape2, delegates) {
800 Q_ASSERT(shape2);
801 if (! types.contains(shape2->shapeId())) {
802 types.append(shape2->shapeId());
803 }
804 }
805 }
806
807 // check if there is still a shape selected the active tool can work on
808 // there needs to be at least one shape for a tool without an activationShapeId
809 // to work
810 // if not change the current tool to the default tool
811
812 const QStringList activationShapeIds = canvasData->activationShapeId.split(',');
813
814 if (!(canvasData->activationShapeId.isNull() && shapes.size() > 0)
815 && !activationShapeIds.contains("flake/always")
816 && !activationShapeIds.contains("flake/edit")) {
817
818 bool currentToolWorks = false;
819 foreach (const QString &type, types) {
820 if (activationShapeIds.contains(type)) {
821 currentToolWorks = true;
822 break;
823 }
824 }
825 if (!currentToolWorks) {
826 switchTool(KoInteractionTool_ID);
827 }
828 }
829
830 Q_EMIT q->toolCodesSelected(types);
831}
832
833void KoToolManager::Private::currentLayerChanged(const KoShapeLayer *layer)
834{
835 Q_EMIT q->currentLayerChanged(canvasData->canvas, layer);
836 layerExplicitlyDisabled = layer && !layer->isShapeEditable();
837 updateToolForProxy();
838
839 debugFlake << "Layer changed to" << layer << "explicitly disabled:" << layerExplicitlyDisabled;
840}
841
842void KoToolManager::Private::updateToolForProxy()
843{
844 KoToolProxy *proxy = proxies.value(canvasData->canvas->canvas());
845 if(!proxy) return;
846
847 bool canUseTool = !layerExplicitlyDisabled || canvasData->activationShapeId.endsWith(QLatin1String("/always"));
848 proxy->setActiveTool(canUseTool ? canvasData->activeTool : 0);
849}
850
851void KoToolManager::Private::switchInputDevice(const KoInputDevice &device)
852{
853 Q_ASSERT(canvasData);
854 if (!canvasData) return;
855 if (inputDevice == device) return;
856 if (inputDevice.isMouse() && device.isMouse()) return;
857 if (device.isMouse() && !inputDevice.isMouse()) {
858 // we never switch back to mouse from a tablet input device, so the user can use the
859 // mouse to edit the settings for a tool activated by a tablet. See bugs
860 // https://bugs.kde.org/show_bug.cgi?id=283130 and https://bugs.kde.org/show_bug.cgi?id=285501.
861 // We do continue to switch between tablet devices, thought.
862 return;
863 }
864
865 QList<CanvasData*> items = canvasses[canvasData->canvas];
866
867 // search for a canvasdata object for the current input device
868 Q_FOREACH (CanvasData *cd, items) {
869 if (cd->inputDevice == device) {
870 switchCanvasData(cd);
871
872 if (!canvasData->activeTool) {
873 switchTool(KoInteractionTool_ID);
874 }
875
876 return;
877 }
878 }
879
880 // still here? That means we need to create a new CanvasData instance with the current InputDevice.
881 CanvasData *cd = createCanvasData(canvasData->canvas, device);
882 // switch to new canvas as the active one.
883 QString oldTool = canvasData->activeToolId;
884
885 items.append(cd);
886 canvasses[cd->canvas] = items;
887
888 switchCanvasData(cd);
889
890 switchTool(oldTool);
891}
892
893void KoToolManager::Private::registerToolProxy(KoToolProxy *proxy, KoCanvasBase *canvas)
894{
895 proxies.insert(canvas, proxy);
896 Q_FOREACH (KoCanvasController *controller, canvasses.keys()) {
897 if (controller->canvas() == canvas) {
898 proxy->priv()->setCanvasController(controller);
899 break;
900 }
901 }
902}
903
904//have to include this because of Q_PRIVATE_SLOT
905#include "moc_KoToolManager.cpp"
#define debugFlake
Definition FlakeDebug.h:15
QList< QString > QStringList
Q_GLOBAL_STATIC(KisStoragePluginRegistry, s_instance)
#define KoInteractionTool_ID
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))
QList< KoToolBase * > mostRecentTools
const KoInputDevice inputDevice
QStringList disabledGlobalActions
QString activationShapeId
QWidget * dummyToolWidget
KoToolBase * activeTool
QHash< QString, KoToolBase * > allTools
QString activeToolId
void deactivateToolActions()
QLabel * dummyToolLabel
CanvasData(KoCanvasController *cc, const KoInputDevice &id)
void activateToolActions()
KoCanvasController *const canvas
QStringList toolActions
A container for a set of QAction objects.
QAction * action(int index) const
QList< QAction * > actions() const
virtual void setCursor(const QCursor &cursor)=0
virtual KoShapeManager * shapeManager() const =0
virtual QWidget * canvasWidget()=0
QPointer< KoCanvasResourceProvider > resourceManager
virtual KoSelectedShapesProxy * selectedShapesProxy() const =0
selectedShapesProxy() is a special interface for keeping a persistent connections to selectionChanged...
KoCanvasController * canvasController() const
QPointer< KoCanvasControllerProxyObject > proxyObject
virtual KoCanvasBase * canvas() const
void setAbstractResource(KoAbstractCanvasResourceInterfaceSP abstractResource)
void addDerivedResourceConverter(KoDerivedResourceConverterSP converter)
const T value(const QString &id) const
QList< QString > keys() const
bool isMouse() const
const QList< KoShape * > selectedEditableShapesAndDelegates() const
static KoShapeRegistry * instance()
QString shapeId() const
Definition KoShape.cpp:1057
virtual bool isShapeEditable(bool recursive=true) const
checks recursively if the shape or one of its parents is not visible or locked
Definition KoShape.cpp:1165
QSet< KoShape * > toolDelegates() const
Definition KoShape.cpp:1310
QString section() const
The section the tool wants to be in.
KoToolFactoryBase * toolFactory
QString id() const
The id of the tool.
int priority() const
Lower number (higher priority) means coming first in the section.
QString visibilityCode() const
This tool should become visible when we Q_EMIT this string in toolCodesSelected()
void setConverter(KoDerivedResourceConverterSP converter)
Q_INVOKABLE QString toolId() const
void updateOptionsWidgetIcons()
bool isOpacityPresetMode() const
void setAbstractResource(KoAbstractCanvasResourceInterfaceSP abstractResource)
void setFactory(KoToolFactoryBase *factory)
KoToolFactoryBase * factory() const
virtual KoToolBase * createTool(KoCanvasBase *canvas)=0
QString activationShapeId() const
QList< KoToolAction * > toolActionList
KoToolBase * toolById(KoCanvasBase *canvas, const QString &id) const
CanvasData * canvasData
void movedFocus(QWidget *from, QWidget *to)
void switchInputDeviceRequested(const KoInputDevice &id)
void attachCanvas(KoCanvasController *controller)
void switchToolRequested(const QString &id)
void setConverter(KoDerivedResourceConverterSP converter, KoToolBase *tool)
void detachCanvas(KoCanvasController *controller)
~KoToolManager() override
QString preferredToolForSelection(const QList< KoShape * > &shapes)
void setAbstractResource(KoAbstractCanvasResourceInterfaceSP abstractResource, KoToolBase *tool)
Private *const d
Private(KoToolManager *qq)
void initializeCurrentToolForCanvas()
void removeCanvasController(KoCanvasController *controller)
void requestToolActivation(KoCanvasController *controller)
Request tool activation for the given canvas controller.
KoToolManager::Private * priv()
void addController(KoCanvasController *controller)
void switchBackRequested()
KoCanvasController * activeCanvasController() const
QString activeToolId() const
Returns the toolId of the currently active tool.
static KoToolManager * instance()
Return the toolmanager singleton.
KoInputDevice currentInputDevice() const
Q_SLOT void attemptCanvasControllerRemoval(QObject *controller)
void initializeToolActions()
void setCanvasController(KoCanvasController *controller)
the toolManager tells us which KoCanvasController this toolProxy is working for.
KoToolProxyPrivate * priv()
virtual void setActiveTool(KoToolBase *tool)
Set the new active tool.
static KoToolRegistry * instance()
void setCanvasController(KoCanvasController *controller)
Definition KoZoomTool.h:35
#define KIS_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:75
KRITAWIDGETUTILS_EXPORT void updateCursor(QWidget *source, QScroller::State state)
static const QString Main
Tools that only work on vector shapes.
KisCanvas2 * canvas