File indexing completed on 2024-12-22 04:13:18
0001 /* This file is part of the KDE project 0002 * Made by Tomislav Lukman (tomislav.lukman@ck.tel.hr) 0003 * SPDX-FileCopyrightText: 2002 Tomislav Lukman <tomislav.lukman@ck.t-com.hr> 0004 * SPDX-FileCopyrightText: 2002-2003 Rob Buis <buis@kde.org> 0005 * SPDX-FileCopyrightText: 2005-2006 Tim Beaulen <tbscope@gmail.com> 0006 * SPDX-FileCopyrightText: 2005-2007 Thomas Zander <zander@kde.org> 0007 * SPDX-FileCopyrightText: 2005-2006, 2011 Inge Wallin <inge@lysator.liu.se> 0008 * SPDX-FileCopyrightText: 2005-2008 Jan Hambrecht <jaham@gmx.net> 0009 * SPDX-FileCopyrightText: 2006 C. Boemann <cbo@boemann.dk> 0010 * SPDX-FileCopyrightText: 2006 Peter Simonsson <psn@linux.se> 0011 * SPDX-FileCopyrightText: 2006 Laurent Montel <montel@kde.org> 0012 * SPDX-FileCopyrightText: 2007, 2011 Thorsten Zachmann <t.zachmann@zagge.de> 0013 * SPDX-FileCopyrightText: 2011 Jean-Nicolas Artaud <jeannicolasartaud@gmail.com> 0014 * 0015 * SPDX-License-Identifier: LGPL-2.0-or-later 0016 */ 0017 0018 // Own 0019 #include "KoStrokeConfigWidget.h" 0020 0021 // Qt 0022 #include <QMenu> 0023 #include <QToolButton> 0024 #include <QButtonGroup> 0025 #include <QVBoxLayout> 0026 #include <QHBoxLayout> 0027 #include <QSizePolicy> 0028 #include <KisSignalMapper.h> 0029 0030 // KDE 0031 #include <klocalizedstring.h> 0032 0033 // Calligra 0034 #include <KoIcon.h> 0035 #include <KoUnit.h> 0036 #include <KoLineStyleSelector.h> 0037 #include <KoUnitDoubleSpinBox.h> 0038 #include <KoMarkerSelector.h> 0039 #include <KoColorPopupAction.h> 0040 #include <KoMarker.h> 0041 #include <KoShapeStroke.h> 0042 #include <KoPathShape.h> 0043 #include <KoMarkerCollection.h> 0044 #include <KoPathShapeMarkerCommand.h> 0045 #include <KoCanvasBase.h> 0046 #include <KoCanvasController.h> 0047 #include <KoCanvasResourceProvider.h> 0048 #include <KoDocumentResourceManager.h> 0049 #include <KoSelection.h> 0050 #include <KoShapeController.h> 0051 #include <KoShapeStrokeCommand.h> 0052 #include <KoShapeStrokeModel.h> 0053 #include <KoSelectedShapesProxy.h> 0054 #include "ui_KoStrokeConfigWidget.h" 0055 #include <KoFlakeUtils.h> 0056 #include <KoFillConfigWidget.h> 0057 #include "kis_canvas_resource_provider.h" 0058 #include "kis_acyclic_signal_connector.h" 0059 #include <kis_signal_compressor.h> 0060 0061 // Krita 0062 #include "kis_double_parse_unit_spin_box.h" 0063 0064 class CapNJoinMenu : public QMenu 0065 { 0066 public: 0067 CapNJoinMenu(QWidget *parent = 0); 0068 QSize sizeHint() const override; 0069 0070 KisDoubleParseUnitSpinBox *miterLimit {0}; 0071 QButtonGroup *capGroup {0}; 0072 QButtonGroup *joinGroup {0}; 0073 }; 0074 0075 CapNJoinMenu::CapNJoinMenu(QWidget *parent) 0076 : QMenu(parent) 0077 { 0078 QGridLayout *mainLayout = new QGridLayout(this); 0079 mainLayout->setMargin(2); 0080 0081 // The cap group 0082 capGroup = new QButtonGroup(this); 0083 capGroup->setExclusive(true); 0084 0085 QToolButton *button = 0; 0086 0087 button = new QToolButton(this); 0088 button->setIcon(koIcon("stroke-cap-butt")); 0089 button->setCheckable(true); 0090 button->setToolTip(i18n("Butt cap")); 0091 capGroup->addButton(button, Qt::FlatCap); 0092 mainLayout->addWidget(button, 2, 0); 0093 0094 button = new QToolButton(this); 0095 button->setIcon(koIcon("stroke-cap-round")); 0096 button->setCheckable(true); 0097 button->setToolTip(i18n("Round cap")); 0098 capGroup->addButton(button, Qt::RoundCap); 0099 mainLayout->addWidget(button, 2, 1); 0100 0101 button = new QToolButton(this); 0102 button->setIcon(koIcon("stroke-cap-square")); 0103 button->setCheckable(true); 0104 button->setToolTip(i18n("Square cap")); 0105 capGroup->addButton(button, Qt::SquareCap); 0106 mainLayout->addWidget(button, 2, 2, Qt::AlignLeft); 0107 0108 // The join group 0109 joinGroup = new QButtonGroup(this); 0110 joinGroup->setExclusive(true); 0111 0112 button = new QToolButton(this); 0113 button->setIcon(koIcon("stroke-join-miter")); 0114 button->setCheckable(true); 0115 button->setToolTip(i18n("Miter join")); 0116 joinGroup->addButton(button, Qt::MiterJoin); 0117 mainLayout->addWidget(button, 3, 0); 0118 0119 button = new QToolButton(this); 0120 button->setIcon(koIcon("stroke-join-round")); 0121 button->setCheckable(true); 0122 button->setToolTip(i18n("Round join")); 0123 joinGroup->addButton(button, Qt::RoundJoin); 0124 mainLayout->addWidget(button, 3, 1); 0125 0126 button = new QToolButton(this); 0127 button->setIcon(koIcon("stroke-join-bevel")); 0128 button->setCheckable(true); 0129 button->setToolTip(i18n("Bevel join")); 0130 joinGroup->addButton(button, Qt::BevelJoin); 0131 mainLayout->addWidget(button, 3, 2, Qt::AlignLeft); 0132 0133 // Miter limit 0134 // set min/max/step and value in points, then set actual unit 0135 miterLimit = new KisDoubleParseUnitSpinBox(this); 0136 miterLimit->setMinMaxStep(0.0, 1000.0, 0.5); 0137 miterLimit->setDecimals(2); 0138 miterLimit->setUnit(KoUnit(KoUnit::Point)); 0139 miterLimit->setToolTip(i18n("Miter limit")); 0140 mainLayout->addWidget(miterLimit, 4, 0, 1, 3); 0141 0142 mainLayout->setSizeConstraint(QLayout::SetMinAndMaxSize); 0143 } 0144 0145 QSize CapNJoinMenu::sizeHint() const 0146 { 0147 return layout()->sizeHint(); 0148 } 0149 0150 0151 class Q_DECL_HIDDEN KoStrokeConfigWidget::Private 0152 { 0153 public: 0154 Private() 0155 : selectionChangedCompressor(200, KisSignalCompressor::FIRST_ACTIVE) 0156 { 0157 } 0158 0159 KoLineStyleSelector *lineStyle {0}; 0160 KisDoubleParseUnitSpinBox *lineWidth {0}; 0161 KoMarkerSelector *startMarkerSelector {0}; 0162 KoMarkerSelector *midMarkerSelector {0}; 0163 KoMarkerSelector *endMarkerSelector {0}; 0164 0165 CapNJoinMenu *capNJoinMenu {0}; 0166 0167 QWidget*spacer {0}; 0168 0169 KoCanvasBase *canvas {0}; 0170 0171 bool active {true}; 0172 bool allowLocalUnitManagement {false}; 0173 0174 KoFillConfigWidget *fillConfigWidget {0}; 0175 bool noSelectionTrackingMode {false}; 0176 0177 KisAcyclicSignalConnector shapeChangedAcyclicConnector; 0178 KisAcyclicSignalConnector resourceManagerAcyclicConnector; 0179 KisSignalCompressor selectionChangedCompressor; 0180 0181 std::vector<KisAcyclicSignalConnector::Blocker> deactivationLocks; 0182 0183 QScopedPointer<Ui_KoStrokeConfigWidget> ui; 0184 }; 0185 0186 0187 KoStrokeConfigWidget::KoStrokeConfigWidget(KoCanvasBase *canvas, QWidget * parent) 0188 : QWidget(parent) 0189 , d(new Private()) 0190 { 0191 // configure GUI 0192 d->ui.reset(new Ui_KoStrokeConfigWidget()); 0193 d->ui->setupUi(this); 0194 0195 setObjectName("Stroke widget"); 0196 0197 { // connect the canvas 0198 d->shapeChangedAcyclicConnector.connectBackwardVoid( 0199 canvas->selectedShapesProxy(), SIGNAL(selectionChanged()), 0200 &d->selectionChangedCompressor, SLOT(start())); 0201 0202 d->shapeChangedAcyclicConnector.connectBackwardVoid( 0203 canvas->selectedShapesProxy(), SIGNAL(selectionContentChanged()), 0204 &d->selectionChangedCompressor, SLOT(start())); 0205 0206 connect(&d->selectionChangedCompressor, SIGNAL(timeout()), this, SLOT(selectionChanged())); 0207 0208 d->resourceManagerAcyclicConnector.connectBackwardResourcePair( 0209 canvas->resourceManager(), SIGNAL(canvasResourceChanged(int,QVariant)), 0210 this, SLOT(canvasResourceChanged(int,QVariant))); 0211 0212 d->canvas = canvas; 0213 } 0214 0215 { 0216 0217 d->fillConfigWidget = new KoFillConfigWidget(canvas, KoFlake::StrokeFill, true, this); 0218 d->fillConfigWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding); 0219 d->ui->fillConfigWidgetLayout->addWidget(d->fillConfigWidget); 0220 connect(d->fillConfigWidget, SIGNAL(sigFillChanged()), SIGNAL(sigStrokeChanged())); 0221 } 0222 0223 d->ui->thicknessLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter); 0224 d->ui->thicknessLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); 0225 0226 // set min/max/step and value in points, then set actual unit 0227 d->ui->lineWidth->setMinMaxStep(0.5, 1000.0, 0.5); // if someone wants 0, just set to "none" on UI 0228 d->ui->lineWidth->setDecimals(2); 0229 d->ui->lineWidth->setUnit(KoUnit(KoUnit::Point)); 0230 d->ui->lineWidth->setToolTip(i18n("Set line width of actual selection")); 0231 0232 d->ui->capNJoinButton->setMinimumHeight(25); 0233 d->capNJoinMenu = new CapNJoinMenu(this); 0234 d->ui->capNJoinButton->setMenu(d->capNJoinMenu); 0235 d->ui->capNJoinButton->setText("..."); 0236 d->ui->capNJoinButton->setPopupMode(QToolButton::InstantPopup); 0237 0238 0239 { 0240 // Line style 0241 d->ui->strokeStyleLabel->setText(i18n("Line Style:")); 0242 d->ui->strokeStyleLabel->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); 0243 0244 d->ui->lineStyle->setToolTip(i18nc("@info:tooltip", "Line style")); 0245 d->ui->lineStyle->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); 0246 d->ui->lineStyle->setLineStyle(Qt::SolidLine, QVector<qreal>()); 0247 } 0248 0249 0250 { 0251 QList<KoMarker*> emptyMarkers; 0252 0253 0254 d->startMarkerSelector = new KoMarkerSelector(KoFlake::StartMarker, this); 0255 d->startMarkerSelector->setToolTip(i18nc("@info:tooltip", "Start marker")); 0256 d->startMarkerSelector->updateMarkers(emptyMarkers); 0257 d->startMarkerSelector->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred ); 0258 d->ui->markerLayout->addWidget(d->startMarkerSelector); 0259 0260 0261 d->midMarkerSelector = new KoMarkerSelector(KoFlake::MidMarker, this); 0262 d->midMarkerSelector->setToolTip(i18nc("@info:tooltip", "Node marker")); 0263 d->midMarkerSelector->updateMarkers(emptyMarkers); 0264 d->midMarkerSelector->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred ); 0265 d->ui->markerLayout->addWidget(d->midMarkerSelector); 0266 0267 0268 d->endMarkerSelector = new KoMarkerSelector(KoFlake::EndMarker, this); 0269 d->endMarkerSelector->setToolTip(i18nc("@info:tooltip", "End marker")); 0270 d->endMarkerSelector->updateMarkers(emptyMarkers); 0271 d->endMarkerSelector->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred ); 0272 0273 d->ui->markerLayout->addWidget(d->endMarkerSelector); 0274 } 0275 0276 // Spacer 0277 d->spacer = new QWidget(); 0278 d->spacer->setObjectName("SpecialSpacer"); 0279 0280 d->ui->markerLayout->addWidget(d->spacer); 0281 0282 connect(d->ui->lineStyle, SIGNAL(currentIndexChanged(int)), this, SLOT(applyDashStyleChanges())); 0283 connect(d->ui->lineWidth, SIGNAL(valueChangedPt(qreal)), this, SLOT(applyLineWidthChanges())); 0284 0285 connect(d->capNJoinMenu->capGroup, SIGNAL(buttonClicked(int)), this, SLOT(applyJoinCapChanges())); 0286 connect(d->capNJoinMenu->joinGroup, SIGNAL(buttonClicked(int)), this, SLOT(applyJoinCapChanges())); 0287 connect(d->capNJoinMenu->miterLimit, SIGNAL(valueChangedPt(qreal)), this, SLOT(applyJoinCapChanges())); 0288 0289 { // Map the marker signals correctly 0290 KisSignalMapper *mapper = new KisSignalMapper(this); 0291 connect(mapper, SIGNAL(mapped(int)), SLOT(applyMarkerChanges(int))); 0292 0293 connect(d->startMarkerSelector, SIGNAL(currentIndexChanged(int)), mapper, SLOT(map())); 0294 connect(d->midMarkerSelector, SIGNAL(currentIndexChanged(int)), mapper, SLOT(map())); 0295 connect(d->endMarkerSelector, SIGNAL(currentIndexChanged(int)), mapper, SLOT(map())); 0296 0297 mapper->setMapping(d->startMarkerSelector, KoFlake::StartMarker); 0298 mapper->setMapping(d->midMarkerSelector, KoFlake::MidMarker); 0299 mapper->setMapping(d->endMarkerSelector, KoFlake::EndMarker); 0300 } 0301 0302 KoDocumentResourceManager *resourceManager = canvas->shapeController()->resourceManager(); 0303 if (resourceManager) { 0304 KoMarkerCollection *collection = resourceManager->resource(KoDocumentResourceManager::MarkerCollection).value<KoMarkerCollection*>(); 0305 if (collection) { 0306 updateMarkers(collection->markers()); 0307 } 0308 } 0309 0310 d->selectionChangedCompressor.start(); 0311 0312 // initialize deactivation locks 0313 d->deactivationLocks.push_back(KisAcyclicSignalConnector::Blocker(d->shapeChangedAcyclicConnector)); 0314 d->deactivationLocks.push_back(KisAcyclicSignalConnector::Blocker(d->resourceManagerAcyclicConnector)); 0315 } 0316 0317 KoStrokeConfigWidget::~KoStrokeConfigWidget() 0318 { 0319 delete d; 0320 } 0321 0322 void KoStrokeConfigWidget::setNoSelectionTrackingMode(bool value) 0323 { 0324 d->fillConfigWidget->setNoSelectionTrackingMode(value); 0325 d->noSelectionTrackingMode = value; 0326 if (!d->noSelectionTrackingMode) { 0327 d->selectionChangedCompressor.start(); 0328 } 0329 } 0330 0331 // ---------------------------------------------------------------- 0332 // getters and setters 0333 0334 0335 Qt::PenStyle KoStrokeConfigWidget::lineStyle() const 0336 { 0337 return d->ui->lineStyle->lineStyle(); 0338 } 0339 0340 QVector<qreal> KoStrokeConfigWidget::lineDashes() const 0341 { 0342 return d->ui->lineStyle->lineDashes(); 0343 } 0344 0345 qreal KoStrokeConfigWidget::lineWidth() const 0346 { 0347 return d->ui->lineWidth->value(); 0348 } 0349 0350 qreal KoStrokeConfigWidget::miterLimit() const 0351 { 0352 return d->capNJoinMenu->miterLimit->value(); 0353 } 0354 0355 KoMarker *KoStrokeConfigWidget::startMarker() const 0356 { 0357 return d->startMarkerSelector->marker(); 0358 } 0359 0360 KoMarker *KoStrokeConfigWidget::endMarker() const 0361 { 0362 return d->endMarkerSelector->marker(); 0363 } 0364 0365 Qt::PenCapStyle KoStrokeConfigWidget::capStyle() const 0366 { 0367 return static_cast<Qt::PenCapStyle>(d->capNJoinMenu->capGroup->checkedId()); 0368 } 0369 0370 Qt::PenJoinStyle KoStrokeConfigWidget::joinStyle() const 0371 { 0372 return static_cast<Qt::PenJoinStyle>(d->capNJoinMenu->joinGroup->checkedId()); 0373 } 0374 0375 KoShapeStrokeSP KoStrokeConfigWidget::createShapeStroke() 0376 { 0377 KoShapeStrokeSP stroke(d->fillConfigWidget->createShapeStroke()); 0378 0379 stroke->setLineWidth(lineWidth()); 0380 stroke->setCapStyle(capStyle()); 0381 stroke->setJoinStyle(joinStyle()); 0382 stroke->setMiterLimit(miterLimit()); 0383 stroke->setLineStyle(lineStyle(), lineDashes()); 0384 0385 return stroke; 0386 } 0387 0388 // ---------------------------------------------------------------- 0389 // Other public functions 0390 0391 void KoStrokeConfigWidget::updateStyleControlsAvailability(bool enabled) 0392 { 0393 d->ui->lineWidth->setEnabled(enabled); 0394 d->capNJoinMenu->setEnabled(enabled); 0395 d->ui->lineStyle->setEnabled(enabled); 0396 0397 d->startMarkerSelector->setEnabled(enabled); 0398 d->midMarkerSelector->setEnabled(enabled); 0399 d->endMarkerSelector->setEnabled(enabled); 0400 } 0401 0402 void KoStrokeConfigWidget::setUnit(const KoUnit &unit, KoShape *representativeShape) 0403 { 0404 if (!d->allowLocalUnitManagement) { 0405 return; //the unit management is completely transferred to the unitManagers. 0406 } 0407 0408 blockChildSignals(true); 0409 0410 /** 0411 * KoStrokeShape knows nothing about the transformations applied 0412 * to the shape, which doesn't prevent the shape to apply them and 0413 * display the stroke differently. So just take that into account 0414 * and show the user correct values using the multiplier in KoUnit. 0415 */ 0416 KoUnit newUnit(unit); 0417 if (representativeShape) { 0418 newUnit.adjustByPixelTransform(representativeShape->absoluteTransformation()); 0419 } 0420 0421 d->ui->lineWidth->setUnit(newUnit); 0422 d->capNJoinMenu->miterLimit->setUnit(newUnit); 0423 0424 d->ui->lineWidth->setLineStep(1.0); 0425 d->capNJoinMenu->miterLimit->setLineStep(1.0); 0426 0427 blockChildSignals(false); 0428 } 0429 0430 void KoStrokeConfigWidget::setUnitManagers(KisSpinBoxUnitManager* managerLineWidth, 0431 KisSpinBoxUnitManager *managerMitterLimit) 0432 { 0433 blockChildSignals(true); 0434 d->allowLocalUnitManagement = false; 0435 d->ui->lineWidth->setUnitManager(managerLineWidth); 0436 d->capNJoinMenu->miterLimit->setUnitManager(managerMitterLimit); 0437 blockChildSignals(false); 0438 } 0439 0440 void KoStrokeConfigWidget::updateMarkers(const QList<KoMarker*> &markers) 0441 { 0442 d->startMarkerSelector->updateMarkers(markers); 0443 d->midMarkerSelector->updateMarkers(markers); 0444 d->endMarkerSelector->updateMarkers(markers); 0445 } 0446 0447 void KoStrokeConfigWidget::activate() 0448 { 0449 KIS_SAFE_ASSERT_RECOVER_NOOP(!d->deactivationLocks.empty()); 0450 d->deactivationLocks.clear(); 0451 d->fillConfigWidget->activate(); 0452 0453 if (!d->noSelectionTrackingMode) { 0454 d->selectionChangedCompressor.start(); 0455 } else { 0456 loadCurrentStrokeFillFromResourceServer(); 0457 } 0458 } 0459 0460 void KoStrokeConfigWidget::deactivate() 0461 { 0462 KIS_SAFE_ASSERT_RECOVER_NOOP(d->deactivationLocks.empty()); 0463 0464 d->deactivationLocks.push_back(KisAcyclicSignalConnector::Blocker(d->shapeChangedAcyclicConnector)); 0465 d->deactivationLocks.push_back(KisAcyclicSignalConnector::Blocker(d->resourceManagerAcyclicConnector)); 0466 d->fillConfigWidget->deactivate(); 0467 } 0468 0469 void KoStrokeConfigWidget::blockChildSignals(bool block) 0470 { 0471 d->ui->lineWidth->blockSignals(block); 0472 d->capNJoinMenu->capGroup->blockSignals(block); 0473 d->capNJoinMenu->joinGroup->blockSignals(block); 0474 d->capNJoinMenu->miterLimit->blockSignals(block); 0475 d->ui->lineStyle->blockSignals(block); 0476 d->startMarkerSelector->blockSignals(block); 0477 d->midMarkerSelector->blockSignals(block); 0478 d->endMarkerSelector->blockSignals(block); 0479 } 0480 0481 void KoStrokeConfigWidget::setActive(bool active) 0482 { 0483 d->active = active; 0484 } 0485 0486 //------------------------ 0487 0488 template <typename ModifyFunction> 0489 auto applyChangeToStrokes(KoCanvasBase *canvas, ModifyFunction modifyFunction) 0490 -> decltype(modifyFunction(KoShapeStrokeSP()), void()) 0491 { 0492 KoSelection *selection = canvas->selectedShapesProxy()->selection(); 0493 0494 if (!selection) return; 0495 0496 QList<KoShape*> shapes = selection->selectedEditableShapes(); 0497 0498 KUndo2Command *command = KoFlake::modifyShapesStrokes(shapes, modifyFunction); 0499 0500 if (command) { 0501 canvas->addCommand(command); 0502 } 0503 } 0504 0505 void KoStrokeConfigWidget::applyDashStyleChanges() 0506 { 0507 applyChangeToStrokes( 0508 d->canvas, 0509 [this] (KoShapeStrokeSP stroke) { 0510 stroke->setLineStyle(lineStyle(), lineDashes()); 0511 }); 0512 0513 emit sigStrokeChanged(); 0514 } 0515 0516 void KoStrokeConfigWidget::applyLineWidthChanges() 0517 { 0518 applyChangeToStrokes( 0519 d->canvas, 0520 [this] (KoShapeStrokeSP stroke) { 0521 stroke->setLineWidth(lineWidth()); 0522 }); 0523 0524 emit sigStrokeChanged(); 0525 } 0526 0527 void KoStrokeConfigWidget::applyJoinCapChanges() 0528 { 0529 applyChangeToStrokes( 0530 d->canvas, 0531 [this] (KoShapeStrokeSP stroke) { 0532 0533 stroke->setCapStyle(static_cast<Qt::PenCapStyle>(d->capNJoinMenu->capGroup->checkedId())); 0534 stroke->setJoinStyle(static_cast<Qt::PenJoinStyle>(d->capNJoinMenu->joinGroup->checkedId())); 0535 stroke->setMiterLimit(miterLimit()); 0536 }); 0537 0538 emit sigStrokeChanged(); 0539 } 0540 0541 void KoStrokeConfigWidget::applyMarkerChanges(int rawPosition) 0542 { 0543 KoSelection *selection = d->canvas->selectedShapesProxy()->selection(); 0544 if (!selection) { 0545 emit sigStrokeChanged(); 0546 return; 0547 } 0548 0549 QList<KoShape*> shapes = selection->selectedEditableShapes(); 0550 QList<KoPathShape*> pathShapes; 0551 Q_FOREACH (KoShape *shape, shapes) { 0552 KoPathShape *pathShape = dynamic_cast<KoPathShape*>(shape); 0553 if (pathShape) { 0554 pathShapes << pathShape; 0555 } 0556 } 0557 0558 if (pathShapes.isEmpty()) { 0559 emit sigStrokeChanged(); 0560 return; 0561 } 0562 0563 0564 KoFlake::MarkerPosition position = KoFlake::MarkerPosition(rawPosition); 0565 QScopedPointer<KoMarker> marker; 0566 0567 switch (position) { 0568 case KoFlake::StartMarker: 0569 if (d->startMarkerSelector->marker()) { 0570 marker.reset(new KoMarker(*d->startMarkerSelector->marker())); 0571 } 0572 break; 0573 case KoFlake::MidMarker: 0574 if (d->midMarkerSelector->marker()) { 0575 marker.reset(new KoMarker(*d->midMarkerSelector->marker())); 0576 } 0577 break; 0578 case KoFlake::EndMarker: 0579 if (d->endMarkerSelector->marker()) { 0580 marker.reset(new KoMarker(*d->endMarkerSelector->marker())); 0581 } 0582 break; 0583 } 0584 0585 KUndo2Command* command = new KoPathShapeMarkerCommand(pathShapes, marker.take(), position); 0586 d->canvas->addCommand(command); 0587 0588 emit sigStrokeChanged(); 0589 } 0590 0591 // ---------------------------------------------------------------- 0592 0593 struct CheckShapeStrokeStyleBasePolicy 0594 { 0595 typedef KoShapeStrokeSP PointerType; 0596 static PointerType getProperty(KoShape *shape) { 0597 return qSharedPointerDynamicCast<KoShapeStroke>(shape->stroke()); 0598 } 0599 }; 0600 0601 struct CheckShapeStrokeDashesPolicy : public CheckShapeStrokeStyleBasePolicy 0602 { 0603 static bool compareTo(PointerType p1, PointerType p2) { 0604 return p1->lineStyle() == p2->lineStyle() && 0605 p1->lineDashes() == p2->lineDashes() && 0606 p1->dashOffset() == p2->dashOffset(); 0607 } 0608 }; 0609 0610 struct CheckShapeStrokeCapJoinPolicy : public CheckShapeStrokeStyleBasePolicy 0611 { 0612 static bool compareTo(PointerType p1, PointerType p2) { 0613 return p1->capStyle() == p2->capStyle() && 0614 p1->joinStyle() == p2->joinStyle() && 0615 p1->miterLimit() == p2->miterLimit(); 0616 } 0617 }; 0618 0619 struct CheckShapeStrokeWidthPolicy : public CheckShapeStrokeStyleBasePolicy 0620 { 0621 static bool compareTo(PointerType p1, PointerType p2) { 0622 return p1->lineWidth() == p2->lineWidth(); 0623 } 0624 }; 0625 0626 struct CheckShapeMarkerPolicy 0627 { 0628 CheckShapeMarkerPolicy(KoFlake::MarkerPosition position) 0629 : m_position(position) 0630 { 0631 } 0632 0633 typedef KoMarker* PointerType; 0634 PointerType getProperty(KoShape *shape) const { 0635 KoPathShape *pathShape = dynamic_cast<KoPathShape*>(shape); 0636 return pathShape ? pathShape->marker(m_position) : 0; 0637 } 0638 bool compareTo(PointerType p1, PointerType p2) const { 0639 if ((!p1 || !p2) && p1 != p2) return false; 0640 if (!p1 && p1 == p2) return true; 0641 0642 return p1 == p2 || *p1 == *p2; 0643 } 0644 0645 KoFlake::MarkerPosition m_position; 0646 }; 0647 0648 void KoStrokeConfigWidget::selectionChanged() 0649 { 0650 if (d->noSelectionTrackingMode) return; 0651 0652 KoSelection *selection = d->canvas->selectedShapesProxy()->selection(); 0653 if (!selection) return; 0654 0655 // we need to linearize update order, and force the child widget to update 0656 // before we start doing it 0657 0658 QList<KoShape*> shapes = selection->selectedEditableShapes(); 0659 0660 KoShape *shape = !shapes.isEmpty() ? shapes.first() : 0; 0661 0662 const KoShapeStrokeSP stroke = shape ? qSharedPointerDynamicCast<KoShapeStroke>(shape->stroke()) : KoShapeStrokeSP(); 0663 0664 // setUnit uses blockChildSignals() so take care not to use it inside the block 0665 setUnit(d->canvas->unit(), shape); 0666 0667 blockChildSignals(true); 0668 0669 // line width 0670 if (stroke && KoFlake::compareShapePropertiesEqual<CheckShapeStrokeWidthPolicy>(shapes)) { 0671 d->ui->lineWidth->changeValue(stroke->lineWidth()); 0672 } else { 0673 d->ui->lineWidth->changeValue(0); 0674 } 0675 0676 0677 // caps & joins 0678 if (stroke && KoFlake::compareShapePropertiesEqual<CheckShapeStrokeCapJoinPolicy>(shapes)) { 0679 Qt::PenCapStyle capStyle = stroke->capStyle() >= 0 ? stroke->capStyle() : Qt::FlatCap; 0680 Qt::PenJoinStyle joinStyle = stroke->joinStyle() >= 0 ? stroke->joinStyle() : Qt::MiterJoin; 0681 0682 { 0683 QAbstractButton *button = d->capNJoinMenu->capGroup->button(capStyle); 0684 KIS_SAFE_ASSERT_RECOVER_RETURN(button); 0685 button->setChecked(true); 0686 } 0687 0688 { 0689 QAbstractButton *button = d->capNJoinMenu->joinGroup->button(joinStyle); 0690 KIS_SAFE_ASSERT_RECOVER_RETURN(button); 0691 button->setChecked(true); 0692 } 0693 0694 d->capNJoinMenu->miterLimit->changeValue(stroke->miterLimit()); 0695 d->capNJoinMenu->miterLimit->setEnabled(joinStyle == Qt::MiterJoin); 0696 } else { 0697 d->capNJoinMenu->capGroup->button(Qt::FlatCap)->setChecked(true); 0698 d->capNJoinMenu->joinGroup->button(Qt::MiterJoin)->setChecked(true); 0699 d->capNJoinMenu->miterLimit->changeValue(0.0); 0700 d->capNJoinMenu->miterLimit->setEnabled(true); 0701 } 0702 0703 0704 // dashes style 0705 if (stroke && KoFlake::compareShapePropertiesEqual<CheckShapeStrokeDashesPolicy>(shapes)) { 0706 d->ui->lineStyle->setLineStyle(stroke->lineStyle(), stroke->lineDashes()); 0707 } else { 0708 d->ui->lineStyle->setLineStyle(Qt::SolidLine, QVector<qreal>()); 0709 } 0710 0711 // markers 0712 KoPathShape *pathShape = dynamic_cast<KoPathShape *>(shape); 0713 if (pathShape) { 0714 if (KoFlake::compareShapePropertiesEqual(shapes, CheckShapeMarkerPolicy(KoFlake::StartMarker))) { 0715 d->startMarkerSelector->setMarker(pathShape->marker(KoFlake::StartMarker)); 0716 } 0717 if (KoFlake::compareShapePropertiesEqual(shapes, CheckShapeMarkerPolicy(KoFlake::MidMarker))) { 0718 d->midMarkerSelector->setMarker(pathShape->marker(KoFlake::MidMarker)); 0719 } 0720 if (KoFlake::compareShapePropertiesEqual(shapes, CheckShapeMarkerPolicy(KoFlake::EndMarker))) { 0721 d->endMarkerSelector->setMarker(pathShape->marker(KoFlake::EndMarker)); 0722 } 0723 } 0724 0725 const bool lineOptionsVisible = (d->fillConfigWidget->selectedFillIndex() != 0); 0726 0727 // This switch statement is to help the tab widget "pages" to be closer to the correct size 0728 // if we don't do this the internal widgets get rendered, then the tab page has to get resized to 0729 // fill up the space, then the internal widgets have to resize yet again...causing flicker 0730 switch(d->fillConfigWidget->selectedFillIndex()) { 0731 case 0: // no fill 0732 this->setMinimumHeight(130); 0733 break; 0734 case 1: // solid fill 0735 this->setMinimumHeight(200); 0736 break; 0737 case 2: // gradient fill 0738 this->setMinimumHeight(350); 0739 case 3: // pattern fill 0740 break; 0741 } 0742 0743 0744 d->ui->thicknessLineBreak->setVisible(lineOptionsVisible); 0745 d->ui->lineWidth->setVisible(lineOptionsVisible); 0746 d->ui->capNJoinButton->setVisible(lineOptionsVisible); 0747 d->ui->lineStyle->setVisible(lineOptionsVisible); 0748 d->startMarkerSelector->setVisible(lineOptionsVisible); 0749 d->midMarkerSelector->setVisible(lineOptionsVisible); 0750 d->endMarkerSelector->setVisible(lineOptionsVisible); 0751 d->ui->thicknessLabel->setVisible(lineOptionsVisible); 0752 d->ui->strokeStyleLabel->setVisible(lineOptionsVisible); 0753 0754 0755 0756 blockChildSignals(false); 0757 0758 updateStyleControlsAvailability(!shapes.isEmpty()); 0759 0760 } 0761 0762 void KoStrokeConfigWidget::canvasResourceChanged(int key, const QVariant &value) 0763 { 0764 switch (key) { 0765 case KoCanvasResource::Unit: 0766 // we request the whole selection to reload because the 0767 // unit of the stroke width depends on the selected shape 0768 d->selectionChangedCompressor.start(); 0769 break; 0770 case KoCanvasResource::Size: 0771 if (d->noSelectionTrackingMode) { 0772 d->ui->lineWidth->changeValue(d->canvas->unit().fromUserValue(value.toReal())); 0773 } 0774 break; 0775 } 0776 } 0777 0778 void KoStrokeConfigWidget::loadCurrentStrokeFillFromResourceServer() 0779 { 0780 if (d->canvas) { 0781 const QVariant value = d->canvas->resourceManager()->resource(KoCanvasResource::Size); 0782 canvasResourceChanged(KoCanvasResource::Size, value); 0783 0784 updateStyleControlsAvailability(true); 0785 0786 emit sigStrokeChanged(); 0787 } 0788 }