Warning, file /office/calligra/libs/widgets/KoStrokeConfigWidget.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /* This file is part of the KDE project
0002  * Made by Tomislav Lukman (tomislav.lukman@ck.tel.hr)
0003  * Copyright (C) 2002 Tomislav Lukman <tomislav.lukman@ck.t-com.hr>
0004  * Copyright (C) 2002-2003 Rob Buis <buis@kde.org>
0005  * Copyright (C) 2005-2006 Tim Beaulen <tbscope@gmail.com>
0006  * Copyright (C) 2005-2007 Thomas Zander <zander@kde.org>
0007  * Copyright (C) 2005-2006, 2011 Inge Wallin <inge@lysator.liu.se>
0008  * Copyright (C) 2005-2008 Jan Hambrecht <jaham@gmx.net>
0009  * Copyright (C) 2006 C. Boemann <cbo@boemann.dk>
0010  * Copyright (C) 2006 Peter Simonsson <psn@linux.se>
0011  * Copyright (C) 2006 Laurent Montel <montel@kde.org>
0012  * Copyright (C) 2007,2011 Thorsten Zachmann <t.zachmann@zagge.de>
0013  * Copyright (C) 2011 Jean-Nicolas Artaud <jeannicolasartaud@gmail.com>
0014  *
0015  * This library is free software; you can redistribute it and/or
0016  * modify it under the terms of the GNU Library General Public
0017  * License as published by the Free Software Foundation; either
0018  * version 2 of the License, or (at your option) any later version.
0019  *
0020  * This library is distributed in the hope that it will be useful,
0021  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0022  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0023  * Library General Public License for more details.
0024  *
0025  * You should have received a copy of the GNU Library General Public License
0026  * along with this library; see the file COPYING.LIB.  If not, write to
0027  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0028  * Boston, MA 02110-1301, USA.
0029  */
0030 
0031 // Own
0032 #include "KoStrokeConfigWidget.h"
0033 
0034 // Qt
0035 #include <QMenu>
0036 #include <QLabel>
0037 #include <QToolButton>
0038 #include <QButtonGroup>
0039 #include <QVBoxLayout>
0040 #include <QHBoxLayout>
0041 #include <QSizePolicy>
0042 
0043 // KF5
0044 #include <klocalizedstring.h>
0045 
0046 // Calligra
0047 #include <KoIcon.h>
0048 #include <KoUnit.h>
0049 #include <KoLineStyleSelector.h>
0050 #include <KoUnitDoubleSpinBox.h>
0051 #include <KoMarkerSelector.h>
0052 #include <KoColorPopupAction.h>
0053 #include <KoMarker.h>
0054 #include <KoShapeStroke.h>
0055 #include <KoPathShape.h>
0056 #include <KoMarkerCollection.h>
0057 #include <KoPathShapeMarkerCommand.h>
0058 #include <KoCanvasBase.h>
0059 #include <KoCanvasController.h>
0060 #include <KoCanvasResourceManager.h>
0061 #include <KoDocumentResourceManager.h>
0062 #include <KoToolManager.h>
0063 #include <KoSelection.h>
0064 #include <KoShapeController.h>
0065 #include <KoShapeManager.h>
0066 #include <KoShapeStrokeCommand.h>
0067 #include <KoShapeStrokeModel.h>
0068 
0069 class CapNJoinMenu : public QMenu
0070 {
0071 Q_OBJECT
0072 public:
0073     CapNJoinMenu(QWidget *parent = nullptr);
0074     QSize sizeHint() const override;
0075 
0076     KoUnitDoubleSpinBox *miterLimit;
0077     QButtonGroup        *capGroup;
0078     QButtonGroup        *joinGroup;
0079 };
0080 
0081 CapNJoinMenu::CapNJoinMenu(QWidget *parent)
0082     : QMenu(parent)
0083 {
0084     QGridLayout *mainLayout = new QGridLayout();
0085     mainLayout->setMargin(2);
0086 
0087      // The cap group
0088     capGroup = new QButtonGroup(this);
0089     capGroup->setExclusive(true);
0090 
0091     QToolButton *button = nullptr;
0092 
0093     button = new QToolButton(this);
0094     button->setIcon(koIcon("stroke-cap-butt"));
0095     button->setCheckable(true);
0096     button->setToolTip(i18n("Butt cap"));
0097     capGroup->addButton(button, Qt::FlatCap);
0098     mainLayout->addWidget(button, 2, 0);
0099 
0100     button = new QToolButton(this);
0101     button->setIcon(koIcon("stroke-cap-round"));
0102     button->setCheckable(true);
0103     button->setToolTip(i18n("Round cap"));
0104     capGroup->addButton(button, Qt::RoundCap);
0105     mainLayout->addWidget(button, 2, 1);
0106 
0107     button = new QToolButton(this);
0108     button->setIcon(koIcon("stroke-cap-square"));
0109     button->setCheckable(true);
0110     button->setToolTip(i18n("Square cap"));
0111     capGroup->addButton(button, Qt::SquareCap);
0112     mainLayout->addWidget(button, 2, 2, Qt::AlignLeft);
0113 
0114     // The join group
0115     joinGroup = new QButtonGroup(this);
0116     joinGroup->setExclusive(true);
0117 
0118     button = new QToolButton(this);
0119     button->setIcon(koIcon("stroke-join-miter"));
0120     button->setCheckable(true);
0121     button->setToolTip(i18n("Miter join"));
0122     joinGroup->addButton(button, Qt::MiterJoin);
0123     mainLayout->addWidget(button, 3, 0);
0124 
0125     button = new QToolButton(this);
0126     button->setIcon(koIcon("stroke-join-round"));
0127     button->setCheckable(true);
0128     button->setToolTip(i18n("Round join"));
0129     joinGroup->addButton(button, Qt::RoundJoin);
0130     mainLayout->addWidget(button, 3, 1);
0131 
0132     button = new QToolButton(this);
0133     button->setIcon(koIcon("stroke-join-bevel"));
0134     button->setCheckable(true);
0135     button->setToolTip(i18n("Bevel join"));
0136     joinGroup->addButton(button, Qt::BevelJoin);
0137     mainLayout->addWidget(button, 3, 2, Qt::AlignLeft);
0138 
0139     // Miter limit
0140     // set min/max/step and value in points, then set actual unit
0141     miterLimit = new KoUnitDoubleSpinBox(this);
0142     miterLimit->setMinMaxStep(0.0, 1000.0, 0.5);
0143     miterLimit->setDecimals(2);
0144     miterLimit->setUnit(KoUnit(KoUnit::Point));
0145     miterLimit->setToolTip(i18n("Miter limit"));
0146     mainLayout->addWidget(miterLimit, 4, 0, 1, 3);
0147 
0148     mainLayout->setSizeConstraint(QLayout::SetMinAndMaxSize);
0149     setLayout(mainLayout);
0150 }
0151 
0152 QSize CapNJoinMenu::sizeHint() const
0153 {
0154     return layout()->sizeHint();
0155 }
0156 
0157 
0158 class Q_DECL_HIDDEN KoStrokeConfigWidget::Private
0159 {
0160 public:
0161     Private()
0162         : canvas(nullptr),
0163         active(true)
0164     {
0165     }
0166 
0167     KoLineStyleSelector *lineStyle;
0168     KoUnitDoubleSpinBox *lineWidth;
0169     KoMarkerSelector    *startMarkerSelector;
0170     KoMarkerSelector    *endMarkerSelector;
0171 
0172     CapNJoinMenu *capNJoinMenu;
0173     QToolButton *colorButton;
0174     KoColorPopupAction *colorAction;
0175 
0176     QWidget *spacer;
0177 
0178     KoCanvasBase *canvas;
0179 
0180     bool active;
0181 };
0182 
0183 KoStrokeConfigWidget::KoStrokeConfigWidget(QWidget * parent)
0184     : QWidget(parent)
0185     , d(new Private())
0186 {
0187     setObjectName("Stroke widget");
0188     QVBoxLayout *mainLayout = new QVBoxLayout(this);
0189     mainLayout->setMargin(0);
0190 
0191     QHBoxLayout *firstLineLayout = new QHBoxLayout();
0192 
0193     // Start marker
0194     QList<KoMarker*> markers;
0195 
0196     d->startMarkerSelector = new KoMarkerSelector(KoMarkerData::MarkerStart, this);
0197     d->startMarkerSelector->updateMarkers(markers);
0198     d->startMarkerSelector->setMaximumWidth(50);
0199     firstLineLayout->addWidget(d->startMarkerSelector);
0200 
0201     // Line style
0202     d->lineStyle = new KoLineStyleSelector(this);
0203     d->lineStyle->setMinimumWidth(70);
0204     firstLineLayout->addWidget(d->lineStyle);
0205 
0206     // End marker
0207     d->endMarkerSelector = new KoMarkerSelector(KoMarkerData::MarkerEnd, this);
0208     d->endMarkerSelector->updateMarkers(markers);
0209     d->endMarkerSelector->setMaximumWidth(50);
0210     firstLineLayout->addWidget(d->endMarkerSelector);
0211 
0212     QHBoxLayout *secondLineLayout = new QHBoxLayout();
0213 
0214     // Line width
0215     QLabel *l = new QLabel(this);
0216     l->setText(i18n("Thickness:"));
0217     l->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
0218     l->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
0219     secondLineLayout->addWidget(l);
0220 
0221     // set min/max/step and value in points, then set actual unit
0222     d->lineWidth = new KoUnitDoubleSpinBox(this);
0223     d->lineWidth->setMinMaxStep(0.0, 1000.0, 0.5);
0224     d->lineWidth->setDecimals(2);
0225     d->lineWidth->setUnit(KoUnit(KoUnit::Point));
0226     d->lineWidth->setToolTip(i18n("Set line width of actual selection"));
0227     secondLineLayout->addWidget(d->lineWidth);
0228 
0229     QToolButton *capNJoinButton = new QToolButton(this);
0230     capNJoinButton->setMinimumHeight(25);
0231     d->capNJoinMenu = new CapNJoinMenu(this);
0232     capNJoinButton->setMenu(d->capNJoinMenu);
0233     capNJoinButton->setText("...");
0234     capNJoinButton->setPopupMode(QToolButton::InstantPopup);
0235 
0236     secondLineLayout->addWidget(capNJoinButton);
0237 
0238     d->colorButton = new QToolButton(this);
0239     secondLineLayout->addWidget(d->colorButton);
0240     d->colorAction = new KoColorPopupAction(this);
0241     d->colorAction->setIcon(koIcon("format-stroke-color"));
0242     d->colorAction->setToolTip(i18n("Change the color of the line/border"));
0243     d->colorButton->setDefaultAction(d->colorAction);
0244 
0245     mainLayout->addLayout(firstLineLayout);
0246     mainLayout->addLayout(secondLineLayout);
0247 
0248     // Spacer
0249     d->spacer = new QWidget();
0250     d->spacer->setObjectName("SpecialSpacer");
0251     mainLayout->addWidget(d->spacer);
0252 
0253 
0254     // set sensitive defaults
0255     d->lineStyle->setLineStyle(Qt::SolidLine);
0256     d->lineWidth->changeValue(1);
0257     d->colorAction->setCurrentColor(Qt::black);
0258 
0259     // Make the signals visible on the outside of this widget.
0260     connect(d->lineStyle,  SIGNAL(currentIndexChanged(int)), this, SLOT(applyChanges()));
0261     connect(d->lineWidth,  SIGNAL(valueChangedPt(qreal)),    this, SLOT(applyChanges()));
0262     connect(d->colorAction, SIGNAL(colorChanged(KoColor)), this, SLOT(applyChanges()));
0263     connect(d->capNJoinMenu->capGroup,   SIGNAL(buttonClicked(int)),       this, SLOT(applyChanges()));
0264     connect(d->capNJoinMenu->joinGroup,  SIGNAL(buttonClicked(int)),       this, SLOT(applyChanges()));
0265     connect(d->capNJoinMenu->miterLimit, SIGNAL(valueChangedPt(qreal)),    this, SLOT(applyChanges()));
0266     connect(d->startMarkerSelector,  SIGNAL(currentIndexChanged(int)), this, SLOT(startMarkerChanged()));
0267     connect(d->endMarkerSelector,  SIGNAL(currentIndexChanged(int)), this, SLOT(endMarkerChanged()));
0268 }
0269 
0270 KoStrokeConfigWidget::~KoStrokeConfigWidget()
0271 {
0272     delete d;
0273 }
0274 
0275 // ----------------------------------------------------------------
0276 //                         getters and setters
0277 
0278 
0279 Qt::PenStyle KoStrokeConfigWidget::lineStyle() const
0280 {
0281     return d->lineStyle->lineStyle();
0282 }
0283 
0284 QVector<qreal> KoStrokeConfigWidget::lineDashes() const
0285 {
0286     return d->lineStyle->lineDashes();
0287 }
0288 
0289 qreal KoStrokeConfigWidget::lineWidth() const
0290 {
0291     return d->lineWidth->value();
0292 }
0293 
0294 QColor KoStrokeConfigWidget::color() const
0295 {
0296     return d->colorAction->currentColor();
0297 }
0298 
0299 qreal KoStrokeConfigWidget::miterLimit() const
0300 {
0301     return d->capNJoinMenu->miterLimit->value();
0302 }
0303 
0304 KoMarker *KoStrokeConfigWidget::startMarker() const
0305 {
0306     return d->startMarkerSelector->marker();
0307 }
0308 
0309 KoMarker *KoStrokeConfigWidget::endMarker() const
0310 {
0311     return d->endMarkerSelector->marker();
0312 }
0313 
0314 Qt::PenCapStyle KoStrokeConfigWidget::capStyle() const
0315 {
0316     return static_cast<Qt::PenCapStyle>(d->capNJoinMenu->capGroup->checkedId());
0317 }
0318 
0319 Qt::PenJoinStyle KoStrokeConfigWidget::joinStyle() const
0320 {
0321     return static_cast<Qt::PenJoinStyle>(d->capNJoinMenu->joinGroup->checkedId());
0322 }
0323 
0324 KoShapeStroke* KoStrokeConfigWidget::createShapeStroke() const
0325 {
0326     KoShapeStroke *stroke = new KoShapeStroke();
0327 
0328     stroke->setColor(color());
0329     stroke->setLineWidth(lineWidth());
0330     stroke->setCapStyle(capStyle());
0331     stroke->setJoinStyle(joinStyle());
0332     stroke->setMiterLimit(miterLimit());
0333     stroke->setLineStyle(lineStyle(), lineDashes());
0334 
0335     return stroke;
0336 }
0337 
0338 // ----------------------------------------------------------------
0339 //                         Other public functions
0340 
0341 void KoStrokeConfigWidget::updateControls(KoShapeStrokeModel *stroke, KoMarker *startMarker, KoMarker *endMarker)
0342 {
0343     blockChildSignals(true);
0344 
0345     const KoShapeStroke *lineStroke = dynamic_cast<const KoShapeStroke*>(stroke);
0346     if (lineStroke) {
0347         d->lineWidth->changeValue(lineStroke->lineWidth());
0348         QAbstractButton *button = d->capNJoinMenu->capGroup->button(lineStroke->capStyle());
0349         if (button) {
0350             button->setChecked(true);
0351         }
0352         button = d->capNJoinMenu->joinGroup->button(lineStroke->joinStyle());
0353         if (button) {
0354             button->setChecked(true);
0355         }
0356         d->capNJoinMenu->miterLimit->changeValue(lineStroke->miterLimit());
0357         d->capNJoinMenu->miterLimit->setEnabled(lineStroke->joinStyle() == Qt::MiterJoin);
0358         d->lineStyle->setLineStyle(lineStroke->lineStyle(), lineStroke->lineDashes());
0359         d->colorAction->setCurrentColor(lineStroke->color());
0360     }
0361     else {
0362         d->lineWidth->changeValue(0.0);
0363         d->capNJoinMenu->capGroup->button(Qt::FlatCap)->setChecked(true);
0364         d->capNJoinMenu->joinGroup->button(Qt::MiterJoin)->setChecked(true);
0365         d->capNJoinMenu->miterLimit->changeValue(0.0);
0366         d->capNJoinMenu->miterLimit->setEnabled(true);
0367         d->lineStyle->setLineStyle(Qt::NoPen, QVector<qreal>());
0368     }
0369 
0370     d->startMarkerSelector->setMarker(startMarker);
0371     d->endMarkerSelector->setMarker(endMarker);
0372 
0373     blockChildSignals(false);
0374 }
0375 
0376 void KoStrokeConfigWidget::setUnit(const KoUnit &unit)
0377 {
0378     blockChildSignals(true);
0379 
0380     KoCanvasController* canvasController = KoToolManager::instance()->activeCanvasController();
0381     KoSelection *selection = canvasController->canvas()->shapeManager()->selection();
0382     KoShape * shape = selection->firstSelectedShape();
0383 
0384     /**
0385      * KoStrokeShape knows nothing about the transformations applied
0386      * to the shape, which doesn't prevent the shape to apply them and
0387      * display the stroke differently. So just take that into account
0388      * and show the user correct values using the multiplier in KoUnit.
0389      */
0390     KoUnit newUnit(unit);
0391     if (shape) {
0392         newUnit.adjustByPixelTransform(shape->absoluteTransformation(0));
0393     }
0394 
0395     d->lineWidth->setUnit(newUnit);
0396     d->capNJoinMenu->miterLimit->setUnit(newUnit);
0397 
0398     blockChildSignals(false);
0399 }
0400 
0401 void KoStrokeConfigWidget::updateMarkers(const QList<KoMarker*> &markers)
0402 {
0403     d->startMarkerSelector->updateMarkers(markers);
0404     d->endMarkerSelector->updateMarkers(markers);
0405 }
0406 
0407 void KoStrokeConfigWidget::blockChildSignals(bool block)
0408 {
0409     d->colorAction->blockSignals(block);
0410     d->lineWidth->blockSignals(block);
0411     d->capNJoinMenu->capGroup->blockSignals(block);
0412     d->capNJoinMenu->joinGroup->blockSignals(block);
0413     d->capNJoinMenu->miterLimit->blockSignals(block);
0414     d->lineStyle->blockSignals(block);
0415     d->startMarkerSelector->blockSignals(block);
0416     d->endMarkerSelector->blockSignals(block);
0417 }
0418 
0419 void KoStrokeConfigWidget::setActive(bool active)
0420 {
0421     d->active = active;
0422 }
0423 
0424 //------------------------
0425 void KoStrokeConfigWidget::applyChanges()
0426 {
0427     KoCanvasController* canvasController = KoToolManager::instance()->activeCanvasController();
0428     KoSelection *selection = canvasController->canvas()->shapeManager()->selection();
0429 
0430     //FIXME d->canvas->resourceManager()->setActiveStroke( d->stroke );
0431 
0432     if (!selection || !selection->count()) {
0433         return;
0434     }
0435 
0436     KoShapeStroke *newStroke = new KoShapeStroke();
0437     KoShapeStroke *oldStroke = dynamic_cast<KoShapeStroke*>( selection->firstSelectedShape()->stroke() );
0438     if (oldStroke) {
0439         newStroke->setLineBrush(oldStroke->lineBrush());
0440     }
0441     newStroke->setColor(color());
0442     newStroke->setLineWidth(lineWidth());
0443     newStroke->setCapStyle(static_cast<Qt::PenCapStyle>(d->capNJoinMenu->capGroup->checkedId()));
0444     newStroke->setJoinStyle(static_cast<Qt::PenJoinStyle>(d->capNJoinMenu->joinGroup->checkedId()));
0445     newStroke->setMiterLimit(miterLimit());
0446     newStroke->setLineStyle(lineStyle(), lineDashes());
0447 
0448     if (d->active) {
0449         KoShapeStrokeCommand *cmd = new KoShapeStrokeCommand(selection->selectedShapes(), newStroke);
0450         canvasController->canvas()->addCommand(cmd);
0451     }
0452 }
0453 
0454 void KoStrokeConfigWidget::applyMarkerChanges(KoMarkerData::MarkerPosition position)
0455 {
0456     KoMarker *marker = nullptr;
0457     if (position == KoMarkerData::MarkerStart) {
0458         marker = startMarker();
0459     }
0460     else if (position == KoMarkerData::MarkerEnd) {
0461         marker = endMarker();
0462     }
0463 
0464     KoCanvasController* canvasController = KoToolManager::instance()->activeCanvasController();
0465     KoSelection *selection = canvasController->canvas()->shapeManager()->selection();
0466 
0467     if (! selection || !selection->count()) {
0468         return;
0469     }
0470 
0471     const QList<KoShape*> shapeList = selection->selectedShapes();
0472     QList<KoPathShape*> pathShapeList;
0473     for (auto itShape : shapeList) {
0474         KoPathShape* pathShape = dynamic_cast<KoPathShape*>(itShape);
0475         if (pathShape) {
0476             pathShapeList << pathShape;
0477         }
0478     }
0479 
0480     if (!pathShapeList.empty()) {
0481         KoPathShapeMarkerCommand* cmdMarker = new KoPathShapeMarkerCommand(pathShapeList, marker, position);
0482         canvasController->canvas()->addCommand(cmdMarker);
0483     }
0484 }
0485 
0486 void KoStrokeConfigWidget::startMarkerChanged()
0487 {
0488     applyMarkerChanges(KoMarkerData::MarkerStart);
0489 }
0490 
0491 void KoStrokeConfigWidget::endMarkerChanged()
0492 {
0493     applyMarkerChanges(KoMarkerData::MarkerEnd);
0494 }
0495 // ----------------------------------------------------------------
0496 
0497 
0498 
0499 void KoStrokeConfigWidget::selectionChanged()
0500 {
0501     // see a comment in setUnit()
0502     setUnit(d->canvas->unit());
0503 
0504 
0505     KoCanvasController* canvasController = KoToolManager::instance()->activeCanvasController();
0506     KoSelection *selection = canvasController->canvas()->shapeManager()->selection();
0507     KoShape * shape = selection->firstSelectedShape();
0508     if (shape && shape->stroke()) {
0509         KoPathShape *pathShape = dynamic_cast<KoPathShape *>(shape);
0510         if (pathShape) {
0511             updateControls(shape->stroke(), pathShape->marker(KoMarkerData::MarkerStart),
0512                                              pathShape->marker(KoMarkerData::MarkerEnd));
0513         }
0514         else {
0515             updateControls(shape->stroke(), 0 ,0);
0516         }
0517     }
0518 }
0519 
0520 void KoStrokeConfigWidget::setCanvas( KoCanvasBase *canvas )
0521 {
0522     if (canvas) {
0523         connect(canvas->shapeManager()->selection(), SIGNAL(selectionChanged()),
0524                 this, SLOT(selectionChanged()));
0525         connect(canvas->shapeManager(), SIGNAL(selectionContentChanged()),
0526                 this, SLOT(selectionChanged()));
0527         connect(canvas->resourceManager(), SIGNAL(canvasResourceChanged(int,QVariant)),
0528                 this, SLOT(canvasResourceChanged(int,QVariant)));
0529         setUnit(canvas->unit());
0530 
0531         KoDocumentResourceManager *resourceManager = canvas->shapeController()->resourceManager();
0532         if (resourceManager) {
0533             KoMarkerCollection *collection = resourceManager->resource(KoDocumentResourceManager::MarkerCollection).value<KoMarkerCollection*>();
0534             if (collection) {
0535                 updateMarkers(collection->markers());
0536             }
0537         }
0538     }
0539 
0540     d->canvas = canvas;
0541 }
0542 
0543 void KoStrokeConfigWidget::canvasResourceChanged(int key, const QVariant &value)
0544 {
0545     switch (key) {
0546     case KoCanvasResourceManager::Unit:
0547         setUnit(value.value<KoUnit>());
0548         break;
0549     }
0550 }
0551 #include "KoStrokeConfigWidget.moc"