cfbe3e3736ea5b4e4beaed17e2c33e699fe22e10
[terminatorX.git] / src / tX_widget.c
1 /*
2     terminatorX - realtime audio scratching software
3     Copyright (C) 1999-2003  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, write to the Free Software
17     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  
19     File: tX_widget.c
20  
21     Description: This contains the implementation of the tx_widget.
22                  This file is based on the GTK+ widget example from
23                  the GTK+ 1.2 tutorial.
24 */
25
26 #include <math.h>
27
28 #include <gtk/gtkwindow.h>
29 #include "tX_widget.h"
30 #include "tX_types.h"
31 #include "tX_global.h"
32 #include <malloc.h>
33 #include <stdlib.h>
34
35 #ifndef WIN32
36 #include <unistd.h>
37 #endif
38
39 #define TX_DEFAULT_SIZE_X 100
40 #define TX_DEFAULT_SIZE_Y 30
41
42 /* forward declaration */
43 static void gtk_tx_class_init(GtkTxClass *);
44 static void gtk_tx_init(GtkTx * tx);
45 GtkWidget *gtk_tx_new(int16_t * wavdata, int wavsamples);
46 static void gtk_tx_destroy(GtkObject * object);
47 void gtk_tx_set_data(GtkTx * tx, int16_t * wavdata, int wavsamples);
48 static void gtk_tx_realize(GtkWidget * widget);
49 static void gtk_tx_size_request(GtkWidget * widget, GtkRequisition * requisition);
50 static void gtk_tx_size_allocate(GtkWidget * widget, GtkAllocation * allocation);
51 static gint gtk_tx_expose(GtkWidget * widget, GdkEventExpose * event);
52 static void gtk_tx_update(GtkTx * tx);
53 static void gtk_tx_prepare(GtkWidget * widget);
54
55 /* data */
56 static GtkWidgetClass *parent_class = NULL;
57
58 /* widget "methods" */
59
60 guint gtk_tx_get_type() {
61         static guint tx_type = 0;
62         
63         if (!tx_type) {
64                 GtkTypeInfo tx_info = {
65                 "GtkTx",
66                 sizeof(GtkTx),
67                 sizeof(GtkTxClass),
68                 (GtkClassInitFunc) gtk_tx_class_init,
69                 (GtkObjectInitFunc) gtk_tx_init,
70                 /* reserved */ NULL,
71                 /* reserved */ NULL,
72                 /* reserved */ NULL
73         };
74         
75          tx_type = gtk_type_unique(gtk_widget_get_type(), &tx_info);
76         }
77         
78         return tx_type;
79 }
80
81 static void gtk_tx_class_init(GtkTxClass * gclass) {
82         GtkObjectClass *object_class;
83         GtkWidgetClass *widget_class;
84         
85         object_class = (GtkObjectClass *) gclass;
86         widget_class = (GtkWidgetClass *) gclass;
87         
88         parent_class = gtk_type_class(gtk_widget_get_type());
89         
90         object_class->destroy = gtk_tx_destroy;
91         
92         widget_class->realize = gtk_tx_realize;
93         widget_class->expose_event = gtk_tx_expose;
94         widget_class->size_request = gtk_tx_size_request;
95         widget_class->size_allocate = gtk_tx_size_allocate;
96 //      widget_class->button_press_event = gtk_tx_button_press;
97 //      widget_class->button_release_event = gtk_tx_button_release;
98 //      widget_class->motion_notify_event = gtk_tx_motion_notify;
99 }
100
101 void gtk_tx_mk_col(GtkTx * tx, GdkColor * col, float r, float g, float b) {
102         float max = 65535.0;
103         
104         col->red = (gint) (r * max);
105         col->green = (gint) (g * max);
106         col->blue = (gint) (b * max);
107                 
108         gdk_colormap_alloc_color(gtk_widget_get_colormap(GTK_WIDGET(tx)), col, 0, 1);
109 }
110
111 static void gtk_tx_init(GtkTx * tx) {
112         GdkColormap *priv;
113
114         tx->disp_data = NULL;
115         tx->data = NULL;
116         tx->samples = 0;
117         tx->do_showframe = 0;
118 #ifdef USE_DISPLAY_NORMALIZE
119         tx->max_value=-1;
120 #endif
121         
122         priv = gdk_colormap_new(gtk_widget_get_visual(GTK_WIDGET(tx)), 6);
123         gtk_widget_set_colormap(GTK_WIDGET(tx), priv);
124         
125         gtk_tx_mk_col(tx, &tx->bg, 0, 0, 0);
126         gtk_tx_mk_col(tx, &tx->fg, 0, 1, 0);
127         gtk_tx_mk_col(tx, &tx->focus_fg, 1, 1, 0);
128         gtk_tx_mk_col(tx, &tx->focus_bg, 0, 0, 0.3);    
129         
130         gtk_tx_mk_col(tx, &tx->busy, 1, 0.4, 0.4);
131         gtk_tx_mk_col(tx, &tx->mute, 1, 1, 1);
132         
133         tx->current_fg=&tx->fg;
134         tx->current_bg=&tx->bg;
135         
136         tx->spp=1;
137         tx->lastmute=-1;
138         tx->zoom=0;
139         tx->cursor_pos=0;
140         tx->cursor_x_pos=0;
141 }
142
143 GtkWidget *gtk_tx_new(int16_t * wavdata, int wavsamples) {
144         GtkTx *tx;
145
146         tx = gtk_type_new(gtk_tx_get_type());
147
148         tx->data = wavdata;
149         tx->samples = wavsamples;
150         tx->zoom=0;
151         tx->display_x_offset=0;
152         
153         return GTK_WIDGET(tx);
154 }
155
156 static void gtk_tx_destroy(GtkObject * object) {
157         GtkTx *tx;
158         g_return_if_fail(object != NULL);
159         g_return_if_fail(GTK_IS_TX(object));
160
161         tx=GTK_TX(object);
162         
163         if (tx->disp_data) { free(tx->disp_data); tx->disp_data=NULL; }
164         
165         if (GTK_OBJECT_CLASS(parent_class)->destroy) {
166                 (*GTK_OBJECT_CLASS(parent_class)->destroy) (object);
167         }       
168 }
169
170 #define MAX_ZOOM_WIDTH 500000.0
171
172 void gtk_tx_set_data(GtkTx * tx, int16_t * wavdata, int wavsamples) {
173         g_return_if_fail(tx != NULL);
174         g_return_if_fail(GTK_IS_TX(tx));
175
176         tx->data = wavdata;
177         tx->samples = wavsamples;
178 #ifdef USE_DISPLAY_NORMALIZE    
179         tx->max_value=-1;
180 #endif
181         
182         gtk_tx_prepare(GTK_WIDGET(tx));
183         gtk_tx_update(tx);
184 }
185
186 static void gtk_tx_realize(GtkWidget * widget) {
187         GtkTx *tx;
188         GdkWindowAttr attributes;
189         gint attributes_mask;
190
191         g_return_if_fail(widget != NULL);
192         g_return_if_fail(GTK_IS_TX(widget));
193
194         GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED);
195         tx = GTK_TX(widget);
196
197         attributes.x = widget->allocation.x;
198         attributes.y = widget->allocation.y;
199         attributes.width = widget->allocation.width;
200         attributes.height = widget->allocation.height;
201         attributes.wclass = GDK_INPUT_OUTPUT;
202         attributes.window_type = GDK_WINDOW_CHILD;
203         attributes.event_mask = gtk_widget_get_events(widget) | GDK_EXPOSURE_MASK;
204         attributes.visual = gtk_widget_get_visual(widget);
205         attributes.colormap = gtk_widget_get_colormap(widget);
206         attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
207
208         widget->window = gdk_window_new(widget->parent->window, &attributes, attributes_mask);
209         widget->style = gtk_style_attach(widget->style, widget->window);
210
211         gdk_window_set_user_data(widget->window, widget);
212
213         gtk_style_set_background(widget->style, widget->window, GTK_STATE_NORMAL);
214 }
215
216 static void gtk_tx_size_request(GtkWidget * widget, GtkRequisition * requisition) {
217         requisition->width = TX_DEFAULT_SIZE_X;
218         requisition->height = TX_DEFAULT_SIZE_Y;
219 }
220
221 static void gtk_tx_prepare(GtkWidget * widget) {
222         int x, sample;
223         int16_t *ptr;
224         f_prec value;
225         GtkTx *tx;
226         int avg_pos;
227         
228         g_return_if_fail(widget != NULL);
229         g_return_if_fail(GTK_IS_TX(widget));
230
231         tx = GTK_TX(widget);
232         
233         tx->yc = widget->allocation.height / 2;
234
235         if (tx->disp_data) { free(tx->disp_data); tx->disp_data=NULL; }
236
237         if (tx->data) {
238                 int max_spp=tx->samples/widget->allocation.width;
239                 int min_spp=tx->samples/MAX_ZOOM_WIDTH;
240                 gdouble diff;
241                 
242                 if (min_spp==0) min_spp=1;
243                 
244                 diff=max_spp-min_spp;
245                 
246                 tx->spp=min_spp+diff*(1.0-tx->zoom);
247                 tx->display_width = tx->samples/tx->spp;
248                 
249 #ifdef USE_DISPLAY_NORMALIZE    
250                 tx->max_value=-1;
251 #endif          
252                 
253             tx->disp_data = (int16_t *) malloc(tx->display_width * sizeof(int16_t));
254
255             if (tx->disp_data) {                        
256 #ifdef USE_DISPLAY_NORMALIZE            
257                         if (tx->max_value==-1) {
258                                 /* We haven't figured a max value yet... */
259                                 //puts("searching max...");
260                 
261                                 for (x = 0, ptr = tx->disp_data; x < tx->display_width; ptr++, x++) {
262                                         value = 0;
263                                         for (sample = x * tx->spp, avg_pos=1; sample < (x + 1) * tx->spp; sample++) {
264                                                 value += (abs(tx->data[sample])-value)/(double) avg_pos++;
265                                         }
266                                         if (value>tx->max_value) tx->max_value=value;
267                                         tx->disp_data[x] = value; 
268                                 }
269                                 for (x = 0; x < tx->display_width; x++) {
270                                         f_prec t=tx->disp_data[x]/(double) tx->max_value;
271                                         tx->disp_data[x]=(int) (t * (f_prec) (tx->yc));
272                                 }
273                         } else {
274 #endif                          
275                                 //puts("have max...");
276                                 /* We have a max value... */
277                                 for (x = 0, ptr = tx->disp_data; x < tx->display_width; ptr++, x++) {
278                                         f_prec t;
279                                         value = 0;
280                                         for (sample = x * tx->spp, avg_pos=1; sample < (x + 1) * tx->spp; sample++) {
281                                                 value += (abs(tx->data[sample])-value)/(double) avg_pos++;
282                                         }
283 #ifdef USE_DISPLAY_NORMALIZE                                    
284                                         t=value/(double) tx->max_value;
285 #else
286                                         t=value/32768.0;
287 #endif                                  
288                                         tx->disp_data[x] = (int) (t * (f_prec) (tx->yc)); 
289                                 }
290 #ifdef USE_DISPLAY_NORMALIZE
291                         }
292 #endif                  
293                 }
294         } else {
295             tx->disp_data = NULL;
296         }
297         
298         tx->cursor_pos=-1;
299         tx->lastmute=-1;
300         
301         //tX_warning("spp: %i samples: %i width %i x %i", tx->spp, tx->samples, tx->display_width, x);
302 }
303
304 static void gtk_tx_size_allocate(GtkWidget * widget, GtkAllocation * allocation) {
305         GtkTx *tx;
306
307         g_return_if_fail(widget != NULL);
308         g_return_if_fail(GTK_IS_TX(widget));
309         g_return_if_fail(allocation != NULL);
310
311         widget->allocation = *allocation;
312
313         gtk_tx_prepare(widget);
314
315         if (GTK_WIDGET_REALIZED(widget)) {
316             tx = GTK_TX(widget);
317 #ifdef USE_DISPLAY_NORMALIZE            
318                 tx->max_value=-1;
319 #endif          
320             gdk_window_move_resize(widget->window, allocation->x, allocation->y, allocation->width, allocation->height);
321         }
322 }
323
324 #define min(a,b) ((a) < (b) ? (a) : (b))
325 #define max(a,b) ((a) > (b) ? (a) : (b))
326
327 static gint gtk_tx_expose(GtkWidget * widget, GdkEventExpose * event) {
328         GtkTx *tx;
329         gint x;
330         GdkRectangle *area;
331         
332         g_return_val_if_fail(widget != NULL, FALSE);
333         g_return_val_if_fail(GTK_IS_TX(widget), FALSE);
334         g_return_val_if_fail(event != NULL, FALSE);
335
336 /*      if (event->count > 0) { 
337                 fprintf(stderr, "Ignoring %i expose events.\n", event->count);
338                 return FALSE;
339         }*/
340
341         area=&event->area;
342
343         tx = GTK_TX(widget);
344
345         gdk_gc_set_foreground(widget->style->fg_gc[widget->state], tx->current_bg);
346
347         gdk_draw_rectangle(widget->window,
348                 widget->style->fg_gc[widget->state], 1, 
349                 area->x, area->y,
350                 area->width, area->height);
351
352         gdk_gc_set_foreground(widget->style->fg_gc[widget->state], tx->current_fg);
353
354         if (tx->disp_data) {
355                 int max_x=area->x+area->width;
356
357             for (x =area->x; x < max_x; x++) {
358                         gdk_draw_line(widget->window,
359                                 widget->style->fg_gc[widget->state], x,
360                                 tx->yc - tx->disp_data[tx->display_x_offset+x], x,
361                                 tx->yc + tx->disp_data[tx->display_x_offset+x]);
362             }
363         } else {
364             gdk_draw_line(widget->window, widget->style->fg_gc[widget->state],
365                          0, tx->yc, widget->allocation.width, tx->yc);
366         }
367
368         return FALSE;
369 }
370
371 void gtk_tx_set_zoom(GtkTx *tx, f_prec zoom) {
372         GtkWidget *wid=GTK_WIDGET(tx);
373         
374         tx->zoom=zoom;
375         gtk_tx_prepare(wid);
376         gtk_widget_queue_draw_area(wid, 0, 0, wid->allocation.width, wid->allocation.height);
377 }
378
379 static void gtk_tx_update(GtkTx * tx) {
380         g_return_if_fail(tx != NULL);
381         g_return_if_fail(GTK_IS_TX(tx));
382
383         gtk_widget_draw(GTK_WIDGET(tx), NULL);
384 }
385
386 void gtk_tx_update_pos_display(GtkTx * tx, int sample, int mute) {
387         GtkWidget *widget;
388         GdkWindow *window;
389         GdkGC *gc;
390
391         int x, y, yc, ymax, tmp;
392         int current_pos, current_pos_x, x_offset;
393         int force_draw=0;
394
395         /* Don't update if not required */
396
397         //current_x = sample / tx->spp + FR_SIZE;
398         current_pos = sample / tx->spp;
399         
400         if ((current_pos == tx->cursor_pos) && 
401                 (tx->lastmute == mute)) return;
402         tx->lastmute = mute;
403
404         /* speedup + easyness */
405
406         widget = GTK_WIDGET(tx);
407         window = widget->window;
408
409         gc = widget->style->fg_gc[widget->state];
410         yc = tx->yc;
411         ymax = widget->allocation.height-1;
412
413         /* clean up last pos */
414         
415         x = tx->cursor_x_pos;
416         
417         if (x >= 0) {
418             gdk_gc_set_foreground(gc, tx->current_bg);
419             gdk_draw_line(window, gc, x, 0, x, ymax);
420
421             gdk_gc_set_foreground(gc, tx->current_fg);
422             y = tx->disp_data[x+tx->display_x_offset];
423             gdk_draw_line(window, gc, x, yc + y, x, yc - y);
424         }
425         
426         /* compute new position */
427         if (tx->zoom==0) {
428                 current_pos_x=current_pos;
429                 x_offset=0;             
430         } else {                
431                 tmp=widget->allocation.width/2+1;
432                 
433                 if (current_pos>tmp) {
434                         x_offset=current_pos-tmp;
435                         
436                         if (x_offset+widget->allocation.width>=tx->display_width) {
437                                 x_offset=tx->display_width-widget->allocation.width;
438                         }
439                         
440                         current_pos_x=current_pos-x_offset;
441                 } else {
442                         x_offset=0;
443                         current_pos_x=current_pos;
444                 }
445                 
446                 if (x_offset!=tx->display_x_offset) {
447                         int x_move=tx->display_x_offset-x_offset;
448                         
449                         if (abs(x_move)<widget->allocation.width) {
450                                 gdk_window_scroll(window, x_move, 0);
451                         } else {
452                                 /* we've moved so far that there's nothing to keep from our current display */
453                                 force_draw=1;
454                         }
455                 }
456         }
457         
458         /* store current_pos */
459
460         tx->cursor_pos = current_pos;
461         tx->cursor_x_pos = current_pos_x;
462         tx->display_x_offset = x_offset;
463         
464         /* not drawing current pos - let expose() take care of this... */
465
466         x = current_pos_x;
467
468         if (mute) gdk_gc_set_foreground(gc, &tx->mute);
469         else gdk_gc_set_foreground(gc, &tx->busy);
470
471         gdk_draw_line(window, gc, x, 0, x, ymax);
472         
473         if (force_draw) {
474                 gtk_widget_queue_draw_area(widget, 0, 0, widget->allocation.width, widget->allocation.height);
475         }
476 }
477
478 void gtk_tx_cleanup_pos_display(GtkTx * tx) {
479         GtkWidget *widget;
480         GdkWindow *window;
481         GdkGC *gc;
482         int ymax, yc;
483
484         widget = GTK_WIDGET(tx);
485         window = widget->window;
486         gc = widget->style->fg_gc[widget->state];
487         yc = tx->yc;
488         ymax = widget->allocation.height - 1;
489
490         tx->display_x_offset=0;
491         tx->cursor_pos=-1;
492         tx->cursor_x_pos=-1;
493         tx->do_showframe=0;
494         //tx->current_fg=&tx->fg;
495         
496         gtk_widget_queue_draw(widget);
497 }
498
499 void gtk_tx_show_frame(GtkTx *tx, int show) {
500         if (show) {
501                 tx->current_fg=&tx->focus_fg;
502                 tx->current_bg=&tx->focus_bg;
503         } else {
504                 tx->current_fg=&tx->fg;
505                 tx->current_bg=&tx->bg;
506         }
507         
508         gtk_widget_queue_draw(GTK_WIDGET(tx));  
509 }
510
511 f_prec gtk_tx_get_zoom(GtkTx *tx) {
512         return tx->zoom;
513 }