Code

Applied patch #1505715
[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     updateSelection(PAGE_MOVE, _getSelection());
139     resetButton = add_button(Gtk::Stock::CLEAR, 0);
140     if (resetButton) {
141         tooltips.set_tip((*resetButton), _("Reset the values on the current tab to defaults"));
142         resetButton->set_sensitive(true);
143         resetButton->signal_clicked().connect(sigc::mem_fun(*this, &Transformation::onClear));
144     }
146     applyButton = add_button(Gtk::Stock::APPLY,   Gtk::RESPONSE_APPLY);
147     if (applyButton) {
148         tooltips.set_tip((*applyButton), _("Apply transformation to selection"));
149         applyButton->set_sensitive(false);
150     }
152     // Connect to the global selection changed & modified signals
153     g_signal_connect (G_OBJECT (INKSCAPE), "change_selection", G_CALLBACK (on_selection_changed), this);
154     g_signal_connect (G_OBJECT (INKSCAPE), "modify_selection", G_CALLBACK (on_selection_modified), this);
156     show_all_children();
159 Transformation::~Transformation()
161     sp_signal_disconnect_by_data (G_OBJECT (INKSCAPE), this);
166 /*########################################################################
167 # U T I L I T Y
168 ########################################################################*/
170 void
171 Transformation::presentPage(Transformation::PageType page)
173     _notebook.set_current_page(page);
174     Gtk::Dialog::show();
175     Gtk::Dialog::present();
181 /*########################################################################
182 # S E T U P   L A Y O U T
183 ########################################################################*/
186 void
187 Transformation::layoutPageMove()
189     _units_move.setUnitType(UNIT_TYPE_LINEAR);
190     _scalar_move_horizontal.initScalar(-1e6, 1e6);
191     _scalar_move_horizontal.setDigits(3);
192     _scalar_move_horizontal.setIncrements(0.1, 1.0);
194     _scalar_move_vertical.initScalar(-1e6, 1e6);
195     _scalar_move_vertical.setDigits(3);
196     _scalar_move_vertical.setIncrements(0.1, 1.0);
198     //_scalar_move_vertical.set_label_image( INKSCAPE_STOCK_ARROWS_HOR );
199     _page_move.table()
200         .attach(_scalar_move_horizontal, 0, 2, 0, 1, Gtk::FILL, Gtk::SHRINK);
202     _page_move.table()
203         .attach(_units_move, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK);
205     _scalar_move_horizontal.signal_value_changed()
206         .connect(sigc::mem_fun(*this, &Transformation::onMoveValueChanged));
208     //_scalar_move_vertical.set_label_image( INKSCAPE_STOCK_ARROWS_VER );
209     _page_move.table()
210         .attach(_scalar_move_vertical, 0, 2, 1, 2, Gtk::FILL, Gtk::SHRINK);
212     _scalar_move_vertical.signal_value_changed()
213         .connect(sigc::mem_fun(*this, &Transformation::onMoveValueChanged));
215     // Relative moves
216     _page_move.table()
217         .attach(_check_move_relative, 0, 2, 2, 3, Gtk::FILL, Gtk::SHRINK);
218     _check_move_relative.set_active(true);
219     _check_move_relative.signal_toggled()
220         .connect(sigc::mem_fun(*this, &Transformation::onMoveRelativeToggled));
223 void
224 Transformation::layoutPageScale()
226     _units_scale.setUnitType(UNIT_TYPE_DIMENSIONLESS);
227     _units_scale.setUnitType(UNIT_TYPE_LINEAR);
229     _scalar_scale_horizontal.initScalar(-1e6, 1e6);
230     _scalar_scale_horizontal.setValue(0.0, "%");
231     _scalar_scale_horizontal.setDigits(3);
232     _scalar_scale_horizontal.setIncrements(0.1, 1.0);
233     _scalar_scale_horizontal.setAbsoluteIsIncrement(true);
234     _scalar_scale_horizontal.setPercentageIsIncrement(true);
236     _scalar_scale_vertical.initScalar(-1e6, 1e6);
237     _scalar_scale_vertical.setValue(0.0, "%");
238     _scalar_scale_vertical.setDigits(3);
239     _scalar_scale_vertical.setIncrements(0.1, 1.0);
240     _scalar_scale_vertical.setAbsoluteIsIncrement(true);
241     _scalar_scale_vertical.setPercentageIsIncrement(true);
243     _page_scale.table()
244         .attach(_scalar_scale_horizontal, 0, 2, 0, 1, Gtk::FILL, Gtk::SHRINK);
245     _scalar_scale_horizontal.signal_value_changed()
246         .connect(sigc::mem_fun(*this, &Transformation::onScaleXValueChanged));
248     _page_scale.table()
249         .attach(_units_scale, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK);
251     _page_scale.table()
252         .attach(_scalar_scale_vertical, 0, 2, 1, 2, Gtk::FILL, Gtk::SHRINK);
253     _scalar_scale_vertical.signal_value_changed()
254         .connect(sigc::mem_fun(*this, &Transformation::onScaleYValueChanged));
256     _page_scale.table()
257         .attach(_check_scale_proportional, 0, 2, 2, 3, Gtk::FILL, Gtk::SHRINK);
258     _check_scale_proportional.set_active(false);
259     _check_scale_proportional.signal_toggled()
260         .connect(sigc::mem_fun(*this, &Transformation::onScaleProportionalToggled));
262     //TODO: add a widget for selecting the fixed point in scaling, or honour rotation center?
265 void
266 Transformation::layoutPageRotate()
268     _units_rotate.setUnitType(UNIT_TYPE_RADIAL);
270     _scalar_rotate.initScalar(-360.0, 360.0);
271     _scalar_rotate.setDigits(3);
272     _scalar_rotate.setIncrements(0.1, 1.0);
274     _page_rotate.table()
275         .attach(_scalar_rotate, 0, 2, 0, 1, Gtk::FILL, Gtk::SHRINK);
277     _page_rotate.table()
278         .attach(_units_rotate, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK);
280     _scalar_rotate.signal_value_changed()
281         .connect(sigc::mem_fun(*this, &Transformation::onRotateValueChanged));
283     //TODO: honour rotation center?
286 void
287 Transformation::layoutPageSkew()
289     _units_skew.setUnitType(UNIT_TYPE_LINEAR);
290     _units_skew.setUnitType(UNIT_TYPE_DIMENSIONLESS);
291     _units_skew.setUnitType(UNIT_TYPE_RADIAL);
293     _scalar_skew_horizontal.initScalar(-1e6, 1e6);
294     _scalar_skew_horizontal.setDigits(3);
295     _scalar_skew_horizontal.setIncrements(0.1, 1.0);
297     _scalar_skew_vertical.initScalar(-1e6, 1e6);
298     _scalar_skew_vertical.setDigits(3);
299     _scalar_skew_vertical.setIncrements(0.1, 1.0);
301     _page_skew.table()
302         .attach(_scalar_skew_horizontal, 0, 2, 0, 1, Gtk::FILL, Gtk::SHRINK);
303     _scalar_skew_horizontal.signal_value_changed()
304         .connect(sigc::mem_fun(*this, &Transformation::onSkewValueChanged));
306     _page_skew.table()
307         .attach(_units_skew, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK);
309     _page_skew.table()
310         .attach(_scalar_skew_vertical, 0, 2, 1, 2, Gtk::FILL, Gtk::SHRINK);
311     _scalar_skew_vertical.signal_value_changed()
312         .connect(sigc::mem_fun(*this, &Transformation::onSkewValueChanged));
314     //TODO: honour rotation center?
319 void
320 Transformation::layoutPageTransform()
322     _scalar_transform_a.setWidgetSizeRequest(65, -1);
323     _scalar_transform_a.setRange(-1e10, 1e10);
324     _scalar_transform_a.setDigits(3);
325     _scalar_transform_a.setIncrements(0.1, 1.0);
326     _scalar_transform_a.setValue(1.0);
327     _page_transform.table()
328         .attach(_scalar_transform_a, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK);
329     _scalar_transform_a.signal_value_changed()
330         .connect(sigc::mem_fun(*this, &Transformation::onTransformValueChanged));
333     _scalar_transform_b.setWidgetSizeRequest(65, -1);
334     _scalar_transform_b.setRange(-1e10, 1e10);
335     _scalar_transform_b.setDigits(3);
336     _scalar_transform_b.setIncrements(0.1, 1.0);
337     _scalar_transform_b.setValue(0.0);
338     _page_transform.table()
339         .attach(_scalar_transform_b, 0, 1, 1, 2, Gtk::SHRINK, Gtk::SHRINK);
340     _scalar_transform_b.signal_value_changed()
341         .connect(sigc::mem_fun(*this, &Transformation::onTransformValueChanged));
344     _scalar_transform_c.setWidgetSizeRequest(65, -1);
345     _scalar_transform_c.setRange(-1e10, 1e10);
346     _scalar_transform_c.setDigits(3);
347     _scalar_transform_c.setIncrements(0.1, 1.0);
348     _scalar_transform_c.setValue(0.0);
349     _page_transform.table()
350         .attach(_scalar_transform_c, 1, 2, 0, 1, Gtk::SHRINK, Gtk::SHRINK);
351     _scalar_transform_c.signal_value_changed()
352         .connect(sigc::mem_fun(*this, &Transformation::onTransformValueChanged));
355     _scalar_transform_d.setWidgetSizeRequest(65, -1);
356     _scalar_transform_d.setRange(-1e10, 1e10);
357     _scalar_transform_d.setDigits(3);
358     _scalar_transform_d.setIncrements(0.1, 1.0);
359     _scalar_transform_d.setValue(1.0);
360     _page_transform.table()
361         .attach(_scalar_transform_d, 1, 2, 1, 2, Gtk::SHRINK, Gtk::SHRINK);
362     _scalar_transform_d.signal_value_changed()
363         .connect(sigc::mem_fun(*this, &Transformation::onTransformValueChanged));
366     _scalar_transform_e.setWidgetSizeRequest(65, -1);
367     _scalar_transform_e.setRange(-1e10, 1e10);
368     _scalar_transform_e.setDigits(3);
369     _scalar_transform_e.setIncrements(0.1, 1.0);
370     _scalar_transform_e.setValue(0.0);
371     _page_transform.table()
372         .attach(_scalar_transform_e, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK);
373     _scalar_transform_e.signal_value_changed()
374         .connect(sigc::mem_fun(*this, &Transformation::onTransformValueChanged));
377     _scalar_transform_f.setWidgetSizeRequest(65, -1);
378     _scalar_transform_f.setRange(-1e10, 1e10);
379     _scalar_transform_f.setDigits(3);
380     _scalar_transform_f.setIncrements(0.1, 1.0);
381     _scalar_transform_f.setValue(0.0);
382     _page_transform.table()
383         .attach(_scalar_transform_f, 2, 3, 1, 2, Gtk::SHRINK, Gtk::SHRINK);
384     _scalar_transform_f.signal_value_changed()
385         .connect(sigc::mem_fun(*this, &Transformation::onTransformValueChanged));
387     // Edit existing matrix
388     _page_transform.table()
389         .attach(_check_replace_matrix, 0, 2, 2, 3, Gtk::FILL, Gtk::SHRINK);
390     _check_replace_matrix.set_active(false);
391     _check_replace_matrix.signal_toggled()
392         .connect(sigc::mem_fun(*this, &Transformation::onReplaceMatrixToggled));
396 /*########################################################################
397 # U P D A T E
398 ########################################################################*/
400 void
401 Transformation::updateSelection(PageType page, Inkscape::Selection *selection)
403     if (!selection || selection->isEmpty())
404         return;
406     switch (page) {
407         case PAGE_MOVE: {
408             updatePageMove(selection);
409             break;
410         }
411         case PAGE_SCALE: {
412             updatePageScale(selection);
413             break;
414         }
415         case PAGE_ROTATE: {
416             updatePageRotate(selection);
417             break;
418         }
419         case PAGE_SKEW: {
420             updatePageSkew(selection);
421             break;
422         }
423         case PAGE_TRANSFORM: {
424             updatePageTransform(selection);
425             break;
426         }
427         case PAGE_QTY: {
428             break;
429         }
430     }
432     set_response_sensitive(Gtk::RESPONSE_APPLY,
433                            selection && !selection->isEmpty());
436 void
437 Transformation::onSwitchPage(GtkNotebookPage *page,
438                                    guint pagenum)
440     updateSelection((PageType)pagenum, sp_desktop_selection(SP_ACTIVE_DESKTOP));
443 void
444 Transformation::updatePageMove(Inkscape::Selection *selection)
446     if (selection && !selection->isEmpty()) {
447         if (!_check_move_relative.get_active()) {
449             NR::Rect bbox = selection->bounds();
450             double x = bbox.min()[NR::X];
451             double y = bbox.min()[NR::Y];
453             _scalar_move_horizontal.setValue(x, "px");
454             _scalar_move_vertical.setValue(y, "px");
455         } else {
456             // do nothing, so you can apply the same relative move to many objects in turn
457         }
458         _page_move.set_sensitive(true);
459     } else {
460         _page_move.set_sensitive(false);
461     }
464 void
465 Transformation::updatePageScale(Inkscape::Selection *selection)
467     if (selection && !selection->isEmpty()) {
468         NR::Rect bbox = selection->bounds();
469         double w = bbox.extent(NR::X);
470         double h = bbox.extent(NR::Y);
471         _scalar_scale_horizontal.setHundredPercent(w);
472         _scalar_scale_vertical.setHundredPercent(h);
473         onScaleXValueChanged(); // to update x/y proportionality if switch is on
474         _page_scale.set_sensitive(true);
475     } else {
476         _page_scale.set_sensitive(false);
477     }
480 void
481 Transformation::updatePageRotate(Inkscape::Selection *selection)
483     if (selection && !selection->isEmpty()) {
484         _page_rotate.set_sensitive(true);
485     } else {
486         _page_rotate.set_sensitive(false);
487     }
490 void
491 Transformation::updatePageSkew(Inkscape::Selection *selection)
493     if (selection && !selection->isEmpty()) {
494         _page_skew.set_sensitive(true);
495     } else {
496         _page_skew.set_sensitive(false);
497     }
500 void
501 Transformation::updatePageTransform(Inkscape::Selection *selection)
503     if (selection && !selection->isEmpty()) {
504         if (_check_replace_matrix.get_active()) {
505             NR::Matrix current (SP_ITEM(selection->itemList()->data)->transform); // take from the first item in selection
507             NR::Matrix new_displayed = current;
509             _scalar_transform_a.setValue(new_displayed[0]);
510             _scalar_transform_b.setValue(new_displayed[1]);
511             _scalar_transform_c.setValue(new_displayed[2]);
512             _scalar_transform_d.setValue(new_displayed[3]);
513             _scalar_transform_e.setValue(new_displayed[4]);
514             _scalar_transform_f.setValue(new_displayed[5]);
515         } else {
516             // do nothing, so you can apply the same matrix to many objects in turn
517         }
518         _page_transform.set_sensitive(true);
519     } else {
520         _page_transform.set_sensitive(false);
521     }
528 /*########################################################################
529 # A P P L Y
530 ########################################################################*/
534 void
535 Transformation::_apply()
537     Inkscape::Selection * const selection = _getSelection();
538     if (!selection || selection->isEmpty())
539         return;
541     int const page = _notebook.get_current_page();
543     switch (page) {
544         case PAGE_MOVE: {
545             applyPageMove(selection);
546             break;
547         }
548         case PAGE_ROTATE: {
549             applyPageRotate(selection);
550             break;
551         }
552         case PAGE_SCALE: {
553             applyPageScale(selection);
554             break;
555         }
556         case PAGE_SKEW: {
557             applyPageSkew(selection);
558             break;
559         }
560         case PAGE_TRANSFORM: {
561             applyPageTransform(selection);
562             break;
563         }
564     }
566     //Let's play with never turning this off
567     //set_response_sensitive(Gtk::RESPONSE_APPLY, false);
570 void
571 Transformation::applyPageMove(Inkscape::Selection *selection)
573     double x = _scalar_move_horizontal.getValue("px");
574     double y = _scalar_move_vertical.getValue("px");
576     if (_check_move_relative.get_active()) {
577         sp_selection_move_relative(selection, x, y);
578     } else {
579         NR::Rect bbox = selection->bounds();
580         sp_selection_move_relative(selection,
581             x - bbox.min()[NR::X], y - bbox.min()[NR::Y]);
582     }
584     sp_document_done ( sp_desktop_document (selection->desktop()) );
587 void
588 Transformation::applyPageScale(Inkscape::Selection *selection)
590     double scaleX = _scalar_scale_horizontal.getValue("px");
591     double scaleY = _scalar_scale_vertical.getValue("px");
593     if (prefs_get_int_attribute_limited ("dialogs.transformation", "applyseparately", 0, 0, 1) == 1) {
594         for (GSList const *l = selection->itemList(); l != NULL; l = l->next) {
595             SPItem *item = SP_ITEM(l->data);
596             NR::Rect  bbox (sp_item_bbox_desktop(item));
597             NR::scale scale (0,0);
598             // the values are increments! 
599             if (_units_scale.isAbsolute()) {
600                 double new_width = bbox.extent(NR::X) + scaleX;
601                 if (new_width < 1e-6) new_width = 1e-6; // not 0, as this would result in a nasty no-bbox object
602                 double new_height = bbox.extent(NR::Y) + scaleY;
603                 if (new_height < 1e-6) new_height = 1e-6;
604                 scale = NR::scale(new_width / bbox.extent(NR::X), new_height / bbox.extent(NR::Y));
605             } else {
606                 double new_width = 100 + scaleX;
607                 if (new_width < 1e-6) new_width = 1e-6;
608                 double new_height = 100 + scaleY;
609                 if (new_height < 1e-6) new_height = 1e-6;
610                 scale = NR::scale(new_width / 100.0, new_height / 100.0);
611             }
612             sp_item_scale_rel (item, scale);
613         }
614     } else {
615         NR::Rect  bbox(selection->bounds());
616         NR::Point center(bbox.midpoint()); // use rotation center?
617         NR::scale scale (0,0);
618         // the values are increments!
619         if (_units_scale.isAbsolute()) {
620             double new_width = bbox.extent(NR::X) + scaleX;
621             if (new_width < 1e-6) new_width = 1e-6;
622             double new_height = bbox.extent(NR::Y) + scaleY;
623             if (new_height < 1e-6) new_height = 1e-6;
624             scale = NR::scale(new_width / bbox.extent(NR::X), new_height / bbox.extent(NR::Y));
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_selection_scale_relative(selection, center, scale);
633     }
635     sp_document_done(sp_desktop_document(selection->desktop()));
638 void
639 Transformation::applyPageRotate(Inkscape::Selection *selection)
641     double angle = _scalar_rotate.getValue("deg");
643     if (prefs_get_int_attribute_limited ("dialogs.transformation", "applyseparately", 0, 0, 1) == 1) {
644         for (GSList const *l = selection->itemList(); l != NULL; l = l->next) {
645             SPItem *item = SP_ITEM(l->data);
646             sp_item_rotate_rel(item, NR::rotate (angle*M_PI/180.0));
647         }
648     } else {
649         NR::Point center = selection->center();
650         sp_selection_rotate_relative(selection, center, angle);
651     }
653     sp_document_done(sp_desktop_document(selection->desktop()));
656 void
657 Transformation::applyPageSkew(Inkscape::Selection *selection)
659     if (prefs_get_int_attribute_limited ("dialogs.transformation", "applyseparately", 0, 0, 1) == 1) {
660         for (GSList const *l = selection->itemList(); l != NULL; l = l->next) {
661             SPItem *item = SP_ITEM(l->data);
663             if (!_units_skew.isAbsolute()) { // percentage
664                 double skewX = _scalar_skew_horizontal.getValue("%");
665                 double skewY = _scalar_skew_vertical.getValue("%");
666                 sp_item_skew_rel (item, 0.01*skewX, 0.01*skewY);
667             } else if (_units_skew.isRadial()) { //deg or rad
668                 double angleX = _scalar_skew_horizontal.getValue("rad");
669                 double angleY = _scalar_skew_vertical.getValue("rad");
670                 double skewX = tan(-angleX);
671                 double skewY = tan(angleY);
672                 sp_item_skew_rel (item, skewX, skewY);
673             } else { // absolute displacement
674                 double skewX = _scalar_skew_horizontal.getValue("px");
675                 double skewY = _scalar_skew_vertical.getValue("px");
676                 NR::Rect bbox(sp_item_bbox_desktop(item));
677                 double width = bbox.dimensions()[NR::X];
678                 double height = bbox.dimensions()[NR::Y];
679                 sp_item_skew_rel (item, skewX/height, skewY/width);
680             }
681         }
682     } else { // transform whole selection
683         NR::Rect bbox = selection->bounds();
684         double width  = bbox.max()[NR::X] - bbox.min()[NR::X];
685         double height = bbox.max()[NR::Y] - bbox.min()[NR::Y];
686         NR::Point center = selection->center();
688         if (!_units_skew.isAbsolute()) { // percentage
689             double skewX = _scalar_skew_horizontal.getValue("%");
690             double skewY = _scalar_skew_vertical.getValue("%");
691             sp_selection_skew_relative(selection, center, 0.01*skewX, 0.01*skewY);
692         } else if (_units_skew.isRadial()) { //deg or rad
693             double angleX = _scalar_skew_horizontal.getValue("rad");
694             double angleY = _scalar_skew_vertical.getValue("rad");
695             double skewX = tan(-angleX);
696             double skewY = tan(angleY);
697             sp_selection_skew_relative(selection, center, skewX, skewY);
698         } else { // absolute displacement
699             double skewX = _scalar_skew_horizontal.getValue("px");
700             double skewY = _scalar_skew_vertical.getValue("px");
701             sp_selection_skew_relative(selection, center, skewX/height, skewY/width);
702         }
703     }
705     sp_document_done(sp_desktop_document(selection->desktop()));
709 void
710 Transformation::applyPageTransform(Inkscape::Selection *selection)
712     double a = _scalar_transform_a.getValue();
713     double b = _scalar_transform_b.getValue();
714     double c = _scalar_transform_c.getValue();
715     double d = _scalar_transform_d.getValue();
716     double e = _scalar_transform_e.getValue();
717     double f = _scalar_transform_f.getValue();
719     NR::Matrix displayed(a, b, c, d, e, f);
721     if (_check_replace_matrix.get_active()) {
722         for (GSList const *l = selection->itemList(); l != NULL; l = l->next) {
723             SPItem *item = SP_ITEM(l->data);
724             sp_item_set_item_transform(item, displayed);
725             SP_OBJECT(item)->updateRepr();
726         }
727     } else {
728         sp_selection_apply_affine(selection, displayed); // post-multiply each object's transform
729     }
731     sp_document_done(sp_desktop_document(selection->desktop()));
738 /*########################################################################
739 # V A L U E - C H A N G E D    C A L L B A C K S
740 ########################################################################*/
742 void
743 Transformation::onMoveValueChanged()
745     set_response_sensitive(Gtk::RESPONSE_APPLY, true);
748 void
749 Transformation::onMoveRelativeToggled()
751     Inkscape::Selection *selection = _getSelection();
753     if (!selection || selection->isEmpty())
754         return;
756     double x = _scalar_move_horizontal.getValue("px");
757     double y = _scalar_move_vertical.getValue("px");
759     //g_message("onMoveRelativeToggled: %f, %f px\n", x, y);
761     NR::Rect bbox = selection->bounds();
763     if (_check_move_relative.get_active()) {
764         // From absolute to relative
765         _scalar_move_horizontal.setValue(x - bbox.min()[NR::X], "px");
766         _scalar_move_vertical.setValue(  y - bbox.min()[NR::Y], "px");
767     } else {
768         // From relative to absolute
769         _scalar_move_horizontal.setValue(bbox.min()[NR::X] + x, "px");
770         _scalar_move_vertical.setValue(  bbox.min()[NR::Y] + y, "px");
771     }
774     set_response_sensitive(Gtk::RESPONSE_APPLY, true);
777 void
778 Transformation::onScaleXValueChanged()
780     if (_scalar_scale_horizontal.setProgrammatically) {
781         _scalar_scale_horizontal.setProgrammatically = false;
782         return;
783     }
785     set_response_sensitive(Gtk::RESPONSE_APPLY, true);
787     if (_check_scale_proportional.get_active()) {
788         if (!_units_scale.isAbsolute()) { // percentage, just copy over
789             _scalar_scale_vertical.setValue(_scalar_scale_horizontal.getValue("%"));
790         } else {
791             double scaleXPercentage = _scalar_scale_horizontal.getAsPercentage();
792             _scalar_scale_vertical.setFromPercentage (scaleXPercentage);
793         }
794     }
797 void
798 Transformation::onScaleYValueChanged()
800     if (_scalar_scale_vertical.setProgrammatically) {
801         _scalar_scale_vertical.setProgrammatically = false;
802         return;
803     }
805     set_response_sensitive(Gtk::RESPONSE_APPLY, true);
807     if (_check_scale_proportional.get_active()) {
808         if (!_units_scale.isAbsolute()) { // percentage, just copy over
809             _scalar_scale_horizontal.setValue(_scalar_scale_vertical.getValue("%"));
810         } else {
811             double scaleYPercentage = _scalar_scale_vertical.getAsPercentage();
812             _scalar_scale_horizontal.setFromPercentage (scaleYPercentage);
813         }
814     }
817 void
818 Transformation::onRotateValueChanged()
820     set_response_sensitive(Gtk::RESPONSE_APPLY, true);
823 void
824 Transformation::onSkewValueChanged()
826     set_response_sensitive(Gtk::RESPONSE_APPLY, true);
829 void
830 Transformation::onTransformValueChanged()
833     /*
834     double a = _scalar_transform_a.getValue();
835     double b = _scalar_transform_b.getValue();
836     double c = _scalar_transform_c.getValue();
837     double d = _scalar_transform_d.getValue();
838     double e = _scalar_transform_e.getValue();
839     double f = _scalar_transform_f.getValue();
841     //g_message("onTransformValueChanged: (%f, %f, %f, %f, %f, %f)\n",
842     //          a, b, c, d, e ,f);
843     */
845     set_response_sensitive(Gtk::RESPONSE_APPLY, true);
848 void
849 Transformation::onReplaceMatrixToggled()
851     Inkscape::Selection *selection = _getSelection();
853     if (!selection || selection->isEmpty())
854         return;
856     double a = _scalar_transform_a.getValue();
857     double b = _scalar_transform_b.getValue();
858     double c = _scalar_transform_c.getValue();
859     double d = _scalar_transform_d.getValue();
860     double e = _scalar_transform_e.getValue();
861     double f = _scalar_transform_f.getValue();
863     NR::Matrix displayed (a, b, c, d, e, f);
864     NR::Matrix current (SP_ITEM(selection->itemList()->data)->transform); // take from the first item in selection
866     NR::Matrix new_displayed;
867     if (_check_replace_matrix.get_active()) {
868         new_displayed = current;
869     } else {
870         new_displayed = current.inverse() * displayed;
871     }
873     _scalar_transform_a.setValue(new_displayed[0]);
874     _scalar_transform_b.setValue(new_displayed[1]);
875     _scalar_transform_c.setValue(new_displayed[2]);
876     _scalar_transform_d.setValue(new_displayed[3]);
877     _scalar_transform_e.setValue(new_displayed[4]);
878     _scalar_transform_f.setValue(new_displayed[5]);
881 void
882 Transformation::onScaleProportionalToggled()
884     onScaleXValueChanged();
888 void
889 Transformation::onClear()
891     int const page = _notebook.get_current_page();
893     switch (page) {
894     case PAGE_MOVE: {
895         Inkscape::Selection *selection = _getSelection();
896         if (!selection || selection->isEmpty() || _check_move_relative.get_active()) {
897             _scalar_move_horizontal.setValue(0);
898             _scalar_move_vertical.setValue(0);
899         } else {
900             NR::Rect bbox = selection->bounds();
901             _scalar_move_horizontal.setValue(bbox.min()[NR::X], "px");
902             _scalar_move_vertical.setValue(bbox.min()[NR::Y], "px");
903         }
904         break;
905     }
906     case PAGE_ROTATE: {
907         _scalar_rotate.setValue(0);
908         break;
909     }
910     case PAGE_SCALE: {
911         _scalar_scale_horizontal.setValue(0);
912         _scalar_scale_vertical.setValue(0);
913         break;
914     }
915     case PAGE_SKEW: {
916         _scalar_skew_horizontal.setValue(0);
917         _scalar_skew_vertical.setValue(0);
918         break;
919     }
920     case PAGE_TRANSFORM: {
921         _scalar_transform_a.setValue(1);
922         _scalar_transform_b.setValue(0);
923         _scalar_transform_c.setValue(0);
924         _scalar_transform_d.setValue(1);
925         _scalar_transform_e.setValue(0);
926         _scalar_transform_f.setValue(0);
927         break;
928     }
929     }
932 void
933 Transformation::onApplySeparatelyToggled()
935     prefs_set_int_attribute ("dialogs.transformation", "applyseparately", _check_apply_separately.get_active()? 1 : 0);
939 } // namespace Dialog
940 } // namespace UI
941 } // namespace Inkscape
945 /*
946   Local Variables:
947   mode:c++
948   c-file-style:"stroustrup"
949   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
950   indent-tabs-mode:nil
951   fill-column:99
952   End:
953 */
954 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :