Code

fix clearing in Scale and unit conversion in Skew
[inkscape.git] / src / ui / dialog / transformation.cpp
1 /**
2  * \brief Object Transformation dialog
3  *
4  * Authors:
5  *   Bryce W. Harrington <bryce@bryceharrington.org>
6  *   buliabyak@gmail.com
7  *
8  * Copyright (C) 2004, 2005 Authors
9  *
10  * Released under GNU GPL.  Read the file 'COPYING' for more information.
11  */
13 #ifdef HAVE_CONFIG_H
14 # include <config.h>
15 #endif
17 #include <gtkmm/stock.h>
18 #include <gtkmm/dialog.h>
20 #include "document.h"
21 #include "desktop-handles.h"
22 #include "transformation.h"
23 #include "libnr/nr-matrix-ops.h"
24 #include "inkscape.h"
25 #include "selection.h"
26 #include "selection-chemistry.h"
27 #include "verbs.h"
28 #include "prefs-utils.h"
29 #include "sp-item-transform.h"
30 #include "macros.h"
31 #include "sp-item.h"
33 namespace Inkscape {
34 namespace UI {
35 namespace Dialog {
37 void on_selection_changed(Inkscape::Application */*inkscape*/, Inkscape::Selection *selection, Transformation *daad)
38 {
39     int page = daad->getCurrentPage();
40     daad->updateSelection((Inkscape::UI::Dialog::Transformation::PageType)page, selection);
41 }
43 void on_selection_modified( Inkscape::Application */*inkscape*/,
44                             Inkscape::Selection *selection,
45                             guint /*flags*/,
46                             Transformation *daad )
47 {
48     int page = daad->getCurrentPage();
49     daad->updateSelection((Inkscape::UI::Dialog::Transformation::PageType)page, selection);
50 }
52 /*########################################################################
53 # C O N S T R U C T O R
54 ########################################################################*/
56 /**
57  * Constructor for Transformation.  This does the initialization
58  * and layout of the dialog used for transforming SVG objects.  It
59  * consists of 5 pages for the 5 operations it handles:
60  * 'Move' allows x,y translation of SVG objects
61  * 'Scale' allows linear resizing of SVG objects
62  * 'Rotate' allows rotating SVG objects by a degree
63  * 'Skew' allows skewing SVG objects
64  * 'Matrix' allows applying a generic affine transform on SVG objects,
65  *     with the user specifying the 6 degrees of freedom manually.
66  *
67  * The dialog is implemented as a Gtk::Notebook with five pages.
68  * The pages are implemented using Inkscape's NotebookPage which
69  * is used to help make sure all of Inkscape's notebooks follow
70  * the same style.  We then populate the pages with our widgets,
71  * we use the ScalarUnit class for this.
72  *
73  */
74 Transformation::Transformation()
75     : UI::Widget::Panel ("", "dialogs.transformation", SP_VERB_DIALOG_TRANSFORM),
76       _page_move              (4, 2),
77       _page_scale             (4, 2),
78       _page_rotate            (4, 2),
79       _page_skew              (4, 2),
80       _page_transform         (3, 3),
81       _scalar_move_horizontal (_("_Horizontal"), _("Horizontal displacement (relative) or position (absolute)"), UNIT_TYPE_LINEAR,
82                                "", "arrows_hor", &_units_move),
83       _scalar_move_vertical   (_("_Vertical"),  _("Vertical displacement (relative) or position (absolute)"), UNIT_TYPE_LINEAR,
84                                "", "arrows_ver", &_units_move),
85       _scalar_scale_horizontal(_("_Width"), _("Horizontal size (absolute or percentage of current)"), UNIT_TYPE_DIMENSIONLESS,
86                                "", "transform_scale_hor", &_units_scale),
87       _scalar_scale_vertical  (_("_Height"),  _("Vertical size (absolute or percentage of current)"), UNIT_TYPE_DIMENSIONLESS,
88                                "", "transform_scale_ver", &_units_scale),
89       _scalar_rotate          (_("A_ngle"), _("Rotation angle (positive = counterclockwise)"), UNIT_TYPE_RADIAL,
90                                "", "transform_rotate", &_units_rotate),
91       _scalar_skew_horizontal (_("_Horizontal"), _("Horizontal skew angle (positive = counterclockwise), or absolute displacement, or percentage displacement"), UNIT_TYPE_LINEAR,
92                                "", "transform_scew_hor", &_units_skew),
93       _scalar_skew_vertical   (_("_Vertical"),  _("Vertical skew angle (positive = counterclockwise), or absolute displacement, or percentage displacement"),  UNIT_TYPE_LINEAR,
94                                "", "transform_scew_ver", &_units_skew),
96       _scalar_transform_a     ("_A", _("Transformation matrix element A")),
97       _scalar_transform_b     ("_B", _("Transformation matrix element B")),
98       _scalar_transform_c     ("_C", _("Transformation matrix element C")),
99       _scalar_transform_d     ("_D", _("Transformation matrix element D")),
100       _scalar_transform_e     ("_E", _("Transformation matrix element E")),
101       _scalar_transform_f     ("_F", _("Transformation matrix element F")),
103       _check_move_relative    (_("Rela_tive move"), _("Add the specified relative displacement to the current position; otherwise, edit the current absolute position directly")),
104       _check_scale_proportional (_("Scale proportionally"), _("Preserve the width/height ratio of the scaled objects")),
105       _check_apply_separately    (_("Apply to each _object separately"), _("Apply the scale/rotate/skew to each selected object separately; otherwise, transform the selection as a whole")),
106       _check_replace_matrix    (_("Edit c_urrent matrix"), _("Edit the current transform= matrix; otherwise, post-multiply transform= by this matrix"))
109     Gtk::Box *contents = _getContents();
111     contents->set_spacing(0);
113     // Notebook for individual transformations
114     contents->pack_start(_notebook, true, true);
116     _notebook.append_page(_page_move, _("_Move"), true);
117     layoutPageMove();
119     _notebook.append_page(_page_scale, _("_Scale"), true);
120     layoutPageScale();
122     _notebook.append_page(_page_rotate, _("_Rotate"), true);
123     layoutPageRotate();
125     _notebook.append_page(_page_skew, _("Ske_w"), true);
126     layoutPageSkew();
128     _notebook.append_page(_page_transform, _("Matri_x"), true);
129     layoutPageTransform();
131     _notebook.signal_switch_page().connect(sigc::mem_fun(*this, &Transformation::onSwitchPage));
133     // Apply separately
134     contents->pack_start(_check_apply_separately, true, true);
135     _check_apply_separately.set_active(prefs_get_int_attribute_limited ("dialogs.transformation", "applyseparately", 0, 0, 1));
136     _check_apply_separately.signal_toggled().connect(sigc::mem_fun(*this, &Transformation::onApplySeparatelyToggled));
138     // make sure all spinbuttons activate Apply on pressing Enter
139       ((Gtk::Entry *) (_scalar_move_horizontal.getWidget()))->set_activates_default(true);
140       ((Gtk::Entry *) (_scalar_move_vertical.getWidget()))->set_activates_default(true);
141       ((Gtk::Entry *) (_scalar_scale_horizontal.getWidget()))->set_activates_default(true);
142       ((Gtk::Entry *) (_scalar_scale_vertical.getWidget()))->set_activates_default(true);
143       ((Gtk::Entry *) (_scalar_rotate.getWidget()))->set_activates_default(true);
144       ((Gtk::Entry *) (_scalar_skew_horizontal.getWidget()))->set_activates_default(true);
145       ((Gtk::Entry *) (_scalar_skew_vertical.getWidget()))->set_activates_default(true);
147     updateSelection(PAGE_MOVE, _getSelection());
149     resetButton = addResponseButton(Gtk::Stock::CLEAR, 0);
150     if (resetButton) {
151         _tooltips.set_tip((*resetButton), _("Reset the values on the current tab to defaults"));
152         resetButton->set_sensitive(true);
153         resetButton->signal_clicked().connect(sigc::mem_fun(*this, &Transformation::onClear));
154     }
156     applyButton = addResponseButton(Gtk::Stock::APPLY, Gtk::RESPONSE_APPLY);
157     if (applyButton) {
158         _tooltips.set_tip((*applyButton), _("Apply transformation to selection"));
159         applyButton->set_sensitive(false);
160     }
162     // Connect to the global selection changed & modified signals
163     g_signal_connect (G_OBJECT (INKSCAPE), "change_selection", G_CALLBACK (on_selection_changed), this);
164     g_signal_connect (G_OBJECT (INKSCAPE), "modify_selection", G_CALLBACK (on_selection_modified), this);
166     show_all_children();
169 Transformation::~Transformation()
171     sp_signal_disconnect_by_data (G_OBJECT (INKSCAPE), this);
175 /*########################################################################
176 # U T I L I T Y
177 ########################################################################*/
179 void
180 Transformation::presentPage(Transformation::PageType page)
182     _notebook.set_current_page(page);
183     show();
184     present();
190 /*########################################################################
191 # S E T U P   L A Y O U T
192 ########################################################################*/
195 void
196 Transformation::layoutPageMove()
198     _units_move.setUnitType(UNIT_TYPE_LINEAR);
199     _scalar_move_horizontal.initScalar(-1e6, 1e6);
200     _scalar_move_horizontal.setDigits(3);
201     _scalar_move_horizontal.setIncrements(0.1, 1.0);
203     _scalar_move_vertical.initScalar(-1e6, 1e6);
204     _scalar_move_vertical.setDigits(3);
205     _scalar_move_vertical.setIncrements(0.1, 1.0);
207     //_scalar_move_vertical.set_label_image( INKSCAPE_STOCK_ARROWS_HOR );
208     _page_move.table()
209         .attach(_scalar_move_horizontal, 0, 2, 0, 1, Gtk::FILL, Gtk::SHRINK);
211     _page_move.table()
212         .attach(_units_move, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK);
214     _scalar_move_horizontal.signal_value_changed()
215         .connect(sigc::mem_fun(*this, &Transformation::onMoveValueChanged));
217     //_scalar_move_vertical.set_label_image( INKSCAPE_STOCK_ARROWS_VER );
218     _page_move.table()
219         .attach(_scalar_move_vertical, 0, 2, 1, 2, Gtk::FILL, Gtk::SHRINK);
221     _scalar_move_vertical.signal_value_changed()
222         .connect(sigc::mem_fun(*this, &Transformation::onMoveValueChanged));
224     // Relative moves
225     _page_move.table()
226         .attach(_check_move_relative, 0, 2, 2, 3, Gtk::FILL, Gtk::SHRINK);
227     _check_move_relative.set_active(true);
228     _check_move_relative.signal_toggled()
229         .connect(sigc::mem_fun(*this, &Transformation::onMoveRelativeToggled));
232 void
233 Transformation::layoutPageScale()
235     _units_scale.setUnitType(UNIT_TYPE_DIMENSIONLESS);
236     _units_scale.setUnitType(UNIT_TYPE_LINEAR);
238     _scalar_scale_horizontal.initScalar(-1e6, 1e6);
239     _scalar_scale_horizontal.setValue(100.0, "%");
240     _scalar_scale_horizontal.setDigits(3);
241     _scalar_scale_horizontal.setIncrements(0.1, 1.0);
242     _scalar_scale_horizontal.setAbsoluteIsIncrement(true);
243     _scalar_scale_horizontal.setPercentageIsIncrement(true);
245     _scalar_scale_vertical.initScalar(-1e6, 1e6);
246     _scalar_scale_vertical.setValue(100.0, "%");
247     _scalar_scale_vertical.setDigits(3);
248     _scalar_scale_vertical.setIncrements(0.1, 1.0);
249     _scalar_scale_vertical.setAbsoluteIsIncrement(true);
250     _scalar_scale_vertical.setPercentageIsIncrement(true);
252     _page_scale.table()
253         .attach(_scalar_scale_horizontal, 0, 2, 0, 1, Gtk::FILL, Gtk::SHRINK);
254     _scalar_scale_horizontal.signal_value_changed()
255         .connect(sigc::mem_fun(*this, &Transformation::onScaleXValueChanged));
257     _page_scale.table()
258         .attach(_units_scale, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK);
260     _page_scale.table()
261         .attach(_scalar_scale_vertical, 0, 2, 1, 2, Gtk::FILL, Gtk::SHRINK);
262     _scalar_scale_vertical.signal_value_changed()
263         .connect(sigc::mem_fun(*this, &Transformation::onScaleYValueChanged));
265     _page_scale.table()
266         .attach(_check_scale_proportional, 0, 2, 2, 3, Gtk::FILL, Gtk::SHRINK);
267     _check_scale_proportional.set_active(false);
268     _check_scale_proportional.signal_toggled()
269         .connect(sigc::mem_fun(*this, &Transformation::onScaleProportionalToggled));
271     //TODO: add a widget for selecting the fixed point in scaling, or honour rotation center?
274 void
275 Transformation::layoutPageRotate()
277     _units_rotate.setUnitType(UNIT_TYPE_RADIAL);
279     _scalar_rotate.initScalar(-360.0, 360.0);
280     _scalar_rotate.setDigits(3);
281     _scalar_rotate.setIncrements(0.1, 1.0);
283     _page_rotate.table()
284         .attach(_scalar_rotate, 0, 2, 0, 1, Gtk::FILL, Gtk::SHRINK);
286     _page_rotate.table()
287         .attach(_units_rotate, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK);
289     _scalar_rotate.signal_value_changed()
290         .connect(sigc::mem_fun(*this, &Transformation::onRotateValueChanged));
292     //TODO: honour rotation center?
295 void
296 Transformation::layoutPageSkew()
298     _units_skew.setUnitType(UNIT_TYPE_LINEAR);
299     _units_skew.setUnitType(UNIT_TYPE_DIMENSIONLESS);
300     _units_skew.setUnitType(UNIT_TYPE_RADIAL);
302     _scalar_skew_horizontal.initScalar(-1e6, 1e6);
303     _scalar_skew_horizontal.setDigits(3);
304     _scalar_skew_horizontal.setIncrements(0.1, 1.0);
306     _scalar_skew_vertical.initScalar(-1e6, 1e6);
307     _scalar_skew_vertical.setDigits(3);
308     _scalar_skew_vertical.setIncrements(0.1, 1.0);
310     _page_skew.table()
311         .attach(_scalar_skew_horizontal, 0, 2, 0, 1, Gtk::FILL, Gtk::SHRINK);
312     _scalar_skew_horizontal.signal_value_changed()
313         .connect(sigc::mem_fun(*this, &Transformation::onSkewValueChanged));
315     _page_skew.table()
316         .attach(_units_skew, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK);
318     _page_skew.table()
319         .attach(_scalar_skew_vertical, 0, 2, 1, 2, Gtk::FILL, Gtk::SHRINK);
320     _scalar_skew_vertical.signal_value_changed()
321         .connect(sigc::mem_fun(*this, &Transformation::onSkewValueChanged));
323     //TODO: honour rotation center?
328 void
329 Transformation::layoutPageTransform()
331     _scalar_transform_a.setWidgetSizeRequest(65, -1);
332     _scalar_transform_a.setRange(-1e10, 1e10);
333     _scalar_transform_a.setDigits(3);
334     _scalar_transform_a.setIncrements(0.1, 1.0);
335     _scalar_transform_a.setValue(1.0);
336     _page_transform.table()
337         .attach(_scalar_transform_a, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK);
338     _scalar_transform_a.signal_value_changed()
339         .connect(sigc::mem_fun(*this, &Transformation::onTransformValueChanged));
342     _scalar_transform_b.setWidgetSizeRequest(65, -1);
343     _scalar_transform_b.setRange(-1e10, 1e10);
344     _scalar_transform_b.setDigits(3);
345     _scalar_transform_b.setIncrements(0.1, 1.0);
346     _scalar_transform_b.setValue(0.0);
347     _page_transform.table()
348         .attach(_scalar_transform_b, 0, 1, 1, 2, Gtk::SHRINK, Gtk::SHRINK);
349     _scalar_transform_b.signal_value_changed()
350         .connect(sigc::mem_fun(*this, &Transformation::onTransformValueChanged));
353     _scalar_transform_c.setWidgetSizeRequest(65, -1);
354     _scalar_transform_c.setRange(-1e10, 1e10);
355     _scalar_transform_c.setDigits(3);
356     _scalar_transform_c.setIncrements(0.1, 1.0);
357     _scalar_transform_c.setValue(0.0);
358     _page_transform.table()
359         .attach(_scalar_transform_c, 1, 2, 0, 1, Gtk::SHRINK, Gtk::SHRINK);
360     _scalar_transform_c.signal_value_changed()
361         .connect(sigc::mem_fun(*this, &Transformation::onTransformValueChanged));
364     _scalar_transform_d.setWidgetSizeRequest(65, -1);
365     _scalar_transform_d.setRange(-1e10, 1e10);
366     _scalar_transform_d.setDigits(3);
367     _scalar_transform_d.setIncrements(0.1, 1.0);
368     _scalar_transform_d.setValue(1.0);
369     _page_transform.table()
370         .attach(_scalar_transform_d, 1, 2, 1, 2, Gtk::SHRINK, Gtk::SHRINK);
371     _scalar_transform_d.signal_value_changed()
372         .connect(sigc::mem_fun(*this, &Transformation::onTransformValueChanged));
375     _scalar_transform_e.setWidgetSizeRequest(65, -1);
376     _scalar_transform_e.setRange(-1e10, 1e10);
377     _scalar_transform_e.setDigits(3);
378     _scalar_transform_e.setIncrements(0.1, 1.0);
379     _scalar_transform_e.setValue(0.0);
380     _page_transform.table()
381         .attach(_scalar_transform_e, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK);
382     _scalar_transform_e.signal_value_changed()
383         .connect(sigc::mem_fun(*this, &Transformation::onTransformValueChanged));
386     _scalar_transform_f.setWidgetSizeRequest(65, -1);
387     _scalar_transform_f.setRange(-1e10, 1e10);
388     _scalar_transform_f.setDigits(3);
389     _scalar_transform_f.setIncrements(0.1, 1.0);
390     _scalar_transform_f.setValue(0.0);
391     _page_transform.table()
392         .attach(_scalar_transform_f, 2, 3, 1, 2, Gtk::SHRINK, Gtk::SHRINK);
393     _scalar_transform_f.signal_value_changed()
394         .connect(sigc::mem_fun(*this, &Transformation::onTransformValueChanged));
396     // Edit existing matrix
397     _page_transform.table()
398         .attach(_check_replace_matrix, 0, 2, 2, 3, Gtk::FILL, Gtk::SHRINK);
399     _check_replace_matrix.set_active(false);
400     _check_replace_matrix.signal_toggled()
401         .connect(sigc::mem_fun(*this, &Transformation::onReplaceMatrixToggled));
405 /*########################################################################
406 # U P D A T E
407 ########################################################################*/
409 void
410 Transformation::updateSelection(PageType page, Inkscape::Selection *selection)
412     if (!selection || selection->isEmpty())
413         return;
415     switch (page) {
416         case PAGE_MOVE: {
417             updatePageMove(selection);
418             break;
419         }
420         case PAGE_SCALE: {
421             updatePageScale(selection);
422             break;
423         }
424         case PAGE_ROTATE: {
425             updatePageRotate(selection);
426             break;
427         }
428         case PAGE_SKEW: {
429             updatePageSkew(selection);
430             break;
431         }
432         case PAGE_TRANSFORM: {
433             updatePageTransform(selection);
434             break;
435         }
436         case PAGE_QTY: {
437             break;
438         }
439     }
441     setResponseSensitive(Gtk::RESPONSE_APPLY,
442                          selection && !selection->isEmpty());
445 void
446 Transformation::onSwitchPage(GtkNotebookPage */*page*/,
447                                    guint pagenum)
449     updateSelection((PageType)pagenum, sp_desktop_selection(getDesktop()));
452 void
453 Transformation::updatePageMove(Inkscape::Selection *selection)
455     if (selection && !selection->isEmpty()) {
456         if (!_check_move_relative.get_active()) {
457             NR::Maybe<NR::Rect> bbox = selection->bounds();
458             if (bbox) {
459                 double x = bbox->min()[NR::X];
460                 double y = bbox->min()[NR::Y];
462                 _scalar_move_horizontal.setValue(x, "px");
463                 _scalar_move_vertical.setValue(y, "px");
464             }
465         } else {
466             // do nothing, so you can apply the same relative move to many objects in turn
467         }
468         _page_move.set_sensitive(true);
469     } else {
470         _page_move.set_sensitive(false);
471     }
474 void
475 Transformation::updatePageScale(Inkscape::Selection *selection)
477     if (selection && !selection->isEmpty()) {
478         NR::Maybe<NR::Rect> bbox = selection->bounds();
479         if (bbox) {
480             double w = bbox->extent(NR::X);
481             double h = bbox->extent(NR::Y);
482             _scalar_scale_horizontal.setHundredPercent(w);
483             _scalar_scale_vertical.setHundredPercent(h);
484             onScaleXValueChanged(); // to update x/y proportionality if switch is on
485             _page_scale.set_sensitive(true);
486         } else {
487             _page_scale.set_sensitive(false);
488         }
489     } else {
490         _page_scale.set_sensitive(false);
491     }
494 void
495 Transformation::updatePageRotate(Inkscape::Selection *selection)
497     if (selection && !selection->isEmpty()) {
498         _page_rotate.set_sensitive(true);
499     } else {
500         _page_rotate.set_sensitive(false);
501     }
504 void
505 Transformation::updatePageSkew(Inkscape::Selection *selection)
507     if (selection && !selection->isEmpty()) {
508         NR::Maybe<NR::Rect> bbox = selection->bounds();
509         if (bbox) {
510             double w = bbox->extent(NR::X);
511             double h = bbox->extent(NR::Y);
512             _scalar_skew_vertical.setHundredPercent(w);
513             _scalar_skew_horizontal.setHundredPercent(h);
514             _page_skew.set_sensitive(true);
515         } else {
516             _page_skew.set_sensitive(false);
517         }
518     } else {
519         _page_skew.set_sensitive(false);
520     }
523 void
524 Transformation::updatePageTransform(Inkscape::Selection *selection)
526     if (selection && !selection->isEmpty()) {
527         if (_check_replace_matrix.get_active()) {
528             NR::Matrix current (SP_ITEM(selection->itemList()->data)->transform); // take from the first item in selection
530             NR::Matrix new_displayed = current;
532             _scalar_transform_a.setValue(new_displayed[0]);
533             _scalar_transform_b.setValue(new_displayed[1]);
534             _scalar_transform_c.setValue(new_displayed[2]);
535             _scalar_transform_d.setValue(new_displayed[3]);
536             _scalar_transform_e.setValue(new_displayed[4]);
537             _scalar_transform_f.setValue(new_displayed[5]);
538         } else {
539             // do nothing, so you can apply the same matrix to many objects in turn
540         }
541         _page_transform.set_sensitive(true);
542     } else {
543         _page_transform.set_sensitive(false);
544     }
551 /*########################################################################
552 # A P P L Y
553 ########################################################################*/
557 void
558 Transformation::_apply()
560     Inkscape::Selection * const selection = _getSelection();
561     if (!selection || selection->isEmpty())
562         return;
564     int const page = _notebook.get_current_page();
566     switch (page) {
567         case PAGE_MOVE: {
568             applyPageMove(selection);
569             break;
570         }
571         case PAGE_ROTATE: {
572             applyPageRotate(selection);
573             break;
574         }
575         case PAGE_SCALE: {
576             applyPageScale(selection);
577             break;
578         }
579         case PAGE_SKEW: {
580             applyPageSkew(selection);
581             break;
582         }
583         case PAGE_TRANSFORM: {
584             applyPageTransform(selection);
585             break;
586         }
587     }
589     //Let's play with never turning this off
590     //setResponseSensitive(Gtk::RESPONSE_APPLY, false);
593 void
594 Transformation::applyPageMove(Inkscape::Selection *selection)
596     double x = _scalar_move_horizontal.getValue("px");
597     double y = _scalar_move_vertical.getValue("px");
599     if (_check_move_relative.get_active()) {
600         sp_selection_move_relative(selection, x, y);
601     } else {
602         NR::Maybe<NR::Rect> bbox = selection->bounds();
603         if (bbox) {
604             sp_selection_move_relative(selection,
605                 x - bbox->min()[NR::X], y - bbox->min()[NR::Y]);
606         }
607     }
609     sp_document_done ( sp_desktop_document (selection->desktop()) , SP_VERB_DIALOG_TRANSFORM,
610                        _("Move"));
613 void
614 Transformation::applyPageScale(Inkscape::Selection *selection)
616     double scaleX = _scalar_scale_horizontal.getValue("px");
617     double scaleY = _scalar_scale_vertical.getValue("px");
619     if (prefs_get_int_attribute_limited ("dialogs.transformation", "applyseparately", 0, 0, 1) == 1) {
620         for (GSList const *l = selection->itemList(); l != NULL; l = l->next) {
621             SPItem *item = SP_ITEM(l->data);
622             NR::scale scale (0,0);
623             // the values are increments!
624             if (_units_scale.isAbsolute()) {
625                 NR::Maybe<NR::Rect> bbox(sp_item_bbox_desktop(item));
626                 if (bbox) {
627                     double new_width = scaleX;
628                     if (fabs(new_width) < 1e-6) new_width = 1e-6; // not 0, as this would result in a nasty no-bbox object
629                     double new_height = scaleY;
630                     if (fabs(new_height) < 1e-6) new_height = 1e-6;
631                     scale = NR::scale(new_width / bbox->extent(NR::X), new_height / bbox->extent(NR::Y));
632                 }
633             } else {
634                 double new_width = scaleX;
635                 if (fabs(new_width) < 1e-6) new_width = 1e-6;
636                 double new_height = scaleY;
637                 if (fabs(new_height) < 1e-6) new_height = 1e-6;
638                 scale = NR::scale(new_width / 100.0, new_height / 100.0);
639             }
640             sp_item_scale_rel (item, scale);
641         }
642     } else {
643         NR::Maybe<NR::Rect> bbox(selection->bounds());
644         if (bbox) {
645             NR::Point center(bbox->midpoint()); // use rotation center?
646             NR::scale scale (0,0);
647             // the values are increments!
648             if (_units_scale.isAbsolute()) {
649                 double new_width = scaleX;
650                 if (fabs(new_width) < 1e-6) new_width = 1e-6;
651                 double new_height = scaleY;
652                 if (fabs(new_height) < 1e-6) new_height = 1e-6;
653                 scale = NR::scale(new_width / bbox->extent(NR::X), new_height / bbox->extent(NR::Y));
654             } else {
655                 double new_width = scaleX;
656                 if (fabs(new_width) < 1e-6) new_width = 1e-6;
657                 double new_height = scaleY;
658                 if (fabs(new_height) < 1e-6) new_height = 1e-6;
659                 scale = NR::scale(new_width / 100.0, new_height / 100.0);
660             }
661             sp_selection_scale_relative(selection, center, scale);
662         }
663     }
665     sp_document_done(sp_desktop_document(selection->desktop()), SP_VERB_DIALOG_TRANSFORM,
666                      _("Scale"));
669 void
670 Transformation::applyPageRotate(Inkscape::Selection *selection)
672     double angle = _scalar_rotate.getValue("deg");
674     if (prefs_get_int_attribute_limited ("dialogs.transformation", "applyseparately", 0, 0, 1) == 1) {
675         for (GSList const *l = selection->itemList(); l != NULL; l = l->next) {
676             SPItem *item = SP_ITEM(l->data);
677             sp_item_rotate_rel(item, NR::rotate (angle*M_PI/180.0));
678         }
679     } else {
680         NR::Maybe<NR::Point> center = selection->center();
681         if (center) {
682             sp_selection_rotate_relative(selection, *center, angle);
683         }
684     }
686     sp_document_done(sp_desktop_document(selection->desktop()), SP_VERB_DIALOG_TRANSFORM,
687                      _("Rotate"));
690 void
691 Transformation::applyPageSkew(Inkscape::Selection *selection)
693     if (prefs_get_int_attribute_limited ("dialogs.transformation", "applyseparately", 0, 0, 1) == 1) {
694         for (GSList const *l = selection->itemList(); l != NULL; l = l->next) {
695             SPItem *item = SP_ITEM(l->data);
697             if (!_units_skew.isAbsolute()) { // percentage
698                 double skewX = _scalar_skew_horizontal.getValue("%");
699                 double skewY = _scalar_skew_vertical.getValue("%");
700                 sp_item_skew_rel (item, 0.01*skewX, 0.01*skewY);
701             } else if (_units_skew.isRadial()) { //deg or rad
702                 double angleX = _scalar_skew_horizontal.getValue("rad");
703                 double angleY = _scalar_skew_vertical.getValue("rad");
704                 double skewX = tan(-angleX);
705                 double skewY = tan(angleY);
706                 sp_item_skew_rel (item, skewX, skewY);
707             } else { // absolute displacement
708                 double skewX = _scalar_skew_horizontal.getValue("px");
709                 double skewY = _scalar_skew_vertical.getValue("px");
710                 NR::Maybe<NR::Rect> bbox(sp_item_bbox_desktop(item));
711                 if (bbox) {
712                     double width = bbox->extent(NR::X);
713                     double height = bbox->extent(NR::Y);
714                     sp_item_skew_rel (item, skewX/height, skewY/width);
715                 }
716             }
717         }
718     } else { // transform whole selection
719         NR::Maybe<NR::Rect> bbox = selection->bounds();
720         NR::Maybe<NR::Point> center = selection->center();
722         if ( bbox && center ) {
723             double width  = bbox->extent(NR::X);
724             double height = bbox->extent(NR::Y);
726             if (!_units_skew.isAbsolute()) { // percentage
727                 double skewX = _scalar_skew_horizontal.getValue("%");
728                 double skewY = _scalar_skew_vertical.getValue("%");
729                 sp_selection_skew_relative(selection, *center, 0.01*skewX, 0.01*skewY);
730             } else if (_units_skew.isRadial()) { //deg or rad
731                 double angleX = _scalar_skew_horizontal.getValue("rad");
732                 double angleY = _scalar_skew_vertical.getValue("rad");
733                 double skewX = tan(-angleX);
734                 double skewY = tan(angleY);
735                 sp_selection_skew_relative(selection, *center, skewX, skewY);
736             } else { // absolute displacement
737                 double skewX = _scalar_skew_horizontal.getValue("px");
738                 double skewY = _scalar_skew_vertical.getValue("px");
739                 sp_selection_skew_relative(selection, *center, skewX/height, skewY/width);
740             }
741         }
742     }
744     sp_document_done(sp_desktop_document(selection->desktop()), SP_VERB_DIALOG_TRANSFORM,
745                      _("Skew"));
749 void
750 Transformation::applyPageTransform(Inkscape::Selection *selection)
752     double a = _scalar_transform_a.getValue();
753     double b = _scalar_transform_b.getValue();
754     double c = _scalar_transform_c.getValue();
755     double d = _scalar_transform_d.getValue();
756     double e = _scalar_transform_e.getValue();
757     double f = _scalar_transform_f.getValue();
759     NR::Matrix displayed(a, b, c, d, e, f);
761     if (_check_replace_matrix.get_active()) {
762         for (GSList const *l = selection->itemList(); l != NULL; l = l->next) {
763             SPItem *item = SP_ITEM(l->data);
764             sp_item_set_item_transform(item, displayed);
765             SP_OBJECT(item)->updateRepr();
766         }
767     } else {
768         sp_selection_apply_affine(selection, displayed); // post-multiply each object's transform
769     }
771     sp_document_done(sp_desktop_document(selection->desktop()), SP_VERB_DIALOG_TRANSFORM,
772                      _("Edit transformation matrix"));
779 /*########################################################################
780 # V A L U E - C H A N G E D    C A L L B A C K S
781 ########################################################################*/
783 void
784 Transformation::onMoveValueChanged()
786     setResponseSensitive(Gtk::RESPONSE_APPLY, true);
789 void
790 Transformation::onMoveRelativeToggled()
792     Inkscape::Selection *selection = _getSelection();
794     if (!selection || selection->isEmpty())
795         return;
797     double x = _scalar_move_horizontal.getValue("px");
798     double y = _scalar_move_vertical.getValue("px");
800     //g_message("onMoveRelativeToggled: %f, %f px\n", x, y);
802     NR::Maybe<NR::Rect> bbox = selection->bounds();
804     if (bbox) {
805         if (_check_move_relative.get_active()) {
806             // From absolute to relative
807             _scalar_move_horizontal.setValue(x - bbox->min()[NR::X], "px");
808             _scalar_move_vertical.setValue(  y - bbox->min()[NR::Y], "px");
809         } else {
810             // From relative to absolute
811             _scalar_move_horizontal.setValue(bbox->min()[NR::X] + x, "px");
812             _scalar_move_vertical.setValue(  bbox->min()[NR::Y] + y, "px");
813         }
814     }
816     setResponseSensitive(Gtk::RESPONSE_APPLY, true);
819 void
820 Transformation::onScaleXValueChanged()
822     if (_scalar_scale_horizontal.setProgrammatically) {
823         _scalar_scale_horizontal.setProgrammatically = false;
824         return;
825     }
827     setResponseSensitive(Gtk::RESPONSE_APPLY, true);
829     if (_check_scale_proportional.get_active()) {
830         if (!_units_scale.isAbsolute()) { // percentage, just copy over
831             _scalar_scale_vertical.setValue(_scalar_scale_horizontal.getValue("%"));
832         } else {
833             double scaleXPercentage = _scalar_scale_horizontal.getAsPercentage();
834             _scalar_scale_vertical.setFromPercentage (scaleXPercentage);
835         }
836     }
839 void
840 Transformation::onScaleYValueChanged()
842     if (_scalar_scale_vertical.setProgrammatically) {
843         _scalar_scale_vertical.setProgrammatically = false;
844         return;
845     }
847     setResponseSensitive(Gtk::RESPONSE_APPLY, true);
849     if (_check_scale_proportional.get_active()) {
850         if (!_units_scale.isAbsolute()) { // percentage, just copy over
851             _scalar_scale_horizontal.setValue(_scalar_scale_vertical.getValue("%"));
852         } else {
853             double scaleYPercentage = _scalar_scale_vertical.getAsPercentage();
854             _scalar_scale_horizontal.setFromPercentage (scaleYPercentage);
855         }
856     }
859 void
860 Transformation::onRotateValueChanged()
862     setResponseSensitive(Gtk::RESPONSE_APPLY, true);
865 void
866 Transformation::onSkewValueChanged()
868     setResponseSensitive(Gtk::RESPONSE_APPLY, true);
871 void
872 Transformation::onTransformValueChanged()
875     /*
876     double a = _scalar_transform_a.getValue();
877     double b = _scalar_transform_b.getValue();
878     double c = _scalar_transform_c.getValue();
879     double d = _scalar_transform_d.getValue();
880     double e = _scalar_transform_e.getValue();
881     double f = _scalar_transform_f.getValue();
883     //g_message("onTransformValueChanged: (%f, %f, %f, %f, %f, %f)\n",
884     //          a, b, c, d, e ,f);
885     */
887     setResponseSensitive(Gtk::RESPONSE_APPLY, true);
890 void
891 Transformation::onReplaceMatrixToggled()
893     Inkscape::Selection *selection = _getSelection();
895     if (!selection || selection->isEmpty())
896         return;
898     double a = _scalar_transform_a.getValue();
899     double b = _scalar_transform_b.getValue();
900     double c = _scalar_transform_c.getValue();
901     double d = _scalar_transform_d.getValue();
902     double e = _scalar_transform_e.getValue();
903     double f = _scalar_transform_f.getValue();
905     NR::Matrix displayed (a, b, c, d, e, f);
906     NR::Matrix current (SP_ITEM(selection->itemList()->data)->transform); // take from the first item in selection
908     NR::Matrix new_displayed;
909     if (_check_replace_matrix.get_active()) {
910         new_displayed = current;
911     } else {
912         new_displayed = current.inverse() * displayed;
913     }
915     _scalar_transform_a.setValue(new_displayed[0]);
916     _scalar_transform_b.setValue(new_displayed[1]);
917     _scalar_transform_c.setValue(new_displayed[2]);
918     _scalar_transform_d.setValue(new_displayed[3]);
919     _scalar_transform_e.setValue(new_displayed[4]);
920     _scalar_transform_f.setValue(new_displayed[5]);
923 void
924 Transformation::onScaleProportionalToggled()
926     onScaleXValueChanged();
930 void
931 Transformation::onClear()
933     int const page = _notebook.get_current_page();
935     switch (page) {
936     case PAGE_MOVE: {
937         Inkscape::Selection *selection = _getSelection();
938         if (!selection || selection->isEmpty() || _check_move_relative.get_active()) {
939             _scalar_move_horizontal.setValue(0);
940             _scalar_move_vertical.setValue(0);
941         } else {
942             NR::Maybe<NR::Rect> bbox = selection->bounds();
943             if (bbox) {
944                 _scalar_move_horizontal.setValue(bbox->min()[NR::X], "px");
945                 _scalar_move_vertical.setValue(bbox->min()[NR::Y], "px");
946             }
947         }
948         break;
949     }
950     case PAGE_ROTATE: {
951         _scalar_rotate.setValue(0);
952         break;
953     }
954     case PAGE_SCALE: {
955         _scalar_scale_horizontal.setValue(100, "%");
956         _scalar_scale_vertical.setValue(100, "%");
957         break;
958     }
959     case PAGE_SKEW: {
960         _scalar_skew_horizontal.setValue(0);
961         _scalar_skew_vertical.setValue(0);
962         break;
963     }
964     case PAGE_TRANSFORM: {
965         _scalar_transform_a.setValue(1);
966         _scalar_transform_b.setValue(0);
967         _scalar_transform_c.setValue(0);
968         _scalar_transform_d.setValue(1);
969         _scalar_transform_e.setValue(0);
970         _scalar_transform_f.setValue(0);
971         break;
972     }
973     }
976 void
977 Transformation::onApplySeparatelyToggled()
979     prefs_set_int_attribute ("dialogs.transformation", "applyseparately", _check_apply_separately.get_active()? 1 : 0);
983 } // namespace Dialog
984 } // namespace UI
985 } // namespace Inkscape
989 /*
990   Local Variables:
991   mode:c++
992   c-file-style:"stroustrup"
993   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
994   indent-tabs-mode:nil
995   fill-column:99
996   End:
997 */
998 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :