diff --git a/libs/flake/KoToolProxy.cpp b/libs/flake/KoToolProxy.cpp index 43e13039fd..3e30f6ccb5 100644 --- a/libs/flake/KoToolProxy.cpp +++ b/libs/flake/KoToolProxy.cpp @@ -1,648 +1,648 @@ /* This file is part of the KDE project * Copyright (C) 2006-2007 Thomas Zander * Copyright (c) 2006-2011 Boudewijn Rempt * * 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 "KoToolProxy.h" #include "KoToolProxy_p.h" #include #include #include #include #include #include #include #include #include #include #include "KoToolBase.h" #include "KoPointerEvent.h" #include "KoInputDevice.h" #include "KoToolManager_p.h" #include "KoToolSelection.h" #include "KoCanvasBase.h" #include "KoCanvasController.h" #include "KoShapeManager.h" #include "KoSelection.h" #include "KoShapeLayer.h" #include "KoShapePaste.h" #include "KoShapeRegistry.h" #include "KoShapeController.h" #include "KoOdf.h" #include "KoViewConverter.h" #include "KoShapeFactoryBase.h" KoToolProxyPrivate::KoToolProxyPrivate(KoToolProxy *p) : activeTool(0), tabletPressed(false), hasSelection(false), controller(0), parent(p) { scrollTimer.setInterval(100); mouseLeaveWorkaround = false; multiClickCount = 0; } void KoToolProxyPrivate::timeout() // Auto scroll the canvas { Q_ASSERT(controller); QPoint offset = QPoint(controller->canvasOffsetX(), controller->canvasOffsetY()); QPoint origin = controller->canvas()->documentOrigin(); QPoint viewPoint = widgetScrollPoint - origin - offset; QRectF mouseArea(viewPoint, QSizeF(10, 10)); mouseArea.setTopLeft(mouseArea.center()); controller->ensureVisible(mouseArea, true); QPoint newOffset = QPoint(controller->canvasOffsetX(), controller->canvasOffsetY()); QPoint moved = offset - newOffset; if (moved.isNull()) return; widgetScrollPoint += moved; QPointF documentPoint = parent->widgetToDocument(widgetScrollPoint); QMouseEvent event(QEvent::MouseMove, widgetScrollPoint, Qt::LeftButton, Qt::LeftButton, 0); KoPointerEvent ev(&event, documentPoint); activeTool->mouseMoveEvent(&ev); } void KoToolProxyPrivate::checkAutoScroll(const KoPointerEvent &event) { if (controller == 0) return; if (!activeTool) return; if (!activeTool->wantsAutoScroll()) return; if (!event.isAccepted()) return; if (event.buttons() != Qt::LeftButton) return; widgetScrollPoint = event.pos(); if (! scrollTimer.isActive()) scrollTimer.start(); } void KoToolProxyPrivate::selectionChanged(bool newSelection) { if (hasSelection == newSelection) return; hasSelection = newSelection; emit parent->selectionChanged(hasSelection); } bool KoToolProxyPrivate::isActiveLayerEditable() { if (!activeTool) return false; KoShapeManager * shapeManager = activeTool->canvas()->shapeManager(); KoShapeLayer * activeLayer = shapeManager->selection()->activeLayer(); if (activeLayer && !activeLayer->isEditable()) return false; return true; } KoToolProxy::KoToolProxy(KoCanvasBase *canvas, QObject *parent) : QObject(parent), d(new KoToolProxyPrivate(this)) { KoToolManager::instance()->priv()->registerToolProxy(this, canvas); connect(&d->scrollTimer, SIGNAL(timeout()), this, SLOT(timeout())); } KoToolProxy::~KoToolProxy() { delete d; } void KoToolProxy::paint(QPainter &painter, const KoViewConverter &converter) { if (d->activeTool) d->activeTool->paint(painter, converter); } void KoToolProxy::repaintDecorations() { if (d->activeTool) d->activeTool->repaintDecorations(); } QPointF KoToolProxy::widgetToDocument(const QPointF &widgetPoint) const { QPoint offset = QPoint(d->controller->canvasOffsetX(), d->controller->canvasOffsetY()); QPoint origin = d->controller->canvas()->documentOrigin(); QPointF viewPoint = widgetPoint.toPoint() - QPointF(origin - offset); return d->controller->canvas()->viewConverter()->viewToDocument(viewPoint); } KoCanvasBase* KoToolProxy::canvas() const { return d->controller->canvas(); } void KoToolProxy::touchEvent(QTouchEvent *event) { QPointF point; QList touchPoints; bool isPrimary = true; Q_FOREACH (QTouchEvent::TouchPoint p, event->touchPoints()) { QPointF docPoint = widgetToDocument(p.screenPos()); if (isPrimary) { point = docPoint; isPrimary = false; } KoTouchPoint touchPoint; touchPoint.touchPoint = p; touchPoint.point = point; touchPoint.lastPoint = widgetToDocument(p.lastNormalizedPos()); touchPoints << touchPoint; } KoPointerEvent ev(event, point, touchPoints); KoInputDevice id; KoToolManager::instance()->priv()->switchInputDevice(id); switch (event->type()) { case QEvent::TouchBegin: ev.setTabletButton(Qt::LeftButton); if (d->activeTool) { if( d->activeTool->wantsTouch() ) d->activeTool->touchEvent(event); else d->activeTool->mousePressEvent(&ev); } break; case QEvent::TouchUpdate: ev.setTabletButton(Qt::LeftButton); if (d->activeTool) { if( d->activeTool->wantsTouch() ) d->activeTool->touchEvent(event); else d->activeTool->mouseMoveEvent(&ev); } break; case QEvent::TouchEnd: ev.setTabletButton(Qt::LeftButton); if (d->activeTool) { if( d->activeTool->wantsTouch() ) d->activeTool->touchEvent(event); else d->activeTool->mouseReleaseEvent(&ev); } break; default: ; // ingore the rest } d->mouseLeaveWorkaround = true; } void KoToolProxy::tabletEvent(QTabletEvent *event, const QPointF &point) { // We get these events exclusively from KisToolProxy - accept them event->accept(); KoInputDevice id(event->device(), event->pointerType(), event->uniqueId()); KoToolManager::instance()->priv()->switchInputDevice(id); KoPointerEvent ev(event, point); switch (event->type()) { case QEvent::TabletPress: ev.setTabletButton(Qt::LeftButton); if (!d->tabletPressed && d->activeTool) d->activeTool->mousePressEvent(&ev); d->tabletPressed = true; break; case QEvent::TabletRelease: ev.setTabletButton(Qt::LeftButton); d->tabletPressed = false; d->scrollTimer.stop(); if (d->activeTool) d->activeTool->mouseReleaseEvent(&ev); break; case QEvent::TabletMove: if (d->tabletPressed) ev.setTabletButton(Qt::LeftButton); if (d->activeTool) d->activeTool->mouseMoveEvent(&ev); d->checkAutoScroll(ev); default: ; // ignore the rest. } d->mouseLeaveWorkaround = true; } void KoToolProxy::mousePressEvent(KoPointerEvent *ev) { d->mouseLeaveWorkaround = false; KoInputDevice id; KoToolManager::instance()->priv()->switchInputDevice(id); d->mouseDownPoint = ev->pos(); if (d->tabletPressed) // refuse to send a press unless there was a release first. return; QPointF globalPoint = ev->globalPos(); if (d->multiClickGlobalPoint != globalPoint) { if (qAbs(globalPoint.x() - d->multiClickGlobalPoint.x()) > 5|| qAbs(globalPoint.y() - d->multiClickGlobalPoint.y()) > 5) { d->multiClickCount = 0; } d->multiClickGlobalPoint = globalPoint; } if (d->multiClickCount && d->multiClickTimeStamp.elapsed() < QApplication::doubleClickInterval()) { // One more multiclick; d->multiClickCount++; } else { d->multiClickTimeStamp.start(); d->multiClickCount = 1; } if (d->activeTool) { switch (d->multiClickCount) { case 0: case 1: d->activeTool->mousePressEvent(ev); break; case 2: d->activeTool->mouseDoubleClickEvent(ev); break; case 3: default: d->activeTool->mouseTripleClickEvent(ev); break; } } else { d->multiClickCount = 0; ev->ignore(); } } void KoToolProxy::mousePressEvent(QMouseEvent *event, const QPointF &point) { KoPointerEvent ev(event, point); mousePressEvent(&ev); } void KoToolProxy::mouseDoubleClickEvent(QMouseEvent *event, const QPointF &point) { KoPointerEvent ev(event, point); mouseDoubleClickEvent(&ev); } void KoToolProxy::mouseDoubleClickEvent(KoPointerEvent *event) { // let us handle it as any other mousepress (where we then detect multi clicks mousePressEvent(event); if (!event->isAccepted() && d->activeTool) d->activeTool->canvas()->shapeManager()->suggestChangeTool(event); } void KoToolProxy::mouseMoveEvent(QMouseEvent *event, const QPointF &point) { if (d->mouseLeaveWorkaround) { d->mouseLeaveWorkaround = false; return; } KoInputDevice id; KoToolManager::instance()->priv()->switchInputDevice(id); if (d->activeTool == 0) { event->ignore(); return; } KoPointerEvent ev(event, point); d->activeTool->mouseMoveEvent(&ev); d->checkAutoScroll(ev); } void KoToolProxy::mouseMoveEvent(KoPointerEvent *event) { if (d->mouseLeaveWorkaround) { d->mouseLeaveWorkaround = false; return; } KoInputDevice id; KoToolManager::instance()->priv()->switchInputDevice(id); if (d->activeTool == 0) { event->ignore(); return; } d->activeTool->mouseMoveEvent(event); d->checkAutoScroll(*event); } void KoToolProxy::mouseReleaseEvent(QMouseEvent *event, const QPointF &point) { d->mouseLeaveWorkaround = false; KoInputDevice id; KoToolManager::instance()->priv()->switchInputDevice(id); d->scrollTimer.stop(); KoPointerEvent ev(event, point); if (d->activeTool) { d->activeTool->mouseReleaseEvent(&ev); if (! event->isAccepted() && event->button() == Qt::LeftButton && event->modifiers() == 0 && qAbs(d->mouseDownPoint.x() - event->x()) < 5 && qAbs(d->mouseDownPoint.y() - event->y()) < 5) { // we potentially will change the selection Q_ASSERT(d->activeTool->canvas()); KoShapeManager *manager = d->activeTool->canvas()->shapeManager(); Q_ASSERT(manager); // only change the selection if that will not lead to losing a complex selection - if (manager->selection()->count() <= 1) { + if (manager->selection() && manager->selection()->count() <= 1) { KoShape *shape = manager->shapeAt(point); if (shape && !manager->selection()->isSelected(shape)) { // make the clicked shape the active one manager->selection()->deselectAll(); manager->selection()->select(shape); QList shapes; shapes << shape; QString tool = KoToolManager::instance()->preferredToolForSelection(shapes); KoToolManager::instance()->switchToolRequested(tool); } } } } else { event->ignore(); } } void KoToolProxy::mouseReleaseEvent(KoPointerEvent* event) { d->mouseLeaveWorkaround = false; KoInputDevice id; KoToolManager::instance()->priv()->switchInputDevice(id); d->scrollTimer.stop(); if (d->activeTool) { d->activeTool->mouseReleaseEvent(event); if (!event->isAccepted() && event->button() == Qt::LeftButton && event->modifiers() == 0 && qAbs(d->mouseDownPoint.x() - event->x()) < 5 && qAbs(d->mouseDownPoint.y() - event->y()) < 5) { // we potentially will change the selection Q_ASSERT(d->activeTool->canvas()); KoShapeManager *manager = d->activeTool->canvas()->shapeManager(); Q_ASSERT(manager); // only change the selection if that will not lead to losing a complex selection - if (manager->selection()->count() <= 1) { + if (manager->selection() && manager->selection()->count() <= 1) { KoShape *shape = manager->shapeAt(event->point); if (shape && !manager->selection()->isSelected(shape)) { // make the clicked shape the active one manager->selection()->deselectAll(); manager->selection()->select(shape); QList shapes; shapes << shape; QString tool = KoToolManager::instance()->preferredToolForSelection(shapes); KoToolManager::instance()->switchToolRequested(tool); } } } } else { event->ignore(); } } void KoToolProxy::keyPressEvent(QKeyEvent *event) { if (d->activeTool) d->activeTool->keyPressEvent(event); else event->ignore(); } void KoToolProxy::keyReleaseEvent(QKeyEvent *event) { if (d->activeTool) d->activeTool->keyReleaseEvent(event); else event->ignore(); } void KoToolProxy::wheelEvent(QWheelEvent *event, const QPointF &point) { KoPointerEvent ev(event, point); if (d->activeTool) d->activeTool->wheelEvent(&ev); else event->ignore(); } void KoToolProxy::wheelEvent(KoPointerEvent *event) { if (d->activeTool) d->activeTool->wheelEvent(event); else event->ignore(); } QVariant KoToolProxy::inputMethodQuery(Qt::InputMethodQuery query, const KoViewConverter &converter) const { if (d->activeTool) return d->activeTool->inputMethodQuery(query, converter); return QVariant(); } void KoToolProxy::inputMethodEvent(QInputMethodEvent *event) { if (d->activeTool) d->activeTool->inputMethodEvent(event); } void KoToolProxy::setActiveTool(KoToolBase *tool) { if (d->activeTool) disconnect(d->activeTool, SIGNAL(selectionChanged(bool)), this, SLOT(selectionChanged(bool))); d->activeTool = tool; if (tool) { connect(d->activeTool, SIGNAL(selectionChanged(bool)), this, SLOT(selectionChanged(bool))); d->selectionChanged(hasSelection()); emit toolChanged(tool->toolId()); } } void KoToolProxyPrivate::setCanvasController(KoCanvasController *c) { controller = c; } QHash KoToolProxy::actions() const { return d->activeTool ? d->activeTool->actions() : QHash(); } bool KoToolProxy::hasSelection() const { return d->activeTool ? d->activeTool->hasSelection() : false; } void KoToolProxy::cut() { if (d->activeTool && d->isActiveLayerEditable()) d->activeTool->cut(); } void KoToolProxy::copy() const { if (d->activeTool) d->activeTool->copy(); } bool KoToolProxy::paste() { bool success = false; KoCanvasBase *canvas = d->controller->canvas(); if (d->activeTool && d->isActiveLayerEditable()) success = d->activeTool->paste(); if (!success) { const QMimeData *data = QApplication::clipboard()->mimeData(); if (data->hasFormat(KoOdf::mimeType(KoOdf::Text))) { KoShapeManager *shapeManager = canvas->shapeManager(); KoShapePaste paste(canvas, shapeManager->selection()->activeLayer()); success = paste.paste(KoOdf::Text, data); if (success) { shapeManager->selection()->deselectAll(); Q_FOREACH (KoShape *shape, paste.pastedShapes()) { shapeManager->selection()->select(shape); } } } } if (!success) { const QMimeData *data = QApplication::clipboard()->mimeData(); QList imageList; QImage image = QApplication::clipboard()->image(); if (!image.isNull()) { imageList << image; } // QT5TODO: figure out how to download data synchronously, which is deprecated in frameworks. else if (data->hasUrls()) { QList urls = QApplication::clipboard()->mimeData()->urls(); foreach (const QUrl &url, urls) { QImage image; image.load(url.toLocalFile()); if (!image.isNull()) { imageList << image; } } } KoShapeFactoryBase *factory = KoShapeRegistry::instance()->value("PictureShape"); QWidget *canvasWidget = canvas->canvasWidget(); const KoViewConverter *converter = canvas->viewConverter(); if (imageList.length() > 0 && factory && canvasWidget) { KUndo2Command *cmd = new KUndo2Command(kundo2_i18n("Paste Image")); Q_FOREACH (const QImage &image, imageList) { if (!image.isNull()) { QPointF p = converter->viewToDocument(canvasWidget->mapFromGlobal(QCursor::pos()) + canvas->canvasController()->documentOffset()- canvasWidget->pos()); KoProperties params; params.setProperty("qimage", image); KoShape *shape = factory->createShape(¶ms, canvas->shapeController()->resourceManager()); shape->setPosition(p); // add shape to the document canvas->shapeController()->addShapeDirect(shape, cmd); success = true; } } canvas->addCommand(cmd); } } return success; } void KoToolProxy::dragMoveEvent(QDragMoveEvent *event, const QPointF &point) { if (d->activeTool) d->activeTool->dragMoveEvent(event, point); } void KoToolProxy::dragLeaveEvent(QDragLeaveEvent *event) { if (d->activeTool) d->activeTool->dragLeaveEvent(event); } void KoToolProxy::dropEvent(QDropEvent *event, const QPointF &point) { if (d->activeTool) d->activeTool->dropEvent(event, point); } QStringList KoToolProxy::supportedPasteMimeTypes() const { if (d->activeTool) return d->activeTool->supportedPasteMimeTypes(); return QStringList(); } QList KoToolProxy::popupActionList() const { if (d->activeTool) return d->activeTool->popupActionList(); return QList(); } void KoToolProxy::deleteSelection() { if (d->activeTool) return d->activeTool->deleteSelection(); } void KoToolProxy::processEvent(QEvent *e) const { if(e->type()==QEvent::ShortcutOverride && d->activeTool && d->activeTool->isInTextMode() && (static_cast(e)->modifiers()==Qt::NoModifier || static_cast(e)->modifiers()==Qt::ShiftModifier)) { e->accept(); } } KoToolProxyPrivate *KoToolProxy::priv() { return d; } //have to include this because of Q_PRIVATE_SLOT #include "moc_KoToolProxy.cpp" diff --git a/libs/ui/kis_selection_manager.cc b/libs/ui/kis_selection_manager.cc index 8057aac8ae..54ae085a09 100644 --- a/libs/ui/kis_selection_manager.cc +++ b/libs/ui/kis_selection_manager.cc @@ -1,620 +1,624 @@ /* * Copyright (c) 2004 Boudewijn Rempt * Copyright (c) 2007 Sven Langkamp * * The outline algorith uses the limn algorithm of fontutils by * Karl Berry and Kathryn Hargreaves * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_selection_manager.h" #include #include #include #include #include #include #include #include #include #include #include "KoCanvasController.h" #include "KoChannelInfo.h" #include "KoIntegerMaths.h" #include #include #include #include #include #include #include #include #include #include #include "kis_adjustment_layer.h" #include "kis_node_manager.h" #include "canvas/kis_canvas2.h" #include "kis_config.h" #include "kis_convolution_painter.h" #include "kis_convolution_kernel.h" #include "kis_debug.h" #include "KisDocument.h" #include "kis_fill_painter.h" #include "kis_group_layer.h" #include "kis_layer.h" #include "kis_statusbar.h" #include "kis_paint_device.h" #include "kis_paint_layer.h" #include "kis_painter.h" #include "kis_transaction.h" #include "kis_selection.h" #include "kis_types.h" #include "kis_canvas_resource_provider.h" #include "kis_undo_adapter.h" #include "kis_pixel_selection.h" #include "flake/kis_shape_selection.h" #include "commands/kis_selection_commands.h" #include "kis_selection_mask.h" #include "flake/kis_shape_layer.h" #include "kis_selection_decoration.h" #include "canvas/kis_canvas_decoration.h" #include "kis_node_commands_adapter.h" #include "kis_iterator_ng.h" #include "kis_clipboard.h" #include "KisViewManager.h" #include "kis_selection_filters.h" #include "kis_figure_painting_tool_helper.h" #include "KisView.h" #include "dialogs/kis_dlg_stroke_selection_properties.h" #include "actions/kis_selection_action_factories.h" #include "kis_action.h" #include "kis_action_manager.h" #include "operations/kis_operation_configuration.h" //new #include "kis_recorded_path_paint_action.h" #include "kis_node_query_path.h" #include "kis_tool_shape.h" KisSelectionManager::KisSelectionManager(KisViewManager * view) : m_view(view), m_doc(0), m_imageView(0), m_adapter(new KisNodeCommandsAdapter(view)), m_copy(0), m_copyMerged(0), m_cut(0), m_paste(0), m_pasteNew(0), m_cutToNewLayer(0), m_selectAll(0), m_deselect(0), m_clear(0), m_reselect(0), m_invert(0), m_copyToNewLayer(0), m_fillForegroundColor(0), m_fillBackgroundColor(0), m_fillPattern(0), m_imageResizeToSelection(0), m_selectionDecoration(0) { m_clipboard = KisClipboard::instance(); } KisSelectionManager::~KisSelectionManager() { } void KisSelectionManager::setup(KisActionManager* actionManager) { m_cut = actionManager->createStandardAction(KStandardAction::Cut, this, SLOT(cut())); m_copy = actionManager->createStandardAction(KStandardAction::Copy, this, SLOT(copy())); m_paste = actionManager->createStandardAction(KStandardAction::Paste, this, SLOT(paste())); KisAction *action = actionManager->createAction("copy_sharp"); connect(action, SIGNAL(triggered()), this, SLOT(copySharp())); action = actionManager->createAction("cut_sharp"); connect(action, SIGNAL(triggered()), this, SLOT(cutSharp())); m_pasteNew = actionManager->createAction("paste_new"); connect(m_pasteNew, SIGNAL(triggered()), this, SLOT(pasteNew())); m_pasteAt = actionManager->createAction("paste_at"); connect(m_pasteAt, SIGNAL(triggered()), this, SLOT(pasteAt())); m_copyMerged = actionManager->createAction("copy_merged"); connect(m_copyMerged, SIGNAL(triggered()), this, SLOT(copyMerged())); m_selectAll = actionManager->createAction("select_all"); connect(m_selectAll, SIGNAL(triggered()), this, SLOT(selectAll())); m_deselect = actionManager->createAction("deselect"); connect(m_deselect, SIGNAL(triggered()), this, SLOT(deselect())); m_clear = actionManager->createAction("clear"); connect(m_clear, SIGNAL(triggered()), SLOT(clear())); m_reselect = actionManager->createAction("reselect"); connect(m_reselect, SIGNAL(triggered()), this, SLOT(reselect())); m_invert = actionManager->createAction("invert"); m_invert->setOperationID("invertselection"); actionManager->registerOperation(new KisInvertSelectionOperaton); m_copyToNewLayer = actionManager->createAction("copy_selection_to_new_layer"); connect(m_copyToNewLayer, SIGNAL(triggered()), this, SLOT(copySelectionToNewLayer())); m_cutToNewLayer = actionManager->createAction("cut_selection_to_new_layer"); connect(m_cutToNewLayer, SIGNAL(triggered()), this, SLOT(cutToNewLayer())); m_fillForegroundColor = actionManager->createAction("fill_selection_foreground_color"); connect(m_fillForegroundColor, SIGNAL(triggered()), this, SLOT(fillForegroundColor())); m_fillBackgroundColor = actionManager->createAction("fill_selection_background_color"); connect(m_fillBackgroundColor, SIGNAL(triggered()), this, SLOT(fillBackgroundColor())); m_fillPattern = actionManager->createAction("fill_selection_pattern"); connect(m_fillPattern, SIGNAL(triggered()), this, SLOT(fillPattern())); m_fillForegroundColorOpacity = actionManager->createAction("fill_selection_foreground_color_opacity"); connect(m_fillForegroundColorOpacity, SIGNAL(triggered()), this, SLOT(fillForegroundColorOpacity())); m_fillBackgroundColorOpacity = actionManager->createAction("fill_selection_background_color_opacity"); connect(m_fillBackgroundColorOpacity, SIGNAL(triggered()), this, SLOT(fillBackgroundColorOpacity())); m_fillPatternOpacity = actionManager->createAction("fill_selection_pattern_opacity"); connect(m_fillPatternOpacity, SIGNAL(triggered()), this, SLOT(fillPatternOpacity())); m_strokeShapes = actionManager->createAction("stroke_shapes"); connect(m_strokeShapes, SIGNAL(triggered()), this, SLOT(paintSelectedShapes())); m_toggleDisplaySelection = actionManager->createAction("toggle_display_selection"); connect(m_toggleDisplaySelection, SIGNAL(triggered()), this, SLOT(toggleDisplaySelection())); m_toggleDisplaySelection->setChecked(true); m_imageResizeToSelection = actionManager->createAction("resizeimagetoselection"); connect(m_imageResizeToSelection, SIGNAL(triggered()), this, SLOT(imageResizeToSelection())); action = actionManager->createAction("convert_to_vector_selection"); connect(action, SIGNAL(triggered()), SLOT(convertToVectorSelection())); action = actionManager->createAction("convert_shapes_to_vector_selection"); connect(action, SIGNAL(triggered()), SLOT(convertShapesToVectorSelection())); action = actionManager->createAction("convert_selection_to_shape"); connect(action, SIGNAL(triggered()), SLOT(convertToShape())); m_toggleSelectionOverlayMode = actionManager->createAction("toggle-selection-overlay-mode"); connect(m_toggleSelectionOverlayMode, SIGNAL(triggered()), SLOT(slotToggleSelectionDecoration())); m_strokeSelected = actionManager->createAction("stroke_selection"); connect(m_strokeSelected, SIGNAL(triggered()), SLOT(slotStrokeSelection())); QClipboard *cb = QApplication::clipboard(); connect(cb, SIGNAL(dataChanged()), SLOT(clipboardDataChanged())); } void KisSelectionManager::setView(QPointerimageView) { if (m_imageView && m_imageView->canvasBase()) { disconnect(m_imageView->canvasBase()->toolProxy(), SIGNAL(toolChanged(const QString&)), this, SLOT(clipboardDataChanged())); KoSelection *selection = m_imageView->canvasBase()->globalShapeManager()->selection(); selection->disconnect(this, SLOT(shapeSelectionChanged())); KisSelectionDecoration *decoration = qobject_cast(m_imageView->canvasBase()->decoration("selection").data()); if (decoration) { disconnect(SIGNAL(currentSelectionChanged()), decoration); } m_imageView->image()->undoAdapter()->disconnect(this); m_selectionDecoration = 0; } m_imageView = imageView; if (m_imageView) { KoSelection * selection = m_imageView->canvasBase()->globalShapeManager()->selection(); Q_ASSERT(selection); connect(selection, SIGNAL(selectionChanged()), this, SLOT(shapeSelectionChanged())); KisSelectionDecoration* decoration = qobject_cast(m_imageView->canvasBase()->decoration("selection").data()); if (!decoration) { decoration = new KisSelectionDecoration(m_imageView); decoration->setVisible(true); m_imageView->canvasBase()->addDecoration(decoration); } m_selectionDecoration = decoration; connect(this, SIGNAL(currentSelectionChanged()), decoration, SLOT(selectionChanged())); connect(m_imageView->image()->undoAdapter(), SIGNAL(selectionChanged()), SLOT(selectionChanged())); connect(m_imageView->canvasBase()->toolProxy(), SIGNAL(toolChanged(const QString&)), SLOT(clipboardDataChanged())); } } void KisSelectionManager::clipboardDataChanged() { m_view->updateGUI(); } bool KisSelectionManager::havePixelsSelected() { KisSelectionSP activeSelection = m_view->selection(); return activeSelection && !activeSelection->selectedRect().isEmpty(); } bool KisSelectionManager::havePixelsInClipboard() { return m_clipboard->hasClip(); } bool KisSelectionManager::haveShapesSelected() { - if (m_view && m_view->canvasBase() && m_view->canvasBase()->shapeManager() && m_view->canvasBase()->shapeManager()->selection()->count()) { + if (m_view + && m_view->canvasBase() + && m_view->canvasBase()->shapeManager() + && m_view->canvasBase()->shapeManager()->selection() + && m_view->canvasBase()->shapeManager()->selection()->count()) { return m_view->canvasBase()->shapeManager()->selection()->count() > 0; } return false; } bool KisSelectionManager::haveShapesInClipboard() { KisShapeLayer *shapeLayer = dynamic_cast(m_view->activeLayer().data()); if (shapeLayer) { const QMimeData* data = QApplication::clipboard()->mimeData(); if (data) { QStringList mimeTypes = m_view->canvasBase()->toolProxy()->supportedPasteMimeTypes(); Q_FOREACH (const QString & mimeType, mimeTypes) { if (data->hasFormat(mimeType)) { return true; } } } } return false; } bool KisSelectionManager::havePixelSelectionWithPixels() { KisSelectionSP selection = m_view->selection(); if (selection && selection->hasPixelSelection()) { return !selection->pixelSelection()->selectedRect().isEmpty(); } return false; } void KisSelectionManager::updateGUI() { Q_ASSERT(m_view); Q_ASSERT(m_clipboard); if (!m_view || !m_clipboard) return; bool havePixelsSelected = this->havePixelsSelected(); bool havePixelsInClipboard = this->havePixelsInClipboard(); bool haveShapesSelected = this->haveShapesSelected(); bool haveShapesInClipboard = this->haveShapesInClipboard(); bool haveDevice = m_view->activeDevice(); KisLayerSP activeLayer = m_view->activeLayer(); KisImageWSP image = activeLayer ? activeLayer->image() : 0; bool canReselect = image && image->canReselectGlobalSelection(); bool canDeselect = image && image->globalSelection(); m_clear->setEnabled(haveDevice || havePixelsSelected || haveShapesSelected); m_cut->setEnabled(havePixelsSelected || haveShapesSelected); m_copy->setEnabled(havePixelsSelected || haveShapesSelected); m_paste->setEnabled(havePixelsInClipboard || haveShapesInClipboard); m_pasteAt->setEnabled(havePixelsInClipboard || haveShapesInClipboard); // FIXME: how about pasting shapes? m_pasteNew->setEnabled(havePixelsInClipboard); m_selectAll->setEnabled(true); m_deselect->setEnabled(canDeselect); m_reselect->setEnabled(canReselect); // m_load->setEnabled(true); // m_save->setEnabled(havePixelsSelected); updateStatusBar(); emit signalUpdateGUI(); } void KisSelectionManager::updateStatusBar() { if (m_view && m_view->statusBar()) { m_view->statusBar()->setSelection(m_view->image()); } } void KisSelectionManager::selectionChanged() { m_view->updateGUI(); emit currentSelectionChanged(); } void KisSelectionManager::cut() { KisCutCopyActionFactory factory; factory.run(true, false, m_view); } void KisSelectionManager::copy() { KisCutCopyActionFactory factory; factory.run(false, false, m_view); } void KisSelectionManager::cutSharp() { KisCutCopyActionFactory factory; factory.run(true, true, m_view); } void KisSelectionManager::copySharp() { KisCutCopyActionFactory factory; factory.run(false, true, m_view); } void KisSelectionManager::copyMerged() { KisCopyMergedActionFactory factory; factory.run(m_view); } void KisSelectionManager::paste() { KisPasteActionFactory factory; factory.run(m_view); } void KisSelectionManager::pasteAt() { //XXX } void KisSelectionManager::pasteNew() { KisPasteNewActionFactory factory; factory.run(m_view); } void KisSelectionManager::selectAll() { KisSelectAllActionFactory factory; factory.run(m_view); } void KisSelectionManager::deselect() { KisDeselectActionFactory factory; factory.run(m_view); } void KisSelectionManager::invert() { if(m_invert) m_invert->trigger(); } void KisSelectionManager::reselect() { KisReselectActionFactory factory; factory.run(m_view); } void KisSelectionManager::convertToVectorSelection() { KisSelectionToVectorActionFactory factory; factory.run(m_view); } void KisSelectionManager::convertShapesToVectorSelection() { KisShapesToVectorSelectionActionFactory factory; factory.run(m_view); } void KisSelectionManager::convertToShape() { KisSelectionToShapeActionFactory factory; factory.run(m_view); } void KisSelectionManager::clear() { KisClearActionFactory factory; factory.run(m_view); } void KisSelectionManager::fillForegroundColor() { KisFillActionFactory factory; factory.run("fg", m_view); } void KisSelectionManager::fillBackgroundColor() { KisFillActionFactory factory; factory.run("bg", m_view); } void KisSelectionManager::fillPattern() { KisFillActionFactory factory; factory.run("pattern", m_view); } void KisSelectionManager::fillForegroundColorOpacity() { KisFillActionFactory factory; factory.run("fg_opacity", m_view); } void KisSelectionManager::fillBackgroundColorOpacity() { KisFillActionFactory factory; factory.run("bg_opacity", m_view); } void KisSelectionManager::fillPatternOpacity() { KisFillActionFactory factory; factory.run("pattern_opacity", m_view); } void KisSelectionManager::copySelectionToNewLayer() { copy(); paste(); } void KisSelectionManager::cutToNewLayer() { cut(); paste(); } void KisSelectionManager::toggleDisplaySelection() { KIS_ASSERT_RECOVER_RETURN(m_selectionDecoration); m_selectionDecoration->toggleVisibility(); m_toggleDisplaySelection->blockSignals(true); m_toggleDisplaySelection->setChecked(m_selectionDecoration->visible()); m_toggleDisplaySelection->blockSignals(false); emit displaySelectionChanged(); } bool KisSelectionManager::displaySelection() { return m_toggleDisplaySelection->isChecked(); } void KisSelectionManager::shapeSelectionChanged() { KoShapeManager* shapeManager = m_view->canvasBase()->globalShapeManager(); KoSelection * selection = shapeManager->selection(); QList selectedShapes = selection->selectedShapes(); KoShapeStroke* border = new KoShapeStroke(0, Qt::lightGray); Q_FOREACH (KoShape* shape, shapeManager->shapes()) { if (dynamic_cast(shape->parent())) { if (selectedShapes.contains(shape)) shape->setStroke(border); else shape->setStroke(0); } } m_view->updateGUI(); } void KisSelectionManager::imageResizeToSelection() { KisImageResizeToSelectionActionFactory factory; factory.run(m_view); } void KisSelectionManager::paintSelectedShapes() { KisImageWSP image = m_view->image(); if (!image) return; KisLayerSP layer = m_view->activeLayer(); if (!layer) return; QList shapes = m_view->canvasBase()->shapeManager()->selection()->selectedShapes(); KisPaintLayerSP paintLayer = new KisPaintLayer(image, i18n("Stroked Shapes"), OPACITY_OPAQUE_U8); KUndo2MagicString actionName = kundo2_i18n("Stroke Shapes"); m_adapter->beginMacro(actionName); m_adapter->addNode(paintLayer.data(), layer->parent().data(), layer.data()); KisFigurePaintingToolHelper helper(actionName, image, paintLayer.data(), m_view->resourceProvider()->resourceManager(), KisPainter::StrokeStyleBrush, KisPainter::FillStyleNone); Q_FOREACH (KoShape* shape, shapes) { QTransform matrix = shape->absoluteTransformation(0) * QTransform::fromScale(image->xRes(), image->yRes()); QPainterPath mapedOutline = matrix.map(shape->outline()); helper.paintPainterPath(mapedOutline); } m_adapter->endMacro(); } void KisSelectionManager::slotToggleSelectionDecoration() { KIS_ASSERT_RECOVER_RETURN(m_selectionDecoration); KisSelectionDecoration::Mode mode = m_selectionDecoration->mode() ? KisSelectionDecoration::Ants : KisSelectionDecoration::Mask; m_selectionDecoration->setMode(mode); emit displaySelectionChanged(); } bool KisSelectionManager::showSelectionAsMask() const { if (m_selectionDecoration) { return m_selectionDecoration->mode() == KisSelectionDecoration::Mask; } return false; } void KisSelectionManager::slotStrokeSelection() { KisImageWSP image = m_view->image(); if (!image ) { return; } KisNodeSP currentNode = m_view->resourceProvider()->resourceManager()->resource(KisCanvasResourceProvider::CurrentKritaNode).value(); bool isVectorLayer = false; if (currentNode->inherits("KisShapeLayer")) { isVectorLayer = true; } QPointer dlg = new KisDlgStrokeSelection(image, m_view, isVectorLayer); if (dlg->exec() == QDialog::Accepted) { StrokeSelectionOptions params = dlg->getParams(); if (params.brushSelected){ KisStrokeBrushSelectionActionFactory factory; factory.run(m_view, params); } else { KisStrokeSelectionActionFactory factory; factory.run(m_view, params); } } delete dlg; } diff --git a/plugins/dockers/shapedockers/shapeproperties/ShapePropertiesDocker.cpp b/plugins/dockers/shapedockers/shapeproperties/ShapePropertiesDocker.cpp index 9f765120ec..bcaff6a69d 100644 --- a/plugins/dockers/shapedockers/shapeproperties/ShapePropertiesDocker.cpp +++ b/plugins/dockers/shapedockers/shapeproperties/ShapePropertiesDocker.cpp @@ -1,189 +1,189 @@ /* This file is part of the KDE project Copyright (C) 2007 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. */ #include "ShapePropertiesDocker.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include class ShapePropertiesDocker::Private { public: - Private() + Private() : widgetStack(0) , currentShape(0) , currentPanel(0) - , canvas(0) + , canvas(0) { } - + QStackedWidget *widgetStack; KoShape *currentShape; KoShapeConfigWidgetBase *currentPanel; QPointer canvas; }; ShapePropertiesDocker::ShapePropertiesDocker(QWidget *parent) : QDockWidget(i18n("Shape Properties") , parent) , d(new Private()) { d->widgetStack = new QStackedWidget(); setWidget(d->widgetStack); } ShapePropertiesDocker::~ShapePropertiesDocker() { delete d; } void ShapePropertiesDocker::unsetCanvas() { setEnabled(false); d->canvas = 0; } void ShapePropertiesDocker::setCanvas(KoCanvasBase *canvas) { setEnabled(canvas != 0); if (d->canvas) { d->canvas->disconnectCanvasObserver(this); // "Every connection you make emits a signal, so duplicate connections emit two signals" } d->canvas = canvas; if (d->canvas) { connect(d->canvas->shapeManager(), SIGNAL(selectionChanged()), this, SLOT(selectionChanged())); connect(d->canvas->shapeManager(), SIGNAL(selectionContentChanged()), this, SLOT(selectionChanged())); connect(d->canvas->resourceManager(), SIGNAL(canvasResourceChanged(int,QVariant)), this, SLOT(canvasResourceChanged(int,QVariant))); } } void ShapePropertiesDocker::selectionChanged() { if (!d->canvas) { return; } KoSelection *selection = d->canvas->shapeManager()->selection(); - if (selection->count() == 1) { + if (selection && selection->count() == 1) { addWidgetForShape(selection->firstSelectedShape()); } else { addWidgetForShape(0); } } void ShapePropertiesDocker::addWidgetForShape(KoShape *shape) { // remove the config widget if a null shape is set, or the shape has changed if (!shape || shape != d->currentShape) { while (d->widgetStack->count()) { d->widgetStack->removeWidget(d->widgetStack->widget(0)); } } if (!shape) { d->currentShape = 0; d->currentPanel = 0; return; } else if (shape != d->currentShape) { // when a shape is set and is differs from the previous one // get the config widget and insert it into the option widget d->currentShape = shape; if (!d->currentShape) { return; } QString shapeId = shape->shapeId(); KoPathShape *path = dynamic_cast(shape); if (path) { // use the path specific shape id if shape is a path, otherwise use the shape id shapeId = path->pathShapeId(); // check if we have an edited parametric shape, then we use the path shape id KoParameterShape *paramShape = dynamic_cast(shape); if (paramShape && ! paramShape->isParametricShape()) { shapeId = shape->shapeId(); } } KoShapeFactoryBase *factory = KoShapeRegistry::instance()->value(shapeId); if (!factory) { return; } QList panels = factory->createShapeOptionPanels(); if (!panels.count()) { return; } d->currentPanel = 0; uint panelCount = panels.count(); for (uint i = 0; i < panelCount; ++i) { if (panels[i]->showOnShapeSelect()) { d->currentPanel = panels[i]; break; } } if (d->currentPanel) { if (d->canvas) { d->currentPanel->setUnit(d->canvas->unit()); } d->widgetStack->insertWidget(0, d->currentPanel); connect(d->currentPanel, SIGNAL(propertyChanged()), this, SLOT(shapePropertyChanged())); } } if (d->currentPanel) { d->currentPanel->open(shape); } } void ShapePropertiesDocker::shapePropertyChanged() { if (d->canvas && d->currentPanel) { KUndo2Command *cmd = d->currentPanel->createCommand(); if (!cmd) { return; } d->canvas->addCommand(cmd); } } void ShapePropertiesDocker::canvasResourceChanged(int key, const QVariant &variant) { if (key == KoCanvasResourceManager::Unit && d->currentPanel) { d->currentPanel->setUnit(variant.value()); } } diff --git a/plugins/tools/defaulttool/defaulttool/DefaultTool.cpp b/plugins/tools/defaulttool/defaulttool/DefaultTool.cpp index c018349fab..02e5e2f2cf 100644 --- a/plugins/tools/defaulttool/defaulttool/DefaultTool.cpp +++ b/plugins/tools/defaulttool/defaulttool/DefaultTool.cpp @@ -1,1236 +1,1242 @@ /* This file is part of the KDE project Copyright (C) 2006-2008 Thorsten Zachmann Copyright (C) 2006-2010 Thomas Zander Copyright (C) 2008-2009 Jan Hambrecht Copyright (C) 2008 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 "DefaultTool.h" #include "DefaultToolWidget.h" #include "DefaultToolArrangeWidget.h" #include "SelectionDecorator.h" #include "ShapeMoveStrategy.h" #include "ShapeRotateStrategy.h" #include "ShapeShearStrategy.h" #include "ShapeResizeStrategy.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 "kis_action_registry.h" #include +#include #include #include #include #include #include #include #define HANDLE_DISTANCE 10 class NopInteractionStrategy : public KoInteractionStrategy { public: - explicit NopInteractionStrategy(KoToolBase *parent) - : KoInteractionStrategy(parent) + explicit NopInteractionStrategy(KoToolBase *parent) + : KoInteractionStrategy(parent) { } KUndo2Command *createCommand() override { return 0; } void handleMouseMove(const QPointF & /*mouseLocation*/, Qt::KeyboardModifiers /*modifiers*/) override {} void finishInteraction(Qt::KeyboardModifiers /*modifiers*/) override {} }; class SelectionHandler : public KoToolSelection { public: SelectionHandler(DefaultTool *parent) : KoToolSelection(parent) , m_selection(parent->koSelection()) { - Q_ASSERT(m_selection); } bool hasSelection() override { - return m_selection->count(); + if (m_selection) { + return m_selection->count(); + } + return false; } private: - KoSelection *m_selection; + QPointer m_selection; }; DefaultTool::DefaultTool(KoCanvasBase *canvas) : KoInteractionTool(canvas) , m_lastHandle(KoFlake::NoHandle) , m_hotPosition(KoFlake::TopLeftCorner) , m_mouseWasInsideHandles(false) , m_moveCommand(0) , m_selectionHandler(new SelectionHandler(this)) , m_customEventStrategy(0) { setupActions(); QPixmap rotatePixmap, shearPixmap; rotatePixmap.load(":/cursor_rotate.png"); Q_ASSERT(!rotatePixmap.isNull()); shearPixmap.load(":/cursor_shear.png"); Q_ASSERT(!shearPixmap.isNull()); m_rotateCursors[0] = QCursor(rotatePixmap.transformed(QTransform().rotate(45))); m_rotateCursors[1] = QCursor(rotatePixmap.transformed(QTransform().rotate(90))); m_rotateCursors[2] = QCursor(rotatePixmap.transformed(QTransform().rotate(135))); m_rotateCursors[3] = QCursor(rotatePixmap.transformed(QTransform().rotate(180))); m_rotateCursors[4] = QCursor(rotatePixmap.transformed(QTransform().rotate(225))); m_rotateCursors[5] = QCursor(rotatePixmap.transformed(QTransform().rotate(270))); m_rotateCursors[6] = QCursor(rotatePixmap.transformed(QTransform().rotate(315))); m_rotateCursors[7] = QCursor(rotatePixmap); /* m_rotateCursors[0] = QCursor(Qt::RotateNCursor); m_rotateCursors[1] = QCursor(Qt::RotateNECursor); m_rotateCursors[2] = QCursor(Qt::RotateECursor); m_rotateCursors[3] = QCursor(Qt::RotateSECursor); m_rotateCursors[4] = QCursor(Qt::RotateSCursor); m_rotateCursors[5] = QCursor(Qt::RotateSWCursor); m_rotateCursors[6] = QCursor(Qt::RotateWCursor); m_rotateCursors[7] = QCursor(Qt::RotateNWCursor); */ m_shearCursors[0] = QCursor(shearPixmap); m_shearCursors[1] = QCursor(shearPixmap.transformed(QTransform().rotate(45))); m_shearCursors[2] = QCursor(shearPixmap.transformed(QTransform().rotate(90))); m_shearCursors[3] = QCursor(shearPixmap.transformed(QTransform().rotate(135))); m_shearCursors[4] = QCursor(shearPixmap.transformed(QTransform().rotate(180))); m_shearCursors[5] = QCursor(shearPixmap.transformed(QTransform().rotate(225))); m_shearCursors[6] = QCursor(shearPixmap.transformed(QTransform().rotate(270))); m_shearCursors[7] = QCursor(shearPixmap.transformed(QTransform().rotate(315))); m_sizeCursors[0] = Qt::SizeVerCursor; m_sizeCursors[1] = Qt::SizeBDiagCursor; m_sizeCursors[2] = Qt::SizeHorCursor; m_sizeCursors[3] = Qt::SizeFDiagCursor; m_sizeCursors[4] = Qt::SizeVerCursor; m_sizeCursors[5] = Qt::SizeBDiagCursor; m_sizeCursors[6] = Qt::SizeHorCursor; m_sizeCursors[7] = Qt::SizeFDiagCursor; KoShapeManager *manager = canvas->shapeManager(); connect(manager, SIGNAL(selectionChanged()), this, SLOT(updateActions())); } DefaultTool::~DefaultTool() { } bool DefaultTool::wantsAutoScroll() const { return true; } void DefaultTool::setupActions() { KisActionRegistry *actionRegistry = KisActionRegistry::instance(); QAction *actionBringToFront = actionRegistry->makeQAction("object_order_front", this); addAction("object_order_front", actionBringToFront); connect(actionBringToFront, SIGNAL(triggered()), this, SLOT(selectionBringToFront())); QAction *actionRaise = actionRegistry->makeQAction("object_order_raise", this); addAction("object_order_raise", actionRaise); connect(actionRaise, SIGNAL(triggered()), this, SLOT(selectionMoveUp())); QAction *actionLower = actionRegistry->makeQAction("object_order_lower", this); addAction("object_order_lower", actionLower); connect(actionLower, SIGNAL(triggered()), this, SLOT(selectionMoveDown())); QAction *actionSendToBack = actionRegistry->makeQAction("object_order_back", this); addAction("object_order_back", actionSendToBack); connect(actionSendToBack, SIGNAL(triggered()), this, SLOT(selectionSendToBack())); QAction *actionAlignLeft = actionRegistry->makeQAction("object_align_horizontal_left", this); addAction("object_align_horizontal_left", actionAlignLeft); connect(actionAlignLeft, SIGNAL(triggered()), this, SLOT(selectionAlignHorizontalLeft())); QAction *actionAlignCenter = actionRegistry->makeQAction("object_align_horizontal_center", this); addAction("object_align_horizontal_center", actionAlignCenter); connect(actionAlignCenter, SIGNAL(triggered()), this, SLOT(selectionAlignHorizontalCenter())); QAction *actionAlignRight = actionRegistry->makeQAction("object_align_horizontal_right", this); addAction("object_align_horizontal_right", actionAlignRight); connect(actionAlignRight, SIGNAL(triggered()), this, SLOT(selectionAlignHorizontalRight())); QAction *actionAlignTop = actionRegistry->makeQAction("object_align_vertical_top", this); addAction("object_align_vertical_top", actionAlignTop); connect(actionAlignTop, SIGNAL(triggered()), this, SLOT(selectionAlignVerticalTop())); QAction *actionAlignMiddle = actionRegistry->makeQAction("object_align_vertical_center", this); addAction("object_align_vertical_center", actionAlignMiddle); connect(actionAlignMiddle, SIGNAL(triggered()), this, SLOT(selectionAlignVerticalCenter())); QAction *actionAlignBottom = actionRegistry->makeQAction("object_align_vertical_bottom", this); addAction("object_align_vertical_bottom", actionAlignBottom); connect(actionAlignBottom, SIGNAL(triggered()), this, SLOT(selectionAlignVerticalBottom())); QAction *actionGroupBottom = actionRegistry->makeQAction("object_group", this); addAction("object_group", actionGroupBottom); connect(actionGroupBottom, SIGNAL(triggered()), this, SLOT(selectionGroup())); QAction *actionUngroupBottom = actionRegistry->makeQAction("object_ungroup", this); addAction("object_ungroup", actionUngroupBottom); connect(actionUngroupBottom, SIGNAL(triggered()), this, SLOT(selectionUngroup())); } qreal DefaultTool::rotationOfHandle(KoFlake::SelectionHandle handle, bool useEdgeRotation) { QPointF selectionCenter = koSelection()->absolutePosition(); QPointF direction; switch (handle) { case KoFlake::TopMiddleHandle: if (useEdgeRotation) { direction = koSelection()->absolutePosition(KoFlake::TopRightCorner) - koSelection()->absolutePosition(KoFlake::TopLeftCorner); } else { QPointF handlePosition = koSelection()->absolutePosition(KoFlake::TopLeftCorner); handlePosition += 0.5 * (koSelection()->absolutePosition(KoFlake::TopRightCorner) - handlePosition); direction = handlePosition - selectionCenter; } break; case KoFlake::TopRightHandle: direction = (QVector2D(koSelection()->absolutePosition(KoFlake::TopRightCorner) - koSelection()->absolutePosition(KoFlake::TopLeftCorner)).normalized() + QVector2D(koSelection()->absolutePosition(KoFlake::TopRightCorner) - koSelection()->absolutePosition(KoFlake::BottomRightCorner)).normalized()).toPointF(); break; case KoFlake::RightMiddleHandle: if (useEdgeRotation) { direction = koSelection()->absolutePosition(KoFlake::BottomRightCorner) - koSelection()->absolutePosition(KoFlake::TopRightCorner); } else { QPointF handlePosition = koSelection()->absolutePosition(KoFlake::TopRightCorner); handlePosition += 0.5 * (koSelection()->absolutePosition(KoFlake::BottomRightCorner) - handlePosition); direction = handlePosition - selectionCenter; } break; case KoFlake::BottomRightHandle: direction = (QVector2D(koSelection()->absolutePosition(KoFlake::BottomRightCorner) - koSelection()->absolutePosition(KoFlake::BottomLeftCorner)).normalized() + QVector2D(koSelection()->absolutePosition(KoFlake::BottomRightCorner) - koSelection()->absolutePosition(KoFlake::TopRightCorner)).normalized()).toPointF(); break; case KoFlake::BottomMiddleHandle: if (useEdgeRotation) { direction = koSelection()->absolutePosition(KoFlake::BottomLeftCorner) - koSelection()->absolutePosition(KoFlake::BottomRightCorner); } else { QPointF handlePosition = koSelection()->absolutePosition(KoFlake::BottomLeftCorner); handlePosition += 0.5 * (koSelection()->absolutePosition(KoFlake::BottomRightCorner) - handlePosition); direction = handlePosition - selectionCenter; } break; case KoFlake::BottomLeftHandle: direction = koSelection()->absolutePosition(KoFlake::BottomLeftCorner) - selectionCenter; direction = (QVector2D(koSelection()->absolutePosition(KoFlake::BottomLeftCorner) - koSelection()->absolutePosition(KoFlake::BottomRightCorner)).normalized() + QVector2D(koSelection()->absolutePosition(KoFlake::BottomLeftCorner) - koSelection()->absolutePosition(KoFlake::TopLeftCorner)).normalized()).toPointF(); break; case KoFlake::LeftMiddleHandle: if (useEdgeRotation) { direction = koSelection()->absolutePosition(KoFlake::TopLeftCorner) - koSelection()->absolutePosition(KoFlake::BottomLeftCorner); } else { QPointF handlePosition = koSelection()->absolutePosition(KoFlake::TopLeftCorner); handlePosition += 0.5 * (koSelection()->absolutePosition(KoFlake::BottomLeftCorner) - handlePosition); direction = handlePosition - selectionCenter; } break; case KoFlake::TopLeftHandle: direction = koSelection()->absolutePosition(KoFlake::TopLeftCorner) - selectionCenter; direction = (QVector2D(koSelection()->absolutePosition(KoFlake::TopLeftCorner) - koSelection()->absolutePosition(KoFlake::TopRightCorner)).normalized() + QVector2D(koSelection()->absolutePosition(KoFlake::TopLeftCorner) - koSelection()->absolutePosition(KoFlake::BottomLeftCorner)).normalized()).toPointF(); break; case KoFlake::NoHandle: return 0.0; break; } qreal rotation = atan2(direction.y(), direction.x()) * 180.0 / M_PI; switch (handle) { case KoFlake::TopMiddleHandle: if (useEdgeRotation) { rotation -= 0.0; } else { rotation -= 270.0; } break; case KoFlake::TopRightHandle: rotation -= 315.0; break; case KoFlake::RightMiddleHandle: if (useEdgeRotation) { rotation -= 90.0; } else { rotation -= 0.0; } break; case KoFlake::BottomRightHandle: rotation -= 45.0; break; case KoFlake::BottomMiddleHandle: if (useEdgeRotation) { rotation -= 180.0; } else { rotation -= 90.0; } break; case KoFlake::BottomLeftHandle: rotation -= 135.0; break; case KoFlake::LeftMiddleHandle: if (useEdgeRotation) { rotation -= 270.0; } else { rotation -= 180.0; } break; case KoFlake::TopLeftHandle: rotation -= 225.0; break; case KoFlake::NoHandle: break; } if (rotation < 0.0) { rotation += 360.0; } return rotation; } void DefaultTool::updateCursor() { QCursor cursor = Qt::ArrowCursor; QString statusText; - - if (koSelection()->count() > 0) { // has a selection + KoSelection * selection = koSelection(); + if (selection && selection->count() > 0) { // has a selection bool editable = editableShapesCount(koSelection()->selectedShapes(KoFlake::StrippedSelection)); if (!m_mouseWasInsideHandles) { m_angle = rotationOfHandle(m_lastHandle, true); int rotOctant = 8 + int(8.5 + m_angle / 45); bool rotateHandle = false; bool shearHandle = false; switch (m_lastHandle) { case KoFlake::TopMiddleHandle: cursor = m_shearCursors[(0 + rotOctant) % 8]; shearHandle = true; break; case KoFlake::TopRightHandle: cursor = m_rotateCursors[(1 + rotOctant) % 8]; rotateHandle = true; break; case KoFlake::RightMiddleHandle: cursor = m_shearCursors[(2 + rotOctant) % 8]; shearHandle = true; break; case KoFlake::BottomRightHandle: cursor = m_rotateCursors[(3 + rotOctant) % 8]; rotateHandle = true; break; case KoFlake::BottomMiddleHandle: cursor = m_shearCursors[(4 + rotOctant) % 8]; shearHandle = true; break; case KoFlake::BottomLeftHandle: cursor = m_rotateCursors[(5 + rotOctant) % 8]; rotateHandle = true; break; case KoFlake::LeftMiddleHandle: cursor = m_shearCursors[(6 + rotOctant) % 8]; shearHandle = true; break; case KoFlake::TopLeftHandle: cursor = m_rotateCursors[(7 + rotOctant) % 8]; rotateHandle = true; break; case KoFlake::NoHandle: cursor = Qt::ArrowCursor; break; } if (rotateHandle) { statusText = i18n("Left click rotates around center, right click around highlighted position."); } if (shearHandle) { statusText = i18n("Click and drag to shear selection."); } } else { statusText = i18n("Click and drag to resize selection."); m_angle = rotationOfHandle(m_lastHandle, false); int rotOctant = 8 + int(8.5 + m_angle / 45); bool cornerHandle = false; switch (m_lastHandle) { case KoFlake::TopMiddleHandle: cursor = m_sizeCursors[(0 + rotOctant) % 8]; break; case KoFlake::TopRightHandle: cursor = m_sizeCursors[(1 + rotOctant) % 8]; cornerHandle = true; break; case KoFlake::RightMiddleHandle: cursor = m_sizeCursors[(2 + rotOctant) % 8]; break; case KoFlake::BottomRightHandle: cursor = m_sizeCursors[(3 + rotOctant) % 8]; cornerHandle = true; break; case KoFlake::BottomMiddleHandle: cursor = m_sizeCursors[(4 + rotOctant) % 8]; break; case KoFlake::BottomLeftHandle: cursor = m_sizeCursors[(5 + rotOctant) % 8]; cornerHandle = true; break; case KoFlake::LeftMiddleHandle: cursor = m_sizeCursors[(6 + rotOctant) % 8]; break; case KoFlake::TopLeftHandle: cursor = m_sizeCursors[(7 + rotOctant) % 8]; cornerHandle = true; break; case KoFlake::NoHandle: cursor = Qt::SizeAllCursor; statusText = i18n("Click and drag to move selection."); break; } if (cornerHandle) { statusText = i18n("Click and drag to resize selection. Middle click to set highlighted position."); } } if (!editable) { cursor = Qt::ArrowCursor; } } else { // there used to be guides... :'''( } useCursor(cursor); if (currentStrategy() == 0) { emit statusTextChanged(statusText); } } void DefaultTool::paint(QPainter &painter, const KoViewConverter &converter) { KoInteractionTool::paint(painter, converter); - if (currentStrategy() == 0 && koSelection()->count() > 0) { + if (currentStrategy() == 0 && koSelection() && koSelection()->count() > 0) { SelectionDecorator decorator(m_mouseWasInsideHandles ? m_lastHandle : KoFlake::NoHandle, true, true); decorator.setSelection(koSelection()); decorator.setHandleRadius(handleRadius()); decorator.setHotPosition(m_hotPosition); decorator.paint(painter, converter); } painter.save(); KoShape::applyConversion(painter, converter); canvas()->snapGuide()->paint(painter, converter); painter.restore(); } void DefaultTool::mousePressEvent(KoPointerEvent *event) { KoInteractionTool::mousePressEvent(event); updateCursor(); } void DefaultTool::mouseMoveEvent(KoPointerEvent *event) { KoInteractionTool::mouseMoveEvent(event); - if (currentStrategy() == 0 && koSelection()->count() > 0) { + if (currentStrategy() == 0 && koSelection() && koSelection()->count() > 0) { QRectF bound = handlesSize(); if (bound.contains(event->point)) { bool inside; KoFlake::SelectionHandle newDirection = handleAt(event->point, &inside); if (inside != m_mouseWasInsideHandles || m_lastHandle != newDirection) { m_lastHandle = newDirection; m_mouseWasInsideHandles = inside; //repaintDecorations(); } } else { /*if (m_lastHandle != KoFlake::NoHandle) repaintDecorations(); */ m_lastHandle = KoFlake::NoHandle; m_mouseWasInsideHandles = false; // there used to be guides... :'''( } } else { // there used to be guides... :'''( } updateCursor(); } QRectF DefaultTool::handlesSize() { QRectF bound = koSelection()->boundingRect(); // expansion Border if (!canvas() || !canvas()->viewConverter()) { return bound; } QPointF border = canvas()->viewConverter()->viewToDocument(QPointF(HANDLE_DISTANCE, HANDLE_DISTANCE)); bound.adjust(-border.x(), -border.y(), border.x(), border.y()); return bound; } void DefaultTool::mouseReleaseEvent(KoPointerEvent *event) { KoInteractionTool::mouseReleaseEvent(event); updateCursor(); } void DefaultTool::mouseDoubleClickEvent(KoPointerEvent *event) { QList shapes; Q_FOREACH (KoShape *shape, koSelection()->selectedShapes()) { if (shape->boundingRect().contains(event->point) && // first 'cheap' check shape->outline().contains(event->point)) { // this is more expensive but weeds out the almost hits shapes.append(shape); } } if (shapes.count() == 0) { // nothing in the selection was clicked on. KoShape *shape = canvas()->shapeManager()->shapeAt(event->point, KoFlake::ShapeOnTop); if (shape) { shapes.append(shape); } // there used to be guides... :'''( } QList shapes2; foreach (KoShape *shape, shapes) { QSet delegates = shape->toolDelegates(); if (delegates.isEmpty()) { shapes2.append(shape); } else { foreach (KoShape *delegatedShape, delegates) { shapes2.append(delegatedShape); } } } KoToolManager::instance()->switchToolRequested( KoToolManager::instance()->preferredToolForSelection(shapes2)); } bool DefaultTool::moveSelection(int direction, Qt::KeyboardModifiers modifiers) { qreal x = 0.0, y = 0.0; if (direction == Qt::Key_Left) { x = -5; } else if (direction == Qt::Key_Right) { x = 5; } else if (direction == Qt::Key_Up) { y = -5; } else if (direction == Qt::Key_Down) { y = 5; } if (x != 0.0 || y != 0.0) { // actually move if ((modifiers & Qt::ShiftModifier) != 0) { x *= 10; y *= 10; } else if ((modifiers & Qt::AltModifier) != 0) { // more precise x /= 5; y /= 5; } QList prevPos; QList newPos; QList shapes; Q_FOREACH (KoShape *shape, koSelection()->selectedShapes(KoFlake::TopLevelSelection)) { if (shape->isGeometryProtected()) { continue; } shapes.append(shape); QPointF p = shape->position(); prevPos.append(p); p.setX(p.x() + x); p.setY(p.y() + y); newPos.append(p); } if (shapes.count() > 0) { // use a timeout to make sure we don't reuse a command possibly deleted by the commandHistory if (m_lastUsedMoveCommand.msecsTo(QTime::currentTime()) > 5000) { m_moveCommand = 0; } if (m_moveCommand) { // alter previous instead of creating new one. m_moveCommand->setNewPositions(newPos); m_moveCommand->redo(); } else { m_moveCommand = new KoShapeMoveCommand(shapes, prevPos, newPos); canvas()->addCommand(m_moveCommand); } m_lastUsedMoveCommand = QTime::currentTime(); return true; } } return false; } void DefaultTool::keyPressEvent(QKeyEvent *event) { KoInteractionTool::keyPressEvent(event); if (currentStrategy() == 0) { switch (event->key()) { case Qt::Key_Left: case Qt::Key_Right: case Qt::Key_Up: case Qt::Key_Down: if (moveSelection(event->key(), event->modifiers())) { event->accept(); } break; case Qt::Key_1: case Qt::Key_2: case Qt::Key_3: case Qt::Key_4: case Qt::Key_5: canvas()->resourceManager()->setResource(HotPosition, event->key() - Qt::Key_1); event->accept(); break; default: return; } } } void DefaultTool::customMoveEvent(KoPointerEvent *event) { - if (!koSelection()->count()) { + if (koSelection() && koSelection()->count() <= 0) { event->ignore(); return; } int move = qMax(qAbs(event->x()), qAbs(event->y())); int zoom = qAbs(event->z()); int rotate = qAbs(event->rotationZ()); const int threshold = 2; if (move < threshold && zoom < threshold && rotate < threshold) { if (m_customEventStrategy) { m_customEventStrategy->finishInteraction(event->modifiers()); KUndo2Command *command = m_customEventStrategy->createCommand(); if (command) { canvas()->addCommand(command); } delete m_customEventStrategy; m_customEventStrategy = 0; repaintDecorations(); } event->accept(); return; } // check if the z-movement is dominant if (zoom > move && zoom > rotate) { // zoom if (!m_customEventStrategy) { m_customEventStrategy = new ShapeResizeStrategy(this, event->point, KoFlake::TopLeftHandle); } } else if (move > zoom && move > rotate) { // check if x-/y-movement is dominant // move if (!m_customEventStrategy) { m_customEventStrategy = new ShapeMoveStrategy(this, event->point); } } else if (rotate > zoom && rotate > move) { // rotation is dominant // rotate if (!m_customEventStrategy) { m_customEventStrategy = new ShapeRotateStrategy(this, event->point, event->buttons()); } } if (m_customEventStrategy) { m_customEventStrategy->handleCustomEvent(event); } event->accept(); } void DefaultTool::repaintDecorations() { - Q_ASSERT(koSelection()); - if (koSelection()->count() > 0) { + if (koSelection() && koSelection()->count() > 0) { canvas()->updateCanvas(handlesSize()); } } void DefaultTool::copy() const { QList shapes = canvas()->shapeManager()->selection()->selectedShapes(KoFlake::TopLevelSelection); if (!shapes.empty()) { KoShapeOdfSaveHelper saveHelper(shapes); KoDrag drag; drag.setOdf(KoOdf::mimeType(KoOdf::Text), saveHelper); drag.addToClipboard(); } } void DefaultTool::deleteSelection() { QList shapes; foreach (KoShape *s, canvas()->shapeManager()->selection()->selectedShapes(KoFlake::TopLevelSelection)) { if (s->isGeometryProtected()) { continue; } shapes << s; } if (!shapes.empty()) { canvas()->addCommand(canvas()->shapeController()->removeShapes(shapes)); } } bool DefaultTool::paste() { // we no longer have to do anything as tool Proxy will do it for us return false; } QStringList DefaultTool::supportedPasteMimeTypes() const { QStringList list; list << KoOdf::mimeType(KoOdf::Text); return list; } KoSelection *DefaultTool::koSelection() { Q_ASSERT(canvas()); Q_ASSERT(canvas()->shapeManager()); return canvas()->shapeManager()->selection(); } KoFlake::SelectionHandle DefaultTool::handleAt(const QPointF &point, bool *innerHandleMeaning) { // check for handles in this order; meaning that when handles overlap the one on top is chosen static const KoFlake::SelectionHandle handleOrder[] = { KoFlake::BottomRightHandle, KoFlake::TopLeftHandle, KoFlake::BottomLeftHandle, KoFlake::TopRightHandle, KoFlake::BottomMiddleHandle, KoFlake::RightMiddleHandle, KoFlake::LeftMiddleHandle, KoFlake::TopMiddleHandle, KoFlake::NoHandle }; - if (koSelection()->count() == 0) { + if (koSelection() && koSelection()->count() == 0) { return KoFlake::NoHandle; } recalcSelectionBox(); const KoViewConverter *converter = canvas()->viewConverter(); if (!converter) { return KoFlake::NoHandle; } if (innerHandleMeaning != 0) { QPainterPath path; path.addPolygon(m_selectionOutline); *innerHandleMeaning = path.contains(point) || path.intersects(handlePaintRect(point)); } for (int i = 0; i < KoFlake::NoHandle; ++i) { KoFlake::SelectionHandle handle = handleOrder[i]; QPointF pt = converter->documentToView(point) - converter->documentToView(m_selectionBox[handle]); // if just inside the outline if (qAbs(pt.x()) < HANDLE_DISTANCE && qAbs(pt.y()) < HANDLE_DISTANCE) { if (innerHandleMeaning != 0) { if (qAbs(pt.x()) < 4 && qAbs(pt.y()) < 4) { *innerHandleMeaning = true; } } return handle; } } return KoFlake::NoHandle; } void DefaultTool::recalcSelectionBox() { + if (!koSelection()) { + return; + } + if (koSelection()->count() == 0) { return; } if (koSelection()->count() > 1) { QTransform matrix = koSelection()->absoluteTransformation(0); m_selectionOutline = matrix.map(QPolygonF(QRectF(QPointF(0, 0), koSelection()->size()))); m_angle = 0.0; //koSelection()->rotation(); } else { QTransform matrix = koSelection()->firstSelectedShape()->absoluteTransformation(0); m_selectionOutline = matrix.map(QPolygonF(QRectF(QPointF(0, 0), koSelection()->firstSelectedShape()->size()))); m_angle = 0.0; //koSelection()->firstSelectedShape()->rotation(); } QPolygonF outline = m_selectionOutline; //shorter name in the following :) m_selectionBox[KoFlake::TopMiddleHandle] = (outline.value(0) + outline.value(1)) / 2; m_selectionBox[KoFlake::TopRightHandle] = outline.value(1); m_selectionBox[KoFlake::RightMiddleHandle] = (outline.value(1) + outline.value(2)) / 2; m_selectionBox[KoFlake::BottomRightHandle] = outline.value(2); m_selectionBox[KoFlake::BottomMiddleHandle] = (outline.value(2) + outline.value(3)) / 2; m_selectionBox[KoFlake::BottomLeftHandle] = outline.value(3); m_selectionBox[KoFlake::LeftMiddleHandle] = (outline.value(3) + outline.value(0)) / 2; m_selectionBox[KoFlake::TopLeftHandle] = outline.value(0); if (koSelection()->count() == 1) { #if 0 // TODO detect mirroring KoShape *s = koSelection()->firstSelectedShape(); if (s->scaleX() < 0) { // vertically mirrored: swap left / right qSwap(m_selectionBox[KoFlake::TopLeftHandle], m_selectionBox[KoFlake::TopRightHandle]); qSwap(m_selectionBox[KoFlake::LeftMiddleHandle], m_selectionBox[KoFlake::RightMiddleHandle]); qSwap(m_selectionBox[KoFlake::BottomLeftHandle], m_selectionBox[KoFlake::BottomRightHandle]); } if (s->scaleY() < 0) { // vertically mirrored: swap top / bottom qSwap(m_selectionBox[KoFlake::TopLeftHandle], m_selectionBox[KoFlake::BottomLeftHandle]); qSwap(m_selectionBox[KoFlake::TopMiddleHandle], m_selectionBox[KoFlake::BottomMiddleHandle]); qSwap(m_selectionBox[KoFlake::TopRightHandle], m_selectionBox[KoFlake::BottomRightHandle]); } #endif } } void DefaultTool::activate(ToolActivation, const QSet &) { m_mouseWasInsideHandles = false; m_lastHandle = KoFlake::NoHandle; useCursor(Qt::ArrowCursor); repaintDecorations(); updateActions(); } void DefaultTool::selectionAlignHorizontalLeft() { selectionAlign(KoShapeAlignCommand::HorizontalLeftAlignment); } void DefaultTool::selectionAlignHorizontalCenter() { selectionAlign(KoShapeAlignCommand::HorizontalCenterAlignment); } void DefaultTool::selectionAlignHorizontalRight() { selectionAlign(KoShapeAlignCommand::HorizontalRightAlignment); } void DefaultTool::selectionAlignVerticalTop() { selectionAlign(KoShapeAlignCommand::VerticalTopAlignment); } void DefaultTool::selectionAlignVerticalCenter() { selectionAlign(KoShapeAlignCommand::VerticalCenterAlignment); } void DefaultTool::selectionAlignVerticalBottom() { selectionAlign(KoShapeAlignCommand::VerticalBottomAlignment); } void DefaultTool::selectionGroup() { KoSelection *selection = koSelection(); if (!selection) { return; } QList selectedShapes = selection->selectedShapes(KoFlake::TopLevelSelection); QList groupedShapes; // only group shapes with an unselected parent foreach (KoShape *shape, selectedShapes) { if (!selectedShapes.contains(shape->parent()) && shape->isEditable()) { groupedShapes << shape; } } KoShapeGroup *group = new KoShapeGroup(); // TODO what if only one shape is left? KUndo2Command *cmd = new KUndo2Command(kundo2_i18n("Group shapes")); canvas()->shapeController()->addShapeDirect(group, cmd); KoShapeGroupCommand::createCommand(group, groupedShapes, cmd); canvas()->addCommand(cmd); // update selection so we can ungroup immediately again selection->deselectAll(); selection->select(group); } void DefaultTool::selectionUngroup() { KoSelection *selection = canvas()->shapeManager()->selection(); if (!selection) { return; } QList selectedShapes = selection->selectedShapes(KoFlake::TopLevelSelection); QList containerSet; // only ungroup shape groups with an unselected parent foreach (KoShape *shape, selectedShapes) { if (!selectedShapes.contains(shape->parent()) && shape->isEditable()) { containerSet << shape; } } KUndo2Command *cmd = 0; // add a ungroup command for each found shape container to the macro command Q_FOREACH (KoShape *shape, containerSet) { KoShapeGroup *group = dynamic_cast(shape); if (group) { cmd = cmd ? cmd : new KUndo2Command(kundo2_i18n("Ungroup shapes")); new KoShapeUngroupCommand(group, group->shapes(), group->parent() ? QList() : canvas()->shapeManager()->topLevelShapes(), cmd); canvas()->shapeController()->removeShape(group, cmd); } } if (cmd) { canvas()->addCommand(cmd); } } void DefaultTool::selectionAlign(KoShapeAlignCommand::Align align) { KoSelection *selection = canvas()->shapeManager()->selection(); if (!selection) { return; } QList selectedShapes = selection->selectedShapes(KoFlake::TopLevelSelection); if (selectedShapes.count() < 1) { return; } QList editableShapes = filterEditableShapes(selectedShapes); // TODO add an option to the widget so that one can align to the page // with multiple selected shapes too QRectF bb; // single selected shape is automatically aligned to document rect if (editableShapes.count() == 1) { if (!canvas()->resourceManager()->hasResource(KoCanvasResourceManager::PageSize)) { return; } bb = QRectF(QPointF(0, 0), canvas()->resourceManager()->sizeResource(KoCanvasResourceManager::PageSize)); } else { Q_FOREACH (KoShape *shape, editableShapes) { bb |= shape->boundingRect(); } } KoShapeAlignCommand *cmd = new KoShapeAlignCommand(editableShapes, align, bb); canvas()->addCommand(cmd); selection->updateSizeAndPosition(); } void DefaultTool::selectionBringToFront() { selectionReorder(KoShapeReorderCommand::BringToFront); } void DefaultTool::selectionMoveUp() { selectionReorder(KoShapeReorderCommand::RaiseShape); } void DefaultTool::selectionMoveDown() { selectionReorder(KoShapeReorderCommand::LowerShape); } void DefaultTool::selectionSendToBack() { selectionReorder(KoShapeReorderCommand::SendToBack); } void DefaultTool::selectionReorder(KoShapeReorderCommand::MoveShapeType order) { KoSelection *selection = canvas()->shapeManager()->selection(); if (!selection) { return; } QList selectedShapes = selection->selectedShapes(KoFlake::TopLevelSelection); if (selectedShapes.count() < 1) { return; } QList editableShapes = filterEditableShapes(selectedShapes); if (editableShapes.count() < 1) { return; } KUndo2Command *cmd = KoShapeReorderCommand::createCommand(editableShapes, canvas()->shapeManager(), order); if (cmd) { canvas()->addCommand(cmd); } } QList > DefaultTool::createOptionWidgets() { QList > widgets; DefaultToolArrangeWidget *defaultArrange = new DefaultToolArrangeWidget(this); defaultArrange->setWindowTitle(i18n("Arrange")); widgets.append(defaultArrange); DefaultToolWidget *defaultTool = new DefaultToolWidget(this); defaultTool->setWindowTitle(i18n("Geometry")); widgets.append(defaultTool); KoStrokeConfigWidget *strokeWidget = new KoStrokeConfigWidget(0); strokeWidget->setWindowTitle(i18n("Line")); strokeWidget->setCanvas(canvas()); widgets.append(strokeWidget); KoFillConfigWidget *fillWidget = new KoFillConfigWidget(0); fillWidget->setWindowTitle(i18n("Fill")); fillWidget->setCanvas(canvas()); widgets.append(fillWidget); KoShadowConfigWidget *shadowWidget = new KoShadowConfigWidget(0); shadowWidget->setWindowTitle(i18n("Shadow")); shadowWidget->setCanvas(canvas()); widgets.append(shadowWidget); return widgets; } void DefaultTool::canvasResourceChanged(int key, const QVariant &res) { if (key == HotPosition) { m_hotPosition = static_cast(res.toInt()); repaintDecorations(); } } KoInteractionStrategy *DefaultTool::createStrategy(KoPointerEvent *event) { // reset the move by keys when a new strategy is created otherwise we might change the // command after a new command was added. This happend when you where faster than the timer. m_moveCommand = 0; KoShapeManager *shapeManager = canvas()->shapeManager(); KoSelection *select = shapeManager->selection(); bool insideSelection; KoFlake::SelectionHandle handle = handleAt(event->point, &insideSelection); bool editableShape = editableShapesCount(select->selectedShapes()); if (event->buttons() & Qt::MidButton) { // change the hot selection position when middle clicking on a handle KoFlake::Position newHotPosition = m_hotPosition; switch (handle) { case KoFlake::TopLeftHandle: newHotPosition = KoFlake::TopLeftCorner; break; case KoFlake::TopRightHandle: newHotPosition = KoFlake::TopRightCorner; break; case KoFlake::BottomLeftHandle: newHotPosition = KoFlake::BottomLeftCorner; break; case KoFlake::BottomRightHandle: newHotPosition = KoFlake::BottomRightCorner; break; default: { // check if we had hit the center point const KoViewConverter *converter = canvas()->viewConverter(); QPointF pt = converter->documentToView(event->point - select->absolutePosition()); if (qAbs(pt.x()) < HANDLE_DISTANCE && qAbs(pt.y()) < HANDLE_DISTANCE) { newHotPosition = KoFlake::CenteredPosition; } break; } } if (m_hotPosition != newHotPosition) { canvas()->resourceManager()->setResource(HotPosition, newHotPosition); } return 0; } bool selectMultiple = event->modifiers() & Qt::ControlModifier; bool selectNextInStack = event->modifiers() & Qt::ShiftModifier; if (editableShape) { // manipulation of selected shapes goes first if (handle != KoFlake::NoHandle) { if (event->buttons() == Qt::LeftButton) { // resizing or shearing only with left mouse button if (insideSelection) { return new ShapeResizeStrategy(this, event->point, handle); } if (handle == KoFlake::TopMiddleHandle || handle == KoFlake::RightMiddleHandle || handle == KoFlake::BottomMiddleHandle || handle == KoFlake::LeftMiddleHandle) { return new ShapeShearStrategy(this, event->point, handle); } } // rotating is allowed for rigth mouse button too if (handle == KoFlake::TopLeftHandle || handle == KoFlake::TopRightHandle || handle == KoFlake::BottomLeftHandle || handle == KoFlake::BottomRightHandle) { return new ShapeRotateStrategy(this, event->point, event->buttons()); } } if (!(selectMultiple || selectNextInStack) && event->buttons() == Qt::LeftButton) { const QPainterPath outlinePath = select->transformation().map(select->outline()); if (outlinePath.contains(event->point) || outlinePath.intersects(handlePaintRect(event->point))) { return new ShapeMoveStrategy(this, event->point); } } } if ((event->buttons() & Qt::LeftButton) == 0) { return 0; // Nothing to do for middle/right mouse button } KoShape *shape = shapeManager->shapeAt(event->point, selectNextInStack ? KoFlake::NextUnselected : KoFlake::ShapeOnTop); if (!shape && handle == KoFlake::NoHandle) { if (!selectMultiple) { repaintDecorations(); select->deselectAll(); } return new KoShapeRubberSelectStrategy(this, event->point); } if (select->isSelected(shape)) { if (selectMultiple) { repaintDecorations(); select->deselect(shape); } } else if (handle == KoFlake::NoHandle) { // clicked on shape which is not selected repaintDecorations(); if (!selectMultiple) { shapeManager->selection()->deselectAll(); } select->select(shape, selectNextInStack ? false : true); repaintDecorations(); // tablet selection isn't precise and may lead to a move, preventing that if (event->isTabletEvent()) { return new NopInteractionStrategy(this); } return new ShapeMoveStrategy(this, event->point); } return 0; } void DefaultTool::updateActions() { KoSelection *selection(koSelection()); if (!selection) { action("object_order_front")->setEnabled(false); action("object_order_raise")->setEnabled(false); action("object_order_lower")->setEnabled(false); action("object_order_back")->setEnabled(false); action("object_align_horizontal_left")->setEnabled(false); action("object_align_horizontal_center")->setEnabled(false); action("object_align_horizontal_right")->setEnabled(false); action("object_align_vertical_top")->setEnabled(false); action("object_align_vertical_center")->setEnabled(false); action("object_align_vertical_bottom")->setEnabled(false); action("object_group")->setEnabled(false); action("object_ungroup")->setEnabled(false); return; } QList editableShapes = filterEditableShapes(selection->selectedShapes(KoFlake::TopLevelSelection)); bool enable = editableShapes.count() > 0; action("object_order_front")->setEnabled(enable); action("object_order_raise")->setEnabled(enable); action("object_order_lower")->setEnabled(enable); action("object_order_back")->setEnabled(enable); enable = (editableShapes.count() > 1) || (enable && canvas()->resourceManager()->hasResource(KoCanvasResourceManager::PageSize)); action("object_align_horizontal_left")->setEnabled(enable); action("object_align_horizontal_center")->setEnabled(enable); action("object_align_horizontal_right")->setEnabled(enable); action("object_align_vertical_top")->setEnabled(enable); action("object_align_vertical_center")->setEnabled(enable); action("object_align_vertical_bottom")->setEnabled(enable); action("object_group")->setEnabled(editableShapes.count() > 1); bool groupShape = false; foreach (KoShape *shape, editableShapes) { if (dynamic_cast(shape)) { groupShape = true; break; } } action("object_ungroup")->setEnabled(groupShape); emit selectionChanged(selection->count()); } KoToolSelection *DefaultTool::selection() { return m_selectionHandler; } QList DefaultTool::filterEditableShapes(const QList &shapes) { QList editableShapes; Q_FOREACH (KoShape *shape, shapes) { if (shape->isEditable()) { editableShapes.append(shape); } } return editableShapes; } uint DefaultTool::editableShapesCount(const QList &shapes) { uint count = 0; Q_FOREACH (KoShape *shape, shapes) { if (shape->isEditable()) { count++; } } return count; } diff --git a/plugins/tools/defaulttool/defaulttool/DefaultToolWidget.cpp b/plugins/tools/defaulttool/defaulttool/DefaultToolWidget.cpp index 2c6ea52a5f..5c5b0e7b91 100644 --- a/plugins/tools/defaulttool/defaulttool/DefaultToolWidget.cpp +++ b/plugins/tools/defaulttool/defaulttool/DefaultToolWidget.cpp @@ -1,277 +1,280 @@ /* This file is part of the KDE project * Copyright (C) 2007 Martin Pfeiffer * Copyright (C) 2007 Jan Hambrecht Copyright (C) 2008 Thorsten Zachmann * Copyright (C) 2010 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 "DefaultToolWidget.h" #include "DefaultTool.h" #include #include #include #include #include #include #include #include #include #include #include "SelectionDecorator.h" #include "DefaultToolTransformWidget.h" #include #include #include #include #include #include #include #include DefaultToolWidget::DefaultToolWidget(KoInteractionTool *tool, QWidget *parent) : QWidget(parent) , m_tool(tool) , m_blockSignals(false) { setupUi(this); setUnit(m_tool->canvas()->unit()); aspectButton->setKeepAspectRatio(false); updatePosition(); updateSize(); connect(positionSelector, SIGNAL(positionSelected(KoFlake::Position)), this, SLOT(positionSelected(KoFlake::Position))); connect(positionXSpinBox, SIGNAL(editingFinished()), this, SLOT(positionHasChanged())); connect(positionYSpinBox, SIGNAL(editingFinished()), this, SLOT(positionHasChanged())); connect(widthSpinBox, SIGNAL(editingFinished()), this, SLOT(sizeHasChanged())); connect(heightSpinBox, SIGNAL(editingFinished()), this, SLOT(sizeHasChanged())); KoSelection *selection = m_tool->canvas()->shapeManager()->selection(); connect(selection, SIGNAL(selectionChanged()), this, SLOT(updatePosition())); connect(selection, SIGNAL(selectionChanged()), this, SLOT(updateSize())); KoShapeManager *manager = m_tool->canvas()->shapeManager(); connect(manager, SIGNAL(selectionContentChanged()), this, SLOT(updatePosition())); connect(manager, SIGNAL(selectionContentChanged()), this, SLOT(updateSize())); connect(m_tool->canvas()->resourceManager(), SIGNAL(canvasResourceChanged(int,QVariant)), this, SLOT(resourceChanged(int,QVariant))); connect(aspectButton, SIGNAL(keepAspectRatioChanged(bool)), this, SLOT(aspectButtonToggled(bool))); } void DefaultToolWidget::positionSelected(KoFlake::Position position) { m_tool->canvas()->resourceManager()->setResource(DefaultTool::HotPosition, QVariant(position)); updatePosition(); } void DefaultToolWidget::updatePosition() { QPointF selPosition(0, 0); KoFlake::Position position = positionSelector->position(); KoSelection *selection = m_tool->canvas()->shapeManager()->selection(); - if (selection->count()) { + if (selection && selection->count()) { selPosition = selection->absolutePosition(position); } - positionXSpinBox->setEnabled(selection->count()); - positionYSpinBox->setEnabled(selection->count()); + positionXSpinBox->setEnabled(selection && selection->count()); + positionYSpinBox->setEnabled(selection && selection->count()); if (m_blockSignals) { return; } m_blockSignals = true; positionXSpinBox->changeValue(selPosition.x()); positionYSpinBox->changeValue(selPosition.y()); QList selectedShapes = selection->selectedShapes(KoFlake::TopLevelSelection); bool aspectLocked = false; foreach (KoShape *shape, selectedShapes) { aspectLocked = aspectLocked | shape->keepAspectRatio(); } aspectButton->setKeepAspectRatio(aspectLocked); m_blockSignals = false; } void DefaultToolWidget::positionHasChanged() { KoSelection *selection = m_tool->canvas()->shapeManager()->selection(); - if (!selection->count()) { + if (!selection || selection->count() <= 0) { return; } KoFlake::Position position = positionSelector->position(); QPointF newPos(positionXSpinBox->value(), positionYSpinBox->value()); QPointF oldPos = selection->absolutePosition(position); if (oldPos == newPos) { return; } QList selectedShapes = selection->selectedShapes(KoFlake::TopLevelSelection); QPointF moveBy = newPos - oldPos; QList oldPositions; QList newPositions; Q_FOREACH (KoShape *shape, selectedShapes) { oldPositions.append(shape->position()); newPositions.append(shape->position() + moveBy); } selection->setPosition(selection->position() + moveBy); m_tool->canvas()->addCommand(new KoShapeMoveCommand(selectedShapes, oldPositions, newPositions)); updatePosition(); } void DefaultToolWidget::updateSize() { QSizeF selSize(0, 0); KoSelection *selection = m_tool->canvas()->shapeManager()->selection(); - uint selectionCount = selection->count(); + uint selectionCount = 0; + if (selection && selection->count()) { + selectionCount = selection->count(); + } if (selectionCount) { selSize = selection->boundingRect().size(); } widthSpinBox->setEnabled(selectionCount); heightSpinBox->setEnabled(selectionCount); if (m_blockSignals) { return; } m_blockSignals = true; widthSpinBox->changeValue(selSize.width()); heightSpinBox->changeValue(selSize.height()); m_blockSignals = false; } void DefaultToolWidget::sizeHasChanged() { if (aspectButton->hasFocus()) { return; } if (m_blockSignals) { return; } QSizeF newSize(widthSpinBox->value(), heightSpinBox->value()); KoSelection *selection = m_tool->canvas()->shapeManager()->selection(); QRectF rect = selection->boundingRect(); if (aspectButton->keepAspectRatio()) { qreal aspect = rect.width() / rect.height(); if (rect.width() != newSize.width()) { newSize.setHeight(newSize.width() / aspect); } else if (rect.height() != newSize.height()) { newSize.setWidth(newSize.height() * aspect); } } if (rect.width() != newSize.width() || rect.height() != newSize.height()) { // get the scale/resize center from the position selector QPointF scaleCenter = selection->absolutePosition(positionSelector->position()); QTransform resizeMatrix; resizeMatrix.translate(scaleCenter.x(), scaleCenter.y()); // make sure not to divide by 0 in case the selection is a line and has no width. In this case just scale by 1. resizeMatrix.scale(rect.width() ? newSize.width() / rect.width() : 1, rect.height() ? newSize.height() / rect.height() : 1); resizeMatrix.translate(-scaleCenter.x(), -scaleCenter.y()); QList selectedShapes = selection->selectedShapes(KoFlake::StrippedSelection); QList oldSizes, newSizes; QList oldState; QList newState; Q_FOREACH (KoShape *shape, selectedShapes) { shape->update(); QSizeF oldSize = shape->size(); oldState << shape->transformation(); QTransform shapeMatrix = shape->absoluteTransformation(0); // calculate the matrix we would apply to the local shape matrix // that tells us the effective scale values we have to use for the resizing QTransform localMatrix = shapeMatrix * resizeMatrix * shapeMatrix.inverted(); // save the effective scale values, without any mirroring portion const qreal scaleX = qAbs(localMatrix.m11()); const qreal scaleY = qAbs(localMatrix.m22()); // calculate the scale matrix which is equivalent to our resizing above QTransform scaleMatrix = (QTransform().scale(scaleX, scaleY)); scaleMatrix = shapeMatrix.inverted() * scaleMatrix * shapeMatrix; // calculate the new size of the shape, using the effective scale values oldSizes << oldSize; QSizeF newSize = QSizeF(scaleX * oldSize.width(), scaleY * oldSize.height()); newSizes << newSize; shape->setSize(newSize); // apply the rest of the transformation without the resizing part shape->applyAbsoluteTransformation(scaleMatrix.inverted() * resizeMatrix); newState << shape->transformation(); } m_tool->repaintDecorations(); selection->applyAbsoluteTransformation(resizeMatrix); KUndo2Command *cmd = new KUndo2Command(kundo2_i18n("Resize")); new KoShapeSizeCommand(selectedShapes, oldSizes, newSizes, cmd); new KoShapeTransformCommand(selectedShapes, oldState, newState, cmd); m_tool->canvas()->addCommand(cmd); updateSize(); updatePosition(); } } void DefaultToolWidget::setUnit(const KoUnit &unit) { m_blockSignals = true; positionXSpinBox->setUnit(unit); positionYSpinBox->setUnit(unit); widthSpinBox->setUnit(unit); heightSpinBox->setUnit(unit); m_blockSignals = false; updatePosition(); updateSize(); } void DefaultToolWidget::resourceChanged(int key, const QVariant &res) { if (key == KoCanvasResourceManager::Unit) { setUnit(res.value()); } else if (key == DefaultTool::HotPosition) { if (res.toInt() != positionSelector->position()) { positionSelector->setPosition(static_cast(res.toInt())); updatePosition(); } } } void DefaultToolWidget::aspectButtonToggled(bool keepAspect) { if (m_blockSignals) { return; } KoSelection *selection = m_tool->canvas()->shapeManager()->selection(); foreach (KoShape *shape, selection->selectedShapes(KoFlake::TopLevelSelection)) { shape->setKeepAspectRatio(keepAspect); } } diff --git a/plugins/tools/defaulttool/defaulttool/SelectionDecorator.cpp b/plugins/tools/defaulttool/defaulttool/SelectionDecorator.cpp index 42e0a94371..ab23f8e653 100644 --- a/plugins/tools/defaulttool/defaulttool/SelectionDecorator.cpp +++ b/plugins/tools/defaulttool/defaulttool/SelectionDecorator.cpp @@ -1,161 +1,163 @@ /* This file is part of the KDE project Copyright (C) 2006 Thorsten Zachmann Copyright (C) 2006-2007 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 "SelectionDecorator.h" #include #include #include #define HANDLE_DISTANCE 10 KoFlake::Position SelectionDecorator::m_hotPosition = KoFlake::TopLeftCorner; SelectionDecorator::SelectionDecorator(KoFlake::SelectionHandle arrows, bool rotationHandles, bool shearHandles) : m_rotationHandles(rotationHandles) , m_shearHandles(shearHandles) , m_arrows(arrows) , m_handleRadius(3) , m_lineWidth(1) { } void SelectionDecorator::setSelection(KoSelection *selection) { m_selection = selection; } void SelectionDecorator::setHandleRadius(int radius) { m_handleRadius = radius; m_lineWidth = qMax(1, (int)(radius / 2)); } void SelectionDecorator::setHotPosition(KoFlake::Position hotPosition) { m_hotPosition = hotPosition; } KoFlake::Position SelectionDecorator::hotPosition() { return m_hotPosition; } void SelectionDecorator::paint(QPainter &painter, const KoViewConverter &converter) { QRectF handleArea; painter.save(); // save the original painter transformation QTransform painterMatrix = painter.worldTransform(); QPen pen; //Use the #00adf5 color with 50% opacity pen.setColor(QColor(0, 173, 245, 127)); pen.setWidth(m_lineWidth); pen.setJoinStyle(Qt::RoundJoin); painter.setPen(pen); bool editable = false; foreach (KoShape *shape, m_selection->selectedShapes(KoFlake::StrippedSelection)) { // apply the shape transformation on top of the old painter transformation painter.setWorldTransform(shape->absoluteTransformation(&converter) * painterMatrix); // apply the zoom factor KoShape::applyConversion(painter, converter); // draw the shape bounding rect painter.drawRect(QRectF(QPointF(), shape->size())); if (!shape->isGeometryProtected()) { editable = true; } } - if (m_selection->count() > 1) { - // more than one shape selected, so we need to draw the selection bounding rect - painter.setPen(Qt::blue); - // apply the selection transformation on top of the old painter transformation - painter.setWorldTransform(m_selection->absoluteTransformation(&converter) * painterMatrix); - // apply the zoom factor - KoShape::applyConversion(painter, converter); - // draw the selection bounding rect - painter.drawRect(QRectF(QPointF(), m_selection->size())); - // save the selection bounding rect for later drawing the selection handles - handleArea = QRectF(QPointF(), m_selection->size()); - } else if (m_selection->firstSelectedShape()) { - // only one shape selected, so we compose the correct painter matrix - painter.setWorldTransform(m_selection->firstSelectedShape()->absoluteTransformation(&converter) * painterMatrix); - KoShape::applyConversion(painter, converter); - // save the only selected shapes bounding rect for later drawing the handles - handleArea = QRectF(QPointF(), m_selection->firstSelectedShape()->size()); + if (m_selection) { + if (m_selection->count() > 1) { + // more than one shape selected, so we need to draw the selection bounding rect + painter.setPen(Qt::blue); + // apply the selection transformation on top of the old painter transformation + painter.setWorldTransform(m_selection->absoluteTransformation(&converter) * painterMatrix); + // apply the zoom factor + KoShape::applyConversion(painter, converter); + // draw the selection bounding rect + painter.drawRect(QRectF(QPointF(), m_selection->size())); + // save the selection bounding rect for later drawing the selection handles + handleArea = QRectF(QPointF(), m_selection->size()); + } + else if (m_selection->firstSelectedShape()) { + // only one shape selected, so we compose the correct painter matrix + painter.setWorldTransform(m_selection->firstSelectedShape()->absoluteTransformation(&converter) * painterMatrix); + KoShape::applyConversion(painter, converter); + // save the only selected shapes bounding rect for later drawing the handles + handleArea = QRectF(QPointF(), m_selection->firstSelectedShape()->size()); + } } - painterMatrix = painter.worldTransform(); painter.restore(); // if we have no editable shape selected there is no need drawing the selection handles if (!editable) { return; } painter.save(); painter.setTransform(QTransform()); painter.setRenderHint(QPainter::Antialiasing, false); painter.setPen(pen); painter.setBrush(pen.color()); QPolygonF outline = painterMatrix.map(handleArea); // the 8 move rects QRectF rect(QPointF(0.5, 0.5), QSizeF(2 * m_handleRadius, 2 * m_handleRadius)); rect.moveCenter(outline.value(0)); painter.drawRect(rect); rect.moveCenter(outline.value(1)); painter.drawRect(rect); rect.moveCenter(outline.value(2)); painter.drawRect(rect); rect.moveCenter(outline.value(3)); painter.drawRect(rect); rect.moveCenter((outline.value(0) + outline.value(1)) / 2); painter.drawRect(rect); rect.moveCenter((outline.value(1) + outline.value(2)) / 2); painter.drawRect(rect); rect.moveCenter((outline.value(2) + outline.value(3)) / 2); painter.drawRect(rect); rect.moveCenter((outline.value(3) + outline.value(0)) / 2); painter.drawRect(rect); // draw the hot position painter.setBrush(Qt::red); QPointF pos; switch (m_hotPosition) { case KoFlake::TopLeftCorner: pos = handleArea.topLeft(); break; case KoFlake::TopRightCorner: pos = handleArea.topRight(); break; case KoFlake::BottomLeftCorner: pos = handleArea.bottomLeft(); break; case KoFlake::BottomRightCorner: pos = handleArea.bottomRight(); break; case KoFlake::CenteredPosition: pos = handleArea.center(); break; } rect.moveCenter(painterMatrix.map(pos)); painter.drawRect(rect); painter.restore(); } diff --git a/plugins/tools/defaulttool/defaulttool/SelectionDecorator.h b/plugins/tools/defaulttool/defaulttool/SelectionDecorator.h index 461480a827..910fb89b61 100644 --- a/plugins/tools/defaulttool/defaulttool/SelectionDecorator.h +++ b/plugins/tools/defaulttool/defaulttool/SelectionDecorator.h @@ -1,80 +1,82 @@ /* This file is part of the KDE project Copyright (C) 2006 Thorsten Zachmann Copyright (C) 2006-2007 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 SELECTIONDECORATOR_H #define SELECTIONDECORATOR_H #include #include + #include +#include class KoSelection; /** * The SelectionDecorator is used to paint extra user-interface items on top of a selection. */ class SelectionDecorator { public: /** * Constructor. * @param arrows the direction that needs highlighting. (currently unused) * @param rotationHandles if true; the rotation handles will be drawn * @param shearHandles if true; the shearhandles will be drawn */ SelectionDecorator(KoFlake::SelectionHandle arrows, bool rotationHandles, bool shearHandles); ~SelectionDecorator() {} /** * paint the decortations. * @param painter the painter to paint to. * @param converter to convert between internal and view coordinates. */ void paint(QPainter &painter, const KoViewConverter &converter); /** * set the selection that is to be painted. * @param selection the current selection. */ void setSelection(KoSelection *selection); /** * set the radius of the selection handles * @param radius the new handle radius */ void setHandleRadius(int radius); /// Sets the hot position to highlight static void setHotPosition(KoFlake::Position hotPosition); /// Returns the hot position static KoFlake::Position hotPosition(); private: bool m_rotationHandles, m_shearHandles; KoFlake::SelectionHandle m_arrows; static KoFlake::Position m_hotPosition; - KoSelection *m_selection; + QPointer m_selection; int m_handleRadius; int m_lineWidth; }; #endif diff --git a/plugins/tools/defaulttool/defaulttool/ShapeResizeStrategy.cpp b/plugins/tools/defaulttool/defaulttool/ShapeResizeStrategy.cpp index eb11570a41..2921be94b1 100644 --- a/plugins/tools/defaulttool/defaulttool/ShapeResizeStrategy.cpp +++ b/plugins/tools/defaulttool/defaulttool/ShapeResizeStrategy.cpp @@ -1,302 +1,305 @@ /* This file is part of the KDE project * Copyright (C) 2006-2007 Thomas Zander * Copyright (C) 2007,2011 Jan Hambrecht + * Copyright (C) 2017 Boudewijn Rempt * * 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 "ShapeResizeStrategy.h" #include "SelectionDecorator.h" #include #include #include #include #include #include #include #include #include #include #include ShapeResizeStrategy::ShapeResizeStrategy(KoToolBase *tool, const QPointF &clicked, KoFlake::SelectionHandle direction) : KoInteractionStrategy(tool) , m_lastScale(1.0, 1.0) { + Q_ASSERT(tool->canvas()->shapeManager()->selection()); Q_ASSERT(tool->canvas()->shapeManager()->selection()->count() > 0); + QList selectedShapes = tool->canvas()->shapeManager()->selection()->selectedShapes(KoFlake::StrippedSelection); Q_FOREACH (KoShape *shape, selectedShapes) { if (!shape->isEditable()) { continue; } m_selectedShapes << shape; m_startPositions << shape->position(); m_oldTransforms << shape->transformation(); m_transformations << QTransform(); m_startSizes << shape->size(); } m_start = clicked; KoShape *shape = 0; if (tool->canvas()->shapeManager()->selection()->count() > 1) { shape = tool->canvas()->shapeManager()->selection(); } if (tool->canvas()->shapeManager()->selection()->count() == 1) { shape = tool->canvas()->shapeManager()->selection()->firstSelectedShape(); } if (shape) { m_windMatrix = shape->absoluteTransformation(0); m_unwindMatrix = m_windMatrix.inverted(); m_initialSize = shape->size(); m_initialPosition = m_windMatrix.map(QPointF()); switch (direction) { case KoFlake::TopMiddleHandle: m_start = 0.5 * (shape->absolutePosition(KoFlake::TopLeftCorner) + shape->absolutePosition(KoFlake::TopRightCorner)); m_top = true; m_bottom = false; m_left = false; m_right = false; break; case KoFlake::TopRightHandle: m_start = shape->absolutePosition(KoFlake::TopRightCorner); m_top = true; m_bottom = false; m_left = false; m_right = true; break; case KoFlake::RightMiddleHandle: m_start = 0.5 * (shape->absolutePosition(KoFlake::TopRightCorner) + shape->absolutePosition(KoFlake::BottomRightCorner)); m_top = false; m_bottom = false; m_left = false; m_right = true; break; case KoFlake::BottomRightHandle: m_start = shape->absolutePosition(KoFlake::BottomRightCorner); m_top = false; m_bottom = true; m_left = false; m_right = true; break; case KoFlake::BottomMiddleHandle: m_start = 0.5 * (shape->absolutePosition(KoFlake::BottomRightCorner) + shape->absolutePosition(KoFlake::BottomLeftCorner)); m_top = false; m_bottom = true; m_left = false; m_right = false; break; case KoFlake::BottomLeftHandle: m_start = shape->absolutePosition(KoFlake::BottomLeftCorner); m_top = false; m_bottom = true; m_left = true; m_right = false; break; case KoFlake::LeftMiddleHandle: m_start = 0.5 * (shape->absolutePosition(KoFlake::BottomLeftCorner) + shape->absolutePosition(KoFlake::TopLeftCorner)); m_top = false; m_bottom = false; m_left = true; m_right = false; break; case KoFlake::TopLeftHandle: m_start = shape->absolutePosition(KoFlake::TopLeftCorner); m_top = true; m_bottom = false; m_left = true; m_right = false; break; default: Q_ASSERT(0); // illegal 'corner' } } tool->setStatusText(i18n("Press CTRL to resize from center.")); } void ShapeResizeStrategy::handleMouseMove(const QPointF &point, Qt::KeyboardModifiers modifiers) { tool()->canvas()->updateCanvas(tool()->canvas()->snapGuide()->boundingRect()); QPointF newPos = tool()->canvas()->snapGuide()->snap(point, modifiers); tool()->canvas()->updateCanvas(tool()->canvas()->snapGuide()->boundingRect()); bool keepAspect = modifiers & Qt::ShiftModifier; Q_FOREACH (KoShape *shape, m_selectedShapes) { keepAspect = keepAspect || shape->keepAspectRatio(); } qreal startWidth = m_initialSize.width(); if (startWidth < std::numeric_limits::epsilon()) { startWidth = std::numeric_limits::epsilon(); } qreal startHeight = m_initialSize.height(); if (startHeight < std::numeric_limits::epsilon()) { startHeight = std::numeric_limits::epsilon(); } QPointF distance = m_unwindMatrix.map(newPos) - m_unwindMatrix.map(m_start); // guard against resizing zero width shapes, which would result in huge zoom factors if (m_initialSize.width() < std::numeric_limits::epsilon()) { distance.rx() = 0.0; } // guard against resizing zero height shapes, which would result in huge zoom factors if (m_initialSize.height() < std::numeric_limits::epsilon()) { distance.ry() = 0.0; } const bool scaleFromCenter = modifiers & Qt::ControlModifier; if (scaleFromCenter) { distance *= 2.0; } qreal newWidth = startWidth; qreal newHeight = startHeight; if (m_left) { newWidth = startWidth - distance.x(); } else if (m_right) { newWidth = startWidth + distance.x(); } if (m_top) { newHeight = startHeight - distance.y(); } else if (m_bottom) { newHeight = startHeight + distance.y(); } /** * Do not let a shape be less than 1px in size in current view * coordinates. If the user wants it to be smaller, he can just * zoom-in a bit. */ QSizeF minViewSize(1.0, 1.0); QSizeF minDocSize = tool()->canvas()->viewConverter()->viewToDocument(minViewSize); if (qAbs(newWidth) < minDocSize.width()) { int sign = newWidth >= 0.0 ? 1 : -1; // zero -> '1' newWidth = sign * minDocSize.width(); } if (qAbs(newHeight) < minDocSize.height()) { int sign = newHeight >= 0.0 ? 1 : -1; // zero -> '1' newHeight = sign * minDocSize.height(); } qreal zoomX = newWidth / startWidth; qreal zoomY = newHeight / startHeight; if (keepAspect) { const bool cornerUsed = ((m_bottom ? 1 : 0) + (m_top ? 1 : 0) + (m_left ? 1 : 0) + (m_right ? 1 : 0)) == 2; if ((cornerUsed && startWidth < startHeight) || m_left || m_right) { zoomY = zoomX; } else { zoomX = zoomY; } } QPointF move; if (scaleFromCenter) { move = QPointF(startWidth / 2.0, startHeight / 2.0); } else { move = QPointF(m_left ? startWidth : 0, m_top ? startHeight : 0); } resizeBy(move, zoomX, zoomY); } void ShapeResizeStrategy::handleCustomEvent(KoPointerEvent *event) { QPointF center = 0.5 * QPointF(m_initialSize.width(), m_initialSize.height()); qreal zoom = pow(1.01, -0.1 * event->z()); m_lastScale *= zoom; resizeBy(center, m_lastScale.x(), m_lastScale.y()); } void ShapeResizeStrategy::resizeBy(const QPointF ¢er, qreal zoomX, qreal zoomY) { QTransform matrix; matrix.translate(center.x(), center.y()); // translate to matrix.scale(zoomX, zoomY); matrix.translate(-center.x(), -center.y()); // and back // that is the transformation we want to apply to the shapes matrix = m_unwindMatrix * matrix * m_windMatrix; // the resizing transformation without the mirroring part QTransform resizeMatrix; resizeMatrix.translate(center.x(), center.y()); // translate to resizeMatrix.scale(qAbs(zoomX), qAbs(zoomY)); resizeMatrix.translate(-center.x(), -center.y()); // and back // the mirroring part of the resizing transformation QTransform mirrorMatrix; mirrorMatrix.translate(center.x(), center.y()); // translate to mirrorMatrix.scale(zoomX < 0 ? -1 : 1, zoomY < 0 ? -1 : 1); mirrorMatrix.translate(-center.x(), -center.y()); // and back int i = 0; Q_FOREACH (KoShape *shape, m_selectedShapes) { shape->update(); // this uses resize for the zooming part shape->applyAbsoluteTransformation(m_unwindMatrix); /* normally we would just apply the resizeMatrix now and be done with it, but we want to resize instead of scale, so we have to separate the scaling part of that transformation which can then be used to resize */ // undo the last resize transformation shape->applyAbsoluteTransformation(m_transformations[i].inverted()); // save the shapes transformation matrix QTransform shapeMatrix = shape->absoluteTransformation(0); // calculate the matrix we would apply to the local shape matrix // that tells us the effective scale values we have to use for the resizing QTransform localMatrix = shapeMatrix * resizeMatrix * shapeMatrix.inverted(); // save the effective scale values qreal scaleX = localMatrix.m11(); qreal scaleY = localMatrix.m22(); // calculate the scale matrix which is equivalent to our resizing above QTransform scaleMatrix = (QTransform().scale(scaleX, scaleY)); scaleMatrix = shapeMatrix.inverted() * scaleMatrix * shapeMatrix; // calculate the new size of the shape, using the effective scale values QSizeF size(scaleX * m_startSizes[i].width(), scaleY * m_startSizes[i].height()); // apply the transformation shape->setSize(size); // apply the rest of the transformation without the resizing part shape->applyAbsoluteTransformation(scaleMatrix.inverted() * resizeMatrix); shape->applyAbsoluteTransformation(mirrorMatrix); // and remember the applied transformation later for later undoing m_transformations[i] = shapeMatrix.inverted() * shape->absoluteTransformation(0); shape->applyAbsoluteTransformation(m_windMatrix); shape->update(); i++; } tool()->canvas()->shapeManager()->selection()->applyAbsoluteTransformation(matrix * m_scaleMatrix.inverted()); m_scaleMatrix = matrix; } KUndo2Command *ShapeResizeStrategy::createCommand() { tool()->canvas()->snapGuide()->reset(); QList newSizes; QList transformations; const int shapeCount = m_selectedShapes.count(); for (int i = 0; i < shapeCount; ++i) { newSizes << m_selectedShapes[i]->size(); transformations << m_selectedShapes[i]->transformation(); } KUndo2Command *cmd = new KUndo2Command(kundo2_i18n("Resize")); new KoShapeSizeCommand(m_selectedShapes, m_startSizes, newSizes, cmd); new KoShapeTransformCommand(m_selectedShapes, m_oldTransforms, transformations, cmd); return cmd; } void ShapeResizeStrategy::finishInteraction(Qt::KeyboardModifiers modifiers) { Q_UNUSED(modifiers); tool()->canvas()->updateCanvas(tool()->canvas()->snapGuide()->boundingRect()); } void ShapeResizeStrategy::paint(QPainter &painter, const KoViewConverter &converter) { SelectionDecorator decorator(KoFlake::NoHandle, false, false); decorator.setSelection(tool()->canvas()->shapeManager()->selection()); decorator.setHandleRadius(handleRadius()); decorator.paint(painter, converter); } diff --git a/plugins/tools/karbonplugins/tools/CalligraphyTool/KarbonCalligraphyTool.cpp b/plugins/tools/karbonplugins/tools/CalligraphyTool/KarbonCalligraphyTool.cpp index f3a86c5067..45f783d41b 100644 --- a/plugins/tools/karbonplugins/tools/CalligraphyTool/KarbonCalligraphyTool.cpp +++ b/plugins/tools/karbonplugins/tools/CalligraphyTool/KarbonCalligraphyTool.cpp @@ -1,512 +1,513 @@ /* This file is part of the KDE project * Copyright (C) 2008 Fela Winkelmolen * * 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 "KarbonCalligraphyTool.h" #include "KarbonCalligraphicShape.h" #include "KarbonCalligraphyOptionWidget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #undef M_PI const qreal M_PI = 3.1415927; using std::pow; using std::sqrt; KarbonCalligraphyTool::KarbonCalligraphyTool(KoCanvasBase *canvas) : KoToolBase(canvas) , m_shape(0) , m_angle(0) , m_selectedPath(0) , m_isDrawing(false) , m_speed(0, 0) , m_lastShape(0) { connect(canvas->shapeManager(), SIGNAL(selectionChanged()), SLOT(updateSelectedPath())); updateSelectedPath(); } KarbonCalligraphyTool::~KarbonCalligraphyTool() { } void KarbonCalligraphyTool::paint(QPainter &painter, const KoViewConverter &converter) { if (m_selectedPath) { painter.save(); painter.setRenderHints(QPainter::Antialiasing, false); painter.setPen(Qt::red); // TODO make configurable QRectF rect = m_selectedPath->boundingRect(); QPointF p1 = converter.documentToView(rect.topLeft()); QPointF p2 = converter.documentToView(rect.bottomRight()); painter.drawRect(QRectF(p1, p2)); painter.restore(); } if (!m_shape) { return; } painter.save(); painter.setTransform(m_shape->absoluteTransformation(&converter) * painter.transform()); KoShapePaintingContext paintContext; //FIXME m_shape->paint(painter, converter, paintContext); painter.restore(); } void KarbonCalligraphyTool::mousePressEvent(KoPointerEvent *event) { if (m_isDrawing) { return; } m_lastPoint = event->point; m_speed = QPointF(0, 0); m_isDrawing = true; m_pointCount = 0; m_shape = new KarbonCalligraphicShape(m_caps); m_shape->setBackground(QSharedPointer(new KoColorBackground(canvas()->resourceManager()->foregroundColor().toQColor()))); //addPoint( event ); } void KarbonCalligraphyTool::mouseMoveEvent(KoPointerEvent *event) { if (!m_isDrawing) { return; } addPoint(event); } void KarbonCalligraphyTool::mouseReleaseEvent(KoPointerEvent *event) { if (!m_isDrawing) { return; } if (m_pointCount == 0) { // handle click: select shape (if any) if (event->point == m_lastPoint) { KoShapeManager *shapeManager = canvas()->shapeManager(); KoShape *selectedShape = shapeManager->shapeAt(event->point); if (selectedShape != 0) { shapeManager->selection()->deselectAll(); shapeManager->selection()->select(selectedShape); } } delete m_shape; m_shape = 0; m_isDrawing = false; return; } else { m_endOfPath = false; // allow last point being added addPoint(event); // add last point m_isDrawing = false; } m_shape->simplifyGuidePath(); KUndo2Command *cmd = canvas()->shapeController()->addShape(m_shape); if (cmd) { m_lastShape = m_shape; canvas()->addCommand(cmd); canvas()->updateCanvas(m_shape->boundingRect()); } else { // don't leak shape when command could not be created delete m_shape; } m_shape = 0; } void KarbonCalligraphyTool::addPoint(KoPointerEvent *event) { if (m_pointCount == 0) { if (m_usePath && m_selectedPath) { m_selectedPathOutline = m_selectedPath->outline(); } m_pointCount = 1; m_endOfPath = false; m_followPathPosition = 0; m_lastMousePos = event->point; m_lastPoint = calculateNewPoint(event->point, &m_speed); m_deviceSupportsTilt = (event->xTilt() != 0 || event->yTilt() != 0); return; } if (m_endOfPath) { return; } ++m_pointCount; setAngle(event); QPointF newSpeed; QPointF newPoint = calculateNewPoint(event->point, &newSpeed); qreal width = calculateWidth(event->pressure()); qreal angle = calculateAngle(m_speed, newSpeed); // add the previous point m_shape->appendPoint(m_lastPoint, angle, width); m_speed = newSpeed; m_lastPoint = newPoint; canvas()->updateCanvas(m_shape->lastPieceBoundingRect()); if (m_usePath && m_selectedPath) { m_speed = QPointF(0, 0); // following path } } void KarbonCalligraphyTool::setAngle(KoPointerEvent *event) { if (!m_useAngle) { m_angle = (360 - m_customAngle + 90) / 180.0 * M_PI; return; } // setting m_angle to the angle of the device if (event->xTilt() != 0 || event->yTilt() != 0) { m_deviceSupportsTilt = false; } if (m_deviceSupportsTilt) { if (event->xTilt() == 0 && event->yTilt() == 0) { return; // leave as is } qDebug() << "using tilt" << m_angle; if (event->x() == 0) { m_angle = M_PI / 2; return; } // y is inverted in qt painting m_angle = std::atan(static_cast(-event->yTilt() / event->xTilt())) + M_PI / 2; } else { m_angle = event->rotation() + M_PI / 2; qDebug() << "using rotation" << m_angle; } } QPointF KarbonCalligraphyTool::calculateNewPoint(const QPointF &mousePos, QPointF *speed) { if (!m_usePath || !m_selectedPath) { // don't follow path QPointF force = mousePos - m_lastPoint; QPointF dSpeed = force / m_mass; *speed = m_speed * (1.0 - m_drag) + dSpeed; return m_lastPoint + *speed; } QPointF sp = mousePos - m_lastMousePos; m_lastMousePos = mousePos; // follow selected path qreal step = QLineF(QPointF(0, 0), sp).length(); m_followPathPosition += step; qreal t; if (m_followPathPosition >= m_selectedPathOutline.length()) { t = 1.0; m_endOfPath = true; } else { t = m_selectedPathOutline.percentAtLength(m_followPathPosition); } QPointF res = m_selectedPathOutline.pointAtPercent(t) - + m_selectedPath->position(); + + m_selectedPath->position(); *speed = res - m_lastPoint; return res; } qreal KarbonCalligraphyTool::calculateWidth(qreal pressure) { // calculate the modulo of the speed qreal speed = std::sqrt(pow(m_speed.x(), 2) + pow(m_speed.y(), 2)); qreal thinning = m_thinning * (speed + 1) / 10.0; // can be negative if (thinning > 1) { thinning = 1; } if (!m_usePressure) { pressure = 1.0; } qreal strokeWidth = m_strokeWidth * pressure * (1 - thinning); const qreal MINIMUM_STROKE_WIDTH = 1.0; if (strokeWidth < MINIMUM_STROKE_WIDTH) { strokeWidth = MINIMUM_STROKE_WIDTH; } return strokeWidth; } qreal KarbonCalligraphyTool::calculateAngle(const QPointF &oldSpeed, const QPointF &newSpeed) { // calculate the avarage of the speed (sum of the normalized values) qreal oldLength = QLineF(QPointF(0, 0), oldSpeed).length(); qreal newLength = QLineF(QPointF(0, 0), newSpeed).length(); QPointF oldSpeedNorm = !qFuzzyCompare(oldLength + 1, 1) ? - oldSpeed / oldLength : QPointF(0, 0); + oldSpeed / oldLength : QPointF(0, 0); QPointF newSpeedNorm = !qFuzzyCompare(newLength + 1, 1) ? - newSpeed / newLength : QPointF(0, 0); + newSpeed / newLength : QPointF(0, 0); QPointF speed = oldSpeedNorm + newSpeedNorm; // angle solely based on the speed qreal speedAngle = 0; if (speed.x() != 0) { // avoid division by zero speedAngle = std::atan(speed.y() / speed.x()); } else if (speed.y() > 0) { // x == 0 && y != 0 speedAngle = M_PI / 2; } else if (speed.y() < 0) { // x == 0 && y != 0 speedAngle = -M_PI / 2; } if (speed.x() < 0) { speedAngle += M_PI; } // move 90 degrees speedAngle += M_PI / 2; qreal fixedAngle = m_angle; // check if the fixed angle needs to be flipped qreal diff = fixedAngle - speedAngle; while (diff >= M_PI) { // normalize diff between -180 and 180 diff -= 2 * M_PI; } while (diff < -M_PI) { diff += 2 * M_PI; } if (std::abs(diff) > M_PI / 2) { // if absolute value < 90 fixedAngle += M_PI; // += 180 } qreal dAngle = speedAngle - fixedAngle; // normalize dAngle between -90 and +90 while (dAngle >= M_PI / 2) { dAngle -= M_PI; } while (dAngle < -M_PI / 2) { dAngle += M_PI; } qreal angle = fixedAngle + dAngle * (1.0 - m_fixation); return angle; } void KarbonCalligraphyTool::activate(ToolActivation, const QSet &) { useCursor(Qt::CrossCursor); m_lastShape = 0; } void KarbonCalligraphyTool::deactivate() { if (m_lastShape && canvas()->shapeManager()->shapes().contains(m_lastShape)) { KoSelection *selection = canvas()->shapeManager()->selection(); selection->deselectAll(); selection->select(m_lastShape); } } QList > KarbonCalligraphyTool::createOptionWidgets() { // if the widget don't exists yet create it QList > widgets; KoFillConfigWidget *fillWidget = new KoFillConfigWidget(0); fillWidget->setWindowTitle(i18n("Fill")); fillWidget->setCanvas(canvas()); widgets.append(fillWidget); KarbonCalligraphyOptionWidget *widget = new KarbonCalligraphyOptionWidget; connect(widget, SIGNAL(usePathChanged(bool)), this, SLOT(setUsePath(bool))); connect(widget, SIGNAL(usePressureChanged(bool)), this, SLOT(setUsePressure(bool))); connect(widget, SIGNAL(useAngleChanged(bool)), this, SLOT(setUseAngle(bool))); connect(widget, SIGNAL(widthChanged(double)), this, SLOT(setStrokeWidth(double))); connect(widget, SIGNAL(thinningChanged(double)), this, SLOT(setThinning(double))); connect(widget, SIGNAL(angleChanged(int)), this, SLOT(setAngle(int))); connect(widget, SIGNAL(fixationChanged(double)), this, SLOT(setFixation(double))); connect(widget, SIGNAL(capsChanged(double)), this, SLOT(setCaps(double))); connect(widget, SIGNAL(massChanged(double)), this, SLOT(setMass(double))); connect(widget, SIGNAL(dragChanged(double)), this, SLOT(setDrag(double))); connect(this, SIGNAL(pathSelectedChanged(bool)), widget, SLOT(setUsePathEnabled(bool))); // add shortcuts QAction *action = new QAction(i18n("Calligraphy: increase width"), this); action->setShortcut(Qt::Key_Right); connect(action, SIGNAL(triggered()), widget, SLOT(increaseWidth())); addAction("calligraphy_increase_width", action); action = new QAction(i18n("Calligraphy: decrease width"), this); action->setShortcut(Qt::Key_Left); connect(action, SIGNAL(triggered()), widget, SLOT(decreaseWidth())); addAction("calligraphy_decrease_width", action); action = new QAction(i18n("Calligraphy: increase angle"), this); action->setShortcut(Qt::Key_Up); connect(action, SIGNAL(triggered()), widget, SLOT(increaseAngle())); addAction("calligraphy_increase_angle", action); action = new QAction(i18n("Calligraphy: decrease angle"), this); action->setShortcut(Qt::Key_Down); connect(action, SIGNAL(triggered()), widget, SLOT(decreaseAngle())); addAction("calligraphy_decrease_angle", action); // sync all parameters with the loaded profile widget->emitAll(); widget->setObjectName(i18n("Calligraphy")); widget->setWindowTitle(i18n("Calligraphy")); widgets.append(widget); return widgets; } void KarbonCalligraphyTool::setStrokeWidth(double width) { m_strokeWidth = width; } void KarbonCalligraphyTool::setThinning(double thinning) { m_thinning = thinning; } void KarbonCalligraphyTool::setAngle(int angle) { m_customAngle = angle; } void KarbonCalligraphyTool::setFixation(double fixation) { m_fixation = fixation; } void KarbonCalligraphyTool::setMass(double mass) { m_mass = mass * mass + 1; } void KarbonCalligraphyTool::setDrag(double drag) { m_drag = drag; } void KarbonCalligraphyTool::setUsePath(bool usePath) { m_usePath = usePath; } void KarbonCalligraphyTool::setUsePressure(bool usePressure) { m_usePressure = usePressure; } void KarbonCalligraphyTool::setUseAngle(bool useAngle) { m_useAngle = useAngle; } void KarbonCalligraphyTool::setCaps(double caps) { m_caps = caps; } void KarbonCalligraphyTool::updateSelectedPath() { KoPathShape *oldSelectedPath = m_selectedPath; // save old value KoSelection *selection = canvas()->shapeManager()->selection(); + if (selection) { + // null pointer if it the selection isn't a KoPathShape + // or if the selection is empty + m_selectedPath = + dynamic_cast(selection->firstSelectedShape()); + + // or if it's a KoPathShape but with no or more than one subpaths + if (m_selectedPath && m_selectedPath->subpathCount() != 1) { + m_selectedPath = 0; + } - // null pointer if it the selection isn't a KoPathShape - // or if the selection is empty - m_selectedPath = - dynamic_cast(selection->firstSelectedShape()); - - // or if it's a KoPathShape but with no or more than one subpaths - if (m_selectedPath && m_selectedPath->subpathCount() != 1) { - m_selectedPath = 0; - } - - // or if there ora none or more than 1 shapes selected - if (selection->count() != 1) { - m_selectedPath = 0; - } + // or if there ora none or more than 1 shapes selected + if (selection->count() != 1) { + m_selectedPath = 0; + } - // emit signal it there wasn't a selected path and now there is - // or the other way around - if ((m_selectedPath != 0) != (oldSelectedPath != 0)) { - emit pathSelectedChanged(m_selectedPath != 0); + // emit signal it there wasn't a selected path and now there is + // or the other way around + if ((m_selectedPath != 0) != (oldSelectedPath != 0)) { + emit pathSelectedChanged(m_selectedPath != 0); + } } }