File indexing completed on 2024-05-12 15:56:52
0001 /* This file is part of the KDE project 0002 * 0003 * SPDX-FileCopyrightText: 2005-2010 Boudewijn Rempt <boud@valdyas.org> 0004 * SPDX-FileCopyrightText: 2006-2008 Thomas Zander <zander@kde.org> 0005 * SPDX-FileCopyrightText: 2006 Thorsten Zachmann <zachmann@kde.org> 0006 * SPDX-FileCopyrightText: 2008 Jan Hambrecht <jaham@gmx.net> 0007 * 0008 * SPDX-License-Identifier: LGPL-2.0-or-later 0009 */ 0010 // flake 0011 #include "KoToolManager.h" 0012 #include "KoToolManager_p.h" 0013 #include "KoToolRegistry.h" 0014 #include "KoToolProxy.h" 0015 #include "KoToolProxy_p.h" 0016 #include "KoSelection.h" 0017 #include "KoCanvasController.h" 0018 #include "KoCanvasControllerWidget.h" 0019 #include "KoShape.h" 0020 #include "KoShapeLayer.h" 0021 #include "KoShapeRegistry.h" 0022 #include "KoShapeManager.h" 0023 #include "KoSelectedShapesProxy.h" 0024 #include "KoCanvasBase.h" 0025 #include "KoPointerEvent.h" 0026 #include "tools/KoZoomTool.h" 0027 #include "kis_action_registry.h" 0028 #include "KoToolFactoryBase.h" 0029 #include "kis_assert.h" 0030 0031 #include <krita_container_utils.h> 0032 0033 // Qt + kde 0034 #include <QWidget> 0035 #include <QEvent> 0036 #include <QWheelEvent> 0037 #include <QMouseEvent> 0038 #include <QPaintEvent> 0039 #include <QTabletEvent> 0040 #include <QVBoxLayout> 0041 #include <QStringList> 0042 #include <QApplication> 0043 #include <kactioncollection.h> 0044 #include <kactioncategory.h> 0045 #include <FlakeDebug.h> 0046 0047 #include <QAction> 0048 #include <klocalizedstring.h> 0049 #include <QKeySequence> 0050 #include <QStack> 0051 #include <QLabel> 0052 #include <QGlobalStatic> 0053 0054 Q_GLOBAL_STATIC(KoToolManager, s_instance) 0055 0056 0057 class CanvasData 0058 { 0059 public: 0060 CanvasData(KoCanvasController *cc, const KoInputDevice &id) 0061 : activeTool(0), 0062 canvas(cc), 0063 inputDevice(id), 0064 dummyToolWidget(0), 0065 dummyToolLabel(0) 0066 { 0067 } 0068 0069 ~CanvasData() 0070 { 0071 // the dummy tool widget does not necessarily have a parent and we create it, so we delete it. 0072 delete dummyToolWidget; 0073 } 0074 0075 void activateToolActions() 0076 { 0077 toolActions.clear(); 0078 disabledGlobalActions.clear(); 0079 0080 KisKActionCollection *windowActionCollection = canvas->actionCollection(); 0081 0082 if (!windowActionCollection) { 0083 qWarning() << "We haven't got an action collection"; 0084 return; 0085 } 0086 0087 QStringList globalActions; 0088 0089 QMap<QKeySequence, QStringList> shortcutMap; 0090 0091 // qDebug() << "................... activating tool" << activeToolId; 0092 0093 Q_FOREACH(QAction *action, windowActionCollection->actions()) { 0094 0095 if (action->property("tool_action").isValid()) { 0096 QStringList tools = action->property("tool_action").toStringList(); 0097 0098 if (KoToolRegistry::instance()->keys().contains(action->objectName())) { 0099 //qDebug() << "This action needs to be enabled!"; 0100 action->setEnabled(true); 0101 toolActions << action->objectName(); 0102 } 0103 else { 0104 if (tools.contains(activeToolId) || action->property("always_enabled").toBool()) { 0105 //qDebug() << "\t\tenabling"; 0106 action->setEnabled(true); 0107 toolActions << action->objectName(); 0108 } 0109 else { 0110 //qDebug() << "\t\tDISabling"; 0111 action->setDisabled(true); 0112 } 0113 } 0114 } 0115 else { 0116 globalActions << action->objectName(); 0117 } 0118 0119 Q_FOREACH(QKeySequence keySequence, action->shortcuts()) { 0120 // After loading a custom shortcut profile, shortcuts can be defined as an empty string, which is not an empty shortcut 0121 if (keySequence.toString() != "") { 0122 if (shortcutMap.contains(keySequence)) { 0123 shortcutMap[keySequence].append(action->objectName()); 0124 } 0125 else { 0126 shortcutMap[keySequence] = QStringList() << action->objectName(); 0127 } 0128 } 0129 } 0130 } 0131 0132 // Make sure the tool's actions override the global actions that aren't associated with the tool. 0133 Q_FOREACH(const QKeySequence &k, shortcutMap.keys()) { 0134 if (shortcutMap[k].size() > 1) { 0135 QStringList actions = shortcutMap[k]; 0136 //qDebug() << k << actions; 0137 bool toolActionFound = false; 0138 Q_FOREACH(const QString &action, actions) { 0139 if (toolActions.contains(action)) { 0140 toolActionFound = true; 0141 } 0142 } 0143 Q_FOREACH(const QString &action, actions) { 0144 if (toolActionFound && globalActions.contains(action)) { 0145 //qDebug() << "\tdisabling global action" << action; 0146 windowActionCollection->action(action)->setEnabled(false); 0147 disabledGlobalActions << action; 0148 } 0149 } 0150 //qDebug() << k << shortcutMap[k]; 0151 } 0152 } 0153 0154 windowActionCollection->readSettings(); // The shortcuts might have been configured in the meantime. 0155 } 0156 0157 void deactivateToolActions() 0158 { 0159 if (!activeTool) 0160 return; 0161 0162 //qDebug() << "............... deactivating previous tool because activating" << activeToolId; 0163 0164 KisKActionCollection *windowActionCollection = canvas->actionCollection(); 0165 0166 Q_FOREACH(const QString &action, toolActions) { 0167 //qDebug() << "disabling" << action; 0168 windowActionCollection->action(action)->setDisabled(true); 0169 } 0170 Q_FOREACH(const QString &action, disabledGlobalActions) { 0171 //qDebug() << "enabling" << action; 0172 windowActionCollection->action(action)->setEnabled(true); 0173 } 0174 } 0175 0176 KoToolBase *activeTool; // active Tool 0177 QString activeToolId; // the id of the active Tool 0178 QString activationShapeId; // the shape-type (KoShape::shapeId()) the activeTool 'belongs' to. 0179 QHash<QString, KoToolBase*> allTools; // all the tools that are created for this canvas. 0180 QList<KoToolBase*> mostRecentTools; // ordered unique list of tools starting from the most recently used, except for the active tool. 0181 KoCanvasController *const canvas; 0182 const KoInputDevice inputDevice; 0183 QWidget *dummyToolWidget; // the widget shown in the toolDocker. 0184 QLabel *dummyToolLabel; 0185 QStringList toolActions; 0186 QStringList disabledGlobalActions; 0187 }; 0188 0189 0190 // ******** KoToolManager ********** 0191 KoToolManager::KoToolManager() 0192 : QObject(), 0193 d(new Private(this)) 0194 { 0195 connect(QApplication::instance(), SIGNAL(focusChanged(QWidget*,QWidget*)), 0196 this, SLOT(movedFocus(QWidget*,QWidget*))); 0197 } 0198 0199 KoToolManager::~KoToolManager() 0200 { 0201 delete d; 0202 } 0203 0204 QList<KoToolAction*> KoToolManager::toolActionList() const 0205 { 0206 return d->toolActionList; 0207 } 0208 0209 void KoToolManager::requestToolActivation(KoCanvasController * controller) 0210 { 0211 if (d->canvasses.contains(controller)) { 0212 d->switchTool(d->canvasses.value(controller).first()->activeToolId); 0213 } 0214 } 0215 0216 KoInputDevice KoToolManager::currentInputDevice() const 0217 { 0218 return d->inputDevice; 0219 } 0220 0221 void KoToolManager::registerToolActions(KisKActionCollection *ac, KoCanvasController *controller) 0222 { 0223 Q_ASSERT(controller); 0224 Q_ASSERT(ac); 0225 0226 d->setup(); 0227 0228 if (!d->canvasses.contains(controller)) { 0229 return; 0230 } 0231 } 0232 0233 void KoToolManager::addController(KoCanvasController *controller) 0234 { 0235 Q_ASSERT(controller); 0236 if (d->canvasses.contains(controller)) 0237 return; 0238 d->setup(); 0239 d->attachCanvas(controller); 0240 connect(controller->proxyObject, SIGNAL(destroyed(QObject*)), this, SLOT(attemptCanvasControllerRemoval(QObject*))); 0241 connect(controller->proxyObject, SIGNAL(canvasRemoved(KoCanvasController*)), this, SLOT(detachCanvas(KoCanvasController*))); 0242 connect(controller->proxyObject, SIGNAL(canvasSet(KoCanvasController*)), this, SLOT(attachCanvas(KoCanvasController*))); 0243 } 0244 0245 void KoToolManager::removeCanvasController(KoCanvasController *controller) 0246 { 0247 Q_ASSERT(controller); 0248 disconnect(controller->proxyObject, SIGNAL(canvasRemoved(KoCanvasController*)), this, SLOT(detachCanvas(KoCanvasController*))); 0249 disconnect(controller->proxyObject, SIGNAL(canvasSet(KoCanvasController*)), this, SLOT(attachCanvas(KoCanvasController*))); 0250 d->detachCanvas(controller); 0251 } 0252 0253 void KoToolManager::attemptCanvasControllerRemoval(QObject* controller) 0254 { 0255 KoCanvasControllerProxyObject* controllerActual = qobject_cast<KoCanvasControllerProxyObject*>(controller); 0256 if (controllerActual) { 0257 removeCanvasController(controllerActual->canvasController()); 0258 } 0259 } 0260 0261 void KoToolManager::switchToolRequested(const QString & id) 0262 { 0263 d->switchTool(id); 0264 } 0265 0266 void KoToolManager::switchInputDeviceRequested(const KoInputDevice &id) 0267 { 0268 if (!d->canvasData) return; 0269 d->switchInputDevice(id); 0270 } 0271 0272 void KoToolManager::switchBackRequested() 0273 { 0274 if (!d->canvasData) return; 0275 if (d->canvasData->mostRecentTools.isEmpty()) return; 0276 d->switchTool(d->canvasData->mostRecentTools.first()->toolId()); 0277 } 0278 0279 KoToolBase *KoToolManager::toolById(KoCanvasBase *canvas, const QString &id) const 0280 { 0281 Q_ASSERT(canvas); 0282 Q_FOREACH (KoCanvasController *controller, d->canvasses.keys()) { 0283 if (controller->canvas() == canvas) 0284 return d->canvasData->allTools.value(id); 0285 } 0286 return 0; 0287 } 0288 0289 KoCanvasController *KoToolManager::activeCanvasController() const 0290 { 0291 if (! d->canvasData) return 0; 0292 return d->canvasData->canvas; 0293 } 0294 0295 QString KoToolManager::preferredToolForSelection(const QList<KoShape*> &shapes) 0296 { 0297 QSet<QString> shapeTypes; 0298 Q_FOREACH (KoShape *shape, shapes) { 0299 shapeTypes << shape->shapeId(); 0300 } 0301 //KritaUtils::makeContainerUnique(types); 0302 0303 QString toolType = KoInteractionTool_ID; 0304 int prio = INT_MAX; 0305 Q_FOREACH (KoToolAction *helper, d->toolActionList) { 0306 if (helper->priority() >= prio) 0307 continue; 0308 0309 bool toolWillWork = false; 0310 foreach (const QString &type, shapeTypes) { 0311 if (helper->toolFactory()->activationShapeId().split(',').contains(type)) { 0312 toolWillWork = true; 0313 break; 0314 } 0315 } 0316 0317 if (toolWillWork) { 0318 toolType = helper->id(); 0319 prio = helper->priority(); 0320 } 0321 } 0322 return toolType; 0323 } 0324 0325 void KoToolManager::initializeCurrentToolForCanvas() 0326 { 0327 KIS_ASSERT_RECOVER_RETURN(d->canvasData); 0328 0329 // make a full reconnect cycle for the currently active tool 0330 d->disconnectActiveTool(); 0331 d->connectActiveTool(); 0332 d->postSwitchTool(); 0333 } 0334 0335 void KoToolManager::themeChanged() 0336 { 0337 for (const QList<CanvasData*> &canvasDataList : d->canvasses) { 0338 for (CanvasData *canvasData : canvasDataList) { 0339 for (KoToolBase *tool : canvasData->allTools) { 0340 tool->updateOptionsWidgetIcons(); 0341 } 0342 } 0343 } 0344 } 0345 0346 KoToolManager* KoToolManager::instance() 0347 { 0348 return s_instance; 0349 } 0350 0351 QString KoToolManager::activeToolId() const 0352 { 0353 if (!d->canvasData) return QString(); 0354 return d->canvasData->activeToolId; 0355 } 0356 0357 0358 KoToolManager::Private *KoToolManager::priv() 0359 { 0360 return d; 0361 } 0362 0363 0364 /**** KoToolManager::Private ****/ 0365 0366 KoToolManager::Private::Private(KoToolManager *qq) 0367 : q(qq), 0368 canvasData(0), 0369 layerExplicitlyDisabled(false) 0370 { 0371 } 0372 0373 KoToolManager::Private::~Private() 0374 { 0375 qDeleteAll(toolActionList); 0376 } 0377 0378 // helper method. 0379 CanvasData *KoToolManager::Private::createCanvasData(KoCanvasController *controller, const KoInputDevice &device) 0380 { 0381 QHash<QString, KoToolBase*> toolsHash; 0382 Q_FOREACH (KoToolAction *toolAction, toolActionList) { 0383 KoToolBase* tool = createTool(controller, toolAction); 0384 if (tool) { // only if a real tool was created 0385 toolsHash.insert(tool->toolId(), tool); 0386 } 0387 } 0388 0389 CanvasData *cd = new CanvasData(controller, device); 0390 cd->allTools = toolsHash; 0391 return cd; 0392 } 0393 0394 KoToolBase *KoToolManager::Private::createTool(KoCanvasController *controller, KoToolAction *toolAction) 0395 { 0396 QHash<QString, KoToolBase*> origHash; 0397 0398 if (canvasses.contains(controller)) { 0399 origHash = canvasses.value(controller).first()->allTools; 0400 } 0401 0402 if (origHash.contains(toolAction->id())) { 0403 return origHash.value(toolAction->id()); 0404 } 0405 0406 debugFlake << "Creating tool" << toolAction->id() << ". Activated on:" << toolAction->visibilityCode() << ", prio:" << toolAction->priority(); 0407 0408 KoToolBase *tool = toolAction->toolFactory()->createTool(controller->canvas()); 0409 if (tool) { 0410 tool->setFactory(toolAction->toolFactory()); 0411 tool->setObjectName(toolAction->id()); 0412 } 0413 0414 KoZoomTool *zoomTool = dynamic_cast<KoZoomTool*>(tool); 0415 if (zoomTool) { 0416 zoomTool->setCanvasController(controller); 0417 } 0418 0419 return tool; 0420 } 0421 0422 void KoToolManager::Private::setup() 0423 { 0424 if (toolActionList.size() > 0) 0425 return; 0426 0427 KoShapeRegistry::instance(); 0428 KoToolRegistry *registry = KoToolRegistry::instance(); 0429 Q_FOREACH (const QString & id, registry->keys()) { 0430 toolActionList.append(new KoToolAction(registry->value(id))); 0431 } 0432 } 0433 0434 void KoToolManager::Private::connectActiveTool() 0435 { 0436 if (canvasData->activeTool) { 0437 connect(canvasData->activeTool, SIGNAL(cursorChanged(QCursor)), 0438 q, SLOT(updateCursor(QCursor))); 0439 connect(canvasData->activeTool, SIGNAL(activateTool(QString)), 0440 q, SLOT(switchToolRequested(QString))); 0441 connect(canvasData->activeTool, SIGNAL(statusTextChanged(QString)), 0442 q, SIGNAL(changedStatusText(QString))); 0443 } 0444 0445 // we expect the tool to emit a cursor on activation. 0446 updateCursor(Qt::ForbiddenCursor); 0447 } 0448 0449 0450 0451 void KoToolManager::Private::disconnectActiveTool() 0452 { 0453 if (canvasData->activeTool) { 0454 canvasData->deactivateToolActions(); 0455 // repaint the decorations before we deactivate the tool as it might deleted 0456 // data needed for the repaint 0457 emit q->aboutToChangeTool(canvasData->canvas); 0458 canvasData->activeTool->deactivate(); 0459 disconnect(canvasData->activeTool, SIGNAL(cursorChanged(QCursor)), 0460 q, SLOT(updateCursor(QCursor))); 0461 disconnect(canvasData->activeTool, SIGNAL(activateTool(QString)), 0462 q, SLOT(switchToolRequested(QString))); 0463 disconnect(canvasData->activeTool, SIGNAL(statusTextChanged(QString)), 0464 q, SIGNAL(changedStatusText(QString))); 0465 } 0466 0467 // emit a empty status text to clear status text from last active tool 0468 emit q->changedStatusText(QString()); 0469 } 0470 0471 void KoToolManager::Private::switchTool(const QString &id) 0472 { 0473 if (!canvasData) return; 0474 0475 canvasData->activeToolId = id; 0476 KoToolBase *tool = canvasData->allTools.value(id); 0477 if (! tool) { 0478 return; 0479 } 0480 0481 canvasData->activationShapeId = tool->factory()->activationShapeId(); 0482 0483 if (canvasData->activeTool == tool && tool->toolId() != KoInteractionTool_ID) 0484 return; 0485 0486 disconnectActiveTool(); 0487 if (canvasData->activeTool) { 0488 canvasData->mostRecentTools.prepend(canvasData->activeTool); 0489 } 0490 canvasData->activeTool = tool; 0491 canvasData->mostRecentTools.removeOne(tool); 0492 connectActiveTool(); 0493 postSwitchTool(); 0494 } 0495 0496 void KoToolManager::Private::postSwitchTool() 0497 { 0498 #ifndef NDEBUG 0499 int canvasCount = 1; 0500 Q_FOREACH (QList<CanvasData*> list, canvasses) { 0501 bool first = true; 0502 Q_FOREACH (CanvasData *data, list) { 0503 if (first) { 0504 debugFlake << "Canvas" << canvasCount++; 0505 } 0506 debugFlake << " +- Tool:" << data->activeToolId << (data == canvasData ? " *" : ""); 0507 first = false; 0508 } 0509 } 0510 #endif 0511 Q_ASSERT(canvasData); 0512 if (!canvasData) return; 0513 0514 QSet<KoShape*> shapesToOperateOn; 0515 if (canvasData->activeTool 0516 && canvasData->activeTool->canvas() 0517 && canvasData->activeTool->canvas()->shapeManager()) { 0518 KoSelection *selection = canvasData->activeTool->canvas()->shapeManager()->selection(); 0519 Q_ASSERT(selection); 0520 #if QT_VERSION >= QT_VERSION_CHECK(5,14,0) 0521 QList<KoShape *> shapesDelegatesList = selection->selectedEditableShapesAndDelegates(); 0522 if (!shapesDelegatesList.isEmpty()) { 0523 shapesToOperateOn = QSet<KoShape*>(shapesDelegatesList.begin(), 0524 shapesDelegatesList.end()); 0525 } 0526 #else 0527 shapesToOperateOn = QSet<KoShape*>::fromList(selection->selectedEditableShapesAndDelegates()); 0528 #endif 0529 } 0530 0531 if (canvasData->canvas->canvas()) { 0532 // Caller of postSwitchTool expect this to be called to update the selected tool 0533 updateToolForProxy(); 0534 canvasData->activeTool->activate(shapesToOperateOn); 0535 KoCanvasBase *canvas = canvasData->canvas->canvas(); 0536 canvas->updateInputMethodInfo(); 0537 } else { 0538 canvasData->activeTool->activate(shapesToOperateOn); 0539 } 0540 0541 QList<QPointer<QWidget> > optionWidgetList = canvasData->activeTool->optionWidgets(); 0542 if (optionWidgetList.empty()) { // no option widget. 0543 QWidget *toolWidget; 0544 QString title = canvasData->activeTool->factory()->toolTip(); 0545 toolWidget = canvasData->dummyToolWidget; 0546 if (toolWidget == 0) { 0547 toolWidget = new QWidget(); 0548 toolWidget->setObjectName("DummyToolWidget"); 0549 QVBoxLayout *layout = new QVBoxLayout(toolWidget); 0550 layout->setMargin(3); 0551 canvasData->dummyToolLabel = new QLabel(toolWidget); 0552 layout->addWidget(canvasData->dummyToolLabel); 0553 layout->addItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::Expanding)); 0554 canvasData->dummyToolWidget = toolWidget; 0555 } 0556 canvasData->dummyToolLabel->setText(i18n("Active tool: %1", title)); 0557 optionWidgetList.append(toolWidget); 0558 } 0559 0560 // Activate the actions for the currently active tool 0561 canvasData->activateToolActions(); 0562 0563 emit q->changedTool(canvasData->canvas); 0564 0565 emit q->toolOptionWidgetsChanged(canvasData->canvas, optionWidgetList); 0566 } 0567 0568 0569 void KoToolManager::Private::switchCanvasData(CanvasData *cd) 0570 { 0571 Q_ASSERT(cd); 0572 0573 KoCanvasBase *oldCanvas = 0; 0574 KoInputDevice oldInputDevice; 0575 0576 if (canvasData) { 0577 oldCanvas = canvasData->canvas->canvas(); 0578 oldInputDevice = canvasData->inputDevice; 0579 0580 if (canvasData->activeTool) { 0581 disconnectActiveTool(); 0582 } 0583 0584 KoToolProxy *proxy = proxies.value(oldCanvas); 0585 Q_ASSERT(proxy); 0586 proxy->setActiveTool(0); 0587 } 0588 0589 canvasData = cd; 0590 inputDevice = canvasData->inputDevice; 0591 0592 if (canvasData->activeTool) { 0593 connectActiveTool(); 0594 postSwitchTool(); 0595 } 0596 0597 if (oldInputDevice != canvasData->inputDevice) { 0598 emit q->inputDeviceChanged(canvasData->inputDevice); 0599 } 0600 0601 if (oldCanvas != canvasData->canvas->canvas()) { 0602 emit q->changedCanvas(canvasData->canvas->canvas()); 0603 } 0604 } 0605 0606 void KoToolManager::Private::detachCanvas(KoCanvasController *controller) 0607 { 0608 Q_ASSERT(controller); 0609 // check if we are removing the active canvas controller 0610 if (canvasData && canvasData->canvas == controller) { 0611 KoCanvasController *newCanvas = 0; 0612 // try to find another canvas controller beside the one we are removing 0613 Q_FOREACH (KoCanvasController* canvas, canvasses.keys()) { 0614 if (canvas != controller) { 0615 // yay found one 0616 newCanvas = canvas; 0617 break; 0618 } 0619 } 0620 if (newCanvas) { 0621 switchCanvasData(canvasses.value(newCanvas).first()); 0622 } else { 0623 disconnectActiveTool(); 0624 emit q->toolOptionWidgetsChanged(controller, QList<QPointer<QWidget> >()); 0625 // as a last resort just set a blank one 0626 canvasData = 0; 0627 } 0628 } 0629 0630 KoToolProxy *proxy = proxies.value(controller->canvas()); 0631 if (proxy) 0632 proxy->setActiveTool(0); 0633 0634 QList<KoToolBase *> tools; 0635 Q_FOREACH (CanvasData *canvasData, canvasses.value(controller)) { 0636 Q_FOREACH (KoToolBase *tool, canvasData->allTools) { 0637 if (! tools.contains(tool)) { 0638 tools.append(tool); 0639 } 0640 } 0641 delete canvasData; 0642 } 0643 Q_FOREACH (KoToolBase *tool, tools) { 0644 delete tool; 0645 } 0646 canvasses.remove(controller); 0647 emit q->changedCanvas(canvasData ? canvasData->canvas->canvas() : 0); 0648 } 0649 0650 void KoToolManager::Private::attachCanvas(KoCanvasController *controller) 0651 { 0652 Q_ASSERT(controller); 0653 CanvasData *cd = createCanvasData(controller, KoInputDevice::mouse()); 0654 0655 // switch to new canvas as the active one. 0656 switchCanvasData(cd); 0657 0658 inputDevice = cd->inputDevice; 0659 QList<CanvasData*> canvasses_; 0660 canvasses_.append(cd); 0661 canvasses[controller] = canvasses_; 0662 0663 KoToolProxy *tp = proxies[controller->canvas()]; 0664 if (tp) 0665 tp->priv()->setCanvasController(controller); 0666 0667 if (cd->activeTool == 0) { 0668 // no active tool, so we activate the highest priority main tool 0669 int highestPriority = INT_MAX; 0670 KoToolAction * helper = 0; 0671 Q_FOREACH (KoToolAction * th, toolActionList) { 0672 if (th->section() == ToolBoxSection::Main) { 0673 if (th->priority() < highestPriority) { 0674 highestPriority = qMin(highestPriority, th->priority()); 0675 helper = th; 0676 } 0677 } 0678 } 0679 if (helper) 0680 switchTool(helper->id()); 0681 } 0682 0683 Connector *connector = new Connector(controller->canvas()->shapeManager()); 0684 connect(connector, SIGNAL(selectionChanged(QList<KoShape*>)), q, 0685 SLOT(selectionChanged(QList<KoShape*>))); 0686 connect(controller->canvas()->selectedShapesProxy(), 0687 SIGNAL(currentLayerChanged(const KoShapeLayer*)), 0688 q, SLOT(currentLayerChanged(const KoShapeLayer*))); 0689 0690 emit q->changedCanvas(canvasData ? canvasData->canvas->canvas() : 0); 0691 } 0692 0693 void KoToolManager::Private::movedFocus(QWidget *from, QWidget *to) 0694 { 0695 Q_UNUSED(from); 0696 // no canvas anyway or no focus set anyway? 0697 if (!canvasData || to == 0) { 0698 return; 0699 } 0700 0701 // Check if this app is about QWidget-based KoCanvasControllerWidget canvasses 0702 // XXX: Focus handling for non-qwidget based canvases! 0703 KoCanvasControllerWidget *canvasControllerWidget = dynamic_cast<KoCanvasControllerWidget*>(canvasData->canvas); 0704 if (!canvasControllerWidget) { 0705 return; 0706 } 0707 0708 // canvasWidget is set as focusproxy for KoCanvasControllerWidget, 0709 // so all focus checks are to be done against canvasWidget objects 0710 0711 // focus returned to current canvas? 0712 if (to == canvasData->canvas->canvas()->canvasWidget()) { 0713 // nothing to do 0714 return; 0715 } 0716 0717 // if the 'to' is one of our canvasWidgets, then switch. 0718 0719 // for code simplicity the current canvas will be checked again, 0720 // but would have been caught already in the lines above, so no issue 0721 KoCanvasController *newCanvas = 0; 0722 Q_FOREACH (KoCanvasController* canvas, canvasses.keys()) { 0723 if (canvas->canvas()->canvasWidget() == to) { 0724 newCanvas = canvas; 0725 break; 0726 } 0727 } 0728 0729 // none of our canvasWidgets got focus? 0730 if (newCanvas == 0) { 0731 return; 0732 } 0733 0734 // switch to canvasdata matching inputdevice used last with this app instance 0735 Q_FOREACH (CanvasData *data, canvasses.value(newCanvas)) { 0736 if (data->inputDevice == inputDevice) { 0737 switchCanvasData(data); 0738 return; 0739 } 0740 } 0741 // if no such inputDevice for this canvas, then simply fallback to first one 0742 switchCanvasData(canvasses.value(newCanvas).first()); 0743 } 0744 0745 void KoToolManager::Private::updateCursor(const QCursor &cursor) 0746 { 0747 Q_ASSERT(canvasData); 0748 Q_ASSERT(canvasData->canvas); 0749 Q_ASSERT(canvasData->canvas->canvas()); 0750 canvasData->canvas->canvas()->setCursor(cursor); 0751 } 0752 0753 void KoToolManager::Private::selectionChanged(const QList<KoShape*> &shapes) 0754 { 0755 QList<QString> types; 0756 Q_FOREACH (KoShape *shape, shapes) { 0757 QSet<KoShape*> delegates = shape->toolDelegates(); 0758 if (delegates.isEmpty()) { // no delegates, just the orig shape 0759 delegates << shape; 0760 } 0761 0762 foreach (KoShape *shape2, delegates) { 0763 Q_ASSERT(shape2); 0764 if (! types.contains(shape2->shapeId())) { 0765 types.append(shape2->shapeId()); 0766 } 0767 } 0768 } 0769 0770 // check if there is still a shape selected the active tool can work on 0771 // there needs to be at least one shape for a tool without an activationShapeId 0772 // to work 0773 // if not change the current tool to the default tool 0774 0775 const QStringList activationShapeIds = canvasData->activationShapeId.split(','); 0776 0777 if (!(canvasData->activationShapeId.isNull() && shapes.size() > 0) 0778 && !activationShapeIds.contains("flake/always") 0779 && !activationShapeIds.contains("flake/edit")) { 0780 0781 bool currentToolWorks = false; 0782 foreach (const QString &type, types) { 0783 if (activationShapeIds.contains(type)) { 0784 currentToolWorks = true; 0785 break; 0786 } 0787 } 0788 if (!currentToolWorks) { 0789 switchTool(KoInteractionTool_ID); 0790 } 0791 } 0792 0793 emit q->toolCodesSelected(types); 0794 } 0795 0796 void KoToolManager::Private::currentLayerChanged(const KoShapeLayer *layer) 0797 { 0798 emit q->currentLayerChanged(canvasData->canvas, layer); 0799 layerExplicitlyDisabled = layer && !layer->isShapeEditable(); 0800 updateToolForProxy(); 0801 0802 debugFlake << "Layer changed to" << layer << "explicitly disabled:" << layerExplicitlyDisabled; 0803 } 0804 0805 void KoToolManager::Private::updateToolForProxy() 0806 { 0807 KoToolProxy *proxy = proxies.value(canvasData->canvas->canvas()); 0808 if(!proxy) return; 0809 0810 bool canUseTool = !layerExplicitlyDisabled || canvasData->activationShapeId.endsWith(QLatin1String("/always")); 0811 proxy->setActiveTool(canUseTool ? canvasData->activeTool : 0); 0812 } 0813 0814 void KoToolManager::Private::switchInputDevice(const KoInputDevice &device) 0815 { 0816 Q_ASSERT(canvasData); 0817 if (!canvasData) return; 0818 if (inputDevice == device) return; 0819 if (inputDevice.isMouse() && device.isMouse()) return; 0820 if (device.isMouse() && !inputDevice.isMouse()) { 0821 // we never switch back to mouse from a tablet input device, so the user can use the 0822 // mouse to edit the settings for a tool activated by a tablet. See bugs 0823 // https://bugs.kde.org/show_bug.cgi?id=283130 and https://bugs.kde.org/show_bug.cgi?id=285501. 0824 // We do continue to switch between tablet devices, thought. 0825 return; 0826 } 0827 0828 QList<CanvasData*> items = canvasses[canvasData->canvas]; 0829 0830 // search for a canvasdata object for the current input device 0831 Q_FOREACH (CanvasData *cd, items) { 0832 if (cd->inputDevice == device) { 0833 switchCanvasData(cd); 0834 0835 if (!canvasData->activeTool) { 0836 switchTool(KoInteractionTool_ID); 0837 } 0838 0839 return; 0840 } 0841 } 0842 0843 // still here? That means we need to create a new CanvasData instance with the current InputDevice. 0844 CanvasData *cd = createCanvasData(canvasData->canvas, device); 0845 // switch to new canvas as the active one. 0846 QString oldTool = canvasData->activeToolId; 0847 0848 items.append(cd); 0849 canvasses[cd->canvas] = items; 0850 0851 switchCanvasData(cd); 0852 0853 switchTool(oldTool); 0854 } 0855 0856 void KoToolManager::Private::registerToolProxy(KoToolProxy *proxy, KoCanvasBase *canvas) 0857 { 0858 proxies.insert(canvas, proxy); 0859 Q_FOREACH (KoCanvasController *controller, canvasses.keys()) { 0860 if (controller->canvas() == canvas) { 0861 proxy->priv()->setCanvasController(controller); 0862 break; 0863 } 0864 } 0865 } 0866 0867 //have to include this because of Q_PRIVATE_SLOT 0868 #include "moc_KoToolManager.cpp"