diff --git a/libs/flake/KoToolManager.cpp b/libs/flake/KoToolManager.cpp index 3c78575483..daf6d43bc5 100644 --- a/libs/flake/KoToolManager.cpp +++ b/libs/flake/KoToolManager.cpp @@ -1,1062 +1,1120 @@ /* This file is part of the KDE project * * Copyright (c) 2005-2010 Boudewijn Rempt * Copyright (C) 2006-2008 Thomas Zander * Copyright (C) 2006 Thorsten Zachmann * Copyright (C) 2008 Jan Hambrecht * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ // flake #include "KoToolManager.h" #include "KoToolManager_p.h" #include "KoToolRegistry.h" #include "KoToolProxy.h" #include "KoToolProxy_p.h" #include "KoSelection.h" #include "KoCanvasController.h" #include "KoCanvasControllerWidget.h" #include "KoShape.h" #include "KoShapeLayer.h" #include "KoShapeRegistry.h" #include "KoShapeManager.h" #include "KoCanvasBase.h" #include "KoInputDeviceHandlerRegistry.h" #include "KoInputDeviceHandlerEvent.h" #include "KoPointerEvent.h" #include "tools/KoCreateShapesTool.h" #include "tools/KoZoomTool.h" #include "tools/KoPanTool.h" // Qt + kde #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include #include #include +class Q_DECL_HIDDEN KoToolAction::Private +{ +public: + ToolHelper* toolHelper; +}; + +KoToolAction::KoToolAction(ToolHelper* toolHelper) + : QObject(toolHelper) + , d(new Private) +{ + d->toolHelper = toolHelper; +} + +KoToolAction::~KoToolAction() +{ + delete d; +} + +void KoToolAction::trigger() +{ + d->toolHelper->activate(); +} + + +QString KoToolAction::iconText() const +{ + return d->toolHelper->iconText(); +} + +QString KoToolAction::toolTip() const +{ + return d->toolHelper->toolTip(); +} + +QString KoToolAction::id() const +{ + return d->toolHelper->id(); +} + +QString KoToolAction::iconName() const +{ + return d->toolHelper->iconName(); +} + +KShortcut KoToolAction::shortcut() const +{ + return d->toolHelper->shortcut(); +} + + +QString KoToolAction::section() const +{ + return d->toolHelper->toolType(); +} + +int KoToolAction::priority() const +{ + return d->toolHelper->priority(); +} + +int KoToolAction::buttonGroupId() const +{ + return d->toolHelper->uniqueId(); +} + +QString KoToolAction::visibilityCode() const +{ + return d->toolHelper->activationShapeId(); +} + + class CanvasData { public: CanvasData(KoCanvasController *cc, const KoInputDevice &id) : activeTool(0), canvas(cc), inputDevice(id), dummyToolWidget(0), dummyToolLabel(0) { } ~CanvasData() { // the dummy tool widget does not necessarily have a parent and we create it, so we delete it. delete dummyToolWidget; } void activateToolActions() { disabledDisabledActions.clear(); disabledActions.clear(); disabledCanvasShortcuts.clear(); // we do several things here // 1. enable the actions of the active tool // 2. disable conflicting actions // 3. replace conflicting actions in the action collection KActionCollection *canvasActionCollection = canvas->actionCollection(); QHash toolActions = activeTool->actions(); QHash::const_iterator it(toolActions.constBegin()); for (; it != toolActions.constEnd(); ++it) { if (canvasActionCollection) { QString toolActionID = it.key(); KAction *toolAction = it.value(); KAction* action = qobject_cast(canvasActionCollection->action(it.key())); if (action) { canvasActionCollection->takeAction(action); if (action != it.value()) { if (action->isEnabled()) { action->setEnabled(false); disabledActions.append(action); } else { disabledDisabledActions.append(action); } } } foreach(QAction *a, canvasActionCollection->actions()) { KAction *canvasAction = dynamic_cast(a); if (canvasAction && canvasAction->shortcut().toString() != "" && canvasAction->shortcut() == toolAction->shortcut()) { kWarning() << activeToolId << ": action" << toolActionID << "conflicts with canvas action" << canvasAction->objectName() << "shortcut:" << canvasAction->shortcut().toString(); disabledCanvasShortcuts[canvasAction] = canvasAction->shortcut().toString(); canvasAction->setShortcut(QKeySequence()); } } canvasActionCollection->addAction(toolActionID, toolAction); } it.value()->setEnabled(true); } canvasActionCollection->readSettings(); // The shortcuts might have been configured in the meantime. } void deactivateToolActions() { if (!activeTool) return; // disable actions of active tool foreach(KAction *action, activeTool->actions()) { action->setEnabled(false); } // enable actions which where disabled on activating the active tool // and re-add them to the action collection KActionCollection *ac = canvas->actionCollection(); foreach(QPointer action, disabledDisabledActions) { if (action) { if (ac) { ac->addAction(action->objectName(), action); } } } disabledDisabledActions.clear(); foreach(QPointer action, disabledActions) { if (action) { action->setEnabled(true); if(ac) { ac->addAction(action->objectName(), action); } } } disabledActions.clear(); QMap, QString>::const_iterator it(disabledCanvasShortcuts.constBegin()); for (; it != disabledCanvasShortcuts.constEnd(); ++it) { KAction *action = it.key(); QString shortcut = it.value(); action->setShortcut(shortcut); } disabledCanvasShortcuts.clear(); } KoToolBase *activeTool; // active Tool QString activeToolId; // the id of the active Tool QString activationShapeId; // the shape-type (KoShape::shapeId()) the activeTool 'belongs' to. QHash allTools; // all the tools that are created for this canvas. QStack stack; // stack of temporary tools KoCanvasController *const canvas; const KoInputDevice inputDevice; QWidget *dummyToolWidget; // the widget shown in the toolDocker. QLabel *dummyToolLabel; QList > disabledActions; ///< disabled conflicting actions QList > disabledDisabledActions; ///< disabled conflicting actions that were already disabled QMap, QString> disabledCanvasShortcuts; ///< Shortcuts that were temporarily removed from canvas actions because the tool overrides }; KoToolManager::Private::Private(KoToolManager *qq) : q(qq), canvasData(0), layerExplicitlyDisabled(false) { } KoToolManager::Private::~Private() { qDeleteAll(tools); } // helper method. CanvasData *KoToolManager::Private::createCanvasData(KoCanvasController *controller, const KoInputDevice &device) { QHash toolsHash; foreach(ToolHelper *tool, tools) { QPair toolPair = q->createTools(controller, tool); if (toolPair.second) { // only if a real tool was created toolsHash.insert(toolPair.first, toolPair.second); } } KoCreateShapesTool *createShapesTool = dynamic_cast(toolsHash.value(KoCreateShapesTool_ID)); Q_ASSERT(createShapesTool); QString id = KoShapeRegistry::instance()->keys()[0]; createShapesTool->setShapeId(id); CanvasData *cd = new CanvasData(controller, device); cd->allTools = toolsHash; return cd; } void KoToolManager::Private::setup() { if (tools.size() > 0) return; KoShapeRegistry::instance(); KoToolRegistry *registry = KoToolRegistry::instance(); foreach(const QString & id, registry->keys()) { ToolHelper *t = new ToolHelper(registry->value(id)); tools.append(t); } // connect to all tools so we can hear their button-clicks foreach(ToolHelper *tool, tools) connect(tool, SIGNAL(toolActivated(ToolHelper*)), q, SLOT(toolActivated(ToolHelper*))); // load pluggable input devices KoInputDeviceHandlerRegistry::instance(); } void KoToolManager::Private::connectActiveTool() { if (canvasData->activeTool) { connect(canvasData->activeTool, SIGNAL(cursorChanged(const QCursor &)), q, SLOT(updateCursor(const QCursor &))); connect(canvasData->activeTool, SIGNAL(activateTool(const QString &)), q, SLOT(switchToolRequested(const QString &))); connect(canvasData->activeTool, SIGNAL(activateTemporary(const QString &)), q, SLOT(switchToolTemporaryRequested(const QString &))); connect(canvasData->activeTool, SIGNAL(done()), q, SLOT(switchBackRequested())); connect(canvasData->activeTool, SIGNAL(statusTextChanged(const QString &)), q, SIGNAL(changedStatusText(const QString &))); } // we expect the tool to emit a cursor on activation. updateCursor(Qt::ForbiddenCursor); } void KoToolManager::Private::disconnectActiveTool() { if (canvasData->activeTool) { canvasData->deactivateToolActions(); // repaint the decorations before we deactivate the tool as it might deleted // data needed for the repaint canvasData->activeTool->deactivate(); disconnect(canvasData->activeTool, SIGNAL(cursorChanged(const QCursor&)), q, SLOT(updateCursor(const QCursor&))); disconnect(canvasData->activeTool, SIGNAL(activateTool(const QString &)), q, SLOT(switchToolRequested(const QString &))); disconnect(canvasData->activeTool, SIGNAL(activateTemporary(const QString &)), q, SLOT(switchToolTemporaryRequested(const QString &))); disconnect(canvasData->activeTool, SIGNAL(done()), q, SLOT(switchBackRequested())); disconnect(canvasData->activeTool, SIGNAL(statusTextChanged(const QString &)), q, SIGNAL(changedStatusText(const QString &))); } // emit a empty status text to clear status text from last active tool emit q->changedStatusText(QString()); } void KoToolManager::Private::switchTool(KoToolBase *tool, bool temporary) { Q_ASSERT(tool); if (canvasData == 0) return; if (canvasData->activeTool == tool && tool->toolId() != KoInteractionTool_ID) return; disconnectActiveTool(); canvasData->activeTool = tool; connectActiveTool(); postSwitchTool(temporary); } void KoToolManager::Private::switchTool(const QString &id, bool temporary) { Q_ASSERT(canvasData); if (!canvasData) return; if (canvasData->activeTool && temporary) canvasData->stack.push(canvasData->activeToolId); canvasData->activeToolId = id; KoToolBase *tool = canvasData->allTools.value(id); if (! tool) { return; } foreach(ToolHelper *th, tools) { if (th->id() == id) { canvasData->activationShapeId = th->activationShapeId(); break; } } switchTool(tool, temporary); } void KoToolManager::Private::postSwitchTool(bool temporary) { #ifndef NDEBUG int canvasCount = 1; foreach(QList list, canvasses) { bool first = true; foreach(CanvasData *data, list) { if (first) { kDebug(30006) << "Canvas" << canvasCount++; } kDebug(30006) << " +- Tool:" << data->activeToolId << (data == canvasData ? " *" : ""); first = false; } } #endif Q_ASSERT(canvasData); if (!canvasData) return; KoToolBase::ToolActivation toolActivation; if (temporary) toolActivation = KoToolBase::TemporaryActivation; else toolActivation = KoToolBase::DefaultActivation; QSet shapesToOperateOn; if (canvasData->activeTool && canvasData->activeTool->canvas() && canvasData->activeTool->canvas()->shapeManager()) { KoSelection *selection = canvasData->activeTool->canvas()->shapeManager()->selection(); Q_ASSERT(selection); foreach(KoShape *shape, selection->selectedShapes()) { QSet delegates = shape->toolDelegates(); if (delegates.isEmpty()) { // no delegates, just the orig shape shapesToOperateOn << shape; } else { shapesToOperateOn += delegates; } } } if (canvasData->canvas->canvas()) { // Caller of postSwitchTool expect this to be called to update the selected tool updateToolForProxy(); canvasData->activeTool->activate(toolActivation, shapesToOperateOn); KoCanvasBase *canvas = canvasData->canvas->canvas(); canvas->updateInputMethodInfo(); } else { canvasData->activeTool->activate(toolActivation, shapesToOperateOn); } QList > optionWidgetList = canvasData->activeTool->optionWidgets(); if (optionWidgetList.empty()) { // no option widget. QWidget *toolWidget; QString title; foreach(ToolHelper *tool, tools) { if (tool->id() == canvasData->activeTool->toolId()) { title = tool->toolTip(); break; } } toolWidget = canvasData->dummyToolWidget; if (toolWidget == 0) { toolWidget = new QWidget(); toolWidget->setObjectName("DummyToolWidget"); QVBoxLayout *layout = new QVBoxLayout(toolWidget); layout->setMargin(3); canvasData->dummyToolLabel = new QLabel(toolWidget); layout->addWidget(canvasData->dummyToolLabel); layout->addItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::Expanding)); toolWidget->setLayout(layout); canvasData->dummyToolWidget = toolWidget; } canvasData->dummyToolLabel->setText(i18n("Active tool: %1", title)); optionWidgetList.append(toolWidget); } // Activate the actions for the currently active tool canvasData->activateToolActions(); emit q->changedTool(canvasData->canvas, uniqueToolIds.value(canvasData->activeTool)); KoCanvasControllerWidget *canvasControllerWidget = dynamic_cast(canvasData->canvas); if (canvasControllerWidget) { canvasControllerWidget->setToolOptionWidgets(optionWidgetList); } } void KoToolManager::Private::switchCanvasData(CanvasData *cd) { Q_ASSERT(cd); KoCanvasBase *oldCanvas = 0; KoInputDevice oldInputDevice; if (canvasData) { oldCanvas = canvasData->canvas->canvas(); oldInputDevice = canvasData->inputDevice; if (canvasData->activeTool) { disconnectActiveTool(); } KoToolProxy *proxy = proxies.value(oldCanvas); Q_ASSERT(proxy); proxy->setActiveTool(0); } canvasData = cd; inputDevice = canvasData->inputDevice; if (canvasData->activeTool) { connectActiveTool(); postSwitchTool(false); } if (oldInputDevice != canvasData->inputDevice) { emit q->inputDeviceChanged(canvasData->inputDevice); } if (oldCanvas != canvasData->canvas->canvas()) { emit q->changedCanvas(canvasData->canvas->canvas()); } } void KoToolManager::Private::toolActivated(ToolHelper *tool) { Q_ASSERT(tool); Q_ASSERT(canvasData); if (!canvasData) return; KoToolBase *t = canvasData->allTools.value(tool->id()); Q_ASSERT(t); canvasData->activeToolId = tool->id(); canvasData->activationShapeId = tool->activationShapeId(); switchTool(t, false); } void KoToolManager::Private::detachCanvas(KoCanvasController *controller) { Q_ASSERT(controller); // check if we are removing the active canvas controller if (canvasData && canvasData->canvas == controller) { KoCanvasController *newCanvas = 0; // try to find another canvas controller beside the one we are removing foreach(KoCanvasController* canvas, canvasses.keys()) { if (canvas != controller) { // yay found one newCanvas = canvas; break; } } if (newCanvas) { switchCanvasData(canvasses.value(newCanvas).first()); } else { KoCanvasControllerWidget *canvasControllerWidget = dynamic_cast(canvasData->canvas); if (canvasControllerWidget) { canvasControllerWidget->setToolOptionWidgets(QList >()); } // as a last resort just set a blank one canvasData = 0; } } KoToolProxy *proxy = proxies.value(controller->canvas()); if (proxy) proxy->setActiveTool(0); QList tools; foreach(CanvasData *canvasData, canvasses.value(controller)) { foreach(KoToolBase *tool, canvasData->allTools) { if (! tools.contains(tool)) { tools.append(tool); } } delete canvasData; } foreach(KoToolBase *tool, tools) { uniqueToolIds.remove(tool); delete tool; } canvasses.remove(controller); emit q->changedCanvas(canvasData ? canvasData->canvas->canvas() : 0); } void KoToolManager::Private::attachCanvas(KoCanvasController *controller) { Q_ASSERT(controller); CanvasData *cd = createCanvasData(controller, KoInputDevice::mouse()); // switch to new canvas as the active one. switchCanvasData(cd); inputDevice = cd->inputDevice; QList canvasses_; canvasses_.append(cd); canvasses[controller] = canvasses_; KoToolProxy *tp = proxies[controller->canvas()]; if (tp) tp->priv()->setCanvasController(controller); if (cd->activeTool == 0) { // no active tool, so we activate the highest priority main tool int highestPriority = INT_MAX; ToolHelper * helper = 0; foreach(ToolHelper * th, tools) { if (th->toolType() == KoToolFactoryBase::mainToolType()) { if (th->priority() < highestPriority) { highestPriority = qMin(highestPriority, th->priority()); helper = th; } } } if (helper) toolActivated(helper); } Connector *connector = new Connector(controller->canvas()->shapeManager()); connect(connector, SIGNAL(selectionChanged(QList)), q, SLOT(selectionChanged(QList))); connect(controller->canvas()->shapeManager()->selection(), SIGNAL(currentLayerChanged(const KoShapeLayer*)), q, SLOT(currentLayerChanged(const KoShapeLayer*))); emit q->changedCanvas(canvasData ? canvasData->canvas->canvas() : 0); } void KoToolManager::Private::movedFocus(QWidget *from, QWidget *to) { Q_UNUSED(from); // XXX: Focus handling for non-qwidget based canvases! if (!canvasData) { return; } KoCanvasControllerWidget *canvasControllerWidget = dynamic_cast(canvasData->canvas); if (!canvasControllerWidget) { return; } if (to == 0 || to == canvasControllerWidget) { return; } KoCanvasController *newCanvas = 0; // if the 'to' is one of our canvasses, or one of its children, then switch. foreach(KoCanvasController* canvas, canvasses.keys()) { if (canvasControllerWidget == to || canvas->canvas()->canvasWidget() == to) { newCanvas = canvas; break; } } if (newCanvas == 0) { return; } if (canvasData && newCanvas == canvasData->canvas) { return; } if (!canvasses.contains(newCanvas)) { return; } foreach(CanvasData *data, canvasses.value(newCanvas)) { if (data->inputDevice == inputDevice) { switchCanvasData(data); return; } } // no such inputDevice for this canvas... switchCanvasData(canvasses.value(newCanvas).first()); } void KoToolManager::Private::updateCursor(const QCursor &cursor) { Q_ASSERT(canvasData); Q_ASSERT(canvasData->canvas); Q_ASSERT(canvasData->canvas->canvas()); canvasData->canvas->canvas()->setCursor(cursor); } void KoToolManager::Private::selectionChanged(const QList &shapes) { QList types; foreach(KoShape *shape, shapes) { QSet delegates = shape->toolDelegates(); if (delegates.isEmpty()) { // no delegates, just the orig shape delegates << shape; } foreach (KoShape *shape2, delegates) { Q_ASSERT(shape2); if (! types.contains(shape2->shapeId())) { types.append(shape2->shapeId()); } } } // check if there is still a shape selected the active tool can work on // there needs to be at least one shape for a tool without an activationShapeId // to work // if not change the current tool to the default tool if (!(canvasData->activationShapeId.isNull() && shapes.size() > 0) && canvasData->activationShapeId != "flake/always" && canvasData->activationShapeId != "flake/edit") { bool currentToolWorks = false; foreach (const QString &type, types) { if (canvasData->activationShapeId.split(',').contains(type)) { currentToolWorks = true; break; } } if (!currentToolWorks) { switchTool(KoInteractionTool_ID, false); } } emit q->toolCodesSelected(types); } void KoToolManager::Private::currentLayerChanged(const KoShapeLayer *layer) { emit q->currentLayerChanged(canvasData->canvas, layer); layerExplicitlyDisabled = layer && !layer->isEditable(); updateToolForProxy(); kDebug(30006) << "Layer changed to" << layer << "explicitly disabled:" << layerExplicitlyDisabled; } void KoToolManager::Private::updateToolForProxy() { KoToolProxy *proxy = proxies.value(canvasData->canvas->canvas()); if(!proxy) return; bool canUseTool = !layerExplicitlyDisabled || canvasData->activationShapeId.endsWith(QLatin1String("/always")); proxy->setActiveTool(canUseTool ? canvasData->activeTool : 0); } void KoToolManager::Private::switchInputDevice(const KoInputDevice &device) { Q_ASSERT(canvasData); if (!canvasData) return; if (inputDevice == device) return; if (inputDevice.isMouse() && device.isMouse()) return; if (device.isMouse() && !inputDevice.isMouse()) { // we never switch back to mouse from a tablet input device, so the user can use the // mouse to edit the settings for a tool activated by a tablet. See bugs // https://bugs.kde.org/show_bug.cgi?id=283130 and https://bugs.kde.org/show_bug.cgi?id=285501. // We do continue to switch between tablet devices, thought. return; } QList items = canvasses[canvasData->canvas]; // disable all actions for all tools in the all canvasdata objects for this canvas. foreach(CanvasData *cd, items) { foreach(KoToolBase* tool, cd->allTools) { foreach(KAction* action, tool->actions()) { action->setEnabled(false); } } } // search for a canvasdata object for the current input device foreach(CanvasData *cd, items) { if (cd->inputDevice == device) { switchCanvasData(cd); if (!canvasData->activeTool) { switchTool(KoInteractionTool_ID, false); } return; } } // still here? That means we need to create a new CanvasData instance with the current InputDevice. CanvasData *cd = createCanvasData(canvasData->canvas, device); // switch to new canvas as the active one. QString oldTool = canvasData->activeToolId; items.append(cd); canvasses[cd->canvas] = items; switchCanvasData(cd); q->switchToolRequested(oldTool); } void KoToolManager::Private::registerToolProxy(KoToolProxy *proxy, KoCanvasBase *canvas) { proxies.insert(canvas, proxy); foreach(KoCanvasController *controller, canvasses.keys()) { if (controller->canvas() == canvas) { proxy->priv()->setCanvasController(controller); break; } } } void KoToolManager::Private::switchToolByShortcut(QKeyEvent *event) { QKeySequence item(event->key() | ((Qt::ControlModifier | Qt::AltModifier) & event->modifiers())); if (event->key() == Qt::Key_Space && event->modifiers() == 0) { switchTool(KoPanTool_ID, true); } else if (event->key() == Qt::Key_Escape && event->modifiers() == 0) { switchTool(KoInteractionTool_ID, false); } } // ******** KoToolManager ********** KoToolManager::KoToolManager() : QObject(), d(new Private(this)) { connect(QApplication::instance(), SIGNAL(focusChanged(QWidget*, QWidget*)), this, SLOT(movedFocus(QWidget*, QWidget*))); } KoToolManager::~KoToolManager() { delete d; } -QVector KoToolManager::createToolList() const +QList KoToolManager::toolActionList() const { - QVector answer; + QList answer; answer.reserve(d->tools.count()); foreach(ToolHelper *tool, d->tools) { if (tool->id() == KoCreateShapesTool_ID) continue; // don't show this one. - KoToolButton button; - button.button = tool->createButton(); - button.section = tool->toolType(); - button.priority = tool->priority(); - button.buttonGroupId = tool->uniqueId(); - button.visibilityCode = tool->activationShapeId(); - answer.append(button); + answer.append(tool->toolAction()); } return answer; } void KoToolManager::requestToolActivation(KoCanvasController * controller) { if (d->canvasses.contains(controller)) { QString activeToolId = d->canvasses.value(controller).first()->activeToolId; foreach(ToolHelper * th, d->tools) { if (th->id() == activeToolId) { d->toolActivated(th); break; } } } } KoInputDevice KoToolManager::currentInputDevice() const { return d->inputDevice; } void KoToolManager::registerTools(KActionCollection *ac, KoCanvasController *controller) { Q_ASSERT(controller); Q_ASSERT(ac); d->setup(); if (!d->canvasses.contains(controller)) { return; } // Actions available during the use of individual tools CanvasData *cd = d->canvasses.value(controller).first(); foreach(KoToolBase *tool, cd->allTools) { QHash actions = tool->actions(); QHash::const_iterator action(actions.constBegin()); for (; action != actions.constEnd(); ++action) { if (!ac->action(action.key())) ac->addAction(action.key(), action.value()); } } - // Actions used to switch tools; connect slot to keep button tooltips updated + // Actions used to switch tools via shortcuts foreach(ToolHelper * th, d->tools) { - ToolAction* action = new ToolAction(this, th->id(), th->toolTip(), ac); - action->setShortcut(th->shortcut()); + if (ac->action(th->id())) { + continue; + } + ShortcutToolAction* action = th->createShortcutToolAction(ac); ac->addAction(th->id(), action); - th->setAction(action); - connect(action, SIGNAL(changed()), th, SLOT(actionUpdated())); } } void KoToolManager::addController(KoCanvasController *controller) { Q_ASSERT(controller); if (d->canvasses.keys().contains(controller)) return; d->setup(); d->attachCanvas(controller); connect(controller->proxyObject, SIGNAL(destroyed(QObject*)), this, SLOT(attemptCanvasControllerRemoval(QObject*))); connect(controller->proxyObject, SIGNAL(canvasRemoved(KoCanvasController*)), this, SLOT(detachCanvas(KoCanvasController*))); connect(controller->proxyObject, SIGNAL(canvasSet(KoCanvasController*)), this, SLOT(attachCanvas(KoCanvasController*))); } void KoToolManager::removeCanvasController(KoCanvasController *controller) { Q_ASSERT(controller); disconnect(controller->proxyObject, SIGNAL(canvasRemoved(KoCanvasController*)), this, SLOT(detachCanvas(KoCanvasController*))); disconnect(controller->proxyObject, SIGNAL(canvasSet(KoCanvasController*)), this, SLOT(attachCanvas(KoCanvasController*))); d->detachCanvas(controller); } void KoToolManager::attemptCanvasControllerRemoval(QObject* controller) { KoCanvasControllerProxyObject* controllerActual = qobject_cast(controller); if (controllerActual) { removeCanvasController(controllerActual->canvasController()); } } void KoToolManager::updateShapeControllerBase(KoShapeBasedDocumentBase *shapeController, KoCanvasController *canvasController) { if (!d->canvasses.keys().contains(canvasController)) return; QList canvasses = d->canvasses[canvasController]; foreach(CanvasData *canvas, canvasses) { foreach(KoToolBase *tool, canvas->allTools.values()) { tool->updateShapeController(shapeController); } } } void KoToolManager::switchToolRequested(const QString & id) { Q_ASSERT(d->canvasData); if (!d->canvasData) return; while (!d->canvasData->stack.isEmpty()) // switching means to flush the stack d->canvasData->stack.pop(); d->switchTool(id, false); } void KoToolManager::switchInputDeviceRequested(const KoInputDevice &id) { if (!d->canvasData) return; d->switchInputDevice(id); } void KoToolManager::switchToolTemporaryRequested(const QString &id) { d->switchTool(id, true); } void KoToolManager::switchBackRequested() { if (!d->canvasData) return; if (d->canvasData->stack.isEmpty()) { // default to changing to the interactionTool d->switchTool(KoInteractionTool_ID, false); return; } d->switchTool(d->canvasData->stack.pop(), false); } KoCreateShapesTool * KoToolManager::shapeCreatorTool(KoCanvasBase *canvas) const { Q_ASSERT(canvas); foreach(KoCanvasController *controller, d->canvasses.keys()) { if (controller->canvas() == canvas) { KoCreateShapesTool *createTool = dynamic_cast (d->canvasData->allTools.value(KoCreateShapesTool_ID)); Q_ASSERT(createTool /* ID changed? */); return createTool; } } Q_ASSERT(0); // this should not happen return 0; } KoToolBase *KoToolManager::toolById(KoCanvasBase *canvas, const QString &id) const { Q_ASSERT(canvas); foreach(KoCanvasController *controller, d->canvasses.keys()) { if (controller->canvas() == canvas) return d->canvasData->allTools.value(id); } return 0; } KoCanvasController *KoToolManager::activeCanvasController() const { if (! d->canvasData) return 0; return d->canvasData->canvas; } QString KoToolManager::preferredToolForSelection(const QList &shapes) { QList types; foreach(KoShape *shape, shapes) if (! types.contains(shape->shapeId())) types.append(shape->shapeId()); QString toolType = KoInteractionTool_ID; int prio = INT_MAX; foreach(ToolHelper *helper, d->tools) { if (helper->priority() >= prio) continue; if (helper->toolType() == KoToolFactoryBase::mainToolType()) continue; bool toolWillWork = false; foreach (const QString &type, types) { if (helper->activationShapeId().split(',').contains(type)) { toolWillWork = true; break; } } if (toolWillWork) { toolType = helper->id(); prio = helper->priority(); } } return toolType; } void KoToolManager::injectDeviceEvent(KoInputDeviceHandlerEvent * event) { if (d->canvasData && d->canvasData->canvas->canvas()) { if (static_cast(event->type()) == KoInputDeviceHandlerEvent::ButtonPressed) d->canvasData->activeTool->customPressEvent(event->pointerEvent()); else if (static_cast(event->type()) == KoInputDeviceHandlerEvent::ButtonReleased) d->canvasData->activeTool->customReleaseEvent(event->pointerEvent()); else if (static_cast(event->type()) == KoInputDeviceHandlerEvent::PositionChanged) d->canvasData->activeTool->customMoveEvent(event->pointerEvent()); } } void KoToolManager::addDeferredToolFactory(KoToolFactoryBase *toolFactory) { ToolHelper *tool = new ToolHelper(toolFactory); // make sure all plugins are loaded as otherwise we will not load them d->setup(); d->tools.append(tool); // connect to all tools so we can hear their button-clicks connect(tool, SIGNAL(toolActivated(ToolHelper*)), this, SLOT(toolActivated(ToolHelper*))); // now create tools for all existing canvases foreach(KoCanvasController *controller, d->canvasses.keys()) { // this canvascontroller is unknown, which is weird if (!d->canvasses.contains(controller)) { continue; } // create a tool for all canvasdata objects (i.e., all input devices on this canvas) foreach (CanvasData *cd, d->canvasses[controller]) { QPair toolPair = createTools(controller, tool); if (toolPair.second) { cd->allTools.insert(toolPair.first, toolPair.second); } } // Then create a button for the toolbox for this canvas if (tool->id() == KoCreateShapesTool_ID) { continue; } - KoToolButton button; - button.button = tool->createButton(); - button.section = tool->toolType(); - button.priority = tool->priority(); - button.buttonGroupId = tool->uniqueId(); - button.visibilityCode = tool->activationShapeId(); - - emit addedTool(button, controller); + emit addedTool(tool->toolAction(), controller); } - } QPair KoToolManager::createTools(KoCanvasController *controller, ToolHelper *tool) { // XXX: maybe this method should go into the private class? QHash origHash; if (d->canvasses.contains(controller)) { origHash = d->canvasses.value(controller).first()->allTools; } if (origHash.contains(tool->id())) { return QPair(tool->id(), origHash.value(tool->id())); } kDebug(30006) << "Creating tool" << tool->id() << ". Activated on:" << tool->activationShapeId() << ", prio:" << tool->priority(); KoToolBase *tl = tool->createTool(controller->canvas()); if (tl) { d->uniqueToolIds.insert(tl, tool->uniqueId()); tl->setObjectName(tool->id()); foreach(KAction *action, tl->actions()) { action->setEnabled(false); } } KoZoomTool *zoomTool = dynamic_cast(tl); if (zoomTool) { zoomTool->setCanvasController(controller); } KoPanTool *panTool = dynamic_cast(tl); if (panTool) { panTool->setCanvasController(controller); } return QPair(tool->id(), tl); } KoToolManager* KoToolManager::instance() { K_GLOBAL_STATIC(KoToolManager, s_instance) return s_instance; } QString KoToolManager::activeToolId() const { if (!d->canvasData) return QString(); return d->canvasData->activeToolId; } KoToolManager::Private *KoToolManager::priv() { return d; } #include diff --git a/libs/flake/KoToolManager.h b/libs/flake/KoToolManager.h index 6a90f75cd4..64c4858d63 100644 --- a/libs/flake/KoToolManager.h +++ b/libs/flake/KoToolManager.h @@ -1,310 +1,339 @@ /* This file is part of the KDE project * Copyright (c) 2005-2006 Boudewijn Rempt * Copyright (C) 2006, 2008 Thomas Zander * Copyright (C) 2006 Thorsten Zachmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KO_TOOL_MANAGER #define KO_TOOL_MANAGER #include "KoInputDevice.h" #include "flake_export.h" #include #include class KoCanvasController; class KoShapeBasedDocumentBase; class KoToolFactoryBase; class KoCanvasBase; class KoToolBase; class KoCreateShapesTool; class KActionCollection; class KoShape; -class QToolButton; class KoInputDeviceHandlerEvent; class KoShapeLayer; class ToolHelper; +class KShortcut; class QCursor; -/// Struct for the createToolList return type. -struct KoToolButton { - QToolButton *button; ///< a newly created button. - QString section; ///< The section the button wants to be in. - int priority; ///< Lower number (higher priority) means coming first in the section. - int buttonGroupId; ///< A unique ID for this tool as passed by changedTool(), >= 0 - QString visibilityCode; ///< This button should become visible when we emit this string in toolCodesSelected() -}; +/** + * This class serves as a QAction-like control object for activation of a tool. + * + * It allows to implement a custom UI to control the activation of tools. + * See KoToolBox & KoModeBox in the kowidgets library. + * + * KoToolAction objects are indirectly owned by the KoToolManager singleton + * and live until the end of its lifetime. + */ +class FLAKE_EXPORT KoToolAction : public QObject +{ + Q_OBJECT +public: + // toolHelper takes over ownership, and those live till the end of KoToolManager. + explicit KoToolAction(ToolHelper *toolHelper); + ~KoToolAction(); + +public: + QString id() const; ///< The id of the tool + QString iconText() const; ///< The icontext of the tool + QString toolTip() const; ///< The tooltip of the tool + QString iconName() const; ///< The icon name of the tool + KShortcut shortcut() const; ///< The shortcut to activate the tool -Q_DECLARE_TYPEINFO(KoToolButton, Q_MOVABLE_TYPE); + QString section() const; ///< The section the tool wants to be in. + int priority() const; ///< Lower number (higher priority) means coming first in the section. + int buttonGroupId() const; ///< A unique ID for this tool as passed by changedTool(), >= 0 + QString visibilityCode() const; ///< This tool should become visible when we emit this string in toolCodesSelected() + +public Q_SLOTS: + void trigger(); ///< Request the activation of the tool + +Q_SIGNALS: + void changed(); ///< Emitted when a property changes (shortcut ATM) + +private: + friend class ToolHelper; + class Private; + Private *const d; +}; /** * This class manages the activation and deactivation of tools for * each input device. * * Managing the active tool and switching tool based on various variables. * * The state of the toolbox will be the same for all views in the process so practically * you can say we have one toolbox per application instance (process). Implementation * does not allow one widget to be in more then one view, so we just make sure the toolbox * is hidden in not-in-focus views. * * The ToolManager is a singleton and will manage all views in all applications that * are loaded in this process. This means you will have to register and unregister your view. * When creating your new view you should use a KoCanvasController() and register that * with the ToolManager like this: @code MyGuiWidget::MyGuiWidget() { m_canvasController = new KoCanvasController(this); m_canvasController->setCanvas(m_canvas); KoToolManager::instance()->addControllers(m_canvasController)); } MyGuiWidget::~MyGuiWidget() { KoToolManager::instance()->removeCanvasController(m_canvasController); } @endcode * * For a new view that extends KoView all you need to do is implement KoView::createToolBox() * * KoToolManager also keeps track of the current tool based on a complex set of conditions and heuristics: - there is one active tool per KoCanvasController (and there is one KoCanvasController per view, because this is a class with scrollbars and a zoomlevel and so on) - for every pointing device (determined by the unique id of tablet, or 0 for mice -- you may have more than one mouse attached, but Qt cannot distinquish between them, there is an associated tool. - depending on things like tablet leave/enter proximity, incoming mouse or tablet events and a little timer (that gets stopped when we know what is what), the active pointing device is determined, and the active tool is set accordingly. Nota bene: if you use KoToolManager and register your canvases with it you no longer have to manually implement methods to route mouse, tablet, key or wheel events to the active tool. In fact, it's no longer interesting to you which tool is active; you can safely route the paint event through KoToolProxy::paint(). (The reason the input events are handled completely by the toolmanager and the paint events not is that, generally speaking, it's okay if the tools get the input events first, but you want to paint your shapes or other canvas stuff first and only then paint the tool stuff.) */ class FLAKE_EXPORT KoToolManager : public QObject { Q_OBJECT public: /// Return the toolmanager singleton static KoToolManager* instance(); ~KoToolManager(); /** * Register actions for switching to tools at the actionCollection parameter. * The actions will have the text / shortcut as stated by the toolFactory. * If the application calls this in their KoView extending class they will have all the benefits * from allowing this in the menus and to allow the use to configure the shortcuts used. * @param ac the actionCollection that will be the parent of the actions. * @param controller tools registered with this controller will have all their actions added as well. */ void registerTools(KActionCollection *ac, KoCanvasController *controller); /** * Register a new canvas controller * @param controller the view controller that this toolmanager will manage the tools for */ void addController(KoCanvasController *controller); /** * Remove a set of controllers * When the controller is no longer used it should be removed so all tools can be * deleted and stop eating memory. * @param controller the controller that is removed */ void removeCanvasController(KoCanvasController *controller); /** * Attempt to remove a controller. * This is automatically called when a controller's proxy object is deleted, and * it ensures that the controller is, in fact, removed, even if the creator forgot * to do so. * @param controller the proxy object of the controller to be removed */ Q_SLOT void attemptCanvasControllerRemoval(QObject *controller); /// @return the active canvas controller KoCanvasController *activeCanvasController() const; /** * Connect all the tools for the given canvas to the new shape controller. * * @param shapecontroller the new shape controller * @param canvasController the canvas */ void updateShapeControllerBase(KoShapeBasedDocumentBase *shapeController, KoCanvasController *canvasController); /** * Return the tool that is able to create shapes for this param canvas. * This is typically used by the KoShapeSelector to set which shape to create next. * @param canvas the canvas that is a child of a previously registered controller * who's tool you want. * @see addController() */ KoCreateShapesTool *shapeCreatorTool(KoCanvasBase *canvas) const; /** * Returns the tool for the given tool id. * @param canvas the canvas that is a child of a previously registered controller * who's tool you want. * @see addController() */ KoToolBase *toolById(KoCanvasBase *canvas, const QString &id) const; /// @return the currently active pointing device KoInputDevice currentInputDevice() const; /** * For the list of shapes find out which tool is the highest priorty tool that can handle it. * @returns the toolId for the shapes. * @param shapes a list of shapes, a selection for example, that is used to look for the tool. */ QString preferredToolForSelection(const QList &shapes); /** - * Create a list of buttons to represent all the tools. - * @returns a list of Buttons. - * This is a factory method for buttons and meta information on the button to better display the button. + * Returns the list of toolActions for the current tools. + * @returns lists of toolActions for the current tools. */ - QVector createToolList() const; + QList toolActionList() const; /// Request tool activation for the given canvas controller void requestToolActivation(KoCanvasController *controller); /// Injects an input event from a plugin based input device void injectDeviceEvent(KoInputDeviceHandlerEvent *event); /// Returns the toolId of the currently active tool QString activeToolId() const; class Private; /** * \internal return the private object for the toolmanager. */ KoToolManager::Private *priv(); public Q_SLOTS: /** * Request switching tool * @param id the id of the tool */ void switchToolRequested(const QString &id); /** * Request change input device * @param id the id of the input device */ void switchInputDeviceRequested(const KoInputDevice &id); /** * a new tool has become known to mankind */ void addDeferredToolFactory(KoToolFactoryBase *toolFactory); /** * Request for temporary switching the tools. * This switch can be later reverted with switchBackRequested(). * @param id the id of the tool * * @see switchBackRequested() */ void switchToolTemporaryRequested(const QString &id); /** * Switches back to the original tool after the temporary switch * has been done. It the user changed the tool manually on the way, * then it switches to the interaction tool */ void switchBackRequested(); Q_SIGNALS: /** * Emitted when a new tool was selected or became active. * @param canvas the currently active canvas. * @param uniqueToolId a random but unique code for the new tool. */ void changedTool(KoCanvasController *canvas, int uniqueToolId); /** * Emitted after the selection changed to state which unique shape-types are now * in the selection. * @param canvas the currently active canvas. * @param types a list of string that are the shape types of the selected objects. */ void toolCodesSelected(const QList &types); /** * Emitted after the current layer changed either its properties or to a new layer. * @param canvas the currently active canvas. * @param layer the layer that is selected. */ void currentLayerChanged(const KoCanvasController *canvas, const KoShapeLayer *layer); /** * Every time a new input device gets used by a tool, this event is emitted. * @param device the new input device that the user picked up. */ void inputDeviceChanged(const KoInputDevice &device); /** * Emitted whenever the active canvas changed. * @param canvas the new activated canvas (might be 0) */ void changedCanvas(const KoCanvasBase *canvas); /** * Emitted whenever the active tool changes the status text. * @param statusText the new status text */ void changedStatusText(const QString &statusText); /** * emitted whenever a new tool is dynamically added for the given canvas */ - void addedTool(const KoToolButton &button, KoCanvasController *canvas); + void addedTool(KoToolAction *toolAction, KoCanvasController *canvas); private: KoToolManager(); KoToolManager(const KoToolManager&); KoToolManager operator=(const KoToolManager&); Q_PRIVATE_SLOT(d, void toolActivated(ToolHelper *tool)) Q_PRIVATE_SLOT(d, void detachCanvas(KoCanvasController *controller)) Q_PRIVATE_SLOT(d, void attachCanvas(KoCanvasController *controller)) Q_PRIVATE_SLOT(d, void movedFocus(QWidget *from, QWidget *to)) Q_PRIVATE_SLOT(d, void updateCursor(const QCursor &cursor)) Q_PRIVATE_SLOT(d, void selectionChanged(const QList &shapes)) Q_PRIVATE_SLOT(d, void currentLayerChanged(const KoShapeLayer *layer)) QPair createTools(KoCanvasController *controller, ToolHelper *tool); Private *const d; }; #endif diff --git a/libs/flake/KoToolManager_p.cpp b/libs/flake/KoToolManager_p.cpp index 4782cc9432..6e58de1ec4 100644 --- a/libs/flake/KoToolManager_p.cpp +++ b/libs/flake/KoToolManager_p.cpp @@ -1,161 +1,190 @@ /* This file is part of the KDE project * Copyright (C) 2006 Thomas Zander * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KoToolManager_p.h" #include #include #include #include -#include -#include -#include + static int newUniqueToolHelperId() { static int idCounter = 0; return ++idCounter; } /* ************ ToolHelper ********** * This class wrangles the tool factory, toolbox button and switch-tool action * for a single tool. It assumes the will continue to live once it is created. * (Hiding the toolbox is OK.) */ ToolHelper::ToolHelper(KoToolFactoryBase *tool) : m_toolFactory(tool), m_uniqueId(newUniqueToolHelperId()), - button(0), - action(0) -{ -} - -QToolButton* ToolHelper::createButton() + m_hasCustomShortcut(false), + m_toolAction(0) { - button = new QToolButton(); - button->setObjectName(m_toolFactory->id()); - button->setIcon(KIcon(m_toolFactory->iconName())); - button->setToolTip(buttonToolTip()); - - connect(button, SIGNAL(clicked()), this, SLOT(buttonPressed())); - return button; + // TODO: how to get an existing custom shortcut in the beginning here? + // Once the first ShortcutToolAction is added to the actionCollection, + // it will get any custom shortcut set by the actionCollection and + // by that trigger shortcutToolActionUpdated(). + // But until then shortcut() will report a wrong shortcut and e.g. show + // that in the tooltips of the KoToolBox. } -void ToolHelper::buttonPressed() +KoToolAction *ToolHelper::toolAction() { - emit toolActivated(this); + // create lazily + if (!m_toolAction) { + m_toolAction = new KoToolAction(this); + } + return m_toolAction; } QString ToolHelper::id() const { return m_toolFactory->id(); } QString ToolHelper::activationShapeId() const { return m_toolFactory->activationShapeId(); } -QString ToolHelper::toolTip() const +QString ToolHelper::iconName() const { + return m_toolFactory->iconName(); +} + +QString ToolHelper::text() const +{ + // TODO: add text property to KoToolFactoryBase return m_toolFactory->toolTip(); } -QString ToolHelper::buttonToolTip() const +QString ToolHelper::iconText() const { - return shortcut().isEmpty() ? - i18nc("@info:tooltip", "%1", toolTip()) : - i18nc("@info:tooltip %2 is shortcut", "%1 (%2)", toolTip(), - shortcut().toString()); + // TODO: add text iconText to KoToolFactoryBase + return m_toolFactory->toolTip(); } -void ToolHelper::actionUpdated() +QString ToolHelper::toolTip() const { - if (button) - button->setToolTip(buttonToolTip()); + return m_toolFactory->toolTip(); +} + +void ToolHelper::activate() +{ + emit toolActivated(this); +} + +void ToolHelper::shortcutToolActionUpdated() +{ + ShortcutToolAction *action = static_cast(sender()); + // check if shortcut changed + const KShortcut actionShortcut = action->shortcut(); + const KShortcut currentShortcut = shortcut(); + if (actionShortcut != currentShortcut) { + m_hasCustomShortcut = true; + m_customShortcut = actionShortcut; + if (m_toolAction) { + emit m_toolAction->changed(); + } + // no need to forward the new shortcut to the other ShortcutToolAction objects, + // they are synchronized behind the scenes + // Thus they will also trigger this method, but due to them having + // the same shortcut not result in any further action. + } } KoToolBase *ToolHelper::createTool(KoCanvasBase *canvas) const { KoToolBase *tool = m_toolFactory->createTool(canvas); if (tool) { tool->setToolId(id()); } return tool; } +ShortcutToolAction* ToolHelper::createShortcutToolAction(QObject *parent) +{ + ShortcutToolAction* action = new ShortcutToolAction(id(), text(), parent); + action->setShortcut(shortcut()); + + connect(action, SIGNAL(changed()), SLOT(shortcutToolActionUpdated())); + + return action; +} + QString ToolHelper::toolType() const { return m_toolFactory->toolType(); } int ToolHelper::priority() const { return m_toolFactory->priority(); } KShortcut ToolHelper::shortcut() const { - if (action) { - return action->shortcut(); + if (m_hasCustomShortcut) { + return m_customShortcut; } return m_toolFactory->shortcut(); } -void ToolHelper::setAction(KAction *a) -{ - action = a; -} - // ************ Connector ********** Connector::Connector(KoShapeManager *parent) : QObject(parent), m_shapeManager(parent) { connect(m_shapeManager, SIGNAL(selectionChanged()), this, SLOT(selectionChanged())); } void Connector::selectionChanged() { emit selectionChanged(m_shapeManager->selection()->selectedShapes()); } -// ************ ToolAction ********** -ToolAction::ToolAction(KoToolManager* toolManager, const QString &id, const QString &name, QObject *parent) - : KAction(name, parent), - m_toolManager(toolManager), - m_toolID(id) +// ************ ShortcutToolAction ********** +ShortcutToolAction::ShortcutToolAction(const QString &id, const QString &name, QObject *parent) + : KAction(name, parent) + , m_toolID(id) { connect(this, SIGNAL(triggered(bool)), this, SLOT(actionTriggered())); } -ToolAction::~ToolAction() +ShortcutToolAction::~ShortcutToolAction() { } -void ToolAction::actionTriggered() +void ShortcutToolAction::actionTriggered() { - m_toolManager->switchToolRequested(m_toolID); + // TODO: why not ToolHelper::activate(); and thus a slightly different behaviour? + KoToolManager::instance()->switchToolRequested(m_toolID); } #include diff --git a/libs/flake/KoToolManager_p.h b/libs/flake/KoToolManager_p.h index 8cde1b7c31..71d019b9bf 100644 --- a/libs/flake/KoToolManager_p.h +++ b/libs/flake/KoToolManager_p.h @@ -1,188 +1,194 @@ /* This file is part of the KDE project * Copyright (C) 2006 Thomas Zander * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KO_TOOL_MANAGER_P #define KO_TOOL_MANAGER_P #include #include #include #include #include #include #include "KoInputDevice.h" #include "KoToolManager.h" #include class KoToolFactoryBase; class KoShapeManager; class KoCanvasBase; class KoToolBase; class KoShape; class KoToolManager; class KoCanvasController; class KoShapeLayer; class ToolHelper; class CanvasData; -class QToolButton; class KoToolProxy; class Q_DECL_HIDDEN KoToolManager::Private { public: Private(KoToolManager *qq); ~Private(); void setup(); void connectActiveTool(); void disconnectActiveTool(); void switchTool(KoToolBase *tool, bool temporary); void switchTool(const QString &id, bool temporary); void postSwitchTool(bool temporary); void switchCanvasData(CanvasData *cd); bool eventFilter(QObject *object, QEvent *event); void toolActivated(ToolHelper *tool); void detachCanvas(KoCanvasController *controller); void attachCanvas(KoCanvasController *controller); void movedFocus(QWidget *from, QWidget *to); void updateCursor(const QCursor &cursor); void switchBackRequested(); void selectionChanged(const QList &shapes); void currentLayerChanged(const KoShapeLayer *layer); void updateToolForProxy(); void switchToolTemporaryRequested(const QString &id); CanvasData *createCanvasData(KoCanvasController *controller, const KoInputDevice &device); /** * Request a switch from to the param input device. * This will cause the tool for that device to be selected. */ void switchInputDevice(const KoInputDevice &device); /** * Whenever a new tool proxy class is instantiated, it will use this method to register itself * so the toolManager can update it to the latest active tool. * @param proxy the proxy to register. * @param canvas which canvas the proxy is associated with; whenever a new tool is selected for that canvas, * the proxy gets an update. */ void registerToolProxy(KoToolProxy *proxy, KoCanvasBase *canvas); void switchToolByShortcut(QKeyEvent *event); KoToolManager *q; QList tools; // list of all available tools via their factories. QHash uniqueToolIds; // for the changedTool signal QHash > canvasses; QHash proxies; CanvasData *canvasData; // data about the active canvas. KoInputDevice inputDevice; bool layerExplicitlyDisabled; }; +class ShortcutToolAction; + /// \internal class ToolHelper : public QObject { Q_OBJECT public: explicit ToolHelper(KoToolFactoryBase *tool); - QToolButton *createButton(); + KoToolAction *toolAction(); /// wrapper around KoToolFactoryBase::id(); QString id() const; - /// wrapper around KoToolFactoryBase::toolTip(); + /// wrapper around KoToolFactoryBase::iconName(); + QString iconName() const; + /// descriptive text, as ; + QString text() const; + /// descriptive icon text, e.g. use on a button next to an icon or without one; + QString iconText() const; + /// tooltip of the tool, e.g. for tooltip of a button; QString toolTip() const; /// wrapper around KoToolFactoryBase::toolType(); QString toolType() const; /// wrapper around KoToolFactoryBase::activationShapeId(); QString activationShapeId() const; /// wrapper around KoToolFactoryBase::priority(); int priority() const; KoToolBase *createTool(KoCanvasBase *canvas) const; + ShortcutToolAction *createShortcutToolAction(QObject *parent); /// unique id, >= 0 int uniqueId() const { return m_uniqueId; } /// KAction->shortcut() if it exists, otherwise KoToolFactoryBase::shortcut() KShortcut shortcut() const; - /// Writes a tooltip for a button, appending the keyboard shortcut if we have one - QString buttonToolTip() const; - /// Associate an action with this tool - void setAction(KAction *a); + +public Q_SLOTS: + void activate(); Q_SIGNALS: - /// Emitted when the generated toolbox button is pressed. + /// Emitted when the tool should be activated, e.g. by pressing the tool's assigned button in the toolbox void toolActivated(ToolHelper *tool); private Q_SLOTS: - void buttonPressed(); - void actionUpdated(); + void shortcutToolActionUpdated(); private: - KoToolFactoryBase *m_toolFactory; + KoToolFactoryBase * const m_toolFactory; const int m_uniqueId; - QToolButton *button; - KAction *action; + KShortcut m_customShortcut; + bool m_hasCustomShortcut; + KoToolAction *m_toolAction; }; /// \internal /// Helper class to transform a simple signal selection changed into a signal with a parameter class Connector : public QObject { Q_OBJECT public: explicit Connector(KoShapeManager *parent); public Q_SLOTS: void selectionChanged(); Q_SIGNALS: void selectionChanged(const QList &shape); private: KoShapeManager *m_shapeManager; }; /// \internal /// Helper class to provide a action for tool shortcuts -class ToolAction : public KAction +class ShortcutToolAction : public KAction { Q_OBJECT public: - ToolAction(KoToolManager* toolManager, const QString &id, const QString &name, QObject *parent); - virtual ~ToolAction(); + ShortcutToolAction(const QString &id, const QString &name, QObject *parent); + virtual ~ShortcutToolAction(); private Q_SLOTS: void actionTriggered(); private: - KoToolManager* m_toolManager; QString m_toolID; }; #endif diff --git a/libs/widgets/CMakeLists.txt b/libs/widgets/CMakeLists.txt index 31578b6864..c9a84e70e4 100644 --- a/libs/widgets/CMakeLists.txt +++ b/libs/widgets/CMakeLists.txt @@ -1,172 +1,173 @@ add_subdirectory( tests ) add_subdirectory( pics ) include_directories(${KOTEXT_INCLUDES} ${KOODF_INCLUDES} ${PIGMENT_INCLUDES}) include_directories(${CMAKE_SOURCE_DIR}/libs/widgetutils) if (LIBATTICA_FOUND) include_directories(${LIBATTICA_INCLUDE_DIR}) endif () set(kowidgets_LIB_SRCS KoGlobal.cpp KoZoomWidget.cpp KoTagToolButton.cpp KoTagChooserWidget.cpp KoTagFilterWidget.cpp KoResourceTaggingManager.cpp KoResourceItemChooserContextMenu.cpp KoAspectButton.cpp KoCsvImportDialog.cpp KoPageLayoutDialog.cpp KoPageLayoutWidget.cpp KoPagePreviewWidget.cpp KoPositionSelector.cpp KoSliderCombo.cpp KoColorPopupButton.cpp KoConfigAuthorPage.cpp KoUnitDoubleSpinBox.cpp KoZoomAction.cpp KoZoomController.cpp KoZoomInput.cpp KoZoomHandler.cpp KoZoomMode.cpp KoDpi.cpp KoGlobal.cpp KoFileDialog.cpp KoColorPatch.cpp KoColorPopupAction.cpp KoColorSetWidget.cpp KoColorSlider.cpp KoDualColorButton.cpp KoEditColorSetDialog.cpp KoTriangleColorSelector.cpp KoResourcePopupAction.cpp KoStrokeConfigWidget.cpp KoFillConfigWidget.cpp KoShadowConfigWidget.cpp KoIconToolTip.cpp KoResourceItemChooser.cpp KoResourceItemChooserSync.cpp KoResourceSelector.cpp KoResourceModel.cpp KoResourceItemDelegate.cpp KoResourceItemView.cpp KoResourceTagStore.cpp KoRuler.cpp KoRulerController.cpp KoItemToolTip.cpp KoCheckerBoardPainter.cpp KoResourceServerAdapter.cpp KoResourceServerProvider.cpp KoLineStyleSelector.cpp KoLineStyleItemDelegate.cpp KoLineStyleModel.cpp KoMarkerModel.cpp KoMarkerItemDelegate.cpp KoMarkerSelector.cpp KoDockWidgetTitleBar.cpp KoDockWidgetTitleBarButton.cpp KoViewItemContextBar.cpp KoContextBarButton.cpp KoResourceFiltering.cpp KoResourceModelBase.cpp + KoToolBoxButton.cpp KoToolBox.cpp KoToolBoxDocker.cpp KoToolBoxFactory.cpp KoToolDocker.cpp KoModeBox.cpp KoModeBoxDocker.cpp KoModeBoxFactory.cpp KoDocumentInfoDlg.cpp KoDocumentInfoPropsPage.cpp KoGlobal.cpp KoTableView.cpp ) kde4_add_ui_files( kowidgets_LIB_SRCS KoConfigAuthorPage.ui KoCsvImportDialog.ui koDocumentInfoAboutWidget.ui koDocumentInfoAuthorWidget.ui KoEditColorSet.ui KoPageLayoutWidget.ui KoShadowConfigWidget.ui ) kde4_add_library(kowidgets SHARED ${kowidgets_LIB_SRCS}) target_link_libraries(kowidgets kotext pigmentcms kowidgetutils ${KDE4_KIO_LIBS}) if(GHNS) target_link_libraries(kowidgets {KDE4_KNEWSTUFF3_LIBS}) endif () target_link_libraries(kowidgets LINK_INTERFACE_LIBRARIES kotext pigmentcms kowidgetutils ${KDE4_KDEUI_LIBS}) set_target_properties(kowidgets PROPERTIES VERSION ${GENERIC_CALLIGRA_LIB_VERSION} SOVERSION ${GENERIC_CALLIGRA_LIB_SOVERSION} ) install(TARGETS kowidgets ${INSTALL_TARGETS_DEFAULT_ARGS}) install( FILES KoGlobal.h KoResourceItemChooserContextMenu.h KoGenericRegistryModel.h KoPageLayoutDialog.h KoPageLayoutWidget.h KoPagePreviewWidget.h KoPositionSelector.h kowidgets_export.h KoZoomAction.h KoZoomController.h KoZoomInput.h KoDpi.h KoZoomHandler.h KoZoomMode.h KoGlobal.h KoFileDialog.h KoColorPatch.h KoStrokeConfigWidget.h KoFillConfigWidget.h KoShadowConfigWidget.h KoColorPopupAction.h KoColorSetWidget.h KoColorSlider.h KoDualColorButton.h KoEditColorSetDialog.h KoTriangleColorSelector.h KoResourceItemChooser.h KoResourceSelector.h KoResourceServer.h KoResourceServerAdapter.h KoResourceServerObserver.h KoResourceServerProvider.h KoResourceTagStore.h KoLineStyleSelector.h KoDockWidgetTitleBar.h KoDockWidgetTitleBarButton.h KoResourceModelBase.h KoGlobal.h DESTINATION ${INCLUDE_INSTALL_DIR}/calligra COMPONENT Devel) set(filedialogtester_SRCS KoFileDialogTester.cpp main.cpp ) kde4_add_ui_files(filedialogtester_SRCS KoFileDialogTester.ui ) kde4_add_executable(filedialogtester ${filedialogtester_SRCS}) target_link_libraries(filedialogtester kowidgets) diff --git a/libs/widgets/KoModeBox.cpp b/libs/widgets/KoModeBox.cpp index 83b3973776..a7e1b50619 100644 --- a/libs/widgets/KoModeBox.cpp +++ b/libs/widgets/KoModeBox.cpp @@ -1,673 +1,671 @@ /* * Copyright (c) 2005-2009 Thomas Zander * Copyright (c) 2009 Peter Simonsson * Copyright (c) 2010 Cyrille Berger * Copyright (c) 2011 C. Boemann * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KoModeBox_p.h" #include #include #include #include #include #include #include #include #include #include #include #include -#include #include #include #include #include #include #include #include #include #include #include #include #include #include class KoModeBox::Private { public: Private(KoCanvasController *c) : canvas(c->canvas()) , activeId(-1) , iconTextFitted(true) , fittingIterations(0) , iconMode(IconAndText) , verticalTabsSide(TopSide) , horizontalTabsSide(LeftSide) , horizontalMode(false) { } KoCanvasBase *canvas; QGridLayout *layout; - QList buttons; // buttons maintained by toolmanager - QList addedButtons; //buttons in the order added to QToolBox + QList toolActions; // toolActions maintained by toolmanager + QList addedToolActions; //tools in the order added to QToolBox QMap addedWidgets; QSet currentAuxWidgets; int activeId; QTabBar *tabBar; QStackedWidget *stack; bool iconTextFitted; int fittingIterations; IconMode iconMode; VerticalTabsSide verticalTabsSide; HorizontalTabsSide horizontalTabsSide; bool horizontalMode; }; QString KoModeBox::applicationName; -static bool compareButton(const KoToolButton &b1, const KoToolButton &b2) +static bool compareToolActions(const KoToolAction *b1, const KoToolAction *b2) { int b1Level; int b2Level; - if (b1.section.contains(KoModeBox::applicationName)) { + if (b1->section().contains(KoModeBox::applicationName)) { b1Level = 0; - } else if (b1.section.contains("main")) { + } else if (b1->section().contains("main")) { b1Level = 1; } else { b1Level = 2; } - if (b2.section.contains(KoModeBox::applicationName)) { + if (b2->section().contains(KoModeBox::applicationName)) { b2Level = 0; - } else if (b2.section.contains("main")) { + } else if (b2->section().contains("main")) { b2Level = 1; } else { b2Level = 2; } if (b1Level == b2Level) { - return b1.priority < b2.priority; + return b1->priority() < b2->priority(); } else { return b1Level < b2Level; } } KoModeBox::KoModeBox(KoCanvasControllerWidget *canvas, const QString &appName) : QWidget() , d(new Private(canvas)) { applicationName = appName; KConfigGroup cfg = KGlobal::config()->group("calligra"); d->iconMode = (IconMode)cfg.readEntry("ModeBoxIconMode", (int)IconAndText); d->verticalTabsSide = (VerticalTabsSide)cfg.readEntry("ModeBoxVerticalTabsSide", (int)TopSide); d->horizontalTabsSide = (HorizontalTabsSide)cfg.readEntry("ModeBoxHorizontalTabsSide", (int)LeftSide); d->layout = new QGridLayout(); d->stack = new QStackedWidget(); d->tabBar = new QTabBar(); d->tabBar->setExpanding(false); if (d->iconMode == IconAndText) { if (d->horizontalMode) { d->tabBar->setIconSize(QSize(38,32)); } else { d->tabBar->setIconSize(QSize(32,64)); } } else { d->tabBar->setIconSize(QSize(22,22)); } d->tabBar->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); if (d->horizontalMode) { switchTabsSide(d->verticalTabsSide); } else { switchTabsSide(d->horizontalTabsSide); } d->layout->addWidget(d->stack, 0, 1); d->layout->setContentsMargins(0,0,0,0); setLayout(d->layout); - foreach(const KoToolButton &button, KoToolManager::instance()->createToolList()) { - addButton(button); + foreach(KoToolAction *toolAction, KoToolManager::instance()->toolActionList()) { + addToolAction(toolAction); } - qSort(d->buttons.begin(), d->buttons.end(), compareButton); + qSort(d->toolActions.begin(), d->toolActions.end(), compareToolActions); - // Update visibility of buttons + // Update visibility of toolActions updateShownTools(QList()); d->tabBar->setContextMenuPolicy(Qt::CustomContextMenu); connect(d->tabBar, SIGNAL(currentChanged(int)), this, SLOT(toolSelected(int))); connect(d->tabBar, SIGNAL(customContextMenuRequested(QPoint)), SLOT(slotContextMenuRequested(QPoint))); connect(KoToolManager::instance(), SIGNAL(changedTool(KoCanvasController *, int)), this, SLOT(setActiveTool(KoCanvasController *, int))); connect(KoToolManager::instance(), SIGNAL(currentLayerChanged(const KoCanvasController *,const KoShapeLayer*)), this, SLOT(setCurrentLayer(const KoCanvasController *,const KoShapeLayer *))); connect(KoToolManager::instance(), SIGNAL(toolCodesSelected(QList)), this, SLOT(updateShownTools(QList))); connect(KoToolManager::instance(), - SIGNAL(addedTool(KoToolButton,KoCanvasController*)), - this, SLOT(toolAdded(KoToolButton,KoCanvasController*))); + SIGNAL(addedTool(KoToolAction*,KoCanvasController*)), + this, SLOT(toolAdded(KoToolAction*,KoCanvasController*))); connect(canvas, SIGNAL(toolOptionWidgetsChanged(const QList > &)), this, SLOT(setOptionWidgets(const QList > &))); } KoModeBox::~KoModeBox() { delete d; } -void KoModeBox::addButton(const KoToolButton &button) +void KoModeBox::addToolAction(KoToolAction *toolAction) { - d->buttons.append(button); - button.button->setVisible(false); + d->toolActions.append(toolAction); } void KoModeBox::locationChanged(Qt::DockWidgetArea area) { resize(0,0); switch(area) { case Qt::TopDockWidgetArea: case Qt::BottomDockWidgetArea: d->horizontalMode = true; d->layout->removeWidget(d->stack); d->layout->addWidget(d->stack, 1, 0); d->layout->setColumnStretch(1, 0); d->layout->setRowStretch(1, 100); break; case Qt::LeftDockWidgetArea: case Qt::RightDockWidgetArea: d->horizontalMode = false; d->layout->removeWidget(d->stack); d->layout->addWidget(d->stack, 0, 1); d->layout->setColumnStretch(1, 100); d->layout->setRowStretch(1, 0); break; default: break; } d->layout->setSizeConstraint(QLayout::SetMinAndMaxSize); d->layout->invalidate(); if (d->iconMode == IconAndText) { if (d->horizontalMode) { d->tabBar->setIconSize(QSize(42,32)); } else { d->tabBar->setIconSize(QSize(32,64)); } } else { d->tabBar->setIconSize(QSize(22,22)); } if (d->horizontalMode) { switchTabsSide(d->verticalTabsSide); } else { switchTabsSide(d->horizontalTabsSide); } } void KoModeBox::setActiveTool(KoCanvasController *canvas, int id) { if (canvas->canvas() == d->canvas) { // Clear the minimumSize instigated by the previous tool // The new minimumSize will be set in updateShownTools() if (d->addedWidgets.contains(d->activeId)) { ScrollArea *sa = qobject_cast(d->addedWidgets[d->activeId]->parentWidget()->parentWidget()); sa->setMinimumWidth(0); sa->setMinimumHeight(0); } d->activeId = id; d->tabBar->blockSignals(true); int i = 0; - foreach (const KoToolButton &button, d->addedButtons) { - if (button.buttonGroupId == d->activeId) { + foreach (KoToolAction *toolAction, d->addedToolActions) { + if (toolAction->buttonGroupId() == d->activeId) { d->tabBar->setCurrentIndex(i); d->stack->setCurrentIndex(i); break; } ++i; } d->tabBar->blockSignals(false); return; } } -QIcon KoModeBox::createTextIcon(const KoToolButton &button) const +QIcon KoModeBox::createTextIcon(KoToolAction *toolAction) const { QSize iconSize = d->tabBar->iconSize(); QFont smallFont = KGlobalSettings::generalFont(); qreal pointSize = KGlobalSettings::smallestReadableFont().pointSizeF(); smallFont.setPointSizeF(pointSize); // This must be a QImage, as drawing to a QPixmap outside the // UI thread will cause sporadic crashes. QImage pm(iconSize, QImage::Format_ARGB32_Premultiplied); pm.fill(Qt::transparent); QPainter p(&pm); if (!d->horizontalMode) { if (d->horizontalTabsSide == LeftSide ) { p.rotate(90); p.translate(0,-iconSize.width()); } else { p.rotate(-90); p.translate(-iconSize.height(),0); } } - button.button->icon().paint(&p, 0, 0, iconSize.height(), 22); + KIcon(toolAction->iconName()).paint(&p, 0, 0, iconSize.height(), 22); - QTextLayout textLayout(button.button->toolTip(), smallFont, p.device()); + QTextLayout textLayout(toolAction->iconText(), smallFont, p.device()); QTextOption option; if (d->horizontalMode) { option = QTextOption(Qt::AlignVCenter | Qt::AlignHCenter); } else { option = QTextOption(Qt::AlignTop | Qt::AlignHCenter); } option.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); textLayout.setTextOption(option); textLayout.beginLayout(); qreal height = 0; while (1) { QTextLine line = textLayout.createLine(); if (!line.isValid()) break; line.setLineWidth(iconSize.height()); line.setPosition(QPointF(0, height)); height += line.height(); } textLayout.endLayout(); if (textLayout.lineCount() > 2) { iconSize.setHeight(iconSize.height() + 8); d->tabBar->setIconSize(iconSize); d->iconTextFitted = false; } else if (height > iconSize.width() - 22) { iconSize.setWidth(22 + height); d->tabBar->setIconSize(iconSize); d->iconTextFitted = false; } p.setFont(smallFont); p.setPen(palette().text().color()); textLayout.draw(&p, QPoint(0, 22)); p.end(); return QIcon(QPixmap::fromImage(pm)); } -QIcon KoModeBox::createSimpleIcon(const KoToolButton &button) const +QIcon KoModeBox::createSimpleIcon(KoToolAction *toolAction) const { QSize iconSize = d->tabBar->iconSize(); // This must be a QImage, as drawing to a QPixmap outside the // UI thread will cause sporadic crashes. QImage pm(iconSize, QImage::Format_ARGB32_Premultiplied); pm.fill(Qt::transparent); QPainter p(&pm); if (!d->horizontalMode) { if (d->horizontalTabsSide == LeftSide ) { p.rotate(90); p.translate(0,-iconSize.width()); } else { p.rotate(-90); p.translate(-iconSize.height(),0); } } - button.button->icon().paint(&p, 0, 0, iconSize.height(), iconSize.width()); + KIcon(toolAction->iconName()).paint(&p, 0, 0, iconSize.height(), iconSize.width()); return QIcon(QPixmap::fromImage(pm)); } -void KoModeBox::addItem(const KoToolButton &button) +void KoModeBox::addItem(KoToolAction *toolAction) { - QWidget *oldwidget = d->addedWidgets[button.buttonGroupId]; + QWidget *oldwidget = d->addedWidgets[toolAction->buttonGroupId()]; QWidget *widget; // We need to create a new widget in all cases as QToolBox seeems to crash if we reuse // a widget (even though the item had been removed) QLayout *layout; if (!oldwidget) { layout = new QGridLayout(); } else { layout = oldwidget->layout(); } widget = new QWidget(); widget->setLayout(layout); layout->setContentsMargins(0,0,0,0); layout->setSizeConstraint(QLayout::SetMinAndMaxSize); - d->addedWidgets[button.buttonGroupId] = widget; + d->addedWidgets[toolAction->buttonGroupId()] = widget; // Create a rotated icon with text if (d->iconMode == IconAndText) { - d->tabBar->addTab(createTextIcon(button), QString()); + d->tabBar->addTab(createTextIcon(toolAction), QString()); } else { - int index = d->tabBar->addTab(createSimpleIcon(button), QString()); - d->tabBar->setTabToolTip(index, button.button->toolTip()); + int index = d->tabBar->addTab(createSimpleIcon(toolAction), QString()); + d->tabBar->setTabToolTip(index, toolAction->toolTip()); } d->tabBar->blockSignals(false); ScrollArea *sa = new ScrollArea(); if (d->horizontalMode) { sa->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); sa->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); } else { sa->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); sa->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); } sa->setWidgetResizable(true); sa->setContentsMargins(0,0,0,0); sa->setWidget(widget); sa->setFrameShape(QFrame::NoFrame); sa->setFocusPolicy(Qt::NoFocus); d->stack->addWidget(sa); - d->addedButtons.append(button); + d->addedToolActions.append(toolAction); } void KoModeBox::updateShownTools(const QList &codes) { if (d->iconTextFitted) { d->fittingIterations = 0; } d->iconTextFitted = true; d->tabBar->blockSignals(true); while (d->tabBar->count()) { d->tabBar->removeTab(0); d->stack->removeWidget(d->stack->widget(0)); } - d->addedButtons.clear(); + d->addedToolActions.clear(); int newIndex = -1; - foreach (const KoToolButton &button, d->buttons) { - QString toolCodes = button.visibilityCode; - if (button.buttonGroupId == d->activeId) { - newIndex = d->addedButtons.length(); + foreach (KoToolAction *toolAction, d->toolActions) { + const QString toolCodes = toolAction->visibilityCode(); + if (toolAction->buttonGroupId() == d->activeId) { + newIndex = d->addedToolActions.length(); } - if (button.section.contains(applicationName)) { - addItem(button); + if (toolAction->section().contains(applicationName)) { + addItem(toolAction); continue; - } else if (!button.section.contains("dynamic") - && !button.section.contains("main")) { + } else if (!toolAction->section().contains("dynamic") + && !toolAction->section().contains("main")) { continue; } if (toolCodes.startsWith(QLatin1String("flake/"))) { - addItem(button); + addItem(toolAction); continue; } if (toolCodes.endsWith( QLatin1String( "/always"))) { - addItem(button); + addItem(toolAction); continue; } else if (toolCodes.isEmpty() && codes.count() != 0) { - addItem(button); + addItem(toolAction); continue; } else { foreach (const QString &shapeCode, codes) { if (toolCodes.contains(shapeCode)) { - addItem(button); + addItem(toolAction); break; } } } } if (newIndex != -1) { d->tabBar->setCurrentIndex(newIndex); d->stack->setCurrentIndex(newIndex); } d->tabBar->blockSignals(false); if (!d->iconTextFitted && d->fittingIterations++ < 8) { updateShownTools(codes); } d->iconTextFitted = true; } void KoModeBox::setOptionWidgets(const QList > &optionWidgetList) { if (! d->addedWidgets.contains(d->activeId)) return; // For some reason we need to set some attr on our placeholder widget here // eventhough these settings should be default // Otherwise Sheets' celltool's optionwidget looks ugly d->addedWidgets[d->activeId]->setAutoFillBackground(false); d->addedWidgets[d->activeId]->setBackgroundRole(QPalette::NoRole); qDeleteAll(d->currentAuxWidgets); d->currentAuxWidgets.clear(); int cnt = 0; QGridLayout *layout = (QGridLayout *)d->addedWidgets[d->activeId]->layout(); // need to unstretch row/column that have previously been stretched layout->setRowStretch(layout->rowCount()-1, 0); layout->setRowStretch(layout->columnCount()-1, 0); layout->setColumnStretch(0, 0); layout->setColumnStretch(1, 0); layout->setColumnStretch(2, 0); layout->setRowStretch(0, 0); layout->setRowStretch(1, 0); layout->setRowStretch(2, 0); if (d->horizontalMode) { layout->setRowStretch(0, 1); layout->setRowStretch(1, 2); layout->setRowStretch(2, 1); layout->setHorizontalSpacing(2); layout->setVerticalSpacing(0); foreach(QWidget *widget, optionWidgetList) { if (!widget->windowTitle().isEmpty()) { QLabel *l; layout->addWidget(l = new QLabel(widget->windowTitle()), 0, cnt, 1, 1, Qt::AlignLeft); d->currentAuxWidgets.insert(l); } layout->addWidget(widget, 1, cnt++, 2, 1); widget->show(); if (widget != optionWidgetList.last()) { QFrame *s; layout->addWidget(s = new QFrame(), 1, cnt, 1, 1, Qt::AlignHCenter); layout->setColumnMinimumWidth(cnt++, 16); s->setFrameStyle(QFrame::VLine | QFrame::Sunken); d->currentAuxWidgets.insert(s); ++cnt; } layout->setColumnStretch(cnt, 100); } } else { layout->setColumnStretch(0, 1); layout->setColumnStretch(1, 2); layout->setColumnStretch(2, 1); layout->setHorizontalSpacing(0); layout->setVerticalSpacing(2); int specialCount = 0; foreach(QWidget *widget, optionWidgetList) { if (!widget->windowTitle().isEmpty()) { QLabel *l; layout->addWidget(l = new QLabel(widget->windowTitle()), cnt++, 0, 1, 3, Qt::AlignHCenter); d->currentAuxWidgets.insert(l); } layout->addWidget(widget, cnt++, 0, 1, 3); QLayout *subLayout = widget->layout(); if (subLayout) { for (int i = 0; i < subLayout->count(); ++i) { QWidget *spacerWidget = subLayout->itemAt(i)->widget(); if (spacerWidget && spacerWidget->objectName().contains("SpecialSpacer")) { specialCount++; } } } widget->show(); if (widget != optionWidgetList.last()) { QFrame *s; layout->addWidget(s = new QFrame(), cnt, 1, 1, 1); layout->setRowMinimumHeight(cnt++, 16); s->setFrameStyle(QFrame::HLine | QFrame::Sunken); d->currentAuxWidgets.insert(s); } } if (specialCount == optionWidgetList.count()) { layout->setRowStretch(cnt, 100); } } } void ScrollArea::showEvent(QShowEvent *e) { QScrollArea::showEvent(e); if (horizontalScrollBarPolicy() == Qt::ScrollBarAlwaysOff) { setMinimumWidth(widget()->minimumSizeHint().width() + (verticalScrollBar()->isVisible() ? verticalScrollBar()->width() : 0)); } else { setMinimumHeight(widget()->minimumSizeHint().height() + (horizontalScrollBar()->isVisible() ? horizontalScrollBar()->height() : 0)); } } void KoModeBox::setCurrentLayer(const KoCanvasController *canvas, const KoShapeLayer *layer) { Q_UNUSED(canvas); Q_UNUSED(layer); //Since tageted application don't use this we won't bother implemeting } void KoModeBox::setCanvas(KoCanvasBase *canvas) { KoCanvasControllerWidget *ccwidget; if (d->canvas) { ccwidget = dynamic_cast(d->canvas->canvasController()); disconnect(ccwidget, SIGNAL(toolOptionWidgetsChanged(const QList > &)), this, SLOT(setOptionWidgets(const QList > &))); } d->canvas = canvas; ccwidget = dynamic_cast(d->canvas->canvasController()); connect( ccwidget, SIGNAL(toolOptionWidgetsChanged(const QList > &)), this, SLOT(setOptionWidgets(const QList > &))); } void KoModeBox::unsetCanvas() { d->canvas = 0; } -void KoModeBox::toolAdded(const KoToolButton &button, KoCanvasController *canvas) +void KoModeBox::toolAdded(KoToolAction *toolAction, KoCanvasController *canvas) { if (canvas->canvas() == d->canvas) { - addButton(button); + addToolAction(toolAction); - qStableSort(d->buttons.begin(), d->buttons.end(), compareButton); + qStableSort(d->toolActions.begin(), d->toolActions.end(), compareToolActions); updateShownTools(QList()); } } void KoModeBox::toolSelected(int index) { if (index != -1) { - d->addedButtons[index].button->click(); + d->addedToolActions.at(index)->trigger(); } } void KoModeBox::slotContextMenuRequested(const QPoint &pos) { QMenu menu; KSelectAction* textAction = new KSelectAction(i18n("Text"), &menu); connect(textAction, SIGNAL(triggered(int)), SLOT(switchIconMode(int))); menu.addAction(textAction); textAction->addAction(i18n("Icon and Text")); textAction->addAction(i18n("Icon only")); textAction->setCurrentItem(d->iconMode); KSelectAction* buttonPositionAction = new KSelectAction(i18n("Tabs side"), &menu); connect(buttonPositionAction, SIGNAL(triggered(int)), SLOT(switchTabsSide(int))); menu.addAction(buttonPositionAction); if (d->horizontalMode) { buttonPositionAction->addAction(i18n("Top side")); buttonPositionAction->addAction(i18n("Bottom side")); buttonPositionAction->setCurrentItem(d->verticalTabsSide); } else { buttonPositionAction->addAction(i18n("Left side")); buttonPositionAction->addAction(i18n("Right side")); buttonPositionAction->setCurrentItem(d->horizontalTabsSide); } menu.exec(d->tabBar->mapToGlobal(pos)); } void KoModeBox::switchIconMode(int mode) { d->iconMode = static_cast(mode); if (d->iconMode == IconAndText) { if (d->horizontalMode) { d->tabBar->setIconSize(QSize(38,32)); } else { d->tabBar->setIconSize(QSize(32,64)); } } else { d->tabBar->setIconSize(QSize(22,22)); } updateShownTools(QList()); KConfigGroup cfg = KGlobal::config()->group("calligra"); cfg.writeEntry("ModeBoxIconMode", (int)d->iconMode); } void KoModeBox::switchTabsSide(int side) { if (d->horizontalMode) { d->verticalTabsSide = static_cast(side); if (d->verticalTabsSide == TopSide) { d->layout->removeWidget(d->tabBar); d->tabBar->setShape(QTabBar::RoundedNorth); d->layout->addWidget(d->tabBar, 0, 0); } else { d->layout->removeWidget(d->tabBar); d->tabBar->setShape(QTabBar::RoundedSouth); d->layout->addWidget(d->tabBar, 2, 0); } KConfigGroup cfg = KGlobal::config()->group("calligra"); cfg.writeEntry("ModeBoxVerticalTabsSide", (int)d->verticalTabsSide); } else { d->horizontalTabsSide = static_cast(side); if (d->horizontalTabsSide == LeftSide) { d->layout->removeWidget(d->tabBar); d->tabBar->setShape(QTabBar::RoundedWest); d->layout->addWidget(d->tabBar, 0, 0); } else { d->layout->removeWidget(d->tabBar); d->tabBar->setShape(QTabBar::RoundedEast); d->layout->addWidget(d->tabBar, 0, 2); } KConfigGroup cfg = KGlobal::config()->group("calligra"); cfg.writeEntry("ModeBoxHorizontalTabsSide", (int)d->horizontalTabsSide); } updateShownTools(QList()); } diff --git a/libs/widgets/KoModeBox_p.h b/libs/widgets/KoModeBox_p.h index 718c9e0d01..cc7dcd8d2e 100644 --- a/libs/widgets/KoModeBox_p.h +++ b/libs/widgets/KoModeBox_p.h @@ -1,160 +1,152 @@ /* * Copyright (c) 2005 Boudewijn Rempt * Copyright (c) 2005-2008 Thomas Zander * Copyright (c) 2009 Peter Simonsson * Copyright (c) 2010 Cyrille Berger * Copyright (c) 2011 C. Boemann * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KO_MODEBOX_H #define KO_MODEBOX_H #include #include #include #include #include #include #include #include class KoCanvasControllerWidget; class KoCanvasController; class KoCanvasBase; class KoShapeLayer; class ScrollArea : public QScrollArea { Q_OBJECT protected: void showEvent(QShowEvent *); }; /** * KoModeBox is housed in a dock widget that presents tools as headings in a QToolBox * according to type and priority. * * The ModeBox is a container for tool buttons which are themselves * divided into sections. * - * Adding buttons using addButton() will allow you to show those buttons. You should connect - * the button to your handling method yourself. - * * @see KoToolManager */ class KoModeBox : public QWidget { Q_OBJECT public: /// constructor explicit KoModeBox(KoCanvasControllerWidget *canvas, const QString &applicationName); ~KoModeBox(); - /** - * Add a button to the modebox. - * The buttons should all be added before the first showing since adding will not really add - * them to the UI until setup() is called. - * - * @param button the new button. Please make sure you connect to the button yourself. - * @param section the section in which this button will be shown. Each section will be its own - * widget. - * @param priority the priority in the section. Lowest value means it will be shown first. - * @param buttonGroupId if passed this will allow you to use setActiveTool() to trigger - * this button - * @see setup() - */ - void addButton(const KoToolButton &button); - /** * Should been called when the docker position has changed. * Organise widgets and icons and orientation of the tabs. * * @param area the new location area */ void locationChanged(Qt::DockWidgetArea area); public Q_SLOTS: /** * Using the buttongroup id passed in addButton() you can set the new active tool. * If the id does not resolve to a visible heading, this call is ignored. * @param canvas the currently active canvas. * @param id an id to identify the tool/heading to activate. */ void setActiveTool(KoCanvasController *canvas, int id); /** * Show only the dynamic buttons that have a code from parameter codes. * The modebox allows buttons to be optionally registered with a visibilityCode. This code * can be passed here and all buttons that have that code are shown. All buttons that * have another visibility code registered are hidden. * @param codes a list of all the codes to show. */ void updateShownTools(const QList &codes); void setOptionWidgets(const QList > &optionWidgetList); /// set the canvas this docker should listen to for changes. void setCanvas(KoCanvasBase *canvas); void unsetCanvas(); private Q_SLOTS: void setCurrentLayer(const KoCanvasController *canvas, const KoShapeLayer* newLayer); /// add a tool post-initialization. The tool will also be activated. - void toolAdded(const KoToolButton &button, KoCanvasController *canvas); + void toolAdded(KoToolAction *toolAction, KoCanvasController *canvas); /// slot for when a new item have been selected in the QToolBox void toolSelected(int index); /// slot for context menu of the tabbar void slotContextMenuRequested(const QPoint &pos); /// switch icon mode void switchIconMode(int); /// switch tabs side void switchTabsSide(int); + /** + * Add a tool to the modebox. + * The tools should all be added before the first showing since adding will not really add + * them to the UI until setup() is called. + * + * @param toolAction the action of the tool + * @see setup() + */ + void addToolAction(KoToolAction *toolAction); + public: static QString applicationName; private: enum IconMode { IconAndText, IconOnly }; enum VerticalTabsSide { TopSide, BottomSide }; enum HorizontalTabsSide { LeftSide, RightSide }; - QIcon createTextIcon(const KoToolButton &button) const; - QIcon createSimpleIcon(const KoToolButton &button) const; - void addItem(const KoToolButton &button); + QIcon createTextIcon(KoToolAction *toolAction) const; + QIcon createSimpleIcon(KoToolAction *toolAction) const; + void addItem(KoToolAction *toolAction); private: class Private; Private * const d; }; #endif // _KO_TOOLBOX_H_ diff --git a/libs/widgets/KoToolBox.cpp b/libs/widgets/KoToolBox.cpp index edeb70459f..9fc1eade6d 100644 --- a/libs/widgets/KoToolBox.cpp +++ b/libs/widgets/KoToolBox.cpp @@ -1,359 +1,359 @@ /* * Copyright (c) 2005-2009 Thomas Zander * Copyright (c) 2009 Peter Simonsson * Copyright (c) 2010 Cyrille Berger * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KoToolBox_p.h" #include "KoToolBoxLayout_p.h" +#include "KoToolBoxButton_p.h" #include #include #include #include #include #include #include #include #include #include #include #include -#include #include #include #include #include #define BUTTON_MARGIN 10 static int buttonSize(int screen) { QRect rc = qApp->desktop()->screenGeometry(screen); if (rc.width() <= 1024) { return 12; } else if (rc.width() <= 1377) { return 14; } else if (rc.width() <= 1920 ) { return 16; } else if (rc.width() <= 2048) { return 32; } else { return 48; } } class KoToolBox::Private { public: Private() : layout(0) , buttonGroup(0) , floating(false) , contextSize(0) { } void addSection(Section *section, const QString &name); QList buttons; QMap sections; KoToolBoxLayout *layout; QButtonGroup *buttonGroup; QHash visibilityCodes; bool floating; QMap contextIconSizes; QMenu* contextSize; }; void KoToolBox::Private::addSection(Section *section, const QString &name) { section->setName(name); layout->addSection(section); sections.insert(name, section); } KoToolBox::KoToolBox() : d(new Private) { d->layout = new KoToolBoxLayout(this); // add defaults d->addSection(new Section(this), "main"); d->addSection(new Section(this), "dynamic"); d->buttonGroup = new QButtonGroup(this); setLayout(d->layout); - foreach(const KoToolButton & button, KoToolManager::instance()->createToolList()) { - addButton(button); + foreach(KoToolAction *toolAction, KoToolManager::instance()->toolActionList()) { + addButton(toolAction); } // Update visibility of buttons setButtonsVisible(QList()); connect(KoToolManager::instance(), SIGNAL(changedTool(KoCanvasController*, int)), this, SLOT(setActiveTool(KoCanvasController*, int))); connect(KoToolManager::instance(), SIGNAL(currentLayerChanged(const KoCanvasController*,const KoShapeLayer*)), this, SLOT(setCurrentLayer(const KoCanvasController*,const KoShapeLayer*))); connect(KoToolManager::instance(), SIGNAL(toolCodesSelected(QList)), this, SLOT(setButtonsVisible(QList))); connect(KoToolManager::instance(), - SIGNAL(addedTool(KoToolButton,KoCanvasController*)), - this, SLOT(toolAdded(KoToolButton,KoCanvasController*))); + SIGNAL(addedTool(KoToolAction*,KoCanvasController*)), + this, SLOT(toolAdded(KoToolAction*,KoCanvasController*))); QTimer::singleShot(0, this, SLOT(adjustToFit())); } KoToolBox::~KoToolBox() { delete d; } -void KoToolBox::addButton(const KoToolButton &button) +void KoToolBox::addButton(KoToolAction *toolAction) { - d->buttons << button.button; - // ensure same L&F - button.button->setCheckable(true); - button.button->setAutoRaise(true); + KoToolBoxButton *button = new KoToolBoxButton(toolAction, this); + + d->buttons << button; int toolbuttonSize = buttonSize(qApp->desktop()->screenNumber(this)); KConfigGroup cfg = KGlobal::config()->group("KoToolBox"); int iconSize = cfg.readEntry("iconSize", toolbuttonSize); - button.button->setIconSize(QSize(iconSize, iconSize)); + button->setIconSize(QSize(iconSize, iconSize)); foreach (Section *section, d->sections.values()) { section->setButtonSize(QSize(iconSize + BUTTON_MARGIN, iconSize + BUTTON_MARGIN)); } QString sectionToBeAddedTo; - if (button.section.contains(qApp->applicationName())) { + const QString section = toolAction->section(); + if (section.contains(qApp->applicationName())) { sectionToBeAddedTo = "main"; - } else if (button.section.contains("main")) { + } else if (section.contains("main")) { sectionToBeAddedTo = "main"; - } else if (button.section.contains("dynamic")) { + } else if (section.contains("dynamic")) { sectionToBeAddedTo = "dynamic"; } else { - sectionToBeAddedTo = button.section; + sectionToBeAddedTo = section; } Section *sectionWidget = d->sections.value(sectionToBeAddedTo); if (sectionWidget == 0) { sectionWidget = new Section(this); d->addSection(sectionWidget, sectionToBeAddedTo); } - sectionWidget->addButton(button.button, button.priority); + sectionWidget->addButton(button, toolAction->priority()); - d->buttonGroup->addButton(button.button, button.buttonGroupId); + d->buttonGroup->addButton(button, toolAction->buttonGroupId()); - d->visibilityCodes.insert(button.button, button.visibilityCode); + d->visibilityCodes.insert(button, toolAction->visibilityCode()); } void KoToolBox::setActiveTool(KoCanvasController *canvas, int id) { Q_UNUSED(canvas); QAbstractButton *button = d->buttonGroup->button(id); if (button) { button->setChecked(true); } else { kWarning(30004) << "KoToolBox::setActiveTool(" << id << "): no such button found"; } } void KoToolBox::setButtonsVisible(const QList &codes) { foreach(QToolButton *button, d->visibilityCodes.keys()) { QString code = d->visibilityCodes.value(button); if (code.startsWith(QLatin1String("flake/"))) { continue; } if (code.endsWith( QLatin1String( "/always"))) { button->setVisible(true); button->setEnabled( true ); } else if (code.isEmpty()) { button->setVisible(true); button->setEnabled( codes.count() != 0 ); } else { button->setVisible( codes.contains(code) ); } } layout()->invalidate(); update(); } void KoToolBox::setCurrentLayer(const KoCanvasController *canvas, const KoShapeLayer *layer) { Q_UNUSED(canvas); const bool enabled = layer == 0 || (layer->isEditable() && layer->isVisible()); foreach (QToolButton *button, d->visibilityCodes.keys()) { if (d->visibilityCodes[button].endsWith( QLatin1String( "/always") ) ) { continue; } button->setEnabled(enabled); } } void KoToolBox::paintEvent(QPaintEvent *) { QPainter painter(this); const QList sections = d->sections.values(); QList::const_iterator iterator = sections.begin(); int halfSpacing = layout()->spacing(); if (halfSpacing > 0) { halfSpacing /= 2; } while(iterator != sections.end()) { Section *section = *iterator; QStyleOption styleoption; styleoption.palette = palette(); if (section->separators() & Section::SeparatorTop) { int y = section->y() - halfSpacing; styleoption.state = QStyle::State_None; styleoption.rect = QRect(section->x(), y-1, section->width(), 2); style()->drawPrimitive(QStyle::PE_IndicatorToolBarSeparator, &styleoption, &painter); } if (section->separators() & Section::SeparatorLeft) { int x = section->x() - halfSpacing; styleoption.state = QStyle::State_Horizontal; styleoption.rect = QRect(x-1, section->y(), 2, section->height()); style()->drawPrimitive(QStyle::PE_IndicatorToolBarSeparator, &styleoption, &painter); } ++iterator; } painter.end(); } void KoToolBox::resizeEvent(QResizeEvent* event) { QWidget::resizeEvent(event); if (!d->floating) { setMinimumSize(layout()->minimumSize()); // This enforces the minimum size on the widget } } void KoToolBox::setOrientation(Qt::Orientation orientation) { d->layout->setOrientation(orientation); QTimer::singleShot(0, this, SLOT(update())); foreach(Section* section, d->sections) { section->setOrientation(orientation); } } void KoToolBox::setFloating(bool v) { setMinimumSize(QSize(1,1)); d->floating = v; } -void KoToolBox::toolAdded(const KoToolButton &button, KoCanvasController *canvas) +void KoToolBox::toolAdded(KoToolAction *toolAction, KoCanvasController *canvas) { Q_UNUSED(canvas); - addButton(button); + addButton(toolAction); setButtonsVisible(QList()); } void KoToolBox::adjustToFit() { int newWidth = width() - (width() % layout()->minimumSize().width()); if (newWidth != width() && newWidth >= layout()->minimumSize().width()) { setMaximumWidth(newWidth); QTimer::singleShot(0, this, SLOT(resizeUnlock())); } } void KoToolBox::resizeUnlock() { setMaximumWidth(QWIDGETSIZE_MAX); } void KoToolBox::slotContextIconSize() { QAction* action = qobject_cast(sender()); if (action && d->contextIconSizes.contains(action)) { const int iconSize = d->contextIconSizes.value(action); KConfigGroup cfg = KGlobal::config()->group("KoToolBox"); cfg.writeEntry("iconSize", iconSize); foreach(QToolButton *button, d->buttons) { button->setIconSize(QSize(iconSize, iconSize)); } foreach(Section *section, d->sections.values()) { section->setButtonSize(QSize(iconSize + BUTTON_MARGIN, iconSize + BUTTON_MARGIN)); } } adjustToFit(); } void KoToolBox::contextMenuEvent(QContextMenuEvent *event) { int toolbuttonSize = buttonSize(qApp->desktop()->screenNumber(this)); if (!d->contextSize) { d->contextSize = new QMenu(i18n("Icon Size"), this); d->contextIconSizes.insert(d->contextSize->addAction(i18nc("@item:inmenu Icon size", "Default"), this, SLOT(slotContextIconSize())), toolbuttonSize); QList sizes; sizes << 12 << 14 << 16 << 22 << 32 << 48 << 64; //<< 96 << 128 << 192 << 256; foreach(int i, sizes) { d->contextIconSizes.insert(d->contextSize->addAction(i18n("%1x%2", i, i), this, SLOT(slotContextIconSize())), i); } QActionGroup *sizeGroup = new QActionGroup(d->contextSize); foreach (QAction *action, d->contextSize->actions()) { action->setActionGroup(sizeGroup); action->setCheckable(true); } } KConfigGroup cfg = KGlobal::config()->group("KoToolBox"); toolbuttonSize = cfg.readEntry("iconSize", toolbuttonSize); QMapIterator< QAction*, int > it = d->contextIconSizes; while (it.hasNext()) { it.next(); if (it.value() == toolbuttonSize) { it.key()->setChecked(true); break; } } d->contextSize->exec(event->globalPos()); } diff --git a/libs/widgets/KoToolBoxButton.cpp b/libs/widgets/KoToolBoxButton.cpp new file mode 100644 index 0000000000..66d2e185b3 --- /dev/null +++ b/libs/widgets/KoToolBoxButton.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2015 Friedrich W. H. Kossebau + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "KoToolBoxButton_p.h" + +#include +#include +#include +#include + + +KoToolBoxButton::KoToolBoxButton(KoToolAction *toolAction, QWidget *parent) + : QToolButton(parent) + , m_toolAction(toolAction) +{ + setObjectName(m_toolAction->id()); + // ensure same L&F + setCheckable(true); + setAutoRaise(true); + setIcon(KIcon(m_toolAction->iconName())); + + setDataFromToolAction(); + + connect(this, SIGNAL(clicked(bool)), m_toolAction, SLOT(trigger())); + connect(m_toolAction, SIGNAL(changed()), SLOT(setDataFromToolAction())); +} + + +void KoToolBoxButton::setDataFromToolAction() +{ + const QString plainToolTip = m_toolAction->toolTip(); + const KShortcut shortcut = m_toolAction->shortcut(); + const QString toolTip = + shortcut.isEmpty() ? + i18nc("@info:tooltip", "%1", plainToolTip) : + i18nc("@info:tooltip %2 is shortcut", "%1 (%2)", plainToolTip, + shortcut.toString()); + + setToolTip(toolTip); +} diff --git a/libs/widgets/KoToolBoxButton_p.h b/libs/widgets/KoToolBoxButton_p.h new file mode 100644 index 0000000000..5df7db8463 --- /dev/null +++ b/libs/widgets/KoToolBoxButton_p.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2015 Friedrich W. H. Kossebau + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _KO_TOOLBOXBUTTON_H_ +#define _KO_TOOLBOXBUTTON_H_ + +#include + +class KoToolAction; + +class KoToolBoxButton : public QToolButton +{ + Q_OBJECT +public: + explicit KoToolBoxButton(KoToolAction *toolAction, QWidget * parent); + +private Q_SLOTS: + void setDataFromToolAction(); + +private: + KoToolAction *m_toolAction; +}; + +#endif // _KO_TOOLBOXBUTTON_H_ diff --git a/libs/widgets/KoToolBox_p.h b/libs/widgets/KoToolBox_p.h index 884e7cb943..52df6c6b02 100644 --- a/libs/widgets/KoToolBox_p.h +++ b/libs/widgets/KoToolBox_p.h @@ -1,118 +1,117 @@ /* * Copyright (c) 2005 Boudewijn Rempt * Copyright (c) 2005-2008 Thomas Zander * Copyright (c) 2009 Peter Simonsson * Copyright (c) 2010 Cyrille Berger * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef _KO_TOOLBOX_H_ #define _KO_TOOLBOX_H_ #include #include #include #include -class QToolButton; class KoCanvasController; class KoShapeLayer; /** * KoToolBox is a dock widget that can order tools according to type and * priority. * * The ToolBox is a container for tool buttons which are themselves * divided into sections. * * Adding buttons using addButton() will allow you to show those buttons. You should connect * the button to your handling method yourself. * * The unique property of this toolbox is that it can be shown horizontal as well as vertical, * rotating in a smart way to show the buttons optimally. * @see KoToolManager */ class KoToolBox : public QWidget { Q_OBJECT public: /// constructor explicit KoToolBox(); ~KoToolBox(); public Q_SLOTS: /** * Using the buttongroup id passed in addButton() you can set the new active button. * If the id does not resolve to a visible button, this call is ignored. * @param canvas the currently active canvas. * @param id an id to identify the button to activate. */ void setActiveTool(KoCanvasController *canvas, int id); /** * Show only the dynamic buttons that have a code from parameter codes. * The toolbox allows buttons to be optionally registered with a visibilityCode. This code * can be passed here and all buttons that have that code are shown. All buttons that * have another visibility code registered are hidden. * @param canvas the currently active canvas. * @param codes a list of all the codes to show. */ void setButtonsVisible(const QList &codes); /// Set the orientation of the layout to @p orientation void setOrientation(Qt::Orientation orientation); void setFloating(bool v); private: /** * Add a button to the toolbox. * The buttons should all be added before the first showing since adding will not really add * them to the UI until setup() is called. * - * @param button the new button. Please make sure you connect to the button yourself. + * @param toolAction the action of the tool * @see setup() */ - void addButton(const KoToolButton &button); + void addButton(KoToolAction *toolAction); private Q_SLOTS: void setCurrentLayer(const KoCanvasController *canvas, const KoShapeLayer* newLayer); /// add a tool post-initialization. The tool will also be activated. - void toolAdded(const KoToolButton &button, KoCanvasController *canvas); + void toolAdded(KoToolAction *toolAction, KoCanvasController *canvas); /// resize the toolbox to show the icons without any gap at the edge void adjustToFit(); /// unlocks the with after adjustToFit void resizeUnlock(); /// set the icon size for all the buttons void slotContextIconSize(); protected: void paintEvent(QPaintEvent *event); void resizeEvent(QResizeEvent* event); void contextMenuEvent(QContextMenuEvent *event); private: class Private; Private * const d; }; #endif // _KO_TOOLBOX_H_