Colors customizable - 'w' + 'f' "fix" - Alex
[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 #define COL_BG_FOCUS     0
102 #define COL_BG_NO_FOCUS  1
103 #define COL_FG_FOCUS     2
104 #define COL_FG_NO_FOCUS  3
105 #define COL_CURSOR       4
106 #define COL_CURSOR_MUTE  5
107
108 void gtk_tx_update_colors(GtkTx *tx)
109 {
110         int i;
111         
112         if (tx->colors_allocated) {
113                 gdk_colormap_free_colors(gtk_widget_get_colormap(GTK_WIDGET(tx)), tx->colors, 6);
114         }
115         
116         gdk_color_parse(globals.wav_display_bg_focus, &tx->colors[COL_BG_FOCUS]);
117         gdk_color_parse(globals.wav_display_bg_no_focus, &tx->colors[COL_BG_NO_FOCUS]);
118         
119         gdk_color_parse(globals.wav_display_fg_focus, &tx->colors[COL_FG_FOCUS]);
120         gdk_color_parse(globals.wav_display_fg_no_focus, &tx->colors[COL_FG_NO_FOCUS]);
121         
122         gdk_color_parse(globals.wav_display_cursor, &tx->colors[COL_CURSOR]);
123         gdk_color_parse(globals.wav_display_cursor_mute, &tx->colors[COL_CURSOR_MUTE]);
124         
125         for (i=0; i<6; i++) {
126                 gdk_colormap_alloc_color(gtk_widget_get_colormap(GTK_WIDGET(tx)), &tx->colors[i], 0, 1);
127         }
128
129         tx->colors_allocated=1;
130 }
131
132
133 static void gtk_tx_init(GtkTx * tx) {
134         GdkColormap *priv;
135
136         tx->disp_data = NULL;
137         tx->data = NULL;
138         tx->colors_allocated=0;
139         tx->samples = 0;
140         tx->do_showframe = 0;
141 #ifdef USE_DISPLAY_NORMALIZE
142         tx->max_value=-1;
143 #endif
144         
145         priv = gdk_colormap_new(gtk_widget_get_visual(GTK_WIDGET(tx)), 6);
146         gtk_widget_set_colormap(GTK_WIDGET(tx), priv);
147
148         memset(tx->colors, 0, sizeof(tx->colors));
149         
150         gtk_tx_update_colors(tx);
151         
152         tx->current_fg=&tx->colors[COL_FG_NO_FOCUS];
153         tx->current_bg=&tx->colors[COL_BG_NO_FOCUS];
154         
155         tx->spp=1;
156         tx->lastmute=-1;
157         tx->zoom=0;
158         tx->cursor_pos=0;
159         tx->cursor_x_pos=0;
160 }
161
162 GtkWidget *gtk_tx_new(int16_t * wavdata, int wavsamples) {
163         GtkTx *tx;
164
165         tx = gtk_type_new(gtk_tx_get_type());
166
167         tx->data = wavdata;
168         tx->samples = wavsamples;
169         tx->zoom=0;
170         tx->display_x_offset=0;
171         
172         return GTK_WIDGET(tx);
173 }
174
175 static void gtk_tx_destroy(GtkObject * object) {
176         GtkTx *tx;
177         g_return_if_fail(object != NULL);
178         g_return_if_fail(GTK_IS_TX(object));
179
180         tx=GTK_TX(object);
181         
182         if (tx->disp_data) { free(tx->disp_data); tx->disp_data=NULL; }
183         
184         if (GTK_OBJECT_CLASS(parent_class)->destroy) {
185                 (*GTK_OBJECT_CLASS(parent_class)->destroy) (object);
186         }       
187 }
188
189 #define MAX_ZOOM_WIDTH 500000.0
190
191 void gtk_tx_set_data(GtkTx * tx, int16_t * wavdata, int wavsamples) {
192         g_return_if_fail(tx != NULL);
193         g_return_if_fail(GTK_IS_TX(tx));
194
195         tx->data = wavdata;
196         tx->samples = wavsamples;
197 #ifdef USE_DISPLAY_NORMALIZE    
198         tx->max_value=-1;
199 #endif
200         
201         gtk_tx_prepare(GTK_WIDGET(tx));
202         gtk_tx_update(tx);
203 }
204
205 static void gtk_tx_realize(GtkWidget * widget) {
206         GtkTx *tx;
207         GdkWindowAttr attributes;
208         gint attributes_mask;
209
210         g_return_if_fail(widget != NULL);
211         g_return_if_fail(GTK_IS_TX(widget));
212
213         GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED);
214         tx = GTK_TX(widget);
215
216         attributes.x = widget->allocation.x;
217         attributes.y = widget->allocation.y;
218         attributes.width = widget->allocation.width;
219         attributes.height = widget->allocation.height;
220         attributes.wclass = GDK_INPUT_OUTPUT;
221         attributes.window_type = GDK_WINDOW_CHILD;
222         attributes.event_mask = gtk_widget_get_events(widget) | GDK_EXPOSURE_MASK;
223         attributes.visual = gtk_widget_get_visual(widget);
224         attributes.colormap = gtk_widget_get_colormap(widget);
225         attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
226
227         widget->window = gdk_window_new(widget->parent->window, &attributes, attributes_mask);
228         widget->style = gtk_style_attach(widget->style, widget->window);
229
230         gdk_window_set_user_data(widget->window, widget);
231
232         gtk_style_set_background(widget->style, widget->window, GTK_STATE_NORMAL);
233 }
234
235 static void gtk_tx_size_request(GtkWidget * widget, GtkRequisition * requisition) {
236         requisition->width = TX_DEFAULT_SIZE_X;
237         requisition->height = TX_DEFAULT_SIZE_Y;
238 }
239
240 static void gtk_tx_prepare(GtkWidget * widget) {
241         int x, sample;
242         int16_t *ptr;
243         f_prec value;
244         GtkTx *tx;
245         int avg_pos;
246         
247         g_return_if_fail(widget != NULL);
248         g_return_if_fail(GTK_IS_TX(widget));
249
250         tx = GTK_TX(widget);
251         
252         tx->yc = widget->allocation.height / 2;
253
254         if (tx->disp_data) { free(tx->disp_data); tx->disp_data=NULL; }
255
256         if (tx->data) {
257                 int max_spp=tx->samples/widget->allocation.width;
258                 int min_spp=tx->samples/MAX_ZOOM_WIDTH;
259                 gdouble diff;
260                 
261                 if (min_spp==0) min_spp=1;
262                 
263                 diff=max_spp-min_spp;
264                 
265                 tx->spp=min_spp+diff*(1.0-tx->zoom);
266                 tx->display_width = tx->samples/tx->spp;
267                 
268 #ifdef USE_DISPLAY_NORMALIZE    
269                 tx->max_value=-1;
270 #endif          
271                 
272             tx->disp_data = (int16_t *) malloc(tx->display_width * sizeof(int16_t));
273
274             if (tx->disp_data) {                        
275 #ifdef USE_DISPLAY_NORMALIZE            
276                         if (tx->max_value==-1) {
277                                 /* We haven't figured a max value yet... */
278                                 //puts("searching max...");
279                 
280                                 for (x = 0, ptr = tx->disp_data; x < tx->display_width; ptr++, x++) {
281                                         value = 0;
282                                         for (sample = x * tx->spp, avg_pos=1; sample < (x + 1) * tx->spp; sample++) {
283                                                 value += (abs(tx->data[sample])-value)/(double) avg_pos++;
284                                         }
285                                         if (value>tx->max_value) tx->max_value=value;
286                                         tx->disp_data[x] = value; 
287                                 }
288                                 for (x = 0; x < tx->display_width; x++) {
289                                         f_prec t=tx->disp_data[x]/(double) tx->max_value;
290                                         tx->disp_data[x]=(int) (t * (f_prec) (tx->yc));
291                                 }
292                         } else {
293 #endif                          
294                                 //puts("have max...");
295                                 /* We have a max value... */
296                                 for (x = 0, ptr = tx->disp_data; x < tx->display_width; ptr++, x++) {
297                                         f_prec t;
298                                         value = 0;
299                                         for (sample = x * tx->spp, avg_pos=1; sample < (x + 1) * tx->spp; sample++) {
300                                                 value += (abs(tx->data[sample])-value)/(double) avg_pos++;
301                                         }
302 #ifdef USE_DISPLAY_NORMALIZE                                    
303                                         t=value/(double) tx->max_value;
304 #else
305                                         t=value/32768.0;
306 #endif                                  
307                                         tx->disp_data[x] = (int) (t * (f_prec) (tx->yc)); 
308                                 }
309 #ifdef USE_DISPLAY_NORMALIZE
310                         }
311 #endif                  
312                 }
313         } else {
314             tx->disp_data = NULL;
315         }
316         
317         tx->cursor_pos=-1;
318         tx->lastmute=-1;
319         
320         //tX_warning("spp: %i samples: %i width %i x %i", tx->spp, tx->samples, tx->display_width, x);
321 }
322
323 static void gtk_tx_size_allocate(GtkWidget * widget, GtkAllocation * allocation) {
324         GtkTx *tx;
325
326         g_return_if_fail(widget != NULL);
327         g_return_if_fail(GTK_IS_TX(widget));
328         g_return_if_fail(allocation != NULL);
329
330         widget->allocation = *allocation;
331
332         gtk_tx_prepare(widget);
333
334         if (GTK_WIDGET_REALIZED(widget)) {
335             tx = GTK_TX(widget);
336 #ifdef USE_DISPLAY_NORMALIZE            
337                 tx->max_value=-1;
338 #endif          
339             gdk_window_move_resize(widget->window, allocation->x, allocation->y, allocation->width, allocation->height);
340         }
341 }
342
343 #define min(a,b) ((a) < (b) ? (a) : (b))
344 #define max(a,b) ((a) > (b) ? (a) : (b))
345
346 static gint gtk_tx_expose(GtkWidget * widget, GdkEventExpose * event) {
347         GtkTx *tx;
348         gint x;
349         GdkRectangle *area;
350         
351         g_return_val_if_fail(widget != NULL, FALSE);
352         g_return_val_if_fail(GTK_IS_TX(widget), FALSE);
353         g_return_val_if_fail(event != NULL, FALSE);
354
355 /*      if (event->count > 0) { 
356                 fprintf(stderr, "Ignoring %i expose events.\n", event->count);
357                 return FALSE;
358         }*/
359
360         area=&event->area;
361
362         tx = GTK_TX(widget);
363
364         gdk_gc_set_foreground(widget->style->fg_gc[widget->state], tx->current_bg);
365
366         gdk_draw_rectangle(widget->window,
367                 widget->style->fg_gc[widget->state], 1, 
368                 area->x, area->y,
369                 area->width, area->height);
370
371         gdk_gc_set_foreground(widget->style->fg_gc[widget->state], tx->current_fg);
372
373         if (tx->disp_data) {
374                 int max_x=area->x+area->width;
375
376             for (x =area->x; x < max_x; x++) {
377                         gdk_draw_line(widget->window,
378                                 widget->style->fg_gc[widget->state], x,
379                                 tx->yc - tx->disp_data[tx->display_x_offset+x], x,
380                                 tx->yc + tx->disp_data[tx->display_x_offset+x]);
381             }
382         } else {
383             gdk_draw_line(widget->window, widget->style->fg_gc[widget->state],
384                          0, tx->yc, widget->allocation.width, tx->yc);
385         }
386
387         return FALSE;
388 }
389
390 void gtk_tx_set_zoom(GtkTx *tx, f_prec zoom) {
391         GtkWidget *wid=GTK_WIDGET(tx);
392         
393         tx->zoom=zoom;
394         gtk_tx_prepare(wid);
395         gtk_widget_queue_draw_area(wid, 0, 0, wid->allocation.width, wid->allocation.height);
396 }
397
398 static void gtk_tx_update(GtkTx * tx) {
399         g_return_if_fail(tx != NULL);
400         g_return_if_fail(GTK_IS_TX(tx));
401
402         gtk_widget_draw(GTK_WIDGET(tx), NULL);
403 }
404
405 void gtk_tx_update_pos_display(GtkTx * tx, int sample, int mute) {
406         GtkWidget *widget;
407         GdkWindow *window;
408         GdkGC *gc;
409
410         int x, y, yc, ymax, tmp;
411         int current_pos, current_pos_x, x_offset;
412         int force_draw=0;
413
414         /* Don't update if not required */
415
416         //current_x = sample / tx->spp + FR_SIZE;
417         current_pos = sample / tx->spp;
418         
419         if ((current_pos == tx->cursor_pos) && 
420                 (tx->lastmute == mute)) return;
421         tx->lastmute = mute;
422
423         /* speedup + easyness */
424
425         widget = GTK_WIDGET(tx);
426         window = widget->window;
427
428         gc = widget->style->fg_gc[widget->state];
429         yc = tx->yc;
430         ymax = widget->allocation.height-1;
431
432         /* clean up last pos */
433         
434         x = tx->cursor_x_pos;
435         
436         if (x >= 0) {
437             gdk_gc_set_foreground(gc, tx->current_bg);
438             gdk_draw_line(window, gc, x, 0, x, ymax);
439
440             gdk_gc_set_foreground(gc, tx->current_fg);
441             y = tx->disp_data[x+tx->display_x_offset];
442             gdk_draw_line(window, gc, x, yc + y, x, yc - y);
443         }
444         
445         /* compute new position */
446         if (tx->zoom==0) {
447                 current_pos_x=current_pos;
448                 x_offset=0;             
449         } else {                
450                 tmp=widget->allocation.width/2+1;
451                 
452                 if (current_pos>tmp) {
453                         x_offset=current_pos-tmp;
454                         
455                         if (x_offset+widget->allocation.width>=tx->display_width) {
456                                 x_offset=tx->display_width-widget->allocation.width;
457                         }
458                         
459                         current_pos_x=current_pos-x_offset;
460                 } else {
461                         x_offset=0;
462                         current_pos_x=current_pos;
463                 }
464                 
465                 if (x_offset!=tx->display_x_offset) {
466                         int x_move=tx->display_x_offset-x_offset;
467                         
468                         if (abs(x_move)<widget->allocation.width) {
469                                 gdk_window_scroll(window, x_move, 0);
470                         } else {
471                                 /* we've moved so far that there's nothing to keep from our current display */
472                                 force_draw=1;
473                         }
474                 }
475         }
476         
477         /* store current_pos */
478
479         tx->cursor_pos = current_pos;
480         tx->cursor_x_pos = current_pos_x;
481         tx->display_x_offset = x_offset;
482         
483         /* not drawing current pos - let expose() take care of this... */
484
485         x = current_pos_x;
486
487         if (mute) gdk_gc_set_foreground(gc, &tx->colors[COL_CURSOR_MUTE]);
488         else gdk_gc_set_foreground(gc, &tx->colors[COL_CURSOR]);
489
490         gdk_draw_line(window, gc, x, 0, x, ymax);
491         
492         if (force_draw) {
493                 gtk_widget_queue_draw_area(widget, 0, 0, widget->allocation.width, widget->allocation.height);
494         }
495 }
496
497 void gtk_tx_cleanup_pos_display(GtkTx * tx) {
498         GtkWidget *widget;
499         GdkWindow *window;
500         GdkGC *gc;
501         int ymax, yc;
502
503         widget = GTK_WIDGET(tx);
504         window = widget->window;
505         gc = widget->style->fg_gc[widget->state];
506         yc = tx->yc;
507         ymax = widget->allocation.height - 1;
508
509         tx->display_x_offset=0;
510         tx->cursor_pos=-1;
511         tx->cursor_x_pos=-1;
512         tx->do_showframe=0;
513         //tx->current_fg=&tx->fg;
514         
515         gtk_widget_queue_draw(widget);
516 }
517
518 void gtk_tx_show_frame(GtkTx *tx, int show) {
519         if (show) {
520                 tx->current_fg=&tx->colors[COL_FG_FOCUS];
521                 tx->current_bg=&tx->colors[COL_BG_FOCUS];
522         } else {
523                 tx->current_fg=&tx->colors[COL_FG_NO_FOCUS];
524                 tx->current_bg=&tx->colors[COL_BG_NO_FOCUS];
525         }
526         
527         gtk_widget_queue_draw(GTK_WIDGET(tx));  
528 }
529
530 f_prec gtk_tx_get_zoom(GtkTx *tx) {
531         return tx->zoom;
532 }