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 "", "transform_scew_hor", &_units_skew),
92 _scalar_skew_vertical (_("_Vertical"), _("Vertical skew angle (positive = counterclockwise), or absolute displacement, or percentage displacement"), UNIT_TYPE_LINEAR,
93 "", "transform_scew_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"))
107 {
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();
167 }
169 Transformation::~Transformation()
170 {
171 sp_signal_disconnect_by_data (G_OBJECT (INKSCAPE), this);
172 }
176 /*########################################################################
177 # U T I L I T Y
178 ########################################################################*/
180 void
181 Transformation::presentPage(Transformation::PageType page)
182 {
183 _notebook.set_current_page(page);
184 Gtk::Dialog::show();
185 Gtk::Dialog::present();
186 }
191 /*########################################################################
192 # S E T U P L A Y O U T
193 ########################################################################*/
196 void
197 Transformation::layoutPageMove()
198 {
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));
231 }
233 void
234 Transformation::layoutPageScale()
235 {
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?
273 }
275 void
276 Transformation::layoutPageRotate()
277 {
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?
294 }
296 void
297 Transformation::layoutPageSkew()
298 {
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?
325 }
329 void
330 Transformation::layoutPageTransform()
331 {
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));
403 }
406 /*########################################################################
407 # U P D A T E
408 ########################################################################*/
410 void
411 Transformation::updateSelection(PageType page, Inkscape::Selection *selection)
412 {
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());
444 }
446 void
447 Transformation::onSwitchPage(GtkNotebookPage *page,
448 guint pagenum)
449 {
450 updateSelection((PageType)pagenum, sp_desktop_selection(SP_ACTIVE_DESKTOP));
451 }
453 void
454 Transformation::updatePageMove(Inkscape::Selection *selection)
455 {
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 }
473 }
475 void
476 Transformation::updatePageScale(Inkscape::Selection *selection)
477 {
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 }
493 }
495 void
496 Transformation::updatePageRotate(Inkscape::Selection *selection)
497 {
498 if (selection && !selection->isEmpty()) {
499 _page_rotate.set_sensitive(true);
500 } else {
501 _page_rotate.set_sensitive(false);
502 }
503 }
505 void
506 Transformation::updatePageSkew(Inkscape::Selection *selection)
507 {
508 if (selection && !selection->isEmpty()) {
509 _page_skew.set_sensitive(true);
510 } else {
511 _page_skew.set_sensitive(false);
512 }
513 }
515 void
516 Transformation::updatePageTransform(Inkscape::Selection *selection)
517 {
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 }
537 }
543 /*########################################################################
544 # A P P L Y
545 ########################################################################*/
549 void
550 Transformation::_apply()
551 {
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);
583 }
585 void
586 Transformation::applyPageMove(Inkscape::Selection *selection)
587 {
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"));
603 }
605 void
606 Transformation::applyPageScale(Inkscape::Selection *selection)
607 {
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"));
659 }
661 void
662 Transformation::applyPageRotate(Inkscape::Selection *selection)
663 {
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"));
680 }
682 void
683 Transformation::applyPageSkew(Inkscape::Selection *selection)
684 {
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"));
738 }
741 void
742 Transformation::applyPageTransform(Inkscape::Selection *selection)
743 {
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"));
765 }
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()
777 {
778 set_response_sensitive(Gtk::RESPONSE_APPLY, true);
779 }
781 void
782 Transformation::onMoveRelativeToggled()
783 {
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);
809 }
811 void
812 Transformation::onScaleXValueChanged()
813 {
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 }
829 }
831 void
832 Transformation::onScaleYValueChanged()
833 {
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 }
849 }
851 void
852 Transformation::onRotateValueChanged()
853 {
854 set_response_sensitive(Gtk::RESPONSE_APPLY, true);
855 }
857 void
858 Transformation::onSkewValueChanged()
859 {
860 set_response_sensitive(Gtk::RESPONSE_APPLY, true);
861 }
863 void
864 Transformation::onTransformValueChanged()
865 {
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);
880 }
882 void
883 Transformation::onReplaceMatrixToggled()
884 {
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]);
913 }
915 void
916 Transformation::onScaleProportionalToggled()
917 {
918 onScaleXValueChanged();
919 }
922 void
923 Transformation::onClear()
924 {
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 }
966 }
968 void
969 Transformation::onApplySeparatelyToggled()
970 {
971 prefs_set_int_attribute ("dialogs.transformation", "applyseparately", _check_apply_separately.get_active()? 1 : 0);
972 }
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 :