Workaround compiler warnings and gtk+ deprecations
[terminatorX.git] / src / tX_widget.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_widget.c
19  
20     Description: This contains the implementation of the tx_widget.
21                  This file is based on the GTK+ widget example from
22                  the GTK+ 1.2 tutorial.
23 */
24
25 #include <math.h>
26
27 #include <gtk/gtk.h>
28 #include "tX_widget.h"
29 #include "tX_types.h"
30 #include "tX_global.h"
31 #include <malloc.h>
32 #include <stdlib.h>
33 #include <string.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 11
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(GtkWidget * widget);
47 void gtk_tx_set_data(GtkTx * tx, int16_t * wavdata, int wavsamples);
48 static void gtk_tx_realize(GtkWidget * widget);
49
50 static void gtk_tx_get_preferred_width (GtkWidget *widget, gint *minimal_height, gint *natural_height);
51 static void gtk_tx_get_preferred_height (GtkWidget *widget, gint *minimal_height, gint *natural_height);
52
53 static void gtk_tx_size_allocate(GtkWidget * widget, GtkAllocation * allocation);
54 static gboolean gtk_tx_draw(GtkWidget * widget, cairo_t *cr);
55 static void gtk_tx_prepare(GtkWidget * widget);
56 static void gtk_tx_update_render_buffer(GtkTx* tx);
57
58 /* data */
59 static GtkWidgetClass *parent_class = NULL;
60
61 /* widget "methods" */
62
63 GType gtk_tx_get_type() {
64         static GType tx_type = 0;
65
66         if (!tx_type) {
67                 static const GTypeInfo tx_info = {
68                         sizeof (GtkTxClass),
69                         NULL,
70                         NULL,
71                         (GClassInitFunc) gtk_tx_class_init, 
72                         NULL,
73                         NULL,
74                         sizeof (GtkTx),
75                         0,
76                         (GInstanceInitFunc) gtk_tx_init,
77                 };
78
79                 tx_type = g_type_register_static(GTK_TYPE_WIDGET, "GtkTx", &tx_info, 0);
80         }
81         
82         return tx_type;
83 }
84
85 static void gtk_tx_class_init(GtkTxClass * gclass) {
86         GtkWidgetClass *widget_class;
87         
88         widget_class = (GtkWidgetClass *) gclass;
89         parent_class = (GtkWidgetClass *) g_type_class_peek(gtk_widget_get_type());
90
91         widget_class->destroy = gtk_tx_destroy; 
92         widget_class->realize = gtk_tx_realize;
93         widget_class->draw = gtk_tx_draw;
94         widget_class->get_preferred_height = gtk_tx_get_preferred_height;
95         widget_class->get_preferred_width = gtk_tx_get_preferred_width;
96         widget_class->size_allocate = gtk_tx_size_allocate;
97 }
98
99 #define COL_FOCUS 0 
100 #define COL_NO_FOCUS 1
101
102 #define COL_BG_FOCUS     0
103 #define COL_BG_NO_FOCUS  1
104 #define COL_FG_FOCUS     2
105 #define COL_FG_NO_FOCUS  3
106 #define COL_CURSOR       4
107 #define COL_CURSOR_MUTE  5
108
109 void gtk_tx_update_colors(GtkTx *tx, GdkRGBA *vtt_color) {
110         int step;
111
112         gdk_rgba_parse(&tx->colors[COL_BG_FOCUS], globals.wav_display_bg_focus);
113         tx->colors[COL_BG_FOCUS].alpha=1.0;
114         gdk_rgba_parse(&tx->colors[COL_BG_NO_FOCUS], globals.wav_display_bg_no_focus);
115         tx->colors[COL_BG_NO_FOCUS].alpha=1.0;
116         
117         if (globals.wav_use_vtt_color && vtt_color) {
118                 memcpy(&tx->colors[COL_FG_FOCUS], vtt_color, sizeof(GdkRGBA));
119         } else {
120                 gdk_rgba_parse(&tx->colors[COL_FG_FOCUS], globals.wav_display_fg_focus);
121         }
122         tx->colors[COL_FG_FOCUS].alpha=1.0;
123         if (globals.wav_use_vtt_color && vtt_color) {
124                 memcpy(&tx->colors[COL_FG_NO_FOCUS], vtt_color, sizeof(GdkRGBA));
125         } else {
126                 gdk_rgba_parse(&tx->colors[COL_FG_NO_FOCUS], globals.wav_display_fg_no_focus);
127         }
128         tx->colors[COL_FG_NO_FOCUS].alpha=1.0;
129         
130         gdk_rgba_parse(&tx->colors[COL_CURSOR], globals.wav_display_cursor);
131         tx->colors[COL_CURSOR].alpha=1.0;
132         gdk_rgba_parse(&tx->colors[COL_CURSOR_MUTE], globals.wav_display_cursor_mute);
133         tx->colors[COL_CURSOR_MUTE].alpha=1.0;
134
135         for (step = 0; step < GTK_TX_HISTORY_LENGTH; step++) {
136                 double frac = (1.0 / ((double) GTK_TX_HISTORY_LENGTH + 2.0)) * ((double) step + 1.0);
137
138                 GdkRGBA *color = &tx->history_colors[step];
139                 color->red = tx->colors[COL_CURSOR].red;
140                 color->green = tx->colors[COL_CURSOR].green;
141                 color->blue = tx->colors[COL_CURSOR].blue;
142                 color->alpha = frac*frac/2;
143         }
144         tx->render_buffer_fg = NULL;
145         gtk_tx_prepare(GTK_WIDGET(tx));
146 }
147
148
149 static void gtk_tx_init(GtkTx * tx) {
150         tx->disp_data = NULL;
151         tx->data = NULL;
152         tx->samples = 0;
153 #ifdef USE_DISPLAY_NORMALIZE
154         tx->max_value=-1;
155 #endif
156         
157         memset(tx->colors, 0, sizeof(tx->colors));
158         memset(tx->history_colors, 0, sizeof(tx->history_colors));
159
160         gtk_tx_update_colors(tx, NULL);
161         
162         tx->current_fg=tx->audio_colors_focus;
163         tx->current_bg=&tx->colors[COL_BG_NO_FOCUS];
164         
165         tx->audio_colors_focus = NULL;
166         tx->audio_colors_nofocus = NULL;
167         
168         tx->spp=1;
169         tx->zoom=0;
170         tx->cursor_pos=0;
171         tx->cursor_x_pos=0;
172         
173         tx->render_buffer_surface_a = NULL;
174         tx->render_buffer_surface_b = NULL;
175         tx->current_render_buffer_surface = NULL;
176         tx->previous_render_buffer_surface = NULL;
177
178         tx->render_buffer_x_offset = -1;
179         tx->render_buffer_display_width = -1;
180         tx->render_buffer_fg = NULL;
181 }
182
183 GtkWidget *gtk_tx_new(int16_t * wavdata, int wavsamples) {
184         GtkTx *tx;
185
186         tx = (GtkTx *) g_object_new(gtk_tx_get_type (), NULL);
187
188         tx->data = wavdata;
189         tx->samples = wavsamples;
190         tx->zoom=0;
191         tx->display_x_offset=0;
192         
193         return GTK_WIDGET(tx);
194 }
195
196 static void gtk_tx_destroy(GtkWidget * widget) {
197         GtkTx *tx;
198         g_return_if_fail(widget != NULL);
199         g_return_if_fail(GTK_IS_TX(widget));
200
201         tx=GTK_TX(widget);
202         
203         if (tx->render_buffer_surface_a) {
204                 cairo_surface_destroy(tx->render_buffer_surface_a);
205                 cairo_surface_destroy(tx->render_buffer_surface_b);
206                 tx->render_buffer_surface_a = NULL; 
207                 tx->render_buffer_surface_b = NULL;
208         }
209
210         if (tx->disp_data) { 
211                 free(tx->disp_data);
212                 tx->disp_data=NULL;
213         }
214         
215         if (GTK_WIDGET_CLASS(parent_class)->destroy) {
216                 (*GTK_WIDGET_CLASS(parent_class)->destroy) (widget);
217         }       
218 }
219
220 #define MAX_ZOOM_WIDTH 500000.0
221
222 void gtk_tx_set_data(GtkTx * tx, int16_t * wavdata, int wavsamples) {
223         g_return_if_fail(tx != NULL);
224         g_return_if_fail(GTK_IS_TX(tx));
225
226         tx->data = wavdata;
227         tx->samples = wavsamples;
228 #ifdef USE_DISPLAY_NORMALIZE    
229         tx->max_value=-1;
230 #endif
231         
232         gtk_tx_prepare(GTK_WIDGET(tx));
233         gtk_widget_queue_draw(GTK_WIDGET(tx));
234 }
235
236 static void gtk_tx_realize(GtkWidget * widget) {
237         GdkWindowAttr attributes;
238         gint attributes_mask;
239         
240         g_return_if_fail(widget != NULL);
241         g_return_if_fail(GTK_IS_TX(widget));
242
243         gtk_widget_set_realized(widget, TRUE);
244
245         GtkAllocation allocation;
246         gtk_widget_get_allocation(widget, &allocation);
247         attributes.x = allocation.x;
248         attributes.y = allocation.y;
249         attributes.width = allocation.width;
250         attributes.height = allocation.height;
251         attributes.wclass = GDK_INPUT_OUTPUT;
252         attributes.window_type = GDK_WINDOW_CHILD;
253         attributes.event_mask = gtk_widget_get_events(widget) | GDK_EXPOSURE_MASK;
254         attributes.visual = gtk_widget_get_visual(widget);
255         attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
256
257         gtk_widget_set_window(widget, gdk_window_new(gtk_widget_get_parent_window(widget), &attributes, attributes_mask));
258
259         gdk_window_set_user_data(gtk_widget_get_window(widget), widget);
260 }
261
262 static void gtk_tx_get_preferred_width (GtkWidget *widget, gint *minimal_width, gint *natural_width) {
263   *minimal_width = *natural_width = TX_DEFAULT_SIZE_X;
264 }
265
266 static void gtk_tx_get_preferred_height (GtkWidget *widget, gint *minimal_height, gint *natural_height) {
267         *minimal_height = *natural_height = TX_DEFAULT_SIZE_Y;
268 }
269
270 static void gtk_tx_reallocate_disp_data(GtkWidget *widget) {
271         GtkAllocation allocation;
272         GtkTx *tx = GTK_TX(widget);
273         int x, sample, avg_pos;
274         int16_t *ptr;
275         f_prec value;
276
277         gtk_widget_get_allocation(widget, &allocation);
278
279         if (tx->disp_data) { free(tx->disp_data); tx->disp_data=NULL; }
280
281         if (tx->data) {
282                 int max_spp=tx->samples/allocation.width;
283                 int min_spp=tx->samples/MAX_ZOOM_WIDTH;
284                 gdouble diff;
285                 
286                 if (min_spp==0) min_spp=1;
287                 
288                 diff=max_spp-min_spp;
289                 
290                 tx->spp=min_spp+diff*(1.0-tx->zoom);
291                 tx->display_width = tx->samples/tx->spp;
292                 
293 #ifdef USE_DISPLAY_NORMALIZE    
294                 tx->max_value=-1;
295 #endif          
296                 
297             tx->disp_data = (int16_t *) malloc(tx->display_width * sizeof(int16_t));
298
299             if (tx->disp_data) {                        
300 #ifdef USE_DISPLAY_NORMALIZE            
301                         if (tx->max_value==-1) {
302                                 /* We haven't figured a max value yet... */
303                                 //puts("searching max...");
304                 
305                                 for (x = 0, ptr = tx->disp_data; x < tx->display_width; ptr++, x++) {
306                                         value = 0;
307                                         for (sample = x * tx->spp, avg_pos=1; sample < (x + 1) * tx->spp; sample++) {
308                                                 value += (abs(tx->data[sample])-value)/(double) avg_pos++;
309                                         }
310                                         if (value>tx->max_value) tx->max_value=value;
311                                         tx->disp_data[x] = value; 
312                                 }
313                                 for (x = 0; x < tx->display_width; x++) {
314                                         f_prec t=tx->disp_data[x]/(double) tx->max_value;
315                                         tx->disp_data[x]=(int) (t * (f_prec) (tx->yc));
316                                 }
317                         } else {
318 #endif                          
319                                 //puts("have max...");
320                                 /* We have a max value... */
321                                 for (x = 0, ptr = tx->disp_data; x < tx->display_width; ptr++, x++) {
322                                         f_prec t;
323                                         value = 0;
324                                         for (sample = x * tx->spp, avg_pos=1; sample < (x + 1) * tx->spp; sample++) {
325                                                 value += (abs(tx->data[sample])-value)/(double) avg_pos++;
326                                         }
327 #ifdef USE_DISPLAY_NORMALIZE                                    
328                                         t=value/(double) tx->max_value;
329 #else
330                                         t=value/32768.0;
331 #endif                                  
332                                         tx->disp_data[x] = (int) (t * (f_prec) (tx->yc)); 
333                                 }
334 #ifdef USE_DISPLAY_NORMALIZE
335                         }
336 #endif                  
337                 }
338         } else {
339             tx->disp_data = NULL;
340         }
341         
342 }
343
344 static void gtk_tx_prepare(GtkWidget * widget) {
345         GtkTx *tx;
346         int color;
347
348         g_return_if_fail(widget != NULL);
349         g_return_if_fail(GTK_IS_TX(widget));
350
351         tx = GTK_TX(widget);
352         
353         GtkAllocation allocation;
354         GdkRGBA midColor;
355         gboolean fg = (tx->current_fg == tx->audio_colors_focus);
356         
357         if (tx->audio_colors_focus) { 
358                 free(tx->audio_colors_focus); 
359                 free(tx->audio_colors_nofocus); 
360                 
361                 tx->audio_colors_focus = NULL; 
362                 tx->audio_colors_nofocus = NULL; 
363         } else {
364                 fg = FALSE;
365         }
366         
367         // update tx->yc
368         
369         gtk_widget_get_allocation(widget, &allocation);
370         tx->xc = allocation.width / 2;
371         tx->xmax = allocation.width;
372         tx->yc = allocation.height / 2;
373         tx->ymax = allocation.height;
374
375         // allocate colors
376         
377         tx->audio_colors_focus = (GdkRGBA *) malloc(tx->yc * sizeof(GdkRGBA));
378         tx->audio_colors_nofocus = (GdkRGBA *) malloc(tx->yc * sizeof(GdkRGBA));
379         
380         // no focus colors
381
382         midColor.red = tx->colors[COL_BG_NO_FOCUS].red + (tx->colors[COL_FG_NO_FOCUS].red - tx->colors[COL_BG_NO_FOCUS].red) / 4;
383         midColor.green = tx->colors[COL_BG_NO_FOCUS].green + (tx->colors[COL_FG_NO_FOCUS].green - tx->colors[COL_BG_NO_FOCUS].green) / 4;
384         midColor.blue = tx->colors[COL_BG_NO_FOCUS].blue + (tx->colors[COL_FG_NO_FOCUS].blue - tx->colors[COL_BG_NO_FOCUS].blue) / 4;
385         
386         for (color=0 ; color < tx->yc; color++) {
387                 float dist = (float) color / (float) tx->yc;
388                 
389                 tx->audio_colors_nofocus[color].red = midColor.red + dist*(tx->colors[COL_FG_NO_FOCUS].red - midColor.red);
390                 tx->audio_colors_nofocus[color].green = midColor.green + dist*(tx->colors[COL_FG_NO_FOCUS].green - midColor.green);
391                 tx->audio_colors_nofocus[color].blue = midColor.blue + dist*(tx->colors[COL_FG_NO_FOCUS].blue - midColor.blue);
392                 tx->audio_colors_nofocus[color].alpha = 1.0;
393         }
394         // focus colors
395
396         midColor.red = tx->colors[COL_BG_FOCUS].red + (tx->colors[COL_FG_FOCUS].red - tx->colors[COL_BG_FOCUS].red) / 4;
397         midColor.green = tx->colors[COL_BG_FOCUS].green + (tx->colors[COL_FG_FOCUS].green - tx->colors[COL_BG_FOCUS].green) / 4;
398         midColor.blue = tx->colors[COL_BG_FOCUS].blue + (tx->colors[COL_FG_FOCUS].blue - tx->colors[COL_BG_FOCUS].blue) / 4;
399         
400         for (color=0 ; color < tx->yc; color++) {
401                 float dist = (float) color / (float) tx->yc;
402                 
403                 tx->audio_colors_focus[color].red = midColor.red + dist*(tx->colors[COL_FG_FOCUS].red - midColor.red);
404                 tx->audio_colors_focus[color].green = midColor.green + dist*(tx->colors[COL_FG_FOCUS].green - midColor.green);
405                 tx->audio_colors_focus[color].blue = midColor.blue + dist*(tx->colors[COL_FG_FOCUS].blue - midColor.blue);
406                 tx->audio_colors_focus[color].alpha = 1.0;
407         }
408         
409         if (fg) {
410                 tx->current_fg = tx->audio_colors_focus;
411         } else {
412                 tx->current_fg = tx->audio_colors_nofocus;
413         }
414         
415         gtk_tx_reallocate_disp_data(widget);
416 }
417
418 static void gtk_tx_size_allocate(GtkWidget * widget, GtkAllocation * allocation) {
419         g_return_if_fail(widget != NULL);
420         g_return_if_fail(GTK_IS_TX(widget));
421         g_return_if_fail(allocation != NULL);
422
423         GtkTx *tx = GTK_TX(widget);
424
425         gtk_widget_set_allocation(widget, allocation);
426         
427         if (tx->render_buffer_surface_a) {
428                 cairo_surface_destroy(tx->render_buffer_surface_a);
429                 cairo_surface_destroy(tx->render_buffer_surface_b);
430                 tx->render_buffer_surface_a = NULL;
431                 tx->render_buffer_surface_b = NULL;
432         }
433         
434         tx->render_buffer_x_offset = -1;
435         tx->render_buffer_display_width = -1;
436         tx->render_buffer_fg = NULL;
437
438         if (gtk_widget_get_window(widget) != NULL) {
439                 tx->render_buffer_surface_a = gdk_window_create_similar_surface(gtk_widget_get_window(widget), CAIRO_CONTENT_COLOR, allocation->width, allocation->height);
440                 tx->render_buffer_surface_b = gdk_window_create_similar_surface(gtk_widget_get_window(widget), CAIRO_CONTENT_COLOR, allocation->width, allocation->height);
441         }
442         
443         tx->current_render_buffer_surface = tx->render_buffer_surface_a;
444         tx->previous_render_buffer_surface = tx->render_buffer_surface_b;
445
446         gtk_tx_prepare(widget);
447
448         if (gtk_widget_get_realized(widget)) {
449 #ifdef USE_DISPLAY_NORMALIZE            
450                 GtkTx *tx = GTK_TX(widget);
451                 tx->max_value=-1;
452 #endif          
453             gdk_window_move_resize(gtk_widget_get_window(widget), allocation->x, allocation->y, allocation->width, allocation->height);
454         }
455 }
456
457 void gtk_tx_set_zoom(GtkTx *tx, f_prec zoom, int is_playing) {
458         GtkWidget *wid=GTK_WIDGET(tx);
459         
460         if (zoom != tx->zoom) {
461                 tx->zoom=zoom;
462                 gtk_tx_reallocate_disp_data(wid);
463                 if (!is_playing || (zoom < 0.01)) {
464                         gtk_widget_queue_draw(wid);
465                 }
466         }
467 }
468
469 #define draw_line(x1, y1, x2, y2, rgba) { gdk_cairo_set_source_rgba(cr, rgba); cairo_move_to(cr, x1, y1); cairo_line_to(cr, x2, y2); cairo_stroke(cr); }
470 #define draw_line2(x1, y1, x2, y2) { cairo_move_to(cr, x1, y1); cairo_line_to(cr, x2, y2); cairo_stroke(cr); }
471 #define draw_sample(x, y1, y2, rgba) draw_line(x, y1, x, y2, rgba)
472 #define draw_sample2(x, y1, y2) draw_line2(x, y1, x, y2)
473 #define draw_rectangle(rect, rgba) { gdk_cairo_set_source_rgba(cr, rgba); cairo_rectangle(cr, rect.x, rect.y, rect.width, rect.height); cairo_fill(cr); }
474 #define draw_rectangle2(x, y, width, height, rgba) { gdk_cairo_set_source_rgba(cr, rgba); cairo_rectangle(cr, x, y, width, height); cairo_fill(cr); }
475
476 static void gtk_tx_update_render_buffer(GtkTx *tx) {
477         int x, src_x;
478         
479         if ((tx->render_buffer_display_width != tx->display_width) || 
480                         (tx->render_buffer_fg != tx->current_fg)) {
481                 
482                 // need to redraw all samples
483                 cairo_t *cr = cairo_create(tx->render_buffer_surface_a);
484                 tx->current_render_buffer_surface = tx->render_buffer_surface_a;
485                 tx->previous_render_buffer_surface = tx->render_buffer_surface_b;
486
487                 draw_rectangle2(0, 0, tx->xmax, tx->ymax, tx->current_bg);
488                 for (x=0, src_x=tx->display_x_offset; x < tx->xmax; x++, src_x++) {
489                         int dy = tx->disp_data[src_x];
490                         draw_sample(x, tx->yc-dy, tx->yc+dy+1, &tx->current_fg[dy]);
491                 }
492                 cairo_destroy(cr);
493
494                 tx->render_buffer_display_width = tx->display_width;
495                 tx->render_buffer_x_offset = tx->display_x_offset;
496                 tx->render_buffer_fg = tx->current_fg;
497         } else if (tx->render_buffer_x_offset != tx->display_x_offset) {
498                 // switch buffers
499                 cairo_surface_t *surface = tx->current_render_buffer_surface;
500                 tx->current_render_buffer_surface = tx->previous_render_buffer_surface;
501                 tx->previous_render_buffer_surface = surface;
502                 int motion = tx->render_buffer_x_offset - tx->display_x_offset;
503                 cairo_t *cr = cairo_create(tx->current_render_buffer_surface);
504
505                 int cur_x, start_x, stop_x;
506                 int width = tx->xmax - abs(motion);
507
508                 if (motion > 0) {
509                         // move right
510                         cur_x = motion;
511                         start_x = 0;
512                         stop_x = motion;
513                 } else {
514                         // move left
515                         cur_x = 0;
516                         start_x = width;
517                         stop_x = tx->xmax;
518                 }
519     
520                 cairo_set_source_surface(cr, tx->previous_render_buffer_surface, motion, 0);
521                 cairo_rectangle(cr, cur_x, 0, width, tx->ymax);
522                 cairo_fill(cr);
523
524                 // draw the rest;
525                 draw_rectangle2(start_x, 0, stop_x - start_x, tx->ymax, tx->current_bg);
526                 for (x=start_x, src_x=tx->display_x_offset+start_x; x < stop_x; x++, src_x++) {
527                         int dy = tx->disp_data[src_x];
528                         draw_sample(x, tx->yc-dy, tx->yc+dy+1, &tx->current_fg[dy]);
529                 }
530
531                 cairo_destroy(cr);
532
533                 tx->render_buffer_x_offset = tx->display_x_offset;
534         }
535 }
536
537 static gboolean gtk_tx_draw(GtkWidget * widget, cairo_t *cr) {
538         GtkTx *tx;
539         gint x;
540         GdkRectangle area;
541         
542         g_return_val_if_fail(widget != NULL, FALSE);
543         g_return_val_if_fail(GTK_IS_TX(widget), FALSE);
544
545         tx = GTK_TX(widget);
546
547         gdk_cairo_get_clip_rectangle(cr, &area);
548         
549         cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
550         cairo_set_line_width(cr, 1);
551
552         if (tx->disp_data) {
553                 int step;
554                 int x_offset;
555
556                 if (tx->zoom > 0.0) {
557                     x_offset= tx->cursor_pos > tx->xc ? tx->cursor_pos-tx->xc : 0;
558                         if (x_offset+tx->xmax > tx->display_width) {
559                                 x_offset = tx->display_width-tx->xmax;
560                         }
561                 } else {
562                     x_offset = 0;                       
563                 }
564
565                 tx->cursor_x_pos = tx->cursor_pos-x_offset;
566                 tx->display_x_offset = x_offset;
567
568                 gtk_tx_update_render_buffer(tx);
569                 cairo_set_source_surface(cr, tx->current_render_buffer_surface, 0, 0);
570                 cairo_paint(cr);
571
572                 tx->cursor_history[tx->cursor_history_offset] = tx->cursor_pos;
573                 tx->cursor_history_offset++;
574                 if (tx->cursor_history_offset >= GTK_TX_HISTORY_LENGTH) {
575                         tx->cursor_history_offset -= GTK_TX_HISTORY_LENGTH;
576                 }
577
578                 if (globals.wav_display_history && !tx->mute) {
579                         int prev_sample_pos = -1;
580                         /* draw history */
581
582                         int max_dist = 1;
583                         for (step = 0; step < GTK_TX_HISTORY_LENGTH; step++) {
584                             max_dist = tX_max(max_dist, abs(tx->cursor_pos - tx->cursor_history[step]));
585                         }                       
586
587                         for (step = 0; step < GTK_TX_HISTORY_LENGTH; step++) {
588                                 int history_pos = tx->cursor_history_offset - step;
589
590                                 if (history_pos < 0) {
591                                         history_pos += GTK_TX_HISTORY_LENGTH;
592                                 }
593
594                                 int sample_pos = tx->cursor_history[history_pos];
595
596                                 if ((sample_pos >= 0) && (prev_sample_pos >= 0)) {
597                                         int sample_x_pos = sample_pos - tx->display_x_offset;
598                                         int prev_sample_x_pos = prev_sample_pos - tx->display_x_offset;
599                                         int min, max;
600
601                                         gdk_cairo_set_source_rgba(cr, &tx->history_colors[GTK_TX_HISTORY_LENGTH-step]);
602                                         if (prev_sample_x_pos > sample_x_pos) {
603                                                 min = sample_x_pos;
604                                                 max = prev_sample_x_pos;
605                                         } else {
606                                                 min = prev_sample_x_pos;
607                                                 max = sample_x_pos;
608                                         }
609                                         
610                                         if ((max - min) <= tx->xc) {
611                                                 for (x=min; x < max; x++) {
612                                                         double scale = (1.0-(fabs(x-tx->cursor_x_pos) / (double) max_dist));
613                                                         int dist = scale * scale * 0.5 * (double) tx->ymax;
614                                                         int value = tx->disp_data[x+tx->display_x_offset] + dist;
615                                                         draw_sample2(x, tx->yc-value, tx->yc+value+1);
616                                                 }
617                                         } else {
618                                                 for (x = 0; x < min; x++) {
619                                                         double scale = (1.0-(fabs(x-tx->cursor_x_pos) / (double) max_dist));
620                                                         int dist = scale * scale * 0.5 * (double) tx->ymax;
621                                                         int value = tx->disp_data[x+tx->display_x_offset] + dist;
622                                                         draw_sample2(x, tx->yc-value, tx->yc+value+1);
623                                                 }
624                                                 for (x= max; x < tx->xmax; x++) {
625                                                         double scale = (1.0-(fabs(x-tx->cursor_x_pos) / (double) max_dist));
626                                                         int dist = scale * scale * 0.5 * (double) tx->ymax;
627                                                         int value = tx->disp_data[x+tx->display_x_offset] + dist;
628                                                         draw_sample2(x, tx->yc-value, tx->yc+value+1);
629                                                 }
630                                         }
631                                 }
632
633                                 prev_sample_pos = sample_pos;
634                         }
635                 }
636                 /* draw cursor */
637                 draw_sample(tx->cursor_x_pos, 0, tx->ymax, tx->mute ? &tx->colors[COL_CURSOR_MUTE] : &tx->colors[COL_CURSOR]);
638         } else {
639                 draw_rectangle(area, tx->current_bg);
640                 draw_line(area.x, tx->yc, area.width, tx->yc, tx->current_fg);
641         }
642         
643         return FALSE;
644 }
645
646 void gtk_tx_update_pos_display(GtkTx * tx, int sample, int mute) {
647         GtkWidget *widget = GTK_WIDGET(tx);
648         
649         tx->cursor_pos = sample / tx->spp;
650         tx->mute = mute;
651
652         gtk_widget_queue_draw(widget);
653 }
654
655 void gtk_tx_cleanup_pos_display(GtkTx * tx) {
656         GtkWidget *widget;
657         GtkAllocation allocation;
658         int step;
659
660         widget = GTK_WIDGET(tx);
661         gtk_widget_get_allocation(widget, &allocation);
662
663         tx->display_x_offset=0;
664         tx->cursor_pos=-1;
665         tx->cursor_x_pos=-1;
666
667         for (step = 0; step < GTK_TX_HISTORY_LENGTH; step++) {
668                 tx->cursor_history[step] = -1;
669         }
670         
671         gtk_widget_queue_draw(widget);
672 }
673
674 void gtk_tx_show_focus(GtkTx *tx, int show) {
675         if (show) {
676                 tx->current_fg=tx->audio_colors_focus;
677                 tx->current_bg=&tx->colors[COL_BG_FOCUS];
678         } else {
679                 tx->current_fg=tx->audio_colors_nofocus;
680                 tx->current_bg=&tx->colors[COL_BG_NO_FOCUS];
681         }
682         
683         gtk_widget_queue_draw(GTK_WIDGET(tx));  
684 }
685
686 f_prec gtk_tx_get_zoom(GtkTx *tx) {
687         return tx->zoom;
688 }