diff --git a/plugins/tools/tool_transform2/kis_tool_transform.cc b/plugins/tools/tool_transform2/kis_tool_transform.cc
index 435cee510a..32ace87044 100644
--- a/plugins/tools/tool_transform2/kis_tool_transform.cc
+++ b/plugins/tools/tool_transform2/kis_tool_transform.cc
@@ -1,1143 +1,1125 @@
/*
* kis_tool_transform.cc -- part of Krita
*
* Copyright (c) 2004 Boudewijn Rempt
* Copyright (c) 2005 C. Boemann
* Copyright (c) 2010 Marc Pegon
* Copyright (c) 2013 Dmitry Kazakov
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_tool_transform.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
"));
connect(cmbFilter, SIGNAL(activated(KoID)),
this, SLOT(slotFilterChanged(KoID)));
// Init Warp Type combo
cmbWarpType->insertItem(KisWarpTransformWorker::AFFINE_TRANSFORM,i18n("Default (Affine)"));
cmbWarpType->insertItem(KisWarpTransformWorker::RIGID_TRANSFORM,i18n("Strong (Rigid)"));
cmbWarpType->insertItem(KisWarpTransformWorker::SIMILITUDE_TRANSFORM,i18n("Strongest (Similitude)"));
cmbWarpType->setCurrentIndex(KisWarpTransformWorker::AFFINE_TRANSFORM);
connect(cmbWarpType, SIGNAL(currentIndexChanged(int)), this, SLOT(slotWarpTypeChanged(int)));
// Init Rotation Center buttons
m_handleDir[0] = QPointF(1, 0);
m_handleDir[1] = QPointF(1, -1);
m_handleDir[2] = QPointF(0, -1);
m_handleDir[3] = QPointF(-1, -1);
m_handleDir[4] = QPointF(-1, 0);
m_handleDir[5] = QPointF(-1, 1);
m_handleDir[6] = QPointF(0, 1);
m_handleDir[7] = QPointF(1, 1);
m_handleDir[8] = QPointF(0, 0); // also add the center
m_rotationCenterButtons = new QButtonGroup(0);
// we set the ids to match m_handleDir
m_rotationCenterButtons->addButton(middleRightButton, 0);
m_rotationCenterButtons->addButton(topRightButton, 1);
m_rotationCenterButtons->addButton(middleTopButton, 2);
m_rotationCenterButtons->addButton(topLeftButton, 3);
m_rotationCenterButtons->addButton(middleLeftButton, 4);
m_rotationCenterButtons->addButton(bottomLeftButton, 5);
m_rotationCenterButtons->addButton(middleBottomButton, 6);
m_rotationCenterButtons->addButton(bottomRightButton, 7);
m_rotationCenterButtons->addButton(centerButton, 8);
QToolButton *nothingSelected = new QToolButton(0);
nothingSelected->setCheckable(true);
nothingSelected->setAutoExclusive(true);
nothingSelected->hide(); // a convenient button for when no button is checked in the group
m_rotationCenterButtons->addButton(nothingSelected, 9);
// initialize values for free transform sliders
shearXBox->setSuffix(i18n(" px"));
shearYBox->setSuffix(i18n(" px"));
shearXBox->setRange(-5.0, 5.0, 2);
shearYBox->setRange(-5.0, 5.0, 2);
shearXBox->setSingleStep(0.01);
shearYBox->setSingleStep(0.01);
shearXBox->setValue(0.0);
shearYBox->setValue(0.0);
translateXBox->setSuffix(i18n(" px"));
translateYBox->setSuffix(i18n(" px"));
translateXBox->setRange(-10000, 10000);
translateYBox->setRange(-10000, 10000);
scaleXBox->setRange(-10000, 10000);
scaleYBox->setRange(-10000, 10000);
scaleXBox->setValue(100.0);
scaleYBox->setValue(100.0);
m_scaleRatio = 1.0;
aXBox->setSuffix(QChar(Qt::Key_degree));
aYBox->setSuffix(QChar(Qt::Key_degree));
aZBox->setSuffix(QChar(Qt::Key_degree));
aXBox->setRange(0.0, 360.0, 2);
aYBox->setRange(0.0, 360.0, 2);
aZBox->setRange(0.0, 360.0, 2);
aXBox->setValue(0.0);
aYBox->setValue(0.0);
aZBox->setValue(0.0);
aXBox->setSingleStep(1.0);
aYBox->setSingleStep(1.0);
aZBox->setSingleStep(1.0);
connect(m_rotationCenterButtons, SIGNAL(buttonPressed(int)), this, SLOT(slotRotationCenterChanged(int)));
connect(btnTransformAroundPivotPoint, SIGNAL(clicked(bool)), this, SLOT(slotTransformAroundRotationCenter(bool)));
// Init Free Transform Values
connect(scaleXBox, SIGNAL(valueChanged(int)), this, SLOT(slotSetScaleX(int)));
connect(scaleYBox, SIGNAL(valueChanged(int)), this, SLOT(slotSetScaleY(int)));
connect(shearXBox, SIGNAL(valueChanged(qreal)), this, SLOT(slotSetShearX(qreal)));
connect(shearYBox, SIGNAL(valueChanged(qreal)), this, SLOT(slotSetShearY(qreal)));
connect(translateXBox, SIGNAL(valueChanged(int)), this, SLOT(slotSetTranslateX(int)));
connect(translateYBox, SIGNAL(valueChanged(int)), this, SLOT(slotSetTranslateY(int)));
connect(aXBox, SIGNAL(valueChanged(qreal)), this, SLOT(slotSetAX(qreal)));
connect(aYBox, SIGNAL(valueChanged(qreal)), this, SLOT(slotSetAY(qreal)));
connect(aZBox, SIGNAL(valueChanged(qreal)), this, SLOT(slotSetAZ(qreal)));
connect(aspectButton, SIGNAL(keepAspectRatioChanged(bool)), this, SLOT(slotSetKeepAspectRatio(bool)));
connect(flipXButton, SIGNAL(clicked(bool)), this, SLOT(slotFlipX()));
connect(flipYButton, SIGNAL(clicked(bool)), this, SLOT(slotFlipY()));
connect(rotateCWButton, SIGNAL(clicked(bool)), this, SLOT(slotRotateCW()));
connect(rotateCCWButton, SIGNAL(clicked(bool)), this, SLOT(slotRotateCCW()));
// toggle visibility of different free buttons
connect(freeMoveRadioButton, SIGNAL(clicked(bool)), SLOT(slotTransformAreaVisible(bool)));
connect(freeRotationRadioButton, SIGNAL(clicked(bool)), SLOT(slotTransformAreaVisible(bool)));
connect(freeScaleRadioButton, SIGNAL(clicked(bool)), SLOT(slotTransformAreaVisible(bool)));
connect(freeShearRadioButton, SIGNAL(clicked(bool)), SLOT(slotTransformAreaVisible(bool)));
// only first group for free transform
rotationGroup->hide();
moveGroup->show();
scaleGroup->hide();
shearGroup->hide();
// Init Warp Transform Values
alphaBox->setSingleStep(0.1);
alphaBox->setRange(0, 10, 1);
connect(alphaBox, SIGNAL(valueChanged(qreal)), this, SLOT(slotSetWarpAlpha(qreal)));
connect(densityBox, SIGNAL(valueChanged(int)), this, SLOT(slotSetWarpDensity(int)));
connect(defaultRadioButton, SIGNAL(clicked(bool)), this, SLOT(slotWarpDefaultPointsButtonClicked(bool)));
connect(customRadioButton, SIGNAL(clicked(bool)), this, SLOT(slotWarpCustomPointsButtonClicked(bool)));
connect(lockUnlockPointsButton, SIGNAL(clicked()), this, SLOT(slotWarpLockPointsButtonClicked()));
connect(resetPointsButton, SIGNAL(clicked()), this, SLOT(slotWarpResetPointsButtonClicked()));
// Init Cage Transform Values
cageTransformButtonGroup->setId(cageAddEditRadio, 0); // we need to set manually since Qt Designer generates negative by default
cageTransformButtonGroup->setId(cageDeformRadio, 1);
connect(cageTransformButtonGroup, SIGNAL(buttonClicked(int)), this, SLOT(slotCageOptionsChanged(int)));
// Init Liquify Transform Values
liquifySizeSlider->setRange(KisLiquifyProperties::minSize(),
KisLiquifyProperties::maxSize(), 2);
liquifySizeSlider->setExponentRatio(4);
liquifySizeSlider->setValue(60.0);
connect(liquifySizeSlider, SIGNAL(valueChanged(qreal)), this, SLOT(liquifySizeChanged(qreal)));
liquifySizeSlider->setToolTip(i18nc("@info:tooltip", "Size of the deformation brush"));
liquifyAmountSlider->setRange(0.0, 1.0, 2);
liquifyAmountSlider->setValue(0.05);
connect(liquifyAmountSlider, SIGNAL(valueChanged(qreal)), this, SLOT(liquifyAmountChanged(qreal)));
liquifyAmountSlider->setToolTip(i18nc("@info:tooltip", "Amount of the deformation you get"));
liquifyFlowSlider->setRange(0.0, 1.0, 2);
liquifyFlowSlider->setValue(1.0);
connect(liquifyFlowSlider, SIGNAL(valueChanged(qreal)), this, SLOT(liquifyFlowChanged(qreal)));
liquifyFlowSlider->setToolTip(i18nc("@info:tooltip", "When in non-buildup mode, shows how fast the deformation limit is reached."));
buidupModeComboBox->setCurrentIndex(0); // set to build-up mode by default
connect(buidupModeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(liquifyBuildUpChanged(int)));
buidupModeComboBox->setToolTip("" + 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.") + "
");
liquifySpacingSlider->setRange(0.0, 3.0, 2);
liquifySizeSlider->setExponentRatio(3);
liquifySpacingSlider->setSingleStep(0.01);
liquifySpacingSlider->setValue(0.2);
connect(liquifySpacingSlider, SIGNAL(valueChanged(qreal)), this, SLOT(liquifySpacingChanged(qreal)));
liquifySpacingSlider->setToolTip(i18nc("@info:tooltip", "Space between two sequential applications of the deformation"));
liquifySizePressureBox->setChecked(true);
connect(liquifySizePressureBox, SIGNAL(toggled(bool)), this, SLOT(liquifySizePressureChanged(bool)));
liquifySizePressureBox->setToolTip(i18nc("@info:tooltip", "Scale Size value according to current stylus pressure"));
liquifyAmountPressureBox->setChecked(true);
connect(liquifyAmountPressureBox, SIGNAL(toggled(bool)), this, SLOT(liquifyAmountPressureChanged(bool)));
liquifyAmountPressureBox->setToolTip(i18nc("@info:tooltip", "Scale Amount value according to current stylus pressure"));
liquifyReverseDirectionChk->setChecked(false);
connect(liquifyReverseDirectionChk, SIGNAL(toggled(bool)), this, SLOT(liquifyReverseDirectionChanged(bool)));
liquifyReverseDirectionChk->setToolTip(i18nc("@info:tooltip", "Reverse direction of the current deformation tool"));
QSignalMapper *liquifyModeMapper = new QSignalMapper(this);
connect(liquifyMove, SIGNAL(toggled(bool)), liquifyModeMapper, SLOT(map()));
connect(liquifyScale, SIGNAL(toggled(bool)), liquifyModeMapper, SLOT(map()));
connect(liquifyRotate, SIGNAL(toggled(bool)), liquifyModeMapper, SLOT(map()));
connect(liquifyOffset, SIGNAL(toggled(bool)), liquifyModeMapper, SLOT(map()));
connect(liquifyUndo, SIGNAL(toggled(bool)), liquifyModeMapper, SLOT(map()));
liquifyModeMapper->setMapping(liquifyMove, (int)KisLiquifyProperties::MOVE);
liquifyModeMapper->setMapping(liquifyScale, (int)KisLiquifyProperties::SCALE);
liquifyModeMapper->setMapping(liquifyRotate, (int)KisLiquifyProperties::ROTATE);
liquifyModeMapper->setMapping(liquifyOffset, (int)KisLiquifyProperties::OFFSET);
liquifyModeMapper->setMapping(liquifyUndo, (int)KisLiquifyProperties::UNDO);
connect(liquifyModeMapper, SIGNAL(mapped(int)), SLOT(slotLiquifyModeChanged(int)));
liquifyMove->setToolTip(i18nc("@info:tooltip", "Move: drag the image along the brush stroke"));
liquifyScale->setToolTip(i18nc("@info:tooltip", "Scale: grow/shrink image under cursor"));
liquifyRotate->setToolTip(i18nc("@info:tooltip", "Rotate: twirl image under cursor"));
liquifyOffset->setToolTip(i18nc("@info:tooltip", "Offset: shift the image to the right of the stroke direction"));
liquifyUndo->setToolTip(i18nc("@info:tooltip", "Undo: erase actions of other tools"));
// Connect all edit boxes to the Editing Finished signal
connect(densityBox, SIGNAL(editingFinished()), this, SLOT(notifyEditingFinished()));
// Connect other widget (not having editingFinished signal) to
// the same slot. From Qt 4.6 onwards the sequence of the signal
// delivery is definite.
connect(cmbFilter, SIGNAL(activated(KoID)), this, SLOT(notifyEditingFinished()));
connect(cmbWarpType, SIGNAL(currentIndexChanged(int)), this, SLOT(notifyEditingFinished()));
connect(m_rotationCenterButtons, SIGNAL(buttonPressed(int)), this, SLOT(notifyEditingFinished()));
connect(aspectButton, SIGNAL(keepAspectRatioChanged(bool)), this, SLOT(notifyEditingFinished()));
connect(lockUnlockPointsButton, SIGNAL(clicked()), this, SLOT(notifyEditingFinished()));
connect(resetPointsButton, SIGNAL(clicked()), this, SLOT(notifyEditingFinished()));
connect(defaultRadioButton, SIGNAL(clicked(bool)), this, SLOT(notifyEditingFinished()));
connect(customRadioButton, SIGNAL(clicked(bool)), this, SLOT(notifyEditingFinished()));
// Liquify
//
// liquify brush options do not affect the image directly and are not
// saved to undo, so we don't emit notifyEditingFinished() for them
// Connect Apply/Reset buttons
connect(buttonBox, SIGNAL(clicked(QAbstractButton*)), this, SLOT(slotButtonBoxClicked(QAbstractButton*)));
// Mode switch buttons
connect(freeTransformButton, SIGNAL(clicked(bool)), this, SLOT(slotSetFreeTransformModeButtonClicked(bool)));
connect(warpButton, SIGNAL(clicked(bool)), this, SLOT(slotSetWarpModeButtonClicked(bool)));
connect(cageButton, SIGNAL(clicked(bool)), this, SLOT(slotSetCageModeButtonClicked(bool)));
connect(perspectiveTransformButton, SIGNAL(clicked(bool)), this, SLOT(slotSetPerspectiveModeButtonClicked(bool)));
connect(liquifyButton, SIGNAL(clicked(bool)), this, SLOT(slotSetLiquifyModeButtonClicked(bool)));
- // Connect Decorations switcher
- connect(showDecorationsBox, SIGNAL(toggled(bool)), canvas, SLOT(updateCanvas()));
-
tooBigLabelWidget->hide();
connect(canvas->viewManager()->mainWindow(), SIGNAL(themeChanged()), SLOT(slotUpdateIcons()), Qt::UniqueConnection);
slotUpdateIcons();
}
void KisToolTransformConfigWidget::slotUpdateIcons()
{
freeTransformButton->setIcon(KisIconUtils::loadIcon("transform_icons_main"));
warpButton->setIcon(KisIconUtils::loadIcon("transform_icons_warp"));
cageButton->setIcon(KisIconUtils::loadIcon("transform_icons_cage"));
perspectiveTransformButton->setIcon(KisIconUtils::loadIcon("transform_icons_perspective"));
liquifyButton->setIcon(KisIconUtils::loadIcon("transform_icons_liquify_main"));
liquifyMove->setIcon(KisIconUtils::loadIcon("transform_icons_liquify_move"));
liquifyScale->setIcon(KisIconUtils::loadIcon("transform_icons_liquify_resize"));
liquifyRotate->setIcon(KisIconUtils::loadIcon("transform_icons_liquify_rotate"));
liquifyOffset->setIcon(KisIconUtils::loadIcon("transform_icons_liquify_offset"));
liquifyUndo->setIcon(KisIconUtils::loadIcon("transform_icons_liquify_erase"));
middleRightButton->setIcon(KisIconUtils::loadIcon("arrow-right"));
topRightButton->setIcon(KisIconUtils::loadIcon("arrow-topright"));
middleTopButton->setIcon(KisIconUtils::loadIcon("arrow-up"));
topLeftButton->setIcon(KisIconUtils::loadIcon("arrow-topleft"));
middleLeftButton->setIcon(KisIconUtils::loadIcon("arrow-left"));
bottomLeftButton->setIcon(KisIconUtils::loadIcon("arrow-downleft"));
middleBottomButton->setIcon(KisIconUtils::loadIcon("arrow-down"));
bottomRightButton->setIcon(KisIconUtils::loadIcon("arrow-downright"));
btnTransformAroundPivotPoint->setIcon(KisIconUtils::loadIcon("pivot-point"));
// pressure icons
liquifySizePressureBox->setIcon(KisIconUtils::loadIcon("transform_icons_penPressure"));
liquifyAmountPressureBox->setIcon(KisIconUtils::loadIcon("transform_icons_penPressure"));
}
double KisToolTransformConfigWidget::radianToDegree(double rad)
{
double piX2 = 2 * M_PI;
if (rad < 0 || rad >= piX2) {
rad = fmod(rad, piX2);
if (rad < 0) {
rad += piX2;
}
}
return (rad * 360. / piX2);
}
double KisToolTransformConfigWidget::degreeToRadian(double degree)
{
if (degree < 0. || degree >= 360.) {
degree = fmod(degree, 360.);
if (degree < 0)
degree += 360.;
}
return (degree * M_PI / 180.);
}
void KisToolTransformConfigWidget::updateLiquifyControls()
{
blockUiSlots();
ToolTransformArgs *config = m_transaction->currentConfig();
KisLiquifyProperties *props =
config->liquifyProperties();
const bool useWashMode = props->useWashMode();
liquifySizeSlider->setValue(props->size());
liquifyAmountSlider->setValue(props->amount());
liquifyFlowSlider->setValue(props->flow());
buidupModeComboBox->setCurrentIndex(useWashMode);
liquifySpacingSlider->setValue(props->spacing());
liquifySizePressureBox->setChecked(props->sizeHasPressure());
liquifyAmountPressureBox->setChecked(props->amountHasPressure());
liquifyReverseDirectionChk->setChecked(props->reverseDirection());
KisLiquifyProperties::LiquifyMode mode =
static_cast(props->mode());
bool canInverseDirection =
mode != KisLiquifyProperties::UNDO;
bool canUseWashMode = mode != KisLiquifyProperties::UNDO;
liquifyReverseDirectionChk->setEnabled(canInverseDirection);
liquifyFlowSlider->setEnabled(canUseWashMode && useWashMode);
buidupModeComboBox->setEnabled(canUseWashMode);
const qreal maxAmount = canUseWashMode ? 5.0 : 1.0;
liquifyAmountSlider->setRange(0.0, maxAmount, 2);
unblockUiSlots();
}
void KisToolTransformConfigWidget::slotLiquifyModeChanged(int value)
{
if (m_uiSlotsBlocked) return;
ToolTransformArgs *config = m_transaction->currentConfig();
KisLiquifyProperties *props =
config->liquifyProperties();
KisLiquifyProperties::LiquifyMode mode =
static_cast(value);
if (mode == props->mode()) return;
props->setMode(mode);
props->loadMode();
updateLiquifyControls();
notifyConfigChanged();
}
void KisToolTransformConfigWidget::liquifySizeChanged(qreal value)
{
if (m_uiSlotsBlocked) return;
ToolTransformArgs *config = m_transaction->currentConfig();
KisLiquifyProperties *props =
config->liquifyProperties();
props->setSize(value);
notifyConfigChanged();
}
void KisToolTransformConfigWidget::liquifyAmountChanged(qreal value)
{
if (m_uiSlotsBlocked) return;
ToolTransformArgs *config = m_transaction->currentConfig();
KisLiquifyProperties *props =
config->liquifyProperties();
props->setAmount(value);
notifyConfigChanged();
}
void KisToolTransformConfigWidget::liquifyFlowChanged(qreal value)
{
if (m_uiSlotsBlocked) return;
ToolTransformArgs *config = m_transaction->currentConfig();
KisLiquifyProperties *props =
config->liquifyProperties();
props->setFlow(value);
notifyConfigChanged();
}
void KisToolTransformConfigWidget::liquifyBuildUpChanged(int value)
{
if (m_uiSlotsBlocked) return;
ToolTransformArgs *config = m_transaction->currentConfig();
KisLiquifyProperties *props =
config->liquifyProperties();
props->setUseWashMode(value); // 0 == build up mode / 1 == wash mode
notifyConfigChanged();
// we need to enable/disable flow slider
updateLiquifyControls();
}
void KisToolTransformConfigWidget::liquifySpacingChanged(qreal value)
{
if (m_uiSlotsBlocked) return;
ToolTransformArgs *config = m_transaction->currentConfig();
KisLiquifyProperties *props =
config->liquifyProperties();
props->setSpacing(value);
notifyConfigChanged();
}
void KisToolTransformConfigWidget::liquifySizePressureChanged(bool value)
{
if (m_uiSlotsBlocked) return;
ToolTransformArgs *config = m_transaction->currentConfig();
KisLiquifyProperties *props =
config->liquifyProperties();
props->setSizeHasPressure(value);
notifyConfigChanged();
}
void KisToolTransformConfigWidget::liquifyAmountPressureChanged(bool value)
{
if (m_uiSlotsBlocked) return;
ToolTransformArgs *config = m_transaction->currentConfig();
KisLiquifyProperties *props =
config->liquifyProperties();
props->setAmountHasPressure(value);
notifyConfigChanged();
}
void KisToolTransformConfigWidget::liquifyReverseDirectionChanged(bool value)
{
if (m_uiSlotsBlocked) return;
ToolTransformArgs *config = m_transaction->currentConfig();
KisLiquifyProperties *props =
config->liquifyProperties();
props->setReverseDirection(value);
notifyConfigChanged();
}
void KisToolTransformConfigWidget::updateConfig(const ToolTransformArgs &config)
{
blockUiSlots();
if (config.mode() == ToolTransformArgs::FREE_TRANSFORM ||
config.mode() == ToolTransformArgs::PERSPECTIVE_4POINT) {
quickTransformGroup->setEnabled(config.mode() == ToolTransformArgs::FREE_TRANSFORM);
stackedWidget->setCurrentIndex(0);
bool freeTransformIsActive = config.mode() == ToolTransformArgs::FREE_TRANSFORM;
if (freeTransformIsActive)
{
freeTransformButton->setChecked(true);
}
else
{
perspectiveTransformButton->setChecked(true);
}
aXBox->setEnabled(freeTransformIsActive);
aYBox->setEnabled(freeTransformIsActive);
aZBox->setEnabled(freeTransformIsActive);
freeRotationRadioButton->setEnabled(freeTransformIsActive);
scaleXBox->setValue(config.scaleX() * 100.);
scaleYBox->setValue(config.scaleY() * 100.);
shearXBox->setValue(config.shearX());
shearYBox->setValue(config.shearY());
const QPointF anchorPoint = config.originalCenter() + config.rotationCenterOffset();
const KisTransformUtils::MatricesPack m(config);
const QPointF anchorPointView = m.finalTransform().map(anchorPoint);
translateXBox->setValue(anchorPointView.x());
translateYBox->setValue(anchorPointView.y());
aXBox->setValue(radianToDegree(config.aX()));
aYBox->setValue(radianToDegree(config.aY()));
aZBox->setValue(radianToDegree(config.aZ()));
aspectButton->setKeepAspectRatio(config.keepAspectRatio());
cmbFilter->setCurrent(config.filterId());
QPointF pt = m_transaction->currentConfig()->rotationCenterOffset();
pt.rx() /= m_transaction->originalHalfWidth();
pt.ry() /= m_transaction->originalHalfHeight();
for (int i = 0; i < 9; i++) {
if (qFuzzyCompare(m_handleDir[i].x(), pt.x()) &&
qFuzzyCompare(m_handleDir[i].y(), pt.y())) {
m_rotationCenterButtons->button(i)->setChecked(true);
break;
}
}
btnTransformAroundPivotPoint->setChecked(config.transformAroundRotationCenter());
} else if (config.mode() == ToolTransformArgs::WARP) {
stackedWidget->setCurrentIndex(1);
warpButton->setChecked(true);
if (config.defaultPoints()) {
densityBox->setValue(std::sqrt(config.numPoints()));
}
cmbWarpType->setCurrentIndex((int)config.warpType());
defaultRadioButton->setChecked(config.defaultPoints());
customRadioButton->setChecked(!config.defaultPoints());
densityBox->setEnabled(config.defaultPoints());
customWarpWidget->setEnabled(!config.defaultPoints());
updateLockPointsButtonCaption();
} else if (config.mode() == ToolTransformArgs::CAGE) {
// default UI options
resetUIOptions();
// we need at least 3 point before we can start actively deforming
if (config.origPoints().size() >= 3)
{
cageTransformDirections->setText(i18n("Switch between editing and deforming cage"));
cageAddEditRadio->setVisible(true);
cageDeformRadio->setVisible(true);
// update to correct radio button
if (config.isEditingTransformPoints())
cageAddEditRadio->setChecked(true);
else
cageDeformRadio->setChecked(true);
changeGranularity->setCurrentIndex(log2(config.pixelPrecision()) - 2);
granularityPreview->setCurrentIndex(log2(config.previewPixelPrecision()) - 2);
}
} else if (config.mode() == ToolTransformArgs::LIQUIFY) {
stackedWidget->setCurrentIndex(3);
liquifyButton->setChecked(true);
const KisLiquifyProperties *props =
config.liquifyProperties();
switch (props->mode()) {
case KisLiquifyProperties::MOVE:
liquifyMove->setChecked(true);
break;
case KisLiquifyProperties::SCALE:
liquifyScale->setChecked(true);
break;
case KisLiquifyProperties::ROTATE:
liquifyRotate->setChecked(true);
break;
case KisLiquifyProperties::OFFSET:
liquifyOffset->setChecked(true);
break;
case KisLiquifyProperties::UNDO:
liquifyUndo->setChecked(true);
break;
case KisLiquifyProperties::N_MODES:
qFatal("Unsupported mode");
}
updateLiquifyControls();
}
unblockUiSlots();
}
void KisToolTransformConfigWidget::updateLockPointsButtonCaption()
{
ToolTransformArgs *config = m_transaction->currentConfig();
if (config->isEditingTransformPoints()) {
lockUnlockPointsButton->setText(i18n("Lock Points"));
} else {
lockUnlockPointsButton->setText(i18n("Unlock Points"));
}
}
void KisToolTransformConfigWidget::setApplyResetDisabled(bool disabled)
{
QAbstractButton *applyButton = buttonBox->button(QDialogButtonBox::Apply);
QAbstractButton *resetButton = buttonBox->button(QDialogButtonBox::Reset);
Q_ASSERT(applyButton);
Q_ASSERT(resetButton);
applyButton->setDisabled(disabled);
resetButton->setDisabled(disabled);
}
void KisToolTransformConfigWidget::resetRotationCenterButtons()
{
int checkedId = m_rotationCenterButtons->checkedId();
if (checkedId >= 0 && checkedId <= 8) {
// uncheck the current checked button
m_rotationCenterButtons->button(9)->setChecked(true);
}
}
bool KisToolTransformConfigWidget::workRecursively() const
{
return chkWorkRecursively->isChecked();
}
void KisToolTransformConfigWidget::setTooBigLabelVisible(bool value)
{
tooBigLabelWidget->setVisible(value);
}
-bool KisToolTransformConfigWidget::showDecorations() const
-{
- return showDecorationsBox->isChecked();
-}
-
void KisToolTransformConfigWidget::blockNotifications()
{
m_notificationsBlocked++;
}
void KisToolTransformConfigWidget::unblockNotifications()
{
m_notificationsBlocked--;
}
void KisToolTransformConfigWidget::notifyConfigChanged()
{
if (!m_notificationsBlocked) {
emit sigConfigChanged();
}
m_configChanged = true;
}
void KisToolTransformConfigWidget::blockUiSlots()
{
m_uiSlotsBlocked++;
}
void KisToolTransformConfigWidget::unblockUiSlots()
{
m_uiSlotsBlocked--;
}
void KisToolTransformConfigWidget::notifyEditingFinished()
{
if (m_uiSlotsBlocked || m_notificationsBlocked || !m_configChanged) return;
emit sigEditingFinished();
m_configChanged = false;
}
void KisToolTransformConfigWidget::resetUIOptions()
{
// reset tool states since we are done (probably can encapsulate this later)
ToolTransformArgs *config = m_transaction->currentConfig();
if (config->mode() == ToolTransformArgs::CAGE)
{
cageAddEditRadio->setVisible(false);
cageAddEditRadio->setChecked(true);
cageDeformRadio->setVisible(false);
cageTransformDirections->setText(i18n("Create 3 points on the canvas to begin"));
// ensure we are on the right options view
stackedWidget->setCurrentIndex(2);
}
}
void KisToolTransformConfigWidget::slotButtonBoxClicked(QAbstractButton *button)
{
QAbstractButton *applyButton = buttonBox->button(QDialogButtonBox::Apply);
QAbstractButton *resetButton = buttonBox->button(QDialogButtonBox::Reset);
resetUIOptions();
if (button == applyButton) {
emit sigApplyTransform();
}
else if (button == resetButton) {
emit sigResetTransform();
}
}
void KisToolTransformConfigWidget::slotSetFreeTransformModeButtonClicked(bool value)
{
if (!value) return;
lblTransformType->setText(freeTransformButton->toolTip());
ToolTransformArgs *config = m_transaction->currentConfig();
config->setMode(ToolTransformArgs::FREE_TRANSFORM);
emit sigResetTransform();
}
void KisToolTransformConfigWidget::slotSetWarpModeButtonClicked(bool value)
{
if (!value) return;
lblTransformType->setText(warpButton->toolTip());
ToolTransformArgs *config = m_transaction->currentConfig();
config->setMode(ToolTransformArgs::WARP);
config->setWarpCalculation(KisWarpTransformWorker::WarpCalculation::GRID);
emit sigResetTransform();
}
void KisToolTransformConfigWidget::slotSetCageModeButtonClicked(bool value)
{
if (!value) return;
lblTransformType->setText(cageButton->toolTip());
ToolTransformArgs *config = m_transaction->currentConfig();
config->setMode(ToolTransformArgs::CAGE);
emit sigResetTransform();
}
void KisToolTransformConfigWidget::slotSetLiquifyModeButtonClicked(bool value)
{
if (!value) return;
lblTransformType->setText(liquifyButton->toolTip());
ToolTransformArgs *config = m_transaction->currentConfig();
config->setMode(ToolTransformArgs::LIQUIFY);
emit sigResetTransform();
}
void KisToolTransformConfigWidget::slotSetPerspectiveModeButtonClicked(bool value)
{
if (!value) return;
lblTransformType->setText(perspectiveTransformButton->toolTip());
ToolTransformArgs *config = m_transaction->currentConfig();
config->setMode(ToolTransformArgs::PERSPECTIVE_4POINT);
emit sigResetTransform();
}
void KisToolTransformConfigWidget::slotFilterChanged(const KoID &filterId)
{
ToolTransformArgs *config = m_transaction->currentConfig();
config->setFilterId(filterId.id());
notifyConfigChanged();
}
void KisToolTransformConfigWidget::slotRotationCenterChanged(int index)
{
if (m_uiSlotsBlocked) return;
if (index >= 0 && index <= 8) {
ToolTransformArgs *config = m_transaction->currentConfig();
double i = m_handleDir[index].x();
double j = m_handleDir[index].y();
config->setRotationCenterOffset(QPointF(i * m_transaction->originalHalfWidth(),
j * m_transaction->originalHalfHeight()));
notifyConfigChanged();
updateConfig(*config);
}
}
void KisToolTransformConfigWidget::slotTransformAroundRotationCenter(bool value)
{
if (m_uiSlotsBlocked) return;
ToolTransformArgs *config = m_transaction->currentConfig();
config->setTransformAroundRotationCenter(value);
notifyConfigChanged();
notifyEditingFinished();
}
void KisToolTransformConfigWidget::slotSetScaleX(int value)
{
if (m_uiSlotsBlocked) return;
ToolTransformArgs *config = m_transaction->currentConfig();
{
KisTransformUtils::AnchorHolder keeper(config->transformAroundRotationCenter(), config);
config->setScaleX(value / 100.);
}
if (config->keepAspectRatio()) {
blockNotifications();
int calculatedValue = int( value/ m_scaleRatio );
scaleYBox->blockSignals(true);
scaleYBox->setValue(calculatedValue);
{
KisTransformUtils::AnchorHolder keeper(config->transformAroundRotationCenter(), config);
config->setScaleY(calculatedValue / 100.);
}
scaleYBox->blockSignals(false);
unblockNotifications();
}
notifyConfigChanged();
notifyEditingFinished();
}
void KisToolTransformConfigWidget::slotSetScaleY(int value)
{
if (m_uiSlotsBlocked) return;
ToolTransformArgs *config = m_transaction->currentConfig();
{
KisTransformUtils::AnchorHolder keeper(config->transformAroundRotationCenter(), config);
config->setScaleY(value / 100.);
}
if (config->keepAspectRatio()) {
blockNotifications();
int calculatedValue = int(m_scaleRatio * value);
scaleXBox->blockSignals(true);
scaleXBox->setValue(calculatedValue);
{
KisTransformUtils::AnchorHolder keeper(config->transformAroundRotationCenter(), config);
config->setScaleX(calculatedValue / 100.);
}
scaleXBox->blockSignals(false);
unblockNotifications();
}
notifyConfigChanged();
notifyEditingFinished();
}
void KisToolTransformConfigWidget::slotSetShearX(qreal value)
{
if (m_uiSlotsBlocked) return;
ToolTransformArgs *config = m_transaction->currentConfig();
{
KisTransformUtils::AnchorHolder keeper(config->transformAroundRotationCenter(), config);
config->setShearX((double)value);
}
notifyConfigChanged();
notifyEditingFinished();
}
void KisToolTransformConfigWidget::slotSetShearY(qreal value)
{
if (m_uiSlotsBlocked) return;
ToolTransformArgs *config = m_transaction->currentConfig();
{
KisTransformUtils::AnchorHolder keeper(config->transformAroundRotationCenter(), config);
config->setShearY((double)value);
}
notifyConfigChanged();
notifyEditingFinished();
}
void KisToolTransformConfigWidget::slotSetTranslateX(int value)
{
if (m_uiSlotsBlocked) return;
ToolTransformArgs *config = m_transaction->currentConfig();
const QPointF anchorPoint = config->originalCenter() + config->rotationCenterOffset();
const KisTransformUtils::MatricesPack m(*config);
const QPointF anchorPointView = m.finalTransform().map(anchorPoint);
const QPointF newAnchorPointView(value, anchorPointView.y());
config->setTransformedCenter(config->transformedCenter() + newAnchorPointView - anchorPointView);
notifyConfigChanged();
}
void KisToolTransformConfigWidget::slotSetTranslateY(int value)
{
if (m_uiSlotsBlocked) return;
ToolTransformArgs *config = m_transaction->currentConfig();
const QPointF anchorPoint = config->originalCenter() + config->rotationCenterOffset();
const KisTransformUtils::MatricesPack m(*config);
const QPointF anchorPointView = m.finalTransform().map(anchorPoint);
const QPointF newAnchorPointView(anchorPointView.x(), value);
config->setTransformedCenter(config->transformedCenter() + newAnchorPointView - anchorPointView);
notifyConfigChanged();
}
void KisToolTransformConfigWidget::slotSetAX(qreal value)
{
if (m_uiSlotsBlocked) return;
ToolTransformArgs *config = m_transaction->currentConfig();
{
KisTransformUtils::AnchorHolder keeper(config->transformAroundRotationCenter(), config);
config->setAX(degreeToRadian((double)value));
}
notifyConfigChanged();
notifyEditingFinished();
}
void KisToolTransformConfigWidget::slotSetAY(qreal value)
{
if (m_uiSlotsBlocked) return;
ToolTransformArgs *config = m_transaction->currentConfig();
{
KisTransformUtils::AnchorHolder keeper(config->transformAroundRotationCenter(), config);
config->setAY(degreeToRadian((double)value));
}
notifyConfigChanged();
notifyEditingFinished();
}
void KisToolTransformConfigWidget::slotSetAZ(qreal value)
{
if (m_uiSlotsBlocked) return;
ToolTransformArgs *config = m_transaction->currentConfig();
{
KisTransformUtils::AnchorHolder keeper(config->transformAroundRotationCenter(), config);
config->setAZ(degreeToRadian((double)value));
}
notifyConfigChanged();
notifyEditingFinished();
}
void KisToolTransformConfigWidget::slotFlipX()
{
ToolTransformArgs *config = m_transaction->currentConfig();
{
KisTransformUtils::AnchorHolder keeper(config->transformAroundRotationCenter(), config);
config->setScaleX(config->scaleX() * -1);
}
notifyConfigChanged();
notifyEditingFinished();
}
void KisToolTransformConfigWidget::slotFlipY()
{
ToolTransformArgs *config = m_transaction->currentConfig();
{
KisTransformUtils::AnchorHolder keeper(config->transformAroundRotationCenter(), config);
config->setScaleY(config->scaleY() * -1);
}
notifyConfigChanged();
notifyEditingFinished();
}
void KisToolTransformConfigWidget::slotRotateCW()
{
ToolTransformArgs *config = m_transaction->currentConfig();
{
KisTransformUtils::AnchorHolder keeper(config->transformAroundRotationCenter(), config);
config->setAZ(normalizeAngle(config->aZ() + M_PI_2));
}
notifyConfigChanged();
notifyEditingFinished();
}
void KisToolTransformConfigWidget::slotRotateCCW()
{
ToolTransformArgs *config = m_transaction->currentConfig();
{
KisTransformUtils::AnchorHolder keeper(config->transformAroundRotationCenter(), config);
config->setAZ(normalizeAngle(config->aZ() - M_PI_2));
}
notifyConfigChanged();
notifyEditingFinished();
}
// change free transform setting we want to alter (all radio buttons call this)
void KisToolTransformConfigWidget::slotTransformAreaVisible(bool value)
{
Q_UNUSED(value);
//QCheckBox sender = (QCheckBox)(*)(QObject::sender());
QString senderName = QObject::sender()->objectName();
// only show setting with what we have selected
rotationGroup->hide();
shearGroup->hide();
scaleGroup->hide();
moveGroup->hide();
if ("freeMoveRadioButton" == senderName)
{
moveGroup->show();
}
else if ("freeShearRadioButton" == senderName)
{
shearGroup->show();
}
else if ("freeScaleRadioButton" == senderName)
{
scaleGroup->show();
}
else
{
rotationGroup->show();
}
}
void KisToolTransformConfigWidget::slotSetKeepAspectRatio(bool value)
{
if (m_uiSlotsBlocked) return;
ToolTransformArgs *config = m_transaction->currentConfig();
config->setKeepAspectRatio(value);
if (value) {
blockNotifications();
int tmpXScaleBox = scaleXBox->value();
int tmpYScaleBox = scaleYBox->value();
m_scaleRatio = (tmpXScaleBox / (double) tmpYScaleBox);
unblockNotifications();
}
notifyConfigChanged();
}
void KisToolTransformConfigWidget::slotSetWarpAlpha(qreal value)
{
if (m_uiSlotsBlocked) return;
ToolTransformArgs *config = m_transaction->currentConfig();
config->setAlpha((double)value);
notifyConfigChanged();
notifyEditingFinished();
}
void KisToolTransformConfigWidget::slotSetWarpDensity(int value)
{
if (m_uiSlotsBlocked) return;
setDefaultWarpPoints(value);
}
void KisToolTransformConfigWidget::setDefaultWarpPoints(int pointsPerLine)
{
ToolTransformArgs *config = m_transaction->currentConfig();
KisTransformUtils::setDefaultWarpPoints(pointsPerLine, m_transaction, config);
notifyConfigChanged();
}
void KisToolTransformConfigWidget::activateCustomWarpPoints(bool enabled)
{
ToolTransformArgs *config = m_transaction->currentConfig();
densityBox->setEnabled(!enabled);
customWarpWidget->setEnabled(enabled);
if (!enabled) {
config->setEditingTransformPoints(false);
setDefaultWarpPoints(densityBox->value());
config->setWarpCalculation(KisWarpTransformWorker::WarpCalculation::GRID);
} else {
config->setEditingTransformPoints(true);
config->setWarpCalculation(KisWarpTransformWorker::WarpCalculation::DRAW);
setDefaultWarpPoints(0);
}
updateLockPointsButtonCaption();
}
void KisToolTransformConfigWidget::slotWarpDefaultPointsButtonClicked(bool value)
{
if (m_uiSlotsBlocked) return;
activateCustomWarpPoints(!value);
}
void KisToolTransformConfigWidget::slotWarpCustomPointsButtonClicked(bool value)
{
if (m_uiSlotsBlocked) return;
activateCustomWarpPoints(value);
}
void KisToolTransformConfigWidget::slotWarpResetPointsButtonClicked()
{
if (m_uiSlotsBlocked) return;
activateCustomWarpPoints(true);
}
void KisToolTransformConfigWidget::slotWarpLockPointsButtonClicked()
{
if (m_uiSlotsBlocked) return;
ToolTransformArgs *config = m_transaction->currentConfig();
config->setEditingTransformPoints(!config->isEditingTransformPoints());
if (config->isEditingTransformPoints()) {
// reinit the transf points to their original value
ToolTransformArgs *config = m_transaction->currentConfig();
int nbPoints = config->origPoints().size();
for (int i = 0; i < nbPoints; ++i) {
config->transfPoint(i) = config->origPoint(i);
}
}
updateLockPointsButtonCaption();
notifyConfigChanged();
}
void KisToolTransformConfigWidget::slotWarpTypeChanged(int index)
{
if (m_uiSlotsBlocked) return;
ToolTransformArgs *config = m_transaction->currentConfig();
switch (index) {
case KisWarpTransformWorker::AFFINE_TRANSFORM:
case KisWarpTransformWorker::SIMILITUDE_TRANSFORM:
case KisWarpTransformWorker::RIGID_TRANSFORM:
config->setWarpType((KisWarpTransformWorker::WarpType)index);
break;
default:
config->setWarpType(KisWarpTransformWorker::RIGID_TRANSFORM);
break;
}
notifyConfigChanged();
}
void KisToolTransformConfigWidget::slotCageOptionsChanged(int value)
{
if ( value == 0)
{
slotEditCagePoints(true);
}
else
{
slotEditCagePoints(false);
}
notifyEditingFinished();
}
void KisToolTransformConfigWidget::slotEditCagePoints(bool value)
{
if (m_uiSlotsBlocked) return;
ToolTransformArgs *config = m_transaction->currentConfig();
config->refTransformedPoints() = config->origPoints();
config->setEditingTransformPoints(value);
notifyConfigChanged();
}
void KisToolTransformConfigWidget::slotGranularityChanged(QString value)
{
if (m_uiSlotsBlocked) return;
KIS_SAFE_ASSERT_RECOVER_RETURN(value.toInt() > 1);
ToolTransformArgs *config = m_transaction->currentConfig();
config->setPixelPrecision(value.toInt());
notifyConfigChanged();
}
void KisToolTransformConfigWidget::slotPreviewGranularityChanged(QString value)
{
if (m_uiSlotsBlocked) return;
KIS_SAFE_ASSERT_RECOVER_RETURN(value.toInt() > 1);
ToolTransformArgs *config = m_transaction->currentConfig();
config->setPreviewPixelPrecision(value.toInt());
notifyConfigChanged();
}
diff --git a/plugins/tools/tool_transform2/kis_tool_transform_config_widget.h b/plugins/tools/tool_transform2/kis_tool_transform_config_widget.h
index 23304e360b..c78bd58ee3 100644
--- a/plugins/tools/tool_transform2/kis_tool_transform_config_widget.h
+++ b/plugins/tools/tool_transform2/kis_tool_transform_config_widget.h
@@ -1,152 +1,151 @@
/*
* Copyright (c) 2013 Dmitry Kazakov
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __KIS_TOOL_TRANSFORM_CONFIG_WIDGET_H
#define __KIS_TOOL_TRANSFORM_CONFIG_WIDGET_H
#include "transform_transaction_properties.h"
#include "tool_transform_args.h"
#include "ui_wdg_tool_transform.h"
class KisCanvas2;
class KisToolTransformConfigWidget : public QWidget, private Ui::WdgToolTransform
{
Q_OBJECT
public:
KisToolTransformConfigWidget(TransformTransactionProperties *transaction, KisCanvas2 *canvas, bool workRecursively, QWidget *parent);
void setApplyResetDisabled(bool disabled);
void resetRotationCenterButtons();
void setDefaultWarpPoints(int pointsPerLine = -1);
void setTooBigLabelVisible(bool value);
- bool showDecorations() const;
bool workRecursively() const;
public Q_SLOTS:
void updateConfig(const ToolTransformArgs &config);
void slotUpdateIcons();
Q_SIGNALS:
void sigConfigChanged();
void sigApplyTransform();
void sigResetTransform();
void sigRestartTransform();
void sigEditingFinished();
public Q_SLOTS:
void slotFilterChanged(const KoID &filter);
void slotWarpTypeChanged(int index);
void slotRotationCenterChanged(int index);
void slotTransformAroundRotationCenter(bool value);
void slotSetScaleX(int value);
void slotSetScaleY(int value);
void slotSetShearX(qreal value);
void slotSetShearY(qreal value);
void slotSetTranslateX(int value);
void slotSetTranslateY(int value);
void slotSetAX(qreal value);
void slotSetAY(qreal value);
void slotSetAZ(qreal value);
void slotFlipX();
void slotFlipY();
void slotRotateCW();
void slotRotateCCW();
void slotSetWarpAlpha(qreal value);
void slotSetWarpDensity(int value);
void slotSetKeepAspectRatio(bool value);
void slotTransformAreaVisible(bool value);
void slotWarpDefaultPointsButtonClicked(bool value);
void slotWarpCustomPointsButtonClicked(bool value);
void slotWarpLockPointsButtonClicked();
void slotWarpResetPointsButtonClicked();
void slotSetFreeTransformModeButtonClicked(bool);
void slotSetWarpModeButtonClicked(bool);
void slotSetCageModeButtonClicked(bool);
void slotCageOptionsChanged(int);
void slotSetPerspectiveModeButtonClicked(bool);
void slotSetLiquifyModeButtonClicked(bool);
void slotButtonBoxClicked(QAbstractButton *button);
void slotEditCagePoints(bool value);
void liquifySizeChanged(qreal value);
void liquifyAmountChanged(qreal value);
void liquifyFlowChanged(qreal value);
void liquifyBuildUpChanged(int value);
void liquifySpacingChanged(qreal value);
void liquifySizePressureChanged(bool value);
void liquifyAmountPressureChanged(bool value);
void liquifyReverseDirectionChanged(bool value);
void slotLiquifyModeChanged(int value);
void notifyEditingFinished();
void slotGranularityChanged(QString value);
void slotPreviewGranularityChanged(QString value);
private:
// rad being in |R, the returned value is in [0; 360]
double radianToDegree(double rad);
// degree being in |R, the returned value is in [0; 2*M_PI]
double degreeToRadian(double degree);
void blockNotifications();
void unblockNotifications();
void notifyConfigChanged();
void blockUiSlots();
void unblockUiSlots();
void activateCustomWarpPoints(bool enabled);
void updateLockPointsButtonCaption();
void updateLiquifyControls();
void resetUIOptions();
private:
static const int DEFAULT_POINTS_PER_LINE;
private:
TransformTransactionProperties *m_transaction;
QPointF m_handleDir[9];
QButtonGroup *m_rotationCenterButtons;
int m_notificationsBlocked;
int m_uiSlotsBlocked;
double m_scaleRatio;
bool m_configChanged;
};
#endif /* __KIS_TOOL_TRANSFORM_CONFIG_WIDGET_H */
diff --git a/plugins/tools/tool_transform2/strokes/transform_stroke_strategy.cpp b/plugins/tools/tool_transform2/strokes/transform_stroke_strategy.cpp
index 67490a23c0..ba97d4f312 100644
--- a/plugins/tools/tool_transform2/strokes/transform_stroke_strategy.cpp
+++ b/plugins/tools/tool_transform2/strokes/transform_stroke_strategy.cpp
@@ -1,688 +1,681 @@
/*
* Copyright (c) 2013 Dmitry Kazakov
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "transform_stroke_strategy.h"
#include
#include "kundo2commandextradata.h"
#include "kis_node_progress_proxy.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include "kis_transform_mask_adapter.h"
#include "kis_transform_utils.h"
#include "kis_abstract_projection_plane.h"
#include "kis_recalculate_transform_mask_job.h"
#include "kis_projection_leaf.h"
#include "kis_modify_transform_mask_command.h"
#include "kis_sequential_iterator.h"
#include "kis_selection_mask.h"
#include "kis_image_config.h"
#include "kis_layer_utils.h"
#include
#include
#include "transform_transaction_properties.h"
#include "krita_container_utils.h"
#include "commands_new/kis_saved_commands.h"
#include "kis_command_ids.h"
#include "KisRunnableStrokeJobUtils.h"
#include "commands_new/KisHoldUIUpdatesCommand.h"
TransformStrokeStrategy::TransformStrokeStrategy(ToolTransformArgs::TransformMode mode,
bool workRecursively,
const QString &filterId,
bool forceReset,
KisNodeSP rootNode,
KisSelectionSP selection,
KisStrokeUndoFacade *undoFacade,
KisUpdatesFacade *updatesFacade)
: KisStrokeStrategyUndoCommandBased(kundo2_i18n("Transform"), false, undoFacade),
m_updatesFacade(updatesFacade),
m_mode(mode),
m_workRecursively(workRecursively),
m_filterId(filterId),
m_forceReset(forceReset),
m_selection(selection)
{
KIS_SAFE_ASSERT_RECOVER_NOOP(!selection || !dynamic_cast(rootNode.data()));
m_rootNode = rootNode;
setMacroId(KisCommandUtils::TransformToolId);
}
TransformStrokeStrategy::~TransformStrokeStrategy()
{
}
KisPaintDeviceSP TransformStrokeStrategy::createDeviceCache(KisPaintDeviceSP dev)
{
KisPaintDeviceSP cache;
if (m_selection) {
QRect srcRect = m_selection->selectedExactRect();
cache = dev->createCompositionSourceDevice();
KisPainter gc(cache);
gc.setSelection(m_selection);
gc.bitBlt(srcRect.topLeft(), dev, srcRect);
} else {
cache = dev->createCompositionSourceDevice(dev);
}
return cache;
}
bool TransformStrokeStrategy::haveDeviceInCache(KisPaintDeviceSP src)
{
QMutexLocker l(&m_devicesCacheMutex);
return m_devicesCacheHash.contains(src.data());
}
void TransformStrokeStrategy::putDeviceCache(KisPaintDeviceSP src, KisPaintDeviceSP cache)
{
QMutexLocker l(&m_devicesCacheMutex);
m_devicesCacheHash.insert(src.data(), cache);
}
KisPaintDeviceSP TransformStrokeStrategy::getDeviceCache(KisPaintDeviceSP src)
{
QMutexLocker l(&m_devicesCacheMutex);
KisPaintDeviceSP cache = m_devicesCacheHash.value(src.data());
if (!cache) {
warnKrita << "WARNING: Transform Stroke: the device is absent in cache!";
}
return cache;
}
bool TransformStrokeStrategy::checkBelongsToSelection(KisPaintDeviceSP device) const
{
return m_selection &&
(device == m_selection->pixelSelection().data() ||
device == m_selection->projection().data());
}
void TransformStrokeStrategy::doStrokeCallback(KisStrokeJobData *data)
{
TransformData *td = dynamic_cast(data);
ClearSelectionData *csd = dynamic_cast(data);
PreparePreviewData *ppd = dynamic_cast(data);
TransformAllData *runAllData = dynamic_cast(data);
if (runAllData) {
// here we only save the passed args, actual
// transformation will be performed during
// finish job
m_savedTransformArgs = runAllData->config;
} else if (ppd) {
KisNodeSP rootNode = m_rootNode;
KisNodeList processedNodes = m_processedNodes;
KisPaintDeviceSP previewDevice;
if (rootNode->childCount() || !rootNode->paintDevice()) {
if (KisTransformMask* tmask =
dynamic_cast(rootNode.data())) {
previewDevice = createDeviceCache(tmask->buildPreviewDevice());
KIS_SAFE_ASSERT_RECOVER(!m_selection) {
m_selection = 0;
}
} else if (KisGroupLayer *group = dynamic_cast(rootNode.data())) {
const QRect bounds = group->image()->bounds();
KisImageSP clonedImage = new KisImage(0,
bounds.width(),
bounds.height(),
group->colorSpace(),
"transformed_image");
KisGroupLayerSP clonedGroup = dynamic_cast(group->clone().data());
// In case the group is pass-through, it needs to be disabled for the preview,
// otherwise it will crash (no parent for a preview leaf).
// Also it needs to be done before setting the root layer for clonedImage.
// Result: preview for pass-through group is the same as for standard group
// (i.e. filter layers in the group won't affect the layer stack for a moment).
clonedGroup->setPassThroughMode(false);
clonedImage->setRootLayer(clonedGroup);
QQueue linearizedSrcNodes;
KisLayerUtils::recursiveApplyNodes(rootNode, [&linearizedSrcNodes] (KisNodeSP node) {
linearizedSrcNodes.enqueue(node);
});
KisLayerUtils::recursiveApplyNodes(KisNodeSP(clonedGroup), [&linearizedSrcNodes, processedNodes] (KisNodeSP node) {
KisNodeSP srcNode = linearizedSrcNodes.dequeue();
if (!processedNodes.contains(srcNode)) {
node->setVisible(false);
}
});
clonedImage->refreshGraph();
KisLayerUtils::refreshHiddenAreaAsync(clonedImage, clonedGroup, clonedImage->bounds());
KisLayerUtils::forceAllDelayedNodesUpdate(clonedGroup);
clonedImage->waitForDone();
previewDevice = createDeviceCache(clonedImage->projection());
previewDevice->setDefaultBounds(group->projection()->defaultBounds());
// we delete the cloned image in GUI thread to ensure
// no signals are still pending
makeKisDeleteLaterWrapper(clonedImage)->deleteLater();
} else {
rootNode->projectionLeaf()->explicitlyRegeneratePassThroughProjection();
previewDevice = createDeviceCache(rootNode->projection());
}
} else {
KisPaintDeviceSP cacheDevice = createDeviceCache(rootNode->paintDevice());
if (dynamic_cast(rootNode.data())) {
KIS_SAFE_ASSERT_RECOVER (cacheDevice->colorSpace()->colorModelId() == GrayAColorModelID &&
cacheDevice->colorSpace()->colorDepthId() == Integer8BitsColorDepthID) {
cacheDevice->convertTo(KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Integer8BitsColorDepthID.id()));
}
previewDevice = new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8());
const QRect srcRect = cacheDevice->exactBounds();
KisSequentialConstIterator srcIt(cacheDevice, srcRect);
KisSequentialIterator dstIt(previewDevice, srcRect);
const int pixelSize = previewDevice->colorSpace()->pixelSize();
KisImageConfig cfg(true);
KoColor pixel(cfg.selectionOverlayMaskColor(), previewDevice->colorSpace());
const qreal coeff = 1.0 / 255.0;
const qreal baseOpacity = 0.5;
while (srcIt.nextPixel() && dstIt.nextPixel()) {
qreal gray = srcIt.rawDataConst()[0];
qreal alpha = srcIt.rawDataConst()[1];
pixel.setOpacity(quint8(gray * alpha * baseOpacity * coeff));
memcpy(dstIt.rawData(), pixel.data(), pixelSize);
}
} else {
previewDevice = cacheDevice;
}
putDeviceCache(rootNode->paintDevice(), cacheDevice);
}
- QPainterPath selectionOutline;
- if (m_selection && m_selection->outlineCacheValid()) {
- selectionOutline = m_selection->outlineCache();
- } else if (previewDevice) {
- selectionOutline.addRect(previewDevice->exactBounds());
- }
-
- emit sigPreviewDeviceReady(previewDevice, selectionOutline);
+ emit sigPreviewDeviceReady(previewDevice);
} else if(td) {
if (td->destination == TransformData::PAINT_DEVICE) {
QRect oldExtent = td->node->extent();
KisPaintDeviceSP device = td->node->paintDevice();
if (device && !checkBelongsToSelection(device)) {
KisPaintDeviceSP cachedPortion = getDeviceCache(device);
Q_ASSERT(cachedPortion);
KisTransaction transaction(device);
KisProcessingVisitor::ProgressHelper helper(td->node);
transformAndMergeDevice(td->config, cachedPortion,
device, &helper);
runAndSaveCommand(KUndo2CommandSP(transaction.endAndTake()),
KisStrokeJobData::CONCURRENT,
KisStrokeJobData::NORMAL);
td->node->setDirty(oldExtent | td->node->extent());
} else if (KisExternalLayer *extLayer =
dynamic_cast(td->node.data())) {
if (td->config.mode() == ToolTransformArgs::FREE_TRANSFORM ||
(td->config.mode() == ToolTransformArgs::PERSPECTIVE_4POINT &&
extLayer->supportsPerspectiveTransform())) {
QVector3D transformedCenter;
KisTransformWorker w = KisTransformUtils::createTransformWorker(td->config, 0, 0, &transformedCenter);
QTransform t = w.transform();
runAndSaveCommand(KUndo2CommandSP(extLayer->transform(t)),
KisStrokeJobData::CONCURRENT,
KisStrokeJobData::NORMAL);
}
} else if (KisTransformMask *transformMask =
dynamic_cast(td->node.data())) {
runAndSaveCommand(KUndo2CommandSP(
new KisModifyTransformMaskCommand(transformMask,
KisTransformMaskParamsInterfaceSP(
new KisTransformMaskAdapter(td->config)))),
KisStrokeJobData::CONCURRENT,
KisStrokeJobData::NORMAL);
}
} else if (m_selection) {
/**
* We use usual transaction here, because we cannot calsulate
* transformation for perspective and warp workers.
*/
KisTransaction transaction(m_selection->pixelSelection());
KisProcessingVisitor::ProgressHelper helper(td->node);
KisTransformUtils::transformDevice(td->config,
m_selection->pixelSelection(),
&helper);
runAndSaveCommand(KUndo2CommandSP(transaction.endAndTake()),
KisStrokeJobData::CONCURRENT,
KisStrokeJobData::NORMAL);
}
} else if (csd) {
KisPaintDeviceSP device = csd->node->paintDevice();
if (device && !checkBelongsToSelection(device)) {
if (!haveDeviceInCache(device)) {
putDeviceCache(device, createDeviceCache(device));
}
clearSelection(device);
/**
* Selection masks might have an overlay enabled, we should disable that
*/
if (KisSelectionMask *mask = dynamic_cast(csd->node.data())) {
KisSelectionSP selection = mask->selection();
if (selection) {
selection->setVisible(false);
m_deactivatedSelections.append(selection);
mask->setDirty();
}
}
} else if (KisExternalLayer *externalLayer = dynamic_cast(csd->node.data())) {
externalLayer->projectionLeaf()->setTemporaryHiddenFromRendering(true);
externalLayer->setDirty();
m_hiddenProjectionLeaves.append(csd->node);
} else if (KisTransformMask *transformMask =
dynamic_cast(csd->node.data())) {
runAndSaveCommand(KUndo2CommandSP(
new KisModifyTransformMaskCommand(transformMask,
KisTransformMaskParamsInterfaceSP(
new KisDumbTransformMaskParams(true)))),
KisStrokeJobData::SEQUENTIAL,
KisStrokeJobData::NORMAL);
}
} else {
KisStrokeStrategyUndoCommandBased::doStrokeCallback(data);
}
}
void TransformStrokeStrategy::clearSelection(KisPaintDeviceSP device)
{
KisTransaction transaction(device);
if (m_selection) {
device->clearSelection(m_selection);
} else {
QRect oldExtent = device->extent();
device->clear();
device->setDirty(oldExtent);
}
runAndSaveCommand(KUndo2CommandSP(transaction.endAndTake()),
KisStrokeJobData::SEQUENTIAL,
KisStrokeJobData::NORMAL);
}
void TransformStrokeStrategy::transformAndMergeDevice(const ToolTransformArgs &config,
KisPaintDeviceSP src,
KisPaintDeviceSP dst,
KisProcessingVisitor::ProgressHelper *helper)
{
KoUpdaterPtr mergeUpdater = src != dst ? helper->updater() : 0;
KisTransformUtils::transformDevice(config, src, helper);
if (src != dst) {
QRect mergeRect = src->extent();
KisPainter painter(dst);
painter.setProgress(mergeUpdater);
painter.bitBlt(mergeRect.topLeft(), src, mergeRect);
painter.end();
}
}
struct TransformExtraData : public KUndo2CommandExtraData
{
ToolTransformArgs savedTransformArgs;
KisNodeSP rootNode;
KisNodeList transformedNodes;
KUndo2CommandExtraData* clone() const override {
return new TransformExtraData(*this);
}
};
void TransformStrokeStrategy::postProcessToplevelCommand(KUndo2Command *command)
{
KIS_SAFE_ASSERT_RECOVER_RETURN(m_savedTransformArgs);
TransformExtraData *data = new TransformExtraData();
data->savedTransformArgs = *m_savedTransformArgs;
data->rootNode = m_rootNode;
data->transformedNodes = m_processedNodes;
command->setExtraData(data);
KisSavedMacroCommand *macroCommand = dynamic_cast(command);
KIS_SAFE_ASSERT_RECOVER_NOOP(macroCommand);
if (m_overriddenCommand && macroCommand) {
macroCommand->setOverrideInfo(m_overriddenCommand, m_skippedWhileMergeCommands);
}
KisStrokeStrategyUndoCommandBased::postProcessToplevelCommand(command);
}
bool TransformStrokeStrategy::fetchArgsFromCommand(const KUndo2Command *command, ToolTransformArgs *args, KisNodeSP *rootNode, KisNodeList *transformedNodes)
{
const TransformExtraData *data = dynamic_cast(command->extraData());
if (data) {
*args = data->savedTransformArgs;
*rootNode = data->rootNode;
*transformedNodes = data->transformedNodes;
}
return bool(data);
}
QList TransformStrokeStrategy::fetchNodesList(ToolTransformArgs::TransformMode mode, KisNodeSP root, bool recursive)
{
QList result;
auto fetchFunc =
[&result, mode, root] (KisNodeSP node) {
if (node->isEditable(node == root) &&
(!node->inherits("KisShapeLayer") || mode == ToolTransformArgs::FREE_TRANSFORM) &&
!node->inherits("KisFileLayer") &&
(!node->inherits("KisTransformMask") || node == root)) {
result << node;
}
};
if (recursive) {
KisLayerUtils::recursiveApplyNodes(root, fetchFunc);
} else {
fetchFunc(root);
}
return result;
}
bool TransformStrokeStrategy::tryInitArgsFromNode(KisNodeSP node, ToolTransformArgs *args)
{
bool result = false;
if (KisTransformMaskSP mask =
dynamic_cast(node.data())) {
KisTransformMaskParamsInterfaceSP savedParams =
mask->transformParams();
KisTransformMaskAdapter *adapter =
dynamic_cast(savedParams.data());
if (adapter) {
*args = adapter->transformArgs();
result = true;
}
}
return result;
}
bool TransformStrokeStrategy::tryFetchArgsFromCommandAndUndo(ToolTransformArgs *outArgs,
ToolTransformArgs::TransformMode mode,
KisNodeSP currentNode,
KisNodeList selectedNodes,
QVector *undoJobs)
{
bool result = false;
const KUndo2Command *lastCommand = undoFacade()->lastExecutedCommand();
KisNodeSP oldRootNode;
KisNodeList oldTransformedNodes;
ToolTransformArgs args;
if (lastCommand &&
TransformStrokeStrategy::fetchArgsFromCommand(lastCommand, &args, &oldRootNode, &oldTransformedNodes) &&
args.mode() == mode &&
oldRootNode == currentNode) {
if (KritaUtils::compareListsUnordered(oldTransformedNodes, selectedNodes)) {
args.saveContinuedState();
*outArgs = args;
const KisSavedMacroCommand *command = dynamic_cast(lastCommand);
KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(command, false);
// the jobs are fetched as !shouldGoToHistory,
// so there is no need to put them into
// m_skippedWhileMergeCommands
command->getCommandExecutionJobs(undoJobs, true, false);
m_overriddenCommand = command;
result = true;
}
}
return result;
}
void TransformStrokeStrategy::initStrokeCallback()
{
KisStrokeStrategyUndoCommandBased::initStrokeCallback();
if (m_selection) {
m_selection->setVisible(false);
m_deactivatedSelections.append(m_selection);
}
ToolTransformArgs initialTransformArgs;
m_processedNodes = fetchNodesList(m_mode, m_rootNode, m_workRecursively);
bool argsAreInitialized = false;
QVector lastCommandUndoJobs;
if (!m_forceReset && tryFetchArgsFromCommandAndUndo(&initialTransformArgs,
m_mode,
m_rootNode,
m_processedNodes,
&lastCommandUndoJobs)) {
argsAreInitialized = true;
} else if (!m_forceReset && tryInitArgsFromNode(m_rootNode, &initialTransformArgs)) {
argsAreInitialized = true;
}
QVector extraInitJobs;
extraInitJobs << new Data(new KisHoldUIUpdatesCommand(m_updatesFacade, KisCommandUtils::FlipFlopCommand::INITIALIZING), false, KisStrokeJobData::BARRIER);
extraInitJobs << lastCommandUndoJobs;
KritaUtils::addJobSequential(extraInitJobs, [this]() {
/**
* We must request shape layers to rerender areas outside image bounds
*/
KisLayerUtils::forceAllHiddenOriginalsUpdate(m_rootNode);
});
KritaUtils::addJobBarrier(extraInitJobs, [this]() {
/**
* We must ensure that the currently selected subtree
* has finished all its updates.
*/
KisLayerUtils::forceAllDelayedNodesUpdate(m_rootNode);
});
KritaUtils::addJobBarrier(extraInitJobs, [this, initialTransformArgs, argsAreInitialized]() mutable {
QRect srcRect;
if (m_selection) {
srcRect = m_selection->selectedExactRect();
} else {
srcRect = QRect();
Q_FOREACH (KisNodeSP node, m_processedNodes) {
// group layers may have a projection of layers
// that are locked and will not be transformed
if (node->inherits("KisGroupLayer")) continue;
if (const KisTransformMask *mask = dynamic_cast(node.data())) {
srcRect |= mask->sourceDataBounds();
} else {
srcRect |= node->exactBounds();
}
}
}
TransformTransactionProperties transaction(srcRect, &initialTransformArgs, m_rootNode, m_processedNodes);
if (!argsAreInitialized) {
initialTransformArgs = KisTransformUtils::resetArgsForMode(m_mode, m_filterId, transaction);
}
this->m_initialTransformArgs = initialTransformArgs;
emit this->sigTransactionGenerated(transaction, initialTransformArgs, this);
});
extraInitJobs << new PreparePreviewData();
Q_FOREACH (KisNodeSP node, m_processedNodes) {
extraInitJobs << new ClearSelectionData(node);
}
extraInitJobs << new Data(toQShared(new KisHoldUIUpdatesCommand(m_updatesFacade, KisCommandUtils::FlipFlopCommand::FINALIZING)), false, KisStrokeJobData::BARRIER);
if (!lastCommandUndoJobs.isEmpty()) {
KIS_SAFE_ASSERT_RECOVER_NOOP(m_overriddenCommand);
for (auto it = extraInitJobs.begin(); it != extraInitJobs.end(); ++it) {
(*it)->setCancellable(false);
}
}
addMutatedJobs(extraInitJobs);
}
void TransformStrokeStrategy::finishStrokeImpl(bool applyTransform, const ToolTransformArgs &args)
{
/**
* Since our finishStrokeCallback() initiates new jobs,
* cancellation request may come even after
* finishStrokeCallback() (cancellations may be called
* until there are no jobs left in the stroke's queue).
*
* Therefore we should check for double-entry here and
* make sure the finilizing jobs are no cancellable.
*/
if (m_finalizingActionsStarted) return;
m_finalizingActionsStarted = true;
QVector mutatedJobs;
if (applyTransform) {
Q_FOREACH (KisNodeSP node, m_processedNodes) {
mutatedJobs << new TransformData(TransformData::PAINT_DEVICE,
args,
node);
}
mutatedJobs << new TransformData(TransformData::SELECTION,
args,
m_rootNode);
}
KritaUtils::addJobBarrier(mutatedJobs, [this, applyTransform]() {
Q_FOREACH (KisSelectionSP selection, m_deactivatedSelections) {
selection->setVisible(true);
}
Q_FOREACH (KisNodeSP node, m_hiddenProjectionLeaves) {
node->projectionLeaf()->setTemporaryHiddenFromRendering(false);
}
if (applyTransform) {
KisStrokeStrategyUndoCommandBased::finishStrokeCallback();
} else {
KisStrokeStrategyUndoCommandBased::cancelStrokeCallback();
}
});
for (auto it = mutatedJobs.begin(); it != mutatedJobs.end(); ++it) {
(*it)->setCancellable(false);
}
addMutatedJobs(mutatedJobs);
}
void TransformStrokeStrategy::finishStrokeCallback()
{
if (!m_savedTransformArgs || m_savedTransformArgs->isIdentity()) {
cancelStrokeCallback();
return;
}
finishStrokeImpl(true, *m_savedTransformArgs);
}
void TransformStrokeStrategy::cancelStrokeCallback()
{
const bool shouldRecoverSavedInitialState =
!m_initialTransformArgs.isIdentity();
if (shouldRecoverSavedInitialState) {
m_savedTransformArgs = m_initialTransformArgs;
}
finishStrokeImpl(shouldRecoverSavedInitialState, *m_savedTransformArgs);
}
diff --git a/plugins/tools/tool_transform2/strokes/transform_stroke_strategy.h b/plugins/tools/tool_transform2/strokes/transform_stroke_strategy.h
index fb196b13a2..7ef746f2a7 100644
--- a/plugins/tools/tool_transform2/strokes/transform_stroke_strategy.h
+++ b/plugins/tools/tool_transform2/strokes/transform_stroke_strategy.h
@@ -1,177 +1,177 @@
/*
* Copyright (c) 2013 Dmitry Kazakov
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __TRANSFORM_STROKE_STRATEGY_H
#define __TRANSFORM_STROKE_STRATEGY_H
#include
#include
#include
#include
#include
#include "tool_transform_args.h"
#include
#include
#include
class KisPostExecutionUndoAdapter;
class TransformTransactionProperties;
class KisUpdatesFacade;
class TransformStrokeStrategy : public QObject, public KisStrokeStrategyUndoCommandBased
{
Q_OBJECT
public:
struct TransformAllData : public KisStrokeJobData {
TransformAllData(const ToolTransformArgs &_config)
: KisStrokeJobData(SEQUENTIAL, NORMAL),
config(_config) {}
ToolTransformArgs config;
};
class TransformData : public KisStrokeJobData {
public:
enum Destination {
PAINT_DEVICE,
SELECTION,
};
public:
TransformData(Destination _destination, const ToolTransformArgs &_config, KisNodeSP _node)
: KisStrokeJobData(CONCURRENT, NORMAL),
destination(_destination),
config(_config),
node(_node)
{
}
Destination destination;
ToolTransformArgs config;
KisNodeSP node;
};
class ClearSelectionData : public KisStrokeJobData {
public:
ClearSelectionData(KisNodeSP _node)
: KisStrokeJobData(SEQUENTIAL, NORMAL),
node(_node)
{
}
KisNodeSP node;
};
class PreparePreviewData : public KisStrokeJobData {
public:
PreparePreviewData()
: KisStrokeJobData(BARRIER, NORMAL)
{
}
};
public:
TransformStrokeStrategy(ToolTransformArgs::TransformMode mode,
bool workRecursively,
const QString &filterId,
bool forceReset,
KisNodeSP rootNode,
KisSelectionSP selection,
KisStrokeUndoFacade *undoFacade, KisUpdatesFacade *updatesFacade);
~TransformStrokeStrategy() override;
void initStrokeCallback() override;
void finishStrokeCallback() override;
void cancelStrokeCallback() override;
void doStrokeCallback(KisStrokeJobData *data) override;
static bool fetchArgsFromCommand(const KUndo2Command *command, ToolTransformArgs *args, KisNodeSP *rootNode, KisNodeList *transformedNodes);
Q_SIGNALS:
void sigTransactionGenerated(TransformTransactionProperties transaction, ToolTransformArgs args, void *cookie);
- void sigPreviewDeviceReady(KisPaintDeviceSP device, const QPainterPath &selectionOutline);
+ void sigPreviewDeviceReady(KisPaintDeviceSP device);
protected:
void postProcessToplevelCommand(KUndo2Command *command) override;
private:
KoUpdaterPtr fetchUpdater(KisNodeSP node);
void transformAndMergeDevice(const ToolTransformArgs &config,
KisPaintDeviceSP src,
KisPaintDeviceSP dst,
KisProcessingVisitor::ProgressHelper *helper);
void transformDevice(const ToolTransformArgs &config,
KisPaintDeviceSP device,
KisProcessingVisitor::ProgressHelper *helper);
void clearSelection(KisPaintDeviceSP device);
//void transformDevice(KisPaintDeviceSP src, KisPaintDeviceSP dst, KisProcessingVisitor::ProgressHelper *helper);
bool checkBelongsToSelection(KisPaintDeviceSP device) const;
KisPaintDeviceSP createDeviceCache(KisPaintDeviceSP src);
bool haveDeviceInCache(KisPaintDeviceSP src);
void putDeviceCache(KisPaintDeviceSP src, KisPaintDeviceSP cache);
KisPaintDeviceSP getDeviceCache(KisPaintDeviceSP src);
QList fetchNodesList(ToolTransformArgs::TransformMode mode, KisNodeSP root, bool recursive);
ToolTransformArgs resetArgsForMode(ToolTransformArgs::TransformMode mode,
const QString &filterId,
const TransformTransactionProperties &transaction);
bool tryInitArgsFromNode(KisNodeSP node, ToolTransformArgs *args);
bool tryFetchArgsFromCommandAndUndo(ToolTransformArgs *args,
ToolTransformArgs::TransformMode mode,
KisNodeSP currentNode,
KisNodeList selectedNodes, QVector *undoJobs);
void finishStrokeImpl(bool applyTransform,
const ToolTransformArgs &args);
private:
KisUpdatesFacade *m_updatesFacade;
ToolTransformArgs::TransformMode m_mode;
bool m_workRecursively;
QString m_filterId;
bool m_forceReset;
KisSelectionSP m_selection;
QMutex m_devicesCacheMutex;
QHash m_devicesCacheHash;
KisTransformMaskSP writeToTransformMask;
ToolTransformArgs m_initialTransformArgs;
boost::optional m_savedTransformArgs;
KisNodeSP m_rootNode;
KisNodeList m_processedNodes;
QList m_deactivatedSelections;
QList m_hiddenProjectionLeaves;
const KisSavedMacroCommand *m_overriddenCommand = 0;
QVector m_skippedWhileMergeCommands;
bool m_finalizingActionsStarted = false;
};
#endif /* __TRANSFORM_STROKE_STRATEGY_H */
diff --git a/plugins/tools/tool_transform2/wdg_tool_transform.ui b/plugins/tools/tool_transform2/wdg_tool_transform.ui
index 028721a566..fa83fe5ffd 100644
--- a/plugins/tools/tool_transform2/wdg_tool_transform.ui
+++ b/plugins/tools/tool_transform2/wdg_tool_transform.ui
@@ -1,2265 +1,2237 @@
WdgToolTransform
0
0
499
751
0
0
0
0
16777215
16777215
0
0
Qt::LeftToRight
-
4
-
QFrame::NoFrame
QFrame::Plain
0
1
1
1
1
-
0
-
0
0
Free
Free Transform
true
true
true
true
-
0
0
Perspective
Perspective
true
true
true
-
0
0
Warp
Warp
true
false
true
true
-
0
0
Cage
Cage
true
true
true
-
0
0
Liquify
Liquify
true
true
true
-
0
0
Free Transform
Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
-
QFrame::StyledPanel
QFrame::Raised
2
0
0
0
0
0
-
0
12
-
QLayout::SetFixedSize
10
10
20
10
15
-
0
0
Qt::RightToLeft
&Filter:
Qt::AlignCenter
cmbFilter
-
QLayout::SetDefaultConstraint
0
-
true
0
0
true
true
-
0
0
true
true
-
0
0
true
true
-
0
0
true
true
true
-
0
0
true
true
-
0
0
true
true
-
0
0
true
true
-
0
0
true
true
-
0
0
true
true
-
0
0
-
0
0
Transform around pivot point (Alt)
true
true
-
-
Position
true
freeTransformRadioGroup
-
Rotate
freeTransformRadioGroup
-
Scale
freeTransformRadioGroup
-
Shear
freeTransformRadioGroup
-
0
0
0
0
75
true
Qt::LeftToRight
false
off canvas
Qt::RichText
false
true
Qt::NoTextInteraction
-
Qt::Horizontal
40
20
-
true
0
0
240
100
Rotation
false
false
false
2
5
0
7
0
-
QFormLayout::AllNonFixedFieldsGrow
5
5
-
0
0
0
0
Rotate around X-Axis
Qt::LeftToRight
&x:
aXBox
-
Rotate around X-Axis
-
0
0
0
0
Rotate around Y-Axis
Qt::LeftToRight
&y:
aYBox
-
Rotate around Y-Axis
-
0
0
Rotate around Z-Axis
-
0
0
0
0
Rotate around Z-Axis
Qt::LeftToRight
&z:
Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
aZBox
-
0
0
240
70
Scale
false
false
false
2
5
0
0
-
-
5
-
0
0
0
0
Horizontal Scale
Width:
Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
scaleXBox
-
0
0
Horizontal Scale
%
-
0
0
Vertical Scale
%
-
0
0
0
0
Vertical Scale
Height:
Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
scaleYBox
-
0
0
20
25
-
0
0
240
70
Position
false
false
false
2
5
0
0
-
QFormLayout::AllNonFixedFieldsGrow
-
0
0
Horizontal Translation
Qt::LeftToRight
x:
translateXBox
-
0
0
Horizontal Translation
Qt::LeftToRight
-
0
0
Vertical Translation
-
0
0
Vertical Translation
y:
translateYBox
-
0
0
240
80
false
Shear
Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter
false
false
false
2
5
0
0
-
QFormLayout::AllNonFixedFieldsGrow
8
20
-
0
0
Horizontal Shear
x:
Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
shearXBox
-
0
0
Vertical Shear
-
0
0
0
0
Vertical Shear
y:
Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
shearYBox
-
0
0
Horizontal Shear
-
Qt::Vertical
QSizePolicy::Preferred
20
10
-
6
-
0
0
32
16777215
Flip selection horizontally
-
0
0
32
16777215
Flip selection vertically
-
Qt::Horizontal
QSizePolicy::Preferred
20
20
-
0
0
32
16777215
Rotate selection counter-clockwise 90 degrees
-
0
0
32
16777215
Rotate the selection clockwise 90 degrees
-
Qt::Horizontal
QSizePolicy::Expanding
20
20
-
Qt::Vertical
QSizePolicy::Preferred
20
10
-
10
0
-
0
0
-
0
0
2
0
Fle&xibility:
Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
alphaBox
-
0
0
-
0
0
Anc&hor Strength:
Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
cmbWarpType
-
false
Anchor Points
false
false
-
0
0
0
0
0
-
0
0
Subdi&vide
true
true
buttonGroup
-
Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
2
30
3
-
0
0
0
0
0
-
0
0
Draw
true
buttonGroup
-
false
0
0
0
0
0
-
Clear Points
-
Lock Points
-
Qt::Vertical
20
10
Qt::LeftToRight
false
-
Create 3 points on the canvas to begin
-
Add/Ed&it Anchor Points
true
cageTransformButtonGroup
-
true
Defor&m Layer
cageTransformButtonGroup
-
Qt::Vertical
QSizePolicy::Minimum
17
17
-
75
true
Adjust Granularity :
-
-
Preview
-
0
0
-
Real
-
0
0
-
Qt::Vertical
20
10
-
0
-
0
0
<html><head/><body><p><br/></p></body></html>
QFrame::NoFrame
QFrame::Plain
0
2
2
15
-
0
0
Move
true
true
true
true
-
0
0
Scale
true
true
true
-
0
0
Rotate
true
true
true
-
0
0
Offset
true
true
true
-
0
0
Undo
true
true
true
-
3
5
-
0
0
51
0
Reverse:
-
-
0
0
49
0
Spacing:
-
0
0
32
0
Flow:
-
-
0
0
27
0
Size:
-
-
0
0
51
0
Amount:
-
-
0
0
38
0
Mode:
-
0
0
0
-
Build Up
-
Wash
-
true
0
0
-
true
Pressure
true
true
true
-
Pressure
true
true
true
-
Qt::Vertical
20
0
-
6
6
-
-
-
-
-
- 0
- 0
-
-
-
-
- 32
- 16777215
-
-
-
- Show Decorations
-
-
-
-
-
- true
-
-
- false
-
-
-
-
0
0
32
16777215
Work Recursively
true
true
-
Qt::Horizontal
13
13
-
Qt::LeftToRight
Qt::Horizontal
QDialogButtonBox::Apply|QDialogButtonBox::Reset
true
-
KisCmbIDList
KoAspectButton
QWidget
1
KisIntParseSpinBox
QSpinBox
KisDoubleSliderSpinBox
QWidget
1
-
+