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) {
54 if (state & GDK_CONTROL_MASK)
55 return 1; // ctrl
56 if (state & GDK_SHIFT_MASK)
57 return 2; // shift
58 return 0;
59 } else {
60 if (!(state & GDK_CONTROL_MASK) && !(state & GDK_SHIFT_MASK))
61 return 0; // none
62 if (old == 1)
63 if (state & GDK_SHIFT_MASK && !(state & GDK_CONTROL_MASK))
64 return 2; // shift
65 else
66 return 1;
67 if (old == 2)
68 if (state & GDK_CONTROL_MASK && !(state & GDK_SHIFT_MASK))
69 return 1; // ctrl
70 else
71 return 2;
72 return old;
73 }
74 }
77 bool Rotateable::on_motion(GdkEventMotion *event) {
78 if (dragging) {
79 double dist = NR::L2(NR::Point(event->x, event->y) - NR::Point(drag_started_x, drag_started_y));
80 double angle = atan2(event->y - drag_started_y, event->x - drag_started_x);
81 if (dist > 20) {
82 working = true;
83 double force = CLAMP (-(angle - current_axis)/maxdecl, -1, 1);
84 if (fabs(force) < 0.002)
85 force = 0; // snap to zero
86 if (modifier != get_single_modifier(modifier, event->state)) {
87 // user has switched modifiers in mid drag, close past drag and start a new
88 // one, redefining axis temporarily
89 do_release(force, modifier);
90 current_axis = angle;
91 modifier = get_single_modifier(modifier, event->state);
92 } else {
93 do_motion(force, modifier);
94 }
95 }
96 gobble_motion_events(GDK_BUTTON1_MASK);
97 return true;
98 }
99 return false;
100 }
103 bool Rotateable::on_release(GdkEventButton *event) {
104 if (dragging && working) {
105 double angle = atan2(event->y - drag_started_y, event->x - drag_started_x);
106 double force = CLAMP (-(angle - current_axis)/maxdecl, -1, 1);
107 if (fabs(force) < 0.002)
108 force = 0; // snap to zero
109 do_release(force, modifier);
110 dragging = false;
111 working = false;
112 current_axis = axis;
113 return true;
114 }
115 return false;
116 }
119 Rotateable::~Rotateable() {
120 }
124 } // namespace Widget
125 } // namespace UI
126 } // namespace Inkscape
128 /*
129 Local Variables:
130 mode:c++
131 c-file-style:"stroustrup"
132 c-file-offsets:((innamespace . 0)(inline-open . 0))
133 indent-tabs-mode:nil
134 fill-column:99
135 End:
136 */
137 // vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :