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