1 /* grr_board_view - GTK+ widget for displaying an rr_board
3 * Copyright © 2003 Carl Worth
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.
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.
24 * Author: Carl Worth <carl@theworths.org>
32 #include <gtk/gtkmain.h>
33 #include <gtk/gtksignal.h>
36 #include "grr_board_view.h"
38 #define SCROLL_DELAY_LENGTH 300
39 #define GRR_BOARD_VIEW_DEFAULT_SIZE 100
41 #define GRR_SVG_ASSUMED_WIDTH 32.0
42 #define GRR_SVG_ASSUMED_HEIGHT 32.0
44 #define GRR_CELL_SVG "svg/cell.svg"
45 #define GRR_WALL_SVG "svg/wall.svg"
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"
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"
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"
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"
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"
72 #define GRR_TARGET_WHIRL_SVG "svg/target_whirl.svg"
75 /* Forward declarations */
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);
94 static void grr_board_view_update_mouse (grr_board_view_t *view, gint x, gint y);
95 static void grr_board_view_update (grr_board_view_t *view);
99 static GtkWidgetClass *parent_class = NULL;
102 grr_board_view_get_type ()
104 static GType view_type = 0;
108 static const GTypeInfo view_info =
110 sizeof (grr_board_view_class_t),
113 (GClassInitFunc) grr_board_view_class_init,
116 sizeof (grr_board_view_t),
118 (GInstanceInitFunc) grr_board_view_init,
121 view_type = g_type_register_static (GTK_TYPE_WIDGET, "grr_board_view", &view_info, 0);
128 grr_board_view_class_init (grr_board_view_class_t *class)
130 GtkObjectClass *object_class;
131 GtkWidgetClass *widget_class;
133 object_class = (GtkObjectClass*) class;
134 widget_class = (GtkWidgetClass*) class;
136 parent_class = gtk_type_class (gtk_widget_get_type ());
138 object_class->destroy = grr_board_view_destroy;
140 widget_class->realize = grr_board_view_realize;
141 widget_class->expose_event = grr_board_view_expose;
142 widget_class->size_request = grr_board_view_size_request;
143 widget_class->size_allocate = grr_board_view_size_allocate;
144 widget_class->button_press_event = grr_board_view_button_press;
145 widget_class->button_release_event = grr_board_view_button_release;
146 widget_class->motion_notify_event = grr_board_view_motion_notify;
150 grr_board_view_init (grr_board_view_t *view)
153 view->owns_board = 0;
160 grr_board_view_new (rr_board_t *board)
162 grr_board_view_t *view;
164 view = g_object_new (grr_board_view_get_type (), NULL);
167 board = rr_board_create (16, 16);
168 view->owns_board = 1;
171 grr_board_view_set_board (view, board);
173 return GTK_WIDGET (view);
177 grr_board_view_destroy (GtkObject *object)
179 grr_board_view_t *view;
181 g_return_if_fail (object != NULL);
182 g_return_if_fail (GRR_IS_BOARD_VIEW (object));
184 view = GRR_BOARD_VIEW (object);
186 rr_board_destroy (view->board);
188 if (view->board && view->owns_board) {
189 rr_board_destroy (view->board);
193 if (GTK_OBJECT_CLASS (parent_class)->destroy)
194 (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
198 grr_board_view_set_board (grr_board_view_t *view,
201 g_return_if_fail (view != NULL);
202 g_return_if_fail (GRR_IS_BOARD_VIEW (view));
206 grr_board_view_update (view);
210 grr_board_view_realize (GtkWidget *widget)
212 grr_board_view_t *view;
213 GdkWindowAttr attributes;
214 gint attributes_mask;
216 g_return_if_fail (widget != NULL);
217 g_return_if_fail (GRR_IS_BOARD_VIEW (widget));
219 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
220 view = GRR_BOARD_VIEW (widget);
222 attributes.x = widget->allocation.x;
223 attributes.y = widget->allocation.y;
224 attributes.width = widget->allocation.width;
225 attributes.height = widget->allocation.height;
226 attributes.wclass = GDK_INPUT_OUTPUT;
227 attributes.window_type = GDK_WINDOW_CHILD;
228 attributes.event_mask = gtk_widget_get_events (widget) |
229 GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK |
230 GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK |
231 GDK_POINTER_MOTION_HINT_MASK;
232 attributes.visual = gtk_widget_get_visual (widget);
233 attributes.colormap = gtk_widget_get_colormap (widget);
235 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
236 widget->window = gdk_window_new (widget->parent->window, &attributes, attributes_mask);
238 widget->style = gtk_style_attach (widget->style, widget->window);
240 gdk_window_set_user_data (widget->window, widget);
242 gtk_style_set_background (widget->style, widget->window, GTK_STATE_ACTIVE);
246 grr_board_view_size_request (GtkWidget *widget,
247 GtkRequisition *requisition)
249 requisition->width = GRR_BOARD_VIEW_DEFAULT_SIZE;
250 requisition->height = GRR_BOARD_VIEW_DEFAULT_SIZE;
254 grr_board_view_size_allocate (GtkWidget *widget,
255 GtkAllocation *allocation)
257 grr_board_view_t *view;
259 g_return_if_fail (widget != NULL);
260 g_return_if_fail (GRR_IS_BOARD_VIEW (widget));
261 g_return_if_fail (allocation != NULL);
263 widget->allocation = *allocation;
264 view = GRR_BOARD_VIEW (widget);
266 if (GTK_WIDGET_REALIZED (widget)) {
267 gdk_window_move_resize (widget->window,
268 allocation->x, allocation->y,
269 allocation->width, allocation->height);
274 grr_svg_draw (XrState *xrs, char *svg)
276 xsvg_status_t status;
279 status = xsvg_create (&xsvg);
281 fprintf (stderr, "xsvg_create error\n");
284 status = xsvg_parse_file (xsvg, svg);
286 fprintf (stderr, "xsvg_parse_file error parsing %s\n", svg);
289 xsvg_render (xsvg, xrs);
291 fprintf (stderr, "xsvg_render error\n");
298 grr_wall_draw (XrState *xrs, rr_wall_t wall)
300 char *wall_svg = GRR_WALL_SVG;
302 if (wall == RR_WALL_NONE)
305 if (wall & RR_WALL_ABOVE) {
306 grr_svg_draw (xrs, wall_svg);
308 if (wall & RR_WALL_LEFT) {
310 XrRotate (xrs, M_PI_2);
311 grr_svg_draw (xrs, wall_svg);
314 if (wall & RR_WALL_RIGHT) {
316 XrTranslate (xrs, GRR_SVG_ASSUMED_WIDTH, 0);
317 XrRotate (xrs, M_PI_2);
318 grr_svg_draw (xrs, wall_svg);
321 if (wall & RR_WALL_BELOW) {
323 XrTranslate (xrs, 0, GRR_SVG_ASSUMED_HEIGHT);
324 grr_svg_draw (xrs, wall_svg);
330 grr_target_draw (XrState *xrs, rr_target_t target)
334 if (target == RR_TARGET_NONE)
338 case RR_TARGET_BLUE_CIRCLE:
339 target_svg = GRR_TARGET_BLUE_CIRCLE_SVG;
341 case RR_TARGET_BLUE_OCTAGON:
342 target_svg = GRR_TARGET_BLUE_OCTAGON_SVG;
344 case RR_TARGET_BLUE_SQUARE:
345 target_svg = GRR_TARGET_BLUE_SQUARE_SVG;
347 case RR_TARGET_BLUE_TRIANGLE:
348 target_svg = GRR_TARGET_BLUE_TRIANGLE_SVG;
350 case RR_TARGET_GREEN_CIRCLE:
351 target_svg = GRR_TARGET_GREEN_CIRCLE_SVG;
353 case RR_TARGET_GREEN_OCTAGON:
354 target_svg = GRR_TARGET_GREEN_OCTAGON_SVG;
356 case RR_TARGET_GREEN_SQUARE:
357 target_svg = GRR_TARGET_GREEN_SQUARE_SVG;
359 case RR_TARGET_GREEN_TRIANGLE:
360 target_svg = GRR_TARGET_GREEN_TRIANGLE_SVG;
362 case RR_TARGET_RED_CIRCLE:
363 target_svg = GRR_TARGET_RED_CIRCLE_SVG;
365 case RR_TARGET_RED_OCTAGON:
366 target_svg = GRR_TARGET_RED_OCTAGON_SVG;
368 case RR_TARGET_RED_SQUARE:
369 target_svg = GRR_TARGET_RED_SQUARE_SVG;
371 case RR_TARGET_RED_TRIANGLE:
372 target_svg = GRR_TARGET_RED_TRIANGLE_SVG;
374 case RR_TARGET_YELLOW_CIRCLE:
375 target_svg = GRR_TARGET_YELLOW_CIRCLE_SVG;
377 case RR_TARGET_YELLOW_OCTAGON:
378 target_svg = GRR_TARGET_YELLOW_OCTAGON_SVG;
380 case RR_TARGET_YELLOW_SQUARE:
381 target_svg = GRR_TARGET_YELLOW_SQUARE_SVG;
383 case RR_TARGET_YELLOW_TRIANGLE:
384 target_svg = GRR_TARGET_YELLOW_TRIANGLE_SVG;
386 case RR_TARGET_WHIRL:
387 target_svg = GRR_TARGET_WHIRL_SVG;
393 grr_svg_draw (xrs, target_svg);
397 grr_robot_draw (XrState *xrs, rr_robot_t robot)
401 if (robot == RR_ROBOT_NONE)
406 robot_svg = GRR_ROBOT_BLUE_SVG;
409 robot_svg = GRR_ROBOT_GREEN_SVG;
412 robot_svg = GRR_ROBOT_RED_SVG;
414 case RR_ROBOT_YELLOW:
415 robot_svg = GRR_ROBOT_YELLOW_SVG;
421 grr_svg_draw (xrs, robot_svg);
425 grr_cell_draw (XrState *xrs, rr_cell_t cell, rr_target_t goal,
426 int width, int height)
433 width / GRR_SVG_ASSUMED_WIDTH,
434 height / GRR_SVG_ASSUMED_HEIGHT);
435 grr_svg_draw (xrs, GRR_CELL_SVG);
436 grr_wall_draw (xrs, RR_CELL_GET_WALLS (cell));
443 XrTranslate (xrs, xpad, ypad);
445 (width - 2*xpad) / GRR_SVG_ASSUMED_WIDTH,
446 (height - 2*ypad) / GRR_SVG_ASSUMED_HEIGHT);
448 target = RR_CELL_GET_TARGET (cell);
449 grr_target_draw (xrs, target);
452 /* This is a hack, (it obscures the cell background in addition to
453 the target). The real way to do this is to draw the target
454 itself with less opacity. */
455 if (target && target != goal) {
458 width / GRR_SVG_ASSUMED_WIDTH,
459 height / GRR_SVG_ASSUMED_HEIGHT);
460 XrRectangle (xrs, 0, 0,
461 GRR_SVG_ASSUMED_WIDTH,
462 GRR_SVG_ASSUMED_HEIGHT);
463 XrSetRGBColor (xrs, 1, 1, 1);
464 XrSetAlpha (xrs, 0.75);
470 XrTranslate (xrs, xpad, ypad);
472 (width - 2*xpad) / GRR_SVG_ASSUMED_WIDTH,
473 (height - 2*ypad) / GRR_SVG_ASSUMED_HEIGHT);
475 grr_robot_draw (xrs, RR_CELL_GET_ROBOT (cell));
482 grr_board_view_expose (GtkWidget *widget,
483 GdkEventExpose *event)
485 grr_board_view_t *view;
489 GdkDrawable *real_drawable;
492 rr_target_t goal_target;
494 g_return_val_if_fail (widget != NULL, FALSE);
495 g_return_val_if_fail (GRR_IS_BOARD_VIEW (widget), FALSE);
496 g_return_val_if_fail (event != NULL, FALSE);
498 if (event->count > 0)
501 view = GRR_BOARD_VIEW (widget);
503 /* Unabstract X from GTK+ */
504 gdk_window_get_internal_paint_info (widget->window, &real_drawable, &x_off, &y_off);
505 dpy = gdk_x11_drawable_get_xdisplay (real_drawable);
506 drawable = gdk_x11_drawable_get_xid (real_drawable);
508 /* Ignore GTK+ and use Xr for drawing. */
510 XrSetTargetDrawable (xrs, dpy, drawable);
511 XrTranslate (xrs, -x_off, -y_off);
513 rr_board_get_size (view->board, &view->board_width, &view->board_height);
515 view->cell_width = widget->allocation.width / view->board_width;
516 if (view->cell_width == 0)
517 view->cell_width = 1;
518 view->cell_height = widget->allocation.height / view->board_height;
519 if (view->cell_height == 0)
520 view->cell_height = 1;
522 view->board_pad_x = (widget->allocation.width - view->board_width * view->cell_width) / 2;
523 view->board_pad_y = (widget->allocation.height - view->board_height * view->cell_height) / 2;
525 XrTranslate (xrs, view->board_pad_x, view->board_pad_y);
527 goal_target = rr_board_get_goal_target (view->board);
529 /* Draw cell targets */
530 for (j=0; j < view->board_height; j++) {
531 for (i=0; i < view->board_width; i++) {
533 XrTranslate (xrs, i * view->cell_width, j * view->cell_height);
535 rr_board_get_cell (view->board, i, j),
537 view->cell_width, view->cell_height);
544 XrSetRGBColor (xrs, .75, .75, 1);
545 XrSetLineWidth (xrs, 1);
546 for (j=0; j < view->board_height; j++) {
547 XrMoveTo (xrs, 0, j * view->cell_height + 0.5);
548 XrRelLineTo (xrs, view->board_width * view->cell_width, 0);
550 for (i=0; i < view->board_width; i++) {
551 XrMoveTo (xrs, i * view->cell_width + 0.5, 0);
552 XrRelLineTo (xrs, 0, view->board_height * view->cell_height);
557 /* Draw goal target in center of board */
560 (view->board_width / 2 - 1) * view->cell_width,
561 (view->board_height / 2 - 1) * view->cell_height);
562 grr_cell_draw (xrs, goal_target, goal_target,
563 view->cell_width * 2, view->cell_height * 2);
567 for (j=0; j < view->board_height; j++) {
568 for (i=0; i < view->board_width; i++) {
570 XrTranslate (xrs, i * view->cell_width, j * view->cell_height);
572 view->cell_width / GRR_SVG_ASSUMED_WIDTH,
573 view->cell_height / GRR_SVG_ASSUMED_HEIGHT);
574 grr_wall_draw (xrs, RR_CELL_GET_WALLS (rr_board_get_cell(view->board, i, j)));
586 grr_board_view_pointer_coords_to_grid (grr_board_view_t *view,
587 int pointer_x, int pointer_y,
591 grr_board_view_button_press (GtkWidget *widget,
592 GdkEventButton *event)
594 grr_board_view_t *view;
596 g_return_val_if_fail (widget != NULL, FALSE);
597 g_return_val_if_fail (GRR_IS_BOARD_VIEW (widget), FALSE);
598 g_return_val_if_fail (event != NULL, FALSE);
600 view = GRR_BOARD_VIEW (widget);
603 grr_board_view_pointer_coords_to_grid (view, event->x, event->y, &i, &j);
608 rr_board_has_robot (view->board, i, j))
610 gtk_grab_add (widget);
612 view->button = event->button;
614 grr_board_view_update_mouse (view, event->x, event->y);
622 grr_board_view_button_release (GtkWidget *widget,
623 GdkEventButton *event)
625 grr_board_view_t *view;
627 g_return_val_if_fail (widget != NULL, FALSE);
628 g_return_val_if_fail (GRR_IS_BOARD_VIEW (widget), FALSE);
629 g_return_val_if_fail (event != NULL, FALSE);
631 view = GRR_BOARD_VIEW (widget);
633 if (view->button == event->button) {
635 gtk_grab_remove (widget);
641 grr_board_view_pointer_coords_to_grid (view, event->x, event->y, &i, &j);
644 /* XXX: Need to compute direction based on (i, j) and old (i, j) */
646 rr_client_move (view->client, view->robot, direction);
655 grr_board_view_motion_notify (GtkWidget *widget,
656 GdkEventMotion *event)
658 grr_board_view_t *view;
659 GdkModifierType mods;
662 g_return_val_if_fail (widget != NULL, FALSE);
663 g_return_val_if_fail (GRR_IS_BOARD_VIEW (widget), FALSE);
664 g_return_val_if_fail (event != NULL, FALSE);
666 view = GRR_BOARD_VIEW (widget);
668 if (view->button != 0) {
672 if (event->is_hint || (event->window != widget->window))
673 gdk_window_get_pointer (widget->window, &x, &y, &mods);
675 switch (view->button) {
677 mask = GDK_BUTTON1_MASK;
680 mask = GDK_BUTTON2_MASK;
683 mask = GDK_BUTTON3_MASK;
691 grr_board_view_update_mouse (view, x,y);
698 grr_board_view_update_mouse (grr_board_view_t *view, gint x, gint y)
702 g_return_if_fail (view != NULL);
703 g_return_if_fail (GRR_IS_BOARD_VIEW (view));
705 xc = GTK_WIDGET(view)->allocation.width / 2;
706 yc = GTK_WIDGET(view)->allocation.height / 2;
710 grr_board_view_update (grr_board_view_t *view)
712 g_return_if_fail (view != NULL);
713 g_return_if_fail (GRR_IS_BOARD_VIEW (view));
715 gtk_widget_queue_draw (GTK_WIDGET (view));