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