1 /**
2 * \brief widget adjustable by dragging it to rotate away from a zero-change axis
3 *
4 * Authors:
5 * buliabyak@gmail.com
6 *
7 * Copyright (C) 2007 authors
8 *
9 * Released under GNU GPL. Read the file 'COPYING' for more information.
10 */
12 #include "event-context.h"
13 #include "rotateable.h"
14 #include "libnr/nr-point.h"
15 #include "libnr/nr-point-fns.h"
16 #include <gtkmm/box.h>
17 #include <gtkmm/eventbox.h>
18 #include <glibmm/i18n.h>
20 namespace Inkscape {
21 namespace UI {
22 namespace Widget {
24 Rotateable::Rotateable():
25 axis(-M_PI/4),
26 maxdecl(M_PI/4)
27 {
28 dragging = false;
29 working = false;
30 modifier = 0;
31 current_axis = axis;
33 signal_button_press_event().connect(sigc::mem_fun(*this, &Rotateable::on_click));
34 signal_motion_notify_event().connect(sigc::mem_fun(*this, &Rotateable::on_motion));
35 signal_button_release_event().connect(sigc::mem_fun(*this, &Rotateable::on_release));
36 }
38 bool Rotateable::on_click(GdkEventButton *event) {
39 if (event->button == 1) {
40 drag_started_x = event->x;
41 drag_started_y = event->y;
42 modifier = get_single_modifier(modifier, event->state);
43 dragging = true;
44 working = false;
45 current_axis = axis;
46 return true;
47 }
48 return false;
49 }
51 guint Rotateable::get_single_modifier(guint old, guint state) {
53 if (old == 0 || old == 3) {
54 if (state & GDK_CONTROL_MASK)
55 return 1; // ctrl
56 if (state & GDK_SHIFT_MASK)
57 return 2; // shift
58 if (state & GDK_MOD1_MASK)
59 return 3; // alt
60 return 0;
61 } else {
62 if (!(state & GDK_CONTROL_MASK) && !(state & GDK_SHIFT_MASK)) {
63 if (state & GDK_MOD1_MASK)
64 return 3; // alt
65 else
66 return 0; // none
67 }
68 if (old == 1) {
69 if (state & GDK_SHIFT_MASK && !(state & GDK_CONTROL_MASK))
70 return 2; // shift
71 if (state & GDK_MOD1_MASK && !(state & GDK_CONTROL_MASK))
72 return 3; // alt
73 return 1;
74 }
75 if (old == 2) {
76 if (state & GDK_CONTROL_MASK && !(state & GDK_SHIFT_MASK))
77 return 1; // ctrl
78 if (state & GDK_MOD1_MASK && !(state & GDK_SHIFT_MASK))
79 return 3; // alt
80 return 2;
81 }
82 return old;
83 }
84 }
87 bool Rotateable::on_motion(GdkEventMotion *event) {
88 if (dragging) {
89 double dist = NR::L2(NR::Point(event->x, event->y) - NR::Point(drag_started_x, drag_started_y));
90 double angle = atan2(event->y - drag_started_y, event->x - drag_started_x);
91 if (dist > 20) {
92 working = true;
93 double force = CLAMP (-(angle - current_axis)/maxdecl, -1, 1);
94 if (fabs(force) < 0.002)
95 force = 0; // snap to zero
96 if (modifier != get_single_modifier(modifier, event->state)) {
97 // user has switched modifiers in mid drag, close past drag and start a new
98 // one, redefining axis temporarily
99 do_release(force, modifier);
100 current_axis = angle;
101 modifier = get_single_modifier(modifier, event->state);
102 } else {
103 do_motion(force, modifier);
104 }
105 }
106 gobble_motion_events(GDK_BUTTON1_MASK);
107 return true;
108 }
109 return false;
110 }
113 bool Rotateable::on_release(GdkEventButton *event) {
114 if (dragging && working) {
115 double angle = atan2(event->y - drag_started_y, event->x - drag_started_x);
116 double force = CLAMP (-(angle - current_axis)/maxdecl, -1, 1);
117 if (fabs(force) < 0.002)
118 force = 0; // snap to zero
119 do_release(force, modifier);
120 current_axis = axis;
121 return true;
122 }
123 dragging = false;
124 working = false;
125 return false;
126 }
129 Rotateable::~Rotateable() {
130 }
134 } // namespace Widget
135 } // namespace UI
136 } // namespace Inkscape
138 /*
139 Local Variables:
140 mode:c++
141 c-file-style:"stroustrup"
142 c-file-offsets:((innamespace . 0)(inline-open . 0))
143 indent-tabs-mode:nil
144 fill-column:99
145 End:
146 */
147 // vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :