fulfill the promise of an in-library Keyboard object, and use platform specific modif...
[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->state & Keyboard::TertiaryModifier) {
78                 /* use page shift */
79
80                 shifted = true;
81         }
82
83         if (ev->state & Keyboard::PrimaryModifier) {
84                 /* go to upper/lower bound on button1/button2 */
85
86                 control = true;
87         }
88
89         /* XXX should figure out which button is left/right */
90
91         switch (ev->button) {
92         case 1:
93                 if (control) {
94                         set_value (left_is_decrement ? lower : upper);
95                         return TRUE;
96                 } else {
97                         if (left_is_decrement) {
98                                 with_decrement = true;
99                         } else {
100                                 with_decrement = false;
101                         }
102                 }
103                 break;
104
105         case 2:
106                 if (!control) {
107                         set_value (initial);
108                 }
109                 return TRUE;
110                 break;
111
112         case 3:
113                 if (control) {
114                         set_value (left_is_decrement ? upper : lower);
115                         return TRUE;
116                 }
117                 break;
118
119         case 4:
120                 if (!control) {
121                         adjust_value (shifted ? page_increment : step_increment);
122                 } else {
123                         set_value (upper);
124                 }
125                 return TRUE;
126                 break;
127
128         case 5:
129                 if (!control) {
130                         adjust_value (shifted ? -page_increment : -step_increment);
131                 } else {
132                         set_value (lower);
133                 }
134                 return TRUE;
135                 break;
136         } 
137
138         start_spinning (with_decrement, shifted);
139         return TRUE;
140 }
141
142 void
143 AutoSpin::start_spinning (bool decrement, bool page)
144 {
145         timer_increment = page ? page_increment : step_increment;
146
147         if (decrement) { 
148                 timer_increment = -timer_increment;
149         }
150
151         adjust_value (timer_increment);
152         
153         have_timer = true;
154         timer_calls = 0;
155         timeout_tag = g_timeout_add (initial_timer_interval,
156                                        AutoSpin::_timer,
157                                        this);
158 }
159
160 gint
161 AutoSpin::_timer (void *arg)
162 {
163         return ((AutoSpin *) arg)->timer ();
164 }
165
166 void
167 AutoSpin::set_value (gfloat value)
168 {
169         if (round_to_steps)
170                 adjustment.set_value (floor((value / step_increment) + 0.5f) * step_increment);
171         else
172                 adjustment.set_value (value);
173 }
174
175 bool
176 AutoSpin::adjust_value (gfloat increment)
177 {
178         gfloat val;
179         bool done = false;
180
181         val = adjustment.get_value();
182
183         val += increment;
184
185         if (val > upper) {
186                 if (wrap) {
187                         val = lower;
188                 } else {
189                         val = upper;
190                         done = true;
191                 }
192         } else if (val < lower) {
193                 if (wrap) {
194                         val = upper;
195                 } else {
196                         val = lower;
197                         done = true;
198                 }
199         }
200
201         set_value(val);
202         return done;
203 }
204
205 gint
206 AutoSpin::timer ()
207 {
208         bool done;
209         int retval = FALSE;
210
211         done = adjust_value (timer_increment);
212
213         if (need_timer) {
214
215                 /* we're in the initial call, which happened
216                    after initial_timer_interval msecs. Now
217                    request a much more frequent update.
218                 */
219                 
220                 timeout_tag = g_timeout_add (timer_interval,
221                                                _timer,
222                                                this);
223                 have_timer = true;
224                 need_timer = false;
225
226                 /* cancel this initial timeout */
227                 
228                 retval = FALSE;
229
230         } else { 
231                 /* this is the regular "fast" call after each
232                    timer_interval msecs. 
233                 */
234
235                 if (timer_calls < climb_timer_calls) {
236                         timer_calls++;
237                 } else {
238                         if (climb_rate > 0.0) {
239                                 if (timer_increment > 0) {
240                                         timer_increment += climb_rate;
241                                 } else {
242                                         timer_increment -= climb_rate;
243                                 }
244                         }
245                         timer_calls = 0;
246                 }
247
248                 if (!done) {
249                         retval = TRUE;
250                 }
251         }
252
253         return retval;
254 }       
255
256 void
257 AutoSpin::set_bounds (gfloat init, gfloat up, gfloat down, bool with_reset)
258 {
259         adjustment.set_upper(up);
260         adjustment.set_lower(down);
261
262         initial = init;
263         
264         adjustment.changed ();
265         
266         if (with_reset) {
267                 adjustment.set_value (init);
268         }
269