really work around the locale problem this time
[gwave-svn.git] / src / event.c
blobd850a5868a6a6f7bfbacabc3c94c8f43923d99c4
1 /*
2 * event.c, part of the gwave waveform viewer tool
3 * Functions for handling low-level events (clicks, drag-and-drop)
4 * Some drawing things are here if they are related to mouse operations;
5 * perhaps they should move.
7 * Copyright (C) 1998-2002 Stephen G. Tell
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU Library General Public
20 * License along with this library; if not, write to the Free
21 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include <ctype.h>
27 #include <math.h>
28 #include <setjmp.h>
29 #include <stdarg.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <unistd.h>
34 #include <errno.h>
35 #include <sys/time.h>
37 #include <gtk/gtk.h>
39 #include <scwm_guile.h>
40 #include <gwave.h>
41 #include <wavewin.h>
42 #include <measurebtn.h>
43 #include <guile-gnome-gobject/gobject.h>
45 void destroy_handler(GtkWidget *widget, gpointer data)
47 gtk_main_quit();
50 /*
51 * Set the X pointer cursor for all wavepanels: used to provide a
52 * hint that we're expecting the user to drag out a line or region.
54 void
55 set_all_wp_cursors(int cnum)
57 GdkCursor *cursor;
58 int i;
59 WavePanel *wp;
61 if(cnum == -1)
62 cursor = NULL;
63 else
64 cursor = gdk_cursor_new(cnum);
65 for(i = 0; i < wtable->npanels; i++) {
66 wp = wtable->panels[i];
67 gdk_window_set_cursor(wp->drawing->window, cursor);
69 if(cursor)
70 gdk_cursor_destroy(cursor);
74 * The next several routines implement the generic operation of
75 * selecting a subset of the visible part of the X axis by dragging
76 * with button 1.
78 SCM_DEFINE(select_range_x, "select-range-x", 1, 0, 0,
79 (SCM proc),
80 "Prompt the user to select a range of the visible X axis using"
81 "button 1 of the mouse. "
82 "When finished, PROC is called with 3 arguments, the"
83 "WavePanel where the range is located, and the"
84 "begining and ending X pixel value of the selection.")
85 #define FUNC_NAME s_select_range_x
87 VALIDATE_ARG_PROC(1, proc);
89 scm_gc_protect_object(proc);
90 wtable->srange->done_proc = proc;
91 wtable->srange->type = SR_X;
93 set_all_wp_cursors(GDK_RIGHT_SIDE);
94 wtable->mstate = M_SELRANGE_ARMED;
96 return SCM_UNSPECIFIED;
98 #undef FUNC_NAME
100 SCM_DEFINE(select_range_y, "select-range-y", 1, 0, 0,
101 (SCM proc),
102 "Prompt the user to select a range of the visible Y axis using"
103 "button 1 of the mouse. "
104 "When finished, PROC is called with 3 arguments, the"
105 "WavePanel where the range is located, and the"
106 "begining and ending Y pixel value of the selection.")
107 #define FUNC_NAME s_select_range_y
109 VALIDATE_ARG_PROC(1, proc);
111 scm_gc_protect_object(proc);
112 wtable->srange->done_proc = proc;
113 wtable->srange->type = SR_Y;
115 set_all_wp_cursors(GDK_TOP_SIDE);
116 wtable->mstate = M_SELRANGE_ARMED;
118 return SCM_UNSPECIFIED;
120 #undef FUNC_NAME
122 SCM_DEFINE(select_range_xy, "select-range-xy", 1, 0, 0,
123 (SCM proc),
124 "Prompt the user to select a region of the "
125 "visible XY plane using button 1 of the mouse. "
126 "When finished, PROC is called with 5 arguments, the"
127 "WavePanel where the range is located, and the"
128 "begining and ending X and Y pixel values of the selection.")
129 #define FUNC_NAME s_select_range_xy
131 VALIDATE_ARG_PROC(1, proc);
133 scm_gc_protect_object(proc);
134 wtable->srange->done_proc = proc;
135 wtable->srange->type = SR_XY;
137 set_all_wp_cursors(GDK_TOP_LEFT_CORNER);
138 wtable->mstate = M_SELRANGE_ARMED;
140 return SCM_UNSPECIFIED;
142 #undef FUNC_NAME
144 /* done selecting range; do the callback */
145 void
146 callback_srange()
148 if(wtable->srange->wp->valid
149 && SCM_NFALSEP(scm_procedure_p(wtable->srange->done_proc))) {
150 wtable->srange->wp->outstanding_smob = 1;
152 switch(wtable->srange->type) {
153 case SR_X:
154 scwm_safe_call3(wtable->srange->done_proc,
155 wtable->srange->wp->smob,
156 scm_long2num(wtable->srange->x1),
157 scm_long2num(wtable->srange->x2));
158 break;
159 case SR_Y:
160 scwm_safe_call3(wtable->srange->done_proc,
161 wtable->srange->wp->smob,
162 scm_long2num(wtable->srange->y1),
163 scm_long2num(wtable->srange->y2));
164 break;
165 case SR_XY:
166 scwm_safe_call5(wtable->srange->done_proc,
167 wtable->srange->wp->smob,
168 scm_long2num(wtable->srange->x1),
169 scm_long2num(wtable->srange->x2),
170 scm_long2num(wtable->srange->y1),
171 scm_long2num(wtable->srange->y2));
172 break;
174 scm_gc_unprotect_object(wtable->srange->done_proc);
178 /* draw or undraw srange line(s), using XOR gc */
179 void
180 draw_srange(SelRange *sr)
182 if(!sr->gc) {
183 gdk_color_alloc(win_colormap, &sr->gdk_color);
184 sr->gc = gdk_gc_new(sr->wp->drawing->window);
185 gdk_gc_set_foreground(sr->gc, &sr->gdk_color);
186 gdk_gc_set_background(sr->gc, &bg_gdk_color);
187 gdk_gc_set_function(sr->gc, GDK_XOR);
189 g_assert(sr->gc != NULL);
190 if(sr->type & SR_X)
191 gdk_draw_line(sr->wp->drawing->window, sr->gc,
192 sr->x1, sr->y1, sr->x2, sr->y1);
193 if(sr->type & SR_Y)
194 gdk_draw_line(sr->wp->drawing->window, sr->gc,
195 sr->x1, sr->y1, sr->x1, sr->y2);
196 if(sr->type == SR_XY) {
197 gdk_draw_line(sr->wp->drawing->window, sr->gc,
198 sr->x1, sr->y2, sr->x2, sr->y2);
199 gdk_draw_line(sr->wp->drawing->window, sr->gc,
200 sr->x2, sr->y1, sr->x2, sr->y2);
204 void
205 update_srange(SelRange *sr, GdkEventMotion *event, int draw)
207 int newx2, newy2;
209 /* the event->y does goofy things if the motion continues
210 * outside the window, so we generate our own from the root
211 * coordinates. */
212 newx2 = event->x;
213 newy2 = sr->y1 + (event->y_root - sr->y1_root);
215 if(sr->drawn) /* undraw old */
216 draw_srange(sr);
217 sr->drawn = draw;
218 if(sr->type & SR_X)
219 sr->x2 = newx2;
220 if(sr->type & SR_Y)
221 sr->y2 = newy2;
222 if(draw) /* draw new if requested */
223 draw_srange(sr);
224 /* printf("update_srange type=%d newx=%d newy=%d draw=%d\n",
225 sr->type, sr->x2, sr->y2, draw);
226 printf("m %d %d %d %d\n",
227 (int)event->x, (int)event->y,
228 (int)event->x_root, (int)event->y_root);
234 * draw (or undraw) a vertical-bar cursor.
236 static void
237 draw_cursor(VBCursor *csp)
239 int h, x, i;
240 WavePanel *wp;
241 for(i = 0; i < wtable->npanels; i++) {
242 wp = wtable->panels[i];
243 h = wp->drawing->allocation.height;
244 if(wp->start_xval <= csp->xval
245 && csp->xval <= wp->end_xval) {
246 x = val2x(wp, csp->xval, wtable->logx);
247 if(wp->drawing->window)
248 gdk_draw_line(wp->drawing->window, csp->gdk_gc,
249 x, 0, x, h);
255 * move cursor at specified location;
256 * turn it on if not on already.
258 void
259 update_cursor(VBCursor *csp, double xval)
261 /* undraw old cursor */
262 if(csp->shown) {
263 draw_cursor(csp);
266 csp->xval = xval;
267 csp->shown = 1;
268 /* draw cursor in each panel */
269 draw_cursor(csp);
271 /* update all measurebuttons, those that show the values of the
272 * cursor, and those that show the value of some WaveVar at a cursor.
274 * TODO: pass in some indication of what changed, since only
275 * one cursor (usually) moves at a time.
277 mbtn_update_all();
280 static void
281 window_update_cursor(WavePanel *wp, VBCursor *csp, int x)
283 double xval;
284 g_assert(csp != NULL);
286 xval = x2val(wp, x, wtable->logx);
287 if(fabs(xval - csp->xval) < DBL_EPSILON && csp->shown)
288 return;
289 if(xval < wp->start_xval || xval > wp->end_xval)
290 return;
291 update_cursor(csp, xval);
295 * button_press in any wave panel.
297 gint
298 button_press_handler(GtkWidget *widget, GdkEventButton *event,
299 gpointer data)
301 WavePanel *wp = (WavePanel *)data;
302 GdkCursor *cursor;
303 SCM scm_event;
305 /* fprintf(stderr, "P%d;mstate=%d\n", event->button, wtable->mstate); */
307 if(wtable->mstate == M_NONE) {
308 if(event->button >= 0 && event->button < N_MOUSE_BUTTONS
309 && wavepanel_mouse_binding[event->button]) {
311 if(event == NULL)
312 scm_event = SCM_BOOL_F;
313 else
314 scm_event = scm_c_gvalue_new_from_boxed(GDK_TYPE_EVENT, event);
315 scwm_safe_call2(
316 wavepanel_mouse_binding[event->button],
317 wp->smob,
318 scm_event);
322 switch(event->button) {
323 case 1:
324 case 2:
325 switch(wtable->mstate) {
326 case M_NONE:
327 gtk_grab_add(widget);
328 wtable->mstate = M_CURSOR_DRAG;
329 wtable->button_down = event->button;
331 cursor = gdk_cursor_new(GDK_SB_H_DOUBLE_ARROW);
332 gdk_window_set_cursor(widget->window, cursor);
333 gdk_cursor_destroy(cursor);
334 wtable->drag_cursor = wtable->cursor[event->button-1];
335 window_update_cursor(wp, wtable->drag_cursor, event->x);
336 break;
337 case M_SELRANGE_ARMED:
338 gtk_grab_add(widget);
339 wtable->button_down = event->button;
340 wtable->mstate = M_SELRANGE_ACTIVE;
342 wtable->srange->y1 = wtable->srange->y2 = event->y;
343 wtable->srange->x1 = wtable->srange->x2 = event->x;
344 wtable->srange->x1_root = event->x_root;
345 wtable->srange->y1_root = event->y_root;
346 wtable->srange->wp = wp;
347 break;
348 /* can't start another drag until first one done */
349 case M_CURSOR_DRAG:
350 case M_SELRANGE_ACTIVE:
351 break;
352 default:
353 break;
356 break;
357 case 3:
358 default:
359 break;
361 return 0;
365 * button_release in any wave panel.
367 gint
368 button_release_handler(GtkWidget *widget, GdkEventButton *event,
369 gpointer data)
371 WavePanel *wp = (WavePanel *)data;
373 if(wtable->button_down != event->button)
374 return 0;
376 switch(wtable->mstate) {
377 case M_CURSOR_DRAG:
378 gtk_grab_remove(widget);
379 gdk_window_set_cursor(widget->window, NULL);
380 window_update_cursor(wp, wtable->drag_cursor, event->x);
381 wtable->drag_cursor = NULL;
382 break;
383 case M_SELRANGE_ACTIVE:
384 gtk_grab_remove(widget);
385 set_all_wp_cursors(-1);
386 update_srange(wtable->srange, (GdkEventMotion *)event, 0);
387 callback_srange();
388 break;
389 default:
390 break;
392 wtable->mstate = M_NONE;
393 wtable->button_down = -1;
394 /* fprintf(stderr, "R%d;mstate=%d\n", event->button, wtable->mstate); */
395 return 0;
399 * GDK_MOTION_NOTIFY in any WavePanel's drawing area
401 gint
402 motion_handler(GtkWidget *widget, GdkEventMotion *event,
403 gpointer data)
405 WavePanel *wp = (WavePanel *)data;
406 VBCursor *csp;
408 switch(wtable->mstate) {
409 case M_CURSOR_DRAG:
410 csp = wtable->drag_cursor;
411 window_update_cursor(wp, csp, event->x);
412 break;
413 case M_SELRANGE_ACTIVE:
414 /* fputc('r', stderr); */
415 update_srange(wtable->srange, event, 1);
416 break;
417 default:
418 /* a sort of debugging output if we get in a bad state */
419 fputc('.', stderr);
420 break;
422 return 0;
426 gint scroll_handler(GtkWidget *widget)
428 GtkAdjustment *hsadj = GTK_ADJUSTMENT(widget);
429 int i;
430 WavePanel *wp;
432 if (!wtable->logx) {
433 wtable->start_xval = hsadj->value
434 * ( wtable->max_xval - wtable->min_xval ) + wtable->min_xval;
435 wtable->end_xval = ( hsadj->value + hsadj->page_size )
436 * ( wtable->max_xval - wtable->min_xval ) + wtable->min_xval;
437 } else {
438 wtable->start_xval = wtable->min_xval * pow( wtable->max_xval / wtable->min_xval, hsadj->value );
439 wtable->end_xval = wtable->min_xval * pow( wtable->max_xval / wtable->min_xval,
440 hsadj->value + hsadj->page_size );
443 draw_labels(wtable);
445 for(i = 0; i < wtable->npanels; i++) {
446 wp = wtable->panels[i];
447 wp->start_xval = wtable->start_xval;
448 wp->end_xval = wtable->end_xval;
450 if(wtable->suppress_redraw == 0) {
451 wtable_redraw_x();
453 return 0;
457 /* Get the foreground color for the waveform and set up its GC
458 * by using the GdkColor of the corresponding label.
460 void vw_wp_setup_gc(VisibleWave *vw, WavePanel *wp)
462 if(!vw->gc) {
463 gdk_color_alloc(win_colormap, &vw->label->style->fg[GTK_STATE_NORMAL]);
464 vw->gc = gdk_gc_new(wp->drawing->window);
465 gdk_gc_set_foreground(vw->gc, &vw->label->style->fg[GTK_STATE_NORMAL]);
470 * expose_handler - first time around, do last-minute setup.
471 * otherwise, arranges to get waveform panel drawing areas redrawn.
472 * Redraw stuff needs an overhaul to make more efficient.
474 gint expose_handler(GtkWidget *widget, GdkEventExpose *event,
475 WavePanel *wp)
477 int w = widget->allocation.width;
478 int h = widget->allocation.height;
480 if(!colors_initialized) {
481 alloc_colors(widget);
482 colors_initialized = 1;
485 /* Make sure we've got GCs for each visible wave. */
486 /* g_list_foreach(wp->vwlist, (GFunc)vw_wp_setup_gc, wp); */
488 if ( wp->pixmap && (wp->width != w || wp->height != h)) {
489 gdk_pixmap_unref(wp->pixmap);
490 wp->width = w;
491 wp->height = h;
492 wp->pixmap = NULL;
495 if(!wp->pixmap)
496 wp->pixmap = gdk_pixmap_new(widget->window, w, h, -1);
498 if(wtable->suppress_redraw == 0)
499 draw_wavepanel(wp->drawing, event, wp);
501 return 0;
504 /* guile initialization */
506 void init_event()
509 #ifndef SCM_MAGIC_SNARF_INITS
510 #include "event.x"
511 #endif