1 /*
2  *  Copyright (c) 2013 Dmitry Kazakov <dimula73@gmail.com>
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  */
18 
19 #include "kis_tool_transform_config_widget.h"
20 
21 #include <kis_icon.h>
22 #include "rotation_icons.h"
23 #include "kis_canvas2.h"
24 #include <KisSignalMapper.h>
25 #include "kis_liquify_properties.h"
26 
27 #include "KisMainWindow.h"
28 #include "KisViewManager.h"
29 #include "kis_transform_utils.h"
30 
31 
sign(T x)32 template<typename T> inline T sign(T x) {
33     return x > 0 ? 1 : x == (T)0 ? 0 : -1;
34 }
35 
36 const int KisToolTransformConfigWidget::DEFAULT_POINTS_PER_LINE = 3;
37 
38 
KisToolTransformConfigWidget(TransformTransactionProperties * transaction,KisCanvas2 * canvas,bool workRecursively,QWidget * parent)39 KisToolTransformConfigWidget::KisToolTransformConfigWidget(TransformTransactionProperties *transaction, KisCanvas2 *canvas, bool workRecursively, QWidget *parent)
40     : QWidget(parent),
41       m_transaction(transaction),
42       m_notificationsBlocked(0),
43       m_uiSlotsBlocked(0),
44       m_configChanged(false)
45 {
46     setupUi(this);
47     chkWorkRecursively->setIcon(KisIconUtils::loadIcon("krita_tool_transform_recursive"));
48     flipXButton->setIcon(KisIconUtils::loadIcon("transform_icons_mirror_x"));
49     flipYButton->setIcon(KisIconUtils::loadIcon("transform_icons_mirror_y"));
50     rotateCWButton->setIcon(KisIconUtils::loadIcon("transform_icons_rotate_cw"));
51     rotateCCWButton->setIcon(KisIconUtils::loadIcon("transform_icons_rotate_ccw"));
52 
53     chkWorkRecursively->setChecked(workRecursively);
54     connect(chkWorkRecursively, SIGNAL(toggled(bool)), this, SIGNAL(sigRestartTransform()));
55 
56     // Granularity can only be specified in the power of 2's
57     QStringList granularityValues{"4","8","16","32"};
58     changeGranularity->addItems(granularityValues);
59     changeGranularity->setCurrentIndex(1);
60     granularityPreview->addItems(granularityValues);
61     granularityPreview->setCurrentIndex(2);
62 
63     connect(changeGranularity,SIGNAL(currentIndexChanged(QString)),
64             this,SLOT(slotGranularityChanged(QString)));
65     connect(granularityPreview, SIGNAL(currentIndexChanged(QString)),
66             this,SLOT(slotPreviewGranularityChanged(QString)));
67 
68     // Init Filter  combo
69     cmbFilter->setIDList(KisFilterStrategyRegistry::instance()->listKeys());
70     cmbFilter->setCurrent("Bicubic");
71     cmbFilter->setToolTip(i18nc("@info:tooltip",
72                                 "<p>Select filtering mode:\n"
73                                 "<ul>"
74                                 "<li><b>Nearest neighbor</b> for pixel art. Does not produce new color.</li>"
75                                 "<li><b>Bilinear</b> for areas with uniform color to avoid artifacts.</li>"
76                                 "<li><b>Bicubic</b> for smoother results.</li>"
77                                 "<li><b>Lanczos3</b> for sharp results. May produce aerials.</li>"
78                                 "</ul></p>"));
79     connect(cmbFilter, SIGNAL(activated(KoID)),
80             this, SLOT(slotFilterChanged(KoID)));
81 
82     // Init Warp Type combo
83     cmbWarpType->insertItem(KisWarpTransformWorker::AFFINE_TRANSFORM,i18n("Default (Affine)"));
84     cmbWarpType->insertItem(KisWarpTransformWorker::RIGID_TRANSFORM,i18n("Strong (Rigid)"));
85     cmbWarpType->insertItem(KisWarpTransformWorker::SIMILITUDE_TRANSFORM,i18n("Strongest (Similitude)"));
86     cmbWarpType->setCurrentIndex(KisWarpTransformWorker::AFFINE_TRANSFORM);
87     connect(cmbWarpType, SIGNAL(currentIndexChanged(int)), this, SLOT(slotWarpTypeChanged(int)));
88 
89     // Init Rotation Center buttons
90     m_handleDir[0] = QPointF(1, 0);
91     m_handleDir[1] = QPointF(1, -1);
92     m_handleDir[2] = QPointF(0, -1);
93     m_handleDir[3] = QPointF(-1, -1);
94     m_handleDir[4] = QPointF(-1, 0);
95     m_handleDir[5] = QPointF(-1, 1);
96     m_handleDir[6] = QPointF(0, 1);
97     m_handleDir[7] = QPointF(1, 1);
98     m_handleDir[8] = QPointF(0, 0); // also add the center
99 
100     m_rotationCenterButtons = new QButtonGroup(0);
101     // we set the ids to match m_handleDir
102     m_rotationCenterButtons->addButton(middleRightButton, 0);
103     m_rotationCenterButtons->addButton(topRightButton, 1);
104     m_rotationCenterButtons->addButton(middleTopButton, 2);
105     m_rotationCenterButtons->addButton(topLeftButton, 3);
106     m_rotationCenterButtons->addButton(middleLeftButton, 4);
107     m_rotationCenterButtons->addButton(bottomLeftButton, 5);
108     m_rotationCenterButtons->addButton(middleBottomButton, 6);
109     m_rotationCenterButtons->addButton(bottomRightButton, 7);
110     m_rotationCenterButtons->addButton(centerButton, 8);
111 
112     QToolButton *nothingSelected = new QToolButton(0);
113     nothingSelected->setCheckable(true);
114     nothingSelected->setAutoExclusive(true);
115     nothingSelected->hide(); // a convenient button for when no button is checked in the group
116     m_rotationCenterButtons->addButton(nothingSelected, 9);
117 
118 
119     // initialize values for free transform sliders
120     shearXBox->setSuffix(QChar(Qt::Key_Percent));
121     shearYBox->setSuffix(QChar(Qt::Key_Percent));
122     shearXBox->setRange(-500, 500, 2);
123     shearYBox->setRange(-500, 500, 2);
124     shearXBox->setSingleStep(1);
125     shearYBox->setSingleStep(1);
126     shearXBox->setValue(0.0);
127     shearYBox->setValue(0.0);
128 
129 
130     translateXBox->setSuffix(i18n(" px"));
131     translateYBox->setSuffix(i18n(" px"));
132     translateXBox->setRange(-10000, 10000);
133     translateYBox->setRange(-10000, 10000);
134 
135 
136     scaleXBox->setRange(-10000, 10000);
137     scaleYBox->setRange(-10000, 10000);
138     scaleXBox->setValue(100.0);
139     scaleYBox->setValue(100.0);
140 
141     m_scaleRatio = 1.0;
142 
143 
144     aXBox->setIncreasingDirection(KisAngleGauge::IncreasingDirection_Clockwise);
145     aYBox->setIncreasingDirection(KisAngleGauge::IncreasingDirection_Clockwise);
146     aZBox->setIncreasingDirection(KisAngleGauge::IncreasingDirection_Clockwise);
147     aXBox->setFlipOptionsMode(KisAngleSelector::FlipOptionsMode_MenuButton);
148     aYBox->setFlipOptionsMode(KisAngleSelector::FlipOptionsMode_MenuButton);
149     aZBox->setFlipOptionsMode(KisAngleSelector::FlipOptionsMode_MenuButton);
150 
151 
152     connect(m_rotationCenterButtons, SIGNAL(buttonPressed(int)), this, SLOT(slotRotationCenterChanged(int)));
153     connect(btnTransformAroundPivotPoint, SIGNAL(clicked(bool)), this, SLOT(slotTransformAroundRotationCenter(bool)));
154 
155     // Init Free Transform Values
156     connect(scaleXBox, SIGNAL(valueChanged(int)), this, SLOT(slotSetScaleX(int)));
157     connect(scaleYBox, SIGNAL(valueChanged(int)), this, SLOT(slotSetScaleY(int)));
158     connect(shearXBox, SIGNAL(valueChanged(qreal)), this, SLOT(slotSetShearX(qreal)));
159     connect(shearYBox, SIGNAL(valueChanged(qreal)), this, SLOT(slotSetShearY(qreal)));
160     connect(translateXBox, SIGNAL(valueChanged(int)), this, SLOT(slotSetTranslateX(int)));
161     connect(translateYBox, SIGNAL(valueChanged(int)), this, SLOT(slotSetTranslateY(int)));
162     connect(aXBox, SIGNAL(angleChanged(qreal)), this, SLOT(slotSetAX(qreal)));
163     connect(aYBox, SIGNAL(angleChanged(qreal)), this, SLOT(slotSetAY(qreal)));
164     connect(aZBox, SIGNAL(angleChanged(qreal)), this, SLOT(slotSetAZ(qreal)));
165     connect(aspectButton, SIGNAL(keepAspectRatioChanged(bool)), this, SLOT(slotSetKeepAspectRatio(bool)));
166     connect(flipXButton, SIGNAL(clicked(bool)), this, SLOT(slotFlipX()));
167     connect(flipYButton, SIGNAL(clicked(bool)), this, SLOT(slotFlipY()));
168     connect(rotateCWButton, SIGNAL(clicked(bool)), this, SLOT(slotRotateCW()));
169     connect(rotateCCWButton, SIGNAL(clicked(bool)), this, SLOT(slotRotateCCW()));
170 
171     // toggle visibility of different free buttons
172     connect(freeMoveRadioButton, SIGNAL(clicked(bool)), SLOT(slotTransformAreaVisible(bool)));
173     connect(freeRotationRadioButton, SIGNAL(clicked(bool)), SLOT(slotTransformAreaVisible(bool)));
174     connect(freeScaleRadioButton, SIGNAL(clicked(bool)), SLOT(slotTransformAreaVisible(bool)));
175     connect(freeShearRadioButton, SIGNAL(clicked(bool)), SLOT(slotTransformAreaVisible(bool)));
176 
177     // only first group for free transform
178     rotationGroup->hide();
179     moveGroup->show();
180     scaleGroup->hide();
181     shearGroup->hide();
182 
183 
184 
185     // Init Warp Transform Values
186     alphaBox->setSingleStep(0.1);
187     alphaBox->setRange(0, 10, 1);
188 
189     connect(alphaBox, SIGNAL(valueChanged(qreal)), this, SLOT(slotSetWarpAlpha(qreal)));
190     connect(densityBox, SIGNAL(valueChanged(int)), this, SLOT(slotSetWarpDensity(int)));
191 
192     connect(defaultRadioButton, SIGNAL(clicked(bool)), this, SLOT(slotWarpDefaultPointsButtonClicked(bool)));
193     connect(customRadioButton, SIGNAL(clicked(bool)), this, SLOT(slotWarpCustomPointsButtonClicked(bool)));
194     connect(lockUnlockPointsButton, SIGNAL(clicked()), this, SLOT(slotWarpLockPointsButtonClicked()));
195     connect(resetPointsButton, SIGNAL(clicked()), this, SLOT(slotWarpResetPointsButtonClicked()));
196 
197     // Init Cage Transform Values
198     cageTransformButtonGroup->setId(cageAddEditRadio, 0); // we need to set manually since Qt Designer generates negative by default
199     cageTransformButtonGroup->setId(cageDeformRadio, 1);
200     connect(cageTransformButtonGroup, SIGNAL(buttonClicked(int)), this, SLOT(slotCageOptionsChanged(int)));
201 
202     // Init Liquify Transform Values
203     liquifySizeSlider->setRange(KisLiquifyProperties::minSize(),
204                                 KisLiquifyProperties::maxSize(), 2);
205     liquifySizeSlider->setExponentRatio(4);
206     liquifySizeSlider->setValue(60.0);
207     connect(liquifySizeSlider, SIGNAL(valueChanged(qreal)), this, SLOT(liquifySizeChanged(qreal)));
208     liquifySizeSlider->setToolTip(i18nc("@info:tooltip", "Size of the deformation brush"));
209 
210     liquifyAmountSlider->setRange(0.0, 1.0, 2);
211     liquifyAmountSlider->setValue(0.05);
212     connect(liquifyAmountSlider, SIGNAL(valueChanged(qreal)), this, SLOT(liquifyAmountChanged(qreal)));
213     liquifyAmountSlider->setToolTip(i18nc("@info:tooltip", "Amount of the deformation you get"));
214 
215     liquifyFlowSlider->setRange(0.0, 1.0, 2);
216     liquifyFlowSlider->setValue(1.0);
217     connect(liquifyFlowSlider, SIGNAL(valueChanged(qreal)), this, SLOT(liquifyFlowChanged(qreal)));
218     liquifyFlowSlider->setToolTip(i18nc("@info:tooltip", "When in non-buildup mode, shows how fast the deformation limit is reached."));
219 
220     buidupModeComboBox->setCurrentIndex(0); // set to build-up mode by default
221     connect(buidupModeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(liquifyBuildUpChanged(int)));
222     buidupModeComboBox->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>");
223 
224     liquifySpacingSlider->setRange(0.0, 3.0, 2);
225     liquifySizeSlider->setExponentRatio(3);
226     liquifySpacingSlider->setSingleStep(0.01);
227     liquifySpacingSlider->setValue(0.2);
228     connect(liquifySpacingSlider, SIGNAL(valueChanged(qreal)), this, SLOT(liquifySpacingChanged(qreal)));
229     liquifySpacingSlider->setToolTip(i18nc("@info:tooltip", "Space between two sequential applications of the deformation"));
230 
231     liquifySizePressureBox->setChecked(true);
232     connect(liquifySizePressureBox, SIGNAL(toggled(bool)), this, SLOT(liquifySizePressureChanged(bool)));
233     liquifySizePressureBox->setToolTip(i18nc("@info:tooltip", "Scale <b>Size</b> value according to current stylus pressure"));
234 
235     liquifyAmountPressureBox->setChecked(true);
236     connect(liquifyAmountPressureBox, SIGNAL(toggled(bool)), this, SLOT(liquifyAmountPressureChanged(bool)));
237     liquifyAmountPressureBox->setToolTip(i18nc("@info:tooltip", "Scale <b>Amount</b> value according to current stylus pressure"));
238 
239     liquifyReverseDirectionChk->setChecked(false);
240     connect(liquifyReverseDirectionChk, SIGNAL(toggled(bool)), this, SLOT(liquifyReverseDirectionChanged(bool)));
241     liquifyReverseDirectionChk->setToolTip(i18nc("@info:tooltip", "Reverse direction of the current deformation tool"));
242 
243     KisSignalMapper *liquifyModeMapper = new KisSignalMapper(this);
244     connect(liquifyMove, SIGNAL(toggled(bool)), liquifyModeMapper, SLOT(map()));
245     connect(liquifyScale, SIGNAL(toggled(bool)), liquifyModeMapper, SLOT(map()));
246     connect(liquifyRotate, SIGNAL(toggled(bool)), liquifyModeMapper, SLOT(map()));
247     connect(liquifyOffset, SIGNAL(toggled(bool)), liquifyModeMapper, SLOT(map()));
248     connect(liquifyUndo, SIGNAL(toggled(bool)), liquifyModeMapper, SLOT(map()));
249     liquifyModeMapper->setMapping(liquifyMove, (int)KisLiquifyProperties::MOVE);
250     liquifyModeMapper->setMapping(liquifyScale, (int)KisLiquifyProperties::SCALE);
251     liquifyModeMapper->setMapping(liquifyRotate, (int)KisLiquifyProperties::ROTATE);
252     liquifyModeMapper->setMapping(liquifyOffset, (int)KisLiquifyProperties::OFFSET);
253     liquifyModeMapper->setMapping(liquifyUndo, (int)KisLiquifyProperties::UNDO);
254     connect(liquifyModeMapper, SIGNAL(mapped(int)), SLOT(slotLiquifyModeChanged(int)));
255 
256     liquifyMove->setToolTip(i18nc("@info:tooltip", "Move: drag the image along the brush stroke"));
257     liquifyScale->setToolTip(i18nc("@info:tooltip", "Scale: grow/shrink image under cursor"));
258     liquifyRotate->setToolTip(i18nc("@info:tooltip", "Rotate: twirl image under cursor"));
259     liquifyOffset->setToolTip(i18nc("@info:tooltip", "Offset: shift the image to the right of the stroke direction"));
260     liquifyUndo->setToolTip(i18nc("@info:tooltip", "Undo: erase actions of other tools"));
261 
262     // Connect all edit boxes to the Editing Finished signal
263     connect(densityBox, SIGNAL(editingFinished()), this, SLOT(notifyEditingFinished()));
264 
265 
266 
267     // Connect other widget (not having editingFinished signal) to
268     // the same slot. From Qt 4.6 onwards the sequence of the signal
269     // delivery is definite.
270     connect(cmbFilter, SIGNAL(activated(KoID)), this, SLOT(notifyEditingFinished()));
271     connect(cmbWarpType, SIGNAL(currentIndexChanged(int)), this, SLOT(notifyEditingFinished()));
272     connect(m_rotationCenterButtons, SIGNAL(buttonPressed(int)), this, SLOT(notifyEditingFinished()));
273     connect(aspectButton, SIGNAL(keepAspectRatioChanged(bool)), this, SLOT(notifyEditingFinished()));
274 
275     connect(lockUnlockPointsButton, SIGNAL(clicked()), this, SLOT(notifyEditingFinished()));
276     connect(resetPointsButton, SIGNAL(clicked()), this, SLOT(notifyEditingFinished()));
277 
278     connect(defaultRadioButton, SIGNAL(clicked(bool)), this, SLOT(notifyEditingFinished()));
279     connect(customRadioButton, SIGNAL(clicked(bool)), this, SLOT(notifyEditingFinished()));
280 
281     // Liquify
282     //
283     // liquify brush options do not affect the image directly and are not
284     // saved to undo, so we don't emit notifyEditingFinished() for them
285 
286     // Connect Apply/Reset buttons
287     connect(buttonBox, SIGNAL(clicked(QAbstractButton*)), this, SLOT(slotButtonBoxClicked(QAbstractButton*)));
288 
289     // Mesh. First set up, then connect.
290     intNumRows->setRange(1, 999);
291     intNumColumns->setRange(1, 999);
292 
293     connect(chkShowControlPoints, SIGNAL(toggled(bool)), this, SLOT(slotMeshShowHandlesChanged()));
294     connect(chkSymmetricalHandles, SIGNAL(toggled(bool)), this, SLOT(slotMeshSymmetricalHandlesChanged()));
295     connect(chkScaleHandles, SIGNAL(toggled(bool)), this, SLOT(slotMeshScaleHandlesChanged()));
296     connect(intNumColumns, SIGNAL(valueChanged(int)), this, SLOT(slotMeshSizeChanged()));
297     connect(intNumRows, SIGNAL(valueChanged(int)), this, SLOT(slotMeshSizeChanged()));
298     connect(intNumColumns, SIGNAL(editingFinished()), this, SLOT(notifyEditingFinished()));
299     connect(intNumRows, SIGNAL(editingFinished()), this, SLOT(notifyEditingFinished()));
300 
301     // Mode switch buttons
302     connect(freeTransformButton, SIGNAL(clicked(bool)), this, SLOT(slotSetFreeTransformModeButtonClicked(bool)));
303     connect(warpButton, SIGNAL(clicked(bool)), this, SLOT(slotSetWarpModeButtonClicked(bool)));
304     connect(cageButton, SIGNAL(clicked(bool)), this, SLOT(slotSetCageModeButtonClicked(bool)));
305     connect(perspectiveTransformButton, SIGNAL(clicked(bool)), this, SLOT(slotSetPerspectiveModeButtonClicked(bool)));
306     connect(liquifyButton, SIGNAL(clicked(bool)), this, SLOT(slotSetLiquifyModeButtonClicked(bool)));
307     connect(meshButton, SIGNAL(clicked(bool)), this, SLOT(slotSetMeshModeButtonClicked(bool)));
308 
309 
310     tooBigLabelWidget->hide();
311 
312     connect(canvas->viewManager()->mainWindow(), SIGNAL(themeChanged()), SLOT(slotUpdateIcons()), Qt::UniqueConnection);
313     slotUpdateIcons();
314 }
315 
slotUpdateIcons()316 void KisToolTransformConfigWidget::slotUpdateIcons()
317 {
318     freeTransformButton->setIcon(KisIconUtils::loadIcon("transform_icons_main"));
319     warpButton->setIcon(KisIconUtils::loadIcon("transform_icons_warp"));
320     cageButton->setIcon(KisIconUtils::loadIcon("transform_icons_cage"));
321     perspectiveTransformButton->setIcon(KisIconUtils::loadIcon("transform_icons_perspective"));
322     liquifyButton->setIcon(KisIconUtils::loadIcon("transform_icons_liquify_main"));
323     meshButton->setIcon(KisIconUtils::loadIcon("transform_icons_mesh"));
324 
325     liquifyMove->setIcon(KisIconUtils::loadIcon("transform_icons_liquify_move"));
326     liquifyScale->setIcon(KisIconUtils::loadIcon("transform_icons_liquify_resize"));
327     liquifyRotate->setIcon(KisIconUtils::loadIcon("transform_icons_liquify_rotate"));
328     liquifyOffset->setIcon(KisIconUtils::loadIcon("transform_icons_liquify_offset"));
329     liquifyUndo->setIcon(KisIconUtils::loadIcon("transform_icons_liquify_erase"));
330 
331 
332 
333     middleRightButton->setIcon(KisIconUtils::loadIcon("arrow-right"));
334     topRightButton->setIcon(KisIconUtils::loadIcon("arrow-topright"));
335     middleTopButton->setIcon(KisIconUtils::loadIcon("arrow-up"));
336     topLeftButton->setIcon(KisIconUtils::loadIcon("arrow-topleft"));
337     middleLeftButton->setIcon(KisIconUtils::loadIcon("arrow-left"));
338     bottomLeftButton->setIcon(KisIconUtils::loadIcon("arrow-downleft"));
339     middleBottomButton->setIcon(KisIconUtils::loadIcon("arrow-down"));
340     bottomRightButton->setIcon(KisIconUtils::loadIcon("arrow-downright"));
341 
342     btnTransformAroundPivotPoint->setIcon(KisIconUtils::loadIcon("pivot-point"));
343 
344 
345     // pressure icons
346     liquifySizePressureBox->setIcon(KisIconUtils::loadIcon("transform_icons_penPressure"));
347     liquifyAmountPressureBox->setIcon(KisIconUtils::loadIcon("transform_icons_penPressure"));
348 }
349 
updateLiquifyControls()350 void KisToolTransformConfigWidget::updateLiquifyControls()
351 {
352     blockUiSlots();
353 
354     ToolTransformArgs *config = m_transaction->currentConfig();
355     KisLiquifyProperties *props =
356         config->liquifyProperties();
357 
358     const bool useWashMode = props->useWashMode();
359 
360     liquifySizeSlider->setValue(props->size());
361     liquifyAmountSlider->setValue(props->amount());
362     liquifyFlowSlider->setValue(props->flow());
363     buidupModeComboBox->setCurrentIndex(useWashMode);
364 
365     liquifySpacingSlider->setValue(props->spacing());
366     liquifySizePressureBox->setChecked(props->sizeHasPressure());
367     liquifyAmountPressureBox->setChecked(props->amountHasPressure());
368     liquifyReverseDirectionChk->setChecked(props->reverseDirection());
369 
370 
371     KisLiquifyProperties::LiquifyMode mode =
372         static_cast<KisLiquifyProperties::LiquifyMode>(props->mode());
373 
374     bool canInverseDirection =
375         mode != KisLiquifyProperties::UNDO;
376 
377     bool canUseWashMode = mode != KisLiquifyProperties::UNDO;
378 
379     liquifyReverseDirectionChk->setEnabled(canInverseDirection);
380     liquifyFlowSlider->setEnabled(canUseWashMode && useWashMode);
381     buidupModeComboBox->setEnabled(canUseWashMode);
382 
383     const qreal maxAmount = canUseWashMode ? 5.0 : 1.0;
384     liquifyAmountSlider->setRange(0.0, maxAmount, 2);
385 
386     unblockUiSlots();
387 }
388 
slotLiquifyModeChanged(int value)389 void KisToolTransformConfigWidget::slotLiquifyModeChanged(int value)
390 {
391     if (m_uiSlotsBlocked) return;
392 
393     ToolTransformArgs *config = m_transaction->currentConfig();
394 
395     KisLiquifyProperties *props =
396         config->liquifyProperties();
397 
398     KisLiquifyProperties::LiquifyMode mode =
399         static_cast<KisLiquifyProperties::LiquifyMode>(value);
400 
401     if (mode == props->mode()) return;
402 
403     props->setMode(mode);
404     props->loadMode();
405 
406     updateLiquifyControls();
407 
408     notifyConfigChanged();
409 }
410 
liquifySizeChanged(qreal value)411 void KisToolTransformConfigWidget::liquifySizeChanged(qreal value)
412 {
413     if (m_uiSlotsBlocked) return;
414 
415     ToolTransformArgs *config = m_transaction->currentConfig();
416     KisLiquifyProperties *props =
417         config->liquifyProperties();
418 
419     props->setSize(value);
420     notifyConfigChanged();
421 }
422 
liquifyAmountChanged(qreal value)423 void KisToolTransformConfigWidget::liquifyAmountChanged(qreal value)
424 {
425     if (m_uiSlotsBlocked) return;
426 
427     ToolTransformArgs *config = m_transaction->currentConfig();
428     KisLiquifyProperties *props =
429         config->liquifyProperties();
430 
431     props->setAmount(value);
432     notifyConfigChanged();
433 }
434 
liquifyFlowChanged(qreal value)435 void KisToolTransformConfigWidget::liquifyFlowChanged(qreal value)
436 {
437     if (m_uiSlotsBlocked) return;
438 
439     ToolTransformArgs *config = m_transaction->currentConfig();
440     KisLiquifyProperties *props =
441         config->liquifyProperties();
442 
443     props->setFlow(value);
444     notifyConfigChanged();
445 }
446 
liquifyBuildUpChanged(int value)447 void KisToolTransformConfigWidget::liquifyBuildUpChanged(int value)
448 {
449     if (m_uiSlotsBlocked) return;
450 
451     ToolTransformArgs *config = m_transaction->currentConfig();
452     KisLiquifyProperties *props =
453         config->liquifyProperties();
454 
455     props->setUseWashMode(value); // 0 == build up mode / 1 == wash mode
456 
457     notifyConfigChanged();
458 
459     // we need to enable/disable flow slider
460     updateLiquifyControls();
461 }
462 
liquifySpacingChanged(qreal value)463 void KisToolTransformConfigWidget::liquifySpacingChanged(qreal value)
464 {
465     if (m_uiSlotsBlocked) return;
466 
467     ToolTransformArgs *config = m_transaction->currentConfig();
468     KisLiquifyProperties *props =
469         config->liquifyProperties();
470 
471     props->setSpacing(value);
472     notifyConfigChanged();
473 }
474 
liquifySizePressureChanged(bool value)475 void KisToolTransformConfigWidget::liquifySizePressureChanged(bool value)
476 {
477     if (m_uiSlotsBlocked) return;
478 
479     ToolTransformArgs *config = m_transaction->currentConfig();
480     KisLiquifyProperties *props =
481         config->liquifyProperties();
482 
483     props->setSizeHasPressure(value);
484     notifyConfigChanged();
485 }
486 
liquifyAmountPressureChanged(bool value)487 void KisToolTransformConfigWidget::liquifyAmountPressureChanged(bool value)
488 {
489     if (m_uiSlotsBlocked) return;
490 
491     ToolTransformArgs *config = m_transaction->currentConfig();
492     KisLiquifyProperties *props =
493         config->liquifyProperties();
494 
495     props->setAmountHasPressure(value);
496     notifyConfigChanged();
497 }
498 
liquifyReverseDirectionChanged(bool value)499 void KisToolTransformConfigWidget::liquifyReverseDirectionChanged(bool value)
500 {
501     if (m_uiSlotsBlocked) return;
502 
503     ToolTransformArgs *config = m_transaction->currentConfig();
504     KisLiquifyProperties *props =
505         config->liquifyProperties();
506 
507     props->setReverseDirection(value);
508     notifyConfigChanged();
509 }
510 
updateConfig(const ToolTransformArgs & config)511 void KisToolTransformConfigWidget::updateConfig(const ToolTransformArgs &config)
512 {
513     blockUiSlots();
514 
515     if (config.mode() == ToolTransformArgs::FREE_TRANSFORM ||
516         config.mode() == ToolTransformArgs::PERSPECTIVE_4POINT) {
517 
518         quickTransformGroup->setEnabled(config.mode() == ToolTransformArgs::FREE_TRANSFORM);
519 
520         stackedWidget->setCurrentIndex(0);
521 
522         bool freeTransformIsActive = config.mode() == ToolTransformArgs::FREE_TRANSFORM;
523 
524         if (freeTransformIsActive)
525         {
526             freeTransformButton->setChecked(true);
527         }
528         else
529         {
530             perspectiveTransformButton->setChecked(true);
531         }
532 
533         aXBox->setEnabled(freeTransformIsActive);
534         aYBox->setEnabled(freeTransformIsActive);
535         aZBox->setEnabled(freeTransformIsActive);
536         freeRotationRadioButton->setEnabled(freeTransformIsActive);
537 
538         scaleXBox->setValue(config.scaleX() * 100.);
539         scaleYBox->setValue(config.scaleY() * 100.);
540         shearXBox->setValue(config.shearX() * 100.);
541         shearYBox->setValue(config.shearY() * 100.);
542 
543         const QPointF anchorPoint = config.originalCenter() + config.rotationCenterOffset();
544         const KisTransformUtils::MatricesPack m(config);
545         const QPointF anchorPointView = m.finalTransform().map(anchorPoint);
546 
547         translateXBox->setValue(anchorPointView.x());
548         translateYBox->setValue(anchorPointView.y());
549 
550         aXBox->setAngle(normalizeAngleDegrees(kisRadiansToDegrees(config.aX())));
551         aYBox->setAngle(normalizeAngleDegrees(kisRadiansToDegrees(config.aY())));
552         aZBox->setAngle(normalizeAngleDegrees(kisRadiansToDegrees(config.aZ())));
553         aspectButton->setKeepAspectRatio(config.keepAspectRatio());
554         cmbFilter->setCurrent(config.filterId());
555 
556         QPointF pt = m_transaction->currentConfig()->rotationCenterOffset();
557         pt.rx() /= m_transaction->originalHalfWidth();
558         pt.ry() /= m_transaction->originalHalfHeight();
559 
560         for (int i = 0; i < 9; i++) {
561             if (qFuzzyCompare(m_handleDir[i].x(), pt.x()) &&
562                 qFuzzyCompare(m_handleDir[i].y(), pt.y())) {
563 
564                 m_rotationCenterButtons->button(i)->setChecked(true);
565                 break;
566             }
567         }
568 
569         btnTransformAroundPivotPoint->setChecked(config.transformAroundRotationCenter());
570 
571     } else if (config.mode() == ToolTransformArgs::WARP) {
572 
573         stackedWidget->setCurrentIndex(1);
574         warpButton->setChecked(true);
575 
576         if (config.defaultPoints()) {
577             densityBox->setValue(std::sqrt(config.numPoints()));
578         }
579 
580         cmbWarpType->setCurrentIndex((int)config.warpType());
581         defaultRadioButton->setChecked(config.defaultPoints());
582         customRadioButton->setChecked(!config.defaultPoints());
583         densityBox->setEnabled(config.defaultPoints());
584         customWarpWidget->setEnabled(!config.defaultPoints());
585 
586         updateLockPointsButtonCaption();
587 
588     } else if (config.mode() == ToolTransformArgs::CAGE) {
589 
590         // default UI options
591         resetUIOptions();
592 
593         // we need at least 3 point before we can start actively deforming
594         if (config.origPoints().size() >= 3)
595         {
596             cageTransformDirections->setText(i18n("Switch between editing and deforming cage"));
597             cageAddEditRadio->setVisible(true);
598             cageDeformRadio->setVisible(true);
599 
600             // update to correct radio button
601             if (config.isEditingTransformPoints())
602                 cageAddEditRadio->setChecked(true);
603             else
604                  cageDeformRadio->setChecked(true);
605 
606             changeGranularity->setCurrentIndex(log2(config.pixelPrecision()) - 2);
607             granularityPreview->setCurrentIndex(log2(config.previewPixelPrecision()) - 2);
608 
609         }
610 
611     } else if (config.mode() == ToolTransformArgs::LIQUIFY) {
612 
613         stackedWidget->setCurrentIndex(3);
614         liquifyButton->setChecked(true);
615 
616         const KisLiquifyProperties *props =
617             config.liquifyProperties();
618 
619         switch (props->mode()) {
620         case KisLiquifyProperties::MOVE:
621             liquifyMove->setChecked(true);
622             break;
623         case KisLiquifyProperties::SCALE:
624             liquifyScale->setChecked(true);
625             break;
626         case KisLiquifyProperties::ROTATE:
627             liquifyRotate->setChecked(true);
628             break;
629         case KisLiquifyProperties::OFFSET:
630             liquifyOffset->setChecked(true);
631             break;
632         case KisLiquifyProperties::UNDO:
633             liquifyUndo->setChecked(true);
634             break;
635         case KisLiquifyProperties::N_MODES:
636             qFatal("Unsupported mode");
637         }
638 
639         updateLiquifyControls();
640     } else if (config.mode() == ToolTransformArgs::MESH) {
641         stackedWidget->setCurrentIndex(4);
642         intNumColumns->setValue(config.meshTransform()->size().width() - 1);
643         intNumRows->setValue(config.meshTransform()->size().height() - 1);
644         chkShowControlPoints->setChecked(config.meshShowHandles());
645         chkSymmetricalHandles->setChecked(config.meshSymmetricalHandles());
646         chkScaleHandles->setChecked(config.meshScaleHandles());
647     }
648 
649     unblockUiSlots();
650 }
651 
updateLockPointsButtonCaption()652 void KisToolTransformConfigWidget::updateLockPointsButtonCaption()
653 {
654     ToolTransformArgs *config = m_transaction->currentConfig();
655 
656     if (config->isEditingTransformPoints()) {
657         lockUnlockPointsButton->setText(i18n("Lock Points"));
658     } else {
659         lockUnlockPointsButton->setText(i18n("Unlock Points"));
660     }
661 }
662 
setApplyResetDisabled(bool disabled)663 void KisToolTransformConfigWidget::setApplyResetDisabled(bool disabled)
664 {
665     QAbstractButton *applyButton = buttonBox->button(QDialogButtonBox::Apply);
666     QAbstractButton *resetButton = buttonBox->button(QDialogButtonBox::Reset);
667 
668     Q_ASSERT(applyButton);
669     Q_ASSERT(resetButton);
670 
671     applyButton->setDisabled(disabled);
672     resetButton->setDisabled(disabled);
673 }
674 
resetRotationCenterButtons()675 void KisToolTransformConfigWidget::resetRotationCenterButtons()
676 {
677     int checkedId = m_rotationCenterButtons->checkedId();
678 
679     if (checkedId >= 0 && checkedId <= 8) {
680         // uncheck the current checked button
681         m_rotationCenterButtons->button(9)->setChecked(true);
682     }
683 }
684 
workRecursively() const685 bool KisToolTransformConfigWidget::workRecursively() const
686 {
687     return chkWorkRecursively->isChecked();
688 }
689 
setTooBigLabelVisible(bool value)690 void KisToolTransformConfigWidget::setTooBigLabelVisible(bool value)
691 {
692     tooBigLabelWidget->setVisible(value);
693 }
694 
blockNotifications()695 void KisToolTransformConfigWidget::blockNotifications()
696 {
697     m_notificationsBlocked++;
698 }
699 
unblockNotifications()700 void KisToolTransformConfigWidget::unblockNotifications()
701 {
702     m_notificationsBlocked--;
703 }
704 
notifyConfigChanged()705 void KisToolTransformConfigWidget::notifyConfigChanged()
706 {
707     if (!m_notificationsBlocked) {
708         emit sigConfigChanged();
709     }
710     m_configChanged = true;
711 }
712 
blockUiSlots()713 void KisToolTransformConfigWidget::blockUiSlots()
714 {
715     m_uiSlotsBlocked++;
716 }
717 
unblockUiSlots()718 void KisToolTransformConfigWidget::unblockUiSlots()
719 {
720     m_uiSlotsBlocked--;
721 }
722 
notifyEditingFinished()723 void KisToolTransformConfigWidget::notifyEditingFinished()
724 {
725     if (m_uiSlotsBlocked || m_notificationsBlocked || !m_configChanged) return;
726 
727     emit sigEditingFinished();
728     m_configChanged = false;
729 }
730 
731 
resetUIOptions()732 void KisToolTransformConfigWidget::resetUIOptions()
733 {
734     // reset tool states since we are done (probably can encapsulate this later)
735     ToolTransformArgs *config = m_transaction->currentConfig();
736     if (config->mode() == ToolTransformArgs::CAGE)
737     {
738         cageAddEditRadio->setVisible(false);
739         cageAddEditRadio->setChecked(true);
740         cageDeformRadio->setVisible(false);
741         cageTransformDirections->setText(i18n("Create 3 points on the canvas to begin"));
742 
743         // ensure we are on the right options view
744         stackedWidget->setCurrentIndex(2);
745     }
746 
747 
748 
749 }
750 
slotButtonBoxClicked(QAbstractButton * button)751 void KisToolTransformConfigWidget::slotButtonBoxClicked(QAbstractButton *button)
752 {
753     QAbstractButton *applyButton = buttonBox->button(QDialogButtonBox::Apply);
754     QAbstractButton *resetButton = buttonBox->button(QDialogButtonBox::Reset);
755 
756     resetUIOptions();
757 
758     if (button == applyButton) {
759         emit sigApplyTransform();
760     }
761     else if (button == resetButton) {
762         emit sigCancelTransform();
763     }
764 
765 }
766 
slotSetMeshModeButtonClicked(bool value)767 void KisToolTransformConfigWidget::slotSetMeshModeButtonClicked(bool value)
768 {
769     if (!value) return;
770 
771     lblTransformType->setText(meshButton->toolTip());
772 
773     emit sigResetTransform(ToolTransformArgs::MESH);
774 }
775 
slotSetFreeTransformModeButtonClicked(bool value)776 void KisToolTransformConfigWidget::slotSetFreeTransformModeButtonClicked(bool value)
777 {
778     if (!value) return;
779 
780     lblTransformType->setText(freeTransformButton->toolTip());
781 
782     emit sigResetTransform(ToolTransformArgs::FREE_TRANSFORM);
783 }
784 
slotSetWarpModeButtonClicked(bool value)785 void KisToolTransformConfigWidget::slotSetWarpModeButtonClicked(bool value)
786 {
787     if (!value) return;
788 
789     lblTransformType->setText(warpButton->toolTip());
790 
791     emit sigResetTransform(ToolTransformArgs::WARP);
792 }
793 
slotSetCageModeButtonClicked(bool value)794 void KisToolTransformConfigWidget::slotSetCageModeButtonClicked(bool value)
795 {
796     if (!value) return;
797 
798     lblTransformType->setText(cageButton->toolTip());
799 
800     emit sigResetTransform(ToolTransformArgs::CAGE);
801 }
802 
slotSetLiquifyModeButtonClicked(bool value)803 void KisToolTransformConfigWidget::slotSetLiquifyModeButtonClicked(bool value)
804 {
805     if (!value) return;
806 
807     lblTransformType->setText(liquifyButton->toolTip());
808 
809     emit sigResetTransform(ToolTransformArgs::LIQUIFY);
810 }
811 
slotSetPerspectiveModeButtonClicked(bool value)812 void KisToolTransformConfigWidget::slotSetPerspectiveModeButtonClicked(bool value)
813 {
814     if (!value) return;
815 
816     lblTransformType->setText(perspectiveTransformButton->toolTip());
817 
818     emit sigResetTransform(ToolTransformArgs::PERSPECTIVE_4POINT);
819 }
820 
slotFilterChanged(const KoID & filterId)821 void KisToolTransformConfigWidget::slotFilterChanged(const KoID &filterId)
822 {
823     ToolTransformArgs *config = m_transaction->currentConfig();
824     config->setFilterId(filterId.id());
825     notifyConfigChanged();
826 }
827 
slotRotationCenterChanged(int index)828 void KisToolTransformConfigWidget::slotRotationCenterChanged(int index)
829 {
830     if (m_uiSlotsBlocked) return;
831 
832     if (index >= 0 && index <= 8) {
833         ToolTransformArgs *config = m_transaction->currentConfig();
834 
835         double i = m_handleDir[index].x();
836         double j = m_handleDir[index].y();
837 
838         config->setRotationCenterOffset(QPointF(i * m_transaction->originalHalfWidth(),
839                                                 j * m_transaction->originalHalfHeight()));
840 
841         notifyConfigChanged();
842         updateConfig(*config);
843     }
844 }
845 
slotTransformAroundRotationCenter(bool value)846 void KisToolTransformConfigWidget::slotTransformAroundRotationCenter(bool value)
847 {
848     if (m_uiSlotsBlocked) return;
849 
850     ToolTransformArgs *config = m_transaction->currentConfig();
851     config->setTransformAroundRotationCenter(value);
852     notifyConfigChanged();
853     notifyEditingFinished();
854 }
855 
slotSetScaleX(int value)856 void KisToolTransformConfigWidget::slotSetScaleX(int value)
857 {
858     if (m_uiSlotsBlocked) return;
859 
860     ToolTransformArgs *config = m_transaction->currentConfig();
861 
862     {
863         KisTransformUtils::AnchorHolder keeper(config->transformAroundRotationCenter(), config);
864         config->setScaleX(value / 100.);
865     }
866 
867     if (config->keepAspectRatio()) {
868 
869         blockNotifications();
870          int calculatedValue = int( value/ m_scaleRatio );
871 
872         scaleYBox->blockSignals(true);
873         scaleYBox->setValue(calculatedValue);
874         {
875             KisTransformUtils::AnchorHolder keeper(config->transformAroundRotationCenter(), config);
876             config->setScaleY(calculatedValue / 100.);
877         }
878         scaleYBox->blockSignals(false);
879 
880         unblockNotifications();
881     }
882 
883     notifyConfigChanged();
884     notifyEditingFinished();
885 }
886 
slotSetScaleY(int value)887 void KisToolTransformConfigWidget::slotSetScaleY(int value)
888 {
889     if (m_uiSlotsBlocked) return;
890 
891     ToolTransformArgs *config = m_transaction->currentConfig();
892 
893     {
894         KisTransformUtils::AnchorHolder keeper(config->transformAroundRotationCenter(), config);
895         config->setScaleY(value / 100.);
896     }
897 
898     if (config->keepAspectRatio()) {
899         blockNotifications();
900         int calculatedValue = int(m_scaleRatio * value);
901         scaleXBox->blockSignals(true);
902         scaleXBox->setValue(calculatedValue);
903         {
904             KisTransformUtils::AnchorHolder keeper(config->transformAroundRotationCenter(), config);
905             config->setScaleX(calculatedValue / 100.);
906         }
907         scaleXBox->blockSignals(false);
908         unblockNotifications();
909     }
910 
911     notifyConfigChanged();
912     notifyEditingFinished();
913 }
914 
slotSetShearX(qreal value)915 void KisToolTransformConfigWidget::slotSetShearX(qreal value)
916 {
917     if (m_uiSlotsBlocked) return;
918 
919     ToolTransformArgs *config = m_transaction->currentConfig();
920 
921     {
922         KisTransformUtils::AnchorHolder keeper(config->transformAroundRotationCenter(), config);
923         config->setShearX((double)value / 100.);
924     }
925 
926     notifyConfigChanged();
927     notifyEditingFinished();
928 }
929 
slotSetShearY(qreal value)930 void KisToolTransformConfigWidget::slotSetShearY(qreal value)
931 {
932     if (m_uiSlotsBlocked) return;
933 
934     ToolTransformArgs *config = m_transaction->currentConfig();
935 
936     {
937         KisTransformUtils::AnchorHolder keeper(config->transformAroundRotationCenter(), config);
938         config->setShearY((double)value / 100.);
939     }
940 
941 
942     notifyConfigChanged();
943     notifyEditingFinished();
944 }
945 
slotSetTranslateX(int value)946 void KisToolTransformConfigWidget::slotSetTranslateX(int value)
947 {
948     if (m_uiSlotsBlocked) return;
949 
950     ToolTransformArgs *config = m_transaction->currentConfig();
951 
952     const QPointF anchorPoint = config->originalCenter() + config->rotationCenterOffset();
953     const KisTransformUtils::MatricesPack m(*config);
954 
955     const QPointF anchorPointView = m.finalTransform().map(anchorPoint);
956     const QPointF newAnchorPointView(value, anchorPointView.y());
957     config->setTransformedCenter(config->transformedCenter() + newAnchorPointView - anchorPointView);
958     translateXBox->setValue(value);
959     notifyConfigChanged();
960 }
961 
slotSetTranslateY(int value)962 void KisToolTransformConfigWidget::slotSetTranslateY(int value)
963 {
964     if (m_uiSlotsBlocked) return;
965 
966     ToolTransformArgs *config = m_transaction->currentConfig();
967 
968     const QPointF anchorPoint = config->originalCenter() + config->rotationCenterOffset();
969     const KisTransformUtils::MatricesPack m(*config);
970 
971     const QPointF anchorPointView = m.finalTransform().map(anchorPoint);
972     const QPointF newAnchorPointView(anchorPointView.x(), value);
973     config->setTransformedCenter(config->transformedCenter() + newAnchorPointView - anchorPointView);
974     translateYBox->setValue(value);
975     notifyConfigChanged();
976 }
977 
slotSetAX(qreal value)978 void KisToolTransformConfigWidget::slotSetAX(qreal value)
979 {
980     if (m_uiSlotsBlocked) return;
981 
982     ToolTransformArgs *config = m_transaction->currentConfig();
983     {
984         KisTransformUtils::AnchorHolder keeper(config->transformAroundRotationCenter(), config);
985         config->setAX(normalizeAngle(kisDegreesToRadians(value)));
986     }
987     notifyConfigChanged();
988     notifyEditingFinished();
989 }
990 
slotSetAY(qreal value)991 void KisToolTransformConfigWidget::slotSetAY(qreal value)
992 {
993     if (m_uiSlotsBlocked) return;
994 
995     ToolTransformArgs *config = m_transaction->currentConfig();
996 
997     {
998         KisTransformUtils::AnchorHolder keeper(config->transformAroundRotationCenter(), config);
999         config->setAY(normalizeAngle(kisDegreesToRadians(value)));
1000     }
1001 
1002     notifyConfigChanged();
1003     notifyEditingFinished();
1004 }
1005 
slotSetAZ(qreal value)1006 void KisToolTransformConfigWidget::slotSetAZ(qreal value)
1007 {
1008     if (m_uiSlotsBlocked) return;
1009 
1010     ToolTransformArgs *config = m_transaction->currentConfig();
1011 
1012     {
1013         KisTransformUtils::AnchorHolder keeper(config->transformAroundRotationCenter(), config);
1014         config->setAZ(normalizeAngle(kisDegreesToRadians(value)));
1015     }
1016 
1017     notifyConfigChanged();
1018     notifyEditingFinished();
1019 }
1020 
slotFlipX()1021 void KisToolTransformConfigWidget::slotFlipX()
1022 {
1023     ToolTransformArgs *config = m_transaction->currentConfig();
1024 
1025     {
1026         KisTransformUtils::AnchorHolder keeper(config->transformAroundRotationCenter(), config);
1027         config->setScaleX(config->scaleX() * -1);
1028     }
1029 
1030     notifyConfigChanged();
1031     notifyEditingFinished();
1032 }
1033 
slotFlipY()1034 void KisToolTransformConfigWidget::slotFlipY()
1035 {
1036     ToolTransformArgs *config = m_transaction->currentConfig();
1037 
1038     {
1039         KisTransformUtils::AnchorHolder keeper(config->transformAroundRotationCenter(), config);
1040         config->setScaleY(config->scaleY() * -1);
1041     }
1042 
1043     notifyConfigChanged();
1044     notifyEditingFinished();
1045 }
1046 
slotRotateCW()1047 void KisToolTransformConfigWidget::slotRotateCW()
1048 {
1049     ToolTransformArgs *config = m_transaction->currentConfig();
1050 
1051     {
1052         KisTransformUtils::AnchorHolder keeper(config->transformAroundRotationCenter(), config);
1053         config->setAZ(normalizeAngle(config->aZ() + M_PI_2));
1054     }
1055 
1056     notifyConfigChanged();
1057     notifyEditingFinished();
1058 }
1059 
slotRotateCCW()1060 void KisToolTransformConfigWidget::slotRotateCCW()
1061 {
1062     ToolTransformArgs *config = m_transaction->currentConfig();
1063 
1064     {
1065         KisTransformUtils::AnchorHolder keeper(config->transformAroundRotationCenter(), config);
1066         config->setAZ(normalizeAngle(config->aZ() - M_PI_2));
1067     }
1068 
1069     notifyConfigChanged();
1070     notifyEditingFinished();
1071 }
1072 
1073 
1074 // change free transform setting we want to alter (all radio buttons call this)
slotTransformAreaVisible(bool value)1075 void KisToolTransformConfigWidget::slotTransformAreaVisible(bool value)
1076 {
1077     Q_UNUSED(value);
1078 
1079     //QCheckBox sender = (QCheckBox)(*)(QObject::sender());
1080     QString senderName = QObject::sender()->objectName();
1081 
1082 
1083     // only show setting with what we have selected
1084     rotationGroup->hide();
1085     shearGroup->hide();
1086     scaleGroup->hide();
1087     moveGroup->hide();
1088 
1089 
1090     if ("freeMoveRadioButton" == senderName)
1091     {
1092         moveGroup->show();
1093     }
1094     else  if ("freeShearRadioButton" == senderName)
1095     {
1096         shearGroup->show();
1097     }
1098     else if ("freeScaleRadioButton" == senderName)
1099     {
1100         scaleGroup->show();
1101     }
1102     else
1103     {
1104         rotationGroup->show();
1105     }
1106 
1107 }
1108 
slotSetKeepAspectRatio(bool value)1109 void KisToolTransformConfigWidget::slotSetKeepAspectRatio(bool value)
1110 {
1111     if (m_uiSlotsBlocked) return;
1112 
1113     ToolTransformArgs *config = m_transaction->currentConfig();
1114     config->setKeepAspectRatio(value);
1115 
1116     if (value) {
1117        blockNotifications();
1118        int tmpXScaleBox = scaleXBox->value();
1119        int tmpYScaleBox = scaleYBox->value();
1120        m_scaleRatio = (tmpXScaleBox / (double) tmpYScaleBox);
1121        unblockNotifications();
1122     }
1123 
1124     notifyConfigChanged();
1125 }
1126 
slotSetWarpAlpha(qreal value)1127 void KisToolTransformConfigWidget::slotSetWarpAlpha(qreal value)
1128 {
1129     if (m_uiSlotsBlocked) return;
1130 
1131     ToolTransformArgs *config = m_transaction->currentConfig();
1132 
1133     config->setAlpha((double)value);
1134     notifyConfigChanged();
1135     notifyEditingFinished();
1136 }
1137 
slotSetWarpDensity(int value)1138 void KisToolTransformConfigWidget::slotSetWarpDensity(int value)
1139 {
1140     if (m_uiSlotsBlocked) return;
1141     setDefaultWarpPoints(value);
1142 }
1143 
setDefaultWarpPoints(int pointsPerLine)1144 void KisToolTransformConfigWidget::setDefaultWarpPoints(int pointsPerLine)
1145 {
1146     ToolTransformArgs *config = m_transaction->currentConfig();
1147 
1148     KisTransformUtils::setDefaultWarpPoints(pointsPerLine, m_transaction, config);
1149     notifyConfigChanged();
1150 }
1151 
activateCustomWarpPoints(bool enabled)1152 void KisToolTransformConfigWidget::activateCustomWarpPoints(bool enabled)
1153 {
1154     ToolTransformArgs *config = m_transaction->currentConfig();
1155 
1156     densityBox->setEnabled(!enabled);
1157     customWarpWidget->setEnabled(enabled);
1158 
1159     if (!enabled) {
1160         config->setEditingTransformPoints(false);
1161         setDefaultWarpPoints(densityBox->value());
1162         config->setWarpCalculation(KisWarpTransformWorker::WarpCalculation::GRID);
1163     } else {
1164         config->setEditingTransformPoints(true);
1165         config->setWarpCalculation(KisWarpTransformWorker::WarpCalculation::DRAW);
1166         setDefaultWarpPoints(0);
1167     }
1168 
1169 
1170 
1171 
1172     updateLockPointsButtonCaption();
1173 }
1174 
slotWarpDefaultPointsButtonClicked(bool value)1175 void KisToolTransformConfigWidget::slotWarpDefaultPointsButtonClicked(bool value)
1176 {
1177     if (m_uiSlotsBlocked) return;
1178 
1179     activateCustomWarpPoints(!value);
1180 }
1181 
slotWarpCustomPointsButtonClicked(bool value)1182 void KisToolTransformConfigWidget::slotWarpCustomPointsButtonClicked(bool value)
1183 {
1184     if (m_uiSlotsBlocked) return;
1185 
1186     activateCustomWarpPoints(value);
1187 }
1188 
slotWarpResetPointsButtonClicked()1189 void KisToolTransformConfigWidget::slotWarpResetPointsButtonClicked()
1190 {
1191     if (m_uiSlotsBlocked) return;
1192 
1193     activateCustomWarpPoints(true);
1194 }
1195 
slotWarpLockPointsButtonClicked()1196 void KisToolTransformConfigWidget::slotWarpLockPointsButtonClicked()
1197 {
1198     if (m_uiSlotsBlocked) return;
1199 
1200     ToolTransformArgs *config = m_transaction->currentConfig();
1201     config->setEditingTransformPoints(!config->isEditingTransformPoints());
1202 
1203     if (config->isEditingTransformPoints()) {
1204         // reinit the transf points to their original value
1205         ToolTransformArgs *config = m_transaction->currentConfig();
1206         int nbPoints = config->origPoints().size();
1207         for (int i = 0; i < nbPoints; ++i) {
1208             config->transfPoint(i) = config->origPoint(i);
1209         }
1210     }
1211 
1212     updateLockPointsButtonCaption();
1213     notifyConfigChanged();
1214 }
1215 
slotWarpTypeChanged(int index)1216 void KisToolTransformConfigWidget::slotWarpTypeChanged(int index)
1217 {
1218     if (m_uiSlotsBlocked) return;
1219 
1220     ToolTransformArgs *config = m_transaction->currentConfig();
1221 
1222     switch (index) {
1223     case KisWarpTransformWorker::AFFINE_TRANSFORM:
1224     case KisWarpTransformWorker::SIMILITUDE_TRANSFORM:
1225     case KisWarpTransformWorker::RIGID_TRANSFORM:
1226         config->setWarpType((KisWarpTransformWorker::WarpType)index);
1227         break;
1228     default:
1229         config->setWarpType(KisWarpTransformWorker::RIGID_TRANSFORM);
1230         break;
1231     }
1232 
1233     notifyConfigChanged();
1234 }
1235 
slotCageOptionsChanged(int value)1236 void KisToolTransformConfigWidget::slotCageOptionsChanged(int value)
1237 {
1238     if ( value == 0)
1239     {
1240         slotEditCagePoints(true);
1241     }
1242     else
1243     {
1244         slotEditCagePoints(false);
1245     }
1246 
1247     notifyEditingFinished();
1248 }
1249 
slotEditCagePoints(bool value)1250 void KisToolTransformConfigWidget::slotEditCagePoints(bool value)
1251 {
1252     if (m_uiSlotsBlocked) return;
1253 
1254     ToolTransformArgs *config = m_transaction->currentConfig();
1255     config->refTransformedPoints() = config->origPoints();
1256 
1257     config->setEditingTransformPoints(value);
1258     notifyConfigChanged();
1259 }
1260 
slotGranularityChanged(QString value)1261 void KisToolTransformConfigWidget::slotGranularityChanged(QString value)
1262 {
1263     if (m_uiSlotsBlocked) return;
1264     KIS_SAFE_ASSERT_RECOVER_RETURN(value.toInt() > 1);
1265     ToolTransformArgs *config = m_transaction->currentConfig();
1266     config->setPixelPrecision(value.toInt());
1267     notifyConfigChanged();
1268 }
1269 
slotPreviewGranularityChanged(QString value)1270 void KisToolTransformConfigWidget::slotPreviewGranularityChanged(QString value)
1271 {
1272     if (m_uiSlotsBlocked) return;
1273     KIS_SAFE_ASSERT_RECOVER_RETURN(value.toInt() > 1);
1274     ToolTransformArgs *config = m_transaction->currentConfig();
1275     config->setPreviewPixelPrecision(value.toInt());
1276     notifyConfigChanged();
1277 }
1278 
slotMeshSizeChanged()1279 void KisToolTransformConfigWidget::slotMeshSizeChanged()
1280 {
1281     if (m_uiSlotsBlocked) return;
1282 
1283     ToolTransformArgs *config = m_transaction->currentConfig();
1284     KisBezierTransformMesh &mesh = *config->meshTransform();
1285 
1286     if (mesh.size().width() != intNumColumns->value() + 1) {
1287         mesh.reshapeMeshHorizontally(intNumColumns->value() + 1);
1288     }
1289 
1290     if (mesh.size().height() != intNumRows->value() + 1) {
1291         mesh.reshapeMeshVertically(intNumRows->value() + 1);
1292     }
1293 
1294     notifyConfigChanged();
1295 }
1296 
slotMeshShowHandlesChanged()1297 void KisToolTransformConfigWidget::slotMeshShowHandlesChanged()
1298 {
1299     if (m_uiSlotsBlocked) return;
1300     ToolTransformArgs *config = m_transaction->currentConfig();
1301     config->setMeshShowHandles(this->chkShowControlPoints->isChecked());
1302     notifyConfigChanged();
1303     notifyEditingFinished();
1304 }
1305 
slotMeshSymmetricalHandlesChanged()1306 void KisToolTransformConfigWidget::slotMeshSymmetricalHandlesChanged()
1307 {
1308     if (m_uiSlotsBlocked) return;
1309     ToolTransformArgs *config = m_transaction->currentConfig();
1310     config->setMeshSymmetricalHandles(this->chkSymmetricalHandles->isChecked());
1311     notifyConfigChanged();
1312     notifyEditingFinished();
1313 }
1314 
slotMeshScaleHandlesChanged()1315 void KisToolTransformConfigWidget::slotMeshScaleHandlesChanged()
1316 {
1317     if (m_uiSlotsBlocked) return;
1318     ToolTransformArgs *config = m_transaction->currentConfig();
1319     config->setMeshScaleHandles(this->chkScaleHandles->isChecked());
1320     notifyConfigChanged();
1321 }
1322