Warning, file /office/calligra/libs/flake/KoToolManager.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /* This file is part of the KDE project
0002  *
0003  * Copyright (c) 2005-2010 Boudewijn Rempt <boud@valdyas.org>
0004  * Copyright (C) 2006-2008 Thomas Zander <zander@kde.org>
0005  * Copyright (C) 2006 Thorsten Zachmann <zachmann@kde.org>
0006  * Copyright (C) 2008 Jan Hambrecht <jaham@gmx.net>
0007  *
0008  * This library is free software; you can redistribute it and/or
0009  * modify it under the terms of the GNU Library General Public
0010  * License as published by the Free Software Foundation; either
0011  * version 2 of the License, or (at your option) any later version.
0012  *
0013  * This library is distributed in the hope that it will be useful,
0014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0016  * Library General Public License for more details.
0017  *
0018  * You should have received a copy of the GNU Library General Public License
0019  * along with this library; see the file COPYING.LIB.  If not, write to
0020  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0021  * Boston, MA 02110-1301, USA.
0022  */
0023 
0024 // flake
0025 #include "KoToolManager.h"
0026 #include "KoToolManager_p.h"
0027 
0028 #include "KoToolRegistry.h"
0029 #include "KoToolProxy.h"
0030 #include "KoToolProxy_p.h"
0031 #include "KoSelection.h"
0032 #include "KoCanvasController.h"
0033 #include "KoCanvasControllerWidget.h"
0034 #include "KoShape.h"
0035 #include "KoShapeLayer.h"
0036 #include "KoShapeRegistry.h"
0037 #include "KoShapeManager.h"
0038 #include "KoCanvasBase.h"
0039 #include "KoInputDeviceHandlerRegistry.h"
0040 #include "KoInputDeviceHandlerEvent.h"
0041 #include "KoPointerEvent.h"
0042 #include "tools/KoCreateShapesTool.h"
0043 #include "tools/KoZoomTool.h"
0044 #include "tools/KoPanTool.h"
0045 #include "FlakeDebug.h"
0046 
0047 // KF5
0048 #include <KActionCollection>
0049 #include <KLocalizedString>
0050 
0051 // Qt
0052 #include <QWidget>
0053 #include <QEvent>
0054 #include <QWheelEvent>
0055 #include <QMouseEvent>
0056 #include <QPaintEvent>
0057 #include <QTabletEvent>
0058 #include <QKeyEvent>
0059 #include <QVBoxLayout>
0060 #include <QStringList>
0061 #include <QApplication>
0062 #include <QAction>
0063 #include <QKeySequence>
0064 #include <QStack>
0065 #include <QLabel>
0066 #include <QGlobalStatic>
0067 
0068 Q_GLOBAL_STATIC(KoToolManager, s_instance)
0069 
0070 class Q_DECL_HIDDEN KoToolAction::Private
0071 {
0072 public:
0073     ToolHelper* toolHelper;
0074 };
0075 
0076 KoToolAction::KoToolAction(ToolHelper* toolHelper)
0077     : QObject(toolHelper)
0078     , d(new Private)
0079 {
0080     d->toolHelper = toolHelper;
0081 }
0082 
0083 KoToolAction::~KoToolAction()
0084 {
0085     delete d;
0086 }
0087 
0088 void KoToolAction::trigger()
0089 {
0090     d->toolHelper->activate();
0091 }
0092 
0093 
0094 QString KoToolAction::iconText() const
0095 {
0096     return d->toolHelper->iconText();
0097 }
0098 
0099 QString KoToolAction::toolTip() const
0100 {
0101     return d->toolHelper->toolTip();
0102 }
0103 
0104 QString KoToolAction::id() const
0105 {
0106     return d->toolHelper->id();
0107 }
0108 
0109 QString KoToolAction::iconName() const
0110 {
0111     return d->toolHelper->iconName();
0112 }
0113 
0114 QKeySequence KoToolAction::shortcut() const
0115 {
0116     return d->toolHelper->shortcut();
0117 }
0118 
0119 
0120 QString KoToolAction::section() const
0121 {
0122     return d->toolHelper->toolType();
0123 }
0124 
0125 int KoToolAction::priority() const
0126 {
0127     return d->toolHelper->priority();
0128 }
0129 
0130 int KoToolAction::buttonGroupId() const
0131 {
0132     return d->toolHelper->uniqueId();
0133 }
0134 
0135 QString KoToolAction::visibilityCode() const
0136 {
0137     return d->toolHelper->activationShapeId();
0138 }
0139 
0140 
0141 class CanvasData
0142 {
0143 public:
0144     CanvasData(KoCanvasController *cc, const KoInputDevice &id)
0145             : activeTool(0),
0146             canvas(cc),
0147             inputDevice(id),
0148             dummyToolWidget(0),
0149             dummyToolLabel(0)
0150     {
0151     }
0152 
0153     ~CanvasData()
0154     {
0155         // the dummy tool widget does not necessarily have a parent and we create it, so we delete it.
0156         delete dummyToolWidget;
0157     }
0158 
0159     void activateToolActions()
0160     {
0161         disabledDisabledActions.clear();
0162         disabledActions.clear();
0163         disabledCanvasShortcuts.clear();
0164         // we do several things here
0165         // 1. enable the actions of the active tool
0166         // 2. disable conflicting actions
0167         // 3. replace conflicting actions in the action collection
0168         KActionCollection *canvasActionCollection = canvas->actionCollection();
0169 
0170         QHash<QString, QAction *> toolActions = activeTool->actions();
0171         QHash<QString, QAction *>::const_iterator it(toolActions.constBegin());
0172 
0173         for (; it != toolActions.constEnd(); ++it) {
0174             if (canvasActionCollection) {
0175 
0176                 QString toolActionID = it.key();
0177                 QAction *toolAction = it.value();
0178 
0179                 QAction * action = qobject_cast<QAction*>(canvasActionCollection->action(it.key()));
0180                 if (action) {
0181                     canvasActionCollection->takeAction(action);
0182                     if (action != it.value()) {
0183                         if (action->isEnabled()) {
0184                             action->setEnabled(false);
0185                             disabledActions.append(action);
0186                         } else  {
0187                             disabledDisabledActions.append(action);
0188                         }
0189                     }
0190                 }
0191                 foreach(QAction *a, canvasActionCollection->actions()) {
0192                     QAction *canvasAction = dynamic_cast<QAction*>(a);
0193                     if (canvasAction && !canvasAction->shortcut().toString().isEmpty() && canvasAction->shortcut() == toolAction->shortcut()) {
0194                         warnFlake << activeToolId << ": action" << toolActionID << "conflicts with canvas action" << canvasAction->objectName() << "shortcut:" << canvasAction->shortcut().toString();
0195                         disabledCanvasShortcuts[canvasAction] = canvasAction->shortcut().toString();
0196                         canvasAction->setShortcut(QKeySequence());
0197                     }
0198                 }
0199                 canvasActionCollection->addAction(toolActionID, toolAction);
0200             }
0201             it.value()->setEnabled(true);
0202         }
0203         canvasActionCollection->readSettings(); // The shortcuts might have been configured in the meantime.
0204     }
0205 
0206     void deactivateToolActions()
0207     {
0208 
0209         if (!activeTool)
0210             return;
0211         // disable actions of active tool
0212         foreach(QAction *action, activeTool->actions()) {
0213             action->setEnabled(false);
0214         }
0215 
0216         // enable actions which where disabled on activating the active tool
0217         // and re-add them to the action collection
0218         KActionCollection *ac = canvas->actionCollection();
0219         foreach(QPointer<QAction> action, disabledDisabledActions) {
0220             if (action) {
0221                 if (ac) {
0222                     ac->addAction(action->objectName(), action);
0223                 }
0224             }
0225         }
0226         disabledDisabledActions.clear();
0227 
0228         foreach(QPointer<QAction> action, disabledActions) {
0229             if (action) {
0230                 action->setEnabled(true);
0231                 if(ac) {
0232                     ac->addAction(action->objectName(), action);
0233                 }
0234             }
0235         }
0236         disabledActions.clear();
0237 
0238         QMap<QPointer<QAction>, QString>::const_iterator it(disabledCanvasShortcuts.constBegin());
0239         for (; it != disabledCanvasShortcuts.constEnd(); ++it) {
0240             QAction *action = it.key();
0241             QString shortcut = it.value();
0242             action->setShortcut(shortcut);
0243         }
0244         disabledCanvasShortcuts.clear();
0245     }
0246 
0247     KoToolBase *activeTool;     // active Tool
0248     QString activeToolId;   // the id of the active Tool
0249     QString activationShapeId; // the shape-type (KoShape::shapeId()) the activeTool 'belongs' to.
0250     QHash<QString, KoToolBase*> allTools; // all the tools that are created for this canvas.
0251     QStack<QString> stack; // stack of temporary tools
0252     KoCanvasController *const canvas;
0253     const KoInputDevice inputDevice;
0254     QWidget *dummyToolWidget;  // the widget shown in the toolDocker.
0255     QLabel *dummyToolLabel;
0256     QList<QPointer<QAction> > disabledActions; ///< disabled conflicting actions
0257     QList<QPointer<QAction> > disabledDisabledActions; ///< disabled conflicting actions that were already disabled
0258     QMap<QPointer<QAction>, QString> disabledCanvasShortcuts; ///< Shortcuts that were temporarily removed from canvas actions because the tool overrides
0259 };
0260 
0261 KoToolManager::Private::Private(KoToolManager *qq)
0262     : q(qq),
0263     canvasData(0),
0264     layerExplicitlyDisabled(false)
0265 {
0266 }
0267 
0268 KoToolManager::Private::~Private()
0269 {
0270     qDeleteAll(tools);
0271 }
0272 
0273 // helper method.
0274 CanvasData *KoToolManager::Private::createCanvasData(KoCanvasController *controller, const KoInputDevice &device)
0275 {
0276     QHash<QString, KoToolBase*> toolsHash;
0277     foreach(ToolHelper *tool, tools) {
0278         QPair<QString, KoToolBase*> toolPair = q->createTools(controller, tool);
0279         if (toolPair.second) { // only if a real tool was created
0280             toolsHash.insert(toolPair.first, toolPair.second);
0281         }
0282     }
0283     KoCreateShapesTool *createShapesTool = dynamic_cast<KoCreateShapesTool*>(toolsHash.value(KoCreateShapesTool_ID));
0284     Q_ASSERT(createShapesTool);
0285     QString id = KoShapeRegistry::instance()->keys()[0];
0286     createShapesTool->setShapeId(id);
0287 
0288     CanvasData *cd = new CanvasData(controller, device);
0289     cd->allTools = toolsHash;
0290     return cd;
0291 }
0292 
0293 void KoToolManager::Private::setup()
0294 {
0295     if (tools.size() > 0)
0296         return;
0297 
0298     KoShapeRegistry::instance();
0299     KoToolRegistry *registry = KoToolRegistry::instance();
0300     foreach(const QString & id, registry->keys()) {
0301         ToolHelper *t = new ToolHelper(registry->value(id));
0302         tools.append(t);
0303     }
0304 
0305     // connect to all tools so we can hear their button-clicks
0306     foreach(ToolHelper *tool, tools)
0307         connect(tool, SIGNAL(toolActivated(ToolHelper*)), q, SLOT(toolActivated(ToolHelper*)));
0308 
0309     // load pluggable input devices
0310     KoInputDeviceHandlerRegistry::instance();
0311 }
0312 
0313 void KoToolManager::Private::connectActiveTool()
0314 {
0315     if (canvasData->activeTool) {
0316         connect(canvasData->activeTool, SIGNAL(cursorChanged(QCursor)),
0317                 q, SLOT(updateCursor(QCursor)));
0318         connect(canvasData->activeTool, SIGNAL(activateTool(QString)),
0319                 q, SLOT(switchToolRequested(QString)));
0320         connect(canvasData->activeTool, SIGNAL(activateTemporary(QString)),
0321                 q, SLOT(switchToolTemporaryRequested(QString)));
0322         connect(canvasData->activeTool, SIGNAL(done()), q, SLOT(switchBackRequested()));
0323         connect(canvasData->activeTool, SIGNAL(statusTextChanged(QString)),
0324                 q, SIGNAL(changedStatusText(QString)));
0325     }
0326 
0327     // we expect the tool to emit a cursor on activation.
0328     updateCursor(Qt::ForbiddenCursor);
0329 }
0330 
0331 void KoToolManager::Private::disconnectActiveTool()
0332 {
0333     if (canvasData->activeTool) {
0334         canvasData->deactivateToolActions();
0335         // repaint the decorations before we deactivate the tool as it might deleted
0336         // data needed for the repaint
0337         canvasData->activeTool->deactivate();
0338         disconnect(canvasData->activeTool, SIGNAL(cursorChanged(QCursor)),
0339                    q, SLOT(updateCursor(QCursor)));
0340         disconnect(canvasData->activeTool, SIGNAL(activateTool(QString)),
0341                    q, SLOT(switchToolRequested(QString)));
0342         disconnect(canvasData->activeTool, SIGNAL(activateTemporary(QString)),
0343                    q, SLOT(switchToolTemporaryRequested(QString)));
0344         disconnect(canvasData->activeTool, SIGNAL(done()), q, SLOT(switchBackRequested()));
0345         disconnect(canvasData->activeTool, SIGNAL(statusTextChanged(QString)),
0346                    q, SIGNAL(changedStatusText(QString)));
0347     }
0348 
0349     // emit a empty status text to clear status text from last active tool
0350     emit q->changedStatusText(QString());
0351 }
0352 
0353 void KoToolManager::Private::switchTool(KoToolBase *tool, bool temporary)
0354 {
0355 
0356     Q_ASSERT(tool);
0357     if (canvasData == 0)
0358         return;
0359 
0360     if (canvasData->activeTool == tool && tool->toolId() != KoInteractionTool_ID)
0361         return;
0362 
0363     disconnectActiveTool();
0364     canvasData->activeTool = tool;
0365     connectActiveTool();
0366     postSwitchTool(temporary);
0367 }
0368 
0369 void KoToolManager::Private::switchTool(const QString &id, bool temporary)
0370 {
0371     Q_ASSERT(canvasData);
0372     if (!canvasData) return;
0373 
0374     if (canvasData->activeTool && temporary)
0375         canvasData->stack.push(canvasData->activeToolId);
0376     canvasData->activeToolId = id;
0377     KoToolBase *tool = canvasData->allTools.value(id);
0378     if (! tool) {
0379         return;
0380     }
0381 
0382     foreach(ToolHelper *th, tools) {
0383         if (th->id() == id) {
0384             canvasData->activationShapeId = th->activationShapeId();
0385             break;
0386         }
0387     }
0388 
0389     switchTool(tool, temporary);
0390 }
0391 
0392 void KoToolManager::Private::postSwitchTool(bool temporary)
0393 {
0394 #ifndef NDEBUG
0395     int canvasCount = 1;
0396     foreach(QList<CanvasData*> list, canvasses) {
0397         bool first = true;
0398         foreach(CanvasData *data, list) {
0399             if (first) {
0400                 debugFlake << "Canvas" << canvasCount++;
0401             }
0402             debugFlake << "  +- Tool:" << data->activeToolId  << (data == canvasData ? " *" : "");
0403             first = false;
0404         }
0405     }
0406 #endif
0407     Q_ASSERT(canvasData);
0408     if (!canvasData) return;
0409 
0410     KoToolBase::ToolActivation toolActivation;
0411     if (temporary)
0412         toolActivation = KoToolBase::TemporaryActivation;
0413     else
0414         toolActivation = KoToolBase::DefaultActivation;
0415     QSet<KoShape*> shapesToOperateOn;
0416     if (canvasData->activeTool
0417             && canvasData->activeTool->canvas()
0418             && canvasData->activeTool->canvas()->shapeManager()) {
0419         KoSelection *selection = canvasData->activeTool->canvas()->shapeManager()->selection();
0420         Q_ASSERT(selection);
0421 
0422         foreach(KoShape *shape, selection->selectedShapes()) {
0423             QSet<KoShape*> delegates = shape->toolDelegates();
0424             if (delegates.isEmpty()) { // no delegates, just the orig shape
0425                 shapesToOperateOn << shape;
0426             } else {
0427                 shapesToOperateOn += delegates;
0428             }
0429         }
0430     }
0431 
0432     if (canvasData->canvas->canvas()) {
0433         // Caller of postSwitchTool expect this to be called to update the selected tool
0434         updateToolForProxy();
0435         canvasData->activeTool->activate(toolActivation, shapesToOperateOn);
0436         KoCanvasBase *canvas = canvasData->canvas->canvas();
0437         canvas->updateInputMethodInfo();
0438     } else {
0439         canvasData->activeTool->activate(toolActivation, shapesToOperateOn);
0440     }
0441 
0442     QList<QPointer<QWidget> > optionWidgetList = canvasData->activeTool->optionWidgets();
0443     if (optionWidgetList.empty()) { // no option widget.
0444         QWidget *toolWidget;
0445         QString title;
0446         foreach(ToolHelper *tool, tools) {
0447             if (tool->id() == canvasData->activeTool->toolId()) {
0448                 title = tool->toolTip();
0449                 break;
0450             }
0451         }
0452         toolWidget = canvasData->dummyToolWidget;
0453         if (toolWidget == 0) {
0454             toolWidget = new QWidget();
0455             toolWidget->setObjectName("DummyToolWidget");
0456             QVBoxLayout *layout = new QVBoxLayout(toolWidget);
0457             layout->setMargin(3);
0458             canvasData->dummyToolLabel = new QLabel(toolWidget);
0459             layout->addWidget(canvasData->dummyToolLabel);
0460             layout->addItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::Expanding));
0461             toolWidget->setLayout(layout);
0462             canvasData->dummyToolWidget = toolWidget;
0463         }
0464         canvasData->dummyToolLabel->setText(i18n("Active tool: %1", title));
0465         optionWidgetList.append(toolWidget);
0466     }
0467 
0468     // Activate the actions for the currently active tool
0469     canvasData->activateToolActions();
0470 
0471     emit q->changedTool(canvasData->canvas, uniqueToolIds.value(canvasData->activeTool));
0472 
0473     KoCanvasControllerWidget *canvasControllerWidget = dynamic_cast<KoCanvasControllerWidget*>(canvasData->canvas);
0474     if (canvasControllerWidget) {
0475         canvasControllerWidget->setToolOptionWidgets(optionWidgetList);
0476     }
0477 }
0478 
0479 
0480 void KoToolManager::Private::switchCanvasData(CanvasData *cd)
0481 {
0482 
0483     Q_ASSERT(cd);
0484 
0485     KoCanvasBase *oldCanvas = 0;
0486     KoInputDevice oldInputDevice;
0487 
0488     if (canvasData) {
0489         oldCanvas = canvasData->canvas->canvas();
0490         oldInputDevice = canvasData->inputDevice;
0491 
0492         if (canvasData->activeTool) {
0493             disconnectActiveTool();
0494         }
0495 
0496         KoToolProxy *proxy = proxies.value(oldCanvas);
0497         Q_ASSERT(proxy);
0498         proxy->setActiveTool(0);
0499     }
0500 
0501     canvasData = cd;
0502     inputDevice = canvasData->inputDevice;
0503 
0504     if (canvasData->activeTool) {
0505         connectActiveTool();
0506         postSwitchTool(false);
0507     }
0508 
0509     if (oldInputDevice != canvasData->inputDevice) {
0510         emit q->inputDeviceChanged(canvasData->inputDevice);
0511     }
0512 
0513     if (oldCanvas != canvasData->canvas->canvas()) {
0514         emit q->changedCanvas(canvasData->canvas->canvas());
0515     }
0516 }
0517 
0518 
0519 void KoToolManager::Private::toolActivated(ToolHelper *tool)
0520 {
0521     Q_ASSERT(tool);
0522 
0523     Q_ASSERT(canvasData);
0524     if (!canvasData) return;
0525     KoToolBase *t = canvasData->allTools.value(tool->id());
0526     Q_ASSERT(t);
0527 
0528     canvasData->activeToolId = tool->id();
0529     canvasData->activationShapeId = tool->activationShapeId();
0530 
0531     switchTool(t, false);
0532 }
0533 
0534 void KoToolManager::Private::detachCanvas(KoCanvasController *controller)
0535 {
0536     Q_ASSERT(controller);
0537     // check if we are removing the active canvas controller
0538     if (canvasData && canvasData->canvas == controller) {
0539         KoCanvasController *newCanvas = 0;
0540         // try to find another canvas controller beside the one we are removing
0541         foreach(KoCanvasController* canvas, canvasses.keys()) {
0542             if (canvas != controller) {
0543                 // yay found one
0544                 newCanvas = canvas;
0545                 break;
0546             }
0547         }
0548         if (newCanvas) {
0549             switchCanvasData(canvasses.value(newCanvas).first());
0550         } else {
0551             KoCanvasControllerWidget *canvasControllerWidget = dynamic_cast<KoCanvasControllerWidget*>(canvasData->canvas);
0552             if (canvasControllerWidget) {
0553                 canvasControllerWidget->setToolOptionWidgets(QList<QPointer<QWidget> >());
0554             }
0555             // as a last resort just set a blank one
0556             canvasData = 0;
0557         }
0558     }
0559 
0560     KoToolProxy *proxy = proxies.value(controller->canvas());
0561     if (proxy)
0562         proxy->setActiveTool(0);
0563 
0564     QList<KoToolBase *> tools;
0565     foreach(CanvasData *canvasData, canvasses.value(controller)) {
0566         foreach(KoToolBase *tool, canvasData->allTools) {
0567             if (! tools.contains(tool)) {
0568                 tools.append(tool);
0569             }
0570         }
0571         delete canvasData;
0572     }
0573     foreach(KoToolBase *tool, tools) {
0574         uniqueToolIds.remove(tool);
0575         delete tool;
0576     }
0577     canvasses.remove(controller);
0578     emit q->changedCanvas(canvasData ? canvasData->canvas->canvas() : 0);
0579 }
0580 
0581 void KoToolManager::Private::attachCanvas(KoCanvasController *controller)
0582 {
0583     Q_ASSERT(controller);
0584     CanvasData *cd = createCanvasData(controller, KoInputDevice::mouse());
0585 
0586     // switch to new canvas as the active one.
0587     switchCanvasData(cd);
0588 
0589     inputDevice = cd->inputDevice;
0590     QList<CanvasData*> canvasses_;
0591     canvasses_.append(cd);
0592     canvasses[controller] = canvasses_;
0593 
0594     KoToolProxy *tp = proxies[controller->canvas()];
0595     if (tp)
0596         tp->priv()->setCanvasController(controller);
0597 
0598     if (cd->activeTool == 0) {
0599         // no active tool, so we activate the highest priority main tool
0600         int highestPriority = INT_MAX;
0601         ToolHelper * helper = 0;
0602         foreach(ToolHelper * th, tools) {
0603             if (th->toolType() == KoToolFactoryBase::mainToolType()) {
0604                 if (th->priority() < highestPriority) {
0605                     highestPriority = qMin(highestPriority, th->priority());
0606                     helper = th;
0607                 }
0608             }
0609         }
0610         if (helper)
0611             toolActivated(helper);
0612     }
0613 
0614     Connector *connector = new Connector(controller->canvas()->shapeManager());
0615     connect(connector, SIGNAL(selectionChanged(QList<KoShape*>)), q,
0616             SLOT(selectionChanged(QList<KoShape*>)));
0617     connect(controller->canvas()->shapeManager()->selection(),
0618             SIGNAL(currentLayerChanged(const KoShapeLayer*)),
0619             q, SLOT(currentLayerChanged(const KoShapeLayer*)));
0620 
0621     emit q->changedCanvas(canvasData ? canvasData->canvas->canvas() : 0);
0622 }
0623 
0624 void KoToolManager::Private::movedFocus(QWidget *from, QWidget *to)
0625 {
0626     Q_UNUSED(from);
0627     // no canvas anyway or no focus set anyway?
0628     if (!canvasData || to == 0) {
0629         return;
0630     }
0631 
0632     // Check if this app is about QWidget-based KoCanvasControllerWidget canvasses
0633     // XXX: Focus handling for non-qwidget based canvases!
0634     KoCanvasControllerWidget *canvasControllerWidget = dynamic_cast<KoCanvasControllerWidget*>(canvasData->canvas);
0635     if (!canvasControllerWidget) {
0636         return;
0637     }
0638 
0639     // canvasWidget is set as focusproxy for KoCanvasControllerWidget,
0640     // so all focus checks are to be done against canvasWidget objects
0641 
0642     // focus returned to current canvas?
0643     if (to == canvasData->canvas->canvas()->canvasWidget()) {
0644         // nothing to do
0645         return;
0646     }
0647 
0648     // if the 'to' is one of our canvasWidgets, then switch.
0649 
0650     // for code simplicity the current canvas will be checked again,
0651     // but would have been caught already in the lines above, so no issue
0652     KoCanvasController *newCanvas = 0;
0653     foreach(KoCanvasController* canvas, canvasses.keys()) {
0654         if (canvas->canvas()->canvasWidget() == to) {
0655             newCanvas = canvas;
0656             break;
0657         }
0658     }
0659 
0660     // none of our canvasWidgets got focus?
0661     if (newCanvas == 0) {
0662         return;
0663     }
0664 
0665     // switch to canvasdata matching inputdevice used last with this app instance
0666     foreach(CanvasData *data, canvasses.value(newCanvas)) {
0667         if (data->inputDevice == inputDevice) {
0668             switchCanvasData(data);
0669             return;
0670         }
0671     }
0672     // if no such inputDevice for this canvas, then simply fallback to first one
0673     switchCanvasData(canvasses.value(newCanvas).first());
0674 }
0675 
0676 void KoToolManager::Private::updateCursor(const QCursor &cursor)
0677 {
0678     Q_ASSERT(canvasData);
0679     Q_ASSERT(canvasData->canvas);
0680     Q_ASSERT(canvasData->canvas->canvas());
0681     canvasData->canvas->canvas()->setCursor(cursor);
0682 }
0683 
0684 void KoToolManager::Private::selectionChanged(const QList<KoShape*> &shapes)
0685 {
0686     QList<QString> types;
0687     foreach(KoShape *shape, shapes) {
0688         QSet<KoShape*> delegates = shape->toolDelegates();
0689         if (delegates.isEmpty()) { // no delegates, just the orig shape
0690             delegates << shape;
0691         }
0692 
0693         foreach (KoShape *shape2, delegates) {
0694             Q_ASSERT(shape2);
0695             if (! types.contains(shape2->shapeId())) {
0696                 types.append(shape2->shapeId());
0697             }
0698         }
0699     }
0700 
0701     // check if there is still a shape selected the active tool can work on
0702     // there needs to be at least one shape for a tool without an activationShapeId
0703     // to work
0704     // if not change the current tool to the default tool
0705     if (!(canvasData->activationShapeId.isNull() && shapes.size() > 0)
0706                 && canvasData->activationShapeId != "flake/always"
0707                 && canvasData->activationShapeId != "flake/edit") {
0708 
0709         bool currentToolWorks = false;
0710         foreach (const QString &type, types) {
0711             if (canvasData->activationShapeId.split(',').contains(type)) {
0712                 currentToolWorks = true;
0713                 break;
0714             }
0715         }
0716         if (!currentToolWorks) {
0717             switchTool(KoInteractionTool_ID, false);
0718         }
0719     }
0720     emit q->toolCodesSelected(types);
0721     // First time the tool is activated, it is not shown
0722     // because activetool must be set before optionwidgets are set.
0723     // Activetool is not set until q->toolCodesSelected() is emitted above,
0724     // so the setting in postSwitchTool() does not work.
0725     // NOTE: May only be true for non-default tools like for chart, formula etc,
0726     // so do not remove the postSwitchTool() setting until you are absolutely certain.
0727     QList<QPointer<QWidget> > optionWidgetList = canvasData->activeTool->optionWidgets();
0728     KoCanvasControllerWidget *canvasControllerWidget = dynamic_cast<KoCanvasControllerWidget*>(canvasData->canvas);
0729     if (canvasControllerWidget && !optionWidgetList.isEmpty()) {
0730         canvasControllerWidget->setToolOptionWidgets(optionWidgetList);
0731     }
0732 }
0733 
0734 void KoToolManager::Private::currentLayerChanged(const KoShapeLayer *layer)
0735 {
0736     emit q->currentLayerChanged(canvasData->canvas, layer);
0737     layerExplicitlyDisabled = layer && !layer->isEditable();
0738     updateToolForProxy();
0739 
0740     debugFlake << "Layer changed to" << layer << "explicitly disabled:" << layerExplicitlyDisabled;
0741 }
0742 
0743 void KoToolManager::Private::updateToolForProxy()
0744 {
0745     KoToolProxy *proxy = proxies.value(canvasData->canvas->canvas());
0746     if(!proxy) return;
0747 
0748     bool canUseTool = !layerExplicitlyDisabled || canvasData->activationShapeId.endsWith(QLatin1String("/always"));
0749     proxy->setActiveTool(canUseTool ? canvasData->activeTool : 0);
0750 }
0751 
0752 void KoToolManager::Private::switchInputDevice(const KoInputDevice &device)
0753 {
0754     Q_ASSERT(canvasData);
0755     if (!canvasData) return;
0756     if (inputDevice == device) return;
0757     if (inputDevice.isMouse() && device.isMouse()) return;
0758     if (device.isMouse() && !inputDevice.isMouse()) {
0759         // we never switch back to mouse from a tablet input device, so the user can use the
0760         // mouse to edit the settings for a tool activated by a tablet. See bugs
0761         // https://bugs.kde.org/show_bug.cgi?id=283130 and https://bugs.kde.org/show_bug.cgi?id=285501.
0762         // We do continue to switch between tablet devices, thought.
0763         return;
0764     }
0765 
0766     QList<CanvasData*> items = canvasses[canvasData->canvas];
0767 
0768     // disable all actions for all tools in the all canvasdata objects for this canvas.
0769     foreach(CanvasData *cd, items) {
0770         foreach(KoToolBase* tool, cd->allTools) {
0771             foreach(QAction * action, tool->actions()) {
0772                 action->setEnabled(false);
0773             }
0774         }
0775     }
0776 
0777     // search for a canvasdata object for the current input device
0778     foreach(CanvasData *cd, items) {
0779         if (cd->inputDevice == device) {
0780             switchCanvasData(cd);
0781 
0782             if (!canvasData->activeTool) {
0783                 switchTool(KoInteractionTool_ID, false);
0784             }
0785 
0786             return;
0787         }
0788     }
0789 
0790     // still here?  That means we need to create a new CanvasData instance with the current InputDevice.
0791     CanvasData *cd = createCanvasData(canvasData->canvas, device);
0792     // switch to new canvas as the active one.
0793     QString oldTool = canvasData->activeToolId;
0794 
0795     items.append(cd);
0796     canvasses[cd->canvas] = items;
0797 
0798     switchCanvasData(cd);
0799 
0800     q->switchToolRequested(oldTool);
0801 }
0802 
0803 void KoToolManager::Private::registerToolProxy(KoToolProxy *proxy, KoCanvasBase *canvas)
0804 {
0805     proxies.insert(canvas, proxy);
0806     foreach(KoCanvasController *controller, canvasses.keys()) {
0807         if (controller->canvas() == canvas) {
0808             proxy->priv()->setCanvasController(controller);
0809             break;
0810         }
0811     }
0812 }
0813 
0814 void KoToolManager::Private::switchToolByShortcut(QKeyEvent *event)
0815 {
0816     if (event->key() == Qt::Key_Space && event->modifiers() == 0) {
0817         switchTool(KoPanTool_ID, true);
0818     } else if (event->key() == Qt::Key_Escape && event->modifiers() == 0) {
0819         switchTool(KoInteractionTool_ID, false);
0820     }
0821 }
0822 
0823 // ******** KoToolManager **********
0824 KoToolManager::KoToolManager()
0825     : QObject(),
0826     d(new Private(this))
0827 {
0828     connect(QApplication::instance(), SIGNAL(focusChanged(QWidget*,QWidget*)),
0829             this, SLOT(movedFocus(QWidget*,QWidget*)));
0830 }
0831 
0832 KoToolManager::~KoToolManager()
0833 {
0834     delete d;
0835 }
0836 
0837 QList<KoToolAction*> KoToolManager::toolActionList() const
0838 {
0839     QList<KoToolAction*> answer;
0840     answer.reserve(d->tools.count());
0841     foreach(ToolHelper *tool, d->tools) {
0842         if (tool->id() == KoCreateShapesTool_ID)
0843             continue; // don't show this one.
0844         answer.append(tool->toolAction());
0845     }
0846     return answer;
0847 }
0848 
0849 void KoToolManager::requestToolActivation(KoCanvasController * controller)
0850 {
0851     if (d->canvasses.contains(controller)) {
0852         QString activeToolId = d->canvasses.value(controller).first()->activeToolId;
0853         foreach(ToolHelper * th, d->tools) {
0854             if (th->id() == activeToolId) {
0855                 d->toolActivated(th);
0856                 break;
0857             }
0858         }
0859     }
0860 }
0861 
0862 KoInputDevice KoToolManager::currentInputDevice() const
0863 {
0864     return d->inputDevice;
0865 }
0866 
0867 void KoToolManager::registerTools(KActionCollection *ac, KoCanvasController *controller)
0868 {
0869     Q_ASSERT(controller);
0870     Q_ASSERT(ac);
0871 
0872     d->setup();
0873 
0874     if (!d->canvasses.contains(controller)) {
0875         return;
0876     }
0877 
0878     // Actions available during the use of individual tools
0879     CanvasData *cd = d->canvasses.value(controller).first();
0880     foreach(KoToolBase *tool, cd->allTools) {
0881         QHash<QString, QAction*> actions = tool->actions();
0882         QHash<QString, QAction*>::const_iterator action(actions.constBegin());
0883         for (; action != actions.constEnd(); ++action) {
0884             if (!ac->action(action.key()))
0885                 ac->addAction(action.key(), action.value());
0886         }
0887     }
0888 
0889     // Actions used to switch tools via shortcuts
0890     foreach(ToolHelper * th, d->tools) {
0891         if (ac->action(th->id())) {
0892             continue;
0893         }
0894         ShortcutToolAction* action = th->createShortcutToolAction(ac);
0895         ac->addAction(th->id(), action);
0896     }
0897 }
0898 
0899 void KoToolManager::addController(KoCanvasController *controller)
0900 {
0901     Q_ASSERT(controller);
0902     if (d->canvasses.contains(controller))
0903         return;
0904     d->setup();
0905     d->attachCanvas(controller);
0906     connect(controller->proxyObject, SIGNAL(destroyed(QObject*)), this, SLOT(attemptCanvasControllerRemoval(QObject*)));
0907     connect(controller->proxyObject, SIGNAL(canvasRemoved(KoCanvasController*)), this, SLOT(detachCanvas(KoCanvasController*)));
0908     connect(controller->proxyObject, SIGNAL(canvasSet(KoCanvasController*)), this, SLOT(attachCanvas(KoCanvasController*)));
0909 }
0910 
0911 void KoToolManager::removeCanvasController(KoCanvasController *controller)
0912 {
0913     Q_ASSERT(controller);
0914     disconnect(controller->proxyObject, SIGNAL(canvasRemoved(KoCanvasController*)), this, SLOT(detachCanvas(KoCanvasController*)));
0915     disconnect(controller->proxyObject, SIGNAL(canvasSet(KoCanvasController*)), this, SLOT(attachCanvas(KoCanvasController*)));
0916     d->detachCanvas(controller);
0917 }
0918 
0919 void KoToolManager::attemptCanvasControllerRemoval(QObject* controller)
0920 {
0921     KoCanvasControllerProxyObject* controllerActual = qobject_cast<KoCanvasControllerProxyObject*>(controller);
0922     if (controllerActual) {
0923         removeCanvasController(controllerActual->canvasController());
0924     }
0925 }
0926 
0927 void KoToolManager::updateShapeControllerBase(KoShapeBasedDocumentBase *shapeController, KoCanvasController *canvasController)
0928 {
0929     if (!d->canvasses.contains(canvasController))
0930         return;
0931 
0932     QList<CanvasData *> canvasses = d->canvasses[canvasController];
0933     foreach(CanvasData *canvas, canvasses) {
0934         foreach(KoToolBase *tool, canvas->allTools) {
0935             tool->updateShapeController(shapeController);
0936         }
0937     }
0938 }
0939 
0940 void KoToolManager::switchToolRequested(const QString & id)
0941 {
0942     Q_ASSERT(d->canvasData);
0943     if (!d->canvasData) return;
0944 
0945     while (!d->canvasData->stack.isEmpty()) // switching means to flush the stack
0946         d->canvasData->stack.pop();
0947     d->switchTool(id, false);
0948 }
0949 
0950 void KoToolManager::switchInputDeviceRequested(const KoInputDevice &id)
0951 {
0952     if (!d->canvasData) return;
0953     d->switchInputDevice(id);
0954 }
0955 
0956 void KoToolManager::switchToolTemporaryRequested(const QString &id)
0957 {
0958     d->switchTool(id, true);
0959 }
0960 
0961 void KoToolManager::switchBackRequested()
0962 {
0963     if (!d->canvasData) return;
0964 
0965     if (d->canvasData->stack.isEmpty()) {
0966         // default to changing to the interactionTool
0967         d->switchTool(KoInteractionTool_ID, false);
0968         return;
0969     }
0970     d->switchTool(d->canvasData->stack.pop(), false);
0971 }
0972 
0973 KoCreateShapesTool * KoToolManager::shapeCreatorTool(KoCanvasBase *canvas) const
0974 {
0975     Q_ASSERT(canvas);
0976     foreach(KoCanvasController *controller, d->canvasses.keys()) {
0977         if (controller->canvas() == canvas) {
0978             KoCreateShapesTool *createTool = dynamic_cast<KoCreateShapesTool*>
0979                                              (d->canvasData->allTools.value(KoCreateShapesTool_ID));
0980             Q_ASSERT(createTool /* ID changed? */);
0981             return createTool;
0982         }
0983     }
0984     Q_ASSERT(0);   // this should not happen
0985     return 0;
0986 }
0987 
0988 KoToolBase *KoToolManager::toolById(KoCanvasBase *canvas, const QString &id) const
0989 {
0990     Q_ASSERT(canvas);
0991     foreach(KoCanvasController *controller, d->canvasses.keys()) {
0992         if (controller->canvas() == canvas)
0993             return d->canvasData->allTools.value(id);
0994     }
0995     return 0;
0996 }
0997 
0998 KoCanvasController *KoToolManager::activeCanvasController() const
0999 {
1000     if (! d->canvasData) return 0;
1001     return d->canvasData->canvas;
1002 }
1003 
1004 QString KoToolManager::preferredToolForSelection(const QList<KoShape*> &shapes)
1005 {
1006     QList<QString> types;
1007     foreach(KoShape *shape, shapes)
1008         if (! types.contains(shape->shapeId()))
1009             types.append(shape->shapeId());
1010 
1011     QString toolType = KoInteractionTool_ID;
1012     int prio = INT_MAX;
1013     foreach(ToolHelper *helper, d->tools) {
1014         if (helper->priority() >= prio)
1015             continue;
1016         if (helper->toolType() == KoToolFactoryBase::mainToolType())
1017             continue;
1018 
1019         bool toolWillWork = false;
1020         foreach (const QString &type, types) {
1021             if (helper->activationShapeId().split(',').contains(type)) {
1022                 toolWillWork = true;
1023                 break;
1024             }
1025         }
1026         if (toolWillWork) {
1027             toolType = helper->id();
1028             prio = helper->priority();
1029         }
1030     }
1031     return toolType;
1032 }
1033 
1034 void KoToolManager::injectDeviceEvent(KoInputDeviceHandlerEvent * event)
1035 {
1036     if (d->canvasData && d->canvasData->canvas->canvas()) {
1037         if (static_cast<KoInputDeviceHandlerEvent::Type>(event->type()) == KoInputDeviceHandlerEvent::ButtonPressed)
1038             d->canvasData->activeTool->customPressEvent(event->pointerEvent());
1039         else if (static_cast<KoInputDeviceHandlerEvent::Type>(event->type()) == KoInputDeviceHandlerEvent::ButtonReleased)
1040             d->canvasData->activeTool->customReleaseEvent(event->pointerEvent());
1041         else if (static_cast<KoInputDeviceHandlerEvent::Type>(event->type()) ==  KoInputDeviceHandlerEvent::PositionChanged)
1042             d->canvasData->activeTool->customMoveEvent(event->pointerEvent());
1043     }
1044 }
1045 
1046 void KoToolManager::addDeferredToolFactory(KoToolFactoryBase *toolFactory)
1047 {
1048     ToolHelper *tool = new ToolHelper(toolFactory);
1049     // make sure all plugins are loaded as otherwise we will not load them
1050     d->setup();
1051     d->tools.append(tool);
1052 
1053     // connect to all tools so we can hear their button-clicks
1054     connect(tool, SIGNAL(toolActivated(ToolHelper*)), this, SLOT(toolActivated(ToolHelper*)));
1055 
1056     // now create tools for all existing canvases
1057     foreach(KoCanvasController *controller, d->canvasses.keys()) {
1058 
1059         // this canvascontroller is unknown, which is weird
1060         if (!d->canvasses.contains(controller)) {
1061             continue;
1062         }
1063 
1064         // create a tool for all canvasdata objects (i.e., all input devices on this canvas)
1065         foreach (CanvasData *cd, d->canvasses[controller]) {
1066             QPair<QString, KoToolBase*> toolPair = createTools(controller, tool);
1067             if (toolPair.second) {
1068                 cd->allTools.insert(toolPair.first, toolPair.second);
1069             }
1070         }
1071 
1072         // Then create a button for the toolbox for this canvas
1073         if (tool->id() == KoCreateShapesTool_ID) {
1074             continue;
1075         }
1076 
1077         emit addedTool(tool->toolAction(), controller);
1078     }
1079 }
1080 
1081 QPair<QString, KoToolBase*> KoToolManager::createTools(KoCanvasController *controller, ToolHelper *tool)
1082 {
1083     // XXX: maybe this method should go into the private class?
1084 
1085     QHash<QString, KoToolBase*> origHash;
1086 
1087     if (d->canvasses.contains(controller)) {
1088         origHash = d->canvasses.value(controller).first()->allTools;
1089     }
1090 
1091     if (origHash.contains(tool->id())) {
1092         return QPair<QString, KoToolBase*>(tool->id(), origHash.value(tool->id()));
1093     }
1094 
1095     debugFlake << "Creating tool" << tool->id() << ". Activated on:" << tool->activationShapeId() << ", prio:" << tool->priority();
1096 
1097     KoToolBase *tl = tool->createTool(controller->canvas());
1098     if (tl) {
1099         d->uniqueToolIds.insert(tl, tool->uniqueId());
1100 
1101         tl->setObjectName(tool->id());
1102 
1103         foreach(QAction *action, tl->actions()) {
1104             action->setEnabled(false);
1105         }
1106 
1107     }
1108 
1109     KoZoomTool *zoomTool = dynamic_cast<KoZoomTool*>(tl);
1110     if (zoomTool) {
1111         zoomTool->setCanvasController(controller);
1112     }
1113 
1114     KoPanTool *panTool = dynamic_cast<KoPanTool*>(tl);
1115     if (panTool) {
1116         panTool->setCanvasController(controller);
1117     }
1118 
1119     return QPair<QString, KoToolBase*>(tool->id(), tl);
1120 }
1121 
1122 
1123 KoToolManager* KoToolManager::instance()
1124 {
1125     return s_instance;
1126 }
1127 
1128 QString KoToolManager::activeToolId() const
1129 {
1130     if (!d->canvasData) return QString();
1131     return d->canvasData->activeToolId;
1132 }
1133 
1134 KoToolManager::Private *KoToolManager::priv()
1135 {
1136     return d;
1137 }
1138 
1139 //have to include this because of Q_PRIVATE_SLOT
1140 #include "moc_KoToolManager.cpp"