Code

Merge and cleanup of GSoC C++-ification project.
[inkscape.git] / src / ui / dialog / transformation.cpp
1 /** @file
2  * \brief Transform dialog - implementation
3  */
4 /* Authors:
5  *   Bryce W. Harrington <bryce@bryceharrington.org>
6  *   buliabyak@gmail.com
7  *   Abhishek Sharma
8  *
9  * Copyright (C) 2004, 2005 Authors
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 "align-and-distribute.h"
24 #include "libnr/nr-matrix-ops.h"
25 #include "inkscape.h"
26 #include "selection.h"
27 #include "selection-chemistry.h"
28 #include "verbs.h"
29 #include "preferences.h"
30 #include "sp-namedview.h"
31 #include "sp-item-transform.h"
32 #include "macros.h"
33 #include "sp-item.h"
34 #include "util/glib-list-iterators.h"
36 namespace Inkscape {
37 namespace UI {
38 namespace Dialog {
40 void on_selection_changed(Inkscape::Application */*inkscape*/, Inkscape::Selection *selection, Transformation *daad)
41 {
42     int page = daad->getCurrentPage();
43     daad->updateSelection((Inkscape::UI::Dialog::Transformation::PageType)page, selection);
44 }
46 void on_selection_modified( Inkscape::Application */*inkscape*/,
47                             Inkscape::Selection *selection,
48                             guint /*flags*/,
49                             Transformation *daad )
50 {
51     int page = daad->getCurrentPage();
52     daad->updateSelection((Inkscape::UI::Dialog::Transformation::PageType)page, selection);
53 }
55 /*########################################################################
56 # C O N S T R U C T O R
57 ########################################################################*/
59 /**
60  * Constructor for Transformation.  This does the initialization
61  * and layout of the dialog used for transforming SVG objects.  It
62  * consists of 5 pages for the 5 operations it handles:
63  * 'Move' allows x,y translation of SVG objects
64  * 'Scale' allows linear resizing of SVG objects
65  * 'Rotate' allows rotating SVG objects by a degree
66  * 'Skew' allows skewing SVG objects
67  * 'Matrix' allows applying a generic affine transform on SVG objects,
68  *     with the user specifying the 6 degrees of freedom manually.
69  *
70  * The dialog is implemented as a Gtk::Notebook with five pages.
71  * The pages are implemented using Inkscape's NotebookPage which
72  * is used to help make sure all of Inkscape's notebooks follow
73  * the same style.  We then populate the pages with our widgets,
74  * we use the ScalarUnit class for this.
75  *
76  */
77 Transformation::Transformation()
78     : UI::Widget::Panel ("", "/dialogs/transformation", SP_VERB_DIALOG_TRANSFORM),
79       _page_move              (4, 2),
80       _page_scale             (4, 2),
81       _page_rotate            (4, 2),
82       _page_skew              (4, 2),
83       _page_transform         (3, 3),
84       _scalar_move_horizontal (_("_Horizontal:"), _("Horizontal displacement (relative) or position (absolute)"), UNIT_TYPE_LINEAR,
85                                "", "transform-move-horizontal", &_units_move),
86       _scalar_move_vertical   (_("_Vertical:"),  _("Vertical displacement (relative) or position (absolute)"), UNIT_TYPE_LINEAR,
87                                "", "transform-move-vertical", &_units_move),
88       _scalar_scale_horizontal(_("_Width:"), _("Horizontal size (absolute or percentage of current)"), UNIT_TYPE_DIMENSIONLESS,
89                                "", "transform-scale-horizontal", &_units_scale),
90       _scalar_scale_vertical  (_("_Height:"),  _("Vertical size (absolute or percentage of current)"), UNIT_TYPE_DIMENSIONLESS,
91                                "", "transform-scale-vertical", &_units_scale),
92       _scalar_rotate          (_("A_ngle:"), _("Rotation angle (positive = counterclockwise)"), UNIT_TYPE_RADIAL,
93                                "", "transform-rotate", &_units_rotate),
94       _scalar_skew_horizontal (_("_Horizontal:"), _("Horizontal skew angle (positive = counterclockwise), or absolute displacement, or percentage displacement"), UNIT_TYPE_LINEAR,
95                                "", "transform-skew-horizontal", &_units_skew),
96       _scalar_skew_vertical   (_("_Vertical:"),  _("Vertical skew angle (positive = counterclockwise), or absolute displacement, or percentage displacement"),  UNIT_TYPE_LINEAR,
97                                "", "transform-skew-vertical", &_units_skew),
99       _scalar_transform_a     ("_A:", _("Transformation matrix element A")),
100       _scalar_transform_b     ("_B:", _("Transformation matrix element B")),
101       _scalar_transform_c     ("_C:", _("Transformation matrix element C")),
102       _scalar_transform_d     ("_D:", _("Transformation matrix element D")),
103       _scalar_transform_e     ("_E:", _("Transformation matrix element E")),
104       _scalar_transform_f     ("_F:", _("Transformation matrix element F")),
106       _check_move_relative    (_("Rela_tive move"), _("Add the specified relative displacement to the current position; otherwise, edit the current absolute position directly")),
107       _check_scale_proportional (_("Scale proportionally"), _("Preserve the width/height ratio of the scaled objects")),
108       _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")),
109       _check_replace_matrix    (_("Edit c_urrent matrix"), _("Edit the current transform= matrix; otherwise, post-multiply transform= by this matrix"))
112     Gtk::Box *contents = _getContents();
114     contents->set_spacing(0);
116     // Notebook for individual transformations
117     contents->pack_start(_notebook, true, true);
119     _notebook.append_page(_page_move, _("_Move"), true);
120     layoutPageMove();
122     _notebook.append_page(_page_scale, _("_Scale"), true);
123     layoutPageScale();
125     _notebook.append_page(_page_rotate, _("_Rotate"), true);
126     layoutPageRotate();
128     _notebook.append_page(_page_skew, _("Ske_w"), true);
129     layoutPageSkew();
131     _notebook.append_page(_page_transform, _("Matri_x"), true);
132     layoutPageTransform();
134     _notebook.signal_switch_page().connect(sigc::mem_fun(*this, &Transformation::onSwitchPage));
136     // Apply separately
137     contents->pack_start(_check_apply_separately, true, true);
138     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
139     _check_apply_separately.set_active(prefs->getBool("/dialogs/transformation/applyseparately"));
140     _check_apply_separately.signal_toggled().connect(sigc::mem_fun(*this, &Transformation::onApplySeparatelyToggled));
142     // make sure all spinbuttons activate Apply on pressing Enter
143       ((Gtk::Entry *) (_scalar_move_horizontal.getWidget()))->set_activates_default(true);
144       ((Gtk::Entry *) (_scalar_move_vertical.getWidget()))->set_activates_default(true);
145       ((Gtk::Entry *) (_scalar_scale_horizontal.getWidget()))->set_activates_default(true);
146       ((Gtk::Entry *) (_scalar_scale_vertical.getWidget()))->set_activates_default(true);
147       ((Gtk::Entry *) (_scalar_rotate.getWidget()))->set_activates_default(true);
148       ((Gtk::Entry *) (_scalar_skew_horizontal.getWidget()))->set_activates_default(true);
149       ((Gtk::Entry *) (_scalar_skew_vertical.getWidget()))->set_activates_default(true);
151     updateSelection(PAGE_MOVE, _getSelection());
153     resetButton = addResponseButton(Gtk::Stock::CLEAR, 0);
154     if (resetButton) {
155         _tooltips.set_tip((*resetButton), _("Reset the values on the current tab to defaults"));
156         resetButton->set_sensitive(true);
157         resetButton->signal_clicked().connect(sigc::mem_fun(*this, &Transformation::onClear));
158     }
160     applyButton = addResponseButton(Gtk::Stock::APPLY, Gtk::RESPONSE_APPLY);
161     if (applyButton) {
162         _tooltips.set_tip((*applyButton), _("Apply transformation to selection"));
163         applyButton->set_sensitive(false);
164     }
166     // Connect to the global selection changed & modified signals
167     g_signal_connect (G_OBJECT (INKSCAPE), "change_selection", G_CALLBACK (on_selection_changed), this);
168     g_signal_connect (G_OBJECT (INKSCAPE), "modify_selection", G_CALLBACK (on_selection_modified), this);
170     show_all_children();
173 Transformation::~Transformation()
175     sp_signal_disconnect_by_data (G_OBJECT (INKSCAPE), this);
179 /*########################################################################
180 # U T I L I T Y
181 ########################################################################*/
183 void
184 Transformation::presentPage(Transformation::PageType page)
186     _notebook.set_current_page(page);
187     show();
188     present();
194 /*########################################################################
195 # S E T U P   L A Y O U T
196 ########################################################################*/
199 void
200 Transformation::layoutPageMove()
202     _units_move.setUnitType(UNIT_TYPE_LINEAR);
203     
204     // Setting default unit to document unit
205     SPDesktop *dt = getDesktop();
206     SPNamedView *nv = sp_desktop_namedview(dt);
207     if (nv->doc_units) {
208         _units_move.setUnit(nv->doc_units->abbr);
209     }
210     
211     _scalar_move_horizontal.initScalar(-1e6, 1e6);
212     _scalar_move_horizontal.setDigits(3);
213     _scalar_move_horizontal.setIncrements(0.1, 1.0);
215     _scalar_move_vertical.initScalar(-1e6, 1e6);
216     _scalar_move_vertical.setDigits(3);
217     _scalar_move_vertical.setIncrements(0.1, 1.0);
219     //_scalar_move_vertical.set_label_image( INKSCAPE_STOCK_ARROWS_HOR );
220     _page_move.table()
221         .attach(_scalar_move_horizontal, 0, 2, 0, 1, Gtk::FILL, Gtk::SHRINK);
223     _page_move.table()
224         .attach(_units_move, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK);
226     _scalar_move_horizontal.signal_value_changed()
227         .connect(sigc::mem_fun(*this, &Transformation::onMoveValueChanged));
229     //_scalar_move_vertical.set_label_image( INKSCAPE_STOCK_ARROWS_VER );
230     _page_move.table()
231         .attach(_scalar_move_vertical, 0, 2, 1, 2, Gtk::FILL, Gtk::SHRINK);
233     _scalar_move_vertical.signal_value_changed()
234         .connect(sigc::mem_fun(*this, &Transformation::onMoveValueChanged));
236     // Relative moves
237     _page_move.table()
238         .attach(_check_move_relative, 0, 2, 2, 3, Gtk::FILL, Gtk::SHRINK);
239     _check_move_relative.set_active(true);
240     _check_move_relative.signal_toggled()
241         .connect(sigc::mem_fun(*this, &Transformation::onMoveRelativeToggled));
244 void
245 Transformation::layoutPageScale()
247     _units_scale.setUnitType(UNIT_TYPE_DIMENSIONLESS);
248     _units_scale.setUnitType(UNIT_TYPE_LINEAR);
250     _scalar_scale_horizontal.initScalar(-1e6, 1e6);
251     _scalar_scale_horizontal.setValue(100.0, "%");
252     _scalar_scale_horizontal.setDigits(3);
253     _scalar_scale_horizontal.setIncrements(0.1, 1.0);
254     _scalar_scale_horizontal.setAbsoluteIsIncrement(true);
255     _scalar_scale_horizontal.setPercentageIsIncrement(true);
257     _scalar_scale_vertical.initScalar(-1e6, 1e6);
258     _scalar_scale_vertical.setValue(100.0, "%");
259     _scalar_scale_vertical.setDigits(3);
260     _scalar_scale_vertical.setIncrements(0.1, 1.0);
261     _scalar_scale_vertical.setAbsoluteIsIncrement(true);
262     _scalar_scale_vertical.setPercentageIsIncrement(true);
264     _page_scale.table()
265         .attach(_scalar_scale_horizontal, 0, 2, 0, 1, Gtk::FILL, Gtk::SHRINK);
266     _scalar_scale_horizontal.signal_value_changed()
267         .connect(sigc::mem_fun(*this, &Transformation::onScaleXValueChanged));
269     _page_scale.table()
270         .attach(_units_scale, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK);
272     _page_scale.table()
273         .attach(_scalar_scale_vertical, 0, 2, 1, 2, Gtk::FILL, Gtk::SHRINK);
274     _scalar_scale_vertical.signal_value_changed()
275         .connect(sigc::mem_fun(*this, &Transformation::onScaleYValueChanged));
277     _page_scale.table()
278         .attach(_check_scale_proportional, 0, 2, 2, 3, Gtk::FILL, Gtk::SHRINK);
279     _check_scale_proportional.set_active(false);
280     _check_scale_proportional.signal_toggled()
281         .connect(sigc::mem_fun(*this, &Transformation::onScaleProportionalToggled));
283     //TODO: add a widget for selecting the fixed point in scaling, or honour rotation center?
286 void
287 Transformation::layoutPageRotate()
289     _units_rotate.setUnitType(UNIT_TYPE_RADIAL);
291     _scalar_rotate.initScalar(-360.0, 360.0);
292     _scalar_rotate.setDigits(3);
293     _scalar_rotate.setIncrements(0.1, 1.0);
295     _page_rotate.table()
296         .attach(_scalar_rotate, 0, 2, 0, 1, Gtk::FILL, Gtk::SHRINK);
298     _page_rotate.table()
299         .attach(_units_rotate, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK);
301     _scalar_rotate.signal_value_changed()
302         .connect(sigc::mem_fun(*this, &Transformation::onRotateValueChanged));
304     //TODO: honour rotation center?
307 void
308 Transformation::layoutPageSkew()
310     _units_skew.setUnitType(UNIT_TYPE_LINEAR);
311     _units_skew.setUnitType(UNIT_TYPE_DIMENSIONLESS);
312     _units_skew.setUnitType(UNIT_TYPE_RADIAL);
314     _scalar_skew_horizontal.initScalar(-1e6, 1e6);
315     _scalar_skew_horizontal.setDigits(3);
316     _scalar_skew_horizontal.setIncrements(0.1, 1.0);
318     _scalar_skew_vertical.initScalar(-1e6, 1e6);
319     _scalar_skew_vertical.setDigits(3);
320     _scalar_skew_vertical.setIncrements(0.1, 1.0);
322     _page_skew.table()
323         .attach(_scalar_skew_horizontal, 0, 2, 0, 1, Gtk::FILL, Gtk::SHRINK);
324     _scalar_skew_horizontal.signal_value_changed()
325         .connect(sigc::mem_fun(*this, &Transformation::onSkewValueChanged));
327     _page_skew.table()
328         .attach(_units_skew, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK);
330     _page_skew.table()
331         .attach(_scalar_skew_vertical, 0, 2, 1, 2, Gtk::FILL, Gtk::SHRINK);
332     _scalar_skew_vertical.signal_value_changed()
333         .connect(sigc::mem_fun(*this, &Transformation::onSkewValueChanged));
335     //TODO: honour rotation center?
340 void
341 Transformation::layoutPageTransform()
343     _scalar_transform_a.setWidgetSizeRequest(65, -1);
344     _scalar_transform_a.setRange(-1e10, 1e10);
345     _scalar_transform_a.setDigits(3);
346     _scalar_transform_a.setIncrements(0.1, 1.0);
347     _scalar_transform_a.setValue(1.0);
348     _page_transform.table()
349         .attach(_scalar_transform_a, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK);
350     _scalar_transform_a.signal_value_changed()
351         .connect(sigc::mem_fun(*this, &Transformation::onTransformValueChanged));
354     _scalar_transform_b.setWidgetSizeRequest(65, -1);
355     _scalar_transform_b.setRange(-1e10, 1e10);
356     _scalar_transform_b.setDigits(3);
357     _scalar_transform_b.setIncrements(0.1, 1.0);
358     _scalar_transform_b.setValue(0.0);
359     _page_transform.table()
360         .attach(_scalar_transform_b, 0, 1, 1, 2, Gtk::SHRINK, Gtk::SHRINK);
361     _scalar_transform_b.signal_value_changed()
362         .connect(sigc::mem_fun(*this, &Transformation::onTransformValueChanged));
365     _scalar_transform_c.setWidgetSizeRequest(65, -1);
366     _scalar_transform_c.setRange(-1e10, 1e10);
367     _scalar_transform_c.setDigits(3);
368     _scalar_transform_c.setIncrements(0.1, 1.0);
369     _scalar_transform_c.setValue(0.0);
370     _page_transform.table()
371         .attach(_scalar_transform_c, 1, 2, 0, 1, Gtk::SHRINK, Gtk::SHRINK);
372     _scalar_transform_c.signal_value_changed()
373         .connect(sigc::mem_fun(*this, &Transformation::onTransformValueChanged));
376     _scalar_transform_d.setWidgetSizeRequest(65, -1);
377     _scalar_transform_d.setRange(-1e10, 1e10);
378     _scalar_transform_d.setDigits(3);
379     _scalar_transform_d.setIncrements(0.1, 1.0);
380     _scalar_transform_d.setValue(1.0);
381     _page_transform.table()
382         .attach(_scalar_transform_d, 1, 2, 1, 2, Gtk::SHRINK, Gtk::SHRINK);
383     _scalar_transform_d.signal_value_changed()
384         .connect(sigc::mem_fun(*this, &Transformation::onTransformValueChanged));
387     _scalar_transform_e.setWidgetSizeRequest(65, -1);
388     _scalar_transform_e.setRange(-1e10, 1e10);
389     _scalar_transform_e.setDigits(3);
390     _scalar_transform_e.setIncrements(0.1, 1.0);
391     _scalar_transform_e.setValue(0.0);
392     _page_transform.table()
393         .attach(_scalar_transform_e, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK);
394     _scalar_transform_e.signal_value_changed()
395         .connect(sigc::mem_fun(*this, &Transformation::onTransformValueChanged));
398     _scalar_transform_f.setWidgetSizeRequest(65, -1);
399     _scalar_transform_f.setRange(-1e10, 1e10);
400     _scalar_transform_f.setDigits(3);
401     _scalar_transform_f.setIncrements(0.1, 1.0);
402     _scalar_transform_f.setValue(0.0);
403     _page_transform.table()
404         .attach(_scalar_transform_f, 2, 3, 1, 2, Gtk::SHRINK, Gtk::SHRINK);
405     _scalar_transform_f.signal_value_changed()
406         .connect(sigc::mem_fun(*this, &Transformation::onTransformValueChanged));
408     // Edit existing matrix
409     _page_transform.table()
410         .attach(_check_replace_matrix, 0, 2, 2, 3, Gtk::FILL, Gtk::SHRINK);
411     _check_replace_matrix.set_active(false);
412     _check_replace_matrix.signal_toggled()
413         .connect(sigc::mem_fun(*this, &Transformation::onReplaceMatrixToggled));
417 /*########################################################################
418 # U P D A T E
419 ########################################################################*/
421 void
422 Transformation::updateSelection(PageType page, Inkscape::Selection *selection)
424     if (!selection || selection->isEmpty())
425         return;
427     switch (page) {
428         case PAGE_MOVE: {
429             updatePageMove(selection);
430             break;
431         }
432         case PAGE_SCALE: {
433             updatePageScale(selection);
434             break;
435         }
436         case PAGE_ROTATE: {
437             updatePageRotate(selection);
438             break;
439         }
440         case PAGE_SKEW: {
441             updatePageSkew(selection);
442             break;
443         }
444         case PAGE_TRANSFORM: {
445             updatePageTransform(selection);
446             break;
447         }
448         case PAGE_QTY: {
449             break;
450         }
451     }
453     setResponseSensitive(Gtk::RESPONSE_APPLY,
454                          selection && !selection->isEmpty());
457 void
458 Transformation::onSwitchPage(GtkNotebookPage */*page*/,
459                                    guint pagenum)
461     updateSelection((PageType)pagenum, sp_desktop_selection(getDesktop()));
465 void
466 Transformation::updatePageMove(Inkscape::Selection *selection)
468     if (selection && !selection->isEmpty()) {
469         if (!_check_move_relative.get_active()) {
470             Geom::OptRect bbox = selection->bounds();
471             if (bbox) {
472                 double x = bbox->min()[Geom::X];
473                 double y = bbox->min()[Geom::Y];
475                 double conversion = _units_move.getConversion("px");
476                 _scalar_move_horizontal.setValue(x / conversion);
477                 _scalar_move_vertical.setValue(y / conversion);
478             }
479         } else {
480             // do nothing, so you can apply the same relative move to many objects in turn
481         }
482         _page_move.set_sensitive(true);
483     } else {
484         _page_move.set_sensitive(false);
485     }
488 void
489 Transformation::updatePageScale(Inkscape::Selection *selection)
491     if (selection && !selection->isEmpty()) {
492         Geom::OptRect bbox = selection->bounds();
493         if (bbox) {
494             double w = bbox->dimensions()[Geom::X];
495             double h = bbox->dimensions()[Geom::Y];
496             _scalar_scale_horizontal.setHundredPercent(w);
497             _scalar_scale_vertical.setHundredPercent(h);
498             onScaleXValueChanged(); // to update x/y proportionality if switch is on
499             _page_scale.set_sensitive(true);
500         } else {
501             _page_scale.set_sensitive(false);
502         }
503     } else {
504         _page_scale.set_sensitive(false);
505     }
508 void
509 Transformation::updatePageRotate(Inkscape::Selection *selection)
511     if (selection && !selection->isEmpty()) {
512         _page_rotate.set_sensitive(true);
513     } else {
514         _page_rotate.set_sensitive(false);
515     }
518 void
519 Transformation::updatePageSkew(Inkscape::Selection *selection)
521     if (selection && !selection->isEmpty()) {
522         Geom::OptRect bbox = selection->bounds();
523         if (bbox) {
524             double w = bbox->dimensions()[Geom::X];
525             double h = bbox->dimensions()[Geom::Y];
526             _scalar_skew_vertical.setHundredPercent(w);
527             _scalar_skew_horizontal.setHundredPercent(h);
528             _page_skew.set_sensitive(true);
529         } else {
530             _page_skew.set_sensitive(false);
531         }
532     } else {
533         _page_skew.set_sensitive(false);
534     }
537 void
538 Transformation::updatePageTransform(Inkscape::Selection *selection)
540     if (selection && !selection->isEmpty()) {
541         if (_check_replace_matrix.get_active()) {
542             Geom::Matrix current (SP_ITEM(selection->itemList()->data)->transform); // take from the first item in selection
544             Geom::Matrix new_displayed = current;
546             _scalar_transform_a.setValue(new_displayed[0]);
547             _scalar_transform_b.setValue(new_displayed[1]);
548             _scalar_transform_c.setValue(new_displayed[2]);
549             _scalar_transform_d.setValue(new_displayed[3]);
550             _scalar_transform_e.setValue(new_displayed[4]);
551             _scalar_transform_f.setValue(new_displayed[5]);
552         } else {
553             // do nothing, so you can apply the same matrix to many objects in turn
554         }
555         _page_transform.set_sensitive(true);
556     } else {
557         _page_transform.set_sensitive(false);
558     }
565 /*########################################################################
566 # A P P L Y
567 ########################################################################*/
571 void
572 Transformation::_apply()
574     Inkscape::Selection * const selection = _getSelection();
575     if (!selection || selection->isEmpty())
576         return;
578     int const page = _notebook.get_current_page();
580     switch (page) {
581         case PAGE_MOVE: {
582             applyPageMove(selection);
583             break;
584         }
585         case PAGE_ROTATE: {
586             applyPageRotate(selection);
587             break;
588         }
589         case PAGE_SCALE: {
590             applyPageScale(selection);
591             break;
592         }
593         case PAGE_SKEW: {
594             applyPageSkew(selection);
595             break;
596         }
597         case PAGE_TRANSFORM: {
598             applyPageTransform(selection);
599             break;
600         }
601     }
603     //Let's play with never turning this off
604     //setResponseSensitive(Gtk::RESPONSE_APPLY, false);
607 void
608 Transformation::applyPageMove(Inkscape::Selection *selection)
610     double x = _scalar_move_horizontal.getValue("px");
611     double y = _scalar_move_vertical.getValue("px");
613     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
614     if (!prefs->getBool("/dialogs/transformation/applyseparately")) {
615         // move selection as a whole
616         if (_check_move_relative.get_active()) {
617             sp_selection_move_relative(selection, x, y);
618         } else {
619             Geom::OptRect bbox = selection->bounds();
620             if (bbox) {
621                 sp_selection_move_relative(selection,
622                                            x - bbox->min()[Geom::X], y - bbox->min()[Geom::Y]);
623             }
624         }
625     } else {
627         if (_check_move_relative.get_active()) {
628             // shift each object relatively to the previous one
629             using Inkscape::Util::GSListConstIterator;
630             std::list<SPItem *> selected;
631             selected.insert<GSListConstIterator<SPItem *> >(selected.end(), selection->itemList(), NULL);
632             if (selected.empty()) return;
634             if (fabs(x) > 1e-6) {
635                 std::vector< BBoxSort  > sorted;
636                 for (std::list<SPItem *>::iterator it(selected.begin());
637                      it != selected.end();
638                      ++it)
639                 {
640                     Geom::OptRect bbox = (*it)->getBboxDesktop();
641                     if (bbox) {
642                         sorted.push_back(BBoxSort(*it, *bbox, Geom::X, x > 0? 1. : 0., x > 0? 0. : 1.));
643                     }
644                 }
645                 //sort bbox by anchors
646                 std::sort(sorted.begin(), sorted.end());
648                 double move = x;
649                 for ( std::vector<BBoxSort> ::iterator it (sorted.begin());
650                       it < sorted.end();
651                       it ++ )
652                 {
653                     sp_item_move_rel(it->item, Geom::Translate(move, 0));
654                     // move each next object by x relative to previous
655                     move += x;
656                 }
657             }
658             if (fabs(y) > 1e-6) {
659                 std::vector< BBoxSort  > sorted;
660                 for (std::list<SPItem *>::iterator it(selected.begin());
661                      it != selected.end();
662                      ++it)
663                 {
664                     Geom::OptRect bbox = (*it)->getBboxDesktop();
665                     if (bbox) {
666                         sorted.push_back(BBoxSort(*it, *bbox, Geom::Y, y > 0? 1. : 0., y > 0? 0. : 1.));
667                     }
668                 }
669                 //sort bbox by anchors
670                 std::sort(sorted.begin(), sorted.end());
672                 double move = y;
673                 for ( std::vector<BBoxSort> ::iterator it (sorted.begin());
674                       it < sorted.end();
675                       it ++ )
676                 {
677                     sp_item_move_rel(it->item, Geom::Translate(0, move));
678                     // move each next object by x relative to previous
679                     move += y;
680                 }
681             }
682         } else {
683             Geom::OptRect bbox = selection->bounds();
684             if (bbox) {
685                 sp_selection_move_relative(selection,
686                                            x - bbox->min()[Geom::X], y - bbox->min()[Geom::Y]);
687             }
688         }
689     }
691     DocumentUndo::done( sp_desktop_document(selection->desktop()) , SP_VERB_DIALOG_TRANSFORM,
692                         _("Move"));
695 void
696 Transformation::applyPageScale(Inkscape::Selection *selection)
698     double scaleX = _scalar_scale_horizontal.getValue("px");
699     double scaleY = _scalar_scale_vertical.getValue("px");
701     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
702     if (prefs->getBool("/dialogs/transformation/applyseparately")) {
703         for (GSList const *l = selection->itemList(); l != NULL; l = l->next) {
704             SPItem *item = SP_ITEM(l->data);
705             Geom::Scale scale (0,0);
706             // the values are increments!
707             if (_units_scale.isAbsolute()) {
708                 Geom::OptRect bbox(item->getBboxDesktop());
709                 if (bbox) {
710                     double new_width = scaleX;
711                     if (fabs(new_width) < 1e-6) new_width = 1e-6; // not 0, as this would result in a nasty no-bbox object
712                     double new_height = scaleY;
713                     if (fabs(new_height) < 1e-6) new_height = 1e-6;
714                     scale = Geom::Scale(new_width / bbox->dimensions()[Geom::X], new_height / bbox->dimensions()[Geom::Y]);
715                 }
716             } else {
717                 double new_width = scaleX;
718                 if (fabs(new_width) < 1e-6) new_width = 1e-6;
719                 double new_height = scaleY;
720                 if (fabs(new_height) < 1e-6) new_height = 1e-6;
721                 scale = Geom::Scale(new_width / 100.0, new_height / 100.0);
722             }
723             sp_item_scale_rel (item, scale);
724         }
725     } else {
726         Geom::OptRect bbox(selection->bounds());
727         if (bbox) {
728             Geom::Point center(bbox->midpoint()); // use rotation center?
729             Geom::Scale scale (0,0);
730             // the values are increments!
731             if (_units_scale.isAbsolute()) {
732                 double new_width = scaleX;
733                 if (fabs(new_width) < 1e-6) new_width = 1e-6;
734                 double new_height = scaleY;
735                 if (fabs(new_height) < 1e-6) new_height = 1e-6;
736                 scale = Geom::Scale(new_width / bbox->dimensions()[Geom::X], new_height / bbox->dimensions()[Geom::Y]);
737             } else {
738                 double new_width = scaleX;
739                 if (fabs(new_width) < 1e-6) new_width = 1e-6;
740                 double new_height = scaleY;
741                 if (fabs(new_height) < 1e-6) new_height = 1e-6;
742                 scale = Geom::Scale(new_width / 100.0, new_height / 100.0);
743             }
744             sp_selection_scale_relative(selection, center, scale);
745         }
746     }
748     DocumentUndo::done(sp_desktop_document(selection->desktop()), SP_VERB_DIALOG_TRANSFORM,
749                        _("Scale"));
752 void
753 Transformation::applyPageRotate(Inkscape::Selection *selection)
755     double angle = _scalar_rotate.getValue("deg");
757     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
758     if (prefs->getBool("/dialogs/transformation/applyseparately")) {
759         for (GSList const *l = selection->itemList(); l != NULL; l = l->next) {
760             SPItem *item = SP_ITEM(l->data);
761             sp_item_rotate_rel(item, Geom::Rotate (angle*M_PI/180.0));
762         }
763     } else {
764         boost::optional<Geom::Point> center = selection->center();
765         if (center) {
766             sp_selection_rotate_relative(selection, *center, angle);
767         }
768     }
770     DocumentUndo::done(sp_desktop_document(selection->desktop()), SP_VERB_DIALOG_TRANSFORM,
771                        _("Rotate"));
774 void
775 Transformation::applyPageSkew(Inkscape::Selection *selection)
777     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
778     if (prefs->getBool("/dialogs/transformation/applyseparately")) {
779         for (GSList const *l = selection->itemList(); l != NULL; l = l->next) {
780             SPItem *item = SP_ITEM(l->data);
782             if (!_units_skew.isAbsolute()) { // percentage
783                 double skewX = _scalar_skew_horizontal.getValue("%");
784                 double skewY = _scalar_skew_vertical.getValue("%");
785                 sp_item_skew_rel (item, 0.01*skewX, 0.01*skewY);
786             } else if (_units_skew.isRadial()) { //deg or rad
787                 double angleX = _scalar_skew_horizontal.getValue("rad");
788                 double angleY = _scalar_skew_vertical.getValue("rad");
789                 double skewX = tan(-angleX);
790                 double skewY = tan(angleY);
791                 sp_item_skew_rel (item, skewX, skewY);
792             } else { // absolute displacement
793                 double skewX = _scalar_skew_horizontal.getValue("px");
794                 double skewY = _scalar_skew_vertical.getValue("px");
795                 Geom::OptRect bbox(item->getBboxDesktop());
796                 if (bbox) {
797                     double width = bbox->dimensions()[Geom::X];
798                     double height = bbox->dimensions()[Geom::Y];
799                     sp_item_skew_rel (item, skewX/height, skewY/width);
800                 }
801             }
802         }
803     } else { // transform whole selection
804         Geom::OptRect bbox = selection->bounds();
805         boost::optional<Geom::Point> center = selection->center();
807         if ( bbox && center ) {
808             double width  = bbox->dimensions()[Geom::X];
809             double height = bbox->dimensions()[Geom::Y];
811             if (!_units_skew.isAbsolute()) { // percentage
812                 double skewX = _scalar_skew_horizontal.getValue("%");
813                 double skewY = _scalar_skew_vertical.getValue("%");
814                 sp_selection_skew_relative(selection, *center, 0.01*skewX, 0.01*skewY);
815             } else if (_units_skew.isRadial()) { //deg or rad
816                 double angleX = _scalar_skew_horizontal.getValue("rad");
817                 double angleY = _scalar_skew_vertical.getValue("rad");
818                 double skewX = tan(-angleX);
819                 double skewY = tan(angleY);
820                 sp_selection_skew_relative(selection, *center, skewX, skewY);
821             } else { // absolute displacement
822                 double skewX = _scalar_skew_horizontal.getValue("px");
823                 double skewY = _scalar_skew_vertical.getValue("px");
824                 sp_selection_skew_relative(selection, *center, skewX/height, skewY/width);
825             }
826         }
827     }
829     DocumentUndo::done(sp_desktop_document(selection->desktop()), SP_VERB_DIALOG_TRANSFORM,
830                        _("Skew"));
834 void
835 Transformation::applyPageTransform(Inkscape::Selection *selection)
837     double a = _scalar_transform_a.getValue();
838     double b = _scalar_transform_b.getValue();
839     double c = _scalar_transform_c.getValue();
840     double d = _scalar_transform_d.getValue();
841     double e = _scalar_transform_e.getValue();
842     double f = _scalar_transform_f.getValue();
844     Geom::Matrix displayed(a, b, c, d, e, f);
846     if (_check_replace_matrix.get_active()) {
847         for (GSList const *l = selection->itemList(); l != NULL; l = l->next) {
848             SPItem *item = SP_ITEM(l->data);
849             item->set_item_transform(displayed);
850             SP_OBJECT(item)->updateRepr();
851         }
852     } else {
853         sp_selection_apply_affine(selection, displayed); // post-multiply each object's transform
854     }
856     DocumentUndo::done(sp_desktop_document(selection->desktop()), SP_VERB_DIALOG_TRANSFORM,
857                        _("Edit transformation matrix"));
864 /*########################################################################
865 # V A L U E - C H A N G E D    C A L L B A C K S
866 ########################################################################*/
868 void
869 Transformation::onMoveValueChanged()
871     setResponseSensitive(Gtk::RESPONSE_APPLY, true);
874 void
875 Transformation::onMoveRelativeToggled()
877     Inkscape::Selection *selection = _getSelection();
879     if (!selection || selection->isEmpty())
880         return;
882     double x = _scalar_move_horizontal.getValue("px");
883     double y = _scalar_move_vertical.getValue("px");
885     double conversion = _units_move.getConversion("px");
887     //g_message("onMoveRelativeToggled: %f, %f px\n", x, y);
889     Geom::OptRect bbox = selection->bounds();
891     if (bbox) {
892         if (_check_move_relative.get_active()) {
893             // From absolute to relative
894             _scalar_move_horizontal.setValue((x - bbox->min()[Geom::X]) / conversion);
895             _scalar_move_vertical.setValue((  y - bbox->min()[Geom::Y]) / conversion);
896         } else {
897             // From relative to absolute
898             _scalar_move_horizontal.setValue((bbox->min()[Geom::X] + x) / conversion);
899             _scalar_move_vertical.setValue((  bbox->min()[Geom::Y] + y) / conversion);
900         }
901     }
903     setResponseSensitive(Gtk::RESPONSE_APPLY, true);
906 void
907 Transformation::onScaleXValueChanged()
909     if (_scalar_scale_horizontal.setProgrammatically) {
910         _scalar_scale_horizontal.setProgrammatically = false;
911         return;
912     }
914     setResponseSensitive(Gtk::RESPONSE_APPLY, true);
916     if (_check_scale_proportional.get_active()) {
917         if (!_units_scale.isAbsolute()) { // percentage, just copy over
918             _scalar_scale_vertical.setValue(_scalar_scale_horizontal.getValue("%"));
919         } else {
920             double scaleXPercentage = _scalar_scale_horizontal.getAsPercentage();
921             _scalar_scale_vertical.setFromPercentage (scaleXPercentage);
922         }
923     }
926 void
927 Transformation::onScaleYValueChanged()
929     if (_scalar_scale_vertical.setProgrammatically) {
930         _scalar_scale_vertical.setProgrammatically = false;
931         return;
932     }
934     setResponseSensitive(Gtk::RESPONSE_APPLY, true);
936     if (_check_scale_proportional.get_active()) {
937         if (!_units_scale.isAbsolute()) { // percentage, just copy over
938             _scalar_scale_horizontal.setValue(_scalar_scale_vertical.getValue("%"));
939         } else {
940             double scaleYPercentage = _scalar_scale_vertical.getAsPercentage();
941             _scalar_scale_horizontal.setFromPercentage (scaleYPercentage);
942         }
943     }
946 void
947 Transformation::onRotateValueChanged()
949     setResponseSensitive(Gtk::RESPONSE_APPLY, true);
952 void
953 Transformation::onSkewValueChanged()
955     setResponseSensitive(Gtk::RESPONSE_APPLY, true);
958 void
959 Transformation::onTransformValueChanged()
962     /*
963     double a = _scalar_transform_a.getValue();
964     double b = _scalar_transform_b.getValue();
965     double c = _scalar_transform_c.getValue();
966     double d = _scalar_transform_d.getValue();
967     double e = _scalar_transform_e.getValue();
968     double f = _scalar_transform_f.getValue();
970     //g_message("onTransformValueChanged: (%f, %f, %f, %f, %f, %f)\n",
971     //          a, b, c, d, e ,f);
972     */
974     setResponseSensitive(Gtk::RESPONSE_APPLY, true);
977 void
978 Transformation::onReplaceMatrixToggled()
980     Inkscape::Selection *selection = _getSelection();
982     if (!selection || selection->isEmpty())
983         return;
985     double a = _scalar_transform_a.getValue();
986     double b = _scalar_transform_b.getValue();
987     double c = _scalar_transform_c.getValue();
988     double d = _scalar_transform_d.getValue();
989     double e = _scalar_transform_e.getValue();
990     double f = _scalar_transform_f.getValue();
992     Geom::Matrix displayed (a, b, c, d, e, f);
993     Geom::Matrix current = SP_ITEM(selection->itemList()->data)->transform; // take from the first item in selection
995     Geom::Matrix new_displayed;
996     if (_check_replace_matrix.get_active()) {
997         new_displayed = current;
998     } else {
999         new_displayed = current.inverse() * displayed;
1000     }
1002     _scalar_transform_a.setValue(new_displayed[0]);
1003     _scalar_transform_b.setValue(new_displayed[1]);
1004     _scalar_transform_c.setValue(new_displayed[2]);
1005     _scalar_transform_d.setValue(new_displayed[3]);
1006     _scalar_transform_e.setValue(new_displayed[4]);
1007     _scalar_transform_f.setValue(new_displayed[5]);
1010 void
1011 Transformation::onScaleProportionalToggled()
1013     onScaleXValueChanged();
1017 void
1018 Transformation::onClear()
1020     int const page = _notebook.get_current_page();
1022     switch (page) {
1023     case PAGE_MOVE: {
1024         Inkscape::Selection *selection = _getSelection();
1025         if (!selection || selection->isEmpty() || _check_move_relative.get_active()) {
1026             _scalar_move_horizontal.setValue(0);
1027             _scalar_move_vertical.setValue(0);
1028         } else {
1029             Geom::OptRect bbox = selection->bounds();
1030             if (bbox) {
1031                 _scalar_move_horizontal.setValue(bbox->min()[Geom::X], "px");
1032                 _scalar_move_vertical.setValue(bbox->min()[Geom::Y], "px");
1033             }
1034         }
1035         break;
1036     }
1037     case PAGE_ROTATE: {
1038         _scalar_rotate.setValue(0);
1039         break;
1040     }
1041     case PAGE_SCALE: {
1042         _scalar_scale_horizontal.setValue(100, "%");
1043         _scalar_scale_vertical.setValue(100, "%");
1044         break;
1045     }
1046     case PAGE_SKEW: {
1047         _scalar_skew_horizontal.setValue(0);
1048         _scalar_skew_vertical.setValue(0);
1049         break;
1050     }
1051     case PAGE_TRANSFORM: {
1052         _scalar_transform_a.setValue(1);
1053         _scalar_transform_b.setValue(0);
1054         _scalar_transform_c.setValue(0);
1055         _scalar_transform_d.setValue(1);
1056         _scalar_transform_e.setValue(0);
1057         _scalar_transform_f.setValue(0);
1058         break;
1059     }
1060     }
1063 void
1064 Transformation::onApplySeparatelyToggled()
1066     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1067     prefs->setBool("/dialogs/transformation/applyseparately", _check_apply_separately.get_active());
1071 } // namespace Dialog
1072 } // namespace UI
1073 } // namespace Inkscape
1075 /*
1076   Local Variables:
1077   mode:c++
1078   c-file-style:"stroustrup"
1079   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1080   indent-tabs-mode:nil
1081   fill-column:99
1082   End:
1083 */
1084 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :