summaryrefslogtreecommitdiffstats
path: root/src/ui/dialog/transformation.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/ui/dialog/transformation.cpp')
-rw-r--r--src/ui/dialog/transformation.cpp1171
1 files changed, 1171 insertions, 0 deletions
diff --git a/src/ui/dialog/transformation.cpp b/src/ui/dialog/transformation.cpp
new file mode 100644
index 0000000..f212654
--- /dev/null
+++ b/src/ui/dialog/transformation.cpp
@@ -0,0 +1,1171 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/**
+ * @file
+ * Transform dialog - implementation.
+ */
+/* Authors:
+ * Bryce W. Harrington <bryce@bryceharrington.org>
+ * buliabyak@gmail.com
+ * Abhishek Sharma
+ *
+ * Copyright (C) 2004, 2005 Authors
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include <gtkmm/dialog.h>
+
+#include <2geom/transforms.h>
+
+#include "align-and-distribute.h"
+#include "desktop.h"
+#include "document-undo.h"
+#include "document.h"
+#include "inkscape.h"
+#include "message-stack.h"
+#include "selection-chemistry.h"
+#include "transformation.h"
+#include "verbs.h"
+
+#include "object/sp-item-transform.h"
+#include "object/sp-namedview.h"
+#include "ui/icon-loader.h"
+
+#include "ui/icon-names.h"
+
+
+
+namespace Inkscape {
+namespace UI {
+namespace Dialog {
+
+static void on_selection_changed(Inkscape::Selection *selection, Transformation *daad)
+{
+ int page = daad->getCurrentPage();
+ daad->updateSelection((Inkscape::UI::Dialog::Transformation::PageType)page, selection);
+}
+
+static void on_selection_modified(Inkscape::Selection *selection, Transformation *daad)
+{
+ int page = daad->getCurrentPage();
+ daad->updateSelection((Inkscape::UI::Dialog::Transformation::PageType)page, selection);
+}
+
+/*########################################################################
+# C O N S T R U C T O R
+########################################################################*/
+
+Transformation::Transformation()
+ : UI::Widget::Panel("/dialogs/transformation", SP_VERB_DIALOG_TRANSFORM),
+ _page_move (4, 2),
+ _page_scale (4, 2),
+ _page_rotate (4, 2),
+ _page_skew (4, 2),
+ _page_transform (3, 3),
+ _scalar_move_horizontal (_("_Horizontal:"), _("Horizontal displacement (relative) or position (absolute)"), UNIT_TYPE_LINEAR,
+ "", "transform-move-horizontal", &_units_move),
+ _scalar_move_vertical (_("_Vertical:"), _("Vertical displacement (relative) or position (absolute)"), UNIT_TYPE_LINEAR,
+ "", "transform-move-vertical", &_units_move),
+ _scalar_scale_horizontal(_("_Width:"), _("Horizontal size (absolute or percentage of current)"), UNIT_TYPE_DIMENSIONLESS,
+ "", "transform-scale-horizontal", &_units_scale),
+ _scalar_scale_vertical (_("_Height:"), _("Vertical size (absolute or percentage of current)"), UNIT_TYPE_DIMENSIONLESS,
+ "", "transform-scale-vertical", &_units_scale),
+ _scalar_rotate (_("A_ngle:"), _("Rotation angle (positive = counterclockwise)"), UNIT_TYPE_RADIAL,
+ "", "transform-rotate", &_units_rotate),
+ _scalar_skew_horizontal (_("_Horizontal:"), _("Horizontal skew angle (positive = counterclockwise), or absolute displacement, or percentage displacement"), UNIT_TYPE_LINEAR,
+ "", "transform-skew-horizontal", &_units_skew),
+ _scalar_skew_vertical (_("_Vertical:"), _("Vertical skew angle (positive = clockwise), or absolute displacement, or percentage displacement"), UNIT_TYPE_LINEAR,
+ "", "transform-skew-vertical", &_units_skew),
+
+ _scalar_transform_a ("_A:", _("Transformation matrix element A")),
+ _scalar_transform_b ("_B:", _("Transformation matrix element B")),
+ _scalar_transform_c ("_C:", _("Transformation matrix element C")),
+ _scalar_transform_d ("_D:", _("Transformation matrix element D")),
+ _scalar_transform_e ("_E:", _("Transformation matrix element E")),
+ _scalar_transform_f ("_F:", _("Transformation matrix element F")),
+
+ _counterclockwise_rotate (),
+ _clockwise_rotate (),
+
+ _check_move_relative (_("Rela_tive move")),
+ _check_scale_proportional (_("_Scale proportionally")),
+ _check_apply_separately (_("Apply to each _object separately")),
+ _check_replace_matrix (_("Edit c_urrent matrix"))
+
+{
+ _check_move_relative.set_use_underline();
+ _check_move_relative.set_tooltip_text(_("Add the specified relative displacement to the current position; otherwise, edit the current absolute position directly"));
+ _check_scale_proportional.set_use_underline();
+ _check_scale_proportional.set_tooltip_text(_("Preserve the width/height ratio of the scaled objects"));
+ _check_apply_separately.set_use_underline();
+ _check_apply_separately.set_tooltip_text(_("Apply the scale/rotate/skew to each selected object separately; otherwise, transform the selection as a whole"));
+ _check_replace_matrix.set_use_underline();
+ _check_replace_matrix.set_tooltip_text(_("Edit the current transform= matrix; otherwise, post-multiply transform= by this matrix"));
+ Gtk::Box *contents = _getContents();
+
+ contents->set_spacing(0);
+
+ // Notebook for individual transformations
+ contents->pack_start(_notebook, false, false);
+
+ _page_move.set_halign(Gtk::ALIGN_START);
+ _notebook.append_page(_page_move, _("_Move"), true);
+ layoutPageMove();
+
+ _page_scale.set_halign(Gtk::ALIGN_START);
+ _notebook.append_page(_page_scale, _("_Scale"), true);
+ layoutPageScale();
+
+ _page_rotate.set_halign(Gtk::ALIGN_START);
+ _notebook.append_page(_page_rotate, _("_Rotate"), true);
+ layoutPageRotate();
+
+ _page_skew.set_halign(Gtk::ALIGN_START);
+ _notebook.append_page(_page_skew, _("Ske_w"), true);
+ layoutPageSkew();
+
+ _page_transform.set_halign(Gtk::ALIGN_START);
+ _notebook.append_page(_page_transform, _("Matri_x"), true);
+ layoutPageTransform();
+
+ _notebook.signal_switch_page().connect(sigc::mem_fun(*this, &Transformation::onSwitchPage));
+
+ // Apply separately
+ contents->pack_start(_check_apply_separately, false, false);
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ _check_apply_separately.set_active(prefs->getBool("/dialogs/transformation/applyseparately"));
+ _check_apply_separately.signal_toggled().connect(sigc::mem_fun(*this, &Transformation::onApplySeparatelyToggled));
+
+ // make sure all spinbuttons activate Apply on pressing Enter
+ ((Gtk::Entry *) (_scalar_move_horizontal.getWidget()))->set_activates_default(true);
+ ((Gtk::Entry *) (_scalar_move_vertical.getWidget()))->set_activates_default(true);
+ ((Gtk::Entry *) (_scalar_scale_horizontal.getWidget()))->set_activates_default(true);
+ ((Gtk::Entry *) (_scalar_scale_vertical.getWidget()))->set_activates_default(true);
+ ((Gtk::Entry *) (_scalar_rotate.getWidget()))->set_activates_default(true);
+ ((Gtk::Entry *) (_scalar_skew_horizontal.getWidget()))->set_activates_default(true);
+ ((Gtk::Entry *) (_scalar_skew_vertical.getWidget()))->set_activates_default(true);
+
+ updateSelection(PAGE_MOVE, _getSelection());
+
+ resetButton = addResponseButton(_("_Clear"), 0);
+ if (resetButton) {
+ resetButton->set_tooltip_text(_("Reset the values on the current tab to defaults"));
+ resetButton->set_sensitive(true);
+ resetButton->signal_clicked().connect(sigc::mem_fun(*this, &Transformation::onClear));
+ }
+
+ applyButton = addResponseButton(_("_Apply"), Gtk::RESPONSE_APPLY);
+ if (applyButton) {
+ applyButton->set_tooltip_text(_("Apply transformation to selection"));
+ applyButton->set_sensitive(false);
+ }
+
+ // Connect to the global selection changed & modified signals
+ _selChangeConn = INKSCAPE.signal_selection_changed.connect(sigc::bind(sigc::ptr_fun(&on_selection_changed), this));
+ _selModifyConn = INKSCAPE.signal_selection_modified.connect(sigc::hide<1>(sigc::bind(sigc::ptr_fun(&on_selection_modified), this)));
+
+ _desktopChangeConn = _deskTrack.connectDesktopChanged( sigc::mem_fun(*this, &Transformation::setDesktop) );
+ _deskTrack.connect(GTK_WIDGET(gobj()));
+
+ show_all_children();
+}
+
+Transformation::~Transformation()
+{
+ _selModifyConn.disconnect();
+ _selChangeConn.disconnect();
+ _desktopChangeConn.disconnect();
+ _deskTrack.disconnect();
+}
+
+void Transformation::setTargetDesktop(SPDesktop *desktop)
+{
+ if (_desktop != desktop) {
+ _desktop = desktop;
+ }
+}
+
+/*########################################################################
+# U T I L I T Y
+########################################################################*/
+
+void Transformation::presentPage(Transformation::PageType page)
+{
+ _notebook.set_current_page(page);
+ show();
+ present();
+}
+
+
+
+
+/*########################################################################
+# S E T U P L A Y O U T
+########################################################################*/
+
+
+void Transformation::layoutPageMove()
+{
+ _units_move.setUnitType(UNIT_TYPE_LINEAR);
+
+ // Setting default unit to document unit
+ SPDesktop *dt = getDesktop();
+ SPNamedView *nv = dt->getNamedView();
+ if (nv->display_units) {
+ _units_move.setUnit(nv->display_units->abbr);
+ }
+
+ _scalar_move_horizontal.initScalar(-1e6, 1e6);
+ _scalar_move_horizontal.setDigits(3);
+ _scalar_move_horizontal.setIncrements(0.1, 1.0);
+ _scalar_move_horizontal.set_hexpand();
+
+ _scalar_move_vertical.initScalar(-1e6, 1e6);
+ _scalar_move_vertical.setDigits(3);
+ _scalar_move_vertical.setIncrements(0.1, 1.0);
+ _scalar_move_vertical.set_hexpand();
+
+ //_scalar_move_vertical.set_label_image( INKSCAPE_STOCK_ARROWS_HOR );
+
+ _page_move.table().attach(_scalar_move_horizontal, 0, 0, 2, 1);
+ _page_move.table().attach(_units_move, 2, 0, 1, 1);
+
+ _scalar_move_horizontal.signal_value_changed()
+ .connect(sigc::mem_fun(*this, &Transformation::onMoveValueChanged));
+
+ //_scalar_move_vertical.set_label_image( INKSCAPE_STOCK_ARROWS_VER );
+ _page_move.table().attach(_scalar_move_vertical, 0, 1, 2, 1);
+
+ _scalar_move_vertical.signal_value_changed()
+ .connect(sigc::mem_fun(*this, &Transformation::onMoveValueChanged));
+
+ // Relative moves
+ _page_move.table().attach(_check_move_relative, 0, 2, 2, 1);
+
+ _check_move_relative.set_active(true);
+ _check_move_relative.signal_toggled()
+ .connect(sigc::mem_fun(*this, &Transformation::onMoveRelativeToggled));
+}
+
+void Transformation::layoutPageScale()
+{
+ _units_scale.setUnitType(UNIT_TYPE_DIMENSIONLESS);
+ _units_scale.setUnitType(UNIT_TYPE_LINEAR);
+
+ _scalar_scale_horizontal.initScalar(-1e6, 1e6);
+ _scalar_scale_horizontal.setValue(100.0, "%");
+ _scalar_scale_horizontal.setDigits(3);
+ _scalar_scale_horizontal.setIncrements(0.1, 1.0);
+ _scalar_scale_horizontal.setAbsoluteIsIncrement(true);
+ _scalar_scale_horizontal.setPercentageIsIncrement(true);
+ _scalar_scale_horizontal.set_hexpand();
+
+ _scalar_scale_vertical.initScalar(-1e6, 1e6);
+ _scalar_scale_vertical.setValue(100.0, "%");
+ _scalar_scale_vertical.setDigits(3);
+ _scalar_scale_vertical.setIncrements(0.1, 1.0);
+ _scalar_scale_vertical.setAbsoluteIsIncrement(true);
+ _scalar_scale_vertical.setPercentageIsIncrement(true);
+ _scalar_scale_vertical.set_hexpand();
+
+ _page_scale.table().attach(_scalar_scale_horizontal, 0, 0, 2, 1);
+
+ _scalar_scale_horizontal.signal_value_changed()
+ .connect(sigc::mem_fun(*this, &Transformation::onScaleXValueChanged));
+
+ _page_scale.table().attach(_units_scale, 2, 0, 1, 1);
+ _page_scale.table().attach(_scalar_scale_vertical, 0, 1, 2, 1);
+
+ _scalar_scale_vertical.signal_value_changed()
+ .connect(sigc::mem_fun(*this, &Transformation::onScaleYValueChanged));
+
+ _page_scale.table().attach(_check_scale_proportional, 0, 2, 2, 1);
+
+ _check_scale_proportional.set_active(false);
+ _check_scale_proportional.signal_toggled()
+ .connect(sigc::mem_fun(*this, &Transformation::onScaleProportionalToggled));
+
+ //TODO: add a widget for selecting the fixed point in scaling, or honour rotation center?
+}
+
+void Transformation::layoutPageRotate()
+{
+ _units_rotate.setUnitType(UNIT_TYPE_RADIAL);
+
+ _scalar_rotate.initScalar(-360.0, 360.0);
+ _scalar_rotate.setDigits(3);
+ _scalar_rotate.setIncrements(0.1, 1.0);
+ _scalar_rotate.set_hexpand();
+
+ auto object_rotate_left_icon = Gtk::manage(sp_get_icon_image("object-rotate-left", Gtk::ICON_SIZE_SMALL_TOOLBAR));
+
+ _counterclockwise_rotate.add(*object_rotate_left_icon);
+ _counterclockwise_rotate.set_mode(false);
+ _counterclockwise_rotate.set_relief(Gtk::RELIEF_NONE);
+ _counterclockwise_rotate.set_tooltip_text(_("Rotate in a counterclockwise direction"));
+
+ auto object_rotate_right_icon = Gtk::manage(sp_get_icon_image("object-rotate-right", Gtk::ICON_SIZE_SMALL_TOOLBAR));
+
+ _clockwise_rotate.add(*object_rotate_right_icon);
+ _clockwise_rotate.set_mode(false);
+ _clockwise_rotate.set_relief(Gtk::RELIEF_NONE);
+ _clockwise_rotate.set_tooltip_text(_("Rotate in a clockwise direction"));
+
+ Gtk::RadioButton::Group group = _counterclockwise_rotate.get_group();
+ _clockwise_rotate.set_group(group);
+
+ _page_rotate.table().attach(_scalar_rotate, 0, 0, 2, 1);
+ _page_rotate.table().attach(_units_rotate, 2, 0, 1, 1);
+ _page_rotate.table().attach(_counterclockwise_rotate, 3, 0, 1, 1);
+ _page_rotate.table().attach(_clockwise_rotate, 4, 0, 1, 1);
+
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ if (prefs->getBool("/dialogs/transformation/rotateCounterClockwise", TRUE) != getDesktop()->is_yaxisdown()) {
+ _counterclockwise_rotate.set_active();
+ onRotateCounterclockwiseClicked();
+ } else {
+ _clockwise_rotate.set_active();
+ onRotateClockwiseClicked();
+ }
+
+ _scalar_rotate.signal_value_changed()
+ .connect(sigc::mem_fun(*this, &Transformation::onRotateValueChanged));
+
+ _counterclockwise_rotate.signal_clicked().connect(sigc::mem_fun(*this, &Transformation::onRotateCounterclockwiseClicked));
+ _clockwise_rotate.signal_clicked().connect(sigc::mem_fun(*this, &Transformation::onRotateClockwiseClicked));
+
+ //TODO: honour rotation center?
+}
+
+void Transformation::layoutPageSkew()
+{
+ _units_skew.setUnitType(UNIT_TYPE_LINEAR);
+ _units_skew.setUnitType(UNIT_TYPE_DIMENSIONLESS);
+ _units_skew.setUnitType(UNIT_TYPE_RADIAL);
+
+ _scalar_skew_horizontal.initScalar(-1e6, 1e6);
+ _scalar_skew_horizontal.setDigits(3);
+ _scalar_skew_horizontal.setIncrements(0.1, 1.0);
+ _scalar_skew_horizontal.set_hexpand();
+
+ _scalar_skew_vertical.initScalar(-1e6, 1e6);
+ _scalar_skew_vertical.setDigits(3);
+ _scalar_skew_vertical.setIncrements(0.1, 1.0);
+ _scalar_skew_vertical.set_hexpand();
+
+ _page_skew.table().attach(_scalar_skew_horizontal, 0, 0, 2, 1);
+ _page_skew.table().attach(_units_skew, 2, 0, 1, 1);
+ _page_skew.table().attach(_scalar_skew_vertical, 0, 1, 2, 1);
+
+ _scalar_skew_horizontal.signal_value_changed()
+ .connect(sigc::mem_fun(*this, &Transformation::onSkewValueChanged));
+ _scalar_skew_vertical.signal_value_changed()
+ .connect(sigc::mem_fun(*this, &Transformation::onSkewValueChanged));
+
+ //TODO: honour rotation center?
+}
+
+
+
+void Transformation::layoutPageTransform()
+{
+ _scalar_transform_a.setWidgetSizeRequest(65, -1);
+ _scalar_transform_a.setRange(-1e10, 1e10);
+ _scalar_transform_a.setDigits(3);
+ _scalar_transform_a.setIncrements(0.1, 1.0);
+ _scalar_transform_a.setValue(1.0);
+ _scalar_transform_a.setWidthChars(6);
+ _scalar_transform_a.set_hexpand();
+
+ _page_transform.table().attach(_scalar_transform_a, 0, 0, 1, 1);
+
+ _scalar_transform_a.signal_value_changed()
+ .connect(sigc::mem_fun(*this, &Transformation::onTransformValueChanged));
+
+ _scalar_transform_b.setWidgetSizeRequest(65, -1);
+ _scalar_transform_b.setRange(-1e10, 1e10);
+ _scalar_transform_b.setDigits(3);
+ _scalar_transform_b.setIncrements(0.1, 1.0);
+ _scalar_transform_b.setValue(0.0);
+ _scalar_transform_b.setWidthChars(6);
+ _scalar_transform_b.set_hexpand();
+
+ _page_transform.table().attach(_scalar_transform_b, 0, 1, 1, 1);
+
+ _scalar_transform_b.signal_value_changed()
+ .connect(sigc::mem_fun(*this, &Transformation::onTransformValueChanged));
+
+ _scalar_transform_c.setWidgetSizeRequest(65, -1);
+ _scalar_transform_c.setRange(-1e10, 1e10);
+ _scalar_transform_c.setDigits(3);
+ _scalar_transform_c.setIncrements(0.1, 1.0);
+ _scalar_transform_c.setValue(0.0);
+ _scalar_transform_c.setWidthChars(6);
+ _scalar_transform_c.set_hexpand();
+
+ _page_transform.table().attach(_scalar_transform_c, 1, 0, 1, 1);
+
+ _scalar_transform_c.signal_value_changed()
+ .connect(sigc::mem_fun(*this, &Transformation::onTransformValueChanged));
+
+
+ _scalar_transform_d.setWidgetSizeRequest(65, -1);
+ _scalar_transform_d.setRange(-1e10, 1e10);
+ _scalar_transform_d.setDigits(3);
+ _scalar_transform_d.setIncrements(0.1, 1.0);
+ _scalar_transform_d.setValue(1.0);
+ _scalar_transform_d.setWidthChars(6);
+ _scalar_transform_d.set_hexpand();
+
+ _page_transform.table().attach(_scalar_transform_d, 1, 1, 1, 1);
+
+ _scalar_transform_d.signal_value_changed()
+ .connect(sigc::mem_fun(*this, &Transformation::onTransformValueChanged));
+
+
+ _scalar_transform_e.setWidgetSizeRequest(65, -1);
+ _scalar_transform_e.setRange(-1e10, 1e10);
+ _scalar_transform_e.setDigits(3);
+ _scalar_transform_e.setIncrements(0.1, 1.0);
+ _scalar_transform_e.setValue(0.0);
+ _scalar_transform_e.setWidthChars(6);
+ _scalar_transform_e.set_hexpand();
+
+ _page_transform.table().attach(_scalar_transform_e, 2, 0, 1, 1);
+
+ _scalar_transform_e.signal_value_changed()
+ .connect(sigc::mem_fun(*this, &Transformation::onTransformValueChanged));
+
+
+ _scalar_transform_f.setWidgetSizeRequest(65, -1);
+ _scalar_transform_f.setRange(-1e10, 1e10);
+ _scalar_transform_f.setDigits(3);
+ _scalar_transform_f.setIncrements(0.1, 1.0);
+ _scalar_transform_f.setValue(0.0);
+ _scalar_transform_f.setWidthChars(6);
+ _scalar_transform_f.set_hexpand();
+
+ _page_transform.table().attach(_scalar_transform_f, 2, 1, 1, 1);
+
+ _scalar_transform_f.signal_value_changed()
+ .connect(sigc::mem_fun(*this, &Transformation::onTransformValueChanged));
+
+ // Edit existing matrix
+ _page_transform.table().attach(_check_replace_matrix, 0, 2, 2, 1);
+
+ _check_replace_matrix.set_active(false);
+ _check_replace_matrix.signal_toggled()
+ .connect(sigc::mem_fun(*this, &Transformation::onReplaceMatrixToggled));
+}
+
+
+/*########################################################################
+# U P D A T E
+########################################################################*/
+
+void Transformation::updateSelection(PageType page, Inkscape::Selection *selection)
+{
+ if (!selection || selection->isEmpty())
+ return;
+
+ switch (page) {
+ case PAGE_MOVE: {
+ updatePageMove(selection);
+ break;
+ }
+ case PAGE_SCALE: {
+ updatePageScale(selection);
+ break;
+ }
+ case PAGE_ROTATE: {
+ updatePageRotate(selection);
+ break;
+ }
+ case PAGE_SKEW: {
+ updatePageSkew(selection);
+ break;
+ }
+ case PAGE_TRANSFORM: {
+ updatePageTransform(selection);
+ break;
+ }
+ case PAGE_QTY: {
+ break;
+ }
+ }
+
+ setResponseSensitive(Gtk::RESPONSE_APPLY,
+ selection && !selection->isEmpty());
+}
+
+void Transformation::onSwitchPage(Gtk::Widget * /*page*/, guint pagenum)
+{
+ updateSelection((PageType)pagenum, getDesktop()->getSelection());
+}
+
+
+void Transformation::updatePageMove(Inkscape::Selection *selection)
+{
+ if (selection && !selection->isEmpty()) {
+ if (!_check_move_relative.get_active()) {
+ Geom::OptRect bbox = selection->preferredBounds();
+ if (bbox) {
+ double x = bbox->min()[Geom::X];
+ double y = bbox->min()[Geom::Y];
+
+ double conversion = _units_move.getConversion("px");
+ _scalar_move_horizontal.setValue(x / conversion);
+ _scalar_move_vertical.setValue(y / conversion);
+ }
+ } else {
+ // do nothing, so you can apply the same relative move to many objects in turn
+ }
+ _page_move.set_sensitive(true);
+ } else {
+ _page_move.set_sensitive(false);
+ }
+}
+
+void Transformation::updatePageScale(Inkscape::Selection *selection)
+{
+ if (selection && !selection->isEmpty()) {
+ Geom::OptRect bbox = selection->preferredBounds();
+ if (bbox) {
+ double w = bbox->dimensions()[Geom::X];
+ double h = bbox->dimensions()[Geom::Y];
+ _scalar_scale_horizontal.setHundredPercent(w);
+ _scalar_scale_vertical.setHundredPercent(h);
+ onScaleXValueChanged(); // to update x/y proportionality if switch is on
+ _page_scale.set_sensitive(true);
+ } else {
+ _page_scale.set_sensitive(false);
+ }
+ } else {
+ _page_scale.set_sensitive(false);
+ }
+}
+
+void Transformation::updatePageRotate(Inkscape::Selection *selection)
+{
+ if (selection && !selection->isEmpty()) {
+ _page_rotate.set_sensitive(true);
+ } else {
+ _page_rotate.set_sensitive(false);
+ }
+}
+
+void Transformation::updatePageSkew(Inkscape::Selection *selection)
+{
+ if (selection && !selection->isEmpty()) {
+ Geom::OptRect bbox = selection->preferredBounds();
+ if (bbox) {
+ double w = bbox->dimensions()[Geom::X];
+ double h = bbox->dimensions()[Geom::Y];
+ _scalar_skew_vertical.setHundredPercent(w);
+ _scalar_skew_horizontal.setHundredPercent(h);
+ _page_skew.set_sensitive(true);
+ } else {
+ _page_skew.set_sensitive(false);
+ }
+ } else {
+ _page_skew.set_sensitive(false);
+ }
+}
+
+void Transformation::updatePageTransform(Inkscape::Selection *selection)
+{
+ if (selection && !selection->isEmpty()) {
+ if (_check_replace_matrix.get_active()) {
+ Geom::Affine current (selection->items().front()->transform); // take from the first item in selection
+
+ Geom::Affine new_displayed = current;
+
+ _scalar_transform_a.setValue(new_displayed[0]);
+ _scalar_transform_b.setValue(new_displayed[1]);
+ _scalar_transform_c.setValue(new_displayed[2]);
+ _scalar_transform_d.setValue(new_displayed[3]);
+ _scalar_transform_e.setValue(new_displayed[4]);
+ _scalar_transform_f.setValue(new_displayed[5]);
+ } else {
+ // do nothing, so you can apply the same matrix to many objects in turn
+ }
+ _page_transform.set_sensitive(true);
+ } else {
+ _page_transform.set_sensitive(false);
+ }
+}
+
+
+
+
+
+/*########################################################################
+# A P P L Y
+########################################################################*/
+
+
+
+void Transformation::_apply()
+{
+ Inkscape::Selection * const selection = _getSelection();
+ if (!selection || selection->isEmpty())
+ return;
+
+ int const page = _notebook.get_current_page();
+
+ switch (page) {
+ case PAGE_MOVE: {
+ applyPageMove(selection);
+ break;
+ }
+ case PAGE_ROTATE: {
+ applyPageRotate(selection);
+ break;
+ }
+ case PAGE_SCALE: {
+ applyPageScale(selection);
+ break;
+ }
+ case PAGE_SKEW: {
+ applyPageSkew(selection);
+ break;
+ }
+ case PAGE_TRANSFORM: {
+ applyPageTransform(selection);
+ break;
+ }
+ }
+
+ //Let's play with never turning this off
+ //setResponseSensitive(Gtk::RESPONSE_APPLY, false);
+}
+
+void Transformation::applyPageMove(Inkscape::Selection *selection)
+{
+ double x = _scalar_move_horizontal.getValue("px");
+ double y = _scalar_move_vertical.getValue("px");
+ if (_check_move_relative.get_active()) {
+ y *= getDesktop()->yaxisdir();
+ }
+
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ if (!prefs->getBool("/dialogs/transformation/applyseparately")) {
+ // move selection as a whole
+ if (_check_move_relative.get_active()) {
+ selection->moveRelative(x, y);
+ } else {
+ Geom::OptRect bbox = selection->preferredBounds();
+ if (bbox) {
+ selection->moveRelative(x - bbox->min()[Geom::X], y - bbox->min()[Geom::Y]);
+ }
+ }
+ } else {
+
+ if (_check_move_relative.get_active()) {
+ // shift each object relatively to the previous one
+ std::vector<SPItem*> selected(selection->items().begin(), selection->items().end());
+ if (selected.empty()) return;
+
+ if (fabs(x) > 1e-6) {
+ std::vector< BBoxSort > sorted;
+ for (auto item : selected)
+ {
+ Geom::OptRect bbox = item->desktopPreferredBounds();
+ if (bbox) {
+ sorted.emplace_back(item, *bbox, Geom::X, x > 0? 1. : 0., x > 0? 0. : 1.);
+ }
+ }
+ //sort bbox by anchors
+ std::stable_sort(sorted.begin(), sorted.end());
+
+ double move = x;
+ for ( std::vector<BBoxSort> ::iterator it (sorted.begin());
+ it < sorted.end();
+ ++it )
+ {
+ it->item->move_rel(Geom::Translate(move, 0));
+ // move each next object by x relative to previous
+ move += x;
+ }
+ }
+ if (fabs(y) > 1e-6) {
+ std::vector< BBoxSort > sorted;
+ for (auto item : selected)
+ {
+ Geom::OptRect bbox = item->desktopPreferredBounds();
+ if (bbox) {
+ sorted.emplace_back(item, *bbox, Geom::Y, y > 0? 1. : 0., y > 0? 0. : 1.);
+ }
+ }
+ //sort bbox by anchors
+ std::stable_sort(sorted.begin(), sorted.end());
+
+ double move = y;
+ for ( std::vector<BBoxSort> ::iterator it (sorted.begin());
+ it < sorted.end();
+ ++it )
+ {
+ it->item->move_rel(Geom::Translate(0, move));
+ // move each next object by x relative to previous
+ move += y;
+ }
+ }
+ } else {
+ Geom::OptRect bbox = selection->preferredBounds();
+ if (bbox) {
+ selection->moveRelative(x - bbox->min()[Geom::X], y - bbox->min()[Geom::Y]);
+ }
+ }
+ }
+
+ DocumentUndo::done( selection->desktop()->getDocument() , SP_VERB_DIALOG_TRANSFORM,
+ _("Move"));
+}
+
+void Transformation::applyPageScale(Inkscape::Selection *selection)
+{
+ double scaleX = _scalar_scale_horizontal.getValue("px");
+ double scaleY = _scalar_scale_vertical.getValue("px");
+
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ bool transform_stroke = prefs->getBool("/options/transform/stroke", true);
+ bool preserve = prefs->getBool("/options/preservetransform/value", false);
+ if (prefs->getBool("/dialogs/transformation/applyseparately")) {
+ auto tmp= selection->items();
+ for(auto i=tmp.begin();i!=tmp.end();++i){
+ SPItem *item = *i;
+ Geom::OptRect bbox_pref = item->desktopPreferredBounds();
+ Geom::OptRect bbox_geom = item->desktopGeometricBounds();
+ if (bbox_pref && bbox_geom) {
+ double new_width = scaleX;
+ double new_height = scaleY;
+ // the values are increments!
+ if (!_units_scale.isAbsolute()) { // Relative scaling, i.e in percent
+ new_width = scaleX/100 * bbox_pref->width();
+ new_height = scaleY/100 * bbox_pref->height();
+ }
+ if (fabs(new_width) < 1e-6) new_width = 1e-6; // not 0, as this would result in a nasty no-bbox object
+ if (fabs(new_height) < 1e-6) new_height = 1e-6;
+
+ double x0 = bbox_pref->midpoint()[Geom::X] - new_width/2;
+ double y0 = bbox_pref->midpoint()[Geom::Y] - new_height/2;
+ double x1 = bbox_pref->midpoint()[Geom::X] + new_width/2;
+ double y1 = bbox_pref->midpoint()[Geom::Y] + new_height/2;
+
+ Geom::Affine scaler = get_scale_transform_for_variable_stroke (*bbox_pref, *bbox_geom, transform_stroke, preserve, x0, y0, x1, y1);
+ item->set_i2d_affine(item->i2dt_affine() * scaler);
+ item->doWriteTransform(item->transform);
+ }
+ }
+ } else {
+ Geom::OptRect bbox_pref = selection->preferredBounds();
+ Geom::OptRect bbox_geom = selection->geometricBounds();
+ if (bbox_pref && bbox_geom) {
+ // the values are increments!
+ double new_width = scaleX;
+ double new_height = scaleY;
+ if (!_units_scale.isAbsolute()) { // Relative scaling, i.e in percent
+ new_width = scaleX/100 * bbox_pref->width();
+ new_height = scaleY/100 * bbox_pref->height();
+ }
+ if (fabs(new_width) < 1e-6) new_width = 1e-6;
+ if (fabs(new_height) < 1e-6) new_height = 1e-6;
+
+ double x0 = bbox_pref->midpoint()[Geom::X] - new_width/2;
+ double y0 = bbox_pref->midpoint()[Geom::Y] - new_height/2;
+ double x1 = bbox_pref->midpoint()[Geom::X] + new_width/2;
+ double y1 = bbox_pref->midpoint()[Geom::Y] + new_height/2;
+ Geom::Affine scaler = get_scale_transform_for_variable_stroke (*bbox_pref, *bbox_geom, transform_stroke, preserve, x0, y0, x1, y1);
+
+ selection->applyAffine(scaler);
+ }
+ }
+
+ DocumentUndo::done(selection->desktop()->getDocument(), SP_VERB_DIALOG_TRANSFORM,
+ _("Scale"));
+}
+
+void Transformation::applyPageRotate(Inkscape::Selection *selection)
+{
+ double angle = _scalar_rotate.getValue(DEG);
+
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ if (!prefs->getBool("/dialogs/transformation/rotateCounterClockwise", TRUE)) {
+ angle *= -1;
+ }
+
+ if (prefs->getBool("/dialogs/transformation/applyseparately")) {
+ auto tmp= selection->items();
+ for(auto i=tmp.begin();i!=tmp.end();++i){
+ SPItem *item = *i;
+ item->rotate_rel(Geom::Rotate (angle*M_PI/180.0));
+ }
+ } else {
+ boost::optional<Geom::Point> center = selection->center();
+ if (center) {
+ selection->rotateRelative(*center, angle);
+ }
+ }
+
+ DocumentUndo::done(selection->desktop()->getDocument(), SP_VERB_DIALOG_TRANSFORM,
+ _("Rotate"));
+}
+
+void Transformation::applyPageSkew(Inkscape::Selection *selection)
+{
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ if (prefs->getBool("/dialogs/transformation/applyseparately")) {
+ auto items = selection->items();
+ for(auto i = items.begin();i!=items.end();++i){
+ SPItem *item = *i;
+
+ if (!_units_skew.isAbsolute()) { // percentage
+ double skewX = _scalar_skew_horizontal.getValue("%");
+ double skewY = _scalar_skew_vertical.getValue("%");
+ skewY *= getDesktop()->yaxisdir();
+ if (fabs(0.01*skewX*0.01*skewY - 1.0) < Geom::EPSILON) {
+ getDesktop()->getMessageStack()->flash(Inkscape::WARNING_MESSAGE, _("Transform matrix is singular, <b>not used</b>."));
+ return;
+ }
+ item->skew_rel(0.01*skewX, 0.01*skewY);
+ } else if (_units_skew.isRadial()) { //deg or rad
+ double angleX = _scalar_skew_horizontal.getValue("rad");
+ double angleY = _scalar_skew_vertical.getValue("rad");
+ if ((fabs(angleX - angleY + M_PI/2) < Geom::EPSILON)
+ || (fabs(angleX - angleY - M_PI/2) < Geom::EPSILON)
+ || (fabs((angleX - angleY)/3 + M_PI/2) < Geom::EPSILON)
+ || (fabs((angleX - angleY)/3 - M_PI/2) < Geom::EPSILON)) {
+ getDesktop()->getMessageStack()->flash(Inkscape::WARNING_MESSAGE, _("Transform matrix is singular, <b>not used</b>."));
+ return;
+ }
+ double skewX = tan(angleX);
+ double skewY = tan(angleY);
+ skewX *= getDesktop()->yaxisdir();
+ skewY *= getDesktop()->yaxisdir();
+ item->skew_rel(skewX, skewY);
+ } else { // absolute displacement
+ double skewX = _scalar_skew_horizontal.getValue("px");
+ double skewY = _scalar_skew_vertical.getValue("px");
+ skewY *= getDesktop()->yaxisdir();
+ Geom::OptRect bbox = item->desktopPreferredBounds();
+ if (bbox) {
+ double width = bbox->dimensions()[Geom::X];
+ double height = bbox->dimensions()[Geom::Y];
+ if (fabs(skewX*skewY - width*height) < Geom::EPSILON) {
+ getDesktop()->getMessageStack()->flash(Inkscape::WARNING_MESSAGE, _("Transform matrix is singular, <b>not used</b>."));
+ return;
+ }
+ item->skew_rel(skewX/height, skewY/width);
+ }
+ }
+ }
+ } else { // transform whole selection
+ Geom::OptRect bbox = selection->preferredBounds();
+ boost::optional<Geom::Point> center = selection->center();
+
+ if ( bbox && center ) {
+ double width = bbox->dimensions()[Geom::X];
+ double height = bbox->dimensions()[Geom::Y];
+
+ if (!_units_skew.isAbsolute()) { // percentage
+ double skewX = _scalar_skew_horizontal.getValue("%");
+ double skewY = _scalar_skew_vertical.getValue("%");
+ skewY *= getDesktop()->yaxisdir();
+ if (fabs(0.01*skewX*0.01*skewY - 1.0) < Geom::EPSILON) {
+ getDesktop()->getMessageStack()->flash(Inkscape::WARNING_MESSAGE, _("Transform matrix is singular, <b>not used</b>."));
+ return;
+ }
+ selection->skewRelative(*center, 0.01 * skewX, 0.01 * skewY);
+ } else if (_units_skew.isRadial()) { //deg or rad
+ double angleX = _scalar_skew_horizontal.getValue("rad");
+ double angleY = _scalar_skew_vertical.getValue("rad");
+ if ((fabs(angleX - angleY + M_PI/2) < Geom::EPSILON)
+ || (fabs(angleX - angleY - M_PI/2) < Geom::EPSILON)
+ || (fabs((angleX - angleY)/3 + M_PI/2) < Geom::EPSILON)
+ || (fabs((angleX - angleY)/3 - M_PI/2) < Geom::EPSILON)) {
+ getDesktop()->getMessageStack()->flash(Inkscape::WARNING_MESSAGE, _("Transform matrix is singular, <b>not used</b>."));
+ return;
+ }
+ double skewX = tan(angleX);
+ double skewY = tan(angleY);
+ skewX *= getDesktop()->yaxisdir();
+ skewY *= getDesktop()->yaxisdir();
+ selection->skewRelative(*center, skewX, skewY);
+ } else { // absolute displacement
+ double skewX = _scalar_skew_horizontal.getValue("px");
+ double skewY = _scalar_skew_vertical.getValue("px");
+ skewY *= getDesktop()->yaxisdir();
+ if (fabs(skewX*skewY - width*height) < Geom::EPSILON) {
+ getDesktop()->getMessageStack()->flash(Inkscape::WARNING_MESSAGE, _("Transform matrix is singular, <b>not used</b>."));
+ return;
+ }
+ selection->skewRelative(*center, skewX / height, skewY / width);
+ }
+ }
+ }
+
+ DocumentUndo::done(selection->desktop()->getDocument(), SP_VERB_DIALOG_TRANSFORM,
+ _("Skew"));
+}
+
+
+void Transformation::applyPageTransform(Inkscape::Selection *selection)
+{
+ double a = _scalar_transform_a.getValue();
+ double b = _scalar_transform_b.getValue();
+ double c = _scalar_transform_c.getValue();
+ double d = _scalar_transform_d.getValue();
+ double e = _scalar_transform_e.getValue();
+ double f = _scalar_transform_f.getValue();
+
+ Geom::Affine displayed(a, b, c, d, e, f);
+ if (displayed.isSingular()) {
+ getDesktop()->getMessageStack()->flash(Inkscape::WARNING_MESSAGE, _("Transform matrix is singular, <b>not used</b>."));
+ return;
+ }
+
+ if (_check_replace_matrix.get_active()) {
+ auto tmp = selection->items();
+ for(auto i=tmp.begin();i!=tmp.end();++i){
+ SPItem *item = *i;
+ item->set_item_transform(displayed);
+ item->updateRepr();
+ }
+ } else {
+ selection->applyAffine(displayed); // post-multiply each object's transform
+ }
+
+ DocumentUndo::done(selection->desktop()->getDocument(), SP_VERB_DIALOG_TRANSFORM,
+ _("Edit transformation matrix"));
+}
+
+
+
+
+
+/*########################################################################
+# V A L U E - C H A N G E D C A L L B A C K S
+########################################################################*/
+
+void Transformation::onMoveValueChanged()
+{
+ setResponseSensitive(Gtk::RESPONSE_APPLY, true);
+}
+
+void Transformation::onMoveRelativeToggled()
+{
+ Inkscape::Selection *selection = _getSelection();
+
+ if (!selection || selection->isEmpty())
+ return;
+
+ double x = _scalar_move_horizontal.getValue("px");
+ double y = _scalar_move_vertical.getValue("px");
+
+ double conversion = _units_move.getConversion("px");
+
+ //g_message("onMoveRelativeToggled: %f, %f px\n", x, y);
+
+ Geom::OptRect bbox = selection->preferredBounds();
+
+ if (bbox) {
+ if (_check_move_relative.get_active()) {
+ // From absolute to relative
+ _scalar_move_horizontal.setValue((x - bbox->min()[Geom::X]) / conversion);
+ _scalar_move_vertical.setValue(( y - bbox->min()[Geom::Y]) / conversion);
+ } else {
+ // From relative to absolute
+ _scalar_move_horizontal.setValue((bbox->min()[Geom::X] + x) / conversion);
+ _scalar_move_vertical.setValue(( bbox->min()[Geom::Y] + y) / conversion);
+ }
+ }
+
+ setResponseSensitive(Gtk::RESPONSE_APPLY, true);
+}
+
+void Transformation::onScaleXValueChanged()
+{
+ if (_scalar_scale_horizontal.setProgrammatically) {
+ _scalar_scale_horizontal.setProgrammatically = false;
+ return;
+ }
+
+ setResponseSensitive(Gtk::RESPONSE_APPLY, true);
+
+ if (_check_scale_proportional.get_active()) {
+ if (!_units_scale.isAbsolute()) { // percentage, just copy over
+ _scalar_scale_vertical.setValue(_scalar_scale_horizontal.getValue("%"));
+ } else {
+ double scaleXPercentage = _scalar_scale_horizontal.getAsPercentage();
+ _scalar_scale_vertical.setFromPercentage (scaleXPercentage);
+ }
+ }
+}
+
+void Transformation::onScaleYValueChanged()
+{
+ if (_scalar_scale_vertical.setProgrammatically) {
+ _scalar_scale_vertical.setProgrammatically = false;
+ return;
+ }
+
+ setResponseSensitive(Gtk::RESPONSE_APPLY, true);
+
+ if (_check_scale_proportional.get_active()) {
+ if (!_units_scale.isAbsolute()) { // percentage, just copy over
+ _scalar_scale_horizontal.setValue(_scalar_scale_vertical.getValue("%"));
+ } else {
+ double scaleYPercentage = _scalar_scale_vertical.getAsPercentage();
+ _scalar_scale_horizontal.setFromPercentage (scaleYPercentage);
+ }
+ }
+}
+
+void Transformation::onRotateValueChanged()
+{
+ setResponseSensitive(Gtk::RESPONSE_APPLY, true);
+}
+
+void Transformation::onRotateCounterclockwiseClicked()
+{
+ _scalar_rotate.setTooltipText(_("Rotation angle (positive = counterclockwise)"));
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ prefs->setBool("/dialogs/transformation/rotateCounterClockwise", !getDesktop()->is_yaxisdown());
+}
+
+void Transformation::onRotateClockwiseClicked()
+{
+ _scalar_rotate.setTooltipText(_("Rotation angle (positive = clockwise)"));
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ prefs->setBool("/dialogs/transformation/rotateCounterClockwise", getDesktop()->is_yaxisdown());
+}
+
+void Transformation::onSkewValueChanged()
+{
+ setResponseSensitive(Gtk::RESPONSE_APPLY, true);
+}
+
+void Transformation::onTransformValueChanged()
+{
+
+ /*
+ double a = _scalar_transform_a.getValue();
+ double b = _scalar_transform_b.getValue();
+ double c = _scalar_transform_c.getValue();
+ double d = _scalar_transform_d.getValue();
+ double e = _scalar_transform_e.getValue();
+ double f = _scalar_transform_f.getValue();
+
+ //g_message("onTransformValueChanged: (%f, %f, %f, %f, %f, %f)\n",
+ // a, b, c, d, e ,f);
+ */
+
+ setResponseSensitive(Gtk::RESPONSE_APPLY, true);
+}
+
+void Transformation::onReplaceMatrixToggled()
+{
+ Inkscape::Selection *selection = _getSelection();
+
+ if (!selection || selection->isEmpty())
+ return;
+
+ double a = _scalar_transform_a.getValue();
+ double b = _scalar_transform_b.getValue();
+ double c = _scalar_transform_c.getValue();
+ double d = _scalar_transform_d.getValue();
+ double e = _scalar_transform_e.getValue();
+ double f = _scalar_transform_f.getValue();
+
+ Geom::Affine displayed (a, b, c, d, e, f);
+ Geom::Affine current = selection->items().front()->transform; // take from the first item in selection
+
+ Geom::Affine new_displayed;
+ if (_check_replace_matrix.get_active()) {
+ new_displayed = current;
+ } else {
+ new_displayed = current.inverse() * displayed;
+ }
+
+ _scalar_transform_a.setValue(new_displayed[0]);
+ _scalar_transform_b.setValue(new_displayed[1]);
+ _scalar_transform_c.setValue(new_displayed[2]);
+ _scalar_transform_d.setValue(new_displayed[3]);
+ _scalar_transform_e.setValue(new_displayed[4]);
+ _scalar_transform_f.setValue(new_displayed[5]);
+}
+
+void Transformation::onScaleProportionalToggled()
+{
+ onScaleXValueChanged();
+ if (_scalar_scale_vertical.setProgrammatically) {
+ _scalar_scale_vertical.setProgrammatically = false;
+ }
+}
+
+
+void Transformation::onClear()
+{
+ int const page = _notebook.get_current_page();
+
+ switch (page) {
+ case PAGE_MOVE: {
+ Inkscape::Selection *selection = _getSelection();
+ if (!selection || selection->isEmpty() || _check_move_relative.get_active()) {
+ _scalar_move_horizontal.setValue(0);
+ _scalar_move_vertical.setValue(0);
+ } else {
+ Geom::OptRect bbox = selection->preferredBounds();
+ if (bbox) {
+ _scalar_move_horizontal.setValue(bbox->min()[Geom::X], "px");
+ _scalar_move_vertical.setValue(bbox->min()[Geom::Y], "px");
+ }
+ }
+ break;
+ }
+ case PAGE_ROTATE: {
+ _scalar_rotate.setValue(0);
+ break;
+ }
+ case PAGE_SCALE: {
+ _scalar_scale_horizontal.setValue(100, "%");
+ _scalar_scale_vertical.setValue(100, "%");
+ break;
+ }
+ case PAGE_SKEW: {
+ _scalar_skew_horizontal.setValue(0);
+ _scalar_skew_vertical.setValue(0);
+ break;
+ }
+ case PAGE_TRANSFORM: {
+ _scalar_transform_a.setValue(1);
+ _scalar_transform_b.setValue(0);
+ _scalar_transform_c.setValue(0);
+ _scalar_transform_d.setValue(1);
+ _scalar_transform_e.setValue(0);
+ _scalar_transform_f.setValue(0);
+ break;
+ }
+ }
+}
+
+void Transformation::onApplySeparatelyToggled()
+{
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ prefs->setBool("/dialogs/transformation/applyseparately", _check_apply_separately.get_active());
+}
+
+
+} // namespace Dialog
+} // namespace UI
+} // namespace Inkscape
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :