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