]> git.cworth.org Git - grrobot/blob - src/grr_board_view.c
Initial revision
[grrobot] / src / grr_board_view.c
1 /* grr_board_view - GTK+ widget for displaying an rr_board
2  *
3  * Copyright © 2003 Carl Worth
4  *
5  * Permission to use, copy, modify, distribute, and sell this software
6  * and its documentation for any purpose is hereby granted without
7  * fee, provided that the above copyright notice appear in all copies
8  * and that both that copyright notice and this permission notice
9  * appear in supporting documentation, and that the name of Carl Worth
10  * not be used in advertising or publicity pertaining to distribution
11  * of the software without specific, written prior permission.
12  * Carl Worth makes no representations about the suitability of this
13  * software for any purpose.  It is provided "as is" without express
14  * or implied warranty.
15  * 
16  * CARL WORTH DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
18  * NO EVENT SHALL CARL WORTH BE LIABLE FOR ANY SPECIAL, INDIRECT OR
19  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
20  * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
21  * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
22  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23  *
24  * Author: Carl Worth <carl@theworths.org>
25  */
26
27 #include <Xr.h>
28 #include <xsvg.h>
29
30 #include <math.h>
31 #include <stdio.h>
32 #include <gtk/gtkmain.h>
33 #include <gtk/gtksignal.h>
34 #include <gdk/gdkx.h>
35
36 #include "grr_board_view.h"
37
38 #define SCROLL_DELAY_LENGTH  300
39 #define GRR_BOARD_VIEW_DEFAULT_SIZE 100
40
41 #define GRR_SVG_ASSUMED_WIDTH 32.0
42 #define GRR_SVG_ASSUMED_HEIGHT 32.0
43
44 #define GRR_CELL_SVG "svg/cell.svg"
45 #define GRR_WALL_SVG "svg/wall.svg"
46
47 #define GRR_ROBOT_BLUE_SVG "svg/robot_blue.svg"
48 #define GRR_ROBOT_GREEN_SVG "svg/robot_green.svg"
49 #define GRR_ROBOT_RED_SVG "svg/robot_red.svg"
50 #define GRR_ROBOT_YELLOW_SVG "svg/robot_yellow.svg"
51
52 #define GRR_TARGET_BLUE_CIRCLE_SVG "svg/target_blue_circle.svg"
53 #define GRR_TARGET_BLUE_OCTAGON_SVG "svg/target_blue_octagon.svg"
54 #define GRR_TARGET_BLUE_SQUARE_SVG "svg/target_blue_square.svg"
55 #define GRR_TARGET_BLUE_TRIANGLE_SVG "svg/target_blue_triangle.svg"
56
57 #define GRR_TARGET_GREEN_CIRCLE_SVG "svg/target_green_circle.svg"
58 #define GRR_TARGET_GREEN_OCTAGON_SVG "svg/target_green_octagon.svg"
59 #define GRR_TARGET_GREEN_SQUARE_SVG "svg/target_green_square.svg"
60 #define GRR_TARGET_GREEN_TRIANGLE_SVG "svg/target_green_triangle.svg"
61
62 #define GRR_TARGET_RED_CIRCLE_SVG "svg/target_red_circle.svg"
63 #define GRR_TARGET_RED_OCTAGON_SVG "svg/target_red_octagon.svg"
64 #define GRR_TARGET_RED_SQUARE_SVG "svg/target_red_square.svg"
65 #define GRR_TARGET_RED_TRIANGLE_SVG "svg/target_red_triangle.svg"
66
67 #define GRR_TARGET_YELLOW_CIRCLE_SVG "svg/target_yellow_circle.svg"
68 #define GRR_TARGET_YELLOW_OCTAGON_SVG "svg/target_yellow_octagon.svg"
69 #define GRR_TARGET_YELLOW_SQUARE_SVG "svg/target_yellow_square.svg"
70 #define GRR_TARGET_YELLOW_TRIANGLE_SVG "svg/target_yellow_triangle.svg"
71
72 #define GRR_TARGET_WHIRL_SVG "svg/target_whirl.svg"
73
74
75 /* Forward declarations */
76
77 static void grr_board_view_class_init               (grr_board_view_class_t    *klass);
78 static void grr_board_view_init                     (grr_board_view_t         *view);
79 static void grr_board_view_destroy                  (GtkObject        *object);
80 static void grr_board_view_realize                  (GtkWidget        *widget);
81 static void grr_board_view_size_request             (GtkWidget      *widget,
82                                                GtkRequisition *requisition);
83 static void grr_board_view_size_allocate            (GtkWidget     *widget,
84                                                GtkAllocation *allocation);
85 static gint grr_board_view_expose                   (GtkWidget        *widget,
86                                                 GdkEventExpose   *event);
87 static gint grr_board_view_button_press             (GtkWidget        *widget,
88                                                 GdkEventButton   *event);
89 static gint grr_board_view_button_release           (GtkWidget        *widget,
90                                                 GdkEventButton   *event);
91 static gint grr_board_view_motion_notify            (GtkWidget        *widget,
92                                                 GdkEventMotion   *event);
93 static gint grr_board_view_timer                    (grr_board_view_t         *view);
94
95 static void grr_board_view_update_mouse             (grr_board_view_t *view, gint x, gint y);
96 static void grr_board_view_update                   (grr_board_view_t *view);
97
98 /* Local data */
99
100 static GtkWidgetClass *parent_class = NULL;
101
102 GType
103 grr_board_view_get_type ()
104 {
105   static GType view_type = 0;
106
107   if (!view_type)
108     {
109       static const GTypeInfo view_info =
110       {
111         sizeof (grr_board_view_class_t),
112         NULL,
113         NULL,
114         (GClassInitFunc) grr_board_view_class_init,
115         NULL,
116         NULL,
117         sizeof (grr_board_view_t),
118         0,
119         (GInstanceInitFunc) grr_board_view_init,
120       };
121
122       view_type = g_type_register_static (GTK_TYPE_WIDGET, "grr_board_view", &view_info, 0);
123     }
124
125   return view_type;
126 }
127
128 static void
129 grr_board_view_class_init (grr_board_view_class_t *class)
130 {
131   GtkObjectClass *object_class;
132   GtkWidgetClass *widget_class;
133
134   object_class = (GtkObjectClass*) class;
135   widget_class = (GtkWidgetClass*) class;
136
137   parent_class = gtk_type_class (gtk_widget_get_type ());
138
139   object_class->destroy = grr_board_view_destroy;
140
141   widget_class->realize = grr_board_view_realize;
142   widget_class->expose_event = grr_board_view_expose;
143   widget_class->size_request = grr_board_view_size_request;
144   widget_class->size_allocate = grr_board_view_size_allocate;
145   widget_class->button_press_event = grr_board_view_button_press;
146   widget_class->button_release_event = grr_board_view_button_release;
147   widget_class->motion_notify_event = grr_board_view_motion_notify;
148 }
149
150 static void
151 grr_board_view_init (grr_board_view_t *view)
152 {
153     view->board = NULL;
154     view->owns_board = 0;
155     
156     view->button = 0;
157     view->policy = GTK_UPDATE_CONTINUOUS;
158     view->timer = 0;
159     view->radius = 0;
160     view->pointer_width = 0;
161     view->angle = 0.0;
162     view->old_value = 0.0;
163     view->old_lower = 0.0;
164     view->old_upper = 0.0;
165 }
166
167 GtkWidget*
168 grr_board_view_new (rr_board_t *board)
169 {
170     grr_board_view_t *view;
171     
172     view = g_object_new (grr_board_view_get_type (), NULL);
173
174     if (board == NULL) {
175         board = rr_board_create (16, 16);
176         view->owns_board = 1;
177     }
178
179     grr_board_view_set_board (view, board);
180
181     return GTK_WIDGET (view);
182 }
183
184 static void
185 grr_board_view_destroy (GtkObject *object)
186 {
187     grr_board_view_t *view;
188
189     g_return_if_fail (object != NULL);
190     g_return_if_fail (GRR_IS_BOARD_VIEW (object));
191
192     view = GRR_BOARD_VIEW (object);
193
194     rr_board_destroy (view->board);
195
196     if (view->board && view->owns_board) {
197         rr_board_destroy (view->board);
198         view->board = NULL;
199     }
200     
201     if (GTK_OBJECT_CLASS (parent_class)->destroy)
202         (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
203 }
204
205 void
206 grr_board_view_set_board (grr_board_view_t      *view,
207                           rr_board_t *board)
208 {
209     g_return_if_fail (view != NULL);
210     g_return_if_fail (GRR_IS_BOARD_VIEW (view));
211
212     view->board = board;
213
214     grr_board_view_update (view);
215 }
216
217 static void
218 grr_board_view_realize (GtkWidget *widget)
219 {
220   grr_board_view_t *view;
221   GdkWindowAttr attributes;
222   gint attributes_mask;
223
224   g_return_if_fail (widget != NULL);
225   g_return_if_fail (GRR_IS_BOARD_VIEW (widget));
226
227   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
228   view = GRR_BOARD_VIEW (widget);
229
230   attributes.x = widget->allocation.x;
231   attributes.y = widget->allocation.y;
232   attributes.width = widget->allocation.width;
233   attributes.height = widget->allocation.height;
234   attributes.wclass = GDK_INPUT_OUTPUT;
235   attributes.window_type = GDK_WINDOW_CHILD;
236   attributes.event_mask = gtk_widget_get_events (widget) | 
237     GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | 
238     GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK |
239     GDK_POINTER_MOTION_HINT_MASK;
240   attributes.visual = gtk_widget_get_visual (widget);
241   attributes.colormap = gtk_widget_get_colormap (widget);
242
243   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
244   widget->window = gdk_window_new (widget->parent->window, &attributes, attributes_mask);
245
246   widget->style = gtk_style_attach (widget->style, widget->window);
247
248   gdk_window_set_user_data (widget->window, widget);
249
250   gtk_style_set_background (widget->style, widget->window, GTK_STATE_ACTIVE);
251 }
252
253 static void 
254 grr_board_view_size_request (GtkWidget      *widget,
255                        GtkRequisition *requisition)
256 {
257   requisition->width = GRR_BOARD_VIEW_DEFAULT_SIZE;
258   requisition->height = GRR_BOARD_VIEW_DEFAULT_SIZE;
259 }
260
261 static void
262 grr_board_view_size_allocate (GtkWidget     *widget,
263                         GtkAllocation *allocation)
264 {
265   grr_board_view_t *view;
266
267   g_return_if_fail (widget != NULL);
268   g_return_if_fail (GRR_IS_BOARD_VIEW (widget));
269   g_return_if_fail (allocation != NULL);
270
271   widget->allocation = *allocation;
272   view = GRR_BOARD_VIEW (widget);
273
274   if (GTK_WIDGET_REALIZED (widget))
275     {
276
277       gdk_window_move_resize (widget->window,
278                               allocation->x, allocation->y,
279                               allocation->width, allocation->height);
280
281     }
282   view->radius = MIN (allocation->width, allocation->height) * 0.45;
283   view->pointer_width = view->radius / 5;
284 }
285
286 static void
287 grr_svg_draw (XrState *xrs, char *svg)
288 {
289     xsvg_status_t status;
290     xsvg_t *xsvg;
291
292     status = xsvg_create (&xsvg);
293     if (status) {
294         fprintf (stderr, "xsvg_create error\n");
295         return;
296     }
297     status = xsvg_parse_file (xsvg, svg);
298     if (status) {
299         fprintf (stderr, "xsvg_parse_file error parsing %s\n", svg);
300         return;
301     }
302     xsvg_render (xsvg, xrs);
303     if (status) {
304         fprintf (stderr, "xsvg_render error\n");
305         return;
306     }
307     xsvg_destroy (xsvg);
308 }
309
310 static void
311 grr_wall_draw (XrState *xrs, rr_wall_t wall)
312 {
313     char *wall_svg = GRR_WALL_SVG;
314
315     if (wall == RR_WALL_NONE)
316         return;
317
318     if (wall & RR_WALL_ABOVE) {
319         grr_svg_draw (xrs, wall_svg);
320     }
321     if (wall & RR_WALL_LEFT) {
322         XrSave (xrs);
323         XrRotate (xrs, M_PI_2);
324         grr_svg_draw (xrs, wall_svg);
325         XrRestore (xrs);
326     }
327     if (wall & RR_WALL_RIGHT) {
328         XrSave (xrs);
329         XrTranslate (xrs, GRR_SVG_ASSUMED_WIDTH, 0);
330         XrRotate (xrs, M_PI_2);
331         grr_svg_draw (xrs, wall_svg);
332         XrRestore (xrs);
333     }
334     if (wall & RR_WALL_BELOW) {
335         XrSave (xrs);
336         XrTranslate (xrs, 0, GRR_SVG_ASSUMED_HEIGHT);
337         grr_svg_draw (xrs, wall_svg);
338         XrRestore (xrs);
339     }
340 }
341
342 static void
343 grr_target_draw (XrState *xrs, rr_target_t target)
344 {
345     char *target_svg;
346
347     if (target == RR_TARGET_NONE)
348         return;
349
350     switch (target) {
351     case RR_TARGET_BLUE_CIRCLE:
352         target_svg = GRR_TARGET_BLUE_CIRCLE_SVG;
353         break;
354     case RR_TARGET_BLUE_OCTAGON:
355         target_svg = GRR_TARGET_BLUE_OCTAGON_SVG;
356         break;
357     case RR_TARGET_BLUE_SQUARE:
358         target_svg = GRR_TARGET_BLUE_SQUARE_SVG;
359         break;
360     case RR_TARGET_BLUE_TRIANGLE:
361         target_svg = GRR_TARGET_BLUE_TRIANGLE_SVG;
362         break;
363     case RR_TARGET_GREEN_CIRCLE:
364         target_svg = GRR_TARGET_GREEN_CIRCLE_SVG;
365         break;
366     case RR_TARGET_GREEN_OCTAGON:
367         target_svg = GRR_TARGET_GREEN_OCTAGON_SVG;
368         break;
369     case RR_TARGET_GREEN_SQUARE:
370         target_svg = GRR_TARGET_GREEN_SQUARE_SVG;
371         break;
372     case RR_TARGET_GREEN_TRIANGLE:
373         target_svg = GRR_TARGET_GREEN_TRIANGLE_SVG;
374         break;
375     case RR_TARGET_RED_CIRCLE:
376         target_svg = GRR_TARGET_RED_CIRCLE_SVG;
377         break;
378     case RR_TARGET_RED_OCTAGON:
379         target_svg = GRR_TARGET_RED_OCTAGON_SVG;
380         break;
381     case RR_TARGET_RED_SQUARE:
382         target_svg = GRR_TARGET_RED_SQUARE_SVG;
383         break;
384     case RR_TARGET_RED_TRIANGLE:
385         target_svg = GRR_TARGET_RED_TRIANGLE_SVG;
386         break;
387     case RR_TARGET_YELLOW_CIRCLE:
388         target_svg = GRR_TARGET_YELLOW_CIRCLE_SVG;
389         break;
390     case RR_TARGET_YELLOW_OCTAGON:
391         target_svg = GRR_TARGET_YELLOW_OCTAGON_SVG;
392         break;
393     case RR_TARGET_YELLOW_SQUARE:
394         target_svg = GRR_TARGET_YELLOW_SQUARE_SVG;
395         break;
396     case RR_TARGET_YELLOW_TRIANGLE:
397         target_svg = GRR_TARGET_YELLOW_TRIANGLE_SVG;
398         break;
399     case RR_TARGET_WHIRL:
400         target_svg = GRR_TARGET_WHIRL_SVG;
401         break;
402     default:
403         return;
404     }
405
406     grr_svg_draw (xrs, target_svg);
407 }
408
409 static void
410 grr_robot_draw (XrState *xrs, rr_robot_t robot)
411 {
412     char *robot_svg;
413
414     if (robot == RR_ROBOT_NONE)
415         return;
416
417     switch (robot) {
418     case RR_ROBOT_BLUE:
419         robot_svg = GRR_ROBOT_BLUE_SVG;
420         break;
421     case RR_ROBOT_GREEN:
422         robot_svg = GRR_ROBOT_GREEN_SVG;
423         break;
424     case RR_ROBOT_RED:
425         robot_svg = GRR_ROBOT_RED_SVG;
426         break;
427     case RR_ROBOT_YELLOW:
428         robot_svg = GRR_ROBOT_YELLOW_SVG;
429         break;
430     default:
431         return;
432     }
433
434     grr_svg_draw (xrs, robot_svg);
435 }
436
437 static void
438 grr_cell_draw (XrState *xrs, rr_cell_t cell, rr_target_t goal,
439                int width, int height)
440 {
441     int xpad, ypad;
442     rr_target_t target;
443
444     XrSave (xrs);
445     XrScale (xrs,
446              width / GRR_SVG_ASSUMED_WIDTH,
447              height / GRR_SVG_ASSUMED_HEIGHT);
448     grr_svg_draw (xrs, GRR_CELL_SVG);
449     grr_wall_draw (xrs, RR_CELL_GET_WALLS (cell));
450     XrRestore (xrs);
451
452     xpad = width / 5;
453     ypad = width / 5;
454
455     XrSave (xrs);
456     XrTranslate (xrs, xpad, ypad);
457     XrScale (xrs,
458              (width - 2*xpad)  / GRR_SVG_ASSUMED_WIDTH,
459              (height - 2*ypad) / GRR_SVG_ASSUMED_HEIGHT);
460
461     target = RR_CELL_GET_TARGET (cell);
462     grr_target_draw (xrs, target);
463     XrRestore (xrs);
464
465     /* This is a hack, (it obscures the cell background in addition to
466        the target). The real way to do this is to draw the target
467        itself with less opacity. */
468     if (target && target != goal) {
469         XrSave (xrs);
470         XrScale (xrs,
471                  width / GRR_SVG_ASSUMED_WIDTH,
472                  height / GRR_SVG_ASSUMED_HEIGHT);
473         XrRectangle (xrs, 0, 0,
474                      GRR_SVG_ASSUMED_WIDTH,
475                      GRR_SVG_ASSUMED_HEIGHT);
476         XrSetRGBColor (xrs, 1, 1, 1);
477         XrSetAlpha (xrs, 0.75);
478         XrFill (xrs);
479         XrRestore (xrs);
480     }
481
482     XrSave (xrs);
483     XrTranslate (xrs, xpad, ypad);
484     XrScale (xrs,
485              (width - 2*xpad)  / GRR_SVG_ASSUMED_WIDTH,
486              (height - 2*ypad) / GRR_SVG_ASSUMED_HEIGHT);
487
488     grr_robot_draw (xrs, RR_CELL_GET_ROBOT (cell));
489
490     XrRestore (xrs);
491
492 }
493
494 static gint
495 grr_board_view_expose (GtkWidget      *widget,
496                        GdkEventExpose *event)
497 {
498     grr_board_view_t *view;
499     Display *dpy;
500     Drawable drawable;
501     XrState *xrs;
502     GdkDrawable *real_drawable;
503     gint x_off, y_off;
504     int i, j;
505     rr_target_t goal_target;
506
507     g_return_val_if_fail (widget != NULL, FALSE);
508     g_return_val_if_fail (GRR_IS_BOARD_VIEW (widget), FALSE);
509     g_return_val_if_fail (event != NULL, FALSE);
510
511     if (event->count > 0)
512         return FALSE;
513
514     view = GRR_BOARD_VIEW (widget);
515
516     /* Unabstract X from GTK+ */
517     gdk_window_get_internal_paint_info (widget->window, &real_drawable, &x_off, &y_off);
518     dpy = gdk_x11_drawable_get_xdisplay (real_drawable);
519     drawable = gdk_x11_drawable_get_xid (real_drawable);
520
521     /* Ignore GTK+ and use Xr for drawing. */
522     xrs = XrCreate ();
523     XrSetTargetDrawable (xrs, dpy, drawable);
524     XrTranslate (xrs, -x_off, -y_off);
525
526     rr_board_get_size (view->board, &view->board_width, &view->board_height);
527
528     view->cell_width = widget->allocation.width / view->board_width;
529     if (view->cell_width == 0)
530         view->cell_width = 1;
531     view->cell_height = widget->allocation.height / view->board_height;
532     if (view->cell_height == 0)
533         view->cell_height = 1;
534
535     view->board_pad_x = (widget->allocation.width - view->board_width * view->cell_width) / 2;
536     view->board_pad_y = (widget->allocation.height - view->board_height * view->cell_height) / 2;
537
538     XrTranslate (xrs, view->board_pad_x, view->board_pad_y);
539
540     goal_target = rr_board_get_goal_target (view->board);
541
542     /* Draw cell targets */
543     for (j=0; j < view->board_height; j++) {
544         for (i=0; i < view->board_width; i++) {
545             XrSave (xrs);
546             XrTranslate (xrs, i * view->cell_width, j * view->cell_height);
547             grr_cell_draw (xrs,
548                            rr_board_get_cell (view->board, i, j),
549                            goal_target,
550                            view->cell_width, view->cell_height);
551             XrRestore (xrs);
552         }
553     }
554
555     /* Draw grid. */
556     XrSave (xrs);
557     XrSetRGBColor (xrs, .75, .75, 1);
558     XrSetLineWidth (xrs, 1);
559     for (j=0; j < view->board_height; j++) {
560         XrMoveTo (xrs, 0, j * view->cell_height + 0.5);
561         XrRelLineTo (xrs, view->board_width * view->cell_width, 0);
562     }
563     for (i=0; i < view->board_width; i++) {
564         XrMoveTo (xrs, i * view->cell_width + 0.5, 0);
565         XrRelLineTo (xrs, 0, view->board_height * view->cell_height);
566     }
567     XrStroke (xrs);
568     XrRestore (xrs);
569
570     /* Draw goal target in center of board */
571     XrSave (xrs);
572     XrTranslate (xrs,
573                  (view->board_width / 2 - 1) * view->cell_width,
574                  (view->board_height / 2 - 1) * view->cell_height);
575     grr_cell_draw (xrs, goal_target, goal_target,
576                    view->cell_width * 2, view->cell_height * 2);
577     XrRestore (xrs);
578
579     /* Draw walls */
580     for (j=0; j < view->board_height; j++) {
581         for (i=0; i < view->board_width; i++) {
582             XrSave (xrs);
583             XrTranslate (xrs, i * view->cell_width, j * view->cell_height);
584             XrScale (xrs,
585                      view->cell_width / GRR_SVG_ASSUMED_WIDTH,
586                      view->cell_height / GRR_SVG_ASSUMED_HEIGHT);
587             grr_wall_draw (xrs, RR_CELL_GET_WALLS (rr_board_get_cell(view->board, i, j)));
588             XrRestore (xrs);
589         }
590     }
591     
592     XrDestroy (xrs);
593
594     return FALSE;
595 }
596
597 static gint
598 grr_board_view_button_press (GtkWidget      *widget,
599                        GdkEventButton *event)
600 {
601   grr_board_view_t *view;
602
603   g_return_val_if_fail (widget != NULL, FALSE);
604   g_return_val_if_fail (GRR_IS_BOARD_VIEW (widget), FALSE);
605   g_return_val_if_fail (event != NULL, FALSE);
606
607   view = GRR_BOARD_VIEW (widget);
608
609   /* XXX:   grr_board_view_pointer_coords_to_grid (view, event->x, event->y, &i, &j);
610   
611   if (!view->button &&
612       rr_board_has_robot (view->board, i, j))
613     {
614       gtk_grab_add (widget);
615
616       view->button = event->button;
617
618       grr_board_view_update_mouse (view, event->x, event->y);
619     }
620   */
621
622   return FALSE;
623 }
624
625 static gint
626 grr_board_view_button_release (GtkWidget      *widget,
627                           GdkEventButton *event)
628 {
629   grr_board_view_t *view;
630
631   g_return_val_if_fail (widget != NULL, FALSE);
632   g_return_val_if_fail (GRR_IS_BOARD_VIEW (widget), FALSE);
633   g_return_val_if_fail (event != NULL, FALSE);
634
635   view = GRR_BOARD_VIEW (widget);
636
637   if (view->button == event->button)
638     {
639       gtk_grab_remove (widget);
640
641       view->button = 0;
642
643       /* XXX:      grr_board_view_pointer_coords_to_grid (view, event->x, event->y, &i, &j); */
644
645       /* XXX: Need to compute direction based on (i, j) and old (i, j) */
646       /*XXX:       rr_client_move (view->client, view->robot, direction); */
647
648     }
649
650   return FALSE;
651 }
652
653 static gint
654 grr_board_view_motion_notify (GtkWidget      *widget,
655                          GdkEventMotion *event)
656 {
657   grr_board_view_t *view;
658   GdkModifierType mods;
659   gint x, y, mask;
660
661   g_return_val_if_fail (widget != NULL, FALSE);
662   g_return_val_if_fail (GRR_IS_BOARD_VIEW (widget), FALSE);
663   g_return_val_if_fail (event != NULL, FALSE);
664
665   view = GRR_BOARD_VIEW (widget);
666
667   if (view->button != 0)
668     {
669       x = event->x;
670       y = event->y;
671
672       if (event->is_hint || (event->window != widget->window))
673         gdk_window_get_pointer (widget->window, &x, &y, &mods);
674
675       switch (view->button)
676         {
677         case 1:
678           mask = GDK_BUTTON1_MASK;
679           break;
680         case 2:
681           mask = GDK_BUTTON2_MASK;
682           break;
683         case 3:
684           mask = GDK_BUTTON3_MASK;
685           break;
686         default:
687           mask = 0;
688           break;
689         }
690
691       if (mods & mask)
692         grr_board_view_update_mouse (view, x,y);
693     }
694
695   return FALSE;
696 }
697
698 static gint
699 grr_board_view_timer (grr_board_view_t *view)
700 {
701     g_return_val_if_fail (view != NULL, FALSE);
702     g_return_val_if_fail (GRR_IS_BOARD_VIEW (view), FALSE);
703
704     return FALSE;
705 }
706
707 static void
708 grr_board_view_update_mouse (grr_board_view_t *view, gint x, gint y)
709 {
710     gint xc, yc;
711     gfloat old_value;
712
713     g_return_if_fail (view != NULL);
714     g_return_if_fail (GRR_IS_BOARD_VIEW (view));
715
716     xc = GTK_WIDGET(view)->allocation.width / 2;
717     yc = GTK_WIDGET(view)->allocation.height / 2;
718
719     old_value = view->adjustment->value;
720     view->angle = atan2(yc-y, x-xc);
721
722     if (view->angle < -G_PI/2.)
723         view->angle += 2*G_PI;
724     
725     if (view->angle < -G_PI/6)
726         view->angle = -G_PI/6;
727
728     if (view->angle > 7.*G_PI/6.)
729         view->angle = 7.*G_PI/6.;
730     
731     view->adjustment->value = view->adjustment->lower + (7.*G_PI/6 - view->angle) *
732         (view->adjustment->upper - view->adjustment->lower) / (4.*G_PI/3.);
733     
734     if (view->adjustment->value != old_value)
735         {
736             if (view->policy == GTK_UPDATE_CONTINUOUS)
737                 {
738                     g_signal_emit_by_name (GTK_OBJECT (view->adjustment), "value_changed");
739                 }
740       else
741           {
742               gtk_widget_queue_draw (GTK_WIDGET (view));
743               
744               if (view->policy == GTK_UPDATE_DELAYED)
745                   {
746                       if (view->timer)
747                           gtk_timeout_remove (view->timer);
748                       
749                       view->timer = gtk_timeout_add (SCROLL_DELAY_LENGTH,
750                                                      (GtkFunction) grr_board_view_timer,
751                                                      (gpointer) view);
752                   }
753           }
754         }
755 }
756
757 static void
758 grr_board_view_update (grr_board_view_t *view)
759 {
760   g_return_if_fail (view != NULL);
761   g_return_if_fail (GRR_IS_BOARD_VIEW (view));
762
763   gtk_widget_queue_draw (GTK_WIDGET (view));
764 }