It's 2020...
[terminatorX.git] / src / tX_dial.c
1 /*
2     terminatorX - realtime audio scratching software
3     Copyright (C) 1999-2020  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 }
226
227 static void gtk_tx_dial_get_preferred_width (GtkWidget *widget, gint *minimal_width, gint *natural_width) {
228         *minimal_width = *natural_width = tX_knob_size;
229 }
230 static void gtk_tx_dial_get_preferred_height (GtkWidget *widget, gint *minimal_height, gint *natural_height) {
231         *minimal_height = *natural_height = tX_knob_size;
232 }
233
234 static void gtk_tx_dial_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
235 {
236         GtkTxDial *tx_dial;
237         
238         g_return_if_fail (widget != NULL);
239         g_return_if_fail (GTK_IS_TX_DIAL (widget));
240         g_return_if_fail (allocation != NULL);
241         
242         gtk_widget_set_allocation(widget, allocation);
243         tx_dial = GTK_TX_DIAL (widget);
244         
245         if (gtk_widget_get_realized (widget)) {
246                 gdk_window_move_resize (gtk_widget_get_window(widget),
247                                   allocation->x, allocation->y,
248                                   allocation->width, allocation->height);
249                 
250                 tx_dial->xofs=(allocation->width-tX_knob_size)/2;
251                 tx_dial->yofs=(allocation->height-tX_knob_size)/2;
252         }
253 }
254
255 gboolean gtk_tx_dial_draw (GtkWidget *widget, cairo_t *cr)
256 {
257         GtkTxDial *tx_dial;
258         
259         g_return_val_if_fail (widget != NULL, FALSE);
260         g_return_val_if_fail (GTK_IS_TX_DIAL (widget), FALSE);
261         
262         tx_dial = GTK_TX_DIAL (widget);
263         
264         if (gtk_widget_is_drawable (widget)) {
265                 gdk_cairo_set_source_pixbuf (cr, knob_pixmaps[tx_dial->old_image], 0, 0);
266                 cairo_paint (cr);
267         }                
268                   
269         return FALSE;
270 }
271
272 static gint gtk_tx_dial_button_press (GtkWidget *widget, GdkEventButton *event)
273 {
274         GtkTxDial *tx_dial;
275         
276         g_return_val_if_fail (widget != NULL, FALSE);
277         g_return_val_if_fail (GTK_IS_TX_DIAL (widget), FALSE);
278         g_return_val_if_fail (event != NULL, FALSE);
279         
280         tx_dial = GTK_TX_DIAL (widget);
281         
282         tx_dial->x = event->x;
283         tx_dial->y = event->y;    
284         
285         if (!tx_dial->button) {
286                 gtk_grab_add (widget);
287                 tx_dial->button = event->button;
288         }
289         
290         return FALSE;
291 }
292
293 static gint gtk_tx_dial_button_release (GtkWidget *widget, GdkEventButton *event)
294 {
295         GtkTxDial *tx_dial;
296         
297         g_return_val_if_fail (widget != NULL, FALSE);
298         g_return_val_if_fail (GTK_IS_TX_DIAL (widget), FALSE);
299         g_return_val_if_fail (event != NULL, FALSE);
300         
301         tx_dial = GTK_TX_DIAL (widget);
302         
303         if (tx_dial->button == event->button) {
304                 gtk_grab_remove (widget);
305                 tx_dial->button = 0;
306         }
307         
308         return FALSE;
309 }
310
311 static gint gtk_tx_dial_motion_notify (GtkWidget *widget, GdkEventMotion *event)
312 {
313         GtkTxDial *tx_dial;
314         GdkModifierType mods;
315         gint x, y, mask;
316         
317         g_return_val_if_fail (widget != NULL, FALSE);
318         g_return_val_if_fail (GTK_IS_TX_DIAL (widget), FALSE);
319         g_return_val_if_fail (event != NULL, FALSE);
320         
321         tx_dial = GTK_TX_DIAL (widget);
322         
323         if (tx_dial->button != 0) {
324                 x = event->x;
325                 y = event->y;
326                 
327                 if (event->is_hint || (event->window != gtk_widget_get_window(widget)))
328                         gdk_window_get_device_position(gtk_widget_get_window(widget), event->device, &x, &y, &mods);
329                 
330                 switch (tx_dial->button) {
331                         case 1:
332                                 mask = GDK_BUTTON1_MASK;
333                                 break;
334                         case 2:
335                                 mask = GDK_BUTTON2_MASK;
336                                 break;
337                         case 3:
338                                 mask = GDK_BUTTON3_MASK;
339                                 break;
340                         default:
341                                 mask = 0;
342                 }
343                 
344                 if (mods & mask)
345                         gtk_tx_dial_update_mouse (tx_dial, x,y);
346         }
347         
348         return FALSE;
349 }
350
351 static void gtk_tx_dial_update_mouse (GtkTxDial *tx_dial, gint x, gint y)
352 {
353         gdouble dx, dy, d;
354         gfloat old_value, new_value;
355
356         g_return_if_fail (tx_dial != NULL);
357         g_return_if_fail (GTK_IS_TX_DIAL (tx_dial));
358         
359         dx=x-tx_dial->x;
360         dy=tx_dial->y-y;
361         tx_dial->x=x;
362         tx_dial->y=y;
363         
364         d=dx+dy;
365         d/=200.0;
366         
367         old_value=gtk_adjustment_get_value(tx_dial->adjustment);    
368         new_value=old_value + d*tx_dial->old_range;
369         
370         if (new_value>tx_dial->old_upper) 
371                 new_value=tx_dial->old_upper;
372         else if (new_value<tx_dial->old_lower) 
373                 new_value=tx_dial->old_lower;
374         
375         gtk_adjustment_set_value(tx_dial->adjustment, new_value);
376 }
377
378 static void gtk_tx_dial_update (GtkTxDial *tx_dial)
379 {
380         gfloat new_value;
381         gint image;
382         
383         g_return_if_fail (tx_dial != NULL);
384         g_return_if_fail (GTK_IS_TX_DIAL (tx_dial));
385         
386         new_value = gtk_adjustment_get_value(tx_dial->adjustment);
387         
388         if (new_value < gtk_adjustment_get_lower(tx_dial->adjustment))
389                 new_value = gtk_adjustment_get_lower(tx_dial->adjustment);
390         
391         if (new_value > gtk_adjustment_get_upper(tx_dial->adjustment))
392                 new_value = gtk_adjustment_get_upper(tx_dial->adjustment);
393         
394         if (new_value != gtk_adjustment_get_value(tx_dial->adjustment)) {
395                 gtk_adjustment_set_value(tx_dial->adjustment, new_value);
396         }
397         
398         calc_image(new_value, image);
399         
400         if (image!=tx_dial->old_image) {
401                 tx_dial->old_image=image;
402                 gtk_widget_queue_draw(GTK_WIDGET(tx_dial));
403         }
404 }
405
406 static void gtk_tx_dial_adjustment_changed (GtkAdjustment *adjustment,
407                               gpointer       data)
408 {
409         GtkTxDial *tx_dial;
410         
411         g_return_if_fail (adjustment != NULL);
412         g_return_if_fail (data != NULL);
413         
414         tx_dial = GTK_TX_DIAL (data);
415         
416         if ((tx_dial->old_value != gtk_adjustment_get_value(adjustment)) ||
417                 (tx_dial->old_lower != gtk_adjustment_get_lower(adjustment)) ||
418                 (tx_dial->old_upper != gtk_adjustment_get_upper(adjustment))) {
419                 tx_dial->old_value = gtk_adjustment_get_value(adjustment);
420                 tx_dial->old_lower = gtk_adjustment_get_lower(adjustment);
421                 tx_dial->old_upper = gtk_adjustment_get_upper(adjustment);
422                 tx_dial->old_range = gtk_adjustment_get_upper(adjustment)-gtk_adjustment_get_lower(adjustment);
423                 
424                 gtk_tx_dial_update (tx_dial);
425         }
426 }
427
428 static void gtk_tx_dial_adjustment_value_changed (GtkAdjustment *adjustment, gpointer data)
429 {
430         GtkTxDial *tx_dial;
431         
432         g_return_if_fail (adjustment != NULL);
433         g_return_if_fail (data != NULL);
434         
435         tx_dial = GTK_TX_DIAL (data);
436         
437         if (tx_dial->old_value != gtk_adjustment_get_value(adjustment)) {
438                 gtk_tx_dial_update (tx_dial);
439                 tx_dial->old_value = gtk_adjustment_get_value(adjustment);
440         }
441 }