1e51c35e77c1628f0df8e17c4dd844182aa2775a
[terminatorX.git] / src / tX_widget.c
1 /*
2     terminatorX - realtime audio scratching software
3     Copyright (C) 1999-2016  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
57 /* data */
58 static GtkWidgetClass *parent_class = NULL;
59
60 /* widget "methods" */
61
62 GType gtk_tx_get_type() {
63         static GType tx_type = 0;
64
65         if (!tx_type) {
66                 static const GTypeInfo tx_info = {
67                         sizeof (GtkTxClass),
68                         NULL,
69                         NULL,
70                         (GClassInitFunc) gtk_tx_class_init, 
71                         NULL,
72                         NULL,
73                         sizeof (GtkTx),
74                         0,
75                         (GInstanceInitFunc) gtk_tx_init,
76                 };
77
78                 tx_type = g_type_register_static(GTK_TYPE_WIDGET, "GtkTx", &tx_info, 0);
79         }
80         
81         return tx_type;
82 }
83
84 static void gtk_tx_class_init(GtkTxClass * gclass) {
85         GtkWidgetClass *widget_class;
86         
87         widget_class = (GtkWidgetClass *) gclass;
88         parent_class = (GtkWidgetClass *) g_type_class_peek(gtk_widget_get_type());
89
90         widget_class->destroy = gtk_tx_destroy; 
91         widget_class->realize = gtk_tx_realize;
92         widget_class->draw = gtk_tx_draw;
93         widget_class->get_preferred_height = gtk_tx_get_preferred_height;
94         widget_class->get_preferred_width = gtk_tx_get_preferred_width;
95         widget_class->size_allocate = gtk_tx_size_allocate;
96 }
97
98 #define COL_FOCUS 0 
99 #define COL_NO_FOCUS 1
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         int step;
110
111         gdk_rgba_parse(&tx->colors[COL_BG_FOCUS], globals.wav_display_bg_focus);
112         tx->colors[COL_BG_FOCUS].alpha=1.0;
113         gdk_rgba_parse(&tx->colors[COL_BG_NO_FOCUS], globals.wav_display_bg_no_focus);
114         tx->colors[COL_BG_NO_FOCUS].alpha=1.0;
115         
116         gdk_rgba_parse(&tx->colors[COL_FG_FOCUS], globals.wav_display_fg_focus);
117         tx->colors[COL_FG_FOCUS].alpha=1.0;
118         gdk_rgba_parse(&tx->colors[COL_FG_NO_FOCUS], globals.wav_display_fg_no_focus);
119         tx->colors[COL_FG_NO_FOCUS].alpha=1.0;
120         
121         gdk_rgba_parse(&tx->colors[COL_CURSOR], globals.wav_display_cursor);
122         tx->colors[COL_CURSOR].alpha=1.0;
123         gdk_rgba_parse(&tx->colors[COL_CURSOR_MUTE], globals.wav_display_cursor_mute);
124         tx->colors[COL_CURSOR_MUTE].alpha=1.0;
125
126         for (step = 0; step < GTK_TX_HISTORY_LENGTH; step++) {
127                 double frac = (1.0 / ((double) GTK_TX_HISTORY_LENGTH + 2.0)) * ((double) step + 1.0);
128
129                 GdkRGBA *color = &tx->history_colors[step];
130                 color->red = tx->colors[COL_CURSOR].red;
131                 color->green = tx->colors[COL_CURSOR].green;
132                 color->blue = tx->colors[COL_CURSOR].blue;
133                 color->alpha = frac*frac/2;
134 //              printf("%i, %lf\n", step, frac);
135         }
136 }
137
138
139 static void gtk_tx_init(GtkTx * tx) {
140         tx->disp_data = NULL;
141         tx->data = NULL;
142         tx->samples = 0;
143 #ifdef USE_DISPLAY_NORMALIZE
144         tx->max_value=-1;
145 #endif
146         
147         memset(tx->colors, 0, sizeof(tx->colors));
148         memset(tx->history_colors, 0, sizeof(tx->history_colors));
149
150         gtk_tx_update_colors(tx);
151         
152         tx->current_fg=tx->audio_colors_focus;
153         tx->current_bg=&tx->colors[COL_BG_NO_FOCUS];
154         
155         tx->audio_colors_focus = NULL;
156         tx->audio_colors_nofocus = NULL;
157         
158         tx->spp=1;
159         tx->zoom=0;
160         tx->cursor_pos=0;
161         tx->cursor_x_pos=0;
162         
163         tx->surface = NULL;
164 }
165
166 GtkWidget *gtk_tx_new(int16_t * wavdata, int wavsamples) {
167         GtkTx *tx;
168
169         tx = (GtkTx *) g_object_new(gtk_tx_get_type (), NULL);
170
171         tx->data = wavdata;
172         tx->samples = wavsamples;
173         tx->zoom=0;
174         tx->display_x_offset=0;
175         
176         return GTK_WIDGET(tx);
177 }
178
179 static void gtk_tx_destroy(GtkWidget * widget) {
180         GtkTx *tx;
181         g_return_if_fail(widget != NULL);
182         g_return_if_fail(GTK_IS_TX(widget));
183
184         tx=GTK_TX(widget);
185         
186         if (tx->disp_data) { 
187                 free(tx->disp_data);
188                 tx->disp_data=NULL;
189         }
190         
191         if (GTK_WIDGET_CLASS(parent_class)->destroy) {
192                 (*GTK_WIDGET_CLASS(parent_class)->destroy) (widget);
193         }       
194 }
195
196 #define MAX_ZOOM_WIDTH 500000.0
197
198 void gtk_tx_set_data(GtkTx * tx, int16_t * wavdata, int wavsamples) {
199         g_return_if_fail(tx != NULL);
200         g_return_if_fail(GTK_IS_TX(tx));
201
202         tx->data = wavdata;
203         tx->samples = wavsamples;
204 #ifdef USE_DISPLAY_NORMALIZE    
205         tx->max_value=-1;
206 #endif
207         
208         gtk_tx_prepare(GTK_WIDGET(tx));
209         gtk_widget_queue_draw(GTK_WIDGET(tx));
210 }
211
212 static void gtk_tx_realize(GtkWidget * widget) {
213         GdkWindowAttr attributes;
214         gint attributes_mask;
215         GtkTx *tx;
216         
217         g_return_if_fail(widget != NULL);
218         g_return_if_fail(GTK_IS_TX(widget));
219
220         tx = GTK_TX(widget);
221         gtk_widget_set_realized(widget, TRUE);
222
223         GtkAllocation allocation;
224         gtk_widget_get_allocation(widget, &allocation);
225         attributes.x = allocation.x;
226         attributes.y = allocation.y;
227         attributes.width = allocation.width;
228         attributes.height = allocation.height;
229         attributes.wclass = GDK_INPUT_OUTPUT;
230         attributes.window_type = GDK_WINDOW_CHILD;
231         attributes.event_mask = gtk_widget_get_events(widget) | GDK_EXPOSURE_MASK;
232         attributes.visual = gtk_widget_get_visual(widget);
233         attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
234
235         gtk_widget_set_window(widget, gdk_window_new(gtk_widget_get_parent_window(widget), &attributes, attributes_mask));
236
237         gdk_window_set_user_data(gtk_widget_get_window(widget), widget);
238
239         if (tx->surface) {
240                 cairo_surface_destroy(tx->surface);
241         }
242         
243         tx->surface = gdk_window_create_similar_surface(gtk_widget_get_window(widget), CAIRO_CONTENT_COLOR, allocation.width, allocation.height);
244 }
245
246 static void gtk_tx_get_preferred_width (GtkWidget *widget, gint *minimal_width, gint *natural_width) {
247   *minimal_width = *natural_width = TX_DEFAULT_SIZE_X;
248 }
249
250 static void gtk_tx_get_preferred_height (GtkWidget *widget, gint *minimal_height, gint *natural_height) {
251         *minimal_height = *natural_height = TX_DEFAULT_SIZE_Y;
252 }
253
254 static void gtk_tx_reallocate_disp_data(GtkWidget *widget) {
255         GtkAllocation allocation;
256         GtkTx *tx = GTK_TX(widget);
257         int x, sample, avg_pos;
258         int16_t *ptr;
259         f_prec value;
260
261         gtk_widget_get_allocation(widget, &allocation);
262
263         if (tx->disp_data) { free(tx->disp_data); tx->disp_data=NULL; }
264
265         if (tx->data) {
266                 int max_spp=tx->samples/allocation.width;
267                 int min_spp=tx->samples/MAX_ZOOM_WIDTH;
268                 gdouble diff;
269                 
270                 if (min_spp==0) min_spp=1;
271                 
272                 diff=max_spp-min_spp;
273                 
274                 tx->spp=min_spp+diff*(1.0-tx->zoom);
275                 tx->display_width = tx->samples/tx->spp;
276                 
277 #ifdef USE_DISPLAY_NORMALIZE    
278                 tx->max_value=-1;
279 #endif          
280                 
281             tx->disp_data = (int16_t *) malloc(tx->display_width * sizeof(int16_t));
282
283             if (tx->disp_data) {                        
284 #ifdef USE_DISPLAY_NORMALIZE            
285                         if (tx->max_value==-1) {
286                                 /* We haven't figured a max value yet... */
287                                 //puts("searching max...");
288                 
289                                 for (x = 0, ptr = tx->disp_data; x < tx->display_width; ptr++, x++) {
290                                         value = 0;
291                                         for (sample = x * tx->spp, avg_pos=1; sample < (x + 1) * tx->spp; sample++) {
292                                                 value += (abs(tx->data[sample])-value)/(double) avg_pos++;
293                                         }
294                                         if (value>tx->max_value) tx->max_value=value;
295                                         tx->disp_data[x] = value; 
296                                 }
297                                 for (x = 0; x < tx->display_width; x++) {
298                                         f_prec t=tx->disp_data[x]/(double) tx->max_value;
299                                         tx->disp_data[x]=(int) (t * (f_prec) (tx->yc));
300                                 }
301                         } else {
302 #endif                          
303                                 //puts("have max...");
304                                 /* We have a max value... */
305                                 for (x = 0, ptr = tx->disp_data; x < tx->display_width; ptr++, x++) {
306                                         f_prec t;
307                                         value = 0;
308                                         for (sample = x * tx->spp, avg_pos=1; sample < (x + 1) * tx->spp; sample++) {
309                                                 value += (abs(tx->data[sample])-value)/(double) avg_pos++;
310                                         }
311 #ifdef USE_DISPLAY_NORMALIZE                                    
312                                         t=value/(double) tx->max_value;
313 #else
314                                         t=value/32768.0;
315 #endif                                  
316                                         tx->disp_data[x] = (int) (t * (f_prec) (tx->yc)); 
317                                 }
318 #ifdef USE_DISPLAY_NORMALIZE
319                         }
320 #endif                  
321                 }
322         } else {
323             tx->disp_data = NULL;
324         }
325         
326 }
327
328 static void gtk_tx_prepare(GtkWidget * widget) {
329         GtkTx *tx;
330         int color;
331
332         g_return_if_fail(widget != NULL);
333         g_return_if_fail(GTK_IS_TX(widget));
334
335         tx = GTK_TX(widget);
336         
337         GtkAllocation allocation;
338         GdkRGBA midColor;
339         gboolean fg = (tx->current_fg == tx->audio_colors_focus);
340         
341         if (tx->audio_colors_focus) { 
342                 free(tx->audio_colors_focus); 
343                 free(tx->audio_colors_nofocus); 
344                 
345                 tx->audio_colors_focus = NULL; 
346                 tx->audio_colors_nofocus = NULL; 
347         } else {
348                 fg = FALSE;
349         }
350         
351         // update tx->yc
352         
353         gtk_widget_get_allocation(widget, &allocation);
354         tx->xc = allocation.width / 2;
355         tx->xmax = allocation.width;
356         tx->yc = allocation.height / 2;
357         tx->ymax = allocation.height;
358
359         // allocate colors
360         
361         tx->audio_colors_focus = (GdkRGBA *) malloc(tx->yc * sizeof(GdkRGBA));
362         tx->audio_colors_nofocus = (GdkRGBA *) malloc(tx->yc * sizeof(GdkRGBA));
363         
364         // no focus colors
365
366         midColor.red = tx->colors[COL_BG_NO_FOCUS].red + (tx->colors[COL_FG_NO_FOCUS].red - tx->colors[COL_BG_NO_FOCUS].red) / 4;
367         midColor.green = tx->colors[COL_BG_NO_FOCUS].green + (tx->colors[COL_FG_NO_FOCUS].green - tx->colors[COL_BG_NO_FOCUS].green) / 4;
368         midColor.blue = tx->colors[COL_BG_NO_FOCUS].blue + (tx->colors[COL_FG_NO_FOCUS].blue - tx->colors[COL_BG_NO_FOCUS].blue) / 4;
369         
370         for (color=0 ; color < tx->yc; color++) {
371                 float dist = (float) color / (float) tx->yc;
372                 
373                 tx->audio_colors_nofocus[color].red = midColor.red + dist*(tx->colors[COL_FG_NO_FOCUS].red - midColor.red);
374                 tx->audio_colors_nofocus[color].green = midColor.green + dist*(tx->colors[COL_FG_NO_FOCUS].green - midColor.green);
375                 tx->audio_colors_nofocus[color].blue = midColor.blue + dist*(tx->colors[COL_FG_NO_FOCUS].blue - midColor.blue);
376                 tx->audio_colors_nofocus[color].alpha = 1.0;
377         }
378         // focus colors
379
380         midColor.red = tx->colors[COL_BG_FOCUS].red + (tx->colors[COL_FG_FOCUS].red - tx->colors[COL_BG_FOCUS].red) / 4;
381         midColor.green = tx->colors[COL_BG_FOCUS].green + (tx->colors[COL_FG_FOCUS].green - tx->colors[COL_BG_FOCUS].green) / 4;
382         midColor.blue = tx->colors[COL_BG_FOCUS].blue + (tx->colors[COL_FG_FOCUS].blue - tx->colors[COL_BG_FOCUS].blue) / 4;
383         
384         for (color=0 ; color < tx->yc; color++) {
385                 float dist = (float) color / (float) tx->yc;
386                 
387                 tx->audio_colors_focus[color].red = midColor.red + dist*(tx->colors[COL_FG_FOCUS].red - midColor.red);
388                 tx->audio_colors_focus[color].green = midColor.green + dist*(tx->colors[COL_FG_FOCUS].green - midColor.green);
389                 tx->audio_colors_focus[color].blue = midColor.blue + dist*(tx->colors[COL_FG_FOCUS].blue - midColor.blue);
390                 tx->audio_colors_focus[color].alpha = 1.0;
391         }
392         
393         if (fg) {
394                 tx->current_fg = tx->audio_colors_focus;
395         } else {
396                 tx->current_fg = tx->audio_colors_nofocus;
397         }
398         
399         gtk_tx_reallocate_disp_data(widget);
400 }
401
402 static void gtk_tx_size_allocate(GtkWidget * widget, GtkAllocation * allocation) {
403         g_return_if_fail(widget != NULL);
404         g_return_if_fail(GTK_IS_TX(widget));
405         g_return_if_fail(allocation != NULL);
406
407         gtk_widget_set_allocation(widget, allocation);
408
409         gtk_tx_prepare(widget);
410
411         if (gtk_widget_get_realized(widget)) {
412 #ifdef USE_DISPLAY_NORMALIZE            
413                 GtkTx *tx = GTK_TX(widget);
414                 tx->max_value=-1;
415 #endif          
416             gdk_window_move_resize(gtk_widget_get_window(widget), allocation->x, allocation->y, allocation->width, allocation->height);
417         }
418 }
419
420 void gtk_tx_set_zoom(GtkTx *tx, f_prec zoom, int is_playing) {
421         GtkWidget *wid=GTK_WIDGET(tx);
422         
423         if (zoom != tx->zoom) {
424                 tx->zoom=zoom;
425                 gtk_tx_reallocate_disp_data(wid);
426                 if (!is_playing || (zoom < 0.01)) {
427                         gtk_widget_queue_draw(wid);
428                 }
429         }
430 }
431
432 #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); }
433 #define draw_sample(x, y1, y2, rgba) draw_line(x, y1, x, y2, rgba)
434 #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); }
435
436 static gboolean gtk_tx_draw(GtkWidget * widget, cairo_t *cr) {
437         GtkTx *tx;
438         gint x;
439         GdkRectangle area;
440         
441         g_return_val_if_fail(widget != NULL, FALSE);
442         g_return_val_if_fail(GTK_IS_TX(widget), FALSE);
443
444         tx = GTK_TX(widget);
445         
446         gdk_cairo_get_clip_rectangle(cr, &area);
447         
448         cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
449         cairo_set_source_surface(cr, tx->surface, 0, 0);
450         cairo_set_line_width(cr, 1);
451
452         if (tx->disp_data) {
453                 int src_x, step;
454                 int x_offset;
455
456                 if (tx->zoom > 0.0) {
457                     x_offset= tx->cursor_pos > tx->xc ? tx->cursor_pos-tx->xc : 0;
458                         if (x_offset+tx->xmax > tx->display_width) {
459                                 x_offset = tx->display_width-tx->xmax;
460                         }
461                 } else {
462                     x_offset = 0;                       
463                 }
464
465                 tx->cursor_x_pos = tx->cursor_pos-x_offset;
466                 tx->display_x_offset = x_offset;
467
468                 draw_rectangle(area, tx->current_bg);
469                 for (x=area.x, src_x=tx->display_x_offset+area.x; x < area.x + area.width; x++, src_x++) {
470                         if (x!=tx->cursor_x_pos) {
471                                 int dy = tx->disp_data[src_x];
472                                 draw_sample(x, tx->yc-dy, tx->yc+dy+1, &tx->current_fg[dy]);
473                         }
474                 }
475
476                 tx->cursor_history[tx->cursor_history_offset] = tx->cursor_pos;
477                 tx->cursor_history_offset++;
478                 if (tx->cursor_history_offset >= GTK_TX_HISTORY_LENGTH) {
479                         tx->cursor_history_offset -= GTK_TX_HISTORY_LENGTH;
480                 }
481
482                 if (globals.wav_display_history && !tx->mute) {
483                         int prev_sample_pos = -1;
484                         /* draw history */
485
486                         for (step = 0; step < GTK_TX_HISTORY_LENGTH; step++) {
487                                 int history_pos = tx->cursor_history_offset - step;
488
489                                 if (history_pos < 0) {
490                                         history_pos += GTK_TX_HISTORY_LENGTH;
491                                 }
492
493                                 int sample_pos = tx->cursor_history[history_pos];
494
495                                 if ((sample_pos >= 0) && (prev_sample_pos >= 0)) {
496                                         int sample_x_pos = sample_pos - tx->display_x_offset;
497                                         int prev_sample_x_pos = prev_sample_pos - tx->display_x_offset;
498                                         int min, max;
499
500                                         if (prev_sample_x_pos > sample_x_pos) {
501                                                 min = sample_x_pos;
502                                                 max = prev_sample_x_pos;
503                                         } else {
504                                                 min = prev_sample_x_pos;
505                                                 max = sample_x_pos;
506                                         }
507                                         
508                                         if ((max - min) <= tx->xc) {
509                                                 for (x=min; x < max; x++) {
510                                                         int value = tx->disp_data[x+tx->display_x_offset];
511                                                         draw_sample(x, tx->yc-value, tx->yc+value+1, &tx->history_colors[GTK_TX_HISTORY_LENGTH-step]);
512                                                 }
513                                         } else {
514                                                 for (x = 0; x < min; x++) {
515                                                         int value = tx->disp_data[x+tx->display_x_offset];
516                                                         draw_sample(x, tx->yc-value, tx->yc+value+1, &tx->history_colors[GTK_TX_HISTORY_LENGTH-step]);
517                                                 }
518                                                 for (x= max; x < tx->xmax; x++) {
519                                                         int value = tx->disp_data[x+tx->display_x_offset];
520                                                         draw_sample(x, tx->yc-value, tx->yc+value+1, &tx->history_colors[GTK_TX_HISTORY_LENGTH-step]);
521                                                 }
522                                         }
523                                 }
524
525                                 prev_sample_pos = sample_pos;
526                         }
527                 }
528                 /* draw cursor */
529                 draw_sample(tx->cursor_x_pos, 0, tx->ymax, tx->mute ? &tx->colors[COL_CURSOR_MUTE] : &tx->colors[COL_CURSOR]);
530         } else {
531                 draw_rectangle(area, tx->current_bg);
532                 draw_line(area.x, tx->yc, area.width, tx->yc, tx->current_fg);
533         }
534         
535         return FALSE;
536 }
537
538 void gtk_tx_update_pos_display(GtkTx * tx, int sample, int mute) {
539         GtkWidget *widget = GTK_WIDGET(tx);
540         
541         tx->cursor_pos = sample / tx->spp;
542         tx->mute = mute;
543
544         gtk_widget_queue_draw(widget);
545 }
546
547 void gtk_tx_cleanup_pos_display(GtkTx * tx) {
548         GtkWidget *widget;
549         GtkAllocation allocation;
550         int step;
551
552         widget = GTK_WIDGET(tx);
553         gtk_widget_get_allocation(widget, &allocation);
554
555         tx->display_x_offset=0;
556         tx->cursor_pos=-1;
557         tx->cursor_x_pos=-1;
558
559         for (step = 0; step < GTK_TX_HISTORY_LENGTH; step++) {
560                 tx->cursor_history[step] = -1;
561         }
562         
563         gtk_widget_queue_draw(widget);
564 }
565
566 void gtk_tx_show_focus(GtkTx *tx, int show) {
567         if (show) {
568                 tx->current_fg=tx->audio_colors_focus;
569                 tx->current_bg=&tx->colors[COL_BG_FOCUS];
570         } else {
571                 tx->current_fg=tx->audio_colors_nofocus;
572                 tx->current_bg=&tx->colors[COL_BG_NO_FOCUS];
573         }
574         
575         gtk_widget_queue_draw(GTK_WIDGET(tx));  
576 }
577
578 f_prec gtk_tx_get_zoom(GtkTx *tx) {
579         return tx->zoom;
580 }