diff --git a/krita/kritamenu.action b/krita/kritamenu.action index 41e9438754..cdfdfa9470 100644 --- a/krita/kritamenu.action +++ b/krita/kritamenu.action @@ -1,1806 +1,1806 @@ File document-new &New Create new document New 0 0 Ctrl+N false document-open &Open... Open an existing document Open 0 0 Ctrl+O false document-open-recent Open &Recent Open a document which was recently opened Open Recent 1 0 false document-save &Save Save Save 1 0 Ctrl+S false document-save-as Save &As... Save document under a new name Save As 1 0 Ctrl+Shift+S false Sessions... Open session manager Sessions 0 0 false document-import Open ex&isting Document as Untitled Document... Open existing Document as Untitled Document Open existing Document as Untitled Document 0 0 false document-export E&xport... Export Export 1 0 false application-pdf &Export as PDF... Export as PDF Export as PDF 1 0 false Import animation frames... Import animation frames Import animation frames 1 0 false &Render Animation... Render Animation to GIF, Image Sequence or Video Render Animation 1000 0 false &Render Image Sequence Again Render Animation to Image Sequence Again Render Animation 1000 0 false Save Incremental &Version Save Incremental Version Save Incremental Version 1 0 Ctrl+Alt+S false Save Incremental &Backup Save Incremental Backup Save Incremental Backup 1 0 F4 false &Create Template From Image... Create Template From Image Create Template From Image 1 0 false Create Copy &From Current Image Create Copy From Current Image Create Copy From Current Image 1 0 false document-print &Print... Print document Print 1 0 Ctrl+P false document-print-preview Print Previe&w Show a print preview of document Print Preview 1 0 false configure &Document Information Document Information Document Information 1 0 false &Close All Close All Close All 1 0 Ctrl+Shift+W false C&lose Close Close 1 0 false &Quit Quit application Quit 0 0 Ctrl+Q false Edit edit-undo Undo Undo last action Undo 1 0 Ctrl+Z false edit-redo Redo Redo last undone action Redo 1 0 Ctrl+Shift+Z false edit-cut Cu&t Cut selection to clipboard Cut 0 0 Ctrl+X false edit-copy &Copy Copy selection to clipboard Copy 0 0 Ctrl+C false C&opy (sharp) Copy (sharp) Copy (sharp) 100000000 0 false Cut (&sharp) Cut (sharp) Cut (sharp) 100000000 0 false Copy &merged Copy merged Copy merged 100000000 0 Ctrl+Shift+C false edit-paste &Paste Paste clipboard content Paste 0 0 Ctrl+V false Paste at Cursor Paste at cursor Paste at cursor 0 0 Ctrl+Alt+V false Paste into &New Image Paste into New Image Paste into New Image 0 0 Ctrl+Shift+N false edit-clear C&lear Clear Clear 1 0 Del false &Fill with Foreground Color Fill with Foreground Color Fill with Foreground Color 10000 1 Shift+Backspace false Fill &with Background Color Fill with Background Color Fill with Background Color 10000 1 Backspace false F&ill with Pattern Fill with Pattern Fill with Pattern 10000 1 false Fill Special Fill with Foreground Color (Opacity) Fill with Foreground Color (Opacity) Fill with Foreground Color (Opacity) 10000 1 Ctrl+Shift+Backspace false Fill with Background Color (Opacity) Fill with Background Color (Opacity) Fill with Background Color (Opacity) 10000 1 Ctrl+Backspace false Fill with Pattern (Opacity) Fill with Pattern (Opacity) Fill with Pattern (Opacity) 10000 1 false Stro&ke selected shapes Stroke selected shapes Stroke selected shapes 1000000000 0 false Stroke Selec&tion... Stroke selection Stroke selection 10000000000 0 false Delete keyframe Delete keyframe Delete keyframe 100000 0 false Window window-new &New Window New Window New Window 0 0 false N&ext Next Next 10 0 false Previous Previous Previous false View &Show Canvas Only Show just the canvas or the whole window Show Canvas Only 0 0 Tab true view-fullscreen F&ull Screen Mode Display the window in full screen Full Screen Mode 0 0 Ctrl+Shift+F true &Wrap Around Mode Wrap Around Mode Wrap Around Mode 1 0 W true &Instant Preview Mode Instant Preview Mode Instant Preview Mode 1 0 Shift+L true Soft Proofing Turns on Soft Proofing Turns on Soft Proofing Ctrl+Y true Out of Gamut Warnings Turns on warnings for colors out of proofed gamut, needs soft proofing to be turned on. Turns on warnings for colors out of proofed gamut, needs soft proofing to be turned on. Ctrl+Shift+Y true mirror-view Mirror View Mirror View Mirror View M false zoom-original &Reset zoom Reset zoom Reset zoom 1 0 Ctrl+0 false zoom-in Zoom &In Zoom In 0 0 Ctrl++ false zoom-out Zoom &Out Zoom Out 0 0 Ctrl+- false rotate-canvas-right Rotate &Canvas Right Rotate Canvas Right Rotate Canvas Right 1 0 Ctrl+] false rotate-canvas-left Rotate Canvas &Left Rotate Canvas Left Rotate Canvas Left 1 0 Ctrl+[ false rotation-reset Reset Canvas Rotation Reset Canvas Rotation Reset Canvas Rotation 1 0 false Show &Rulers The rulers show the horizontal and vertical positions of the mouse on the image and can be used to position your mouse at the right place on the canvas. <p>Uncheck this to hide the rulers.</p> Show Rulers Show Rulers 1 0 true Rulers Track Pointer The rulers will track current mouse position and show it on screen. It can cause suptle performance slowdown Rulers Track Pointer Rulers Track Pointer 1 0 true Show Guides Show or hide guides Show Guides 1 0 true Lock Guides Lock or unlock guides Lock Guides 1 0 true Snap to Guides Snap cursor to guides position Snap to Guides 1 0 true Show Status &Bar Show or hide the status bar Show Status Bar 0 0 true Show Pixel Grid Show Pixel Grid Show Pixel Grid 1000 0 true view-grid Show &Grid Show Grid Show Grid 1000 0 Ctrl+Shift+' true Snap To Grid Snap To Grid Snap To Grid 1000 Ctrl+Shift+; true Show Snap Options Popup Show Snap Options Popup Show Snap Options Popup 1000 Shift+s false Snap Orthogonal Snap Orthogonal Snap Orthogonal 1000 true Snap Node Snap Node Snap Node 1000 true Snap Extension Snap Extension Snap Extension 1000 true Snap Intersection Snap Intersection Snap Intersection 1000 true Snap Bounding Box Snap Bounding Box Snap Bounding Box 1000 true Snap Image Bounds Snap Image Bounds Snap Image Bounds 1000 true Snap Image Center Snap Image Center Snap Image Center 1000 true S&how Painting Assistants Show Painting Assistants Show Painting Assistants 1000 0 true Show &Assistant Previews Show Assistant Previews Show Assistant Previews 1000 0 true S&how Reference Images Show Reference Images Show Reference Images 1000 0 true Image document-properties &Properties... Properties Properties 1000 0 false format-stroke-color &Image Background Color and Transparency... Change the background color of the image Image Background Color and Transparency 1000 0 false &Convert Image Color Space... Convert Image Color Space Convert Image Color Space 1000 0 false trim-to-image &Trim to Image Size Trim to Image Size Trim to Image Size 1 0 false Trim to Current &Layer Trim to Current Layer Trim to Current Layer 100000 0 false Trim to S&election Trim to Selection Trim to Selection 100000000 0 false &Rotate Image... Rotate Image Rotate Image 1000 0 false object-rotate-right Rotate &Image 90° to the Right Rotate Image 90° to the Right Rotate Image 90° to the Right 1000 0 false object-rotate-left Rotate Image &90° to the Left Rotate Image 90° to the Left Rotate Image 90° to the Left 1000 0 false Rotate Image &180° Rotate Image 180° Rotate Image 180° 1000 0 false &Shear Image... Shear Image Shear Image 1000 0 false symmetry-horizontal &Mirror Image Horizontally Mirror Image Horizontally Mirror Image Horizontally 1000 0 false symmetry-vertical Mirror Image &Vertically Mirror Image Vertically Mirror Image Vertically 1000 0 false Scale Image To &New Size... Scale Image To New Size Scale Image To New Size 1000 0 Ctrl+Alt+I false &Offset Image... Offset Image Offset Image 1000 0 false R&esize Canvas... Resize Canvas Resize Canvas 1000 0 Ctrl+Alt+C false Im&age Split Image Split Image Split 1000 0 false Separate Ima&ge... Separate Image Separate Image 1000 0 false Select edit-select-all Select &All Select All Select All 0 0 Ctrl+A false edit-select-all &Deselect Deselect Deselect 1100000000 0 Ctrl+Shift+A false &Reselect Reselect Reselect 0 0 Ctrl+Shift+D false &Invert Selection Invert Selection Invert Selection 10000 0 Ctrl+I false &Convert to Vector Selection Convert to Vector Selection Convert to Vector Selection 10000000000 0 false Convert Shapes to &Vector Selection Convert Shapes to Vector Selection Convert Shapes to Vector Selection 1000000000 0 false &Feather Selection... Feather Selection Feather Selection 10000000000 100 Shift+F6 false Dis&play Selection Display Selection Display Selection 1000 0 Ctrl+H true Sca&le... Scale Scale 100000000 100 false S&elect from Color Range... Select from Color Range Select from Color Range 10000 100 false - Select &Opaque + Select &Opaque (Replace) Select Opaque Select Opaque 10000 100 false Select Opaque (&Add) Select Opaque (Add) Select Opaque (Add) 10000 100 false Select Opaque (&Subtract) Select Opaque (Subtract) Select Opaque (Subtract) 10000 100 false Select Opaque (&Intersect) Select Opaque (Intersect) Select Opaque (Intersect) 10000 100 false &Grow Selection... Grow Selection Grow Selection 10000000000 100 false S&hrink Selection... Shrink Selection Shrink Selection 10000000000 100 false &Border Selection... Border Selection Border Selection 10000000000 100 false S&mooth Smooth Smooth 10000000000 100 false Filter &Apply Filter Again Apply Filter Again Apply Filter Again 0 0 Ctrl+F false Adjust Adjust Adjust false Artistic Artistic Artistic false Blur Blur Blur false Colors Colors Colors false Edge Detection Edge Detection Edge Detection false Enhance Enhance Enhance false Emboss Emboss Emboss false Map Map Map false Other Other Other false gmic Start G'MIC-Qt Start G'Mic-Qt Start G'Mic-Qt false gmic Re-apply the last G'MIC filter Apply the last G'Mic-Qt action again Apply the last G'Mic-Qt action again false Settings configure &Configure Krita... Configure Krita Configure Krita 0 0 false &Manage Resources... Manage Resources Manage Resources 0 0 false preferences-desktop-locale Switch Application &Language... Switch Application Language Switch Application Language false &Show Dockers Show Dockers Show Dockers 0 0 true configure Configure Tool&bars... Configure Toolbars Configure Toolbars 0 0 false Dockers Dockers Dockers false &Themes Themes Themes false im-user Active Author Profile Active Author Profile Active Author Profile configure-shortcuts Configure S&hortcuts... Configure Shortcuts Configure Shortcuts 0 0 false &Window Window Window false Help help-contents Krita &Handbook Krita Handbook Krita Handbook F1 false tools-report-bug &Report Bug... Report Bug Report Bug false calligrakrita &About Krita About Krita About Krita false kde About &KDE About KDE About KDE false Brushes and Stuff &Gradients Gradients Gradients false &Patterns Patterns Patterns false &Color Color Color false &Painter's Tools Painter's Tools Painter's Tools false Brush composite Brush composite Brush composite false Brush option slider 1 Brush option slider 1 Brush option slider 1 false Brush option slider 2 Brush option slider 2 Brush option slider 2 false Brush option slider 3 Brush option slider 3 Brush option slider 3 false Mirror Mirror Mirror false Layouts Select layout false Workspaces Workspaces Workspaces false diff --git a/libs/basicflakes/tools/KoCreatePathTool.cpp b/libs/basicflakes/tools/KoCreatePathTool.cpp index c2063193bb..fa54cfdd5e 100644 --- a/libs/basicflakes/tools/KoCreatePathTool.cpp +++ b/libs/basicflakes/tools/KoCreatePathTool.cpp @@ -1,533 +1,581 @@ /* This file is part of the KDE project * * Copyright (C) 2006 Thorsten Zachmann * Copyright (C) 2008-2010 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 "KoCreatePathTool.h" #include "KoCreatePathTool_p.h" #include #include "KoPointerEvent.h" #include "KoPathShape.h" #include "KoSelection.h" #include "KoDocumentResourceManager.h" #include "KoShapePaintingContext.h" #include "KoShapeStroke.h" #include "KoCanvasBase.h" #include "kis_int_parse_spin_box.h" #include #include "kis_canvas_resource_provider.h" #include +#include "KoPathPointTypeCommand.h" #include #include #include #include #include #include KoCreatePathTool::KoCreatePathTool(KoCanvasBase *canvas) : KoToolBase(*(new KoCreatePathToolPrivate(this, canvas))) { } KoCreatePathTool::~KoCreatePathTool() { } void KoCreatePathTool::paint(QPainter &painter, const KoViewConverter &converter) { Q_D(KoCreatePathTool); if (pathStarted()) { painter.save(); paintPath(*(d->shape), painter, converter); painter.restore(); KisHandlePainterHelper helper = KoShape::createHandlePainterHelper(&painter, d->shape, converter, d->handleRadius); const bool firstPointActive = d->firstPoint == d->activePoint; if (d->pointIsDragged || firstPointActive) { const bool onlyPaintActivePoints = false; KoPathPoint::PointTypes paintFlags = KoPathPoint::ControlPoint2; if (d->activePoint->activeControlPoint1()) { paintFlags |= KoPathPoint::ControlPoint1; } helper.setHandleStyle(KisHandleStyle::highlightedPrimaryHandles()); d->activePoint->paint(helper, paintFlags, onlyPaintActivePoints); } if (!firstPointActive) { helper.setHandleStyle(d->mouseOverFirstPoint ? KisHandleStyle::highlightedPrimaryHandles() : KisHandleStyle::primarySelection()); d->firstPoint->paint(helper, KoPathPoint::Node); } } if (d->hoveredPoint) { KisHandlePainterHelper helper = KoShape::createHandlePainterHelper(&painter, d->hoveredPoint->parent(), converter, d->handleRadius); helper.setHandleStyle(KisHandleStyle::highlightedPrimaryHandles()); d->hoveredPoint->paint(helper, KoPathPoint::Node); } painter.save(); KoShape::applyConversion(painter, converter); canvas()->snapGuide()->paint(painter, converter); painter.restore(); } void KoCreatePathTool::paintPath(KoPathShape& pathShape, QPainter &painter, const KoViewConverter &converter) { Q_D(KoCreatePathTool); painter.setTransform(pathShape.absoluteTransformation(&converter) * painter.transform()); painter.save(); KoShapePaintingContext paintContext; //FIXME pathShape.paint(painter, converter, paintContext); painter.restore(); if (pathShape.stroke()) { painter.save(); pathShape.stroke()->paint(d->shape, painter, converter); painter.restore(); } } void KoCreatePathTool::mousePressEvent(KoPointerEvent *event) { Q_D(KoCreatePathTool); //Right click removes last point if (event->button() == Qt::RightButton) { removeLastPoint(); return; } const bool isOverFirstPoint = d->shape && handleGrabRect(d->firstPoint->point()).contains(event->point); bool haveCloseModifier = (listeningToModifiers() && (event->modifiers() & Qt::ShiftModifier)); if ((event->button() == Qt::LeftButton) && haveCloseModifier && !isOverFirstPoint) { endPathWithoutLastPoint(); return; } d->finishAfterThisPoint = false; if (pathStarted()) { if (isOverFirstPoint) { d->activePoint->setPoint(d->firstPoint->point()); canvas()->updateCanvas(d->shape->boundingRect()); canvas()->updateCanvas(canvas()->snapGuide()->boundingRect()); if (haveCloseModifier) { d->shape->closeMerge(); // we are closing the path, so reset the existing start path point d->existingStartPoint = 0; // finish path endPath(); } else { // the path shape will get closed when the user releases // the mouse button d->finishAfterThisPoint = true; } } else { canvas()->updateCanvas(canvas()->snapGuide()->boundingRect()); QPointF point = canvas()->snapGuide()->snap(event->point, event->modifiers()); // check whether we hit an start/end node of an existing path d->existingEndPoint = d->endPointAtPosition(point); if (d->existingEndPoint.isValid() && d->existingEndPoint != d->existingStartPoint) { point = d->existingEndPoint.path->shapeToDocument(d->existingEndPoint.point->point()); d->activePoint->setPoint(point); // finish path endPath(); } else { d->activePoint->setPoint(point); canvas()->updateCanvas(d->shape->boundingRect()); canvas()->updateCanvas(canvas()->snapGuide()->boundingRect()); } } } else { KoPathShape *pathShape = new KoPathShape(); d->shape = pathShape; pathShape->setShapeId(KoPathShapeId); KoShapeStrokeSP stroke(new KoShapeStroke()); const qreal size = canvas()->resourceManager()->resource(KisCanvasResourceProvider::Size).toReal(); stroke->setLineWidth(canvas()->unit().fromUserValue(size)); stroke->setColor(canvas()->resourceManager()->foregroundColor().toQColor()); pathShape->setStroke(stroke); canvas()->updateCanvas(canvas()->snapGuide()->boundingRect()); QPointF point = canvas()->snapGuide()->snap(event->point, event->modifiers()); // check whether we hit an start/end node of an existing path d->existingStartPoint = d->endPointAtPosition(point); if (d->existingStartPoint.isValid()) { point = d->existingStartPoint.path->shapeToDocument(d->existingStartPoint.point->point()); } d->activePoint = pathShape->moveTo(point); d->firstPoint = d->activePoint; canvas()->updateCanvas(handlePaintRect(point)); canvas()->updateCanvas(canvas()->snapGuide()->boundingRect()); canvas()->snapGuide()->setAdditionalEditedShape(pathShape); d->angleSnapStrategy = new AngleSnapStrategy(d->angleSnappingDelta, d->angleSnapStatus); canvas()->snapGuide()->addCustomSnapStrategy(d->angleSnapStrategy); } d->dragStartPoint = event->point; if (d->angleSnapStrategy) d->angleSnapStrategy->setStartPoint(d->activePoint->point()); } bool KoCreatePathTool::listeningToModifiers() { Q_D(KoCreatePathTool); return d->listeningToModifiers; } bool KoCreatePathTool::pathStarted() { Q_D(KoCreatePathTool); return ((bool) d->shape); } bool KoCreatePathTool::tryMergeInPathShape(KoPathShape *pathShape) { return addPathShapeImpl(pathShape, true); } void KoCreatePathTool::mouseDoubleClickEvent(KoPointerEvent *event) { //remove handle canvas()->updateCanvas(handlePaintRect(event->point)); endPathWithoutLastPoint(); } void KoCreatePathTool::mouseMoveEvent(KoPointerEvent *event) { Q_D(KoCreatePathTool); KoPathPoint *endPoint = d->endPointAtPosition(event->point); if (d->hoveredPoint != endPoint) { if (d->hoveredPoint) { QPointF nodePos = d->hoveredPoint->parent()->shapeToDocument(d->hoveredPoint->point()); canvas()->updateCanvas(handlePaintRect(nodePos)); } d->hoveredPoint = endPoint; if (d->hoveredPoint) { QPointF nodePos = d->hoveredPoint->parent()->shapeToDocument(d->hoveredPoint->point()); canvas()->updateCanvas(handlePaintRect(nodePos)); } } if (!pathStarted()) { canvas()->updateCanvas(canvas()->snapGuide()->boundingRect()); canvas()->snapGuide()->snap(event->point, event->modifiers()); canvas()->updateCanvas(canvas()->snapGuide()->boundingRect()); d->mouseOverFirstPoint = false; return; } d->mouseOverFirstPoint = handleGrabRect(d->firstPoint->point()).contains(event->point); canvas()->updateCanvas(d->shape->boundingRect()); canvas()->updateCanvas(canvas()->snapGuide()->boundingRect()); QPointF snappedPosition = canvas()->snapGuide()->snap(event->point, event->modifiers()); d->repaintActivePoint(); if (event->buttons() & Qt::LeftButton) { if (d->pointIsDragged || !handleGrabRect(d->dragStartPoint).contains(event->point)) { d->pointIsDragged = true; QPointF offset = snappedPosition - d->activePoint->point(); d->activePoint->setControlPoint2(d->activePoint->point() + offset); // pressing stops controls points moving symmetrically if ((event->modifiers() & Qt::AltModifier) == 0) { d->activePoint->setControlPoint1(d->activePoint->point() - offset); } d->repaintActivePoint(); } } else { d->activePoint->setPoint(snappedPosition); + + if (!d->prevPointWasDragged && d->autoSmoothCurves) { + KoPathPointIndex index = d->shape->pathPointIndex(d->activePoint); + if (index.second > 0) { + + KoPathPointIndex prevIndex(index.first, index.second - 1); + KoPathPoint *prevPoint = d->shape->pointByIndex(prevIndex); + + if (prevPoint) { + KoPathPoint *prevPrevPoint = 0; + + if (index.second > 1) { + KoPathPointIndex prevPrevIndex(index.first, index.second - 2); + prevPrevPoint = d->shape->pointByIndex(prevPrevIndex); + } + + if (prevPrevPoint) { + const QPointF control1 = prevPoint->point() + 0.3 * (prevPrevPoint->point() - prevPoint->point()); + prevPoint->setControlPoint1(control1); + } + + const QPointF control2 = prevPoint->point() + 0.3 * (d->activePoint->point() - prevPoint->point()); + prevPoint->setControlPoint2(control2); + + const QPointF activeControl = d->activePoint->point() + 0.3 * (prevPoint->point() - d->activePoint->point()); + d->activePoint->setControlPoint1(activeControl); + + KoPathPointTypeCommand::makeCubicPointSmooth(prevPoint); + } + } + } + } canvas()->updateCanvas(d->shape->boundingRect()); canvas()->updateCanvas(canvas()->snapGuide()->boundingRect()); } void KoCreatePathTool::mouseReleaseEvent(KoPointerEvent *event) { Q_D(KoCreatePathTool); if (! d->shape || (event->buttons() & Qt::RightButton)) return; d->listeningToModifiers = true; // After the first press-and-release d->repaintActivePoint(); + d->prevPointWasDragged = d->pointIsDragged; d->pointIsDragged = false; KoPathPoint *lastActivePoint = d->activePoint; if (!d->finishAfterThisPoint) { d->activePoint = d->shape->lineTo(event->point); canvas()->snapGuide()->setIgnoredPathPoints((QList() << d->activePoint)); } // apply symmetric point property if applicable if (lastActivePoint->activeControlPoint1() && lastActivePoint->activeControlPoint2()) { QPointF diff1 = lastActivePoint->point() - lastActivePoint->controlPoint1(); QPointF diff2 = lastActivePoint->controlPoint2() - lastActivePoint->point(); if (qFuzzyCompare(diff1.x(), diff2.x()) && qFuzzyCompare(diff1.y(), diff2.y())) lastActivePoint->setProperty(KoPathPoint::IsSymmetric); } if (d->finishAfterThisPoint) { d->firstPoint->setControlPoint1(d->activePoint->controlPoint1()); delete d->shape->removePoint(d->shape->pathPointIndex(d->activePoint)); d->activePoint = d->firstPoint; + + if (!d->prevPointWasDragged && d->autoSmoothCurves) { + KoPathPointTypeCommand::makeCubicPointSmooth(d->activePoint); + } + d->shape->closeMerge(); // we are closing the path, so reset the existing start path point d->existingStartPoint = 0; // finish path endPath(); } if (d->angleSnapStrategy && lastActivePoint->activeControlPoint2()) { d->angleSnapStrategy->deactivate(); } } void KoCreatePathTool::keyPressEvent(QKeyEvent *event) { if (event->key() == Qt::Key_Escape) { emit done(); } else { event->ignore(); } } void KoCreatePathTool::endPath() { Q_D(KoCreatePathTool); d->addPathShape(); } void KoCreatePathTool::endPathWithoutLastPoint() { Q_D(KoCreatePathTool); if (d->shape) { QRectF dirtyRect = d->shape->boundingRect(); delete d->shape->removePoint(d->shape->pathPointIndex(d->activePoint)); canvas()->updateCanvas(dirtyRect); d->addPathShape(); } } void KoCreatePathTool::cancelPath() { Q_D(KoCreatePathTool); if (d->shape) { canvas()->updateCanvas(handlePaintRect(d->firstPoint->point())); canvas()->updateCanvas(d->shape->boundingRect()); d->firstPoint = 0; d->activePoint = 0; } d->cleanUp(); } void KoCreatePathTool::removeLastPoint() { Q_D(KoCreatePathTool); if ((d->shape)) { KoPathPointIndex lastPointIndex = d->shape->pathPointIndex(d->activePoint); if (lastPointIndex.second > 1) { lastPointIndex.second--; delete d->shape->removePoint(lastPointIndex); d->hoveredPoint = 0; d->repaintActivePoint(); canvas()->updateCanvas(d->shape->boundingRect()); } } } void KoCreatePathTool::activate(ToolActivation activation, const QSet &shapes) { KoToolBase::activate(activation, shapes); Q_D(KoCreatePathTool); useCursor(Qt::ArrowCursor); // retrieve the actual global handle radius d->handleRadius = handleRadius(); + d->loadAutoSmoothValueFromConfig(); // reset snap guide canvas()->updateCanvas(canvas()->snapGuide()->boundingRect()); canvas()->snapGuide()->reset(); } void KoCreatePathTool::deactivate() { cancelPath(); KoToolBase::deactivate(); } void KoCreatePathTool::documentResourceChanged(int key, const QVariant & res) { Q_D(KoCreatePathTool); switch (key) { case KoDocumentResourceManager::HandleRadius: { d->handleRadius = res.toUInt(); } break; default: return; } } bool KoCreatePathTool::addPathShapeImpl(KoPathShape *pathShape, bool tryMergeOnly) { Q_D(KoCreatePathTool); KoPathShape *startShape = 0; KoPathShape *endShape = 0; pathShape->normalize(); // check if existing start/end points are still valid d->existingStartPoint.validate(canvas()); d->existingEndPoint.validate(canvas()); if (d->connectPaths(pathShape, d->existingStartPoint, d->existingEndPoint)) { if (d->existingStartPoint.isValid()) { startShape = d->existingStartPoint.path; } if (d->existingEndPoint.isValid() && d->existingEndPoint != d->existingStartPoint) { endShape = d->existingEndPoint.path; } } if (tryMergeOnly && !startShape && !endShape) { return false; } KUndo2Command *cmd = canvas()->shapeController()->addShape(pathShape, 0); KIS_SAFE_ASSERT_RECOVER(cmd) { canvas()->updateCanvas(pathShape->boundingRect()); delete pathShape; return true; } KoSelection *selection = canvas()->shapeManager()->selection(); selection->deselectAll(); selection->select(pathShape); if (startShape) { pathShape->setBackground(startShape->background()); pathShape->setStroke(startShape->stroke()); } else if (endShape) { pathShape->setBackground(endShape->background()); pathShape->setStroke(endShape->stroke()); } if (startShape) { canvas()->shapeController()->removeShape(startShape, cmd); } if (endShape && startShape != endShape) { canvas()->shapeController()->removeShape(endShape, cmd); } canvas()->addCommand(cmd); return true; } void KoCreatePathTool::addPathShape(KoPathShape *pathShape) { addPathShapeImpl(pathShape, false); } QList > KoCreatePathTool::createOptionWidgets() { Q_D(KoCreatePathTool); QList > list; + QCheckBox *smoothCurves = new QCheckBox(i18n("Autosmooth curve")); + smoothCurves->setObjectName("smooth-curves-widget"); + smoothCurves->setChecked(d->autoSmoothCurves); + connect(smoothCurves, SIGNAL(toggled(bool)), this, SLOT(autoSmoothCurvesChanged(bool))); + connect(this, SIGNAL(sigUpdateAutoSmoothCurvesGUI(bool)), smoothCurves, SLOT(setChecked(bool))); + + list.append(smoothCurves); + QWidget *angleWidget = new QWidget(); angleWidget->setObjectName("Angle Constraints"); QGridLayout *layout = new QGridLayout(angleWidget); layout->addWidget(new QLabel(i18n("Angle snapping delta:"), angleWidget), 0, 0); QSpinBox *angleEdit = new KisIntParseSpinBox(angleWidget); angleEdit->setValue(d->angleSnappingDelta); angleEdit->setRange(1, 360); angleEdit->setSingleStep(1); angleEdit->setSuffix(QChar(Qt::Key_degree)); layout->addWidget(angleEdit, 0, 1); layout->addWidget(new QLabel(i18n("Activate angle snap:"), angleWidget), 1, 0); QCheckBox *angleSnap = new QCheckBox(angleWidget); angleSnap->setChecked(false); angleSnap->setCheckable(true); layout->addWidget(angleSnap, 1, 1); QWidget *specialSpacer = new QWidget(); specialSpacer->setObjectName("SpecialSpacer"); layout->addWidget(specialSpacer, 2, 1); angleWidget->setWindowTitle(i18n("Angle Constraints")); list.append(angleWidget); connect(angleEdit, SIGNAL(valueChanged(int)), this, SLOT(angleDeltaChanged(int))); connect(angleSnap, SIGNAL(stateChanged(int)), this, SLOT(angleSnapChanged(int))); return list; } //have to include this because of Q_PRIVATE_SLOT #include diff --git a/libs/basicflakes/tools/KoCreatePathTool.h b/libs/basicflakes/tools/KoCreatePathTool.h index 3924e354f1..a1c5a437e9 100644 --- a/libs/basicflakes/tools/KoCreatePathTool.h +++ b/libs/basicflakes/tools/KoCreatePathTool.h @@ -1,113 +1,117 @@ /* This file is part of the KDE project * * Copyright (C) 2006 Thorsten Zachmann * Copyright (C) 2008-2009 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. */ #ifndef KOCREATEPATHTOOL_H #define KOCREATEPATHTOOL_H #include "kritabasicflakes_export.h" #include #include #include class KoPathShape; class KoShapeStroke; class KoCreatePathToolPrivate; #define KoCreatePathTool_ID "CreatePathTool" /** * Tool for creating path shapes. */ class KRITABASICFLAKES_EXPORT KoCreatePathTool : public KoToolBase { Q_OBJECT public: /** * Constructor for the tool that allows you to create new paths by hand. * @param canvas the canvas this tool will be working for. */ explicit KoCreatePathTool(KoCanvasBase * canvas); ~KoCreatePathTool() override; /// reimplemented void paint(QPainter &painter, const KoViewConverter &converter) override; /// reimplemented void mousePressEvent(KoPointerEvent *event) override; /// reimplemented void mouseDoubleClickEvent(KoPointerEvent *event) override; /// reimplemented void mouseMoveEvent(KoPointerEvent *event) override; /// reimplemented void mouseReleaseEvent(KoPointerEvent *event) override; /// reimplemented void keyPressEvent(QKeyEvent *event) override; /// For behavior as selection tool and with initial shift-key virtual bool listeningToModifiers(); /** * Returns true if path has been started */ bool pathStarted(); bool tryMergeInPathShape(KoPathShape *pathShape); public Q_SLOTS: /// reimplemented void activate(ToolActivation activation, const QSet &shapes) override; /// reimplemented void deactivate() override; /// reimplemented void documentResourceChanged(int key, const QVariant & res) override; +Q_SIGNALS: + void sigUpdateAutoSmoothCurvesGUI(bool value); + protected: /** * Add path shape to document. * This method can be overridden and change the behaviour of the tool. In that case the subclass takes ownership of pathShape. * It gets only called if there are two or more points in the path. */ virtual void addPathShape(KoPathShape* pathShape); protected: /** * This method is called to paint the path. Decorations are drawn by KoCreatePathTool afterwards. */ virtual void paintPath(KoPathShape& pathShape, QPainter &painter, const KoViewConverter &converter); void endPath(); void endPathWithoutLastPoint(); void cancelPath(); void removeLastPoint(); bool addPathShapeImpl(KoPathShape* pathShape, bool tryMergeOnly); /// reimplemented QList > createOptionWidgets() override; private: Q_DECLARE_PRIVATE(KoCreatePathTool) Q_PRIVATE_SLOT(d_func(), void angleDeltaChanged(int)) Q_PRIVATE_SLOT(d_func(), void angleSnapChanged(int)) + Q_PRIVATE_SLOT(d_func(), void autoSmoothCurvesChanged(bool)) }; #endif diff --git a/libs/basicflakes/tools/KoCreatePathTool_p.h b/libs/basicflakes/tools/KoCreatePathTool_p.h index 2f68d9f479..c6b863a56c 100644 --- a/libs/basicflakes/tools/KoCreatePathTool_p.h +++ b/libs/basicflakes/tools/KoCreatePathTool_p.h @@ -1,429 +1,446 @@ /* This file is part of the KDE project * * Copyright (C) 2006 Thorsten Zachmann * Copyright (C) 2008-2010 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. */ #ifndef KOCREATEPATHTOOL_P_H #define KOCREATEPATHTOOL_P_H #include "KoCreatePathTool.h" #include "KoPathPoint.h" #include "KoPathPointData.h" #include "KoPathPointMergeCommand.h" #include "KoParameterShape.h" #include "KoShapeManager.h" #include "KoSnapStrategy.h" #include "KoToolBase_p.h" #include +#include "kis_config.h" #include "math.h" class KoStrokeConfigWidget; class KoConverter; /// Small helper to keep track of a path point and its parent path shape struct PathConnectionPoint { PathConnectionPoint() : path(0), point(0) { } // reset state to invalid void reset() { path = 0; point = 0; } PathConnectionPoint& operator =(KoPathPoint * pathPoint) { if (!pathPoint || ! pathPoint->parent()) { reset(); } else { path = pathPoint->parent(); point = pathPoint; } return *this; } bool operator != (const PathConnectionPoint &rhs) const { return rhs.path != path || rhs.point != point; } bool operator == (const PathConnectionPoint &rhs) const { return rhs.path == path && rhs.point == point; } bool isValid() const { return path && point; } // checks if the path and point are still valid void validate(KoCanvasBase *canvas) { // no point in validating an already invalid state if (!isValid()) { return; } // we need canvas to validate if (!canvas) { reset(); return; } // check if path is still part of the docment if (!canvas->shapeManager()->shapes().contains(path)) { reset(); return; } // check if point is still part of the path if (path->pathPointIndex(point) == KoPathPointIndex(-1, -1)) { reset(); return; } } KoPathShape * path; KoPathPoint * point; }; inline qreal squareDistance(const QPointF &p1, const QPointF &p2) { qreal dx = p1.x() - p2.x(); qreal dy = p1.y() - p2.y(); return dx * dx + dy * dy; } class AngleSnapStrategy : public KoSnapStrategy { public: explicit AngleSnapStrategy(qreal angleStep, bool active) : KoSnapStrategy(KoSnapGuide::CustomSnapping), m_angleStep(angleStep), m_active(active) { } void setStartPoint(const QPointF &startPoint) { m_startPoint = startPoint; } void setAngleStep(qreal angleStep) { m_angleStep = qAbs(angleStep); } bool snap(const QPointF &mousePosition, KoSnapProxy * proxy, qreal maxSnapDistance) override { Q_UNUSED(proxy); if (!m_active) return false; QLineF line(m_startPoint, mousePosition); qreal currentAngle = line.angle(); int prevStep = qAbs(currentAngle / m_angleStep); int nextStep = prevStep + 1; qreal prevAngle = prevStep * m_angleStep; qreal nextAngle = nextStep * m_angleStep; if (qAbs(currentAngle - prevAngle) <= qAbs(currentAngle - nextAngle)) { line.setAngle(prevAngle); } else { line.setAngle(nextAngle); } qreal maxSquareSnapDistance = maxSnapDistance * maxSnapDistance; qreal snapDistance = squareDistance(mousePosition, line.p2()); if (snapDistance > maxSquareSnapDistance) return false; setSnappedPosition(line.p2()); return true; } QPainterPath decoration(const KoViewConverter &converter) const override { Q_UNUSED(converter); QPainterPath decoration; decoration.moveTo(m_startPoint); decoration.lineTo(snappedPosition()); return decoration; } void deactivate() { m_active = false; } void activate() { m_active = true; } private: QPointF m_startPoint; qreal m_angleStep; bool m_active; }; class KoCreatePathToolPrivate : public KoToolBasePrivate { KoCreatePathTool * const q; public: KoCreatePathToolPrivate(KoCreatePathTool * const qq, KoCanvasBase* canvas) : KoToolBasePrivate(qq, canvas), q(qq), shape(0), activePoint(0), firstPoint(0), handleRadius(3), mouseOverFirstPoint(false), pointIsDragged(false), finishAfterThisPoint(false), hoveredPoint(0), listeningToModifiers(false), angleSnapStrategy(0), angleSnappingDelta(15), angleSnapStatus(false) { } KoPathShape *shape; KoPathPoint *activePoint; KoPathPoint *firstPoint; int handleRadius; bool mouseOverFirstPoint; bool pointIsDragged; bool finishAfterThisPoint; PathConnectionPoint existingStartPoint; ///< an existing path point we started a new path at PathConnectionPoint existingEndPoint; ///< an existing path point we finished a new path at KoPathPoint *hoveredPoint; ///< an existing path end point the mouse is hovering on bool listeningToModifiers; // Fine tune when to begin processing modifiers at the beginning of a stroke. + bool prevPointWasDragged = false; + bool autoSmoothCurves = false; QPointF dragStartPoint; AngleSnapStrategy *angleSnapStrategy; int angleSnappingDelta; bool angleSnapStatus; void repaintActivePoint() const { const bool isFirstPoint = (activePoint == firstPoint); if (!isFirstPoint && !pointIsDragged) return; QRectF rect = activePoint->boundingRect(false); // make sure that we have the second control point inside our // update rect, as KoPathPoint::boundingRect will not include // the second control point of the last path point if the path // is not closed const QPointF &point = activePoint->point(); const QPointF &controlPoint = activePoint->controlPoint2(); rect = rect.united(QRectF(point, controlPoint).normalized()); // when painting the first point we want the // first control point to be painted as well if (isFirstPoint) { const QPointF &controlPoint = activePoint->controlPoint1(); rect = rect.united(QRectF(point, controlPoint).normalized()); } QPointF border = q->canvas()->viewConverter() ->viewToDocument(QPointF(handleRadius, handleRadius)); rect.adjust(-border.x(), -border.y(), border.x(), border.y()); q->canvas()->updateCanvas(rect); } /// returns the nearest existing path point KoPathPoint* endPointAtPosition(const QPointF &position) const { QRectF roi = q->handleGrabRect(position); QList shapes = q->canvas()->shapeManager()->shapesAt(roi); KoPathPoint * nearestPoint = 0; qreal minDistance = HUGE_VAL; uint grabSensitivity = q->grabSensitivity(); qreal maxDistance = q->canvas()->viewConverter()->viewToDocumentX(grabSensitivity); Q_FOREACH(KoShape * s, shapes) { KoPathShape * path = dynamic_cast(s); if (!path) continue; KoParameterShape *paramShape = dynamic_cast(s); if (paramShape && paramShape->isParametricShape()) continue; KoPathPoint * p = 0; uint subpathCount = path->subpathCount(); for (uint i = 0; i < subpathCount; ++i) { if (path->isClosedSubpath(i)) continue; p = path->pointByIndex(KoPathPointIndex(i, 0)); // check start of subpath qreal d = squareDistance(position, path->shapeToDocument(p->point())); if (d < minDistance && d < maxDistance) { nearestPoint = p; minDistance = d; } // check end of subpath p = path->pointByIndex(KoPathPointIndex(i, path->subpathPointCount(i) - 1)); d = squareDistance(position, path->shapeToDocument(p->point())); if (d < minDistance && d < maxDistance) { nearestPoint = p; minDistance = d; } } } return nearestPoint; } /// Connects given path with the ones we hit when starting/finishing bool connectPaths(KoPathShape *pathShape, const PathConnectionPoint &pointAtStart, const PathConnectionPoint &pointAtEnd) const { KoPathShape * startShape = 0; KoPathShape * endShape = 0; KoPathPoint * startPoint = 0; KoPathPoint * endPoint = 0; if (pointAtStart.isValid()) { startShape = pointAtStart.path; startPoint = pointAtStart.point; } if (pointAtEnd.isValid()) { endShape = pointAtEnd.path; endPoint = pointAtEnd.point; } // at least one point must be valid if (!startPoint && !endPoint) return false; // do not allow connecting to the same point twice if (startPoint == endPoint) endPoint = 0; // we have hit an existing path point on start/finish // what we now do is: // 1. combine the new created path with the ones we hit on start/finish // 2. merge the endpoints of the corresponding subpaths uint newPointCount = pathShape->subpathPointCount(0); KoPathPointIndex newStartPointIndex(0, 0); KoPathPointIndex newEndPointIndex(0, newPointCount - 1); KoPathPoint * newStartPoint = pathShape->pointByIndex(newStartPointIndex); KoPathPoint * newEndPoint = pathShape->pointByIndex(newEndPointIndex); // combine with the path we hit on start KoPathPointIndex startIndex(-1, -1); if (startShape && startPoint) { startIndex = startShape->pathPointIndex(startPoint); pathShape->combine(startShape); pathShape->moveSubpath(0, pathShape->subpathCount() - 1); } // combine with the path we hit on finish KoPathPointIndex endIndex(-1, -1); if (endShape && endPoint) { endIndex = endShape->pathPointIndex(endPoint); if (endShape != startShape) { endIndex.first += pathShape->subpathCount(); pathShape->combine(endShape); } } // do we connect twice to a single subpath ? bool connectToSingleSubpath = (startShape == endShape && startIndex.first == endIndex.first); if (startIndex.second == 0 && !connectToSingleSubpath) { pathShape->reverseSubpath(startIndex.first); startIndex.second = pathShape->subpathPointCount(startIndex.first) - 1; } if (endIndex.second > 0 && !connectToSingleSubpath) { pathShape->reverseSubpath(endIndex.first); endIndex.second = 0; } // after combining we have a path where with the subpaths in the following // order: // 1. the subpaths of the pathshape we started the new path at // 2. the subpath we just created // 3. the subpaths of the pathshape we finished the new path at // get the path points we want to merge, as these are not going to // change while merging KoPathPoint * existingStartPoint = pathShape->pointByIndex(startIndex); KoPathPoint * existingEndPoint = pathShape->pointByIndex(endIndex); // merge first two points if (existingStartPoint) { KoPathPointData pd1(pathShape, pathShape->pathPointIndex(existingStartPoint)); KoPathPointData pd2(pathShape, pathShape->pathPointIndex(newStartPoint)); KoPathPointMergeCommand cmd1(pd1, pd2); cmd1.redo(); } // merge last two points if (existingEndPoint) { KoPathPointData pd3(pathShape, pathShape->pathPointIndex(newEndPoint)); KoPathPointData pd4(pathShape, pathShape->pathPointIndex(existingEndPoint)); KoPathPointMergeCommand cmd2(pd3, pd4); cmd2.redo(); } return true; } void addPathShape() { if (!shape) return; if (shape->pointCount() < 2) { cleanUp(); return; } // this is done so that nothing happens when the mouseReleaseEvent for the this event is received KoPathShape *pathShape = shape; shape = 0; q->addPathShape(pathShape); cleanUp(); return; } void cleanUp() { // reset snap guide q->canvas()->updateCanvas(q->canvas()->snapGuide()->boundingRect()); q->canvas()->snapGuide()->reset(); angleSnapStrategy = 0; delete shape; shape = 0; existingStartPoint = 0; existingEndPoint = 0; hoveredPoint = 0; listeningToModifiers = false; } void angleDeltaChanged(int value) { angleSnappingDelta = value; if (angleSnapStrategy) angleSnapStrategy->setAngleStep(angleSnappingDelta); } + void autoSmoothCurvesChanged(bool value) { + autoSmoothCurves = value; + + KisConfig cfg(false); + cfg.setAutoSmoothBezierCurves(value); + } + + void loadAutoSmoothValueFromConfig() { + KisConfig cfg(true); + autoSmoothCurves = cfg.autoSmoothBezierCurves(); + + emit q->sigUpdateAutoSmoothCurvesGUI(autoSmoothCurves); + } + void angleSnapChanged(int angleSnap) { angleSnapStatus = ! angleSnapStatus; if (angleSnapStrategy) { if (angleSnap == Qt::Checked) angleSnapStrategy->activate(); else angleSnapStrategy->deactivate(); } } }; #endif // KOCREATEPATHTOOL_P_H diff --git a/libs/flake/commands/KoPathPointTypeCommand.cpp b/libs/flake/commands/KoPathPointTypeCommand.cpp index d61ac53e7b..57d4351bd7 100644 --- a/libs/flake/commands/KoPathPointTypeCommand.cpp +++ b/libs/flake/commands/KoPathPointTypeCommand.cpp @@ -1,224 +1,235 @@ /* This file is part of the KDE project * Copyright (C) 2006,2008 Jan Hambrecht * Copyright (C) 2006,2007 Thorsten Zachmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KoPathPointTypeCommand.h" #include #include #include "KoPathSegment.h" KoPathPointTypeCommand::KoPathPointTypeCommand( const QList & pointDataList, PointType pointType, KUndo2Command *parent) : KoPathBaseCommand(parent) , m_pointType(pointType) { QList::const_iterator it(pointDataList.begin()); for (; it != pointDataList.end(); ++it) { KoPathPoint *point = it->pathShape->pointByIndex(it->pointIndex); if (point) { PointData pointData(*it); pointData.m_oldControlPoint1 = it->pathShape->shapeToDocument(point->controlPoint1()); pointData.m_oldControlPoint2 = it->pathShape->shapeToDocument(point->controlPoint2()); pointData.m_oldProperties = point->properties(); pointData.m_hadControlPoint1 = point->activeControlPoint1(); pointData.m_hadControlPoint2 = point->activeControlPoint2(); m_oldPointData.append(pointData); m_shapes.insert(it->pathShape); } } setText(kundo2_i18n("Set point type")); } KoPathPointTypeCommand::~KoPathPointTypeCommand() { } void KoPathPointTypeCommand::redo() { KUndo2Command::redo(); repaint(false); m_additionalPointData.clear(); QList::iterator it(m_oldPointData.begin()); for (; it != m_oldPointData.end(); ++it) { KoPathPoint *point = it->m_pointData.pathShape->pointByIndex(it->m_pointData.pointIndex); KoPathPoint::PointProperties properties = point->properties(); switch (m_pointType) { case Line: { point->removeControlPoint1(); point->removeControlPoint2(); break; } case Curve: { KoPathPointIndex pointIndex = it->m_pointData.pointIndex; KoPathPointIndex prevIndex; KoPathPointIndex nextIndex; KoPathShape * path = it->m_pointData.pathShape; // get previous path node if (pointIndex.second > 0) prevIndex = KoPathPointIndex(pointIndex.first, pointIndex.second - 1); else if (pointIndex.second == 0 && path->isClosedSubpath(pointIndex.first)) prevIndex = KoPathPointIndex(pointIndex.first, path->subpathPointCount(pointIndex.first) - 1); // get next node if (pointIndex.second < path->subpathPointCount(pointIndex.first) - 1) nextIndex = KoPathPointIndex(pointIndex.first, pointIndex.second + 1); else if (pointIndex.second < path->subpathPointCount(pointIndex.first) - 1 && path->isClosedSubpath(pointIndex.first)) nextIndex = KoPathPointIndex(pointIndex.first, 0); KoPathPoint * prevPoint = path->pointByIndex(prevIndex); KoPathPoint * nextPoint = path->pointByIndex(nextIndex); if (prevPoint && ! point->activeControlPoint1() && appendPointData(KoPathPointData(path, prevIndex))) { KoPathSegment cubic = KoPathSegment(prevPoint, point).toCubic(); if (prevPoint->activeControlPoint2()) { prevPoint->setControlPoint2(cubic.first()->controlPoint2()); point->setControlPoint1(cubic.second()->controlPoint1()); } else point->setControlPoint1(cubic.second()->controlPoint1()); } if (nextPoint && ! point->activeControlPoint2() && appendPointData(KoPathPointData(path, nextIndex))) { KoPathSegment cubic = KoPathSegment(point, nextPoint).toCubic(); if (nextPoint->activeControlPoint1()) { point->setControlPoint2(cubic.first()->controlPoint2()); nextPoint->setControlPoint1(cubic.second()->controlPoint1()); } else point->setControlPoint2(cubic.first()->controlPoint2()); } + point->setProperties(properties); break; } case Symmetric: { properties &= ~KoPathPoint::IsSmooth; properties |= KoPathPoint::IsSymmetric; // calculate vector from node point to first control point and normalize it QPointF directionC1 = point->controlPoint1() - point->point(); qreal dirLengthC1 = sqrt(directionC1.x() * directionC1.x() + directionC1.y() * directionC1.y()); directionC1 /= dirLengthC1; // calculate vector from node point to second control point and normalize it QPointF directionC2 = point->controlPoint2() - point->point(); qreal dirLengthC2 = sqrt(directionC2.x() * directionC2.x() + directionC2.y() * directionC2.y()); directionC2 /= dirLengthC2; // calculate the average distance of the control points to the node point qreal averageLength = 0.5 * (dirLengthC1 + dirLengthC2); // compute position of the control points so that they lie on a line going through the node point // the new distance of the control points is the average distance to the node point point->setControlPoint1(point->point() + 0.5 * averageLength * (directionC1 - directionC2)); point->setControlPoint2(point->point() + 0.5 * averageLength * (directionC2 - directionC1)); + point->setProperties(properties); } break; case Smooth: { - properties &= ~KoPathPoint::IsSymmetric; - properties |= KoPathPoint::IsSmooth; - - // calculate vector from node point to first control point and normalize it - QPointF directionC1 = point->controlPoint1() - point->point(); - qreal dirLengthC1 = sqrt(directionC1.x() * directionC1.x() + directionC1.y() * directionC1.y()); - directionC1 /= dirLengthC1; - // calculate vector from node point to second control point and normalize it - QPointF directionC2 = point->controlPoint2() - point->point(); - qreal dirLengthC2 = sqrt(directionC2.x() * directionC2.x() + directionC2.y() * directionC2.y()); - directionC2 /= dirLengthC2; - // compute position of the control points so that they lie on a line going through the node point - // the new distance of the control points is the average distance to the node point - point->setControlPoint1(point->point() + 0.5 * dirLengthC1 * (directionC1 - directionC2)); - point->setControlPoint2(point->point() + 0.5 * dirLengthC2 * (directionC2 - directionC1)); + makeCubicPointSmooth(point); } break; case Corner: default: properties &= ~KoPathPoint::IsSymmetric; properties &= ~KoPathPoint::IsSmooth; + point->setProperties(properties); break; } - point->setProperties(properties); } repaint(true); } void KoPathPointTypeCommand::undo() { KUndo2Command::undo(); repaint(false); /* QList::iterator it(m_oldPointData.begin()); for (; it != m_oldPointData.end(); ++it) { KoPathShape *pathShape = it->m_pointData.pathShape; KoPathPoint *point = pathShape->pointByIndex(it->m_pointData.pointIndex); point->setProperties(it->m_oldProperties); if (it->m_hadControlPoint1) point->setControlPoint1(pathShape->documentToShape(it->m_oldControlPoint1)); else point->removeControlPoint1(); if (it->m_hadControlPoint2) point->setControlPoint2(pathShape->documentToShape(it->m_oldControlPoint2)); else point->removeControlPoint2(); } */ undoChanges(m_oldPointData); undoChanges(m_additionalPointData); repaint(true); } +void KoPathPointTypeCommand::makeCubicPointSmooth(KoPathPoint *point) +{ + KoPathPoint::PointProperties properties = point->properties(); + + properties &= ~KoPathPoint::IsSymmetric; + properties |= KoPathPoint::IsSmooth; + + // calculate vector from node point to first control point and normalize it + QPointF directionC1 = point->controlPoint1() - point->point(); + qreal dirLengthC1 = sqrt(directionC1.x() * directionC1.x() + directionC1.y() * directionC1.y()); + directionC1 /= dirLengthC1; + // calculate vector from node point to second control point and normalize it + QPointF directionC2 = point->controlPoint2() - point->point(); + qreal dirLengthC2 = sqrt(directionC2.x() * directionC2.x() + directionC2.y() * directionC2.y()); + directionC2 /= dirLengthC2; + // compute position of the control points so that they lie on a line going through the node point + // the new distance of the control points is the average distance to the node point + point->setControlPoint1(point->point() + 0.5 * dirLengthC1 * (directionC1 - directionC2)); + point->setControlPoint2(point->point() + 0.5 * dirLengthC2 * (directionC2 - directionC1)); + + point->setProperties(properties); +} + void KoPathPointTypeCommand::undoChanges(const QList &data) { QList::const_iterator it(data.begin()); for (; it != data.end(); ++it) { KoPathShape *pathShape = it->m_pointData.pathShape; KoPathPoint *point = pathShape->pointByIndex(it->m_pointData.pointIndex); point->setProperties(it->m_oldProperties); if (it->m_hadControlPoint1) point->setControlPoint1(pathShape->documentToShape(it->m_oldControlPoint1)); else point->removeControlPoint1(); if (it->m_hadControlPoint2) point->setControlPoint2(pathShape->documentToShape(it->m_oldControlPoint2)); else point->removeControlPoint2(); } } bool KoPathPointTypeCommand::appendPointData(KoPathPointData data) { KoPathPoint *point = data.pathShape->pointByIndex(data.pointIndex); if (! point) return false; PointData pointData(data); pointData.m_oldControlPoint1 = data.pathShape->shapeToDocument(point->controlPoint1()); pointData.m_oldControlPoint2 = data.pathShape->shapeToDocument(point->controlPoint2()); pointData.m_oldProperties = point->properties(); pointData.m_hadControlPoint1 = point->activeControlPoint1(); pointData.m_hadControlPoint2 = point->activeControlPoint2(); m_additionalPointData.append(pointData); return true; } diff --git a/libs/flake/commands/KoPathPointTypeCommand.h b/libs/flake/commands/KoPathPointTypeCommand.h index 849ec8b804..f025a2314f 100644 --- a/libs/flake/commands/KoPathPointTypeCommand.h +++ b/libs/flake/commands/KoPathPointTypeCommand.h @@ -1,79 +1,81 @@ /* This file is part of the KDE project * Copyright (C) 2006,2008 Jan Hambrecht * Copyright (C) 2006,2007 Thorsten Zachmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KOPATHPOINTTYPECOMMAND_H #define KOPATHPOINTTYPECOMMAND_H #include #include #include "KoPathBaseCommand.h" #include "KoPathPoint.h" #include "KoPathPointData.h" #include "kritaflake_export.h" /// The undo / redo command for changing the path point type. class KRITAFLAKE_EXPORT KoPathPointTypeCommand : public KoPathBaseCommand { public: /// The type of the point enum PointType { Corner, Smooth, Symmetric, Line, Curve }; /** * Command to change the type of the given points * @param pointDataList List of point for changing the points * @param pointType the new point type to set * @param parent the parent command used for macro commands */ KoPathPointTypeCommand(const QList &pointDataList, PointType pointType, KUndo2Command *parent = 0); ~KoPathPointTypeCommand() override; /// redo the command void redo() override; /// revert the actions done in redo void undo() override; + static void makeCubicPointSmooth(KoPathPoint *point); + private: // used for storing the data for undo struct PointData { PointData(const KoPathPointData pointData) : m_pointData(pointData) {} KoPathPointData m_pointData; // old control points in document coordinates QPointF m_oldControlPoint1; QPointF m_oldControlPoint2; KoPathPoint::PointProperties m_oldProperties; bool m_hadControlPoint1; bool m_hadControlPoint2; }; bool appendPointData(KoPathPointData data); void undoChanges(const QList &data); PointType m_pointType; QList m_oldPointData; QList m_additionalPointData; }; #endif // KOPATHPOINTTYPECOMMAND_H diff --git a/libs/flake/text/KoSvgTextShape.cpp b/libs/flake/text/KoSvgTextShape.cpp index df2fb9b035..321a00ed38 100644 --- a/libs/flake/text/KoSvgTextShape.cpp +++ b/libs/flake/text/KoSvgTextShape.cpp @@ -1,628 +1,628 @@ /* * Copyright (c) 2017 Dmitry Kazakov * * 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 "KoSvgTextShape.h" #include #include #include "KoSvgText.h" #include "KoSvgTextProperties.h" #include #include #include #include #include #include "kis_debug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -struct KoSvgTextShapePrivate : public KoSvgTextChunkShapePrivate +class KoSvgTextShapePrivate : public KoSvgTextChunkShapePrivate { KoSvgTextShapePrivate(KoSvgTextShape *_q) : KoSvgTextChunkShapePrivate(_q) { } KoSvgTextShapePrivate(const KoSvgTextShapePrivate &rhs, KoSvgTextShape *q) : KoSvgTextChunkShapePrivate(rhs, q) { } std::vector> cachedLayouts; std::vector cachedLayoutsOffsets; QThread *cachedLayoutsWorkingThread = 0; void clearAssociatedOutlines(KoShape *rootShape); Q_DECLARE_PUBLIC(KoSvgTextShape) }; KoSvgTextShape::KoSvgTextShape() : KoSvgTextChunkShape(new KoSvgTextShapePrivate(this)) { setShapeId(KoSvgTextShape_SHAPEID); } KoSvgTextShape::KoSvgTextShape(const KoSvgTextShape &rhs) : KoSvgTextChunkShape(new KoSvgTextShapePrivate(*rhs.d_func(), this)) { setShapeId(KoSvgTextShape_SHAPEID); // QTextLayout has no copy-ctor, so just relayout everything! relayout(); } KoSvgTextShape::~KoSvgTextShape() { } KoShape *KoSvgTextShape::cloneShape() const { return new KoSvgTextShape(*this); } void KoSvgTextShape::shapeChanged(ChangeType type, KoShape *shape) { KoSvgTextChunkShape::shapeChanged(type, shape); if (type == StrokeChanged || type == BackgroundChanged || type == ContentChanged) { relayout(); } } void KoSvgTextShape::paintComponent(QPainter &painter, const KoViewConverter &converter, KoShapePaintingContext &paintContext) { Q_D(KoSvgTextShape); Q_UNUSED(paintContext); /** * HACK ALERT: * QTextLayout should only be accessed from the thread it has been created in. * If the cached layout has been created in a different thread, we should just * recreate the layouts in the current thread to be able to render them. */ if (QThread::currentThread() != d->cachedLayoutsWorkingThread) { relayout(); } applyConversion(painter, converter); for (int i = 0; i < (int)d->cachedLayouts.size(); i++) { d->cachedLayouts[i]->draw(&painter, d->cachedLayoutsOffsets[i]); } /** * HACK ALERT: * The layouts of non-gui threads must be destroyed in the same thread * they have been created. Because the thread might be restarted in the * meantime or just destroyed, meaning that the per-thread freetype data * will not be available. */ if (QThread::currentThread() != qApp->thread()) { d->cachedLayouts.clear(); d->cachedLayoutsOffsets.clear(); d->cachedLayoutsWorkingThread = 0; } } void KoSvgTextShape::paintStroke(QPainter &painter, const KoViewConverter &converter, KoShapePaintingContext &paintContext) { Q_UNUSED(painter); Q_UNUSED(converter); Q_UNUSED(paintContext); // do nothing! everything is painted in paintComponent() } QPainterPath KoSvgTextShape::textOutline() { Q_D(KoSvgTextShape); QPainterPath result; result.setFillRule(Qt::WindingFill); for (int i = 0; i < (int)d->cachedLayouts.size(); i++) { const QPointF layoutOffset = d->cachedLayoutsOffsets[i]; const QTextLayout *layout = d->cachedLayouts[i].get(); for (int j = 0; j < layout->lineCount(); j++) { QTextLine line = layout->lineAt(j); Q_FOREACH (const QGlyphRun &run, line.glyphRuns()) { const QVector indexes = run.glyphIndexes(); const QVector positions = run.positions(); const QRawFont font = run.rawFont(); KIS_SAFE_ASSERT_RECOVER(indexes.size() == positions.size()) { continue; } for (int k = 0; k < indexes.size(); k++) { QPainterPath glyph = font.pathForGlyph(indexes[k]); glyph.translate(positions[k] + layoutOffset); result += glyph; } const qreal thickness = font.lineThickness(); const QRectF runBounds = run.boundingRect(); if (run.overline()) { // the offset is calculated to be consistent with the way how Qt renders the text const qreal y = line.y(); QRectF overlineBlob(runBounds.x(), y, runBounds.width(), thickness); overlineBlob.translate(layoutOffset); QPainterPath path; path.addRect(overlineBlob); // don't use direct addRect, because it doesn't care about Qt::WindingFill result += path; } if (run.strikeOut()) { // the offset is calculated to be consistent with the way how Qt renders the text const qreal y = line.y() + 0.5 * line.height(); QRectF strikeThroughBlob(runBounds.x(), y, runBounds.width(), thickness); strikeThroughBlob.translate(layoutOffset); QPainterPath path; path.addRect(strikeThroughBlob); // don't use direct addRect, because it doesn't care about Qt::WindingFill result += path; } if (run.underline()) { const qreal y = line.y() + line.ascent() + font.underlinePosition(); QRectF underlineBlob(runBounds.x(), y, runBounds.width(), thickness); underlineBlob.translate(layoutOffset); QPainterPath path; path.addRect(underlineBlob); // don't use direct addRect, because it doesn't care about Qt::WindingFill result += path; } } } } return result; } void KoSvgTextShape::resetTextShape() { KoSvgTextChunkShape::resetTextShape(); relayout(); } struct TextChunk { QString text; QVector formats; Qt::LayoutDirection direction = Qt::LeftToRight; Qt::Alignment alignment = Qt::AlignLeading; struct SubChunkOffset { QPointF offset; int start = 0; }; QVector offsets; boost::optional xStartPos; boost::optional yStartPos; QPointF applyStartPosOverride(const QPointF &pos) const { QPointF result = pos; if (xStartPos) { result.rx() = *xStartPos; } if (yStartPos) { result.ry() = *yStartPos; } return result; } }; QVector mergeIntoChunks(const QVector &subChunks) { QVector chunks; for (auto it = subChunks.begin(); it != subChunks.end(); ++it) { if (it->transformation.startsNewChunk() || it == subChunks.begin()) { TextChunk newChunk = TextChunk(); newChunk.direction = it->format.layoutDirection(); newChunk.alignment = it->format.calculateAlignment(); newChunk.xStartPos = it->transformation.xPos; newChunk.yStartPos = it->transformation.yPos; chunks.append(newChunk); } TextChunk ¤tChunk = chunks.last(); if (it->transformation.hasRelativeOffset()) { TextChunk::SubChunkOffset o; o.start = currentChunk.text.size(); o.offset = it->transformation.relativeOffset(); KIS_SAFE_ASSERT_RECOVER_NOOP(!o.offset.isNull()); currentChunk.offsets.append(o); } QTextLayout::FormatRange formatRange; formatRange.start = currentChunk.text.size(); formatRange.length = it->text.size(); formatRange.format = it->format; currentChunk.formats.append(formatRange); currentChunk.text += it->text; } return chunks; } /** * Qt's QTextLayout has a weird trait, it doesn't count space characters as * distinct characters in QTextLayout::setNumColumns(), that is, if we want to * position a block of text that starts with a space character in a specific * position, QTextLayout will drop this space and will move the text to the left. * * That is why we have a special wrapper object that ensures that no spaces are * dropped and their horizontal advance parameter is taken into account. */ struct LayoutChunkWrapper { LayoutChunkWrapper(QTextLayout *layout) : m_layout(layout) { } QPointF addTextChunk(int startPos, int length, const QPointF &textChunkStartPos) { QPointF currentTextPos = textChunkStartPos; const int lastPos = startPos + length - 1; KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(startPos == m_addedChars, currentTextPos); KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(lastPos < m_layout->text().size(), currentTextPos); QTextLine line; std::swap(line, m_danglingLine); if (!line.isValid()) { line = m_layout->createLine(); } // skip all the space characters that were not included into the Qt's text line const int currentLineStart = line.isValid() ? line.textStart() : startPos + length; while (startPos < currentLineStart && startPos <= lastPos) { currentTextPos.rx() += skipSpaceCharacter(startPos); startPos++; } if (startPos <= lastPos) { const int numChars = lastPos - startPos + 1; line.setNumColumns(numChars); line.setPosition(currentTextPos - QPointF(0, line.ascent())); currentTextPos.rx() += line.horizontalAdvance(); // skip all the space characters that were not included into the Qt's text line for (int i = line.textStart() + line.textLength(); i < lastPos; i++) { currentTextPos.rx() += skipSpaceCharacter(i); } } else { // keep the created but unused line for future use std::swap(line, m_danglingLine); } m_addedChars += length; return currentTextPos; } private: qreal skipSpaceCharacter(int pos) { const QTextCharFormat format = formatForPos(pos, m_layout->formats()); const QChar skippedChar = m_layout->text()[pos]; KIS_SAFE_ASSERT_RECOVER_NOOP(skippedChar.isSpace() || !skippedChar.isPrint()); QFontMetrics metrics(format.font()); return metrics.width(skippedChar); } static QTextCharFormat formatForPos(int pos, const QVector &formats) { Q_FOREACH (const QTextLayout::FormatRange &range, formats) { if (pos >= range.start && pos < range.start + range.length) { return range.format; } } KIS_SAFE_ASSERT_RECOVER_NOOP(0 && "pos should be within the bounds of the layouted text"); return QTextCharFormat(); } private: int m_addedChars = 0; QTextLayout *m_layout; QTextLine m_danglingLine; }; void KoSvgTextShape::relayout() { Q_D(KoSvgTextShape); d->cachedLayouts.clear(); d->cachedLayoutsOffsets.clear(); d->cachedLayoutsWorkingThread = QThread::currentThread(); QPointF currentTextPos; QVector textChunks = mergeIntoChunks(layoutInterface()->collectSubChunks()); Q_FOREACH (const TextChunk &chunk, textChunks) { std::unique_ptr layout(new QTextLayout()); QTextOption option; // WARNING: never activate this option! It breaks the RTL text layout! //option.setFlags(QTextOption::ShowTabsAndSpaces); option.setWrapMode(QTextOption::WrapAnywhere); option.setUseDesignMetrics(true); // TODO: investigate if it is needed? option.setTextDirection(chunk.direction); layout->setText(chunk.text); layout->setTextOption(option); layout->setFormats(chunk.formats); layout->setCacheEnabled(true); layout->beginLayout(); currentTextPos = chunk.applyStartPosOverride(currentTextPos); const QPointF anchorPointPos = currentTextPos; int lastSubChunkStart = 0; QPointF lastSubChunkOffset; LayoutChunkWrapper wrapper(layout.get()); for (int i = 0; i <= chunk.offsets.size(); i++) { const bool isFinalPass = i == chunk.offsets.size(); const int length = !isFinalPass ? chunk.offsets[i].start - lastSubChunkStart : chunk.text.size() - lastSubChunkStart; if (length > 0) { currentTextPos += lastSubChunkOffset; currentTextPos = wrapper.addTextChunk(lastSubChunkStart, length, currentTextPos); } if (!isFinalPass) { lastSubChunkOffset = chunk.offsets[i].offset; lastSubChunkStart = chunk.offsets[i].start; } } layout->endLayout(); QPointF diff; if (chunk.alignment & Qt::AlignTrailing || chunk.alignment & Qt::AlignHCenter) { if (chunk.alignment & Qt::AlignTrailing) { diff = currentTextPos - anchorPointPos; } else if (chunk.alignment & Qt::AlignHCenter) { diff = 0.5 * (currentTextPos - anchorPointPos); } // TODO: fix after t2b text implemented diff.ry() = 0; } d->cachedLayouts.push_back(std::move(layout)); d->cachedLayoutsOffsets.push_back(-diff); } d->clearAssociatedOutlines(this); for (int i = 0; i < int(d->cachedLayouts.size()); i++) { const QTextLayout &layout = *d->cachedLayouts[i]; const QPointF layoutOffset = d->cachedLayoutsOffsets[i]; using namespace KoSvgText; Q_FOREACH (const QTextLayout::FormatRange &range, layout.formats()) { const KoSvgCharChunkFormat &format = static_cast(range.format); AssociatedShapeWrapper wrapper = format.associatedShapeWrapper(); const int rangeStart = range.start; const int safeRangeLength = range.length > 0 ? range.length : layout.text().size() - rangeStart; if (safeRangeLength <= 0) continue; const int rangeEnd = range.start + safeRangeLength - 1; const int firstLineIndex = layout.lineForTextPosition(rangeStart).lineNumber(); const int lastLineIndex = layout.lineForTextPosition(rangeEnd).lineNumber(); for (int i = firstLineIndex; i <= lastLineIndex; i++) { const QTextLine line = layout.lineAt(i); // It might happen that the range contains only one (or two) // symbol that is a whitespace symbol. In such a case we should // just skip this (invalid) line. if (!line.isValid()) continue; const int posStart = qMax(line.textStart(), rangeStart); const int posEnd = qMin(line.textStart() + line.textLength() - 1, rangeEnd); const QList glyphRuns = line.glyphRuns(posStart, posEnd - posStart + 1); Q_FOREACH (const QGlyphRun &run, glyphRuns) { const QPointF firstPosition = run.positions().first(); const quint32 firstGlyphIndex = run.glyphIndexes().first(); const QPointF lastPosition = run.positions().last(); const quint32 lastGlyphIndex = run.glyphIndexes().last(); const QRawFont rawFont = run.rawFont(); const QRectF firstGlyphRect = rawFont.boundingRect(firstGlyphIndex).translated(firstPosition); const QRectF lastGlyphRect = rawFont.boundingRect(lastGlyphIndex).translated(lastPosition); QRectF rect = run.boundingRect(); /** * HACK ALERT: there is a bug in a way how Qt calculates boundingRect() * of the glyph run. It doesn't care about left and right bearings * of the border chars in the run, therefore it becomes cropped. * * Here we just add a half-char width margin to both sides * of the glyph run to make sure the glyphs are fully painted. * * BUG: 389528 * BUG: 392068 */ rect.setLeft(qMin(rect.left(), lastGlyphRect.left()) - 0.5 * firstGlyphRect.width()); rect.setRight(qMax(rect.right(), lastGlyphRect.right()) + 0.5 * lastGlyphRect.width()); wrapper.addCharacterRect(rect.translated(layoutOffset)); } } } } } void KoSvgTextShapePrivate::clearAssociatedOutlines(KoShape *rootShape) { KoSvgTextChunkShape *chunkShape = dynamic_cast(rootShape); KIS_SAFE_ASSERT_RECOVER_RETURN(chunkShape); chunkShape->layoutInterface()->clearAssociatedOutline(); Q_FOREACH (KoShape *child, chunkShape->shapes()) { clearAssociatedOutlines(child); } } bool KoSvgTextShape::isRootTextNode() const { return true; } KoSvgTextShapeFactory::KoSvgTextShapeFactory() : KoShapeFactoryBase(KoSvgTextShape_SHAPEID, i18n("Text")) { setToolTip(i18n("SVG Text Shape")); setIconName(koIconNameCStr("x-shape-text")); setLoadingPriority(5); setXmlElementNames(KoXmlNS::svg, QStringList("text")); KoShapeTemplate t; t.name = i18n("SVG Text"); t.iconName = koIconName("x-shape-text"); t.toolTip = i18n("SVG Text Shape"); addTemplate(t); } KoShape *KoSvgTextShapeFactory::createDefaultShape(KoDocumentResourceManager *documentResources) const { debugFlake << "Create default svg text shape"; KoSvgTextShape *shape = new KoSvgTextShape(); shape->setShapeId(KoSvgTextShape_SHAPEID); KoSvgTextShapeMarkupConverter converter(shape); converter.convertFromSvg("Lorem ipsum dolor sit amet, consectetur adipiscing elit.", "", QRectF(0, 0, 200, 60), documentResources->shapeController()->pixelsPerInch()); debugFlake << converter.errors() << converter.warnings(); return shape; } KoShape *KoSvgTextShapeFactory::createShape(const KoProperties *params, KoDocumentResourceManager *documentResources) const { KoSvgTextShape *shape = new KoSvgTextShape(); shape->setShapeId(KoSvgTextShape_SHAPEID); QString svgText = params->stringProperty("svgText", "Lorem ipsum dolor sit amet, consectetur adipiscing elit."); QString defs = params->stringProperty("defs" , ""); QRectF shapeRect = QRectF(0, 0, 200, 60); QVariant rect = params->property("shapeRect"); if (rect.type()==QVariant::RectF) { shapeRect = rect.toRectF(); } KoSvgTextShapeMarkupConverter converter(shape); converter.convertFromSvg(svgText, defs, shapeRect, documentResources->shapeController()->pixelsPerInch()); shape->setBackground(QSharedPointer(new KoColorBackground(QColor(Qt::black)))); shape->setPosition(shapeRect.topLeft()); return shape; } bool KoSvgTextShapeFactory::supports(const KoXmlElement &/*e*/, KoShapeLoadingContext &/*context*/) const { return false; } diff --git a/libs/image/CMakeLists.txt b/libs/image/CMakeLists.txt index 1fa68ecb61..06d757f161 100644 --- a/libs/image/CMakeLists.txt +++ b/libs/image/CMakeLists.txt @@ -1,377 +1,379 @@ add_subdirectory( tests ) add_subdirectory( tiles3 ) include_directories( ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/metadata ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty ${CMAKE_CURRENT_SOURCE_DIR}/brushengine ${CMAKE_CURRENT_SOURCE_DIR}/commands ${CMAKE_CURRENT_SOURCE_DIR}/commands_new ${CMAKE_CURRENT_SOURCE_DIR}/filter ${CMAKE_CURRENT_SOURCE_DIR}/floodfill ${CMAKE_CURRENT_SOURCE_DIR}/generator ${CMAKE_CURRENT_SOURCE_DIR}/layerstyles ${CMAKE_CURRENT_SOURCE_DIR}/processing ${CMAKE_SOURCE_DIR}/sdk/tests ) include_directories(SYSTEM ${EIGEN3_INCLUDE_DIR} ) if(FFTW3_FOUND) include_directories(${FFTW3_INCLUDE_DIR}) endif() if(HAVE_VC) include_directories(SYSTEM ${Vc_INCLUDE_DIR} ${Qt5Core_INCLUDE_DIRS} ${Qt5Gui_INCLUDE_DIRS}) ko_compile_for_all_implementations(__per_arch_circle_mask_generator_objs kis_brush_mask_applicator_factories.cpp) else() set(__per_arch_circle_mask_generator_objs kis_brush_mask_applicator_factories.cpp) endif() set(kritaimage_LIB_SRCS tiles3/kis_tile.cc tiles3/kis_tile_data.cc tiles3/kis_tile_data_store.cc tiles3/kis_tile_data_pooler.cc tiles3/kis_tiled_data_manager.cc tiles3/KisTiledExtentManager.cpp tiles3/kis_memento_manager.cc tiles3/kis_hline_iterator.cpp tiles3/kis_vline_iterator.cpp tiles3/kis_random_accessor.cc tiles3/swap/kis_abstract_compression.cpp tiles3/swap/kis_lzf_compression.cpp tiles3/swap/kis_abstract_tile_compressor.cpp tiles3/swap/kis_legacy_tile_compressor.cpp tiles3/swap/kis_tile_compressor_2.cpp tiles3/swap/kis_chunk_allocator.cpp tiles3/swap/kis_memory_window.cpp tiles3/swap/kis_swapped_data_store.cpp tiles3/swap/kis_tile_data_swapper.cpp kis_distance_information.cpp kis_painter.cc kis_painter_blt_multi_fixed.cpp kis_marker_painter.cpp KisPrecisePaintDeviceWrapper.cpp kis_progress_updater.cpp brushengine/kis_paint_information.cc brushengine/kis_random_source.cpp brushengine/KisPerStrokeRandomSource.cpp brushengine/kis_stroke_random_source.cpp brushengine/kis_paintop.cc brushengine/kis_paintop_factory.cpp brushengine/kis_paintop_preset.cpp brushengine/kis_paintop_registry.cc brushengine/kis_paintop_settings.cpp brushengine/kis_paintop_settings_update_proxy.cpp brushengine/kis_paintop_utils.cpp brushengine/kis_no_size_paintop_settings.cpp brushengine/kis_locked_properties.cc brushengine/kis_locked_properties_proxy.cpp brushengine/kis_locked_properties_server.cpp brushengine/kis_paintop_config_widget.cpp brushengine/kis_uniform_paintop_property.cpp brushengine/kis_combo_based_paintop_property.cpp brushengine/kis_slider_based_paintop_property.cpp brushengine/kis_standard_uniform_properties_factory.cpp brushengine/KisStrokeSpeedMeasurer.cpp brushengine/KisPaintopSettingsIds.cpp commands/kis_deselect_global_selection_command.cpp + commands/KisDeselectActiveSelectionCommand.cpp commands/kis_image_change_layers_command.cpp commands/kis_image_change_visibility_command.cpp commands/kis_image_command.cpp commands/kis_image_set_projection_color_space_command.cpp commands/kis_image_layer_add_command.cpp commands/kis_image_layer_move_command.cpp commands/kis_image_layer_remove_command.cpp commands/kis_image_layer_remove_command_impl.cpp commands/kis_image_lock_command.cpp commands/kis_node_command.cpp commands/kis_node_compositeop_command.cpp commands/kis_node_opacity_command.cpp commands/kis_node_property_list_command.cpp commands/kis_reselect_global_selection_command.cpp + commands/KisReselectActiveSelectionCommand.cpp commands/kis_set_global_selection_command.cpp commands_new/kis_saved_commands.cpp commands_new/kis_processing_command.cpp commands_new/kis_image_resize_command.cpp commands_new/kis_image_set_resolution_command.cpp commands_new/kis_node_move_command2.cpp commands_new/kis_set_layer_style_command.cpp commands_new/kis_selection_move_command2.cpp commands_new/kis_update_command.cpp commands_new/kis_switch_current_time_command.cpp commands_new/kis_change_projection_color_command.cpp commands_new/kis_activate_selection_mask_command.cpp processing/kis_do_nothing_processing_visitor.cpp processing/kis_simple_processing_visitor.cpp processing/kis_crop_processing_visitor.cpp processing/kis_crop_selections_processing_visitor.cpp processing/kis_transform_processing_visitor.cpp processing/kis_mirror_processing_visitor.cpp filter/kis_filter.cc filter/kis_filter_category_ids.cpp filter/kis_filter_configuration.cc filter/kis_color_transformation_configuration.cc filter/kis_filter_registry.cc filter/kis_color_transformation_filter.cc generator/kis_generator.cpp generator/kis_generator_layer.cpp generator/kis_generator_registry.cpp floodfill/kis_fill_interval_map.cpp floodfill/kis_scanline_fill.cpp lazybrush/kis_min_cut_worker.cpp lazybrush/kis_lazy_fill_tools.cpp lazybrush/kis_multiway_cut.cpp lazybrush/KisWatershedWorker.cpp lazybrush/kis_colorize_mask.cpp lazybrush/kis_colorize_stroke_strategy.cpp KisDelayedUpdateNodeInterface.cpp kis_adjustment_layer.cc kis_selection_based_layer.cpp kis_node_filter_interface.cpp kis_base_accessor.cpp kis_base_node.cpp kis_base_processor.cpp kis_bookmarked_configuration_manager.cc kis_node_uuid_info.cpp kis_clone_layer.cpp kis_colorspace_convert_visitor.cpp kis_config_widget.cpp kis_convolution_kernel.cc kis_convolution_painter.cc kis_gaussian_kernel.cpp kis_edge_detection_kernel.cpp kis_cubic_curve.cpp kis_default_bounds.cpp kis_default_bounds_base.cpp kis_effect_mask.cc kis_fast_math.cpp kis_fill_painter.cc kis_filter_mask.cpp kis_filter_strategy.cc kis_transform_mask.cpp kis_transform_mask_params_interface.cpp kis_recalculate_transform_mask_job.cpp kis_recalculate_generator_layer_job.cpp kis_transform_mask_params_factory_registry.cpp kis_safe_transform.cpp kis_gradient_painter.cc kis_gradient_shape_strategy.cpp kis_cached_gradient_shape_strategy.cpp kis_polygonal_gradient_shape_strategy.cpp kis_iterator_ng.cpp kis_async_merger.cpp kis_merge_walker.cc kis_updater_context.cpp kis_update_job_item.cpp kis_stroke_strategy_undo_command_based.cpp kis_simple_stroke_strategy.cpp KisRunnableBasedStrokeStrategy.cpp KisRunnableStrokeJobData.cpp KisRunnableStrokeJobsInterface.cpp KisFakeRunnableStrokeJobsExecutor.cpp kis_stroke_job_strategy.cpp kis_stroke_strategy.cpp kis_stroke.cpp kis_strokes_queue.cpp KisStrokesQueueMutatedJobInterface.cpp kis_simple_update_queue.cpp kis_update_scheduler.cpp kis_queues_progress_updater.cpp kis_composite_progress_proxy.cpp kis_sync_lod_cache_stroke_strategy.cpp kis_lod_capable_layer_offset.cpp kis_update_time_monitor.cpp KisImageConfigNotifier.cpp kis_group_layer.cc kis_count_visitor.cpp kis_histogram.cc kis_image_interfaces.cpp kis_image_animation_interface.cpp kis_time_range.cpp kis_node_graph_listener.cpp kis_image.cc kis_image_signal_router.cpp kis_image_config.cpp kis_projection_updates_filter.cpp kis_suspend_projection_updates_stroke_strategy.cpp kis_regenerate_frame_stroke_strategy.cpp kis_switch_time_stroke_strategy.cpp kis_crop_saved_extra_data.cpp kis_timed_signal_threshold.cpp kis_layer.cc kis_indirect_painting_support.cpp kis_abstract_projection_plane.cpp kis_layer_projection_plane.cpp kis_layer_utils.cpp kis_mask_projection_plane.cpp kis_projection_leaf.cpp kis_mask.cc kis_base_mask_generator.cpp kis_rect_mask_generator.cpp kis_circle_mask_generator.cpp kis_gauss_circle_mask_generator.cpp kis_gauss_rect_mask_generator.cpp ${__per_arch_circle_mask_generator_objs} kis_curve_circle_mask_generator.cpp kis_curve_rect_mask_generator.cpp kis_math_toolbox.cpp kis_memory_statistics_server.cpp kis_name_server.cpp kis_node.cpp kis_node_facade.cpp kis_node_progress_proxy.cpp kis_busy_progress_indicator.cpp kis_node_visitor.cpp kis_paint_device.cc kis_paint_device_debug_utils.cpp kis_fixed_paint_device.cpp KisOptimizedByteArray.cpp kis_paint_layer.cc kis_perspective_math.cpp kis_pixel_selection.cpp kis_processing_information.cpp kis_properties_configuration.cc kis_random_accessor_ng.cpp kis_random_generator.cc kis_random_sub_accessor.cpp kis_wrapped_random_accessor.cpp kis_selection.cc KisSelectionUpdateCompressor.cpp kis_selection_mask.cpp kis_update_outline_job.cpp kis_update_selection_job.cpp kis_serializable_configuration.cc kis_transaction_data.cpp kis_transform_worker.cc kis_perspectivetransform_worker.cpp bsplines/kis_bspline_1d.cpp bsplines/kis_bspline_2d.cpp bsplines/kis_nu_bspline_2d.cpp kis_warptransform_worker.cc kis_cage_transform_worker.cpp kis_liquify_transform_worker.cpp kis_green_coordinates_math.cpp kis_transparency_mask.cc kis_undo_adapter.cpp kis_macro_based_undo_store.cpp kis_surrogate_undo_adapter.cpp kis_legacy_undo_adapter.cpp kis_post_execution_undo_adapter.cpp kis_processing_visitor.cpp kis_processing_applicator.cpp krita_utils.cpp kis_outline_generator.cpp kis_layer_composition.cpp kis_selection_filters.cpp KisProofingConfiguration.h metadata/kis_meta_data_entry.cc metadata/kis_meta_data_filter.cc metadata/kis_meta_data_filter_p.cc metadata/kis_meta_data_filter_registry.cc metadata/kis_meta_data_filter_registry_model.cc metadata/kis_meta_data_io_backend.cc metadata/kis_meta_data_merge_strategy.cc metadata/kis_meta_data_merge_strategy_p.cc metadata/kis_meta_data_merge_strategy_registry.cc metadata/kis_meta_data_parser.cc metadata/kis_meta_data_schema.cc metadata/kis_meta_data_schema_registry.cc metadata/kis_meta_data_store.cc metadata/kis_meta_data_type_info.cc metadata/kis_meta_data_validator.cc metadata/kis_meta_data_value.cc kis_keyframe.cpp kis_keyframe_channel.cpp kis_keyframe_commands.cpp kis_scalar_keyframe_channel.cpp kis_raster_keyframe_channel.cpp kis_onion_skin_compositor.cpp kis_onion_skin_cache.cpp kis_idle_watcher.cpp kis_psd_layer_style.cpp kis_layer_properties_icons.cpp layerstyles/kis_multiple_projection.cpp layerstyles/kis_layer_style_filter.cpp layerstyles/kis_layer_style_filter_environment.cpp layerstyles/kis_layer_style_filter_projection_plane.cpp layerstyles/kis_layer_style_projection_plane.cpp layerstyles/kis_ls_drop_shadow_filter.cpp layerstyles/kis_ls_satin_filter.cpp layerstyles/kis_ls_stroke_filter.cpp layerstyles/kis_ls_bevel_emboss_filter.cpp layerstyles/kis_ls_overlay_filter.cpp layerstyles/kis_ls_utils.cpp layerstyles/gimp_bump_map.cpp KisProofingConfiguration.cpp kis_node_query_path.cc ) set(einspline_SRCS 3rdparty/einspline/bspline_create.cpp 3rdparty/einspline/bspline_data.cpp 3rdparty/einspline/multi_bspline_create.cpp 3rdparty/einspline/nubasis.cpp 3rdparty/einspline/nubspline_create.cpp 3rdparty/einspline/nugrid.cpp ) add_library(kritaimage SHARED ${kritaimage_LIB_SRCS} ${einspline_SRCS}) generate_export_header(kritaimage BASE_NAME kritaimage) target_link_libraries(kritaimage PUBLIC kritaversion kritawidgets kritaglobal kritapsd kritaodf kritapigment kritacommand kritawidgetutils Qt5::Concurrent ) target_link_libraries(kritaimage PUBLIC ${Boost_SYSTEM_LIBRARY}) if(OPENEXR_FOUND) target_link_libraries(kritaimage PUBLIC ${OPENEXR_LIBRARIES}) endif() if(FFTW3_FOUND) target_link_libraries(kritaimage PRIVATE ${FFTW3_LIBRARIES}) endif() if(HAVE_VC) target_link_libraries(kritaimage PUBLIC ${Vc_LIBRARIES}) endif() if (NOT GSL_FOUND) message (WARNING "KRITA WARNING! No GNU Scientific Library was found! Krita's Shaped Gradients might be non-normalized! Please install GSL library.") else () target_link_libraries(kritaimage PRIVATE ${GSL_LIBRARIES} ${GSL_CBLAS_LIBRARIES}) endif () target_include_directories(kritaimage PUBLIC $ $ $ $ $ $ ) set_target_properties(kritaimage PROPERTIES VERSION ${GENERIC_KRITA_LIB_VERSION} SOVERSION ${GENERIC_KRITA_LIB_SOVERSION} ) install(TARGETS kritaimage ${INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/libs/image/KisLazyStorage.h b/libs/image/KisLazyStorage.h index 347621da5f..3b3f91e941 100644 --- a/libs/image/KisLazyStorage.h +++ b/libs/image/KisLazyStorage.h @@ -1,70 +1,88 @@ +/* + * Copyright (c) 2018 Dmitry Kazakov + * + * 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. + */ + #ifndef KISLAZYSTORAGE_H #define KISLAZYSTORAGE_H #include #include #include #include #include template class KisLazyStorage { public: template explicit KisLazyStorage(P1 p1) : m_builder([=]() { return new T(p1); }), m_data(0) { } template explicit KisLazyStorage(P1 p1, P2 p2) : m_builder([=]() { return new T(p1, p2); }), m_data(0) { } template explicit KisLazyStorage(P1 p1, P2 p2, P2 p3) : m_builder([=]() { return new T(p1, p2, p3); }), m_data(0) { } KisLazyStorage(const KisLazyStorage &rgh) = delete; KisLazyStorage(KisLazyStorage &&rgh) = delete; ~KisLazyStorage() { delete m_data.load(); } T* operator->() { return getPointer(); } T& operator*() { return *getPointer(); } private: T* getPointer() { if(!m_data) { QMutexLocker l(&m_mutex); if(!m_data) { m_data = m_builder(); } } return m_data; } private: std::function m_builder; std::atomic m_data; QMutex m_mutex; }; #endif // KISLAZYSTORAGE_H diff --git a/libs/image/commands/kis_selection_commands.h b/libs/image/KisSelectionTags.h similarity index 68% copy from libs/image/commands/kis_selection_commands.h copy to libs/image/KisSelectionTags.h index 2cdc0fb095..12f0e67859 100644 --- a/libs/image/commands/kis_selection_commands.h +++ b/libs/image/KisSelectionTags.h @@ -1,27 +1,36 @@ /* - * Copyright (c) 2008 Boudewijn Rempt + * Copyright (c) 2018 Dmitry Kazakov * * 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. */ -#ifndef KIS_SELECTION_COMMANDS_H -#define KIS_SELECTION_COMMANDS_H +#ifndef KISSELECTIONTAGS_H +#define KISSELECTIONTAGS_H -#include "kis_deselect_global_selection_command.h" -#include "kis_reselect_global_selection_command.h" -#include "kis_set_global_selection_command.h" -#endif +enum SelectionMode { + PIXEL_SELECTION, + SHAPE_PROTECTION +}; +enum SelectionAction { + SELECTION_REPLACE, + SELECTION_ADD, + SELECTION_SUBTRACT, + SELECTION_INTERSECT, + SELECTION_DEFAULT +}; + +#endif // KISSELECTIONTAGS_H diff --git a/libs/image/commands/KisDeselectActiveSelectionCommand.cpp b/libs/image/commands/KisDeselectActiveSelectionCommand.cpp new file mode 100644 index 0000000000..195a6edd7e --- /dev/null +++ b/libs/image/commands/KisDeselectActiveSelectionCommand.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2018 Dmitry Kazakov + * + * 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 "KisDeselectActiveSelectionCommand.h" +#include "kis_image.h" +#include "kis_selection.h" +#include "kis_selection_mask.h" + +KisDeselectActiveSelectionCommand::KisDeselectActiveSelectionCommand(KisSelectionSP activeSelection, KisImageWSP image, KUndo2Command *parent) + : KisDeselectGlobalSelectionCommand(image, parent), + m_activeSelection(activeSelection) +{ +} + +KisDeselectActiveSelectionCommand::~KisDeselectActiveSelectionCommand() +{ +} + +void KisDeselectActiveSelectionCommand::redo() +{ + KisImageSP image = m_image.toStrongRef(); + KIS_SAFE_ASSERT_RECOVER_RETURN(image); + + if (m_activeSelection && m_activeSelection == image->globalSelection()) { + KisDeselectGlobalSelectionCommand::redo(); + } else if (m_activeSelection) { + KisNodeSP parentNode = m_activeSelection->parentNode(); + if (!parentNode) return; + + m_deselectedMask = dynamic_cast(parentNode.data()); + if (m_deselectedMask) { + KIS_SAFE_ASSERT_RECOVER(m_deselectedMask->active()) { + m_deselectedMask.clear(); + return; + } + + m_deselectedMask->setActive(false); + } + } +} + +void KisDeselectActiveSelectionCommand::undo() +{ + if (m_deselectedMask) { + m_deselectedMask->setActive(true); + m_deselectedMask.clear(); + } else { + KisDeselectGlobalSelectionCommand::undo(); + } +} diff --git a/libs/image/commands/kis_selection_commands.h b/libs/image/commands/KisDeselectActiveSelectionCommand.h similarity index 56% copy from libs/image/commands/kis_selection_commands.h copy to libs/image/commands/KisDeselectActiveSelectionCommand.h index 2cdc0fb095..c9742ccb72 100644 --- a/libs/image/commands/kis_selection_commands.h +++ b/libs/image/commands/KisDeselectActiveSelectionCommand.h @@ -1,27 +1,38 @@ /* - * Copyright (c) 2008 Boudewijn Rempt + * Copyright (c) 2018 Dmitry Kazakov * * 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. */ -#ifndef KIS_SELECTION_COMMANDS_H -#define KIS_SELECTION_COMMANDS_H +#ifndef KISDESELECTACTIVESELECTIONCOMMAND_H +#define KISDESELECTACTIVESELECTIONCOMMAND_H #include "kis_deselect_global_selection_command.h" -#include "kis_reselect_global_selection_command.h" -#include "kis_set_global_selection_command.h" -#endif +class KRITAIMAGE_EXPORT KisDeselectActiveSelectionCommand : public KisDeselectGlobalSelectionCommand +{ +public: + KisDeselectActiveSelectionCommand(KisSelectionSP activeSelection, KisImageWSP image, KUndo2Command * parent = 0); + ~KisDeselectActiveSelectionCommand() override; + void redo() override; + void undo() override; + +private: + KisSelectionSP m_activeSelection; + KisSelectionMaskSP m_deselectedMask; +}; + +#endif // KISDESELECTACTIVESELECTIONCOMMAND_H diff --git a/libs/image/commands/KisReselectActiveSelectionCommand.cpp b/libs/image/commands/KisReselectActiveSelectionCommand.cpp new file mode 100644 index 0000000000..4af691f9c5 --- /dev/null +++ b/libs/image/commands/KisReselectActiveSelectionCommand.cpp @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2018 Dmitry Kazakov + * + * 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 "KisReselectActiveSelectionCommand.h" + +#include "kis_image.h" +#include "kis_node.h" +#include "kis_layer.h" +#include "kis_selection_mask.h" +#include + + +KisReselectActiveSelectionCommand::KisReselectActiveSelectionCommand(KisNodeSP activeNode, KisImageWSP image, KUndo2Command *parent) + : KisReselectGlobalSelectionCommand(image, parent), + m_activeNode(activeNode) +{ +} + +void KisReselectActiveSelectionCommand::redo() +{ + bool shouldReselectFGlobalSelection = true; + + if (m_activeNode) { + KisSelectionMaskSP mask = dynamic_cast(m_activeNode.data()); + + if (!mask) { + + KisLayerSP layer; + KisNodeSP node = m_activeNode; + while (node && !(layer = dynamic_cast(node.data()))) { + node = node->parent(); + } + + if (layer && !layer->selectionMask()) { + KoProperties properties; + properties.setProperty("active", false); + properties.setProperty("visible", true); + QList masks = layer->childNodes(QStringList("KisSelectionMask"), properties); + + if (!masks.isEmpty()) { + mask = dynamic_cast(masks.first().data()); + } + } else if (layer && layer->selectionMask()) { + shouldReselectFGlobalSelection = false; + } + } + + if (mask) { + mask->setActive(true); + shouldReselectFGlobalSelection = false; + m_reselectedMask = mask; + } + } + + if (shouldReselectFGlobalSelection) { + KisReselectGlobalSelectionCommand::redo(); + } +} + +void KisReselectActiveSelectionCommand::undo() +{ + if (m_reselectedMask) { + m_reselectedMask->setActive(false); + m_reselectedMask.clear(); + } else { + KisReselectGlobalSelectionCommand::undo(); + } +} diff --git a/libs/image/commands/kis_selection_commands.h b/libs/image/commands/KisReselectActiveSelectionCommand.h similarity index 59% copy from libs/image/commands/kis_selection_commands.h copy to libs/image/commands/KisReselectActiveSelectionCommand.h index 2cdc0fb095..2ffb565a41 100644 --- a/libs/image/commands/kis_selection_commands.h +++ b/libs/image/commands/KisReselectActiveSelectionCommand.h @@ -1,27 +1,38 @@ /* - * Copyright (c) 2008 Boudewijn Rempt + * Copyright (c) 2018 Dmitry Kazakov * * 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. */ -#ifndef KIS_SELECTION_COMMANDS_H -#define KIS_SELECTION_COMMANDS_H +#ifndef KISRESELECTACTIVESELECTIONCOMMAND_H +#define KISRESELECTACTIVESELECTIONCOMMAND_H -#include "kis_deselect_global_selection_command.h" #include "kis_reselect_global_selection_command.h" -#include "kis_set_global_selection_command.h" -#endif +class KRITAIMAGE_EXPORT KisReselectActiveSelectionCommand : public KisReselectGlobalSelectionCommand +{ +public: + KisReselectActiveSelectionCommand(KisNodeSP activeNode, KisImageWSP image, KUndo2Command * parent = 0); + + void redo() override; + void undo() override; + +private: + KisNodeSP m_activeNode; + KisSelectionMaskSP m_reselectedMask; +}; + +#endif // KISRESELECTACTIVESELECTIONCOMMAND_H diff --git a/libs/image/commands/kis_deselect_global_selection_command.h b/libs/image/commands/kis_deselect_global_selection_command.h index 39030beb8f..64982eaace 100644 --- a/libs/image/commands/kis_deselect_global_selection_command.h +++ b/libs/image/commands/kis_deselect_global_selection_command.h @@ -1,47 +1,47 @@ /* * Copyright (c) 2007 Sven Langkamp * * 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. */ #ifndef KIS_DESELECT_GLOBAL_SELECTION_COMMAND_H_ #define KIS_DESELECT_GLOBAL_SELECTION_COMMAND_H_ #include #include #include "kis_types.h" /// The command for deselection the global selection of KisImage class KRITAIMAGE_EXPORT KisDeselectGlobalSelectionCommand : public KUndo2Command { public: /** * Constructor * @param image the image * @param parent the parent command */ KisDeselectGlobalSelectionCommand(KisImageWSP image, KUndo2Command * parent = 0); ~KisDeselectGlobalSelectionCommand() override; void redo() override; void undo() override; -private: +protected: KisImageWSP m_image; KisSelectionSP m_oldSelection; }; #endif diff --git a/libs/image/commands/kis_reselect_global_selection_command.h b/libs/image/commands/kis_reselect_global_selection_command.h index c27a7b848c..0e5c9dad02 100644 --- a/libs/image/commands/kis_reselect_global_selection_command.h +++ b/libs/image/commands/kis_reselect_global_selection_command.h @@ -1,47 +1,47 @@ /* * Copyright (c) 2007 Sven Langkamp * * 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. */ #ifndef KIS_RESELECT_GLOBAL_SELECTION_COMMAND_H #define KIS_RESELECT_GLOBAL_SELECTION_COMMAND_H #include #include #include "kis_types.h" /// The command for deselection the global selection of KisImage class KRITAIMAGE_EXPORT KisReselectGlobalSelectionCommand : public KUndo2Command { public: /** * Constructor * @param image the image * @param parent the parent command */ KisReselectGlobalSelectionCommand(KisImageWSP image, KUndo2Command * parent = 0); ~KisReselectGlobalSelectionCommand() override; void redo() override; void undo() override; -private: +protected: KisImageWSP m_image; bool m_canReselect; }; #endif diff --git a/libs/image/commands/kis_selection_commands.h b/libs/image/commands/kis_selection_commands.h index 2cdc0fb095..78f3125284 100644 --- a/libs/image/commands/kis_selection_commands.h +++ b/libs/image/commands/kis_selection_commands.h @@ -1,27 +1,29 @@ /* * Copyright (c) 2008 Boudewijn Rempt * * 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. */ #ifndef KIS_SELECTION_COMMANDS_H #define KIS_SELECTION_COMMANDS_H #include "kis_deselect_global_selection_command.h" +#include "KisDeselectActiveSelectionCommand.h" #include "kis_reselect_global_selection_command.h" +#include "KisReselectActiveSelectionCommand.h" #include "kis_set_global_selection_command.h" #endif diff --git a/libs/image/kis_selection.h b/libs/image/kis_selection.h index e72a7f028a..97ce6f2c25 100644 --- a/libs/image/kis_selection.h +++ b/libs/image/kis_selection.h @@ -1,228 +1,218 @@ /* * Copyright (c) 2004 Boudewijn Rempt * * 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. */ #ifndef KIS_SELECTION_H_ #define KIS_SELECTION_H_ #include #include "kis_types.h" #include "kritaimage_export.h" #include "kis_default_bounds.h" #include "kis_image.h" -enum SelectionMode { - PIXEL_SELECTION, - SHAPE_PROTECTION -}; - -enum SelectionAction { - SELECTION_REPLACE, - SELECTION_ADD, - SELECTION_SUBTRACT, - SELECTION_INTERSECT, - SELECTION_DEFAULT -}; +#include "KisSelectionTags.h" #include "kis_pixel_selection.h" class KisSelectionComponent; class QPainterPath; /** * KisSelection is a composite object. It may contain an instance * of KisPixelSelection and a KisShapeSelection object. Both these * selections are merged into a projection of the KisSelection. * * Every pixel in the paint device can indicate a degree of selectedness, varying * between MIN_SELECTED and MAX_SELECTED. * * The projection() paint device itself is only a projection: you can * read from it, but not write to it. You need to keep track of * the need for updating the projection yourself: there is no * automatic updating after changing the contents of one or more * of the selection components. */ class KRITAIMAGE_EXPORT KisSelection : public KisShared { public: /** * Create a new KisSelection. * * @param defaultBounds defines the bounds of the selection when * Select All is initiated. */ KisSelection(KisDefaultBoundsBaseSP defaultBounds = KisDefaultBoundsBaseSP()); /** * Copy the selection. The selection components are copied, too. */ KisSelection(const KisSelection& rhs); KisSelection& operator=(const KisSelection &rhs); /** * Delete the selection. The shape selection component is deleted, the * pixel selection component is contained in a shared pointer, so that * may still be valid. */ virtual ~KisSelection(); /** * The paint device of the pixel selection should report * about it's setDirty events to its parent. The creator * should set the parent manually if it wants to get the * signals */ void setParentNode(KisNodeWSP node); bool hasPixelSelection() const; bool hasShapeSelection() const; bool outlineCacheValid() const; QPainterPath outlineCache() const; void recalculateOutlineCache(); /** * Tells whether the cached thumbnail of the selection is still valid */ bool thumbnailImageValid() const; /** * Recalculates the thumbnail of the selection */ void recalculateThumbnailImage(const QColor &maskColor); /** * Returns the thumbnail of the selection. */ QImage thumbnailImage() const; /** * Returns the transformation which should be applied to the thumbnail before * being painted over the image */ QTransform thumbnailImageTransform() const; /** * return the pixel selection component of this selection. Pixel * selection component is always present in the selection. In case * the user wants a vector selection, pixel selection will store * the pixelated version of it. * * NOTE: use pixelSelection() for changing the selection only. For * reading the selection and passing the data to bitBlt function use * projection(). Although projection() and pixelSelection() currently * point ot the same paint device, this behavior may change in the * future. */ KisPixelSelectionSP pixelSelection() const; /** * return the vector selection component of this selection or zero * if hasShapeSelection() returns false. */ KisSelectionComponent* shapeSelection() const; void setShapeSelection(KisSelectionComponent* shapeSelection); /** * Returns the projection of the selection. It may be the same * as pixel selection. You must read selection data from this * paint device only */ KisPixelSelectionSP projection() const; /** * Updates the projection of the selection. You should call this * method after the every change of the selection components. * There is no automatic updates framework present */ void updateProjection(const QRect& rect); void updateProjection(); void setVisible(bool visible); bool isVisible(); /** * Convenience functions. Just call the corresponding methods * of the underlying projection */ bool isTotallyUnselected(const QRect & r) const; QRect selectedRect() const; /** * @brief Slow, but exact way of determining the rectangle * that encloses the selection. * * Default pixel of the selection device may vary and you would get wrong bounds. * selectedExactRect() handles all these cases. * */ QRect selectedExactRect() const; void setX(qint32 x); void setY(qint32 y); qint32 x() const; qint32 y() const; void setDefaultBounds(KisDefaultBoundsBaseSP bounds); void clear(); /** * @brief flatten creates a new pixel selection component from the shape selection * and throws away the shape selection. This has no effect if there is no * shape selection. */ KUndo2Command* flatten(); void notifySelectionChanged(); /** * Request rerendering of the shape selection component in a * compressed way. Usually, you don't need to call it manually, * because all the work is done by KisShapeSelectionModel. */ void requestCompressedProjectionUpdate(const QRect &rc); /// XXX: This method was marked KDE_DEPRECATED but without information on what to /// replace it with. Undeprecate, therefore. quint8 selected(qint32 x, qint32 y) const; private: friend class KisSelectionTest; friend class KisMaskTest; friend class KisAdjustmentLayerTest; friend class KisUpdateSelectionJob; friend class KisSelectionUpdateCompressor; + friend class KisDeselectActiveSelectionCommand; KisNodeWSP parentNode() const; void copyFrom(const KisSelection &rhs); private: struct Private; Private * const m_d; }; #endif // KIS_SELECTION_H_ diff --git a/libs/image/kis_updater_context.h b/libs/image/kis_updater_context.h index e6cb8705b6..2e8f69dd0f 100644 --- a/libs/image/kis_updater_context.h +++ b/libs/image/kis_updater_context.h @@ -1,212 +1,210 @@ /* * Copyright (c) 2010 Dmitry Kazakov * * 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. */ #ifndef __KIS_UPDATER_CONTEXT_H #define __KIS_UPDATER_CONTEXT_H #include #include #include #include #include "kis_base_rects_walker.h" #include "kis_async_merger.h" #include "kis_lock_free_lod_counter.h" #include "KisUpdaterContextSnapshotEx.h" #include "kis_update_scheduler.h" class KisUpdateJobItem; class KisSpontaneousJob; class KisStrokeJob; class KRITAIMAGE_EXPORT KisUpdaterContext : public QObject { Q_OBJECT public: static const int useIdealThreadCountTag; public: KisUpdaterContext(qint32 threadCount = useIdealThreadCountTag, QObject *parent = 0); ~KisUpdaterContext() override; /** * Returns the number of currently running jobs of each type. * To use this information you should lock the context beforehand. * * \see lock() */ void getJobsSnapshot(qint32 &numMergeJobs, qint32 &numStrokeJobs); KisUpdaterContextSnapshotEx getContextSnapshotEx() const; /** * Returns the current level of detail of all the running jobs in the * context. If there are no jobs, returns -1. */ int currentLevelOfDetail() const; /** * Check whether there is a spare thread for running * one more job */ bool hasSpareThread(); /** * Checks whether the walker intersects with any * of currently executing walkers. If it does, * it is not allowed to go in. It should be called * with the lock held. * * \see lock() */ bool isJobAllowed(KisBaseRectsWalkerSP walker); /** * Registers the job and starts executing it. * The caller must ensure that the context is locked * with lock(), job is allowed with isWalkerAllowed() and * there is a spare thread for running it with hasSpareThread() * * \see lock() * \see isWalkerAllowed() * \see hasSpareThread() */ virtual void addMergeJob(KisBaseRectsWalkerSP walker); /** * Adds a stroke job to the context. The prerequisites are * the same as for addMergeJob() * \see addMergeJob() */ virtual void addStrokeJob(KisStrokeJob *strokeJob); /** * Adds a spontaneous job to the context. The prerequisites are * the same as for addMergeJob() * \see addMergeJob() */ virtual void addSpontaneousJob(KisSpontaneousJob *spontaneousJob); /** * Block execution of the caller until all the jobs are finished */ void waitForDone(); /** * Locks the context to guarantee an exclusive access * to the context */ void lock(); /** * Unlocks the context * * \see lock() */ void unlock(); /** * Set the number of threads available for this updater context * WARNING: one cannot change the number of threads if there is * at least one job running in the context! So before * calling this method make sure you do two things: * 1) barrierLock() the update scheduler * 2) lock() the context */ void setThreadsLimit(int value); /** * Return the number of available threads in the context. Make sure you * lock the context before calling this function! */ int threadsLimit() const; void continueUpdate(const QRect& rc); void doSomeUsefulWork(); void jobFinished(); - friend class KisUpdateJobItem; - protected: static bool walkerIntersectsJob(KisBaseRectsWalkerSP walker, const KisUpdateJobItem* job); qint32 findSpareThread(); protected: /** * The lock is shared by all the child update job items. * When an item wants to run a usual (non-exclusive) job, * it locks the lock for read access. When an exclusive * access is requested, it locks it for write */ QReadWriteLock m_exclusiveJobLock; QMutex m_lock; QVector m_jobs; QThreadPool m_threadPool; KisLockFreeLodCounter m_lodCounter; KisUpdateScheduler *m_scheduler; private: friend class KisUpdaterContextTest; friend class KisUpdateSchedulerTest; friend class KisStrokesQueueTest; friend class KisSimpleUpdateQueueTest; friend class KisUpdateJobItem; void addMergeJobTest(KisBaseRectsWalkerSP walker); void addStrokeJobTest(KisStrokeJob *strokeJob); void addSpontaneousJobTest(KisSpontaneousJob *spontaneousJob); const QVector getJobs(); void clear(); }; class KRITAIMAGE_EXPORT KisTestableUpdaterContext : public KisUpdaterContext { public: /** * Creates an explicit number of threads */ KisTestableUpdaterContext(qint32 threadCount); ~KisTestableUpdaterContext() override; /** * The only difference - it doesn't start execution * of the job */ void addMergeJob(KisBaseRectsWalkerSP walker) override; void addStrokeJob(KisStrokeJob *strokeJob) override; void addSpontaneousJob(KisSpontaneousJob *spontaneousJob) override; const QVector getJobs(); void clear(); friend class KisUpdateJobItem; }; #endif /* __KIS_UPDATER_CONTEXT_H */ diff --git a/libs/image/tests/scheduler_utils.h b/libs/image/tests/scheduler_utils.h index 6b5d683969..3c28f6e0de 100644 --- a/libs/image/tests/scheduler_utils.h +++ b/libs/image/tests/scheduler_utils.h @@ -1,281 +1,281 @@ /* * Copyright (c) 2011 Dmitry Kazakov * * 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. */ #ifndef __SCHEDULER_UTILS_H #define __SCHEDULER_UTILS_H #include #include "kis_merge_walker.h" #include "kis_stroke_strategy.h" #include "kis_stroke_job.h" #include "kis_spontaneous_job.h" #include "kis_stroke.h" #include "kis_image.h" #define SCOMPARE(s1, s2) QCOMPARE(QString(s1), QString(s2)) #define COMPARE_WALKER(item, walker) \ QCOMPARE(item->walker(), walker) #define COMPARE_NAME(item, name) \ QCOMPARE(getJobName(item->strokeJob()), QString(name)) #define VERIFY_EMPTY(item) \ QVERIFY(!item->isRunning()) void executeStrokeJobs(KisStroke *stroke) { KisStrokeJob *job; while((job = stroke->popOneJob())) { job->run(); delete job; } } bool checkWalker(KisBaseRectsWalkerSP walker, const QRect &rect, int lod = 0) { if(walker->requestedRect() == rect && walker->levelOfDetail() == lod) { return true; } else { dbgKrita << "walker rect:" << walker->requestedRect(); dbgKrita << "expected rect:" << rect; dbgKrita << "walker lod:" << walker->levelOfDetail(); dbgKrita << "expected lod:" << lod; return false; } } class KisNoopSpontaneousJob : public KisSpontaneousJob { public: KisNoopSpontaneousJob(bool overridesEverything = false, int lod = 0) : m_overridesEverything(overridesEverything), m_lod(lod) { } void run() override { } bool overrides(const KisSpontaneousJob *otherJob) override { Q_UNUSED(otherJob); return m_overridesEverything; } int levelOfDetail() const override { return m_lod; } private: bool m_overridesEverything; int m_lod; }; static QStringList globalExecutedDabs; class KisNoopDabStrategy : public KisStrokeJobStrategy { public: KisNoopDabStrategy(QString name) : m_name(name), m_isMarked(false) {} void run(KisStrokeJobData *data) override { Q_UNUSED(data); globalExecutedDabs << m_name; } virtual QString name(KisStrokeJobData *data) const { Q_UNUSED(data); return m_name; } void setMarked() { m_isMarked = true; } bool isMarked() const { return m_isMarked; } private: QString m_name; bool m_isMarked; }; class KisTestingStrokeJobData : public KisStrokeJobData { public: KisTestingStrokeJobData(Sequentiality sequentiality = SEQUENTIAL, Exclusivity exclusivity = NORMAL, bool addMutatedJobs = false, const QString &customSuffix = QString()) : KisStrokeJobData(sequentiality, exclusivity), m_addMutatedJobs(addMutatedJobs), m_customSuffix(customSuffix) { } KisTestingStrokeJobData(const KisTestingStrokeJobData &rhs) : KisStrokeJobData(rhs), m_addMutatedJobs(rhs.m_addMutatedJobs) { } KisStrokeJobData* createLodClone(int levelOfDetail) override { Q_UNUSED(levelOfDetail); return new KisTestingStrokeJobData(*this); } bool m_addMutatedJobs = false; bool m_isMutated = false; QString m_customSuffix; }; class KisMutatableDabStrategy : public KisNoopDabStrategy { public: KisMutatableDabStrategy(const QString &name, KisStrokeStrategy *parentStrokeStrategy) : KisNoopDabStrategy(name), m_parentStrokeStrategy(parentStrokeStrategy) { } void run(KisStrokeJobData *data) override { KisTestingStrokeJobData *td = dynamic_cast(data); if (td && td->m_isMutated) { globalExecutedDabs << QString("%1_mutated").arg(name(data)); } else if (td && td->m_addMutatedJobs) { globalExecutedDabs << name(data); for (int i = 0; i < 3; i++) { KisTestingStrokeJobData *newData = new KisTestingStrokeJobData(td->sequentiality(), td->exclusivity(), false); newData->m_isMutated = true; m_parentStrokeStrategy->addMutatedJob(newData); } } else { globalExecutedDabs << name(data); } } - virtual QString name(KisStrokeJobData *data) const { + virtual QString name(KisStrokeJobData *data) const override { const QString baseName = KisNoopDabStrategy::name(data); KisTestingStrokeJobData *td = dynamic_cast(data); return !td || td->m_customSuffix.isEmpty() ? baseName : QString("%1_%2").arg(baseName).arg(td->m_customSuffix); } private: KisStrokeStrategy *m_parentStrokeStrategy = 0; }; class KisTestingStrokeStrategy : public KisStrokeStrategy { public: KisTestingStrokeStrategy(const QString &prefix = QString(), bool exclusive = false, bool inhibitServiceJobs = false, bool forceAllowInitJob = false, bool forceAllowCancelJob = false) : KisStrokeStrategy(prefix, kundo2_noi18n(prefix)), m_prefix(prefix), m_inhibitServiceJobs(inhibitServiceJobs), m_forceAllowInitJob(forceAllowInitJob), m_forceAllowCancelJob(forceAllowCancelJob), m_cancelSeqNo(0) { setExclusive(exclusive); } KisTestingStrokeStrategy(const KisTestingStrokeStrategy &rhs, int levelOfDetail) : KisStrokeStrategy(rhs), m_prefix(rhs.m_prefix), m_inhibitServiceJobs(rhs.m_inhibitServiceJobs), m_forceAllowInitJob(rhs.m_forceAllowInitJob), m_cancelSeqNo(rhs.m_cancelSeqNo) { m_prefix = QString("clone%1_%2").arg(levelOfDetail).arg(m_prefix); } KisStrokeJobStrategy* createInitStrategy() override { return m_forceAllowInitJob || !m_inhibitServiceJobs ? new KisNoopDabStrategy(m_prefix + "init") : 0; } KisStrokeJobStrategy* createFinishStrategy() override { return !m_inhibitServiceJobs ? new KisNoopDabStrategy(m_prefix + "finish") : 0; } KisStrokeJobStrategy* createCancelStrategy() override { return m_forceAllowCancelJob || !m_inhibitServiceJobs ? new KisNoopDabStrategy(m_prefix + "cancel") : 0; } KisStrokeJobStrategy* createDabStrategy() override { return new KisMutatableDabStrategy(m_prefix + "dab", this); } KisStrokeStrategy* createLodClone(int levelOfDetail) override { return new KisTestingStrokeStrategy(*this, levelOfDetail); } class CancelData : public KisStrokeJobData { public: CancelData(int seqNo) : m_seqNo(seqNo) {} int seqNo() const { return m_seqNo; } private: int m_seqNo; }; KisStrokeJobData* createCancelData() override { return new CancelData(m_cancelSeqNo++); } private: QString m_prefix; bool m_inhibitServiceJobs; int m_forceAllowInitJob; bool m_forceAllowCancelJob; int m_cancelSeqNo; }; inline QString getJobName(KisStrokeJob *job) { KisNoopDabStrategy *pointer = dynamic_cast(job->testingGetDabStrategy()); KIS_ASSERT(pointer); return pointer->name(job->testingGetDabData()); } inline int cancelSeqNo(KisStrokeJob *job) { KisTestingStrokeStrategy::CancelData *pointer = dynamic_cast (job->testingGetDabData()); Q_ASSERT(pointer); return pointer->seqNo(); } #endif /* __SCHEDULER_UTILS_H */ diff --git a/libs/libqml/plugins/kritasketchplugin/models/LayerModel.cpp b/libs/libqml/plugins/kritasketchplugin/models/LayerModel.cpp index 2c13f720b1..6ce5a76e8b 100644 --- a/libs/libqml/plugins/kritasketchplugin/models/LayerModel.cpp +++ b/libs/libqml/plugins/kritasketchplugin/models/LayerModel.cpp @@ -1,973 +1,983 @@ /* This file is part of the KDE project * Copyright (C) 2012 Dan Leinir Turthra Jensen * * 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 "LayerModel.h" #include "LayerThumbProvider.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include "KisSelectionActionsAdapter.h" struct LayerModelMetaInfo { LayerModelMetaInfo() : canMoveUp(false) , canMoveRight(false) , canMoveDown(false) , canMoveLeft(false) , depth(-1) {} bool canMoveUp; bool canMoveRight; bool canMoveDown; bool canMoveLeft; int depth; }; class LayerModel::Private { public: Private(LayerModel* qq) : q(qq) , nodeModel(new KisNodeModel(qq)) , aboutToRemoveRoots(false) , canvas(0) , nodeManager(0) , image(0) , activeNode(0) , declarativeEngine(0) , thumbProvider(0) , updateActiveLayerWithNewFilterConfigTimer(new QTimer(qq)) , imageChangedTimer(new QTimer(qq)) { QList tmpFilters = KisFilterRegistry::instance()->values(); Q_FOREACH (const KisFilterSP& filter, tmpFilters) { filters[filter.data()->id()] = filter.data(); } updateActiveLayerWithNewFilterConfigTimer->setInterval(0); updateActiveLayerWithNewFilterConfigTimer->setSingleShot(true); connect(updateActiveLayerWithNewFilterConfigTimer, SIGNAL(timeout()), qq, SLOT(updateActiveLayerWithNewFilterConfig())); imageChangedTimer->setInterval(250); imageChangedTimer->setSingleShot(true); connect(imageChangedTimer, SIGNAL(timeout()), qq, SLOT(imageHasChanged())); } LayerModel* q; QList layers; QHash layerMeta; KisNodeModel* nodeModel; bool aboutToRemoveRoots; KisViewManager* view; KisCanvas2* canvas; + QScopedPointer selectionActionsAdapter; QPointer nodeManager; KisImageWSP image; KisNodeSP activeNode; QQmlEngine* declarativeEngine; LayerThumbProvider* thumbProvider; QHash filters; KisFilterConfigurationSP newConfig; QTimer* updateActiveLayerWithNewFilterConfigTimer; QTimer* imageChangedTimer; static int counter() { static int count = 0; return count++; } static QStringList layerClassNames() { QStringList list; list << "KisGroupLayer"; list << "KisPaintLayer"; list << "KisFilterMask"; list << "KisAdjustmentLayer"; return list; } int deepChildCount(KisNodeSP layer) { quint32 childCount = layer->childCount(); QList children = layer->childNodes(layerClassNames(), KoProperties()); for(quint32 i = 0; i < childCount; ++i) childCount += deepChildCount(children.at(i)); return childCount; } void rebuildLayerList(KisNodeSP layer = 0) { bool refreshingFromRoot = false; if (!image) { layers.clear(); return; } if (layer == 0) { refreshingFromRoot = true; layers.clear(); layer = image->rootLayer(); } // implementation node: The root node is not a visible node, and so // is never added to the list of layers QList children = layer->childNodes(layerClassNames(), KoProperties()); if (children.count() == 0) return; for(quint32 i = children.count(); i > 0; --i) { layers << children.at(i-1); rebuildLayerList(children.at(i-1)); } if (refreshingFromRoot) refreshLayerMovementAbilities(); } void refreshLayerMovementAbilities() { layerMeta.clear(); if (layers.count() == 0) return; for(int i = 0; i < layers.count(); ++i) { const KisNodeSP layer = layers.at(i); LayerModelMetaInfo ability; if (i > 0) ability.canMoveUp = true; if (i < layers.count() - 1) ability.canMoveDown = true; KisNodeSP parent = layer; while(parent) { ++ability.depth; parent = parent->parent(); } if (ability.depth > 1) ability.canMoveLeft = true; if (i < layers.count() - 1 && qobject_cast(layers.at(i + 1).constData())) ability.canMoveRight = true; layerMeta[layer] = ability; } } }; LayerModel::LayerModel(QObject* parent) : QAbstractListModel(parent) , d(new Private(this)) { connect(d->nodeModel, SIGNAL(rowsAboutToBeInserted(QModelIndex, int, int)), this, SLOT(source_rowsAboutToBeInserted(QModelIndex, int, int))); connect(d->nodeModel, SIGNAL(rowsInserted(QModelIndex, int, int)), this, SLOT(source_rowsInserted(QModelIndex, int, int))); connect(d->nodeModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex, int, int)), this, SLOT(source_rowsAboutToBeRemoved(QModelIndex, int, int))); connect(d->nodeModel, SIGNAL(rowsRemoved(QModelIndex, int, int)), this, SLOT(source_rowsRemoved(QModelIndex, int, int))); connect(d->nodeModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(source_dataChanged(QModelIndex,QModelIndex))); connect(d->nodeModel, SIGNAL(modelReset()), this, SLOT(source_modelReset())); connect(d->nodeModel, SIGNAL(layoutAboutToBeChanged()), this, SIGNAL(layoutAboutToBeChanged())); connect(d->nodeModel, SIGNAL(layoutChanged()), this, SIGNAL(layoutChanged())); } LayerModel::~LayerModel() { delete d; } QHash LayerModel::roleNames() const { QHash roles; roles[IconRole] = "icon"; roles[NameRole] = "name"; roles[ActiveLayerRole] = "activeLayer"; roles[OpacityRole] = "opacity"; roles[PercentOpacityRole] = "percentOpacity"; roles[VisibleRole] = "visible"; roles[CompositeDetailsRole] = "compositeDetails"; roles[FilterRole] = "filter"; roles[ChildCountRole] = "childCount"; roles[DeepChildCountRole] = "deepChildCount"; roles[DepthRole] = "depth"; roles[PreviousItemDepthRole] = "previousItemDepth"; roles[NextItemDepthRole] = "nextItemDepth"; roles[CanMoveDownRole] = "canMoveDown"; roles[CanMoveLeftRole] = "canMoveLeft"; roles[CanMoveRightRole] = "canMoveRight"; roles[CanMoveUpRole] = "canMoveUp"; return roles; } QObject* LayerModel::view() const { return d->view; } void LayerModel::setView(QObject *newView) { KisViewManager* view = qobject_cast(newView); // This has to happen very early, and we will be filling it back up again soon anyway... if (d->canvas) { d->canvas->disconnectCanvasObserver(this); disconnect(d->image, 0, this, 0); disconnect(d->nodeManager, 0, this, 0); disconnect(d->nodeModel, 0, d->nodeManager, 0); disconnect(d->nodeModel, SIGNAL(nodeActivated(KisNodeSP)), this, SLOT(currentNodeChanged(KisNodeSP))); d->image = 0; d->nodeManager = 0; d->layers.clear(); d->activeNode.clear(); d->canvas = 0; - d->nodeModel->setDummiesFacade(0, 0, 0, 0, 0); + d->nodeModel->setDummiesFacade(0, 0, 0, 0, 0, 0); + d->selectionActionsAdapter.reset(); } d->view = view; if (!d->view) { return; } d->canvas = view->canvasBase(); d->thumbProvider = new LayerThumbProvider(); d->thumbProvider->setLayerModel(this); d->thumbProvider->setLayerID(Private::counter()); // QT5TODO: results in a crash d->declarativeEngine->addImageProvider(QString("layerthumb%1").arg(d->thumbProvider->layerID()), d->thumbProvider); if (d->canvas) { d->image = d->canvas->imageView()->image(); d->nodeManager = d->canvas->viewManager()->nodeManager(); KisDummiesFacadeBase *kritaDummiesFacade = dynamic_cast(d->canvas->imageView()->document()->shapeController()); KisShapeController *shapeController = dynamic_cast(d->canvas->imageView()->document()->shapeController()); - d->nodeModel->setDummiesFacade(kritaDummiesFacade, d->image, shapeController, d->nodeManager->nodeSelectionAdapter(), d->nodeManager->nodeInsertionAdapter()); + + d->selectionActionsAdapter.reset(new KisSelectionActionsAdapter(d->canvas->viewManager()->selectionManager())); + d->nodeModel->setDummiesFacade(kritaDummiesFacade, + d->image, + shapeController, + d->nodeManager->nodeSelectionAdapter(), + d->nodeManager->nodeInsertionAdapter(), + d->selectionActionsAdapter.data()); connect(d->image, SIGNAL(sigAboutToBeDeleted()), SLOT(notifyImageDeleted())); connect(d->image, SIGNAL(sigNodeChanged(KisNodeSP)), SLOT(nodeChanged(KisNodeSP))); connect(d->image, SIGNAL(sigImageUpdated(QRect)), SLOT(imageChanged())); connect(d->image, SIGNAL(sigRemoveNodeAsync(KisNodeSP)), SLOT(aboutToRemoveNode(KisNodeSP))); // cold start currentNodeChanged(d->nodeManager->activeNode()); // Connection KisNodeManager -> KisLayerBox connect(d->nodeManager, SIGNAL(sigUiNeedChangeActiveNode(KisNodeSP)), this, SLOT(currentNodeChanged(KisNodeSP))); d->rebuildLayerList(); beginResetModel(); endResetModel(); } } QObject* LayerModel::engine() const { return d->declarativeEngine; } void LayerModel::setEngine(QObject* newEngine) { d->declarativeEngine = qobject_cast(newEngine); emit engineChanged(); } QString LayerModel::fullImageThumbUrl() const { return QString("image://layerthumb%1/fullimage/%2").arg(d->thumbProvider->layerID()).arg(QDateTime::currentMSecsSinceEpoch()); } void LayerModel::currentNodeChanged(KisNodeSP newActiveNode) { if (!d->activeNode.isNull()) { QModelIndex oldIndex = d->nodeModel->indexFromNode(d->activeNode); source_dataChanged(oldIndex, oldIndex); } d->activeNode = newActiveNode; emitActiveChanges(); if (!d->activeNode.isNull()) { QModelIndex oldIndex = d->nodeModel->indexFromNode(d->activeNode); source_dataChanged(oldIndex, oldIndex); } } QVariant LayerModel::data(const QModelIndex& index, int role) const { QVariant data; if (index.isValid()) { index.internalPointer(); KisNodeSP node = d->layers.at(index.row()); if (node.isNull()) return data; KisNodeSP parent; switch(role) { case IconRole: if (dynamic_cast(node.constData())) data = QLatin1String("../images/svg/icon-layer_group-black.svg"); else if (dynamic_cast(node.constData())) data = QLatin1String("../images/svg/icon-layer_filter-black.svg"); else if (dynamic_cast(node.constData())) data = QLatin1String("../images/svg/icon-layer_filter-black.svg"); else // We add the currentMSecsSinceEpoch to ensure we force an update (even with cache turned // off, we still apparently get some caching behaviour on delegates in QML) data = QString("image://layerthumb%1/%2/%3").arg(d->thumbProvider->layerID()).arg(index.row()).arg(QDateTime::currentMSecsSinceEpoch()); break; case NameRole: data = node->name(); break; case ActiveLayerRole: data = (node == d->activeNode); break; case OpacityRole: data = node->opacity(); break; case PercentOpacityRole: data = node->percentOpacity(); break; case VisibleRole: data = node->visible(); break; case CompositeDetailsRole: // composite op goes here... if (node->compositeOp()) data = node->compositeOp()->description(); break; case FilterRole: break; case ChildCountRole: data = node->childNodes(d->layerClassNames(), KoProperties()).count(); break; case DeepChildCountRole: data = d->deepChildCount(d->layers.at(index.row())); break; case DepthRole: data = d->layerMeta[node.data()].depth; break; case PreviousItemDepthRole: if (index.row() == 0) data = -1; else data = d->layerMeta[d->layers[index.row() - 1].data()].depth; break; case NextItemDepthRole: if (index.row() == d->layers.count() - 1) data = -1; else data = d->layerMeta[d->layers[index.row() + 1].data()].depth; break; case CanMoveDownRole: data = (node == d->activeNode) && d->layerMeta[node.data()].canMoveDown; break; case CanMoveLeftRole: data = (node == d->activeNode) && d->layerMeta[node.data()].canMoveLeft; break; case CanMoveRightRole: data = (node == d->activeNode) && d->layerMeta[node.data()].canMoveRight; break; case CanMoveUpRole: data = (node == d->activeNode) && d->layerMeta[node.data()].canMoveUp; break; default: break; } } return data; } int LayerModel::rowCount(const QModelIndex& parent) const { if ( parent.isValid() ) { return 0; } return d->layers.count(); } QVariant LayerModel::headerData(int section, Qt::Orientation orientation, int role) const { return QAbstractItemModel::headerData(section, orientation, role); } void LayerModel::setActive(int index) { if (index > -1 && index < d->layers.count()) { KisNodeSP newNode = d->layers.at(index); d->nodeManager->slotUiActivatedNode(newNode); currentNodeChanged(newNode); } } void LayerModel::moveUp() { KisNodeSP node = d->nodeManager->activeNode(); KisNodeSP parent = node->parent(); KisNodeSP grandParent = parent->parent(); if (!d->nodeManager->activeNode()->nextSibling()) { //dbgKrita << "Active node apparently has no next sibling, however that has happened..."; if (!grandParent) return; //dbgKrita << "Node has grandparent"; if (!grandParent->parent() && node->inherits("KisMask")) return; //dbgKrita << "Node isn't a mask"; d->nodeManager->moveNodeAt(node, grandParent, grandParent->index(parent) + 1); } else { //dbgKrita << "Move node directly"; d->nodeManager->lowerNode(); } } void LayerModel::moveDown() { KisNodeSP node = d->nodeManager->activeNode(); KisNodeSP parent = node->parent(); KisNodeSP grandParent = parent->parent(); if (!d->nodeManager->activeNode()->prevSibling()) { //dbgKrita << "Active node apparently has no previous sibling, however that has happened..."; if (!grandParent) return; //dbgKrita << "Node has grandparent"; if (!grandParent->parent() && node->inherits("KisMask")) return; //dbgKrita << "Node isn't a mask"; d->nodeManager->moveNodeAt(node, grandParent, grandParent->index(parent)); } else { //dbgKrita << "Move node directly"; d->nodeManager->raiseNode(); } } void LayerModel::moveLeft() { KisNodeSP node = d->nodeManager->activeNode(); KisNodeSP parent = node->parent(); KisNodeSP grandParent = parent->parent(); quint16 nodeIndex = parent->index(node); if (!grandParent) return; if (!grandParent->parent() && node->inherits("KisMask")) return; if (nodeIndex <= parent->childCount() / 2) { d->nodeManager->moveNodeAt(node, grandParent, grandParent->index(parent)); } else { d->nodeManager->moveNodeAt(node, grandParent, grandParent->index(parent) + 1); } } void LayerModel::moveRight() { KisNodeSP node = d->nodeManager->activeNode(); KisNodeSP parent = d->nodeManager->activeNode()->parent(); KisNodeSP newParent; int nodeIndex = parent->index(node); int indexAbove = nodeIndex + 1; int indexBelow = nodeIndex - 1; if (parent->at(indexBelow) && parent->at(indexBelow)->allowAsChild(node)) { newParent = parent->at(indexBelow); d->nodeManager->moveNodeAt(node, newParent, newParent->childCount()); } else if (parent->at(indexAbove) && parent->at(indexAbove)->allowAsChild(node)) { newParent = parent->at(indexAbove); d->nodeManager->moveNodeAt(node, newParent, 0); } else { return; } } void LayerModel::clear() { d->canvas->viewManager()->selectionManager()->clear(); } void LayerModel::clone() { d->nodeManager->duplicateActiveNode(); } void LayerModel::setLocked(int index, bool newLocked) { if (index > -1 && index < d->layers.count()) { if(d->layers[index]->userLocked() == newLocked) return; d->layers[index]->setUserLocked(newLocked); QModelIndex idx = createIndex(index, 0); dataChanged(idx, idx); } } void LayerModel::setOpacity(int index, float newOpacity) { if (index > -1 && index < d->layers.count()) { if(qFuzzyCompare(d->layers[index]->opacity() + 1, newOpacity + 1)) return; d->layers[index]->setOpacity(newOpacity); d->layers[index]->setDirty(); QModelIndex idx = createIndex(index, 0); dataChanged(idx, idx); } } void LayerModel::setVisible(int index, bool newVisible) { if (index > -1 && index < d->layers.count()) { KisBaseNode::PropertyList props = d->layers[index]->sectionModelProperties(); if(props[0].state == newVisible) return; KisBaseNode::Property prop = props[0]; prop.state = newVisible; props[0] = prop; d->nodeModel->setData( d->nodeModel->indexFromNode(d->layers[index]), QVariant::fromValue(props), KisNodeModel::PropertiesRole ); d->layers[index]->setDirty(d->layers[index]->extent()); QModelIndex idx = createIndex(index, 0); dataChanged(idx, idx); } } QImage LayerModel::layerThumbnail(QString layerID) const { // So, yeah, this is a complete cheatery hack. However, it ensures // we actually get updates when we want them (every time the image is supposed // to be changed). Had hoped we could avoid it, but apparently not. int index = layerID.section(QChar('/'), 0, 0).toInt(); QImage thumb; if (index > -1 && index < d->layers.count()) { if (d->thumbProvider) thumb = d->layers[index]->createThumbnail(120, 120); } return thumb; } void LayerModel::deleteCurrentLayer() { d->activeNode.clear(); d->nodeManager->removeNode(); } void LayerModel::deleteLayer(int index) { if (index > -1 && index < d->layers.count()) { if (d->activeNode == d->layers.at(index)) d->activeNode.clear(); d->nodeManager->slotUiActivatedNode(d->layers.at(index)); d->nodeManager->removeNode(); d->rebuildLayerList(); beginResetModel(); endResetModel(); } } void LayerModel::addLayer(int layerType) { switch(layerType) { case 0: d->nodeManager->createNode("KisPaintLayer"); break; case 1: d->nodeManager->createNode("KisGroupLayer"); break; case 2: d->nodeManager->createNode("KisFilterMask", true); break; default: break; } } void LayerModel::source_rowsAboutToBeInserted(QModelIndex /*p*/, int /*from*/, int /*to*/) { beginResetModel(); } void LayerModel::source_rowsInserted(QModelIndex /*p*/, int, int) { d->rebuildLayerList(); emit countChanged(); endResetModel(); } void LayerModel::source_rowsAboutToBeRemoved(QModelIndex /*p*/, int /*from*/, int /*to*/) { beginResetModel(); } void LayerModel::source_rowsRemoved(QModelIndex, int, int) { d->rebuildLayerList(); emit countChanged(); endResetModel(); } void LayerModel::source_dataChanged(QModelIndex /*tl*/, QModelIndex /*br*/) { QModelIndex top = createIndex(0, 0); QModelIndex bottom = createIndex(d->layers.count() - 1, 0); dataChanged(top, bottom); } void LayerModel::source_modelReset() { beginResetModel(); d->rebuildLayerList(); d->activeNode.clear(); if (d->layers.count() > 0) { d->nodeManager->slotUiActivatedNode(d->layers.at(0)); currentNodeChanged(d->layers.at(0)); } emit countChanged(); endResetModel(); } void LayerModel::notifyImageDeleted() { } void LayerModel::nodeChanged(KisNodeSP node) { QModelIndex index = createIndex(d->layers.indexOf(node), 0); dataChanged(index, index); } void LayerModel::imageChanged() { d->imageChangedTimer->start(); } void LayerModel::imageHasChanged() { QModelIndex top = createIndex(0, 0); QModelIndex bottom = createIndex(d->layers.count() - 1, 0); dataChanged(top, bottom); } void LayerModel::aboutToRemoveNode(KisNodeSP node) { Q_UNUSED(node) QTimer::singleShot(0, this, SLOT(source_modelReset())); } void LayerModel::emitActiveChanges() { emit activeFilterConfigChanged(); emit activeNameChanged(); emit activeTypeChanged(); emit activeCompositeOpChanged(); emit activeOpacityChanged(); emit activeVisibleChanged(); emit activeLockedChanged(); emit activeRChannelActiveChanged(); emit activeGChannelActiveChanged(); emit activeBChannelActiveChanged(); emit activeAChannelActiveChanged(); } QString LayerModel::activeName() const { if (d->activeNode.isNull()) return QString(); return d->activeNode->name(); } void LayerModel::setActiveName(QString newName) { if (d->activeNode.isNull()) return; d->activeNode->setName(newName); emit activeNameChanged(); } QString LayerModel::activeType() const { return d->activeNode->metaObject()->className(); } int LayerModel::activeCompositeOp() const { if (d->activeNode.isNull()) return 0; KoID entry(d->activeNode->compositeOp()->id()); QModelIndex idx = KisCompositeOpListModel::sharedInstance()->indexOf(entry); if (idx.isValid()) return idx.row(); return 0; } void LayerModel::setActiveCompositeOp(int newOp) { if (d->activeNode.isNull()) return; KoID entry; if (KisCompositeOpListModel::sharedInstance()->entryAt(entry, KisCompositeOpListModel::sharedInstance()->index(newOp))) { d->activeNode->setCompositeOpId(entry.id()); d->activeNode->setDirty(); emit activeCompositeOpChanged(); } } int LayerModel::activeOpacity() const { if (d->activeNode.isNull()) return 0; return d->activeNode->opacity(); } void LayerModel::setActiveOpacity(int newOpacity) { d->activeNode->setOpacity(newOpacity); d->activeNode->setDirty(); emit activeOpacityChanged(); } bool LayerModel::activeVisible() const { if (d->activeNode.isNull()) return false; return d->activeNode->visible(); } void LayerModel::setActiveVisible(bool newVisible) { if (d->activeNode.isNull()) return; setVisible(d->layers.indexOf(d->activeNode), newVisible); emit activeVisibleChanged(); } bool LayerModel::activeLocked() const { if (d->activeNode.isNull()) return false; return d->activeNode->userLocked(); } void LayerModel::setActiveLocked(bool newLocked) { if (d->activeNode.isNull()) return; d->activeNode->setUserLocked(newLocked); emit activeLockedChanged(); } bool LayerModel::activeAChannelActive() const { KisLayer* layer = qobject_cast(d->activeNode.data()); bool state = false; if (layer) state = !layer->alphaChannelDisabled(); return state; } void LayerModel::setActiveAChannelActive(bool newActive) { KisLayer* layer = qobject_cast(d->activeNode.data()); if (layer) { layer->disableAlphaChannel(!newActive); layer->setDirty(); emit activeAChannelActiveChanged(); } } bool getActiveChannel(KisNodeSP node, int channelIndex) { KisLayer* layer = qobject_cast(node.data()); bool flag = false; if (layer) { QBitArray flags = layer->channelFlags(); if (channelIndex < flags.size()) { flag = flags[channelIndex]; } } return flag; } void setChannelActive(KisNodeSP node, int channelIndex, bool newActive) { KisLayer* layer = qobject_cast(node.data()); if (layer) { QBitArray flags = layer->channelFlags(); flags.setBit(channelIndex, newActive); layer->setChannelFlags(flags); layer->setDirty(); } } bool LayerModel::activeBChannelActive() const { return getActiveChannel(d->activeNode, 2); } void LayerModel::setActiveBChannelActive(bool newActive) { setChannelActive(d->activeNode, 2, newActive); emit activeBChannelActiveChanged(); } bool LayerModel::activeGChannelActive() const { return getActiveChannel(d->activeNode, 1); } void LayerModel::setActiveGChannelActive(bool newActive) { setChannelActive(d->activeNode, 1, newActive); emit activeGChannelActiveChanged(); } bool LayerModel::activeRChannelActive() const { return getActiveChannel(d->activeNode, 0); } void LayerModel::setActiveRChannelActive(bool newActive) { setChannelActive(d->activeNode, 0, newActive); emit activeRChannelActiveChanged(); } QObject* LayerModel::activeFilterConfig() const { QMap props; QString filterId; KisFilterMask* filterMask = qobject_cast(d->activeNode.data()); if (filterMask) { props = filterMask->filter()->getProperties(); filterId = filterMask->filter()->name(); } else { KisAdjustmentLayer* adjustmentLayer = qobject_cast(d->activeNode.data()); if (adjustmentLayer) { props = adjustmentLayer->filter()->getProperties(); filterId = adjustmentLayer->filter()->name(); } } PropertyContainer* config = new PropertyContainer(filterId, 0); QMap::const_iterator i; for(i = props.constBegin(); i != props.constEnd(); ++i) { config->setProperty(i.key().toLatin1(), i.value()); //dbgKrita << "Getting active config..." << i.key() << i.value(); } return config; } void LayerModel::setActiveFilterConfig(QObject* newConfig) { if (d->activeNode.isNull()) return; PropertyContainer* config = qobject_cast(newConfig); if (!config) return; //dbgKrita << "Attempting to set new config" << config->name(); KisFilterConfigurationSP realConfig = d->filters.value(config->name())->factoryConfiguration(); QMap::const_iterator i; for(i = realConfig->getProperties().constBegin(); i != realConfig->getProperties().constEnd(); ++i) { realConfig->setProperty(i.key(), config->property(i.key().toLatin1())); //dbgKrita << "Creating config..." << i.key() << i.value(); } // The following code causes sporadic crashes, and disabling causes leaks. So, leaks it must be, for now. // The cause is the lack of a smart pointer interface for passing filter configs around // Must be remedied, but for now... // if (d->newConfig) // delete(d->newConfig); d->newConfig = realConfig; //d->updateActiveLayerWithNewFilterConfigTimer->start(); updateActiveLayerWithNewFilterConfig(); } void LayerModel::updateActiveLayerWithNewFilterConfig() { if (!d->newConfig) return; //dbgKrita << "Setting new config..." << d->newConfig->name(); KisFilterMask* filterMask = qobject_cast(d->activeNode.data()); if (filterMask) { //dbgKrita << "Filter mask"; if (filterMask->filter() == d->newConfig) return; //dbgKrita << "Setting filter mask"; filterMask->setFilter(d->newConfig); } else { KisAdjustmentLayer* adjustmentLayer = qobject_cast(d->activeNode.data()); if (adjustmentLayer) { //dbgKrita << "Adjustment layer"; if (adjustmentLayer->filter() == d->newConfig) return; //dbgKrita << "Setting filter on adjustment layer"; adjustmentLayer->setFilter(d->newConfig); } else { //dbgKrita << "UNKNOWN, BAIL OUT!"; } } d->newConfig = 0; d->activeNode->setDirty(d->activeNode->extent()); d->image->setModified(); QTimer::singleShot(100, this, SIGNAL(activeFilterConfigChanged())); } diff --git a/libs/pigment/KoColorSpaceRegistry.h b/libs/pigment/KoColorSpaceRegistry.h index 3bee4e3a26..edb5908ab1 100644 --- a/libs/pigment/KoColorSpaceRegistry.h +++ b/libs/pigment/KoColorSpaceRegistry.h @@ -1,361 +1,361 @@ /* * Copyright (c) 2003 Patrick Julien * Copyright (c) 2004,2010 Cyrille Berger * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser 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 KOCOLORSPACEREGISTRY_H #define KOCOLORSPACEREGISTRY_H #include #include #include #include "kritapigment_export.h" #include #include #include class KoColorProfile; class KoColorConversionSystem; class KoColorConversionCache; class KoColorConversionTransformation; /** * The registry for colorspaces and profiles. * This class contains: * - a registry of colorspace instantiated with specific profiles. * - a registry of singleton colorspace factories. * - a registry of icc profiles * * Locking policy details: * * Basically, we have two levels of locks in the registry: * 1) (outer level) is Private::registrylock, which controls the structures * of the color space registry itself * 2) (inner level) is KoColorProfileStorage::Private::lock controls * the structures related to profiles. * * The locks can be taken individually, but if you are going to take both * of them, you should always follow the order 1) registry; 2) profiles. * Otherwise you'll get a deadlock. * * To avoid recursive deadlocks, all the dependent classes * (KoColorConversionSystem and KoColorSpaceFactory) now do not use the direct * links to the registry. Instead, they use special private interfaces that * skip recursive locking and ensure we take a lock twice. */ class KRITAPIGMENT_EXPORT KoColorSpaceRegistry { public: KoColorSpaceRegistry(); enum ColorSpaceListVisibility { OnlyUserVisible = 1, ///< Only user visible color space AllColorSpaces = 4 ///< All color space even those not visible to the user }; enum ColorSpaceListProfilesSelection { OnlyDefaultProfile = 1, ///< Only add the default profile AllProfiles = 4 ///< Add all profiles }; /** * Return an instance of the KoColorSpaceRegistry * Creates an instance if that has never happened before and returns the singleton instance. */ static KoColorSpaceRegistry * instance(); virtual ~KoColorSpaceRegistry(); public: /** * add a color space to the registry * @param item the color space factory to add */ void add(KoColorSpaceFactory* item); /** * Remove a color space factory from the registry. Note that it is the * responsibility of the caller to ensure that the colorspaces are not * used anymore. */ void remove(KoColorSpaceFactory* item); /** * Add a profile to the profile map but do not add it to the * color conversion system yet. * @param profile the new profile to be registered. */ void addProfileToMap(KoColorProfile *p); /** * register the profile with the color space registry * @param profile the new profile to be registered so it can be combined with * colorspaces. */ void addProfile(KoColorProfile* profile); void addProfile(const KoColorProfile* profile); // TODO why ? void removeProfile(KoColorProfile* profile); /** * Create an alias to a profile with a different name. Then @ref profileByName * will return the profile @p to when passed @p name as a parameter. */ void addProfileAlias(const QString& name, const QString& to); /** * @return the profile alias, or name if not aliased */ QString profileAlias(const QString& name) const; /** * create a profile of the specified type. */ const KoColorProfile *createColorProfile(const QString & colorModelId, const QString & colorDepthId, const QByteArray& rawData); /** * Return a profile by its given name, or 0 if none registered. * @return a profile by its given name, or 0 if none registered. * @param name the product name as set on the profile. * @see addProfile() * @see KoColorProfile::productName() */ const KoColorProfile * profileByName(const QString & name) const ; /** * Returns a profile by its unique id stored/calculated in the header. * The first call to this function might take long, because the map is * created on the first use only (atm used by SVG only) * @param id unique ProfileID of the profile (MD5 sum of its header) * @return the profile or 0 if not found */ const KoColorProfile *profileByUniqueId(const QByteArray &id) const; bool profileIsCompatible(const KoColorProfile* profile, const QString &colorSpaceId); /** * Return the list of profiles for a colorspace with the argument id. * Profiles will not work with any color space, you can query which profiles * that are registered with this registry can be used in combination with the * argument factory. * @param colorSpaceId the colorspace-id with which all the returned profiles will work. * @return a list of profiles for the factory */ QList profilesFor(const QString& csID) const; QString defaultProfileForColorSpace(const QString &colorSpaceId) const; /** * This function is called by the color space to create a color conversion * between two color space. This function search in the graph of transformations * the best possible path between the two color space. */ KoColorConversionTransformation* createColorConverter(const KoColorSpace * srcColorSpace, const KoColorSpace * dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const; /** * This function creates two transformations, one from the color space and one to the * color space. The destination color space is picked from a list of color space, such * as the conversion between the two color space is of the best quality. * * The typical use case of this function is for KoColorTransformationFactory which * doesn't support all color spaces, so unsupported color space have to find an * acceptable conversion in order to use that KoColorTransformationFactory. * * @param colorSpace the source color space * @param possibilities a list of color space among which we need to find the best * conversion * @param fromCS the conversion from the source color space will be affected to this * variable * @param toCS the revert conversion to the source color space will be affected to this * variable */ void createColorConverters(const KoColorSpace* colorSpace, const QList< QPair >& possibilities, KoColorConversionTransformation*& fromCS, KoColorConversionTransformation*& toCS) const; /** * Return a colorspace that works with the parameter profile. * @param colorSpaceId the ID string of the colorspace that you want to have returned * @param profile the profile be combined with the colorspace * @return the wanted colorspace, or 0 when the cs and profile can not be combined. */ const KoColorSpace * colorSpace(const QString & colorModelId, const QString & colorDepthId, const KoColorProfile *profile); /** * Return a colorspace that works with the parameter profile. * @param profileName the name of the KoColorProfile to be combined with the colorspace * @return the wanted colorspace, or 0 when the cs and profile can not be combined. */ const KoColorSpace * colorSpace(const QString & colorModelId, const QString & colorDepthId, const QString &profileName); const KoColorSpace * colorSpace(const QString & colorModelId, const QString & colorDepthId); /** * Return the id of the colorspace that have the defined colorModelId with colorDepthId. * @param colorModelId id of the color model * @param colorDepthId id of the color depth * @return the id of the wanted colorspace, or "" if no colorspace correspond to those ids */ QString colorSpaceId(const QString & colorModelId, const QString & colorDepthId) const; /** * It's a convenient function that behave like the above. * Return the id of the colorspace that have the defined colorModelId with colorDepthId. * @param colorModelId id of the color model * @param colorDepthId id of the color depth * @return the id of the wanted colorspace, or "" if no colorspace correspond to those ids */ QString colorSpaceId(const KoID& colorModelId, const KoID& colorDepthId) const; /** * @return a the identifiant of the color model for the given color space id. * * This function is a compatibility function used to get the color space from * all kra files. */ KoID colorSpaceColorModelId(const QString & _colorSpaceId) const; /** * @return a the identifiant of the color depth for the given color space id. * * This function is a compatibility function used to get the color space from * all kra files. */ KoID colorSpaceColorDepthId(const QString & _colorSpaceId) const; /** * Convenience methods to get the often used alpha colorspaces */ const KoColorSpace *alpha8(); const KoColorSpace *alpha16(); #include #ifdef HAVE_OPENEXR const KoColorSpace *alpha16f(); #endif const KoColorSpace *alpha32f(); /** * Convenience method to get an RGBA 8bit colorspace. If a profile is not specified, * an sRGB profile will be used. * @param profileName the name of an RGB color profile * @return the wanted colorspace, or 0 if the color space and profile can not be combined. */ const KoColorSpace * rgb8(const QString &profileName = QString()); /** * Convenience method to get an RGBA 8bit colorspace with the given profile. * @param profile an RGB profile * @return the wanted colorspace, or 0 if the color space and profile can not be combined. */ const KoColorSpace * rgb8(const KoColorProfile * profile); /** * Convenience method to get an RGBA 16bit colorspace. If a profile is not specified, * an sRGB profile will be used. * @param profileName the name of an RGB color profile * @return the wanted colorspace, or 0 if the color space and profile can not be combined. */ const KoColorSpace * rgb16(const QString &profileName = QString()); /** * Convenience method to get an RGBA 16bit colorspace with the given profile. * @param profile an RGB profile * @return the wanted colorspace, or 0 if the color space and profile can not be combined. */ const KoColorSpace * rgb16(const KoColorProfile * profile); /** * Convenience method to get an Lab 16bit colorspace. If a profile is not specified, * an Lab profile with a D50 whitepoint will be used. * @param profileName the name of an Lab color profile * @return the wanted colorspace, or 0 if the color space and profile can not be combined. */ const KoColorSpace * lab16(const QString &profileName = QString()); /** * Convenience method to get an Lab 16bit colorspace with the given profile. * @param profile an Lab profile * @return the wanted colorspace, or 0 if the color space and profile can not be combined. */ const KoColorSpace * lab16(const KoColorProfile * profile); /** * @return the list of available color models */ QList colorModelsList(ColorSpaceListVisibility option) const; /** * @return the list of available color models for the given colorModelId */ QList colorDepthList(const KoID& colorModelId, ColorSpaceListVisibility option) const; /** * @return the list of available color models for the given colorModelId */ QList colorDepthList(const QString & colorModelId, ColorSpaceListVisibility option) const; /** * @return the cache of color conversion transformation to be use by KoColorSpace */ KoColorConversionCache* colorConversionCache() const; /** * @return a permanent colorspace owned by the registry, of the same type and profile * as the one given in argument */ const KoColorSpace* permanentColorspace(const KoColorSpace* _colorSpace); /** * This function return a list of all the keys in KoID format by using the name() method * on the objects stored in the registry. */ QList listKeys() const; private: friend class KisCsConversionTest; friend class KisIteratorTest; friend class KisIteratorNGTest; friend class KisPainterTest; friend class KisCrashFilterTest; friend class KoColorSpacesBenchmark; friend class TestKoColorSpaceSanity; friend class TestColorConversionSystem; - friend class FriendOfColorSpaceRegistry; + friend struct FriendOfColorSpaceRegistry; /** * @return a list with an instance of all color space with their default profile. */ QList allColorSpaces(ColorSpaceListVisibility visibility, ColorSpaceListProfilesSelection pSelection); /** * @return the color conversion system use by the registry and the color * spaces to create color conversion transformation. * * WARNING: conversion system is guared by the registry locks, don't * use it anywhere other than unttests! */ const KoColorConversionSystem* colorConversionSystem() const; private: KoColorSpaceRegistry(const KoColorSpaceRegistry&); KoColorSpaceRegistry operator=(const KoColorSpaceRegistry&); void init(); private: struct Private; Private * const d; }; #endif // KOCOLORSPACEREGISTRY_H diff --git a/libs/ui/CMakeLists.txt b/libs/ui/CMakeLists.txt index dbee566cdd..af24770d85 100644 --- a/libs/ui/CMakeLists.txt +++ b/libs/ui/CMakeLists.txt @@ -1,577 +1,578 @@ include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/qtlockedfile ${EXIV2_INCLUDE_DIR} ) include_directories(SYSTEM ${EIGEN3_INCLUDE_DIR} ${OCIO_INCLUDE_DIR} ) add_subdirectory( tests ) if (APPLE) find_library(FOUNDATION_LIBRARY Foundation) find_library(APPKIT_LIBRARY AppKit) endif () set(kritaui_LIB_SRCS canvas/kis_canvas_widget_base.cpp canvas/kis_canvas2.cpp canvas/kis_canvas_updates_compressor.cpp canvas/kis_canvas_controller.cpp canvas/kis_paintop_transformation_connector.cpp canvas/kis_display_color_converter.cpp canvas/kis_display_filter.cpp canvas/kis_exposure_gamma_correction_interface.cpp canvas/kis_tool_proxy.cpp canvas/kis_canvas_decoration.cc canvas/kis_coordinates_converter.cpp canvas/kis_grid_manager.cpp canvas/kis_grid_decoration.cpp canvas/kis_grid_config.cpp canvas/kis_prescaled_projection.cpp canvas/kis_qpainter_canvas.cpp canvas/kis_projection_backend.cpp canvas/kis_update_info.cpp canvas/kis_image_patch.cpp canvas/kis_image_pyramid.cpp canvas/kis_infinity_manager.cpp canvas/kis_change_guides_command.cpp canvas/kis_guides_decoration.cpp canvas/kis_guides_manager.cpp canvas/kis_guides_config.cpp canvas/kis_snap_config.cpp canvas/kis_snap_line_strategy.cpp canvas/KisSnapPointStrategy.cpp dialogs/kis_about_application.cpp dialogs/kis_dlg_adj_layer_props.cc dialogs/kis_dlg_adjustment_layer.cc dialogs/kis_dlg_filter.cpp dialogs/kis_dlg_generator_layer.cpp dialogs/kis_dlg_file_layer.cpp dialogs/kis_dlg_filter.cpp dialogs/kis_dlg_stroke_selection_properties.cpp dialogs/kis_dlg_image_properties.cc dialogs/kis_dlg_layer_properties.cc dialogs/kis_dlg_preferences.cc dialogs/slider_and_spin_box_sync.cpp dialogs/kis_dlg_blacklist_cleanup.cpp dialogs/kis_dlg_layer_style.cpp dialogs/kis_dlg_png_import.cpp dialogs/kis_dlg_import_image_sequence.cpp dialogs/kis_delayed_save_dialog.cpp dialogs/KisSessionManagerDialog.cpp dialogs/KisNewWindowLayoutDialog.cpp flake/kis_node_dummies_graph.cpp flake/kis_dummies_facade_base.cpp flake/kis_dummies_facade.cpp flake/kis_node_shapes_graph.cpp flake/kis_node_shape.cpp flake/kis_shape_controller.cpp flake/kis_shape_layer.cc flake/kis_shape_layer_canvas.cpp flake/kis_shape_selection.cpp flake/kis_shape_selection_canvas.cpp flake/kis_shape_selection_model.cpp flake/kis_take_all_shapes_command.cpp brushhud/kis_uniform_paintop_property_widget.cpp brushhud/kis_brush_hud.cpp brushhud/kis_round_hud_button.cpp brushhud/kis_dlg_brush_hud_config.cpp brushhud/kis_brush_hud_properties_list.cpp brushhud/kis_brush_hud_properties_config.cpp kis_aspect_ratio_locker.cpp kis_autogradient.cc kis_bookmarked_configurations_editor.cc kis_bookmarked_configurations_model.cc kis_bookmarked_filter_configurations_model.cc KisPaintopPropertiesBase.cpp kis_canvas_resource_provider.cpp kis_derived_resources.cpp kis_categories_mapper.cpp kis_categorized_list_model.cpp kis_categorized_item_delegate.cpp kis_clipboard.cc kis_config.cc kis_control_frame.cpp kis_composite_ops_model.cc kis_paint_ops_model.cpp kis_cursor.cc kis_cursor_cache.cpp kis_custom_pattern.cc kis_file_layer.cpp kis_change_file_layer_command.h kis_safe_document_loader.cpp kis_splash_screen.cpp kis_filter_manager.cc kis_filters_model.cc kis_histogram_view.cc KisImageBarrierLockerWithFeedback.cpp kis_image_manager.cc kis_image_view_converter.cpp kis_import_catcher.cc kis_layer_manager.cc kis_mask_manager.cc kis_mimedata.cpp kis_node_commands_adapter.cpp kis_node_manager.cpp kis_node_juggler_compressed.cpp kis_node_selection_adapter.cpp kis_node_insertion_adapter.cpp kis_node_model.cpp kis_node_filter_proxy_model.cpp kis_model_index_converter_base.cpp kis_model_index_converter.cpp kis_model_index_converter_show_all.cpp kis_painting_assistant.cc kis_painting_assistants_decoration.cpp KisDecorationsManager.cpp kis_paintop_box.cc kis_paintop_option.cpp kis_paintop_options_model.cpp kis_paintop_settings_widget.cpp kis_popup_palette.cpp kis_png_converter.cpp kis_preference_set_registry.cpp KisResourceServerProvider.cpp KisResourceBundleServerProvider.cpp KisSelectedShapesProxy.cpp kis_selection_decoration.cc kis_selection_manager.cc + KisSelectionActionsAdapter.cpp kis_statusbar.cc kis_zoom_manager.cc kis_favorite_resource_manager.cpp kis_workspace_resource.cpp kis_action.cpp kis_action_manager.cpp KisActionPlugin.cpp kis_canvas_controls_manager.cpp kis_tooltip_manager.cpp kis_multinode_property.cpp kis_stopgradient_editor.cpp KisWelcomePageWidget.cpp kisexiv2/kis_exif_io.cpp kisexiv2/kis_exiv2.cpp kisexiv2/kis_iptc_io.cpp kisexiv2/kis_xmp_io.cpp opengl/kis_opengl.cpp opengl/kis_opengl_canvas2.cpp opengl/kis_opengl_canvas_debugger.cpp opengl/kis_opengl_image_textures.cpp opengl/kis_texture_tile.cpp opengl/kis_opengl_shader_loader.cpp opengl/kis_texture_tile_info_pool.cpp opengl/KisOpenGLUpdateInfoBuilder.cpp kis_fps_decoration.cpp tool/kis_selection_tool_helper.cpp tool/kis_selection_tool_config_widget_helper.cpp tool/kis_rectangle_constraint_widget.cpp tool/kis_shape_tool_helper.cpp tool/kis_tool.cc tool/kis_delegated_tool_policies.cpp tool/kis_tool_freehand.cc tool/kis_speed_smoother.cpp tool/kis_painting_information_builder.cpp tool/kis_stabilized_events_sampler.cpp tool/kis_tool_freehand_helper.cpp tool/kis_tool_multihand_helper.cpp tool/kis_figure_painting_tool_helper.cpp tool/kis_tool_paint.cc tool/kis_tool_shape.cc tool/kis_tool_ellipse_base.cpp tool/kis_tool_rectangle_base.cpp tool/kis_tool_polyline_base.cpp tool/kis_tool_utils.cpp tool/kis_resources_snapshot.cpp tool/kis_smoothing_options.cpp tool/KisStabilizerDelayedPaintHelper.cpp tool/KisStrokeSpeedMonitor.cpp tool/strokes/freehand_stroke.cpp tool/strokes/KisStrokeEfficiencyMeasurer.cpp tool/strokes/kis_painter_based_stroke_strategy.cpp tool/strokes/kis_filter_stroke_strategy.cpp tool/strokes/kis_color_picker_stroke_strategy.cpp tool/strokes/KisFreehandStrokeInfo.cpp tool/strokes/KisMaskedFreehandStrokePainter.cpp tool/strokes/KisMaskingBrushRenderer.cpp tool/strokes/KisMaskingBrushCompositeOpFactory.cpp widgets/kis_cmb_composite.cc widgets/kis_cmb_contour.cpp widgets/kis_cmb_gradient.cpp widgets/kis_paintop_list_widget.cpp widgets/kis_cmb_idlist.cc widgets/kis_color_space_selector.cc widgets/kis_advanced_color_space_selector.cc widgets/kis_cie_tongue_widget.cpp widgets/kis_tone_curve_widget.cpp widgets/kis_curve_widget.cpp widgets/kis_custom_image_widget.cc widgets/kis_image_from_clipboard_widget.cpp widgets/kis_double_widget.cc widgets/kis_filter_selector_widget.cc widgets/kis_gradient_chooser.cc widgets/kis_iconwidget.cc widgets/kis_mask_widgets.cpp widgets/kis_meta_data_merge_strategy_chooser_widget.cc widgets/kis_multi_bool_filter_widget.cc widgets/kis_multi_double_filter_widget.cc widgets/kis_multi_integer_filter_widget.cc widgets/kis_multipliers_double_slider_spinbox.cpp widgets/kis_paintop_presets_popup.cpp widgets/kis_tool_options_popup.cpp widgets/kis_paintop_presets_chooser_popup.cpp widgets/kis_paintop_presets_save.cpp widgets/kis_paintop_preset_icon_library.cpp widgets/kis_pattern_chooser.cc widgets/kis_preset_chooser.cpp widgets/kis_progress_widget.cpp widgets/kis_selection_options.cc widgets/kis_scratch_pad.cpp widgets/kis_scratch_pad_event_filter.cpp widgets/kis_preset_selector_strip.cpp widgets/kis_slider_spin_box.cpp widgets/KisSelectionPropertySlider.cpp widgets/kis_size_group.cpp widgets/kis_size_group_p.cpp widgets/kis_wdg_generator.cpp widgets/kis_workspace_chooser.cpp widgets/kis_categorized_list_view.cpp widgets/kis_widget_chooser.cpp widgets/kis_tool_button.cpp widgets/kis_floating_message.cpp widgets/kis_lod_availability_widget.cpp widgets/kis_color_label_selector_widget.cpp widgets/kis_color_filter_combo.cpp widgets/kis_elided_label.cpp widgets/kis_stopgradient_slider_widget.cpp widgets/kis_preset_live_preview_view.cpp widgets/KisScreenColorPicker.cpp widgets/KoDualColorButton.cpp widgets/KoStrokeConfigWidget.cpp widgets/KoFillConfigWidget.cpp utils/kis_document_aware_spin_box_unit_manager.cpp input/kis_input_manager.cpp input/kis_input_manager_p.cpp input/kis_extended_modifiers_mapper.cpp input/kis_abstract_input_action.cpp input/kis_tool_invocation_action.cpp input/kis_pan_action.cpp input/kis_alternate_invocation_action.cpp input/kis_rotate_canvas_action.cpp input/kis_zoom_action.cpp input/kis_change_frame_action.cpp input/kis_gamma_exposure_action.cpp input/kis_show_palette_action.cpp input/kis_change_primary_setting_action.cpp input/kis_abstract_shortcut.cpp input/kis_native_gesture_shortcut.cpp input/kis_single_action_shortcut.cpp input/kis_stroke_shortcut.cpp input/kis_shortcut_matcher.cpp input/kis_select_layer_action.cpp input/KisQtWidgetsTweaker.cpp input/KisInputActionGroup.cpp operations/kis_operation.cpp operations/kis_operation_configuration.cpp operations/kis_operation_registry.cpp operations/kis_operation_ui_factory.cpp operations/kis_operation_ui_widget.cpp operations/kis_filter_selection_operation.cpp actions/kis_selection_action_factories.cpp actions/KisPasteActionFactory.cpp input/kis_touch_shortcut.cpp kis_document_undo_store.cpp kis_transaction_based_command.cpp kis_gui_context_command.cpp kis_gui_context_command_p.cpp input/kis_tablet_debugger.cpp input/kis_input_profile_manager.cpp input/kis_input_profile.cpp input/kis_shortcut_configuration.cpp input/config/kis_input_configuration_page.cpp input/config/kis_edit_profiles_dialog.cpp input/config/kis_input_profile_model.cpp input/config/kis_input_configuration_page_item.cpp input/config/kis_action_shortcuts_model.cpp input/config/kis_input_type_delegate.cpp input/config/kis_input_mode_delegate.cpp input/config/kis_input_button.cpp input/config/kis_input_editor_delegate.cpp input/config/kis_mouse_input_editor.cpp input/config/kis_wheel_input_editor.cpp input/config/kis_key_input_editor.cpp processing/fill_processing_visitor.cpp kis_asl_layer_style_serializer.cpp kis_psd_layer_style_resource.cpp canvas/kis_mirror_axis.cpp kis_abstract_perspective_grid.cpp KisApplication.cpp KisAutoSaveRecoveryDialog.cpp KisDetailsPane.cpp KisDocument.cpp KisCloneDocumentStroke.cpp KisNodeDelegate.cpp kis_node_view_visibility_delegate.cpp KisNodeToolTip.cpp KisNodeView.cpp kis_node_view_color_scheme.cpp KisImportExportFilter.cpp KisFilterEntry.cpp KisImportExportManager.cpp KisImportExportUtils.cpp kis_async_action_feedback.cpp KisMainWindow.cpp KisOpenPane.cpp KisPart.cpp KisPrintJob.cpp KisTemplate.cpp KisTemplateCreateDia.cpp KisTemplateGroup.cpp KisTemplates.cpp KisTemplatesPane.cpp KisTemplateTree.cpp KisUndoActionsUpdateManager.cpp KisView.cpp thememanager.cpp kis_mainwindow_observer.cpp KisViewManager.cpp kis_mirror_manager.cpp qtlockedfile/qtlockedfile.cpp qtsingleapplication/qtlocalpeer.cpp qtsingleapplication/qtsingleapplication.cpp KisResourceBundle.cpp KisResourceBundleManifest.cpp kis_md5_generator.cpp KisApplicationArguments.cpp KisNetworkAccessManager.cpp KisMultiFeedRSSModel.cpp KisRemoteFileFetcher.cpp KisSaveGroupVisitor.cpp KisWindowLayoutResource.cpp KisWindowLayoutManager.cpp KisSessionResource.cpp KisReferenceImagesDecoration.cpp KisReferenceImage.cpp flake/KisReferenceImagesLayer.cpp flake/KisReferenceImagesLayer.h ) if(WIN32) set(kritaui_LIB_SRCS ${kritaui_LIB_SRCS} input/kis_tablet_event.cpp input/wintab/kis_tablet_support_win.cpp input/wintab/kis_screen_size_choice_dialog.cpp qtlockedfile/qtlockedfile_win.cpp input/wintab/kis_tablet_support_win8.cpp opengl/kis_opengl_win.cpp ) endif() set(kritaui_LIB_SRCS ${kritaui_LIB_SRCS} kis_animation_frame_cache.cpp kis_animation_cache_populator.cpp KisAsyncAnimationRendererBase.cpp KisAsyncAnimationCacheRenderer.cpp KisAsyncAnimationFramesSavingRenderer.cpp dialogs/KisAsyncAnimationRenderDialogBase.cpp dialogs/KisAsyncAnimationCacheRenderDialog.cpp dialogs/KisAsyncAnimationFramesSaveDialog.cpp canvas/kis_animation_player.cpp kis_animation_importer.cpp KisSyncedAudioPlayback.cpp KisFrameDataSerializer.cpp KisFrameCacheStore.cpp KisFrameCacheSwapper.cpp KisAbstractFrameCacheSwapper.cpp KisInMemoryFrameCacheSwapper.cpp input/wintab/drawpile_tablettester/tablettester.cpp input/wintab/drawpile_tablettester/tablettest.cpp ) if(UNIX) set(kritaui_LIB_SRCS ${kritaui_LIB_SRCS} input/kis_tablet_event.cpp input/wintab/kis_tablet_support.cpp qtlockedfile/qtlockedfile_unix.cpp ) if(NOT APPLE) set(kritaui_LIB_SRCS ${kritaui_LIB_SRCS} input/wintab/qxcbconnection_xi2.cpp input/wintab/qxcbconnection.cpp input/wintab/kis_xi2_event_filter.cpp ) endif() endif() if(APPLE) set(kritaui_LIB_SRCS ${kritaui_LIB_SRCS} osx.mm ) endif() ki18n_wrap_ui(kritaui_LIB_SRCS widgets/KoFillConfigWidget.ui widgets/KoStrokeConfigWidget.ui forms/wdgdlgpngimport.ui forms/wdgfullscreensettings.ui forms/wdgautogradient.ui forms/wdggeneralsettings.ui forms/wdgperformancesettings.ui forms/wdggenerators.ui forms/wdgbookmarkedconfigurationseditor.ui forms/wdgapplyprofile.ui forms/wdgcustompattern.ui forms/wdglayerproperties.ui forms/wdgcolorsettings.ui forms/wdgtabletsettings.ui forms/wdgcolorspaceselector.ui forms/wdgcolorspaceselectoradvanced.ui forms/wdgdisplaysettings.ui forms/kis_previewwidgetbase.ui forms/kis_matrix_widget.ui forms/wdgselectionoptions.ui forms/wdggeometryoptions.ui forms/wdgnewimage.ui forms/wdgimageproperties.ui forms/wdgmaskfromselection.ui forms/wdgmasksource.ui forms/wdgfilterdialog.ui forms/wdgmetadatamergestrategychooser.ui forms/wdgpaintoppresets.ui forms/wdgpaintopsettings.ui forms/wdgdlggeneratorlayer.ui forms/wdgdlgfilelayer.ui forms/wdgfilterselector.ui forms/wdgfilternodecreation.ui forms/wdgmultipliersdoublesliderspinbox.ui forms/wdgnodequerypatheditor.ui forms/wdgpresetselectorstrip.ui forms/wdgsavebrushpreset.ui forms/wdgpreseticonlibrary.ui forms/wdgdlgblacklistcleanup.ui forms/wdgrectangleconstraints.ui forms/wdgimportimagesequence.ui forms/wdgstrokeselectionproperties.ui forms/KisDetailsPaneBase.ui forms/KisOpenPaneBase.ui forms/wdgstopgradienteditor.ui forms/wdgsessionmanager.ui forms/wdgnewwindowlayout.ui forms/KisWelcomePage.ui brushhud/kis_dlg_brush_hud_config.ui dialogs/kis_delayed_save_dialog.ui input/config/kis_input_configuration_page.ui input/config/kis_edit_profiles_dialog.ui input/config/kis_input_configuration_page_item.ui input/config/kis_mouse_input_editor.ui input/config/kis_wheel_input_editor.ui input/config/kis_key_input_editor.ui layerstyles/wdgBevelAndEmboss.ui layerstyles/wdgblendingoptions.ui layerstyles/WdgColorOverlay.ui layerstyles/wdgContour.ui layerstyles/wdgdropshadow.ui layerstyles/WdgGradientOverlay.ui layerstyles/wdgInnerGlow.ui layerstyles/wdglayerstyles.ui layerstyles/WdgPatternOverlay.ui layerstyles/WdgSatin.ui layerstyles/WdgStroke.ui layerstyles/wdgstylesselector.ui layerstyles/wdgTexture.ui wdgsplash.ui input/wintab/kis_screen_size_choice_dialog.ui input/wintab/drawpile_tablettester/tablettest.ui ) QT5_WRAP_CPP(kritaui_HEADERS_MOC KisNodePropertyAction_p.h) add_library(kritaui SHARED ${kritaui_HEADERS_MOC} ${kritaui_LIB_SRCS} ) generate_export_header(kritaui BASE_NAME kritaui) target_link_libraries(kritaui KF5::CoreAddons KF5::Completion KF5::I18n KF5::ItemViews Qt5::Network kritaimpex kritacolor kritaimage kritalibbrush kritawidgets kritawidgetutils kritaresources ${PNG_LIBRARIES} ${EXIV2_LIBRARIES} ) if (HAVE_QT_MULTIMEDIA) target_link_libraries(kritaui Qt5::Multimedia) endif() if (HAVE_KIO) target_link_libraries(kritaui KF5::KIOCore) endif() if (NOT WIN32 AND NOT APPLE) target_link_libraries(kritaui ${X11_X11_LIB} ${X11_Xinput_LIB} ${XCB_LIBRARIES}) endif() if(APPLE) target_link_libraries(kritaui ${FOUNDATION_LIBRARY}) target_link_libraries(kritaui ${APPKIT_LIBRARY}) endif () target_link_libraries(kritaui ${OPENEXR_LIBRARIES}) # Add VSync disable workaround if(NOT WIN32 AND NOT APPLE) target_link_libraries(kritaui ${CMAKE_DL_LIBS} Qt5::X11Extras) endif() if(X11_FOUND) target_link_libraries(kritaui Qt5::X11Extras ${X11_LIBRARIES}) endif() target_include_directories(kritaui PUBLIC $ $ $ $ $ $ $ ) set_target_properties(kritaui PROPERTIES VERSION ${GENERIC_KRITA_LIB_VERSION} SOVERSION ${GENERIC_KRITA_LIB_SOVERSION} ) install(TARGETS kritaui ${INSTALL_TARGETS_DEFAULT_ARGS}) if (APPLE) install(FILES osx.stylesheet DESTINATION ${DATA_INSTALL_DIR}/krita) endif () diff --git a/libs/ui/KisNodeDelegate.cpp b/libs/ui/KisNodeDelegate.cpp index 328e38abc4..0e73f9ba79 100644 --- a/libs/ui/KisNodeDelegate.cpp +++ b/libs/ui/KisNodeDelegate.cpp @@ -1,918 +1,956 @@ /* Copyright (c) 2006 Gábor Lehel Copyright (c) 2008 Cyrille Berger Copyright (c) 2011 José Luis Vergara 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 "kis_config.h" #include "KisNodeDelegate.h" #include "kis_node_model.h" #include "KisNodeToolTip.h" #include "KisNodeView.h" #include "KisPart.h" #include "input/kis_input_manager.h" #include #include #include #include #include #include #include #include #include #include #include #include "kis_node_view_color_scheme.h" #include "kis_icon_utils.h" #include "kis_layer_properties_icons.h" #include "krita_utils.h" #include "kis_config_notifier.h" typedef KisBaseNode::Property* OptionalProperty; #include class KisNodeDelegate::Private { public: Private() : view(0), edit(0) { } KisNodeView *view; QPointer edit; KisNodeToolTip tip; QColor checkersColor1; QColor checkersColor2; QList rightmostProperties(const KisBaseNode::PropertyList &props) const; int numProperties(const QModelIndex &index) const; OptionalProperty findProperty(KisBaseNode::PropertyList &props, const OptionalProperty &refProp) const; OptionalProperty findVisibilityProperty(KisBaseNode::PropertyList &props) const; void toggleProperty(KisBaseNode::PropertyList &props, OptionalProperty prop, bool controlPressed, const QModelIndex &index); }; KisNodeDelegate::KisNodeDelegate(KisNodeView *view, QObject *parent) : QAbstractItemDelegate(parent) , d(new Private) { d->view = view; QApplication::instance()->installEventFilter(this); connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotConfigChanged())); slotConfigChanged(); } KisNodeDelegate::~KisNodeDelegate() { delete d; } QSize KisNodeDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const { Q_UNUSED(index); KisNodeViewColorScheme scm; return QSize(option.rect.width(), scm.rowHeight()); } void KisNodeDelegate::paint(QPainter *p, const QStyleOptionViewItem &o, const QModelIndex &index) const { p->save(); { QStyleOptionViewItem option = getOptions(o, index); QStyle *style = option.widget ? option.widget->style() : QApplication::style(); style->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, p, option.widget); bool shouldGrayOut = index.data(KisNodeModel::ShouldGrayOutRole).toBool(); if (shouldGrayOut) { option.state &= ~QStyle::State_Enabled; } p->setFont(option.font); drawColorLabel(p, option, index); drawFrame(p, option, index); drawThumbnail(p, option, index); drawText(p, option, index); drawIcons(p, option, index); drawVisibilityIconHijack(p, option, index); drawDecoration(p, option, index); drawExpandButton(p, option, index); drawBranch(p, option, index); drawProgressBar(p, option, index); } p->restore(); } void KisNodeDelegate::drawBranch(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const { Q_UNUSED(index); KisNodeViewColorScheme scm; const QPoint base = scm.relThumbnailRect().translated(option.rect.topLeft()).topLeft() - QPoint( scm.indentation(), 0); // there is no indention if we are starting negative, so don't draw a branch if (base.x() < 0) { return; } QPen oldPen = p->pen(); const qreal oldOpacity = p->opacity(); // remember previous opacity p->setOpacity(1.0); QColor color = scm.gridColor(option, d->view); QColor bgColor = option.state & QStyle::State_Selected ? qApp->palette().color(QPalette::Base) : qApp->palette().color(QPalette::Text); color = KritaUtils::blendColors(color, bgColor, 0.9); // TODO: if we are a mask type, use dotted lines for the branch style // p->setPen(QPen(p->pen().color(), 2, Qt::DashLine, Qt::RoundCap, Qt::RoundJoin)); p->setPen(QPen(color, 0, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)); QPoint p2 = base + QPoint(scm.iconSize() - scm.decorationMargin()*2, scm.iconSize()*0.45); QPoint p3 = base + QPoint(scm.iconSize() - scm.decorationMargin()*2, scm.iconSize()); QPoint p4 = base + QPoint(scm.iconSize()*1.4, scm.iconSize()); p->drawLine(p2, p3); p->drawLine(p3, p4); // draw parent lines (keep drawing until x position is less than 0 QPoint p5 = p2 - QPoint(scm.indentation(), 0); QPoint p6 = p3 - QPoint(scm.indentation(), 0); QPoint parentBase1 = p5; QPoint parentBase2 = p6; // indent lines needs to be very subtle to avoid making the docker busy looking color = KritaUtils::blendColors(color, bgColor, 0.9); // makes it a little lighter than L lines p->setPen(QPen(color, 0, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)); while (parentBase1.x() > scm.visibilityColumnWidth()) { p->drawLine(parentBase1, parentBase2); parentBase1 = parentBase1 - QPoint(scm.indentation(), 0); parentBase2 = parentBase2 - QPoint(scm.indentation(), 0); } p->setPen(oldPen); p->setOpacity(oldOpacity); } void KisNodeDelegate::drawColorLabel(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const { KisNodeViewColorScheme scm; const int label = index.data(KisNodeModel::ColorLabelIndexRole).toInt(); QColor color = scm.colorLabel(label); if (color.alpha() <= 0) return; QColor bgColor = qApp->palette().color(QPalette::Base); color = KritaUtils::blendColors(color, bgColor, 0.3); const QRect rect = option.state & QStyle::State_Selected ? iconsRect(option, index) : option.rect.adjusted(-scm.indentation(), 0, 0, 0); p->fillRect(rect, color); } void KisNodeDelegate::drawFrame(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const { KisNodeViewColorScheme scm; QPen oldPen = p->pen(); p->setPen(scm.gridColor(option, d->view)); const QPoint base = option.rect.topLeft(); QPoint p2 = base + QPoint(-scm.indentation() - 1, 0); QPoint p3 = base + QPoint(2 * scm.decorationMargin() + scm.decorationSize(), 0); QPoint p4 = base + QPoint(-1, 0); QPoint p5(iconsRect(option, index).left() - 1, base.y()); QPoint p6(option.rect.right(), base.y()); QPoint v(0, option.rect.height()); // draw a line that goes the length of the entire frame. one for the // top, and one for the bottom QPoint pTopLeft(0, option.rect.topLeft().y()); QPoint pTopRight(option.rect.bottomRight().x(),option.rect.topLeft().y() ); p->drawLine(pTopLeft, pTopRight); QPoint pBottomLeft(0, option.rect.topLeft().y() + scm.rowHeight()); QPoint pBottomRight(option.rect.bottomRight().x(),option.rect.topLeft().y() + scm.rowHeight() ); p->drawLine(pBottomLeft, pBottomRight); const bool paintForParent = index.parent().isValid() && !index.row(); if (paintForParent) { QPoint p1(-2 * scm.indentation() - 1, 0); p1 += base; p->drawLine(p1, p2); } QPoint k0(0, base.y()); QPoint k1(1 * scm.border() + 2 * scm.visibilityMargin() + scm.visibilitySize(), base.y()); p->drawLine(k0, k1); p->drawLine(k0 + v, k1 + v); p->drawLine(k0, k0 + v); p->drawLine(k1, k1 + v); p->drawLine(p2, p6); p->drawLine(p2 + v, p6 + v); p->drawLine(p2, p2 + v); p->drawLine(p3, p3 + v); p->drawLine(p4, p4 + v); p->drawLine(p5, p5 + v); p->drawLine(p6, p6 + v); //// For debugging purposes only //p->setPen(Qt::blue); //KritaUtils::renderExactRect(p, iconsRect(option, index)); //KritaUtils::renderExactRect(p, textRect(option, index)); //KritaUtils::renderExactRect(p, scm.relThumbnailRect().translated(option.rect.topLeft())); p->setPen(oldPen); } -void KisNodeDelegate::drawThumbnail(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const +QRect KisNodeDelegate::thumbnailClickRect(const QStyleOptionViewItem &option, const QModelIndex &index) const { - KisNodeViewColorScheme scm; + Q_UNUSED(index); + const int steps = 0; + KisNodeViewColorScheme scm; + return QRect(scm.border() + + 2 * scm.visibilityMargin() + scm.visibilitySize() + + scm.border() + steps * scm.indentation(), + scm.border() + option.rect.top(), + 2 * scm.thumbnailMargin() + scm.thumbnailSize(), + scm.rowHeight() - scm.border()); +} + +void KisNodeDelegate::drawThumbnail(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + KisNodeViewColorScheme scm; const int thumbSize = scm.thumbnailSize(); const qreal oldOpacity = p->opacity(); // remember previous opacity QImage img = index.data(int(KisNodeModel::BeginThumbnailRole) + thumbSize).value(); if (!(option.state & QStyle::State_Enabled)) { p->setOpacity(0.35); } QRect fitRect = scm.relThumbnailRect().translated(option.rect.topLeft()); QPoint offset; offset.setX((fitRect.width() - img.width()) / 2); offset.setY((fitRect.height() - img.height()) / 2); offset += fitRect.topLeft(); // paint in a checkerboard pattern behind the layer contents to represent transparent const int step = scm.thumbnailSize() / 6; QImage checkers(2 * step, 2 * step, QImage::Format_ARGB32); QPainter gc(&checkers); gc.fillRect(QRect(0, 0, step, step), d->checkersColor1); gc.fillRect(QRect(step, 0, step, step), d->checkersColor2); gc.fillRect(QRect(step, step, step, step), d->checkersColor1); gc.fillRect(QRect(0, step, step, step), d->checkersColor2); QBrush brush(checkers); p->setBrushOrigin(offset); p->fillRect(img.rect().translated(offset), brush); p->drawImage(offset, img); p->setOpacity(oldOpacity); // restore old opacity QRect borderRect = kisGrowRect(img.rect(), 1).translated(offset); KritaUtils::renderExactRect(p, borderRect, scm.gridColor(option, d->view)); } QRect KisNodeDelegate::iconsRect(const QStyleOptionViewItem &option, const QModelIndex &index) const { KisNodeViewColorScheme scm; int propCount = d->numProperties(index); const int iconsWidth = propCount * (scm.iconSize() + 2 * scm.iconMargin()) + (propCount - 1) * scm.border(); const int x = option.rect.x() + option.rect.width() - (iconsWidth + scm.border()); const int y = option.rect.y() + scm.border(); return QRect(x, y, iconsWidth, scm.rowHeight() - scm.border()); } QRect KisNodeDelegate::textRect(const QStyleOptionViewItem &option, const QModelIndex &index) const { KisNodeViewColorScheme scm; static QFont f; static int minbearing = 1337 + 666; //can be 0 or negative, 2003 is less likely if (minbearing == 2003 || f != option.font) { f = option.font; //getting your bearings can be expensive, so we cache them minbearing = option.fontMetrics.minLeftBearing() + option.fontMetrics.minRightBearing(); } const int decorationOffset = 2 * scm.border() + 2 * scm.decorationMargin() + scm.decorationSize(); const int width = iconsRect(option, index).left() - option.rect.x() - scm.border() + minbearing - decorationOffset; return QRect(option.rect.x() - minbearing + decorationOffset, option.rect.y() + scm.border(), width, scm.rowHeight() - scm.border()); } void KisNodeDelegate::drawText(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const { KisNodeViewColorScheme scm; const QRect rc = textRect(option, index) .adjusted(scm.textMargin(), 0, -scm.textMargin(), 0); QPen oldPen = p->pen(); const qreal oldOpacity = p->opacity(); // remember previous opacity p->setPen(option.palette.color(QPalette::Active,QPalette::Text )); if (!(option.state & QStyle::State_Enabled)) { p->setOpacity(0.55); } const QString text = index.data(Qt::DisplayRole).toString(); const QString elided = elidedText(p->fontMetrics(), rc.width(), Qt::ElideRight, text); p->drawText(rc, Qt::AlignLeft | Qt::AlignVCenter, elided); p->setPen(oldPen); // restore pen settings p->setOpacity(oldOpacity); } QList KisNodeDelegate::Private::rightmostProperties(const KisBaseNode::PropertyList &props) const { QList list; QList prependList; list << OptionalProperty(0); list << OptionalProperty(0); list << OptionalProperty(0); KisBaseNode::PropertyList::const_iterator it = props.constBegin(); KisBaseNode::PropertyList::const_iterator end = props.constEnd(); for (; it != end; ++it) { if (!it->isMutable) continue; if (it->id == KisLayerPropertiesIcons::visible.id()) { // noop... } else if (it->id == KisLayerPropertiesIcons::locked.id()) { list[0] = OptionalProperty(&(*it)); } else if (it->id == KisLayerPropertiesIcons::inheritAlpha.id()) { list[1] = OptionalProperty(&(*it)); } else if (it->id == KisLayerPropertiesIcons::alphaLocked.id()) { list[2] = OptionalProperty(&(*it)); } else { prependList.prepend(OptionalProperty(&(*it))); } } { QMutableListIterator i(prependList); i.toBack(); while (i.hasPrevious()) { OptionalProperty val = i.previous(); int emptyIndex = list.lastIndexOf(0); if (emptyIndex < 0) break; list[emptyIndex] = val; i.remove(); } } return prependList + list; } int KisNodeDelegate::Private::numProperties(const QModelIndex &index) const { KisBaseNode::PropertyList props = index.data(KisNodeModel::PropertiesRole).value(); QList realProps = rightmostProperties(props); return realProps.size(); } OptionalProperty KisNodeDelegate::Private::findProperty(KisBaseNode::PropertyList &props, const OptionalProperty &refProp) const { KisBaseNode::PropertyList::iterator it = props.begin(); KisBaseNode::PropertyList::iterator end = props.end(); for (; it != end; ++it) { if (it->id == refProp->id) { return &(*it); } } return 0; } OptionalProperty KisNodeDelegate::Private::findVisibilityProperty(KisBaseNode::PropertyList &props) const { KisBaseNode::PropertyList::iterator it = props.begin(); KisBaseNode::PropertyList::iterator end = props.end(); for (; it != end; ++it) { if (it->id == KisLayerPropertiesIcons::visible.id()) { return &(*it); } } return 0; } void KisNodeDelegate::drawIcons(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const { KisNodeViewColorScheme scm; const QRect r = iconsRect(option, index); QTransform oldTransform = p->transform(); QPen oldPen = p->pen(); p->setTransform(QTransform::fromTranslate(r.x(), r.y())); p->setPen(scm.gridColor(option, d->view)); int x = 0; const int y = (scm.rowHeight() - scm.border() - scm.iconSize()) / 2; KisBaseNode::PropertyList props = index.data(KisNodeModel::PropertiesRole).value(); QList realProps = d->rightmostProperties(props); Q_FOREACH (OptionalProperty prop, realProps) { x += scm.iconMargin(); if (prop) { QIcon icon = prop->state.toBool() ? prop->onIcon : prop->offIcon; bool fullColor = prop->state.toBool() && option.state & QStyle::State_Enabled; const qreal oldOpacity = p->opacity(); // remember previous opacity if (fullColor) { p->setOpacity(1.0); } else { p->setOpacity(0.35); } p->drawPixmap(x, y, icon.pixmap(scm.iconSize(), QIcon::Normal)); p->setOpacity(oldOpacity); // restore old opacity } x += scm.iconSize() + scm.iconMargin(); p->drawLine(x, 0, x, scm.rowHeight() - scm.border()); x += scm.border(); } p->setTransform(oldTransform); p->setPen(oldPen); } QRect KisNodeDelegate::visibilityClickRect(const QStyleOptionViewItem &option, const QModelIndex &index) const { Q_UNUSED(index); KisNodeViewColorScheme scm; return QRect(scm.border(), scm.border() + option.rect.top(), 2 * scm.visibilityMargin() + scm.visibilitySize(), scm.rowHeight() - scm.border()); } QRect KisNodeDelegate::decorationClickRect(const QStyleOptionViewItem &option, const QModelIndex &index) const { Q_UNUSED(option); Q_UNUSED(index); KisNodeViewColorScheme scm; QRect realVisualRect = d->view->originalVisualRect(index); return QRect(realVisualRect.left(), scm.border() + realVisualRect.top(), 2 * scm.decorationMargin() + scm.decorationSize(), scm.rowHeight() - scm.border()); } void KisNodeDelegate::drawVisibilityIconHijack(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const { /** * Small hack Alert: * * Here wepaint over the area that sits basically outside our layer's * row. Anyway, just update it later... */ KisNodeViewColorScheme scm; KisBaseNode::PropertyList props = index.data(KisNodeModel::PropertiesRole).value(); OptionalProperty prop = d->findVisibilityProperty(props); if (!prop) return; const int x = scm.border() + scm.visibilityMargin(); const int y = option.rect.top() + (scm.rowHeight() - scm.border() - scm.visibilitySize()) / 2; QIcon icon = prop->state.toBool() ? prop->onIcon : prop->offIcon; // if we are not showing the layer, make the icon slightly transparent like other inactive icons const qreal oldOpacity = p->opacity(); if (prop->state.toBool() == true) { p->setOpacity(1.0); } else { p->setOpacity(0.35); } p->drawPixmap(x, y, icon.pixmap(scm.visibilitySize(), QIcon::Normal)); p->setOpacity(oldOpacity); //// For debugging purposes only // p->save(); // p->setPen(Qt::blue); // KritaUtils::renderExactRect(p, visibilityClickRect(option, index)); // p->restore(); } void KisNodeDelegate::drawDecoration(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const { KisNodeViewColorScheme scm; QIcon icon = index.data(Qt::DecorationRole).value(); if (!icon.isNull()) { QPixmap pixmap = icon.pixmap(scm.decorationSize(), (option.state & QStyle::State_Enabled) ? QIcon::Normal : QIcon::Disabled); const QRect rc = scm.relDecorationRect().translated(option.rect.topLeft()); const qreal oldOpacity = p->opacity(); // remember previous opacity if (!(option.state & QStyle::State_Enabled)) { p->setOpacity(0.35); } p->drawPixmap(rc.topLeft(), pixmap); p->setOpacity(oldOpacity); // restore old opacity } } void KisNodeDelegate::drawExpandButton(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const { Q_UNUSED(index); KisNodeViewColorScheme scm; QRect rc = scm.relExpandButtonRect().translated(option.rect.topLeft()); rc = kisGrowRect(rc, 0); if (!(option.state & QStyle::State_Children)) { return; } QString iconName = option.state & QStyle::State_Open ? "arrow-down" : "arrow-right"; QIcon icon = KisIconUtils::loadIcon(iconName); QPixmap pixmap = icon.pixmap(rc.width(), (option.state & QStyle::State_Enabled) ? QIcon::Normal : QIcon::Disabled); p->drawPixmap(rc.topLeft(), pixmap); } void KisNodeDelegate::Private::toggleProperty(KisBaseNode::PropertyList &props, OptionalProperty clickedProperty, bool controlPressed, const QModelIndex &index) { QAbstractItemModel *model = view->model(); // Using Ctrl+click to enter stasis if (controlPressed && clickedProperty->canHaveStasis) { // STEP 0: Prepare to Enter or Leave control key stasis quint16 numberOfLeaves = model->rowCount(index.parent()); QModelIndex eachItem; // STEP 1: Go. if (clickedProperty->isInStasis == false) { // Enter /* Make every leaf of this node go State = False, saving the old property value to stateInStasis */ for (quint16 i = 0; i < numberOfLeaves; ++i) { // Foreach leaf in the node (index.parent()) eachItem = model->index(i, 1, index.parent()); // The entire property list has to be altered because model->setData cannot set individual properties KisBaseNode::PropertyList eachPropertyList = eachItem.data(KisNodeModel::PropertiesRole).value(); OptionalProperty prop = findProperty(eachPropertyList, clickedProperty); if (!prop) continue; prop->stateInStasis = prop->state.toBool(); prop->state = eachItem == index; prop->isInStasis = true; model->setData(eachItem, QVariant::fromValue(eachPropertyList), KisNodeModel::PropertiesRole); } for (quint16 i = 0; i < numberOfLeaves; ++i) { // Foreach leaf in the node (index.parent()) eachItem = model->index(i, 1, index.parent()); KisBaseNode::PropertyList eachPropertyList = eachItem.data(KisNodeModel::PropertiesRole).value(); OptionalProperty prop = findProperty(eachPropertyList, clickedProperty); if (!prop) continue; } } else { // Leave /* Make every leaf of this node go State = stateInStasis */ for (quint16 i = 0; i < numberOfLeaves; ++i) { eachItem = model->index(i, 1, index.parent()); // The entire property list has to be altered because model->setData cannot set individual properties KisBaseNode::PropertyList eachPropertyList = eachItem.data(KisNodeModel::PropertiesRole).value(); OptionalProperty prop = findProperty(eachPropertyList, clickedProperty); if (!prop) continue; prop->state = prop->stateInStasis; prop->isInStasis = false; model->setData(eachItem, QVariant::fromValue(eachPropertyList), KisNodeModel::PropertiesRole); } } } else { clickedProperty->state = !clickedProperty->state.toBool(); model->setData(index, QVariant::fromValue(props), KisNodeModel::PropertiesRole); } } bool KisNodeDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index) { KisNodeViewColorScheme scm; if ((event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonDblClick) && (index.flags() & Qt::ItemIsEnabled)) { QMouseEvent *mouseEvent = static_cast(event); /** * Small hack Alert: * * Here we handle clicking even when it happened outside * the rectangle of the current index. The point is, we * use some virtual scroling offset to move the tree to the * right of the visibility icon. So the icon itself is placed * in an empty area that doesn't belong to any index. But we still * handle it. */ const QRect iconsRect = this->iconsRect(option, index); const bool iconsClicked = iconsRect.isValid() && iconsRect.contains(mouseEvent->pos()); const QRect visibilityRect = visibilityClickRect(option, index); const bool visibilityClicked = visibilityRect.isValid() && visibilityRect.contains(mouseEvent->pos()); const QRect decorationRect = decorationClickRect(option, index); const bool decorationClicked = decorationRect.isValid() && decorationRect.contains(mouseEvent->pos()); const bool leftButton = mouseEvent->buttons() & Qt::LeftButton; + const QRect thumbnailRect = thumbnailClickRect(option, index); + const bool thumbnailClicked = thumbnailRect.contains(mouseEvent->pos()); + if (leftButton && iconsClicked) { KisBaseNode::PropertyList props = index.data(KisNodeModel::PropertiesRole).value(); QList realProps = d->rightmostProperties(props); const int numProps = realProps.size(); const int iconWidth = scm.iconSize() + 2 * scm.iconMargin() + scm.border(); const int xPos = mouseEvent->pos().x() - iconsRect.left(); const int clickedIcon = xPos / iconWidth; const int distToBorder = qMin(xPos % iconWidth, iconWidth - xPos % iconWidth); if (iconsClicked && clickedIcon >= 0 && clickedIcon < numProps && distToBorder > scm.iconMargin()) { OptionalProperty clickedProperty = realProps[clickedIcon]; if (!clickedProperty) return false; d->toggleProperty(props, clickedProperty, mouseEvent->modifiers() == Qt::ControlModifier, index); return true; } } else if (leftButton && visibilityClicked) { KisBaseNode::PropertyList props = index.data(KisNodeModel::PropertiesRole).value(); OptionalProperty clickedProperty = d->findVisibilityProperty(props); if (!clickedProperty) return false; d->toggleProperty(props, clickedProperty, mouseEvent->modifiers() == Qt::ControlModifier, index); return true; } else if (leftButton && decorationClicked) { bool isExpandable = model->hasChildren(index); if (isExpandable) { bool isExpanded = d->view->isExpanded(index); d->view->setExpanded(index, !isExpanded); + } + return true; + } else if (leftButton && thumbnailClicked) { + bool hasCorrectModifier = false; + SelectionAction action = SELECTION_REPLACE; + + if (mouseEvent->modifiers() == Qt::ControlModifier) { + action = SELECTION_REPLACE; + hasCorrectModifier = true; + } else if (mouseEvent->modifiers() == (Qt::ControlModifier | Qt::ShiftModifier)) { + action = SELECTION_ADD; + hasCorrectModifier = true; + } else if (mouseEvent->modifiers() == (Qt::ControlModifier | Qt::AltModifier)) { + action = SELECTION_SUBTRACT; + hasCorrectModifier = true; + } else if (mouseEvent->modifiers() == (Qt::ControlModifier | Qt::ShiftModifier | Qt::AltModifier)) { + action = SELECTION_INTERSECT; + hasCorrectModifier = true; + } + + if (hasCorrectModifier) { + model->setData(index, QVariant(int(action)), KisNodeModel::SelectOpaqueRole); return true; } } if (mouseEvent->button() == Qt::LeftButton && mouseEvent->modifiers() == Qt::AltModifier) { d->view->setCurrentIndex(index); model->setData(index, true, KisNodeModel::AlternateActiveRole); return true; } } else if (event->type() == QEvent::ToolTip) { if (!KisConfig(true).hidePopups()) { QHelpEvent *helpEvent = static_cast(event); d->tip.showTip(d->view, helpEvent->pos(), option, index); } return true; } else if (event->type() == QEvent::Leave) { d->tip.hide(); } return false; } QWidget *KisNodeDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem&, const QModelIndex&) const { d->edit = new QLineEdit(parent); d->edit->installEventFilter(const_cast(this)); //hack? return d->edit; } void KisNodeDelegate::setEditorData(QWidget *widget, const QModelIndex &index) const { QLineEdit *edit = qobject_cast(widget); Q_ASSERT(edit); edit->setText(index.data(Qt::DisplayRole).toString()); } void KisNodeDelegate::setModelData(QWidget *widget, QAbstractItemModel *model, const QModelIndex &index) const { QLineEdit *edit = qobject_cast(widget); Q_ASSERT(edit); model->setData(index, edit->text(), Qt::DisplayRole); } void KisNodeDelegate::updateEditorGeometry(QWidget *widget, const QStyleOptionViewItem &option, const QModelIndex &index) const { Q_UNUSED(index); widget->setGeometry(option.rect); } // PROTECTED bool KisNodeDelegate::eventFilter(QObject *object, QEvent *event) { switch (event->type()) { case QEvent::MouseButtonPress: { if (d->edit) { QMouseEvent *me = static_cast(event); if (!QRect(d->edit->mapToGlobal(QPoint()), d->edit->size()).contains(me->globalPos())) { emit commitData(d->edit); emit closeEditor(d->edit); } } } break; case QEvent::KeyPress: { QLineEdit *edit = qobject_cast(object); if (edit && edit == d->edit) { QKeyEvent *ke = static_cast(event); switch (ke->key()) { case Qt::Key_Escape: emit closeEditor(edit); return true; case Qt::Key_Tab: emit commitData(edit); emit closeEditor(edit,EditNextItem); return true; case Qt::Key_Backtab: emit commitData(edit); emit closeEditor(edit, EditPreviousItem); return true; case Qt::Key_Return: case Qt::Key_Enter: emit commitData(edit); emit closeEditor(edit); return true; default: break; } } } break; case QEvent::ShortcutOverride : { QLineEdit *edit = qobject_cast(object); if (edit && edit == d->edit){ auto* key = static_cast(event); if (key->modifiers() == Qt::NoModifier){ switch (key->key()){ case Qt::Key_Escape: case Qt::Key_Tab: case Qt::Key_Backtab: case Qt::Key_Return: case Qt::Key_Enter: event->accept(); return true; default: break; } } } } break; case QEvent::FocusOut : { QLineEdit *edit = qobject_cast(object); if (edit && edit == d->edit) { emit commitData(edit); emit closeEditor(edit); } } default: break; } return QAbstractItemDelegate::eventFilter(object, event); } // PRIVATE QStyleOptionViewItem KisNodeDelegate::getOptions(const QStyleOptionViewItem &o, const QModelIndex &index) { QStyleOptionViewItem option = o; QVariant v = index.data(Qt::FontRole); if (v.isValid()) { option.font = v.value(); option.fontMetrics = QFontMetrics(option.font); } v = index.data(Qt::TextAlignmentRole); if (v.isValid()) option.displayAlignment = QFlag(v.toInt()); v = index.data(Qt::TextColorRole); if (v.isValid()) option.palette.setColor(QPalette::Text, v.value()); v = index.data(Qt::BackgroundColorRole); if (v.isValid()) option.palette.setColor(QPalette::Window, v.value()); return option; } void KisNodeDelegate::drawProgressBar(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const { QVariant value = index.data(KisNodeModel::ProgressRole); if (!value.isNull() && (value.toInt() >= 0 && value.toInt() <= 100)) { /// The progress bar will display under the layer name area. The bars have accurate data, so we /// probably don't need to also show the actual number for % complete KisNodeViewColorScheme scm; const int width = textRect(option, index).width() + scm.iconSize()*2; const int height = 5; const QPoint base = option.rect.bottomLeft() - QPoint(0, height ); const QRect r = QRect(base.x(), base.y(), width, height); p->save(); { p->setClipRect(r); QStyle* style = QApplication::style(); QStyleOptionProgressBar opt; opt.minimum = 0; opt.maximum = 100; opt.progress = value.toInt(); opt.textVisible = false; opt.textAlignment = Qt::AlignHCenter; opt.text = i18n("%1 %", opt.progress); opt.rect = r; opt.orientation = Qt::Horizontal; opt.state = option.state; style->drawControl(QStyle::CE_ProgressBar, &opt, p, 0); } p->restore(); } } void KisNodeDelegate::slotConfigChanged() { KisConfig cfg(true); d->checkersColor1 = cfg.checkersColor1(); d->checkersColor2 = cfg.checkersColor2(); } void KisNodeDelegate::slotUpdateIcon() { KisLayerPropertiesIcons::instance()->updateIcons(); } diff --git a/libs/ui/KisNodeDelegate.h b/libs/ui/KisNodeDelegate.h index cdf6c5e73c..ad53f443e1 100644 --- a/libs/ui/KisNodeDelegate.h +++ b/libs/ui/KisNodeDelegate.h @@ -1,85 +1,85 @@ /* Copyright (c) 2006 Gábor Lehel 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 KIS_DOCUMENT_SECTION_DELEGATE_H #define KIS_DOCUMENT_SECTION_DELEGATE_H #include class KisNodeView; class KisNodeModel; /** * See KisNodeModel and KisNodeView. * * A delegate provides the gui machinery, using Qt's model/view terminology. * This class is owned by KisNodeView to do the work of generating the * graphical representation of each item. */ class KisNodeDelegate: public QAbstractItemDelegate { Q_OBJECT public: explicit KisNodeDelegate(KisNodeView *view, QObject *parent = 0); ~KisNodeDelegate() override; void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override; bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index) override; QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override; void setEditorData(QWidget *editor, const QModelIndex &index) const override; void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override; void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex& index) const override; void slotUpdateIcon(); protected: bool eventFilter(QObject *object, QEvent *event) override; - private: typedef KisNodeModel Model; typedef KisNodeView View; class Private; Private* const d; static QStyleOptionViewItem getOptions(const QStyleOptionViewItem &option, const QModelIndex &index); void drawProgressBar(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const; void drawBranch(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const; void drawColorLabel(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const; void drawFrame(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const; void drawThumbnail(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const; QRect iconsRect(const QStyleOptionViewItem &option, const QModelIndex &index) const; QRect textRect(const QStyleOptionViewItem &option, const QModelIndex &index) const; void drawText(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const; void drawIcons(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const; QRect visibilityClickRect(const QStyleOptionViewItem &option, const QModelIndex &index) const; QRect decorationClickRect(const QStyleOptionViewItem &option, const QModelIndex &index) const; + QRect thumbnailClickRect(const QStyleOptionViewItem &option, const QModelIndex &index) const; void drawVisibilityIconHijack(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const; void drawDecoration(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const; void drawExpandButton(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const; private Q_SLOTS: void slotConfigChanged(); }; #endif diff --git a/libs/ui/tool/kis_shape_tool_helper.h b/libs/ui/KisSelectionActionsAdapter.cpp similarity index 60% copy from libs/ui/tool/kis_shape_tool_helper.h copy to libs/ui/KisSelectionActionsAdapter.cpp index 60e3074907..b4cdb5a46a 100644 --- a/libs/ui/tool/kis_shape_tool_helper.h +++ b/libs/ui/KisSelectionActionsAdapter.cpp @@ -1,41 +1,33 @@ /* - * Copyright (c) 2009 Sven Langkamp + * Copyright (c) 2018 Dmitry Kazakov * * 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. */ -#ifndef KIS_SHAPE_TOOL_HELPER_H -#define KIS_SHAPE_TOOL_HELPER_H -#include +#include "KisSelectionActionsAdapter.h" -#include +#include "kis_selection_manager.h" -class KoShape; -/** - * KisShapeToolHelper provides shapes and fallback shapes for shape based tools - */ -class KRITAUI_EXPORT KisShapeToolHelper +KisSelectionActionsAdapter::KisSelectionActionsAdapter(KisSelectionManager *selectionManager) + : m_selectionManager(selectionManager) { -public: - static KoShape* createRectangleShape(const QRectF& rect); - - static KoShape* createEllipseShape(const QRectF& rect); - +} -}; - - -#endif +void KisSelectionActionsAdapter::selectOpaqueOnNode(KisNodeSP node, SelectionAction action) +{ + KIS_SAFE_ASSERT_RECOVER_RETURN(m_selectionManager); + m_selectionManager->selectOpaqueOnNode(node, action); +} diff --git a/plugins/extensions/colorrange/colorrange.h b/libs/ui/KisSelectionActionsAdapter.h similarity index 59% copy from plugins/extensions/colorrange/colorrange.h copy to libs/ui/KisSelectionActionsAdapter.h index e373ef0fa0..fd19f175ea 100644 --- a/plugins/extensions/colorrange/colorrange.h +++ b/libs/ui/KisSelectionActionsAdapter.h @@ -1,46 +1,39 @@ /* - * colorrange.h -- Part of Krita - * - * Copyright (c) 2004 Boudewijn Rempt (boud@valdyas.org) + * Copyright (c) 2018 Dmitry Kazakov * * 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. */ -#ifndef COLORRANGE_H -#define COLORRANGE_H - -#include +#ifndef KISSELECTIONACTIONSADAPTER_H +#define KISSELECTIONACTIONSADAPTER_H -#include +#include "kritaui_export.h" +#include "kis_types.h" +#include "KisSelectionTags.h" -#include "kis_selection.h" +class KisSelectionManager; -class ColorRange : public KisActionPlugin +class KRITAUI_EXPORT KisSelectionActionsAdapter { - Q_OBJECT public: - ColorRange(QObject *parent, const QVariantList &); - ~ColorRange() override; - -private Q_SLOTS: - void slotActivated(); - void selectOpaque(int id); + KisSelectionActionsAdapter(KisSelectionManager *selectionManager); + void selectOpaqueOnNode(KisNodeSP node, SelectionAction action); private: - void selectOpaqueImpl(SelectionAction action); + KisSelectionManager *m_selectionManager; }; -#endif // COLORRANGE_H +#endif // KISSELECTIONACTIONSADAPTER_H diff --git a/libs/ui/actions/kis_selection_action_factories.cpp b/libs/ui/actions/kis_selection_action_factories.cpp index 193ae2cf84..93091ce586 100644 --- a/libs/ui/actions/kis_selection_action_factories.cpp +++ b/libs/ui/actions/kis_selection_action_factories.cpp @@ -1,615 +1,615 @@ /* * Copyright (c) 2012 Dmitry Kazakov * * 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_action_factories.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "KisViewManager.h" #include "kis_canvas_resource_provider.h" #include "kis_clipboard.h" #include "kis_pixel_selection.h" #include "kis_paint_layer.h" #include "kis_image.h" #include "kis_image_barrier_locker.h" #include "kis_fill_painter.h" #include "kis_transaction.h" #include "kis_iterator_ng.h" #include "kis_processing_applicator.h" #include "kis_group_layer.h" #include "commands/kis_selection_commands.h" #include "commands/kis_image_layer_add_command.h" #include "kis_tool_proxy.h" #include "kis_canvas2.h" #include "kis_canvas_controller.h" #include "kis_selection_manager.h" #include "kis_transaction_based_command.h" #include "kis_selection_filters.h" #include "kis_shape_selection.h" #include "kis_shape_layer.h" #include #include "kis_image_animation_interface.h" #include "kis_time_range.h" #include "kis_keyframe_channel.h" #include #include #include "kis_figure_painting_tool_helper.h" #include "kis_update_outline_job.h" namespace ActionHelper { void copyFromDevice(KisViewManager *view, KisPaintDeviceSP device, bool makeSharpClip = false, const KisTimeRange &range = KisTimeRange()) { KisImageWSP image = view->image(); if (!image) return; KisSelectionSP selection = view->selection(); QRect rc = (selection) ? selection->selectedExactRect() : image->bounds(); KisPaintDeviceSP clip = new KisPaintDevice(device->colorSpace()); Q_CHECK_PTR(clip); const KoColorSpace *cs = clip->colorSpace(); // TODO if the source is linked... copy from all linked layers?!? // Copy image data KisPainter::copyAreaOptimized(QPoint(), device, clip, rc); if (selection) { // Apply selection mask. KisPaintDeviceSP selectionProjection = selection->projection(); KisHLineIteratorSP layerIt = clip->createHLineIteratorNG(0, 0, rc.width()); KisHLineConstIteratorSP selectionIt = selectionProjection->createHLineIteratorNG(rc.x(), rc.y(), rc.width()); const KoColorSpace *selCs = selection->projection()->colorSpace(); for (qint32 y = 0; y < rc.height(); y++) { for (qint32 x = 0; x < rc.width(); x++) { /** * Sharp method is an exact reverse of COMPOSITE_OVER * so if you cover the cut/copied piece over its source * you get an exactly the same image without any seams */ if (makeSharpClip) { qreal dstAlpha = cs->opacityF(layerIt->rawData()); qreal sel = selCs->opacityF(selectionIt->oldRawData()); qreal newAlpha = sel * dstAlpha / (1.0 - dstAlpha + sel * dstAlpha); float mask = newAlpha / dstAlpha; cs->applyAlphaNormedFloatMask(layerIt->rawData(), &mask, 1); } else { cs->applyAlphaU8Mask(layerIt->rawData(), selectionIt->oldRawData(), 1); } layerIt->nextPixel(); selectionIt->nextPixel(); } layerIt->nextRow(); selectionIt->nextRow(); } } KisClipboard::instance()->setClip(clip, rc.topLeft(), range); } } void KisSelectAllActionFactory::run(KisViewManager *view) { KisImageWSP image = view->image(); if (!image) return; KisProcessingApplicator *ap = beginAction(view, kundo2_i18n("Select All")); if (!image->globalSelection()) { ap->applyCommand(new KisSetEmptyGlobalSelectionCommand(image), KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE); } struct SelectAll : public KisTransactionBasedCommand { SelectAll(KisImageSP image) : m_image(image) {} KisImageSP m_image; KUndo2Command* paint() override { KisSelectionSP selection = m_image->globalSelection(); KisSelectionTransaction transaction(selection->pixelSelection()); selection->pixelSelection()->clear(); selection->pixelSelection()->select(m_image->bounds()); return transaction.endAndTake(); } }; ap->applyCommand(new SelectAll(image), KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE); endAction(ap, KisOperationConfiguration(id()).toXML()); } void KisDeselectActionFactory::run(KisViewManager *view) { KisImageWSP image = view->image(); if (!image) return; - KUndo2Command *cmd = new KisDeselectGlobalSelectionCommand(image); + KUndo2Command *cmd = new KisDeselectActiveSelectionCommand(view->selection(), image); KisProcessingApplicator *ap = beginAction(view, cmd->text()); ap->applyCommand(cmd, KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE); endAction(ap, KisOperationConfiguration(id()).toXML()); } void KisReselectActionFactory::run(KisViewManager *view) { KisImageWSP image = view->image(); if (!image) return; - KUndo2Command *cmd = new KisReselectGlobalSelectionCommand(image); + KUndo2Command *cmd = new KisReselectActiveSelectionCommand(view->activeNode(), image); KisProcessingApplicator *ap = beginAction(view, cmd->text()); ap->applyCommand(cmd, KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE); endAction(ap, KisOperationConfiguration(id()).toXML()); } void KisFillActionFactory::run(const QString &fillSource, KisViewManager *view) { KisNodeSP node = view->activeNode(); if (!node || !node->hasEditablePaintDevice()) return; KisSelectionSP selection = view->selection(); QRect selectedRect = selection ? selection->selectedRect() : view->image()->bounds(); Q_UNUSED(selectedRect); KisPaintDeviceSP filled = node->paintDevice()->createCompositionSourceDevice(); Q_UNUSED(filled); bool usePattern = false; bool useBgColor = false; if (fillSource.contains("pattern")) { usePattern = true; } else if (fillSource.contains("bg")) { useBgColor = true; } KisProcessingApplicator applicator(view->image(), node, KisProcessingApplicator::NONE, KisImageSignalVector() << ModifiedSignal, kundo2_i18n("Flood Fill Layer")); KisResourcesSnapshotSP resources = new KisResourcesSnapshot(view->image(), node, view->resourceProvider()->resourceManager()); if (!fillSource.contains("opacity")) { resources->setOpacity(1.0); } KisProcessingVisitorSP visitor = new FillProcessingVisitor(QPoint(0, 0), // start position selection, resources, false, // fast mode usePattern, true, // fill only selection, 0, // feathering radius 0, // sizemod 80, // threshold, false, // unmerged useBgColor); applicator.applyVisitor(visitor, KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE); applicator.end(); } void KisClearActionFactory::run(KisViewManager *view) { // XXX: "Add saving of XML data for Clear action" view->canvasBase()->toolProxy()->deleteSelection(); } void KisImageResizeToSelectionActionFactory::run(KisViewManager *view) { // XXX: "Add saving of XML data for Image Resize To Selection action" KisSelectionSP selection = view->selection(); if (!selection) return; view->image()->cropImage(selection->selectedExactRect()); } void KisCutCopyActionFactory::run(bool willCut, bool makeSharpClip, KisViewManager *view) { KisImageSP image = view->image(); if (!image) return; bool haveShapesSelected = view->selectionManager()->haveShapesSelected(); if (haveShapesSelected) { // XXX: "Add saving of XML data for Cut/Copy of shapes" KisImageBarrierLocker locker(image); if (willCut) { view->canvasBase()->toolProxy()->cut(); } else { view->canvasBase()->toolProxy()->copy(); } } else { KisNodeSP node = view->activeNode(); if (!node) return; KisSelectionSP selection = view->selection(); if (selection.isNull()) return; { KisImageBarrierLocker locker(image); KisPaintDeviceSP dev = node->paintDevice(); if (!dev) { dev = node->projection(); } if (!dev) { view->showFloatingMessage( i18nc("floating message when cannot copy from a node", "Cannot copy pixels from this type of layer "), QIcon(), 3000, KisFloatingMessage::Medium); return; } if (dev->exactBounds().isEmpty()) { view->showFloatingMessage( i18nc("floating message when copying empty selection", "Selection is empty: no pixels were copied "), QIcon(), 3000, KisFloatingMessage::Medium); return; } KisTimeRange range; KisKeyframeChannel *channel = node->getKeyframeChannel(KisKeyframeChannel::Content.id()); if (channel) { const int currentTime = image->animationInterface()->currentTime(); range = channel->affectedFrames(currentTime); } ActionHelper::copyFromDevice(view, dev, makeSharpClip, range); } KUndo2Command *command = 0; if (willCut && node->hasEditablePaintDevice()) { struct ClearSelection : public KisTransactionBasedCommand { ClearSelection(KisNodeSP node, KisSelectionSP sel) : m_node(node), m_sel(sel) {} KisNodeSP m_node; KisSelectionSP m_sel; KUndo2Command* paint() override { KisSelectionSP cutSelection = m_sel; // Shrinking the cutting area was previously used // for getting seamless cut-paste. Now we use makeSharpClip // instead. // QRect originalRect = cutSelection->selectedExactRect(); // static const int preciseSelectionThreshold = 16; // // if (originalRect.width() > preciseSelectionThreshold || // originalRect.height() > preciseSelectionThreshold) { // cutSelection = new KisSelection(*m_sel); // delete cutSelection->flatten(); // // KisSelectionFilter* filter = new KisShrinkSelectionFilter(1, 1, false); // // QRect processingRect = filter->changeRect(originalRect); // filter->process(cutSelection->pixelSelection(), processingRect); // } KisTransaction transaction(m_node->paintDevice()); m_node->paintDevice()->clearSelection(cutSelection); m_node->setDirty(cutSelection->selectedRect()); return transaction.endAndTake(); } }; command = new ClearSelection(node, selection); } KUndo2MagicString actionName = willCut ? kundo2_i18n("Cut") : kundo2_i18n("Copy"); KisProcessingApplicator *ap = beginAction(view, actionName); if (command) { ap->applyCommand(command, KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::NORMAL); } KisOperationConfiguration config(id()); config.setProperty("will-cut", willCut); endAction(ap, config.toXML()); } } void KisCopyMergedActionFactory::run(KisViewManager *view) { KisImageWSP image = view->image(); if (!image) return; if (!view->blockUntilOperationsFinished(image)) return; image->barrierLock(); KisPaintDeviceSP dev = image->root()->projection(); ActionHelper::copyFromDevice(view, dev); image->unlock(); KisProcessingApplicator *ap = beginAction(view, kundo2_i18n("Copy Merged")); endAction(ap, KisOperationConfiguration(id()).toXML()); } void KisPasteNewActionFactory::run(KisViewManager *viewManager) { Q_UNUSED(viewManager); KisPaintDeviceSP clip = KisClipboard::instance()->clip(QRect(), true); if (!clip) return; QRect rect = clip->exactBounds(); if (rect.isEmpty()) return; KisDocument *doc = KisPart::instance()->createDocument(); doc->documentInfo()->setAboutInfo("title", i18n("Untitled")); KisImageSP image = new KisImage(doc->createUndoStore(), rect.width(), rect.height(), clip->colorSpace(), i18n("Pasted")); KisPaintLayerSP layer = new KisPaintLayer(image.data(), image->nextLayerName() + " " + i18n("(pasted)"), OPACITY_OPAQUE_U8, clip->colorSpace()); KisPainter::copyAreaOptimized(QPoint(), clip, layer->paintDevice(), rect); image->addNode(layer.data(), image->rootLayer()); doc->setCurrentImage(image); KisPart::instance()->addDocument(doc); KisMainWindow *win = viewManager->mainWindow(); win->addViewAndNotifyLoadingCompleted(doc); } void KisInvertSelectionOperation::runFromXML(KisViewManager* view, const KisOperationConfiguration& config) { KisSelectionFilter* filter = new KisInvertSelectionFilter(); runFilter(filter, view, config); } void KisSelectionToVectorActionFactory::run(KisViewManager *view) { KisSelectionSP selection = view->selection(); if (selection->hasShapeSelection()) { view->showFloatingMessage(i18nc("floating message", "Selection is already in a vector format "), QIcon(), 2000, KisFloatingMessage::Low); return; } if (!selection->outlineCacheValid()) { view->image()->addSpontaneousJob(new KisUpdateOutlineJob(selection, false, Qt::transparent)); if (!view->blockUntilOperationsFinished(view->image())) { return; } } QPainterPath selectionOutline = selection->outlineCache(); QTransform transform = view->canvasBase()->coordinatesConverter()->imageToDocumentTransform(); KoShape *shape = KoPathShape::createShapeFromPainterPath(transform.map(selectionOutline)); shape->setShapeId(KoPathShapeId); /** * Mark a shape that it belongs to a shape selection */ if(!shape->userData()) { shape->setUserData(new KisShapeSelectionMarker); } KisProcessingApplicator *ap = beginAction(view, kundo2_i18n("Convert to Vector Selection")); ap->applyCommand(view->canvasBase()->shapeController()->addShape(shape, 0), KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE); endAction(ap, KisOperationConfiguration(id()).toXML()); } void KisShapesToVectorSelectionActionFactory::run(KisViewManager* view) { const QList originalShapes = view->canvasBase()->shapeManager()->selection()->selectedShapes(); bool hasSelectionShapes = false; QList clonedShapes; Q_FOREACH (KoShape *shape, originalShapes) { if (dynamic_cast(shape->userData())) { hasSelectionShapes = true; continue; } clonedShapes << shape->cloneShape(); } if (clonedShapes.isEmpty()) { if (hasSelectionShapes) { view->showFloatingMessage(i18nc("floating message", "The shape already belongs to a selection"), QIcon(), 2000, KisFloatingMessage::Low); } return; } KisSelectionToolHelper helper(view->canvasBase(), kundo2_i18n("Convert shapes to vector selection")); helper.addSelectionShapes(clonedShapes); } void KisSelectionToShapeActionFactory::run(KisViewManager *view) { KisSelectionSP selection = view->selection(); if (!selection->outlineCacheValid()) { return; } QPainterPath selectionOutline = selection->outlineCache(); QTransform transform = view->canvasBase()->coordinatesConverter()->imageToDocumentTransform(); KoShape *shape = KoPathShape::createShapeFromPainterPath(transform.map(selectionOutline)); shape->setShapeId(KoPathShapeId); KoColor fgColor = view->canvasBase()->resourceManager()->resource(KoCanvasResourceManager::ForegroundColor).value(); KoShapeStrokeSP border(new KoShapeStroke(1.0, fgColor.toQColor())); shape->setStroke(border); view->document()->shapeController()->addShape(shape); } void KisStrokeSelectionActionFactory::run(KisViewManager *view, StrokeSelectionOptions params) { KisImageWSP image = view->image(); if (!image) { return; } KisSelectionSP selection = view->selection(); if (!selection) { return; } int size = params.lineSize; KisPixelSelectionSP pixelSelection = selection->projection(); if (!pixelSelection->outlineCacheValid()) { pixelSelection->recalculateOutlineCache(); } QPainterPath outline = pixelSelection->outlineCache(); QColor color = params.color.toQColor(); KisNodeSP currentNode = view->resourceProvider()->resourceManager()->resource(KisCanvasResourceProvider::CurrentKritaNode).value(); if (!currentNode->inherits("KisShapeLayer") && currentNode->childCount() == 0) { KoCanvasResourceManager * rManager = view->resourceProvider()->resourceManager(); KisPainter::StrokeStyle strokeStyle = KisPainter::StrokeStyleBrush; KisPainter::FillStyle fillStyle = params.fillStyle(); KisFigurePaintingToolHelper helper(kundo2_i18n("Draw Polyline"), image, currentNode, rManager , strokeStyle, fillStyle); helper.setFGColorOverride(params.color); helper.setSelectionOverride(0); QPen pen(Qt::red, size); pen.setJoinStyle(Qt::RoundJoin); if (fillStyle != KisPainter::FillStyleNone) { helper.paintPainterPathQPenFill(outline, pen, params.fillColor); } else { helper.paintPainterPathQPen(outline, pen, params.fillColor); } } else { QTransform transform = view->canvasBase()->coordinatesConverter()->imageToDocumentTransform(); KoShape *shape = KoPathShape::createShapeFromPainterPath(transform.map(outline)); shape->setShapeId(KoPathShapeId); KoShapeStrokeSP border(new KoShapeStroke(size, color)); shape->setStroke(border); view->document()->shapeController()->addShape(shape); } image->setModified(); } void KisStrokeBrushSelectionActionFactory::run(KisViewManager *view, StrokeSelectionOptions params) { KisImageWSP image = view->image(); if (!image) { return; } KisSelectionSP selection = view->selection(); if (!selection) { return; } KisPixelSelectionSP pixelSelection = selection->projection(); if (!pixelSelection->outlineCacheValid()) { pixelSelection->recalculateOutlineCache(); } KisNodeSP currentNode = view->resourceProvider()->resourceManager()->resource(KisCanvasResourceProvider::CurrentKritaNode).value(); if (!currentNode->inherits("KisShapeLayer") && currentNode->childCount() == 0) { KoCanvasResourceManager * rManager = view->resourceProvider()->resourceManager(); QPainterPath outline = pixelSelection->outlineCache(); KisPainter::StrokeStyle strokeStyle = KisPainter::StrokeStyleBrush; KisPainter::FillStyle fillStyle = KisPainter::FillStyleNone; KoColor color = params.color; KisFigurePaintingToolHelper helper(kundo2_i18n("Draw Polyline"), image, currentNode, rManager , strokeStyle, fillStyle); helper.setFGColorOverride(color); helper.setSelectionOverride(0); helper.paintPainterPath(outline); image->setModified(); } } diff --git a/libs/ui/forms/wdgrectangleconstraints.ui b/libs/ui/forms/wdgrectangleconstraints.ui index c232202b2f..ccb9d1b86d 100644 --- a/libs/ui/forms/wdgrectangleconstraints.ui +++ b/libs/ui/forms/wdgrectangleconstraints.ui @@ -1,198 +1,266 @@ WdgRectangleConstraints 0 0 - 230 - 149 + 243 + 328 0 0 Size - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - + + 7 7 7 7 7 true true true + + + 0 + 0 + + Height px 99999 Width: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + 0 + 0 + + Aspect ratio 10000.000000000000000 0.100000000000000 + + + 0 + 0 + + Width px 99999 Ratio: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Height: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - Qt::Horizontal - - - - 40 - 20 - - - + + + + + + Round X: + + + + + + + + 0 + 0 + + + + Height + + + px + + + 99999 + + + + + + + + 0 + 0 + + + + + 24 + 24 + + + + + + + + Round Y: + + + + + + + + 0 + 0 + + + + Height + + + px + + + 99999 + + + + - + Qt::Vertical 20 - 40 + 187 KisIntParseSpinBox QSpinBox
kis_int_parse_spin_box.h
KisDoubleParseSpinBox QDoubleSpinBox
kis_double_parse_spin_box.h
+ + KoAspectButton + QWidget +
KoAspectButton.h
+ 1 +
diff --git a/libs/ui/kis_config.cc b/libs/ui/kis_config.cc index 9b224e836e..f0cafa1175 100644 --- a/libs/ui/kis_config.cc +++ b/libs/ui/kis_config.cc @@ -1,1996 +1,2006 @@ /* * Copyright (c) 2002 Patrick Julien * * 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_config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_canvas_resource_provider.h" #include "kis_config_notifier.h" #include "kis_snap_config.h" #include #include KisConfig::KisConfig(bool readOnly) : m_cfg( KSharedConfig::openConfig()->group("")) , m_readOnly(readOnly) { if (!readOnly) { KIS_SAFE_ASSERT_RECOVER_RETURN(qApp->thread() == QThread::currentThread()); } } KisConfig::~KisConfig() { if (m_readOnly) return; if (qApp->thread() != QThread::currentThread()) { dbgKrita << "WARNING: KisConfig: requested config synchronization from nonGUI thread! Called from:" << kisBacktrace(); return; } m_cfg.sync(); } bool KisConfig::disableTouchOnCanvas(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("disableTouchOnCanvas", false)); } void KisConfig::setDisableTouchOnCanvas(bool value) const { m_cfg.writeEntry("disableTouchOnCanvas", value); } bool KisConfig::useProjections(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("useProjections", true)); } void KisConfig::setUseProjections(bool useProj) const { m_cfg.writeEntry("useProjections", useProj); } bool KisConfig::undoEnabled(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("undoEnabled", true)); } void KisConfig::setUndoEnabled(bool undo) const { m_cfg.writeEntry("undoEnabled", undo); } int KisConfig::undoStackLimit(bool defaultValue) const { return (defaultValue ? 30 : m_cfg.readEntry("undoStackLimit", 30)); } void KisConfig::setUndoStackLimit(int limit) const { m_cfg.writeEntry("undoStackLimit", limit); } bool KisConfig::useCumulativeUndoRedo(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("useCumulativeUndoRedo",false)); } void KisConfig::setCumulativeUndoRedo(bool value) { m_cfg.writeEntry("useCumulativeUndoRedo", value); } qreal KisConfig::stackT1(bool defaultValue) const { return (defaultValue ? 5 : m_cfg.readEntry("stackT1",5)); } void KisConfig::setStackT1(int T1) { m_cfg.writeEntry("stackT1", T1); } qreal KisConfig::stackT2(bool defaultValue) const { return (defaultValue ? 1 : m_cfg.readEntry("stackT2",1)); } void KisConfig::setStackT2(int T2) { m_cfg.writeEntry("stackT2", T2); } int KisConfig::stackN(bool defaultValue) const { return (defaultValue ? 5 : m_cfg.readEntry("stackN",5)); } void KisConfig::setStackN(int N) { m_cfg.writeEntry("stackN", N); } qint32 KisConfig::defImageWidth(bool defaultValue) const { return (defaultValue ? 1600 : m_cfg.readEntry("imageWidthDef", 1600)); } qint32 KisConfig::defImageHeight(bool defaultValue) const { return (defaultValue ? 1200 : m_cfg.readEntry("imageHeightDef", 1200)); } qreal KisConfig::defImageResolution(bool defaultValue) const { return (defaultValue ? 100.0 : m_cfg.readEntry("imageResolutionDef", 100.0)) / 72.0; } QString KisConfig::defColorModel(bool defaultValue) const { return (defaultValue ? KoColorSpaceRegistry::instance()->rgb8()->colorModelId().id() : m_cfg.readEntry("colorModelDef", KoColorSpaceRegistry::instance()->rgb8()->colorModelId().id())); } void KisConfig::defColorModel(const QString & model) const { m_cfg.writeEntry("colorModelDef", model); } QString KisConfig::defaultColorDepth(bool defaultValue) const { return (defaultValue ? KoColorSpaceRegistry::instance()->rgb8()->colorDepthId().id() : m_cfg.readEntry("colorDepthDef", KoColorSpaceRegistry::instance()->rgb8()->colorDepthId().id())); } void KisConfig::setDefaultColorDepth(const QString & depth) const { m_cfg.writeEntry("colorDepthDef", depth); } QString KisConfig::defColorProfile(bool defaultValue) const { return (defaultValue ? KoColorSpaceRegistry::instance()->rgb8()->profile()->name() : m_cfg.readEntry("colorProfileDef", KoColorSpaceRegistry::instance()->rgb8()->profile()->name())); } void KisConfig::defColorProfile(const QString & profile) const { m_cfg.writeEntry("colorProfileDef", profile); } void KisConfig::defImageWidth(qint32 width) const { m_cfg.writeEntry("imageWidthDef", width); } void KisConfig::defImageHeight(qint32 height) const { m_cfg.writeEntry("imageHeightDef", height); } void KisConfig::defImageResolution(qreal res) const { m_cfg.writeEntry("imageResolutionDef", res*72.0); } int KisConfig::preferredVectorImportResolutionPPI(bool defaultValue) const { return defaultValue ? 100.0 : m_cfg.readEntry("preferredVectorImportResolution", 100.0); } void KisConfig::setPreferredVectorImportResolutionPPI(int value) const { m_cfg.writeEntry("preferredVectorImportResolution", value); } void cleanOldCursorStyleKeys(KConfigGroup &cfg) { if (cfg.hasKey("newCursorStyle") && cfg.hasKey("newOutlineStyle")) { cfg.deleteEntry("cursorStyleDef"); } } CursorStyle KisConfig::newCursorStyle(bool defaultValue) const { if (defaultValue) { return CURSOR_STYLE_NO_CURSOR; } int style = m_cfg.readEntry("newCursorStyle", int(-1)); if (style < 0) { // old style format style = m_cfg.readEntry("cursorStyleDef", int(OLD_CURSOR_STYLE_OUTLINE)); switch (style) { case OLD_CURSOR_STYLE_TOOLICON: style = CURSOR_STYLE_TOOLICON; break; case OLD_CURSOR_STYLE_CROSSHAIR: case OLD_CURSOR_STYLE_OUTLINE_CENTER_CROSS: style = CURSOR_STYLE_CROSSHAIR; break; case OLD_CURSOR_STYLE_POINTER: style = CURSOR_STYLE_POINTER; break; case OLD_CURSOR_STYLE_OUTLINE: case OLD_CURSOR_STYLE_NO_CURSOR: style = CURSOR_STYLE_NO_CURSOR; break; case OLD_CURSOR_STYLE_SMALL_ROUND: case OLD_CURSOR_STYLE_OUTLINE_CENTER_DOT: style = CURSOR_STYLE_SMALL_ROUND; break; case OLD_CURSOR_STYLE_TRIANGLE_RIGHTHANDED: case OLD_CURSOR_STYLE_OUTLINE_TRIANGLE_RIGHTHANDED: style = CURSOR_STYLE_TRIANGLE_RIGHTHANDED; break; case OLD_CURSOR_STYLE_TRIANGLE_LEFTHANDED: case OLD_CURSOR_STYLE_OUTLINE_TRIANGLE_LEFTHANDED: style = CURSOR_STYLE_TRIANGLE_LEFTHANDED; break; default: style = -1; } } cleanOldCursorStyleKeys(m_cfg); // compatibility with future versions if (style < 0 || style >= N_CURSOR_STYLE_SIZE) { style = CURSOR_STYLE_NO_CURSOR; } return (CursorStyle) style; } void KisConfig::setNewCursorStyle(CursorStyle style) { m_cfg.writeEntry("newCursorStyle", (int)style); } QColor KisConfig::getCursorMainColor(bool defaultValue) const { QColor col; col.setRgbF(0.501961, 1.0, 0.501961); return (defaultValue ? col : m_cfg.readEntry("cursorMaincColor", col)); } void KisConfig::setCursorMainColor(const QColor &v) const { m_cfg.writeEntry("cursorMaincColor", v); } OutlineStyle KisConfig::newOutlineStyle(bool defaultValue) const { if (defaultValue) { return OUTLINE_FULL; } int style = m_cfg.readEntry("newOutlineStyle", int(-1)); if (style < 0) { // old style format style = m_cfg.readEntry("cursorStyleDef", int(OLD_CURSOR_STYLE_OUTLINE)); switch (style) { case OLD_CURSOR_STYLE_TOOLICON: case OLD_CURSOR_STYLE_CROSSHAIR: case OLD_CURSOR_STYLE_POINTER: case OLD_CURSOR_STYLE_NO_CURSOR: case OLD_CURSOR_STYLE_SMALL_ROUND: case OLD_CURSOR_STYLE_TRIANGLE_RIGHTHANDED: case OLD_CURSOR_STYLE_TRIANGLE_LEFTHANDED: style = OUTLINE_NONE; break; case OLD_CURSOR_STYLE_OUTLINE: case OLD_CURSOR_STYLE_OUTLINE_CENTER_DOT: case OLD_CURSOR_STYLE_OUTLINE_CENTER_CROSS: case OLD_CURSOR_STYLE_OUTLINE_TRIANGLE_RIGHTHANDED: case OLD_CURSOR_STYLE_OUTLINE_TRIANGLE_LEFTHANDED: style = OUTLINE_FULL; break; default: style = -1; } } cleanOldCursorStyleKeys(m_cfg); // compatibility with future versions if (style < 0 || style >= N_OUTLINE_STYLE_SIZE) { style = OUTLINE_FULL; } return (OutlineStyle) style; } void KisConfig::setNewOutlineStyle(OutlineStyle style) { m_cfg.writeEntry("newOutlineStyle", (int)style); } QRect KisConfig::colorPreviewRect() const { return m_cfg.readEntry("colorPreviewRect", QVariant(QRect(32, 32, 48, 48))).toRect(); } void KisConfig::setColorPreviewRect(const QRect &rect) { m_cfg.writeEntry("colorPreviewRect", QVariant(rect)); } bool KisConfig::useDirtyPresets(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("useDirtyPresets",false)); } void KisConfig::setUseDirtyPresets(bool value) { m_cfg.writeEntry("useDirtyPresets",value); KisConfigNotifier::instance()->notifyConfigChanged(); } bool KisConfig::useEraserBrushSize(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("useEraserBrushSize",false)); } void KisConfig::setUseEraserBrushSize(bool value) { m_cfg.writeEntry("useEraserBrushSize",value); KisConfigNotifier::instance()->notifyConfigChanged(); } bool KisConfig::useEraserBrushOpacity(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("useEraserBrushOpacity",false)); } void KisConfig::setUseEraserBrushOpacity(bool value) { m_cfg.writeEntry("useEraserBrushOpacity",value); KisConfigNotifier::instance()->notifyConfigChanged(); } QColor KisConfig::getMDIBackgroundColor(bool defaultValue) const { QColor col(77, 77, 77); return (defaultValue ? col : m_cfg.readEntry("mdiBackgroundColor", col)); } void KisConfig::setMDIBackgroundColor(const QColor &v) const { m_cfg.writeEntry("mdiBackgroundColor", v); } QString KisConfig::getMDIBackgroundImage(bool defaultValue) const { return (defaultValue ? "" : m_cfg.readEntry("mdiBackgroundImage", "")); } void KisConfig::setMDIBackgroundImage(const QString &filename) const { m_cfg.writeEntry("mdiBackgroundImage", filename); } QString KisConfig::monitorProfile(int screen) const { // Note: keep this in sync with the default profile for the RGB colorspaces! QString profile = m_cfg.readEntry("monitorProfile" + QString(screen == 0 ? "": QString("_%1").arg(screen)), "sRGB-elle-V2-srgbtrc.icc"); //dbgKrita << "KisConfig::monitorProfile()" << profile; return profile; } QString KisConfig::monitorForScreen(int screen, const QString &defaultMonitor, bool defaultValue) const { return (defaultValue ? defaultMonitor : m_cfg.readEntry(QString("monitor_for_screen_%1").arg(screen), defaultMonitor)); } void KisConfig::setMonitorForScreen(int screen, const QString& monitor) { m_cfg.writeEntry(QString("monitor_for_screen_%1").arg(screen), monitor); } void KisConfig::setMonitorProfile(int screen, const QString & monitorProfile, bool override) const { m_cfg.writeEntry("monitorProfile/OverrideX11", override); m_cfg.writeEntry("monitorProfile" + QString(screen == 0 ? "": QString("_%1").arg(screen)), monitorProfile); } const KoColorProfile *KisConfig::getScreenProfile(int screen) { if (screen < 0) return 0; KisConfig cfg(true); QString monitorId; if (KisColorManager::instance()->devices().size() > screen) { monitorId = cfg.monitorForScreen(screen, KisColorManager::instance()->devices()[screen]); } //dbgKrita << "getScreenProfile(). Screen" << screen << "monitor id" << monitorId; if (monitorId.isEmpty()) { return 0; } QByteArray bytes = KisColorManager::instance()->displayProfile(monitorId); //dbgKrita << "\tgetScreenProfile()" << bytes.size(); if (bytes.length() > 0) { const KoColorProfile *profile = KoColorSpaceRegistry::instance()->createColorProfile(RGBAColorModelID.id(), Integer8BitsColorDepthID.id(), bytes); //dbgKrita << "\tKisConfig::getScreenProfile for screen" << screen << profile->name(); return profile; } else { //dbgKrita << "\tCould not get a system monitor profile"; return 0; } } const KoColorProfile *KisConfig::displayProfile(int screen) const { if (screen < 0) return 0; // if the user plays with the settings, they can override the display profile, in which case // we don't want the system setting. bool override = useSystemMonitorProfile(); //dbgKrita << "KisConfig::displayProfile(). Override X11:" << override; const KoColorProfile *profile = 0; if (override) { //dbgKrita << "\tGoing to get the screen profile"; profile = KisConfig::getScreenProfile(screen); } // if it fails. check the configuration if (!profile || !profile->isSuitableForDisplay()) { //dbgKrita << "\tGoing to get the monitor profile"; QString monitorProfileName = monitorProfile(screen); //dbgKrita << "\t\tmonitorProfileName:" << monitorProfileName; if (!monitorProfileName.isEmpty()) { profile = KoColorSpaceRegistry::instance()->profileByName(monitorProfileName); } if (profile) { //dbgKrita << "\t\tsuitable for display" << profile->isSuitableForDisplay(); } else { //dbgKrita << "\t\tstill no profile"; } } // if we still don't have a profile, or the profile isn't suitable for display, // we need to get a last-resort profile. the built-in sRGB is a good choice then. if (!profile || !profile->isSuitableForDisplay()) { //dbgKrita << "\tnothing worked, going to get sRGB built-in"; profile = KoColorSpaceRegistry::instance()->profileByName("sRGB Built-in"); } if (profile) { //dbgKrita << "\tKisConfig::displayProfile for screen" << screen << "is" << profile->name(); } else { //dbgKrita << "\tCouldn't get a display profile at all"; } return profile; } QString KisConfig::workingColorSpace(bool defaultValue) const { return (defaultValue ? "RGBA" : m_cfg.readEntry("workingColorSpace", "RGBA")); } void KisConfig::setWorkingColorSpace(const QString & workingColorSpace) const { m_cfg.writeEntry("workingColorSpace", workingColorSpace); } QString KisConfig::printerColorSpace(bool /*defaultValue*/) const { //TODO currently only rgb8 is supported //return (defaultValue ? "RGBA" : m_cfg.readEntry("printerColorSpace", "RGBA")); return QString("RGBA"); } void KisConfig::setPrinterColorSpace(const QString & printerColorSpace) const { m_cfg.writeEntry("printerColorSpace", printerColorSpace); } QString KisConfig::printerProfile(bool defaultValue) const { return (defaultValue ? "" : m_cfg.readEntry("printerProfile", "")); } void KisConfig::setPrinterProfile(const QString & printerProfile) const { m_cfg.writeEntry("printerProfile", printerProfile); } bool KisConfig::useBlackPointCompensation(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("useBlackPointCompensation", true)); } void KisConfig::setUseBlackPointCompensation(bool useBlackPointCompensation) const { m_cfg.writeEntry("useBlackPointCompensation", useBlackPointCompensation); } bool KisConfig::allowLCMSOptimization(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("allowLCMSOptimization", true)); } void KisConfig::setAllowLCMSOptimization(bool allowLCMSOptimization) { m_cfg.writeEntry("allowLCMSOptimization", allowLCMSOptimization); } bool KisConfig::showRulers(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("showrulers", false)); } void KisConfig::setShowRulers(bool rulers) const { m_cfg.writeEntry("showrulers", rulers); } bool KisConfig::forceShowSaveMessages(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("forceShowSaveMessages", false)); } void KisConfig::setForceShowSaveMessages(bool value) const { m_cfg.writeEntry("forceShowSaveMessages", value); } bool KisConfig::forceShowAutosaveMessages(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("forceShowAutosaveMessages", false)); } void KisConfig::setForceShowAutosaveMessages(bool value) const { m_cfg.writeEntry("forceShowAutosaveMessages", value); } bool KisConfig::rulersTrackMouse(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("rulersTrackMouse", true)); } void KisConfig::setRulersTrackMouse(bool value) const { m_cfg.writeEntry("rulersTrackMouse", value); } qint32 KisConfig::pasteBehaviour(bool defaultValue) const { return (defaultValue ? 2 : m_cfg.readEntry("pasteBehaviour", 2)); } void KisConfig::setPasteBehaviour(qint32 renderIntent) const { m_cfg.writeEntry("pasteBehaviour", renderIntent); } qint32 KisConfig::monitorRenderIntent(bool defaultValue) const { qint32 intent = m_cfg.readEntry("renderIntent", INTENT_PERCEPTUAL); if (intent > 3) intent = 3; if (intent < 0) intent = 0; return (defaultValue ? INTENT_PERCEPTUAL : intent); } void KisConfig::setRenderIntent(qint32 renderIntent) const { if (renderIntent > 3) renderIntent = 3; if (renderIntent < 0) renderIntent = 0; m_cfg.writeEntry("renderIntent", renderIntent); } bool KisConfig::useOpenGL(bool defaultValue) const { if (defaultValue) { return true; } //dbgKrita << "use opengl" << m_cfg.readEntry("useOpenGL", true) << "success" << m_cfg.readEntry("canvasState", "OPENGL_SUCCESS"); QString cs = canvasState(); #ifdef Q_OS_WIN return (m_cfg.readEntry("useOpenGLWindows", true) && (cs == "OPENGL_SUCCESS" || cs == "TRY_OPENGL")); #else return (m_cfg.readEntry("useOpenGL", true) && (cs == "OPENGL_SUCCESS" || cs == "TRY_OPENGL")); #endif } void KisConfig::setUseOpenGL(bool useOpenGL) const { #ifdef Q_OS_WIN m_cfg.writeEntry("useOpenGLWindows", useOpenGL); #else m_cfg.writeEntry("useOpenGL", useOpenGL); #endif } int KisConfig::openGLFilteringMode(bool defaultValue) const { return (defaultValue ? 3 : m_cfg.readEntry("OpenGLFilterMode", 3)); } void KisConfig::setOpenGLFilteringMode(int filteringMode) { m_cfg.writeEntry("OpenGLFilterMode", filteringMode); } bool KisConfig::useOpenGLTextureBuffer(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("useOpenGLTextureBuffer", true)); } void KisConfig::setUseOpenGLTextureBuffer(bool useBuffer) { m_cfg.writeEntry("useOpenGLTextureBuffer", useBuffer); } int KisConfig::openGLTextureSize(bool defaultValue) const { return (defaultValue ? 256 : m_cfg.readEntry("textureSize", 256)); } bool KisConfig::disableVSync(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("disableVSync", true)); } void KisConfig::setDisableVSync(bool disableVSync) { m_cfg.writeEntry("disableVSync", disableVSync); } bool KisConfig::showAdvancedOpenGLSettings(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("showAdvancedOpenGLSettings", false)); } bool KisConfig::forceOpenGLFenceWorkaround(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("forceOpenGLFenceWorkaround", false)); } int KisConfig::numMipmapLevels(bool defaultValue) const { return (defaultValue ? 4 : m_cfg.readEntry("numMipmapLevels", 4)); } int KisConfig::textureOverlapBorder() const { return 1 << qMax(0, numMipmapLevels()); } quint32 KisConfig::getGridMainStyle(bool defaultValue) const { int v = m_cfg.readEntry("gridmainstyle", 0); v = qBound(0, v, 2); return (defaultValue ? 0 : v); } void KisConfig::setGridMainStyle(quint32 v) const { m_cfg.writeEntry("gridmainstyle", v); } quint32 KisConfig::getGridSubdivisionStyle(bool defaultValue) const { quint32 v = m_cfg.readEntry("gridsubdivisionstyle", 1); if (v > 2) v = 2; return (defaultValue ? 1 : v); } void KisConfig::setGridSubdivisionStyle(quint32 v) const { m_cfg.writeEntry("gridsubdivisionstyle", v); } QColor KisConfig::getGridMainColor(bool defaultValue) const { QColor col(99, 99, 99); return (defaultValue ? col : m_cfg.readEntry("gridmaincolor", col)); } void KisConfig::setGridMainColor(const QColor & v) const { m_cfg.writeEntry("gridmaincolor", v); } QColor KisConfig::getGridSubdivisionColor(bool defaultValue) const { QColor col(150, 150, 150); return (defaultValue ? col : m_cfg.readEntry("gridsubdivisioncolor", col)); } void KisConfig::setGridSubdivisionColor(const QColor & v) const { m_cfg.writeEntry("gridsubdivisioncolor", v); } QColor KisConfig::getPixelGridColor(bool defaultValue) const { QColor col(255, 255, 255); return (defaultValue ? col : m_cfg.readEntry("pixelGridColor", col)); } void KisConfig::setPixelGridColor(const QColor & v) const { m_cfg.writeEntry("pixelGridColor", v); } qreal KisConfig::getPixelGridDrawingThreshold(bool defaultValue) const { qreal border = 24.0f; return (defaultValue ? border : m_cfg.readEntry("pixelGridDrawingThreshold", border)); } void KisConfig::setPixelGridDrawingThreshold(qreal v) const { m_cfg.writeEntry("pixelGridDrawingThreshold", v); } bool KisConfig::pixelGridEnabled(bool defaultValue) const { bool enabled = true; return (defaultValue ? enabled : m_cfg.readEntry("pixelGridEnabled", enabled)); } void KisConfig::enablePixelGrid(bool v) const { m_cfg.writeEntry("pixelGridEnabled", v); } quint32 KisConfig::guidesLineStyle(bool defaultValue) const { int v = m_cfg.readEntry("guidesLineStyle", 0); v = qBound(0, v, 2); return (defaultValue ? 0 : v); } void KisConfig::setGuidesLineStyle(quint32 v) const { m_cfg.writeEntry("guidesLineStyle", v); } QColor KisConfig::guidesColor(bool defaultValue) const { QColor col(99, 99, 99); return (defaultValue ? col : m_cfg.readEntry("guidesColor", col)); } void KisConfig::setGuidesColor(const QColor & v) const { m_cfg.writeEntry("guidesColor", v); } void KisConfig::loadSnapConfig(KisSnapConfig *config, bool defaultValue) const { KisSnapConfig defaultConfig(false); if (defaultValue) { *config = defaultConfig; return; } config->setOrthogonal(m_cfg.readEntry("globalSnapOrthogonal", defaultConfig.orthogonal())); config->setNode(m_cfg.readEntry("globalSnapNode", defaultConfig.node())); config->setExtension(m_cfg.readEntry("globalSnapExtension", defaultConfig.extension())); config->setIntersection(m_cfg.readEntry("globalSnapIntersection", defaultConfig.intersection())); config->setBoundingBox(m_cfg.readEntry("globalSnapBoundingBox", defaultConfig.boundingBox())); config->setImageBounds(m_cfg.readEntry("globalSnapImageBounds", defaultConfig.imageBounds())); config->setImageCenter(m_cfg.readEntry("globalSnapImageCenter", defaultConfig.imageCenter())); } void KisConfig::saveSnapConfig(const KisSnapConfig &config) { m_cfg.writeEntry("globalSnapOrthogonal", config.orthogonal()); m_cfg.writeEntry("globalSnapNode", config.node()); m_cfg.writeEntry("globalSnapExtension", config.extension()); m_cfg.writeEntry("globalSnapIntersection", config.intersection()); m_cfg.writeEntry("globalSnapBoundingBox", config.boundingBox()); m_cfg.writeEntry("globalSnapImageBounds", config.imageBounds()); m_cfg.writeEntry("globalSnapImageCenter", config.imageCenter()); } qint32 KisConfig::checkSize(bool defaultValue) const { return (defaultValue ? 32 : m_cfg.readEntry("checksize", 32)); } void KisConfig::setCheckSize(qint32 checksize) const { m_cfg.writeEntry("checksize", checksize); } bool KisConfig::scrollCheckers(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("scrollingcheckers", false)); } void KisConfig::setScrollingCheckers(bool sc) const { m_cfg.writeEntry("scrollingcheckers", sc); } QColor KisConfig::canvasBorderColor(bool defaultValue) const { QColor color(QColor(128,128,128)); return (defaultValue ? color : m_cfg.readEntry("canvasBorderColor", color)); } void KisConfig::setCanvasBorderColor(const QColor& color) const { m_cfg.writeEntry("canvasBorderColor", color); } bool KisConfig::hideScrollbars(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("hideScrollbars", false)); } void KisConfig::setHideScrollbars(bool value) const { m_cfg.writeEntry("hideScrollbars", value); } QColor KisConfig::checkersColor1(bool defaultValue) const { QColor col(220, 220, 220); return (defaultValue ? col : m_cfg.readEntry("checkerscolor", col)); } void KisConfig::setCheckersColor1(const QColor & v) const { m_cfg.writeEntry("checkerscolor", v); } QColor KisConfig::checkersColor2(bool defaultValue) const { return (defaultValue ? QColor(Qt::white) : m_cfg.readEntry("checkerscolor2", QColor(Qt::white))); } void KisConfig::setCheckersColor2(const QColor & v) const { m_cfg.writeEntry("checkerscolor2", v); } bool KisConfig::antialiasCurves(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("antialiascurves", true)); } void KisConfig::setAntialiasCurves(bool v) const { m_cfg.writeEntry("antialiascurves", v); } bool KisConfig::antialiasSelectionOutline(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("AntialiasSelectionOutline", false)); } void KisConfig::setAntialiasSelectionOutline(bool v) const { m_cfg.writeEntry("AntialiasSelectionOutline", v); } bool KisConfig::showRootLayer(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("ShowRootLayer", false)); } void KisConfig::setShowRootLayer(bool showRootLayer) const { m_cfg.writeEntry("ShowRootLayer", showRootLayer); } bool KisConfig::showGlobalSelection(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("ShowGlobalSelection", false)); } void KisConfig::setShowGlobalSelection(bool showGlobalSelection) const { m_cfg.writeEntry("ShowGlobalSelection", showGlobalSelection); } bool KisConfig::showOutlineWhilePainting(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("ShowOutlineWhilePainting", true)); } void KisConfig::setShowOutlineWhilePainting(bool showOutlineWhilePainting) const { m_cfg.writeEntry("ShowOutlineWhilePainting", showOutlineWhilePainting); } bool KisConfig::forceAlwaysFullSizedOutline(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("forceAlwaysFullSizedOutline", false)); } void KisConfig::setForceAlwaysFullSizedOutline(bool value) const { m_cfg.writeEntry("forceAlwaysFullSizedOutline", value); } KisConfig::SessionOnStartup KisConfig::sessionOnStartup(bool defaultValue) const { int value = defaultValue ? SOS_BlankSession : m_cfg.readEntry("sessionOnStartup", (int)SOS_BlankSession); return (KisConfig::SessionOnStartup)value; } void KisConfig::setSessionOnStartup(SessionOnStartup value) { m_cfg.writeEntry("sessionOnStartup", (int)value); } bool KisConfig::saveSessionOnQuit(bool defaultValue) const { return defaultValue ? false : m_cfg.readEntry("saveSessionOnQuit", false); } void KisConfig::setSaveSessionOnQuit(bool value) { m_cfg.writeEntry("saveSessionOnQuit", value); } qreal KisConfig::outlineSizeMinimum(bool defaultValue) const { return (defaultValue ? 1.0 : m_cfg.readEntry("OutlineSizeMinimum", 1.0)); } void KisConfig::setOutlineSizeMinimum(qreal outlineSizeMinimum) const { m_cfg.writeEntry("OutlineSizeMinimum", outlineSizeMinimum); } qreal KisConfig::selectionViewSizeMinimum(bool defaultValue) const { return (defaultValue ? 5.0 : m_cfg.readEntry("SelectionViewSizeMinimum", 5.0)); } void KisConfig::setSelectionViewSizeMinimum(qreal outlineSizeMinimum) const { m_cfg.writeEntry("SelectionViewSizeMinimum", outlineSizeMinimum); } int KisConfig::autoSaveInterval(bool defaultValue) const { return (defaultValue ? 15 * 60 : m_cfg.readEntry("AutoSaveInterval", 15 * 60)); } void KisConfig::setAutoSaveInterval(int seconds) const { return m_cfg.writeEntry("AutoSaveInterval", seconds); } bool KisConfig::backupFile(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("CreateBackupFile", true)); } void KisConfig::setBackupFile(bool backupFile) const { m_cfg.writeEntry("CreateBackupFile", backupFile); } bool KisConfig::showFilterGallery(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("showFilterGallery", false)); } void KisConfig::setShowFilterGallery(bool showFilterGallery) const { m_cfg.writeEntry("showFilterGallery", showFilterGallery); } bool KisConfig::showFilterGalleryLayerMaskDialog(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("showFilterGalleryLayerMaskDialog", true)); } void KisConfig::setShowFilterGalleryLayerMaskDialog(bool showFilterGallery) const { m_cfg.writeEntry("setShowFilterGalleryLayerMaskDialog", showFilterGallery); } QString KisConfig::canvasState(bool defaultValue) const { const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); QSettings kritarc(configPath + QStringLiteral("/kritadisplayrc"), QSettings::IniFormat); return (defaultValue ? "OPENGL_NOT_TRIED" : kritarc.value("canvasState", "OPENGL_NOT_TRIED").toString()); } void KisConfig::setCanvasState(const QString& state) const { static QStringList acceptableStates; if (acceptableStates.isEmpty()) { acceptableStates << "OPENGL_SUCCESS" << "TRY_OPENGL" << "OPENGL_NOT_TRIED" << "OPENGL_FAILED"; } if (acceptableStates.contains(state)) { const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); QSettings kritarc(configPath + QStringLiteral("/kritadisplayrc"), QSettings::IniFormat); kritarc.setValue("canvasState", state); } } bool KisConfig::toolOptionsPopupDetached(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("ToolOptionsPopupDetached", false)); } void KisConfig::setToolOptionsPopupDetached(bool detached) const { m_cfg.writeEntry("ToolOptionsPopupDetached", detached); } bool KisConfig::paintopPopupDetached(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("PaintopPopupDetached", false)); } void KisConfig::setPaintopPopupDetached(bool detached) const { m_cfg.writeEntry("PaintopPopupDetached", detached); } QString KisConfig::pressureTabletCurve(bool defaultValue) const { return (defaultValue ? "0,0;1,1" : m_cfg.readEntry("tabletPressureCurve","0,0;1,1;")); } void KisConfig::setPressureTabletCurve(const QString& curveString) const { m_cfg.writeEntry("tabletPressureCurve", curveString); } bool KisConfig::useWin8PointerInput(bool defaultValue) const { #ifdef Q_OS_WIN return (defaultValue ? false : m_cfg.readEntry("useWin8PointerInput", false)); #else Q_UNUSED(defaultValue); return false; #endif } void KisConfig::setUseWin8PointerInput(bool value) const { #ifdef Q_OS_WIN // Special handling: Only set value if changed // I don't want it to be set if the user hasn't touched it if (useWin8PointerInput() != value) { m_cfg.writeEntry("useWin8PointerInput", value); } #else Q_UNUSED(value) #endif } qreal KisConfig::vastScrolling(bool defaultValue) const { return (defaultValue ? 0.9 : m_cfg.readEntry("vastScrolling", 0.9)); } void KisConfig::setVastScrolling(const qreal factor) const { m_cfg.writeEntry("vastScrolling", factor); } int KisConfig::presetChooserViewMode(bool defaultValue) const { return (defaultValue ? 0 : m_cfg.readEntry("presetChooserViewMode", 0)); } void KisConfig::setPresetChooserViewMode(const int mode) const { m_cfg.writeEntry("presetChooserViewMode", mode); } int KisConfig::presetIconSize(bool defaultValue) const { return (defaultValue ? 60 : m_cfg.readEntry("presetIconSize", 60)); } void KisConfig::setPresetIconSize(const int value) const { m_cfg.writeEntry("presetIconSize", value); } bool KisConfig::firstRun(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("firstRun", true)); } void KisConfig::setFirstRun(const bool first) const { m_cfg.writeEntry("firstRun", first); } int KisConfig::horizontalSplitLines(bool defaultValue) const { return (defaultValue ? 1 : m_cfg.readEntry("horizontalSplitLines", 1)); } void KisConfig::setHorizontalSplitLines(const int numberLines) const { m_cfg.writeEntry("horizontalSplitLines", numberLines); } int KisConfig::verticalSplitLines(bool defaultValue) const { return (defaultValue ? 1 : m_cfg.readEntry("verticalSplitLines", 1)); } void KisConfig::setVerticalSplitLines(const int numberLines) const { m_cfg.writeEntry("verticalSplitLines", numberLines); } bool KisConfig::clicklessSpacePan(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("clicklessSpacePan", true)); } void KisConfig::setClicklessSpacePan(const bool toggle) const { m_cfg.writeEntry("clicklessSpacePan", toggle); } bool KisConfig::hideDockersFullscreen(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("hideDockersFullScreen", true)); } void KisConfig::setHideDockersFullscreen(const bool value) const { m_cfg.writeEntry("hideDockersFullScreen", value); } bool KisConfig::showDockers(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("showDockers", true)); } void KisConfig::setShowDockers(const bool value) const { m_cfg.writeEntry("showDockers", value); } bool KisConfig::showStatusBar(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("showStatusBar", true)); } void KisConfig::setShowStatusBar(const bool value) const { m_cfg.writeEntry("showStatusBar", value); } bool KisConfig::hideMenuFullscreen(bool defaultValue) const { return (defaultValue ? true: m_cfg.readEntry("hideMenuFullScreen", true)); } void KisConfig::setHideMenuFullscreen(const bool value) const { m_cfg.writeEntry("hideMenuFullScreen", value); } bool KisConfig::hideScrollbarsFullscreen(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("hideScrollbarsFullScreen", true)); } void KisConfig::setHideScrollbarsFullscreen(const bool value) const { m_cfg.writeEntry("hideScrollbarsFullScreen", value); } bool KisConfig::hideStatusbarFullscreen(bool defaultValue) const { return (defaultValue ? true: m_cfg.readEntry("hideStatusbarFullScreen", true)); } void KisConfig::setHideStatusbarFullscreen(const bool value) const { m_cfg.writeEntry("hideStatusbarFullScreen", value); } bool KisConfig::hideTitlebarFullscreen(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("hideTitleBarFullscreen", true)); } void KisConfig::setHideTitlebarFullscreen(const bool value) const { m_cfg.writeEntry("hideTitleBarFullscreen", value); } bool KisConfig::hideToolbarFullscreen(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("hideToolbarFullscreen", true)); } void KisConfig::setHideToolbarFullscreen(const bool value) const { m_cfg.writeEntry("hideToolbarFullscreen", value); } bool KisConfig::fullscreenMode(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("fullscreenMode", true)); } void KisConfig::setFullscreenMode(const bool value) const { m_cfg.writeEntry("fullscreenMode", value); } QStringList KisConfig::favoriteCompositeOps(bool defaultValue) const { return (defaultValue ? QStringList() : m_cfg.readEntry("favoriteCompositeOps", QStringList())); } void KisConfig::setFavoriteCompositeOps(const QStringList& compositeOps) const { m_cfg.writeEntry("favoriteCompositeOps", compositeOps); } QString KisConfig::exportConfiguration(const QString &filterId, bool defaultValue) const { return (defaultValue ? QString() : m_cfg.readEntry("ExportConfiguration-" + filterId, QString())); } void KisConfig::setExportConfiguration(const QString &filterId, KisPropertiesConfigurationSP properties) const { QString exportConfig = properties->toXML(); m_cfg.writeEntry("ExportConfiguration-" + filterId, exportConfig); } QString KisConfig::importConfiguration(const QString &filterId, bool defaultValue) const { return (defaultValue ? QString() : m_cfg.readEntry("ImportConfiguration-" + filterId, QString())); } void KisConfig::setImportConfiguration(const QString &filterId, KisPropertiesConfigurationSP properties) const { QString importConfig = properties->toXML(); m_cfg.writeEntry("ImportConfiguration-" + filterId, importConfig); } bool KisConfig::useOcio(bool defaultValue) const { #ifdef HAVE_OCIO return (defaultValue ? false : m_cfg.readEntry("Krita/Ocio/UseOcio", false)); #else Q_UNUSED(defaultValue); return false; #endif } void KisConfig::setUseOcio(bool useOCIO) const { m_cfg.writeEntry("Krita/Ocio/UseOcio", useOCIO); } int KisConfig::favoritePresets(bool defaultValue) const { return (defaultValue ? 10 : m_cfg.readEntry("numFavoritePresets", 10)); } void KisConfig::setFavoritePresets(const int value) { m_cfg.writeEntry("numFavoritePresets", value); } bool KisConfig::levelOfDetailEnabled(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("levelOfDetailEnabled", false)); } void KisConfig::setLevelOfDetailEnabled(bool value) { m_cfg.writeEntry("levelOfDetailEnabled", value); } KisConfig::OcioColorManagementMode KisConfig::ocioColorManagementMode(bool defaultValue) const { return (OcioColorManagementMode)(defaultValue ? INTERNAL : m_cfg.readEntry("Krita/Ocio/OcioColorManagementMode", (int) INTERNAL)); } void KisConfig::setOcioColorManagementMode(OcioColorManagementMode mode) const { m_cfg.writeEntry("Krita/Ocio/OcioColorManagementMode", (int) mode); } QString KisConfig::ocioConfigurationPath(bool defaultValue) const { return (defaultValue ? QString() : m_cfg.readEntry("Krita/Ocio/OcioConfigPath", QString())); } void KisConfig::setOcioConfigurationPath(const QString &path) const { m_cfg.writeEntry("Krita/Ocio/OcioConfigPath", path); } QString KisConfig::ocioLutPath(bool defaultValue) const { return (defaultValue ? QString() : m_cfg.readEntry("Krita/Ocio/OcioLutPath", QString())); } void KisConfig::setOcioLutPath(const QString &path) const { m_cfg.writeEntry("Krita/Ocio/OcioLutPath", path); } int KisConfig::ocioLutEdgeSize(bool defaultValue) const { return (defaultValue ? 64 : m_cfg.readEntry("Krita/Ocio/LutEdgeSize", 64)); } void KisConfig::setOcioLutEdgeSize(int value) { m_cfg.writeEntry("Krita/Ocio/LutEdgeSize", value); } bool KisConfig::ocioLockColorVisualRepresentation(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("Krita/Ocio/OcioLockColorVisualRepresentation", false)); } void KisConfig::setOcioLockColorVisualRepresentation(bool value) { m_cfg.writeEntry("Krita/Ocio/OcioLockColorVisualRepresentation", value); } QString KisConfig::defaultPalette(bool defaultValue) const { return (defaultValue ? QString() : m_cfg.readEntry("defaultPalette", "Default")); } void KisConfig::setDefaultPalette(const QString& name) const { m_cfg.writeEntry("defaultPalette", name); } QString KisConfig::toolbarSlider(int sliderNumber, bool defaultValue) const { QString def = "flow"; if (sliderNumber == 1) { def = "opacity"; } if (sliderNumber == 2) { def = "size"; } return (defaultValue ? def : m_cfg.readEntry(QString("toolbarslider_%1").arg(sliderNumber), def)); } void KisConfig::setToolbarSlider(int sliderNumber, const QString &slider) { m_cfg.writeEntry(QString("toolbarslider_%1").arg(sliderNumber), slider); } int KisConfig::layerThumbnailSize(bool defaultValue) const { return (defaultValue ? 20 : m_cfg.readEntry("layerThumbnailSize", 20)); } void KisConfig::setLayerThumbnailSize(int size) { m_cfg.writeEntry("layerThumbnailSize", size); } bool KisConfig::sliderLabels(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("sliderLabels", true)); } void KisConfig::setSliderLabels(bool enabled) { m_cfg.writeEntry("sliderLabels", enabled); } QString KisConfig::currentInputProfile(bool defaultValue) const { return (defaultValue ? QString() : m_cfg.readEntry("currentInputProfile", QString())); } void KisConfig::setCurrentInputProfile(const QString& name) { m_cfg.writeEntry("currentInputProfile", name); } bool KisConfig::useSystemMonitorProfile(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("ColorManagement/UseSystemMonitorProfile", false)); } void KisConfig::setUseSystemMonitorProfile(bool _useSystemMonitorProfile) const { m_cfg.writeEntry("ColorManagement/UseSystemMonitorProfile", _useSystemMonitorProfile); } bool KisConfig::presetStripVisible(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("presetStripVisible", true)); } void KisConfig::setPresetStripVisible(bool visible) { m_cfg.writeEntry("presetStripVisible", visible); } bool KisConfig::scratchpadVisible(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("scratchpadVisible", true)); } void KisConfig::setScratchpadVisible(bool visible) { m_cfg.writeEntry("scratchpadVisible", visible); } bool KisConfig::showSingleChannelAsColor(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("showSingleChannelAsColor", false)); } void KisConfig::setShowSingleChannelAsColor(bool asColor) { m_cfg.writeEntry("showSingleChannelAsColor", asColor); } bool KisConfig::hidePopups(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("hidePopups", false)); } void KisConfig::setHidePopups(bool hidepopups) { m_cfg.writeEntry("hidePopups", hidepopups); } int KisConfig::numDefaultLayers(bool defaultValue) const { return (defaultValue ? 2 : m_cfg.readEntry("NumberOfLayersForNewImage", 2)); } void KisConfig::setNumDefaultLayers(int num) { m_cfg.writeEntry("NumberOfLayersForNewImage", num); } quint8 KisConfig::defaultBackgroundOpacity(bool defaultValue) const { return (defaultValue ? (int)OPACITY_OPAQUE_U8 : m_cfg.readEntry("BackgroundOpacityForNewImage", (int)OPACITY_OPAQUE_U8)); } void KisConfig::setDefaultBackgroundOpacity(quint8 value) { m_cfg.writeEntry("BackgroundOpacityForNewImage", (int)value); } QColor KisConfig::defaultBackgroundColor(bool defaultValue) const { return (defaultValue ? QColor(Qt::white) : m_cfg.readEntry("BackgroundColorForNewImage", QColor(Qt::white))); } void KisConfig::setDefaultBackgroundColor(QColor value) { m_cfg.writeEntry("BackgroundColorForNewImage", value); } KisConfig::BackgroundStyle KisConfig::defaultBackgroundStyle(bool defaultValue) const { return (KisConfig::BackgroundStyle)(defaultValue ? LAYER : m_cfg.readEntry("BackgroundStyleForNewImage", (int)LAYER)); } void KisConfig::setDefaultBackgroundStyle(KisConfig::BackgroundStyle value) { m_cfg.writeEntry("BackgroundStyleForNewImage", (int)value); } int KisConfig::lineSmoothingType(bool defaultValue) const { return (defaultValue ? 1 : m_cfg.readEntry("LineSmoothingType", 1)); } void KisConfig::setLineSmoothingType(int value) { m_cfg.writeEntry("LineSmoothingType", value); } qreal KisConfig::lineSmoothingDistance(bool defaultValue) const { return (defaultValue ? 50.0 : m_cfg.readEntry("LineSmoothingDistance", 50.0)); } void KisConfig::setLineSmoothingDistance(qreal value) { m_cfg.writeEntry("LineSmoothingDistance", value); } qreal KisConfig::lineSmoothingTailAggressiveness(bool defaultValue) const { return (defaultValue ? 0.15 : m_cfg.readEntry("LineSmoothingTailAggressiveness", 0.15)); } void KisConfig::setLineSmoothingTailAggressiveness(qreal value) { m_cfg.writeEntry("LineSmoothingTailAggressiveness", value); } bool KisConfig::lineSmoothingSmoothPressure(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("LineSmoothingSmoothPressure", false)); } void KisConfig::setLineSmoothingSmoothPressure(bool value) { m_cfg.writeEntry("LineSmoothingSmoothPressure", value); } bool KisConfig::lineSmoothingScalableDistance(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("LineSmoothingScalableDistance", true)); } void KisConfig::setLineSmoothingScalableDistance(bool value) { m_cfg.writeEntry("LineSmoothingScalableDistance", value); } qreal KisConfig::lineSmoothingDelayDistance(bool defaultValue) const { return (defaultValue ? 50.0 : m_cfg.readEntry("LineSmoothingDelayDistance", 50.0)); } void KisConfig::setLineSmoothingDelayDistance(qreal value) { m_cfg.writeEntry("LineSmoothingDelayDistance", value); } bool KisConfig::lineSmoothingUseDelayDistance(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("LineSmoothingUseDelayDistance", true)); } void KisConfig::setLineSmoothingUseDelayDistance(bool value) { m_cfg.writeEntry("LineSmoothingUseDelayDistance", value); } bool KisConfig::lineSmoothingFinishStabilizedCurve(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("LineSmoothingFinishStabilizedCurve", true)); } void KisConfig::setLineSmoothingFinishStabilizedCurve(bool value) { m_cfg.writeEntry("LineSmoothingFinishStabilizedCurve", value); } bool KisConfig::lineSmoothingStabilizeSensors(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("LineSmoothingStabilizeSensors", true)); } void KisConfig::setLineSmoothingStabilizeSensors(bool value) { m_cfg.writeEntry("LineSmoothingStabilizeSensors", value); } int KisConfig::tabletEventsDelay(bool defaultValue) const { return (defaultValue ? 10 : m_cfg.readEntry("tabletEventsDelay", 10)); } void KisConfig::setTabletEventsDelay(int value) { m_cfg.writeEntry("tabletEventsDelay", value); } bool KisConfig::trackTabletEventLatency(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("trackTabletEventLatency", false)); } void KisConfig::setTrackTabletEventLatency(bool value) { m_cfg.writeEntry("trackTabletEventLatency", value); } bool KisConfig::testingAcceptCompressedTabletEvents(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("testingAcceptCompressedTabletEvents", false)); } void KisConfig::setTestingAcceptCompressedTabletEvents(bool value) { m_cfg.writeEntry("testingAcceptCompressedTabletEvents", value); } bool KisConfig::shouldEatDriverShortcuts(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("shouldEatDriverShortcuts", false)); } bool KisConfig::testingCompressBrushEvents(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("testingCompressBrushEvents", false)); } void KisConfig::setTestingCompressBrushEvents(bool value) { m_cfg.writeEntry("testingCompressBrushEvents", value); } int KisConfig::workaroundX11SmoothPressureSteps(bool defaultValue) const { return (defaultValue ? 0 : m_cfg.readEntry("workaroundX11SmoothPressureSteps", 0)); } bool KisConfig::showCanvasMessages(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("showOnCanvasMessages", true)); } void KisConfig::setShowCanvasMessages(bool show) { m_cfg.writeEntry("showOnCanvasMessages", show); } bool KisConfig::compressKra(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("compressLayersInKra", false)); } void KisConfig::setCompressKra(bool compress) { m_cfg.writeEntry("compressLayersInKra", compress); } bool KisConfig::toolOptionsInDocker(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("ToolOptionsInDocker", true)); } void KisConfig::setToolOptionsInDocker(bool inDocker) { m_cfg.writeEntry("ToolOptionsInDocker", inDocker); } int KisConfig::kineticScrollingGesture(bool defaultValue) const { return (defaultValue ? 0 : m_cfg.readEntry("KineticScrollingGesture", 0)); } void KisConfig::setKineticScrollingGesture(int gesture) { m_cfg.writeEntry("KineticScrollingGesture", gesture); } int KisConfig::kineticScrollingSensitivity(bool defaultValue) const { return (defaultValue ? 75 : m_cfg.readEntry("KineticScrollingSensitivity", 75)); } void KisConfig::setKineticScrollingSensitivity(int sensitivity) { m_cfg.writeEntry("KineticScrollingSensitivity", sensitivity); } bool KisConfig::kineticScrollingScrollbar(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("KineticScrollingScrollbar", true)); } void KisConfig::setKineticScrollingScrollbar(bool scrollbar) { m_cfg.writeEntry("KineticScrollingScrollbar", scrollbar); } const KoColorSpace* KisConfig::customColorSelectorColorSpace(bool defaultValue) const { const KoColorSpace *cs = 0; KConfigGroup cfg = KSharedConfig::openConfig()->group("advancedColorSelector"); if (defaultValue || cfg.readEntry("useCustomColorSpace", true)) { KoColorSpaceRegistry* csr = KoColorSpaceRegistry::instance(); QString modelID = cfg.readEntry("customColorSpaceModel", "RGBA"); QString depthID = cfg.readEntry("customColorSpaceDepthID", "U8"); QString profile = cfg.readEntry("customColorSpaceProfile", "sRGB built-in - (lcms internal)"); if (profile == "default") { // qDebug() << "Falling back to default color profile."; profile = "sRGB built-in - (lcms internal)"; } cs = csr->colorSpace(modelID, depthID, profile); } return cs; } void KisConfig::setCustomColorSelectorColorSpace(const KoColorSpace *cs) { KConfigGroup cfg = KSharedConfig::openConfig()->group("advancedColorSelector"); cfg.writeEntry("useCustomColorSpace", bool(cs)); if(cs) { cfg.writeEntry("customColorSpaceModel", cs->colorModelId().id()); cfg.writeEntry("customColorSpaceDepthID", cs->colorDepthId().id()); cfg.writeEntry("customColorSpaceProfile", cs->profile()->name()); } KisConfigNotifier::instance()->notifyConfigChanged(); } bool KisConfig::enableOpenGLFramerateLogging(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("enableOpenGLFramerateLogging", false)); } void KisConfig::setEnableOpenGLFramerateLogging(bool value) const { m_cfg.writeEntry("enableOpenGLFramerateLogging", value); } bool KisConfig::enableBrushSpeedLogging(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("enableBrushSpeedLogging", false)); } void KisConfig::setEnableBrushSpeedLogging(bool value) const { m_cfg.writeEntry("enableBrushSpeedLogging", value); } void KisConfig::setEnableAmdVectorizationWorkaround(bool value) { m_cfg.writeEntry("amdDisableVectorWorkaround", value); } bool KisConfig::enableAmdVectorizationWorkaround(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("amdDisableVectorWorkaround", false)); } void KisConfig::setAnimationDropFrames(bool value) { bool oldValue = animationDropFrames(); if (value == oldValue) return; m_cfg.writeEntry("animationDropFrames", value); KisConfigNotifier::instance()->notifyDropFramesModeChanged(); } bool KisConfig::animationDropFrames(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("animationDropFrames", true)); } int KisConfig::scrubbingUpdatesDelay(bool defaultValue) const { return (defaultValue ? 30 : m_cfg.readEntry("scrubbingUpdatesDelay", 30)); } void KisConfig::setScrubbingUpdatesDelay(int value) { m_cfg.writeEntry("scrubbingUpdatesDelay", value); } int KisConfig::scrubbingAudioUpdatesDelay(bool defaultValue) const { return (defaultValue ? -1 : m_cfg.readEntry("scrubbingAudioUpdatesDelay", -1)); } void KisConfig::setScrubbingAudioUpdatesDelay(int value) { m_cfg.writeEntry("scrubbingAudioUpdatesDelay", value); } int KisConfig::audioOffsetTolerance(bool defaultValue) const { return (defaultValue ? -1 : m_cfg.readEntry("audioOffsetTolerance", -1)); } void KisConfig::setAudioOffsetTolerance(int value) { m_cfg.writeEntry("audioOffsetTolerance", value); } bool KisConfig::switchSelectionCtrlAlt(bool defaultValue) const { return defaultValue ? false : m_cfg.readEntry("switchSelectionCtrlAlt", false); } void KisConfig::setSwitchSelectionCtrlAlt(bool value) { m_cfg.writeEntry("switchSelectionCtrlAlt", value); KisConfigNotifier::instance()->notifyConfigChanged(); } bool KisConfig::convertToImageColorspaceOnImport(bool defaultValue) const { return defaultValue ? false : m_cfg.readEntry("ConvertToImageColorSpaceOnImport", false); } void KisConfig::setConvertToImageColorspaceOnImport(bool value) { m_cfg.writeEntry("ConvertToImageColorSpaceOnImport", value); } int KisConfig::stabilizerSampleSize(bool defaultValue) const { #ifdef Q_OS_WIN const int defaultSampleSize = 50; #else const int defaultSampleSize = 15; #endif return defaultValue ? defaultSampleSize : m_cfg.readEntry("stabilizerSampleSize", defaultSampleSize); } void KisConfig::setStabilizerSampleSize(int value) { m_cfg.writeEntry("stabilizerSampleSize", value); } bool KisConfig::stabilizerDelayedPaint(bool defaultValue) const { const bool defaultEnabled = true; return defaultValue ? defaultEnabled : m_cfg.readEntry("stabilizerDelayedPaint", defaultEnabled); } void KisConfig::setStabilizerDelayedPaint(bool value) { m_cfg.writeEntry("stabilizerDelayedPaint", value); } QString KisConfig::customFFMpegPath(bool defaultValue) const { return defaultValue ? QString() : m_cfg.readEntry("ffmpegExecutablePath", QString()); } void KisConfig::setCustomFFMpegPath(const QString &value) const { m_cfg.writeEntry("ffmpegExecutablePath", value); } bool KisConfig::showBrushHud(bool defaultValue) const { return defaultValue ? false : m_cfg.readEntry("showBrushHud", false); } void KisConfig::setShowBrushHud(bool value) { m_cfg.writeEntry("showBrushHud", value); } QString KisConfig::brushHudSetting(bool defaultValue) const { QString defaultDoc = "\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n"; return defaultValue ? defaultDoc : m_cfg.readEntry("brushHudSettings", defaultDoc); } void KisConfig::setBrushHudSetting(const QString &value) const { m_cfg.writeEntry("brushHudSettings", value); } bool KisConfig::calculateAnimationCacheInBackground(bool defaultValue) const { return defaultValue ? true : m_cfg.readEntry("calculateAnimationCacheInBackground", true); } void KisConfig::setCalculateAnimationCacheInBackground(bool value) { m_cfg.writeEntry("calculateAnimationCacheInBackground", value); } QColor KisConfig::defaultAssistantsColor(bool defaultValue) const { static const QColor defaultColor = QColor(176, 176, 176, 255); return defaultValue ? defaultColor : m_cfg.readEntry("defaultAssistantsColor", defaultColor); } void KisConfig::setDefaultAssistantsColor(const QColor &color) const { m_cfg.writeEntry("defaultAssistantsColor", color); } +bool KisConfig::autoSmoothBezierCurves(bool defaultValue) const +{ + return defaultValue ? false : m_cfg.readEntry("autoSmoothBezierCurves", false); +} + +void KisConfig::setAutoSmoothBezierCurves(bool value) +{ + m_cfg.writeEntry("autoSmoothBezierCurves", value); +} + #include #include void KisConfig::writeKoColor(const QString& name, const KoColor& color) const { QDomDocument doc = QDomDocument(name); QDomElement el = doc.createElement(name); doc.appendChild(el); color.toXML(doc, el); m_cfg.writeEntry(name, doc.toString()); } //ported from kispropertiesconfig. KoColor KisConfig::readKoColor(const QString& name, const KoColor& color) const { QDomDocument doc; if (!m_cfg.readEntry(name).isNull()) { doc.setContent(m_cfg.readEntry(name)); QDomElement e = doc.documentElement().firstChild().toElement(); return KoColor::fromXML(e, Integer16BitsColorDepthID.id()); } else { QString blackColor = "\n\n \n\n"; doc.setContent(blackColor); QDomElement e = doc.documentElement().firstChild().toElement(); return KoColor::fromXML(e, Integer16BitsColorDepthID.id()); } return color; } diff --git a/libs/ui/kis_config.h b/libs/ui/kis_config.h index a8f04c75e2..c0d24bef74 100644 --- a/libs/ui/kis_config.h +++ b/libs/ui/kis_config.h @@ -1,603 +1,606 @@ /* * Copyright (c) 2002 Patrick Julien * * 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. */ #ifndef KIS_CONFIG_H_ #define KIS_CONFIG_H_ #include #include #include #include #include #include #include #include #include "kritaui_export.h" class KoColorProfile; class KoColorSpace; class KisSnapConfig; class KRITAUI_EXPORT KisConfig { public: /** * @brief KisConfig create a kisconfig object * @param readOnly if true, there will be no call to sync when the object is deleted. * Any KisConfig object created in a thread must be read-only. */ KisConfig(bool readOnly); ~KisConfig(); bool disableTouchOnCanvas(bool defaultValue = false) const; void setDisableTouchOnCanvas(bool value) const; bool useProjections(bool defaultValue = false) const; void setUseProjections(bool useProj) const; bool undoEnabled(bool defaultValue = false) const; void setUndoEnabled(bool undo) const; int undoStackLimit(bool defaultValue = false) const; void setUndoStackLimit(int limit) const; bool useCumulativeUndoRedo(bool defaultValue = false) const; void setCumulativeUndoRedo(bool value); double stackT1(bool defaultValue = false) const; void setStackT1(int T1); double stackT2(bool defaultValue = false) const; void setStackT2(int T2); int stackN(bool defaultValue = false) const; void setStackN(int N); qint32 defImageWidth(bool defaultValue = false) const; void defImageWidth(qint32 width) const; qint32 defImageHeight(bool defaultValue = false) const; void defImageHeight(qint32 height) const; qreal defImageResolution(bool defaultValue = false) const; void defImageResolution(qreal res) const; int preferredVectorImportResolutionPPI(bool defaultValue = false) const; void setPreferredVectorImportResolutionPPI(int value) const; /** * @return the id of the default color model used for creating new images. */ QString defColorModel(bool defaultValue = false) const; /** * set the id of the default color model used for creating new images. */ void defColorModel(const QString & model) const; /** * @return the id of the default color depth used for creating new images. */ QString defaultColorDepth(bool defaultValue = false) const; /** * set the id of the default color depth used for creating new images. */ void setDefaultColorDepth(const QString & depth) const; /** * @return the id of the default color profile used for creating new images. */ QString defColorProfile(bool defaultValue = false) const; /** * set the id of the default color profile used for creating new images. */ void defColorProfile(const QString & depth) const; CursorStyle newCursorStyle(bool defaultValue = false) const; void setNewCursorStyle(CursorStyle style); QColor getCursorMainColor(bool defaultValue = false) const; void setCursorMainColor(const QColor& v) const; OutlineStyle newOutlineStyle(bool defaultValue = false) const; void setNewOutlineStyle(OutlineStyle style); QRect colorPreviewRect() const; void setColorPreviewRect(const QRect &rect); /// get the profile the user has selected for the given screen QString monitorProfile(int screen) const; void setMonitorProfile(int screen, const QString & monitorProfile, bool override) const; QString monitorForScreen(int screen, const QString &defaultMonitor, bool defaultValue = true) const; void setMonitorForScreen(int screen, const QString& monitor); /// Get the actual profile to be used for the given screen, which is /// either the screen profile set by the color management system or /// the custom monitor profile set by the user, depending on the configuration const KoColorProfile *displayProfile(int screen) const; QString workingColorSpace(bool defaultValue = false) const; void setWorkingColorSpace(const QString & workingColorSpace) const; QString importProfile(bool defaultValue = false) const; void setImportProfile(const QString & importProfile) const; QString printerColorSpace(bool defaultValue = false) const; void setPrinterColorSpace(const QString & printerColorSpace) const; QString printerProfile(bool defaultValue = false) const; void setPrinterProfile(const QString & printerProfile) const; bool useBlackPointCompensation(bool defaultValue = false) const; void setUseBlackPointCompensation(bool useBlackPointCompensation) const; bool allowLCMSOptimization(bool defaultValue = false) const; void setAllowLCMSOptimization(bool allowLCMSOptimization); void writeKoColor(const QString& name, const KoColor& color) const; KoColor readKoColor(const QString& name, const KoColor& color = KoColor()) const; bool showRulers(bool defaultValue = false) const; void setShowRulers(bool rulers) const; bool forceShowSaveMessages(bool defaultValue = true) const; void setForceShowSaveMessages(bool value) const; bool forceShowAutosaveMessages(bool defaultValue = true) const; void setForceShowAutosaveMessages(bool ShowAutosaveMessages) const; bool rulersTrackMouse(bool defaultValue = false) const; void setRulersTrackMouse(bool value) const; qint32 pasteBehaviour(bool defaultValue = false) const; void setPasteBehaviour(qint32 behaviour) const; qint32 monitorRenderIntent(bool defaultValue = false) const; void setRenderIntent(qint32 monitorRenderIntent) const; bool useOpenGL(bool defaultValue = false) const; void setUseOpenGL(bool useOpenGL) const; int openGLFilteringMode(bool defaultValue = false) const; void setOpenGLFilteringMode(int filteringMode); bool useOpenGLTextureBuffer(bool defaultValue = false) const; void setUseOpenGLTextureBuffer(bool useBuffer); bool disableVSync(bool defaultValue = false) const; void setDisableVSync(bool disableVSync); bool showAdvancedOpenGLSettings(bool defaultValue = false) const; bool forceOpenGLFenceWorkaround(bool defaultValue = false) const; int numMipmapLevels(bool defaultValue = false) const; int openGLTextureSize(bool defaultValue = false) const; int textureOverlapBorder() const; quint32 getGridMainStyle(bool defaultValue = false) const; void setGridMainStyle(quint32 v) const; quint32 getGridSubdivisionStyle(bool defaultValue = false) const; void setGridSubdivisionStyle(quint32 v) const; QColor getGridMainColor(bool defaultValue = false) const; void setGridMainColor(const QColor & v) const; QColor getGridSubdivisionColor(bool defaultValue = false) const; void setGridSubdivisionColor(const QColor & v) const; QColor getPixelGridColor(bool defaultValue = false) const; void setPixelGridColor(const QColor & v) const; qreal getPixelGridDrawingThreshold(bool defaultValue = false) const; void setPixelGridDrawingThreshold(qreal v) const; bool pixelGridEnabled(bool defaultValue = false) const; void enablePixelGrid(bool v) const; quint32 guidesLineStyle(bool defaultValue = false) const; void setGuidesLineStyle(quint32 v) const; QColor guidesColor(bool defaultValue = false) const; void setGuidesColor(const QColor & v) const; void loadSnapConfig(KisSnapConfig *config, bool defaultValue = false) const; void saveSnapConfig(const KisSnapConfig &config); qint32 checkSize(bool defaultValue = false) const; void setCheckSize(qint32 checkSize) const; bool scrollCheckers(bool defaultValue = false) const; void setScrollingCheckers(bool scollCheckers) const; QColor checkersColor1(bool defaultValue = false) const; void setCheckersColor1(const QColor & v) const; QColor checkersColor2(bool defaultValue = false) const; void setCheckersColor2(const QColor & v) const; QColor canvasBorderColor(bool defaultValue = false) const; void setCanvasBorderColor(const QColor &color) const; bool hideScrollbars(bool defaultValue = false) const; void setHideScrollbars(bool value) const; bool antialiasCurves(bool defaultValue = false) const; void setAntialiasCurves(bool v) const; bool antialiasSelectionOutline(bool defaultValue = false) const; void setAntialiasSelectionOutline(bool v) const; bool showRootLayer(bool defaultValue = false) const; void setShowRootLayer(bool showRootLayer) const; bool showGlobalSelection(bool defaultValue = false) const; void setShowGlobalSelection(bool showGlobalSelection) const; bool showOutlineWhilePainting(bool defaultValue = false) const; void setShowOutlineWhilePainting(bool showOutlineWhilePainting) const; bool forceAlwaysFullSizedOutline(bool defaultValue = false) const; void setForceAlwaysFullSizedOutline(bool value) const; enum SessionOnStartup { SOS_BlankSession, SOS_PreviousSession, SOS_ShowSessionManager }; SessionOnStartup sessionOnStartup(bool defaultValue = false) const; void setSessionOnStartup(SessionOnStartup value); bool saveSessionOnQuit(bool defaultValue) const; void setSaveSessionOnQuit(bool value); qreal outlineSizeMinimum(bool defaultValue = false) const; void setOutlineSizeMinimum(qreal outlineSizeMinimum) const; qreal selectionViewSizeMinimum(bool defaultValue = false) const; void setSelectionViewSizeMinimum(qreal outlineSizeMinimum) const; int autoSaveInterval(bool defaultValue = false) const; void setAutoSaveInterval(int seconds) const; bool backupFile(bool defaultValue = false) const; void setBackupFile(bool backupFile) const; bool showFilterGallery(bool defaultValue = false) const; void setShowFilterGallery(bool showFilterGallery) const; bool showFilterGalleryLayerMaskDialog(bool defaultValue = false) const; void setShowFilterGalleryLayerMaskDialog(bool showFilterGallery) const; // OPENGL_SUCCESS, TRY_OPENGL, OPENGL_NOT_TRIED, OPENGL_FAILED QString canvasState(bool defaultValue = false) const; void setCanvasState(const QString& state) const; bool toolOptionsPopupDetached(bool defaultValue = false) const; void setToolOptionsPopupDetached(bool detached) const; bool paintopPopupDetached(bool defaultValue = false) const; void setPaintopPopupDetached(bool detached) const; QString pressureTabletCurve(bool defaultValue = false) const; void setPressureTabletCurve(const QString& curveString) const; bool useWin8PointerInput(bool defaultValue = false) const; void setUseWin8PointerInput(bool value) const; qreal vastScrolling(bool defaultValue = false) const; void setVastScrolling(const qreal factor) const; int presetChooserViewMode(bool defaultValue = false) const; void setPresetChooserViewMode(const int mode) const; int presetIconSize(bool defaultValue = false) const; void setPresetIconSize(const int value) const; bool firstRun(bool defaultValue = false) const; void setFirstRun(const bool firstRun) const; bool clicklessSpacePan(bool defaultValue = false) const; void setClicklessSpacePan(const bool toggle) const; int horizontalSplitLines(bool defaultValue = false) const; void setHorizontalSplitLines(const int numberLines) const; int verticalSplitLines(bool defaultValue = false) const; void setVerticalSplitLines(const int numberLines) const; bool hideDockersFullscreen(bool defaultValue = false) const; void setHideDockersFullscreen(const bool value) const; bool showDockers(bool defaultValue = false) const; void setShowDockers(const bool value) const; bool showStatusBar(bool defaultValue = false) const; void setShowStatusBar(const bool value) const; bool hideMenuFullscreen(bool defaultValue = false) const; void setHideMenuFullscreen(const bool value) const; bool hideScrollbarsFullscreen(bool defaultValue = false) const; void setHideScrollbarsFullscreen(const bool value) const; bool hideStatusbarFullscreen(bool defaultValue = false) const; void setHideStatusbarFullscreen(const bool value) const; bool hideTitlebarFullscreen(bool defaultValue = false) const; void setHideTitlebarFullscreen(const bool value) const; bool hideToolbarFullscreen(bool defaultValue = false) const; void setHideToolbarFullscreen(const bool value) const; bool fullscreenMode(bool defaultValue = false) const; void setFullscreenMode(const bool value) const; QStringList favoriteCompositeOps(bool defaultValue = false) const; void setFavoriteCompositeOps(const QStringList& compositeOps) const; QString exportConfiguration(const QString &filterId, bool defaultValue = false) const; void setExportConfiguration(const QString &filterId, KisPropertiesConfigurationSP properties) const; QString importConfiguration(const QString &filterId, bool defaultValue = false) const; void setImportConfiguration(const QString &filterId, KisPropertiesConfigurationSP properties) const; bool useOcio(bool defaultValue = false) const; void setUseOcio(bool useOCIO) const; int favoritePresets(bool defaultValue = false) const; void setFavoritePresets(const int value); bool levelOfDetailEnabled(bool defaultValue = false) const; void setLevelOfDetailEnabled(bool value); enum OcioColorManagementMode { INTERNAL = 0, OCIO_CONFIG, OCIO_ENVIRONMENT }; OcioColorManagementMode ocioColorManagementMode(bool defaultValue = false) const; void setOcioColorManagementMode(OcioColorManagementMode mode) const; QString ocioConfigurationPath(bool defaultValue = false) const; void setOcioConfigurationPath(const QString &path) const; QString ocioLutPath(bool defaultValue = false) const; void setOcioLutPath(const QString &path) const; int ocioLutEdgeSize(bool defaultValue = false) const; void setOcioLutEdgeSize(int value); bool ocioLockColorVisualRepresentation(bool defaultValue = false) const; void setOcioLockColorVisualRepresentation(bool value); bool useSystemMonitorProfile(bool defaultValue = false) const; void setUseSystemMonitorProfile(bool _useSystemMonitorProfile) const; QString defaultPalette(bool defaultValue = false) const; void setDefaultPalette(const QString& name) const; QString toolbarSlider(int sliderNumber, bool defaultValue = false) const; void setToolbarSlider(int sliderNumber, const QString &slider); int layerThumbnailSize(bool defaultValue = false) const; void setLayerThumbnailSize(int size); bool sliderLabels(bool defaultValue = false) const; void setSliderLabels(bool enabled); QString currentInputProfile(bool defaultValue = false) const; void setCurrentInputProfile(const QString& name); bool presetStripVisible(bool defaultValue = false) const; void setPresetStripVisible(bool visible); bool scratchpadVisible(bool defaultValue = false) const; void setScratchpadVisible(bool visible); bool showSingleChannelAsColor(bool defaultValue = false) const; void setShowSingleChannelAsColor(bool asColor); bool hidePopups(bool defaultValue = false) const; void setHidePopups(bool hidepopups); int numDefaultLayers(bool defaultValue = false) const; void setNumDefaultLayers(int num); quint8 defaultBackgroundOpacity(bool defaultValue = false) const; void setDefaultBackgroundOpacity(quint8 value); QColor defaultBackgroundColor(bool defaultValue = false) const; void setDefaultBackgroundColor(QColor value); enum BackgroundStyle { LAYER = 0, PROJECTION = 1 }; BackgroundStyle defaultBackgroundStyle(bool defaultValue = false) const; void setDefaultBackgroundStyle(BackgroundStyle value); int lineSmoothingType(bool defaultValue = false) const; void setLineSmoothingType(int value); qreal lineSmoothingDistance(bool defaultValue = false) const; void setLineSmoothingDistance(qreal value); qreal lineSmoothingTailAggressiveness(bool defaultValue = false) const; void setLineSmoothingTailAggressiveness(qreal value); bool lineSmoothingSmoothPressure(bool defaultValue = false) const; void setLineSmoothingSmoothPressure(bool value); bool lineSmoothingScalableDistance(bool defaultValue = false) const; void setLineSmoothingScalableDistance(bool value); qreal lineSmoothingDelayDistance(bool defaultValue = false) const; void setLineSmoothingDelayDistance(qreal value); bool lineSmoothingUseDelayDistance(bool defaultValue = false) const; void setLineSmoothingUseDelayDistance(bool value); bool lineSmoothingFinishStabilizedCurve(bool defaultValue = false) const; void setLineSmoothingFinishStabilizedCurve(bool value); bool lineSmoothingStabilizeSensors(bool defaultValue = false) const; void setLineSmoothingStabilizeSensors(bool value); int tabletEventsDelay(bool defaultValue = false) const; void setTabletEventsDelay(int value); bool trackTabletEventLatency(bool defaultValue = false) const; void setTrackTabletEventLatency(bool value); bool testingAcceptCompressedTabletEvents(bool defaultValue = false) const; void setTestingAcceptCompressedTabletEvents(bool value); bool shouldEatDriverShortcuts(bool defaultValue = false) const; bool testingCompressBrushEvents(bool defaultValue = false) const; void setTestingCompressBrushEvents(bool value); const KoColorSpace* customColorSelectorColorSpace(bool defaultValue = false) const; void setCustomColorSelectorColorSpace(const KoColorSpace *cs); bool useDirtyPresets(bool defaultValue = false) const; void setUseDirtyPresets(bool value); bool useEraserBrushSize(bool defaultValue = false) const; void setUseEraserBrushSize(bool value); bool useEraserBrushOpacity(bool defaultValue = false) const; void setUseEraserBrushOpacity(bool value); QColor getMDIBackgroundColor(bool defaultValue = false) const; void setMDIBackgroundColor(const QColor & v) const; QString getMDIBackgroundImage(bool defaultValue = false) const; void setMDIBackgroundImage(const QString & fileName) const; int workaroundX11SmoothPressureSteps(bool defaultValue = false) const; bool showCanvasMessages(bool defaultValue = false) const; void setShowCanvasMessages(bool show); bool compressKra(bool defaultValue = false) const; void setCompressKra(bool compress); bool toolOptionsInDocker(bool defaultValue = false) const; void setToolOptionsInDocker(bool inDocker); int kineticScrollingGesture(bool defaultValue = false) const; void setKineticScrollingGesture(int kineticScroll); int kineticScrollingSensitivity(bool defaultValue = false) const; void setKineticScrollingSensitivity(int sensitivity); bool kineticScrollingScrollbar(bool defaultValue = false) const; void setKineticScrollingScrollbar(bool scrollbar); void setEnableOpenGLFramerateLogging(bool value) const; bool enableOpenGLFramerateLogging(bool defaultValue = false) const; void setEnableBrushSpeedLogging(bool value) const; bool enableBrushSpeedLogging(bool defaultValue = false) const; void setEnableAmdVectorizationWorkaround(bool value); bool enableAmdVectorizationWorkaround(bool defaultValue = false) const; bool animationDropFrames(bool defaultValue = false) const; void setAnimationDropFrames(bool value); int scrubbingUpdatesDelay(bool defaultValue = false) const; void setScrubbingUpdatesDelay(int value); int scrubbingAudioUpdatesDelay(bool defaultValue = false) const; void setScrubbingAudioUpdatesDelay(int value); int audioOffsetTolerance(bool defaultValue = false) const; void setAudioOffsetTolerance(int value); bool switchSelectionCtrlAlt(bool defaultValue = false) const; void setSwitchSelectionCtrlAlt(bool value); bool convertToImageColorspaceOnImport(bool defaultValue = false) const; void setConvertToImageColorspaceOnImport(bool value); int stabilizerSampleSize(bool defaultValue = false) const; void setStabilizerSampleSize(int value); bool stabilizerDelayedPaint(bool defaultValue = false) const; void setStabilizerDelayedPaint(bool value); QString customFFMpegPath(bool defaultValue = false) const; void setCustomFFMpegPath(const QString &value) const; bool showBrushHud(bool defaultValue = false) const; void setShowBrushHud(bool value); QString brushHudSetting(bool defaultValue = false) const; void setBrushHudSetting(const QString &value) const; bool calculateAnimationCacheInBackground(bool defaultValue = false) const; void setCalculateAnimationCacheInBackground(bool value); QColor defaultAssistantsColor(bool defaultValue = false) const; void setDefaultAssistantsColor(const QColor &color) const; + bool autoSmoothBezierCurves(bool defaultValue = false) const; + void setAutoSmoothBezierCurves(bool value); + template void writeEntry(const QString& name, const T& value) { m_cfg.writeEntry(name, value); } template void writeList(const QString& name, const QList& value) { m_cfg.writeEntry(name, value); } template T readEntry(const QString& name, const T& defaultValue=T()) { return m_cfg.readEntry(name, defaultValue); } template QList readList(const QString& name, const QList& defaultValue=QList()) { return m_cfg.readEntry(name, defaultValue); } /// get the profile the color management system has stored for the given screen static const KoColorProfile* getScreenProfile(int screen); private: KisConfig(const KisConfig&); KisConfig& operator=(const KisConfig&) const; private: mutable KConfigGroup m_cfg; bool m_readOnly; }; #endif // KIS_CONFIG_H_ diff --git a/libs/ui/kis_mask_manager.cc b/libs/ui/kis_mask_manager.cc index a943f5eed7..b469b58b0b 100644 --- a/libs/ui/kis_mask_manager.cc +++ b/libs/ui/kis_mask_manager.cc @@ -1,328 +1,344 @@ /* This file is part of the KDE project * Copyright (C) Boudewijn Rempt , (C) 2006 * * 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_mask_manager.h" #include #include #include #include #include #include #include #include "KisDocument.h" #include "KisViewManager.h" #include #include #include #include #include #include #include #include #include #include "dialogs/kis_dlg_adjustment_layer.h" #include "widgets/kis_mask_widgets.h" #include #include #include #include "dialogs/kis_dlg_adj_layer_props.h" #include #include #include #include #include "kis_node_commands_adapter.h" -#include "commands/kis_selection_commands.h" +#include "commands/kis_deselect_global_selection_command.h" #include "kis_iterator_ng.h" KisMaskManager::KisMaskManager(KisViewManager * view) : m_view(view) , m_imageView(0) , m_commandsAdapter(new KisNodeCommandsAdapter(m_view)) { } void KisMaskManager::setView(QPointerimageView) { m_imageView = imageView; } void KisMaskManager::setup(KActionCollection *actionCollection, KisActionManager *actionManager) { Q_UNUSED(actionCollection); Q_UNUSED(actionManager); } void KisMaskManager::updateGUI() { // XXX: enable/disable menu items according to whether there's a mask selected currently // XXX: disable the selection mask item if there's already a selection mask // YYY: doesn't KisAction do that already? } KisMaskSP KisMaskManager::activeMask() { if (m_imageView) { return m_imageView->currentMask(); } return 0; } KisPaintDeviceSP KisMaskManager::activeDevice() { KisMaskSP mask = activeMask(); return mask ? mask->paintDevice() : 0; } void KisMaskManager::activateMask(KisMaskSP mask) { Q_UNUSED(mask); } void KisMaskManager::masksUpdated() { m_view->updateGUI(); } void KisMaskManager::adjustMaskPosition(KisNodeSP node, KisNodeSP activeNode, bool avoidActiveNode, KisNodeSP &parent, KisNodeSP &above) { Q_ASSERT(node); Q_ASSERT(activeNode); if (!avoidActiveNode && activeNode->allowAsChild(node)) { parent = activeNode; above = activeNode->lastChild(); } else if (activeNode->parent() && activeNode->parent()->allowAsChild(node)) { parent = activeNode->parent(); above = activeNode; } else { KisNodeSP t = activeNode; while ((t = t->nextSibling())) { if (t->allowAsChild(node)) { parent = t; above = t->lastChild(); break; } } if (!t) { t = activeNode; while ((t = t->prevSibling())) { if (t->allowAsChild(node)) { parent = t; above = t->lastChild(); break; } } } if (!t && activeNode->parent()) { adjustMaskPosition(node, activeNode->parent(), true, parent, above); } else if (!t) { KisImageWSP image = m_view->image(); KisLayerSP layer = new KisPaintLayer(image.data(), image->nextLayerName(), OPACITY_OPAQUE_U8, image->colorSpace()); m_commandsAdapter->addNode(layer, activeNode, 0); parent = layer; above = 0; } } } -void KisMaskManager::createMaskCommon(KisMaskSP mask, KisNodeSP activeNode, KisPaintDeviceSP copyFrom, const KUndo2MagicString& macroName, const QString &nodeType, const QString &nodeName, bool suppressSelection, bool avoidActiveNode, bool updateImage) +void KisMaskManager::createMaskCommon(KisMaskSP mask, + KisNodeSP activeNode, + KisPaintDeviceSP copyFrom, + const KUndo2MagicString& macroName, + const QString &nodeType, + const QString &nodeName, + bool suppressSelection, + bool avoidActiveNode, + bool updateImage) { m_commandsAdapter->beginMacro(macroName); KisNodeSP parent; KisNodeSP above; adjustMaskPosition(mask, activeNode, avoidActiveNode, parent, above); KisLayerSP parentLayer = qobject_cast(parent.data()); Q_ASSERT(parentLayer); + bool shouldDeselectGlobalSelection = false; + if (!suppressSelection) { if (copyFrom) { mask->initSelection(copyFrom, parentLayer); } else { mask->initSelection(m_view->selection(), parentLayer); + shouldDeselectGlobalSelection = m_view->selection(); } } //counting number of KisSelectionMask QList masks = parentLayer->childNodes(QStringList(nodeType),KoProperties()); int number = masks.count() + 1; mask->setName(nodeName + QString(" ") + QString::number(number)); m_commandsAdapter->addNode(mask, parentLayer, above, updateImage, updateImage); + + if (shouldDeselectGlobalSelection) { + m_commandsAdapter->addExtraCommand(new KisDeselectGlobalSelectionCommand(m_imageView->image())); + } + m_commandsAdapter->endMacro(); masksUpdated(); } bool KisMaskManager::createSelectionMask(KisNodeSP activeNode, KisPaintDeviceSP copyFrom, bool convertActiveNode) { KisSelectionMaskSP mask = new KisSelectionMask(m_view->image()); createMaskCommon(mask, activeNode, copyFrom, kundo2_i18n("Add Selection Mask"), "KisSelectionMask", i18n("Selection"), false, convertActiveNode, false); mask->setActive(true); if (convertActiveNode) { m_commandsAdapter->removeNode(activeNode); } return true; } bool KisMaskManager::createTransparencyMask(KisNodeSP activeNode, KisPaintDeviceSP copyFrom, bool convertActiveNode) { KisMaskSP mask = new KisTransparencyMask(); createMaskCommon(mask, activeNode, copyFrom, kundo2_i18n("Add Transparency Mask"), "KisTransparencyMask", i18n("Transparency Mask"), false, convertActiveNode); if (convertActiveNode) { m_commandsAdapter->removeNode(activeNode); } return true; } bool KisMaskManager::createFilterMask(KisNodeSP activeNode, KisPaintDeviceSP copyFrom, bool quiet, bool convertActiveNode) { KisFilterMaskSP mask = new KisFilterMask(); createMaskCommon(mask, activeNode, copyFrom, kundo2_i18n("Add Filter Mask"), "KisFilterMask", i18n("Filter Mask"), false, convertActiveNode); if (convertActiveNode) { m_commandsAdapter->removeNode(activeNode); } /** * FIXME: We'll use layer's original for creation of a thumbnail. * Actually, we can't use it's projection as newly created mask * may be going to be inserted in the middle of the masks stack */ KisPaintDeviceSP originalDevice = mask->parent()->original(); KisDlgAdjustmentLayer dialog(mask, mask.data(), originalDevice, mask->name(), i18n("New Filter Mask"), m_view); // If we are supposed to not disturb the user, don't start asking them about things. if(quiet) { KisFilterConfigurationSP filter = KisFilterRegistry::instance()->values().first()->defaultConfiguration(); if (filter) { mask->setFilter(filter); mask->setName(mask->name()); } return true; } bool result = false; if (dialog.exec() == QDialog::Accepted) { KisFilterConfigurationSP filter = dialog.filterConfiguration(); if (filter) { QString name = dialog.layerName(); mask->setFilter(filter); mask->setName(name); } result = true; } else { m_commandsAdapter->undoLastCommand(); } return result; } void KisMaskManager::createColorizeMask(KisNodeSP activeNode) { KisColorizeMaskSP mask = new KisColorizeMask(); createMaskCommon(mask, activeNode, 0, kundo2_i18n("Add Colorize Mask"), "KisColorizeMask", i18n("Colorize Mask"), true, false); mask->setImage(m_view->image()); mask->initializeCompositeOp(); delete mask->setColorSpace(mask->parent()->colorSpace()); } void KisMaskManager::createTransformMask(KisNodeSP activeNode) { KisTransformMaskSP mask = new KisTransformMask(); createMaskCommon(mask, activeNode, 0, kundo2_i18n("Add Transform Mask"), "KisTransformMask", i18n("Transform Mask"), true, false); } void KisMaskManager::maskProperties() { if (!activeMask()) return; if (activeMask()->inherits("KisFilterMask")) { KisFilterMask *mask = static_cast(activeMask().data()); KisLayerSP layer = qobject_cast(mask->parent().data()); if (! layer) return; KisPaintDeviceSP dev = layer->original(); if (!dev) { return; } KisDlgAdjLayerProps dlg(layer, mask, dev, m_view, mask->filter().data(), mask->name(), i18n("Filter Mask Properties"), m_view->mainWindow(), "dlgeffectmaskprops"); KisFilterConfigurationSP configBefore(mask->filter()); Q_ASSERT(configBefore); QString xmlBefore = configBefore->toXML(); if (dlg.exec() == QDialog::Accepted) { KisFilterConfigurationSP configAfter(dlg.filterConfiguration()); Q_ASSERT(configAfter); QString xmlAfter = configAfter->toXML(); mask->setName(dlg.layerName()); if(xmlBefore != xmlAfter) { KisChangeFilterCmd *cmd = new KisChangeFilterCmd(mask, configBefore->name(), xmlBefore, configAfter->name(), xmlAfter, false); // FIXME: check whether is needed cmd->redo(); m_view->undoAdapter()->addCommand(cmd); m_view->document()->setModified(true); } } else { KisFilterConfigurationSP configAfter(dlg.filterConfiguration()); Q_ASSERT(configAfter); QString xmlAfter = configAfter->toXML(); if(xmlBefore != xmlAfter) { mask->setFilter(KisFilterRegistry::instance()->cloneConfiguration(configBefore.data())); mask->setDirty(); } } } else { // Not much to show for transparency or selection masks? } } diff --git a/libs/ui/kis_node_filter_proxy_model.cpp b/libs/ui/kis_node_filter_proxy_model.cpp index 083741e1af..8686863b7a 100644 --- a/libs/ui/kis_node_filter_proxy_model.cpp +++ b/libs/ui/kis_node_filter_proxy_model.cpp @@ -1,169 +1,169 @@ /* * Copyright (c) 2015 Dmitry Kazakov * * 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_node_filter_proxy_model.h" #include #include "kis_node.h" #include "kis_node_model.h" #include "kis_node_manager.h" #include "kis_signal_compressor.h" #include "kis_image.h" struct KisNodeFilterProxyModel::Private { Private() : nodeModel(0), activeNodeCompressor(1000, KisSignalCompressor::FIRST_INACTIVE) {} KisNodeModel *nodeModel; KisNodeSP pendingActiveNode; KisNodeSP activeNode; QSet acceptedLabels; KisSignalCompressor activeNodeCompressor; bool isUpdatingFilter = false; bool checkIndexAllowedRecursively(QModelIndex srcIndex); }; KisNodeFilterProxyModel::KisNodeFilterProxyModel(QObject *parent) : QSortFilterProxyModel(parent), m_d(new Private) { connect(&m_d->activeNodeCompressor, SIGNAL(timeout()), SLOT(slotUpdateCurrentNodeFilter())); } KisNodeFilterProxyModel::~KisNodeFilterProxyModel() { } void KisNodeFilterProxyModel::setNodeModel(KisNodeModel *model) { m_d->nodeModel = model; setSourceModel(model); } bool KisNodeFilterProxyModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (m_d->isUpdatingFilter && role == KisNodeModel::ActiveRole) { return false; } return QSortFilterProxyModel::setData(index, value, role); } bool KisNodeFilterProxyModel::Private::checkIndexAllowedRecursively(QModelIndex srcIndex) { if (!srcIndex.isValid()) return false; KisNodeSP node = nodeModel->nodeFromIndex(srcIndex); if (node == activeNode || acceptedLabels.contains(node->colorLabelIndex())) { return true; } bool result = false; const int numChildren = srcIndex.model()->rowCount(srcIndex); for (int i = 0; i < numChildren; i++) { QModelIndex child = nodeModel->index(i, 0, srcIndex); if (checkIndexAllowedRecursively(child)) { result = true; break; } } return result; } bool KisNodeFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const { KIS_ASSERT_RECOVER(m_d->nodeModel) { return true; } const QModelIndex index = sourceModel()->index(source_row, 0, source_parent); if (!index.isValid()) return false; KisNodeSP node = m_d->nodeModel->nodeFromIndex(index); return !node || m_d->acceptedLabels.isEmpty() || m_d->checkIndexAllowedRecursively(index); } KisNodeSP KisNodeFilterProxyModel::nodeFromIndex(const QModelIndex &index) const { KIS_ASSERT_RECOVER(m_d->nodeModel) { return 0; } QModelIndex srcIndex = mapToSource(index); return m_d->nodeModel->nodeFromIndex(srcIndex); } QModelIndex KisNodeFilterProxyModel::indexFromNode(KisNodeSP node) const { KIS_ASSERT_RECOVER(m_d->nodeModel) { return QModelIndex(); } QModelIndex srcIndex = m_d->nodeModel->indexFromNode(node); return mapFromSource(srcIndex); } void KisNodeFilterProxyModel::setAcceptedLabels(const QList &value) { m_d->acceptedLabels = QSet::fromList(value); invalidateFilter(); } void KisNodeFilterProxyModel::setActiveNode(KisNodeSP node) { // the new node may temporary become null when the last layer // of the document in removed m_d->pendingActiveNode = node; if (node && indexFromNode(node).isValid()) { m_d->activeNodeCompressor.start(); } else { slotUpdateCurrentNodeFilter(); } } void KisNodeFilterProxyModel::slotUpdateCurrentNodeFilter() { m_d->activeNode = m_d->pendingActiveNode; /** * During the filter update the model might emit "current changed" signals, * which (in their turn) will issue setData(..., KisNodeModel::ActiveRole) * call, leading to a double recursion. Which, obviously, crashes Krita. * * Right now, just blocking the KisNodeModel::ActiveRole call is the * most obvious solution for the problem. */ m_d->isUpdatingFilter = true; invalidateFilter(); m_d->isUpdatingFilter = false; } void KisNodeFilterProxyModel::unsetDummiesFacade() { - m_d->nodeModel->setDummiesFacade(0, 0, 0, 0, 0); + m_d->nodeModel->setDummiesFacade(0, 0, 0, 0, 0, 0); m_d->pendingActiveNode = 0; m_d->activeNode = 0; } diff --git a/libs/ui/kis_node_model.cpp b/libs/ui/kis_node_model.cpp index 916d3f9ed3..4938070cdb 100644 --- a/libs/ui/kis_node_model.cpp +++ b/libs/ui/kis_node_model.cpp @@ -1,706 +1,722 @@ /* * Copyright (c) 2007 Boudewijn Rempt * Copyright (c) 2008 Cyrille Berger * * 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_node_model.h" #include #include #include #include #include #include #include "kis_mimedata.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_dummies_facade_base.h" #include "kis_node_dummies_graph.h" #include "kis_model_index_converter.h" #include "kis_model_index_converter_show_all.h" #include "kis_node_selection_adapter.h" #include "kis_node_insertion_adapter.h" +#include #include "kis_config.h" #include "kis_config_notifier.h" #include struct KisNodeModel::Private { KisImageWSP image; KisShapeController *shapeController = 0; KisNodeSelectionAdapter *nodeSelectionAdapter = 0; KisNodeInsertionAdapter *nodeInsertionAdapter = 0; + KisSelectionActionsAdapter *selectionActionsAdapter = 0; QList updateQueue; QTimer updateTimer; KisModelIndexConverterBase *indexConverter = 0; QPointer dummiesFacade = 0; bool needFinishRemoveRows = false; bool needFinishInsertRows = false; bool showRootLayer = false; bool showGlobalSelection = false; QPersistentModelIndex activeNodeIndex; QPointer parentOfRemovedNode = 0; QSet dropEnabled; }; KisNodeModel::KisNodeModel(QObject * parent) : QAbstractItemModel(parent) , m_d(new Private) { updateSettings(); connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), this, SLOT(updateSettings())); m_d->updateTimer.setSingleShot(true); connect(&m_d->updateTimer, SIGNAL(timeout()), SLOT(processUpdateQueue())); } KisNodeModel::~KisNodeModel() { delete m_d->indexConverter; delete m_d; } KisNodeSP KisNodeModel::nodeFromIndex(const QModelIndex &index) const { Q_ASSERT(index.isValid()); KisNodeDummy *dummy = m_d->indexConverter->dummyFromIndex(index); if (dummy) { return dummy->node(); } return 0; } QModelIndex KisNodeModel::indexFromNode(KisNodeSP node) const { KisNodeDummy *dummy = m_d->dummiesFacade->dummyForNode(node); if(dummy) return m_d->indexConverter->indexFromDummy(dummy); return QModelIndex(); } bool KisNodeModel::belongsToIsolatedGroup(KisImageSP image, KisNodeSP node, KisDummiesFacadeBase *dummiesFacade) { KisNodeSP isolatedRoot = image->isolatedModeRoot(); if (!isolatedRoot) return true; KisNodeDummy *isolatedRootDummy = dummiesFacade->dummyForNode(isolatedRoot); KisNodeDummy *dummy = dummiesFacade->dummyForNode(node); while (dummy) { if (dummy == isolatedRootDummy) { return true; } dummy = dummy->parent(); } return false; } bool KisNodeModel::belongsToIsolatedGroup(KisNodeSP node) const { return belongsToIsolatedGroup(m_d->image, node, m_d->dummiesFacade); } void KisNodeModel::resetIndexConverter() { delete m_d->indexConverter; m_d->indexConverter = 0; if(m_d->dummiesFacade) { m_d->indexConverter = createIndexConverter(); } } KisModelIndexConverterBase *KisNodeModel::createIndexConverter() { if(m_d->showRootLayer) { return new KisModelIndexConverterShowAll(m_d->dummiesFacade, this); } else { return new KisModelIndexConverter(m_d->dummiesFacade, this, m_d->showGlobalSelection); } } void KisNodeModel::regenerateItems(KisNodeDummy *dummy) { const QModelIndex &index = m_d->indexConverter->indexFromDummy(dummy); emit dataChanged(index, index); dummy = dummy->firstChild(); while (dummy) { regenerateItems(dummy); dummy = dummy->nextSibling(); } } void KisNodeModel::slotIsolatedModeChanged() { regenerateItems(m_d->dummiesFacade->rootDummy()); } bool KisNodeModel::showGlobalSelection() const { KisConfig cfg(true); return cfg.showGlobalSelection(); } void KisNodeModel::setShowGlobalSelection(bool value) { KisConfig cfg(false); cfg.setShowGlobalSelection(value); updateSettings(); } void KisNodeModel::updateSettings() { KisConfig cfg(true); bool oldShowRootLayer = m_d->showRootLayer; bool oldShowGlobalSelection = m_d->showGlobalSelection; m_d->showRootLayer = cfg.showRootLayer(); m_d->showGlobalSelection = cfg.showGlobalSelection(); if (m_d->showRootLayer != oldShowRootLayer || m_d->showGlobalSelection != oldShowGlobalSelection) { resetIndexConverter(); beginResetModel(); endResetModel(); } } void KisNodeModel::progressPercentageChanged(int, const KisNodeSP node) { if(!m_d->dummiesFacade) return; // Need to check here as the node might already be removed, but there might // still be some signals arriving from another thread if (m_d->dummiesFacade->hasDummyForNode(node)) { QModelIndex index = indexFromNode(node); emit dataChanged(index, index); } } KisModelIndexConverterBase * KisNodeModel::indexConverter() const { return m_d->indexConverter; } KisDummiesFacadeBase *KisNodeModel::dummiesFacade() const { return m_d->dummiesFacade; } void KisNodeModel::connectDummy(KisNodeDummy *dummy, bool needConnect) { KisNodeSP node = dummy->node(); if (!node) { qWarning() << "Dummy node has no node!" << dummy << dummy->node(); return; } KisNodeProgressProxy *progressProxy = node->nodeProgressProxy(); if(progressProxy) { if(needConnect) { connect(progressProxy, SIGNAL(percentageChanged(int,KisNodeSP)), SLOT(progressPercentageChanged(int,KisNodeSP))); } else { progressProxy->disconnect(this); } } } void KisNodeModel::connectDummies(KisNodeDummy *dummy, bool needConnect) { connectDummy(dummy, needConnect); dummy = dummy->firstChild(); while(dummy) { connectDummies(dummy, needConnect); dummy = dummy->nextSibling(); } } -void KisNodeModel::setDummiesFacade(KisDummiesFacadeBase *dummiesFacade, KisImageWSP image, KisShapeController *shapeController, KisNodeSelectionAdapter *nodeSelectionAdapter, KisNodeInsertionAdapter *nodeInsertionAdapter) +void KisNodeModel::setDummiesFacade(KisDummiesFacadeBase *dummiesFacade, + KisImageWSP image, + KisShapeController *shapeController, + KisNodeSelectionAdapter *nodeSelectionAdapter, + KisNodeInsertionAdapter *nodeInsertionAdapter, + KisSelectionActionsAdapter *selectionActionsAdapter) { QPointer oldDummiesFacade(m_d->dummiesFacade); KisShapeController *oldShapeController = m_d->shapeController; m_d->shapeController = shapeController; m_d->nodeSelectionAdapter = nodeSelectionAdapter; m_d->nodeInsertionAdapter = nodeInsertionAdapter; + m_d->selectionActionsAdapter = selectionActionsAdapter; if (oldDummiesFacade && m_d->image) { m_d->image->disconnect(this); oldDummiesFacade->disconnect(this); connectDummies(m_d->dummiesFacade->rootDummy(), false); } m_d->image = image; m_d->dummiesFacade = dummiesFacade; m_d->parentOfRemovedNode = 0; resetIndexConverter(); if (m_d->dummiesFacade) { KisNodeDummy *rootDummy = m_d->dummiesFacade->rootDummy(); if (rootDummy) { connectDummies(rootDummy, true); } connect(m_d->dummiesFacade, SIGNAL(sigBeginInsertDummy(KisNodeDummy*,int,QString)), SLOT(slotBeginInsertDummy(KisNodeDummy*,int,QString))); connect(m_d->dummiesFacade, SIGNAL(sigEndInsertDummy(KisNodeDummy*)), SLOT(slotEndInsertDummy(KisNodeDummy*))); connect(m_d->dummiesFacade, SIGNAL(sigBeginRemoveDummy(KisNodeDummy*)), SLOT(slotBeginRemoveDummy(KisNodeDummy*))); connect(m_d->dummiesFacade, SIGNAL(sigEndRemoveDummy()), SLOT(slotEndRemoveDummy())); connect(m_d->dummiesFacade, SIGNAL(sigDummyChanged(KisNodeDummy*)), SLOT(slotDummyChanged(KisNodeDummy*))); if (m_d->image.isValid()) { connect(m_d->image, SIGNAL(sigIsolatedModeChanged()), SLOT(slotIsolatedModeChanged())); } } if (m_d->dummiesFacade != oldDummiesFacade || m_d->shapeController != oldShapeController) { beginResetModel(); endResetModel(); } } void KisNodeModel::slotBeginInsertDummy(KisNodeDummy *parent, int index, const QString &metaObjectType) { int row = 0; QModelIndex parentIndex; bool willAdd = m_d->indexConverter->indexFromAddedDummy(parent, index, metaObjectType, parentIndex, row); if(willAdd) { beginInsertRows(parentIndex, row, row); m_d->needFinishInsertRows = true; } } void KisNodeModel::slotEndInsertDummy(KisNodeDummy *dummy) { if(m_d->needFinishInsertRows) { connectDummy(dummy, true); endInsertRows(); m_d->needFinishInsertRows = false; } } void KisNodeModel::slotBeginRemoveDummy(KisNodeDummy *dummy) { if (!dummy) return; // FIXME: is it really what we want? m_d->updateTimer.stop(); m_d->updateQueue.clear(); m_d->parentOfRemovedNode = dummy->parent(); QModelIndex parentIndex; if (m_d->parentOfRemovedNode) { parentIndex = m_d->indexConverter->indexFromDummy(m_d->parentOfRemovedNode); } QModelIndex itemIndex = m_d->indexConverter->indexFromDummy(dummy); if (itemIndex.isValid()) { connectDummy(dummy, false); beginRemoveRows(parentIndex, itemIndex.row(), itemIndex.row()); m_d->needFinishRemoveRows = true; } } void KisNodeModel::slotEndRemoveDummy() { if(m_d->needFinishRemoveRows) { endRemoveRows(); m_d->needFinishRemoveRows = false; } } void KisNodeModel::slotDummyChanged(KisNodeDummy *dummy) { if (!m_d->updateQueue.contains(dummy)) { m_d->updateQueue.append(dummy); } m_d->updateTimer.start(1000); } void addChangedIndex(const QModelIndex &idx, QSet *indexes) { if (!idx.isValid() || indexes->contains(idx)) return; indexes->insert(idx); const int rowCount = idx.model()->rowCount(idx); for (int i = 0; i < rowCount; i++) { addChangedIndex(idx.model()->index(i, 0, idx), indexes); } } void KisNodeModel::processUpdateQueue() { QSet indexes; Q_FOREACH (KisNodeDummy *dummy, m_d->updateQueue) { QModelIndex index = m_d->indexConverter->indexFromDummy(dummy); addChangedIndex(index, &indexes); } Q_FOREACH (const QModelIndex &index, indexes) { emit dataChanged(index, index); } m_d->updateQueue.clear(); } QModelIndex KisNodeModel::index(int row, int col, const QModelIndex &parent) const { if(!m_d->dummiesFacade || !hasIndex(row, col, parent)) return QModelIndex(); QModelIndex itemIndex; KisNodeDummy *dummy = m_d->indexConverter->dummyFromRow(row, parent); if(dummy) { itemIndex = m_d->indexConverter->indexFromDummy(dummy); } return itemIndex; } int KisNodeModel::rowCount(const QModelIndex &parent) const { if(!m_d->dummiesFacade) return 0; return m_d->indexConverter->rowCount(parent); } int KisNodeModel::columnCount(const QModelIndex&) const { return 1; } QModelIndex KisNodeModel::parent(const QModelIndex &index) const { if(!m_d->dummiesFacade || !index.isValid()) return QModelIndex(); KisNodeDummy *dummy = m_d->indexConverter->dummyFromIndex(index); KisNodeDummy *parentDummy = dummy->parent(); QModelIndex parentIndex; if(parentDummy) { parentIndex = m_d->indexConverter->indexFromDummy(parentDummy); } return parentIndex; } QVariant KisNodeModel::data(const QModelIndex &index, int role) const { if (!m_d->dummiesFacade || !index.isValid() || !m_d->image.isValid()) return QVariant(); KisNodeSP node = nodeFromIndex(index); switch (role) { case Qt::DisplayRole: return node->name(); case Qt::DecorationRole: return node->icon(); case Qt::EditRole: return node->name(); case Qt::SizeHintRole: return m_d->image->size(); // FIXME case Qt::TextColorRole: return belongsToIsolatedGroup(node) && !node->projectionLeaf()->isDroppedMask() ? QVariant() : QVariant(QColor(Qt::gray)); case Qt::FontRole: { QFont baseFont; if (node->projectionLeaf()->isDroppedMask()) { baseFont.setStrikeOut(true); } if (m_d->activeNodeIndex == index) { baseFont.setBold(true); } return baseFont; } case KisNodeModel::PropertiesRole: return QVariant::fromValue(node->sectionModelProperties()); case KisNodeModel::AspectRatioRole: return double(m_d->image->width()) / m_d->image->height(); case KisNodeModel::ProgressRole: { KisNodeProgressProxy *proxy = node->nodeProgressProxy(); return proxy ? proxy->percentage() : -1; } case KisNodeModel::ActiveRole: { return m_d->activeNodeIndex == index; } case KisNodeModel::ShouldGrayOutRole: { return !node->visible(true); } case KisNodeModel::ColorLabelIndexRole: { return node->colorLabelIndex(); } default: if (role >= int(KisNodeModel::BeginThumbnailRole) && belongsToIsolatedGroup(node)) { const int maxSize = role - int(KisNodeModel::BeginThumbnailRole); QSize size = node->extent().size(); size.scale(maxSize, maxSize, Qt::KeepAspectRatio); if (size.width() == 0 || size.height() == 0) { // No thumbnail can be shown if there isn't width or height... return QVariant(); } return node->createThumbnail(size.width(), size.height()); } else { return QVariant(); } } return QVariant(); } Qt::ItemFlags KisNodeModel::flags(const QModelIndex &index) const { if(!m_d->dummiesFacade || !index.isValid()) return Qt::ItemIsDropEnabled; Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsEditable; if (m_d->dropEnabled.contains(index.internalId())) { flags |= Qt::ItemIsDropEnabled; } return flags; } bool KisNodeModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (role == KisNodeModel::DropEnabled) { const QMimeData *mimeData = static_cast(value.value()); setDropEnabled(mimeData); return true; } if (role == KisNodeModel::ActiveRole || role == KisNodeModel::AlternateActiveRole) { QModelIndex parentIndex; if (!index.isValid() && m_d->parentOfRemovedNode && m_d->dummiesFacade && m_d->indexConverter) { parentIndex = m_d->indexConverter->indexFromDummy(m_d->parentOfRemovedNode); m_d->parentOfRemovedNode = 0; } KisNodeSP activatedNode; if (index.isValid() && value.toBool()) { activatedNode = nodeFromIndex(index); } else if (parentIndex.isValid() && value.toBool()) { activatedNode = nodeFromIndex(parentIndex); } else { activatedNode = 0; } QModelIndex newActiveNode = activatedNode ? indexFromNode(activatedNode) : QModelIndex(); if (role == KisNodeModel::ActiveRole && value.toBool() && m_d->activeNodeIndex == newActiveNode) { return true; } m_d->activeNodeIndex = newActiveNode; if (m_d->nodeSelectionAdapter) { m_d->nodeSelectionAdapter->setActiveNode(activatedNode); } if (role == KisNodeModel::AlternateActiveRole) { emit toggleIsolateActiveNode(); } emit dataChanged(index, index); return true; } if(!m_d->dummiesFacade || !index.isValid()) return false; bool result = true; + bool shouldUpdate = true; bool shouldUpdateRecursively = false; KisNodeSP node = nodeFromIndex(index); switch (role) { case Qt::DisplayRole: case Qt::EditRole: node->setName(value.toString()); break; case KisNodeModel::PropertiesRole: { // don't record undo/redo for visibility, locked or alpha locked changes KisBaseNode::PropertyList proplist = value.value(); KisNodePropertyListCommand::setNodePropertiesNoUndo(node, m_d->image, proplist); shouldUpdateRecursively = true; break; } + case KisNodeModel::SelectOpaqueRole: + if (node && m_d->selectionActionsAdapter) { + SelectionAction action = SelectionAction(value.toInt()); + m_d->selectionActionsAdapter->selectOpaqueOnNode(node, action); + } + shouldUpdate = false; + break; default: result = false; } - if(result) { + if (result && shouldUpdate) { if (shouldUpdateRecursively) { QSet indexes; addChangedIndex(index, &indexes); Q_FOREACH (const QModelIndex &index, indexes) { emit dataChanged(index, index); } } else { emit dataChanged(index, index); } } return result; } Qt::DropActions KisNodeModel::supportedDragActions() const { return Qt::CopyAction | Qt::MoveAction; } Qt::DropActions KisNodeModel::supportedDropActions() const { return Qt::MoveAction | Qt::CopyAction; } bool KisNodeModel::hasDummiesFacade() { return m_d->dummiesFacade != 0; } QStringList KisNodeModel::mimeTypes() const { QStringList types; types << QLatin1String("application/x-krita-node"); types << QLatin1String("application/x-qt-image"); return types; } QMimeData * KisNodeModel::mimeData(const QModelIndexList &indexes) const { KisNodeList nodes; Q_FOREACH (const QModelIndex &idx, indexes) { nodes << nodeFromIndex(idx); } return KisMimeData::mimeForLayers(nodes, m_d->image); } bool KisNodeModel::dropMimeData(const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent) { Q_UNUSED(column); bool copyNode = (action == Qt::CopyAction); KisNodeDummy *parentDummy = 0; KisNodeDummy *aboveThisDummy = 0; parentDummy = parent.isValid() ? m_d->indexConverter->dummyFromIndex(parent) : m_d->dummiesFacade->rootDummy(); if (row == -1) { aboveThisDummy = parent.isValid() ? parentDummy->lastChild() : 0; } else { aboveThisDummy = row < m_d->indexConverter->rowCount(parent) ? m_d->indexConverter->dummyFromRow(row, parent) : 0; } return KisMimeData::insertMimeLayers(data, m_d->image, m_d->shapeController, parentDummy, aboveThisDummy, copyNode, m_d->nodeInsertionAdapter); } bool KisNodeModel::canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const { if (parent.isValid()) { // drop occurred on an item. always return true as returning false will mess up // QT5's drag handling (see KisNodeModel::setDropEnabled). return true; } else { return QAbstractItemModel::canDropMimeData(data, action, row, column, parent); } } void KisNodeModel::setDropEnabled(const QMimeData *data) { // what happens here should really happen in KisNodeModel::canDropMimeData(), but QT5 // will mess up if an item's Qt::ItemIsDropEnabled does not match what is returned by // canDropMimeData; specifically, if we set the flag, but decide in canDropMimeData() // later on that an "onto" drag is not allowed, QT will display an drop indicator for // insertion, but not perform any drop when the mouse is released. // the only robust implementation seems to set all flags correctly, which is done here. bool copyNode = false; KisNodeList nodes = KisMimeData::loadNodesFast(data, m_d->image, m_d->shapeController, copyNode); m_d->dropEnabled.clear(); updateDropEnabled(nodes); } void KisNodeModel::updateDropEnabled(const QList &nodes, QModelIndex parent) { for (int r = 0; r < rowCount(parent); r++) { QModelIndex idx = index(r, 0, parent); KisNodeSP target = nodeFromIndex(idx); bool dropEnabled = true; Q_FOREACH (const KisNodeSP &node, nodes) { if (!target->allowAsChild(node)) { dropEnabled = false; break; } } if (dropEnabled) { m_d->dropEnabled.insert(idx.internalId()); } emit dataChanged(idx, idx); // indicate to QT that flags() have changed if (hasChildren(idx)) { updateDropEnabled(nodes, idx); } } } diff --git a/libs/ui/kis_node_model.h b/libs/ui/kis_node_model.h index 7d737af260..52e4e3146c 100644 --- a/libs/ui/kis_node_model.h +++ b/libs/ui/kis_node_model.h @@ -1,176 +1,188 @@ /* * Copyright (c) 2007 Boudewijn Rempt * * 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. */ #ifndef KIS_NODE_MODEL #define KIS_NODE_MODEL #include "kritaui_export.h" #include #include #include #include #include #include +#include + class KisDummiesFacadeBase; class KisNodeDummy; class KisShapeController; class KisModelIndexConverterBase; class KisNodeSelectionAdapter; class KisNodeInsertionAdapter; +class KisSelectionActionsAdapter; /** * KisNodeModel offers a Qt model-view compatible view of the node * hierarchy. The KisNodeView displays a thumbnail and a row of * icon properties for every document section. * * Note that there's a discrepancy between the krita node tree model * and the model Qt wants to see: we hide the root node from Qt. * * The node model also shows an inverse view of the layer tree: we want * the first layer to show up at the bottom. * * See also the Qt documentation for QAbstractItemModel. * This class extends that interface to provide a name and set of toggle * properties (like visible, locked, selected.) * */ class KRITAUI_EXPORT KisNodeModel : public QAbstractItemModel { Q_OBJECT public: /// Extensions to Qt::ItemDataRole. enum ItemDataRole { /// Whether the section is the active one ActiveRole = Qt::UserRole + 1, /// A list of properties the part has. PropertiesRole, /// The aspect ratio of the section as a floating point value: width divided by height. AspectRatioRole, /// Use to communicate a progress report to the section delegate on an action (a value of -1 or a QVariant() disable the progress bar ProgressRole, /// Speacial activation role which is emitted when the user Atl-clicks on a section /// The item is first activated with ActiveRole, then a separate AlternateActiveRole comes AlternateActiveRole, // When a layer is not (recursively) visible, then it should be gayed out ShouldGrayOutRole, // An index of a color label associated with the node ColorLabelIndexRole, // Instruct this model to update all its items' Qt::ItemIsDropEnabled flags in order to // reflect if the item allows an "onto" drop of the given QMimeData*. DropEnabled, + // Instructs the model to activate "select opaque" action, + // the selection action (of type SelectionAction) value + // is passed via QVariant as integer + SelectOpaqueRole, + /// This is to ensure that we can extend the data role in the future, since it's not possible to add a role after BeginThumbnailRole (due to "Hack") - ReservedRole = 99, + ReservedRole = Qt::UserRole + 99, /** * For values of BeginThumbnailRole or higher, a thumbnail of the layer of which neither dimension * is larger than (int) value - (int) BeginThumbnailRole. * This is a hack to work around the fact that Interview doesn't have a nice way to * request thumbnails of arbitrary size. */ BeginThumbnailRole }; public: // from QAbstractItemModel KisNodeModel(QObject * parent); ~KisNodeModel() override; - void setDummiesFacade(KisDummiesFacadeBase *dummiesFacade, KisImageWSP image, KisShapeController *shapeController, KisNodeSelectionAdapter *nodeSelectionAdapter, KisNodeInsertionAdapter *nodeInsertionAdapter); + void setDummiesFacade(KisDummiesFacadeBase *dummiesFacade, + KisImageWSP image, + KisShapeController *shapeController, + KisNodeSelectionAdapter *nodeSelectionAdapter, + KisNodeInsertionAdapter *nodeInsertionAdapter, + KisSelectionActionsAdapter *selectionActionsAdapter); KisNodeSP nodeFromIndex(const QModelIndex &index) const; QModelIndex indexFromNode(KisNodeSP node) const; bool showGlobalSelection() const; - public Q_SLOTS: void setShowGlobalSelection(bool value); public: int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; QModelIndex parent(const QModelIndex &index) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; Qt::ItemFlags flags(const QModelIndex &index) const override; bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; QStringList mimeTypes() const override; QMimeData* mimeData(const QModelIndexList & indexes) const override; bool dropMimeData(const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent) override; bool canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const override; Qt::DropActions supportedDragActions() const override; Qt::DropActions supportedDropActions() const override; bool hasDummiesFacade(); static bool belongsToIsolatedGroup(KisImageSP image, KisNodeSP node, KisDummiesFacadeBase *dummiesFacade); Q_SIGNALS: void toggleIsolateActiveNode(); protected Q_SLOTS: void slotBeginInsertDummy(KisNodeDummy *parent, int index, const QString &metaObjectType); void slotEndInsertDummy(KisNodeDummy *dummy); void slotBeginRemoveDummy(KisNodeDummy *dummy); void slotEndRemoveDummy(); void slotDummyChanged(KisNodeDummy *dummy); void slotIsolatedModeChanged(); void updateSettings(); void processUpdateQueue(); void progressPercentageChanged(int, const KisNodeSP); protected: virtual KisModelIndexConverterBase *createIndexConverter(); KisModelIndexConverterBase *indexConverter() const; KisDummiesFacadeBase *dummiesFacade() const; private: friend class KisModelIndexConverter; friend class KisModelIndexConverterShowAll; void connectDummy(KisNodeDummy *dummy, bool needConnect); void connectDummies(KisNodeDummy *dummy, bool needConnect); void resetIndexConverter(); void regenerateItems(KisNodeDummy *dummy); bool belongsToIsolatedGroup(KisNodeSP node) const; void setDropEnabled(const QMimeData *data); void updateDropEnabled(const QList &nodes, QModelIndex parent = QModelIndex()); private: struct Private; Private * const m_d; }; #endif diff --git a/libs/ui/kis_selection_manager.cc b/libs/ui/kis_selection_manager.cc index ebdb562892..b7ff576663 100644 --- a/libs/ui/kis_selection_manager.cc +++ b/libs/ui/kis_selection_manager.cc @@ -1,607 +1,678 @@ /* * Copyright (c) 2004 Boudewijn Rempt * Copyright (c) 2007 Sven Langkamp * * The outline algorithm 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 #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 "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 "actions/KisPasteActionFactory.h" #include "kis_action.h" #include "kis_action_manager.h" #include "operations/kis_operation_configuration.h" //new #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_selection"); m_invert->setOperationID("invertselection"); actionManager->registerOperation(new KisInvertSelectionOperation); 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) { connect(m_imageView->canvasBase()->selectedShapesProxy(), 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()) { return m_view->canvasBase()->selectedShapesProxy()->selection()->count() > 0; } return false; } bool KisSelectionManager::haveShapesInClipboard() { KoSvgPaste paste; return paste.hasShapes(); } 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(false, m_view); } void KisSelectionManager::pasteAt() { KisPasteActionFactory factory; factory.run(true, m_view); } 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(); KoShapeStrokeSP 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(KoShapeStrokeSP()); } } 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; } + +#include "kis_image_barrier_locker.h" +#include "kis_selection_tool_helper.h" + +void KisSelectionManager::selectOpaqueOnNode(KisNodeSP node, SelectionAction action) +{ + KisImageSP image = m_view->image(); + + if (!m_view->blockUntilOperationsFinished(image)) { + return; + } + + KUndo2MagicString actionName; + KisPixelSelectionSP tmpSel = KisPixelSelectionSP(new KisPixelSelection()); + KisCanvas2 *canvas = m_view->canvasBase(); + + + { + KisImageBarrierLocker locker(image); + + KisPaintDeviceSP device = node->projection(); + if (!device) device = node->paintDevice(); + if (!device) device = node->original(); + KIS_ASSERT_RECOVER_RETURN(canvas && device); + + QRect rc = device->exactBounds(); + if (rc.isEmpty()) return; + + /** + * If there is nothing selected, just create a new selection + */ + if (!canvas->imageView()->selection()) { + action = SELECTION_REPLACE; + } + + switch (action) { + case SELECTION_ADD: + actionName = kundo2_i18n("Select Opaque (Add)"); + break; + case SELECTION_SUBTRACT: + actionName = kundo2_i18n("Select Opaque (Subtract)"); + break; + case SELECTION_INTERSECT: + actionName = kundo2_i18n("Select Opaque (Intersect)"); + break; + default: + actionName = kundo2_i18n("Select Opaque"); + break; + } + + qint32 x, y, w, h; + rc.getRect(&x, &y, &w, &h); + + const KoColorSpace * cs = device->colorSpace(); + + KisHLineConstIteratorSP deviter = device->createHLineConstIteratorNG(x, y, w); + KisHLineIteratorSP selIter = tmpSel ->createHLineIteratorNG(x, y, w); + + for (int row = y; row < h + y; ++row) { + do { + *selIter->rawData() = cs->opacityU8(deviter->oldRawData()); + } while (deviter->nextPixel() && selIter->nextPixel()); + deviter->nextRow(); + selIter->nextRow(); + } + } + + KisSelectionToolHelper helper(canvas, actionName); + tmpSel->invalidateOutlineCache(); + helper.selectPixelSelection(tmpSel, action); +} diff --git a/libs/ui/kis_selection_manager.h b/libs/ui/kis_selection_manager.h index 5b8046030e..bd165eb952 100644 --- a/libs/ui/kis_selection_manager.h +++ b/libs/ui/kis_selection_manager.h @@ -1,171 +1,174 @@ /* * Copyright (c) 2004 Boudewijn Rempt * * 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. */ #ifndef KIS_SELECTION_MANAGER_ #define KIS_SELECTION_MANAGER_ #include #include #include #include #include "KisView.h" +#include #include class KisActionManager; class KisAction; class QAction; class KoViewConverter; class KisDocument; class KisViewManager; class KisClipboard; class KisNodeCommandsAdapter; class KisView; class KisSelectionFilter; class KisSelectionDecoration; /** * The selection manager is responsible selections * and the clipboard. */ class KRITAUI_EXPORT KisSelectionManager : public QObject { Q_OBJECT Q_PROPERTY(bool displaySelection READ displaySelection NOTIFY displaySelectionChanged); Q_PROPERTY(bool havePixelsSelected READ havePixelsSelected NOTIFY currentSelectionChanged); public: KisSelectionManager(KisViewManager * view); ~KisSelectionManager() override; void setup(KisActionManager* actionManager); void setView(QPointerimageView); public: /** * This function return if the selection should be displayed */ bool displaySelection(); bool showSelectionAsMask() const; public Q_SLOTS: void updateGUI(); void selectionChanged(); void clipboardDataChanged(); void cut(); void copy(); void cutSharp(); void copySharp(); void copyMerged(); void paste(); void pasteNew(); void pasteAt(); void cutToNewLayer(); void selectAll(); void deselect(); void invert(); void clear(); void fillForegroundColor(); void fillBackgroundColor(); void fillPattern(); void fillForegroundColorOpacity(); void fillBackgroundColorOpacity(); void fillPatternOpacity(); void reselect(); void convertToVectorSelection(); void convertShapesToVectorSelection(); void convertToShape(); void copySelectionToNewLayer(); void toggleDisplaySelection(); void shapeSelectionChanged(); void imageResizeToSelection(); void paintSelectedShapes(); void slotToggleSelectionDecoration(); void slotStrokeSelection(); + void selectOpaqueOnNode(KisNodeSP node, SelectionAction action); + Q_SIGNALS: void currentSelectionChanged(); void signalUpdateGUI(); void displaySelectionChanged(); void strokeSelected(); public: bool havePixelsSelected(); bool havePixelsInClipboard(); bool haveShapesSelected(); bool haveShapesInClipboard(); /// Checks if the current selection is editable and has some pixels selected in the pixel selection bool havePixelSelectionWithPixels(); private: void fill(const KoColor& color, bool fillWithPattern, const QString& transactionText); void updateStatusBar(); KisViewManager * m_view; KisDocument * m_doc; QPointerm_imageView; KisClipboard * m_clipboard; KisNodeCommandsAdapter* m_adapter; KisAction *m_copy; KisAction *m_copyMerged; KisAction *m_cut; KisAction *m_paste; KisAction *m_pasteAt; KisAction *m_pasteNew; KisAction *m_cutToNewLayer; KisAction *m_selectAll; KisAction *m_deselect; KisAction *m_clear; KisAction *m_reselect; KisAction *m_invert; KisAction *m_copyToNewLayer; KisAction *m_fillForegroundColor; KisAction *m_fillBackgroundColor; KisAction *m_fillPattern; KisAction *m_fillForegroundColorOpacity; KisAction *m_fillBackgroundColorOpacity; KisAction *m_fillPatternOpacity; KisAction *m_imageResizeToSelection; KisAction *m_strokeShapes; KisAction *m_toggleDisplaySelection; KisAction *m_toggleSelectionOverlayMode; KisAction *m_strokeSelected; QList m_pluginActions; QPointer m_selectionDecoration; }; #endif // KIS_SELECTION_MANAGER_ diff --git a/libs/ui/tests/FreehandStrokeBenchmark.cpp b/libs/ui/tests/FreehandStrokeBenchmark.cpp index 9bc3094520..a185279ff7 100644 --- a/libs/ui/tests/FreehandStrokeBenchmark.cpp +++ b/libs/ui/tests/FreehandStrokeBenchmark.cpp @@ -1,156 +1,157 @@ /* * Copyright (c) 2017 Dmitry Kazakov * * 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 "FreehandStrokeBenchmark.h" #include #include #include #include "stroke_testing_utils.h" #include "strokes/freehand_stroke.h" #include "strokes/KisFreehandStrokeInfo.h" #include "kis_resources_snapshot.h" #include "kis_image.h" #include class FreehandStrokeBenchmarkTester : public utils::StrokeTester { public: FreehandStrokeBenchmarkTester(const QString &presetFilename) : StrokeTester("freehand_benchmark", QSize(5000, 5000), presetFilename) { + Q_UNUSED(m_strokeInfo); setBaseFuzziness(3); } void setCpuCoresLimit(int value) { m_cpuCoresLimit = value; } protected: using utils::StrokeTester::initImage; void initImage(KisImageWSP image, KisNodeSP activeNode) override { Q_UNUSED(activeNode); if (m_cpuCoresLimit > 0) { image->setWorkingThreadsLimit(m_cpuCoresLimit); } } KisStrokeStrategy* createStroke(KisResourcesSnapshotSP resources, KisImageWSP image) override { Q_UNUSED(image); KisFreehandStrokeInfo *strokeInfo = new KisFreehandStrokeInfo(); QScopedPointer stroke( new FreehandStrokeStrategy(resources, strokeInfo, kundo2_noi18n("Freehand Stroke"))); return stroke.take(); } void addPaintingJobs(KisImageWSP image, KisResourcesSnapshotSP resources) override { addPaintingJobs(image, resources, 0); } void addPaintingJobs(KisImageWSP image, KisResourcesSnapshotSP resources, int iteration) override { Q_UNUSED(iteration); Q_UNUSED(resources); for (int y = 100; y < 4900; y += 300) { KisPaintInformation pi1; KisPaintInformation pi2; pi1 = KisPaintInformation(QPointF(100, y), 0.5); pi2 = KisPaintInformation(QPointF(4900, y + 100), 1.0); QScopedPointer data( new FreehandStrokeStrategy::Data(0, pi1, pi2)); image->addJob(strokeId(), data.take()); } image->addJob(strokeId(), new FreehandStrokeStrategy::UpdateData(true)); } private: KisFreehandStrokeInfo *m_strokeInfo; int m_cpuCoresLimit = -1; }; void benchmarkBrush(const QString &presetName) { FreehandStrokeBenchmarkTester tester(presetName); for (int i = 1; i <= QThread::idealThreadCount(); i++) { tester.setCpuCoresLimit(i); tester.benchmark(); qDebug() << qPrintable(QString("Cores: %1 Time: %2 (ms)").arg(i).arg(tester.lastStrokeTime())); } } #include void FreehandStrokeBenchmark::initTestCase() { KoResourcePaths::addResourceType("kis_brushes", "data", FILES_DATA_DIR); } void FreehandStrokeBenchmark::testDefaultTip() { benchmarkBrush("testing_1000px_auto_deafult.kpp"); } void FreehandStrokeBenchmark::testSoftTip() { benchmarkBrush("testing_1000px_auto_soft.kpp"); } void FreehandStrokeBenchmark::testGaussianTip() { benchmarkBrush("testing_1000px_auto_gaussian.kpp"); } void FreehandStrokeBenchmark::testRectangularTip() { benchmarkBrush("testing_1000px_auto_rectangular.kpp"); } void FreehandStrokeBenchmark::testRectGaussianTip() { benchmarkBrush("testing_1000px_auto_gaussian_rect.kpp"); } void FreehandStrokeBenchmark::testRectSoftTip() { benchmarkBrush("testing_1000px_auto_soft_rect.kpp"); } void FreehandStrokeBenchmark::testStampTip() { benchmarkBrush("testing_1000px_stamp_450_rotated.kpp"); } void FreehandStrokeBenchmark::testColorsmudgeDefaultTip() { benchmarkBrush("testing_200px_colorsmudge_default.kpp"); } QTEST_MAIN(FreehandStrokeBenchmark) diff --git a/libs/ui/tests/KisFrameCacheStoreTest.cpp b/libs/ui/tests/KisFrameCacheStoreTest.cpp index 7566cbf48f..1bceff71bc 100644 --- a/libs/ui/tests/KisFrameCacheStoreTest.cpp +++ b/libs/ui/tests/KisFrameCacheStoreTest.cpp @@ -1,197 +1,197 @@ /* * Copyright (c) 2018 Dmitry Kazakov * * 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 "KisFrameCacheStoreTest.h" #include #include #include #include "KisAsyncAnimationRendererBase.h" #include "kis_image_animation_interface.h" #include "opengl/KisOpenGLUpdateInfoBuilder.h" #include "KoColorSpaceRegistry.h" #include "KoColorSpace.h" // TODO: conversion options into a separate file! #include "kis_update_info.h" #include "opengl/kis_texture_tile_update_info.h" #include "KisFrameCacheStore.h" static const int maxTileSize = 256; bool compareTextureTileUpdateInfo(KisTextureTileUpdateInfoSP tile1, KisTextureTileUpdateInfoSP tile2) { KIS_COMPARE_RF(tile1->patchLevelOfDetail(), tile2->patchLevelOfDetail()); KIS_COMPARE_RF(tile1->realPatchOffset(), tile2->realPatchOffset()); KIS_COMPARE_RF(tile1->realPatchRect(), tile2->realPatchRect()); KIS_COMPARE_RF(tile1->realTileSize(), tile2->realTileSize()); KIS_COMPARE_RF(tile1->isTopmost(), tile2->isTopmost()); KIS_COMPARE_RF(tile1->isLeftmost(), tile2->isLeftmost()); KIS_COMPARE_RF(tile1->isRightmost(), tile2->isRightmost()); KIS_COMPARE_RF(tile1->isBottommost(), tile2->isBottommost()); KIS_COMPARE_RF(tile1->isEntireTileUpdated(), tile2->isEntireTileUpdated()); KIS_COMPARE_RF(tile1->tileCol(), tile2->tileCol()); KIS_COMPARE_RF(tile1->tileRow(), tile2->tileRow()); KIS_COMPARE_RF(tile1->pixelSize(), tile2->pixelSize()); KIS_COMPARE_RF(tile1->valid(), tile2->valid()); KIS_COMPARE_RF(tile1->patchPixelsLength(), tile2->patchPixelsLength()); const int numRealPixelBytes = tile1->realPatchRect().width() * tile1->realPatchRect().height() * tile1->pixelSize(); if (memcmp(tile1->data(), tile2->data(), numRealPixelBytes) != 0) { qWarning() << "Tile pixels differ!"; qWarning() << " " << ppVar(tile1->tileCol()) << ppVar(tile1->tileRow()); qWarning() << " " << ppVar(numRealPixelBytes); quint8 *src = tile1->data(); quint8 *dst = tile2->data(); for (int i = 0; i < numRealPixelBytes; i++) { if (*src != *dst) { qDebug() << " " << ppVar(i) << ppVar(*src) << ppVar(*dst); } src++; dst++; } return false; } return true; } bool compareUpdateInfo(KisOpenGLUpdateInfoSP info1, KisOpenGLUpdateInfoSP info2) { KIS_COMPARE_RF(info1->dirtyImageRect(), info2->dirtyImageRect()); KIS_COMPARE_RF(info1->levelOfDetail(), info2->levelOfDetail()); KIS_COMPARE_RF(info1->tileList.size(), info2->tileList.size()); for (int i = 0; i < info1->tileList.size(); i++) { if (!compareTextureTileUpdateInfo(info1->tileList[i], info2->tileList[i])) { return false; } } return true; } class TestFramesRenderer : public KisAsyncAnimationRendererBase { Q_OBJECT public: TestFramesRenderer() : m_pool(m_poolRegistry.getPool(maxTileSize, maxTileSize)) { m_updateInfoBuilder.setTextureInfoPool(m_pool); const KoColorSpace *dstColorSpace = KoColorSpaceRegistry::instance()->rgb8(); m_updateInfoBuilder.setConversionOptions( ConversionOptions(dstColorSpace, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags())); // TODO: refactor setting texture size in raw values! m_updateInfoBuilder.setTextureBorder(8); m_updateInfoBuilder.setEffectiveTextureSize(QSize(256 - 16, 256 - 16)); connect(this, SIGNAL(sigCompleteRegenerationInternal(int)), SLOT(notifyFrameCompleted(int))); connect(this, SIGNAL(sigCancelRegenerationInternal(int)), SLOT(notifyFrameCancelled(int))); } void frameCompletedCallback(int frame, const QRegion &requestedRegion) override { ENTER_FUNCTION() << ppVar(frame); KisImageSP image = requestedImage(); KIS_SAFE_ASSERT_RECOVER_NOOP(frame == image->animationInterface()->currentTime()); // by default we request update for the entire image KIS_SAFE_ASSERT_RECOVER_NOOP(requestedRegion == image->bounds()); KisOpenGLUpdateInfoSP info = m_updateInfoBuilder.buildUpdateInfo(image->bounds(), image, true); KIS_ASSERT_RECOVER_NOOP(info); qDebug() << ppVar(info->tileList.size()); KisOpenGLUpdateInfoSP infoForSave = m_updateInfoBuilder.buildUpdateInfo(image->bounds(), image, true); m_store.saveFrame(11, infoForSave, image->bounds()); KIS_SAFE_ASSERT_RECOVER_NOOP(m_store.hasFrame(11)); KisOpenGLUpdateInfoSP loadedInfo = m_store.loadFrame(11, m_updateInfoBuilder); qDebug() << ppVar(loadedInfo->tileList.size()); KIS_SAFE_ASSERT_RECOVER_NOOP(compareUpdateInfo(info, loadedInfo)); emit sigCompleteRegenerationInternal(frame); } - void frameCancelledCallback(int frame) { + void frameCancelledCallback(int frame) override { emit sigCancelRegenerationInternal(frame); } Q_SIGNALS: void sigCompleteRegenerationInternal(int frame); void sigCancelRegenerationInternal(int frame); private: KisOpenGLUpdateInfoBuilder m_updateInfoBuilder; KisTextureTileInfoPoolRegistry m_poolRegistry; KisTextureTileInfoPoolSP m_pool; KisFrameCacheStore m_store; }; void KisFrameCacheStoreTest::test() { QRect refRect(QRect(0,0,512,512)); TestUtil::MaskParent p(refRect); const KoColor fillColor(Qt::red, p.image->colorSpace()); KisPaintLayerSP layer1 = p.layer; layer1->paintDevice()->fill(QRect(100,100,300,300), fillColor); TestFramesRenderer renderer; renderer.startFrameRegeneration(p.image, 10); p.image->waitForDone(); while (renderer.isActive()) { QTest::qWait(500); } } QTEST_MAIN(KisFrameCacheStoreTest) #include "KisFrameCacheStoreTest.moc" diff --git a/libs/ui/tests/KisPaintOnTransparencyMaskTest.cpp b/libs/ui/tests/KisPaintOnTransparencyMaskTest.cpp index a1d9b6ab1e..74c28da94c 100644 --- a/libs/ui/tests/KisPaintOnTransparencyMaskTest.cpp +++ b/libs/ui/tests/KisPaintOnTransparencyMaskTest.cpp @@ -1,134 +1,134 @@ #include "KisPaintOnTransparencyMaskTest.h" #include #include #include #include "stroke_testing_utils.h" #include "strokes/freehand_stroke.h" #include "strokes/KisFreehandStrokeInfo.h" #include "kis_resources_snapshot.h" #include "kis_image.h" #include #include "kis_transparency_mask.h" #include "kis_paint_device_debug_utils.h" #include "kis_tool_utils.h" #include "kis_sequential_iterator.h" class PaintOnTransparencyMaskTester : public utils::StrokeTester { public: PaintOnTransparencyMaskTester(const QString &presetFilename) : StrokeTester("freehand_benchmark", QSize(5000, 5000), presetFilename) { setBaseFuzziness(3); } protected: using utils::StrokeTester::initImage; void initImage(KisImageWSP image, KisNodeSP activeNode) override { activeNode->paintDevice()->fill(QRect(0,0,1024,1024), KoColor(Qt::red, image->colorSpace())); m_mask = new KisTransparencyMask(); m_mask->setSelection(new KisSelection()); m_mask->paintDevice()->clear(); image->addNode(m_mask, activeNode); image->setWorkingThreadsLimit(8); } using utils::StrokeTester::modifyResourceManager; void modifyResourceManager(KoCanvasResourceManager *manager, - KisImageWSP image) { + KisImageWSP image) override { KoColor color(Qt::red, image->colorSpace()); color.setOpacity(0.5); QVariant i; i.setValue(color); manager->setResource(KoCanvasResourceManager::ForegroundColor, i); } KisStrokeStrategy* createStroke(KisResourcesSnapshotSP resources, KisImageWSP image) override { Q_UNUSED(image); resources->setCurrentNode(m_mask); KisFreehandStrokeInfo *strokeInfo = new KisFreehandStrokeInfo(); QScopedPointer stroke( new FreehandStrokeStrategy(resources, strokeInfo, kundo2_noi18n("Freehand Stroke"))); return stroke.take(); } void addPaintingJobs(KisImageWSP image, KisResourcesSnapshotSP resources) override { addPaintingJobs(image, resources, 0); } void addPaintingJobs(KisImageWSP image, KisResourcesSnapshotSP resources, int iteration) override { Q_UNUSED(iteration); Q_UNUSED(resources); KisPaintInformation pi1; KisPaintInformation pi2; pi1 = KisPaintInformation(QPointF(100, 100), 1.0); pi2 = KisPaintInformation(QPointF(800, 800), 1.0); QScopedPointer data( new FreehandStrokeStrategy::Data(0, pi1, pi2)); image->addJob(strokeId(), data.take()); image->addJob(strokeId(), new FreehandStrokeStrategy::UpdateData(true)); } void checkDeviceIsEmpty(KisPaintDeviceSP dev, const QString &name) { const KoColorSpace *cs = dev->colorSpace(); KisSequentialConstIterator it(dev, QRect(0,0,1024,1024)); while (it.nextPixel()) { if (cs->opacityU8(it.rawDataConst()) > 0) { KIS_DUMP_DEVICE_2(dev, QRect(0,0,1024,1024), "image", "dd"); qFatal("%s", QString("failed: %1").arg(name).toLatin1().data()); } } } - void beforeCheckingResult(KisImageWSP image, KisNodeSP activeNode) { + void beforeCheckingResult(KisImageWSP image, KisNodeSP activeNode) override { ENTER_FUNCTION() << ppVar(image) << ppVar(activeNode); KisToolUtils::clearImage(image, activeNode, 0); image->waitForDone(); checkDeviceIsEmpty(m_mask->paintDevice(), "mask"); checkDeviceIsEmpty(m_mask->parent()->projection(), "projection"); checkDeviceIsEmpty(image->projection(), "image"); } private: KisMaskSP m_mask; }; #include void KisPaintOnTransparencyMaskTest::initTestCase() { KoResourcePaths::addResourceType("kis_brushes", "data", FILES_DATA_DIR); } void KisPaintOnTransparencyMaskTest::test() { for (int i = 0; i < 1000; i++) { PaintOnTransparencyMaskTester tester("testing_wet_circle.kpp"); tester.testSimpleStrokeNoVerification(); } } QTEST_MAIN(KisPaintOnTransparencyMaskTest) diff --git a/libs/ui/tests/freehand_stroke_test.cpp b/libs/ui/tests/freehand_stroke_test.cpp index 4393d600d2..1f07d88512 100644 --- a/libs/ui/tests/freehand_stroke_test.cpp +++ b/libs/ui/tests/freehand_stroke_test.cpp @@ -1,190 +1,191 @@ /* * Copyright (c) 2011 Dmitry Kazakov * * 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 "freehand_stroke_test.h" #include #include #include #include "stroke_testing_utils.h" #include "strokes/freehand_stroke.h" #include "strokes/KisFreehandStrokeInfo.h" #include "kis_resources_snapshot.h" #include "kis_image.h" #include "kis_painter.h" #include #include "kistest.h" class FreehandStrokeTester : public utils::StrokeTester { public: FreehandStrokeTester(const QString &presetFilename, bool useLod = false) : StrokeTester(useLod ? "freehand-lod" : "freehand", QSize(500, 500), presetFilename), m_useLod(useLod), m_flipLineDirection(false) { + Q_UNUSED(m_strokeInfo); setBaseFuzziness(3); } void setFlipLineDirection(bool value) { m_flipLineDirection = value; setNumIterations(2); } void setPaintColor(const QColor &color) { m_paintColor.reset(new QColor(color)); } protected: using utils::StrokeTester::initImage; void initImage(KisImageWSP image, KisNodeSP activeNode) override { Q_UNUSED(activeNode); if (m_useLod) { image->setDesiredLevelOfDetail(1); } } void beforeCheckingResult(KisImageWSP image, KisNodeSP activeNode) override { Q_UNUSED(image) Q_UNUSED(activeNode); if (m_useLod) { //image->testingSetLevelOfDetailsEnabled(true); } } void modifyResourceManager(KoCanvasResourceManager *manager, KisImageWSP image) override { modifyResourceManager(manager, image, 0); } void modifyResourceManager(KoCanvasResourceManager *manager, KisImageWSP image, int iteration) override { if (m_paintColor && iteration > 0) { QVariant i; i.setValue(KoColor(*m_paintColor, image->colorSpace())); manager->setResource(KoCanvasResourceManager::ForegroundColor, i); } } KisStrokeStrategy* createStroke(KisResourcesSnapshotSP resources, KisImageWSP image) override { Q_UNUSED(image); KisFreehandStrokeInfo *strokeInfo = new KisFreehandStrokeInfo(); QScopedPointer stroke( new FreehandStrokeStrategy(resources, strokeInfo, kundo2_noi18n("Freehand Stroke"))); return stroke.take(); } void addPaintingJobs(KisImageWSP image, KisResourcesSnapshotSP resources) override { addPaintingJobs(image, resources, 0); } void addPaintingJobs(KisImageWSP image, KisResourcesSnapshotSP resources, int iteration) override { Q_UNUSED(resources); KisPaintInformation pi1; KisPaintInformation pi2; if (!iteration) { pi1 = KisPaintInformation(QPointF(200, 200)); pi2 = KisPaintInformation(QPointF(300, 300)); } else { pi1 = KisPaintInformation(QPointF(200, 300)); pi2 = KisPaintInformation(QPointF(300, 200)); } QScopedPointer data( new FreehandStrokeStrategy::Data(0, pi1, pi2)); image->addJob(strokeId(), data.take()); image->addJob(strokeId(), new FreehandStrokeStrategy::UpdateData(true)); } private: KisFreehandStrokeInfo *m_strokeInfo; bool m_useLod; bool m_flipLineDirection; QScopedPointer m_paintColor; }; void FreehandStrokeTest::testAutoBrushStroke() { FreehandStrokeTester tester("autobrush_300px.kpp"); tester.test(); } void FreehandStrokeTest::testHatchingStroke() { FreehandStrokeTester tester("hatching_30px.kpp"); tester.test(); } void FreehandStrokeTest::testColorSmudgeStroke() { FreehandStrokeTester tester("colorsmudge_predefined.kpp"); tester.test(); } void FreehandStrokeTest::testAutoTextured17() { FreehandStrokeTester tester("auto_textured_17.kpp"); tester.test(); } void FreehandStrokeTest::testAutoTextured38() { FreehandStrokeTester tester("auto_textured_38.kpp"); tester.test(); } void FreehandStrokeTest::testMixDullCompositioning() { FreehandStrokeTester tester("Mix_dull.kpp"); tester.setFlipLineDirection(true); tester.setPaintColor(Qt::red); tester.test(); } void FreehandStrokeTest::testAutoBrushStrokeLod() { FreehandStrokeTester tester("Basic_tip_default.kpp", true); tester.testSimpleStroke(); } void FreehandStrokeTest::testPredefinedBrushStrokeLod() { qsrand(QTime::currentTime().msec()); FreehandStrokeTester tester("testing_predefined_lod_spc13.kpp", true); //FreehandStrokeTester tester("testing_predefined_lod.kpp", true); tester.testSimpleStroke(); } KISTEST_MAIN(FreehandStrokeTest) diff --git a/libs/ui/tests/kis_model_index_converter_test.cpp b/libs/ui/tests/kis_model_index_converter_test.cpp index 795217d45d..e2cd6e2660 100644 --- a/libs/ui/tests/kis_model_index_converter_test.cpp +++ b/libs/ui/tests/kis_model_index_converter_test.cpp @@ -1,451 +1,451 @@ /* * Copyright (c) 2011 Dmitry Kazakov * * 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_model_index_converter_test.h" #include #include "kis_node_model.h" #include "kis_dummies_facade.h" #include "kis_node_dummies_graph.h" #include "kis_model_index_converter.h" #include "kis_model_index_converter_show_all.h" void KisModelIndexConverterTest::init() { m_dummiesFacade = new KisDummiesFacade(0); m_nodeModel = new KisNodeModel(0); initBase(); constructImage(); addSelectionMasks(); m_dummiesFacade->setImage(m_image); - m_nodeModel->setDummiesFacade(m_dummiesFacade, m_image, 0, 0, 0); + m_nodeModel->setDummiesFacade(m_dummiesFacade, m_image, 0, 0, 0, 0); } void KisModelIndexConverterTest::cleanup() { - m_nodeModel->setDummiesFacade(0, 0, 0, 0, 0); + m_nodeModel->setDummiesFacade(0, 0, 0, 0, 0, 0); m_dummiesFacade->setImage(0); cleanupBase(); delete m_indexConverter; delete m_nodeModel; delete m_dummiesFacade; } inline void KisModelIndexConverterTest::checkIndexFromDummy(KisNodeSP node, int row) { QModelIndex index; KisNodeDummy *dummy; dummy = m_dummiesFacade->dummyForNode(node); index = m_indexConverter->indexFromDummy(dummy); QVERIFY(index.isValid()); QCOMPARE(index.column(), 0); QCOMPARE(index.row(), row); QCOMPARE(m_indexConverter->dummyFromIndex(index), dummy); } inline void KisModelIndexConverterTest::checkInvalidIndexFromDummy(KisNodeSP node) { QModelIndex index; KisNodeDummy *dummy; dummy = m_dummiesFacade->dummyForNode(node); index = m_indexConverter->indexFromDummy(dummy); QVERIFY(!index.isValid()); } inline void KisModelIndexConverterTest::checkIndexFromAddedAllowedDummy(KisNodeSP parent, int index, int parentRow, int childRow, bool parentValid) { QString type = KisLayer::staticMetaObject.className(); checkIndexFromAddedDummy(parent, index, type, parentRow, childRow, parentValid); } inline void KisModelIndexConverterTest::checkIndexFromAddedDeniedDummy(KisNodeSP parent, int index, int parentRow, int childRow, bool parentValid) { QString type = KisSelectionMask::staticMetaObject.className(); checkIndexFromAddedDummy(parent, index, type, parentRow, childRow, parentValid); } inline void KisModelIndexConverterTest::checkIndexFromAddedDummy(KisNodeSP parent, int index, const QString &type, int parentRow, int childRow, bool parentValid) { QModelIndex modelIndex; KisNodeDummy *dummy; int row = 0; bool result; dummy = parent ? m_dummiesFacade->dummyForNode(parent) : 0; result = m_indexConverter->indexFromAddedDummy(dummy, index, type, modelIndex, row); if(!result) dbgKrita << "Failing parent:" << (parent ? parent->name() : "none") << "index:" << index; QVERIFY(result); QCOMPARE(modelIndex.isValid(), parentValid); if(modelIndex.isValid()) { QCOMPARE(modelIndex.row(), parentRow); QCOMPARE(modelIndex.column(), 0); } if(row != childRow) dbgKrita << "Failing parent:" << (parent ? parent->name() : "none") << "index:" << index; QCOMPARE(row, childRow); } inline void KisModelIndexConverterTest::checkInvalidIndexFromAddedAllowedDummy(KisNodeSP parent, int index) { QString type = KisLayer::staticMetaObject.className(); checkInvalidIndexFromAddedDummy(parent, index, type); } inline void KisModelIndexConverterTest::checkInvalidIndexFromAddedDeniedDummy(KisNodeSP parent, int index) { QString type = KisSelectionMask::staticMetaObject.className(); checkInvalidIndexFromAddedDummy(parent, index, type); } inline void KisModelIndexConverterTest::checkInvalidIndexFromAddedDummy(KisNodeSP parent, int index, const QString &type) { QModelIndex modelIndex; KisNodeDummy *dummy; int row = 0; bool result; dummy = parent ? m_dummiesFacade->dummyForNode(parent) : 0; result = m_indexConverter->indexFromAddedDummy(dummy, index, type, modelIndex, row); QVERIFY(!result); } inline void KisModelIndexConverterTest::checkDummyFromRow(KisNodeSP parent, int row, KisNodeSP expectedNode) { QModelIndex parentIndex; KisNodeDummy *parentDummy; if(parent) { parentDummy = m_dummiesFacade->dummyForNode(parent); parentIndex = m_indexConverter->indexFromDummy(parentDummy); } KisNodeDummy *resultDummy = m_indexConverter->dummyFromRow(row, parentIndex); KisNodeSP resultNode = resultDummy ? resultDummy->node() : 0; if(resultNode != expectedNode) { dbgKrita << "Actual node: " << (resultNode ? resultNode->name() : "none"); dbgKrita << "Expected node:" << (expectedNode ? expectedNode->name() : "none"); QFAIL("Wrong node"); } } inline void KisModelIndexConverterTest::checkRowCount(KisNodeSP parent, int rowCount) { QModelIndex parentIndex; KisNodeDummy *parentDummy; if(parent) { parentDummy = m_dummiesFacade->dummyForNode(parent); parentIndex = m_indexConverter->indexFromDummy(parentDummy); } int resultRowCount = m_indexConverter->rowCount(parentIndex); if(resultRowCount != rowCount) { dbgKrita << "Wrong row count for:" << (parent ? parent->name() : "none"); dbgKrita << "Actual: " << resultRowCount; dbgKrita << "Expected:" << rowCount; QFAIL("Wrong row count"); } } void KisModelIndexConverterTest::testIndexFromDummy() { m_indexConverter = new KisModelIndexConverter(m_dummiesFacade, m_nodeModel, false); checkIndexFromDummy(m_layer1, 3); checkIndexFromDummy(m_layer2, 2); checkIndexFromDummy(m_layer3, 1); checkIndexFromDummy(m_layer4, 0); checkIndexFromDummy(m_mask1, 1); checkIndexFromDummy(m_sel3, 0); checkInvalidIndexFromDummy(m_image->root()); checkInvalidIndexFromDummy(m_sel1); checkInvalidIndexFromDummy(m_sel2); } void KisModelIndexConverterTest::testIndexFromAddedAllowedDummy() { m_indexConverter = new KisModelIndexConverter(m_dummiesFacade, m_nodeModel, false); checkIndexFromAddedAllowedDummy(m_image->root(), 0, 0, 4, false); checkIndexFromAddedAllowedDummy(m_image->root(), 1, 0, 4, false); checkIndexFromAddedAllowedDummy(m_image->root(), 2, 0, 3, false); checkIndexFromAddedAllowedDummy(m_image->root(), 3, 0, 2, false); checkIndexFromAddedAllowedDummy(m_image->root(), 4, 0, 2, false); checkIndexFromAddedAllowedDummy(m_image->root(), 5, 0, 1, false); checkIndexFromAddedAllowedDummy(m_image->root(), 6, 0, 0, false); checkIndexFromAddedAllowedDummy(m_layer1, 0, 3, 0, true); checkIndexFromAddedAllowedDummy(m_layer3, 0, 1, 2, true); checkIndexFromAddedAllowedDummy(m_layer3, 1, 1, 1, true); checkIndexFromAddedAllowedDummy(m_layer3, 2, 1, 0, true); checkInvalidIndexFromAddedAllowedDummy(0, 0); } void KisModelIndexConverterTest::testIndexFromAddedDeniedDummy() { m_indexConverter = new KisModelIndexConverter(m_dummiesFacade, m_nodeModel, false); checkInvalidIndexFromAddedDeniedDummy(m_image->root(), 0); checkInvalidIndexFromAddedDeniedDummy(m_image->root(), 1); checkInvalidIndexFromAddedDeniedDummy(m_image->root(), 2); checkInvalidIndexFromAddedDeniedDummy(m_image->root(), 3); checkInvalidIndexFromAddedDeniedDummy(m_image->root(), 4); checkInvalidIndexFromAddedDeniedDummy(m_image->root(), 5); checkInvalidIndexFromAddedDeniedDummy(m_image->root(), 6); checkIndexFromAddedDeniedDummy(m_layer1, 0, 3, 0, true); checkIndexFromAddedDeniedDummy(m_layer3, 0, 1, 2, true); checkIndexFromAddedDeniedDummy(m_layer3, 1, 1, 1, true); checkIndexFromAddedDeniedDummy(m_layer3, 2, 1, 0, true); checkInvalidIndexFromAddedDeniedDummy(0, 0); } void KisModelIndexConverterTest::testDummyFromRow() { m_indexConverter = new KisModelIndexConverter(m_dummiesFacade, m_nodeModel, false); checkDummyFromRow(m_image->root(), 0, m_layer4); checkDummyFromRow(m_image->root(), 1, m_layer3); checkDummyFromRow(m_image->root(), 2, m_layer2); checkDummyFromRow(m_image->root(), 3, m_layer1); checkDummyFromRow(0, 0, m_layer4); checkDummyFromRow(0, 1, m_layer3); checkDummyFromRow(0, 2, m_layer2); checkDummyFromRow(0, 3, m_layer1); checkDummyFromRow(m_layer3, 0, m_sel3); checkDummyFromRow(m_layer3, 1, m_mask1); } void KisModelIndexConverterTest::testRowCount() { m_indexConverter = new KisModelIndexConverter(m_dummiesFacade, m_nodeModel, false); checkRowCount(m_image->root(), 4); checkRowCount(0, 4); checkRowCount(m_layer1, 0); checkRowCount(m_layer2, 0); checkRowCount(m_layer3, 2); checkRowCount(m_layer4, 0); } void KisModelIndexConverterTest::testIndexFromDummyShowGlobalSelection() { m_indexConverter = new KisModelIndexConverter(m_dummiesFacade, m_nodeModel, true); checkIndexFromDummy(m_sel1, 5); checkIndexFromDummy(m_layer1, 4); checkIndexFromDummy(m_layer2, 3); checkIndexFromDummy(m_sel2, 2); checkIndexFromDummy(m_layer3, 1); checkIndexFromDummy(m_layer4, 0); checkIndexFromDummy(m_mask1, 1); checkIndexFromDummy(m_sel3, 0); checkInvalidIndexFromDummy(m_image->root()); } void KisModelIndexConverterTest::testIndexFromAddedAllowedDummyShowGlobalSelection() { m_indexConverter = new KisModelIndexConverter(m_dummiesFacade, m_nodeModel, true); checkIndexFromAddedAllowedDummy(m_image->root(), 0, 0, 6, false); checkIndexFromAddedAllowedDummy(m_image->root(), 1, 0, 5, false); checkIndexFromAddedAllowedDummy(m_image->root(), 2, 0, 4, false); checkIndexFromAddedAllowedDummy(m_image->root(), 3, 0, 3, false); checkIndexFromAddedAllowedDummy(m_image->root(), 4, 0, 2, false); checkIndexFromAddedAllowedDummy(m_image->root(), 5, 0, 1, false); checkIndexFromAddedAllowedDummy(m_image->root(), 6, 0, 0, false); checkIndexFromAddedAllowedDummy(m_layer1, 0, 4, 0, true); checkIndexFromAddedAllowedDummy(m_layer3, 0, 1, 2, true); checkIndexFromAddedAllowedDummy(m_layer3, 1, 1, 1, true); checkIndexFromAddedAllowedDummy(m_layer3, 2, 1, 0, true); checkInvalidIndexFromAddedAllowedDummy(0, 0); } void KisModelIndexConverterTest::testIndexFromAddedDeniedDummyShowGlobalSelection() { m_indexConverter = new KisModelIndexConverter(m_dummiesFacade, m_nodeModel, true); checkIndexFromAddedDeniedDummy(m_image->root(), 0, 0, 6, false); checkIndexFromAddedDeniedDummy(m_image->root(), 1, 0, 5, false); checkIndexFromAddedDeniedDummy(m_image->root(), 2, 0, 4, false); checkIndexFromAddedDeniedDummy(m_image->root(), 3, 0, 3, false); checkIndexFromAddedDeniedDummy(m_image->root(), 4, 0, 2, false); checkIndexFromAddedDeniedDummy(m_image->root(), 5, 0, 1, false); checkIndexFromAddedDeniedDummy(m_image->root(), 6, 0, 0, false); checkIndexFromAddedDeniedDummy(m_layer1, 0, 4, 0, true); checkIndexFromAddedDeniedDummy(m_layer3, 0, 1, 2, true); checkIndexFromAddedDeniedDummy(m_layer3, 1, 1, 1, true); checkIndexFromAddedDeniedDummy(m_layer3, 2, 1, 0, true); checkInvalidIndexFromAddedDeniedDummy(0, 0); } void KisModelIndexConverterTest::testDummyFromRowShowGlobalSelection() { m_indexConverter = new KisModelIndexConverter(m_dummiesFacade, m_nodeModel, true); checkDummyFromRow(m_image->root(), 0, m_layer4); checkDummyFromRow(m_image->root(), 1, m_layer3); checkDummyFromRow(m_image->root(), 2, m_sel2); checkDummyFromRow(m_image->root(), 3, m_layer2); checkDummyFromRow(m_image->root(), 4, m_layer1); checkDummyFromRow(m_image->root(), 5, m_sel1); checkDummyFromRow(0, 0, m_layer4); checkDummyFromRow(0, 1, m_layer3); checkDummyFromRow(0, 2, m_sel2); checkDummyFromRow(0, 3, m_layer2); checkDummyFromRow(0, 4, m_layer1); checkDummyFromRow(0, 5, m_sel1); checkDummyFromRow(m_layer3, 0, m_sel3); checkDummyFromRow(m_layer3, 1, m_mask1); } void KisModelIndexConverterTest::testRowCountShowGlobalSelection() { m_indexConverter = new KisModelIndexConverter(m_dummiesFacade, m_nodeModel, true); checkRowCount(m_image->root(), 6); checkRowCount(0, 6); checkRowCount(m_layer1, 0); checkRowCount(m_layer2, 0); checkRowCount(m_layer3, 2); checkRowCount(m_layer4, 0); } void KisModelIndexConverterTest::testIndexFromDummyShowAll() { m_indexConverter = new KisModelIndexConverterShowAll(m_dummiesFacade, m_nodeModel); checkIndexFromDummy(m_sel1, 5); checkIndexFromDummy(m_layer1, 4); checkIndexFromDummy(m_layer2, 3); checkIndexFromDummy(m_sel2, 2); checkIndexFromDummy(m_layer3, 1); checkIndexFromDummy(m_layer4, 0); checkIndexFromDummy(m_mask1, 1); checkIndexFromDummy(m_sel3, 0); checkIndexFromDummy(m_image->root(), 0); } void KisModelIndexConverterTest::testIndexFromAddedAllowedDummyShowAll() { m_indexConverter = new KisModelIndexConverterShowAll(m_dummiesFacade, m_nodeModel); checkIndexFromAddedAllowedDummy(m_image->root(), 0, 0, 6, true); checkIndexFromAddedAllowedDummy(m_image->root(), 1, 0, 5, true); checkIndexFromAddedAllowedDummy(m_image->root(), 2, 0, 4, true); checkIndexFromAddedAllowedDummy(m_image->root(), 3, 0, 3, true); checkIndexFromAddedAllowedDummy(m_image->root(), 4, 0, 2, true); checkIndexFromAddedAllowedDummy(m_image->root(), 5, 0, 1, true); checkIndexFromAddedAllowedDummy(m_image->root(), 6, 0, 0, true); checkIndexFromAddedAllowedDummy(m_layer1, 0, 4, 0, true); checkIndexFromAddedAllowedDummy(m_layer3, 0, 1, 2, true); checkIndexFromAddedAllowedDummy(m_layer3, 1, 1, 1, true); checkIndexFromAddedAllowedDummy(m_layer3, 2, 1, 0, true); checkIndexFromAddedAllowedDummy(0, 0, 0, 0, false); } void KisModelIndexConverterTest::testIndexFromAddedDeniedDummyShowAll() { m_indexConverter = new KisModelIndexConverterShowAll(m_dummiesFacade, m_nodeModel); checkIndexFromAddedDeniedDummy(m_image->root(), 0, 0, 6, true); checkIndexFromAddedDeniedDummy(m_image->root(), 1, 0, 5, true); checkIndexFromAddedDeniedDummy(m_image->root(), 2, 0, 4, true); checkIndexFromAddedDeniedDummy(m_image->root(), 3, 0, 3, true); checkIndexFromAddedDeniedDummy(m_image->root(), 4, 0, 2, true); checkIndexFromAddedDeniedDummy(m_image->root(), 5, 0, 1, true); checkIndexFromAddedDeniedDummy(m_image->root(), 6, 0, 0, true); checkIndexFromAddedDeniedDummy(m_layer1, 0, 4, 0, true); checkIndexFromAddedDeniedDummy(m_layer3, 0, 1, 2, true); checkIndexFromAddedDeniedDummy(m_layer3, 1, 1, 1, true); checkIndexFromAddedDeniedDummy(m_layer3, 2, 1, 0, true); checkIndexFromAddedDeniedDummy(0, 0, 0, 0, false); } void KisModelIndexConverterTest::testDummyFromRowShowAll() { m_indexConverter = new KisModelIndexConverterShowAll(m_dummiesFacade, m_nodeModel); checkDummyFromRow(m_image->root(), 0, m_layer4); checkDummyFromRow(m_image->root(), 1, m_layer3); checkDummyFromRow(m_image->root(), 2, m_sel2); checkDummyFromRow(m_image->root(), 3, m_layer2); checkDummyFromRow(m_image->root(), 4, m_layer1); checkDummyFromRow(m_image->root(), 5, m_sel1); checkDummyFromRow(0, 0, m_image->root()); checkDummyFromRow(m_layer3, 0, m_sel3); checkDummyFromRow(m_layer3, 1, m_mask1); } void KisModelIndexConverterTest::testRowCountShowAll() { m_indexConverter = new KisModelIndexConverterShowAll(m_dummiesFacade, m_nodeModel); checkRowCount(m_image->root(), 6); checkRowCount(0, 1); checkRowCount(m_layer1, 0); checkRowCount(m_layer2, 0); checkRowCount(m_layer3, 2); checkRowCount(m_layer4, 0); } QTEST_MAIN(KisModelIndexConverterTest) diff --git a/libs/ui/tests/kis_node_model_test.cpp b/libs/ui/tests/kis_node_model_test.cpp index 2e6e55cd5b..db97778d7c 100644 --- a/libs/ui/tests/kis_node_model_test.cpp +++ b/libs/ui/tests/kis_node_model_test.cpp @@ -1,113 +1,113 @@ /* * Copyright (C) 2007 Boudewijn Rempt * * 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_node_model_test.h" #include #include #include "KisDocument.h" #include "KisPart.h" #include "kis_node_model.h" #include "kis_name_server.h" #include "flake/kis_shape_controller.h" #include #include "modeltest.h" void KisNodeModelTest::init() { m_doc = KisPart::instance()->createDocument(); m_nameServer = new KisNameServer(); m_shapeController = new KisShapeController(m_doc, m_nameServer); m_nodeModel = new KisNodeModel(0); initBase(); } void KisNodeModelTest::cleanup() { cleanupBase(); delete m_nodeModel; delete m_shapeController; delete m_nameServer; delete m_doc; } void KisNodeModelTest::testSetImage() { constructImage(); m_shapeController->setImage(m_image); - m_nodeModel->setDummiesFacade(m_shapeController, m_image, 0, 0, 0); + m_nodeModel->setDummiesFacade(m_shapeController, m_image, 0, 0, 0, 0); new ModelTest(m_nodeModel, this); } void KisNodeModelTest::testAddNode() { m_shapeController->setImage(m_image); - m_nodeModel->setDummiesFacade(m_shapeController, m_image, 0, 0, 0); + m_nodeModel->setDummiesFacade(m_shapeController, m_image, 0, 0, 0, 0); new ModelTest(m_nodeModel, this); constructImage(); } void KisNodeModelTest::testRemoveAllNodes() { constructImage(); m_shapeController->setImage(m_image); - m_nodeModel->setDummiesFacade(m_shapeController, m_image, 0, 0, 0); + m_nodeModel->setDummiesFacade(m_shapeController, m_image, 0, 0, 0, 0); new ModelTest(m_nodeModel, this); m_image->removeNode(m_layer4); m_image->removeNode(m_layer3); m_image->removeNode(m_layer2); m_image->removeNode(m_layer1); } void KisNodeModelTest::testRemoveIncludingRoot() { constructImage(); m_shapeController->setImage(m_image); - m_nodeModel->setDummiesFacade(m_shapeController, m_image, 0, 0, 0); + m_nodeModel->setDummiesFacade(m_shapeController, m_image, 0, 0, 0, 0); new ModelTest(m_nodeModel, this); m_image->removeNode(m_layer4); m_image->removeNode(m_layer3); m_image->removeNode(m_layer2); m_image->removeNode(m_layer1); m_image->removeNode(m_image->root()); } void KisNodeModelTest::testSubstituteRootNode() { constructImage(); m_shapeController->setImage(m_image); - m_nodeModel->setDummiesFacade(m_shapeController, m_image, 0, 0, 0); + m_nodeModel->setDummiesFacade(m_shapeController, m_image, 0, 0, 0, 0); new ModelTest(m_nodeModel, this); m_image->flatten(); } KISTEST_MAIN(KisNodeModelTest) diff --git a/libs/ui/tests/kis_node_view_test.cpp b/libs/ui/tests/kis_node_view_test.cpp index bb40cd6e62..923a601303 100644 --- a/libs/ui/tests/kis_node_view_test.cpp +++ b/libs/ui/tests/kis_node_view_test.cpp @@ -1,133 +1,133 @@ /* * Copyright (c) 2015 Dmitry Kazakov * * 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_node_view_test.h" #include #include #include #include #include #include #include "KisDocument.h" #include "KisPart.h" #include "kis_name_server.h" #include "flake/kis_shape_controller.h" #include "kis_undo_adapter.h" #include "kis_node_model.h" #include "kis_color_filter_combo.h" #include //#define ENABLE_GUI_TESTS void KisNodeViewTest::init() { m_doc = KisPart::instance()->createDocument(); m_nameServer = new KisNameServer(); m_shapeController = new KisShapeController(m_doc, m_nameServer); initBase(); } void KisNodeViewTest::cleanup() { cleanupBase(); delete m_shapeController; delete m_nameServer; delete m_doc; } void KisNodeViewTest::testLayers() { #ifndef ENABLE_GUI_TESTS return; #endif QDialog dlg; QFont font; font.setPointSizeF(8); dlg.setFont(font); KisNodeModel *model = new KisNodeModel(this); KisNodeView *view = new KisNodeView(&dlg); view->setModel(model); constructImage(); addSelectionMasks(); m_shapeController->setImage(m_image); - model->setDummiesFacade(m_shapeController, m_image, m_shapeController, 0, 0); + model->setDummiesFacade(m_shapeController, m_image, m_shapeController, 0, 0, 0); QVBoxLayout *layout = new QVBoxLayout(&dlg); KisColorFilterCombo *cb = new KisColorFilterCombo(&dlg); QSet labels; for (int i = 0; i < 6; i++) { labels.insert(i); } cb->updateAvailableLabels(labels); QHBoxLayout *hbox = new QHBoxLayout(&dlg); hbox->addStretch(1); hbox->addWidget(cb); layout->addLayout(hbox); layout->addWidget(view); dlg.resize(280, 400); view->expandAll(); dlg.exec(); } #include "kis_color_label_selector_widget.h" void KisNodeViewTest::testColorLabels() { #ifndef ENABLE_GUI_TESTS return; #endif QDialog dlg; QFont font; font.setPointSizeF(8); dlg.setFont(font); KisColorLabelSelectorWidget *widget = new KisColorLabelSelectorWidget(&dlg); QSizePolicy policy(QSizePolicy::Preferred, QSizePolicy::Preferred); widget->setSizePolicy(policy); QVBoxLayout *layout = new QVBoxLayout(&dlg); layout->addWidget(widget); layout->addStretch(1); dlg.resize(280, 400); dlg.exec(); } KISTEST_MAIN(KisNodeViewTest) diff --git a/libs/ui/tool/kis_rectangle_constraint_widget.cpp b/libs/ui/tool/kis_rectangle_constraint_widget.cpp index 0b0f124792..83b03c4794 100644 --- a/libs/ui/tool/kis_rectangle_constraint_widget.cpp +++ b/libs/ui/tool/kis_rectangle_constraint_widget.cpp @@ -1,71 +1,123 @@ /* * 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_rectangle_constraint_widget.h" #include "kis_tool_rectangle_base.h" #include +#include "kis_aspect_ratio_locker.h" +#include "kis_signals_blocker.h" +#include +#include -KisRectangleConstraintWidget::KisRectangleConstraintWidget(QWidget *parent, KisToolRectangleBase *tool) : QWidget(parent) +KisRectangleConstraintWidget::KisRectangleConstraintWidget(QWidget *parent, KisToolRectangleBase *tool, bool showRoundCornersGUI) + : QWidget(parent) { m_tool = tool; setupUi(this); lockWidthButton->setIcon(KisIconUtils::loadIcon("layer-locked")); lockHeightButton->setIcon(KisIconUtils::loadIcon("layer-locked")); lockRatioButton->setIcon(KisIconUtils::loadIcon("layer-locked")); connect(lockWidthButton, SIGNAL(toggled(bool)), this, SLOT(inputsChanged(void))); connect(lockHeightButton, SIGNAL(toggled(bool)), this, SLOT(inputsChanged(void))); connect(lockRatioButton, SIGNAL(toggled(bool)), this, SLOT(inputsChanged(void))); connect(intWidth, SIGNAL(valueChanged(int)), this, SLOT(inputsChanged(void))); connect(intHeight, SIGNAL(valueChanged(int)), this, SLOT(inputsChanged(void))); connect(doubleRatio, SIGNAL(valueChanged(double)), this, SLOT(inputsChanged(void))); connect(this, SIGNAL(constraintsChanged(bool,bool,bool,float,float,float)), m_tool, SLOT(constraintsChanged(bool,bool,bool,float,float,float))); connect(m_tool, SIGNAL(rectangleChanged(QRectF)), this, SLOT(rectangleChanged(QRectF))); + + m_cornersAspectLocker = new KisAspectRatioLocker(this); + m_cornersAspectLocker->connectSpinBoxes(intRoundCornersX, intRoundCornersY, cornersAspectButton); + + connect(m_cornersAspectLocker, SIGNAL(sliderValueChanged()), SLOT(slotRoundCornersChanged())); + connect(m_cornersAspectLocker, SIGNAL(aspectButtonChanged()), SLOT(slotRoundCornersAspectLockChanged())); + + connect(m_tool, SIGNAL(sigRequestReloadConfig()), SLOT(slotReloadConfig())); + slotReloadConfig(); + + if (!showRoundCornersGUI) { + intRoundCornersX->setVisible(false); + intRoundCornersY->setVisible(false); + lblRoundCornersX->setVisible(false); + lblRoundCornersY->setVisible(false); + cornersAspectButton->setVisible(false); + } } void KisRectangleConstraintWidget::inputsChanged() { emit constraintsChanged( lockRatioButton->isChecked(), lockWidthButton->isChecked(), lockHeightButton->isChecked(), doubleRatio->value(), intWidth->value(), intHeight->value() - ); + ); +} + +void KisRectangleConstraintWidget::slotRoundCornersChanged() +{ + m_tool->roundCornersChanged(intRoundCornersX->value(), intRoundCornersY->value()); + + KConfigGroup cfg = KSharedConfig::openConfig()->group(m_tool->toolId()); + cfg.writeEntry("roundCornersX", intRoundCornersX->value()); + cfg.writeEntry("roundCornersY", intRoundCornersY->value()); +} + +void KisRectangleConstraintWidget::slotRoundCornersAspectLockChanged() +{ + KConfigGroup cfg = KSharedConfig::openConfig()->group(m_tool->toolId()); + cfg.writeEntry("roundCornersAspectLocked", cornersAspectButton->keepAspectRatio()); +} + +void KisRectangleConstraintWidget::slotReloadConfig() +{ + KConfigGroup cfg = KSharedConfig::openConfig()->group(m_tool->toolId()); + + { + KisSignalsBlocker b(intRoundCornersX, intRoundCornersY, cornersAspectButton); + intRoundCornersX->setValue(cfg.readEntry("roundCornersX", 0)); + intRoundCornersY->setValue(cfg.readEntry("roundCornersY", 0)); + cornersAspectButton->setKeepAspectRatio(cfg.readEntry("roundCornersAspectLocked", true)); + m_cornersAspectLocker->updateAspect(); + } + + slotRoundCornersChanged(); } void KisRectangleConstraintWidget::rectangleChanged(const QRectF &rect) { intWidth->blockSignals(true); intHeight->blockSignals(true); doubleRatio->blockSignals(true); if (!lockWidthButton->isChecked()) intWidth->setValue(rect.width()); if (!lockHeightButton->isChecked()) intHeight->setValue(rect.height()); if (!lockRatioButton->isChecked() && !(rect.width() == 0 && rect.height() == 0)) { doubleRatio->setValue(fabs(rect.width()) / fabs(rect.height())); } intWidth->blockSignals(false); intHeight->blockSignals(false); doubleRatio->blockSignals(false); } diff --git a/libs/ui/tool/kis_rectangle_constraint_widget.h b/libs/ui/tool/kis_rectangle_constraint_widget.h index fc803db6e4..f4cfdefb4d 100644 --- a/libs/ui/tool/kis_rectangle_constraint_widget.h +++ b/libs/ui/tool/kis_rectangle_constraint_widget.h @@ -1,44 +1,51 @@ /* * 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. */ #ifndef KISRECTANGLECONSTRAINTWIDGET_H #define KISRECTANGLECONSTRAINTWIDGET_H #include "ui_wdgrectangleconstraints.h" #include class KisToolRectangleBase; +class KisAspectRatioLocker; class KRITAUI_EXPORT KisRectangleConstraintWidget : public QWidget, public Ui::WdgRectangleConstraints { Q_OBJECT public: - KisRectangleConstraintWidget(QWidget *parentWidget, KisToolRectangleBase *tool); + KisRectangleConstraintWidget(QWidget *parentWidget, KisToolRectangleBase *tool, bool showRoundCornersGUI); Q_SIGNALS: void constraintsChanged(bool forceRatio, bool forceWidth, bool forceHeight, float ratio, float width, float height); protected Q_SLOTS: void rectangleChanged(const QRectF &rect); void inputsChanged(); + + void slotRoundCornersChanged(); + void slotRoundCornersAspectLockChanged(); + + void slotReloadConfig(); protected: KisToolRectangleBase* m_tool; Ui_WdgRectangleConstraints *m_widget; + KisAspectRatioLocker *m_cornersAspectLocker; }; #endif diff --git a/libs/ui/tool/kis_shape_tool_helper.cpp b/libs/ui/tool/kis_shape_tool_helper.cpp index 54552765d0..4edab2a3d7 100644 --- a/libs/ui/tool/kis_shape_tool_helper.cpp +++ b/libs/ui/tool/kis_shape_tool_helper.cpp @@ -1,70 +1,79 @@ /* * Copyright (c) 2009 Sven Langkamp * * 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_shape_tool_helper.h" #include #include #include +#include -KoShape* KisShapeToolHelper::createRectangleShape(const QRectF& rect) + +KoShape* KisShapeToolHelper::createRectangleShape(const QRectF& rect, qreal roundCornersX, qreal roundCornersY) { KoShape* shape; + KoShapeFactoryBase *rectFactory = KoShapeRegistry::instance()->value("RectangleShape"); if (rectFactory) { - shape = rectFactory->createDefaultShape(); - shape->setSize(rect.size()); - shape->setPosition(rect.topLeft()); + KoProperties props; + props.setProperty("x", rect.x()); + props.setProperty("y", rect.y()); + props.setProperty("width", rect.width()); + props.setProperty("height", rect.height()); + props.setProperty("rx", 2 * 100.0 * roundCornersX / rect.width()); + props.setProperty("ry", 2 * 100.0 * roundCornersY / rect.height()); + + shape = rectFactory->createShape(&props); } else { //Fallback if the plugin wasn't found - KoPathShape* path = new KoPathShape(); - path->setShapeId(KoPathShapeId); - path->moveTo(rect.topLeft()); - path->lineTo(rect.topLeft() + QPointF(rect.width(), 0)); - path->lineTo(rect.bottomRight()); - path->lineTo(rect.topLeft() + QPointF(0, rect.height())); - path->close(); - path->normalize(); - shape = path; + QPainterPath path; + if (roundCornersX > 0 || roundCornersY > 0) { + path.addRoundedRect(rect, roundCornersX, roundCornersY); + } else { + path.addRect(rect); + } + KoPathShape *pathShape = KoPathShape::createShapeFromPainterPath(path); + pathShape->normalize(); + shape = pathShape; } return shape; } KoShape* KisShapeToolHelper::createEllipseShape(const QRectF& rect) { KoShape* shape; KoShapeFactoryBase *rectFactory = KoShapeRegistry::instance()->value("EllipseShape"); if (rectFactory) { shape = rectFactory->createDefaultShape(); shape->setSize(rect.size()); shape->setPosition(rect.topLeft()); } else { //Fallback if the plugin wasn't found KoPathShape* path = new KoPathShape(); path->setShapeId(KoPathShapeId); QPointF rightMiddle = QPointF(rect.left() + rect.width(), rect.top() + rect.height() / 2); path->moveTo(rightMiddle); path->arcTo(rect.width() / 2, rect.height() / 2, 0, 360.0); path->close(); path->normalize(); shape = path; } return shape; } diff --git a/libs/ui/tool/kis_shape_tool_helper.h b/libs/ui/tool/kis_shape_tool_helper.h index 60e3074907..36d18b10c8 100644 --- a/libs/ui/tool/kis_shape_tool_helper.h +++ b/libs/ui/tool/kis_shape_tool_helper.h @@ -1,41 +1,41 @@ /* * Copyright (c) 2009 Sven Langkamp * * 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. */ #ifndef KIS_SHAPE_TOOL_HELPER_H #define KIS_SHAPE_TOOL_HELPER_H #include #include class KoShape; /** * KisShapeToolHelper provides shapes and fallback shapes for shape based tools */ class KRITAUI_EXPORT KisShapeToolHelper { public: - static KoShape* createRectangleShape(const QRectF& rect); + static KoShape* createRectangleShape(const QRectF& rect, qreal roundCornersX, qreal roundCornersY); static KoShape* createEllipseShape(const QRectF& rect); }; #endif diff --git a/libs/ui/tool/kis_tool.cc b/libs/ui/tool/kis_tool.cc index f9ee6ee555..c6091e2e37 100644 --- a/libs/ui/tool/kis_tool.cc +++ b/libs/ui/tool/kis_tool.cc @@ -1,703 +1,709 @@ /* * Copyright (c) 2006, 2010 Boudewijn Rempt * * 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_tool.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_node_manager.h" #include #include #include #include #include #include #include #include #include #include #include #include "opengl/kis_opengl_canvas2.h" #include "kis_canvas_resource_provider.h" #include "canvas/kis_canvas2.h" #include "kis_coordinates_converter.h" #include "filter/kis_filter_configuration.h" #include "kis_config.h" #include "kis_config_notifier.h" #include "kis_cursor.h" #include #include "kis_resources_snapshot.h" #include #include "kis_action_registry.h" #include "kis_tool_utils.h" struct Q_DECL_HIDDEN KisTool::Private { QCursor cursor; // the cursor that should be shown on tool activation. // From the canvas resources KoPattern* currentPattern{0}; KoAbstractGradient* currentGradient{0}; KoColor currentFgColor; KoColor currentBgColor; float currentExposure{1.0}; KisFilterConfigurationSP currentGenerator; QWidget* optionWidget{0}; ToolMode m_mode{HOVER_MODE}; bool m_isActive{false}; }; KisTool::KisTool(KoCanvasBase * canvas, const QCursor & cursor) : KoToolBase(canvas) , d(new Private) { d->cursor = cursor; connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(resetCursorStyle())); connect(this, SIGNAL(isActiveChanged()), SLOT(resetCursorStyle())); KActionCollection *collection = this->canvas()->canvasController()->actionCollection(); if (!collection->action("toggle_fg_bg")) { QAction *toggleFgBg = KisActionRegistry::instance()->makeQAction("toggle_fg_bg", collection); collection->addAction("toggle_fg_bg", toggleFgBg); } if (!collection->action("reset_fg_bg")) { QAction *toggleFgBg = KisActionRegistry::instance()->makeQAction("reset_fg_bg", collection); collection->addAction("reset_fg_bg", toggleFgBg); } addAction("toggle_fg_bg", dynamic_cast(collection->action("toggle_fg_bg"))); addAction("reset_fg_bg", dynamic_cast(collection->action("reset_fg_bg"))); } KisTool::~KisTool() { delete d; } void KisTool::activate(ToolActivation activation, const QSet &shapes) { KoToolBase::activate(activation, shapes); resetCursorStyle(); if (!canvas()) return; if (!canvas()->resourceManager()) return; d->currentFgColor = canvas()->resourceManager()->resource(KoCanvasResourceManager::ForegroundColor).value(); d->currentBgColor = canvas()->resourceManager()->resource(KoCanvasResourceManager::BackgroundColor).value(); if (canvas()->resourceManager()->hasResource(KisCanvasResourceProvider::CurrentPattern)) { d->currentPattern = canvas()->resourceManager()->resource(KisCanvasResourceProvider::CurrentPattern).value(); } if (canvas()->resourceManager()->hasResource(KisCanvasResourceProvider::CurrentGradient)) { d->currentGradient = canvas()->resourceManager()->resource(KisCanvasResourceProvider::CurrentGradient).value(); } KisPaintOpPresetSP preset = canvas()->resourceManager()->resource(KisCanvasResourceProvider::CurrentPaintOpPreset).value(); if (preset && preset->settings()) { preset->settings()->activate(); } if (canvas()->resourceManager()->hasResource(KisCanvasResourceProvider::HdrExposure)) { d->currentExposure = static_cast(canvas()->resourceManager()->resource(KisCanvasResourceProvider::HdrExposure).toDouble()); } if (canvas()->resourceManager()->hasResource(KisCanvasResourceProvider::CurrentGeneratorConfiguration)) { d->currentGenerator = canvas()->resourceManager()->resource(KisCanvasResourceProvider::CurrentGeneratorConfiguration).value(); } connect(action("toggle_fg_bg"), SIGNAL(triggered()), SLOT(slotToggleFgBg()), Qt::UniqueConnection); connect(action("reset_fg_bg"), SIGNAL(triggered()), SLOT(slotResetFgBg()), Qt::UniqueConnection); d->m_isActive = true; emit isActiveChanged(); } void KisTool::deactivate() { bool result = true; result &= disconnect(action("toggle_fg_bg"), 0, this, 0); result &= disconnect(action("reset_fg_bg"), 0, this, 0); if (!result) { warnKrita << "WARNING: KisTool::deactivate() failed to disconnect" << "some signal connections. Your actions might be executed twice!"; } d->m_isActive = false; emit isActiveChanged(); KoToolBase::deactivate(); } void KisTool::canvasResourceChanged(int key, const QVariant & v) { QString formattedBrushName; if (key == KisCanvasResourceProvider::CurrentPaintOpPreset) { formattedBrushName = v.value()->name().replace("_", " "); } switch (key) { case(KoCanvasResourceManager::ForegroundColor): d->currentFgColor = v.value(); break; case(KoCanvasResourceManager::BackgroundColor): d->currentBgColor = v.value(); break; case(KisCanvasResourceProvider::CurrentPattern): d->currentPattern = static_cast(v.value()); break; case(KisCanvasResourceProvider::CurrentGradient): d->currentGradient = static_cast(v.value()); break; case(KisCanvasResourceProvider::HdrExposure): d->currentExposure = static_cast(v.toDouble()); break; case(KisCanvasResourceProvider::CurrentGeneratorConfiguration): d->currentGenerator = static_cast(v.value()); break; case(KisCanvasResourceProvider::CurrentPaintOpPreset): emit statusTextChanged(formattedBrushName); break; case(KisCanvasResourceProvider::CurrentKritaNode): resetCursorStyle(); break; default: break; // Do nothing }; } void KisTool::updateSettingsViews() { } QPointF KisTool::widgetCenterInWidgetPixels() { KisCanvas2 *kritaCanvas = dynamic_cast(canvas()); Q_ASSERT(kritaCanvas); const KisCoordinatesConverter *converter = kritaCanvas->coordinatesConverter(); return converter->flakeToWidget(converter->flakeCenterPoint()); } QPointF KisTool::convertDocumentToWidget(const QPointF& pt) { KisCanvas2 *kritaCanvas = dynamic_cast(canvas()); Q_ASSERT(kritaCanvas); return kritaCanvas->coordinatesConverter()->documentToWidget(pt); } QPointF KisTool::convertToPixelCoord(KoPointerEvent *e) { if (!image()) return e->point; return image()->documentToPixel(e->point); } QPointF KisTool::convertToPixelCoord(const QPointF& pt) { if (!image()) return pt; return image()->documentToPixel(pt); } QPointF KisTool::convertToPixelCoordAndSnap(KoPointerEvent *e, const QPointF &offset, bool useModifiers) { if (!image()) return e->point; KoSnapGuide *snapGuide = canvas()->snapGuide(); QPointF pos = snapGuide->snap(e->point, offset, useModifiers ? e->modifiers() : Qt::NoModifier); return image()->documentToPixel(pos); } QPointF KisTool::convertToPixelCoordAndSnap(const QPointF& pt, const QPointF &offset) { if (!image()) return pt; KoSnapGuide *snapGuide = canvas()->snapGuide(); QPointF pos = snapGuide->snap(pt, offset, Qt::NoModifier); return image()->documentToPixel(pos); } QPoint KisTool::convertToImagePixelCoordFloored(KoPointerEvent *e) { if (!image()) return e->point.toPoint(); return image()->documentToImagePixelFloored(e->point); } QPointF KisTool::viewToPixel(const QPointF &viewCoord) const { if (!image()) return viewCoord; return image()->documentToPixel(canvas()->viewConverter()->viewToDocument(viewCoord)); } QRectF KisTool::convertToPt(const QRectF &rect) { if (!image()) return rect; QRectF r; //We add 1 in the following to the extreme coords because a pixel always has size r.setCoords(int(rect.left()) / image()->xRes(), int(rect.top()) / image()->yRes(), int(rect.right()) / image()->xRes(), int( rect.bottom()) / image()->yRes()); return r; } +qreal KisTool::convertToPt(qreal value) +{ + const qreal avgResolution = 0.5 * (image()->xRes() + image()->yRes()); + return value / avgResolution; +} + QPointF KisTool::pixelToView(const QPoint &pixelCoord) const { if (!image()) return pixelCoord; QPointF documentCoord = image()->pixelToDocument(pixelCoord); return canvas()->viewConverter()->documentToView(documentCoord); } QPointF KisTool::pixelToView(const QPointF &pixelCoord) const { if (!image()) return pixelCoord; QPointF documentCoord = image()->pixelToDocument(pixelCoord); return canvas()->viewConverter()->documentToView(documentCoord); } QRectF KisTool::pixelToView(const QRectF &pixelRect) const { if (!image()) return pixelRect; QPointF topLeft = pixelToView(pixelRect.topLeft()); QPointF bottomRight = pixelToView(pixelRect.bottomRight()); return QRectF(topLeft, bottomRight); } QPainterPath KisTool::pixelToView(const QPainterPath &pixelPolygon) const { QTransform matrix; qreal zoomX, zoomY; canvas()->viewConverter()->zoom(&zoomX, &zoomY); matrix.scale(zoomX/image()->xRes(), zoomY/ image()->yRes()); return matrix.map(pixelPolygon); } QPolygonF KisTool::pixelToView(const QPolygonF &pixelPath) const { QTransform matrix; qreal zoomX, zoomY; canvas()->viewConverter()->zoom(&zoomX, &zoomY); matrix.scale(zoomX/image()->xRes(), zoomY/ image()->yRes()); return matrix.map(pixelPath); } void KisTool::updateCanvasPixelRect(const QRectF &pixelRect) { canvas()->updateCanvas(convertToPt(pixelRect)); } void KisTool::updateCanvasViewRect(const QRectF &viewRect) { canvas()->updateCanvas(canvas()->viewConverter()->viewToDocument(viewRect)); } KisImageWSP KisTool::image() const { // For now, krita tools only work in krita, not for a krita shape. Krita shapes are for 2.1 KisCanvas2 * kisCanvas = dynamic_cast(canvas()); if (kisCanvas) { return kisCanvas->currentImage(); } return 0; } QCursor KisTool::cursor() const { return d->cursor; } void KisTool::notifyModified() const { if (image()) { image()->setModified(); } } KoPattern * KisTool::currentPattern() { return d->currentPattern; } KoAbstractGradient * KisTool::currentGradient() { return d->currentGradient; } KisPaintOpPresetSP KisTool::currentPaintOpPreset() { return canvas()->resourceManager()->resource(KisCanvasResourceProvider::CurrentPaintOpPreset).value(); } KisNodeSP KisTool::currentNode() const { KisNodeSP node = canvas()->resourceManager()->resource(KisCanvasResourceProvider::CurrentKritaNode).value(); return node; } KisNodeList KisTool::selectedNodes() const { KisCanvas2 * kiscanvas = static_cast(canvas()); KisViewManager* viewManager = kiscanvas->viewManager(); return viewManager->nodeManager()->selectedNodes(); } KoColor KisTool::currentFgColor() { return d->currentFgColor; } KoColor KisTool::currentBgColor() { return d->currentBgColor; } KisImageWSP KisTool::currentImage() { return image(); } KisFilterConfigurationSP KisTool::currentGenerator() { return d->currentGenerator; } void KisTool::setMode(ToolMode mode) { d->m_mode = mode; } KisTool::ToolMode KisTool::mode() const { return d->m_mode; } void KisTool::setCursor(const QCursor &cursor) { d->cursor = cursor; } KisTool::AlternateAction KisTool::actionToAlternateAction(ToolAction action) { KIS_ASSERT_RECOVER_RETURN_VALUE(action != Primary, Secondary); return (AlternateAction)action; } void KisTool::activatePrimaryAction() { resetCursorStyle(); } void KisTool::deactivatePrimaryAction() { resetCursorStyle(); } void KisTool::beginPrimaryAction(KoPointerEvent *event) { Q_UNUSED(event); } void KisTool::beginPrimaryDoubleClickAction(KoPointerEvent *event) { beginPrimaryAction(event); } void KisTool::continuePrimaryAction(KoPointerEvent *event) { Q_UNUSED(event); } void KisTool::endPrimaryAction(KoPointerEvent *event) { Q_UNUSED(event); } bool KisTool::primaryActionSupportsHiResEvents() const { return false; } void KisTool::activateAlternateAction(AlternateAction action) { Q_UNUSED(action); } void KisTool::deactivateAlternateAction(AlternateAction action) { Q_UNUSED(action); } void KisTool::beginAlternateAction(KoPointerEvent *event, AlternateAction action) { Q_UNUSED(event); Q_UNUSED(action); } void KisTool::beginAlternateDoubleClickAction(KoPointerEvent *event, AlternateAction action) { beginAlternateAction(event, action); } void KisTool::continueAlternateAction(KoPointerEvent *event, AlternateAction action) { Q_UNUSED(event); Q_UNUSED(action); } void KisTool::endAlternateAction(KoPointerEvent *event, AlternateAction action) { Q_UNUSED(event); Q_UNUSED(action); } void KisTool::mouseDoubleClickEvent(KoPointerEvent *event) { Q_UNUSED(event); } void KisTool::mouseTripleClickEvent(KoPointerEvent *event) { mouseDoubleClickEvent(event); } void KisTool::mousePressEvent(KoPointerEvent *event) { Q_UNUSED(event); } void KisTool::mouseReleaseEvent(KoPointerEvent *event) { Q_UNUSED(event); } void KisTool::mouseMoveEvent(KoPointerEvent *event) { Q_UNUSED(event); } void KisTool::deleteSelection() { KisResourcesSnapshotSP resources = new KisResourcesSnapshot(image(), currentNode(), this->canvas()->resourceManager()); if (!blockUntilOperationsFinished()) { return; } if (!KisToolUtils::clearImage(image(), resources->currentNode(), resources->activeSelection())) { KoToolBase::deleteSelection(); } } QWidget* KisTool::createOptionWidget() { d->optionWidget = new QLabel(i18n("No options")); d->optionWidget->setObjectName("SpecialSpacer"); return d->optionWidget; } #define NEAR_VAL -1000.0 #define FAR_VAL 1000.0 #define PROGRAM_VERTEX_ATTRIBUTE 0 void KisTool::paintToolOutline(QPainter* painter, const QPainterPath &path) { KisOpenGLCanvas2 *canvasWidget = dynamic_cast(canvas()->canvasWidget()); if (canvasWidget) { painter->beginNativePainting(); canvasWidget->paintToolOutline(path); painter->endNativePainting(); } else { painter->save(); painter->setCompositionMode(QPainter::RasterOp_SourceXorDestination); painter->setPen(QColor(128, 255, 128)); painter->drawPath(path); painter->restore(); } } void KisTool::resetCursorStyle() { useCursor(d->cursor); } bool KisTool::overrideCursorIfNotEditable() { // override cursor for canvas iff this tool is active // and we can't paint on the active layer if (isActive()) { KisNodeSP node = currentNode(); if (node && !node->isEditable()) { canvas()->setCursor(Qt::ForbiddenCursor); return true; } } return false; } bool KisTool::blockUntilOperationsFinished() { KisCanvas2 * kiscanvas = static_cast(canvas()); KisViewManager* viewManager = kiscanvas->viewManager(); return viewManager->blockUntilOperationsFinished(image()); } void KisTool::blockUntilOperationsFinishedForced() { KisCanvas2 * kiscanvas = static_cast(canvas()); KisViewManager* viewManager = kiscanvas->viewManager(); viewManager->blockUntilOperationsFinishedForced(image()); } bool KisTool::isActive() const { return d->m_isActive; } void KisTool::slotToggleFgBg() { KoCanvasResourceManager* resourceManager = canvas()->resourceManager(); KoColor newFg = resourceManager->backgroundColor(); KoColor newBg = resourceManager->foregroundColor(); /** * NOTE: Some of color selectors do not differentiate foreground * and background colors, so if one wants them to end up * being set up to foreground color, it should be set the * last. */ resourceManager->setBackgroundColor(newBg); resourceManager->setForegroundColor(newFg); } void KisTool::slotResetFgBg() { KoCanvasResourceManager* resourceManager = canvas()->resourceManager(); // see a comment in slotToggleFgBg() resourceManager->setBackgroundColor(KoColor(Qt::white, KoColorSpaceRegistry::instance()->rgb8())); resourceManager->setForegroundColor(KoColor(Qt::black, KoColorSpaceRegistry::instance()->rgb8())); } bool KisTool::nodeEditable() { KisNodeSP node = currentNode(); if (!node) { return false; } bool blockedNoIndirectPainting = false; const bool presetUsesIndirectPainting = !currentPaintOpPreset()->settings()->paintIncremental(); if (!presetUsesIndirectPainting) { const KisIndirectPaintingSupport *indirectPaintingLayer = dynamic_cast(node.data()); if (indirectPaintingLayer) { blockedNoIndirectPainting = !indirectPaintingLayer->supportsNonIndirectPainting(); } } bool nodeEditable = node->isEditable() && !blockedNoIndirectPainting; if (!nodeEditable) { KisCanvas2 * kiscanvas = static_cast(canvas()); QString message; if (!node->visible() && node->userLocked()) { message = i18n("Layer is locked and invisible."); } else if (node->userLocked()) { message = i18n("Layer is locked."); } else if(!node->visible()) { message = i18n("Layer is invisible."); } else if (blockedNoIndirectPainting) { message = i18n("Layer can be painted in Wash Mode only."); } else { message = i18n("Group not editable."); } kiscanvas->viewManager()->showFloatingMessage(message, KisIconUtils::loadIcon("object-locked")); } return nodeEditable; } bool KisTool::selectionEditable() { KisCanvas2 * kisCanvas = static_cast(canvas()); KisViewManager * view = kisCanvas->viewManager(); bool editable = view->selectionEditable(); if (!editable) { KisCanvas2 * kiscanvas = static_cast(canvas()); kiscanvas->viewManager()->showFloatingMessage(i18n("Local selection is locked."), KisIconUtils::loadIcon("object-locked")); } return editable; } void KisTool::listenToModifiers(bool listen) { Q_UNUSED(listen); } bool KisTool::listeningToModifiers() { return false; } diff --git a/libs/ui/tool/kis_tool.h b/libs/ui/tool/kis_tool.h index dcf5368519..efede6c1fd 100644 --- a/libs/ui/tool/kis_tool.h +++ b/libs/ui/tool/kis_tool.h @@ -1,321 +1,322 @@ /* * Copyright (c) 2006 Boudewijn Rempt * * 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. */ #ifndef KIS_TOOL_H_ #define KIS_TOOL_H_ #include #include #include #include #include #include #include #ifdef __GNUC__ #define WARN_WRONG_MODE(_mode) warnKrita << "Unexpected tool event has come to" << __func__ << "while being mode" << _mode << "!" #else #define WARN_WRONG_MODE(_mode) warnKrita << "Unexpected tool event has come while being mode" << _mode << "!" #endif #define CHECK_MODE_SANITY_OR_RETURN(_mode) if (mode() != _mode) { WARN_WRONG_MODE(mode()); return; } class KoCanvasBase; class KoPattern; class KoAbstractGradient; class KisFilterConfiguration; class QPainter; class QPainterPath; class QPolygonF; /// Definitions of the toolgroups of Krita static const QString TOOL_TYPE_SHAPE = "0 Krita/Shape"; // Geometric shapes like ellipses and lines static const QString TOOL_TYPE_TRANSFORM = "2 Krita/Transform"; // Tools that transform the layer; static const QString TOOL_TYPE_FILL = "3 Krita/Fill"; // Tools that fill parts of the canvas static const QString TOOL_TYPE_VIEW = "4 Krita/View"; // Tools that affect the canvas: pan, zoom, etc. static const QString TOOL_TYPE_SELECTION = "5 Krita/Select"; // Tools that select pixels //activation id for Krita tools, Krita tools are always active and handle locked and invisible layers by themself static const QString KRITA_TOOL_ACTIVATION_ID = "flake/always"; class KRITAUI_EXPORT KisTool : public KoToolBase { Q_OBJECT Q_PROPERTY(bool isActive READ isActive NOTIFY isActiveChanged) public: enum { FLAG_USES_CUSTOM_PRESET=0x01, FLAG_USES_CUSTOM_COMPOSITEOP=0x02, FLAG_USES_CUSTOM_SIZE=0x04 }; KisTool(KoCanvasBase * canvas, const QCursor & cursor); ~KisTool() override; virtual int flags() const { return 0; } void deleteSelection() override; // KoToolBase Implementation. public: /** * Called by KisToolProxy when the primary action of the tool is * going to be started now, that is when all the modifiers are * pressed and the only thing left is just to press the mouse * button. On coming of this callback the tool is supposed to * prepare the cursor and/or the outline to show the user shat is * going to happen next */ virtual void activatePrimaryAction(); /** * Called by KisToolProxy when the primary is no longer possible * to be started now, e.g. when its modifiers and released. The * tool is supposed revert all the preparetions it has doen in * activatePrimaryAction(). */ virtual void deactivatePrimaryAction(); /** * Called by KisToolProxy when a primary action for the tool is * started. The \p event stores the original event that * started the stroke. The \p event is _accepted_ by default. If * the tool decides to ignore this particular action (e.g. when * the node is not editable), it should call event->ignore(). Then * no further continuePrimaryAction() or endPrimaryAction() will * be called until the next user action. */ virtual void beginPrimaryAction(KoPointerEvent *event); /** * Called by KisToolProxy when the primary action is in progress * of pointer movement. If the tool has ignored the event in * beginPrimaryAction(), this method will not be called. */ virtual void continuePrimaryAction(KoPointerEvent *event); /** * Called by KisToolProxy when the primary action is being * finished, that is while mouseRelease or tabletRelease event. * If the tool has ignored the event in beginPrimaryAction(), this * method will not be called. */ virtual void endPrimaryAction(KoPointerEvent *event); /** * The same as beginPrimaryAction(), but called when the stroke is * started by a double-click * * \see beginPrimaryAction() */ virtual void beginPrimaryDoubleClickAction(KoPointerEvent *event); /** * Returns true if the tool can handle (and wants to handle) a * very tight flow of input events from the tablet */ virtual bool primaryActionSupportsHiResEvents() const; enum ToolAction { Primary, AlternateChangeSize, AlternatePickFgNode, AlternatePickBgNode, AlternatePickFgImage, AlternatePickBgImage, AlternateSecondary, AlternateThird, AlternateFourth, AlternateFifth, Alternate_NONE = 10000 }; // Technically users are allowed to configure this, but nobody ever would do that. // So these can basically be thought of as aliases to ctrl+click, etc. enum AlternateAction { ChangeSize = AlternateChangeSize, // Default: Shift+Left click PickFgNode = AlternatePickFgNode, // Default: Ctrl+Alt+Left click PickBgNode = AlternatePickBgNode, // Default: Ctrl+Alt+Right click PickFgImage = AlternatePickFgImage, // Default: Ctrl+Left click PickBgImage = AlternatePickBgImage, // Default: Ctrl+Right click Secondary = AlternateSecondary, Third = AlternateThird, Fourth = AlternateFourth, Fifth = AlternateFifth, NONE = 10000 }; static AlternateAction actionToAlternateAction(ToolAction action); virtual void activateAlternateAction(AlternateAction action); virtual void deactivateAlternateAction(AlternateAction action); virtual void beginAlternateAction(KoPointerEvent *event, AlternateAction action); virtual void continueAlternateAction(KoPointerEvent *event, AlternateAction action); virtual void endAlternateAction(KoPointerEvent *event, AlternateAction action); virtual void beginAlternateDoubleClickAction(KoPointerEvent *event, AlternateAction action); void mousePressEvent(KoPointerEvent *event) override; void mouseDoubleClickEvent(KoPointerEvent *event) override; void mouseTripleClickEvent(KoPointerEvent *event) override; void mouseReleaseEvent(KoPointerEvent *event) override; void mouseMoveEvent(KoPointerEvent *event) override; bool isActive() const; public Q_SLOTS: void activate(ToolActivation activation, const QSet &shapes) override; void deactivate() override; void canvasResourceChanged(int key, const QVariant & res) override; // Implement this slot in case there are any widgets or properties which need // to be updated after certain operations, to reflect the inner state correctly. // At the moment this is used for smoothing options in the freehand brush, but // this will likely be expanded. virtual void updateSettingsViews(); Q_SIGNALS: void isActiveChanged(); protected: // conversion methods are also needed by the paint information builder friend class KisToolPaintingInformationBuilder; /// Convert from native (postscript points) to image pixel /// coordinates. QPointF convertToPixelCoord(KoPointerEvent *e); QPointF convertToPixelCoord(const QPointF& pt); QPointF convertToPixelCoordAndSnap(KoPointerEvent *e, const QPointF &offset = QPointF(), bool useModifiers = true); QPointF convertToPixelCoordAndSnap(const QPointF& pt, const QPointF &offset = QPointF()); protected: QPointF widgetCenterInWidgetPixels(); QPointF convertDocumentToWidget(const QPointF& pt); /// Convert from native (postscript points) to integer image pixel /// coordinates. This rounds down (not truncate) the pixel coordinates and /// should be used in preference to QPointF::toPoint(), which rounds, /// to ensure the cursor acts on the pixel it is visually over. QPoint convertToImagePixelCoordFloored(KoPointerEvent *e); QRectF convertToPt(const QRectF &rect); + qreal convertToPt(qreal value); QPointF viewToPixel(const QPointF &viewCoord) const; /// Convert an integer pixel coordinate into a view coordinate. /// The view coordinate is at the centre of the pixel. QPointF pixelToView(const QPoint &pixelCoord) const; /// Convert a floating point pixel coordinate into a view coordinate. QPointF pixelToView(const QPointF &pixelCoord) const; /// Convert a pixel rectangle into a view rectangle. QRectF pixelToView(const QRectF &pixelRect) const; /// Convert a pixel path into a view path QPainterPath pixelToView(const QPainterPath &pixelPath) const; /// Convert a pixel polygon into a view path QPolygonF pixelToView(const QPolygonF &pixelPolygon) const; /// Update the canvas for the given rectangle in image pixel coordinates. void updateCanvasPixelRect(const QRectF &pixelRect); /// Update the canvas for the given rectangle in view coordinates. void updateCanvasViewRect(const QRectF &viewRect); QWidget* createOptionWidget() override; /** * To determine whether this tool will change its behavior when * modifier keys are pressed */ virtual bool listeningToModifiers(); /** * Request that this tool no longer listen to modifier keys * (Responding to the request is optional) */ virtual void listenToModifiers(bool listen); protected: KisImageWSP image() const; QCursor cursor() const; /// Call this to set the document modified void notifyModified() const; KisImageWSP currentImage(); KoPattern* currentPattern(); KoAbstractGradient *currentGradient(); KisNodeSP currentNode() const; KisNodeList selectedNodes() const; KoColor currentFgColor(); KoColor currentBgColor(); KisPaintOpPresetSP currentPaintOpPreset(); KisFilterConfigurationSP currentGenerator(); /// paint the path which is in view coordinates, default paint mode is XOR_MODE, BW_MODE is also possible /// never apply transformations to the painter, they would be useless, if drawing in OpenGL mode. The coordinates in the path should be in view coordinates. void paintToolOutline(QPainter * painter, const QPainterPath &path); /// Checks checks if the current node is editable bool nodeEditable(); /// Checks checks if the selection is editable, only applies to local selection as global selection is always editable bool selectionEditable(); /// Override the cursor appropriately if current node is not editable bool overrideCursorIfNotEditable(); bool blockUntilOperationsFinished(); void blockUntilOperationsFinishedForced(); protected: enum ToolMode { HOVER_MODE, PAINT_MODE, SECONDARY_PAINT_MODE, MIRROR_AXIS_SETUP_MODE, GESTURE_MODE, PAN_MODE, OTHER // not used now }; virtual void setMode(ToolMode mode); virtual ToolMode mode() const; void setCursor(const QCursor &cursor); protected Q_SLOTS: /** * Called whenever the configuration settings change. */ virtual void resetCursorStyle(); private Q_SLOTS: void slotToggleFgBg(); void slotResetFgBg(); private: struct Private; Private* const d; }; #endif // KIS_TOOL_H_ diff --git a/libs/ui/tool/kis_tool_ellipse_base.cpp b/libs/ui/tool/kis_tool_ellipse_base.cpp index fa291d2d93..5ea46fb467 100644 --- a/libs/ui/tool/kis_tool_ellipse_base.cpp +++ b/libs/ui/tool/kis_tool_ellipse_base.cpp @@ -1,43 +1,48 @@ /* This file is part of the KDE project * Copyright (C) 2009 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 "kis_tool_ellipse_base.h" #include #include #include #include #include "kis_canvas2.h" KisToolEllipseBase::KisToolEllipseBase(KoCanvasBase * canvas, KisToolEllipseBase::ToolType type, const QCursor & cursor) : KisToolRectangleBase(canvas, type, cursor) { } void KisToolEllipseBase::paintRectangle(QPainter &gc, const QRectF &imageRect) { KIS_ASSERT_RECOVER_RETURN(canvas()); QRect viewRect = pixelToView(imageRect).toRect(); QPainterPath path; path.addEllipse(viewRect); paintToolOutline(&gc, path); } + +bool KisToolEllipseBase::showRoundCornersGUI() const +{ + return false; +} diff --git a/libs/ui/tool/kis_tool_ellipse_base.h b/libs/ui/tool/kis_tool_ellipse_base.h index 8d5231f9e5..487c80a3f5 100644 --- a/libs/ui/tool/kis_tool_ellipse_base.h +++ b/libs/ui/tool/kis_tool_ellipse_base.h @@ -1,34 +1,37 @@ /* This file is part of the KDE project * Copyright (C) 2009 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. */ #ifndef KIS_TOOL_ELLIPSE_BASE_H #define KIS_TOOL_ELLIPSE_BASE_H #include #include class KRITAUI_EXPORT KisToolEllipseBase : public KisToolRectangleBase { public: KisToolEllipseBase(KoCanvasBase * canvas, KisToolEllipseBase::ToolType type, const QCursor & cursor=KisCursor::load("tool_ellipse_cursor.png", 6, 6)); void paintRectangle(QPainter &gc, const QRectF &imageRect) override; + +protected: + bool showRoundCornersGUI() const override; }; #endif // KIS_TOOL_ELLIPSE_BASE_H diff --git a/libs/ui/tool/kis_tool_rectangle_base.cpp b/libs/ui/tool/kis_tool_rectangle_base.cpp index 1ae46e4b0e..2c6e9270c2 100644 --- a/libs/ui/tool/kis_tool_rectangle_base.cpp +++ b/libs/ui/tool/kis_tool_rectangle_base.cpp @@ -1,247 +1,281 @@ /* This file is part of the KDE project * Copyright (C) 2009 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 "kis_tool_rectangle_base.h" #include #include #include #include #include +#include "kis_canvas2.h" #include "kis_rectangle_constraint_widget.h" KisToolRectangleBase::KisToolRectangleBase(KoCanvasBase * canvas, KisToolRectangleBase::ToolType type, const QCursor & cursor) : KisToolShape(canvas, cursor) , m_dragStart(0, 0) , m_dragEnd(0, 0) , m_type(type) , m_isRatioForced(false) , m_isWidthForced(false) , m_isHeightForced(false) , m_listenToModifiers(true) , m_forcedRatio(1.0) , m_forcedWidth(0) , m_forcedHeight(0) + , m_roundCornersX(0) + , m_roundCornersY(0) { } QList > KisToolRectangleBase::createOptionWidgets() { QList > widgetsList = KisToolShape::createOptionWidgets(); - widgetsList.append(new KisRectangleConstraintWidget(0, this)); + widgetsList.append(new KisRectangleConstraintWidget(0, this, showRoundCornersGUI())); return widgetsList; } void KisToolRectangleBase::constraintsChanged(bool forceRatio, bool forceWidth, bool forceHeight, float ratio, float width, float height) { m_isWidthForced = forceWidth; m_isHeightForced = forceHeight; m_isRatioForced = forceRatio; m_forcedHeight = height; m_forcedWidth = width; m_forcedRatio = ratio; // Avoid division by zero in size calculations if (ratio < 0.0001f) m_isRatioForced = false; } +void KisToolRectangleBase::roundCornersChanged(int rx, int ry) +{ + m_roundCornersX = rx; + m_roundCornersY = ry; +} + void KisToolRectangleBase::paint(QPainter& gc, const KoViewConverter &converter) { if(mode() == KisTool::PAINT_MODE) { paintRectangle(gc, createRect(m_dragStart, m_dragEnd)); } KisToolPaint::paint(gc, converter); } +void KisToolRectangleBase::activate(KoToolBase::ToolActivation toolActivation, const QSet &shapes) +{ + KisToolShape::activate(toolActivation, shapes); + + emit sigRequestReloadConfig(); +} + void KisToolRectangleBase::deactivate() { updateArea(); KisToolShape::deactivate(); } void KisToolRectangleBase::listenToModifiers(bool listen) { m_listenToModifiers = listen; } bool KisToolRectangleBase::listeningToModifiers() { return m_listenToModifiers; } void KisToolRectangleBase::beginPrimaryAction(KoPointerEvent *event) { if ((m_type == PAINT && (!nodeEditable() || nodePaintAbility() == NONE)) || (m_type == SELECT && !selectionEditable())) { event->ignore(); return; } setMode(KisTool::PAINT_MODE); QPointF pos = convertToPixelCoordAndSnap(event, QPointF(), false); m_dragStart = m_dragCenter = pos; QSizeF area = QSizeF(0,0); applyConstraints(area, false); m_dragEnd.setX(m_dragStart.x() + area.width()); m_dragEnd.setY(m_dragStart.y() + area.height()); event->accept(); } bool KisToolRectangleBase::isFixedSize() { if (m_isWidthForced && m_isHeightForced) return true; if (m_isRatioForced && (m_isWidthForced || m_isHeightForced)) return true; return false; } void KisToolRectangleBase::applyConstraints(QSizeF &area, bool overrideRatio) { if (m_isWidthForced) { area.setWidth(m_forcedWidth); } if (m_isHeightForced) { area.setHeight(m_forcedHeight); } if (m_isHeightForced && m_isWidthForced) return; if (m_isRatioForced || overrideRatio) { float ratio = m_isRatioForced ? m_forcedRatio : 1.0f; if (m_isWidthForced) { area.setHeight(area.width() / ratio); } else { area.setWidth(area.height() * ratio); } } } void KisToolRectangleBase::continuePrimaryAction(KoPointerEvent *event) { CHECK_MODE_SANITY_OR_RETURN(KisTool::PAINT_MODE); bool constraintToggle = (event->modifiers() & Qt::ShiftModifier) && m_listenToModifiers; bool translateMode = (event->modifiers() & Qt::AltModifier) && m_listenToModifiers; bool expandFromCenter = (event->modifiers() & Qt::ControlModifier) && m_listenToModifiers; bool fixedSize = isFixedSize() && !constraintToggle; QPointF pos = convertToPixelCoordAndSnap(event, QPointF(), false); if (fixedSize) { m_dragStart = pos; } else if (translateMode) { QPointF trans = pos - m_dragEnd; m_dragStart += trans; m_dragEnd += trans; } QPointF diag = pos - m_dragStart; QSizeF area = QSizeF(fabs(diag.x()), fabs(diag.y())); bool overrideRatio = constraintToggle && !(m_isHeightForced || m_isWidthForced || m_isRatioForced); if (!constraintToggle || overrideRatio) { applyConstraints(area, overrideRatio); } diag = QPointF( (diag.x() < 0) ? -area.width() : area.width(), (diag.y() < 0) ? -area.height() : area.height() ); // resize around center point? if (expandFromCenter && !fixedSize) { m_dragStart = m_dragCenter - diag / 2; m_dragEnd = m_dragCenter + diag / 2; } else { m_dragEnd = m_dragStart + diag; } updateArea(); m_dragCenter = QPointF((m_dragStart.x() + m_dragEnd.x()) / 2, (m_dragStart.y() + m_dragEnd.y()) / 2); KisToolPaint::requestUpdateOutline(event->point, event); } void KisToolRectangleBase::endPrimaryAction(KoPointerEvent *event) { CHECK_MODE_SANITY_OR_RETURN(KisTool::PAINT_MODE); setMode(KisTool::HOVER_MODE); updateArea(); - finishRect(createRect(m_dragStart, m_dragEnd)); + finishRect(createRect(m_dragStart, m_dragEnd), m_roundCornersX, m_roundCornersY); event->accept(); } QRectF KisToolRectangleBase::createRect(const QPointF &start, const QPointF &end) { /** * To make the dragging user-friendly it should work in a bit * non-obvious way: the start-drag point must be handled with * "ceil"/"floor" (depending on the direction of the drag) and the * end-drag point should follow usual "round" semantics. */ qreal x0 = start.x(); qreal y0 = start.y(); qreal x1 = end.x(); qreal y1 = end.y(); int newX0 = qRound(x0); int newY0 = qRound(y0); int newX1 = qRound(x1); int newY1 = qRound(y1); QRectF result; result.setCoords(newX0, newY0, newX1, newY1); return result.normalized(); } +bool KisToolRectangleBase::showRoundCornersGUI() const +{ + return true; +} + void KisToolRectangleBase::paintRectangle(QPainter &gc, const QRectF &imageRect) { KIS_ASSERT_RECOVER_RETURN(canvas()); - QRect viewRect = pixelToView(imageRect).toAlignedRect(); + const QRect viewRect = pixelToView(imageRect).toAlignedRect(); + + KisCanvas2 *kritaCanvas = dynamic_cast(canvas()); + KIS_SAFE_ASSERT_RECOVER_RETURN(kritaCanvas); + + const KisCoordinatesConverter *converter = kritaCanvas->coordinatesConverter(); + const qreal roundCornersX = converter->effectiveZoom() * m_roundCornersX; + const qreal roundCornersY = converter->effectiveZoom() * m_roundCornersY; QPainterPath path; - path.addRect(viewRect); + + if (m_roundCornersX > 0 || m_roundCornersY > 0) { + path.addRoundedRect(viewRect, + roundCornersX, roundCornersY); + } else { + path.addRect(viewRect); + } paintToolOutline(&gc, path); } void KisToolRectangleBase::updateArea() { const QRectF bound = createRect(m_dragStart, m_dragEnd); canvas()->updateCanvas(convertToPt(bound).adjusted(-100, -100, +200, +200)); emit rectangleChanged(bound); } diff --git a/libs/ui/tool/kis_tool_rectangle_base.h b/libs/ui/tool/kis_tool_rectangle_base.h index 3ab9b2116d..f5684f092d 100644 --- a/libs/ui/tool/kis_tool_rectangle_base.h +++ b/libs/ui/tool/kis_tool_rectangle_base.h @@ -1,79 +1,84 @@ /* This file is part of the KDE project * Copyright (C) 2009 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. */ #ifndef KIS_TOOL_RECTANGLE_BASE_H #define KIS_TOOL_RECTANGLE_BASE_H #include #include class KRITAUI_EXPORT KisToolRectangleBase : public KisToolShape { Q_OBJECT Q_SIGNALS: void rectangleChanged(const QRectF &newRect); + void sigRequestReloadConfig(); public Q_SLOTS: void constraintsChanged(bool forceRatio, bool forceWidth, bool forceHeight, float ratio, float width, float height); - + void roundCornersChanged(int rx, int ry); public: enum ToolType { PAINT, SELECT }; explicit KisToolRectangleBase(KoCanvasBase * canvas, KisToolRectangleBase::ToolType type, const QCursor & cursor=KisCursor::load("tool_rectangle_cursor.png", 6, 6)); void beginPrimaryAction(KoPointerEvent *event) override; void continuePrimaryAction(KoPointerEvent *event) override; void endPrimaryAction(KoPointerEvent *event) override; void paint(QPainter& gc, const KoViewConverter &converter) override; + void activate(ToolActivation toolActivation, const QSet &shapes) override; void deactivate() override; void listenToModifiers(bool listen) override; bool listeningToModifiers() override; QList > createOptionWidgets() override; protected: - virtual void finishRect(const QRectF&)=0; + virtual void finishRect(const QRectF &rect, qreal roundCornersX, qreal roundCornersY) = 0; QPointF m_dragCenter; QPointF m_dragStart; QPointF m_dragEnd; ToolType m_type; bool m_isRatioForced; bool m_isWidthForced; bool m_isHeightForced; bool m_listenToModifiers; float m_forcedRatio; float m_forcedWidth; float m_forcedHeight; + int m_roundCornersX; + int m_roundCornersY; bool isFixedSize(); void applyConstraints(QSizeF& area, bool overrideRatio); void updateArea(); virtual void paintRectangle(QPainter &gc, const QRectF &imageRect); virtual QRectF createRect(const QPointF &start, const QPointF &end); + virtual bool showRoundCornersGUI() const; }; #endif // KIS_TOOL_RECTANGLE_BASE_H diff --git a/plugins/dockers/defaultdockers/kis_layer_box.cpp b/plugins/dockers/defaultdockers/kis_layer_box.cpp index 5c7f23c00c..006cccba70 100644 --- a/plugins/dockers/defaultdockers/kis_layer_box.cpp +++ b/plugins/dockers/defaultdockers/kis_layer_box.cpp @@ -1,1072 +1,1082 @@ /* * kis_layer_box.cc - part of Krita aka Krayon aka KimageShop * * Copyright (c) 2002 Patrick Julien * Copyright (C) 2006 Gábor Lehel * Copyright (C) 2007 Thomas Zander * Copyright (C) 2007 Boudewijn Rempt * Copyright (c) 2011 José Luis Vergara * * 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_layer_box.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_action.h" #include "kis_action_manager.h" #include "widgets/kis_cmb_composite.h" #include "widgets/kis_slider_spin_box.h" #include "KisViewManager.h" #include "kis_node_manager.h" #include "kis_node_model.h" #include "canvas/kis_canvas2.h" #include "kis_dummies_facade_base.h" #include "kis_shape_controller.h" #include "kis_selection_mask.h" #include "kis_config.h" #include "KisView.h" #include "krita_utils.h" #include "sync_button_and_action.h" #include "kis_color_label_selector_widget.h" #include "kis_signals_blocker.h" #include "kis_color_filter_combo.h" #include "kis_node_filter_proxy_model.h" #include "kis_selection.h" #include "kis_processing_applicator.h" #include "commands/kis_set_global_selection_command.h" +#include "KisSelectionActionsAdapter.h" #include "kis_layer_utils.h" #include "ui_wdglayerbox.h" #include class KisLayerBoxStyle : public QProxyStyle { public: KisLayerBoxStyle(QStyle *baseStyle = 0) : QProxyStyle(baseStyle) {} void drawPrimitive(PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const { if (element == QStyle::PE_IndicatorItemViewItemDrop) { QColor color(widget->palette().color(QPalette::Highlight).lighter()); if (option->rect.height() == 0) { QBrush brush(color); QRect r(option->rect); r.setTop(r.top() - 2); r.setBottom(r.bottom() + 2); painter->fillRect(r, brush); } else { color.setAlpha(200); QBrush brush(color); painter->fillRect(option->rect, brush); } } else { QProxyStyle::drawPrimitive(element, option, painter, widget); } } }; inline void KisLayerBox::connectActionToButton(KisViewManager* viewManager, QAbstractButton *button, const QString &id) { if (!viewManager || !button) return; KisAction *action = viewManager->actionManager()->actionByName(id); if (!action) return; connect(button, SIGNAL(clicked()), action, SLOT(trigger())); connect(action, SIGNAL(sigEnableSlaves(bool)), button, SLOT(setEnabled(bool))); connect(viewManager->mainWindow(), SIGNAL(themeChanged()), this, SLOT(slotUpdateIcons())); } inline void KisLayerBox::addActionToMenu(QMenu *menu, const QString &id) { if (m_canvas) { menu->addAction(m_canvas->viewManager()->actionManager()->actionByName(id)); } } KisLayerBox::KisLayerBox() : QDockWidget(i18n("Layers")) , m_canvas(0) , m_wdgLayerBox(new Ui_WdgLayerBox) , m_thumbnailCompressor(500, KisSignalCompressor::FIRST_INACTIVE) , m_colorLabelCompressor(900, KisSignalCompressor::FIRST_INACTIVE) , m_thumbnailSizeCompressor(100, KisSignalCompressor::FIRST_INACTIVE) { KisConfig cfg(false); QWidget* mainWidget = new QWidget(this); setWidget(mainWidget); m_opacityDelayTimer.setSingleShot(true); m_wdgLayerBox->setupUi(mainWidget); m_wdgLayerBox->listLayers->setStyle(new KisLayerBoxStyle(m_wdgLayerBox->listLayers->style())); connect(m_wdgLayerBox->listLayers, SIGNAL(contextMenuRequested(const QPoint&, const QModelIndex&)), this, SLOT(slotContextMenuRequested(const QPoint&, const QModelIndex&))); connect(m_wdgLayerBox->listLayers, SIGNAL(collapsed(const QModelIndex&)), SLOT(slotCollapsed(const QModelIndex &))); connect(m_wdgLayerBox->listLayers, SIGNAL(expanded(const QModelIndex&)), SLOT(slotExpanded(const QModelIndex &))); connect(m_wdgLayerBox->listLayers, SIGNAL(selectionChanged(const QModelIndexList&)), SLOT(selectionChanged(const QModelIndexList&))); slotUpdateIcons(); m_wdgLayerBox->bnDelete->setIconSize(QSize(22, 22)); m_wdgLayerBox->bnRaise->setIconSize(QSize(22, 22)); m_wdgLayerBox->bnLower->setIconSize(QSize(22, 22)); m_wdgLayerBox->bnProperties->setIconSize(QSize(22, 22)); m_wdgLayerBox->bnDuplicate->setIconSize(QSize(22, 22)); m_wdgLayerBox->bnLower->setEnabled(false); m_wdgLayerBox->bnRaise->setEnabled(false); if (cfg.sliderLabels()) { m_wdgLayerBox->opacityLabel->hide(); m_wdgLayerBox->doubleOpacity->setPrefix(QString("%1: ").arg(i18n("Opacity"))); } m_wdgLayerBox->doubleOpacity->setRange(0, 100, 0); m_wdgLayerBox->doubleOpacity->setSuffix("%"); connect(m_wdgLayerBox->doubleOpacity, SIGNAL(valueChanged(qreal)), SLOT(slotOpacitySliderMoved(qreal))); connect(&m_opacityDelayTimer, SIGNAL(timeout()), SLOT(slotOpacityChanged())); connect(m_wdgLayerBox->cmbComposite, SIGNAL(activated(int)), SLOT(slotCompositeOpChanged(int))); m_newLayerMenu = new QMenu(this); m_wdgLayerBox->bnAdd->setMenu(m_newLayerMenu); m_wdgLayerBox->bnAdd->setPopupMode(QToolButton::MenuButtonPopup); m_nodeModel = new KisNodeModel(this); m_filteringModel = new KisNodeFilterProxyModel(this); m_filteringModel->setNodeModel(m_nodeModel); /** * Connect model updateUI() to enable/disable controls. * Note: nodeActivated() is connected separately in setImage(), because * it needs particular order of calls: first the connection to the * node manager should be called, then updateUI() */ connect(m_nodeModel, SIGNAL(rowsInserted(const QModelIndex&, int, int)), SLOT(updateUI())); connect(m_nodeModel, SIGNAL(rowsRemoved(const QModelIndex&, int, int)), SLOT(updateUI())); connect(m_nodeModel, SIGNAL(rowsMoved(const QModelIndex&, int, int, const QModelIndex&, int)), SLOT(updateUI())); connect(m_nodeModel, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), SLOT(updateUI())); connect(m_nodeModel, SIGNAL(modelReset()), SLOT(slotModelReset())); KisAction *showGlobalSelectionMask = new KisAction(i18n("&Show Global Selection Mask"), this); showGlobalSelectionMask->setObjectName("show-global-selection-mask"); showGlobalSelectionMask->setActivationFlags(KisAction::ACTIVE_IMAGE); showGlobalSelectionMask->setToolTip(i18nc("@info:tooltip", "Shows global selection as a usual selection mask in Layers docker")); showGlobalSelectionMask->setCheckable(true); connect(showGlobalSelectionMask, SIGNAL(triggered(bool)), SLOT(slotEditGlobalSelection(bool))); m_actions.append(showGlobalSelectionMask); showGlobalSelectionMask->setChecked(cfg.showGlobalSelection()); m_colorSelector = new KisColorLabelSelectorWidget(this); connect(m_colorSelector, SIGNAL(currentIndexChanged(int)), SLOT(slotColorLabelChanged(int))); m_colorSelectorAction = new QWidgetAction(this); m_colorSelectorAction->setDefaultWidget(m_colorSelector); connect(m_nodeModel, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)), &m_colorLabelCompressor, SLOT(start())); m_wdgLayerBox->listLayers->setModel(m_filteringModel); // this connection should be done *after* the setModel() call to // happen later than the internal selection model connect(m_filteringModel.data(), &KisNodeFilterProxyModel::rowsAboutToBeRemoved, this, &KisLayerBox::slotAboutToRemoveRows); connect(m_wdgLayerBox->cmbFilter, SIGNAL(selectedColorsChanged()), SLOT(updateLayerFiltering())); setEnabled(false); connect(&m_thumbnailCompressor, SIGNAL(timeout()), SLOT(updateThumbnail())); connect(&m_colorLabelCompressor, SIGNAL(timeout()), SLOT(updateAvailableLabels())); // set up the configure menu for changing thumbnail size QMenu* configureMenu = new QMenu(this); configureMenu->setStyleSheet("margin: 6px"); configureMenu->addSection(i18n("Thumbnail Size")); m_wdgLayerBox->configureLayerDockerToolbar->setMenu(configureMenu); m_wdgLayerBox->configureLayerDockerToolbar->setIcon(KisIconUtils::loadIcon("configure")); m_wdgLayerBox->configureLayerDockerToolbar->setIconSize(QSize(13, 13)); m_wdgLayerBox->configureLayerDockerToolbar->setPopupMode(QToolButton::InstantPopup); // add horizontal slider thumbnailSizeSlider = new QSlider(this); thumbnailSizeSlider->setOrientation(Qt::Horizontal); thumbnailSizeSlider->setRange(20, 80); thumbnailSizeSlider->setValue(cfg.layerThumbnailSize(false)); // grab this from the kritarc thumbnailSizeSlider->setMinimumHeight(20); thumbnailSizeSlider->setMinimumWidth(40); thumbnailSizeSlider->setTickInterval(5); QWidgetAction *sliderAction= new QWidgetAction(this); sliderAction->setDefaultWidget(thumbnailSizeSlider); configureMenu->addAction(sliderAction); connect(thumbnailSizeSlider, SIGNAL(sliderMoved(int)), &m_thumbnailSizeCompressor, SLOT(start())); connect(&m_thumbnailSizeCompressor, SIGNAL(timeout()), SLOT(slotUpdateThumbnailIconSize())); } KisLayerBox::~KisLayerBox() { delete m_wdgLayerBox; } void expandNodesRecursively(KisNodeSP root, QPointer filteringModel, KisNodeView *nodeView) { if (!root) return; if (filteringModel.isNull()) return; if (!nodeView) return; nodeView->blockSignals(true); KisNodeSP node = root->firstChild(); while (node) { QModelIndex idx = filteringModel->indexFromNode(node); if (idx.isValid()) { nodeView->setExpanded(idx, !node->collapsed()); } if (node->childCount() > 0) { expandNodesRecursively(node, filteringModel, nodeView); } node = node->nextSibling(); } nodeView->blockSignals(false); } void KisLayerBox::slotAddLayerBnClicked() { if (m_canvas) { KisNodeList nodes = m_nodeManager->selectedNodes(); if (nodes.size() == 1) { KisAction *action = m_canvas->viewManager()->actionManager()->actionByName("add_new_paint_layer"); action->trigger(); } else { KisAction *action = m_canvas->viewManager()->actionManager()->actionByName("create_quick_group"); action->trigger(); } } } void KisLayerBox::setViewManager(KisViewManager* kisview) { m_nodeManager = kisview->nodeManager(); Q_FOREACH (KisAction *action, m_actions) { kisview->actionManager()-> addAction(action->objectName(), action); } connect(m_wdgLayerBox->bnAdd, SIGNAL(clicked()), this, SLOT(slotAddLayerBnClicked())); connectActionToButton(kisview, m_wdgLayerBox->bnDuplicate, "duplicatelayer"); KisActionManager *actionManager = kisview->actionManager(); KisAction *action = actionManager->createAction("RenameCurrentLayer"); Q_ASSERT(action); connect(action, SIGNAL(triggered()), this, SLOT(slotRenameCurrentNode())); m_propertiesAction = actionManager->createAction("layer_properties"); Q_ASSERT(m_propertiesAction); new SyncButtonAndAction(m_propertiesAction, m_wdgLayerBox->bnProperties, this); connect(m_propertiesAction, SIGNAL(triggered()), this, SLOT(slotPropertiesClicked())); m_removeAction = actionManager->createAction("remove_layer"); Q_ASSERT(m_removeAction); new SyncButtonAndAction(m_removeAction, m_wdgLayerBox->bnDelete, this); connect(m_removeAction, SIGNAL(triggered()), this, SLOT(slotRmClicked())); action = actionManager->createAction("move_layer_up"); Q_ASSERT(action); new SyncButtonAndAction(action, m_wdgLayerBox->bnRaise, this); connect(action, SIGNAL(triggered()), this, SLOT(slotRaiseClicked())); action = actionManager->createAction("move_layer_down"); Q_ASSERT(action); new SyncButtonAndAction(action, m_wdgLayerBox->bnLower, this); connect(action, SIGNAL(triggered()), this, SLOT(slotLowerClicked())); } void KisLayerBox::setCanvas(KoCanvasBase *canvas) { if (m_canvas == canvas) return; setEnabled(canvas != 0); if (m_canvas) { m_canvas->disconnectCanvasObserver(this); - m_nodeModel->setDummiesFacade(0, 0, 0, 0, 0); + m_nodeModel->setDummiesFacade(0, 0, 0, 0, 0, 0); + m_selectionActionsAdapter.reset(); if (m_image) { KisImageAnimationInterface *animation = m_image->animationInterface(); animation->disconnect(this); } disconnect(m_image, 0, this, 0); disconnect(m_nodeManager, 0, this, 0); disconnect(m_nodeModel, 0, m_nodeManager, 0); m_nodeManager->slotSetSelectedNodes(KisNodeList()); } m_canvas = dynamic_cast(canvas); if (m_canvas) { m_image = m_canvas->image(); connect(m_image, SIGNAL(sigImageUpdated(QRect)), &m_thumbnailCompressor, SLOT(start())); KisDocument* doc = static_cast(m_canvas->imageView()->document()); KisShapeController *kritaShapeController = dynamic_cast(doc->shapeController()); KisDummiesFacadeBase *kritaDummiesFacade = static_cast(kritaShapeController); - m_nodeModel->setDummiesFacade(kritaDummiesFacade, m_image, kritaShapeController, m_nodeManager->nodeSelectionAdapter(), m_nodeManager->nodeInsertionAdapter()); + + + m_selectionActionsAdapter.reset(new KisSelectionActionsAdapter(m_canvas->viewManager()->selectionManager())); + m_nodeModel->setDummiesFacade(kritaDummiesFacade, + m_image, + kritaShapeController, + m_nodeManager->nodeSelectionAdapter(), + m_nodeManager->nodeInsertionAdapter(), + m_selectionActionsAdapter.data()); connect(m_image, SIGNAL(sigAboutToBeDeleted()), SLOT(notifyImageDeleted())); connect(m_image, SIGNAL(sigNodeCollapsedChanged()), SLOT(slotNodeCollapsedChanged())); // cold start if (m_nodeManager) { setCurrentNode(m_nodeManager->activeNode()); // Connection KisNodeManager -> KisLayerBox connect(m_nodeManager, SIGNAL(sigUiNeedChangeActiveNode(KisNodeSP)), this, SLOT(setCurrentNode(KisNodeSP))); connect(m_nodeManager, SIGNAL(sigUiNeedChangeSelectedNodes(const QList &)), SLOT(slotNodeManagerChangedSelection(const QList &))); } else { setCurrentNode(m_canvas->imageView()->currentNode()); } // Connection KisLayerBox -> KisNodeManager (isolate layer) connect(m_nodeModel, SIGNAL(toggleIsolateActiveNode()), m_nodeManager, SLOT(toggleIsolateActiveNode())); KisImageAnimationInterface *animation = m_image->animationInterface(); connect(animation, &KisImageAnimationInterface::sigUiTimeChanged, this, &KisLayerBox::slotImageTimeChanged); expandNodesRecursively(m_image->rootLayer(), m_filteringModel, m_wdgLayerBox->listLayers); m_wdgLayerBox->listLayers->scrollTo(m_wdgLayerBox->listLayers->currentIndex()); updateAvailableLabels(); addActionToMenu(m_newLayerMenu, "add_new_paint_layer"); addActionToMenu(m_newLayerMenu, "add_new_group_layer"); addActionToMenu(m_newLayerMenu, "add_new_clone_layer"); addActionToMenu(m_newLayerMenu, "add_new_shape_layer"); addActionToMenu(m_newLayerMenu, "add_new_adjustment_layer"); addActionToMenu(m_newLayerMenu, "add_new_fill_layer"); addActionToMenu(m_newLayerMenu, "add_new_file_layer"); m_newLayerMenu->addSeparator(); addActionToMenu(m_newLayerMenu, "add_new_transparency_mask"); addActionToMenu(m_newLayerMenu, "add_new_filter_mask"); addActionToMenu(m_newLayerMenu, "add_new_colorize_mask"); addActionToMenu(m_newLayerMenu, "add_new_transform_mask"); addActionToMenu(m_newLayerMenu, "add_new_selection_mask"); } } void KisLayerBox::unsetCanvas() { setEnabled(false); if (m_canvas) { m_newLayerMenu->clear(); } m_filteringModel->unsetDummiesFacade(); disconnect(m_image, 0, this, 0); disconnect(m_nodeManager, 0, this, 0); disconnect(m_nodeModel, 0, m_nodeManager, 0); m_nodeManager->slotSetSelectedNodes(KisNodeList()); m_canvas = 0; } void KisLayerBox::notifyImageDeleted() { setCanvas(0); } void KisLayerBox::updateUI() { if (!m_canvas) return; if (!m_nodeManager) return; KisNodeSP activeNode = m_nodeManager->activeNode(); if (activeNode != m_activeNode) { if( !m_activeNode.isNull() ) m_activeNode->disconnect(this); m_activeNode = activeNode; if (activeNode) { KisKeyframeChannel *opacityChannel = activeNode->getKeyframeChannel(KisKeyframeChannel::Opacity.id(), false); if (opacityChannel) { watchOpacityChannel(opacityChannel); } else { watchOpacityChannel(0); connect(activeNode.data(), &KisNode::keyframeChannelAdded, this, &KisLayerBox::slotKeyframeChannelAdded); } } } m_wdgLayerBox->bnRaise->setEnabled(activeNode && activeNode->isEditable(false) && (activeNode->nextSibling() || (activeNode->parent() && activeNode->parent() != m_image->root()))); m_wdgLayerBox->bnLower->setEnabled(activeNode && activeNode->isEditable(false) && (activeNode->prevSibling() || (activeNode->parent() && activeNode->parent() != m_image->root()))); m_wdgLayerBox->doubleOpacity->setEnabled(activeNode && activeNode->isEditable(false)); m_wdgLayerBox->cmbComposite->setEnabled(activeNode && activeNode->isEditable(false)); m_wdgLayerBox->cmbComposite->validate(m_image->colorSpace()); if (activeNode) { if (activeNode->inherits("KisColorizeMask") || activeNode->inherits("KisLayer")) { m_wdgLayerBox->doubleOpacity->setEnabled(true); slotSetOpacity(activeNode->opacity() * 100.0 / 255); const KoCompositeOp* compositeOp = activeNode->compositeOp(); if (compositeOp) { slotSetCompositeOp(compositeOp); } else { m_wdgLayerBox->cmbComposite->setEnabled(false); } const KisGroupLayer *group = qobject_cast(activeNode.data()); bool compositeSelectionActive = !(group && group->passThroughMode()); m_wdgLayerBox->cmbComposite->setEnabled(compositeSelectionActive); } else if (activeNode->inherits("KisMask")) { m_wdgLayerBox->cmbComposite->setEnabled(false); m_wdgLayerBox->doubleOpacity->setEnabled(false); } } } /** * This method is called *only* when non-GUI code requested the * change of the current node */ void KisLayerBox::setCurrentNode(KisNodeSP node) { m_filteringModel->setActiveNode(node); QModelIndex index = node ? m_filteringModel->indexFromNode(node) : QModelIndex(); m_filteringModel->setData(index, true, KisNodeModel::ActiveRole); updateUI(); } void KisLayerBox::slotModelReset() { if(m_nodeModel->hasDummiesFacade()) { QItemSelection selection; Q_FOREACH (const KisNodeSP node, m_nodeManager->selectedNodes()) { const QModelIndex &idx = m_filteringModel->indexFromNode(node); if(idx.isValid()){ QItemSelectionRange selectionRange(idx); selection << selectionRange; } } m_wdgLayerBox->listLayers->selectionModel()->select(selection, QItemSelectionModel::ClearAndSelect); } updateUI(); } void KisLayerBox::slotSetCompositeOp(const KoCompositeOp* compositeOp) { KoID opId = KoCompositeOpRegistry::instance().getKoID(compositeOp->id()); m_wdgLayerBox->cmbComposite->blockSignals(true); m_wdgLayerBox->cmbComposite->selectCompositeOp(opId); m_wdgLayerBox->cmbComposite->blockSignals(false); } // range: 0-100 void KisLayerBox::slotSetOpacity(double opacity) { Q_ASSERT(opacity >= 0 && opacity <= 100); m_wdgLayerBox->doubleOpacity->blockSignals(true); m_wdgLayerBox->doubleOpacity->setValue(opacity); m_wdgLayerBox->doubleOpacity->blockSignals(false); } void KisLayerBox::slotContextMenuRequested(const QPoint &pos, const QModelIndex &index) { KisNodeList nodes = m_nodeManager->selectedNodes(); KisNodeSP activeNode = m_nodeManager->activeNode(); if (nodes.isEmpty() || !activeNode) return; if (m_canvas) { QMenu menu; const bool singleLayer = nodes.size() == 1; if (index.isValid()) { menu.addAction(m_propertiesAction); if (singleLayer) { addActionToMenu(&menu, "layer_style"); } { KisSignalsBlocker b(m_colorSelector); m_colorSelector->setCurrentIndex(singleLayer ? activeNode->colorLabelIndex() : -1); } menu.addAction(m_colorSelectorAction); menu.addSeparator(); addActionToMenu(&menu, "cut_layer_clipboard"); addActionToMenu(&menu, "copy_layer_clipboard"); addActionToMenu(&menu, "paste_layer_from_clipboard"); menu.addAction(m_removeAction); addActionToMenu(&menu, "duplicatelayer"); addActionToMenu(&menu, "merge_layer"); if (singleLayer) { addActionToMenu(&menu, "flatten_image"); addActionToMenu(&menu, "flatten_layer"); } menu.addSeparator(); QMenu *selectMenu = menu.addMenu(i18n("&Select")); addActionToMenu(selectMenu, "select_all_layers"); addActionToMenu(selectMenu, "select_visible_layers"); addActionToMenu(selectMenu, "select_invisible_layers"); addActionToMenu(selectMenu, "select_locked_layers"); addActionToMenu(selectMenu, "select_unlocked_layers"); QMenu *groupMenu = menu.addMenu(i18n("&Group")); addActionToMenu(groupMenu, "create_quick_group"); addActionToMenu(groupMenu, "create_quick_clipping_group"); addActionToMenu(groupMenu, "quick_ungroup"); QMenu *locksMenu = menu.addMenu(i18n("&Toggle Locks && Visibility")); addActionToMenu(locksMenu, "toggle_layer_visibility"); addActionToMenu(locksMenu, "toggle_layer_lock"); addActionToMenu(locksMenu, "toggle_layer_inherit_alpha"); addActionToMenu(locksMenu, "toggle_layer_alpha_lock"); if (singleLayer) { QMenu *addLayerMenu = menu.addMenu(i18n("&Add")); addActionToMenu(addLayerMenu, "add_new_transparency_mask"); addActionToMenu(addLayerMenu, "add_new_filter_mask"); addActionToMenu(addLayerMenu, "add_new_colorize_mask"); addActionToMenu(addLayerMenu, "add_new_transform_mask"); addActionToMenu(addLayerMenu, "add_new_selection_mask"); QMenu *convertToMenu = menu.addMenu(i18n("&Convert")); addActionToMenu(convertToMenu, "convert_to_paint_layer"); addActionToMenu(convertToMenu, "convert_to_transparency_mask"); addActionToMenu(convertToMenu, "convert_to_filter_mask"); addActionToMenu(convertToMenu, "convert_to_selection_mask"); addActionToMenu(convertToMenu, "convert_layer_to_file_layer"); QMenu *splitAlphaMenu = menu.addMenu(i18n("S&plit Alpha")); addActionToMenu(splitAlphaMenu, "split_alpha_into_mask"); addActionToMenu(splitAlphaMenu, "split_alpha_write"); addActionToMenu(splitAlphaMenu, "split_alpha_save_merged"); } menu.addSeparator(); addActionToMenu(&menu, "show_in_timeline"); if (singleLayer) { KisNodeSP node = m_filteringModel->nodeFromIndex(index); if (node && !node->inherits("KisTransformMask")) { addActionToMenu(&menu, "isolate_layer"); } addActionToMenu(&menu, "selectopaque"); } } menu.exec(pos); } } void KisLayerBox::slotMinimalView() { m_wdgLayerBox->listLayers->setDisplayMode(KisNodeView::MinimalMode); } void KisLayerBox::slotDetailedView() { m_wdgLayerBox->listLayers->setDisplayMode(KisNodeView::DetailedMode); } void KisLayerBox::slotThumbnailView() { m_wdgLayerBox->listLayers->setDisplayMode(KisNodeView::ThumbnailMode); } void KisLayerBox::slotRmClicked() { if (!m_canvas) return; m_nodeManager->removeNode(); } void KisLayerBox::slotRaiseClicked() { if (!m_canvas) return; m_nodeManager->raiseNode(); } void KisLayerBox::slotLowerClicked() { if (!m_canvas) return; m_nodeManager->lowerNode(); } void KisLayerBox::slotPropertiesClicked() { if (!m_canvas) return; if (KisNodeSP active = m_nodeManager->activeNode()) { m_nodeManager->nodeProperties(active); } } void KisLayerBox::slotCompositeOpChanged(int index) { Q_UNUSED(index); if (!m_canvas) return; QString compositeOp = m_wdgLayerBox->cmbComposite->selectedCompositeOp().id(); m_nodeManager->nodeCompositeOpChanged(m_nodeManager->activeColorSpace()->compositeOp(compositeOp)); } void KisLayerBox::slotOpacityChanged() { if (!m_canvas) return; m_blockOpacityUpdate = true; m_nodeManager->nodeOpacityChanged(m_newOpacity, true); m_blockOpacityUpdate = false; } void KisLayerBox::slotOpacitySliderMoved(qreal opacity) { m_newOpacity = opacity; m_opacityDelayTimer.start(200); } void KisLayerBox::slotCollapsed(const QModelIndex &index) { KisNodeSP node = m_filteringModel->nodeFromIndex(index); if (node) { node->setCollapsed(true); } } void KisLayerBox::slotExpanded(const QModelIndex &index) { KisNodeSP node = m_filteringModel->nodeFromIndex(index); if (node) { node->setCollapsed(false); } } void KisLayerBox::slotSelectOpaque() { if (!m_canvas) return; QAction *action = m_canvas->viewManager()->actionManager()->actionByName("selectopaque"); if (action) { action->trigger(); } } void KisLayerBox::slotNodeCollapsedChanged() { expandNodesRecursively(m_image->rootLayer(), m_filteringModel, m_wdgLayerBox->listLayers); } inline bool isSelectionMask(KisNodeSP node) { return dynamic_cast(node.data()); } KisNodeSP KisLayerBox::findNonHidableNode(KisNodeSP startNode) { if (KisNodeManager::isNodeHidden(startNode, true) && startNode->parent() && !startNode->parent()->parent()) { KisNodeSP node = startNode->prevSibling(); while (node && KisNodeManager::isNodeHidden(node, true)) { node = node->prevSibling(); } if (!node) { node = startNode->nextSibling(); while (node && KisNodeManager::isNodeHidden(node, true)) { node = node->nextSibling(); } } if (!node) { node = m_image->root()->lastChild(); while (node && KisNodeManager::isNodeHidden(node, true)) { node = node->prevSibling(); } } KIS_ASSERT_RECOVER_NOOP(node && "cannot activate any node!"); startNode = node; } return startNode; } void KisLayerBox::slotEditGlobalSelection(bool showSelections) { KisNodeSP lastActiveNode = m_nodeManager->activeNode(); KisNodeSP activateNode = lastActiveNode; KisSelectionMaskSP globalSelectionMask; if (!showSelections) { activateNode = findNonHidableNode(activateNode); } m_nodeModel->setShowGlobalSelection(showSelections); globalSelectionMask = m_image->rootLayer()->selectionMask(); // try to find deactivated, but visible masks if (!globalSelectionMask) { KoProperties properties; properties.setProperty("visible", true); QList masks = m_image->rootLayer()->childNodes(QStringList("KisSelectionMask"), properties); if (!masks.isEmpty()) { globalSelectionMask = dynamic_cast(masks.first().data()); } } // try to find at least any selection mask if (!globalSelectionMask) { KoProperties properties; QList masks = m_image->rootLayer()->childNodes(QStringList("KisSelectionMask"), properties); if (!masks.isEmpty()) { globalSelectionMask = dynamic_cast(masks.first().data()); } } if (globalSelectionMask) { if (showSelections) { activateNode = globalSelectionMask; } } if (activateNode != lastActiveNode) { m_nodeManager->slotNonUiActivatedNode(activateNode); } else if (lastActiveNode) { setCurrentNode(lastActiveNode); } if (showSelections && !globalSelectionMask) { KisProcessingApplicator applicator(m_image, 0, KisProcessingApplicator::NONE, KisImageSignalVector() << ModifiedSignal, kundo2_i18n("Quick Selection Mask")); applicator.applyCommand( new KisLayerUtils::KeepNodesSelectedCommand( m_nodeManager->selectedNodes(), KisNodeList(), lastActiveNode, 0, m_image, false), KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE); applicator.applyCommand(new KisSetEmptyGlobalSelectionCommand(m_image), KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE); applicator.applyCommand(new KisLayerUtils::SelectGlobalSelectionMask(m_image), KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE); applicator.end(); } else if (!showSelections && globalSelectionMask && globalSelectionMask->selection()->selectedRect().isEmpty()) { KisProcessingApplicator applicator(m_image, 0, KisProcessingApplicator::NONE, KisImageSignalVector() << ModifiedSignal, kundo2_i18n("Cancel Quick Selection Mask")); applicator.applyCommand(new KisSetGlobalSelectionCommand(m_image, 0), KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE); applicator.end(); } } void KisLayerBox::selectionChanged(const QModelIndexList selection) { if (!m_nodeManager) return; /** * When the user clears the extended selection by clicking on the * empty area of the docker, the selection should be reset on to * the active layer, which might be even unselected(!). */ if (selection.isEmpty() && m_nodeManager->activeNode()) { QModelIndex selectedIndex = m_filteringModel->indexFromNode(m_nodeManager->activeNode()); m_wdgLayerBox->listLayers->selectionModel()-> setCurrentIndex(selectedIndex, QItemSelectionModel::ClearAndSelect); return; } QList selectedNodes; Q_FOREACH (const QModelIndex &idx, selection) { selectedNodes << m_filteringModel->nodeFromIndex(idx); } m_nodeManager->slotSetSelectedNodes(selectedNodes); updateUI(); } void KisLayerBox::slotAboutToRemoveRows(const QModelIndex &parent, int start, int end) { /** * Qt has changed its behavior when deleting an item. Previously * the selection priority was on the next item in the list, and * now it has shanged to the previous item. Here we just adjust * the selected item after the node removal. Please take care that * this method overrides what was done by the corresponding method * of QItemSelectionModel, which *has already done* its work. That * is why we use (start - 1) and (end + 1) in the activation * condition. * * See bug: https://bugs.kde.org/show_bug.cgi?id=345601 */ QModelIndex currentIndex = m_wdgLayerBox->listLayers->currentIndex(); QAbstractItemModel *model = m_filteringModel; if (currentIndex.isValid() && parent == currentIndex.parent() && currentIndex.row() >= start - 1 && currentIndex.row() <= end + 1) { QModelIndex old = currentIndex; if (model && end < model->rowCount(parent) - 1) // there are rows left below the change currentIndex = model->index(end + 1, old.column(), parent); else if (start > 0) // there are rows left above the change currentIndex = model->index(start - 1, old.column(), parent); else // there are no rows left in the table currentIndex = QModelIndex(); if (currentIndex.isValid() && currentIndex != old) { m_wdgLayerBox->listLayers->setCurrentIndex(currentIndex); } } } void KisLayerBox::slotNodeManagerChangedSelection(const KisNodeList &nodes) { if (!m_nodeManager) return; QModelIndexList newSelection; Q_FOREACH(KisNodeSP node, nodes) { newSelection << m_filteringModel->indexFromNode(node); } QItemSelectionModel *model = m_wdgLayerBox->listLayers->selectionModel(); if (KritaUtils::compareListsUnordered(newSelection, model->selectedIndexes())) { return; } QItemSelection selection; Q_FOREACH(const QModelIndex &idx, newSelection) { selection.select(idx, idx); } model->select(selection, QItemSelectionModel::ClearAndSelect); } void KisLayerBox::updateThumbnail() { m_wdgLayerBox->listLayers->updateNode(m_wdgLayerBox->listLayers->currentIndex()); } void KisLayerBox::slotRenameCurrentNode() { m_wdgLayerBox->listLayers->edit(m_wdgLayerBox->listLayers->currentIndex()); } void KisLayerBox::slotColorLabelChanged(int label) { KisNodeList nodes = m_nodeManager->selectedNodes(); Q_FOREACH(KisNodeSP node, nodes) { auto applyLabelFunc = [label](KisNodeSP node) { node->setColorLabelIndex(label); }; KisLayerUtils::recursiveApplyNodes(node, applyLabelFunc); } } void KisLayerBox::updateAvailableLabels() { if (!m_image) return; m_wdgLayerBox->cmbFilter->updateAvailableLabels(m_image->root()); } void KisLayerBox::updateLayerFiltering() { m_filteringModel->setAcceptedLabels(m_wdgLayerBox->cmbFilter->selectedColors()); } void KisLayerBox::slotKeyframeChannelAdded(KisKeyframeChannel *channel) { if (channel->id() == KisKeyframeChannel::Opacity.id()) { watchOpacityChannel(channel); } } void KisLayerBox::watchOpacityChannel(KisKeyframeChannel *channel) { if (m_opacityChannel) { m_opacityChannel->disconnect(this); } m_opacityChannel = channel; if (m_opacityChannel) { connect(m_opacityChannel, SIGNAL(sigKeyframeAdded(KisKeyframeSP)), this, SLOT(slotOpacityKeyframeChanged(KisKeyframeSP))); connect(m_opacityChannel, SIGNAL(sigKeyframeRemoved(KisKeyframeSP)), this, SLOT(slotOpacityKeyframeChanged(KisKeyframeSP))); connect(m_opacityChannel, SIGNAL(sigKeyframeMoved(KisKeyframeSP)), this, SLOT(slotOpacityKeyframeMoved(KisKeyframeSP))); connect(m_opacityChannel, SIGNAL(sigKeyframeChanged(KisKeyframeSP)), this, SLOT(slotOpacityKeyframeChanged(KisKeyframeSP))); } } void KisLayerBox::slotOpacityKeyframeChanged(KisKeyframeSP keyframe) { Q_UNUSED(keyframe); if (m_blockOpacityUpdate) return; updateUI(); } void KisLayerBox::slotOpacityKeyframeMoved(KisKeyframeSP keyframe, int fromTime) { Q_UNUSED(fromTime); slotOpacityKeyframeChanged(keyframe); } void KisLayerBox::slotImageTimeChanged(int time) { Q_UNUSED(time); updateUI(); } void KisLayerBox::slotUpdateIcons() { m_wdgLayerBox->bnAdd->setIcon(KisIconUtils::loadIcon("addlayer")); m_wdgLayerBox->bnRaise->setIcon(KisIconUtils::loadIcon("arrowupblr")); m_wdgLayerBox->bnDelete->setIcon(KisIconUtils::loadIcon("deletelayer")); m_wdgLayerBox->bnLower->setIcon(KisIconUtils::loadIcon("arrowdown")); m_wdgLayerBox->bnProperties->setIcon(KisIconUtils::loadIcon("properties")); m_wdgLayerBox->bnDuplicate->setIcon(KisIconUtils::loadIcon("duplicatelayer")); // call child function about needing to update icons m_wdgLayerBox->listLayers->slotUpdateIcons(); } void KisLayerBox::slotUpdateThumbnailIconSize() { KisConfig cfg(false); cfg.setLayerThumbnailSize(thumbnailSizeSlider->value()); // this is a hack to force the layers list to update its display and // re-layout all the layers with the new thumbnail size resize(this->width()+1, this->height()+1); resize(this->width()-1, this->height()-1); } #include "moc_kis_layer_box.cpp" diff --git a/plugins/dockers/defaultdockers/kis_layer_box.h b/plugins/dockers/defaultdockers/kis_layer_box.h index 9cd1e51048..e392cf0933 100644 --- a/plugins/dockers/defaultdockers/kis_layer_box.h +++ b/plugins/dockers/defaultdockers/kis_layer_box.h @@ -1,198 +1,200 @@ /* * kis_layer_box.h - part of Krita aka Krayon aka KimageShop * * Copyright (c) 2002 Patrick Julien * Copyright (C) 2006 Gábor Lehel * Copyright (C) 2007 Thomas Zander * Copyright (C) 2007-2009 Boudewijn Rempt * * 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. */ #ifndef KIS_LAYERBOX_H #define KIS_LAYERBOX_H #include #include #include #include #include #include #include #include #include #include "kis_action.h" #include "KisViewManager.h" #include "kis_mainwindow_observer.h" #include "kis_signal_compressor.h" #include class QModelIndex; typedef QList QModelIndexList; class QMenu; class QAbstractButton; class KoCompositeOp; class KisCanvas2; class KisNodeModel; class KisNodeFilterProxyModel; class Ui_WdgLayerBox; class KisNodeJugglerCompressed; class KisColorLabelSelectorWidget; class QWidgetAction; class KisKeyframeChannel; +class KisSelectionActionsAdapter; /** * A widget that shows a visualization of the layer structure. * * The center of the layer box is KisNodeModel, which shows the actual layers. * This widget adds docking functionality and command buttons. * */ class KisLayerBox : public QDockWidget, public KisMainwindowObserver { Q_OBJECT public: KisLayerBox(); ~KisLayerBox() override; QString observerName() override { return "KisLayerBox"; } /// reimplemented from KisMainwindowObserver void setViewManager(KisViewManager* kisview) override; void setCanvas(KoCanvasBase *canvas) override; void unsetCanvas() override; private Q_SLOTS: void notifyImageDeleted(); void slotContextMenuRequested(const QPoint &pos, const QModelIndex &index); void slotMinimalView(); void slotDetailedView(); void slotThumbnailView(); // From the node manager to the layerbox void slotSetCompositeOp(const KoCompositeOp* compositeOp); void slotSetOpacity(double opacity); void updateUI(); void setCurrentNode(KisNodeSP node); void slotModelReset(); // from the layerbox to the node manager void slotRmClicked(); void slotRaiseClicked(); void slotLowerClicked(); void slotPropertiesClicked(); void slotCompositeOpChanged(int index); void slotOpacityChanged(); void slotOpacitySliderMoved(qreal opacity); void slotCollapsed(const QModelIndex &index); void slotExpanded(const QModelIndex &index); void slotSelectOpaque(); void slotNodeCollapsedChanged(); void slotEditGlobalSelection(bool showSelections); void slotRenameCurrentNode(); void slotAboutToRemoveRows(const QModelIndex &parent, int first, int last); void selectionChanged(const QModelIndexList selection); void slotNodeManagerChangedSelection(const QList &nodes); void slotColorLabelChanged(int index); void slotUpdateIcons(); void slotAddLayerBnClicked(); void updateThumbnail(); void updateAvailableLabels(); void updateLayerFiltering(); void slotUpdateThumbnailIconSize(); // Opacity keyframing void slotKeyframeChannelAdded(KisKeyframeChannel *channel); void slotOpacityKeyframeChanged(KisKeyframeSP keyframe); void slotOpacityKeyframeMoved(KisKeyframeSP keyframe, int fromTime); void slotImageTimeChanged(int time); private: inline void connectActionToButton(KisViewManager* view, QAbstractButton *button, const QString &id); inline void addActionToMenu(QMenu *menu, const QString &id); void watchOpacityChannel(KisKeyframeChannel *channel); KisNodeSP findNonHidableNode(KisNodeSP startNode); private: QPointer m_canvas; + QScopedPointer m_selectionActionsAdapter; QMenu *m_newLayerMenu; KisImageWSP m_image; QPointer m_nodeModel; QPointer m_filteringModel; QPointer m_nodeManager; QPointer m_colorSelector; QPointer m_colorSelectorAction; Ui_WdgLayerBox* m_wdgLayerBox; QTimer m_opacityDelayTimer; int m_newOpacity; QVector m_actions; KisAction* m_removeAction; KisAction* m_propertiesAction; KisSignalCompressor m_thumbnailCompressor; KisSignalCompressor m_colorLabelCompressor; KisSignalCompressor m_thumbnailSizeCompressor; QSlider* thumbnailSizeSlider; KisNodeSP m_activeNode; QPointer m_opacityChannel; bool m_blockOpacityUpdate {false}; }; class KisLayerBoxFactory : public KoDockFactoryBase { public: KisLayerBoxFactory() { } QString id() const override { return QString("KisLayerBox"); } QDockWidget* createDockWidget() override { KisLayerBox * dockWidget = new KisLayerBox(); dockWidget->setObjectName(id()); return dockWidget; } DockPosition defaultDockPosition() const override { return DockRight; } }; #endif // KIS_LAYERBOX_H diff --git a/plugins/extensions/colorrange/colorrange.cc b/plugins/extensions/colorrange/colorrange.cc index cbec33f725..72161a6bf0 100644 --- a/plugins/extensions/colorrange/colorrange.cc +++ b/plugins/extensions/colorrange/colorrange.cc @@ -1,150 +1,96 @@ /* * colorrange.h -- Part of Krita * * Copyright (c) 2004 Boudewijn Rempt (boud@valdyas.org) * * 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 "colorrange.h" #include #include #include #include "kis_image.h" #include "kis_layer.h" #include "kis_paint_device.h" #include "kis_global.h" #include "kis_types.h" #include "KisViewManager.h" #include "kis_selection.h" #include "kis_selection_manager.h" #include "kis_selection_tool_helper.h" #include "kis_canvas2.h" #include "kis_iterator_ng.h" #include "kis_action.h" #include "dlg_colorrange.h" #include #include K_PLUGIN_FACTORY_WITH_JSON(ColorRangeFactory, "kritacolorrange.json", registerPlugin();) ColorRange::ColorRange(QObject *parent, const QVariantList &) : KisActionPlugin(parent) { KisAction* action = createAction("colorrange"); connect(action, SIGNAL(triggered()), this, SLOT(slotActivated())); QSignalMapper *mapper = new QSignalMapper(this); connect(mapper, SIGNAL(mapped(int)), SLOT(selectOpaque(int))); action = createAction("selectopaque"); mapper->setMapping(action, int(SELECTION_REPLACE)); connect(action, SIGNAL(triggered(bool)), mapper, SLOT(map())); action = createAction("selectopaque_add"); mapper->setMapping(action, int(SELECTION_ADD)); connect(action, SIGNAL(triggered(bool)), mapper, SLOT(map())); action = createAction("selectopaque_subtract"); mapper->setMapping(action, int(SELECTION_SUBTRACT)); connect(action, SIGNAL(triggered(bool)), mapper, SLOT(map())); action = createAction("selectopaque_intersect"); mapper->setMapping(action, int(SELECTION_INTERSECT)); connect(action, SIGNAL(triggered(bool)), mapper, SLOT(map())); } ColorRange::~ColorRange() { } void ColorRange::slotActivated() { DlgColorRange *dlgColorRange = new DlgColorRange(viewManager(), viewManager()->mainWindow()); Q_CHECK_PTR(dlgColorRange); dlgColorRange->exec(); } void ColorRange::selectOpaque(int id) { - selectOpaqueImpl(SelectionAction(id)); -} + KisNodeSP node = viewManager()->activeNode(); + if (!node) return; -void ColorRange::selectOpaqueImpl(SelectionAction action) -{ - KisCanvas2 *canvas = viewManager()->canvasBase(); - KisPaintDeviceSP device = viewManager()->activeNode()->projection(); - if (!device) device = viewManager()->activeNode()->paintDevice(); - if (!device) device = viewManager()->activeNode()->original(); - KIS_ASSERT_RECOVER_RETURN(canvas && device); - - QRect rc = device->exactBounds(); - if (rc.isEmpty()) return; - - /** - * If there is nothing selected, just create a new selection - */ - if (!canvas->imageView()->selection()) { - action = SELECTION_REPLACE; - } - - KUndo2MagicString actionName; - - switch (action) { - case SELECTION_ADD: - actionName = kundo2_i18n("Select Opaque (Add)"); - break; - case SELECTION_SUBTRACT: - actionName = kundo2_i18n("Select Opaque (Subtract)"); - break; - case SELECTION_INTERSECT: - actionName = kundo2_i18n("Select Opaque (Intersect)"); - break; - default: - actionName = kundo2_i18n("Select Opaque"); - break; - } - - KisSelectionToolHelper helper(canvas, actionName); - - qint32 x, y, w, h; - rc.getRect(&x, &y, &w, &h); - - const KoColorSpace * cs = device->colorSpace(); - KisPixelSelectionSP tmpSel = KisPixelSelectionSP(new KisPixelSelection()); - - KisHLineConstIteratorSP deviter = device->createHLineConstIteratorNG(x, y, w); - KisHLineIteratorSP selIter = tmpSel ->createHLineIteratorNG(x, y, w); - - for (int row = y; row < h + y; ++row) { - do { - *selIter->rawData() = cs->opacityU8(deviter->oldRawData()); - } while (deviter->nextPixel() && selIter->nextPixel()); - deviter->nextRow(); - selIter->nextRow(); - } - - tmpSel->invalidateOutlineCache(); - helper.selectPixelSelection(tmpSel, action); + viewManager()->selectionManager()-> + selectOpaqueOnNode(node, SelectionAction(id)); } #include "colorrange.moc" diff --git a/plugins/extensions/colorrange/colorrange.h b/plugins/extensions/colorrange/colorrange.h index e373ef0fa0..ef5c197be9 100644 --- a/plugins/extensions/colorrange/colorrange.h +++ b/plugins/extensions/colorrange/colorrange.h @@ -1,46 +1,40 @@ /* * colorrange.h -- Part of Krita * * Copyright (c) 2004 Boudewijn Rempt (boud@valdyas.org) * * 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. */ #ifndef COLORRANGE_H #define COLORRANGE_H #include #include -#include "kis_selection.h" - - class ColorRange : public KisActionPlugin { Q_OBJECT public: ColorRange(QObject *parent, const QVariantList &); ~ColorRange() override; private Q_SLOTS: void slotActivated(); void selectOpaque(int id); - -private: - void selectOpaqueImpl(SelectionAction action); }; #endif // COLORRANGE_H diff --git a/plugins/flake/pathshapes/rectangle/RectangleShape.cpp b/plugins/flake/pathshapes/rectangle/RectangleShape.cpp index 3209bb7de7..6e0de7e4d3 100644 --- a/plugins/flake/pathshapes/rectangle/RectangleShape.cpp +++ b/plugins/flake/pathshapes/rectangle/RectangleShape.cpp @@ -1,389 +1,387 @@ /* This file is part of the KDE project Copyright (C) 2006-2008 Thorsten Zachmann Copyright (C) 2006-2008 Jan Hambrecht Copyright (C) 2009 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 "RectangleShape.h" #include #include #include #include #include #include #include #include #include #include #include RectangleShape::RectangleShape() : m_cornerRadiusX(0) , m_cornerRadiusY(0) { QList handles; handles.push_back(QPointF(100, 0)); handles.push_back(QPointF(100, 0)); setHandles(handles); QSizeF size(100, 100); updatePath(size); } RectangleShape::RectangleShape(const RectangleShape &rhs) : KoParameterShape(new KoParameterShapePrivate(*rhs.d_func(), this)), m_cornerRadiusX(rhs.m_cornerRadiusX), m_cornerRadiusY(rhs.m_cornerRadiusY) { } RectangleShape::~RectangleShape() { } KoShape *RectangleShape::cloneShape() const { return new RectangleShape(*this); } bool RectangleShape::loadOdf(const KoXmlElement &element, KoShapeLoadingContext &context) { loadOdfAttributes(element, context, OdfMandatories | OdfGeometry | OdfAdditionalAttributes | OdfCommonChildElements); if (element.hasAttributeNS(KoXmlNS::svg, "rx") && element.hasAttributeNS(KoXmlNS::svg, "ry")) { qreal rx = KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "rx", "0")); qreal ry = KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "ry", "0")); m_cornerRadiusX = rx / (0.5 * size().width()) * 100; m_cornerRadiusY = ry / (0.5 * size().height()) * 100; } else { QString cornerRadius = element.attributeNS(KoXmlNS::draw, "corner-radius", ""); if (!cornerRadius.isEmpty()) { qreal radius = KoUnit::parseValue(cornerRadius); m_cornerRadiusX = qMin(radius / (0.5 * size().width()) * 100, qreal(100)); m_cornerRadiusY = qMin(radius / (0.5 * size().height()) * 100, qreal(100)); } } updatePath(size()); updateHandles(); loadOdfAttributes(element, context, OdfTransformation); loadText(element, context); return true; } void RectangleShape::saveOdf(KoShapeSavingContext &context) const { if (isParametricShape()) { context.xmlWriter().startElement("draw:rect"); saveOdfAttributes(context, OdfAllAttributes); if (m_cornerRadiusX > 0 && m_cornerRadiusY > 0) { context.xmlWriter().addAttribute("svg:rx", m_cornerRadiusX * (0.5 * size().width()) / 100.0); context.xmlWriter().addAttribute("svg:ry", m_cornerRadiusY * (0.5 * size().height()) / 100.0); } saveOdfCommonChildElements(context); saveText(context); context.xmlWriter().endElement(); } else { KoPathShape::saveOdf(context); } } void RectangleShape::moveHandleAction(int handleId, const QPointF &point, Qt::KeyboardModifiers modifiers) { Q_UNUSED(modifiers); QPointF p(point); qreal width2 = size().width() / 2.0; qreal height2 = size().height() / 2.0; switch (handleId) { case 0: if (p.x() < width2) { p.setX(width2); } else if (p.x() > size().width()) { p.setX(size().width()); } p.setY(0); m_cornerRadiusX = (size().width() - p.x()) / width2 * 100.0; if (!(modifiers & Qt::ControlModifier)) { m_cornerRadiusY = (size().width() - p.x()) / height2 * 100.0; } break; case 1: if (p.y() < 0) { p.setY(0); } else if (p.y() > height2) { p.setY(height2); } p.setX(size().width()); m_cornerRadiusY = p.y() / height2 * 100.0; if (!(modifiers & Qt::ControlModifier)) { m_cornerRadiusX = p.y() / width2 * 100.0; } break; } // this is needed otherwise undo/redo might not end in the same result if (100 - m_cornerRadiusX < 1e-10) { m_cornerRadiusX = 100; } if (100 - m_cornerRadiusY < 1e-10) { m_cornerRadiusY = 100; } updateHandles(); } void RectangleShape::updateHandles() { QList handles; handles.append(QPointF(size().width() - m_cornerRadiusX / 100.0 * 0.5 * size().width(), 0.0)); handles.append(QPointF(size().width(), m_cornerRadiusY / 100.0 * 0.5 * size().height())); setHandles(handles); } void RectangleShape::updatePath(const QSizeF &size) { Q_D(KoParameterShape); qreal rx = 0; qreal ry = 0; if (m_cornerRadiusX > 0 && m_cornerRadiusY > 0) { rx = size.width() / 200.0 * m_cornerRadiusX; ry = size.height() / 200.0 * m_cornerRadiusY; } qreal x2 = size.width() - rx; qreal y2 = size.height() - ry; QPointF curvePoints[12]; int requiredCurvePointCount = 4; if (rx && m_cornerRadiusX < 100) { requiredCurvePointCount += 2; } if (ry && m_cornerRadiusY < 100) { requiredCurvePointCount += 2; } createPoints(requiredCurvePointCount); KoSubpath &points = *d->subpaths[0]; int cp = 0; // first path starts and closes path points[cp]->setProperty(KoPathPoint::StartSubpath); points[cp]->setProperty(KoPathPoint::CloseSubpath); points[cp]->setPoint(QPointF(rx, 0)); points[cp]->removeControlPoint1(); points[cp]->removeControlPoint2(); if (m_cornerRadiusX < 100 || m_cornerRadiusY == 0) { // end point of the top edge points[++cp]->setPoint(QPointF(x2, 0)); points[cp]->removeControlPoint1(); points[cp]->removeControlPoint2(); } if (rx) { // the top right radius arcToCurve(rx, ry, 90, -90, points[cp]->point(), curvePoints); points[cp]->setControlPoint2(curvePoints[0]); points[++cp]->setControlPoint1(curvePoints[1]); points[cp]->setPoint(curvePoints[2]); points[cp]->removeControlPoint2(); } if (m_cornerRadiusY < 100 || m_cornerRadiusX == 0) { // the right edge points[++cp]->setPoint(QPointF(size.width(), y2)); points[cp]->removeControlPoint1(); points[cp]->removeControlPoint2(); } if (rx) { // the bottom right radius arcToCurve(rx, ry, 0, -90, points[cp]->point(), curvePoints); points[cp]->setControlPoint2(curvePoints[0]); points[++cp]->setControlPoint1(curvePoints[1]); points[cp]->setPoint(curvePoints[2]); points[cp]->removeControlPoint2(); } if (m_cornerRadiusX < 100 || m_cornerRadiusY == 0) { // the bottom edge points[++cp]->setPoint(QPointF(rx, size.height())); points[cp]->removeControlPoint1(); points[cp]->removeControlPoint2(); } if (rx) { // the bottom left radius arcToCurve(rx, ry, 270, -90, points[cp]->point(), curvePoints); points[cp]->setControlPoint2(curvePoints[0]); points[++cp]->setControlPoint1(curvePoints[1]); points[cp]->setPoint(curvePoints[2]); points[cp]->removeControlPoint2(); } if ((m_cornerRadiusY < 100 || m_cornerRadiusX == 0) && ry) { // the right edge points[++cp]->setPoint(QPointF(0, ry)); points[cp]->removeControlPoint1(); points[cp]->removeControlPoint2(); } if (rx) { // the top left radius arcToCurve(rx, ry, 180, -90, points[cp]->point(), curvePoints); points[cp]->setControlPoint2(curvePoints[0]); points[0]->setControlPoint1(curvePoints[1]); points[0]->setPoint(curvePoints[2]); } // unset all stop/close path properties for (int i = 1; i < cp; ++i) { points[i]->unsetProperty(KoPathPoint::StopSubpath); points[i]->unsetProperty(KoPathPoint::CloseSubpath); } // last point stops and closes path points.last()->setProperty(KoPathPoint::StopSubpath); points.last()->setProperty(KoPathPoint::CloseSubpath); notifyPointsChanged(); } void RectangleShape::createPoints(int requiredPointCount) { Q_D(KoParameterShape); if (d->subpaths.count() != 1) { clear(); d->subpaths.append(new KoSubpath()); } int currentPointCount = d->subpaths[0]->count(); if (currentPointCount > requiredPointCount) { for (int i = 0; i < currentPointCount - requiredPointCount; ++i) { delete d->subpaths[0]->front(); d->subpaths[0]->pop_front(); } } else if (requiredPointCount > currentPointCount) { for (int i = 0; i < requiredPointCount - currentPointCount; ++i) { d->subpaths[0]->append(new KoPathPoint(this, QPointF())); } } notifyPointsChanged(); } qreal RectangleShape::cornerRadiusX() const { return m_cornerRadiusX; } void RectangleShape::setCornerRadiusX(qreal radius) { - if (radius >= 0.0 && radius <= 100.0) { - m_cornerRadiusX = radius; - updatePath(size()); - updateHandles(); - } + radius = qBound(0.0, radius, 100.0); + m_cornerRadiusX = radius; + updatePath(size()); + updateHandles(); } qreal RectangleShape::cornerRadiusY() const { return m_cornerRadiusY; } void RectangleShape::setCornerRadiusY(qreal radius) { - if (radius >= 0.0 && radius <= 100.0) { - m_cornerRadiusY = radius; - updatePath(size()); - updateHandles(); - } + radius = qBound(0.0, radius, 100.0); + m_cornerRadiusY = radius; + updatePath(size()); + updateHandles(); } QString RectangleShape::pathShapeId() const { return RectangleShapeId; } bool RectangleShape::saveSvg(SvgSavingContext &context) { // let basic path saiving code handle our saving if (!isParametricShape()) return false; context.shapeWriter().startElement("rect"); context.shapeWriter().addAttribute("id", context.getID(this)); SvgUtil::writeTransformAttributeLazy("transform", transformation(), context.shapeWriter()); SvgStyleWriter::saveSvgStyle(this, context); const QSizeF size = this->size(); context.shapeWriter().addAttribute("width", size.width()); context.shapeWriter().addAttribute("height", size.height()); double rx = cornerRadiusX(); if (rx > 0.0) { context.shapeWriter().addAttribute("rx", 0.01 * rx * 0.5 * size.width()); } double ry = cornerRadiusY(); if (ry > 0.0) { context.shapeWriter().addAttribute("ry", 0.01 * ry * 0.5 * size.height()); } context.shapeWriter().endElement(); return true; } bool RectangleShape::loadSvg(const KoXmlElement &element, SvgLoadingContext &context) { const qreal x = SvgUtil::parseUnitX(context.currentGC(), element.attribute("x")); const qreal y = SvgUtil::parseUnitY(context.currentGC(), element.attribute("y")); const qreal w = SvgUtil::parseUnitX(context.currentGC(), element.attribute("width")); const qreal h = SvgUtil::parseUnitY(context.currentGC(), element.attribute("height")); const QString rxStr = element.attribute("rx"); const QString ryStr = element.attribute("ry"); qreal rx = rxStr.isEmpty() ? 0.0 : SvgUtil::parseUnitX(context.currentGC(), rxStr); qreal ry = ryStr.isEmpty() ? 0.0 : SvgUtil::parseUnitY(context.currentGC(), ryStr); // if one radius is given but not the other, use the same value for both if (!rxStr.isEmpty() && ryStr.isEmpty()) { ry = rx; } if (rxStr.isEmpty() && !ryStr.isEmpty()) { rx = ry; } setSize(QSizeF(w, h)); setPosition(QPointF(x, y)); if (rx >= 0.0) { setCornerRadiusX(qMin(qreal(100.0), qreal(rx / (0.5 * w) * 100.0))); } if (ry >= 0.0) { setCornerRadiusY(qMin(qreal(100.0), qreal(ry / (0.5 * h) * 100.0))); } if (w == 0.0 || h == 0.0) { setVisible(false); } return true; } diff --git a/plugins/flake/pathshapes/rectangle/RectangleShapeFactory.cpp b/plugins/flake/pathshapes/rectangle/RectangleShapeFactory.cpp index 7a5538670a..a518a0625a 100644 --- a/plugins/flake/pathshapes/rectangle/RectangleShapeFactory.cpp +++ b/plugins/flake/pathshapes/rectangle/RectangleShapeFactory.cpp @@ -1,77 +1,101 @@ /* This file is part of the KDE project * Copyright (C) 2006 Thomas Zander * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "RectangleShapeFactory.h" #include "RectangleShape.h" #include "RectangleShapeConfigWidget.h" #include "KoShapeStroke.h" #include #include #include #include +#include +#include "kis_assert.h" #include #include #include "kis_pointer_utils.h" RectangleShapeFactory::RectangleShapeFactory() : KoShapeFactoryBase(RectangleShapeId, i18n("Rectangle")) { setToolTip(i18n("A rectangle")); setIconName(koIconNameCStr("rectangle-shape")); setFamily("geometric"); setLoadingPriority(1); QList > elementNamesList; elementNamesList.append(qMakePair(QString(KoXmlNS::draw), QStringList("rect"))); elementNamesList.append(qMakePair(QString(KoXmlNS::svg), QStringList("rect"))); setXmlElements(elementNamesList); } KoShape *RectangleShapeFactory::createDefaultShape(KoDocumentResourceManager *) const { RectangleShape *rect = new RectangleShape(); rect->setStroke(toQShared(new KoShapeStroke(1.0))); rect->setShapeId(KoPathShapeId); QLinearGradient *gradient = new QLinearGradient(QPointF(0, 0), QPointF(1, 1)); gradient->setCoordinateMode(QGradient::ObjectBoundingMode); gradient->setColorAt(0.0, Qt::white); gradient->setColorAt(1.0, Qt::green); rect->setBackground(QSharedPointer(new KoGradientBackground(gradient))); return rect; } +KoShape *RectangleShapeFactory::createShape(const KoProperties *params, KoDocumentResourceManager *documentResources) const +{ + KoShape *shape = createDefaultShape(documentResources); + RectangleShape *rectShape = dynamic_cast(shape); + KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(rectShape, shape); + + rectShape->setSize( + QSizeF(params->doubleProperty("width", rectShape->size().width()), + params->doubleProperty("height", rectShape->size().height()))); + + rectShape->setAbsolutePosition( + QPointF(params->doubleProperty("x", rectShape->absolutePosition(KoFlake::TopLeft).x()), + params->doubleProperty("y", rectShape->absolutePosition(KoFlake::TopLeft).y())), + KoFlake::TopLeft); + + + rectShape->setCornerRadiusX(params->doubleProperty("rx", 0.0)); + rectShape->setCornerRadiusY(params->doubleProperty("ry", 0.0)); + + return shape; +} + bool RectangleShapeFactory::supports(const KoXmlElement &e, KoShapeLoadingContext &/*context*/) const { Q_UNUSED(e); return (e.localName() == "rect" && e.namespaceURI() == KoXmlNS::draw); } QList RectangleShapeFactory::createShapeOptionPanels() { QList panels; panels.append(new RectangleShapeConfigWidget()); return panels; } diff --git a/plugins/flake/pathshapes/rectangle/RectangleShapeFactory.h b/plugins/flake/pathshapes/rectangle/RectangleShapeFactory.h index 2e9aebcfa4..360813514c 100644 --- a/plugins/flake/pathshapes/rectangle/RectangleShapeFactory.h +++ b/plugins/flake/pathshapes/rectangle/RectangleShapeFactory.h @@ -1,39 +1,41 @@ /* This file is part of the KDE project Copyright (C) 2006 Thorsten Zachmann This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KORECTANGLESHAPEFACTORY_H #define KORECTANGLESHAPEFACTORY_H #include "KoShapeFactoryBase.h" class KoShape; /// Factory for path shapes class RectangleShapeFactory : public KoShapeFactoryBase { public: /// constructor RectangleShapeFactory(); ~RectangleShapeFactory() override {} KoShape *createDefaultShape(KoDocumentResourceManager *documentResources = 0) const override; + KoShape *createShape(const KoProperties *params, KoDocumentResourceManager *documentResources = 0) const override; + bool supports(const KoXmlElement &e, KoShapeLoadingContext &context) const override; QList createShapeOptionPanels() override; }; #endif diff --git a/plugins/tools/basictools/kis_tool_ellipse.cc b/plugins/tools/basictools/kis_tool_ellipse.cc index 92ef3c6b0f..812f3ed8a0 100644 --- a/plugins/tools/basictools/kis_tool_ellipse.cc +++ b/plugins/tools/basictools/kis_tool_ellipse.cc @@ -1,80 +1,83 @@ /* * kis_tool_ellipse.cc - part of Krayon * * Copyright (c) 2000 John Califf * Copyright (c) 2002 Patrick Julien * Copyright (c) 2004 Boudewijn Rempt * Copyright (c) 2004 Clarence Dang * Copyright (c) 2009 Lukáš Tvrdý * Copyright (c) 2010 Cyrille Berger * * 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_tool_ellipse.h" #include #include #include #include "kis_figure_painting_tool_helper.h" #include KisToolEllipse::KisToolEllipse(KoCanvasBase * canvas) : KisToolEllipseBase(canvas, KisToolEllipseBase::PAINT, KisCursor::load("tool_ellipse_cursor.png", 6, 6)) { setObjectName("tool_ellipse"); setSupportOutline(true); } KisToolEllipse::~KisToolEllipse() { } void KisToolEllipse::resetCursorStyle() { KisToolEllipseBase::resetCursorStyle(); overrideCursorIfNotEditable(); } -void KisToolEllipse::finishRect(const QRectF& rect) +void KisToolEllipse::finishRect(const QRectF& rect, qreal roundCornersX, qreal roundCornersY) { + Q_UNUSED(roundCornersX); + Q_UNUSED(roundCornersY); + if (rect.isEmpty() || !blockUntilOperationsFinished()) return; const KisToolShape::ShapeAddInfo info = shouldAddShape(currentNode()); if (!info.shouldAddShape) { KisFigurePaintingToolHelper helper(kundo2_i18n("Draw Ellipse"), image(), currentNode(), canvas()->resourceManager(), strokeStyle(), fillStyle()); helper.paintEllipse(rect); } else { QRectF r = convertToPt(rect); KoShape* shape = KisShapeToolHelper::createEllipseShape(r); KoShapeStrokeSP border(new KoShapeStroke(currentStrokeWidth(), currentFgColor().toQColor())); shape->setStroke(border); info.markAsSelectionShapeIfNeeded(shape); addShape(shape); } notifyModified(); } diff --git a/plugins/tools/basictools/kis_tool_ellipse.h b/plugins/tools/basictools/kis_tool_ellipse.h index 70c83f34cb..f655aa662c 100644 --- a/plugins/tools/basictools/kis_tool_ellipse.h +++ b/plugins/tools/basictools/kis_tool_ellipse.h @@ -1,75 +1,75 @@ /* * kis_tool_ellipse.h - part of Krayon * * Copyright (c) 2000 John Califf * Copyright (c) 2002 Patrick Julien * Copyright (c) 2004 Clarence Dang * * 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. */ #ifndef __KIS_TOOL_ELLIPSE_H__ #define __KIS_TOOL_ELLIPSE_H__ #include "kis_tool_shape.h" #include "kis_types.h" #include "KoToolFactoryBase.h" #include "flake/kis_node_shape.h" #include #include class KoCanvasBase; class KisToolEllipse : public KisToolEllipseBase { Q_OBJECT public: KisToolEllipse(KoCanvasBase * canvas); ~KisToolEllipse() override; protected Q_SLOTS: void resetCursorStyle() override; protected: - void finishRect(const QRectF& rect) override; + void finishRect(const QRectF& rect, qreal roundCornersX, qreal roundCornersY) override; }; class KisToolEllipseFactory : public KoToolFactoryBase { public: KisToolEllipseFactory() : KoToolFactoryBase("KritaShape/KisToolEllipse") { setToolTip(i18n("Ellipse Tool")); setSection(TOOL_TYPE_SHAPE); setActivationShapeId(KRITA_TOOL_ACTIVATION_ID); setIconName(koIconNameCStr("krita_tool_ellipse")); setPriority(3); } ~KisToolEllipseFactory() override {} KoToolBase * createTool(KoCanvasBase *canvas) override { return new KisToolEllipse(canvas); } }; #endif //__KIS_TOOL_ELLIPSE_H__ diff --git a/plugins/tools/basictools/kis_tool_rectangle.cc b/plugins/tools/basictools/kis_tool_rectangle.cc index d11c5a678a..fadc347dcb 100644 --- a/plugins/tools/basictools/kis_tool_rectangle.cc +++ b/plugins/tools/basictools/kis_tool_rectangle.cc @@ -1,89 +1,100 @@ /* * kis_tool_rectangle.cc - part of Krita * * Copyright (c) 2000 John Califf * Copyright (c) 2002 Patrick Julien * Copyright (c) 2004 Boudewijn Rempt * Copyright (c) 2004 Clarence Dang * Copyright (c) 2009 Lukáš Tvrdý * Copyright (c) 2010 Cyrille Berger * * 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_tool_rectangle.h" #include #include #include "KoCanvasBase.h" #include "kis_shape_tool_helper.h" #include "kis_figure_painting_tool_helper.h" #include #include KisToolRectangle::KisToolRectangle(KoCanvasBase * canvas) : KisToolRectangleBase(canvas, KisToolRectangleBase::PAINT, KisCursor::load("tool_rectangle_cursor.png", 6, 6)) { setSupportOutline(true); setObjectName("tool_rectangle"); } KisToolRectangle::~KisToolRectangle() { } void KisToolRectangle::resetCursorStyle() { KisToolRectangleBase::resetCursorStyle(); overrideCursorIfNotEditable(); } -void KisToolRectangle::finishRect(const QRectF &rect) +void KisToolRectangle::finishRect(const QRectF &rect, qreal roundCornersX, qreal roundCornersY) { if (rect.isNull() || !blockUntilOperationsFinished()) return; const KisToolShape::ShapeAddInfo info = shouldAddShape(currentNode()); if (!info.shouldAddShape) { KisFigurePaintingToolHelper helper(kundo2_i18n("Draw Rectangle"), image(), currentNode(), canvas()->resourceManager(), strokeStyle(), fillStyle()); - helper.paintRect(rect); + + QPainterPath path; + + if (roundCornersX > 0 || roundCornersY > 0) { + path.addRoundedRect(rect, roundCornersX, roundCornersY); + } else { + path.addRect(rect); + } + + helper.paintPainterPath(path); } else { - QRectF r = convertToPt(rect); - KoShape* shape = KisShapeToolHelper::createRectangleShape(r); + const QRectF r = convertToPt(rect); + const qreal docRoundCornersX = convertToPt(roundCornersX); + const qreal docRoundCornersY = convertToPt(roundCornersY); + KoShape* shape = KisShapeToolHelper::createRectangleShape(r, docRoundCornersX, docRoundCornersY); KoShapeStrokeSP border; if (strokeStyle() == KisPainter::StrokeStyleBrush) { border = toQShared(new KoShapeStroke(currentStrokeWidth(), currentFgColor().toQColor())); } shape->setStroke(border); info.markAsSelectionShapeIfNeeded(shape); addShape(shape); } notifyModified(); } diff --git a/plugins/tools/basictools/kis_tool_rectangle.h b/plugins/tools/basictools/kis_tool_rectangle.h index 754483cd63..5798e8841c 100644 --- a/plugins/tools/basictools/kis_tool_rectangle.h +++ b/plugins/tools/basictools/kis_tool_rectangle.h @@ -1,79 +1,79 @@ /* * kis_tool_rectangle.h - part of KImageShop^WKrayon^WKrita * * Copyright (c) 1999 Michael Koch * Copyright (c) 2002 Patrick Julien * Copyright (c) 2004 Boudewijn Rempt * Copyright (c) 2004 Clarence Dang * * 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. */ #ifndef __KIS_TOOL_RECTANGLE_H__ #define __KIS_TOOL_RECTANGLE_H__ #include "kis_tool_shape.h" #include "kis_types.h" #include "KoToolFactoryBase.h" #include "flake/kis_node_shape.h" #include #include class QRect; class KoCanvasBase; class KisToolRectangle : public KisToolRectangleBase { Q_OBJECT public: KisToolRectangle(KoCanvasBase * canvas); ~KisToolRectangle() override; protected: - void finishRect(const QRectF& rect) override; + void finishRect(const QRectF& rect, qreal roundCornersX, qreal roundCornersY) override; protected Q_SLOTS: void resetCursorStyle() override; }; class KisToolRectangleFactory : public KoToolFactoryBase { public: KisToolRectangleFactory() : KoToolFactoryBase("KritaShape/KisToolRectangle") { setToolTip(i18n("Rectangle Tool")); setSection(TOOL_TYPE_SHAPE); setActivationShapeId(KRITA_TOOL_ACTIVATION_ID); setIconName(koIconNameCStr("krita_tool_rectangle")); //setShortcut( Qt::Key_F6 ); setPriority(2); } ~KisToolRectangleFactory() override {} KoToolBase * createTool(KoCanvasBase *canvas) override { return new KisToolRectangle(canvas); } }; #endif // __KIS_TOOL_RECTANGLE_H__ diff --git a/plugins/tools/selectiontools/kis_tool_select_elliptical.cc b/plugins/tools/selectiontools/kis_tool_select_elliptical.cc index df26677de9..d925bfe624 100644 --- a/plugins/tools/selectiontools/kis_tool_select_elliptical.cc +++ b/plugins/tools/selectiontools/kis_tool_select_elliptical.cc @@ -1,100 +1,103 @@ /* * kis_tool_select_elliptical.cc -- part of Krita * * Copyright (c) 2004 Boudewijn Rempt (boud@valdyas.org) * Copyright (c) 2007 Sven Langkamp * Copyright (c) 2015 Michael Abrahams * * 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_tool_select_elliptical.h" #include #include "kis_painter.h" #include #include "kis_selection_options.h" #include "kis_canvas2.h" #include "kis_pixel_selection.h" #include "kis_selection_tool_helper.h" #include "kis_shape_tool_helper.h" #include "KisViewManager.h" #include "kis_selection_manager.h" __KisToolSelectEllipticalLocal::__KisToolSelectEllipticalLocal(KoCanvasBase *canvas) : KisToolEllipseBase(canvas, KisToolEllipseBase::SELECT, KisCursor::load("tool_elliptical_selection_cursor.png", 6, 6)) { setObjectName("tool_select_elliptical"); } -void __KisToolSelectEllipticalLocal::finishRect(const QRectF &rect) +void __KisToolSelectEllipticalLocal::finishRect(const QRectF &rect, qreal roundCornersX, qreal roundCornersY) { + Q_UNUSED(roundCornersX); + Q_UNUSED(roundCornersY); + KisCanvas2 * kisCanvas = dynamic_cast(canvas()); Q_ASSERT(kisCanvas); KisSelectionToolHelper helper(kisCanvas, kundo2_i18n("Select Ellipse")); if (helper.tryDeselectCurrentSelection(pixelToView(rect), selectionAction())) { return; } if (selectionMode() == PIXEL_SELECTION) { KisPixelSelectionSP tmpSel = new KisPixelSelection(); KisPainter painter(tmpSel); painter.setPaintColor(KoColor(Qt::black, tmpSel->colorSpace())); painter.setAntiAliasPolygonFill(antiAliasSelection()); painter.setFillStyle(KisPainter::FillStyleForegroundColor); painter.setStrokeStyle(KisPainter::StrokeStyleNone); painter.paintEllipse(rect); QPainterPath cache; cache.addEllipse(rect); tmpSel->setOutlineCache(cache); helper.selectPixelSelection(tmpSel, selectionAction()); } else { QRectF ptRect = convertToPt(rect); KoShape* shape = KisShapeToolHelper::createEllipseShape(ptRect); helper.addSelectionShape(shape); } } KisToolSelectElliptical::KisToolSelectElliptical(KoCanvasBase *canvas): KisToolSelectEllipticalTemplate(canvas, i18n("Elliptical Selection")) { connect(&m_widgetHelper, &KisSelectionToolConfigWidgetHelper::selectionActionChanged, this, &KisToolSelectElliptical::setSelectionAction); } void KisToolSelectElliptical::setSelectionAction(int action) { changeSelectionAction(action); } QMenu* KisToolSelectElliptical::popupActionsMenu() { KisCanvas2 * kisCanvas = dynamic_cast(canvas()); Q_ASSERT(kisCanvas); return KisSelectionToolHelper::getSelectionContextMenu(kisCanvas); } diff --git a/plugins/tools/selectiontools/kis_tool_select_elliptical.h b/plugins/tools/selectiontools/kis_tool_select_elliptical.h index c2ba6ece97..e06b938d84 100644 --- a/plugins/tools/selectiontools/kis_tool_select_elliptical.h +++ b/plugins/tools/selectiontools/kis_tool_select_elliptical.h @@ -1,93 +1,93 @@ /* * kis_tool_select_elliptical.h - part of Krayon^WKrita * * Copyright (c) 2000 John Califf * Copyright (c) 2002 Patrick Julien * Copyright (c) 2004 Boudewijn Rempt * * Copyright (c) 2015 Michael Abrahams * * 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. */ #ifndef __KIS_TOOL_SELECT_ELLIPTICAL_H__ #define __KIS_TOOL_SELECT_ELLIPTICAL_H__ #include "KoToolFactoryBase.h" #include "kis_tool_ellipse_base.h" #include #include "kis_selection_tool_config_widget_helper.h" #include #include #include #include class __KisToolSelectEllipticalLocal : public KisToolEllipseBase { Q_OBJECT public: __KisToolSelectEllipticalLocal(KoCanvasBase *canvas); protected: virtual SelectionMode selectionMode() const = 0; virtual SelectionAction selectionAction() const = 0; virtual bool antiAliasSelection() const = 0; private: - void finishRect(const QRectF &rect) override; + void finishRect(const QRectF &rect, qreal roundCornersX, qreal roundCornersY) override; }; typedef KisToolSelectBase<__KisToolSelectEllipticalLocal> KisToolSelectEllipticalTemplate; class KisToolSelectElliptical : public KisToolSelectEllipticalTemplate { Q_OBJECT public: KisToolSelectElliptical(KoCanvasBase* canvas); QMenu* popupActionsMenu() override; public Q_SLOTS: void setSelectionAction(int); }; class KisToolSelectEllipticalFactory : public KoToolFactoryBase { public: KisToolSelectEllipticalFactory() : KoToolFactoryBase("KisToolSelectElliptical") { setToolTip(i18n("Elliptical Selection Tool")); setSection(TOOL_TYPE_SELECTION); setActivationShapeId(KRITA_TOOL_ACTIVATION_ID); setIconName(koIconNameCStr("tool_elliptical_selection")); setShortcut(QKeySequence(Qt::Key_J)); setPriority(1); } ~KisToolSelectEllipticalFactory() override {} KoToolBase * createTool(KoCanvasBase *canvas) override { return new KisToolSelectElliptical(canvas); } }; #endif //__KIS_TOOL_SELECT_ELLIPTICAL_H__ diff --git a/plugins/tools/selectiontools/kis_tool_select_rectangular.cc b/plugins/tools/selectiontools/kis_tool_select_rectangular.cc index e38b9346e0..b6257f83b6 100644 --- a/plugins/tools/selectiontools/kis_tool_select_rectangular.cc +++ b/plugins/tools/selectiontools/kis_tool_select_rectangular.cc @@ -1,100 +1,109 @@ /* * kis_tool_select_rectangular.cc -- part of Krita * * Copyright (c) 1999 Michael Koch * Copyright (c) 2001 John Califf * Copyright (c) 2002 Patrick Julien * Copyright (c) 2007 Sven Langkamp * Copyright (c) 2015 Michael Abrahams * * 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_tool_select_rectangular.h" #include "kis_painter.h" #include #include "kis_selection_options.h" #include "kis_canvas2.h" #include "kis_pixel_selection.h" #include "kis_selection_tool_helper.h" #include "kis_shape_tool_helper.h" #include "KisViewManager.h" #include "kis_selection_manager.h" __KisToolSelectRectangularLocal::__KisToolSelectRectangularLocal(KoCanvasBase * canvas) : KisToolRectangleBase(canvas, KisToolRectangleBase::SELECT, KisCursor::load("tool_rectangular_selection_cursor.png", 6, 6)) { setObjectName("tool_select_rectangular"); } -void __KisToolSelectRectangularLocal::finishRect(const QRectF& rect) +void __KisToolSelectRectangularLocal::finishRect(const QRectF& rect, qreal roundCornersX, qreal roundCornersY) { KisCanvas2 * kisCanvas = dynamic_cast(canvas()); if (!kisCanvas) return; KisSelectionToolHelper helper(kisCanvas, kundo2_i18n("Select Rectangle")); QRect rc(rect.normalized().toRect()); if (helper.tryDeselectCurrentSelection(pixelToView(rc), selectionAction())) { return; } if (helper.canShortcutToNoop(rc, selectionAction())) { return; } if (selectionMode() == PIXEL_SELECTION) { if (rc.isValid()) { KisPixelSelectionSP tmpSel = KisPixelSelectionSP(new KisPixelSelection()); tmpSel->select(rc); QPainterPath cache; - cache.addRect(rc); + + if (roundCornersX > 0 || roundCornersY > 0) { + cache.addRoundedRect(rc, roundCornersX, roundCornersY); + } else { + cache.addRect(rc); + } + tmpSel->setOutlineCache(cache); helper.selectPixelSelection(tmpSel, selectionAction()); } } else { QRectF documentRect = convertToPt(rc); - helper.addSelectionShape(KisShapeToolHelper::createRectangleShape(documentRect)); + const qreal docRoundCornersX = convertToPt(roundCornersX); + const qreal docRoundCornersY = convertToPt(roundCornersY); + + helper.addSelectionShape(KisShapeToolHelper::createRectangleShape(documentRect, docRoundCornersX, docRoundCornersY)); } } KisToolSelectRectangular::KisToolSelectRectangular(KoCanvasBase *canvas): KisToolSelectBase<__KisToolSelectRectangularLocal>(canvas, i18n("Rectangular Selection")) { connect(&m_widgetHelper, &KisSelectionToolConfigWidgetHelper::selectionActionChanged, this, &KisToolSelectRectangular::setSelectionAction); } void KisToolSelectRectangular::setSelectionAction(int action) { changeSelectionAction(action); } QMenu* KisToolSelectRectangular::popupActionsMenu() { KisCanvas2 * kisCanvas = dynamic_cast(canvas()); Q_ASSERT(kisCanvas); return KisSelectionToolHelper::getSelectionContextMenu(kisCanvas); } diff --git a/plugins/tools/selectiontools/kis_tool_select_rectangular.h b/plugins/tools/selectiontools/kis_tool_select_rectangular.h index ecb33d5e3b..4f3eda0cb2 100644 --- a/plugins/tools/selectiontools/kis_tool_select_rectangular.h +++ b/plugins/tools/selectiontools/kis_tool_select_rectangular.h @@ -1,86 +1,86 @@ /* * kis_tool_select_rectangular.h - part of Krita * * Copyright (c) 1999 Michael Koch * 2002 Patrick Julien * * * 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. */ #ifndef KIS_TOOL_SELECT_RECTANGULAR_H_ #define KIS_TOOL_SELECT_RECTANGULAR_H_ #include "KoToolFactoryBase.h" #include "kis_tool_rectangle_base.h" #include #include "kis_selection_tool_config_widget_helper.h" #include #include class __KisToolSelectRectangularLocal : public KisToolRectangleBase { Q_OBJECT public: __KisToolSelectRectangularLocal(KoCanvasBase * canvas); protected: virtual SelectionMode selectionMode() const = 0; virtual SelectionAction selectionAction() const = 0; private: - void finishRect(const QRectF& rect) override; + void finishRect(const QRectF& rect, qreal roundCornersX, qreal roundCornersY) override; }; class KisToolSelectRectangular : public KisToolSelectBase<__KisToolSelectRectangularLocal> { Q_OBJECT public: KisToolSelectRectangular(KoCanvasBase* canvas); QMenu* popupActionsMenu() override; public Q_SLOTS: void setSelectionAction(int); }; class KisToolSelectRectangularFactory : public KoToolFactoryBase { public: KisToolSelectRectangularFactory() : KoToolFactoryBase("KisToolSelectRectangular") { setToolTip(i18n("Rectangular Selection Tool")); setSection(TOOL_TYPE_SELECTION); setActivationShapeId(KRITA_TOOL_ACTIVATION_ID); setIconName(koIconNameCStr("tool_rect_selection")); setShortcut(QKeySequence(Qt::CTRL + Qt::Key_R)); setPriority(0); } ~KisToolSelectRectangularFactory() override {} KoToolBase * createTool(KoCanvasBase *canvas) override { return new KisToolSelectRectangular(canvas); } }; #endif // KIS_TOOL_SELECT_RECTANGULAR_H_ diff --git a/sdk/tests/testutil.h b/sdk/tests/testutil.h index 17bb2a5fc2..f3a8e9b48e 100644 --- a/sdk/tests/testutil.h +++ b/sdk/tests/testutil.h @@ -1,528 +1,528 @@ /* * Copyright (c) 2007 Boudewijn Rempt * * 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. */ #ifndef TEST_UTIL #define TEST_UTIL #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_node_graph_listener.h" #include "kis_iterator_ng.h" #include "kis_image.h" #include "testing_nodes.h" #include "kistest.h" #ifndef FILES_DATA_DIR #define FILES_DATA_DIR "." #endif #ifndef FILES_DEFAULT_DATA_DIR #define FILES_DEFAULT_DATA_DIR "." #endif #include "qimage_test_util.h" #define KIS_COMPARE_RF(expr, ref) \ if ((expr) != (ref)) { \ qDebug() << "Compared values are not the same at line" << __LINE__; \ qDebug() << " Actual : " << #expr << "=" << (expr); \ qDebug() << " Expected: " << #ref << "=" << (ref); \ return false; \ } /** * Routines that are useful for writing efficient tests */ namespace TestUtil { inline KisNodeSP findNode(KisNodeSP root, const QString &name) { if(root->name() == name) return root; KisNodeSP child = root->firstChild(); while (child) { if((root = findNode(child, name))) return root; child = child->nextSibling(); } return KisNodeSP(); } inline void dumpNodeStack(KisNodeSP node, QString prefix = QString("\t")) { qDebug() << node->name(); KisNodeSP child = node->firstChild(); while (child) { if (child->childCount() > 0) { dumpNodeStack(child, prefix + "\t"); } else { qDebug() << prefix << child->name(); } child = child->nextSibling(); } } class TestProgressBar : public KoProgressProxy { public: TestProgressBar() : m_min(0), m_max(0), m_value(0) {} int maximum() const override { return m_max; } void setValue(int value) override { m_value = value; } void setRange(int min, int max) override { m_min = min; m_max = max; } void setFormat(const QString &format) override { m_format = format; } - void setAutoNestedName(const QString &name) { + void setAutoNestedName(const QString &name) override { m_autoNestedName = name; KoProgressProxy::setAutoNestedName(name); } int min() { return m_min; } int max() { return m_max; } int value() { return m_value; } QString format() { return m_format; } QString autoNestedName() { return m_autoNestedName; } private: int m_min; int m_max; int m_value; QString m_format; QString m_autoNestedName; }; inline bool comparePaintDevices(QPoint & pt, const KisPaintDeviceSP dev1, const KisPaintDeviceSP dev2) { // QTime t; // t.start(); QRect rc1 = dev1->exactBounds(); QRect rc2 = dev2->exactBounds(); if (rc1 != rc2) { pt.setX(-1); pt.setY(-1); } KisHLineConstIteratorSP iter1 = dev1->createHLineConstIteratorNG(0, 0, rc1.width()); KisHLineConstIteratorSP iter2 = dev2->createHLineConstIteratorNG(0, 0, rc1.width()); int pixelSize = dev1->pixelSize(); for (int y = 0; y < rc1.height(); ++y) { do { if (memcmp(iter1->oldRawData(), iter2->oldRawData(), pixelSize) != 0) return false; } while (iter1->nextPixel() && iter2->nextPixel()); iter1->nextRow(); iter2->nextRow(); } // qDebug() << "comparePaintDevices time elapsed:" << t.elapsed(); return true; } template inline bool comparePaintDevicesClever(const KisPaintDeviceSP dev1, const KisPaintDeviceSP dev2, channel_type alphaThreshold = 0) { QRect rc1 = dev1->exactBounds(); QRect rc2 = dev2->exactBounds(); if (rc1 != rc2) { qDebug() << "Devices have different size" << ppVar(rc1) << ppVar(rc2); return false; } KisHLineConstIteratorSP iter1 = dev1->createHLineConstIteratorNG(0, 0, rc1.width()); KisHLineConstIteratorSP iter2 = dev2->createHLineConstIteratorNG(0, 0, rc1.width()); int pixelSize = dev1->pixelSize(); for (int y = 0; y < rc1.height(); ++y) { do { if (memcmp(iter1->oldRawData(), iter2->oldRawData(), pixelSize) != 0) { const channel_type* p1 = reinterpret_cast(iter1->oldRawData()); const channel_type* p2 = reinterpret_cast(iter2->oldRawData()); if (p1[3] < alphaThreshold && p2[3] < alphaThreshold) continue; qDebug() << "Failed compare paint devices:" << iter1->x() << iter1->y(); qDebug() << "src:" << p1[0] << p1[1] << p1[2] << p1[3]; qDebug() << "dst:" << p2[0] << p2[1] << p2[2] << p2[3]; return false; } } while (iter1->nextPixel() && iter2->nextPixel()); iter1->nextRow(); iter2->nextRow(); } return true; } #ifdef FILES_OUTPUT_DIR struct ReferenceImageChecker { enum StorageType { InternalStorage = 0, ExternalStorage }; ReferenceImageChecker(const QString &prefix, const QString &testName, StorageType storageType = ExternalStorage) : m_storageType(storageType), m_prefix(prefix), m_testName(testName), m_success(true), m_maxFailingPixels(100), m_fuzzy(1) { } void setMaxFailingPixels(int value) { m_maxFailingPixels = value; } void setFuzzy(int fuzzy){ m_fuzzy = fuzzy; } bool testPassed() const { return m_success; } inline bool checkDevice(KisPaintDeviceSP device, KisImageSP image, const QString &caseName) { bool result = false; if (m_storageType == ExternalStorage) { result = checkQImageExternal(device->convertToQImage(0, image->bounds()), m_testName, m_prefix, caseName, m_fuzzy, m_fuzzy, m_maxFailingPixels); } else { result = checkQImage(device->convertToQImage(0, image->bounds()), m_testName, m_prefix, caseName, m_fuzzy, m_fuzzy, m_maxFailingPixels); } m_success &= result; return result; } inline bool checkImage(KisImageSP image, const QString &testName) { bool result = checkDevice(image->projection(), image, testName); m_success &= result; return result; } private: bool m_storageType; QString m_prefix; QString m_testName; bool m_success; int m_maxFailingPixels; int m_fuzzy; }; #endif inline quint8 alphaDevicePixel(KisPaintDeviceSP dev, qint32 x, qint32 y) { KisHLineConstIteratorSP iter = dev->createHLineConstIteratorNG(x, y, 1); const quint8 *pix = iter->oldRawData(); return *pix; } inline void alphaDeviceSetPixel(KisPaintDeviceSP dev, qint32 x, qint32 y, quint8 s) { KisHLineIteratorSP iter = dev->createHLineIteratorNG(x, y, 1); quint8 *pix = iter->rawData(); *pix = s; } inline bool checkAlphaDeviceFilledWithPixel(KisPaintDeviceSP dev, const QRect &rc, quint8 expected) { KisHLineIteratorSP it = dev->createHLineIteratorNG(rc.x(), rc.y(), rc.width()); for (int y = rc.y(); y < rc.y() + rc.height(); y++) { for (int x = rc.x(); x < rc.x() + rc.width(); x++) { if(*((quint8*)it->rawData()) != expected) { errKrita << "At point:" << x << y; errKrita << "Expected pixel:" << expected; errKrita << "Actual pixel: " << *((quint8*)it->rawData()); return false; } it->nextPixel(); } it->nextRow(); } return true; } class TestNode : public DefaultNode { Q_OBJECT public: KisNodeSP clone() const override { return KisNodeSP(new TestNode(*this)); } }; class TestGraphListener : public KisNodeGraphListener { public: void aboutToAddANode(KisNode *parent, int index) override { KisNodeGraphListener::aboutToAddANode(parent, index); beforeInsertRow = true; } void nodeHasBeenAdded(KisNode *parent, int index) override { KisNodeGraphListener::nodeHasBeenAdded(parent, index); afterInsertRow = true; } void aboutToRemoveANode(KisNode *parent, int index) override { KisNodeGraphListener::aboutToRemoveANode(parent, index); beforeRemoveRow = true; } void nodeHasBeenRemoved(KisNode *parent, int index) override { KisNodeGraphListener::nodeHasBeenRemoved(parent, index); afterRemoveRow = true; } void aboutToMoveNode(KisNode *parent, int oldIndex, int newIndex) override { KisNodeGraphListener::aboutToMoveNode(parent, oldIndex, newIndex); beforeMove = true; } void nodeHasBeenMoved(KisNode *parent, int oldIndex, int newIndex) override { KisNodeGraphListener::nodeHasBeenMoved(parent, oldIndex, newIndex); afterMove = true; } bool beforeInsertRow; bool afterInsertRow; bool beforeRemoveRow; bool afterRemoveRow; bool beforeMove; bool afterMove; void resetBools() { beforeRemoveRow = false; afterRemoveRow = false; beforeInsertRow = false; afterInsertRow = false; beforeMove = false; afterMove = false; } }; } #include #include #include "kis_undo_stores.h" #include "kis_layer_utils.h" namespace TestUtil { struct MaskParent { MaskParent(const QRect &_imageRect = QRect(0,0,512,512)) : imageRect(_imageRect) { const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8(); undoStore = new KisSurrogateUndoStore(); image = new KisImage(undoStore, imageRect.width(), imageRect.height(), cs, "test image"); layer = KisPaintLayerSP(new KisPaintLayer(image, "paint1", OPACITY_OPAQUE_U8)); image->addNode(KisNodeSP(layer.data())); } void waitForImageAndShapeLayers() { qApp->processEvents(); image->waitForDone(); KisLayerUtils::forceAllDelayedNodesUpdate(image->root()); qApp->processEvents(); image->waitForDone(); } KisSurrogateUndoStore *undoStore; const QRect imageRect; KisImageSP image; KisPaintLayerSP layer; }; } namespace TestUtil { class MeasureAvgPortion { public: MeasureAvgPortion(int period) : m_period(period), m_val(0), m_total(0), m_cycles(0) { } ~MeasureAvgPortion() { printValues(true); } void addVal(int x) { m_val += x; } void addTotal(int x) { m_total += x; m_cycles++; printValues(); } private: void printValues(bool force = false) { if (m_cycles > m_period || force) { qDebug() << "Val / Total:" << qreal(m_val) / qreal(m_total); qDebug() << "Avg. Val: " << qreal(m_val) / m_cycles; qDebug() << "Avg. Total: " << qreal(m_total) / m_cycles; qDebug() << ppVar(m_val) << ppVar(m_total) << ppVar(m_cycles); m_val = 0; m_total = 0; m_cycles = 0; } } private: int m_period; qint64 m_val; qint64 m_total; qint64 m_cycles; }; struct MeasureDistributionStats { MeasureDistributionStats(int numBins, const QString &name = QString()) : m_numBins(numBins), m_name(name) { reset(); } void reset() { m_values.clear(); m_values.resize(m_numBins); } void addValue(int value) { addValue(value, 1); } void addValue(int value, int increment) { KIS_SAFE_ASSERT_RECOVER_RETURN(value >= 0); if (value >= m_numBins) { m_values[m_numBins - 1] += increment; } else { m_values[value] += increment; } } void print() { qCritical() << "============= Stats =============="; if (!m_name.isEmpty()) { qCritical() << "Name:" << m_name; } int total = 0; for (int i = 0; i < m_numBins; i++) { total += m_values[i]; } for (int i = 0; i < m_numBins; i++) { if (!m_values[i]) continue; const QString lastMarker = i == m_numBins - 1 ? "> " : " "; const QString line = QString(" %1%2: %3 (%4%)") .arg(lastMarker) .arg(i, 3) .arg(m_values[i], 5) .arg(qreal(m_values[i]) / total * 100.0, 7, 'g', 2); qCritical() << qPrintable(line); } qCritical() << "---- ----"; qCritical() << qPrintable(QString("Total: %1").arg(total)); qCritical() << "=================================="; } private: QVector m_values; int m_numBins = 0; QString m_name; }; QStringList getHierarchy(KisNodeSP root, const QString &prefix = ""); bool checkHierarchy(KisNodeSP root, const QStringList &expected); } #endif