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"))
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 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();
157 }
159 Transformation::~Transformation()
160 {
161 sp_signal_disconnect_by_data (G_OBJECT (INKSCAPE), this);
162 }
166 /*########################################################################
167 # U T I L I T Y
168 ########################################################################*/
170 void
171 Transformation::presentPage(Transformation::PageType page)
172 {
173 _notebook.set_current_page(page);
174 Gtk::Dialog::show();
175 Gtk::Dialog::present();
176 }
181 /*########################################################################
182 # S E T U P L A Y O U T
183 ########################################################################*/
186 void
187 Transformation::layoutPageMove()
188 {
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));
221 }
223 void
224 Transformation::layoutPageScale()
225 {
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?
263 }
265 void
266 Transformation::layoutPageRotate()
267 {
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?
284 }
286 void
287 Transformation::layoutPageSkew()
288 {
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?
315 }
319 void
320 Transformation::layoutPageTransform()
321 {
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));
393 }
396 /*########################################################################
397 # U P D A T E
398 ########################################################################*/
400 void
401 Transformation::updateSelection(PageType page, Inkscape::Selection *selection)
402 {
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());
434 }
436 void
437 Transformation::onSwitchPage(GtkNotebookPage *page,
438 guint pagenum)
439 {
440 updateSelection((PageType)pagenum, sp_desktop_selection(SP_ACTIVE_DESKTOP));
441 }
443 void
444 Transformation::updatePageMove(Inkscape::Selection *selection)
445 {
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 }
462 }
464 void
465 Transformation::updatePageScale(Inkscape::Selection *selection)
466 {
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 }
478 }
480 void
481 Transformation::updatePageRotate(Inkscape::Selection *selection)
482 {
483 if (selection && !selection->isEmpty()) {
484 _page_rotate.set_sensitive(true);
485 } else {
486 _page_rotate.set_sensitive(false);
487 }
488 }
490 void
491 Transformation::updatePageSkew(Inkscape::Selection *selection)
492 {
493 if (selection && !selection->isEmpty()) {
494 _page_skew.set_sensitive(true);
495 } else {
496 _page_skew.set_sensitive(false);
497 }
498 }
500 void
501 Transformation::updatePageTransform(Inkscape::Selection *selection)
502 {
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 }
522 }
528 /*########################################################################
529 # A P P L Y
530 ########################################################################*/
534 void
535 Transformation::_apply()
536 {
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);
568 }
570 void
571 Transformation::applyPageMove(Inkscape::Selection *selection)
572 {
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()) , SP_VERB_DIALOG_TRANSFORM,
585 /* TODO: annotate */ "transformation.cpp:585");
586 }
588 void
589 Transformation::applyPageScale(Inkscape::Selection *selection)
590 {
591 double scaleX = _scalar_scale_horizontal.getValue("px");
592 double scaleY = _scalar_scale_vertical.getValue("px");
594 if (prefs_get_int_attribute_limited ("dialogs.transformation", "applyseparately", 0, 0, 1) == 1) {
595 for (GSList const *l = selection->itemList(); l != NULL; l = l->next) {
596 SPItem *item = SP_ITEM(l->data);
597 NR::Rect bbox (sp_item_bbox_desktop(item));
598 NR::scale scale (0,0);
599 // the values are increments!
600 if (_units_scale.isAbsolute()) {
601 double new_width = bbox.extent(NR::X) + scaleX;
602 if (new_width < 1e-6) new_width = 1e-6; // not 0, as this would result in a nasty no-bbox object
603 double new_height = bbox.extent(NR::Y) + scaleY;
604 if (new_height < 1e-6) new_height = 1e-6;
605 scale = NR::scale(new_width / bbox.extent(NR::X), new_height / bbox.extent(NR::Y));
606 } else {
607 double new_width = 100 + scaleX;
608 if (new_width < 1e-6) new_width = 1e-6;
609 double new_height = 100 + scaleY;
610 if (new_height < 1e-6) new_height = 1e-6;
611 scale = NR::scale(new_width / 100.0, new_height / 100.0);
612 }
613 sp_item_scale_rel (item, scale);
614 }
615 } else {
616 NR::Rect bbox(selection->bounds());
617 NR::Point center(bbox.midpoint()); // use rotation center?
618 NR::scale scale (0,0);
619 // the values are increments!
620 if (_units_scale.isAbsolute()) {
621 double new_width = bbox.extent(NR::X) + scaleX;
622 if (new_width < 1e-6) new_width = 1e-6;
623 double new_height = bbox.extent(NR::Y) + scaleY;
624 if (new_height < 1e-6) new_height = 1e-6;
625 scale = NR::scale(new_width / bbox.extent(NR::X), new_height / bbox.extent(NR::Y));
626 } else {
627 double new_width = 100 + scaleX;
628 if (new_width < 1e-6) new_width = 1e-6;
629 double new_height = 100 + scaleY;
630 if (new_height < 1e-6) new_height = 1e-6;
631 scale = NR::scale(new_width / 100.0, new_height / 100.0);
632 }
633 sp_selection_scale_relative(selection, center, scale);
634 }
636 sp_document_done(sp_desktop_document(selection->desktop()), SP_VERB_DIALOG_TRANSFORM,
637 /* TODO: annotate */ "transformation.cpp:637");
638 }
640 void
641 Transformation::applyPageRotate(Inkscape::Selection *selection)
642 {
643 double angle = _scalar_rotate.getValue("deg");
645 if (prefs_get_int_attribute_limited ("dialogs.transformation", "applyseparately", 0, 0, 1) == 1) {
646 for (GSList const *l = selection->itemList(); l != NULL; l = l->next) {
647 SPItem *item = SP_ITEM(l->data);
648 sp_item_rotate_rel(item, NR::rotate (angle*M_PI/180.0));
649 }
650 } else {
651 NR::Point center = selection->center();
652 sp_selection_rotate_relative(selection, center, angle);
653 }
655 sp_document_done(sp_desktop_document(selection->desktop()), SP_VERB_DIALOG_TRANSFORM,
656 /* TODO: annotate */ "transformation.cpp:656");
657 }
659 void
660 Transformation::applyPageSkew(Inkscape::Selection *selection)
661 {
662 if (prefs_get_int_attribute_limited ("dialogs.transformation", "applyseparately", 0, 0, 1) == 1) {
663 for (GSList const *l = selection->itemList(); l != NULL; l = l->next) {
664 SPItem *item = SP_ITEM(l->data);
666 if (!_units_skew.isAbsolute()) { // percentage
667 double skewX = _scalar_skew_horizontal.getValue("%");
668 double skewY = _scalar_skew_vertical.getValue("%");
669 sp_item_skew_rel (item, 0.01*skewX, 0.01*skewY);
670 } else if (_units_skew.isRadial()) { //deg or rad
671 double angleX = _scalar_skew_horizontal.getValue("rad");
672 double angleY = _scalar_skew_vertical.getValue("rad");
673 double skewX = tan(-angleX);
674 double skewY = tan(angleY);
675 sp_item_skew_rel (item, skewX, skewY);
676 } else { // absolute displacement
677 double skewX = _scalar_skew_horizontal.getValue("px");
678 double skewY = _scalar_skew_vertical.getValue("px");
679 NR::Rect bbox(sp_item_bbox_desktop(item));
680 double width = bbox.dimensions()[NR::X];
681 double height = bbox.dimensions()[NR::Y];
682 sp_item_skew_rel (item, skewX/height, skewY/width);
683 }
684 }
685 } else { // transform whole selection
686 NR::Rect bbox = selection->bounds();
687 double width = bbox.max()[NR::X] - bbox.min()[NR::X];
688 double height = bbox.max()[NR::Y] - bbox.min()[NR::Y];
689 NR::Point center = selection->center();
691 if (!_units_skew.isAbsolute()) { // percentage
692 double skewX = _scalar_skew_horizontal.getValue("%");
693 double skewY = _scalar_skew_vertical.getValue("%");
694 sp_selection_skew_relative(selection, center, 0.01*skewX, 0.01*skewY);
695 } else if (_units_skew.isRadial()) { //deg or rad
696 double angleX = _scalar_skew_horizontal.getValue("rad");
697 double angleY = _scalar_skew_vertical.getValue("rad");
698 double skewX = tan(-angleX);
699 double skewY = tan(angleY);
700 sp_selection_skew_relative(selection, center, skewX, skewY);
701 } else { // absolute displacement
702 double skewX = _scalar_skew_horizontal.getValue("px");
703 double skewY = _scalar_skew_vertical.getValue("px");
704 sp_selection_skew_relative(selection, center, skewX/height, skewY/width);
705 }
706 }
708 sp_document_done(sp_desktop_document(selection->desktop()), SP_VERB_DIALOG_TRANSFORM,
709 /* TODO: annotate */ "transformation.cpp:709");
710 }
713 void
714 Transformation::applyPageTransform(Inkscape::Selection *selection)
715 {
716 double a = _scalar_transform_a.getValue();
717 double b = _scalar_transform_b.getValue();
718 double c = _scalar_transform_c.getValue();
719 double d = _scalar_transform_d.getValue();
720 double e = _scalar_transform_e.getValue();
721 double f = _scalar_transform_f.getValue();
723 NR::Matrix displayed(a, b, c, d, e, f);
725 if (_check_replace_matrix.get_active()) {
726 for (GSList const *l = selection->itemList(); l != NULL; l = l->next) {
727 SPItem *item = SP_ITEM(l->data);
728 sp_item_set_item_transform(item, displayed);
729 SP_OBJECT(item)->updateRepr();
730 }
731 } else {
732 sp_selection_apply_affine(selection, displayed); // post-multiply each object's transform
733 }
735 sp_document_done(sp_desktop_document(selection->desktop()), SP_VERB_DIALOG_TRANSFORM,
736 /* TODO: annotate */ "transformation.cpp:736");
737 }
743 /*########################################################################
744 # V A L U E - C H A N G E D C A L L B A C K S
745 ########################################################################*/
747 void
748 Transformation::onMoveValueChanged()
749 {
750 set_response_sensitive(Gtk::RESPONSE_APPLY, true);
751 }
753 void
754 Transformation::onMoveRelativeToggled()
755 {
756 Inkscape::Selection *selection = _getSelection();
758 if (!selection || selection->isEmpty())
759 return;
761 double x = _scalar_move_horizontal.getValue("px");
762 double y = _scalar_move_vertical.getValue("px");
764 //g_message("onMoveRelativeToggled: %f, %f px\n", x, y);
766 NR::Rect bbox = selection->bounds();
768 if (_check_move_relative.get_active()) {
769 // From absolute to relative
770 _scalar_move_horizontal.setValue(x - bbox.min()[NR::X], "px");
771 _scalar_move_vertical.setValue( y - bbox.min()[NR::Y], "px");
772 } else {
773 // From relative to absolute
774 _scalar_move_horizontal.setValue(bbox.min()[NR::X] + x, "px");
775 _scalar_move_vertical.setValue( bbox.min()[NR::Y] + y, "px");
776 }
779 set_response_sensitive(Gtk::RESPONSE_APPLY, true);
780 }
782 void
783 Transformation::onScaleXValueChanged()
784 {
785 if (_scalar_scale_horizontal.setProgrammatically) {
786 _scalar_scale_horizontal.setProgrammatically = false;
787 return;
788 }
790 set_response_sensitive(Gtk::RESPONSE_APPLY, true);
792 if (_check_scale_proportional.get_active()) {
793 if (!_units_scale.isAbsolute()) { // percentage, just copy over
794 _scalar_scale_vertical.setValue(_scalar_scale_horizontal.getValue("%"));
795 } else {
796 double scaleXPercentage = _scalar_scale_horizontal.getAsPercentage();
797 _scalar_scale_vertical.setFromPercentage (scaleXPercentage);
798 }
799 }
800 }
802 void
803 Transformation::onScaleYValueChanged()
804 {
805 if (_scalar_scale_vertical.setProgrammatically) {
806 _scalar_scale_vertical.setProgrammatically = false;
807 return;
808 }
810 set_response_sensitive(Gtk::RESPONSE_APPLY, true);
812 if (_check_scale_proportional.get_active()) {
813 if (!_units_scale.isAbsolute()) { // percentage, just copy over
814 _scalar_scale_horizontal.setValue(_scalar_scale_vertical.getValue("%"));
815 } else {
816 double scaleYPercentage = _scalar_scale_vertical.getAsPercentage();
817 _scalar_scale_horizontal.setFromPercentage (scaleYPercentage);
818 }
819 }
820 }
822 void
823 Transformation::onRotateValueChanged()
824 {
825 set_response_sensitive(Gtk::RESPONSE_APPLY, true);
826 }
828 void
829 Transformation::onSkewValueChanged()
830 {
831 set_response_sensitive(Gtk::RESPONSE_APPLY, true);
832 }
834 void
835 Transformation::onTransformValueChanged()
836 {
838 /*
839 double a = _scalar_transform_a.getValue();
840 double b = _scalar_transform_b.getValue();
841 double c = _scalar_transform_c.getValue();
842 double d = _scalar_transform_d.getValue();
843 double e = _scalar_transform_e.getValue();
844 double f = _scalar_transform_f.getValue();
846 //g_message("onTransformValueChanged: (%f, %f, %f, %f, %f, %f)\n",
847 // a, b, c, d, e ,f);
848 */
850 set_response_sensitive(Gtk::RESPONSE_APPLY, true);
851 }
853 void
854 Transformation::onReplaceMatrixToggled()
855 {
856 Inkscape::Selection *selection = _getSelection();
858 if (!selection || selection->isEmpty())
859 return;
861 double a = _scalar_transform_a.getValue();
862 double b = _scalar_transform_b.getValue();
863 double c = _scalar_transform_c.getValue();
864 double d = _scalar_transform_d.getValue();
865 double e = _scalar_transform_e.getValue();
866 double f = _scalar_transform_f.getValue();
868 NR::Matrix displayed (a, b, c, d, e, f);
869 NR::Matrix current (SP_ITEM(selection->itemList()->data)->transform); // take from the first item in selection
871 NR::Matrix new_displayed;
872 if (_check_replace_matrix.get_active()) {
873 new_displayed = current;
874 } else {
875 new_displayed = current.inverse() * displayed;
876 }
878 _scalar_transform_a.setValue(new_displayed[0]);
879 _scalar_transform_b.setValue(new_displayed[1]);
880 _scalar_transform_c.setValue(new_displayed[2]);
881 _scalar_transform_d.setValue(new_displayed[3]);
882 _scalar_transform_e.setValue(new_displayed[4]);
883 _scalar_transform_f.setValue(new_displayed[5]);
884 }
886 void
887 Transformation::onScaleProportionalToggled()
888 {
889 onScaleXValueChanged();
890 }
893 void
894 Transformation::onClear()
895 {
896 int const page = _notebook.get_current_page();
898 switch (page) {
899 case PAGE_MOVE: {
900 Inkscape::Selection *selection = _getSelection();
901 if (!selection || selection->isEmpty() || _check_move_relative.get_active()) {
902 _scalar_move_horizontal.setValue(0);
903 _scalar_move_vertical.setValue(0);
904 } else {
905 NR::Rect bbox = selection->bounds();
906 _scalar_move_horizontal.setValue(bbox.min()[NR::X], "px");
907 _scalar_move_vertical.setValue(bbox.min()[NR::Y], "px");
908 }
909 break;
910 }
911 case PAGE_ROTATE: {
912 _scalar_rotate.setValue(0);
913 break;
914 }
915 case PAGE_SCALE: {
916 _scalar_scale_horizontal.setValue(0);
917 _scalar_scale_vertical.setValue(0);
918 break;
919 }
920 case PAGE_SKEW: {
921 _scalar_skew_horizontal.setValue(0);
922 _scalar_skew_vertical.setValue(0);
923 break;
924 }
925 case PAGE_TRANSFORM: {
926 _scalar_transform_a.setValue(1);
927 _scalar_transform_b.setValue(0);
928 _scalar_transform_c.setValue(0);
929 _scalar_transform_d.setValue(1);
930 _scalar_transform_e.setValue(0);
931 _scalar_transform_f.setValue(0);
932 break;
933 }
934 }
935 }
937 void
938 Transformation::onApplySeparatelyToggled()
939 {
940 prefs_set_int_attribute ("dialogs.transformation", "applyseparately", _check_apply_separately.get_active()? 1 : 0);
941 }
944 } // namespace Dialog
945 } // namespace UI
946 } // namespace Inkscape
950 /*
951 Local Variables:
952 mode:c++
953 c-file-style:"stroustrup"
954 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
955 indent-tabs-mode:nil
956 fill-column:99
957 End:
958 */
959 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :