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"