60ec7e1cd6d686a69d586118d2f2c389d7b8c995
[ardour.git] / libs / gtkmm2ext / barcontroller.cc
1 /*
2     Copyright (C) 2004 Paul Davis
3     This program is free software; you can redistribute it and/or modify
4     it under the terms of the GNU General Public License as published by
5     the Free Software Foundation; either version 2 of the License, or
6     (at your option) any later version.
7
8     This program is distributed in the hope that it will be useful,
9     but WITHOUT ANY WARRANTY; without even the implied warranty of
10     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11     GNU General Public License for more details.
12
13     You should have received a copy of the GNU General Public License
14     along with this program; if not, write to the Free Software
15     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
16
17     $Id$
18 */
19
20 #include <string>
21 #include <climits>
22 #include <cstdio>
23 #include <cmath>
24 #include <algorithm>
25
26 #include <pbd/controllable.h>
27
28 #include <gtkmm2ext/gtk_ui.h>
29 #include <gtkmm2ext/utils.h>
30 #include <gtkmm2ext/barcontroller.h>
31
32 #include "i18n.h"
33
34 using namespace std;
35 using namespace Gtk;
36 using namespace Gtkmm2ext;
37
38 BarController::BarController (Gtk::Adjustment& adj,
39                               boost::shared_ptr<PBD::Controllable> mc)
40
41         : adjustment (adj),
42           binding_proxy (mc),
43           spinner (adjustment)
44
45 {                         
46         _style = LeftToRight;
47         grabbed = false;
48         switching = false;
49         switch_on_release = false;
50         use_parent = false;
51
52         layout = darea.create_pango_layout("");
53
54         set_shadow_type (SHADOW_NONE);
55
56         initial_value = adjustment.get_value ();
57
58         adjustment.signal_value_changed().connect (mem_fun (*this, &Gtk::Widget::queue_draw));
59         adjustment.signal_changed().connect (mem_fun (*this, &Gtk::Widget::queue_draw));
60
61         darea.add_events (Gdk::BUTTON_RELEASE_MASK|
62                           Gdk::BUTTON_PRESS_MASK|
63                           Gdk::POINTER_MOTION_MASK|
64                           Gdk::ENTER_NOTIFY_MASK|
65                           Gdk::LEAVE_NOTIFY_MASK|
66                           Gdk::SCROLL_MASK);
67
68         darea.signal_expose_event().connect (mem_fun (*this, &BarController::expose));
69         darea.signal_motion_notify_event().connect (mem_fun (*this, &BarController::motion));
70         darea.signal_button_press_event().connect (mem_fun (*this, &BarController::button_press), false);
71         darea.signal_button_release_event().connect (mem_fun (*this, &BarController::button_release), false);
72         darea.signal_scroll_event().connect (mem_fun (*this, &BarController::scroll));
73
74         spinner.signal_activate().connect (mem_fun (*this, &BarController::entry_activated));
75         spinner.signal_focus_out_event().connect (mem_fun (*this, &BarController::entry_focus_out));
76         spinner.signal_input().connect (mem_fun (*this, &BarController::entry_input));
77         spinner.signal_output().connect (mem_fun (*this, &BarController::entry_output));
78         spinner.set_digits (3);
79
80         add (darea);
81         show_all ();
82 }
83
84 void
85 BarController::drop_grab ()
86 {
87         if (grabbed) {
88                 grabbed = false;
89                 darea.remove_modal_grab();
90                 StopGesture ();
91         }
92 }
93
94 bool
95 BarController::button_press (GdkEventButton* ev)
96 {
97         double fract;
98
99         if (binding_proxy.button_press_handler (ev)) {
100                 return true;
101         }
102
103         switch (ev->button) {
104         case 1:
105                 if (ev->type == GDK_2BUTTON_PRESS) {
106                         switch_on_release = true;
107                         drop_grab ();
108                 } else {
109                         switch_on_release = false;
110                         darea.add_modal_grab();
111                         grabbed = true;
112                         grab_x = ev->x;
113                         grab_window = ev->window;
114                         StartGesture ();
115                 }
116                 return true;
117                 break;
118
119         case 2:
120                 fract = ev->x / (darea.get_width() - 2.0);
121                 adjustment.set_value (adjustment.get_lower() + fract * (adjustment.get_upper() - adjustment.get_lower()));
122
123         case 3:
124                 break;
125
126         case 4:
127         case 5:
128                 break;
129         }
130
131         return false;
132 }
133
134 bool
135 BarController::button_release (GdkEventButton* ev)
136 {
137         drop_grab ();
138         
139         switch (ev->button) {
140         case 1:
141                 if (switch_on_release) {
142                         Glib::signal_idle().connect (mem_fun (*this, &BarController::switch_to_spinner));
143                         return true;
144                 }
145
146                 if ((ev->state & (GDK_SHIFT_MASK|GDK_CONTROL_MASK)) == GDK_SHIFT_MASK) {
147                         adjustment.set_value (initial_value);
148                 } else {
149                         double scale;
150
151                         if ((ev->state & (GDK_CONTROL_MASK|GDK_SHIFT_MASK)) == (GDK_CONTROL_MASK|GDK_SHIFT_MASK)) {
152                                 scale = 0.01;
153                         } else if (ev->state & GDK_CONTROL_MASK) {
154                                 scale = 0.1;
155                         } else {
156                                 scale = 1.0;
157                         }
158
159                         mouse_control (ev->x, ev->window, scale);
160                 }
161                 break;
162
163         case 2:
164                 break;
165                 
166         case 3:
167                 return false;
168                 
169         default:
170                 break;
171         }
172
173         return true;
174 }
175
176 bool
177 BarController::scroll (GdkEventScroll* ev)
178 {
179         double scale;
180
181         if ((ev->state & (GDK_CONTROL_MASK|GDK_SHIFT_MASK)) == (GDK_CONTROL_MASK|GDK_SHIFT_MASK)) {
182                 scale = 0.01;
183         } else if (ev->state & GDK_CONTROL_MASK) {
184                 scale = 0.1;
185         } else {
186                 scale = 1.0;
187         }
188
189         switch (ev->direction) {
190         case GDK_SCROLL_UP:
191         case GDK_SCROLL_RIGHT:
192                 adjustment.set_value (adjustment.get_value() + (scale * adjustment.get_step_increment()));
193                 break;
194
195         case GDK_SCROLL_DOWN:
196         case GDK_SCROLL_LEFT:
197                 adjustment.set_value (adjustment.get_value() - (scale * adjustment.get_step_increment()));
198                 break;
199         }
200
201         return true;
202 }
203
204 bool
205 BarController::motion (GdkEventMotion* ev)
206 {
207         double scale;
208         
209         if (!grabbed) {
210                 return true;
211         }
212
213         if ((ev->state & (GDK_SHIFT_MASK|GDK_CONTROL_MASK)) == GDK_SHIFT_MASK) {
214                 return TRUE;
215         }
216
217         if ((ev->state & (GDK_CONTROL_MASK|GDK_SHIFT_MASK)) == (GDK_CONTROL_MASK|GDK_SHIFT_MASK)) {
218                 scale = 0.01;
219         } else if (ev->state & GDK_CONTROL_MASK) {
220                 scale = 0.1;
221         } else {
222                 scale = 1.0;
223         }
224
225         return mouse_control (ev->x, ev->window, scale);
226 }
227
228 gint
229 BarController::mouse_control (double x, GdkWindow* window, double scaling)
230 {
231         double fract = 0.0;
232         double delta;
233
234         if (window != grab_window) {
235                 grab_x = x;
236                 grab_window = window;
237                 return TRUE;
238         }
239
240         delta = x - grab_x;
241         grab_x = x;
242
243         switch (_style) {
244         case Line:
245         case LeftToRight:
246                 fract = scaling * (delta / (darea.get_width() - 2));
247                 fract = min (1.0, fract);
248                 fract = max (-1.0, fract);
249                 adjustment.set_value (adjustment.get_value() + fract * (adjustment.get_upper() - adjustment.get_lower()));
250                 break;
251
252         default:
253                 fract = 0.0;
254         }
255         
256         
257         return TRUE;
258 }
259
260 bool
261 BarController::expose (GdkEventExpose* /*event*/)
262 {
263         Glib::RefPtr<Gdk::Window> win (darea.get_window());
264         Widget* parent;
265         gint x1=0, x2=0, y1=0, y2=0;
266         gint w, h;
267         double fract;
268
269         fract = ((adjustment.get_value() - adjustment.get_lower()) /
270                  (adjustment.get_upper() - adjustment.get_lower()));
271         
272         switch (_style) {
273         case Line:
274                 w = darea.get_width() - 1;
275                 h = darea.get_height();
276                 x1 = (gint) floor (w * fract);
277                 x2 = x1;
278                 y1 = 0;
279                 y2 = h - 1;
280
281                 if (use_parent) {
282                         parent = get_parent();
283                         
284                         if (parent) {
285                                 win->draw_rectangle (parent->get_style()->get_fg_gc (parent->get_state()),
286                                                      true,
287                                                      0, 0, darea.get_width(), darea.get_height());
288                         }
289
290                 } else {
291
292                         win->draw_rectangle (get_style()->get_bg_gc (get_state()),
293                                              true,
294                                              0, 0, darea.get_width() - ((darea.get_width()+1) % 2), darea.get_height());
295                 }
296                 
297                 win->draw_line (get_style()->get_fg_gc (get_state()), x1, 0, x1, h);
298                 break;
299
300         case CenterOut:
301                 break;
302
303         case LeftToRight:
304
305                 w = darea.get_width() - 2;
306                 h = darea.get_height() - 2;
307
308                 x1 = 0;
309                 x2 = (gint) floor (w * fract);
310                 y1 = 0;
311                 y2 = h - 1;
312
313                 win->draw_rectangle (get_style()->get_bg_gc (get_state()),
314                                     false,
315                                     0, 0, darea.get_width() - 1, darea.get_height() - 1);
316
317                 /* draw active box */
318
319                 win->draw_rectangle (get_style()->get_fg_gc (get_state()),
320                                     true,
321                                     1 + x1,
322                                     1 + y1,
323                                     x2,
324                                     1 + y2);
325                 
326                 /* draw inactive box */
327
328                 win->draw_rectangle (get_style()->get_fg_gc (STATE_INSENSITIVE),
329                                     true,
330                                     1 + x2,
331                                     1 + y1,
332                                     w - x2,
333                                     1 + y2);
334
335                 break;
336
337         case RightToLeft:
338                 break;
339         case TopToBottom:
340                 break;
341         case BottomToTop:
342                 break;
343         }
344
345         /* draw label */
346
347         int xpos = -1;
348         std::string const label = get_label (xpos);
349
350         if (!label.empty()) {
351                 
352                 layout->set_text (label);
353                 
354                 int width, height;
355                 layout->get_pixel_size (width, height);
356
357                 if (xpos == -1) {
358                         xpos = max (3, 1 + (x2 - (width/2)));
359                         xpos = min (darea.get_width() - width - 3, xpos);
360                 }
361                 
362                 win->draw_layout (get_style()->get_text_gc (get_state()),
363                                   xpos,
364                                   (darea.get_height()/2) - (height/2),
365                                   layout);
366         }
367         
368         return true;
369 }
370
371 void
372 BarController::set_style (Style s)
373 {
374         _style = s;
375         darea.queue_draw ();
376 }
377
378 gint
379 BarController::switch_to_bar ()
380 {
381         if (switching) {
382                 return FALSE;
383         }
384
385         switching = true;
386
387         if (get_child() == &darea) {
388                 return FALSE;
389         }
390
391         remove ();
392         add (darea);
393         darea.show ();
394
395         switching = false;
396         return FALSE;
397 }
398
399 gint
400 BarController::switch_to_spinner ()
401 {
402         if (switching) {
403                 return FALSE;
404         }
405
406         switching = true;
407
408         if (get_child() == &spinner) {
409                 return FALSE;
410         }
411
412         remove ();
413         add (spinner);
414         spinner.show ();
415         spinner.select_region (0, spinner.get_text_length());
416         spinner.grab_focus ();
417
418         switching = false;
419         return FALSE;
420 }
421
422 void
423 BarController::entry_activated ()
424 {
425         string text = spinner.get_text ();
426         float val;
427
428         if (sscanf (text.c_str(), "%f", &val) == 1) {
429                 adjustment.set_value (val);
430         }
431         
432         switch_to_bar ();
433 }
434
435 bool
436 BarController::entry_focus_out (GdkEventFocus* /*ev*/)
437 {
438         entry_activated ();
439         return true;
440 }
441
442 void
443 BarController::set_use_parent (bool yn)
444 {
445         use_parent = yn;
446         queue_draw ();
447 }
448
449 void
450 BarController::set_sensitive (bool yn)
451 {
452         Frame::set_sensitive (yn);
453         darea.set_sensitive (yn);
454 }
455
456 bool
457 BarController::entry_input (double* /*v*/)
458 {
459         return false;
460 }
461
462 bool
463 BarController::entry_output ()
464 {
465         return false;
466 }
467
468