MIDI/Controllables for monitor section, and related fixes
[ardour.git] / libs / gtkmm2ext / motionfeedback.cc
1 /*
2     Copyright (C) 1998-99 Paul Barton-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: motionfeedback.cc,v 1.5 2004/03/01 03:44:19 pauld Exp $
18 */
19
20 #include <iostream>
21 #include <cmath>
22 #include <unistd.h>
23 #include <stdio.h> /* for snprintf, grrr */
24
25 #include <gdk/gdkkeysyms.h>
26 #include <gtkmm.h>
27
28 #include "gtkmm2ext/motionfeedback.h"
29 #include "gtkmm2ext/keyboard.h"
30
31 using namespace Gtk;
32 using namespace Gtkmm2ext;
33 using namespace sigc;
34
35 MotionFeedback::MotionFeedback (Glib::RefPtr<Gdk::Pixbuf> pix,
36                                 Type t,
37                                 const char *widget_name, 
38                                 Adjustment *adj,
39                                 bool with_numeric_display, int subw, int subh) 
40         : type (t)
41         , value_packer (0)
42         , value (0)
43         , pixbuf (pix)
44         , subwidth (subw)
45         , subheight (subh)
46 {
47         char value_name[1024];
48
49         if (adj == NULL) {
50             i_own_my_adjustment = true;
51             set_adjustment (new Adjustment (0, 0, 10000, 1, 10, 0));
52         } else {
53             i_own_my_adjustment = false;
54             set_adjustment (adj);
55         }
56
57         default_value = adjustment->get_value();
58
59         HBox* hpacker = manage (new HBox);
60         hpacker->pack_start (pixwin, true, false);
61         hpacker->show ();
62         pack_start (*hpacker, false, false);
63         pixwin.show ();
64
65         if (with_numeric_display) {
66
67                 value_packer = new HBox;
68                 value = new SpinButton (*adjustment);
69                 value_packer->pack_start (*value, false, false);
70
71                 if (step_inc < 1) {
72                         value->set_digits (abs ((int) ceil (log10 (step_inc))));
73                 }
74                 
75                 pack_start (*value_packer, false, false);
76
77                 if (widget_name) {
78                         snprintf (value_name, sizeof(value_name), "%sValue", widget_name);
79                         value->set_name (value_name);
80                 }
81
82                 value->show ();
83         }
84
85         adjustment->signal_value_changed().connect (mem_fun (*this, &MotionFeedback::adjustment_changed));
86
87         pixwin.set_events (Gdk::BUTTON_PRESS_MASK|
88                            Gdk::BUTTON_RELEASE_MASK|
89                            Gdk::POINTER_MOTION_MASK|
90                            Gdk::ENTER_NOTIFY_MASK|
91                            Gdk::LEAVE_NOTIFY_MASK|
92                            Gdk::SCROLL_MASK|
93                            Gdk::KEY_PRESS_MASK|
94                            Gdk::KEY_RELEASE_MASK);
95
96         pixwin.set_flags (CAN_FOCUS);
97
98         /* Proxy all important events on the pixwin to ourselves */
99
100         pixwin.signal_button_press_event().connect(mem_fun (*this,&MotionFeedback::pixwin_button_press_event));
101         pixwin.signal_button_release_event().connect(mem_fun (*this,&MotionFeedback::pixwin_button_release_event));
102         pixwin.signal_motion_notify_event().connect(mem_fun (*this,&MotionFeedback::pixwin_motion_notify_event));
103         pixwin.signal_enter_notify_event().connect(mem_fun (*this,&MotionFeedback::pixwin_enter_notify_event));
104         pixwin.signal_leave_notify_event().connect(mem_fun (*this,&MotionFeedback::pixwin_leave_notify_event));
105         pixwin.signal_key_press_event().connect(mem_fun (*this,&MotionFeedback::pixwin_key_press_event));
106         pixwin.signal_scroll_event().connect(mem_fun (*this,&MotionFeedback::pixwin_scroll_event));
107         pixwin.signal_expose_event().connect(mem_fun (*this,&MotionFeedback::pixwin_expose_event), true);
108         pixwin.signal_size_request().connect(mem_fun (*this,&MotionFeedback::pixwin_size_request));
109 }
110
111 MotionFeedback::~MotionFeedback()
112
113 {
114         if (i_own_my_adjustment) {
115                 delete adjustment;
116         }
117
118         delete value;
119         delete value_packer;
120 }
121
122 void
123 MotionFeedback::set_adjustment (Adjustment *adj)
124 {
125         adjustment = adj;
126
127         if (value) {
128                 value->set_adjustment (*adj);
129         }
130
131         _lower = adj->get_lower();
132         _upper = adj->get_upper();
133         _range = _upper - _lower;
134         step_inc = adj->get_step_increment();
135         page_inc = adj->get_page_increment();
136 }
137
138 bool
139 MotionFeedback::pixwin_button_press_event (GdkEventButton *ev) 
140
141         if (binding_proxy.button_press_handler (ev)) {
142                 return true;
143         }
144
145         switch (ev->button) {
146         case 2:
147                 return FALSE;  /* XXX why ? */
148
149         case 1:
150                 grab_is_fine = false;
151                 break;
152         case 3:
153                 grab_is_fine = true;
154                 break;
155         }
156
157         gtk_grab_add(GTK_WIDGET(pixwin.gobj()));
158         grabbed_y = ev->y_root;
159         grabbed_x = ev->x_root;
160
161         /* XXX should we return TRUE ? */
162
163         return FALSE;
164 }
165
166 bool
167 MotionFeedback::pixwin_button_release_event (GdkEventButton *ev) 
168
169         switch (ev->button) {
170         case 1:
171                 if (pixwin.has_grab()) {
172                         if (!grab_is_fine) {
173                                 gtk_grab_remove
174                                         (GTK_WIDGET(pixwin.gobj()));
175                         }
176                 }
177                 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
178                         /* shift click back to the default */
179                         adjustment->set_value (default_value);
180                         return true;
181                 }
182                 break;
183                 
184         case 3:
185                 if (pixwin.has_grab()) {
186                         if (grab_is_fine) {
187                                 gtk_grab_remove
188                                         (GTK_WIDGET(pixwin.gobj()));
189                         }
190                 }
191                 break;
192         }
193
194         return VBox::on_button_release_event (ev); 
195 }
196
197 bool
198 MotionFeedback::pixwin_motion_notify_event (GdkEventMotion *ev) 
199
200         gfloat multiplier;
201         gfloat x_delta;
202         gfloat y_delta;
203
204         if(!pixwin.has_grab()) {
205                 return VBox::on_motion_notify_event (ev); 
206         }
207
208         multiplier = ((ev->state & Keyboard::TertiaryModifier) ? 100 : 1) *
209                 ((ev->state & Keyboard::SecondaryModifier) ? 10 : 1) * 
210                 ((ev->state & Keyboard::PrimaryModifier) ? 2 : 1);
211
212         y_delta = grabbed_y - ev->y_root;
213         grabbed_y = ev->y_root;
214
215         x_delta = ev->x_root - grabbed_x;
216
217         if (y_delta == 0) return TRUE;
218
219         y_delta *= 1 + (x_delta/100);
220         y_delta *= multiplier;
221         y_delta /= 10;
222
223         adjustment->set_value (adjustment->get_value() + 
224                                ((grab_is_fine ? step_inc : page_inc) * y_delta));
225
226         return true;
227 }
228
229 bool
230 MotionFeedback::pixwin_enter_notify_event (GdkEventCrossing *ev) 
231 {
232         pixwin.grab_focus();
233         return false;
234 }
235
236 bool
237 MotionFeedback::pixwin_leave_notify_event (GdkEventCrossing *ev) 
238 {
239         pixwin.unset_flags (HAS_FOCUS);
240         return false;
241 }
242
243 bool
244 MotionFeedback::pixwin_key_press_event (GdkEventKey *ev) 
245 {
246         bool retval = false;
247         gfloat curval;
248         gfloat multiplier;
249
250         multiplier = ((ev->state & Keyboard::TertiaryModifier) ? 100 : 1) *
251                 ((ev->state & Keyboard::SecondaryModifier) ? 10 : 1) * 
252                 ((ev->state & Keyboard::PrimaryModifier) ? 2 : 1);
253
254         switch (ev->keyval) {
255         case GDK_Page_Up:
256                 retval = true;
257                 curval = adjustment->get_value();
258                 adjustment->set_value (curval + (multiplier * page_inc));
259                 break;
260
261         case GDK_Page_Down:
262                 retval = true;
263                 curval = adjustment->get_value();
264                 adjustment->set_value (curval - (multiplier * page_inc));
265                 break;
266
267         case GDK_Up:
268                 retval = true;
269                 curval = adjustment->get_value();
270                 adjustment->set_value (curval + (multiplier * step_inc));
271                 break;
272
273         case GDK_Down:
274                 retval = true;
275                 curval = adjustment->get_value();
276                 adjustment->set_value (curval - (multiplier * step_inc));
277                 break;
278
279         case GDK_Home:
280                 retval = true;
281                 adjustment->set_value (_lower);
282                 break;
283
284         case GDK_End:
285                 retval = true;
286                 adjustment->set_value (_upper);
287                 break;
288         }
289         
290         return retval;
291 }
292
293 void
294 MotionFeedback::adjustment_changed ()
295 {
296         pixwin.queue_draw ();
297 }
298
299 bool
300 MotionFeedback::pixwin_expose_event (GdkEventExpose* ev)
301 {
302         GtkWidget* widget = GTK_WIDGET(pixwin.gobj());
303         GdkWindow *window = pixwin.get_window()->gobj();
304         GtkAdjustment* adj = adjustment->gobj();
305
306         int phase = (int)((adj->value - adj->lower) * 64 / 
307                           (adj->upper - adj->lower));
308
309         // skip middle phase except for true middle value
310
311         if (type == Rotary && phase == 32) {
312                 double pt = (adj->value - adj->lower) * 2.0 / 
313                         (adj->upper - adj->lower) - 1.0;
314                 if (pt < 0)
315                         phase = 31;
316                 if (pt > 0)
317                         phase = 33;
318         }
319
320         // endless knob: skip 90deg highlights unless the value is really a multiple of 90deg
321
322         if (type == Endless && !(phase % 16)) {
323                 if (phase == 64) {
324                         phase = 0;
325                 }
326
327                 double nom = adj->lower + phase * (adj->upper - adj->lower) / 64.0;
328                 double diff = (adj->value - nom) / (adj->upper - adj->lower);
329
330                 if (diff > 0.0001)
331                         phase = (phase + 1) % 64;
332                 if (diff < -0.0001)
333                         phase = (phase + 63) % 64;
334         }
335
336         gdk_draw_pixbuf (GDK_DRAWABLE(window), widget->style->fg_gc[0], 
337                          pixbuf->gobj(), 
338                          phase * subwidth, type * subheight, 
339                          0, 0, subwidth, subheight, GDK_RGB_DITHER_NORMAL, 0, 0);
340         
341         return true;
342 }
343
344 bool
345 MotionFeedback::pixwin_scroll_event (GdkEventScroll* ev)
346 {
347         double scale;
348
349         if ((ev->state & (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier)) == (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier)) {
350                 scale = 0.01;
351         } else if (ev->state & Keyboard::PrimaryModifier) {
352                 scale = 0.1;
353         } else {
354                 scale = 1.0;
355         }
356
357         switch (ev->direction) {
358         case GDK_SCROLL_UP:
359         case GDK_SCROLL_RIGHT:
360                 adjustment->set_value (adjustment->get_value() + (scale * adjustment->get_step_increment()));
361                 break;
362
363         case GDK_SCROLL_DOWN:
364         case GDK_SCROLL_LEFT:
365                 adjustment->set_value (adjustment->get_value() - (scale * adjustment->get_step_increment()));
366                 break;
367         }
368
369         return true;
370 }
371
372 void
373 MotionFeedback::pixwin_size_request (GtkRequisition* req)
374 {
375         req->width = subwidth;
376         req->height = subheight;
377 }