1 /* Copyright (C) 2000 Tim Northover <tim@pnorthover.freeserve.co.uk>
2  * using xpms from Tim Whittock
3  *
4  * Map editor for GNU robots game
5  *
6  * GNU Robots is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * GNU Robots is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with GNU Robots.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 /* compile with:
21    gcc mapedit.c -omapedit -I../lib `gtk-config --cflags --libs` */
22 
23 /* Usage: mapedit [filename] */
24 
25 #include <gtk/gtk.h>		/* xwindows functions */
26 #include <stdio.h>		/* for file functions */
27 #include <stdlib.h>		/* for atoi */
28 
29 /* indexes into the blocks array */
30 
31 #define SPACE 0
32 #define FOOD 1
33 #define WALL 2
34 #define BADDIE 3
35 #define PRIZE 4
36 
37 /* this is 1 more than the real size to allow for a border around
38    images: */
39 
40 #define IMGSIZE 17
41 
42 /* Stores information on blocks for display and storage */
43 
44 typedef struct
45 {
46   int type;			/* probably unnecessary at the moment,
47 				   but potentially useful (pointers) */
48   char *name;			/* for display */
49   char save_char;		/* character in map files */
50   char **xpm_data;		/* again at the moment not really necessary,
51 				   but potentially */
52   GdkPixmap *pixmap;		/* The image in gtk useable form */
53 }
54 BlockType;
55 
56 typedef struct
57 {
58   int width, height;
59   char *data;			/* will contain index into blocks array */
60 }
61 GridType;
62 
63 GridType grid;			/* The internal grid representation */
64 
65 /* Now define known blocks -- leave xpm_data & pixmap for later init */
66 
67 BlockType blocks[] = { {SPACE, "Empty Space", '.', NULL, NULL},
68 {FOOD, "Food", '+', NULL, NULL},
69 {WALL, "Wall", '#', NULL, NULL},
70 {BADDIE, "Baddie", '@', NULL, NULL},
71 {PRIZE, "Prize", '$', NULL, NULL}
72 };
73 
74 int nblocks = 5;		/* number of blocks */
75 
76 GtkEntry *width_text, *height_text;	/* for use by the size setting
77 					   button -- not ideal as
78 					   globals but simpler */
79 
80 int dragging;			/* whether to change "grid" --
81 				   modified by button_press &
82 				   button_release */
83 
84 int current_tool;		/* index into blocks */
85 
86 GtkWidget *grid_window;		/* main window for displaying map --
87                                    used by various functions */
88 
89 /* initialisation/end funcs */
90 
91 /* blocks global */
92 void init_blocks ();
93 
94 /* initialises global "grid" -- doesn't touch anything X related */
95 void init_grid (char *filename);
96 
97 /* used at beginning and in load button func to actually set "grid" */
98 void load_file (char *filename);
99 
100 /* used to report failure to load/save file */
101 void message_box (char *title, char *message);
102 
103 /* ATM only frees "grid.data" */
104 void freestuff ();
105 
106 /* creates a box with pixmap & label -- for toolbox display */
107 GtkWidget *create_pixmap_with_label (GtkWidget * parent, GdkPixmap * pixmap,
108 				     char *text);
109 /* handler function when new tool is clicked */
110 void change_tool (GtkWidget * widget, gpointer data);
111 
112 
113 /* Grid update hanlders */
114 
115 /* (mouse) button pressed in map window -- start draw */
116 void button_press (GtkFixed * fixed, GdkEventButton * event, gpointer data);
117 
118 /* mouse moved in map window -- checks "dragging" */
119 void mouse_move (GtkFixed * fixed, GdkEventMotion * event, gpointer data);
120 
121 /* stop drawing */
122 void button_release (GtkFixed * fixed, GdkEventButton * event, gpointer data);
123 
124 /* sets "grid" and individual pixmap on map window */
125 void update_grid (GtkFixed * fixed, int x, int y);
126 
127 /* creates radio button vertical box (for tools) */
128 GtkWidget *create_toolbox_box (GtkWidget * parent, GtkSignalFunc func);
129 
130 /* creates box with load, save... */
131 GtkWidget *create_command_box (char *filename);
132 
133 /* creates window for both of above widgets */
134 GtkWidget *create_toolbox_window (char *filename);
135 
136 /* creates map display window */
137 GtkWidget *create_grid_window ();
138 
139 /* clicked routine for Save */
140 void save_button (GtkWidget * widget, GtkEntry * textbox);
141 
142 /* clicked routine for Load */
143 void load_button (GtkWidget * widget, GtkEntry * textbox);
144 
145 /* clicked routine for Set Size */
146 void set_size (GtkWidget * widget, gpointer data);
147 
148 /* destroys current map window and creates a new one from "grid" */
149 void recreate_grid_window ();
150 
151 int
main(int argc,char ** argv)152 main (int argc, char **argv)
153 {
154   char *filename;
155   FILE *fp;			/* for checking filename given */
156 
157   GtkWidget *toolbox;		/* main window -- currently unused but
158 				   saved for future -- and it's nice
159 				   to know I know something about the
160 				   window I've created */
161 
162   /* Global initialisation */
163 
164   if (argc > 1)			/* check if filename specified */
165     {
166       filename = argv[1];
167       fp = fopen (filename, "r");
168       if (!fp)
169 	{
170 	  g_print ("Could not open file '%s'\n", filename);
171 	  g_print ("Usage: %s [filename]\n", argv[0]);
172 	  return 1;
173 	}
174       fclose (fp);
175     }
176   else
177     filename = NULL;
178 
179   gtk_init (&argc, &argv);	/* must be before any other gtk_functions... */
180   current_tool = SPACE;
181   init_blocks ();		/* ...like the ones in here */
182 
183   init_grid (filename);		/* loads file or creates 16x16 grid
184                                    with SPACE */
185 
186   toolbox = create_toolbox_window (filename);	/* main window creation */
187 
188   gtk_main ();			/* Main message loop -- Go! */
189 
190   return 0;			/* Everything went fine (from our
191                                    perspective) */
192 }
193 
194 /* initialises the rest of the "blocks" array */
195 void
init_blocks()196 init_blocks ()
197 {
198   int i;			/* counter */
199   GdkColormap *colour;		/* necessary for creating pixmaps
200 				   -- otherwise warnings ensue */
201 #include "xpm/food.xpm"
202 #include "xpm/space.xpm"
203 #include "xpm/prize.xpm"
204 #include "xpm/baddie.xpm"
205 #include "xpm/wall.xpm"
206 
207   /* These could be set globally, but this avoids making the xpm data
208      global */
209 
210   blocks[0].xpm_data = space_xpm;
211   blocks[1].xpm_data = food_xpm;
212   blocks[2].xpm_data = wall_xpm;
213   blocks[3].xpm_data = baddie_xpm;
214   blocks[4].xpm_data = prize_xpm;
215 
216   colour = gdk_colormap_get_system ();	/* needed to prevent warnings */
217 
218   for (i = 0; i < nblocks; i++)
219     {
220       blocks[i].pixmap =
221 	gdk_pixmap_colormap_create_from_xpm_d (NULL, colour, NULL, NULL,
222 					       blocks[i].xpm_data);
223     }
224 }
225 
226 /* either loads file or creates blank map -- called at beginning */
227 
228 void
init_grid(char * filename)229 init_grid (char *filename)
230 {
231   int i;			/* counter */
232 
233   if (filename)			/* file specified -- only false first
234                                    time if no command line options */
235     {
236       load_file (filename);
237     }
238 
239   else
240     {
241       grid.width = grid.height = 16;
242 
243       grid.data = (char *) g_malloc (grid.width * grid.height * sizeof (char));	/* grab memory */
244 
245       for (i = 0; i < grid.width * grid.height; i++)	/* initialise grid */
246 	{
247 	  grid.data[i] = SPACE;
248 	}
249     }
250 }
251 
252 /* callback for radio buttons -- changes tool */
253 
254 void
change_tool(GtkWidget * widget,gpointer data)255 change_tool (GtkWidget * widget, gpointer data)
256 {
257   /* can't just pass integer -- need GINT_TO_POINTER and converse */
258 
259   current_tool = GPOINTER_TO_INT (data);
260 }
261 
262 /* This creates a pixmap and a label from data -- returns hbox with contents */
263 
264 GtkWidget *
create_pixmap_with_label(GtkWidget * parent,GdkPixmap * pixmap,char * text)265 create_pixmap_with_label (GtkWidget * parent, GdkPixmap * pixmap, char *text)
266 {
267   GtkWidget *pixmapwid, *box, *label;	/* All variables -- easier
268                                            than doubling up */
269 
270   box = gtk_hbox_new (FALSE, 2);
271 
272   /* Create them */
273 
274   pixmapwid = gtk_pixmap_new (pixmap, NULL);
275   gtk_widget_show (pixmapwid);
276   label = gtk_label_new (text);
277   gtk_widget_show (label);
278 
279   /* Put them in the box */
280 
281   gtk_box_pack_start (GTK_BOX (box), pixmapwid, FALSE, FALSE, 4);
282   gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 4);
283   gtk_widget_show (box);
284 
285   return box;
286 }
287 
288 /* This creates a vbox with each tool in linked to specified function */
289 
290 GtkWidget *
create_toolbox_box(GtkWidget * parent,GtkSignalFunc func)291 create_toolbox_box (GtkWidget * parent, GtkSignalFunc func)
292 {
293   int i;			/* counter */
294   GtkWidget *box;		/* Box to return */
295   GtkWidget *button;		/* radio button for tools */
296   GtkWidget *pixmap;		/* Picture to display -- with label */
297 
298   box = gtk_vbox_new (FALSE, 2);
299 
300   for (i = 0, button = NULL; i < nblocks; i++)
301     {
302       pixmap = create_pixmap_with_label (parent, blocks[i].pixmap, blocks[i].name);	/* actually a box -- but contains a pixmap */
303 
304       /* create button radio group -- use ?: to prevent special case */
305 
306       button =
307 	gtk_radio_button_new (button ?
308 			      gtk_radio_button_group (GTK_RADIO_BUTTON
309 						      (button)) : NULL);
310 
311       gtk_container_add (GTK_CONTAINER (button), pixmap);
312 
313       gtk_signal_connect (GTK_OBJECT (button), "clicked",
314 			  GTK_SIGNAL_FUNC (func), GINT_TO_POINTER (i));
315 
316       gtk_box_pack_start (GTK_BOX (box), button, FALSE, FALSE, 3);
317 
318       gtk_widget_show (button);
319     }
320 
321   return box;			/* return a nice package */
322 }
323 
324 /* callback for clicking save button -- currently only way so it does
325    th work here */
326 
327 void
save_button(GtkWidget * widget,GtkEntry * textbox)328 save_button (GtkWidget * widget, GtkEntry * textbox)
329 {
330   /* textbox is passed as data in ...signal_connect */
331 
332   int i, j;			/* counters -- width & height */
333   FILE *fp;
334   char *filename;		/* obtained from "textbox" */
335 
336   if (!grid_window)		/* Don't save if they've closed the window */
337     {
338       return;
339     }
340 
341   /* Open and check file */
342 
343   filename = gtk_entry_get_text (GTK_ENTRY (textbox));
344   fp = fopen (filename, "w");
345 
346   if (!fp)			/* can't proceed */
347     {
348       message_box ("Error:", "Could not save file");
349       return;			/* Can't proceed */
350     }
351 
352   /* Write file */
353 
354   for (i = 0; i < grid.height; i++)
355     {
356       for (j = 0; j < grid.width; j++)
357 	fputc (blocks[(int) grid.data[i * grid.width + j]].save_char, fp);	/* put the save_char for correct "blocks" entry */
358 
359       fputc ('\n', fp);		/* newline */
360     }
361 
362   fclose (fp);
363 }
364 
365 /* actual function to load file -- don't put more spare \n's at end
366    than width -- it won't like it */
367 
368 void
load_file(char * filename)369 load_file (char *filename)
370 {
371   FILE *fp;
372   int length;			/* length of file */
373 
374   int ctr, i, first;		/* counter into data,
375 				   counter for finding correct block,
376 				   whether first line */
377 
378   char *data, c;		/* buffer and current character */
379 
380   /* standard open and check... */
381 
382   fp = fopen (filename, "r");
383 
384   if (!fp)
385     {
386       message_box ("Error", "Could not open file");
387       return;
388     }
389 
390   /* find file size  -- and allocation */
391 
392   fseek (fp, 0, SEEK_END);
393   length = ftell (fp);
394   rewind (fp);			/* for reading */
395 
396   data = (char *) g_malloc (length * sizeof (char));
397 
398   /* will be larger than necessary -- but only by (height) bytes
399      usually */
400 
401   ctr = 0;			/* index into "data" */
402 
403   first = 1;			/* first line */
404 
405   while ((c = fgetc (fp)) != EOF)
406     {
407       /* This is executed more frequently than strictly necessary
408          (ideally only on \n as well as normal) -- but this ensures
409          things work */
410 
411       data[ctr] = WALL;		/* Sensible default (used in xrobots
412                                    as default) */
413 
414       /* find correct block */
415 
416       for (i = 0; i < nblocks; i++)
417 	if (c == blocks[i].save_char)
418 	  {
419 	    data[ctr++] = i;
420 	    break;
421 	  }
422 
423       if (c == '\n' && first)	/* end of first line -- now know width */
424 	{
425 	  first = 0;		/* not first any more */
426 	  grid.width = ctr;
427 	}
428     }
429   fclose (fp);			/* Done here */
430 
431   grid.height = length / (grid.width + 1);	/* height * (width + newlines) == file size (roughly) */
432 
433   g_free (grid.data);		/* free the old... */
434 
435   grid.data = data;		/* ...and replace with new */
436 }
437 
438 /* Actual load button clicked -- calls load_file for hard work*/
439 
440 void
load_button(GtkWidget * widget,GtkEntry * textbox)441 load_button (GtkWidget * widget, GtkEntry * textbox)
442 {
443   char *filename;
444 
445   filename = gtk_entry_get_text (textbox);
446   load_file (filename);
447 
448   recreate_grid_window ();	/* destroys current window and creates new */
449 }
450 
451 /* Callback for Setting Size button */
452 
453 void
set_size(GtkWidget * widget,gpointer data)454 set_size (GtkWidget * widget, gpointer data)
455 {
456   int x, y, i;
457 
458   x = atoi (gtk_entry_get_text (width_text));
459   y = atoi (gtk_entry_get_text (height_text));
460   if (x > 0 && y > 0 && (x != grid.width || y != grid.height))
461     /* check data in boxes (don't change to same size as now) */
462     {
463       grid.width = x;
464       grid.height = y;
465 
466       if (grid.data)
467 	g_free (grid.data);
468 
469       grid.data =
470 	(char *) g_malloc (grid.width * grid.height * sizeof (char));
471 
472       for (i = 0; i < grid.width * grid.height; i++)	/* note that current grid is not saved or scaled */
473 	grid.data[i] = SPACE;
474 
475       recreate_grid_window ();	/* inform gtk of change */
476     }
477 }
478 
479 /* destroys current map window and creates new */
480 
481 void
recreate_grid_window()482 recreate_grid_window ()
483 {
484   if (grid_window)		/* just in case -- person could have
485                                    closed the window */
486     {
487       gtk_widget_destroy (grid_window);
488     }
489   grid_window = create_grid_window ();
490 }
491 
492 /* create box of functions -- very tedious */
493 
494 GtkWidget *
create_command_box(char * filename)495 create_command_box (char *filename)
496 {
497   char str[255];		/* to set text boxes based on
498                                    width/height -- I know it's
499                                    unlikely that it will be 255 chars
500                                    long */
501 
502   GtkWidget *vbox, *sizebox;	/* vbox == main box for widgets,
503                                    sizebox == box for Entrys & labels
504                                    for size */
505 
506   GtkWidget *button, *textbox;	/* generic button followed by textbox
507                                    for filename */
508 
509   GtkWidget *label, *xsize, *ysize;	/* stuff for setting size */
510 
511   vbox = gtk_vbox_new (FALSE, 3);	/* create main box */
512 
513   /* create filename textentry */
514 
515   textbox = gtk_entry_new ();
516 
517   gtk_entry_set_text (GTK_ENTRY (textbox),
518 		      filename ? filename : "newmap.map");
519 
520   gtk_widget_show (textbox);
521 
522   gtk_box_pack_start (GTK_BOX (vbox), textbox, FALSE, FALSE, 2);
523 
524   /* create save button & link with callback */
525 
526   button = gtk_button_new_with_label ("Save");
527 
528   gtk_signal_connect (GTK_OBJECT (button), "clicked",
529 		      GTK_SIGNAL_FUNC (save_button), textbox);	/* note it gets the filename as data */
530 
531   gtk_widget_show (button);
532 
533   gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 2);
534 
535   /* create the load button & link */
536 
537   button = gtk_button_new_with_label ("Load");
538 
539   gtk_signal_connect (GTK_OBJECT (button), "clicked",
540 		      GTK_SIGNAL_FUNC (load_button), textbox);	/* Also gets callback */
541 
542   gtk_widget_show (button);
543 
544   gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 2);
545 
546   /* create row for size controls -- except actual button */
547 
548   sizebox = gtk_hbox_new (FALSE, 1);
549 
550   /* X label & text */
551 
552   label = gtk_label_new ("X:");
553   gtk_widget_show (label);
554 
555   gtk_box_pack_start (GTK_BOX (sizebox), label, FALSE, FALSE, 1);
556 
557   xsize = gtk_entry_new_with_max_length (3);
558   width_text = GTK_ENTRY (xsize);
559   sprintf (str, "%d", grid.width);
560   gtk_entry_set_text (GTK_ENTRY (xsize), str);
561   gtk_widget_set_usize (xsize, 30, 20);
562   gtk_widget_show (xsize);
563 
564   gtk_box_pack_start (GTK_BOX (sizebox), xsize, FALSE, FALSE, 1);
565 
566   /* Y label && text */
567 
568   label = gtk_label_new ("Y:");
569   gtk_widget_show (label);
570 
571   gtk_box_pack_start (GTK_BOX (sizebox), label, FALSE, FALSE, 1);
572 
573   ysize = gtk_entry_new_with_max_length (3);
574   height_text = GTK_ENTRY (ysize);
575   sprintf (str, "%d", grid.height);
576   gtk_entry_set_text (GTK_ENTRY (ysize), str);
577   gtk_widget_set_usize (ysize, 30, 20);
578   gtk_widget_show (ysize);
579 
580   gtk_box_pack_start (GTK_BOX (sizebox), ysize, FALSE, FALSE, 1);
581 
582   gtk_widget_show (sizebox);
583   gtk_box_pack_start (GTK_BOX (vbox), sizebox, FALSE, FALSE, 2);
584 
585   /* Done size controls */
586 
587   /* Actual size button */
588 
589   button = gtk_button_new_with_label ("Set Size");
590 
591   gtk_signal_connect (GTK_OBJECT (button), "clicked",
592 		      GTK_SIGNAL_FUNC (set_size), NULL);
593 
594   gtk_widget_show (button);
595 
596   gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 2);
597 
598   return vbox;			/* return nice simple package */
599 }
600 
601 /* This creates and initialises the toolbox window (actually the MAIN
602    WINDOW)*/
603 
604 GtkWidget *
create_toolbox_window(char * filename)605 create_toolbox_window (char *filename)
606 {
607   GtkWidget *window;		/* main window */
608 
609   GtkWidget *toolbox, *command_box, *hbox;	/* three important components (hbox to store others) */
610 
611   grid_window = create_grid_window ();	/* map window controlled by
612                                            this -- needs to be able to
613                                            load */
614 
615   /* Create our window -- its closure causes the app to quit... */
616 
617   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
618   gtk_window_set_title (GTK_WINDOW (window), "Toolbox");
619 
620   gtk_signal_connect (GTK_OBJECT (window), "delete_event",
621 		      GTK_SIGNAL_FUNC (gtk_exit), NULL);	/* ... as setup on this line */
622 
623   /* create and display two panes of display */
624 
625   toolbox = create_toolbox_box (window, change_tool);
626   gtk_widget_show (toolbox);
627   command_box = create_command_box (filename);
628   gtk_widget_show (command_box);
629 
630   /* join two panes together */
631 
632   hbox = gtk_hbox_new (FALSE, 5);
633   gtk_box_pack_start (GTK_BOX (hbox), toolbox, FALSE, FALSE, 5);
634   gtk_box_pack_start (GTK_BOX (hbox), command_box, FALSE, FALSE, 5);
635   gtk_widget_show (hbox);
636   gtk_container_add (GTK_CONTAINER (window), hbox);
637 
638   /* show window */
639 
640   gtk_widget_show (window);
641   return window;		/* give it back to main */
642 }
643 
644 /* receives position to change -- puts correct pixmap in place */
645 
646 /* See Fixed(Wibble1) in create_grid_window for description of fixed */
647 
648 void
update_grid(GtkFixed * fixed,int x,int y)649 update_grid (GtkFixed * fixed, int x, int y)
650 {
651   int length, i, dx, dy;	/* length == how many children are in fixed,
652 				   i == counter for iteration,
653 				   dx, dy == How far apart the current position is from the desired one */
654 
655   GList *children;		/* children of fixed -- i.e. the
656                                    pixmap widgets */
657 
658   GtkFixedChild *child;		/* individual child of fixed */
659 
660   grid.data[x + y * grid.width] = current_tool;
661 
662   /* initialise for search */
663 
664   children = fixed->children;
665   length = g_list_length (children);
666 
667   /* start finding -- this is a horrible kludge, but the simplest to
668      code */
669 
670   for (i = 0; i < length; i++)
671     {
672       child = (GtkFixedChild *) g_list_nth_data (children, i);	/* get current thing */
673 
674       dx = x - (child->x / IMGSIZE);	/* position in pixels -- must be changed to reality */
675 
676       dy = y - (child->y / IMGSIZE);
677 
678       if (dx == 0 && dy == 0)	/* if correct position */
679 	{
680 	  gtk_pixmap_set (GTK_PIXMAP (child->widget),
681 			  blocks[current_tool].pixmap, NULL);	/* change the pixmap */
682 
683 	  break;		/* and quit -- don't compound folly */
684 	}
685     }
686 }
687 
688 /* handler for drawing -- set dragging true */
689 
690 void
button_press(GtkFixed * fixed,GdkEventButton * event,gpointer data)691 button_press (GtkFixed * fixed, GdkEventButton * event, gpointer data)
692 {
693   int i, j;			/* positions */
694 
695   dragging = 1;
696 
697   i = (int) (event->x / IMGSIZE);
698   j = (int) (event->y / IMGSIZE);
699 
700   update_grid (fixed, i, j);	/* set current square so user doesn't
701                                    have to move mouse */
702 }
703 
704 /* callback for mouse moved on map window -- checks dragging */
705 
706 /* This is not ideal as it puts extra load on the cpu when the mouse
707    is moving in the map window */
708 
709 void
mouse_move(GtkFixed * fixed,GdkEventMotion * event,gpointer data)710 mouse_move (GtkFixed * fixed, GdkEventMotion * event, gpointer data)
711 {
712   int i, j;
713 
714   i = (int) (event->x / IMGSIZE);
715   j = (int) (event->y / IMGSIZE);
716   if (dragging)
717     update_grid (fixed, i, j);
718 }
719 
720 /* stop drawing */
721 
722 void
button_release(GtkFixed * fixed,GdkEventButton * event,gpointer data)723 button_release (GtkFixed * fixed, GdkEventButton * event, gpointer data)
724 {
725   dragging = 0;
726 }
727 
728 /* called when user closes window -- sets grid_window to NULL to
729    prevent recreate_grid_window segfaulting */
730 
731 void
destroy_func(GtkWidget * widget,gpointer data)732 destroy_func (GtkWidget * widget, gpointer data)
733 {
734   *((GtkWidget **) data) = NULL;
735   grid_window = NULL;
736 }
737 
738 /* Does the donkeywork in creating the map view */
739 
740 GtkWidget *
create_grid_window()741 create_grid_window ()
742 {
743   int i, j, type;		/* i, j for iteration through "grid.data",
744 				   type == index into blocks -- stored
745 				   for convenience */
746 
747   GtkWidget *window;		/* actual window */
748   GtkWidget *fixed;		/* layout manager for pictures */
749 
750   GtkWidget *pixmap;		/* temporary sotrage for each picture
751                                    while in use */
752 
753   /* first create and set up main features */
754 
755   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
756   gtk_signal_connect (GTK_OBJECT (window), "destroy",
757 		      GTK_SIGNAL_FUNC (destroy_func), &window);
758   gtk_window_set_title (GTK_WINDOW (window), "Map View");
759 
760   /* Fixed(Wibble1):
761      fixed is a method of storing widgets by position,
762      1. I create a sequence of pixmap widgets and place them in correct positions,
763      2. I initialise them to the correct picture,
764      3. I link the fixed's press, motion & release events to something to find which
765      pixmap is under pointer and change picture & "grid" */
766 
767   fixed = gtk_fixed_new ();
768 
769   for (i = 0; i < grid.width; i++)
770     for (j = 0; j < grid.height; j++)
771       {
772 	type = grid.data[i + j * grid.width];
773 	pixmap = gtk_pixmap_new (blocks[type].pixmap, NULL);
774 
775 	gtk_fixed_put (GTK_FIXED (fixed), pixmap, i * IMGSIZE, j * IMGSIZE);	/* position */
776 
777 	gtk_widget_show (pixmap);
778       }
779 
780   /* enable events needed... */
781 
782   gtk_widget_set_events (fixed,
783 			 GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK |
784 			 GDK_BUTTON_RELEASE_MASK);
785 
786   /* ... and take control */
787 
788   gtk_signal_connect (GTK_OBJECT (fixed), "button_press_event",
789 		      GTK_SIGNAL_FUNC (button_press), NULL);
790 
791   gtk_signal_connect (GTK_OBJECT (fixed), "motion_notify_event",
792 		      GTK_SIGNAL_FUNC (mouse_move), NULL);
793 
794   gtk_signal_connect (GTK_OBJECT (fixed), "button_release_event",
795 		      GTK_SIGNAL_FUNC (button_release), NULL);
796 
797   gtk_widget_show (fixed);
798 
799   gtk_container_add (GTK_CONTAINER (window), fixed);	/* window only contains pictures */
800 
801   gtk_widget_show (window);
802 
803   return window;		/* give the window back to
804                                    create_toolbox_window */
805 }
806 
807 /* Free all the rubbish */
808 
809 void
freestuff()810 freestuff ()
811 {
812   g_free (grid.data);
813 }
814 
815 /* Doesn't look very nice but is functional (message box and code) */
816 
817 void
message_box(char * title,char * text)818 message_box (char *title, char *text)
819 {
820   GtkWidget *label, *window, *button, *box;
821 
822   window = gtk_window_new (GTK_WINDOW_DIALOG);	/* make window */
823 
824   gtk_window_set_title (GTK_WINDOW (window), title);
825 
826   label = gtk_label_new (text);	/* Label as required */
827 
828   gtk_widget_show (label);
829 
830   button = gtk_button_new_with_label ("OK");	/* Just an OK button
831                                                    that kills the
832                                                    window */
833 
834   gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
835 			     GTK_SIGNAL_FUNC (gtk_widget_destroy),
836 			     (gpointer) window);
837 
838   gtk_widget_show (button);
839 
840   box = gtk_vbox_new (FALSE, 3);	/* Put everything in... */
841 
842   gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 3);
843   gtk_box_pack_start (GTK_BOX (box), button, FALSE, FALSE, 3);
844 
845   gtk_widget_show (box);
846 
847   gtk_container_add (GTK_CONTAINER (window), box);
848 
849   gtk_widget_show (window);	/* ... and show */
850 
851   /* Don't return anything --  no interaction necessary */
852 }
853