libs/flake

KoToolManager.cpp

Go to the documentation of this file.
00001 /* This file is part of the KDE project
00002  *
00003  * Copyright (c) 2005-2009 Boudewijn Rempt <boud@valdyas.org>
00004  * Copyright (C) 2006-2008 Thomas Zander <zander@kde.org>
00005  * Copyright (C) 2006 Thorsten Zachmann <zachmann@kde.org>
00006  * Copyright (C) 2008 Jan Hambrecht <jaham@gmx.net>
00007  *
00008  * This library is free software; you can redistribute it and/or
00009  * modify it under the terms of the GNU Library General Public
00010  * License as published by the Free Software Foundation; either
00011  * version 2 of the License, or (at your option) any later version.
00012  *
00013  * This library is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00016  * Library General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU Library General Public License
00019  * along with this library; see the file COPYING.LIB.  If not, write to
00020  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00021  * Boston, MA 02110-1301, USA.
00022  */
00023 // flake
00024 #include "KoToolManager.h"
00025 #include "KoToolManager_p.h"
00026 #include "KoToolRegistry.h"
00027 #include "KoToolProxy.h"
00028 #include "KoSelection.h"
00029 #include "tools/KoCreatePathToolFactory.h"
00030 #include "tools/KoCreateShapesToolFactory.h"
00031 #include "tools/KoCreateShapesTool.h"
00032 #include "tools/KoPathToolFactory.h"
00033 #include "KoCanvasController.h"
00034 #include "KoShape.h"
00035 #include "KoShapeLayer.h"
00036 #include "KoShapeRegistry.h"
00037 #include "KoShapeManager.h"
00038 #include "KoCanvasBase.h"
00039 #include "KoDeviceRegistry.h"
00040 #include "KoDeviceEvent.h"
00041 #include "KoPointerEvent.h"
00042 #include "tools/KoZoomTool.h"
00043 #include "tools/KoZoomToolFactory.h"
00044 #include "tools/KoPanTool.h"
00045 #include "tools/KoPanToolFactory.h"
00046 #include "tools/KoGuidesTool.h"
00047 
00048 // Qt + kde
00049 #include <QWidget>
00050 #include <QEvent>
00051 #include <QWheelEvent>
00052 #include <QMouseEvent>
00053 #include <QPaintEvent>
00054 #include <QTabletEvent>
00055 #include <QKeyEvent>
00056 #include <QGridLayout>
00057 #include <QDockWidget>
00058 #include <QStringList>
00059 #include <QAbstractButton>
00060 #include <QApplication>
00061 #include <QTimer>
00062 #include <kactioncollection.h>
00063 #include <kdebug.h>
00064 #include <kglobal.h>
00065 #include <kaction.h>
00066 #include <QStack>
00067 #include <QLabel>
00068 
00069 class CanvasData
00070 {
00071 public:
00072     CanvasData(KoCanvasController *cc, const KoInputDevice &id)
00073             : activeTool(0),
00074             canvas(cc),
00075             inputDevice(id),
00076             dummyToolWidget(0),
00077             dummyToolLabel(0) {
00078     }
00079 
00080     KoTool *activeTool;     // active Tool
00081     QString activeToolId;   // the id of the active Tool
00082     QString activationShapeId; // the shape-type (KoShape::shapeId()) the activeTool 'belongs' to.
00083     QHash<QString, KoTool*> allTools; // all the tools that are created for this canvas.
00084     QStack<QString> stack; // stack of temporary tools
00085     KoCanvasController *const canvas;
00086     const KoInputDevice inputDevice;
00087     QWidget *dummyToolWidget;  // the widget shown in the toolDocker.
00088     QLabel *dummyToolLabel;
00089 };
00090 
00091 class KoToolManager::Private
00092 {
00093 public:
00094     Private() : canvasData(0), layerEnabled(true) {
00095         tabletEventTimer.setSingleShot(true);
00096     }
00097     ~Private() {
00098         qDeleteAll(tools);
00099     }
00100 
00101     QList<ToolHelper*> tools; // list of all available tools via their factories.
00102 
00103     QHash<KoTool*, int> uniqueToolIds; // for the changedTool signal
00104     QHash<KoCanvasController*, QList<CanvasData*> > canvasses;
00105     QHash<KoCanvasBase*, KoToolProxy*> proxies;
00106 
00107     CanvasData *canvasData; // data about the active canvas.
00108 
00109     KoInputDevice inputDevice;
00110     QTimer tabletEventTimer; // Runs for a short while after any tablet event is
00111     // received to help correct input device detection.
00112 
00113     bool layerEnabled;
00114 
00115     // helper method.
00116     CanvasData *createCanvasData(KoCanvasController *controller, KoInputDevice device) {
00117         QHash<QString, KoTool*> origHash;
00118         if (canvasses.contains(controller))
00119             origHash = canvasses.value(controller).first()->allTools;
00120 
00121         QHash<QString, KoTool*> toolsHash;
00122         foreach(ToolHelper *tool, tools) {
00123             if (tool->inputDeviceAgnostic() && origHash.contains(tool->id())) {
00124                 // reuse ones that are marked as inputDeviceAgnostic();
00125                 toolsHash.insert(tool->id(), origHash.value(tool->id()));
00126                 continue;
00127             }
00128             if (! tool->canCreateTool(controller->canvas())) {
00129                 kDebug(30006) << "Skipping the creation of tool" << tool->id();
00130                 continue;
00131             }
00132             kDebug(30006) << "Creating tool" << tool->id() << ". Activated on:" << tool->activationShapeId() << ", prio:" << tool->priority();
00133             KoTool *tl = tool->createTool(controller->canvas());
00134             Q_ASSERT(tl);
00135             uniqueToolIds.insert(tl, tool->uniqueId());
00136             toolsHash.insert(tool->id(), tl);
00137             tl->setObjectName(tool->id());
00138             foreach(KAction *action, tl->actions())
00139                 action->setEnabled(false);
00140             KoZoomTool *zoomTool = dynamic_cast<KoZoomTool*>(tl);
00141             if (zoomTool)
00142                 zoomTool->setCanvasController(controller);
00143             KoPanTool *panTool = dynamic_cast<KoPanTool*>(tl);
00144             if (panTool)
00145                 panTool->setCanvasController(controller);
00146         }
00147         KoCreateShapesTool *createTool = dynamic_cast<KoCreateShapesTool*>(toolsHash.value(KoCreateShapesTool_ID));
00148         Q_ASSERT(createTool);
00149         QString id = KoShapeRegistry::instance()->keys()[0];
00150         createTool->setShapeId(id);
00151 
00152         CanvasData *cd = new CanvasData(controller, device);
00153         cd->allTools = toolsHash;
00154         return cd;
00155     }
00156 
00157     bool toolCanBeUsed( const QString &activationShapeId)
00158     {
00159         if (layerEnabled)
00160             return true;
00161         if (activationShapeId.endsWith(QLatin1String( "/always")))
00162             return true;
00163         return false;
00164     }
00165 };
00166 
00167 // ******** KoToolManager **********
00168 KoToolManager::KoToolManager()
00169         : QObject(),
00170         d(new Private())
00171 {
00172     connect(QApplication::instance(), SIGNAL(focusChanged(QWidget*, QWidget*)),
00173             this, SLOT(movedFocus(QWidget*, QWidget*)));
00174 }
00175 
00176 KoToolManager::~KoToolManager()
00177 {
00178     delete d;
00179 }
00180 
00181 void KoToolManager::setup()
00182 {
00183     if (d->tools.size() > 0)
00184         return;
00185 
00186     d->tools.append(new ToolHelper(new KoCreatePathToolFactory(this)));
00187     d->tools.append(new ToolHelper(new KoCreateShapesToolFactory(this)));
00188     d->tools.append(new ToolHelper(new KoPathToolFactory(this)));
00189     d->tools.append(new ToolHelper(new KoZoomToolFactory(this)));
00190     d->tools.append(new ToolHelper(new KoPanToolFactory(this)));
00191 
00192     KoShapeRegistry::instance();
00193     KoToolRegistry *registry = KoToolRegistry::instance();
00194     foreach(const QString & id, registry->keys()) {
00195         ToolHelper *t = new ToolHelper(registry->value(id));
00196         d->tools.append(t);
00197     }
00198 
00199     // connect to all tools so we can hear their button-clicks
00200     foreach(ToolHelper *tool, d->tools)
00201         connect(tool, SIGNAL(toolActivated(ToolHelper*)), this, SLOT(toolActivated(ToolHelper*)));
00202 
00203     // load pluggable input devices
00204     KoDeviceRegistry::instance();
00205 }
00206 
00207 QList<KoToolManager::Button> KoToolManager::createToolList(KoCanvasBase *canvas) const
00208 {
00209     QList<KoToolManager::Button> answer;
00210     foreach(ToolHelper *tool, d->tools) {
00211         if (tool->id() == KoCreateShapesTool_ID)
00212             continue; // don't show this one.
00213         if (tool->id() == KoGuidesTool_ID)
00214             continue; // don't show this one.
00215         if (! tool->canCreateTool(canvas))
00216             continue;
00217         Button button;
00218         button.button = tool->createButton();
00219         button.section = tool->toolType();
00220         button.priority = tool->priority();
00221         button.buttonGroupId = tool->uniqueId();
00222         button.visibilityCode = tool->activationShapeId();
00223         answer.append(button);
00224     }
00225     return answer;
00226 }
00227 
00228 void KoToolManager::requestToolActivation(KoCanvasController * controller)
00229 {
00230     if (d->canvasses.contains(controller)) {
00231         QString activeToolId = d->canvasses.value(controller).first()->activeToolId;
00232         foreach(ToolHelper * th, d->tools) {
00233             if (th->id() == activeToolId) {
00234                 toolActivated(th);
00235                 break;
00236             }
00237         }
00238     }
00239 }
00240 
00241 KoInputDevice KoToolManager::currentInputDevice() const
00242 {
00243     return d->inputDevice;
00244 }
00245 
00246 void KoToolManager::registerTools(KActionCollection *ac, KoCanvasController *controller)
00247 {
00248     Q_ASSERT(controller);
00249     Q_ASSERT(ac);
00250 
00251     setup();
00252 
00253     if (! d->canvasses.contains(controller)) {
00254         kWarning(30006) << "registerTools called on a canvasController that has not been registered (yet)!";
00255         return;
00256     }
00257     CanvasData *cd = d->canvasses.value(controller).first();
00258     foreach(KoTool *tool, cd->allTools) {
00259         QHash<QString, KAction*> actions = tool->actions();
00260         QHash<QString, KAction*>::const_iterator it(actions.constBegin());
00261         for (; it != actions.constEnd(); ++it) {
00262             ac->addAction(it.key(), it.value());
00263         }
00264     }
00265 }
00266 
00267 void KoToolManager::addController(KoCanvasController *controller)
00268 {
00269     Q_ASSERT(controller);
00270     if (d->canvasses.keys().contains(controller))
00271         return;
00272     setup();
00273     attachCanvas(controller);
00274     connect(controller, SIGNAL(canvasRemoved(KoCanvasController*)), this, SLOT(detachCanvas(KoCanvasController*)));
00275     connect(controller, SIGNAL(canvasSet(KoCanvasController*)), this, SLOT(attachCanvas(KoCanvasController*)));
00276 }
00277 
00278 void KoToolManager::removeCanvasController(KoCanvasController *controller)
00279 {
00280     Q_ASSERT(controller);
00281     detachCanvas(controller);
00282     disconnect(controller, SIGNAL(canvasRemoved(KoCanvasController*)), this, SLOT(detachCanvas(KoCanvasController*)));
00283     disconnect(controller, SIGNAL(canvasSet(KoCanvasController*)), this, SLOT(attachCanvas(KoCanvasController*)));
00284 }
00285 
00286 void KoToolManager::toolActivated(ToolHelper *tool)
00287 {
00288     Q_ASSERT(tool);
00289     if (!d->toolCanBeUsed(tool->activationShapeId()))
00290         return;
00291 
00292     Q_ASSERT(d->canvasData);
00293     if (!d->canvasData) return;
00294     KoTool *t = d->canvasData->allTools.value(tool->id());
00295     Q_ASSERT(t);
00296 
00297     d->canvasData->activeToolId = tool->id();
00298     d->canvasData->activationShapeId = tool->activationShapeId();
00299 
00300     switchTool(t, false);
00301 }
00302 
00303 void KoToolManager::switchTool(const QString &id, bool temporary)
00304 {
00305     Q_ASSERT(d->canvasData);
00306     if (!d->canvasData) return;
00307 
00308     if (d->canvasData->activeTool && temporary)
00309         d->canvasData->stack.push(d->canvasData->activeToolId);
00310     d->canvasData->activeToolId = id;
00311     KoTool *tool = d->canvasData->allTools.value(id);
00312     if (! tool) {
00313         kWarning(30006) << "KoToolManager::switchTool() " << (temporary ? "temporary" : "") << " got request to unknown tool: '" << id << "'";
00314         return;
00315     }
00316 
00317     foreach(ToolHelper *th, d->tools) {
00318         if (th->id() == id) {
00319             if(!d->toolCanBeUsed(th->activationShapeId()) ) return;
00320             d->canvasData->activationShapeId = th->activationShapeId();
00321             break;
00322         }
00323     }
00324 
00325     switchTool(tool, temporary);
00326 }
00327 
00328 void KoToolManager::switchTool(KoTool *tool, bool temporary)
00329 {
00330     Q_ASSERT(tool);
00331     if (d->canvasData == 0)
00332         return;
00333 
00334     if (d->canvasData->activeTool == tool && tool->toolId() != KoInteractionTool_ID)
00335         return;
00336 
00337     bool newActiveTool = d->canvasData->activeTool != 0;
00338 
00339     if (newActiveTool) {
00340         d->canvasData->activeTool->repaintDecorations();
00341         // check if this tool is inputDeviceAgnostic and used by other devices, in which case we should not deactivate.
00342         QList<CanvasData*> items = d->canvasses[d->canvasData->canvas];
00343         foreach(CanvasData *cd, items) {
00344             if (cd == d->canvasData) continue;
00345             if (cd->activeTool == d->canvasData->activeTool) {
00346                 newActiveTool = false;
00347                 break;
00348             }
00349         }
00350     }
00351 
00352     if (newActiveTool) {
00353         foreach(KAction *action, d->canvasData->activeTool->actions())
00354             action->setEnabled(false);
00355         // repaint the decorations before we deactivate the tool as it might deleted
00356         // data needed for the repaint
00357         d->canvasData->activeTool->deactivate();
00358         disconnect(d->canvasData->activeTool, SIGNAL(cursorChanged(const QCursor&)),
00359                    this, SLOT(updateCursor(const QCursor&)));
00360         disconnect(d->canvasData->activeTool, SIGNAL(activateTool(const QString &)),
00361                    this, SLOT(switchToolRequested(const QString &)));
00362         disconnect(d->canvasData->activeTool, SIGNAL(activateTemporary(const QString &)),
00363                    this, SLOT(switchToolTemporaryRequested(const QString &)));
00364         disconnect(d->canvasData->activeTool, SIGNAL(done()), this, SLOT(switchBackRequested()));
00365         disconnect(d->canvasData->activeTool, SIGNAL(statusTextChanged(const QString &)),
00366                    this, SIGNAL(changedStatusText(const QString &)));
00367     }
00368 
00369     d->canvasData->activeTool = tool;
00370 
00371     connect(d->canvasData->activeTool, SIGNAL(cursorChanged(const QCursor &)),
00372             this, SLOT(updateCursor(const QCursor &)));
00373     connect(d->canvasData->activeTool, SIGNAL(activateTool(const QString &)),
00374             this, SLOT(switchToolRequested(const QString &)));
00375     connect(d->canvasData->activeTool, SIGNAL(activateTemporary(const QString &)),
00376             this, SLOT(switchToolTemporaryRequested(const QString &)));
00377     connect(d->canvasData->activeTool, SIGNAL(done()), this, SLOT(switchBackRequested()));
00378     connect(d->canvasData->activeTool, SIGNAL(statusTextChanged(const QString &)),
00379             this, SIGNAL(changedStatusText(const QString &)));
00380 
00381     // emit a empty status text to clear status text from last active tool
00382     emit changedStatusText(QString());
00383 
00384     // we expect the tool to emit a cursor on activation.  This is for quick-fail :)
00385     d->canvasData->canvas->canvas()->canvasWidget()->setCursor(Qt::ForbiddenCursor);
00386     foreach(KAction *action, d->canvasData->activeTool->actions()) {
00387         action->setEnabled(true);
00388         d->canvasData->canvas->addAction(action);
00389     }
00390 
00391     postSwitchTool(temporary);
00392 }
00393 
00394 void KoToolManager::postSwitchTool(bool temporary)
00395 {
00396 #ifndef NDEBUG
00397     int canvasCount = 1;
00398     foreach(QList<CanvasData*> list, d->canvasses) {
00399         bool first = true;
00400         foreach(CanvasData *data, list) {
00401             if (first)
00402                 kDebug(30006) << "Canvas" << canvasCount++;
00403             kDebug(30006) << "  +- Tool:" << data->activeToolId  << (data == d->canvasData ? " *" : "");
00404             first = false;
00405         }
00406     }
00407 #endif
00408     Q_ASSERT(d->canvasData);
00409     if (!d->canvasData) return;
00410 
00411     if (d->canvasData->canvas->canvas()) {
00412         KoCanvasBase *canvas = d->canvasData->canvas->canvas();
00413         // Caller of postSwitchTool expect this to be called to update the selected tool
00414         KoToolProxy *tp = d->proxies.value(canvas);
00415         if (tp)
00416             tp->setActiveTool(d->canvasData->activeTool);
00417         d->canvasData->activeTool->activate(temporary);
00418         canvas->updateInputMethodInfo();
00419     } else {
00420         d->canvasData->activeTool->activate(temporary);
00421     }
00422 
00423     QMap<QString, QWidget *> optionWidgetMap = d->canvasData->activeTool->optionWidgets();
00424     if (optionWidgetMap.empty()) { // no option widget.
00425         QWidget *toolWidget;
00426         QString name;
00427         foreach(ToolHelper *tool, d->tools) {
00428             if (tool->id() == d->canvasData->activeTool->toolId()) {
00429                 name = tool->name();
00430                 break;
00431             }
00432         }
00433         toolWidget = d->canvasData->dummyToolWidget;
00434         if (toolWidget == 0) {
00435             toolWidget = new QWidget();
00436             toolWidget->setObjectName( "DummyToolWidget" );
00437             QVBoxLayout *layout = new QVBoxLayout(toolWidget);
00438             layout->setMargin(3);
00439             d->canvasData->dummyToolLabel = new QLabel(toolWidget);
00440             layout->addWidget(d->canvasData->dummyToolLabel);
00441             layout->addItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::Expanding));
00442             toolWidget->setLayout(layout);
00443             d->canvasData->dummyToolWidget = toolWidget;
00444         }
00445         d->canvasData->dummyToolLabel->setText(i18n("Active tool: %1", name));
00446         optionWidgetMap.insert(i18n("Tool Options"), toolWidget);
00447     }
00448 
00449     // Activate the actions for the currently active tool
00450     foreach(KAction *action, d->canvasData->activeTool->actions()) {
00451         action->setEnabled(true);
00452     }
00453 
00454     d->canvasData->canvas->setToolOptionWidgets(optionWidgetMap);
00455     emit changedTool(d->canvasData->canvas, d->uniqueToolIds.value(d->canvasData->activeTool));
00456 }
00457 
00458 
00459 void KoToolManager::attachCanvas(KoCanvasController *controller)
00460 {
00461     Q_ASSERT(controller);
00462     CanvasData *cd = d->createCanvasData(controller, KoInputDevice::mouse());
00463     // switch to new canvas as the active one.
00464     if (d->canvasData == 0) {
00465         QApplication::instance()->installEventFilter(this);
00466     }
00467     d->canvasData = cd;
00468     d->inputDevice = cd->inputDevice;
00469     QList<CanvasData*> canvasses;
00470     canvasses.append(cd);
00471     d->canvasses[controller] = canvasses;
00472 
00473     KoToolProxy *tp = d->proxies[controller->canvas()];
00474     if (tp)
00475         tp->setCanvasController(controller);
00476 
00477     if (cd->activeTool == 0) {
00478         // no active tool, so we activate the highest priority main tool
00479         int highestPriority = INT_MAX;
00480         ToolHelper * helper = 0;
00481         foreach(ToolHelper * th, d->tools) {
00482             if (th->toolType() == KoToolFactory::mainToolType()) {
00483                 if (th->priority() < highestPriority) {
00484                     highestPriority = qMin(highestPriority, th->priority());
00485                     helper = th;
00486                 }
00487             }
00488         }
00489         if (helper)
00490             toolActivated(helper);
00491     }
00492 
00493     Connector *connector = new Connector(controller->canvas()->shapeManager());
00494     connect(connector, SIGNAL(selectionChanged(QList<KoShape*>)), this,
00495             SLOT(selectionChanged(QList<KoShape*>)));
00496     connect(controller->canvas()->shapeManager()->selection(),
00497             SIGNAL(currentLayerChanged(const KoShapeLayer*)),
00498             this, SLOT(currentLayerChanged(const KoShapeLayer*)));
00499 
00500     d->canvasData->canvas->activate();
00501 
00502     emit changedCanvas(d->canvasData ? d->canvasData->canvas->canvas() : 0);
00503 }
00504 
00505 void KoToolManager::movedFocus(QWidget *from, QWidget *to)
00506 {
00507     Q_UNUSED(from);
00508     if (to == 0 || (d->canvasData && to == d->canvasData->canvas))
00509         return;
00510 
00511     KoCanvasController *newCanvas = 0;
00512     // if the 'to' is one of our canvasses, or one of its children, then switch.
00513     foreach(KoCanvasController* canvas, d->canvasses.keys()) {
00514         if (canvas == to || canvas->canvas()->canvasWidget() == to) {
00515             newCanvas = canvas;
00516             break;
00517         }
00518     }
00519 
00520     if (newCanvas == 0)
00521         return;
00522     if (d->canvasData && newCanvas == d->canvasData->canvas)
00523         return;
00524 
00525     if (! d->canvasses.contains(newCanvas))
00526         return;
00527     foreach(CanvasData *data, d->canvasses.value(newCanvas)) {
00528         if (data->inputDevice == d->inputDevice) {
00529             if (d->canvasData) { // deactivate the old one.
00530                 d->canvasData->canvas->canvas()->canvasWidget()->setCursor(Qt::ArrowCursor);
00531                 if (d->canvasData->activeTool) {
00532                     d->canvasData->activeTool->deactivate();
00533                     KoToolProxy *proxy = d->proxies.value(d->canvasData->canvas->canvas());
00534                     Q_ASSERT(proxy);
00535                     proxy->setActiveTool(0);
00536                 }
00537             }
00538 
00539             d->canvasData = data;
00540             d->canvasData->canvas->canvas()->canvasWidget()->setCursor(d->canvasData->activeTool->cursor());
00541             d->canvasData->canvas->activate();
00542             postSwitchTool(false);
00543             emit changedCanvas(d->canvasData ? d->canvasData->canvas->canvas() : 0);
00544             return;
00545         }
00546     }
00547     // no such inputDevice for this canvas...
00548     d->canvasData = d->canvasses.value(newCanvas).first();
00549     d->inputDevice = d->canvasData->inputDevice;
00550     d->canvasData->canvas->activate();
00551     emit changedCanvas(d->canvasData ? d->canvasData->canvas->canvas() : 0);
00552 }
00553 
00554 void KoToolManager::detachCanvas(KoCanvasController *controller)
00555 {
00556     Q_ASSERT(controller);
00557     // check if we are removing the active canvas controller
00558     if (d->canvasData && d->canvasData->canvas == controller) {
00559         KoCanvasController *newCanvas = 0;
00560         // try to find another canvas controller beside the one we are removing
00561         foreach(KoCanvasController* canvas, d->canvasses.keys()) {
00562             if (canvas != controller) {
00563                 // yay found one
00564                 newCanvas = canvas;
00565                 break;
00566             }
00567         }
00568         if (newCanvas) {
00569             // activate the found canvas controller
00570             d->canvasData = d->canvasses.value(newCanvas).first();
00571             d->inputDevice = d->canvasData->inputDevice;
00572             d->canvasData->canvas->activate();
00573         } else {
00574             // as a last resort just set a blank one
00575             d->canvasData = 0;
00576             // and stop the event filter
00577             QApplication::instance()->removeEventFilter(this);
00578         }
00579     }
00580 
00581     QList<KoTool *> tools;
00582     foreach(CanvasData *cd, d->canvasses.value(controller)) {
00583         foreach(KoTool *tool, cd->allTools)
00584             if (! tools.contains(tool))
00585                 tools.append(tool);
00586         delete cd;
00587     }
00588     foreach(KoTool *tool, tools) {
00589         d->uniqueToolIds.remove(tool);
00590         delete tool;
00591     }
00592     d->canvasses.remove(controller);
00593     emit changedCanvas(d->canvasData ? d->canvasData->canvas->canvas() : 0);
00594 }
00595 
00596 void KoToolManager::updateCursor(const QCursor &cursor)
00597 {
00598     Q_ASSERT(d->canvasData);
00599     Q_ASSERT(d->canvasData->canvas);
00600     Q_ASSERT(d->canvasData->canvas->canvas());
00601     d->canvasData->canvas->canvas()->canvasWidget()->setCursor(cursor);
00602 }
00603 
00604 void KoToolManager::switchToolRequested(const QString & id)
00605 {
00606     Q_ASSERT(d->canvasData);
00607     if (!d->canvasData) return;
00608 
00609     while (!d->canvasData->stack.isEmpty()) // switching means to flush the stack
00610         d->canvasData->stack.pop();
00611     switchTool(id, false);
00612 }
00613 
00614 void KoToolManager::switchToolTemporaryRequested(const QString &id)
00615 {
00616     switchTool(id, true);
00617 }
00618 
00619 void KoToolManager::switchBackRequested()
00620 {
00621     Q_ASSERT(d->canvasData);
00622     if (!d->canvasData) return;
00623 
00624     if (d->canvasData->stack.isEmpty()) {
00625         // default to changing to the interactionTool
00626         switchTool(KoInteractionTool_ID, false);
00627         return;
00628     }
00629     switchTool(d->canvasData->stack.pop(), false);
00630 }
00631 
00632 KoCreateShapesTool * KoToolManager::shapeCreatorTool(KoCanvasBase *canvas) const
00633 {
00634     Q_ASSERT(canvas);
00635     foreach(KoCanvasController *controller, d->canvasses.keys()) {
00636         if (controller->canvas() == canvas) {
00637             KoCreateShapesTool *createTool = dynamic_cast<KoCreateShapesTool*>
00638                                              (d->canvasData->allTools.value(KoCreateShapesTool_ID));
00639             Q_ASSERT(createTool /* ID changed? */);
00640             return createTool;
00641         }
00642     }
00643     Q_ASSERT(0);   // this should not happen
00644     return 0;
00645 }
00646 
00647 KoGuidesTool * KoToolManager::guidesTool(KoCanvasBase * canvas) const
00648 {
00649     Q_ASSERT(canvas);
00650     foreach(KoCanvasController *controller, d->canvasses.keys()) {
00651         if (controller->canvas() == canvas) {
00652             KoGuidesTool * guidesTool = dynamic_cast<KoGuidesTool*>
00653                                         (d->canvasData->allTools.value(KoGuidesTool_ID));
00654             Q_ASSERT(guidesTool /* ID changed? */);
00655             return guidesTool;
00656         }
00657     }
00658     Q_ASSERT(0);   // this should not happen
00659     return 0;
00660 }
00661 
00662 void KoToolManager::selectionChanged(QList<KoShape*> shapes)
00663 {
00664     QList<QString> types;
00665     foreach(KoShape *shape, shapes) {
00666         if (! types.contains(shape->shapeId())) {
00667             types.append(shape->shapeId());
00668         }
00669     }
00670 
00671     // check if there is still a shape selected the active tool can work on
00672     // there needs to be at least one shape for a tool without an activationShapeId
00673     // to work
00674     // if not change the current tool to the default tool
00675     if (!(d->canvasData->activationShapeId.isNull() && shapes.size() > 0)
00676         && d->canvasData->activationShapeId != "flake/always"
00677         && d->canvasData->activationShapeId != "flake/edit"
00678         && ! types.contains(d->canvasData->activationShapeId)) {
00679         switchTool(KoInteractionTool_ID, false);
00680     }
00681 
00682     emit toolCodesSelected(d->canvasData->canvas, types);
00683 }
00684 
00685 void KoToolManager::currentLayerChanged(const KoShapeLayer *layer)
00686 {
00687     kDebug(30006) << "layer changed to" << layer;
00688 
00689     emit currentLayerChanged(d->canvasData->canvas, layer);
00690     d->layerEnabled = layer == 0 || (layer->isEditable() && layer->isVisible());
00691 
00692     kDebug(30006 ) << "and the layer enabled is" << (d->layerEnabled ? "true" : "false");
00693 
00694     KoToolProxy *proxy = d->proxies.value(d->canvasData->canvas->canvas());
00695     kDebug(30006) << " and the proxy is" << proxy;
00696     if (proxy) {
00697         kDebug( 30006 ) << " set " << d->canvasData->activeTool << (d->layerEnabled ? "enabled" : "disabled");
00698         proxy->setActiveTool(d->toolCanBeUsed(d->canvasData->activationShapeId) ? d->canvasData->activeTool : 0);
00699     }
00700 }
00701 
00702 KoCanvasController *KoToolManager::activeCanvasController() const
00703 {
00704     if (! d->canvasData) return 0;
00705     return d->canvasData->canvas;
00706 }
00707 
00708 void KoToolManager::switchToolByShortcut(QKeyEvent *event)
00709 {
00710     QKeySequence item(event->key() | ((Qt::ControlModifier | Qt::AltModifier) & event->modifiers()));
00711 
00712     foreach(ToolHelper *th, d->tools) {
00713         if (th->shortcut().contains(item)) {
00714             event->accept();
00715             switchTool(th->id(), false);
00716             return;
00717         }
00718     }
00719     if (event->key() == Qt::Key_Space && event->modifiers() == 0) {
00720         switchTool(KoPanTool_ID, true);
00721     }
00722 }
00723 
00724 QString KoToolManager::preferredToolForSelection(const QList<KoShape*> &shapes)
00725 {
00726     QList<QString> types;
00727     foreach(KoShape *shape, shapes)
00728         if (! types.contains(shape->shapeId()))
00729             types.append(shape->shapeId());
00730 
00731     QString toolType = KoInteractionTool_ID;
00732     int prio = INT_MAX;
00733     foreach(ToolHelper *helper, d->tools) {
00734         if (helper->priority() >= prio)
00735             continue;
00736         if (helper->toolType() == KoToolFactory::mainToolType())
00737             continue;
00738         if (types.contains(helper->activationShapeId())) {
00739             toolType = helper->id();
00740             prio = helper->priority();
00741         }
00742     }
00743     return toolType;
00744 }
00745 
00746 #define MSECS_TO_IGNORE_SWITCH_TO_MOUSE_AFTER_TABLET_EVENT_RECEIVED 100
00747 
00748 void KoToolManager::switchInputDevice(const KoInputDevice &device)
00749 {
00750     Q_ASSERT(d->canvasData);
00751     if (!d->canvasData) return;
00752     if (!device.isMouse()) {
00753         d->tabletEventTimer.start(MSECS_TO_IGNORE_SWITCH_TO_MOUSE_AFTER_TABLET_EVENT_RECEIVED);
00754     }
00755     if (d->inputDevice == device) return;
00756     if (device.isMouse() && d->tabletEventTimer.isActive()) {
00757         // Ignore switch to mouse for a short time after a tablet event
00758         // is received, as this is likely to be either the mouse event sent
00759         // to a widget that doesn't accept the tablet event, or, on X11,
00760         // a core event sent after the tablet event.
00761         return;
00762     }
00763     d->inputDevice = device;
00764     QList<CanvasData*> items = d->canvasses[d->canvasData->canvas];
00765 
00766     // disable all actions for all tools in the all canvasdata objects for this canvas.
00767     foreach(CanvasData *cd, items) {
00768         foreach(KoTool* tool, cd->allTools) {
00769             foreach(KAction* action, tool->actions()) {
00770                 action->setEnabled(false);
00771             }
00772         }
00773     }
00774 
00775     // search for a canvasdata object for the current input device
00776     foreach(CanvasData *cd, items) {
00777 
00778         if (cd->inputDevice == device) {
00779             d->canvasData = cd;
00780             if (cd->activeTool == 0)
00781                 switchTool(KoInteractionTool_ID, false);
00782             else {
00783                 postSwitchTool(false);
00784                 d->canvasData->canvas->canvas()->canvasWidget()->setCursor(d->canvasData->activeTool->cursor());
00785             }
00786             d->canvasData->canvas->activate();
00787             emit inputDeviceChanged(device);
00788             emit changedCanvas(d->canvasData ? d->canvasData->canvas->canvas() : 0);
00789             return;
00790         }
00791     }
00792 
00793     // still here?  That means we need to create a new CanvasData instance with the current InputDevice.
00794     CanvasData *cd = d->createCanvasData(d->canvasData->canvas, device);
00795     // switch to new canvas as the active one.
00796     QString oldTool = d->canvasData->activeToolId;
00797 
00798     d->canvasData = cd;
00799     items.append(cd);
00800     d->canvasses[d->canvasData->canvas] = items;
00801 
00802     switchToolRequested(oldTool);
00803     emit inputDeviceChanged(device);
00804     d->canvasData->canvas->activate();
00805     emit changedCanvas(d->canvasData ? d->canvasData->canvas->canvas() : 0);
00806 }
00807 
00808 bool KoToolManager::eventFilter(QObject *object, QEvent *event)
00809 {
00810     switch (event->type()) {
00811     case QEvent::TabletMove:
00812     case QEvent::TabletPress:
00813     case QEvent::TabletRelease:
00814     case QEvent::TabletEnterProximity:
00815     case QEvent::TabletLeaveProximity: {
00816         QTabletEvent *tabletEvent = static_cast<QTabletEvent *>(event);
00817 
00818         KoInputDevice id(tabletEvent->device(), tabletEvent->pointerType(), tabletEvent->uniqueId());
00819         switchInputDevice(id);
00820         break;
00821     }
00822     case QEvent::MouseButtonPress:
00823     case QEvent::MouseMove:
00824     case QEvent::MouseButtonRelease:
00825         switchInputDevice(KoInputDevice::mouse());
00826         break;
00827     default:
00828         break;
00829     }
00830 
00831     return QObject::eventFilter(object, event);
00832 }
00833 
00834 void KoToolManager::registerToolProxy(KoToolProxy *proxy, KoCanvasBase *canvas)
00835 {
00836     d->proxies.insert(canvas, proxy);
00837     foreach(KoCanvasController *controller, d->canvasses.keys()) {
00838         if (controller->canvas() == canvas) {
00839             proxy->setCanvasController(controller);
00840             break;
00841         }
00842     }
00843 }
00844 
00845 void KoToolManager::injectDeviceEvent(KoDeviceEvent * event)
00846 {
00847     if (d->canvasData && d->canvasData->canvas->canvas()) {
00848         if (static_cast<KoDeviceEvent::Type>(event->type()) == KoDeviceEvent::ButtonPressed)
00849             d->canvasData->activeTool->customPressEvent(event->pointerEvent());
00850         else if (static_cast<KoDeviceEvent::Type>(event->type()) == KoDeviceEvent::ButtonReleased)
00851             d->canvasData->activeTool->customReleaseEvent(event->pointerEvent());
00852         else if (static_cast<KoDeviceEvent::Type>(event->type()) ==  KoDeviceEvent::PositionChanged)
00853             d->canvasData->activeTool->customMoveEvent(event->pointerEvent());
00854     }
00855 }
00856 
00857 KoToolManager* KoToolManager::instance()
00858 {
00859     K_GLOBAL_STATIC(KoToolManager, s_instance)
00860     return s_instance;
00861 }
00862 
00863 QString KoToolManager::activeToolId() const
00864 {
00865     if (!d->canvasData) return QString();
00866     return d->canvasData->activeToolId;
00867 }
00868 
00869 #include "KoToolManager.moc"