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