Changeset View
Changeset View
Standalone View
Standalone View
libs/flake/KoToolManager.cpp
Show All 34 Lines | |||||
35 | #include "KoShapeManager.h" | 35 | #include "KoShapeManager.h" | ||
36 | #include "KoCanvasBase.h" | 36 | #include "KoCanvasBase.h" | ||
37 | #include "KoInputDeviceHandlerRegistry.h" | 37 | #include "KoInputDeviceHandlerRegistry.h" | ||
38 | #include "KoInputDeviceHandlerEvent.h" | 38 | #include "KoInputDeviceHandlerEvent.h" | ||
39 | #include "KoPointerEvent.h" | 39 | #include "KoPointerEvent.h" | ||
40 | #include "tools/KoCreateShapesTool.h" | 40 | #include "tools/KoCreateShapesTool.h" | ||
41 | #include "tools/KoZoomTool.h" | 41 | #include "tools/KoZoomTool.h" | ||
42 | #include "tools/KoPanTool.h" | 42 | #include "tools/KoPanTool.h" | ||
43 | #include "kis_action_registry.h" | ||||
43 | 44 | | |||
44 | // Qt + kde | 45 | // Qt + kde | ||
45 | #include <QWidget> | 46 | #include <QWidget> | ||
46 | #include <QEvent> | 47 | #include <QEvent> | ||
47 | #include <QWheelEvent> | 48 | #include <QWheelEvent> | ||
48 | #include <QMouseEvent> | 49 | #include <QMouseEvent> | ||
49 | #include <QPaintEvent> | 50 | #include <QPaintEvent> | ||
50 | #include <QTabletEvent> | 51 | #include <QTabletEvent> | ||
51 | #include <QKeyEvent> | 52 | #include <QKeyEvent> | ||
52 | #include <QVBoxLayout> | 53 | #include <QVBoxLayout> | ||
53 | #include <QStringList> | 54 | #include <QStringList> | ||
54 | #include <QApplication> | 55 | #include <QApplication> | ||
55 | #include <kactioncollection.h> | 56 | #include <kactioncollection.h> | ||
57 | #include <kactioncategory.h> | ||||
56 | #include <FlakeDebug.h> | 58 | #include <FlakeDebug.h> | ||
57 | 59 | | |||
58 | #include <QAction> | 60 | #include <QAction> | ||
59 | #include <klocalizedstring.h> | 61 | #include <klocalizedstring.h> | ||
60 | #include <QKeySequence> | 62 | #include <QKeySequence> | ||
61 | #include <QStack> | 63 | #include <QStack> | ||
62 | #include <QLabel> | 64 | #include <QLabel> | ||
63 | #include <QGlobalStatic> | 65 | #include <QGlobalStatic> | ||
64 | 66 | | |||
65 | Q_GLOBAL_STATIC(KoToolManager, s_instance) | 67 | Q_GLOBAL_STATIC(KoToolManager, s_instance) | ||
66 | 68 | | |||
67 | class Q_DECL_HIDDEN KoToolAction::Private | | |||
68 | { | | |||
69 | public: | | |||
70 | ToolHelper* toolHelper; | | |||
71 | }; | | |||
72 | | ||||
73 | KoToolAction::KoToolAction(ToolHelper* toolHelper) | | |||
74 | : QObject(toolHelper) | | |||
75 | , d(new Private) | | |||
76 | { | | |||
77 | d->toolHelper = toolHelper; | | |||
78 | } | | |||
79 | | ||||
80 | KoToolAction::~KoToolAction() | | |||
81 | { | | |||
82 | delete d; | | |||
83 | } | | |||
84 | | ||||
85 | void KoToolAction::trigger() | | |||
86 | { | | |||
87 | d->toolHelper->activate(); | | |||
88 | } | | |||
89 | | ||||
90 | | ||||
91 | QString KoToolAction::iconText() const | | |||
92 | { | | |||
93 | return d->toolHelper->iconText(); | | |||
94 | } | | |||
95 | | ||||
96 | QString KoToolAction::toolTip() const | | |||
97 | { | | |||
98 | return d->toolHelper->toolTip(); | | |||
99 | } | | |||
100 | | ||||
101 | QString KoToolAction::id() const | | |||
102 | { | | |||
103 | return d->toolHelper->id(); | | |||
104 | } | | |||
105 | | ||||
106 | QString KoToolAction::iconName() const | | |||
107 | { | | |||
108 | return d->toolHelper->iconName(); | | |||
109 | } | | |||
110 | | ||||
111 | QKeySequence KoToolAction::shortcut() const | | |||
112 | { | | |||
113 | return d->toolHelper->shortcut(); | | |||
114 | } | | |||
115 | | ||||
116 | | ||||
117 | QString KoToolAction::section() const | | |||
118 | { | | |||
119 | return d->toolHelper->toolType(); | | |||
120 | } | | |||
121 | | ||||
122 | int KoToolAction::priority() const | | |||
123 | { | | |||
124 | return d->toolHelper->priority(); | | |||
125 | } | | |||
126 | | ||||
127 | int KoToolAction::buttonGroupId() const | | |||
128 | { | | |||
129 | return d->toolHelper->uniqueId(); | | |||
130 | } | | |||
131 | | ||||
132 | QString KoToolAction::visibilityCode() const | | |||
133 | { | | |||
134 | return d->toolHelper->activationShapeId(); | | |||
135 | } | | |||
136 | | ||||
137 | 69 | | |||
138 | class CanvasData | 70 | class CanvasData | ||
139 | { | 71 | { | ||
140 | public: | 72 | public: | ||
141 | CanvasData(KoCanvasController *cc, const KoInputDevice &id) | 73 | CanvasData(KoCanvasController *cc, const KoInputDevice &id) | ||
142 | : activeTool(0), | 74 | : activeTool(0), | ||
143 | canvas(cc), | 75 | canvas(cc), | ||
144 | inputDevice(id), | 76 | inputDevice(id), | ||
▲ Show 20 Lines • Show All 105 Lines • ▼ Show 20 Line(s) | |||||
250 | const KoInputDevice inputDevice; | 182 | const KoInputDevice inputDevice; | ||
251 | QWidget *dummyToolWidget; // the widget shown in the toolDocker. | 183 | QWidget *dummyToolWidget; // the widget shown in the toolDocker. | ||
252 | QLabel *dummyToolLabel; | 184 | QLabel *dummyToolLabel; | ||
253 | QList<QPointer<QAction> > disabledActions; ///< disabled conflicting actions | 185 | QList<QPointer<QAction> > disabledActions; ///< disabled conflicting actions | ||
254 | QList<QPointer<QAction> > disabledDisabledActions; ///< disabled conflicting actions that were already disabled | 186 | QList<QPointer<QAction> > disabledDisabledActions; ///< disabled conflicting actions that were already disabled | ||
255 | QMap<QPointer<QAction>, QString> disabledCanvasShortcuts; ///< Shortcuts that were temporarily removed from canvas actions because the tool overrides | 187 | QMap<QPointer<QAction>, QString> disabledCanvasShortcuts; ///< Shortcuts that were temporarily removed from canvas actions because the tool overrides | ||
256 | }; | 188 | }; | ||
257 | 189 | | |||
258 | KoToolManager::Private::Private(KoToolManager *qq) | 190 | | ||
259 | : q(qq), | 191 | // ******** KoToolManager ********** | ||
260 | canvasData(0), | 192 | KoToolManager::KoToolManager() | ||
261 | layerExplicitlyDisabled(false) | 193 | : QObject(), | ||
194 | d(new Private(this)) | ||||
262 | { | 195 | { | ||
196 | connect(QApplication::instance(), SIGNAL(focusChanged(QWidget*, QWidget*)), | ||||
197 | this, SLOT(movedFocus(QWidget*, QWidget*))); | ||||
263 | } | 198 | } | ||
264 | 199 | | |||
265 | KoToolManager::Private::~Private() | 200 | KoToolManager::~KoToolManager() | ||
266 | { | 201 | { | ||
267 | qDeleteAll(tools); | 202 | delete d; | ||
268 | } | 203 | } | ||
269 | 204 | | |||
270 | // helper method. | 205 | QList<KoToolAction*> KoToolManager::toolActionList() const | ||
271 | CanvasData *KoToolManager::Private::createCanvasData(KoCanvasController *controller, const KoInputDevice &device) | | |||
272 | { | 206 | { | ||
273 | QHash<QString, KoToolBase*> toolsHash; | 207 | QList<KoToolAction*> answer; | ||
274 | foreach(ToolHelper *tool, tools) { | 208 | answer.reserve(d->tools.count()); | ||
275 | QPair<QString, KoToolBase*> toolPair = q->createTools(controller, tool); | 209 | foreach(ToolHelper *tool, d->tools) { | ||
276 | if (toolPair.second) { // only if a real tool was created | 210 | if (tool->id() == KoCreateShapesTool_ID) | ||
277 | toolsHash.insert(toolPair.first, toolPair.second); | 211 | continue; // don't show this one. | ||
212 | answer.append(tool->toolAction()); | ||||
278 | } | 213 | } | ||
214 | return answer; | ||||
279 | } | 215 | } | ||
280 | KoCreateShapesTool *createShapesTool = dynamic_cast<KoCreateShapesTool*>(toolsHash.value(KoCreateShapesTool_ID)); | | |||
281 | Q_ASSERT(createShapesTool); | | |||
282 | QString id = KoShapeRegistry::instance()->keys()[0]; | | |||
283 | createShapesTool->setShapeId(id); | | |||
284 | 216 | | |||
285 | CanvasData *cd = new CanvasData(controller, device); | 217 | void KoToolManager::requestToolActivation(KoCanvasController * controller) | ||
286 | cd->allTools = toolsHash; | 218 | { | ||
287 | return cd; | 219 | if (d->canvasses.contains(controller)) { | ||
220 | QString activeToolId = d->canvasses.value(controller).first()->activeToolId; | ||||
221 | foreach(ToolHelper * th, d->tools) { | ||||
222 | if (th->id() == activeToolId) { | ||||
223 | d->toolActivated(th); | ||||
224 | break; | ||||
225 | } | ||||
226 | } | ||||
227 | } | ||||
288 | } | 228 | } | ||
289 | 229 | | |||
290 | void KoToolManager::Private::setup() | 230 | KoInputDevice KoToolManager::currentInputDevice() const | ||
291 | { | 231 | { | ||
292 | if (tools.size() > 0) | 232 | return d->inputDevice; | ||
293 | return; | | |||
294 | | ||||
295 | KoShapeRegistry::instance(); | | |||
296 | KoToolRegistry *registry = KoToolRegistry::instance(); | | |||
297 | foreach(const QString & id, registry->keys()) { | | |||
298 | ToolHelper *t = new ToolHelper(registry->value(id)); | | |||
299 | tools.append(t); | | |||
300 | } | 233 | } | ||
301 | 234 | | |||
302 | // connect to all tools so we can hear their button-clicks | 235 | void KoToolManager::registerToolActions(KActionCollection *ac, KoCanvasController *controller) | ||
303 | foreach(ToolHelper *tool, tools) | 236 | { | ||
304 | connect(tool, SIGNAL(toolActivated(ToolHelper*)), q, SLOT(toolActivated(ToolHelper*))); | 237 | Q_ASSERT(controller); | ||
238 | Q_ASSERT(ac); | ||||
305 | 239 | | |||
306 | // load pluggable input devices | 240 | d->setup(); | ||
307 | KoInputDeviceHandlerRegistry::instance(); | 241 | | ||
242 | if (!d->canvasses.contains(controller)) { | ||||
243 | return; | ||||
308 | } | 244 | } | ||
309 | 245 | | |||
310 | void KoToolManager::Private::connectActiveTool() | 246 | // Actions available during the use of individual tools | ||
311 | { | 247 | CanvasData *cd = d->canvasses.value(controller).first(); | ||
312 | if (canvasData->activeTool) { | 248 | foreach(KoToolBase *tool, cd->allTools) { | ||
313 | connect(canvasData->activeTool, SIGNAL(cursorChanged(const QCursor &)), | 249 | QHash<QString, QAction*> actions = tool->actions(); | ||
314 | q, SLOT(updateCursor(const QCursor &))); | 250 | QHash<QString, QAction*>::const_iterator action(actions.constBegin()); | ||
315 | connect(canvasData->activeTool, SIGNAL(activateTool(const QString &)), | 251 | for (; action != actions.constEnd(); ++action) { | ||
316 | q, SLOT(switchToolRequested(const QString &))); | 252 | if (!ac->action(action.key())) | ||
317 | connect(canvasData->activeTool, SIGNAL(activateTemporary(const QString &)), | 253 | ac->addAction(action.key(), action.value()); | ||
318 | q, SLOT(switchToolTemporaryRequested(const QString &))); | 254 | } | ||
319 | connect(canvasData->activeTool, SIGNAL(done()), q, SLOT(switchBackRequested())); | | |||
320 | connect(canvasData->activeTool, SIGNAL(statusTextChanged(const QString &)), | | |||
321 | q, SIGNAL(changedStatusText(const QString &))); | | |||
322 | } | 255 | } | ||
323 | 256 | | |||
324 | // we expect the tool to emit a cursor on activation. | 257 | // Actions used to switch tools via shortcuts | ||
325 | updateCursor(Qt::ForbiddenCursor); | 258 | foreach(ToolHelper * th, d->tools) { | ||
259 | if (ac->action(th->id())) { | ||||
260 | continue; | ||||
261 | } | ||||
262 | ShortcutToolAction* action = th->createShortcutToolAction(ac); | ||||
263 | ac->addCategorizedAction(th->id(), action, "tool-shortcuts"); | ||||
264 | } | ||||
326 | } | 265 | } | ||
327 | 266 | | |||
328 | void KoToolManager::Private::disconnectActiveTool() | 267 | void KoToolManager::addController(KoCanvasController *controller) | ||
329 | { | 268 | { | ||
330 | if (canvasData->activeTool) { | 269 | Q_ASSERT(controller); | ||
331 | canvasData->deactivateToolActions(); | 270 | if (d->canvasses.contains(controller)) | ||
332 | // repaint the decorations before we deactivate the tool as it might deleted | 271 | return; | ||
333 | // data needed for the repaint | 272 | d->setup(); | ||
334 | canvasData->activeTool->deactivate(); | 273 | d->attachCanvas(controller); | ||
335 | disconnect(canvasData->activeTool, SIGNAL(cursorChanged(const QCursor&)), | 274 | connect(controller->proxyObject, SIGNAL(destroyed(QObject*)), this, SLOT(attemptCanvasControllerRemoval(QObject*))); | ||
336 | q, SLOT(updateCursor(const QCursor&))); | 275 | connect(controller->proxyObject, SIGNAL(canvasRemoved(KoCanvasController*)), this, SLOT(detachCanvas(KoCanvasController*))); | ||
337 | disconnect(canvasData->activeTool, SIGNAL(activateTool(const QString &)), | 276 | connect(controller->proxyObject, SIGNAL(canvasSet(KoCanvasController*)), this, SLOT(attachCanvas(KoCanvasController*))); | ||
338 | q, SLOT(switchToolRequested(const QString &))); | | |||
339 | disconnect(canvasData->activeTool, SIGNAL(activateTemporary(const QString &)), | | |||
340 | q, SLOT(switchToolTemporaryRequested(const QString &))); | | |||
341 | disconnect(canvasData->activeTool, SIGNAL(done()), q, SLOT(switchBackRequested())); | | |||
342 | disconnect(canvasData->activeTool, SIGNAL(statusTextChanged(const QString &)), | | |||
343 | q, SIGNAL(changedStatusText(const QString &))); | | |||
344 | } | 277 | } | ||
345 | 278 | | |||
346 | // emit a empty status text to clear status text from last active tool | 279 | void KoToolManager::removeCanvasController(KoCanvasController *controller) | ||
347 | emit q->changedStatusText(QString()); | 280 | { | ||
281 | Q_ASSERT(controller); | ||||
282 | disconnect(controller->proxyObject, SIGNAL(canvasRemoved(KoCanvasController*)), this, SLOT(detachCanvas(KoCanvasController*))); | ||||
283 | disconnect(controller->proxyObject, SIGNAL(canvasSet(KoCanvasController*)), this, SLOT(attachCanvas(KoCanvasController*))); | ||||
284 | d->detachCanvas(controller); | ||||
348 | } | 285 | } | ||
349 | 286 | | |||
350 | void KoToolManager::Private::switchTool(KoToolBase *tool, bool temporary) | 287 | void KoToolManager::attemptCanvasControllerRemoval(QObject* controller) | ||
351 | { | 288 | { | ||
289 | KoCanvasControllerProxyObject* controllerActual = qobject_cast<KoCanvasControllerProxyObject*>(controller); | ||||
290 | if (controllerActual) { | ||||
291 | removeCanvasController(controllerActual->canvasController()); | ||||
292 | } | ||||
293 | } | ||||
352 | 294 | | |||
353 | Q_ASSERT(tool); | 295 | void KoToolManager::updateShapeControllerBase(KoShapeBasedDocumentBase *shapeController, KoCanvasController *canvasController) | ||
354 | if (canvasData == 0) | 296 | { | ||
355 | return; | 297 | if (!d->canvasses.contains(canvasController)) | ||
356 | | ||||
357 | if (canvasData->activeTool == tool && tool->toolId() != KoInteractionTool_ID) | | |||
358 | return; | 298 | return; | ||
359 | 299 | | |||
360 | disconnectActiveTool(); | 300 | QList<CanvasData *> canvasses = d->canvasses[canvasController]; | ||
361 | canvasData->activeTool = tool; | 301 | foreach(CanvasData *canvas, canvasses) { | ||
362 | connectActiveTool(); | 302 | foreach(KoToolBase *tool, canvas->allTools.values()) { | ||
363 | postSwitchTool(temporary); | 303 | tool->updateShapeController(shapeController); | ||
304 | } | ||||
305 | } | ||||
364 | } | 306 | } | ||
365 | 307 | | |||
366 | void KoToolManager::Private::switchTool(const QString &id, bool temporary) | 308 | void KoToolManager::switchToolRequested(const QString & id) | ||
367 | { | 309 | { | ||
368 | Q_ASSERT(canvasData); | 310 | Q_ASSERT(d->canvasData); | ||
369 | if (!canvasData) return; | 311 | if (!d->canvasData) return; | ||
370 | 312 | | |||
371 | if (canvasData->activeTool && temporary) | 313 | while (!d->canvasData->stack.isEmpty()) // switching means to flush the stack | ||
372 | canvasData->stack.push(canvasData->activeToolId); | 314 | d->canvasData->stack.pop(); | ||
373 | canvasData->activeToolId = id; | 315 | d->switchTool(id, false); | ||
374 | KoToolBase *tool = canvasData->allTools.value(id); | | |||
375 | if (! tool) { | | |||
376 | return; | | |||
377 | } | 316 | } | ||
378 | 317 | | |||
379 | foreach(ToolHelper *th, tools) { | 318 | void KoToolManager::switchInputDeviceRequested(const KoInputDevice &id) | ||
380 | if (th->id() == id) { | 319 | { | ||
381 | canvasData->activationShapeId = th->activationShapeId(); | 320 | if (!d->canvasData) return; | ||
382 | break; | 321 | d->switchInputDevice(id); | ||
383 | } | | |||
384 | } | 322 | } | ||
385 | 323 | | |||
386 | switchTool(tool, temporary); | 324 | void KoToolManager::switchToolTemporaryRequested(const QString &id) | ||
325 | { | ||||
326 | d->switchTool(id, true); | ||||
387 | } | 327 | } | ||
388 | 328 | | |||
389 | void KoToolManager::Private::postSwitchTool(bool temporary) | 329 | void KoToolManager::switchBackRequested() | ||
390 | { | 330 | { | ||
391 | #ifndef NDEBUG | 331 | if (!d->canvasData) return; | ||
392 | int canvasCount = 1; | 332 | | ||
393 | foreach(QList<CanvasData*> list, canvasses) { | 333 | if (d->canvasData->stack.isEmpty()) { | ||
394 | bool first = true; | 334 | // default to changing to the interactionTool | ||
395 | foreach(CanvasData *data, list) { | 335 | d->switchTool(KoInteractionTool_ID, false); | ||
396 | if (first) { | 336 | return; | ||
397 | debugFlake << "Canvas" << canvasCount++; | | |||
398 | } | | |||
399 | debugFlake << " +- Tool:" << data->activeToolId << (data == canvasData ? " *" : ""); | | |||
400 | first = false; | | |||
401 | } | 337 | } | ||
338 | d->switchTool(d->canvasData->stack.pop(), false); | ||||
402 | } | 339 | } | ||
403 | #endif | | |||
404 | Q_ASSERT(canvasData); | | |||
405 | if (!canvasData) return; | | |||
406 | 340 | | |||
407 | KoToolBase::ToolActivation toolActivation; | 341 | KoCreateShapesTool * KoToolManager::shapeCreatorTool(KoCanvasBase *canvas) const | ||
408 | if (temporary) | 342 | { | ||
409 | toolActivation = KoToolBase::TemporaryActivation; | 343 | Q_ASSERT(canvas); | ||
410 | else | 344 | foreach(KoCanvasController *controller, d->canvasses.keys()) { | ||
411 | toolActivation = KoToolBase::DefaultActivation; | 345 | if (controller->canvas() == canvas) { | ||
412 | QSet<KoShape*> shapesToOperateOn; | 346 | KoCreateShapesTool *createTool = dynamic_cast<KoCreateShapesTool*> | ||
413 | if (canvasData->activeTool | 347 | (d->canvasData->allTools.value(KoCreateShapesTool_ID)); | ||
414 | && canvasData->activeTool->canvas() | 348 | Q_ASSERT(createTool /* ID changed? */); | ||
415 | && canvasData->activeTool->canvas()->shapeManager()) { | 349 | return createTool; | ||
416 | KoSelection *selection = canvasData->activeTool->canvas()->shapeManager()->selection(); | | |||
417 | Q_ASSERT(selection); | | |||
418 | | ||||
419 | foreach(KoShape *shape, selection->selectedShapes()) { | | |||
420 | QSet<KoShape*> delegates = shape->toolDelegates(); | | |||
421 | if (delegates.isEmpty()) { // no delegates, just the orig shape | | |||
422 | shapesToOperateOn << shape; | | |||
423 | } else { | | |||
424 | shapesToOperateOn += delegates; | | |||
425 | } | 350 | } | ||
426 | } | 351 | } | ||
352 | Q_ASSERT(0); // this should not happen | ||||
353 | return 0; | ||||
427 | } | 354 | } | ||
428 | 355 | | |||
429 | if (canvasData->canvas->canvas()) { | 356 | KoToolBase *KoToolManager::toolById(KoCanvasBase *canvas, const QString &id) const | ||
430 | // Caller of postSwitchTool expect this to be called to update the selected tool | 357 | { | ||
431 | updateToolForProxy(); | 358 | Q_ASSERT(canvas); | ||
432 | canvasData->activeTool->activate(toolActivation, shapesToOperateOn); | 359 | foreach(KoCanvasController *controller, d->canvasses.keys()) { | ||
433 | KoCanvasBase *canvas = canvasData->canvas->canvas(); | 360 | if (controller->canvas() == canvas) | ||
434 | canvas->updateInputMethodInfo(); | 361 | return d->canvasData->allTools.value(id); | ||
435 | } else { | 362 | } | ||
436 | canvasData->activeTool->activate(toolActivation, shapesToOperateOn); | 363 | return 0; | ||
437 | } | 364 | } | ||
438 | 365 | | |||
439 | QList<QPointer<QWidget> > optionWidgetList = canvasData->activeTool->optionWidgets(); | 366 | KoCanvasController *KoToolManager::activeCanvasController() const | ||
440 | if (optionWidgetList.empty()) { // no option widget. | 367 | { | ||
441 | QWidget *toolWidget; | 368 | if (! d->canvasData) return 0; | ||
442 | QString title; | 369 | return d->canvasData->canvas; | ||
443 | foreach(ToolHelper *tool, tools) { | 370 | } | ||
444 | if (tool->id() == canvasData->activeTool->toolId()) { | 371 | | ||
445 | title = tool->toolTip(); | 372 | QString KoToolManager::preferredToolForSelection(const QList<KoShape*> &shapes) | ||
373 | { | ||||
374 | QList<QString> types; | ||||
375 | foreach(KoShape *shape, shapes) | ||||
376 | if (! types.contains(shape->shapeId())) | ||||
377 | types.append(shape->shapeId()); | ||||
378 | | ||||
379 | QString toolType = KoInteractionTool_ID; | ||||
380 | int prio = INT_MAX; | ||||
381 | foreach(ToolHelper *helper, d->tools) { | ||||
382 | if (helper->priority() >= prio) | ||||
383 | continue; | ||||
384 | if (helper->toolType() == KoToolFactoryBase::mainToolType()) | ||||
385 | continue; | ||||
386 | | ||||
387 | bool toolWillWork = false; | ||||
388 | foreach (const QString &type, types) { | ||||
389 | if (helper->activationShapeId().split(',').contains(type)) { | ||||
390 | toolWillWork = true; | ||||
446 | break; | 391 | break; | ||
447 | } | 392 | } | ||
448 | } | 393 | } | ||
449 | toolWidget = canvasData->dummyToolWidget; | 394 | if (toolWillWork) { | ||
450 | if (toolWidget == 0) { | 395 | toolType = helper->id(); | ||
451 | toolWidget = new QWidget(); | 396 | prio = helper->priority(); | ||
452 | toolWidget->setObjectName("DummyToolWidget"); | | |||
453 | QVBoxLayout *layout = new QVBoxLayout(toolWidget); | | |||
454 | layout->setMargin(3); | | |||
455 | canvasData->dummyToolLabel = new QLabel(toolWidget); | | |||
456 | layout->addWidget(canvasData->dummyToolLabel); | | |||
457 | layout->addItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::Expanding)); | | |||
458 | toolWidget->setLayout(layout); | | |||
459 | canvasData->dummyToolWidget = toolWidget; | | |||
460 | } | 397 | } | ||
461 | canvasData->dummyToolLabel->setText(i18n("Active tool: %1", title)); | 398 | } | ||
462 | optionWidgetList.append(toolWidget); | 399 | return toolType; | ||
463 | } | 400 | } | ||
464 | 401 | | |||
465 | // Activate the actions for the currently active tool | 402 | void KoToolManager::injectDeviceEvent(KoInputDeviceHandlerEvent * event) | ||
466 | canvasData->activateToolActions(); | 403 | { | ||
467 | 404 | if (d->canvasData && d->canvasData->canvas->canvas()) { | |||
468 | emit q->changedTool(canvasData->canvas, uniqueToolIds.value(canvasData->activeTool)); | 405 | if (static_cast<KoInputDeviceHandlerEvent::Type>(event->type()) == KoInputDeviceHandlerEvent::ButtonPressed) | ||
469 | 406 | d->canvasData->activeTool->customPressEvent(event->pointerEvent()); | |||
470 | KoCanvasControllerWidget *canvasControllerWidget = dynamic_cast<KoCanvasControllerWidget*>(canvasData->canvas); | 407 | else if (static_cast<KoInputDeviceHandlerEvent::Type>(event->type()) == KoInputDeviceHandlerEvent::ButtonReleased) | ||
471 | if (canvasControllerWidget) { | 408 | d->canvasData->activeTool->customReleaseEvent(event->pointerEvent()); | ||
472 | canvasControllerWidget->setToolOptionWidgets(optionWidgetList); | 409 | else if (static_cast<KoInputDeviceHandlerEvent::Type>(event->type()) == KoInputDeviceHandlerEvent::PositionChanged) | ||
410 | d->canvasData->activeTool->customMoveEvent(event->pointerEvent()); | ||||
473 | } | 411 | } | ||
474 | } | 412 | } | ||
475 | 413 | | |||
476 | 414 | void KoToolManager::addDeferredToolFactory(KoToolFactoryBase *toolFactory) | |||
477 | void KoToolManager::Private::switchCanvasData(CanvasData *cd) | | |||
478 | { | 415 | { | ||
479 | Q_ASSERT(cd); | 416 | ToolHelper *tool = new ToolHelper(toolFactory); | ||
417 | // make sure all plugins are loaded as otherwise we will not load them | ||||
418 | d->setup(); | ||||
419 | d->tools.append(tool); | ||||
480 | 420 | | |||
481 | KoCanvasBase *oldCanvas = 0; | 421 | // connect to all tools so we can hear their button-clicks | ||
482 | KoInputDevice oldInputDevice; | 422 | connect(tool, SIGNAL(toolActivated(ToolHelper*)), this, SLOT(toolActivated(ToolHelper*))); | ||
483 | 423 | | |||
484 | if (canvasData) { | 424 | // now create tools for all existing canvases | ||
485 | oldCanvas = canvasData->canvas->canvas(); | 425 | foreach(KoCanvasController *controller, d->canvasses.keys()) { | ||
486 | oldInputDevice = canvasData->inputDevice; | | |||
487 | 426 | | |||
488 | if (canvasData->activeTool) { | 427 | // this canvascontroller is unknown, which is weird | ||
489 | disconnectActiveTool(); | 428 | if (!d->canvasses.contains(controller)) { | ||
429 | continue; | ||||
490 | } | 430 | } | ||
491 | 431 | | |||
492 | KoToolProxy *proxy = proxies.value(oldCanvas); | 432 | // create a tool for all canvasdata objects (i.e., all input devices on this canvas) | ||
493 | Q_ASSERT(proxy); | 433 | foreach (CanvasData *cd, d->canvasses[controller]) { | ||
494 | proxy->setActiveTool(0); | 434 | QPair<QString, KoToolBase*> toolPair = createTools(controller, tool); | ||
435 | if (toolPair.second) { | ||||
436 | cd->allTools.insert(toolPair.first, toolPair.second); | ||||
495 | } | 437 | } | ||
496 | | ||||
497 | canvasData = cd; | | |||
498 | inputDevice = canvasData->inputDevice; | | |||
499 | | ||||
500 | if (canvasData->activeTool) { | | |||
501 | connectActiveTool(); | | |||
502 | postSwitchTool(false); | | |||
503 | } | 438 | } | ||
504 | 439 | | |||
505 | if (oldInputDevice != canvasData->inputDevice) { | 440 | // Then create a button for the toolbox for this canvas | ||
506 | emit q->inputDeviceChanged(canvasData->inputDevice); | 441 | if (tool->id() == KoCreateShapesTool_ID) { | ||
442 | continue; | ||||
507 | } | 443 | } | ||
508 | 444 | | |||
509 | if (oldCanvas != canvasData->canvas->canvas()) { | 445 | emit addedTool(tool->toolAction(), controller); | ||
510 | emit q->changedCanvas(canvasData->canvas->canvas()); | | |||
511 | } | 446 | } | ||
512 | } | 447 | } | ||
513 | 448 | | |||
514 | 449 | QPair<QString, KoToolBase*> KoToolManager::createTools(KoCanvasController *controller, ToolHelper *tool) | |||
515 | void KoToolManager::Private::toolActivated(ToolHelper *tool) | | |||
516 | { | 450 | { | ||
517 | Q_ASSERT(tool); | 451 | // XXX: maybe this method should go into the private class? | ||
518 | 452 | | |||
519 | Q_ASSERT(canvasData); | 453 | QHash<QString, KoToolBase*> origHash; | ||
520 | if (!canvasData) return; | | |||
521 | KoToolBase *t = canvasData->allTools.value(tool->id()); | | |||
522 | Q_ASSERT(t); | | |||
523 | 454 | | |||
524 | canvasData->activeToolId = tool->id(); | 455 | if (d->canvasses.contains(controller)) { | ||
525 | canvasData->activationShapeId = tool->activationShapeId(); | 456 | origHash = d->canvasses.value(controller).first()->allTools; | ||
457 | } | ||||
526 | 458 | | |||
527 | switchTool(t, false); | 459 | if (origHash.contains(tool->id())) { | ||
460 | return QPair<QString, KoToolBase*>(tool->id(), origHash.value(tool->id())); | ||||
528 | } | 461 | } | ||
529 | 462 | | |||
530 | void KoToolManager::Private::detachCanvas(KoCanvasController *controller) | 463 | debugFlake << "Creating tool" << tool->id() << ". Activated on:" << tool->activationShapeId() << ", prio:" << tool->priority(); | ||
531 | { | 464 | | ||
532 | Q_ASSERT(controller); | 465 | KoToolBase *tl = tool->createTool(controller->canvas()); | ||
533 | // check if we are removing the active canvas controller | 466 | if (tl) { | ||
534 | if (canvasData && canvasData->canvas == controller) { | 467 | d->uniqueToolIds.insert(tl, tool->uniqueId()); | ||
535 | KoCanvasController *newCanvas = 0; | 468 | | ||
536 | // try to find another canvas controller beside the one we are removing | 469 | tl->setObjectName(tool->id()); | ||
537 | foreach(KoCanvasController* canvas, canvasses.keys()) { | 470 | | ||
538 | if (canvas != controller) { | 471 | foreach(QAction *action, tl->actions()) { | ||
539 | // yay found one | 472 | action->setEnabled(false); | ||
540 | newCanvas = canvas; | | |||
541 | break; | | |||
542 | } | 473 | } | ||
474 | | ||||
543 | } | 475 | } | ||
544 | if (newCanvas) { | 476 | | ||
545 | switchCanvasData(canvasses.value(newCanvas).first()); | 477 | KoZoomTool *zoomTool = dynamic_cast<KoZoomTool*>(tl); | ||
546 | } else { | 478 | if (zoomTool) { | ||
547 | KoCanvasControllerWidget *canvasControllerWidget = dynamic_cast<KoCanvasControllerWidget*>(canvasData->canvas); | 479 | zoomTool->setCanvasController(controller); | ||
548 | if (canvasControllerWidget) { | | |||
549 | canvasControllerWidget->setToolOptionWidgets(QList<QPointer<QWidget> >()); | | |||
550 | } | 480 | } | ||
551 | // as a last resort just set a blank one | 481 | | ||
552 | canvasData = 0; | 482 | KoPanTool *panTool = dynamic_cast<KoPanTool*>(tl); | ||
483 | if (panTool) { | ||||
484 | panTool->setCanvasController(controller); | ||||
553 | } | 485 | } | ||
486 | | ||||
487 | return QPair<QString, KoToolBase*>(tool->id(), tl); | ||||
554 | } | 488 | } | ||
555 | 489 | | |||
556 | KoToolProxy *proxy = proxies.value(controller->canvas()); | | |||
557 | if (proxy) | | |||
558 | proxy->setActiveTool(0); | | |||
559 | 490 | | |||
560 | QList<KoToolBase *> tools; | 491 | // NOT IMPLEMENTED | ||
561 | foreach(CanvasData *canvasData, canvasses.value(controller)) { | 492 | void KoToolManager::updateToolShortcuts() | ||
562 | foreach(KoToolBase *tool, canvasData->allTools) { | 493 | { | ||
563 | if (! tools.contains(tool)) { | 494 | // auto actionRegistry = KisActionRegistry::instance(); | ||
564 | tools.append(tool); | 495 | // foreach (KoToolBase *t, allTools) { | ||
565 | } | 496 | // for (auto it = t->actions().constBegin(); | ||
566 | } | 497 | // it != t->actions().constEnd(); | ||
567 | delete canvasData; | 498 | // ++it;) { | ||
568 | } | 499 | // actionRegistry->updateShortcut(it.key(), it.value()); | ||
569 | foreach(KoToolBase *tool, tools) { | 500 | // } | ||
570 | uniqueToolIds.remove(tool); | 501 | // } | ||
571 | delete tool; | | |||
572 | } | 502 | } | ||
573 | canvasses.remove(controller); | 503 | | ||
574 | emit q->changedCanvas(canvasData ? canvasData->canvas->canvas() : 0); | 504 | KoToolManager* KoToolManager::instance() | ||
505 | { | ||||
506 | return s_instance; | ||||
575 | } | 507 | } | ||
576 | 508 | | |||
577 | void KoToolManager::Private::attachCanvas(KoCanvasController *controller) | 509 | QString KoToolManager::activeToolId() const | ||
578 | { | 510 | { | ||
579 | Q_ASSERT(controller); | 511 | if (!d->canvasData) return QString(); | ||
580 | CanvasData *cd = createCanvasData(controller, KoInputDevice::mouse()); | 512 | return d->canvasData->activeToolId; | ||
513 | } | ||||
581 | 514 | | |||
582 | // switch to new canvas as the active one. | | |||
583 | switchCanvasData(cd); | | |||
584 | 515 | | |||
585 | inputDevice = cd->inputDevice; | 516 | KoToolManager::Private *KoToolManager::priv() | ||
586 | QList<CanvasData*> canvasses_; | 517 | { | ||
587 | canvasses_.append(cd); | 518 | return d; | ||
588 | canvasses[controller] = canvasses_; | 519 | } | ||
589 | 520 | | |||
590 | KoToolProxy *tp = proxies[controller->canvas()]; | | |||
591 | if (tp) | | |||
592 | tp->priv()->setCanvasController(controller); | | |||
593 | 521 | | |||
594 | if (cd->activeTool == 0) { | 522 | /**** KoToolManager::Private ****/ | ||
595 | // no active tool, so we activate the highest priority main tool | 523 | | ||
596 | int highestPriority = INT_MAX; | 524 | KoToolManager::Private::Private(KoToolManager *qq) | ||
597 | ToolHelper * helper = 0; | 525 | : q(qq), | ||
598 | foreach(ToolHelper * th, tools) { | 526 | canvasData(0), | ||
599 | if (th->toolType() == KoToolFactoryBase::mainToolType()) { | 527 | layerExplicitlyDisabled(false) | ||
600 | if (th->priority() < highestPriority) { | 528 | { | ||
601 | highestPriority = qMin(highestPriority, th->priority()); | | |||
602 | helper = th; | | |||
603 | } | 529 | } | ||
530 | | ||||
531 | KoToolManager::Private::~Private() | ||||
532 | { | ||||
533 | qDeleteAll(tools); | ||||
604 | } | 534 | } | ||
535 | | ||||
536 | // helper method. | ||||
537 | CanvasData *KoToolManager::Private::createCanvasData(KoCanvasController *controller, const KoInputDevice &device) | ||||
538 | { | ||||
539 | QHash<QString, KoToolBase*> toolsHash; | ||||
540 | foreach(ToolHelper *tool, tools) { | ||||
541 | QPair<QString, KoToolBase*> toolPair = q->createTools(controller, tool); | ||||
542 | if (toolPair.second) { // only if a real tool was created | ||||
543 | toolsHash.insert(toolPair.first, toolPair.second); | ||||
605 | } | 544 | } | ||
606 | if (helper) | | |||
607 | toolActivated(helper); | | |||
608 | } | 545 | } | ||
546 | KoCreateShapesTool *createShapesTool = dynamic_cast<KoCreateShapesTool*>(toolsHash.value(KoCreateShapesTool_ID)); | ||||
547 | Q_ASSERT(createShapesTool); | ||||
548 | QString id = KoShapeRegistry::instance()->keys()[0]; | ||||
549 | createShapesTool->setShapeId(id); | ||||
609 | 550 | | |||
610 | Connector *connector = new Connector(controller->canvas()->shapeManager()); | 551 | CanvasData *cd = new CanvasData(controller, device); | ||
611 | connect(connector, SIGNAL(selectionChanged(QList<KoShape*>)), q, | 552 | cd->allTools = toolsHash; | ||
612 | SLOT(selectionChanged(QList<KoShape*>))); | 553 | return cd; | ||
613 | connect(controller->canvas()->shapeManager()->selection(), | | |||
614 | SIGNAL(currentLayerChanged(const KoShapeLayer*)), | | |||
615 | q, SLOT(currentLayerChanged(const KoShapeLayer*))); | | |||
616 | | ||||
617 | emit q->changedCanvas(canvasData ? canvasData->canvas->canvas() : 0); | | |||
618 | } | 554 | } | ||
619 | 555 | | |||
620 | void KoToolManager::Private::movedFocus(QWidget *from, QWidget *to) | 556 | void KoToolManager::Private::setup() | ||
621 | { | 557 | { | ||
622 | Q_UNUSED(from); | 558 | if (tools.size() > 0) | ||
623 | // no canvas anyway or no focus set anyway? | | |||
624 | if (!canvasData || to == 0) { | | |||
625 | return; | 559 | return; | ||
560 | | ||||
561 | KoShapeRegistry::instance(); | ||||
562 | KoToolRegistry *registry = KoToolRegistry::instance(); | ||||
563 | foreach(const QString & id, registry->keys()) { | ||||
564 | ToolHelper *t = new ToolHelper(registry->value(id)); | ||||
565 | tools.append(t); | ||||
626 | } | 566 | } | ||
627 | 567 | | |||
628 | // Check if this app is about QWidget-based KoCanvasControllerWidget canvasses | 568 | // connect to all tools so we can hear their button-clicks | ||
629 | // XXX: Focus handling for non-qwidget based canvases! | 569 | foreach(ToolHelper *tool, tools) | ||
630 | KoCanvasControllerWidget *canvasControllerWidget = dynamic_cast<KoCanvasControllerWidget*>(canvasData->canvas); | 570 | connect(tool, SIGNAL(toolActivated(ToolHelper*)), q, SLOT(toolActivated(ToolHelper*))); | ||
631 | if (!canvasControllerWidget) { | 571 | | ||
632 | return; | 572 | // load pluggable input devices | ||
573 | KoInputDeviceHandlerRegistry::instance(); | ||||
633 | } | 574 | } | ||
634 | 575 | | |||
635 | // canvasWidget is set as focusproxy for KoCanvasControllerWidget, | 576 | void KoToolManager::Private::connectActiveTool() | ||
636 | // so all focus checks are to be done against canvasWidget objects | 577 | { | ||
578 | if (canvasData->activeTool) { | ||||
579 | connect(canvasData->activeTool, SIGNAL(cursorChanged(const QCursor &)), | ||||
580 | q, SLOT(updateCursor(const QCursor &))); | ||||
581 | connect(canvasData->activeTool, SIGNAL(activateTool(const QString &)), | ||||
582 | q, SLOT(switchToolRequested(const QString &))); | ||||
583 | connect(canvasData->activeTool, SIGNAL(activateTemporary(const QString &)), | ||||
584 | q, SLOT(switchToolTemporaryRequested(const QString &))); | ||||
585 | connect(canvasData->activeTool, SIGNAL(done()), q, SLOT(switchBackRequested())); | ||||
586 | connect(canvasData->activeTool, SIGNAL(statusTextChanged(const QString &)), | ||||
587 | q, SIGNAL(changedStatusText(const QString &))); | ||||
588 | } | ||||
637 | 589 | | |||
638 | // focus returned to current canvas? | 590 | // we expect the tool to emit a cursor on activation. | ||
639 | if (to == canvasData->canvas->canvas()->canvasWidget()) { | 591 | updateCursor(Qt::ForbiddenCursor); | ||
640 | // nothing to do | | |||
641 | return; | | |||
642 | } | 592 | } | ||
643 | 593 | | |||
644 | // if the 'to' is one of our canvasWidgets, then switch. | | |||
645 | 594 | | |||
646 | // for code simplicity the current canvas will be checked again, | 595 | | ||
647 | // but would have been catched already in the lines above, so no issue | 596 | void KoToolManager::Private::disconnectActiveTool() | ||
648 | KoCanvasController *newCanvas = 0; | 597 | { | ||
649 | foreach(KoCanvasController* canvas, canvasses.keys()) { | 598 | if (canvasData->activeTool) { | ||
650 | if (canvas->canvas()->canvasWidget() == to) { | 599 | canvasData->deactivateToolActions(); | ||
651 | newCanvas = canvas; | 600 | // repaint the decorations before we deactivate the tool as it might deleted | ||
652 | break; | 601 | // data needed for the repaint | ||
602 | canvasData->activeTool->deactivate(); | ||||
603 | disconnect(canvasData->activeTool, SIGNAL(cursorChanged(const QCursor&)), | ||||
604 | q, SLOT(updateCursor(const QCursor&))); | ||||
605 | disconnect(canvasData->activeTool, SIGNAL(activateTool(const QString &)), | ||||
606 | q, SLOT(switchToolRequested(const QString &))); | ||||
607 | disconnect(canvasData->activeTool, SIGNAL(activateTemporary(const QString &)), | ||||
608 | q, SLOT(switchToolTemporaryRequested(const QString &))); | ||||
609 | disconnect(canvasData->activeTool, SIGNAL(done()), q, SLOT(switchBackRequested())); | ||||
610 | disconnect(canvasData->activeTool, SIGNAL(statusTextChanged(const QString &)), | ||||
611 | q, SIGNAL(changedStatusText(const QString &))); | ||||
653 | } | 612 | } | ||
613 | | ||||
614 | // emit a empty status text to clear status text from last active tool | ||||
615 | emit q->changedStatusText(QString()); | ||||
654 | } | 616 | } | ||
655 | 617 | | |||
656 | // none of our canvasWidgets got focus? | 618 | | ||
657 | if (newCanvas == 0) { | 619 | void KoToolManager::Private::switchTool(KoToolBase *tool, bool temporary) | ||
620 | { | ||||
621 | | ||||
622 | Q_ASSERT(tool); | ||||
623 | if (canvasData == 0) | ||||
658 | return; | 624 | return; | ||
659 | } | | |||
660 | 625 | | |||
661 | // switch to canvasdata matching inputdevice used last with this app instance | 626 | if (canvasData->activeTool == tool && tool->toolId() != KoInteractionTool_ID) | ||
662 | foreach(CanvasData *data, canvasses.value(newCanvas)) { | | |||
663 | if (data->inputDevice == inputDevice) { | | |||
664 | switchCanvasData(data); | | |||
665 | return; | 627 | return; | ||
666 | } | | |||
667 | } | | |||
668 | // if no such inputDevice for this canvas, then simply fallback to first one | | |||
669 | switchCanvasData(canvasses.value(newCanvas).first()); | | |||
670 | } | | |||
671 | 628 | | |||
672 | void KoToolManager::Private::updateCursor(const QCursor &cursor) | 629 | disconnectActiveTool(); | ||
673 | { | 630 | canvasData->activeTool = tool; | ||
674 | Q_ASSERT(canvasData); | 631 | connectActiveTool(); | ||
675 | Q_ASSERT(canvasData->canvas); | 632 | postSwitchTool(temporary); | ||
676 | Q_ASSERT(canvasData->canvas->canvas()); | | |||
677 | canvasData->canvas->canvas()->setCursor(cursor); | | |||
678 | } | 633 | } | ||
679 | 634 | | |||
680 | void KoToolManager::Private::selectionChanged(const QList<KoShape*> &shapes) | 635 | | ||
636 | | ||||
637 | void KoToolManager::Private::switchTool(const QString &id, bool temporary) | ||||
681 | { | 638 | { | ||
682 | QList<QString> types; | 639 | Q_ASSERT(canvasData); | ||
683 | foreach(KoShape *shape, shapes) { | 640 | if (!canvasData) return; | ||
684 | QSet<KoShape*> delegates = shape->toolDelegates(); | | |||
685 | if (delegates.isEmpty()) { // no delegates, just the orig shape | | |||
686 | delegates << shape; | | |||
687 | } | | |||
688 | 641 | | |||
689 | foreach (KoShape *shape2, delegates) { | 642 | if (canvasData->activeTool && temporary) | ||
690 | Q_ASSERT(shape2); | 643 | canvasData->stack.push(canvasData->activeToolId); | ||
691 | if (! types.contains(shape2->shapeId())) { | 644 | canvasData->activeToolId = id; | ||
692 | types.append(shape2->shapeId()); | 645 | KoToolBase *tool = canvasData->allTools.value(id); | ||
693 | } | 646 | if (! tool) { | ||
694 | } | 647 | return; | ||
695 | } | 648 | } | ||
696 | 649 | | |||
697 | // check if there is still a shape selected the active tool can work on | 650 | foreach(ToolHelper *th, tools) { | ||
698 | // there needs to be at least one shape for a tool without an activationShapeId | 651 | if (th->id() == id) { | ||
699 | // to work | 652 | canvasData->activationShapeId = th->activationShapeId(); | ||
700 | // if not change the current tool to the default tool | | |||
701 | if (!(canvasData->activationShapeId.isNull() && shapes.size() > 0) | | |||
702 | && canvasData->activationShapeId != "flake/always" | | |||
703 | && canvasData->activationShapeId != "flake/edit") { | | |||
704 | | ||||
705 | bool currentToolWorks = false; | | |||
706 | foreach (const QString &type, types) { | | |||
707 | if (canvasData->activationShapeId.split(',').contains(type)) { | | |||
708 | currentToolWorks = true; | | |||
709 | break; | 653 | break; | ||
710 | } | 654 | } | ||
711 | } | 655 | } | ||
712 | if (!currentToolWorks) { | | |||
713 | switchTool(KoInteractionTool_ID, false); | | |||
714 | } | | |||
715 | } | | |||
716 | 656 | | |||
717 | emit q->toolCodesSelected(types); | 657 | switchTool(tool, temporary); | ||
718 | } | 658 | } | ||
719 | 659 | | |||
720 | void KoToolManager::Private::currentLayerChanged(const KoShapeLayer *layer) | 660 | void KoToolManager::Private::postSwitchTool(bool temporary) | ||
721 | { | 661 | { | ||
722 | emit q->currentLayerChanged(canvasData->canvas, layer); | 662 | #ifndef NDEBUG | ||
723 | layerExplicitlyDisabled = layer && !layer->isEditable(); | 663 | int canvasCount = 1; | ||
724 | updateToolForProxy(); | 664 | foreach(QList<CanvasData*> list, canvasses) { | ||
725 | 665 | bool first = true; | |||
726 | debugFlake << "Layer changed to" << layer << "explicitly disabled:" << layerExplicitlyDisabled; | 666 | foreach(CanvasData *data, list) { | ||
667 | if (first) { | ||||
668 | debugFlake << "Canvas" << canvasCount++; | ||||
727 | } | 669 | } | ||
728 | 670 | debugFlake << " +- Tool:" << data->activeToolId << (data == canvasData ? " *" : ""); | |||
729 | void KoToolManager::Private::updateToolForProxy() | 671 | first = false; | ||
730 | { | | |||
731 | KoToolProxy *proxy = proxies.value(canvasData->canvas->canvas()); | | |||
732 | if(!proxy) return; | | |||
733 | | ||||
734 | bool canUseTool = !layerExplicitlyDisabled || canvasData->activationShapeId.endsWith(QLatin1String("/always")); | | |||
735 | proxy->setActiveTool(canUseTool ? canvasData->activeTool : 0); | | |||
736 | } | 672 | } | ||
737 | 673 | } | |||
738 | void KoToolManager::Private::switchInputDevice(const KoInputDevice &device) | 674 | #endif | ||
739 | { | | |||
740 | Q_ASSERT(canvasData); | 675 | Q_ASSERT(canvasData); | ||
741 | if (!canvasData) return; | 676 | if (!canvasData) return; | ||
742 | if (inputDevice == device) return; | 677 | | ||
743 | if (inputDevice.isMouse() && device.isMouse()) return; | 678 | KoToolBase::ToolActivation toolActivation; | ||
744 | if (device.isMouse() && !inputDevice.isMouse()) { | 679 | if (temporary) | ||
745 | // we never switch back to mouse from a tablet input device, so the user can use the | 680 | toolActivation = KoToolBase::TemporaryActivation; | ||
746 | // mouse to edit the settings for a tool activated by a tablet. See bugs | 681 | else | ||
747 | // https://bugs.kde.org/show_bug.cgi?id=283130 and https://bugs.kde.org/show_bug.cgi?id=285501. | 682 | toolActivation = KoToolBase::DefaultActivation; | ||
748 | // We do continue to switch between tablet devices, thought. | 683 | QSet<KoShape*> shapesToOperateOn; | ||
749 | return; | 684 | if (canvasData->activeTool | ||
685 | && canvasData->activeTool->canvas() | ||||
686 | && canvasData->activeTool->canvas()->shapeManager()) { | ||||
687 | KoSelection *selection = canvasData->activeTool->canvas()->shapeManager()->selection(); | ||||
688 | Q_ASSERT(selection); | ||||
689 | | ||||
690 | foreach(KoShape *shape, selection->selectedShapes()) { | ||||
691 | QSet<KoShape*> delegates = shape->toolDelegates(); | ||||
692 | if (delegates.isEmpty()) { // no delegates, just the orig shape | ||||
693 | shapesToOperateOn << shape; | ||||
694 | } else { | ||||
695 | shapesToOperateOn += delegates; | ||||
696 | } | ||||
697 | } | ||||
750 | } | 698 | } | ||
751 | 699 | | |||
752 | QList<CanvasData*> items = canvasses[canvasData->canvas]; | 700 | if (canvasData->canvas->canvas()) { | ||
701 | // Caller of postSwitchTool expect this to be called to update the selected tool | ||||
702 | updateToolForProxy(); | ||||
703 | canvasData->activeTool->activate(toolActivation, shapesToOperateOn); | ||||
704 | KoCanvasBase *canvas = canvasData->canvas->canvas(); | ||||
705 | canvas->updateInputMethodInfo(); | ||||
706 | } else { | ||||
707 | canvasData->activeTool->activate(toolActivation, shapesToOperateOn); | ||||
708 | } | ||||
753 | 709 | | |||
754 | // disable all actions for all tools in the all canvasdata objects for this canvas. | 710 | QList<QPointer<QWidget> > optionWidgetList = canvasData->activeTool->optionWidgets(); | ||
755 | foreach(CanvasData *cd, items) { | 711 | if (optionWidgetList.empty()) { // no option widget. | ||
756 | foreach(KoToolBase* tool, cd->allTools) { | 712 | QWidget *toolWidget; | ||
757 | foreach(QAction * action, tool->actions()) { | 713 | QString title; | ||
758 | action->setEnabled(false); | 714 | foreach(ToolHelper *tool, tools) { | ||
715 | if (tool->id() == canvasData->activeTool->toolId()) { | ||||
716 | title = tool->toolTip(); | ||||
717 | break; | ||||
759 | } | 718 | } | ||
760 | } | 719 | } | ||
720 | toolWidget = canvasData->dummyToolWidget; | ||||
721 | if (toolWidget == 0) { | ||||
722 | toolWidget = new QWidget(); | ||||
723 | toolWidget->setObjectName("DummyToolWidget"); | ||||
724 | QVBoxLayout *layout = new QVBoxLayout(toolWidget); | ||||
725 | layout->setMargin(3); | ||||
726 | canvasData->dummyToolLabel = new QLabel(toolWidget); | ||||
727 | layout->addWidget(canvasData->dummyToolLabel); | ||||
728 | layout->addItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::Expanding)); | ||||
729 | toolWidget->setLayout(layout); | ||||
730 | canvasData->dummyToolWidget = toolWidget; | ||||
731 | } | ||||
732 | canvasData->dummyToolLabel->setText(i18n("Active tool: %1", title)); | ||||
733 | optionWidgetList.append(toolWidget); | ||||
761 | } | 734 | } | ||
762 | 735 | | |||
763 | // search for a canvasdata object for the current input device | 736 | // Activate the actions for the currently active tool | ||
764 | foreach(CanvasData *cd, items) { | 737 | canvasData->activateToolActions(); | ||
765 | if (cd->inputDevice == device) { | | |||
766 | switchCanvasData(cd); | | |||
767 | 738 | | |||
768 | if (!canvasData->activeTool) { | 739 | emit q->changedTool(canvasData->canvas, uniqueToolIds.value(canvasData->activeTool)); | ||
769 | switchTool(KoInteractionTool_ID, false); | | |||
770 | } | | |||
771 | 740 | | |||
772 | return; | 741 | KoCanvasControllerWidget *canvasControllerWidget = dynamic_cast<KoCanvasControllerWidget*>(canvasData->canvas); | ||
742 | if (canvasControllerWidget) { | ||||
743 | canvasControllerWidget->setToolOptionWidgets(optionWidgetList); | ||||
773 | } | 744 | } | ||
774 | } | 745 | } | ||
775 | 746 | | |||
776 | // still here? That means we need to create a new CanvasData instance with the current InputDevice. | | |||
777 | CanvasData *cd = createCanvasData(canvasData->canvas, device); | | |||
778 | // switch to new canvas as the active one. | | |||
779 | QString oldTool = canvasData->activeToolId; | | |||
780 | 747 | | |||
781 | items.append(cd); | 748 | void KoToolManager::Private::switchCanvasData(CanvasData *cd) | ||
782 | canvasses[cd->canvas] = items; | 749 | { | ||
750 | Q_ASSERT(cd); | ||||
783 | 751 | | |||
784 | switchCanvasData(cd); | 752 | KoCanvasBase *oldCanvas = 0; | ||
753 | KoInputDevice oldInputDevice; | ||||
785 | 754 | | |||
786 | q->switchToolRequested(oldTool); | 755 | if (canvasData) { | ||
787 | } | 756 | oldCanvas = canvasData->canvas->canvas(); | ||
757 | oldInputDevice = canvasData->inputDevice; | ||||
788 | 758 | | |||
789 | void KoToolManager::Private::registerToolProxy(KoToolProxy *proxy, KoCanvasBase *canvas) | 759 | if (canvasData->activeTool) { | ||
790 | { | 760 | disconnectActiveTool(); | ||
791 | proxies.insert(canvas, proxy); | | |||
792 | foreach(KoCanvasController *controller, canvasses.keys()) { | | |||
793 | if (controller->canvas() == canvas) { | | |||
794 | proxy->priv()->setCanvasController(controller); | | |||
795 | break; | | |||
796 | } | | |||
797 | } | 761 | } | ||
762 | | ||||
763 | KoToolProxy *proxy = proxies.value(oldCanvas); | ||||
764 | Q_ASSERT(proxy); | ||||
765 | proxy->setActiveTool(0); | ||||
798 | } | 766 | } | ||
799 | 767 | | |||
800 | void KoToolManager::Private::switchToolByShortcut(QKeyEvent *event) | 768 | canvasData = cd; | ||
801 | { | 769 | inputDevice = canvasData->inputDevice; | ||
802 | QKeySequence item(event->key() | ((Qt::ControlModifier | Qt::AltModifier) & event->modifiers())); | | |||
803 | 770 | | |||
804 | if (event->key() == Qt::Key_Space && event->modifiers() == 0) { | 771 | if (canvasData->activeTool) { | ||
805 | switchTool(KoPanTool_ID, true); | 772 | connectActiveTool(); | ||
806 | } else if (event->key() == Qt::Key_Escape && event->modifiers() == 0) { | 773 | postSwitchTool(false); | ||
807 | switchTool(KoInteractionTool_ID, false); | | |||
808 | } | | |||
809 | } | 774 | } | ||
810 | 775 | | |||
811 | // ******** KoToolManager ********** | 776 | if (oldInputDevice != canvasData->inputDevice) { | ||
812 | KoToolManager::KoToolManager() | 777 | emit q->inputDeviceChanged(canvasData->inputDevice); | ||
813 | : QObject(), | | |||
814 | d(new Private(this)) | | |||
815 | { | | |||
816 | connect(QApplication::instance(), SIGNAL(focusChanged(QWidget*, QWidget*)), | | |||
817 | this, SLOT(movedFocus(QWidget*, QWidget*))); | | |||
818 | } | 778 | } | ||
819 | 779 | | |||
820 | KoToolManager::~KoToolManager() | 780 | if (oldCanvas != canvasData->canvas->canvas()) { | ||
821 | { | 781 | emit q->changedCanvas(canvasData->canvas->canvas()); | ||
822 | delete d; | 782 | } | ||
823 | } | 783 | } | ||
824 | 784 | | |||
825 | QList<KoToolAction*> KoToolManager::toolActionList() const | 785 | | ||
786 | void KoToolManager::Private::toolActivated(ToolHelper *tool) | ||||
826 | { | 787 | { | ||
827 | QList<KoToolAction*> answer; | 788 | Q_ASSERT(tool); | ||
828 | answer.reserve(d->tools.count()); | 789 | | ||
829 | foreach(ToolHelper *tool, d->tools) { | 790 | Q_ASSERT(canvasData); | ||
830 | if (tool->id() == KoCreateShapesTool_ID) | 791 | if (!canvasData) return; | ||
831 | continue; // don't show this one. | 792 | KoToolBase *t = canvasData->allTools.value(tool->id()); | ||
832 | answer.append(tool->toolAction()); | 793 | Q_ASSERT(t); | ||
833 | } | 794 | | ||
834 | return answer; | 795 | canvasData->activeToolId = tool->id(); | ||
796 | canvasData->activationShapeId = tool->activationShapeId(); | ||||
797 | | ||||
798 | switchTool(t, false); | ||||
835 | } | 799 | } | ||
836 | 800 | | |||
837 | void KoToolManager::requestToolActivation(KoCanvasController * controller) | 801 | void KoToolManager::Private::detachCanvas(KoCanvasController *controller) | ||
838 | { | 802 | { | ||
839 | if (d->canvasses.contains(controller)) { | 803 | Q_ASSERT(controller); | ||
840 | QString activeToolId = d->canvasses.value(controller).first()->activeToolId; | 804 | // check if we are removing the active canvas controller | ||
841 | foreach(ToolHelper * th, d->tools) { | 805 | if (canvasData && canvasData->canvas == controller) { | ||
842 | if (th->id() == activeToolId) { | 806 | KoCanvasController *newCanvas = 0; | ||
843 | d->toolActivated(th); | 807 | // try to find another canvas controller beside the one we are removing | ||
808 | foreach(KoCanvasController* canvas, canvasses.keys()) { | ||||
809 | if (canvas != controller) { | ||||
810 | // yay found one | ||||
811 | newCanvas = canvas; | ||||
844 | break; | 812 | break; | ||
845 | } | 813 | } | ||
846 | } | 814 | } | ||
815 | if (newCanvas) { | ||||
816 | switchCanvasData(canvasses.value(newCanvas).first()); | ||||
817 | } else { | ||||
818 | KoCanvasControllerWidget *canvasControllerWidget = dynamic_cast<KoCanvasControllerWidget*>(canvasData->canvas); | ||||
819 | if (canvasControllerWidget) { | ||||
820 | canvasControllerWidget->setToolOptionWidgets(QList<QPointer<QWidget> >()); | ||||
847 | } | 821 | } | ||
822 | // as a last resort just set a blank one | ||||
823 | canvasData = 0; | ||||
848 | } | 824 | } | ||
849 | | ||||
850 | KoInputDevice KoToolManager::currentInputDevice() const | | |||
851 | { | | |||
852 | return d->inputDevice; | | |||
853 | } | 825 | } | ||
854 | 826 | | |||
855 | void KoToolManager::registerTools(KActionCollection *ac, KoCanvasController *controller) | 827 | KoToolProxy *proxy = proxies.value(controller->canvas()); | ||
856 | { | 828 | if (proxy) | ||
857 | Q_ASSERT(controller); | 829 | proxy->setActiveTool(0); | ||
858 | Q_ASSERT(ac); | | |||
859 | | ||||
860 | d->setup(); | | |||
861 | | ||||
862 | if (!d->canvasses.contains(controller)) { | | |||
863 | return; | | |||
864 | } | | |||
865 | 830 | | |||
866 | // Actions available during the use of individual tools | 831 | QList<KoToolBase *> tools; | ||
867 | CanvasData *cd = d->canvasses.value(controller).first(); | 832 | foreach(CanvasData *canvasData, canvasses.value(controller)) { | ||
868 | foreach(KoToolBase *tool, cd->allTools) { | 833 | foreach(KoToolBase *tool, canvasData->allTools) { | ||
869 | QHash<QString, QAction*> actions = tool->actions(); | 834 | if (! tools.contains(tool)) { | ||
870 | QHash<QString, QAction*>::const_iterator action(actions.constBegin()); | 835 | tools.append(tool); | ||
871 | for (; action != actions.constEnd(); ++action) { | | |||
872 | if (!ac->action(action.key())) | | |||
873 | ac->addAction(action.key(), action.value()); | | |||
874 | } | 836 | } | ||
875 | } | 837 | } | ||
876 | 838 | delete canvasData; | |||
877 | // Actions used to switch tools via shortcuts | | |||
878 | foreach(ToolHelper * th, d->tools) { | | |||
879 | if (ac->action(th->id())) { | | |||
880 | continue; | | |||
881 | } | 839 | } | ||
882 | ShortcutToolAction* action = th->createShortcutToolAction(ac); | 840 | foreach(KoToolBase *tool, tools) { | ||
883 | ac->addAction(th->id(), action); | 841 | uniqueToolIds.remove(tool); | ||
842 | delete tool; | ||||
884 | } | 843 | } | ||
844 | canvasses.remove(controller); | ||||
845 | emit q->changedCanvas(canvasData ? canvasData->canvas->canvas() : 0); | ||||
885 | } | 846 | } | ||
886 | 847 | | |||
887 | void KoToolManager::addController(KoCanvasController *controller) | 848 | void KoToolManager::Private::attachCanvas(KoCanvasController *controller) | ||
888 | { | 849 | { | ||
889 | Q_ASSERT(controller); | 850 | Q_ASSERT(controller); | ||
890 | if (d->canvasses.contains(controller)) | 851 | CanvasData *cd = createCanvasData(controller, KoInputDevice::mouse()); | ||
891 | return; | | |||
892 | d->setup(); | | |||
893 | d->attachCanvas(controller); | | |||
894 | connect(controller->proxyObject, SIGNAL(destroyed(QObject*)), this, SLOT(attemptCanvasControllerRemoval(QObject*))); | | |||
895 | connect(controller->proxyObject, SIGNAL(canvasRemoved(KoCanvasController*)), this, SLOT(detachCanvas(KoCanvasController*))); | | |||
896 | connect(controller->proxyObject, SIGNAL(canvasSet(KoCanvasController*)), this, SLOT(attachCanvas(KoCanvasController*))); | | |||
897 | } | | |||
898 | 852 | | |||
899 | void KoToolManager::removeCanvasController(KoCanvasController *controller) | 853 | // switch to new canvas as the active one. | ||
900 | { | 854 | switchCanvasData(cd); | ||
901 | Q_ASSERT(controller); | | |||
902 | disconnect(controller->proxyObject, SIGNAL(canvasRemoved(KoCanvasController*)), this, SLOT(detachCanvas(KoCanvasController*))); | | |||
903 | disconnect(controller->proxyObject, SIGNAL(canvasSet(KoCanvasController*)), this, SLOT(attachCanvas(KoCanvasController*))); | | |||
904 | d->detachCanvas(controller); | | |||
905 | } | | |||
906 | 855 | | |||
907 | void KoToolManager::attemptCanvasControllerRemoval(QObject* controller) | 856 | inputDevice = cd->inputDevice; | ||
908 | { | 857 | QList<CanvasData*> canvasses_; | ||
909 | KoCanvasControllerProxyObject* controllerActual = qobject_cast<KoCanvasControllerProxyObject*>(controller); | 858 | canvasses_.append(cd); | ||
910 | if (controllerActual) { | 859 | canvasses[controller] = canvasses_; | ||
911 | removeCanvasController(controllerActual->canvasController()); | | |||
912 | } | | |||
913 | } | | |||
914 | 860 | | |||
915 | void KoToolManager::updateShapeControllerBase(KoShapeBasedDocumentBase *shapeController, KoCanvasController *canvasController) | 861 | KoToolProxy *tp = proxies[controller->canvas()]; | ||
916 | { | 862 | if (tp) | ||
917 | if (!d->canvasses.contains(canvasController)) | 863 | tp->priv()->setCanvasController(controller); | ||
918 | return; | | |||
919 | 864 | | |||
920 | QList<CanvasData *> canvasses = d->canvasses[canvasController]; | 865 | if (cd->activeTool == 0) { | ||
921 | foreach(CanvasData *canvas, canvasses) { | 866 | // no active tool, so we activate the highest priority main tool | ||
922 | foreach(KoToolBase *tool, canvas->allTools.values()) { | 867 | int highestPriority = INT_MAX; | ||
923 | tool->updateShapeController(shapeController); | 868 | ToolHelper * helper = 0; | ||
869 | foreach(ToolHelper * th, tools) { | ||||
870 | if (th->toolType() == KoToolFactoryBase::mainToolType()) { | ||||
871 | if (th->priority() < highestPriority) { | ||||
872 | highestPriority = qMin(highestPriority, th->priority()); | ||||
873 | helper = th; | ||||
924 | } | 874 | } | ||
925 | } | 875 | } | ||
926 | } | 876 | } | ||
877 | if (helper) | ||||
878 | toolActivated(helper); | ||||
879 | } | ||||
927 | 880 | | |||
928 | void KoToolManager::switchToolRequested(const QString & id) | 881 | Connector *connector = new Connector(controller->canvas()->shapeManager()); | ||
929 | { | 882 | connect(connector, SIGNAL(selectionChanged(QList<KoShape*>)), q, | ||
930 | Q_ASSERT(d->canvasData); | 883 | SLOT(selectionChanged(QList<KoShape*>))); | ||
931 | if (!d->canvasData) return; | 884 | connect(controller->canvas()->shapeManager()->selection(), | ||
932 | 885 | SIGNAL(currentLayerChanged(const KoShapeLayer*)), | |||
933 | while (!d->canvasData->stack.isEmpty()) // switching means to flush the stack | 886 | q, SLOT(currentLayerChanged(const KoShapeLayer*))); | ||
934 | d->canvasData->stack.pop(); | 887 | | ||
935 | d->switchTool(id, false); | 888 | emit q->changedCanvas(canvasData ? canvasData->canvas->canvas() : 0); | ||
936 | } | 889 | } | ||
937 | 890 | | |||
938 | void KoToolManager::switchInputDeviceRequested(const KoInputDevice &id) | 891 | void KoToolManager::Private::movedFocus(QWidget *from, QWidget *to) | ||
939 | { | 892 | { | ||
940 | if (!d->canvasData) return; | 893 | Q_UNUSED(from); | ||
941 | d->switchInputDevice(id); | 894 | // no canvas anyway or no focus set anyway? | ||
895 | if (!canvasData || to == 0) { | ||||
896 | return; | ||||
942 | } | 897 | } | ||
943 | 898 | | |||
944 | void KoToolManager::switchToolTemporaryRequested(const QString &id) | 899 | // Check if this app is about QWidget-based KoCanvasControllerWidget canvasses | ||
945 | { | 900 | // XXX: Focus handling for non-qwidget based canvases! | ||
946 | d->switchTool(id, true); | 901 | KoCanvasControllerWidget *canvasControllerWidget = dynamic_cast<KoCanvasControllerWidget*>(canvasData->canvas); | ||
902 | if (!canvasControllerWidget) { | ||||
903 | return; | ||||
947 | } | 904 | } | ||
948 | 905 | | |||
949 | void KoToolManager::switchBackRequested() | 906 | // canvasWidget is set as focusproxy for KoCanvasControllerWidget, | ||
950 | { | 907 | // so all focus checks are to be done against canvasWidget objects | ||
951 | if (!d->canvasData) return; | | |||
952 | 908 | | |||
953 | if (d->canvasData->stack.isEmpty()) { | 909 | // focus returned to current canvas? | ||
954 | // default to changing to the interactionTool | 910 | if (to == canvasData->canvas->canvas()->canvasWidget()) { | ||
955 | d->switchTool(KoInteractionTool_ID, false); | 911 | // nothing to do | ||
956 | return; | 912 | return; | ||
957 | } | 913 | } | ||
958 | d->switchTool(d->canvasData->stack.pop(), false); | | |||
959 | } | | |||
960 | 914 | | |||
961 | KoCreateShapesTool * KoToolManager::shapeCreatorTool(KoCanvasBase *canvas) const | 915 | // if the 'to' is one of our canvasWidgets, then switch. | ||
962 | { | 916 | | ||
963 | Q_ASSERT(canvas); | 917 | // for code simplicity the current canvas will be checked again, | ||
964 | foreach(KoCanvasController *controller, d->canvasses.keys()) { | 918 | // but would have been catched already in the lines above, so no issue | ||
965 | if (controller->canvas() == canvas) { | 919 | KoCanvasController *newCanvas = 0; | ||
966 | KoCreateShapesTool *createTool = dynamic_cast<KoCreateShapesTool*> | 920 | foreach(KoCanvasController* canvas, canvasses.keys()) { | ||
967 | (d->canvasData->allTools.value(KoCreateShapesTool_ID)); | 921 | if (canvas->canvas()->canvasWidget() == to) { | ||
968 | Q_ASSERT(createTool /* ID changed? */); | 922 | newCanvas = canvas; | ||
969 | return createTool; | 923 | break; | ||
970 | } | 924 | } | ||
971 | } | 925 | } | ||
972 | Q_ASSERT(0); // this should not happen | 926 | | ||
973 | return 0; | 927 | // none of our canvasWidgets got focus? | ||
928 | if (newCanvas == 0) { | ||||
929 | return; | ||||
974 | } | 930 | } | ||
975 | 931 | | |||
976 | KoToolBase *KoToolManager::toolById(KoCanvasBase *canvas, const QString &id) const | 932 | // switch to canvasdata matching inputdevice used last with this app instance | ||
977 | { | 933 | foreach(CanvasData *data, canvasses.value(newCanvas)) { | ||
978 | Q_ASSERT(canvas); | 934 | if (data->inputDevice == inputDevice) { | ||
979 | foreach(KoCanvasController *controller, d->canvasses.keys()) { | 935 | switchCanvasData(data); | ||
980 | if (controller->canvas() == canvas) | 936 | return; | ||
981 | return d->canvasData->allTools.value(id); | | |||
982 | } | 937 | } | ||
983 | return 0; | 938 | } | ||
939 | // if no such inputDevice for this canvas, then simply fallback to first one | ||||
940 | switchCanvasData(canvasses.value(newCanvas).first()); | ||||
984 | } | 941 | } | ||
985 | 942 | | |||
986 | KoCanvasController *KoToolManager::activeCanvasController() const | 943 | void KoToolManager::Private::updateCursor(const QCursor &cursor) | ||
987 | { | 944 | { | ||
988 | if (! d->canvasData) return 0; | 945 | Q_ASSERT(canvasData); | ||
989 | return d->canvasData->canvas; | 946 | Q_ASSERT(canvasData->canvas); | ||
947 | Q_ASSERT(canvasData->canvas->canvas()); | ||||
948 | canvasData->canvas->canvas()->setCursor(cursor); | ||||
990 | } | 949 | } | ||
991 | 950 | | |||
992 | QString KoToolManager::preferredToolForSelection(const QList<KoShape*> &shapes) | 951 | void KoToolManager::Private::selectionChanged(const QList<KoShape*> &shapes) | ||
993 | { | 952 | { | ||
994 | QList<QString> types; | 953 | QList<QString> types; | ||
995 | foreach(KoShape *shape, shapes) | 954 | foreach(KoShape *shape, shapes) { | ||
996 | if (! types.contains(shape->shapeId())) | 955 | QSet<KoShape*> delegates = shape->toolDelegates(); | ||
997 | types.append(shape->shapeId()); | 956 | if (delegates.isEmpty()) { // no delegates, just the orig shape | ||
957 | delegates << shape; | ||||
958 | } | ||||
998 | 959 | | |||
999 | QString toolType = KoInteractionTool_ID; | 960 | foreach (KoShape *shape2, delegates) { | ||
1000 | int prio = INT_MAX; | 961 | Q_ASSERT(shape2); | ||
1001 | foreach(ToolHelper *helper, d->tools) { | 962 | if (! types.contains(shape2->shapeId())) { | ||
1002 | if (helper->priority() >= prio) | 963 | types.append(shape2->shapeId()); | ||
1003 | continue; | 964 | } | ||
1004 | if (helper->toolType() == KoToolFactoryBase::mainToolType()) | 965 | } | ||
1005 | continue; | 966 | } | ||
1006 | 967 | | |||
1007 | bool toolWillWork = false; | 968 | // check if there is still a shape selected the active tool can work on | ||
969 | // there needs to be at least one shape for a tool without an activationShapeId | ||||
970 | // to work | ||||
971 | // if not change the current tool to the default tool | ||||
972 | if (!(canvasData->activationShapeId.isNull() && shapes.size() > 0) | ||||
973 | && canvasData->activationShapeId != "flake/always" | ||||
974 | && canvasData->activationShapeId != "flake/edit") { | ||||
975 | | ||||
976 | bool currentToolWorks = false; | ||||
1008 | foreach (const QString &type, types) { | 977 | foreach (const QString &type, types) { | ||
1009 | if (helper->activationShapeId().split(',').contains(type)) { | 978 | if (canvasData->activationShapeId.split(',').contains(type)) { | ||
1010 | toolWillWork = true; | 979 | currentToolWorks = true; | ||
1011 | break; | 980 | break; | ||
1012 | } | 981 | } | ||
1013 | } | 982 | } | ||
1014 | if (toolWillWork) { | 983 | if (!currentToolWorks) { | ||
1015 | toolType = helper->id(); | 984 | switchTool(KoInteractionTool_ID, false); | ||
1016 | prio = helper->priority(); | | |||
1017 | } | | |||
1018 | } | 985 | } | ||
1019 | return toolType; | | |||
1020 | } | 986 | } | ||
1021 | 987 | | |||
1022 | void KoToolManager::injectDeviceEvent(KoInputDeviceHandlerEvent * event) | 988 | emit q->toolCodesSelected(types); | ||
1023 | { | | |||
1024 | if (d->canvasData && d->canvasData->canvas->canvas()) { | | |||
1025 | if (static_cast<KoInputDeviceHandlerEvent::Type>(event->type()) == KoInputDeviceHandlerEvent::ButtonPressed) | | |||
1026 | d->canvasData->activeTool->customPressEvent(event->pointerEvent()); | | |||
1027 | else if (static_cast<KoInputDeviceHandlerEvent::Type>(event->type()) == KoInputDeviceHandlerEvent::ButtonReleased) | | |||
1028 | d->canvasData->activeTool->customReleaseEvent(event->pointerEvent()); | | |||
1029 | else if (static_cast<KoInputDeviceHandlerEvent::Type>(event->type()) == KoInputDeviceHandlerEvent::PositionChanged) | | |||
1030 | d->canvasData->activeTool->customMoveEvent(event->pointerEvent()); | | |||
1031 | } | | |||
1032 | } | 989 | } | ||
1033 | 990 | | |||
1034 | void KoToolManager::addDeferredToolFactory(KoToolFactoryBase *toolFactory) | 991 | void KoToolManager::Private::currentLayerChanged(const KoShapeLayer *layer) | ||
1035 | { | 992 | { | ||
1036 | ToolHelper *tool = new ToolHelper(toolFactory); | 993 | emit q->currentLayerChanged(canvasData->canvas, layer); | ||
1037 | // make sure all plugins are loaded as otherwise we will not load them | 994 | layerExplicitlyDisabled = layer && !layer->isEditable(); | ||
1038 | d->setup(); | 995 | updateToolForProxy(); | ||
1039 | d->tools.append(tool); | | |||
1040 | 996 | | |||
1041 | // connect to all tools so we can hear their button-clicks | 997 | debugFlake << "Layer changed to" << layer << "explicitly disabled:" << layerExplicitlyDisabled; | ||
1042 | connect(tool, SIGNAL(toolActivated(ToolHelper*)), this, SLOT(toolActivated(ToolHelper*))); | 998 | } | ||
1043 | 999 | | |||
1044 | // now create tools for all existing canvases | 1000 | void KoToolManager::Private::updateToolForProxy() | ||
1045 | foreach(KoCanvasController *controller, d->canvasses.keys()) { | 1001 | { | ||
1002 | KoToolProxy *proxy = proxies.value(canvasData->canvas->canvas()); | ||||
1003 | if(!proxy) return; | ||||
1046 | 1004 | | |||
1047 | // this canvascontroller is unknown, which is weird | 1005 | bool canUseTool = !layerExplicitlyDisabled || canvasData->activationShapeId.endsWith(QLatin1String("/always")); | ||
1048 | if (!d->canvasses.contains(controller)) { | 1006 | proxy->setActiveTool(canUseTool ? canvasData->activeTool : 0); | ||
1049 | continue; | | |||
1050 | } | 1007 | } | ||
1051 | 1008 | | |||
1052 | // create a tool for all canvasdata objects (i.e., all input devices on this canvas) | 1009 | void KoToolManager::Private::switchInputDevice(const KoInputDevice &device) | ||
1053 | foreach (CanvasData *cd, d->canvasses[controller]) { | 1010 | { | ||
1054 | QPair<QString, KoToolBase*> toolPair = createTools(controller, tool); | 1011 | Q_ASSERT(canvasData); | ||
1055 | if (toolPair.second) { | 1012 | if (!canvasData) return; | ||
1056 | cd->allTools.insert(toolPair.first, toolPair.second); | 1013 | if (inputDevice == device) return; | ||
1057 | } | 1014 | if (inputDevice.isMouse() && device.isMouse()) return; | ||
1015 | if (device.isMouse() && !inputDevice.isMouse()) { | ||||
1016 | // we never switch back to mouse from a tablet input device, so the user can use the | ||||
1017 | // mouse to edit the settings for a tool activated by a tablet. See bugs | ||||
1018 | // https://bugs.kde.org/show_bug.cgi?id=283130 and https://bugs.kde.org/show_bug.cgi?id=285501. | ||||
1019 | // We do continue to switch between tablet devices, thought. | ||||
1020 | return; | ||||
1058 | } | 1021 | } | ||
1059 | 1022 | | |||
1060 | // Then create a button for the toolbox for this canvas | 1023 | QList<CanvasData*> items = canvasses[canvasData->canvas]; | ||
1061 | if (tool->id() == KoCreateShapesTool_ID) { | | |||
1062 | continue; | | |||
1063 | } | | |||
1064 | 1024 | | |||
1065 | emit addedTool(tool->toolAction(), controller); | 1025 | // disable all actions for all tools in the all canvasdata objects for this canvas. | ||
1026 | foreach(CanvasData *cd, items) { | ||||
1027 | foreach(KoToolBase* tool, cd->allTools) { | ||||
1028 | foreach(QAction * action, tool->actions()) { | ||||
1029 | action->setEnabled(false); | ||||
1030 | } | ||||
1066 | } | 1031 | } | ||
1067 | } | 1032 | } | ||
1068 | 1033 | | |||
1069 | QPair<QString, KoToolBase*> KoToolManager::createTools(KoCanvasController *controller, ToolHelper *tool) | 1034 | // search for a canvasdata object for the current input device | ||
1070 | { | 1035 | foreach(CanvasData *cd, items) { | ||
1071 | // XXX: maybe this method should go into the private class? | 1036 | if (cd->inputDevice == device) { | ||
1072 | 1037 | switchCanvasData(cd); | |||
1073 | QHash<QString, KoToolBase*> origHash; | | |||
1074 | 1038 | | |||
1075 | if (d->canvasses.contains(controller)) { | 1039 | if (!canvasData->activeTool) { | ||
1076 | origHash = d->canvasses.value(controller).first()->allTools; | 1040 | switchTool(KoInteractionTool_ID, false); | ||
1077 | } | 1041 | } | ||
1078 | 1042 | | |||
1079 | if (origHash.contains(tool->id())) { | 1043 | return; | ||
1080 | return QPair<QString, KoToolBase*>(tool->id(), origHash.value(tool->id())); | 1044 | } | ||
1081 | } | 1045 | } | ||
1082 | 1046 | | |||
1083 | debugFlake << "Creating tool" << tool->id() << ". Activated on:" << tool->activationShapeId() << ", prio:" << tool->priority(); | 1047 | // still here? That means we need to create a new CanvasData instance with the current InputDevice. | ||
1084 | 1048 | CanvasData *cd = createCanvasData(canvasData->canvas, device); | |||
1085 | KoToolBase *tl = tool->createTool(controller->canvas()); | 1049 | // switch to new canvas as the active one. | ||
1086 | if (tl) { | 1050 | QString oldTool = canvasData->activeToolId; | ||
1087 | d->uniqueToolIds.insert(tl, tool->uniqueId()); | | |||
1088 | 1051 | | |||
1089 | tl->setObjectName(tool->id()); | 1052 | items.append(cd); | ||
1053 | canvasses[cd->canvas] = items; | ||||
1090 | 1054 | | |||
1091 | foreach(QAction *action, tl->actions()) { | 1055 | switchCanvasData(cd); | ||
1092 | action->setEnabled(false); | | |||
1093 | } | | |||
1094 | 1056 | | |||
1057 | q->switchToolRequested(oldTool); | ||||
1095 | } | 1058 | } | ||
1096 | 1059 | | |||
1097 | KoZoomTool *zoomTool = dynamic_cast<KoZoomTool*>(tl); | 1060 | void KoToolManager::Private::registerToolProxy(KoToolProxy *proxy, KoCanvasBase *canvas) | ||
1098 | if (zoomTool) { | 1061 | { | ||
1099 | zoomTool->setCanvasController(controller); | 1062 | proxies.insert(canvas, proxy); | ||
1063 | foreach(KoCanvasController *controller, canvasses.keys()) { | ||||
1064 | if (controller->canvas() == canvas) { | ||||
1065 | proxy->priv()->setCanvasController(controller); | ||||
1066 | break; | ||||
1100 | } | 1067 | } | ||
1101 | | ||||
1102 | KoPanTool *panTool = dynamic_cast<KoPanTool*>(tl); | | |||
1103 | if (panTool) { | | |||
1104 | panTool->setCanvasController(controller); | | |||
1105 | } | 1068 | } | ||
1106 | | ||||
1107 | return QPair<QString, KoToolBase*>(tool->id(), tl); | | |||
1108 | } | 1069 | } | ||
1109 | 1070 | | |||
1110 | 1071 | void KoToolManager::Private::switchToolByShortcut(QKeyEvent *event) | |||
1111 | KoToolManager* KoToolManager::instance() | | |||
1112 | { | 1072 | { | ||
1113 | return s_instance; | 1073 | QKeySequence item(event->key() | ((Qt::ControlModifier | Qt::AltModifier) & event->modifiers())); | ||
1114 | } | | |||
1115 | 1074 | | |||
1116 | QString KoToolManager::activeToolId() const | 1075 | if (event->key() == Qt::Key_Space && event->modifiers() == 0) { | ||
1117 | { | 1076 | switchTool(KoPanTool_ID, true); | ||
1118 | if (!d->canvasData) return QString(); | 1077 | } else if (event->key() == Qt::Key_Escape && event->modifiers() == 0) { | ||
1119 | return d->canvasData->activeToolId; | 1078 | switchTool(KoInteractionTool_ID, false); | ||
1120 | } | 1079 | } | ||
1121 | | ||||
1122 | KoToolManager::Private *KoToolManager::priv() | | |||
1123 | { | | |||
1124 | return d; | | |||
1125 | } | 1080 | } | ||
1126 | 1081 | | |||
1127 | //have to include this because of Q_PRIVATE_SLOT | 1082 | //have to include this because of Q_PRIVATE_SLOT | ||
1128 | #include "moc_KoToolManager.cpp" | 1083 | #include "moc_KoToolManager.cpp" |