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.
39 #include <scwm_guile.h>
42 #include <measurebtn.h>
43 #include <guile-gnome-gobject/gobject.h>
45 void destroy_handler(GtkWidget
*widget
, gpointer data
)
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.
55 set_all_wp_cursors(int cnum
)
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
);
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
78 SCM_DEFINE(select_range_x
, "select-range-x", 1, 0, 0,
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
;
100 SCM_DEFINE(select_range_y
, "select-range-y", 1, 0, 0,
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
;
122 SCM_DEFINE(select_range_xy
, "select-range-xy", 1, 0, 0,
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
;
144 /* done selecting range; do the callback */
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
) {
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
));
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
));
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
));
174 scm_gc_unprotect_object(wtable
->srange
->done_proc
);
178 /* draw or undraw srange line(s), using XOR gc */
180 draw_srange(SelRange
*sr
)
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
);
191 gdk_draw_line(sr
->wp
->drawing
->window
, sr
->gc
,
192 sr
->x1
, sr
->y1
, sr
->x2
, sr
->y1
);
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
);
205 update_srange(SelRange
*sr
, GdkEventMotion
*event
, int draw
)
209 /* the event->y does goofy things if the motion continues
210 * outside the window, so we generate our own from the root
213 newy2
= sr
->y1
+ (event
->y_root
- sr
->y1_root
);
215 if(sr
->drawn
) /* undraw old */
222 if(draw
) /* draw new if requested */
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.
237 draw_cursor(VBCursor
*csp
)
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
,
255 * move cursor at specified location;
256 * turn it on if not on already.
259 update_cursor(VBCursor
*csp
, double xval
)
261 /* undraw old cursor */
268 /* draw cursor in each panel */
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.
281 window_update_cursor(WavePanel
*wp
, VBCursor
*csp
, int x
)
284 g_assert(csp
!= NULL
);
286 xval
= x2val(wp
, x
, wtable
->logx
);
287 if(fabs(xval
- csp
->xval
) < DBL_EPSILON
&& csp
->shown
)
289 if(xval
< wp
->start_xval
|| xval
> wp
->end_xval
)
291 update_cursor(csp
, xval
);
295 * button_press in any wave panel.
298 button_press_handler(GtkWidget
*widget
, GdkEventButton
*event
,
301 WavePanel
*wp
= (WavePanel
*)data
;
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
]) {
312 scm_event
= SCM_BOOL_F
;
314 scm_event
= scm_c_gvalue_new_from_boxed(GDK_TYPE_EVENT
, event
);
316 wavepanel_mouse_binding
[event
->button
],
322 switch(event
->button
) {
325 switch(wtable
->mstate
) {
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
);
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
;
348 /* can't start another drag until first one done */
350 case M_SELRANGE_ACTIVE
:
365 * button_release in any wave panel.
368 button_release_handler(GtkWidget
*widget
, GdkEventButton
*event
,
371 WavePanel
*wp
= (WavePanel
*)data
;
373 if(wtable
->button_down
!= event
->button
)
376 switch(wtable
->mstate
) {
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
;
383 case M_SELRANGE_ACTIVE
:
384 gtk_grab_remove(widget
);
385 set_all_wp_cursors(-1);
386 update_srange(wtable
->srange
, (GdkEventMotion
*)event
, 0);
392 wtable
->mstate
= M_NONE
;
393 wtable
->button_down
= -1;
394 /* fprintf(stderr, "R%d;mstate=%d\n", event->button, wtable->mstate); */
399 * GDK_MOTION_NOTIFY in any WavePanel's drawing area
402 motion_handler(GtkWidget
*widget
, GdkEventMotion
*event
,
405 WavePanel
*wp
= (WavePanel
*)data
;
408 switch(wtable
->mstate
) {
410 csp
= wtable
->drag_cursor
;
411 window_update_cursor(wp
, csp
, event
->x
);
413 case M_SELRANGE_ACTIVE
:
414 /* fputc('r', stderr); */
415 update_srange(wtable
->srange
, event
, 1);
418 /* a sort of debugging output if we get in a bad state */
426 gint
scroll_handler(GtkWidget
*widget
)
428 GtkAdjustment
*hsadj
= GTK_ADJUSTMENT(widget
);
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
;
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
);
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) {
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
)
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
,
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
);
496 wp
->pixmap
= gdk_pixmap_new(widget
->window
, w
, h
, -1);
498 if(wtable
->suppress_redraw
== 0)
499 draw_wavepanel(wp
->drawing
, event
, wp
);
504 /* guile initialization */
509 #ifndef SCM_MAGIC_SNARF_INITS