Code

Pot and Dutch translation update
[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  *
8  * Copyright (C) 2004, 2005 Authors
9  * Released under GNU GPL.  Read the file 'COPYING' for more information.
10  */
12 #ifdef HAVE_CONFIG_H
13 # include <config.h>
14 #endif
16 #include <gtkmm/stock.h>
17 #include <gtkmm/dialog.h>
19 #include "document.h"
20 #include "desktop-handles.h"
21 #include "transformation.h"
22 #include "align-and-distribute.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 "preferences.h"
29 #include "sp-namedview.h"
30 #include "sp-item-transform.h"
31 #include "macros.h"
32 #include "sp-item.h"
33 #include "util/glib-list-iterators.h"
35 namespace Inkscape {
36 namespace UI {
37 namespace Dialog {
39 void on_selection_changed(Inkscape::Application */*inkscape*/, Inkscape::Selection *selection, Transformation *daad)
40 {
41     int page = daad->getCurrentPage();
42     daad->updateSelection((Inkscape::UI::Dialog::Transformation::PageType)page, selection);
43 }
45 void on_selection_modified( Inkscape::Application */*inkscape*/,
46                             Inkscape::Selection *selection,
47                             guint /*flags*/,
48                             Transformation *daad )
49 {
50     int page = daad->getCurrentPage();
51     daad->updateSelection((Inkscape::UI::Dialog::Transformation::PageType)page, selection);
52 }
54 /*########################################################################
55 # C O N S T R U C T O R
56 ########################################################################*/
58 /**
59  * Constructor for Transformation.  This does the initialization
60  * and layout of the dialog used for transforming SVG objects.  It
61  * consists of 5 pages for the 5 operations it handles:
62  * 'Move' allows x,y translation of SVG objects
63  * 'Scale' allows linear resizing of SVG objects
64  * 'Rotate' allows rotating SVG objects by a degree
65  * 'Skew' allows skewing SVG objects
66  * 'Matrix' allows applying a generic affine transform on SVG objects,
67  *     with the user specifying the 6 degrees of freedom manually.
68  *
69  * The dialog is implemented as a Gtk::Notebook with five pages.
70  * The pages are implemented using Inkscape's NotebookPage which
71  * is used to help make sure all of Inkscape's notebooks follow
72  * the same style.  We then populate the pages with our widgets,
73  * we use the ScalarUnit class for this.
74  *
75  */
76 Transformation::Transformation()
77     : UI::Widget::Panel ("", "/dialogs/transformation", SP_VERB_DIALOG_TRANSFORM),
78       _page_move              (4, 2),
79       _page_scale             (4, 2),
80       _page_rotate            (4, 2),
81       _page_skew              (4, 2),
82       _page_transform         (3, 3),
83       _scalar_move_horizontal (_("_Horizontal:"), _("Horizontal displacement (relative) or position (absolute)"), UNIT_TYPE_LINEAR,
84                                "", "transform-move-horizontal", &_units_move),
85       _scalar_move_vertical   (_("_Vertical:"),  _("Vertical displacement (relative) or position (absolute)"), UNIT_TYPE_LINEAR,
86                                "", "transform-move-vertical", &_units_move),
87       _scalar_scale_horizontal(_("_Width:"), _("Horizontal size (absolute or percentage of current)"), UNIT_TYPE_DIMENSIONLESS,
88                                "", "transform-scale-horizontal", &_units_scale),
89       _scalar_scale_vertical  (_("_Height:"),  _("Vertical size (absolute or percentage of current)"), UNIT_TYPE_DIMENSIONLESS,
90                                "", "transform-scale-vertical", &_units_scale),
91       _scalar_rotate          (_("A_ngle:"), _("Rotation angle (positive = counterclockwise)"), UNIT_TYPE_RADIAL,
92                                "", "transform-rotate", &_units_rotate),
93       _scalar_skew_horizontal (_("_Horizontal:"), _("Horizontal skew angle (positive = counterclockwise), or absolute displacement, or percentage displacement"), UNIT_TYPE_LINEAR,
94                                "", "transform-skew-horizontal", &_units_skew),
95       _scalar_skew_vertical   (_("_Vertical:"),  _("Vertical skew angle (positive = counterclockwise), or absolute displacement, or percentage displacement"),  UNIT_TYPE_LINEAR,
96                                "", "transform-skew-vertical", &_units_skew),
98       _scalar_transform_a     ("_A:", _("Transformation matrix element A")),
99       _scalar_transform_b     ("_B:", _("Transformation matrix element B")),
100       _scalar_transform_c     ("_C:", _("Transformation matrix element C")),
101       _scalar_transform_d     ("_D:", _("Transformation matrix element D")),
102       _scalar_transform_e     ("_E:", _("Transformation matrix element E")),
103       _scalar_transform_f     ("_F:", _("Transformation matrix element F")),
105       _check_move_relative    (_("Rela_tive move"), _("Add the specified relative displacement to the current position; otherwise, edit the current absolute position directly")),
106       _check_scale_proportional (_("Scale proportionally"), _("Preserve the width/height ratio of the scaled objects")),
107       _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")),
108       _check_replace_matrix    (_("Edit c_urrent matrix"), _("Edit the current transform= matrix; otherwise, post-multiply transform= by this matrix"))
111     Gtk::Box *contents = _getContents();
113     contents->set_spacing(0);
115     // Notebook for individual transformations
116     contents->pack_start(_notebook, true, true);
118     _notebook.append_page(_page_move, _("_Move"), true);
119     layoutPageMove();
121     _notebook.append_page(_page_scale, _("_Scale"), true);
122     layoutPageScale();
124     _notebook.append_page(_page_rotate, _("_Rotate"), true);
125     layoutPageRotate();
127     _notebook.append_page(_page_skew, _("Ske_w"), true);
128     layoutPageSkew();
130     _notebook.append_page(_page_transform, _("Matri_x"), true);
131     layoutPageTransform();
133     _notebook.signal_switch_page().connect(sigc::mem_fun(*this, &Transformation::onSwitchPage));
135     // Apply separately
136     contents->pack_start(_check_apply_separately, true, true);
137     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
138     _check_apply_separately.set_active(prefs->getBool("/dialogs/transformation/applyseparately"));
139     _check_apply_separately.signal_toggled().connect(sigc::mem_fun(*this, &Transformation::onApplySeparatelyToggled));
141     // make sure all spinbuttons activate Apply on pressing Enter
142       ((Gtk::Entry *) (_scalar_move_horizontal.getWidget()))->set_activates_default(true);
143       ((Gtk::Entry *) (_scalar_move_vertical.getWidget()))->set_activates_default(true);
144       ((Gtk::Entry *) (_scalar_scale_horizontal.getWidget()))->set_activates_default(true);
145       ((Gtk::Entry *) (_scalar_scale_vertical.getWidget()))->set_activates_default(true);
146       ((Gtk::Entry *) (_scalar_rotate.getWidget()))->set_activates_default(true);
147       ((Gtk::Entry *) (_scalar_skew_horizontal.getWidget()))->set_activates_default(true);
148       ((Gtk::Entry *) (_scalar_skew_vertical.getWidget()))->set_activates_default(true);
150     updateSelection(PAGE_MOVE, _getSelection());
152     resetButton = addResponseButton(Gtk::Stock::CLEAR, 0);
153     if (resetButton) {
154         _tooltips.set_tip((*resetButton), _("Reset the values on the current tab to defaults"));
155         resetButton->set_sensitive(true);
156         resetButton->signal_clicked().connect(sigc::mem_fun(*this, &Transformation::onClear));
157     }
159     applyButton = addResponseButton(Gtk::Stock::APPLY, Gtk::RESPONSE_APPLY);
160     if (applyButton) {
161         _tooltips.set_tip((*applyButton), _("Apply transformation to selection"));
162         applyButton->set_sensitive(false);
163     }
165     // Connect to the global selection changed & modified signals
166     g_signal_connect (G_OBJECT (INKSCAPE), "change_selection", G_CALLBACK (on_selection_changed), this);
167     g_signal_connect (G_OBJECT (INKSCAPE), "modify_selection", G_CALLBACK (on_selection_modified), this);
169     show_all_children();
172 Transformation::~Transformation()
174     sp_signal_disconnect_by_data (G_OBJECT (INKSCAPE), this);
178 /*########################################################################
179 # U T I L I T Y
180 ########################################################################*/
182 void
183 Transformation::presentPage(Transformation::PageType page)
185     _notebook.set_current_page(page);
186     show();
187     present();
193 /*########################################################################
194 # S E T U P   L A Y O U T
195 ########################################################################*/
198 void
199 Transformation::layoutPageMove()
201     _units_move.setUnitType(UNIT_TYPE_LINEAR);
202     
203     // Setting default unit to document unit
204     SPDesktop *dt = getDesktop();
205     SPNamedView *nv = sp_desktop_namedview(dt);
206     if (nv->doc_units) {
207         _units_move.setUnit(nv->doc_units->abbr);
208     }
209     
210     _scalar_move_horizontal.initScalar(-1e6, 1e6);
211     _scalar_move_horizontal.setDigits(3);
212     _scalar_move_horizontal.setIncrements(0.1, 1.0);
214     _scalar_move_vertical.initScalar(-1e6, 1e6);
215     _scalar_move_vertical.setDigits(3);
216     _scalar_move_vertical.setIncrements(0.1, 1.0);
218     //_scalar_move_vertical.set_label_image( INKSCAPE_STOCK_ARROWS_HOR );
219     _page_move.table()
220         .attach(_scalar_move_horizontal, 0, 2, 0, 1, Gtk::FILL, Gtk::SHRINK);
222     _page_move.table()
223         .attach(_units_move, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK);
225     _scalar_move_horizontal.signal_value_changed()
226         .connect(sigc::mem_fun(*this, &Transformation::onMoveValueChanged));
228     //_scalar_move_vertical.set_label_image( INKSCAPE_STOCK_ARROWS_VER );
229     _page_move.table()
230         .attach(_scalar_move_vertical, 0, 2, 1, 2, Gtk::FILL, Gtk::SHRINK);
232     _scalar_move_vertical.signal_value_changed()
233         .connect(sigc::mem_fun(*this, &Transformation::onMoveValueChanged));
235     // Relative moves
236     _page_move.table()
237         .attach(_check_move_relative, 0, 2, 2, 3, Gtk::FILL, Gtk::SHRINK);
238     _check_move_relative.set_active(true);
239     _check_move_relative.signal_toggled()
240         .connect(sigc::mem_fun(*this, &Transformation::onMoveRelativeToggled));
243 void
244 Transformation::layoutPageScale()
246     _units_scale.setUnitType(UNIT_TYPE_DIMENSIONLESS);
247     _units_scale.setUnitType(UNIT_TYPE_LINEAR);
249     _scalar_scale_horizontal.initScalar(-1e6, 1e6);
250     _scalar_scale_horizontal.setValue(100.0, "%");
251     _scalar_scale_horizontal.setDigits(3);
252     _scalar_scale_horizontal.setIncrements(0.1, 1.0);
253     _scalar_scale_horizontal.setAbsoluteIsIncrement(true);
254     _scalar_scale_horizontal.setPercentageIsIncrement(true);
256     _scalar_scale_vertical.initScalar(-1e6, 1e6);
257     _scalar_scale_vertical.setValue(100.0, "%");
258     _scalar_scale_vertical.setDigits(3);
259     _scalar_scale_vertical.setIncrements(0.1, 1.0);
260     _scalar_scale_vertical.setAbsoluteIsIncrement(true);
261     _scalar_scale_vertical.setPercentageIsIncrement(true);
263     _page_scale.table()
264         .attach(_scalar_scale_horizontal, 0, 2, 0, 1, Gtk::FILL, Gtk::SHRINK);
265     _scalar_scale_horizontal.signal_value_changed()
266         .connect(sigc::mem_fun(*this, &Transformation::onScaleXValueChanged));
268     _page_scale.table()
269         .attach(_units_scale, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK);
271     _page_scale.table()
272         .attach(_scalar_scale_vertical, 0, 2, 1, 2, Gtk::FILL, Gtk::SHRINK);
273     _scalar_scale_vertical.signal_value_changed()
274         .connect(sigc::mem_fun(*this, &Transformation::onScaleYValueChanged));
276     _page_scale.table()
277         .attach(_check_scale_proportional, 0, 2, 2, 3, Gtk::FILL, Gtk::SHRINK);
278     _check_scale_proportional.set_active(false);
279     _check_scale_proportional.signal_toggled()
280         .connect(sigc::mem_fun(*this, &Transformation::onScaleProportionalToggled));
282     //TODO: add a widget for selecting the fixed point in scaling, or honour rotation center?
285 void
286 Transformation::layoutPageRotate()
288     _units_rotate.setUnitType(UNIT_TYPE_RADIAL);
290     _scalar_rotate.initScalar(-360.0, 360.0);
291     _scalar_rotate.setDigits(3);
292     _scalar_rotate.setIncrements(0.1, 1.0);
294     _page_rotate.table()
295         .attach(_scalar_rotate, 0, 2, 0, 1, Gtk::FILL, Gtk::SHRINK);
297     _page_rotate.table()
298         .attach(_units_rotate, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK);
300     _scalar_rotate.signal_value_changed()
301         .connect(sigc::mem_fun(*this, &Transformation::onRotateValueChanged));
303     //TODO: honour rotation center?
306 void
307 Transformation::layoutPageSkew()
309     _units_skew.setUnitType(UNIT_TYPE_LINEAR);
310     _units_skew.setUnitType(UNIT_TYPE_DIMENSIONLESS);
311     _units_skew.setUnitType(UNIT_TYPE_RADIAL);
313     _scalar_skew_horizontal.initScalar(-1e6, 1e6);
314     _scalar_skew_horizontal.setDigits(3);
315     _scalar_skew_horizontal.setIncrements(0.1, 1.0);
317     _scalar_skew_vertical.initScalar(-1e6, 1e6);
318     _scalar_skew_vertical.setDigits(3);
319     _scalar_skew_vertical.setIncrements(0.1, 1.0);
321     _page_skew.table()
322         .attach(_scalar_skew_horizontal, 0, 2, 0, 1, Gtk::FILL, Gtk::SHRINK);
323     _scalar_skew_horizontal.signal_value_changed()
324         .connect(sigc::mem_fun(*this, &Transformation::onSkewValueChanged));
326     _page_skew.table()
327         .attach(_units_skew, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK);
329     _page_skew.table()
330         .attach(_scalar_skew_vertical, 0, 2, 1, 2, Gtk::FILL, Gtk::SHRINK);
331     _scalar_skew_vertical.signal_value_changed()
332         .connect(sigc::mem_fun(*this, &Transformation::onSkewValueChanged));
334     //TODO: honour rotation center?
339 void
340 Transformation::layoutPageTransform()
342     _scalar_transform_a.setWidgetSizeRequest(65, -1);
343     _scalar_transform_a.setRange(-1e10, 1e10);
344     _scalar_transform_a.setDigits(3);
345     _scalar_transform_a.setIncrements(0.1, 1.0);
346     _scalar_transform_a.setValue(1.0);
347     _page_transform.table()
348         .attach(_scalar_transform_a, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK);
349     _scalar_transform_a.signal_value_changed()
350         .connect(sigc::mem_fun(*this, &Transformation::onTransformValueChanged));
353     _scalar_transform_b.setWidgetSizeRequest(65, -1);
354     _scalar_transform_b.setRange(-1e10, 1e10);
355     _scalar_transform_b.setDigits(3);
356     _scalar_transform_b.setIncrements(0.1, 1.0);
357     _scalar_transform_b.setValue(0.0);
358     _page_transform.table()
359         .attach(_scalar_transform_b, 0, 1, 1, 2, Gtk::SHRINK, Gtk::SHRINK);
360     _scalar_transform_b.signal_value_changed()
361         .connect(sigc::mem_fun(*this, &Transformation::onTransformValueChanged));
364     _scalar_transform_c.setWidgetSizeRequest(65, -1);
365     _scalar_transform_c.setRange(-1e10, 1e10);
366     _scalar_transform_c.setDigits(3);
367     _scalar_transform_c.setIncrements(0.1, 1.0);
368     _scalar_transform_c.setValue(0.0);
369     _page_transform.table()
370         .attach(_scalar_transform_c, 1, 2, 0, 1, Gtk::SHRINK, Gtk::SHRINK);
371     _scalar_transform_c.signal_value_changed()
372         .connect(sigc::mem_fun(*this, &Transformation::onTransformValueChanged));
375     _scalar_transform_d.setWidgetSizeRequest(65, -1);
376     _scalar_transform_d.setRange(-1e10, 1e10);
377     _scalar_transform_d.setDigits(3);
378     _scalar_transform_d.setIncrements(0.1, 1.0);
379     _scalar_transform_d.setValue(1.0);
380     _page_transform.table()
381         .attach(_scalar_transform_d, 1, 2, 1, 2, Gtk::SHRINK, Gtk::SHRINK);
382     _scalar_transform_d.signal_value_changed()
383         .connect(sigc::mem_fun(*this, &Transformation::onTransformValueChanged));
386     _scalar_transform_e.setWidgetSizeRequest(65, -1);
387     _scalar_transform_e.setRange(-1e10, 1e10);
388     _scalar_transform_e.setDigits(3);
389     _scalar_transform_e.setIncrements(0.1, 1.0);
390     _scalar_transform_e.setValue(0.0);
391     _page_transform.table()
392         .attach(_scalar_transform_e, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK);
393     _scalar_transform_e.signal_value_changed()
394         .connect(sigc::mem_fun(*this, &Transformation::onTransformValueChanged));
397     _scalar_transform_f.setWidgetSizeRequest(65, -1);
398     _scalar_transform_f.setRange(-1e10, 1e10);
399     _scalar_transform_f.setDigits(3);
400     _scalar_transform_f.setIncrements(0.1, 1.0);
401     _scalar_transform_f.setValue(0.0);
402     _page_transform.table()
403         .attach(_scalar_transform_f, 2, 3, 1, 2, Gtk::SHRINK, Gtk::SHRINK);
404     _scalar_transform_f.signal_value_changed()
405         .connect(sigc::mem_fun(*this, &Transformation::onTransformValueChanged));
407     // Edit existing matrix
408     _page_transform.table()
409         .attach(_check_replace_matrix, 0, 2, 2, 3, Gtk::FILL, Gtk::SHRINK);
410     _check_replace_matrix.set_active(false);
411     _check_replace_matrix.signal_toggled()
412         .connect(sigc::mem_fun(*this, &Transformation::onReplaceMatrixToggled));
416 /*########################################################################
417 # U P D A T E
418 ########################################################################*/
420 void
421 Transformation::updateSelection(PageType page, Inkscape::Selection *selection)
423     if (!selection || selection->isEmpty())
424         return;
426     switch (page) {
427         case PAGE_MOVE: {
428             updatePageMove(selection);
429             break;
430         }
431         case PAGE_SCALE: {
432             updatePageScale(selection);
433             break;
434         }
435         case PAGE_ROTATE: {
436             updatePageRotate(selection);
437             break;
438         }
439         case PAGE_SKEW: {
440             updatePageSkew(selection);
441             break;
442         }
443         case PAGE_TRANSFORM: {
444             updatePageTransform(selection);
445             break;
446         }
447         case PAGE_QTY: {
448             break;
449         }
450     }
452     setResponseSensitive(Gtk::RESPONSE_APPLY,
453                          selection && !selection->isEmpty());
456 void
457 Transformation::onSwitchPage(GtkNotebookPage */*page*/,
458                                    guint pagenum)
460     updateSelection((PageType)pagenum, sp_desktop_selection(getDesktop()));
464 void
465 Transformation::updatePageMove(Inkscape::Selection *selection)
467     if (selection && !selection->isEmpty()) {
468         if (!_check_move_relative.get_active()) {
469             Geom::OptRect bbox = selection->bounds();
470             if (bbox) {
471                 double x = bbox->min()[Geom::X];
472                 double y = bbox->min()[Geom::Y];
474                 double conversion = _units_move.getConversion("px");
475                 _scalar_move_horizontal.setValue(x / conversion);
476                 _scalar_move_vertical.setValue(y / conversion);
477             }
478         } else {
479             // do nothing, so you can apply the same relative move to many objects in turn
480         }
481         _page_move.set_sensitive(true);
482     } else {
483         _page_move.set_sensitive(false);
484     }
487 void
488 Transformation::updatePageScale(Inkscape::Selection *selection)
490     if (selection && !selection->isEmpty()) {
491         Geom::OptRect bbox = selection->bounds();
492         if (bbox) {
493             double w = bbox->dimensions()[Geom::X];
494             double h = bbox->dimensions()[Geom::Y];
495             _scalar_scale_horizontal.setHundredPercent(w);
496             _scalar_scale_vertical.setHundredPercent(h);
497             onScaleXValueChanged(); // to update x/y proportionality if switch is on
498             _page_scale.set_sensitive(true);
499         } else {
500             _page_scale.set_sensitive(false);
501         }
502     } else {
503         _page_scale.set_sensitive(false);
504     }
507 void
508 Transformation::updatePageRotate(Inkscape::Selection *selection)
510     if (selection && !selection->isEmpty()) {
511         _page_rotate.set_sensitive(true);
512     } else {
513         _page_rotate.set_sensitive(false);
514     }
517 void
518 Transformation::updatePageSkew(Inkscape::Selection *selection)
520     if (selection && !selection->isEmpty()) {
521         Geom::OptRect bbox = selection->bounds();
522         if (bbox) {
523             double w = bbox->dimensions()[Geom::X];
524             double h = bbox->dimensions()[Geom::Y];
525             _scalar_skew_vertical.setHundredPercent(w);
526             _scalar_skew_horizontal.setHundredPercent(h);
527             _page_skew.set_sensitive(true);
528         } else {
529             _page_skew.set_sensitive(false);
530         }
531     } else {
532         _page_skew.set_sensitive(false);
533     }
536 void
537 Transformation::updatePageTransform(Inkscape::Selection *selection)
539     if (selection && !selection->isEmpty()) {
540         if (_check_replace_matrix.get_active()) {
541             Geom::Matrix current (SP_ITEM(selection->itemList()->data)->transform); // take from the first item in selection
543             Geom::Matrix new_displayed = current;
545             _scalar_transform_a.setValue(new_displayed[0]);
546             _scalar_transform_b.setValue(new_displayed[1]);
547             _scalar_transform_c.setValue(new_displayed[2]);
548             _scalar_transform_d.setValue(new_displayed[3]);
549             _scalar_transform_e.setValue(new_displayed[4]);
550             _scalar_transform_f.setValue(new_displayed[5]);
551         } else {
552             // do nothing, so you can apply the same matrix to many objects in turn
553         }
554         _page_transform.set_sensitive(true);
555     } else {
556         _page_transform.set_sensitive(false);
557     }
564 /*########################################################################
565 # A P P L Y
566 ########################################################################*/
570 void
571 Transformation::_apply()
573     Inkscape::Selection * const selection = _getSelection();
574     if (!selection || selection->isEmpty())
575         return;
577     int const page = _notebook.get_current_page();
579     switch (page) {
580         case PAGE_MOVE: {
581             applyPageMove(selection);
582             break;
583         }
584         case PAGE_ROTATE: {
585             applyPageRotate(selection);
586             break;
587         }
588         case PAGE_SCALE: {
589             applyPageScale(selection);
590             break;
591         }
592         case PAGE_SKEW: {
593             applyPageSkew(selection);
594             break;
595         }
596         case PAGE_TRANSFORM: {
597             applyPageTransform(selection);
598             break;
599         }
600     }
602     //Let's play with never turning this off
603     //setResponseSensitive(Gtk::RESPONSE_APPLY, false);
606 void
607 Transformation::applyPageMove(Inkscape::Selection *selection)
609     double x = _scalar_move_horizontal.getValue("px");
610     double y = _scalar_move_vertical.getValue("px");
612     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
613     if (!prefs->getBool("/dialogs/transformation/applyseparately")) {
614         // move selection as a whole
615         if (_check_move_relative.get_active()) {
616             sp_selection_move_relative(selection, x, y);
617         } else {
618             Geom::OptRect bbox = selection->bounds();
619             if (bbox) {
620                 sp_selection_move_relative(selection,
621                                            x - bbox->min()[Geom::X], y - bbox->min()[Geom::Y]);
622             }
623         }
624     } else {
626         if (_check_move_relative.get_active()) {
627             // shift each object relatively to the previous one
628             using Inkscape::Util::GSListConstIterator;
629             std::list<SPItem *> selected;
630             selected.insert<GSListConstIterator<SPItem *> >(selected.end(), selection->itemList(), NULL);
631             if (selected.empty()) return;
633             if (fabs(x) > 1e-6) {
634                 std::vector< BBoxSort  > sorted;
635                 for (std::list<SPItem *>::iterator it(selected.begin());
636                      it != selected.end();
637                      ++it)
638                 {
639                     Geom::OptRect bbox = sp_item_bbox_desktop(*it);
640                     if (bbox) {
641                         sorted.push_back(BBoxSort(*it, *bbox, Geom::X, x > 0? 1. : 0., x > 0? 0. : 1.));
642                     }
643                 }
644                 //sort bbox by anchors
645                 std::sort(sorted.begin(), sorted.end());
647                 double move = x;
648                 for ( std::vector<BBoxSort> ::iterator it (sorted.begin());
649                       it < sorted.end();
650                       it ++ )
651                 {
652                     sp_item_move_rel(it->item, Geom::Translate(move, 0));
653                     // move each next object by x relative to previous
654                     move += x;
655                 }
656             }
657             if (fabs(y) > 1e-6) {
658                 std::vector< BBoxSort  > sorted;
659                 for (std::list<SPItem *>::iterator it(selected.begin());
660                      it != selected.end();
661                      ++it)
662                 {
663                     Geom::OptRect bbox = sp_item_bbox_desktop(*it);
664                     if (bbox) {
665                         sorted.push_back(BBoxSort(*it, *bbox, Geom::Y, y > 0? 1. : 0., y > 0? 0. : 1.));
666                     }
667                 }
668                 //sort bbox by anchors
669                 std::sort(sorted.begin(), sorted.end());
671                 double move = y;
672                 for ( std::vector<BBoxSort> ::iterator it (sorted.begin());
673                       it < sorted.end();
674                       it ++ )
675                 {
676                     sp_item_move_rel(it->item, Geom::Translate(0, move));
677                     // move each next object by x relative to previous
678                     move += y;
679                 }
680             }
681         } else {
682             Geom::OptRect bbox = selection->bounds();
683             if (bbox) {
684                 sp_selection_move_relative(selection,
685                                            x - bbox->min()[Geom::X], y - bbox->min()[Geom::Y]);
686             }
687         }
688     }
690     sp_document_done ( sp_desktop_document (selection->desktop()) , SP_VERB_DIALOG_TRANSFORM,
691                        _("Move"));
694 void
695 Transformation::applyPageScale(Inkscape::Selection *selection)
697     double scaleX = _scalar_scale_horizontal.getValue("px");
698     double scaleY = _scalar_scale_vertical.getValue("px");
700     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
701     if (prefs->getBool("/dialogs/transformation/applyseparately")) {
702         for (GSList const *l = selection->itemList(); l != NULL; l = l->next) {
703             SPItem *item = SP_ITEM(l->data);
704             Geom::Scale scale (0,0);
705             // the values are increments!
706             if (_units_scale.isAbsolute()) {
707                 Geom::OptRect bbox(sp_item_bbox_desktop(item));
708                 if (bbox) {
709                     double new_width = scaleX;
710                     if (fabs(new_width) < 1e-6) new_width = 1e-6; // not 0, as this would result in a nasty no-bbox object
711                     double new_height = scaleY;
712                     if (fabs(new_height) < 1e-6) new_height = 1e-6;
713                     scale = Geom::Scale(new_width / bbox->dimensions()[Geom::X], new_height / bbox->dimensions()[Geom::Y]);
714                 }
715             } else {
716                 double new_width = scaleX;
717                 if (fabs(new_width) < 1e-6) new_width = 1e-6;
718                 double new_height = scaleY;
719                 if (fabs(new_height) < 1e-6) new_height = 1e-6;
720                 scale = Geom::Scale(new_width / 100.0, new_height / 100.0);
721             }
722             sp_item_scale_rel (item, scale);
723         }
724     } else {
725         Geom::OptRect bbox(selection->bounds());
726         if (bbox) {
727             Geom::Point center(bbox->midpoint()); // use rotation center?
728             Geom::Scale scale (0,0);
729             // the values are increments!
730             if (_units_scale.isAbsolute()) {
731                 double new_width = scaleX;
732                 if (fabs(new_width) < 1e-6) new_width = 1e-6;
733                 double new_height = scaleY;
734                 if (fabs(new_height) < 1e-6) new_height = 1e-6;
735                 scale = Geom::Scale(new_width / bbox->dimensions()[Geom::X], new_height / bbox->dimensions()[Geom::Y]);
736             } else {
737                 double new_width = scaleX;
738                 if (fabs(new_width) < 1e-6) new_width = 1e-6;
739                 double new_height = scaleY;
740                 if (fabs(new_height) < 1e-6) new_height = 1e-6;
741                 scale = Geom::Scale(new_width / 100.0, new_height / 100.0);
742             }
743             sp_selection_scale_relative(selection, center, scale);
744         }
745     }
747     sp_document_done(sp_desktop_document(selection->desktop()), SP_VERB_DIALOG_TRANSFORM,
748                      _("Scale"));
751 void
752 Transformation::applyPageRotate(Inkscape::Selection *selection)
754     double angle = _scalar_rotate.getValue("deg");
756     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
757     if (prefs->getBool("/dialogs/transformation/applyseparately")) {
758         for (GSList const *l = selection->itemList(); l != NULL; l = l->next) {
759             SPItem *item = SP_ITEM(l->data);
760             sp_item_rotate_rel(item, Geom::Rotate (angle*M_PI/180.0));
761         }
762     } else {
763         boost::optional<Geom::Point> center = selection->center();
764         if (center) {
765             sp_selection_rotate_relative(selection, *center, angle);
766         }
767     }
769     sp_document_done(sp_desktop_document(selection->desktop()), SP_VERB_DIALOG_TRANSFORM,
770                      _("Rotate"));
773 void
774 Transformation::applyPageSkew(Inkscape::Selection *selection)
776     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
777     if (prefs->getBool("/dialogs/transformation/applyseparately")) {
778         for (GSList const *l = selection->itemList(); l != NULL; l = l->next) {
779             SPItem *item = SP_ITEM(l->data);
781             if (!_units_skew.isAbsolute()) { // percentage
782                 double skewX = _scalar_skew_horizontal.getValue("%");
783                 double skewY = _scalar_skew_vertical.getValue("%");
784                 sp_item_skew_rel (item, 0.01*skewX, 0.01*skewY);
785             } else if (_units_skew.isRadial()) { //deg or rad
786                 double angleX = _scalar_skew_horizontal.getValue("rad");
787                 double angleY = _scalar_skew_vertical.getValue("rad");
788                 double skewX = tan(-angleX);
789                 double skewY = tan(angleY);
790                 sp_item_skew_rel (item, skewX, skewY);
791             } else { // absolute displacement
792                 double skewX = _scalar_skew_horizontal.getValue("px");
793                 double skewY = _scalar_skew_vertical.getValue("px");
794                 Geom::OptRect bbox(sp_item_bbox_desktop(item));
795                 if (bbox) {
796                     double width = bbox->dimensions()[Geom::X];
797                     double height = bbox->dimensions()[Geom::Y];
798                     sp_item_skew_rel (item, skewX/height, skewY/width);
799                 }
800             }
801         }
802     } else { // transform whole selection
803         Geom::OptRect bbox = selection->bounds();
804         boost::optional<Geom::Point> center = selection->center();
806         if ( bbox && center ) {
807             double width  = bbox->dimensions()[Geom::X];
808             double height = bbox->dimensions()[Geom::Y];
810             if (!_units_skew.isAbsolute()) { // percentage
811                 double skewX = _scalar_skew_horizontal.getValue("%");
812                 double skewY = _scalar_skew_vertical.getValue("%");
813                 sp_selection_skew_relative(selection, *center, 0.01*skewX, 0.01*skewY);
814             } else if (_units_skew.isRadial()) { //deg or rad
815                 double angleX = _scalar_skew_horizontal.getValue("rad");
816                 double angleY = _scalar_skew_vertical.getValue("rad");
817                 double skewX = tan(-angleX);
818                 double skewY = tan(angleY);
819                 sp_selection_skew_relative(selection, *center, skewX, skewY);
820             } else { // absolute displacement
821                 double skewX = _scalar_skew_horizontal.getValue("px");
822                 double skewY = _scalar_skew_vertical.getValue("px");
823                 sp_selection_skew_relative(selection, *center, skewX/height, skewY/width);
824             }
825         }
826     }
828     sp_document_done(sp_desktop_document(selection->desktop()), SP_VERB_DIALOG_TRANSFORM,
829                      _("Skew"));
833 void
834 Transformation::applyPageTransform(Inkscape::Selection *selection)
836     double a = _scalar_transform_a.getValue();
837     double b = _scalar_transform_b.getValue();
838     double c = _scalar_transform_c.getValue();
839     double d = _scalar_transform_d.getValue();
840     double e = _scalar_transform_e.getValue();
841     double f = _scalar_transform_f.getValue();
843     Geom::Matrix displayed(a, b, c, d, e, f);
845     if (_check_replace_matrix.get_active()) {
846         for (GSList const *l = selection->itemList(); l != NULL; l = l->next) {
847             SPItem *item = SP_ITEM(l->data);
848             sp_item_set_item_transform(item, displayed);
849             SP_OBJECT(item)->updateRepr();
850         }
851     } else {
852         sp_selection_apply_affine(selection, displayed); // post-multiply each object's transform
853     }
855     sp_document_done(sp_desktop_document(selection->desktop()), SP_VERB_DIALOG_TRANSFORM,
856                      _("Edit transformation matrix"));
863 /*########################################################################
864 # V A L U E - C H A N G E D    C A L L B A C K S
865 ########################################################################*/
867 void
868 Transformation::onMoveValueChanged()
870     setResponseSensitive(Gtk::RESPONSE_APPLY, true);
873 void
874 Transformation::onMoveRelativeToggled()
876     Inkscape::Selection *selection = _getSelection();
878     if (!selection || selection->isEmpty())
879         return;
881     double x = _scalar_move_horizontal.getValue("px");
882     double y = _scalar_move_vertical.getValue("px");
884     double conversion = _units_move.getConversion("px");
886     //g_message("onMoveRelativeToggled: %f, %f px\n", x, y);
888     Geom::OptRect bbox = selection->bounds();
890     if (bbox) {
891         if (_check_move_relative.get_active()) {
892             // From absolute to relative
893             _scalar_move_horizontal.setValue((x - bbox->min()[Geom::X]) / conversion);
894             _scalar_move_vertical.setValue((  y - bbox->min()[Geom::Y]) / conversion);
895         } else {
896             // From relative to absolute
897             _scalar_move_horizontal.setValue((bbox->min()[Geom::X] + x) / conversion);
898             _scalar_move_vertical.setValue((  bbox->min()[Geom::Y] + y) / conversion);
899         }
900     }
902     setResponseSensitive(Gtk::RESPONSE_APPLY, true);
905 void
906 Transformation::onScaleXValueChanged()
908     if (_scalar_scale_horizontal.setProgrammatically) {
909         _scalar_scale_horizontal.setProgrammatically = false;
910         return;
911     }
913     setResponseSensitive(Gtk::RESPONSE_APPLY, true);
915     if (_check_scale_proportional.get_active()) {
916         if (!_units_scale.isAbsolute()) { // percentage, just copy over
917             _scalar_scale_vertical.setValue(_scalar_scale_horizontal.getValue("%"));
918         } else {
919             double scaleXPercentage = _scalar_scale_horizontal.getAsPercentage();
920             _scalar_scale_vertical.setFromPercentage (scaleXPercentage);
921         }
922     }
925 void
926 Transformation::onScaleYValueChanged()
928     if (_scalar_scale_vertical.setProgrammatically) {
929         _scalar_scale_vertical.setProgrammatically = false;
930         return;
931     }
933     setResponseSensitive(Gtk::RESPONSE_APPLY, true);
935     if (_check_scale_proportional.get_active()) {
936         if (!_units_scale.isAbsolute()) { // percentage, just copy over
937             _scalar_scale_horizontal.setValue(_scalar_scale_vertical.getValue("%"));
938         } else {
939             double scaleYPercentage = _scalar_scale_vertical.getAsPercentage();
940             _scalar_scale_horizontal.setFromPercentage (scaleYPercentage);
941         }
942     }
945 void
946 Transformation::onRotateValueChanged()
948     setResponseSensitive(Gtk::RESPONSE_APPLY, true);
951 void
952 Transformation::onSkewValueChanged()
954     setResponseSensitive(Gtk::RESPONSE_APPLY, true);
957 void
958 Transformation::onTransformValueChanged()
961     /*
962     double a = _scalar_transform_a.getValue();
963     double b = _scalar_transform_b.getValue();
964     double c = _scalar_transform_c.getValue();
965     double d = _scalar_transform_d.getValue();
966     double e = _scalar_transform_e.getValue();
967     double f = _scalar_transform_f.getValue();
969     //g_message("onTransformValueChanged: (%f, %f, %f, %f, %f, %f)\n",
970     //          a, b, c, d, e ,f);
971     */
973     setResponseSensitive(Gtk::RESPONSE_APPLY, true);
976 void
977 Transformation::onReplaceMatrixToggled()
979     Inkscape::Selection *selection = _getSelection();
981     if (!selection || selection->isEmpty())
982         return;
984     double a = _scalar_transform_a.getValue();
985     double b = _scalar_transform_b.getValue();
986     double c = _scalar_transform_c.getValue();
987     double d = _scalar_transform_d.getValue();
988     double e = _scalar_transform_e.getValue();
989     double f = _scalar_transform_f.getValue();
991     Geom::Matrix displayed (a, b, c, d, e, f);
992     Geom::Matrix current = SP_ITEM(selection->itemList()->data)->transform; // take from the first item in selection
994     Geom::Matrix new_displayed;
995     if (_check_replace_matrix.get_active()) {
996         new_displayed = current;
997     } else {
998         new_displayed = current.inverse() * displayed;
999     }
1001     _scalar_transform_a.setValue(new_displayed[0]);
1002     _scalar_transform_b.setValue(new_displayed[1]);
1003     _scalar_transform_c.setValue(new_displayed[2]);
1004     _scalar_transform_d.setValue(new_displayed[3]);
1005     _scalar_transform_e.setValue(new_displayed[4]);
1006     _scalar_transform_f.setValue(new_displayed[5]);
1009 void
1010 Transformation::onScaleProportionalToggled()
1012     onScaleXValueChanged();
1016 void
1017 Transformation::onClear()
1019     int const page = _notebook.get_current_page();
1021     switch (page) {
1022     case PAGE_MOVE: {
1023         Inkscape::Selection *selection = _getSelection();
1024         if (!selection || selection->isEmpty() || _check_move_relative.get_active()) {
1025             _scalar_move_horizontal.setValue(0);
1026             _scalar_move_vertical.setValue(0);
1027         } else {
1028             Geom::OptRect bbox = selection->bounds();
1029             if (bbox) {
1030                 _scalar_move_horizontal.setValue(bbox->min()[Geom::X], "px");
1031                 _scalar_move_vertical.setValue(bbox->min()[Geom::Y], "px");
1032             }
1033         }
1034         break;
1035     }
1036     case PAGE_ROTATE: {
1037         _scalar_rotate.setValue(0);
1038         break;
1039     }
1040     case PAGE_SCALE: {
1041         _scalar_scale_horizontal.setValue(100, "%");
1042         _scalar_scale_vertical.setValue(100, "%");
1043         break;
1044     }
1045     case PAGE_SKEW: {
1046         _scalar_skew_horizontal.setValue(0);
1047         _scalar_skew_vertical.setValue(0);
1048         break;
1049     }
1050     case PAGE_TRANSFORM: {
1051         _scalar_transform_a.setValue(1);
1052         _scalar_transform_b.setValue(0);
1053         _scalar_transform_c.setValue(0);
1054         _scalar_transform_d.setValue(1);
1055         _scalar_transform_e.setValue(0);
1056         _scalar_transform_f.setValue(0);
1057         break;
1058     }
1059     }
1062 void
1063 Transformation::onApplySeparatelyToggled()
1065     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1066     prefs->setBool("/dialogs/transformation/applyseparately", _check_apply_separately.get_active());
1070 } // namespace Dialog
1071 } // namespace UI
1072 } // namespace Inkscape
1074 /*
1075   Local Variables:
1076   mode:c++
1077   c-file-style:"stroustrup"
1078   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1079   indent-tabs-mode:nil
1080   fill-column:99
1081   End:
1082 */
1083 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :