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: 2012 Jean-Nicolas Artaud <jeannicolasartaud@gmail.com>
0004  *
0005  * SPDX-License-Identifier: LGPL-2.0-or-later
0006  */
0007 
0008 #include "KoFillConfigWidget.h"
0009 
0010 #include <QToolButton>
0011 #include <QHBoxLayout>
0012 #include <QVBoxLayout>
0013 #include <QButtonGroup>
0014 #include <QLabel>
0015 #include <QSizePolicy>
0016 #include <QBitmap>
0017 #include <QAction>
0018 #include <QSharedPointer>
0019 #include <QMessageBox>
0020 
0021 #include <klocalizedstring.h>
0022 
0023 #include <KoIcon.h>
0024 #include <KoColor.h>
0025 #include <KoColorPopupAction.h>
0026 #include "KoResourceServerProvider.h"
0027 #include <KoSelection.h>
0028 #include <KoCanvasBase.h>
0029 #include <KoCanvasResourceProvider.h>
0030 #include <KoDocumentResourceManager.h>
0031 #include <KoShape.h>
0032 #include <KoShapeController.h>
0033 #include <KoShapeBackground.h>
0034 #include <KoShapeBackgroundCommand.h>
0035 #include <KoShapeStrokeCommand.h>
0036 #include <KoShapeStroke.h>
0037 #include <KoSelectedShapesProxy.h>
0038 #include <KoColorBackground.h>
0039 #include <KoGradientBackground.h>
0040 #include <KoPatternBackground.h>
0041 #include <KoResourcePopupAction.h>
0042 #include "KoZoomHandler.h"
0043 #include "KoColorPopupButton.h"
0044 #include "ui_KoFillConfigWidget.h"
0045 #include <kis_signals_blocker.h>
0046 #include <kis_signal_compressor_with_param.h>
0047 #include <kis_acyclic_signal_connector.h>
0048 #include <kis_assert.h>
0049 #include "kis_canvas_resource_provider.h"
0050 #include <KoStopGradient.h>
0051 #include <QInputDialog>
0052 #include <KoShapeFillWrapper.h>
0053 #include <functional>
0054 
0055 #include <KisResourceUserOperations.h>
0056 
0057 #include "kis_global.h"
0058 #include "kis_debug.h"
0059 
0060 static const char* const buttonnone[]={
0061     "16 16 3 1",
0062     "# c #000000",
0063     "e c #ff0000",
0064     "- c #ffffff",
0065     "################",
0066     "#--------------#",
0067     "#-e----------e-#",
0068     "#--e--------e--#",
0069     "#---e------e---#",
0070     "#----e----e----#",
0071     "#-----e--e-----#",
0072     "#------ee------#",
0073     "#------ee------#",
0074     "#-----e--e-----#",
0075     "#----e----e----#",
0076     "#---e------e---#",
0077     "#--e--------e--#",
0078     "#-e----------e-#",
0079     "#--------------#",
0080     "################"};
0081 
0082 static const char* const buttonsolid[]={
0083     "16 16 2 1",
0084     "# c #000000",
0085     ". c #969696",
0086     "################",
0087     "#..............#",
0088     "#..............#",
0089     "#..............#",
0090     "#..............#",
0091     "#..............#",
0092     "#..............#",
0093     "#..............#",
0094     "#..............#",
0095     "#..............#",
0096     "#..............#",
0097     "#..............#",
0098     "#..............#",
0099     "#..............#",
0100     "#..............#",
0101     "################"};
0102 
0103 
0104 // FIXME: Smoother gradient button.
0105 
0106 static const char* const buttongradient[]={
0107     "16 16 15 1",
0108     "# c #000000",
0109     "n c #101010",
0110     "m c #202020",
0111     "l c #303030",
0112     "k c #404040",
0113     "j c #505050",
0114     "i c #606060",
0115     "h c #707070",
0116     "g c #808080",
0117     "f c #909090",
0118     "e c #a0a0a0",
0119     "d c #b0b0b0",
0120     "c c #c0c0c0",
0121     "b c #d0d0d0",
0122     "a c #e0e0e0",
0123     "################",
0124     "#abcdefghijklmn#",
0125     "#abcdefghijklmn#",
0126     "#abcdefghijklmn#",
0127     "#abcdefghijklmn#",
0128     "#abcdefghijklmn#",
0129     "#abcdefghijklmn#",
0130     "#abcdefghijklmn#",
0131     "#abcdefghijklmn#",
0132     "#abcdefghijklmn#",
0133     "#abcdefghijklmn#",
0134     "#abcdefghijklmn#",
0135     "#abcdefghijklmn#",
0136     "#abcdefghijklmn#",
0137     "#abcdefghijklmn#",
0138     "################"};
0139 
0140 static const char* const buttonpattern[]={
0141     "16 16 4 1",
0142     ". c #0a0a0a",
0143     "# c #333333",
0144     "a c #a0a0a0",
0145     "b c #ffffffff",
0146     "################",
0147     "#aaaaabbbbaaaaa#",
0148     "#aaaaabbbbaaaaa#",
0149     "#aaaaabbbbaaaaa#",
0150     "#aaaaabbbbaaaaa#",
0151     "#aaaaabbbbaaaaa#",
0152     "#bbbbbaaaabbbbb#",
0153     "#bbbbbaaaabbbbb#",
0154     "#bbbbbaaaabbbbb#",
0155     "#bbbbbaaaabbbbb#",
0156     "#aaaaabbbbaaaaa#",
0157     "#aaaaabbbbaaaaa#",
0158     "#aaaaabbbbaaaaa#",
0159     "#aaaaabbbbaaaaa#",
0160     "#aaaaabbbbaaaaa#",
0161     "################"};
0162 
0163 using namespace std::placeholders;
0164 
0165 
0166 class Q_DECL_HIDDEN KoFillConfigWidget::Private
0167 {
0168 public:
0169     Private(KoFlake::FillVariant _fillVariant, KoFillConfigWidget* q)
0170     : canvas(0),
0171       colorChangedCompressor(100, std::bind(&KoFillConfigWidget::colorChanged, q, _1)),
0172       gradientChangedCompressor(100, KisSignalCompressor::FIRST_ACTIVE),
0173       shapeChangedCompressor(200,KisSignalCompressor::FIRST_ACTIVE),
0174       fillVariant(_fillVariant),
0175       noSelectionTrackingMode(false)
0176     {
0177     }
0178 
0179     KoColorPopupAction *colorAction {nullptr};
0180     KoResourcePopupAction *gradientAction {nullptr};
0181     KoResourcePopupAction *patternAction {nullptr};
0182     QButtonGroup *group {nullptr};
0183 
0184     KoCanvasBase *canvas {nullptr};
0185 
0186     KisSignalCompressorWithParam<std::pair<QColor, KoFlake::FillVariant>> colorChangedCompressor;
0187     KisAcyclicSignalConnector resourceManagerAcyclicConnector;
0188     KoFillConfigWidget::StyleButton selectedFillIndex {KoFillConfigWidget::None};
0189 
0190     KoStopGradientSP activeGradient;
0191     KisSignalCompressor gradientChangedCompressor;
0192     KisSignalCompressor shapeChangedCompressor;
0193     KoFlake::FillVariant fillVariant;
0194 
0195     bool noSelectionTrackingMode {false};
0196 
0197     SvgMeshPosition meshposition;
0198     QScopedPointer<SvgMeshGradient> activeMeshGradient;
0199 
0200     QScopedPointer<Ui_KoFillConfigWidget> ui;
0201 
0202     std::vector<KisAcyclicSignalConnector::Blocker> deactivationLocks;
0203 
0204     std::array<boost::optional<KoColor>, 2> overriddenColorFromProvider;
0205 };
0206 
0207 KoFillConfigWidget::KoFillConfigWidget(KoCanvasBase *canvas, KoFlake::FillVariant fillVariant, bool trackShapeSelection, QWidget *parent)
0208     :  QWidget(parent)
0209     , d(new Private(fillVariant, this))
0210 {
0211     d->canvas = canvas;
0212 
0213     if (trackShapeSelection) {
0214         connect(d->canvas->selectedShapesProxy(), SIGNAL(selectionChanged()), &d->shapeChangedCompressor,
0215                 SLOT(start()));
0216         connect(&d->shapeChangedCompressor, SIGNAL(timeout()), this, SLOT(shapeChanged()));
0217     }
0218 
0219     d->resourceManagerAcyclicConnector.connectBackwardResourcePair(
0220             d->canvas->resourceManager(), SIGNAL(canvasResourceChanged(int,QVariant)),
0221             this, SLOT(slotCanvasResourceChanged(int,QVariant)));
0222 
0223     d->resourceManagerAcyclicConnector.connectForwardVoid(
0224          this, SIGNAL(sigInternalRequestColorToResourceManager()),
0225          this, SLOT(slotProposeCurrentColorToResourceManager()));
0226 
0227     KisAcyclicSignalConnector *resetConnector = d->resourceManagerAcyclicConnector.createCoordinatedConnector();
0228     resetConnector->connectForwardVoid(
0229          this, SIGNAL(sigInternalRecoverColorInResourceManager()),
0230          this, SLOT(slotRecoverColorInResourceManager()));
0231 
0232     // configure GUI
0233 
0234     d->ui.reset(new Ui_KoFillConfigWidget());
0235     d->ui->setupUi(this);
0236 
0237     d->group = new QButtonGroup(this);
0238     d->group->setExclusive(true);
0239 
0240     d->ui->btnNoFill->setIcon(QPixmap((const char **) buttonnone));
0241     d->group->addButton(d->ui->btnNoFill, None);
0242 
0243     d->ui->btnSolidFill->setIcon(QPixmap((const char **) buttonsolid));
0244     d->group->addButton(d->ui->btnSolidFill, Solid);
0245 
0246     d->ui->btnGradientFill->setIcon(QPixmap((const char **) buttongradient));
0247     d->group->addButton(d->ui->btnGradientFill, Gradient);
0248 
0249     d->ui->btnPatternFill->setIcon(QPixmap((const char **) buttonpattern));
0250     d->group->addButton(d->ui->btnPatternFill, Pattern);
0251     d->ui->btnPatternFill->setVisible(false);
0252 
0253     if (fillVariant == KoFlake::Fill) {
0254         // FIXME: different button
0255         d->ui->btnMeshFill->setIcon(QPixmap((const char**) buttonpattern));
0256         d->group->addButton(d->ui->btnMeshFill, MeshGradient);
0257     } else {
0258         d->ui->btnMeshFill->setVisible(false);
0259     }
0260 
0261     d->colorAction = new KoColorPopupAction(d->ui->btnChooseSolidColor);
0262     d->colorAction->setToolTip(i18n("Change the filling color"));
0263     d->colorAction->setCurrentColor(Qt::white);
0264 
0265     d->ui->btnChooseSolidColor->setDefaultAction(d->colorAction);
0266     d->ui->btnChooseSolidColor->setPopupMode(QToolButton::InstantPopup);
0267     d->ui->btnSolidColorSample->setIcon(KisIconUtils::loadIcon("krita_tool_color_sampler"));
0268 
0269     // TODO: for now the color sampling button is disabled!
0270     d->ui->btnSolidColorSample->setEnabled(false);
0271     d->ui->btnSolidColorSample->setVisible(false);
0272 
0273     connect(d->colorAction, &KoColorPopupAction::colorChanged, [this](KoColor color) {
0274         d->colorChangedCompressor.start({color.toQColor(), d->fillVariant});
0275     });
0276 
0277     connect(d->ui->btnChooseSolidColor, SIGNAL(iconSizeChanged()), d->colorAction, SLOT(updateIcon()));
0278 
0279     connect(d->group, SIGNAL(buttonClicked(int)), SLOT(styleButtonPressed(int)));
0280 
0281     connect(d->group, SIGNAL(buttonClicked(int)), SLOT(slotUpdateFillTitle()));
0282 
0283     slotUpdateFillTitle();
0284     styleButtonPressed(d->group->checkedId());
0285 
0286 
0287     // Gradient selector
0288     d->ui->wdgGradientEditor->setCompactMode(true);
0289     d->ui->wdgGradientEditor->setCanvasResourcesInterface(canvas->resourceManager()->canvasResourcesInterface());
0290     connect(d->ui->wdgGradientEditor, SIGNAL(sigGradientChanged()), &d->gradientChangedCompressor, SLOT(start()));
0291     connect(&d->gradientChangedCompressor, SIGNAL(timeout()), SLOT(activeGradientChanged()));
0292 
0293     d->gradientAction = new KoResourcePopupAction(ResourceType::Gradients, canvas->resourceManager()->canvasResourcesInterface(), d->ui->btnChoosePredefinedGradient);
0294 
0295     d->gradientAction->setToolTip(i18n("Change filling gradient"));
0296     d->ui->btnChoosePredefinedGradient->setDefaultAction(d->gradientAction);
0297     d->ui->btnChoosePredefinedGradient->setPopupMode(QToolButton::InstantPopup);
0298 
0299     connect(d->gradientAction, SIGNAL(resourceSelected(QSharedPointer<KoShapeBackground>)),
0300             SLOT(gradientResourceChanged()));
0301     connect(d->ui->btnChoosePredefinedGradient, SIGNAL(iconSizeChanged()), d->gradientAction, SLOT(updateIcon()));
0302 
0303     d->ui->btnSaveGradient->setIcon(KisIconUtils::loadIcon("document-save"));
0304     connect(d->ui->btnSaveGradient, SIGNAL(clicked()), SLOT(slotSavePredefinedGradientClicked()));
0305 
0306     connect(d->ui->cmbGradientRepeat, SIGNAL(currentIndexChanged(int)), SLOT(slotGradientRepeatChanged()));
0307     connect(d->ui->cmbGradientType, SIGNAL(currentIndexChanged(int)), SLOT(slotGradientTypeChanged()));
0308 
0309     // meshgradient
0310     connect(d->ui->meshStopColorButton, SIGNAL(changed(const KoColor&)), this, SLOT(slotMeshHandleColorChanged(const KoColor&)));
0311 
0312     d->ui->spinbRows->setRange(1, 20);
0313     d->ui->spinbColumns->setRange(1, 20);
0314     connect(d->ui->spinbRows, SIGNAL(valueChanged(int)), SLOT(slotMeshGradientChanged()));
0315     connect(d->ui->spinbColumns, SIGNAL(valueChanged(int)), SLOT(slotMeshGradientChanged()));
0316     connect(d->ui->cmbSmoothingType, SIGNAL(currentIndexChanged(int)), SLOT(slotMeshGradientShadingChanged(int)));
0317 
0318     // initialize deactivation locks
0319     d->deactivationLocks.push_back(KisAcyclicSignalConnector::Blocker(d->resourceManagerAcyclicConnector));
0320 
0321 
0322 /*
0323     // Pattern selector
0324     d->patternAction = new KoResourcePopupAction(ResourceType::Patterns, d->colorButton);
0325     d->patternAction->setToolTip(i18n("Change the filling pattern"));
0326     connect(d->patternAction, SIGNAL(resourceSelected(QSharedPointer<KoShapeBackground>)), this, SLOT(patternChanged(QSharedPointer<KoShapeBackground>)));
0327     connect(d->colorButton, SIGNAL(iconSizeChanged()), d->patternAction, SLOT(updateIcon()));
0328 */
0329 
0330 
0331 }
0332 
0333 KoFillConfigWidget::~KoFillConfigWidget()
0334 {
0335     delete d;
0336 }
0337 
0338 void KoFillConfigWidget::activate()
0339 {
0340     KIS_SAFE_ASSERT_RECOVER_NOOP(!d->deactivationLocks.empty());
0341     d->deactivationLocks.clear();
0342 
0343     if (!d->noSelectionTrackingMode) {
0344         d->shapeChangedCompressor.start();
0345     } else {
0346         loadCurrentFillFromResourceServer();
0347     }
0348 
0349     updateWidgetComponentVisibility();
0350 }
0351 
0352 void KoFillConfigWidget::deactivate()
0353 {
0354     emit sigInternalRecoverColorInResourceManager();
0355 
0356     KIS_SAFE_ASSERT_RECOVER_NOOP(d->deactivationLocks.empty());
0357     d->deactivationLocks.push_back(KisAcyclicSignalConnector::Blocker(d->resourceManagerAcyclicConnector));
0358 }
0359 
0360 void KoFillConfigWidget::forceUpdateOnSelectionChanged()
0361 {
0362     d->shapeChangedCompressor.start();
0363 }
0364 
0365 void KoFillConfigWidget::setSelectedMeshGradientHandle(const SvgMeshPosition &position)
0366 {
0367     d->meshposition = position;
0368     updateMeshGradientUI();
0369 }
0370 
0371 void KoFillConfigWidget::setNoSelectionTrackingMode(bool value)
0372 {
0373     d->noSelectionTrackingMode = value;
0374     if (!d->noSelectionTrackingMode) {
0375         d->shapeChangedCompressor.start();
0376     }
0377 }
0378 
0379 void KoFillConfigWidget::slotUpdateFillTitle()
0380 {
0381     QString text = d->group->checkedButton() ? d->group->checkedButton()->text() : QString();
0382     text.replace('&', QString());
0383     d->ui->lblFillTitle->setText(text);
0384 }
0385 
0386 void KoFillConfigWidget::slotCanvasResourceChanged(int key, const QVariant &value)
0387 {
0388     if (key == KoCanvasResource::ForegroundColor
0389         || key == KoCanvasResource::BackgroundColor) {
0390 
0391         KoColor color = value.value<KoColor>();
0392 
0393         const int checkedId = d->group->checkedId();
0394 
0395         if (checkedId < 0 || checkedId == None || checkedId == Solid) {
0396 
0397             d->group->button(Solid)->setChecked(true);
0398             d->selectedFillIndex = Solid;
0399 
0400             KoFlake::FillVariant colorSlot = (key == KoCanvasResource::ForegroundColor)
0401                                                ? KoFlake::Fill
0402                                                : KoFlake::StrokeFill;
0403 
0404             if (key == d->fillVariant) {
0405                 d->colorAction->setCurrentColor(color);
0406             }
0407             colorChanged({color.toQColor(), colorSlot});
0408         } else if (checkedId == Gradient && key == KoCanvasResource::ForegroundColor) {
0409             d->ui->wdgGradientEditor->notifyGlobalColorChanged(color);
0410         }
0411     } else if (key == KoCanvasResource::CurrentGradient) {
0412         KoResourceSP gradient = value.value<KoAbstractGradientSP>();
0413         const int checkedId = d->group->checkedId();
0414 
0415         if (gradient && (checkedId < 0 || checkedId == None || checkedId == Gradient)) {
0416             d->group->button(Gradient)->setChecked(true);
0417             d->gradientAction->setCurrentResource(gradient);
0418         }
0419     }
0420 }
0421 
0422 QList<KoShape*> KoFillConfigWidget::currentShapes()
0423 {
0424     return d->canvas->selectedShapesProxy()->selection()->selectedEditableShapes();
0425 }
0426 
0427 int KoFillConfigWidget::selectedFillIndex() {
0428     return d->selectedFillIndex;
0429 }
0430 
0431 void KoFillConfigWidget::styleButtonPressed(int buttonId)
0432 {
0433     QList<KoShape*> shapes = currentShapes();
0434 
0435     switch (buttonId) {
0436         case KoFillConfigWidget::None:
0437             noColorSelected();
0438             break;
0439         case KoFillConfigWidget::Solid:
0440             colorChanged({d->colorAction->currentColor(), d->fillVariant});
0441             break;
0442         case KoFillConfigWidget::Gradient:
0443             if (d->activeGradient) {
0444                 setNewGradientBackgroundToShape();
0445                 updateGradientSaveButtonAvailability();
0446             } else {
0447                 gradientResourceChanged();
0448             }
0449             break;
0450         case KoFillConfigWidget::Pattern:
0451             // Only select mode in the widget, don't set actual pattern :/
0452             //d->colorButton->setDefaultAction(d->patternAction);
0453             //patternChanged(d->patternAction->currentBackground());
0454             break;
0455         case KoFillConfigWidget::MeshGradient:
0456             if (d->activeMeshGradient) {
0457                 setNewMeshGradientBackgroundToShape();
0458             } else {
0459                 createNewMeshGradientBackground();
0460                 setNewMeshGradientBackgroundToShape();
0461             }
0462             break;
0463     }
0464 
0465 
0466     // update tool option fields with first selected object
0467     if (shapes.isEmpty() == false) {
0468         KoShape *firstShape = shapes.first();
0469         updateUiFromFillType(firstShape);
0470     }
0471 
0472     updateWidgetComponentVisibility();
0473 }
0474 
0475 KoShapeStrokeSP KoFillConfigWidget::createShapeStroke()
0476 {
0477     KoShapeStrokeSP stroke(new KoShapeStroke());
0478     KIS_ASSERT_RECOVER_RETURN_VALUE(d->fillVariant == KoFlake::StrokeFill, stroke);
0479 
0480     switch (d->group->checkedId()) {
0481     case KoFillConfigWidget::None:
0482         stroke->setColor(Qt::transparent);
0483         break;
0484     case KoFillConfigWidget::Solid:
0485         stroke->setColor(d->colorAction->currentColor());
0486         break;
0487     case KoFillConfigWidget::Gradient: {
0488         QScopedPointer<QGradient> g(d->activeGradient->toQGradient());
0489         QBrush newBrush = *g;
0490         stroke->setLineBrush(newBrush);
0491         stroke->setColor(Qt::transparent);
0492         break;
0493     }
0494     case KoFillConfigWidget::Pattern:
0495         break;
0496     }
0497 
0498     return stroke;
0499 }
0500 
0501 void KoFillConfigWidget::noColorSelected()
0502 {
0503     QList<KoShape*> selectedShapes = currentShapes();
0504     if (selectedShapes.isEmpty()) {
0505         emit sigFillChanged();
0506         return;
0507     }
0508 
0509     KoShapeFillWrapper wrapper(selectedShapes, d->fillVariant);
0510     KUndo2Command *command = wrapper.setColor(QColor());
0511 
0512     if (command) {
0513         d->canvas->addCommand(command);
0514     }
0515 
0516     emit sigFillChanged();
0517 }
0518 
0519 void KoFillConfigWidget::colorChanged(std::pair<QColor, KoFlake::FillVariant> resource)
0520 {
0521     QColor color = resource.first;
0522     KoFlake::FillVariant fillVariant = resource.second;
0523     if (!color.isValid()) {
0524         return;
0525     }
0526 
0527     QList<KoShape*> selectedShapes = currentShapes();
0528     if (selectedShapes.isEmpty()) {
0529         emit sigInternalRequestColorToResourceManager();
0530         emit sigFillChanged();
0531         return;
0532     }
0533 
0534     d->overriddenColorFromProvider[fillVariant] = boost::none;
0535 
0536     KoShapeFillWrapper wrapper(selectedShapes, fillVariant);
0537 
0538     KUndo2Command *command = wrapper.setColor(color);
0539     if (command) {
0540         d->canvas->addCommand(command);
0541     }
0542 
0543     // only returns true if it is a stroke object that has a 0 for line width
0544     if (wrapper.hasZeroLineWidth() ) {
0545          KUndo2Command *lineCommand = wrapper.setLineWidth(1.0);
0546          if (lineCommand) {
0547              d->canvas->addCommand(lineCommand);
0548          }
0549 
0550          // * line to test out
0551          QColor solidColor = d->colorAction->currentColor();
0552          solidColor.setAlpha(255);
0553          command = wrapper.setColor(solidColor);
0554          if (command) {
0555              d->canvas->addCommand(command);
0556          }
0557 
0558     }
0559 
0560     emit sigFillChanged();
0561     emit sigInternalRequestColorToResourceManager();
0562 }
0563 
0564 void KoFillConfigWidget::slotProposeCurrentColorToResourceManager()
0565 {
0566     const int checkedId = d->group->checkedId();
0567 
0568     auto uploadColorToResourceManager = [this](KoCanvasResource::CanvasResourceId res,
0569                               KoFlake::FillVariant var, KoColor &color) {
0570         if (!d->overriddenColorFromProvider[var]) {
0571             d->overriddenColorFromProvider[var] =
0572                 d->canvas->resourceManager()->resource(res).value<KoColor>();
0573         }
0574 
0575         /**
0576          * Don't let opacity leak to our resource manager system
0577          *
0578          * NOTE: theoretically, we could guarantee it on a level of the
0579          * resource manager itself,
0580          */
0581         color.setOpacity(OPACITY_OPAQUE_U8);
0582         d->canvas->resourceManager()->setResource(res, QVariant::fromValue(color));
0583     };
0584 
0585     auto uploadColorFromShape = [&](KoCanvasResource::CanvasResourceId res, KoFlake::FillVariant fill) {
0586         KoShapeFillWrapper wrapper(currentShapes(), fill);
0587         if (!wrapper.color().isValid()) {
0588             return;
0589         }
0590         KoColor color;
0591         color.fromQColor(wrapper.color());
0592         uploadColorToResourceManager(res, fill, color);
0593     };
0594 
0595     if (checkedId == Solid) {
0596         if (currentShapes().isEmpty()) {
0597             KoCanvasResource::CanvasResourceId res =
0598                 (d->fillVariant == KoFlake::Fill) ? KoCanvasResource::ForegroundColor
0599                                                   : KoCanvasResource::BackgroundColor;
0600             KoColor color = d->colorAction->currentKoColor();
0601             uploadColorToResourceManager(res, d->fillVariant, color);
0602         } else {
0603             uploadColorFromShape(KoCanvasResource::BackgroundColor, KoFlake::StrokeFill);
0604             uploadColorFromShape(KoCanvasResource::ForegroundColor, KoFlake::Fill);
0605         }
0606     } else if (checkedId == Gradient) {
0607         if (boost::optional<KoColor> gradientColor =
0608                 d->ui->wdgGradientEditor->currentActiveStopColor()) {
0609             KoColor color = *gradientColor;
0610             KoCanvasResource::CanvasResourceId res =
0611                 (d->fillVariant == KoFlake::Fill) ? KoCanvasResource::ForegroundColor
0612                                                   : KoCanvasResource::BackgroundColor;
0613             uploadColorToResourceManager(res, d->fillVariant, color);
0614         }
0615     }
0616 }
0617 
0618 void KoFillConfigWidget::slotRecoverColorInResourceManager()
0619 {
0620     auto checkAndRecover = [this](KoCanvasResource::CanvasResourceId res,
0621                                KoFlake::FillVariant var) {
0622         if (d->overriddenColorFromProvider[var]) {
0623             d->canvas->resourceManager()->setResource(
0624                 res, QVariant::fromValue(*d->overriddenColorFromProvider[var]));
0625             d->overriddenColorFromProvider[var] = boost::none;
0626         }
0627     };
0628 
0629     checkAndRecover(KoCanvasResource::BackgroundColor, KoFlake::StrokeFill);
0630     checkAndRecover(KoCanvasResource::ForegroundColor, KoFlake::Fill);
0631 }
0632 
0633 void KoFillConfigWidget::slotSavePredefinedGradientClicked()
0634 {
0635     KoResourceServerProvider *serverProvider = KoResourceServerProvider::instance();
0636     auto server = serverProvider->gradientServer();
0637 
0638     const QString defaultGradientNamePrefix = i18nc("default prefix for the saved gradient", "gradient");
0639     const QString saveLocation = server->saveLocation();
0640 
0641     QString name = d->activeGradient->name().isEmpty() ? defaultGradientNamePrefix : d->activeGradient->name();
0642     QFileInfo fileInfo(saveLocation + name.split(" ").join("_") + d->activeGradient->defaultFileExtension());
0643     bool fileOverWriteAccepted = false;
0644 
0645     while(!fileOverWriteAccepted) {
0646         name = QInputDialog::getText(this,
0647                                      i18nc("@title:window", "Save Gradient"),
0648                                      i18n("Enter gradient name:"),
0649                                      QLineEdit::Normal, name);
0650         if (name.isNull() || name.isEmpty()) {
0651             return;
0652         } else {
0653             fileInfo = QFileInfo(saveLocation + name.split(" ").join("_") + d->activeGradient->defaultFileExtension());
0654             if (fileInfo.exists()) {
0655                 int res = QMessageBox::warning(this, i18nc("@title:window", "Name Already Exists")
0656                                                             , i18n("The name '%1' already exists, do you wish to overwrite it?", name)
0657                                                             , QMessageBox::Yes | QMessageBox::No);
0658                 if (res == QMessageBox::Yes) fileOverWriteAccepted = true;
0659             } else {
0660                 fileOverWriteAccepted = true;
0661             }
0662         }
0663     }
0664 
0665     d->activeGradient->setName(name);
0666     d->activeGradient->setFilename(name.split(" ").join("_") + d->activeGradient->defaultFileExtension());
0667 
0668     KoAbstractGradientSP newGradient = d->activeGradient->clone().dynamicCast<KoAbstractGradient>();
0669 
0670     KisResourceUserOperations::addResourceWithUserInput(this, newGradient);
0671 
0672     d->gradientAction->setCurrentResource(newGradient);
0673 }
0674 
0675 void KoFillConfigWidget::activeGradientChanged()
0676 {
0677     setNewGradientBackgroundToShape();
0678     updateGradientSaveButtonAvailability();
0679 
0680     emit sigInternalRequestColorToResourceManager();
0681 }
0682 
0683 void KoFillConfigWidget::gradientResourceChanged()
0684 {
0685     QSharedPointer<KoGradientBackground> bg =
0686         qSharedPointerDynamicCast<KoGradientBackground>(
0687             d->gradientAction->currentBackground());
0688 
0689     updateGradientUi(bg->gradient());
0690 
0691     setNewGradientBackgroundToShape();
0692     updateGradientSaveButtonAvailability();
0693 }
0694 
0695 void KoFillConfigWidget::slotGradientTypeChanged()
0696 {
0697     QGradient::Type type =
0698         d->ui->cmbGradientType->currentIndex() == 0 ?
0699             QGradient::LinearGradient : QGradient::RadialGradient;
0700 
0701     d->activeGradient->setType(type);
0702     activeGradientChanged();
0703 }
0704 
0705 void KoFillConfigWidget::slotGradientRepeatChanged()
0706 {
0707     QGradient::Spread spread =
0708         QGradient::Spread(d->ui->cmbGradientRepeat->currentIndex());
0709 
0710     d->activeGradient->setSpread(spread);
0711     activeGradientChanged();
0712 }
0713 
0714 void KoFillConfigWidget::updateGradientUi(const QGradient *gradient)
0715 {
0716     KisSignalsBlocker b1(d->ui->wdgGradientEditor,
0717                          d->ui->cmbGradientType,
0718                          d->ui->cmbGradientRepeat);
0719 
0720     d->ui->wdgGradientEditor->setGradient(0);
0721 
0722     d->activeGradient = KoStopGradient::fromQGradient(gradient);
0723 
0724     d->ui->wdgGradientEditor->setGradient(d->activeGradient);
0725     d->ui->cmbGradientType->setCurrentIndex(d->activeGradient->type() != QGradient::LinearGradient);
0726     d->ui->cmbGradientRepeat->setCurrentIndex(int(d->activeGradient->spread()));
0727 }
0728 
0729 void KoFillConfigWidget::setNewGradientBackgroundToShape()
0730 {
0731     QList<KoShape*> selectedShapes = currentShapes();
0732     if (selectedShapes.isEmpty()) {
0733         emit sigFillChanged();
0734         return;
0735     }
0736 
0737     KoShapeFillWrapper wrapper(selectedShapes, d->fillVariant);
0738     QScopedPointer<QGradient> srcQGradient(d->activeGradient->toQGradient());
0739     KUndo2Command *command = wrapper.applyGradientStopsOnly(srcQGradient.data());
0740 
0741     if (command) {
0742         d->canvas->addCommand(command);
0743     }
0744 
0745     emit sigFillChanged();
0746 }
0747 
0748 void KoFillConfigWidget::updateGradientSaveButtonAvailability()
0749 {
0750     bool savingEnabled = false;
0751 
0752     QScopedPointer<QGradient> currentGradient(d->activeGradient->toQGradient());
0753     QSharedPointer<KoShapeBackground> bg = d->gradientAction->currentBackground();
0754     if (bg) {
0755         QSharedPointer<KoGradientBackground> resourceBackground =
0756             qSharedPointerDynamicCast<KoGradientBackground>(bg);
0757 
0758         savingEnabled = resourceBackground->gradient()->stops() != currentGradient->stops();
0759         savingEnabled |= resourceBackground->gradient()->type() != currentGradient->type();
0760         savingEnabled |= resourceBackground->gradient()->spread() != currentGradient->spread();
0761     }
0762 
0763     d->ui->btnSaveGradient->setEnabled(savingEnabled);
0764 }
0765 
0766 void KoFillConfigWidget::patternChanged(QSharedPointer<KoShapeBackground>  background)
0767 {
0768     Q_UNUSED(background);
0769 
0770 #if 0
0771     QSharedPointer<KoPatternBackground> patternBackground = qSharedPointerDynamicCast<KoPatternBackground>(background);
0772     if (! patternBackground) {
0773         return;
0774     }
0775 
0776     QList<KoShape*> selectedShapes = currentShapes();
0777     if (selectedShapes.isEmpty()) {
0778         return;
0779     }
0780 
0781     QSharedPointer<KoPatternBackground> fill(new KoPatternBackground());
0782     fill->setPattern(patternBackground->pattern());
0783     d->canvas->addCommand(new KoShapeBackgroundCommand(selectedShapes, fill));
0784 
0785 #endif
0786 }
0787 
0788 void KoFillConfigWidget::slotMeshGradientChanged()
0789 {
0790     createNewDefaultMeshGradientBackground();
0791     setNewMeshGradientBackgroundToShape();
0792     d->meshposition = SvgMeshPosition();
0793     emit sigMeshGradientResetted();
0794 }
0795 
0796 void KoFillConfigWidget::slotMeshGradientShadingChanged(int index)
0797 {
0798     d->activeMeshGradient->setType(static_cast<SvgMeshGradient::Shading>(index));
0799     setNewMeshGradientBackgroundToShape();
0800 }
0801 
0802 void KoFillConfigWidget::slotMeshHandleColorChanged(const KoColor &c)
0803 {
0804     QList<KoShape*> selectedShapes = currentShapes();
0805     KIS_SAFE_ASSERT_RECOVER_RETURN(!selectedShapes.isEmpty());
0806 
0807     KoShapeFillWrapper wrapper(selectedShapes, d->fillVariant);
0808     const SvgMeshGradient *gradient = wrapper.meshgradient();
0809 
0810     // if we changed the handle, the gradient *has* to exist
0811     KIS_SAFE_ASSERT_RECOVER_RETURN(gradient);
0812 
0813     if (d->meshposition.isValid()) {
0814         // We don't have any signals firing when we change the structure (i.e position of stops etc) of a
0815         // meshgradient. So, activeMeshGradient isn't updated when this happens. Hence we update it here and
0816         // then modify the color.
0817         d->activeMeshGradient.reset(new SvgMeshGradient(*gradient));
0818 
0819         d->activeMeshGradient->getMeshArray()->modifyColor(d->meshposition, c.toQColor());
0820         setNewMeshGradientBackgroundToShape();
0821     }
0822     return;
0823 }
0824 
0825 void KoFillConfigWidget::loadCurrentFillFromResourceServer()
0826 {
0827     {
0828         KoColor color = d->canvas->resourceManager()->foregroundColor();
0829         if (d->group->checkedId() == -1 || d->group->checkedId() == None) {
0830             d->group->button(Solid)->setChecked(true);
0831         }
0832         d->selectedFillIndex = Solid;
0833         d->colorAction->setCurrentColor(color);
0834     }
0835 
0836     Q_FOREACH (QAbstractButton *button, d->group->buttons()) {
0837         button->setEnabled(true);
0838     }
0839 
0840     emit sigFillChanged();
0841 }
0842 
0843 void KoFillConfigWidget::createNewMeshGradientBackground()
0844 {
0845     QList<KoShape*> selectedShapes = currentShapes();
0846     if (selectedShapes.isEmpty()) {
0847         return;
0848     }
0849 
0850     KoShapeFillWrapper wrapper(selectedShapes, d->fillVariant);
0851     const SvgMeshGradient *g = wrapper.meshgradient();
0852     if (g) {
0853         d->activeMeshGradient.reset(new SvgMeshGradient(*g));
0854     } else {
0855         createNewDefaultMeshGradientBackground();
0856     }
0857 
0858     updateMeshGradientUI();
0859 }
0860 
0861 void KoFillConfigWidget::createNewDefaultMeshGradientBackground()
0862 {
0863     QList<KoShape*> selectedShapes = currentShapes();
0864     if (selectedShapes.isEmpty()) {
0865         return;
0866     }
0867 
0868     // use this for mesh creation
0869     QSizeF maxSize;
0870     for (const auto& shape: selectedShapes) {
0871         QSizeF size = shape->boundingRect().size();
0872         if (size.height() > maxSize.height()) {
0873             maxSize.rheight() = size.height();
0874         }
0875         if (size.width() > maxSize.width()) {
0876             maxSize.rwidth() = size.width();
0877         }
0878     }
0879 
0880     SvgMeshGradient *gradient = new SvgMeshGradient;
0881 
0882     QColor color =  d->canvas->resourceManager()->resource(KoFlake::Background).value<KoColor>().toQColor();
0883 
0884     int nrows = d->ui->spinbRows->value();
0885     int ncols = d->ui->spinbColumns->value();
0886 
0887     if (d->ui->cmbSmoothingType->currentIndex()) {
0888         gradient->setType(SvgMeshGradient::BICUBIC);
0889     } else {
0890         gradient->setType(SvgMeshGradient::BILINEAR);
0891     }
0892 
0893     gradient->getMeshArray()->createDefaultMesh(nrows, ncols, color, maxSize);
0894     gradient->setGradientUnits(KoFlake::ObjectBoundingBox);
0895     d->activeMeshGradient.reset(gradient);
0896 }
0897 
0898 void KoFillConfigWidget::setNewMeshGradientBackgroundToShape()
0899 {
0900     QList<KoShape*> selectedShapes = currentShapes();
0901     // if called by "manager"
0902     if (selectedShapes.isEmpty()) {
0903         emit sigFillChanged();
0904         return;
0905     }
0906 
0907     KoShapeFillWrapper wrapper(selectedShapes, d->fillVariant);
0908 
0909     KUndo2Command *command = wrapper.setMeshGradient(d->activeMeshGradient.data(), QTransform());
0910     if (command) {
0911         d->canvas->addCommand(command);
0912     }
0913 
0914     emit sigFillChanged();
0915 }
0916 
0917 void KoFillConfigWidget::updateMeshGradientUI()
0918 {
0919     if (!d->activeMeshGradient) return;
0920 
0921     KisSignalsBlocker b(d->ui->spinbRows,
0922                         d->ui->spinbColumns,
0923                         d->ui->cmbSmoothingType,
0924                         d->ui->meshStopColorButton);
0925 
0926     SvgMeshArray *mesharray = d->activeMeshGradient->getMeshArray().data();
0927     d->ui->spinbRows->setValue(mesharray->numRows());
0928     d->ui->spinbColumns->setValue(mesharray->numColumns());
0929     d->ui->cmbSmoothingType->setCurrentIndex(d->activeMeshGradient->type());
0930     if (d->meshposition.isValid()) {
0931         QColor qc = d->activeMeshGradient->getMeshArray()->getStop(d->meshposition).color;
0932 
0933         KoColor c = d->ui->meshStopColorButton->color();
0934         c.fromQColor(qc);
0935 
0936         d->ui->meshStopColorButton->setColor(c);
0937         d->ui->meshStopColorButton->setDisabled(false);
0938     } else {
0939         d->ui->meshStopColorButton->setDisabled(true);
0940     }
0941 }
0942 
0943 void KoFillConfigWidget::shapeChanged()
0944 {
0945     if (d->noSelectionTrackingMode) return;
0946 
0947     QList<KoShape*> shapes = currentShapes();
0948 
0949     bool shouldUploadColorToResourceManager = false;
0950 
0951     // Disable the buttons if there aren't any selected shapes or we have a several shapes with different
0952     // gradient backgrounds.
0953     if (shapes.isEmpty() ||
0954         (shapes.size() > 1 && KoShapeFillWrapper(shapes, d->fillVariant).isMixedFill())) {
0955 
0956         Q_FOREACH (QAbstractButton *button, d->group->buttons()) {
0957             button->setEnabled(!shapes.isEmpty());
0958         }
0959     } else {
0960         // only one vector object selected
0961         Q_FOREACH (QAbstractButton *button, d->group->buttons()) {
0962             button->setEnabled(true);
0963         }
0964 
0965         // update active index of shape
0966         KoShape *shape = shapes.first();
0967         updateUiFromFillType(shape); // updates tool options fields
0968 
0969         shouldUploadColorToResourceManager = true;
0970     }
0971 
0972     // updates the UI
0973     d->group->button(d->selectedFillIndex)->setChecked(true);
0974 
0975     updateWidgetComponentVisibility();
0976     slotUpdateFillTitle();
0977 
0978     if (shouldUploadColorToResourceManager) {
0979         emit sigInternalRequestColorToResourceManager();
0980     } else {
0981         emit sigInternalRecoverColorInResourceManager();
0982     }
0983 }
0984 
0985 void KoFillConfigWidget::updateUiFromFillType(KoShape *shape)
0986 {
0987     KIS_SAFE_ASSERT_RECOVER_RETURN(shape);
0988     KoShapeFillWrapper wrapper(shape, d->fillVariant);
0989 
0990     switch (wrapper.type()) {
0991         case KoFlake::None:
0992              d->selectedFillIndex = KoFillConfigWidget::None;
0993             break;
0994         case KoFlake::Solid: {
0995             d->selectedFillIndex = KoFillConfigWidget::Solid;
0996             QColor color = wrapper.color();
0997             if (color.alpha() > 0) {
0998                 d->colorAction->setCurrentColor(wrapper.color());
0999             }
1000             break;
1001         }
1002         case KoFlake::Gradient:
1003             d->selectedFillIndex = KoFillConfigWidget::Gradient;
1004             updateGradientUi(wrapper.gradient());
1005             updateGradientSaveButtonAvailability();
1006             break;
1007         case KoFlake::Pattern:
1008             d->selectedFillIndex = KoFillConfigWidget::Pattern;
1009             break;
1010         case KoFlake::MeshGradient:
1011             d->selectedFillIndex = KoFillConfigWidget::MeshGradient;
1012             createNewMeshGradientBackground();
1013             break;
1014     }
1015 }
1016 
1017 
1018 void KoFillConfigWidget::updateWidgetComponentVisibility()
1019 {
1020     // The UI is showing/hiding things like this because the 'stacked widget' isn't very flexible
1021     // and makes it difficult to put anything underneath it without a lot empty space
1022 
1023     // hide everything first
1024     d->ui->wdgGradientEditor->setVisible(false);
1025     d->ui->btnChoosePredefinedGradient->setVisible(false);
1026     d->ui->btnChooseSolidColor->setVisible(false);
1027     d->ui->typeLabel->setVisible(false);
1028     d->ui->repeatLabel->setVisible(false);
1029     d->ui->cmbGradientRepeat->setVisible(false);
1030     d->ui->cmbGradientType->setVisible(false);
1031     d->ui->btnSolidColorSample->setVisible(false);
1032     d->ui->btnSaveGradient->setVisible(false);
1033     d->ui->gradientTypeLine->setVisible(false);
1034     d->ui->soldStrokeColorLabel->setVisible(false);
1035     d->ui->presetLabel->setVisible(false);
1036     d->ui->stopColorLabel->setVisible(false);
1037     d->ui->meshStopColorButton->setVisible(false);
1038     d->ui->rowsLabel->setVisible(false);
1039     d->ui->spinbRows->setVisible(false);
1040     d->ui->columnsLabel->setVisible(false);
1041     d->ui->spinbColumns->setVisible(false);
1042     d->ui->smoothingTypeLabel->setVisible(false);
1043     d->ui->cmbSmoothingType->setVisible(false);
1044 
1045     // keep options hidden if no vector shapes are selected
1046     if(currentShapes().isEmpty()) {
1047         return;
1048     }
1049 
1050 
1051     switch (d->selectedFillIndex) {
1052         case KoFillConfigWidget::None:
1053             break;
1054         case KoFillConfigWidget::Solid:
1055             d->ui->btnChooseSolidColor->setVisible(true);
1056             d->ui->btnSolidColorSample->setVisible(false);
1057             d->ui->soldStrokeColorLabel->setVisible(true);
1058             break;
1059         case KoFillConfigWidget::Gradient:
1060             d->ui->wdgGradientEditor->setVisible(true);
1061             d->ui->btnChoosePredefinedGradient->setVisible(true);
1062             d->ui->typeLabel->setVisible(true);
1063             d->ui->repeatLabel->setVisible(true);
1064             d->ui->cmbGradientRepeat->setVisible(true);
1065             d->ui->cmbGradientType->setVisible(true);
1066             d->ui->btnSaveGradient->setVisible(true);
1067             d->ui->gradientTypeLine->setVisible(true);
1068             d->ui->presetLabel->setVisible(true);
1069             break;
1070         case KoFillConfigWidget::Pattern:
1071             break;
1072         case KoFillConfigWidget::MeshGradient:
1073             d->ui->stopColorLabel->setVisible(true);
1074             d->ui->meshStopColorButton->setVisible(true);
1075             d->ui->rowsLabel->setVisible(true);
1076             d->ui->spinbRows->setVisible(true);
1077             d->ui->columnsLabel->setVisible(true);
1078             d->ui->spinbColumns->setVisible(true);
1079             d->ui->smoothingTypeLabel->setVisible(true);
1080             d->ui->cmbSmoothingType->setVisible(true);
1081             d->ui->meshStopColorButton->setAlphaChannelEnabled(true);
1082             break;
1083     }
1084 
1085 }