b063f43d51c385edf33e719c4a27621b5f8904dc
[terminatorX.git] / src / tX_dial.c
1 /*
2     terminatorX - realtime audio scratching software
3     Copyright (C) 1999-2016  Alexander K├Ânig
4  
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9  
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14  
15     You should have received a copy of the GNU General Public License
16     along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  
18     File: tX_dial.ch
19  
20     Description: Implements the dial widget - this widget is based on the 
21     gtk_dial example from the gtk+ tutorial which is
22     Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
23 */    
24
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28
29 #include <math.h>
30 #include <stdio.h>
31 #include <gtk/gtk.h>
32 #include "tX_knobloader.h"
33
34 #include "tX_dial.h"
35
36 #define SCROLL_DELAY_LENGTH  300
37 #define TX_DIAL_DEFAULT_SIZE 100
38
39 /* Forward declarations */
40
41 static void gtk_tx_dial_class_init              (GtkTxDialClass *klass);
42 static void gtk_tx_dial_init                    (GtkTxDial *tx_dial);
43 static void gtk_tx_dial_destroy                 (GtkWidget *widget);
44 static void gtk_tx_dial_realize                 (GtkWidget *widget);
45
46 static void gtk_tx_dial_get_preferred_width (GtkWidget *widget, gint *minimal_height, gint *natural_height);
47 static void gtk_tx_dial_get_preferred_height (GtkWidget *widget, gint *minimal_height, gint *natural_height);
48 static void gtk_tx_dial_size_allocate   (GtkWidget *widget, GtkAllocation *allocation);
49 static gboolean gtk_tx_dial_draw                (GtkWidget *widget, cairo_t* cairo);
50 static gint gtk_tx_dial_button_press    (GtkWidget *widget, GdkEventButton *event);
51 static gint gtk_tx_dial_button_release  (GtkWidget *widget, GdkEventButton *event);
52 static gint gtk_tx_dial_motion_notify   (GtkWidget *widget, GdkEventMotion *event);
53 static void gtk_tx_dial_update_mouse    (GtkTxDial *tx_dial, gint x, gint y);
54 static void gtk_tx_dial_update                  (GtkTxDial *tx_dial);
55 static void gtk_tx_dial_adjustment_changed                      (GtkAdjustment *adjustment, gpointer data);
56 static void gtk_tx_dial_adjustment_value_changed        (GtkAdjustment *adjustment, gpointer data);
57
58 /* Local data */
59
60 static GtkWidgetClass *parent_class = NULL;
61
62 #define calc_image(f,i); i=(gint) ((f - tx_dial->old_lower)/(tx_dial->old_range) * ((float) TX_MAX_KNOB_PIX)); if(i>TX_MAX_KNOB_PIX) i=TX_MAX_KNOB_PIX; else if (i<0) i=0;
63
64 GType gtk_tx_dial_get_type ()
65 {
66         static GType tx_dial_type = 0;
67
68         if (!tx_dial_type) {
69                 static const GTypeInfo tx_dial_info = {
70                         sizeof (GtkTxDialClass),
71                         NULL,
72                         NULL,
73                         (GClassInitFunc) gtk_tx_dial_class_init, 
74                         NULL,
75                         NULL,
76                         sizeof (GtkTxDial),
77                 0,
78                         (GInstanceInitFunc) gtk_tx_dial_init,
79                 };
80
81                 tx_dial_type = g_type_register_static(GTK_TYPE_WIDGET, "GtkTxDial", &tx_dial_info, 0);
82     }
83         
84         return tx_dial_type;
85 }
86
87 static void gtk_tx_dial_class_init (GtkTxDialClass *class)
88 {
89         GtkWidgetClass *widget_class;
90
91         widget_class = (GtkWidgetClass*) class;
92
93         parent_class = (GtkWidgetClass*) g_type_class_peek (gtk_widget_get_type ());
94
95         widget_class->destroy = gtk_tx_dial_destroy;
96
97         widget_class->realize = gtk_tx_dial_realize;
98         widget_class->draw = gtk_tx_dial_draw;
99         widget_class->get_preferred_height = gtk_tx_dial_get_preferred_height;
100         widget_class->get_preferred_width = gtk_tx_dial_get_preferred_width;
101         widget_class->size_allocate = gtk_tx_dial_size_allocate;
102         widget_class->button_press_event = gtk_tx_dial_button_press;
103         widget_class->button_release_event = gtk_tx_dial_button_release;
104         widget_class->motion_notify_event = gtk_tx_dial_motion_notify;
105 }
106
107 static void gtk_tx_dial_init (GtkTxDial *tx_dial)
108 {
109         tx_dial->button = 0;
110
111         tx_dial->old_value = 0.0;
112         tx_dial->old_lower = 0.0;
113         tx_dial->old_upper = 0.0;
114         tx_dial->old_range = 0.0; // Dangerous!
115
116         tx_dial->old_image = 0;
117
118         tx_dial->yofs=0;
119         tx_dial->xofs=0;
120
121         tx_dial->adjustment = NULL;
122 }
123
124 GtkWidget* gtk_tx_dial_new (GtkAdjustment *adjustment)
125 {
126         GtkTxDial *tx_dial;
127         
128         tx_dial = (GtkTxDial *) g_object_new(gtk_tx_dial_get_type(), NULL);
129         
130         if (!adjustment) {
131                 adjustment = (GtkAdjustment*) gtk_adjustment_new (0.0, 0.0, 0.0,
132                                           0.0, 0.0, 0.0);
133         }
134
135         gtk_tx_dial_set_adjustment (tx_dial, adjustment);
136         g_object_ref (G_OBJECT (tx_dial->adjustment));
137
138         return GTK_WIDGET (tx_dial);
139 }
140
141 static void gtk_tx_dial_destroy (GtkWidget *widget)
142 {
143         GtkTxDial *tx_dial;
144
145         g_return_if_fail (widget != NULL);
146         g_return_if_fail (GTK_IS_TX_DIAL (widget));
147         
148         tx_dial = GTK_TX_DIAL (widget);
149
150         if (tx_dial->adjustment)
151                 g_object_unref (G_OBJECT (tx_dial->adjustment));
152         
153         if (GTK_WIDGET_CLASS (parent_class)->destroy)
154                 (*GTK_WIDGET_CLASS (parent_class)->destroy) (widget);
155 }
156
157 GtkAdjustment* gtk_tx_dial_get_adjustment (GtkTxDial *tx_dial)
158 {
159         g_return_val_if_fail (tx_dial != NULL, NULL);
160         g_return_val_if_fail (GTK_IS_TX_DIAL (tx_dial), NULL);
161         
162         return tx_dial->adjustment;
163 }
164
165 void gtk_tx_dial_set_adjustment (GtkTxDial *tx_dial, GtkAdjustment *adjustment)
166 {
167         g_return_if_fail (tx_dial != NULL);
168         g_return_if_fail (GTK_IS_TX_DIAL (tx_dial));
169         
170         if (tx_dial->adjustment) {
171                 g_signal_handlers_disconnect_matched(G_OBJECT(tx_dial->adjustment),
172                         G_SIGNAL_MATCH_DATA, 0, 0, 0, 0,
173                         (gpointer) tx_dial);
174                 g_object_unref (G_OBJECT (tx_dial->adjustment));
175         }
176         
177         tx_dial->adjustment = adjustment;
178         g_object_ref (G_OBJECT (tx_dial->adjustment));
179
180         g_signal_connect (G_OBJECT (adjustment), "changed",
181                           (GCallback) gtk_tx_dial_adjustment_changed,
182                           (gpointer) tx_dial);
183         g_signal_connect (G_OBJECT (adjustment), "value_changed",
184                           (GCallback) gtk_tx_dial_adjustment_value_changed,
185                           (gpointer) tx_dial);
186
187         tx_dial->old_value = gtk_adjustment_get_value(adjustment);
188         tx_dial->old_lower = gtk_adjustment_get_lower(adjustment);
189         tx_dial->old_upper = gtk_adjustment_get_upper(adjustment);
190         tx_dial->old_range = gtk_adjustment_get_upper(adjustment) - gtk_adjustment_get_lower(adjustment);
191
192         calc_image(gtk_adjustment_get_value(adjustment),tx_dial->old_image);
193
194         gtk_tx_dial_update (tx_dial);
195 }
196
197 static void gtk_tx_dial_realize (GtkWidget *widget)
198 {
199         GdkWindowAttr attributes;
200         gint attributes_mask;
201         
202         g_return_if_fail (widget != NULL);
203         g_return_if_fail (GTK_IS_TX_DIAL (widget));
204         
205         gtk_widget_set_realized(widget, TRUE);
206         
207         GtkAllocation allocation;
208         gtk_widget_get_allocation(widget, &allocation);
209         attributes.x = allocation.x;
210         attributes.y = allocation.y;
211         attributes.width = allocation.width;
212         attributes.height = allocation.height;
213         attributes.wclass = GDK_INPUT_OUTPUT;
214         attributes.window_type = GDK_WINDOW_CHILD;
215         attributes.event_mask = gtk_widget_get_events (widget) | 
216                 GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | 
217                 GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK |
218                 GDK_POINTER_MOTION_HINT_MASK;
219         attributes.visual = gtk_widget_get_visual (widget);
220                 
221         attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
222         gtk_widget_set_window(widget, gdk_window_new(gtk_widget_get_parent_window(widget), &attributes, attributes_mask));
223         
224         gdk_window_set_user_data (gtk_widget_get_window(widget), widget);
225         gtk_style_context_set_background(gtk_widget_get_style_context(widget), gtk_widget_get_window(widget));
226 }
227
228 static void gtk_tx_dial_get_preferred_width (GtkWidget *widget, gint *minimal_width, gint *natural_width) {
229         *minimal_width = *natural_width = tX_knob_size;
230 }
231 static void gtk_tx_dial_get_preferred_height (GtkWidget *widget, gint *minimal_height, gint *natural_height) {
232         *minimal_height = *natural_height = tX_knob_size;
233 }
234
235 static void gtk_tx_dial_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
236 {
237         GtkTxDial *tx_dial;
238         
239         g_return_if_fail (widget != NULL);
240         g_return_if_fail (GTK_IS_TX_DIAL (widget));
241         g_return_if_fail (allocation != NULL);
242         
243         gtk_widget_set_allocation(widget, allocation);
244         tx_dial = GTK_TX_DIAL (widget);
245         
246         if (gtk_widget_get_realized (widget)) {
247                 gdk_window_move_resize (gtk_widget_get_window(widget),
248                                   allocation->x, allocation->y,
249                                   allocation->width, allocation->height);
250                 
251                 tx_dial->xofs=(allocation->width-tX_knob_size)/2;
252                 tx_dial->yofs=(allocation->height-tX_knob_size)/2;
253         }
254 }
255
256 inline void gtk_tx_dial_do_draw (GtkTxDial *tx_dial, GtkWidget *widget, cairo_t *cr)
257 {
258         if (gtk_widget_is_drawable (widget)) {
259 //              gdk_draw_pixbuf(gtk_widget_is_drawable (widget), 
260 //                              //gtk_widget_get_stlye(widget)->bg_gc[GTK_WIDGET_STATE(widget)],
261 //                              NULL, //TODO: this needs to be ported to cairo!
262 //                              knob_pixmaps[tx_dial->old_image],
263 //                              0, 0, tx_dial->xofs, tx_dial->yofs,
264 //                                              tX_knob_size, tX_knob_size, GDK_RGB_DITHER_NORMAL, 0, 0);
265
266                 gdk_cairo_set_source_pixbuf (cr, knob_pixmaps[tx_dial->old_image], 0, 0);
267                 cairo_paint (cr);
268         }                
269 }
270
271 gboolean gtk_tx_dial_draw (GtkWidget *widget, cairo_t *cr)
272 {
273         GtkTxDial *tx_dial;
274         
275         g_return_val_if_fail (widget != NULL, FALSE);
276         g_return_val_if_fail (GTK_IS_TX_DIAL (widget), FALSE);
277         
278         tx_dial = GTK_TX_DIAL (widget);
279         
280         gtk_tx_dial_do_draw(tx_dial, widget, cr);
281                   
282         return FALSE;
283 }
284
285 static gint gtk_tx_dial_button_press (GtkWidget *widget, GdkEventButton *event)
286 {
287         GtkTxDial *tx_dial;
288         
289         g_return_val_if_fail (widget != NULL, FALSE);
290         g_return_val_if_fail (GTK_IS_TX_DIAL (widget), FALSE);
291         g_return_val_if_fail (event != NULL, FALSE);
292         
293         tx_dial = GTK_TX_DIAL (widget);
294         
295         tx_dial->x = event->x;
296         tx_dial->y = event->y;    
297         
298         if (!tx_dial->button) {
299                 gtk_grab_add (widget);
300                 tx_dial->button = event->button;
301         }
302         
303         return FALSE;
304 }
305
306 static gint gtk_tx_dial_button_release (GtkWidget *widget, GdkEventButton *event)
307 {
308         GtkTxDial *tx_dial;
309         
310         g_return_val_if_fail (widget != NULL, FALSE);
311         g_return_val_if_fail (GTK_IS_TX_DIAL (widget), FALSE);
312         g_return_val_if_fail (event != NULL, FALSE);
313         
314         tx_dial = GTK_TX_DIAL (widget);
315         
316         if (tx_dial->button == event->button) {
317                 gtk_grab_remove (widget);
318                 tx_dial->button = 0;
319         }
320         
321         return FALSE;
322 }
323
324 static gint gtk_tx_dial_motion_notify (GtkWidget *widget, GdkEventMotion *event)
325 {
326         GtkTxDial *tx_dial;
327         GdkModifierType mods;
328         gint x, y, mask;
329         
330         g_return_val_if_fail (widget != NULL, FALSE);
331         g_return_val_if_fail (GTK_IS_TX_DIAL (widget), FALSE);
332         g_return_val_if_fail (event != NULL, FALSE);
333         
334         tx_dial = GTK_TX_DIAL (widget);
335         
336         if (tx_dial->button != 0) {
337                 x = event->x;
338                 y = event->y;
339                 
340                 if (event->is_hint || (event->window != gtk_widget_get_window(widget)))
341                         gdk_window_get_device_position(gtk_widget_get_window(widget), event->device, &x, &y, &mods);
342                 
343                 switch (tx_dial->button) {
344                         case 1:
345                                 mask = GDK_BUTTON1_MASK;
346                                 break;
347                         case 2:
348                                 mask = GDK_BUTTON2_MASK;
349                                 break;
350                         case 3:
351                                 mask = GDK_BUTTON3_MASK;
352                                 break;
353                         default:
354                                 mask = 0;
355                 }
356                 
357                 if (mods & mask)
358                         gtk_tx_dial_update_mouse (tx_dial, x,y);
359         }
360         
361         return FALSE;
362 }
363
364 static void gtk_tx_dial_update_mouse (GtkTxDial *tx_dial, gint x, gint y)
365 {
366         gdouble dx, dy, d;
367         gfloat old_value, new_value;
368
369         g_return_if_fail (tx_dial != NULL);
370         g_return_if_fail (GTK_IS_TX_DIAL (tx_dial));
371         
372         dx=x-tx_dial->x;
373         dy=tx_dial->y-y;
374         tx_dial->x=x;
375         tx_dial->y=y;
376         
377         d=dx+dy;
378         d/=200.0;
379         
380         old_value=gtk_adjustment_get_value(tx_dial->adjustment);    
381         new_value=old_value + d*tx_dial->old_range;
382         
383         if (new_value>tx_dial->old_upper) 
384                 new_value=tx_dial->old_upper;
385         else if (new_value<tx_dial->old_lower) 
386                 new_value=tx_dial->old_lower;
387         
388         gtk_adjustment_set_value(tx_dial->adjustment, new_value);
389 }
390
391 static void gtk_tx_dial_update (GtkTxDial *tx_dial)
392 {
393         gfloat new_value;
394         gint image;
395         
396         g_return_if_fail (tx_dial != NULL);
397         g_return_if_fail (GTK_IS_TX_DIAL (tx_dial));
398         
399         new_value = gtk_adjustment_get_value(tx_dial->adjustment);
400         
401         if (new_value < gtk_adjustment_get_lower(tx_dial->adjustment))
402                 new_value = gtk_adjustment_get_lower(tx_dial->adjustment);
403         
404         if (new_value > gtk_adjustment_get_upper(tx_dial->adjustment))
405                 new_value = gtk_adjustment_get_upper(tx_dial->adjustment);
406         
407         if (new_value != gtk_adjustment_get_value(tx_dial->adjustment)) {
408                 gtk_adjustment_set_value(tx_dial->adjustment, new_value);
409         }
410         
411         calc_image(new_value, image);
412         
413         if (image!=tx_dial->old_image) {
414                 tx_dial->old_image=image;
415                 gtk_widget_queue_draw(GTK_WIDGET(tx_dial));
416         }
417 }
418
419 static void gtk_tx_dial_adjustment_changed (GtkAdjustment *adjustment,
420                               gpointer       data)
421 {
422         GtkTxDial *tx_dial;
423         
424         g_return_if_fail (adjustment != NULL);
425         g_return_if_fail (data != NULL);
426         
427         tx_dial = GTK_TX_DIAL (data);
428         
429         if ((tx_dial->old_value != gtk_adjustment_get_value(adjustment)) ||
430                 (tx_dial->old_lower != gtk_adjustment_get_lower(adjustment)) ||
431                 (tx_dial->old_upper != gtk_adjustment_get_upper(adjustment))) {
432                 tx_dial->old_value = gtk_adjustment_get_value(adjustment);
433                 tx_dial->old_lower = gtk_adjustment_get_lower(adjustment);
434                 tx_dial->old_upper = gtk_adjustment_get_upper(adjustment);
435                 tx_dial->old_range = gtk_adjustment_get_upper(adjustment)-gtk_adjustment_get_lower(adjustment);
436                 
437                 gtk_tx_dial_update (tx_dial);
438         }
439 }
440
441 static void gtk_tx_dial_adjustment_value_changed (GtkAdjustment *adjustment, gpointer data)
442 {
443         GtkTxDial *tx_dial;
444         
445         g_return_if_fail (adjustment != NULL);
446         g_return_if_fail (data != NULL);
447         
448         tx_dial = GTK_TX_DIAL (data);
449         
450         if (tx_dial->old_value != gtk_adjustment_get_value(adjustment)) {
451                 gtk_tx_dial_update (tx_dial);
452                 tx_dial->old_value = gtk_adjustment_get_value(adjustment);
453         }
454 }