fix crash when copy'ing latent plugins
[ardour.git] / libs / gtkmm2ext / auto_spin.cc
1 /*
2     Copyright (C) 1999 Paul Barton-Davis
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18     $Id$
19 */
20
21 #include <cmath>
22 #include "gtkmm2ext/auto_spin.h"
23 #include "gtkmm2ext/keyboard.h"
24
25 using namespace Gtkmm2ext;
26 using namespace std;
27
28 #define upper          adjustment.get_upper()
29 #define lower          adjustment.get_lower()
30 #define step_increment adjustment.get_step_increment()
31 #define page_increment adjustment.get_page_increment()
32
33 const unsigned int AutoSpin::initial_timer_interval = 500;   /* msecs */
34 const unsigned int AutoSpin::timer_interval = 20;            /* msecs */
35 const unsigned int AutoSpin::climb_timer_calls = 5;    /* between climbing */
36
37 AutoSpin::AutoSpin (Gtk::Adjustment &adjr, gfloat cr, bool round_to_steps_yn)
38         : adjustment (adjr),
39           climb_rate (cr)
40
41 {
42         initial = adjustment.get_value ();
43         left_is_decrement = true;
44         wrap = false;
45         have_timer = false;
46         need_timer = false;
47         timer_calls = 0;
48         round_to_steps = round_to_steps_yn;
49 }
50
51 void
52 AutoSpin::stop_timer ()
53 {
54         if (have_timer) {
55                 g_source_remove (timeout_tag);
56                 have_timer = false;
57         }
58 }
59
60 gint
61 AutoSpin::stop_spinning (GdkEventButton */*ev*/)
62 {
63         need_timer = false;
64         stop_timer ();
65         return FALSE;
66 }
67
68 gint
69 AutoSpin::button_press (GdkEventButton *ev)
70 {
71         bool shifted = false;
72         bool control = false;
73         bool with_decrement = false;
74
75         stop_spinning (0);
76
77         if (ev->type == GDK_2BUTTON_PRESS || ev->type == GDK_3BUTTON_PRESS ) {
78                 return true;
79         }
80
81         if (ev->state & Keyboard::TertiaryModifier) {
82                 /* use page shift */
83
84                 shifted = true;
85         }
86
87         if (ev->state & Keyboard::PrimaryModifier) {
88                 /* go to upper/lower bound on button1/button2 */
89
90                 control = true;
91         }
92
93         /* XXX should figure out which button is left/right */
94
95         switch (ev->button) {
96                 case 1:
97                         if (control) {
98                                 set_value (left_is_decrement ? lower : upper);
99                                 return TRUE;
100                         } else {
101                                 if (left_is_decrement) {
102                                         with_decrement = true;
103                                 } else {
104                                         with_decrement = false;
105                                 }
106                         }
107                         break;
108
109                 case 2:
110                         if (!control) {
111                                 set_value (initial);
112                         }
113                         return TRUE;
114                         break;
115
116                 case 3:
117                         if (control) {
118                                 set_value (left_is_decrement ? upper : lower);
119                                 return TRUE;
120                         }
121                         break;
122
123                 case 4:
124                         if (!control) {
125                                 adjust_value (shifted ? page_increment : step_increment);
126                         } else {
127                                 set_value (upper);
128                         }
129                         return TRUE;
130                         break;
131
132                 case 5:
133                         if (!control) {
134                                 adjust_value (shifted ? -page_increment : -step_increment);
135                         } else {
136                                 set_value (lower);
137                         }
138                         return TRUE;
139                         break;
140         }
141
142         start_spinning (with_decrement, shifted);
143         return TRUE;
144 }
145
146 gint
147 AutoSpin::scroll_event (GdkEventScroll *ev)
148 {
149         stop_spinning (0);
150
151         gfloat increment = step_increment;
152
153         if (ev->state & Keyboard::TertiaryModifier) {
154                 increment = page_increment;
155         }
156
157         switch (ev->direction) {
158                 case GDK_SCROLL_DOWN:
159                 case GDK_SCROLL_LEFT:
160                         adjust_value (-increment);
161                         break;
162                 case GDK_SCROLL_RIGHT:
163                 case GDK_SCROLL_UP:
164                         adjust_value (increment);
165                         break;
166         }
167         return TRUE;
168 }
169
170 void
171 AutoSpin::start_spinning (bool decrement, bool page)
172 {
173         timer_increment = page ? page_increment : step_increment;
174
175         if (decrement) {
176                 timer_increment = -timer_increment;
177         }
178
179         adjust_value (timer_increment);
180
181         have_timer = true;
182         timer_calls = 0;
183         timeout_tag = g_timeout_add (initial_timer_interval,
184                         AutoSpin::_timer,
185                         this);
186 }
187
188 gint
189 AutoSpin::_timer (void *arg)
190 {
191         return ((AutoSpin *) arg)->timer ();
192 }
193
194 void
195 AutoSpin::set_value (gfloat value)
196 {
197         if (round_to_steps)
198                 adjustment.set_value (floor((value / step_increment) + 0.5f) * step_increment);
199         else
200                 adjustment.set_value (value);
201 }
202
203 bool
204 AutoSpin::adjust_value (gfloat increment)
205 {
206         gfloat val;
207         bool done = false;
208
209         val = adjustment.get_value ();
210
211         val += increment;
212
213         if (val > upper) {
214                 if (wrap) {
215                         val = lower;
216                 } else {
217                         val = upper;
218                         done = true;
219                 }
220         } else if (val < lower) {
221                 if (wrap) {
222                         val = upper;
223                 } else {
224                         val = lower;
225                         done = true;
226                 }
227         }
228
229         set_value (val);
230         return done;
231 }
232
233 gint
234 AutoSpin::timer ()
235 {
236         bool done;
237         int retval = FALSE;
238
239         done = adjust_value (timer_increment);
240
241         if (need_timer) {
242
243                 /* we're in the initial call, which happened
244                    after initial_timer_interval msecs. Now
245                    request a much more frequent update.
246                    */
247
248                 timeout_tag = g_timeout_add (timer_interval,
249                                 _timer,
250                                 this);
251                 have_timer = true;
252                 need_timer = false;
253
254                 /* cancel this initial timeout */
255
256                 retval = FALSE;
257
258         } else {
259                 /* this is the regular "fast" call after each
260                    timer_interval msecs.
261                    */
262
263                 if (timer_calls < climb_timer_calls) {
264                         timer_calls++;
265                 } else {
266                         if (climb_rate > 0.0) {
267                                 if (timer_increment > 0) {
268                                         timer_increment += climb_rate;
269                                 } else {
270                                         timer_increment -= climb_rate;
271                                 }
272                         }
273                         timer_calls = 0;
274                 }
275
276                 if (!done) {
277                         retval = TRUE;
278                 }
279         }
280
281         return retval;
282 }
283
284 void
285 AutoSpin::set_bounds (gfloat init, gfloat up, gfloat down, bool with_reset)
286 {
287         adjustment.set_upper (up);
288         adjustment.set_lower (down);
289
290         initial = init;
291
292         adjustment.changed ();
293
294         if (with_reset) {
295                 adjustment.set_value (init);
296         }
297 }