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);
93 static gint grr_board_view_timer (grr_board_view_t *view);
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);
100 static GtkWidgetClass *parent_class = NULL;
103 grr_board_view_get_type ()
105 static GType view_type = 0;
109 static const GTypeInfo view_info =
111 sizeof (grr_board_view_class_t),
114 (GClassInitFunc) grr_board_view_class_init,
117 sizeof (grr_board_view_t),
119 (GInstanceInitFunc) grr_board_view_init,
122 view_type = g_type_register_static (GTK_TYPE_WIDGET, "grr_board_view", &view_info, 0);
129 grr_board_view_class_init (grr_board_view_class_t *class)
131 GtkObjectClass *object_class;
132 GtkWidgetClass *widget_class;
134 object_class = (GtkObjectClass*) class;
135 widget_class = (GtkWidgetClass*) class;
137 parent_class = gtk_type_class (gtk_widget_get_type ());
139 object_class->destroy = grr_board_view_destroy;
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;
151 grr_board_view_init (grr_board_view_t *view)
154 view->owns_board = 0;
157 view->policy = GTK_UPDATE_CONTINUOUS;
160 view->pointer_width = 0;
162 view->old_value = 0.0;
163 view->old_lower = 0.0;
164 view->old_upper = 0.0;
168 grr_board_view_new (rr_board_t *board)
170 grr_board_view_t *view;
172 view = g_object_new (grr_board_view_get_type (), NULL);
175 board = rr_board_create (16, 16);
176 view->owns_board = 1;
179 grr_board_view_set_board (view, board);
181 return GTK_WIDGET (view);
185 grr_board_view_destroy (GtkObject *object)
187 grr_board_view_t *view;
189 g_return_if_fail (object != NULL);
190 g_return_if_fail (GRR_IS_BOARD_VIEW (object));
192 view = GRR_BOARD_VIEW (object);
194 rr_board_destroy (view->board);
196 if (view->board && view->owns_board) {
197 rr_board_destroy (view->board);
201 if (GTK_OBJECT_CLASS (parent_class)->destroy)
202 (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
206 grr_board_view_set_board (grr_board_view_t *view,
209 g_return_if_fail (view != NULL);
210 g_return_if_fail (GRR_IS_BOARD_VIEW (view));
214 grr_board_view_update (view);
218 grr_board_view_realize (GtkWidget *widget)
220 grr_board_view_t *view;
221 GdkWindowAttr attributes;
222 gint attributes_mask;
224 g_return_if_fail (widget != NULL);
225 g_return_if_fail (GRR_IS_BOARD_VIEW (widget));
227 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
228 view = GRR_BOARD_VIEW (widget);
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);
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);
246 widget->style = gtk_style_attach (widget->style, widget->window);
248 gdk_window_set_user_data (widget->window, widget);
250 gtk_style_set_background (widget->style, widget->window, GTK_STATE_ACTIVE);
254 grr_board_view_size_request (GtkWidget *widget,
255 GtkRequisition *requisition)
257 requisition->width = GRR_BOARD_VIEW_DEFAULT_SIZE;
258 requisition->height = GRR_BOARD_VIEW_DEFAULT_SIZE;
262 grr_board_view_size_allocate (GtkWidget *widget,
263 GtkAllocation *allocation)
265 grr_board_view_t *view;
267 g_return_if_fail (widget != NULL);
268 g_return_if_fail (GRR_IS_BOARD_VIEW (widget));
269 g_return_if_fail (allocation != NULL);
271 widget->allocation = *allocation;
272 view = GRR_BOARD_VIEW (widget);
274 if (GTK_WIDGET_REALIZED (widget))
277 gdk_window_move_resize (widget->window,
278 allocation->x, allocation->y,
279 allocation->width, allocation->height);
282 view->radius = MIN (allocation->width, allocation->height) * 0.45;
283 view->pointer_width = view->radius / 5;
287 grr_svg_draw (XrState *xrs, char *svg)
289 xsvg_status_t status;
292 status = xsvg_create (&xsvg);
294 fprintf (stderr, "xsvg_create error\n");
297 status = xsvg_parse_file (xsvg, svg);
299 fprintf (stderr, "xsvg_parse_file error parsing %s\n", svg);
302 xsvg_render (xsvg, xrs);
304 fprintf (stderr, "xsvg_render error\n");
311 grr_wall_draw (XrState *xrs, rr_wall_t wall)
313 char *wall_svg = GRR_WALL_SVG;
315 if (wall == RR_WALL_NONE)
318 if (wall & RR_WALL_ABOVE) {
319 grr_svg_draw (xrs, wall_svg);
321 if (wall & RR_WALL_LEFT) {
323 XrRotate (xrs, M_PI_2);
324 grr_svg_draw (xrs, wall_svg);
327 if (wall & RR_WALL_RIGHT) {
329 XrTranslate (xrs, GRR_SVG_ASSUMED_WIDTH, 0);
330 XrRotate (xrs, M_PI_2);
331 grr_svg_draw (xrs, wall_svg);
334 if (wall & RR_WALL_BELOW) {
336 XrTranslate (xrs, 0, GRR_SVG_ASSUMED_HEIGHT);
337 grr_svg_draw (xrs, wall_svg);
343 grr_target_draw (XrState *xrs, rr_target_t target)
347 if (target == RR_TARGET_NONE)
351 case RR_TARGET_BLUE_CIRCLE:
352 target_svg = GRR_TARGET_BLUE_CIRCLE_SVG;
354 case RR_TARGET_BLUE_OCTAGON:
355 target_svg = GRR_TARGET_BLUE_OCTAGON_SVG;
357 case RR_TARGET_BLUE_SQUARE:
358 target_svg = GRR_TARGET_BLUE_SQUARE_SVG;
360 case RR_TARGET_BLUE_TRIANGLE:
361 target_svg = GRR_TARGET_BLUE_TRIANGLE_SVG;
363 case RR_TARGET_GREEN_CIRCLE:
364 target_svg = GRR_TARGET_GREEN_CIRCLE_SVG;
366 case RR_TARGET_GREEN_OCTAGON:
367 target_svg = GRR_TARGET_GREEN_OCTAGON_SVG;
369 case RR_TARGET_GREEN_SQUARE:
370 target_svg = GRR_TARGET_GREEN_SQUARE_SVG;
372 case RR_TARGET_GREEN_TRIANGLE:
373 target_svg = GRR_TARGET_GREEN_TRIANGLE_SVG;
375 case RR_TARGET_RED_CIRCLE:
376 target_svg = GRR_TARGET_RED_CIRCLE_SVG;
378 case RR_TARGET_RED_OCTAGON:
379 target_svg = GRR_TARGET_RED_OCTAGON_SVG;
381 case RR_TARGET_RED_SQUARE:
382 target_svg = GRR_TARGET_RED_SQUARE_SVG;
384 case RR_TARGET_RED_TRIANGLE:
385 target_svg = GRR_TARGET_RED_TRIANGLE_SVG;
387 case RR_TARGET_YELLOW_CIRCLE:
388 target_svg = GRR_TARGET_YELLOW_CIRCLE_SVG;
390 case RR_TARGET_YELLOW_OCTAGON:
391 target_svg = GRR_TARGET_YELLOW_OCTAGON_SVG;
393 case RR_TARGET_YELLOW_SQUARE:
394 target_svg = GRR_TARGET_YELLOW_SQUARE_SVG;
396 case RR_TARGET_YELLOW_TRIANGLE:
397 target_svg = GRR_TARGET_YELLOW_TRIANGLE_SVG;
399 case RR_TARGET_WHIRL:
400 target_svg = GRR_TARGET_WHIRL_SVG;
406 grr_svg_draw (xrs, target_svg);
410 grr_robot_draw (XrState *xrs, rr_robot_t robot)
414 if (robot == RR_ROBOT_NONE)
419 robot_svg = GRR_ROBOT_BLUE_SVG;
422 robot_svg = GRR_ROBOT_GREEN_SVG;
425 robot_svg = GRR_ROBOT_RED_SVG;
427 case RR_ROBOT_YELLOW:
428 robot_svg = GRR_ROBOT_YELLOW_SVG;
434 grr_svg_draw (xrs, robot_svg);
438 grr_cell_draw (XrState *xrs, rr_cell_t cell, rr_target_t goal,
439 int width, int height)
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));
456 XrTranslate (xrs, xpad, ypad);
458 (width - 2*xpad) / GRR_SVG_ASSUMED_WIDTH,
459 (height - 2*ypad) / GRR_SVG_ASSUMED_HEIGHT);
461 target = RR_CELL_GET_TARGET (cell);
462 grr_target_draw (xrs, target);
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) {
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);
483 XrTranslate (xrs, xpad, ypad);
485 (width - 2*xpad) / GRR_SVG_ASSUMED_WIDTH,
486 (height - 2*ypad) / GRR_SVG_ASSUMED_HEIGHT);
488 grr_robot_draw (xrs, RR_CELL_GET_ROBOT (cell));
495 grr_board_view_expose (GtkWidget *widget,
496 GdkEventExpose *event)
498 grr_board_view_t *view;
502 GdkDrawable *real_drawable;
505 rr_target_t goal_target;
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);
511 if (event->count > 0)
514 view = GRR_BOARD_VIEW (widget);
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);
521 /* Ignore GTK+ and use Xr for drawing. */
523 XrSetTargetDrawable (xrs, dpy, drawable);
524 XrTranslate (xrs, -x_off, -y_off);
526 rr_board_get_size (view->board, &view->board_width, &view->board_height);
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;
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;
538 XrTranslate (xrs, view->board_pad_x, view->board_pad_y);
540 goal_target = rr_board_get_goal_target (view->board);
542 /* Draw cell targets */
543 for (j=0; j < view->board_height; j++) {
544 for (i=0; i < view->board_width; i++) {
546 XrTranslate (xrs, i * view->cell_width, j * view->cell_height);
548 rr_board_get_cell (view->board, i, j),
550 view->cell_width, view->cell_height);
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);
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);
570 /* Draw goal target in center of board */
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);
580 for (j=0; j < view->board_height; j++) {
581 for (i=0; i < view->board_width; i++) {
583 XrTranslate (xrs, i * view->cell_width, j * view->cell_height);
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)));
598 grr_board_view_button_press (GtkWidget *widget,
599 GdkEventButton *event)
601 grr_board_view_t *view;
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);
607 view = GRR_BOARD_VIEW (widget);
609 /* XXX: grr_board_view_pointer_coords_to_grid (view, event->x, event->y, &i, &j);
612 rr_board_has_robot (view->board, i, j))
614 gtk_grab_add (widget);
616 view->button = event->button;
618 grr_board_view_update_mouse (view, event->x, event->y);
626 grr_board_view_button_release (GtkWidget *widget,
627 GdkEventButton *event)
629 grr_board_view_t *view;
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);
635 view = GRR_BOARD_VIEW (widget);
637 if (view->button == event->button)
639 gtk_grab_remove (widget);
643 /* XXX: grr_board_view_pointer_coords_to_grid (view, event->x, event->y, &i, &j); */
645 /* XXX: Need to compute direction based on (i, j) and old (i, j) */
646 /*XXX: rr_client_move (view->client, view->robot, direction); */
654 grr_board_view_motion_notify (GtkWidget *widget,
655 GdkEventMotion *event)
657 grr_board_view_t *view;
658 GdkModifierType mods;
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);
665 view = GRR_BOARD_VIEW (widget);
667 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)
678 mask = GDK_BUTTON1_MASK;
681 mask = GDK_BUTTON2_MASK;
684 mask = GDK_BUTTON3_MASK;
692 grr_board_view_update_mouse (view, x,y);
699 grr_board_view_timer (grr_board_view_t *view)
701 g_return_val_if_fail (view != NULL, FALSE);
702 g_return_val_if_fail (GRR_IS_BOARD_VIEW (view), FALSE);
708 grr_board_view_update_mouse (grr_board_view_t *view, gint x, gint y)
713 g_return_if_fail (view != NULL);
714 g_return_if_fail (GRR_IS_BOARD_VIEW (view));
716 xc = GTK_WIDGET(view)->allocation.width / 2;
717 yc = GTK_WIDGET(view)->allocation.height / 2;
719 old_value = view->adjustment->value;
720 view->angle = atan2(yc-y, x-xc);
722 if (view->angle < -G_PI/2.)
723 view->angle += 2*G_PI;
725 if (view->angle < -G_PI/6)
726 view->angle = -G_PI/6;
728 if (view->angle > 7.*G_PI/6.)
729 view->angle = 7.*G_PI/6.;
731 view->adjustment->value = view->adjustment->lower + (7.*G_PI/6 - view->angle) *
732 (view->adjustment->upper - view->adjustment->lower) / (4.*G_PI/3.);
734 if (view->adjustment->value != old_value)
736 if (view->policy == GTK_UPDATE_CONTINUOUS)
738 g_signal_emit_by_name (GTK_OBJECT (view->adjustment), "value_changed");
742 gtk_widget_queue_draw (GTK_WIDGET (view));
744 if (view->policy == GTK_UPDATE_DELAYED)
747 gtk_timeout_remove (view->timer);
749 view->timer = gtk_timeout_add (SCROLL_DELAY_LENGTH,
750 (GtkFunction) grr_board_view_timer,
758 grr_board_view_update (grr_board_view_t *view)
760 g_return_if_fail (view != NULL);
761 g_return_if_fail (GRR_IS_BOARD_VIEW (view));
763 gtk_widget_queue_draw (GTK_WIDGET (view));