File indexing completed on 2024-06-16 04:18:01

0001 /*
0002  *  SPDX-FileCopyrightText: 2013 Dmitry Kazakov <dimula73@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "kis_tool_transform_config_widget.h"
0008 
0009 #include <kis_icon.h>
0010 #include "rotation_icons.h"
0011 #include "kis_canvas2.h"
0012 #include <KisSignalMapper.h>
0013 #include "kis_liquify_properties.h"
0014 
0015 #include "KisMainWindow.h"
0016 #include "KisViewManager.h"
0017 #include "kis_transform_utils.h"
0018 #include "kis_config_notifier.h"
0019 #include <kstandardguiitem.h>
0020 
0021 
0022 template<typename T> inline T sign(T x) {
0023     return x > 0 ? 1 : x == (T)0 ? 0 : -1;
0024 }
0025 
0026 const int KisToolTransformConfigWidget::DEFAULT_POINTS_PER_LINE = 3;
0027 
0028 
0029 KisToolTransformConfigWidget::KisToolTransformConfigWidget(TransformTransactionProperties *transaction, KisCanvas2 *canvas, QWidget *parent)
0030     : QWidget(parent),
0031       m_transaction(transaction),
0032       m_notificationsBlocked(0),
0033       m_uiSlotsBlocked(0),
0034       m_configChanged(false)
0035 {
0036     setupUi(this);
0037 
0038     KConfigGroup group = KSharedConfig::openConfig()->group("KisToolTransform");
0039     bool useInStackPreview = !group.readEntry("useOverlayPreviewStyle", false);
0040     bool forceLodMode = group.readEntry("forceLodMode", true);
0041 
0042     if (useInStackPreview && !forceLodMode) {
0043        cmbPreviewMode->setCurrentIndex(0);
0044     }
0045     else if (useInStackPreview && forceLodMode) {
0046         cmbPreviewMode->setCurrentIndex(1);
0047     }
0048     else {
0049         cmbPreviewMode->setCurrentIndex(2);
0050     }
0051 
0052     connect(cmbPreviewMode, SIGNAL(currentIndexChanged(int)), this, SLOT(slotPreviewChanged(int)));
0053 
0054     flipXButton->setIcon(KisIconUtils::loadIcon("transform_icons_mirror_x"));
0055     flipYButton->setIcon(KisIconUtils::loadIcon("transform_icons_mirror_y"));
0056     rotateCWButton->setIcon(KisIconUtils::loadIcon("transform_icons_rotate_cw"));
0057     rotateCCWButton->setIcon(KisIconUtils::loadIcon("transform_icons_rotate_ccw"));
0058 
0059     // Granularity can only be specified in the power of 2's
0060     QStringList granularityValues{"4","8","16","32"};
0061     changeGranularity->addItems(granularityValues);
0062     changeGranularity->setCurrentIndex(1);
0063     granularityPreview->addItems(granularityValues);
0064     granularityPreview->setCurrentIndex(2);
0065 
0066     connect(changeGranularity,SIGNAL(currentIndexChanged(QString)),
0067             this,SLOT(slotGranularityChanged(QString)));
0068     connect(granularityPreview, SIGNAL(currentIndexChanged(QString)),
0069             this,SLOT(slotPreviewGranularityChanged(QString)));
0070 
0071     // Init Filter  combo
0072     cmbFilter->setIDList(KisFilterStrategyRegistry::instance()->listKeys());
0073     cmbFilter->setCurrent("Bicubic");
0074     cmbFilter->setToolTip(i18nc("@info:tooltip",
0075                                 "<p>Select filtering mode:\n"
0076                                 "<ul>"
0077                                 "<li><b>Nearest neighbor</b> for pixel art. Does not produce new color.</li>"
0078                                 "<li><b>Bilinear</b> for areas with uniform color to avoid artifacts.</li>"
0079                                 "<li><b>Bicubic</b> for smoother results.</li>"
0080                                 "<li><b>Lanczos3</b> for sharp results. May produce aerials.</li>"
0081                                 "</ul></p>"));
0082     connect(cmbFilter, SIGNAL(activated(KoID)),
0083             this, SLOT(slotFilterChanged(KoID)));
0084 
0085     // Init Warp Type combo
0086     cmbWarpType->insertItem(KisWarpTransformWorker::AFFINE_TRANSFORM,i18n("Default (Affine)"));
0087     cmbWarpType->insertItem(KisWarpTransformWorker::RIGID_TRANSFORM,i18n("Strong (Rigid)"));
0088     cmbWarpType->insertItem(KisWarpTransformWorker::SIMILITUDE_TRANSFORM,i18n("Strongest (Similitude)"));
0089     cmbWarpType->setCurrentIndex(KisWarpTransformWorker::AFFINE_TRANSFORM);
0090     connect(cmbWarpType, SIGNAL(currentIndexChanged(int)), this, SLOT(slotWarpTypeChanged(int)));
0091 
0092     // Init Rotation Center buttons
0093     m_handleDir[0] = QPointF(1, 0);
0094     m_handleDir[1] = QPointF(1, -1);
0095     m_handleDir[2] = QPointF(0, -1);
0096     m_handleDir[3] = QPointF(-1, -1);
0097     m_handleDir[4] = QPointF(-1, 0);
0098     m_handleDir[5] = QPointF(-1, 1);
0099     m_handleDir[6] = QPointF(0, 1);
0100     m_handleDir[7] = QPointF(1, 1);
0101     m_handleDir[8] = QPointF(0, 0); // also add the center
0102 
0103     m_rotationCenterButtons = new QButtonGroup(0);
0104     // we set the ids to match m_handleDir
0105     m_rotationCenterButtons->addButton(middleRightButton, 0);
0106     m_rotationCenterButtons->addButton(topRightButton, 1);
0107     m_rotationCenterButtons->addButton(middleTopButton, 2);
0108     m_rotationCenterButtons->addButton(topLeftButton, 3);
0109     m_rotationCenterButtons->addButton(middleLeftButton, 4);
0110     m_rotationCenterButtons->addButton(bottomLeftButton, 5);
0111     m_rotationCenterButtons->addButton(middleBottomButton, 6);
0112     m_rotationCenterButtons->addButton(bottomRightButton, 7);
0113     m_rotationCenterButtons->addButton(centerButton, 8);
0114 
0115     QToolButton *nothingSelected = new QToolButton(0);
0116     nothingSelected->setCheckable(true);
0117     nothingSelected->setAutoExclusive(true);
0118     nothingSelected->hide(); // a convenient button for when no button is checked in the group
0119     m_rotationCenterButtons->addButton(nothingSelected, 9);
0120 
0121 
0122     // initialize values for free transform sliders
0123     shearXBox->setSuffix(QChar(Qt::Key_Percent));
0124     shearYBox->setSuffix(QChar(Qt::Key_Percent));
0125     shearXBox->setRange(-500, 500, 2);
0126     shearYBox->setRange(-500, 500, 2);
0127     shearXBox->setSingleStep(1);
0128     shearYBox->setSingleStep(1);
0129     shearXBox->setValue(0.0);
0130     shearYBox->setValue(0.0);
0131 
0132 
0133     translateXBox->setSuffix(i18n(" px"));
0134     translateYBox->setSuffix(i18n(" px"));
0135     translateXBox->setRange(-10000, 10000);
0136     translateYBox->setRange(-10000, 10000);
0137 
0138 
0139     scaleXBox->setRange(-10000, 10000);
0140     scaleYBox->setRange(-10000, 10000);
0141     scaleXBox->setValue(100.0);
0142     scaleYBox->setValue(100.0);
0143 
0144     m_scaleRatio = 1.0;
0145 
0146 
0147     aXBox->setIncreasingDirection(KisAngleGauge::IncreasingDirection_Clockwise);
0148     aYBox->setIncreasingDirection(KisAngleGauge::IncreasingDirection_Clockwise);
0149     aZBox->setIncreasingDirection(KisAngleGauge::IncreasingDirection_Clockwise);
0150     aXBox->setFlipOptionsMode(KisAngleSelector::FlipOptionsMode_MenuButton);
0151     aYBox->setFlipOptionsMode(KisAngleSelector::FlipOptionsMode_MenuButton);
0152     aZBox->setFlipOptionsMode(KisAngleSelector::FlipOptionsMode_MenuButton);
0153 
0154     cameraHeightBox->setRange(1, 20000, 2);
0155 
0156     connect(m_rotationCenterButtons, SIGNAL(buttonPressed(int)), this, SLOT(slotRotationCenterChanged(int)));
0157     connect(btnTransformAroundPivotPoint, SIGNAL(clicked(bool)), this, SLOT(slotTransformAroundRotationCenter(bool)));
0158 
0159     // Init Free Transform Values
0160     connect(scaleXBox, SIGNAL(valueChanged(int)), this, SLOT(slotSetScaleX(int)));
0161     connect(scaleYBox, SIGNAL(valueChanged(int)), this, SLOT(slotSetScaleY(int)));
0162     connect(shearXBox, SIGNAL(valueChanged(qreal)), this, SLOT(slotSetShearX(qreal)));
0163     connect(shearYBox, SIGNAL(valueChanged(qreal)), this, SLOT(slotSetShearY(qreal)));
0164     connect(translateXBox, SIGNAL(valueChanged(int)), this, SLOT(slotSetTranslateX(int)));
0165     connect(translateYBox, SIGNAL(valueChanged(int)), this, SLOT(slotSetTranslateY(int)));
0166     connect(aXBox, SIGNAL(angleChanged(qreal)), this, SLOT(slotSetAX(qreal)));
0167     connect(aYBox, SIGNAL(angleChanged(qreal)), this, SLOT(slotSetAY(qreal)));
0168     connect(aZBox, SIGNAL(angleChanged(qreal)), this, SLOT(slotSetAZ(qreal)));
0169     connect(cameraHeightBox, SIGNAL(valueChanged(qreal)), this, SLOT(slotSetCameraHeight(qreal)));
0170     connect(aspectButton, SIGNAL(keepAspectRatioChanged(bool)), this, SLOT(slotSetKeepAspectRatio(bool)));
0171     connect(flipXButton, SIGNAL(clicked(bool)), this, SLOT(slotFlipX()));
0172     connect(flipYButton, SIGNAL(clicked(bool)), this, SLOT(slotFlipY()));
0173     connect(rotateCWButton, SIGNAL(clicked(bool)), this, SLOT(slotRotateCW()));
0174     connect(rotateCCWButton, SIGNAL(clicked(bool)), this, SLOT(slotRotateCCW()));
0175 
0176     // toggle visibility of different free buttons
0177     connect(freeMoveRadioButton, SIGNAL(clicked(bool)), SLOT(slotTransformAreaVisible(bool)));
0178     connect(freeRotationRadioButton, SIGNAL(clicked(bool)), SLOT(slotTransformAreaVisible(bool)));
0179     connect(freeScaleRadioButton, SIGNAL(clicked(bool)), SLOT(slotTransformAreaVisible(bool)));
0180     connect(freeShearRadioButton, SIGNAL(clicked(bool)), SLOT(slotTransformAreaVisible(bool)));
0181 
0182     // only first group for free transform
0183     rotationGroup->hide();
0184     moveGroup->show();
0185     scaleGroup->hide();
0186     shearGroup->hide();
0187 
0188 
0189 
0190     // Init Warp Transform Values
0191     alphaBox->setSingleStep(0.1);
0192     alphaBox->setRange(0, 10, 1);
0193 
0194     connect(alphaBox, SIGNAL(valueChanged(qreal)), this, SLOT(slotSetWarpAlpha(qreal)));
0195     connect(densityBox, SIGNAL(valueChanged(int)), this, SLOT(slotSetWarpDensity(int)));
0196 
0197     connect(defaultRadioButton, SIGNAL(clicked(bool)), this, SLOT(slotWarpDefaultPointsButtonClicked(bool)));
0198     connect(customRadioButton, SIGNAL(clicked(bool)), this, SLOT(slotWarpCustomPointsButtonClicked(bool)));
0199     connect(lockUnlockPointsButton, SIGNAL(clicked()), this, SLOT(slotWarpLockPointsButtonClicked()));
0200     connect(resetPointsButton, SIGNAL(clicked()), this, SLOT(slotWarpResetPointsButtonClicked()));
0201 
0202     // Init Cage Transform Values
0203     cageTransformButtonGroup->setId(cageAddEditRadio, 0); // we need to set manually since Qt Designer generates negative by default
0204     cageTransformButtonGroup->setId(cageDeformRadio, 1);
0205     connect(cageTransformButtonGroup, SIGNAL(buttonClicked(int)), this, SLOT(slotCageOptionsChanged(int)));
0206 
0207     // Init Liquify Transform Values
0208     liquifySizeSlider->setRange(KisLiquifyProperties::minSize(),
0209                                 KisLiquifyProperties::maxSize(), 2);
0210     liquifySizeSlider->setExponentRatio(3);
0211     liquifySizeSlider->setValue(60.0);
0212     connect(liquifySizeSlider, SIGNAL(valueChanged(qreal)), this, SLOT(liquifySizeChanged(qreal)));
0213     connect(liquifySizeSlider, SIGNAL(editingFinished()), SLOT(notifyEditingFinished()));
0214     liquifySizeSlider->setToolTip(i18nc("@info:tooltip", "Size of the deformation brush"));
0215 
0216     liquifyAmountSlider->setRange(0.0, 1.0, 2);
0217     liquifyAmountSlider->setSingleStep(0.01);
0218     liquifyAmountSlider->setValue(0.05);
0219     connect(liquifyAmountSlider, SIGNAL(valueChanged(qreal)), this, SLOT(liquifyAmountChanged(qreal)));
0220     connect(liquifyAmountSlider, SIGNAL(editingFinished()), SLOT(notifyEditingFinished()));
0221     liquifyAmountSlider->setToolTip(i18nc("@info:tooltip", "Amount of the deformation you get"));
0222 
0223     liquifyFlowSlider->setRange(0.0, 1.0, 2);
0224     liquifyFlowSlider->setSingleStep(0.01);
0225     liquifyFlowSlider->setValue(1.0);
0226     connect(liquifyFlowSlider, SIGNAL(valueChanged(qreal)), this, SLOT(liquifyFlowChanged(qreal)));
0227     connect(liquifyFlowSlider, SIGNAL(editingFinished()), SLOT(notifyEditingFinished()));
0228     liquifyFlowSlider->setToolTip(i18nc("@info:tooltip", "When in non-buildup mode, shows how fast the deformation limit is reached."));
0229 
0230     buildupModeComboBox->setCurrentIndex(0); // set to build-up mode by default
0231     connect(buildupModeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(liquifyBuildUpChanged(int)));
0232     connect(buildupModeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(notifyEditingFinished()));
0233     buildupModeComboBox->setToolTip("<p>" + i18nc("@info:tooltip", "Switch between Build Up and Wash mode of painting. Build Up mode adds deformations one on top of the other without any limits. Wash mode gradually deforms the piece to the selected deformation level.") + "</p>");
0234 
0235     liquifySpacingSlider->setRange(0.0, 3.0, 2);
0236     liquifySpacingSlider->setExponentRatio(3);
0237     liquifySpacingSlider->setSingleStep(0.01);
0238     liquifySpacingSlider->setValue(0.2);
0239     connect(liquifySpacingSlider, SIGNAL(valueChanged(qreal)), this, SLOT(liquifySpacingChanged(qreal)));
0240     connect(liquifySpacingSlider, SIGNAL(editingFinished()), SLOT(notifyEditingFinished()));
0241     liquifySpacingSlider->setToolTip(i18nc("@info:tooltip", "Space between two sequential applications of the deformation"));
0242 
0243     liquifySizePressureBox->setChecked(true);
0244     connect(liquifySizePressureBox, SIGNAL(toggled(bool)), this, SLOT(liquifySizePressureChanged(bool)));
0245     connect(liquifySizePressureBox, SIGNAL(toggled(bool)), this, SLOT(notifyEditingFinished()));
0246     liquifySizePressureBox->setToolTip(i18nc("@info:tooltip", "Scale <b>Size</b> value according to current stylus pressure"));
0247 
0248     liquifyAmountPressureBox->setChecked(true);
0249     connect(liquifyAmountPressureBox, SIGNAL(toggled(bool)), this, SLOT(liquifyAmountPressureChanged(bool)));
0250     connect(liquifyAmountPressureBox, SIGNAL(toggled(bool)), this, SLOT(notifyEditingFinished()));
0251     liquifyAmountPressureBox->setToolTip(i18nc("@info:tooltip", "Scale <b>Amount</b> value according to current stylus pressure"));
0252 
0253     liquifyReverseDirectionChk->setChecked(false);
0254     connect(liquifyReverseDirectionChk, SIGNAL(toggled(bool)), this, SLOT(liquifyReverseDirectionChanged(bool)));
0255     connect(liquifyReverseDirectionChk, SIGNAL(toggled(bool)), this, SLOT(notifyEditingFinished()));
0256     liquifyReverseDirectionChk->setToolTip(i18nc("@info:tooltip", "Reverse direction of the current deformation tool"));
0257 
0258     KisSignalMapper *liquifyModeMapper = new KisSignalMapper(this);
0259     connect(liquifyMove, SIGNAL(toggled(bool)), liquifyModeMapper, SLOT(map()));
0260     connect(liquifyScale, SIGNAL(toggled(bool)), liquifyModeMapper, SLOT(map()));
0261     connect(liquifyRotate, SIGNAL(toggled(bool)), liquifyModeMapper, SLOT(map()));
0262     connect(liquifyOffset, SIGNAL(toggled(bool)), liquifyModeMapper, SLOT(map()));
0263     connect(liquifyUndo, SIGNAL(toggled(bool)), liquifyModeMapper, SLOT(map()));
0264     liquifyModeMapper->setMapping(liquifyMove, (int)KisLiquifyProperties::MOVE);
0265     liquifyModeMapper->setMapping(liquifyScale, (int)KisLiquifyProperties::SCALE);
0266     liquifyModeMapper->setMapping(liquifyRotate, (int)KisLiquifyProperties::ROTATE);
0267     liquifyModeMapper->setMapping(liquifyOffset, (int)KisLiquifyProperties::OFFSET);
0268     liquifyModeMapper->setMapping(liquifyUndo, (int)KisLiquifyProperties::UNDO);
0269     connect(liquifyModeMapper, SIGNAL(mapped(int)), SLOT(slotLiquifyModeChanged(int)));
0270     connect(liquifyModeMapper, SIGNAL(mapped(int)), SLOT(notifyEditingFinished()));
0271 
0272     liquifyMove->setToolTip(i18nc("@info:tooltip", "Move: drag the image along the brush stroke"));
0273     liquifyScale->setToolTip(i18nc("@info:tooltip", "Scale: grow/shrink image under cursor"));
0274     liquifyRotate->setToolTip(i18nc("@info:tooltip", "Rotate: twirl image under cursor"));
0275     liquifyOffset->setToolTip(i18nc("@info:tooltip", "Offset: shift the image to the right of the stroke direction"));
0276     liquifyUndo->setToolTip(i18nc("@info:tooltip", "Undo: erase actions of other tools"));
0277 
0278     // Connect all edit boxes to the Editing Finished signal
0279     connect(densityBox, SIGNAL(editingFinished()), this, SLOT(notifyEditingFinished()));
0280 
0281 
0282 
0283     // Connect other widget (not having editingFinished signal) to
0284     // the same slot. From Qt 4.6 onwards the sequence of the signal
0285     // delivery is definite.
0286     connect(cmbFilter, SIGNAL(activated(KoID)), this, SLOT(notifyEditingFinished()));
0287     connect(cmbWarpType, SIGNAL(currentIndexChanged(int)), this, SLOT(notifyEditingFinished()));
0288     connect(m_rotationCenterButtons, SIGNAL(buttonPressed(int)), this, SLOT(notifyEditingFinished()));
0289     connect(aspectButton, SIGNAL(keepAspectRatioChanged(bool)), this, SLOT(notifyEditingFinished()));
0290 
0291     connect(lockUnlockPointsButton, SIGNAL(clicked()), this, SLOT(notifyEditingFinished()));
0292     connect(resetPointsButton, SIGNAL(clicked()), this, SLOT(notifyEditingFinished()));
0293 
0294     connect(defaultRadioButton, SIGNAL(clicked(bool)), this, SLOT(notifyEditingFinished()));
0295     connect(customRadioButton, SIGNAL(clicked(bool)), this, SLOT(notifyEditingFinished()));
0296 
0297     // Liquify
0298     //
0299     // liquify brush options do not affect the image directly and are not
0300     // saved to undo, so we don't emit notifyEditingFinished() for them
0301 
0302     // Connect Apply/Reset buttons
0303     connect(buttonBox, SIGNAL(clicked(QAbstractButton*)), this, SLOT(slotButtonBoxClicked(QAbstractButton*)));
0304 
0305     // Mesh. First set up, then connect.
0306     intNumRows->setRange(1, 999);
0307     intNumColumns->setRange(1, 999);
0308 
0309     connect(chkShowControlPoints, SIGNAL(toggled(bool)), this, SLOT(slotMeshShowHandlesChanged()));
0310     connect(chkSymmetricalHandles, SIGNAL(toggled(bool)), this, SLOT(slotMeshSymmetricalHandlesChanged()));
0311     connect(chkScaleHandles, SIGNAL(toggled(bool)), this, SLOT(slotMeshScaleHandlesChanged()));
0312     connect(intNumColumns, SIGNAL(valueChanged(int)), this, SLOT(slotMeshSizeChanged()));
0313     connect(intNumRows, SIGNAL(valueChanged(int)), this, SLOT(slotMeshSizeChanged()));
0314     connect(intNumColumns, SIGNAL(editingFinished()), this, SLOT(notifyEditingFinished()));
0315     connect(intNumRows, SIGNAL(editingFinished()), this, SLOT(notifyEditingFinished()));
0316 
0317     // Mode switch buttons
0318     connect(freeTransformButton, SIGNAL(clicked(bool)), this, SLOT(slotSetFreeTransformModeButtonClicked(bool)));
0319     connect(warpButton, SIGNAL(clicked(bool)), this, SLOT(slotSetWarpModeButtonClicked(bool)));
0320     connect(cageButton, SIGNAL(clicked(bool)), this, SLOT(slotSetCageModeButtonClicked(bool)));
0321     connect(perspectiveTransformButton, SIGNAL(clicked(bool)), this, SLOT(slotSetPerspectiveModeButtonClicked(bool)));
0322     connect(liquifyButton, SIGNAL(clicked(bool)), this, SLOT(slotSetLiquifyModeButtonClicked(bool)));
0323     connect(meshButton, SIGNAL(clicked(bool)), this, SLOT(slotSetMeshModeButtonClicked(bool)));
0324 
0325 
0326     tooBigLabelWidget->hide();
0327 
0328     connect(canvas->viewManager()->mainWindow(), SIGNAL(themeChanged()), SLOT(slotUpdateIcons()), Qt::UniqueConnection);
0329     slotUpdateIcons();
0330 
0331     KGuiItem::assign(buttonBox->button(QDialogButtonBox::Apply), KStandardGuiItem::apply());
0332     KGuiItem::assign(buttonBox->button(QDialogButtonBox::Reset), KStandardGuiItem::reset());
0333 }
0334 
0335 void KisToolTransformConfigWidget::slotUpdateIcons()
0336 {
0337     freeTransformButton->setIcon(KisIconUtils::loadIcon("transform_icons_main"));
0338     warpButton->setIcon(KisIconUtils::loadIcon("transform_icons_warp"));
0339     cageButton->setIcon(KisIconUtils::loadIcon("transform_icons_cage"));
0340     perspectiveTransformButton->setIcon(KisIconUtils::loadIcon("transform_icons_perspective"));
0341     liquifyButton->setIcon(KisIconUtils::loadIcon("transform_icons_liquify_main"));
0342     meshButton->setIcon(KisIconUtils::loadIcon("transform_icons_mesh"));
0343 
0344     liquifyMove->setIcon(KisIconUtils::loadIcon("transform_icons_liquify_move"));
0345     liquifyScale->setIcon(KisIconUtils::loadIcon("transform_icons_liquify_resize"));
0346     liquifyRotate->setIcon(KisIconUtils::loadIcon("transform_icons_liquify_rotate"));
0347     liquifyOffset->setIcon(KisIconUtils::loadIcon("transform_icons_liquify_offset"));
0348     liquifyUndo->setIcon(KisIconUtils::loadIcon("transform_icons_liquify_erase"));
0349 
0350 
0351 
0352     middleRightButton->setIcon(KisIconUtils::loadIcon("arrow-right"));
0353     topRightButton->setIcon(KisIconUtils::loadIcon("arrow-topright"));
0354     middleTopButton->setIcon(KisIconUtils::loadIcon("arrow-up"));
0355     topLeftButton->setIcon(KisIconUtils::loadIcon("arrow-topleft"));
0356     middleLeftButton->setIcon(KisIconUtils::loadIcon("arrow-left"));
0357     bottomLeftButton->setIcon(KisIconUtils::loadIcon("arrow-downleft"));
0358     middleBottomButton->setIcon(KisIconUtils::loadIcon("arrow-down"));
0359     bottomRightButton->setIcon(KisIconUtils::loadIcon("arrow-downright"));
0360 
0361     btnTransformAroundPivotPoint->setIcon(KisIconUtils::loadIcon("pivot-point"));
0362 
0363 
0364     // pressure icons
0365     liquifySizePressureBox->setIcon(KisIconUtils::loadIcon("transform_icons_penPressure"));
0366     liquifyAmountPressureBox->setIcon(KisIconUtils::loadIcon("transform_icons_penPressure"));
0367 }
0368 
0369 void KisToolTransformConfigWidget::updateLiquifyControls()
0370 {
0371     blockUiSlots();
0372 
0373     ToolTransformArgs *config = m_transaction->currentConfig();
0374     KisLiquifyProperties *props =
0375         config->liquifyProperties();
0376 
0377     const bool useWashMode = props->useWashMode();
0378 
0379     liquifySizeSlider->setValue(props->size());
0380     liquifyAmountSlider->setValue(props->amount());
0381     liquifyFlowSlider->setValue(props->flow());
0382     buildupModeComboBox->setCurrentIndex(useWashMode);
0383 
0384     liquifySpacingSlider->setValue(props->spacing());
0385     liquifySizePressureBox->setChecked(props->sizeHasPressure());
0386     liquifyAmountPressureBox->setChecked(props->amountHasPressure());
0387     liquifyReverseDirectionChk->setChecked(props->reverseDirection());
0388 
0389 
0390     KisLiquifyProperties::LiquifyMode mode =
0391         static_cast<KisLiquifyProperties::LiquifyMode>(props->mode());
0392 
0393     bool canInverseDirection =
0394         mode != KisLiquifyProperties::UNDO;
0395 
0396     bool canUseWashMode = mode != KisLiquifyProperties::UNDO;
0397 
0398     liquifyReverseDirectionChk->setEnabled(canInverseDirection);
0399     liquifyFlowSlider->setEnabled(canUseWashMode && useWashMode);
0400     buildupModeComboBox->setEnabled(canUseWashMode);
0401 
0402     const qreal maxAmount = canUseWashMode ? 5.0 : 1.0;
0403     liquifyAmountSlider->setRange(0.0, maxAmount, 2);
0404 
0405     unblockUiSlots();
0406 }
0407 
0408 void KisToolTransformConfigWidget::slotLiquifyModeChanged(int value)
0409 {
0410     if (m_uiSlotsBlocked) return;
0411 
0412     ToolTransformArgs *config = m_transaction->currentConfig();
0413 
0414     KisLiquifyProperties *props =
0415         config->liquifyProperties();
0416 
0417     KisLiquifyProperties::LiquifyMode mode =
0418         static_cast<KisLiquifyProperties::LiquifyMode>(value);
0419 
0420     if (mode == props->mode()) return;
0421 
0422     props->setMode(mode);
0423     props->loadMode();
0424 
0425     updateLiquifyControls();
0426 
0427     notifyConfigChanged(false);
0428 }
0429 
0430 void KisToolTransformConfigWidget::liquifySizeChanged(qreal value)
0431 {
0432     if (m_uiSlotsBlocked) return;
0433 
0434     ToolTransformArgs *config = m_transaction->currentConfig();
0435     KisLiquifyProperties *props =
0436         config->liquifyProperties();
0437 
0438     props->setSize(value);
0439     notifyConfigChanged(false);
0440 }
0441 
0442 void KisToolTransformConfigWidget::liquifyAmountChanged(qreal value)
0443 {
0444     if (m_uiSlotsBlocked) return;
0445 
0446     ToolTransformArgs *config = m_transaction->currentConfig();
0447     KisLiquifyProperties *props =
0448         config->liquifyProperties();
0449 
0450     props->setAmount(value);
0451     notifyConfigChanged(false);
0452 }
0453 
0454 void KisToolTransformConfigWidget::liquifyFlowChanged(qreal value)
0455 {
0456     if (m_uiSlotsBlocked) return;
0457 
0458     ToolTransformArgs *config = m_transaction->currentConfig();
0459     KisLiquifyProperties *props =
0460         config->liquifyProperties();
0461 
0462     props->setFlow(value);
0463     notifyConfigChanged(false);
0464 }
0465 
0466 void KisToolTransformConfigWidget::liquifyBuildUpChanged(int value)
0467 {
0468     if (m_uiSlotsBlocked) return;
0469 
0470     ToolTransformArgs *config = m_transaction->currentConfig();
0471     KisLiquifyProperties *props =
0472         config->liquifyProperties();
0473 
0474     props->setUseWashMode(value); // 0 == build up mode / 1 == wash mode
0475 
0476     notifyConfigChanged(false);
0477 
0478     // we need to enable/disable flow slider
0479     updateLiquifyControls();
0480 }
0481 
0482 void KisToolTransformConfigWidget::liquifySpacingChanged(qreal value)
0483 {
0484     if (m_uiSlotsBlocked) return;
0485 
0486     ToolTransformArgs *config = m_transaction->currentConfig();
0487     KisLiquifyProperties *props =
0488         config->liquifyProperties();
0489 
0490     props->setSpacing(value);
0491     notifyConfigChanged(false);
0492 }
0493 
0494 void KisToolTransformConfigWidget::liquifySizePressureChanged(bool value)
0495 {
0496     if (m_uiSlotsBlocked) return;
0497 
0498     ToolTransformArgs *config = m_transaction->currentConfig();
0499     KisLiquifyProperties *props =
0500         config->liquifyProperties();
0501 
0502     props->setSizeHasPressure(value);
0503     notifyConfigChanged(false);
0504 }
0505 
0506 void KisToolTransformConfigWidget::liquifyAmountPressureChanged(bool value)
0507 {
0508     if (m_uiSlotsBlocked) return;
0509 
0510     ToolTransformArgs *config = m_transaction->currentConfig();
0511     KisLiquifyProperties *props =
0512         config->liquifyProperties();
0513 
0514     props->setAmountHasPressure(value);
0515     notifyConfigChanged(false);
0516 }
0517 
0518 void KisToolTransformConfigWidget::liquifyReverseDirectionChanged(bool value)
0519 {
0520     if (m_uiSlotsBlocked) return;
0521 
0522     ToolTransformArgs *config = m_transaction->currentConfig();
0523     KisLiquifyProperties *props =
0524         config->liquifyProperties();
0525 
0526     props->setReverseDirection(value);
0527     notifyConfigChanged(false);
0528 }
0529 
0530 void KisToolTransformConfigWidget::updateConfig(const ToolTransformArgs &config)
0531 {
0532     blockUiSlots();
0533  
0534     if (config.mode() == ToolTransformArgs::FREE_TRANSFORM ||
0535         config.mode() == ToolTransformArgs::PERSPECTIVE_4POINT) {
0536 
0537         quickTransformGroup->setEnabled(config.mode() == ToolTransformArgs::FREE_TRANSFORM);
0538 
0539         stackedWidget->setCurrentIndex(0);
0540 
0541         bool freeTransformIsActive = config.mode() == ToolTransformArgs::FREE_TRANSFORM;
0542 
0543         if (freeTransformIsActive)
0544         {
0545             freeTransformButton->setChecked(true);
0546         }
0547         else
0548         {
0549             perspectiveTransformButton->setChecked(true);
0550         }
0551 
0552         aXBox->setEnabled(freeTransformIsActive);
0553         aYBox->setEnabled(freeTransformIsActive);
0554         aZBox->setEnabled(freeTransformIsActive);
0555         cameraHeightBox->setEnabled(freeTransformIsActive);
0556         freeRotationRadioButton->setEnabled(freeTransformIsActive);
0557 
0558         scaleXBox->setValue(config.scaleX() * 100.);
0559         scaleYBox->setValue(config.scaleY() * 100.);
0560         shearXBox->setValue(config.shearX() * 100.);
0561         shearYBox->setValue(config.shearY() * 100.);
0562 
0563         const QPointF anchorPoint = config.originalCenter() + config.rotationCenterOffset();
0564         const KisTransformUtils::MatricesPack m(config);
0565         const QPointF anchorPointView = m.finalTransform().map(anchorPoint);
0566 
0567         translateXBox->setValue(anchorPointView.x());
0568         translateYBox->setValue(anchorPointView.y());
0569 
0570         aXBox->setAngle(normalizeAngleDegrees(kisRadiansToDegrees(config.aX())));
0571         aYBox->setAngle(normalizeAngleDegrees(kisRadiansToDegrees(config.aY())));
0572         aZBox->setAngle(normalizeAngleDegrees(kisRadiansToDegrees(config.aZ())));
0573         cameraHeightBox->setValue(config.cameraPos().z());
0574         aspectButton->setKeepAspectRatio(config.keepAspectRatio());
0575         cmbFilter->setCurrent(config.filterId());
0576 
0577         QPointF pt = m_transaction->currentConfig()->rotationCenterOffset();
0578         pt.rx() /= m_transaction->originalHalfWidth();
0579         pt.ry() /= m_transaction->originalHalfHeight();
0580 
0581         for (int i = 0; i < 9; i++) {
0582             if (qFuzzyCompare(m_handleDir[i].x(), pt.x()) &&
0583                 qFuzzyCompare(m_handleDir[i].y(), pt.y())) {
0584 
0585                 m_rotationCenterButtons->button(i)->setChecked(true);
0586                 break;
0587             }
0588         }
0589 
0590         btnTransformAroundPivotPoint->setChecked(config.transformAroundRotationCenter());
0591 
0592     } else if (config.mode() == ToolTransformArgs::WARP) {
0593 
0594         stackedWidget->setCurrentIndex(1);
0595         warpButton->setChecked(true);
0596 
0597         if (config.defaultPoints()) {
0598             densityBox->setValue(std::sqrt(config.numPoints()));
0599         }
0600 
0601         cmbWarpType->setCurrentIndex((int)config.warpType());
0602         defaultRadioButton->setChecked(config.defaultPoints());
0603         customRadioButton->setChecked(!config.defaultPoints());
0604         densityBox->setEnabled(config.defaultPoints());
0605         customWarpWidget->setEnabled(!config.defaultPoints());
0606 
0607         updateLockPointsButtonCaption();
0608 
0609     } else if (config.mode() == ToolTransformArgs::CAGE) {
0610 
0611         // default UI options
0612         resetUIOptions();
0613 
0614         // we need at least 3 point before we can start actively deforming
0615         if (config.origPoints().size() >= 3)
0616         {
0617             cageTransformDirections->setText(i18n("Switch between editing and deforming cage"));
0618             cageAddEditRadio->setVisible(true);
0619             cageDeformRadio->setVisible(true);
0620 
0621             // update to correct radio button
0622             if (config.isEditingTransformPoints())
0623                 cageAddEditRadio->setChecked(true);
0624             else
0625                  cageDeformRadio->setChecked(true);
0626 
0627             changeGranularity->setCurrentIndex(log2(config.pixelPrecision()) - 2);
0628             granularityPreview->setCurrentIndex(log2(config.previewPixelPrecision()) - 2);
0629 
0630         }
0631 
0632     } else if (config.mode() == ToolTransformArgs::LIQUIFY) {
0633 
0634         stackedWidget->setCurrentIndex(3);
0635         liquifyButton->setChecked(true);
0636 
0637         const KisLiquifyProperties *props =
0638             config.liquifyProperties();
0639 
0640         switch (props->mode()) {
0641         case KisLiquifyProperties::MOVE:
0642             liquifyMove->setChecked(true);
0643             break;
0644         case KisLiquifyProperties::SCALE:
0645             liquifyScale->setChecked(true);
0646             break;
0647         case KisLiquifyProperties::ROTATE:
0648             liquifyRotate->setChecked(true);
0649             break;
0650         case KisLiquifyProperties::OFFSET:
0651             liquifyOffset->setChecked(true);
0652             break;
0653         case KisLiquifyProperties::UNDO:
0654             liquifyUndo->setChecked(true);
0655             break;
0656         case KisLiquifyProperties::N_MODES:
0657             qFatal("Unsupported mode");
0658         }
0659 
0660         updateLiquifyControls();
0661     } else if (config.mode() == ToolTransformArgs::MESH) {
0662         stackedWidget->setCurrentIndex(4);
0663         intNumColumns->setValue(config.meshTransform()->size().width() - 1);
0664         intNumRows->setValue(config.meshTransform()->size().height() - 1);
0665         chkShowControlPoints->setChecked(config.meshShowHandles());
0666         chkSymmetricalHandles->setChecked(config.meshSymmetricalHandles());
0667         chkScaleHandles->setChecked(config.meshScaleHandles());
0668     }
0669 
0670     unblockUiSlots();
0671 }
0672 
0673 void KisToolTransformConfigWidget::updateLockPointsButtonCaption()
0674 {
0675     ToolTransformArgs *config = m_transaction->currentConfig();
0676 
0677     if (config->isEditingTransformPoints()) {
0678         lockUnlockPointsButton->setText(i18n("Lock Points"));
0679     } else {
0680         lockUnlockPointsButton->setText(i18n("Unlock Points"));
0681     }
0682 }
0683 
0684 void KisToolTransformConfigWidget::setApplyResetDisabled(bool disabled)
0685 {
0686     QAbstractButton *applyButton = buttonBox->button(QDialogButtonBox::Apply);
0687     QAbstractButton *resetButton = buttonBox->button(QDialogButtonBox::Reset);
0688 
0689     Q_ASSERT(applyButton);
0690     Q_ASSERT(resetButton);
0691 
0692     applyButton->setDisabled(disabled);
0693     resetButton->setDisabled(disabled);
0694 }
0695 
0696 void KisToolTransformConfigWidget::resetRotationCenterButtons()
0697 {
0698     int checkedId = m_rotationCenterButtons->checkedId();
0699 
0700     if (checkedId >= 0 && checkedId <= 8) {
0701         // uncheck the current checked button
0702         m_rotationCenterButtons->button(9)->setChecked(true);
0703     }
0704 }
0705 
0706 void KisToolTransformConfigWidget::setTooBigLabelVisible(bool value)
0707 {
0708     tooBigLabelWidget->setVisible(value);
0709 }
0710 
0711 void KisToolTransformConfigWidget::blockNotifications()
0712 {
0713     m_notificationsBlocked++;
0714 }
0715 
0716 void KisToolTransformConfigWidget::unblockNotifications()
0717 {
0718     m_notificationsBlocked--;
0719 }
0720 
0721 void KisToolTransformConfigWidget::notifyConfigChanged(bool needsPreviewRecalculation)
0722 {
0723     if (!m_notificationsBlocked) {
0724         emit sigConfigChanged(needsPreviewRecalculation);
0725     }
0726     m_configChanged = true;
0727 }
0728 
0729 void KisToolTransformConfigWidget::blockUiSlots()
0730 {
0731     m_uiSlotsBlocked++;
0732 }
0733 
0734 void KisToolTransformConfigWidget::unblockUiSlots()
0735 {
0736     m_uiSlotsBlocked--;
0737 }
0738 
0739 void KisToolTransformConfigWidget::notifyEditingFinished()
0740 {
0741     if (m_uiSlotsBlocked || m_notificationsBlocked || !m_configChanged) return;
0742 
0743     emit sigEditingFinished();
0744     m_configChanged = false;
0745 }
0746 
0747 
0748 void KisToolTransformConfigWidget::resetUIOptions()
0749 {
0750     // reset tool states since we are done (probably can encapsulate this later)
0751     ToolTransformArgs *config = m_transaction->currentConfig();
0752     if (config->mode() == ToolTransformArgs::CAGE)
0753     {
0754         cageAddEditRadio->setVisible(false);
0755         cageAddEditRadio->setChecked(true);
0756         cageDeformRadio->setVisible(false);
0757         cageTransformDirections->setText(i18n("Create 3 points on the canvas to begin"));
0758 
0759         // ensure we are on the right options view
0760         stackedWidget->setCurrentIndex(2);
0761     }
0762 
0763 
0764 
0765 }
0766 
0767 void KisToolTransformConfigWidget::slotButtonBoxClicked(QAbstractButton *button)
0768 {
0769     QAbstractButton *applyButton = buttonBox->button(QDialogButtonBox::Apply);
0770     QAbstractButton *resetButton = buttonBox->button(QDialogButtonBox::Reset);
0771 
0772     resetUIOptions();
0773 
0774     if (button == applyButton) {
0775         emit sigApplyTransform();
0776     }
0777     else if (button == resetButton) {
0778         emit sigCancelTransform();
0779     }
0780 
0781 }
0782 
0783 void KisToolTransformConfigWidget::slotSetMeshModeButtonClicked(bool value)
0784 {
0785     if (!value) return;
0786 
0787     lblTransformType->setText(meshButton->toolTip());
0788 
0789     emit sigResetTransform(ToolTransformArgs::MESH);
0790 }
0791 
0792 void KisToolTransformConfigWidget::slotSetFreeTransformModeButtonClicked(bool value)
0793 {
0794     if (!value) return;
0795 
0796     lblTransformType->setText(freeTransformButton->toolTip());
0797 
0798     emit sigResetTransform(ToolTransformArgs::FREE_TRANSFORM);
0799 }
0800 
0801 void KisToolTransformConfigWidget::slotSetWarpModeButtonClicked(bool value)
0802 {
0803     if (!value) return;
0804 
0805     lblTransformType->setText(warpButton->toolTip());
0806 
0807     emit sigResetTransform(ToolTransformArgs::WARP);
0808 }
0809 
0810 void KisToolTransformConfigWidget::slotSetCageModeButtonClicked(bool value)
0811 {
0812     if (!value) return;
0813 
0814     lblTransformType->setText(cageButton->toolTip());
0815 
0816     emit sigResetTransform(ToolTransformArgs::CAGE);
0817 }
0818 
0819 void KisToolTransformConfigWidget::slotSetLiquifyModeButtonClicked(bool value)
0820 {
0821     if (!value) return;
0822 
0823     lblTransformType->setText(liquifyButton->toolTip());
0824 
0825     emit sigResetTransform(ToolTransformArgs::LIQUIFY);
0826 }
0827 
0828 void KisToolTransformConfigWidget::slotSetPerspectiveModeButtonClicked(bool value)
0829 {
0830     if (!value) return;
0831 
0832     lblTransformType->setText(perspectiveTransformButton->toolTip());
0833 
0834     emit sigResetTransform(ToolTransformArgs::PERSPECTIVE_4POINT);
0835 }
0836 
0837 void KisToolTransformConfigWidget::slotFilterChanged(const KoID &filterId)
0838 {
0839     ToolTransformArgs *config = m_transaction->currentConfig();
0840     config->setFilterId(filterId.id());
0841     notifyConfigChanged();
0842 }
0843 
0844 void KisToolTransformConfigWidget::slotRotationCenterChanged(int index)
0845 {
0846     if (m_uiSlotsBlocked) return;
0847 
0848     if (index >= 0 && index <= 8) {
0849         ToolTransformArgs *config = m_transaction->currentConfig();
0850 
0851         double i = m_handleDir[index].x();
0852         double j = m_handleDir[index].y();
0853 
0854         config->setRotationCenterOffset(QPointF(i * m_transaction->originalHalfWidth(),
0855                                                 j * m_transaction->originalHalfHeight()));
0856 
0857         notifyConfigChanged();
0858         updateConfig(*config);
0859     }
0860 }
0861 
0862 void KisToolTransformConfigWidget::slotTransformAroundRotationCenter(bool value)
0863 {
0864     if (m_uiSlotsBlocked) return;
0865 
0866     ToolTransformArgs *config = m_transaction->currentConfig();
0867     config->setTransformAroundRotationCenter(value);
0868     notifyConfigChanged();
0869     notifyEditingFinished();
0870 }
0871 
0872 void KisToolTransformConfigWidget::slotSetScaleX(int value)
0873 {
0874     if (m_uiSlotsBlocked) return;
0875 
0876     ToolTransformArgs *config = m_transaction->currentConfig();
0877 
0878     {
0879         KisTransformUtils::AnchorHolder keeper(config->transformAroundRotationCenter(), config);
0880         config->setScaleX(value / 100.);
0881     }
0882 
0883     if (config->keepAspectRatio()) {
0884 
0885         blockNotifications();
0886          int calculatedValue = int( value/ m_scaleRatio );
0887 
0888         scaleYBox->blockSignals(true);
0889         scaleYBox->setValue(calculatedValue);
0890         {
0891             KisTransformUtils::AnchorHolder keeper(config->transformAroundRotationCenter(), config);
0892             config->setScaleY(calculatedValue / 100.);
0893         }
0894         scaleYBox->blockSignals(false);
0895 
0896         unblockNotifications();
0897     }
0898 
0899     notifyConfigChanged();
0900     notifyEditingFinished();
0901 }
0902 
0903 void KisToolTransformConfigWidget::slotSetScaleY(int value)
0904 {
0905     if (m_uiSlotsBlocked) return;
0906 
0907     ToolTransformArgs *config = m_transaction->currentConfig();
0908 
0909     {
0910         KisTransformUtils::AnchorHolder keeper(config->transformAroundRotationCenter(), config);
0911         config->setScaleY(value / 100.);
0912     }
0913 
0914     if (config->keepAspectRatio()) {
0915         blockNotifications();
0916         int calculatedValue = int(m_scaleRatio * value);
0917         scaleXBox->blockSignals(true);
0918         scaleXBox->setValue(calculatedValue);
0919         {
0920             KisTransformUtils::AnchorHolder keeper(config->transformAroundRotationCenter(), config);
0921             config->setScaleX(calculatedValue / 100.);
0922         }
0923         scaleXBox->blockSignals(false);
0924         unblockNotifications();
0925     }
0926 
0927     notifyConfigChanged();
0928     notifyEditingFinished();
0929 }
0930 
0931 void KisToolTransformConfigWidget::slotSetShearX(qreal value)
0932 {
0933     if (m_uiSlotsBlocked) return;
0934 
0935     ToolTransformArgs *config = m_transaction->currentConfig();
0936 
0937     {
0938         KisTransformUtils::AnchorHolder keeper(config->transformAroundRotationCenter(), config);
0939         config->setShearX((double)value / 100.);
0940     }
0941 
0942     notifyConfigChanged();
0943     notifyEditingFinished();
0944 }
0945 
0946 void KisToolTransformConfigWidget::slotSetShearY(qreal value)
0947 {
0948     if (m_uiSlotsBlocked) return;
0949 
0950     ToolTransformArgs *config = m_transaction->currentConfig();
0951 
0952     {
0953         KisTransformUtils::AnchorHolder keeper(config->transformAroundRotationCenter(), config);
0954         config->setShearY((double)value / 100.);
0955     }
0956 
0957 
0958     notifyConfigChanged();
0959     notifyEditingFinished();
0960 }
0961 
0962 void KisToolTransformConfigWidget::slotSetTranslateX(int value)
0963 {
0964     if (m_uiSlotsBlocked) return;
0965 
0966     ToolTransformArgs *config = m_transaction->currentConfig();
0967 
0968     const QPointF anchorPoint = config->originalCenter() + config->rotationCenterOffset();
0969     const KisTransformUtils::MatricesPack m(*config);
0970 
0971     const QPointF anchorPointView = m.finalTransform().map(anchorPoint);
0972     const QPointF newAnchorPointView(value, anchorPointView.y());
0973     config->setTransformedCenter(config->transformedCenter() + newAnchorPointView - anchorPointView);
0974     translateXBox->setValue(value);
0975     notifyConfigChanged();
0976 }
0977 
0978 void KisToolTransformConfigWidget::slotSetTranslateY(int value)
0979 {
0980     if (m_uiSlotsBlocked) return;
0981 
0982     ToolTransformArgs *config = m_transaction->currentConfig();
0983 
0984     const QPointF anchorPoint = config->originalCenter() + config->rotationCenterOffset();
0985     const KisTransformUtils::MatricesPack m(*config);
0986 
0987     const QPointF anchorPointView = m.finalTransform().map(anchorPoint);
0988     const QPointF newAnchorPointView(anchorPointView.x(), value);
0989     config->setTransformedCenter(config->transformedCenter() + newAnchorPointView - anchorPointView);
0990     translateYBox->setValue(value);
0991     notifyConfigChanged();
0992 }
0993 
0994 void KisToolTransformConfigWidget::slotSetAX(qreal value)
0995 {
0996     if (m_uiSlotsBlocked) return;
0997 
0998     ToolTransformArgs *config = m_transaction->currentConfig();
0999     {
1000         KisTransformUtils::AnchorHolder keeper(config->transformAroundRotationCenter(), config);
1001         config->setAX(kisDegreesToRadians(value));
1002     }
1003     notifyConfigChanged();
1004     notifyEditingFinished();
1005 }
1006 
1007 void KisToolTransformConfigWidget::slotSetAY(qreal value)
1008 {
1009     if (m_uiSlotsBlocked) return;
1010 
1011     ToolTransformArgs *config = m_transaction->currentConfig();
1012 
1013     {
1014         KisTransformUtils::AnchorHolder keeper(config->transformAroundRotationCenter(), config);
1015         config->setAY(kisDegreesToRadians(value));
1016     }
1017 
1018     notifyConfigChanged();
1019     notifyEditingFinished();
1020 }
1021 
1022 void KisToolTransformConfigWidget::slotSetAZ(qreal value)
1023 {
1024     if (m_uiSlotsBlocked) return;
1025 
1026     ToolTransformArgs *config = m_transaction->currentConfig();
1027 
1028     {
1029         KisTransformUtils::AnchorHolder keeper(config->transformAroundRotationCenter(), config);
1030         config->setAZ(kisDegreesToRadians(value));
1031     }
1032 
1033     notifyConfigChanged();
1034     notifyEditingFinished();
1035 }
1036 
1037 void KisToolTransformConfigWidget::slotSetCameraHeight(qreal value)
1038 {
1039     if (m_uiSlotsBlocked) return;
1040 
1041     ToolTransformArgs *config = m_transaction->currentConfig();
1042     config->setCameraPos(QVector3D(0,0,value));
1043 
1044     notifyConfigChanged();
1045     notifyEditingFinished();
1046 }
1047 
1048 void KisToolTransformConfigWidget::slotFlipX()
1049 {
1050     ToolTransformArgs *config = m_transaction->currentConfig();
1051 
1052     {
1053         KisTransformUtils::AnchorHolder keeper(config->transformAroundRotationCenter(), config);
1054         config->setScaleX(config->scaleX() * -1);
1055     }
1056 
1057     notifyConfigChanged();
1058     notifyEditingFinished();
1059 }
1060 
1061 void KisToolTransformConfigWidget::slotFlipY()
1062 {
1063     ToolTransformArgs *config = m_transaction->currentConfig();
1064 
1065     {
1066         KisTransformUtils::AnchorHolder keeper(config->transformAroundRotationCenter(), config);
1067         config->setScaleY(config->scaleY() * -1);
1068     }
1069 
1070     notifyConfigChanged();
1071     notifyEditingFinished();
1072 }
1073 
1074 void KisToolTransformConfigWidget::slotRotateCW()
1075 {
1076     ToolTransformArgs *config = m_transaction->currentConfig();
1077 
1078     {
1079         KisTransformUtils::AnchorHolder keeper(config->transformAroundRotationCenter(), config);
1080         config->setAZ(normalizeAngle(config->aZ() + M_PI_2));
1081     }
1082 
1083     notifyConfigChanged();
1084     notifyEditingFinished();
1085 }
1086 
1087 void KisToolTransformConfigWidget::slotRotateCCW()
1088 {
1089     ToolTransformArgs *config = m_transaction->currentConfig();
1090 
1091     {
1092         KisTransformUtils::AnchorHolder keeper(config->transformAroundRotationCenter(), config);
1093         config->setAZ(normalizeAngle(config->aZ() - M_PI_2));
1094     }
1095 
1096     notifyConfigChanged();
1097     notifyEditingFinished();
1098 }
1099 
1100 
1101 // change free transform setting we want to alter (all radio buttons call this)
1102 void KisToolTransformConfigWidget::slotTransformAreaVisible(bool value)
1103 {
1104     Q_UNUSED(value);
1105 
1106     //QCheckBox sender = (QCheckBox)(*)(QObject::sender());
1107     QString senderName = QObject::sender()->objectName();
1108 
1109 
1110     // only show setting with what we have selected
1111     rotationGroup->hide();
1112     shearGroup->hide();
1113     scaleGroup->hide();
1114     moveGroup->hide();
1115 
1116 
1117     if ("freeMoveRadioButton" == senderName)
1118     {
1119         moveGroup->show();
1120     }
1121     else  if ("freeShearRadioButton" == senderName)
1122     {
1123         shearGroup->show();
1124     }
1125     else if ("freeScaleRadioButton" == senderName)
1126     {
1127         scaleGroup->show();
1128     }
1129     else
1130     {
1131         rotationGroup->show();
1132     }
1133 
1134 }
1135 
1136 void KisToolTransformConfigWidget::slotSetKeepAspectRatio(bool value)
1137 {
1138     if (m_uiSlotsBlocked) return;
1139 
1140     ToolTransformArgs *config = m_transaction->currentConfig();
1141     config->setKeepAspectRatio(value);
1142 
1143     if (value) {
1144        blockNotifications();
1145        int tmpXScaleBox = scaleXBox->value();
1146        int tmpYScaleBox = scaleYBox->value();
1147        m_scaleRatio = (tmpXScaleBox / (double) tmpYScaleBox);
1148        unblockNotifications();
1149     }
1150 
1151     notifyConfigChanged();
1152 }
1153 
1154 void KisToolTransformConfigWidget::slotSetWarpAlpha(qreal value)
1155 {
1156     if (m_uiSlotsBlocked) return;
1157 
1158     ToolTransformArgs *config = m_transaction->currentConfig();
1159 
1160     config->setAlpha((double)value);
1161     notifyConfigChanged();
1162     notifyEditingFinished();
1163 }
1164 
1165 void KisToolTransformConfigWidget::slotSetWarpDensity(int value)
1166 {
1167     if (m_uiSlotsBlocked) return;
1168     setDefaultWarpPoints(value);
1169 }
1170 
1171 void KisToolTransformConfigWidget::setDefaultWarpPoints(int pointsPerLine)
1172 {
1173     ToolTransformArgs *config = m_transaction->currentConfig();
1174 
1175     KisTransformUtils::setDefaultWarpPoints(pointsPerLine, m_transaction, config);
1176     notifyConfigChanged();
1177 }
1178 
1179 void KisToolTransformConfigWidget::activateCustomWarpPoints(bool enabled)
1180 {
1181     ToolTransformArgs *config = m_transaction->currentConfig();
1182 
1183     densityBox->setEnabled(!enabled);
1184     customWarpWidget->setEnabled(enabled);
1185 
1186     if (!enabled) {
1187         config->setEditingTransformPoints(false);
1188         setDefaultWarpPoints(densityBox->value());
1189         config->setWarpCalculation(KisWarpTransformWorker::WarpCalculation::GRID);
1190     } else {
1191         config->setEditingTransformPoints(true);
1192         config->setWarpCalculation(KisWarpTransformWorker::WarpCalculation::DRAW);
1193         setDefaultWarpPoints(0);
1194     }
1195 
1196 
1197 
1198 
1199     updateLockPointsButtonCaption();
1200 }
1201 
1202 void KisToolTransformConfigWidget::slotWarpDefaultPointsButtonClicked(bool value)
1203 {
1204     if (m_uiSlotsBlocked) return;
1205 
1206     activateCustomWarpPoints(!value);
1207 }
1208 
1209 void KisToolTransformConfigWidget::slotWarpCustomPointsButtonClicked(bool value)
1210 {
1211     if (m_uiSlotsBlocked) return;
1212 
1213     activateCustomWarpPoints(value);
1214 }
1215 
1216 void KisToolTransformConfigWidget::slotWarpResetPointsButtonClicked()
1217 {
1218     if (m_uiSlotsBlocked) return;
1219 
1220     activateCustomWarpPoints(true);
1221 }
1222 
1223 void KisToolTransformConfigWidget::slotWarpLockPointsButtonClicked()
1224 {
1225     if (m_uiSlotsBlocked) return;
1226 
1227     ToolTransformArgs *config = m_transaction->currentConfig();
1228     config->setEditingTransformPoints(!config->isEditingTransformPoints());
1229 
1230     if (config->isEditingTransformPoints()) {
1231         // reinit the transf points to their original value
1232         ToolTransformArgs *config = m_transaction->currentConfig();
1233         int nbPoints = config->origPoints().size();
1234         for (int i = 0; i < nbPoints; ++i) {
1235             config->transfPoint(i) = config->origPoint(i);
1236         }
1237     }
1238 
1239     updateLockPointsButtonCaption();
1240     notifyConfigChanged();
1241 }
1242 
1243 void KisToolTransformConfigWidget::slotWarpTypeChanged(int index)
1244 {
1245     if (m_uiSlotsBlocked) return;
1246 
1247     ToolTransformArgs *config = m_transaction->currentConfig();
1248 
1249     switch (index) {
1250     case KisWarpTransformWorker::AFFINE_TRANSFORM:
1251     case KisWarpTransformWorker::SIMILITUDE_TRANSFORM:
1252     case KisWarpTransformWorker::RIGID_TRANSFORM:
1253         config->setWarpType((KisWarpTransformWorker::WarpType)index);
1254         break;
1255     default:
1256         config->setWarpType(KisWarpTransformWorker::RIGID_TRANSFORM);
1257         break;
1258     }
1259 
1260     notifyConfigChanged();
1261 }
1262 
1263 void KisToolTransformConfigWidget::slotCageOptionsChanged(int value)
1264 {
1265     if ( value == 0)
1266     {
1267         slotEditCagePoints(true);
1268     }
1269     else
1270     {
1271         slotEditCagePoints(false);
1272     }
1273 
1274     notifyEditingFinished();
1275 }
1276 
1277 void KisToolTransformConfigWidget::slotEditCagePoints(bool value)
1278 {
1279     if (m_uiSlotsBlocked) return;
1280 
1281     ToolTransformArgs *config = m_transaction->currentConfig();
1282     config->refTransformedPoints() = config->origPoints();
1283 
1284     config->setEditingTransformPoints(value);
1285     notifyConfigChanged();
1286 }
1287 
1288 void KisToolTransformConfigWidget::slotGranularityChanged(QString value)
1289 {
1290     if (m_uiSlotsBlocked) return;
1291     KIS_SAFE_ASSERT_RECOVER_RETURN(value.toInt() > 1);
1292     ToolTransformArgs *config = m_transaction->currentConfig();
1293     config->setPixelPrecision(value.toInt());
1294     notifyConfigChanged();
1295 }
1296 
1297 void KisToolTransformConfigWidget::slotPreviewGranularityChanged(QString value)
1298 {
1299     if (m_uiSlotsBlocked) return;
1300     KIS_SAFE_ASSERT_RECOVER_RETURN(value.toInt() > 1);
1301     ToolTransformArgs *config = m_transaction->currentConfig();
1302     config->setPreviewPixelPrecision(value.toInt());
1303     notifyConfigChanged();
1304 }
1305 
1306 void KisToolTransformConfigWidget::slotMeshSizeChanged()
1307 {
1308     if (m_uiSlotsBlocked) return;
1309 
1310     ToolTransformArgs *config = m_transaction->currentConfig();
1311     KisBezierTransformMesh &mesh = *config->meshTransform();
1312 
1313     if (mesh.size().width() != intNumColumns->value() + 1) {
1314         mesh.reshapeMeshHorizontally(intNumColumns->value() + 1);
1315     }
1316 
1317     if (mesh.size().height() != intNumRows->value() + 1) {
1318         mesh.reshapeMeshVertically(intNumRows->value() + 1);
1319     }
1320 
1321     notifyConfigChanged();
1322 }
1323 
1324 void KisToolTransformConfigWidget::slotMeshShowHandlesChanged()
1325 {
1326     if (m_uiSlotsBlocked) return;
1327     ToolTransformArgs *config = m_transaction->currentConfig();
1328     config->setMeshShowHandles(this->chkShowControlPoints->isChecked());
1329     notifyConfigChanged();
1330     notifyEditingFinished();
1331 }
1332 
1333 void KisToolTransformConfigWidget::slotMeshSymmetricalHandlesChanged()
1334 {
1335     if (m_uiSlotsBlocked) return;
1336     ToolTransformArgs *config = m_transaction->currentConfig();
1337     config->setMeshSymmetricalHandles(this->chkSymmetricalHandles->isChecked());
1338     notifyConfigChanged();
1339     notifyEditingFinished();
1340 }
1341 
1342 void KisToolTransformConfigWidget::slotMeshScaleHandlesChanged()
1343 {
1344     if (m_uiSlotsBlocked) return;
1345     ToolTransformArgs *config = m_transaction->currentConfig();
1346     config->setMeshScaleHandles(this->chkScaleHandles->isChecked());
1347     notifyConfigChanged();
1348     notifyEditingFinished();
1349 }
1350 
1351 void KisToolTransformConfigWidget::slotPreviewChanged(int index)
1352 {
1353     KConfigGroup group = KSharedConfig::openConfig()->group("KisToolTransform");
1354     switch(index) {
1355     case 0:
1356         group.writeEntry("useOverlayPreviewStyle", false);
1357         group.writeEntry("forceLodMode", false);
1358         break;
1359     case 1:
1360         group.writeEntry("useOverlayPreviewStyle", false);
1361         group.writeEntry("forceLodMode", true);
1362         break;
1363     default:
1364         group.writeEntry("useOverlayPreviewStyle", true);
1365     }
1366 
1367     /**
1368      * We don't use global config notifier here, because it
1369      * requires too many updates in the GUI, which can theoretically
1370      * lead to deadlocks
1371      */
1372     emit sigUpdateGlobalConfig();
1373     emit sigRestartAndContinueTransform();
1374 }