Code

Switch selection bounds and center to use NR::Maybe, addressing most of the
[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>
19 #include "document.h"
20 #include "desktop-handles.h"
21 #include "transformation.h"
22 #include "libnr/nr-matrix-ops.h"
23 #include "inkscape.h"
24 #include "selection.h"
25 #include "selection-chemistry.h"
26 #include "verbs.h"
27 #include "prefs-utils.h"
28 #include "sp-item-transform.h"
29 #include "macros.h"
30 #include "sp-item.h"
32 namespace Inkscape {
33 namespace UI {
34 namespace Dialog {
36 void on_selection_changed(Inkscape::Application *inkscape, Inkscape::Selection *selection, Transformation *daad)
37 {
38     int page = daad->getCurrentPage();
39     daad->updateSelection((Inkscape::UI::Dialog::Transformation::PageType)page, selection);
40 }
42 void on_selection_modified ( Inkscape::Application *inkscape, 
43                                Inkscape::Selection *selection, 
44                                guint flags,
45                                Transformation *daad )
46 {
47     int page = daad->getCurrentPage();
48     daad->updateSelection((Inkscape::UI::Dialog::Transformation::PageType)page, selection);
49 }
51 /*########################################################################
52 # C O N S T R U C T O R
53 ########################################################################*/
55 /**
56  * Constructor for Transformation.  This does the initialization
57  * and layout of the dialog used for transforming SVG objects.  It
58  * consists of 5 pages for the 5 operations it handles:
59  * 'Move' allows x,y translation of SVG objects
60  * 'Scale' allows linear resizing of SVG objects
61  * 'Rotate' allows rotating SVG objects by a degree
62  * 'Skew' allows skewing SVG objects
63  * 'Matrix' allows applying a generic affine transform on SVG objects,
64  *     with the user specifying the 6 degrees of freedom manually.
65  *
66  * The dialog is implemented as a Gtk::Notebook with five pages.
67  * The pages are implemented using Inkscape's NotebookPage which
68  * is used to help make sure all of Inkscape's notebooks follow
69  * the same style.  We then populate the pages with our widgets,
70  * we use the ScalarUnit class for this.
71  *
72  */
73 Transformation::Transformation()
74     : Dialog ("dialogs.transformation", SP_VERB_DIALOG_TRANSFORM),
75       _page_move              (4, 2),
76       _page_scale             (4, 2),
77       _page_rotate            (4, 2),
78       _page_skew              (4, 2),
79       _page_transform         (3, 3),
80       _scalar_move_horizontal (_("_Horizontal"), _("Horizontal displacement (relative) or position (absolute)"), UNIT_TYPE_LINEAR, 
81                                "", "arrows_hor", &_units_move),
82       _scalar_move_vertical   (_("_Vertical"),  _("Vertical displacement (relative) or position (absolute)"), UNIT_TYPE_LINEAR, 
83                                "", "arrows_ver", &_units_move),
84       _scalar_scale_horizontal(_("_Width"), _("Horizontal size increment (absolute or percentage)"), UNIT_TYPE_DIMENSIONLESS, 
85                                "", "transform_scale_hor", &_units_scale),
86       _scalar_scale_vertical  (_("_Height"),  _("Vertical size increment (absolute or percentage)"), UNIT_TYPE_DIMENSIONLESS, 
87                                "", "transform_scale_ver", &_units_scale),
88       _scalar_rotate          (_("A_ngle"), _("Rotation angle (positive = counterclockwise)"), UNIT_TYPE_RADIAL, 
89                                "", "transform_rotate", &_units_rotate),
90       _scalar_skew_horizontal (_("_Horizontal"), _("Horizontal skew angle (positive = counterclockwise), or absolute displacement, or percentage displacement"), UNIT_TYPE_LINEAR, 
91                                "", "arrows_hor", &_units_skew),
92       _scalar_skew_vertical   (_("_Vertical"),  _("Vertical skew angle (positive = counterclockwise), or absolute displacement, or percentage displacement"),  UNIT_TYPE_LINEAR, 
93                                "", "arrows_ver", &_units_skew),
95       _scalar_transform_a     ("_A", _("Transformation matrix element A")),
96       _scalar_transform_b     ("_B", _("Transformation matrix element B")),
97       _scalar_transform_c     ("_C", _("Transformation matrix element C")),
98       _scalar_transform_d     ("_D", _("Transformation matrix element D")),
99       _scalar_transform_e     ("_E", _("Transformation matrix element E")),
100       _scalar_transform_f     ("_F", _("Transformation matrix element F")),
102       _check_move_relative    (_("Rela_tive move"), _("Add the specified relative displacement to the current position; otherwise, edit the current absolute position directly")),
103       _check_scale_proportional (_("Scale proportionally"), _("Preserve the width/height ratio of the scaled objects")),
104       _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")),
105       _check_replace_matrix    (_("Edit c_urrent matrix"), _("Edit the current transform= matrix; otherwise, post-multiply transform= by this matrix"))
108     // Top level vbox
109     Gtk::VBox *vbox = get_vbox();
110     vbox->set_spacing(0);
112     // Notebook for individual transformations
113     vbox->pack_start(_notebook, true, true);
115     _notebook.append_page(_page_move, _("_Move"), true);
116     layoutPageMove();
118     _notebook.append_page(_page_scale, _("_Scale"), true);
119     layoutPageScale();
121     _notebook.append_page(_page_rotate, _("_Rotate"), true);
122     layoutPageRotate();
124     _notebook.append_page(_page_skew, _("Ske_w"), true);
125     layoutPageSkew();
127     _notebook.append_page(_page_transform, _("Matri_x"), true);
128     layoutPageTransform();
130     _notebook.signal_switch_page().connect(sigc::mem_fun(*this, &Transformation::onSwitchPage));
132     // Apply separately
133     vbox->pack_start(_check_apply_separately, true, true);
134     _check_apply_separately.set_active(prefs_get_int_attribute_limited ("dialogs.transformation", "applyseparately", 0, 0, 1));
135     _check_apply_separately.signal_toggled().connect(sigc::mem_fun(*this, &Transformation::onApplySeparatelyToggled));
137     // make sure all spinbuttons activate Apply on pressing Enter
138       ((Gtk::Entry *) (_scalar_move_horizontal.getWidget()))->set_activates_default(true);
139       ((Gtk::Entry *) (_scalar_move_vertical.getWidget()))->set_activates_default(true);
140       ((Gtk::Entry *) (_scalar_scale_horizontal.getWidget()))->set_activates_default(true);
141       ((Gtk::Entry *) (_scalar_scale_vertical.getWidget()))->set_activates_default(true);
142       ((Gtk::Entry *) (_scalar_rotate.getWidget()))->set_activates_default(true);
143       ((Gtk::Entry *) (_scalar_skew_horizontal.getWidget()))->set_activates_default(true);
144       ((Gtk::Entry *) (_scalar_skew_vertical.getWidget()))->set_activates_default(true);
146     updateSelection(PAGE_MOVE, _getSelection());
148     resetButton = add_button(Gtk::Stock::CLEAR, 0);
149     if (resetButton) {
150         tooltips.set_tip((*resetButton), _("Reset the values on the current tab to defaults"));
151         resetButton->set_sensitive(true);
152         resetButton->signal_clicked().connect(sigc::mem_fun(*this, &Transformation::onClear));
153     }
155     applyButton = add_button(Gtk::Stock::APPLY,   Gtk::RESPONSE_APPLY);
156     if (applyButton) {
157         tooltips.set_tip((*applyButton), _("Apply transformation to selection"));
158         applyButton->set_sensitive(false);
159         set_default (*applyButton); // activable by Enter in spinbuttons
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);
176 /*########################################################################
177 # U T I L I T Y
178 ########################################################################*/
180 void
181 Transformation::presentPage(Transformation::PageType page)
183     _notebook.set_current_page(page);
184     Gtk::Dialog::show();
185     Gtk::Dialog::present();
191 /*########################################################################
192 # S E T U P   L A Y O U T
193 ########################################################################*/
196 void
197 Transformation::layoutPageMove()
199     _units_move.setUnitType(UNIT_TYPE_LINEAR);
200     _scalar_move_horizontal.initScalar(-1e6, 1e6);
201     _scalar_move_horizontal.setDigits(3);
202     _scalar_move_horizontal.setIncrements(0.1, 1.0);
204     _scalar_move_vertical.initScalar(-1e6, 1e6);
205     _scalar_move_vertical.setDigits(3);
206     _scalar_move_vertical.setIncrements(0.1, 1.0);
208     //_scalar_move_vertical.set_label_image( INKSCAPE_STOCK_ARROWS_HOR );
209     _page_move.table()
210         .attach(_scalar_move_horizontal, 0, 2, 0, 1, Gtk::FILL, Gtk::SHRINK);
212     _page_move.table()
213         .attach(_units_move, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK);
215     _scalar_move_horizontal.signal_value_changed()
216         .connect(sigc::mem_fun(*this, &Transformation::onMoveValueChanged));
218     //_scalar_move_vertical.set_label_image( INKSCAPE_STOCK_ARROWS_VER );
219     _page_move.table()
220         .attach(_scalar_move_vertical, 0, 2, 1, 2, Gtk::FILL, Gtk::SHRINK);
222     _scalar_move_vertical.signal_value_changed()
223         .connect(sigc::mem_fun(*this, &Transformation::onMoveValueChanged));
225     // Relative moves
226     _page_move.table()
227         .attach(_check_move_relative, 0, 2, 2, 3, Gtk::FILL, Gtk::SHRINK);
228     _check_move_relative.set_active(true);
229     _check_move_relative.signal_toggled()
230         .connect(sigc::mem_fun(*this, &Transformation::onMoveRelativeToggled));
233 void
234 Transformation::layoutPageScale()
236     _units_scale.setUnitType(UNIT_TYPE_DIMENSIONLESS);
237     _units_scale.setUnitType(UNIT_TYPE_LINEAR);
239     _scalar_scale_horizontal.initScalar(-1e6, 1e6);
240     _scalar_scale_horizontal.setValue(0.0, "%");
241     _scalar_scale_horizontal.setDigits(3);
242     _scalar_scale_horizontal.setIncrements(0.1, 1.0);
243     _scalar_scale_horizontal.setAbsoluteIsIncrement(true);
244     _scalar_scale_horizontal.setPercentageIsIncrement(true);
246     _scalar_scale_vertical.initScalar(-1e6, 1e6);
247     _scalar_scale_vertical.setValue(0.0, "%");
248     _scalar_scale_vertical.setDigits(3);
249     _scalar_scale_vertical.setIncrements(0.1, 1.0);
250     _scalar_scale_vertical.setAbsoluteIsIncrement(true);
251     _scalar_scale_vertical.setPercentageIsIncrement(true);
253     _page_scale.table()
254         .attach(_scalar_scale_horizontal, 0, 2, 0, 1, Gtk::FILL, Gtk::SHRINK);
255     _scalar_scale_horizontal.signal_value_changed()
256         .connect(sigc::mem_fun(*this, &Transformation::onScaleXValueChanged));
258     _page_scale.table()
259         .attach(_units_scale, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK);
261     _page_scale.table()
262         .attach(_scalar_scale_vertical, 0, 2, 1, 2, Gtk::FILL, Gtk::SHRINK);
263     _scalar_scale_vertical.signal_value_changed()
264         .connect(sigc::mem_fun(*this, &Transformation::onScaleYValueChanged));
266     _page_scale.table()
267         .attach(_check_scale_proportional, 0, 2, 2, 3, Gtk::FILL, Gtk::SHRINK);
268     _check_scale_proportional.set_active(false);
269     _check_scale_proportional.signal_toggled()
270         .connect(sigc::mem_fun(*this, &Transformation::onScaleProportionalToggled));
272     //TODO: add a widget for selecting the fixed point in scaling, or honour rotation center?
275 void
276 Transformation::layoutPageRotate()
278     _units_rotate.setUnitType(UNIT_TYPE_RADIAL);
280     _scalar_rotate.initScalar(-360.0, 360.0);
281     _scalar_rotate.setDigits(3);
282     _scalar_rotate.setIncrements(0.1, 1.0);
284     _page_rotate.table()
285         .attach(_scalar_rotate, 0, 2, 0, 1, Gtk::FILL, Gtk::SHRINK);
287     _page_rotate.table()
288         .attach(_units_rotate, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK);
290     _scalar_rotate.signal_value_changed()
291         .connect(sigc::mem_fun(*this, &Transformation::onRotateValueChanged));
293     //TODO: honour rotation center?
296 void
297 Transformation::layoutPageSkew()
299     _units_skew.setUnitType(UNIT_TYPE_LINEAR);
300     _units_skew.setUnitType(UNIT_TYPE_DIMENSIONLESS);
301     _units_skew.setUnitType(UNIT_TYPE_RADIAL);
303     _scalar_skew_horizontal.initScalar(-1e6, 1e6);
304     _scalar_skew_horizontal.setDigits(3);
305     _scalar_skew_horizontal.setIncrements(0.1, 1.0);
307     _scalar_skew_vertical.initScalar(-1e6, 1e6);
308     _scalar_skew_vertical.setDigits(3);
309     _scalar_skew_vertical.setIncrements(0.1, 1.0);
311     _page_skew.table()
312         .attach(_scalar_skew_horizontal, 0, 2, 0, 1, Gtk::FILL, Gtk::SHRINK);
313     _scalar_skew_horizontal.signal_value_changed()
314         .connect(sigc::mem_fun(*this, &Transformation::onSkewValueChanged));
316     _page_skew.table()
317         .attach(_units_skew, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK);
319     _page_skew.table()
320         .attach(_scalar_skew_vertical, 0, 2, 1, 2, Gtk::FILL, Gtk::SHRINK);
321     _scalar_skew_vertical.signal_value_changed()
322         .connect(sigc::mem_fun(*this, &Transformation::onSkewValueChanged));
324     //TODO: honour rotation center?
329 void
330 Transformation::layoutPageTransform()
332     _scalar_transform_a.setWidgetSizeRequest(65, -1);
333     _scalar_transform_a.setRange(-1e10, 1e10);
334     _scalar_transform_a.setDigits(3);
335     _scalar_transform_a.setIncrements(0.1, 1.0);
336     _scalar_transform_a.setValue(1.0);
337     _page_transform.table()
338         .attach(_scalar_transform_a, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK);
339     _scalar_transform_a.signal_value_changed()
340         .connect(sigc::mem_fun(*this, &Transformation::onTransformValueChanged));
343     _scalar_transform_b.setWidgetSizeRequest(65, -1);
344     _scalar_transform_b.setRange(-1e10, 1e10);
345     _scalar_transform_b.setDigits(3);
346     _scalar_transform_b.setIncrements(0.1, 1.0);
347     _scalar_transform_b.setValue(0.0);
348     _page_transform.table()
349         .attach(_scalar_transform_b, 0, 1, 1, 2, Gtk::SHRINK, Gtk::SHRINK);
350     _scalar_transform_b.signal_value_changed()
351         .connect(sigc::mem_fun(*this, &Transformation::onTransformValueChanged));
354     _scalar_transform_c.setWidgetSizeRequest(65, -1);
355     _scalar_transform_c.setRange(-1e10, 1e10);
356     _scalar_transform_c.setDigits(3);
357     _scalar_transform_c.setIncrements(0.1, 1.0);
358     _scalar_transform_c.setValue(0.0);
359     _page_transform.table()
360         .attach(_scalar_transform_c, 1, 2, 0, 1, Gtk::SHRINK, Gtk::SHRINK);
361     _scalar_transform_c.signal_value_changed()
362         .connect(sigc::mem_fun(*this, &Transformation::onTransformValueChanged));
365     _scalar_transform_d.setWidgetSizeRequest(65, -1);
366     _scalar_transform_d.setRange(-1e10, 1e10);
367     _scalar_transform_d.setDigits(3);
368     _scalar_transform_d.setIncrements(0.1, 1.0);
369     _scalar_transform_d.setValue(1.0);
370     _page_transform.table()
371         .attach(_scalar_transform_d, 1, 2, 1, 2, Gtk::SHRINK, Gtk::SHRINK);
372     _scalar_transform_d.signal_value_changed()
373         .connect(sigc::mem_fun(*this, &Transformation::onTransformValueChanged));
376     _scalar_transform_e.setWidgetSizeRequest(65, -1);
377     _scalar_transform_e.setRange(-1e10, 1e10);
378     _scalar_transform_e.setDigits(3);
379     _scalar_transform_e.setIncrements(0.1, 1.0);
380     _scalar_transform_e.setValue(0.0);
381     _page_transform.table()
382         .attach(_scalar_transform_e, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK);
383     _scalar_transform_e.signal_value_changed()
384         .connect(sigc::mem_fun(*this, &Transformation::onTransformValueChanged));
387     _scalar_transform_f.setWidgetSizeRequest(65, -1);
388     _scalar_transform_f.setRange(-1e10, 1e10);
389     _scalar_transform_f.setDigits(3);
390     _scalar_transform_f.setIncrements(0.1, 1.0);
391     _scalar_transform_f.setValue(0.0);
392     _page_transform.table()
393         .attach(_scalar_transform_f, 2, 3, 1, 2, Gtk::SHRINK, Gtk::SHRINK);
394     _scalar_transform_f.signal_value_changed()
395         .connect(sigc::mem_fun(*this, &Transformation::onTransformValueChanged));
397     // Edit existing matrix
398     _page_transform.table()
399         .attach(_check_replace_matrix, 0, 2, 2, 3, Gtk::FILL, Gtk::SHRINK);
400     _check_replace_matrix.set_active(false);
401     _check_replace_matrix.signal_toggled()
402         .connect(sigc::mem_fun(*this, &Transformation::onReplaceMatrixToggled));
406 /*########################################################################
407 # U P D A T E
408 ########################################################################*/
410 void
411 Transformation::updateSelection(PageType page, Inkscape::Selection *selection)
413     if (!selection || selection->isEmpty())
414         return;
416     switch (page) {
417         case PAGE_MOVE: {
418             updatePageMove(selection);
419             break;
420         }
421         case PAGE_SCALE: {
422             updatePageScale(selection);
423             break;
424         }
425         case PAGE_ROTATE: {
426             updatePageRotate(selection);
427             break;
428         }
429         case PAGE_SKEW: {
430             updatePageSkew(selection);
431             break;
432         }
433         case PAGE_TRANSFORM: {
434             updatePageTransform(selection);
435             break;
436         }
437         case PAGE_QTY: {
438             break;
439         }
440     }
442     set_response_sensitive(Gtk::RESPONSE_APPLY,
443                            selection && !selection->isEmpty());
446 void
447 Transformation::onSwitchPage(GtkNotebookPage *page,
448                                    guint pagenum)
450     updateSelection((PageType)pagenum, sp_desktop_selection(SP_ACTIVE_DESKTOP));
453 void
454 Transformation::updatePageMove(Inkscape::Selection *selection)
456     if (selection && !selection->isEmpty()) {
457         if (!_check_move_relative.get_active()) {
458             NR::Maybe<NR::Rect> bbox = selection->bounds();
459             if (bbox) {
460                 double x = bbox->min()[NR::X];
461                 double y = bbox->min()[NR::Y];
463                 _scalar_move_horizontal.setValue(x, "px");
464                 _scalar_move_vertical.setValue(y, "px");
465             }
466         } else {
467             // do nothing, so you can apply the same relative move to many objects in turn
468         }
469         _page_move.set_sensitive(true);
470     } else {
471         _page_move.set_sensitive(false);
472     }
475 void
476 Transformation::updatePageScale(Inkscape::Selection *selection)
478     if (selection && !selection->isEmpty()) {
479         NR::Maybe<NR::Rect> bbox = selection->bounds();
480         if (bbox) {
481             double w = bbox->extent(NR::X);
482             double h = bbox->extent(NR::Y);
483             _scalar_scale_horizontal.setHundredPercent(w);
484             _scalar_scale_vertical.setHundredPercent(h);
485             onScaleXValueChanged(); // to update x/y proportionality if switch is on
486             _page_scale.set_sensitive(true);
487         } else {
488             _page_scale.set_sensitive(false);
489         }
490     } else {
491         _page_scale.set_sensitive(false);
492     }
495 void
496 Transformation::updatePageRotate(Inkscape::Selection *selection)
498     if (selection && !selection->isEmpty()) {
499         _page_rotate.set_sensitive(true);
500     } else {
501         _page_rotate.set_sensitive(false);
502     }
505 void
506 Transformation::updatePageSkew(Inkscape::Selection *selection)
508     if (selection && !selection->isEmpty()) {
509         _page_skew.set_sensitive(true);
510     } else {
511         _page_skew.set_sensitive(false);
512     }
515 void
516 Transformation::updatePageTransform(Inkscape::Selection *selection)
518     if (selection && !selection->isEmpty()) {
519         if (_check_replace_matrix.get_active()) {
520             NR::Matrix current (SP_ITEM(selection->itemList()->data)->transform); // take from the first item in selection
522             NR::Matrix new_displayed = current;
524             _scalar_transform_a.setValue(new_displayed[0]);
525             _scalar_transform_b.setValue(new_displayed[1]);
526             _scalar_transform_c.setValue(new_displayed[2]);
527             _scalar_transform_d.setValue(new_displayed[3]);
528             _scalar_transform_e.setValue(new_displayed[4]);
529             _scalar_transform_f.setValue(new_displayed[5]);
530         } else {
531             // do nothing, so you can apply the same matrix to many objects in turn
532         }
533         _page_transform.set_sensitive(true);
534     } else {
535         _page_transform.set_sensitive(false);
536     }
543 /*########################################################################
544 # A P P L Y
545 ########################################################################*/
549 void
550 Transformation::_apply()
552     Inkscape::Selection * const selection = _getSelection();
553     if (!selection || selection->isEmpty())
554         return;
556     int const page = _notebook.get_current_page();
558     switch (page) {
559         case PAGE_MOVE: {
560             applyPageMove(selection);
561             break;
562         }
563         case PAGE_ROTATE: {
564             applyPageRotate(selection);
565             break;
566         }
567         case PAGE_SCALE: {
568             applyPageScale(selection);
569             break;
570         }
571         case PAGE_SKEW: {
572             applyPageSkew(selection);
573             break;
574         }
575         case PAGE_TRANSFORM: {
576             applyPageTransform(selection);
577             break;
578         }
579     }
581     //Let's play with never turning this off
582     //set_response_sensitive(Gtk::RESPONSE_APPLY, false);
585 void
586 Transformation::applyPageMove(Inkscape::Selection *selection)
588     double x = _scalar_move_horizontal.getValue("px");
589     double y = _scalar_move_vertical.getValue("px");
591     if (_check_move_relative.get_active()) {
592         sp_selection_move_relative(selection, x, y);
593     } else {
594         NR::Maybe<NR::Rect> bbox = selection->bounds();
595         if (bbox) {
596             sp_selection_move_relative(selection,
597                 x - bbox->min()[NR::X], y - bbox->min()[NR::Y]);
598         }
599     }
601     sp_document_done ( sp_desktop_document (selection->desktop()) , SP_VERB_DIALOG_TRANSFORM, 
602                        _("Move"));
605 void
606 Transformation::applyPageScale(Inkscape::Selection *selection)
608     double scaleX = _scalar_scale_horizontal.getValue("px");
609     double scaleY = _scalar_scale_vertical.getValue("px");
611     if (prefs_get_int_attribute_limited ("dialogs.transformation", "applyseparately", 0, 0, 1) == 1) {
612         for (GSList const *l = selection->itemList(); l != NULL; l = l->next) {
613             SPItem *item = SP_ITEM(l->data);
614             NR::scale scale (0,0);
615             // the values are increments! 
616             if (_units_scale.isAbsolute()) {
617                 NR::Maybe<NR::Rect> bbox(sp_item_bbox_desktop(item));
618                 if (bbox) {
619                     double new_width = bbox->extent(NR::X) + scaleX;
620                     if (new_width < 1e-6) new_width = 1e-6; // not 0, as this would result in a nasty no-bbox object
621                     double new_height = bbox->extent(NR::Y) + scaleY;
622                     if (new_height < 1e-6) new_height = 1e-6;
623                     scale = NR::scale(new_width / bbox->extent(NR::X), new_height / bbox->extent(NR::Y));
624                 }
625             } else {
626                 double new_width = 100 + scaleX;
627                 if (new_width < 1e-6) new_width = 1e-6;
628                 double new_height = 100 + scaleY;
629                 if (new_height < 1e-6) new_height = 1e-6;
630                 scale = NR::scale(new_width / 100.0, new_height / 100.0);
631             }
632             sp_item_scale_rel (item, scale);
633         }
634     } else {
635         NR::Maybe<NR::Rect> bbox(selection->bounds());
636         if (bbox) {
637             NR::Point center(bbox->midpoint()); // use rotation center?
638             NR::scale scale (0,0);
639             // the values are increments!
640             if (_units_scale.isAbsolute()) {
641                 double new_width = bbox->extent(NR::X) + scaleX;
642                 if (new_width < 1e-6) new_width = 1e-6;
643                 double new_height = bbox->extent(NR::Y) + scaleY;
644                 if (new_height < 1e-6) new_height = 1e-6;
645                 scale = NR::scale(new_width / bbox->extent(NR::X), new_height / bbox->extent(NR::Y));
646             } else {
647                 double new_width = 100 + scaleX;
648                 if (new_width < 1e-6) new_width = 1e-6;
649                 double new_height = 100 + scaleY;
650                 if (new_height < 1e-6) new_height = 1e-6;
651                 scale = NR::scale(new_width / 100.0, new_height / 100.0);
652             }
653             sp_selection_scale_relative(selection, center, scale);
654         }
655     }
657     sp_document_done(sp_desktop_document(selection->desktop()), SP_VERB_DIALOG_TRANSFORM, 
658                      _("Scale"));
661 void
662 Transformation::applyPageRotate(Inkscape::Selection *selection)
664     double angle = _scalar_rotate.getValue("deg");
666     if (prefs_get_int_attribute_limited ("dialogs.transformation", "applyseparately", 0, 0, 1) == 1) {
667         for (GSList const *l = selection->itemList(); l != NULL; l = l->next) {
668             SPItem *item = SP_ITEM(l->data);
669             sp_item_rotate_rel(item, NR::rotate (angle*M_PI/180.0));
670         }
671     } else {
672         NR::Maybe<NR::Point> center = selection->center();
673         if (center) {
674             sp_selection_rotate_relative(selection, *center, angle);
675         }
676     }
678     sp_document_done(sp_desktop_document(selection->desktop()), SP_VERB_DIALOG_TRANSFORM, 
679                      _("Rotate"));
682 void
683 Transformation::applyPageSkew(Inkscape::Selection *selection)
685     if (prefs_get_int_attribute_limited ("dialogs.transformation", "applyseparately", 0, 0, 1) == 1) {
686         for (GSList const *l = selection->itemList(); l != NULL; l = l->next) {
687             SPItem *item = SP_ITEM(l->data);
689             if (!_units_skew.isAbsolute()) { // percentage
690                 double skewX = _scalar_skew_horizontal.getValue("%");
691                 double skewY = _scalar_skew_vertical.getValue("%");
692                 sp_item_skew_rel (item, 0.01*skewX, 0.01*skewY);
693             } else if (_units_skew.isRadial()) { //deg or rad
694                 double angleX = _scalar_skew_horizontal.getValue("rad");
695                 double angleY = _scalar_skew_vertical.getValue("rad");
696                 double skewX = tan(-angleX);
697                 double skewY = tan(angleY);
698                 sp_item_skew_rel (item, skewX, skewY);
699             } else { // absolute displacement
700                 double skewX = _scalar_skew_horizontal.getValue("px");
701                 double skewY = _scalar_skew_vertical.getValue("px");
702                 NR::Maybe<NR::Rect> bbox(sp_item_bbox_desktop(item));
703                 if (bbox) {
704                     double width = bbox->extent(NR::X);
705                     double height = bbox->extent(NR::Y);
706                     sp_item_skew_rel (item, skewX/height, skewY/width);
707                 }
708             }
709         }
710     } else { // transform whole selection
711         NR::Maybe<NR::Rect> bbox = selection->bounds();
712         NR::Maybe<NR::Point> center = selection->center();
714         if ( bbox && center ) {
715             double width  = bbox->extent(NR::X);
716             double height = bbox->extent(NR::Y);
718             if (!_units_skew.isAbsolute()) { // percentage
719                 double skewX = _scalar_skew_horizontal.getValue("%");
720                 double skewY = _scalar_skew_vertical.getValue("%");
721                 sp_selection_skew_relative(selection, *center, 0.01*skewX, 0.01*skewY);
722             } else if (_units_skew.isRadial()) { //deg or rad
723                 double angleX = _scalar_skew_horizontal.getValue("rad");
724                 double angleY = _scalar_skew_vertical.getValue("rad");
725                 double skewX = tan(-angleX);
726                 double skewY = tan(angleY);
727                 sp_selection_skew_relative(selection, *center, skewX, skewY);
728             } else { // absolute displacement
729                 double skewX = _scalar_skew_horizontal.getValue("px");
730                 double skewY = _scalar_skew_vertical.getValue("px");
731                 sp_selection_skew_relative(selection, *center, skewX/height, skewY/width);
732             }
733         }
734     }
736     sp_document_done(sp_desktop_document(selection->desktop()), SP_VERB_DIALOG_TRANSFORM, 
737                      _("Skew"));
741 void
742 Transformation::applyPageTransform(Inkscape::Selection *selection)
744     double a = _scalar_transform_a.getValue();
745     double b = _scalar_transform_b.getValue();
746     double c = _scalar_transform_c.getValue();
747     double d = _scalar_transform_d.getValue();
748     double e = _scalar_transform_e.getValue();
749     double f = _scalar_transform_f.getValue();
751     NR::Matrix displayed(a, b, c, d, e, f);
753     if (_check_replace_matrix.get_active()) {
754         for (GSList const *l = selection->itemList(); l != NULL; l = l->next) {
755             SPItem *item = SP_ITEM(l->data);
756             sp_item_set_item_transform(item, displayed);
757             SP_OBJECT(item)->updateRepr();
758         }
759     } else {
760         sp_selection_apply_affine(selection, displayed); // post-multiply each object's transform
761     }
763     sp_document_done(sp_desktop_document(selection->desktop()), SP_VERB_DIALOG_TRANSFORM, 
764                      _("Edit transformation matrix"));
771 /*########################################################################
772 # V A L U E - C H A N G E D    C A L L B A C K S
773 ########################################################################*/
775 void
776 Transformation::onMoveValueChanged()
778     set_response_sensitive(Gtk::RESPONSE_APPLY, true);
781 void
782 Transformation::onMoveRelativeToggled()
784     Inkscape::Selection *selection = _getSelection();
786     if (!selection || selection->isEmpty())
787         return;
789     double x = _scalar_move_horizontal.getValue("px");
790     double y = _scalar_move_vertical.getValue("px");
792     //g_message("onMoveRelativeToggled: %f, %f px\n", x, y);
794     NR::Maybe<NR::Rect> bbox = selection->bounds();
796     if (bbox) {
797         if (_check_move_relative.get_active()) {
798             // From absolute to relative
799             _scalar_move_horizontal.setValue(x - bbox->min()[NR::X], "px");
800             _scalar_move_vertical.setValue(  y - bbox->min()[NR::Y], "px");
801         } else {
802             // From relative to absolute
803             _scalar_move_horizontal.setValue(bbox->min()[NR::X] + x, "px");
804             _scalar_move_vertical.setValue(  bbox->min()[NR::Y] + y, "px");
805         }
806     }
808     set_response_sensitive(Gtk::RESPONSE_APPLY, true);
811 void
812 Transformation::onScaleXValueChanged()
814     if (_scalar_scale_horizontal.setProgrammatically) {
815         _scalar_scale_horizontal.setProgrammatically = false;
816         return;
817     }
819     set_response_sensitive(Gtk::RESPONSE_APPLY, true);
821     if (_check_scale_proportional.get_active()) {
822         if (!_units_scale.isAbsolute()) { // percentage, just copy over
823             _scalar_scale_vertical.setValue(_scalar_scale_horizontal.getValue("%"));
824         } else {
825             double scaleXPercentage = _scalar_scale_horizontal.getAsPercentage();
826             _scalar_scale_vertical.setFromPercentage (scaleXPercentage);
827         }
828     }
831 void
832 Transformation::onScaleYValueChanged()
834     if (_scalar_scale_vertical.setProgrammatically) {
835         _scalar_scale_vertical.setProgrammatically = false;
836         return;
837     }
839     set_response_sensitive(Gtk::RESPONSE_APPLY, true);
841     if (_check_scale_proportional.get_active()) {
842         if (!_units_scale.isAbsolute()) { // percentage, just copy over
843             _scalar_scale_horizontal.setValue(_scalar_scale_vertical.getValue("%"));
844         } else {
845             double scaleYPercentage = _scalar_scale_vertical.getAsPercentage();
846             _scalar_scale_horizontal.setFromPercentage (scaleYPercentage);
847         }
848     }
851 void
852 Transformation::onRotateValueChanged()
854     set_response_sensitive(Gtk::RESPONSE_APPLY, true);
857 void
858 Transformation::onSkewValueChanged()
860     set_response_sensitive(Gtk::RESPONSE_APPLY, true);
863 void
864 Transformation::onTransformValueChanged()
867     /*
868     double a = _scalar_transform_a.getValue();
869     double b = _scalar_transform_b.getValue();
870     double c = _scalar_transform_c.getValue();
871     double d = _scalar_transform_d.getValue();
872     double e = _scalar_transform_e.getValue();
873     double f = _scalar_transform_f.getValue();
875     //g_message("onTransformValueChanged: (%f, %f, %f, %f, %f, %f)\n",
876     //          a, b, c, d, e ,f);
877     */
879     set_response_sensitive(Gtk::RESPONSE_APPLY, true);
882 void
883 Transformation::onReplaceMatrixToggled()
885     Inkscape::Selection *selection = _getSelection();
887     if (!selection || selection->isEmpty())
888         return;
890     double a = _scalar_transform_a.getValue();
891     double b = _scalar_transform_b.getValue();
892     double c = _scalar_transform_c.getValue();
893     double d = _scalar_transform_d.getValue();
894     double e = _scalar_transform_e.getValue();
895     double f = _scalar_transform_f.getValue();
897     NR::Matrix displayed (a, b, c, d, e, f);
898     NR::Matrix current (SP_ITEM(selection->itemList()->data)->transform); // take from the first item in selection
900     NR::Matrix new_displayed;
901     if (_check_replace_matrix.get_active()) {
902         new_displayed = current;
903     } else {
904         new_displayed = current.inverse() * displayed;
905     }
907     _scalar_transform_a.setValue(new_displayed[0]);
908     _scalar_transform_b.setValue(new_displayed[1]);
909     _scalar_transform_c.setValue(new_displayed[2]);
910     _scalar_transform_d.setValue(new_displayed[3]);
911     _scalar_transform_e.setValue(new_displayed[4]);
912     _scalar_transform_f.setValue(new_displayed[5]);
915 void
916 Transformation::onScaleProportionalToggled()
918     onScaleXValueChanged();
922 void
923 Transformation::onClear()
925     int const page = _notebook.get_current_page();
927     switch (page) {
928     case PAGE_MOVE: {
929         Inkscape::Selection *selection = _getSelection();
930         if (!selection || selection->isEmpty() || _check_move_relative.get_active()) {
931             _scalar_move_horizontal.setValue(0);
932             _scalar_move_vertical.setValue(0);
933         } else {
934             NR::Maybe<NR::Rect> bbox = selection->bounds();
935             if (bbox) {
936                 _scalar_move_horizontal.setValue(bbox->min()[NR::X], "px");
937                 _scalar_move_vertical.setValue(bbox->min()[NR::Y], "px");
938             }
939         }
940         break;
941     }
942     case PAGE_ROTATE: {
943         _scalar_rotate.setValue(0);
944         break;
945     }
946     case PAGE_SCALE: {
947         _scalar_scale_horizontal.setValue(0);
948         _scalar_scale_vertical.setValue(0);
949         break;
950     }
951     case PAGE_SKEW: {
952         _scalar_skew_horizontal.setValue(0);
953         _scalar_skew_vertical.setValue(0);
954         break;
955     }
956     case PAGE_TRANSFORM: {
957         _scalar_transform_a.setValue(1);
958         _scalar_transform_b.setValue(0);
959         _scalar_transform_c.setValue(0);
960         _scalar_transform_d.setValue(1);
961         _scalar_transform_e.setValue(0);
962         _scalar_transform_f.setValue(0);
963         break;
964     }
965     }
968 void
969 Transformation::onApplySeparatelyToggled()
971     prefs_set_int_attribute ("dialogs.transformation", "applyseparately", _check_apply_separately.get_active()? 1 : 0);
975 } // namespace Dialog
976 } // namespace UI
977 } // namespace Inkscape
981 /*
982   Local Variables:
983   mode:c++
984   c-file-style:"stroustrup"
985   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
986   indent-tabs-mode:nil
987   fill-column:99
988   End:
989 */
990 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :