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>
35 #include <gtk/gtkmain.h>
36 #include <gtk/gtksignal.h>
39 #include "grr_board_view.h"
42 #define SCROLL_DELAY_LENGTH 300
43 #define GRR_BOARD_VIEW_DEFAULT_SIZE 100
45 #define GRR_SVG_ASSUMED_WIDTH 32.0
46 #define GRR_SVG_ASSUMED_HEIGHT 32.0
48 #define GRR_CELL_SVG "svg/cell.svg"
49 #define GRR_WALL_SVG "svg/wall.svg"
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"
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"
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"
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"
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"
76 #define GRR_TARGET_WHIRL_SVG "svg/target_whirl.svg"
79 /* Forward declarations */
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);
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);
103 static GtkWidgetClass *parent_class = NULL;
106 grr_board_view_get_type ()
108 static GType view_type = 0;
112 static const GTypeInfo view_info =
114 sizeof (grr_board_view_class_t),
117 (GClassInitFunc) grr_board_view_class_init,
120 sizeof (grr_board_view_t),
122 (GInstanceInitFunc) grr_board_view_init,
125 view_type = g_type_register_static (GTK_TYPE_WIDGET, "grr_board_view", &view_info, 0);
132 grr_board_view_class_init (grr_board_view_class_t *class)
134 GtkObjectClass *object_class;
135 GtkWidgetClass *widget_class;
137 object_class = (GtkObjectClass*) class;
138 widget_class = (GtkWidgetClass*) class;
140 parent_class = gtk_type_class (gtk_widget_get_type ());
142 object_class->destroy = grr_board_view_destroy;
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;
154 grr_board_view_init (grr_board_view_t *view)
164 grr_board_view_new (rr_board_t *board)
166 grr_board_view_t *view;
168 view = g_object_new (grr_board_view_get_type (), NULL);
172 grr_board_view_update (view);
174 return GTK_WIDGET (view);
178 grr_board_view_set_client (grr_board_view_t *view, rr_client_t *client)
180 g_return_if_fail (view != NULL);
182 view->client = client;
186 grr_board_view_destroy (GtkObject *object)
188 grr_board_view_t *view;
190 g_return_if_fail (object != NULL);
191 g_return_if_fail (GRR_IS_BOARD_VIEW (object));
193 view = GRR_BOARD_VIEW (object);
198 if (GTK_OBJECT_CLASS (parent_class)->destroy)
199 (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
203 grr_board_view_realize (GtkWidget *widget)
205 grr_board_view_t *view;
206 GdkWindowAttr attributes;
207 gint attributes_mask;
209 g_return_if_fail (widget != NULL);
210 g_return_if_fail (GRR_IS_BOARD_VIEW (widget));
212 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
213 view = GRR_BOARD_VIEW (widget);
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);
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);
231 widget->style = gtk_style_attach (widget->style, widget->window);
233 gdk_window_set_user_data (widget->window, widget);
235 gtk_style_set_background (widget->style, widget->window, GTK_STATE_ACTIVE);
239 grr_board_view_size_request (GtkWidget *widget,
240 GtkRequisition *requisition)
242 requisition->width = GRR_BOARD_VIEW_DEFAULT_SIZE;
243 requisition->height = GRR_BOARD_VIEW_DEFAULT_SIZE;
247 grr_board_view_size_allocate (GtkWidget *widget,
248 GtkAllocation *allocation)
250 grr_board_view_t *view;
252 g_return_if_fail (widget != NULL);
253 g_return_if_fail (GRR_IS_BOARD_VIEW (widget));
254 g_return_if_fail (allocation != NULL);
256 widget->allocation = *allocation;
257 view = GRR_BOARD_VIEW (widget);
259 if (GTK_WIDGET_REALIZED (widget)) {
260 gdk_window_move_resize (widget->window,
261 allocation->x, allocation->y,
262 allocation->width, allocation->height);
267 grr_svg_draw (XrState *xrs, char *svg)
269 xsvg_status_t status;
272 status = xsvg_create (&xsvg);
274 fprintf (stderr, "xsvg_create error\n");
277 status = xsvg_parse_file (xsvg, svg);
279 fprintf (stderr, "xsvg_parse_file error parsing %s\n", svg);
282 xsvg_render (xsvg, xrs);
284 fprintf (stderr, "xsvg_render error\n");
291 grr_wall_draw (XrState *xrs, rr_wall_t wall)
293 char *wall_svg = GRR_WALL_SVG;
295 if (wall == RR_WALL_NONE)
298 if (wall & RR_WALL_ABOVE) {
299 grr_svg_draw (xrs, wall_svg);
301 if (wall & RR_WALL_LEFT) {
303 XrRotate (xrs, M_PI_2);
304 grr_svg_draw (xrs, wall_svg);
307 if (wall & RR_WALL_RIGHT) {
309 XrTranslate (xrs, GRR_SVG_ASSUMED_WIDTH, 0);
310 XrRotate (xrs, M_PI_2);
311 grr_svg_draw (xrs, wall_svg);
314 if (wall & RR_WALL_BELOW) {
316 XrTranslate (xrs, 0, GRR_SVG_ASSUMED_HEIGHT);
317 grr_svg_draw (xrs, wall_svg);
323 grr_target_draw (XrState *xrs, rr_target_t target)
327 if (target == RR_TARGET_NONE)
331 case RR_TARGET_BLUE_CIRCLE:
332 target_svg = GRR_TARGET_BLUE_CIRCLE_SVG;
334 case RR_TARGET_BLUE_OCTAGON:
335 target_svg = GRR_TARGET_BLUE_OCTAGON_SVG;
337 case RR_TARGET_BLUE_SQUARE:
338 target_svg = GRR_TARGET_BLUE_SQUARE_SVG;
340 case RR_TARGET_BLUE_TRIANGLE:
341 target_svg = GRR_TARGET_BLUE_TRIANGLE_SVG;
343 case RR_TARGET_GREEN_CIRCLE:
344 target_svg = GRR_TARGET_GREEN_CIRCLE_SVG;
346 case RR_TARGET_GREEN_OCTAGON:
347 target_svg = GRR_TARGET_GREEN_OCTAGON_SVG;
349 case RR_TARGET_GREEN_SQUARE:
350 target_svg = GRR_TARGET_GREEN_SQUARE_SVG;
352 case RR_TARGET_GREEN_TRIANGLE:
353 target_svg = GRR_TARGET_GREEN_TRIANGLE_SVG;
355 case RR_TARGET_RED_CIRCLE:
356 target_svg = GRR_TARGET_RED_CIRCLE_SVG;
358 case RR_TARGET_RED_OCTAGON:
359 target_svg = GRR_TARGET_RED_OCTAGON_SVG;
361 case RR_TARGET_RED_SQUARE:
362 target_svg = GRR_TARGET_RED_SQUARE_SVG;
364 case RR_TARGET_RED_TRIANGLE:
365 target_svg = GRR_TARGET_RED_TRIANGLE_SVG;
367 case RR_TARGET_YELLOW_CIRCLE:
368 target_svg = GRR_TARGET_YELLOW_CIRCLE_SVG;
370 case RR_TARGET_YELLOW_OCTAGON:
371 target_svg = GRR_TARGET_YELLOW_OCTAGON_SVG;
373 case RR_TARGET_YELLOW_SQUARE:
374 target_svg = GRR_TARGET_YELLOW_SQUARE_SVG;
376 case RR_TARGET_YELLOW_TRIANGLE:
377 target_svg = GRR_TARGET_YELLOW_TRIANGLE_SVG;
379 case RR_TARGET_WHIRL:
380 target_svg = GRR_TARGET_WHIRL_SVG;
386 grr_svg_draw (xrs, target_svg);
390 grr_robot_draw (XrState *xrs, rr_robot_t robot)
394 if (robot == RR_ROBOT_NONE)
399 robot_svg = GRR_ROBOT_BLUE_SVG;
402 robot_svg = GRR_ROBOT_GREEN_SVG;
405 robot_svg = GRR_ROBOT_RED_SVG;
407 case RR_ROBOT_YELLOW:
408 robot_svg = GRR_ROBOT_YELLOW_SVG;
414 grr_svg_draw (xrs, robot_svg);
418 grr_cell_draw (XrState *xrs, rr_cell_t cell, rr_target_t goal,
419 int width, int height)
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));
436 XrTranslate (xrs, xpad, ypad);
438 (width - 2*xpad) / GRR_SVG_ASSUMED_WIDTH,
439 (height - 2*ypad) / GRR_SVG_ASSUMED_HEIGHT);
441 target = RR_CELL_GET_TARGET (cell);
442 grr_target_draw (xrs, target);
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) {
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);
463 XrTranslate (xrs, xpad, ypad);
465 (width - 2*xpad) / GRR_SVG_ASSUMED_WIDTH,
466 (height - 2*ypad) / GRR_SVG_ASSUMED_HEIGHT);
468 grr_robot_draw (xrs, RR_CELL_GET_ROBOT (cell));
475 grr_board_view_expose (GtkWidget *widget,
476 GdkEventExpose *event)
478 grr_board_view_t *view;
483 GdkDrawable *real_drawable;
486 rr_target_t goal_target;
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);
492 if (event->count > 0)
495 view = GRR_BOARD_VIEW (widget);
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);
503 /* Ignore GTK+ and use Xr for drawing. */
505 XrSetTargetDrawable (xrs, dpy, drawable);
506 XrTranslate (xrs, -x_off, -y_off);
508 rr_board_get_size (board, &view->board_width, &view->board_height);
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;
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;
520 XrTranslate (xrs, view->board_pad_x, view->board_pad_y);
522 goal_target = rr_board_get_goal_target (board);
524 /* Draw cell targets */
525 for (j=0; j < view->board_height; j++) {
526 for (i=0; i < view->board_width; i++) {
528 XrTranslate (xrs, i * view->cell_width, j * view->cell_height);
530 rr_board_get_cell (board, i, j),
532 view->cell_width, view->cell_height);
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);
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);
554 /* Draw goal target in center of board */
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);
566 for (j=0; j < view->board_height; j++) {
567 for (i=0; i < view->board_width; i++) {
569 XrTranslate (xrs, i * view->cell_width, j * view->cell_height);
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)));
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)
588 *grid_x = (pointer_x - view->board_pad_x) / view->cell_width;
589 *grid_y = (pointer_y - view->board_pad_y) / view->cell_height;
594 grr_board_view_button_press (GtkWidget *widget,
595 GdkEventButton *event)
597 grr_board_view_t *view;
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);
605 view = GRR_BOARD_VIEW (widget);
607 grr_board_view_pointer_coords_to_grid (view,
611 cell = rr_board_get_cell (view->board, x, y);
612 if (!view->button && RR_CELL_GET_ROBOT (cell)) {
614 gtk_grab_add (widget);
616 view->button = event->button;
617 view->drag_robot = RR_CELL_GET_ROBOT (cell);
619 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;
633 int robot_x, robot_y;
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);
640 view = GRR_BOARD_VIEW (widget);
642 if (view->button == event->button) {
643 gtk_grab_remove (widget);
647 grr_board_view_pointer_coords_to_grid (view, event->x, event->y, &x, &y);
649 rr_board_find_robot (view->board, view->drag_robot, &robot_x, &robot_y);
652 if (dx == 0 && dy == 0)
655 if (abs(dx) > abs(dy))
666 switch (view->drag_robot) {
676 case RR_ROBOT_YELLOW:
686 grr_sprintf_alloc (&move_str, "%s %s", robot, dir);
687 if (move_str == NULL)
689 rr_client_move (view->client, move_str);
697 grr_board_view_motion_notify (GtkWidget *widget,
698 GdkEventMotion *event)
700 grr_board_view_t *view;
701 GdkModifierType mods;
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);
708 view = GRR_BOARD_VIEW (widget);
710 if (view->button != 0) {
714 if (event->is_hint || (event->window != widget->window))
715 gdk_window_get_pointer (widget->window, &x, &y, &mods);
717 switch (view->button) {
719 mask = GDK_BUTTON1_MASK;
722 mask = GDK_BUTTON2_MASK;
725 mask = GDK_BUTTON3_MASK;
733 grr_board_view_update_mouse (view, x,y);
740 grr_board_view_update_mouse (grr_board_view_t *view, gint x, gint y)
742 g_return_if_fail (view != NULL);
743 g_return_if_fail (GRR_IS_BOARD_VIEW (view));
745 /* XXX: Should draw a robot here */
749 grr_board_view_update (grr_board_view_t *view)
751 g_return_if_fail (view != NULL);
752 g_return_if_fail (GRR_IS_BOARD_VIEW (view));
754 gtk_widget_queue_draw (GTK_WIDGET (view));