root/trunk/src/gtk/main-gtk.c

Revision 1024, 61.4 kB (checked in by shanoah, 1 month ago)

Install the icon direcory, and add ANGBAND_DIR_XTRA_* to variable.c, so every port doesn't have to individually assign them.

  • Property svn:eol-style set to native
Line 
1 /*
2  * File: main-gtk.c
3  * Purpose: GTK port for Angband
4  *
5  * Copyright (c) 2000-2007 Robert Ruehlmann, Shanoah Alkire
6  *
7  * This work is free software; you can redistribute it and/or modify it
8  * under the terms of either:
9  *
10  * a) the GNU General Public License as published by the Free Software
11  *    Foundation, version 2, or
12  *
13  * b) the "Angband licence":
14  *    This software may be copied and distributed for educational, research,
15  *    and not for profit purposes provided that this copyright and statement
16  *    are included in all such copies.  Other copyrights may also apply.
17  */
18  
19 #include "angband.h"
20
21 #ifdef USE_GTK
22 #include "main-gtk.h"
23 /*
24  *Add a bunch of debugger message, to trace where problems are.
25  */
26 /*#define GTK_DEBUG*/
27
28 /*
29  *Write all debugger messages to the command line, as well as the debugger window.
30 */
31 #define VERBOSE_DEBUG
32
33 #ifdef GTK_DEBUG
34 static void surface_status(cptr function_name, term_data *td)
35 {
36         if (td->surface == NULL)
37                 glog("No Surface for %s in '%s'", function_name, td->name);
38         else
39                 if (cairo_surface_status(td->surface) != CAIRO_STATUS_SUCCESS)
40                         glog("%s: Surface status for '%s': %s",function_name,td->name, cairo_status_to_string(cairo_surface_status(td->surface)));
41 }
42 #endif
43
44 /*#define USE_GTK_BUILDER*/
45
46 #ifdef USE_GTK_BUILDER
47 #define GTK_XML "angband.xml"
48
49 static GtkWidget *get_widget(GtkBuilder *xml, cptr name)
50 {
51         return GTK_WIDGET(gtk_builder_get_object(xml, name));
52 }
53
54 static GtkBuilder *get_gtk_xml(cptr buf, cptr secondary)
55 {
56         GtkBuilder *xml;
57         int e = 0;
58        
59         xml = gtk_builder_new();
60         e = gtk_builder_add_from_file (xml, buf, NULL);
61        
62         return xml;
63 }
64
65 #else
66 #define GTK_XML "angband.glade"
67
68 static GtkWidget *get_widget(GladeXML *xml, cptr name)
69 {
70         return GTK_WIDGET(glade_xml_get_widget(xml, name));
71 }
72
73 static GladeXML *get_gtk_xml(cptr buf, cptr secondary)
74 {
75         return glade_xml_new(buf, secondary, NULL);
76 }
77 #endif
78
79 static void recreate_surface(term_data *td)
80 {
81         if ((td->drawing_area != NULL) && (cairo_surface_get_reference_count(td->surface) > 0))
82         {
83                 cairo_surface_destroy(td->surface);
84         }
85        
86         td->surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, td->size.w, td->size.h);
87         set_term_matrix(td);   
88 }
89
90 static void set_term_menu(int number, bool value)
91 {
92         term_data *td = &data[number];
93        
94         if (td->menu_item != NULL)
95                 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(td->menu_item), value);
96
97 }
98
99 static bool get_term_menu(int number)
100 {
101         bool value = FALSE;
102         term_data *td = &data[number];
103        
104         if (td->menu_item != NULL)
105                 value = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(td->menu_item));
106        
107         return value;
108
109 }
110
111 static int xtra_window_from_name(cptr s)
112 {
113         xtra_win_data *xd;
114         int i = 0,t = -1;
115        
116         for (i = 0; i < MAX_XTRA_WIN_DATA; i++)
117         {
118                 xd = &xdata[i];
119                 if (GTK_IS_WIDGET(xd->win) && (xd->win != NULL) && (s == gtk_widget_get_name(xd->win)) )
120                         t = i;
121         }
122         return(t);
123 }
124
125 static int xtra_window_from_drawing_area(cptr s)
126 {
127         xtra_win_data *xd;
128         int i = 0,t = -1;
129         for (i = 0; i < MAX_XTRA_WIN_DATA; i ++)
130         {
131                 xd = &xdata[i];
132                 if ((xd->drawing_area != NULL) && (s == gtk_widget_get_name(xd->drawing_area))) t = i;
133         }
134         return(t);
135 }
136
137 static int xtra_window_from_widget(GtkWidget *widget)
138 {
139         return xtra_window_from_name(gtk_widget_get_name(widget));
140 }
141
142 static int td_from_name(cptr s, cptr pattern)
143 {
144         int t = -1;
145         sscanf(s, pattern, &t);
146         return(t);
147 }
148 static int term_window_from_name(cptr s)
149 {
150         return td_from_name(s, "term_window_%d");
151 }
152 static int td_from_widget(GtkWidget *widget, cptr pattern)
153 {
154         return td_from_name(gtk_widget_get_name(widget), pattern);
155 }
156
157 static int term_window_from_widget(GtkWidget *widget)
158 {
159         return term_window_from_name(gtk_widget_get_name(widget));
160 }
161
162 static int max_win_width(term_data *td)
163 {
164         return (255 * td->font.w);
165 }
166
167 static int max_win_height(term_data *td)
168 {
169         return(255 * td->font.h);
170 }
171
172 static int row_in_pixels(term_data *td, int x)
173 {
174         return(x * td->font.w);
175 }
176
177 static int col_in_pixels(term_data *td, int y)
178 {
179         return(y * td->font.h);
180 }
181
182 /*
183  * Find the square a particular pixel is part of.
184  */
185 static void pixel_to_square(int * const x, int * const y, const int ox, const int oy)
186 {
187         term_data *td = (term_data*)(Term->data);
188
189         (*x) = (ox / td->font.w);
190         (*y) = (oy / td->font.h);
191 }
192
193 void set_term_matrix(term_data *td)
194 {
195         /* Get a matrix set up to scale the graphics. */
196         if ((td->surface != NULL) && (td->visible))
197                 matrix = cairo_font_scaling(td->surface, td->tile.w, td->tile.h, td->actual.w, td->actual.h);
198 }
199
200 gboolean on_big_tiles(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
201 {
202         term_data *td = &data[0];
203        
204         if ((widget != NULL) && (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))))
205                 use_bigtile = FALSE;
206         else
207                 use_bigtile = TRUE;
208        
209         load_font_by_name(td, td->font.name);
210         term_data_redraw(td);
211         return(TRUE);
212 }
213
214 static GdkRectangle cairo_rect_to_gdk(cairo_rectangle_t *r)
215 {
216         GdkRectangle s;
217        
218         s.x = r->x;
219         s.y = r->y;
220         s.width = r->width;
221         s.height = r->height;
222        
223         return(s);
224 }
225
226 static cairo_rectangle_t gdk_rect_to_cairo(GdkRectangle *r)
227 {
228         cairo_rectangle_t s;
229        
230         s.x = r->x;
231         s.y = r->y;
232         s.width = r->width;
233         s.height = r->height;
234        
235         return(s);
236 }
237
238 static void invalidate_drawing_area(GtkWidget *widget, cairo_rectangle_t r)
239 {
240         GdkRectangle s;
241        
242         s = cairo_rect_to_gdk(&r);
243         if (widget->window != NULL)
244         {
245                 gdk_window_invalidate_rect(widget->window, &s, TRUE);
246         }
247 }
248
249 void set_row_and_cols(term_data *td)
250 {
251         int cols = td->cols;
252         int rows = td->rows;
253        
254         td->cols = td->size.w / td->font.w;
255         td->rows= td->size.h / td->font.h;
256        
257         if (MAIN_WINDOW(td))
258         {
259                 if (td->cols < 80) td->cols = cols;
260                 if (td->rows < 24) td->rows = rows;
261         }
262        
263         if ((cols != td->cols) || (rows != td->rows))
264         {
265                 recreate_surface(td);
266                 term_data_redraw(td);
267         }
268 }
269 static void redraw_term(term_data *td)
270 {               
271         set_window_size(td);
272                
273         recreate_surface(td);
274         term_data_redraw(td);
275         force_redraw();
276 }
277
278 gboolean set_term_row(GtkWidget *widget, GdkEventConfigure *event, gpointer user_data)
279 {
280         int t = td_from_widget(widget, "row_%d");
281        
282         if (t != -1)
283         {
284                 term_data *td = &data[t];
285                 td->rows = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget));
286                 redraw_term(td);
287         }
288         return(FALSE);
289 }
290
291 gboolean set_term_col(GtkWidget *widget, GdkEventConfigure *event, gpointer user_data)
292 {
293         int t = td_from_widget(widget, "col_%d");
294        
295         if (t != -1)
296         {
297                 term_data *td = &data[t];
298                 td->cols = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget));
299                 redraw_term(td);
300         }
301         return(FALSE);
302 }
303
304 /*
305  * Get the position of the window and save it.
306  */
307 gboolean configure_event_handler(GtkWidget *widget, GdkEventConfigure *event, gpointer user_data)
308 {
309         int t = -1;
310         term_data *td;
311        
312         t = term_window_from_widget(widget);
313        
314         if (t != -1)
315         {
316                 td = &data[t];
317                
318                 if (td->initialized)
319                 {
320                         if (event->x != 0) td->location.x = event->x;
321                         if (event->y != 0) td->location.y = event->y;
322                         if (event->width != 0)  td->size.w = event->width;
323                         if (event->height != 0) td->size.h = event->height;
324                 }
325         }
326         return(FALSE);
327 }
328
329 gboolean xtra_configure_event_handler(GtkWidget *widget, GdkEventConfigure *event, gpointer user_data)
330 {
331         xtra_win_data *xd;
332         int t;
333        
334         t = xtra_window_from_widget(widget);
335         if (t != -1)
336         {
337                 int x = 0, y = 0, w = 0, h = 0;
338                 GdkRectangle r;
339                
340                 xd = &xdata[t];
341        
342                 if (GTK_WIDGET_VISIBLE(xd->win))
343                 {
344                         gdk_window_get_frame_extents(xd->win->window, &r);
345                         x = r.x;
346                         y = r.y;
347                        
348                         gtk_window_get_size(GTK_WINDOW(xd->win), &w, &h);
349                
350                         if (x != 0)  xd->location.x = x;
351                         if (y != 0)  xd->location.y = y;
352                         if (w != 0) xd->size.w = w;
353                         if (h != 0)  xd->size.h = h;
354                
355                         xd->visible = TRUE;
356                 }
357                 else
358                         xd->visible = FALSE;
359         }
360         return(FALSE);
361 }
362
363 static void set_window_defaults(term_data *td)
364 {
365         GdkGeometry geo;
366        
367         geo.width_inc = td->font.w;
368         geo.height_inc = td->font.h;
369        
370         geo.max_width =(td->font.w * td->cols);
371         geo.max_height = (td->font.h * td->rows);
372        
373         if (MAIN_WINDOW(td))
374         {
375                 geo.min_width = geo.max_width;
376                 geo.min_height = geo.max_height;
377         }
378         else
379         {
380                 geo.min_width = td->font.w;
381                 geo.min_height = td->font.h;
382         }
383        
384         gtk_window_set_geometry_hints(GTK_WINDOW(td->win), td->drawing_area, &geo,
385         GDK_HINT_RESIZE_INC | GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE);
386 }
387
388 static void set_window_size(term_data *td)
389 {
390         set_window_defaults(td);
391         gtk_window_set_position(GTK_WINDOW(td->win),GTK_WIN_POS_NONE);
392        
393         if MAIN_WINDOW(td)
394         {
395                 gtk_widget_set_size_request(GTK_WIDGET(td->drawing_area), td->size.w, td->size.h);
396         }
397        
398         gtk_window_resize(GTK_WINDOW(td->win), td->size.w, td->size.h);
399        
400         gtk_window_move(GTK_WINDOW(td->win), td->location.x, td->location.y);
401        
402         gtk_window_get_size(GTK_WINDOW(td->win), &td->size.w, &td->size.h);
403 }
404
405 static void set_xtra_window_size(xtra_win_data *xd)
406 {
407         gtk_window_set_position(GTK_WINDOW(xd->win),GTK_WIN_POS_NONE);
408         if ((xd->size.w > 0) && (xd->size.h > 0))
409                 gtk_window_resize(GTK_WINDOW(xd->win), xd->size.w, xd->size.h);
410         gtk_window_move(GTK_WINDOW(xd->win), xd->location.x, xd->location.y);
411 }
412
413 int button_add_gui(const char *label, unsigned char keypress)
414 {
415         int i=0;
416         bool assigned = FALSE;
417         GtkWidget *widget;
418         int length = strlen(label);
419        
420         /*static GtkWidget *toolbar;
421         static GtkWidget *toolbar_items[12];
422         static int toolbar_size;
423         static unsigned char toolbar_keypress[12];*/
424        
425
426         /* Check we haven't already got a button for this keypress */
427         for (i=0; i < toolbar_size; i++)
428         {
429                 if (toolbar_keypress[i] == keypress)
430                         assigned = TRUE;
431         }
432         /* Make the button */
433         if (!assigned)
434         {
435                 widget = gtk_button_new_with_label(label);
436                 toolbar_size++;
437                 toolbar_keypress[toolbar_size]=keypress;
438                 toolbar_items[toolbar_size] = widget;
439                 /*gtk_container_add(toolbar, widget);*/
440         }
441
442         /* Return the size of the button */
443         return (length);
444 }
445
446 int button_kill_gui(unsigned char keypress)
447 {
448         /*int length;*/
449
450         /* Find the button */
451
452         /* No such button */
453         if (0)
454         {
455                 return 0;
456         }
457
458         /* Find the length */
459
460         /* Move each button up one */
461
462         /* Wipe the data */
463
464         /* Redraw */
465         p_ptr->redraw |= (PR_BUTTONS);
466         redraw_stuff();
467
468         /* Return the size of the button */
469         return 0 /*(length)*/;
470 }
471 /*
472  * Erase the whole term.
473  */
474 static errr Term_clear_gtk(void)
475 {
476         term_data *td = (term_data*)(Term->data);
477         cairo_rectangle_t r;
478        
479         init_cairo_rect(&r, 0, 0, max_win_width(td), max_win_height(td));
480         cairo_clear(td->surface, r, TERM_DARK);
481         invalidate_drawing_area(td->drawing_area, r);
482
483         return (0);
484 }
485
486 /*
487  * Erase some characters.
488  */
489 static errr Term_wipe_gtk(int x, int y, int n)
490 {
491         term_data *td = (term_data*)(Term->data);
492         cairo_rectangle_t r;
493        
494         init_cairo_rect(&r, (td->actual.w * x), (td->actual.h * y), (td->actual.w * n), (td->actual.h));
495         cairo_clear(td->surface, r, TERM_DARK);
496         invalidate_drawing_area(td->drawing_area, r);
497
498         return (0);
499 }
500
501 /*
502  * Draw some textual characters.
503  */
504 static errr Term_text_gtk(int x, int y, int n, byte a, cptr s)
505 {
506         term_data *td = (term_data*)(Term->data);
507         cairo_rectangle_t r;
508        
509         init_cairo_rect(&r, (td->font.w * x), (td->font.h * y),  (td->font.w * n), td->font.h);
510         draw_text(td->surface, &td->font, &td->actual, x, y, n, a, s);
511         invalidate_drawing_area(td->drawing_area, r);
512                        
513         return (0);
514 }
515
516 static errr Term_pict_gtk(int x, int y, int n, const byte *ap, const char *cp, const byte *tap, const char *tcp)
517 {
518         term_data *td = (term_data*)(Term->data);
519         cairo_rectangle_t r;
520        
521         init_cairo_rect(&r, (td->actual.w * x), (td->actual.h * y),  (td->actual.w * n), (td->actual.h));
522         draw_tiles(td->surface, x, y, n, ap, cp, tap, tcp, &td->font, &td->actual, &td->tile);
523         invalidate_drawing_area(td->drawing_area, r);
524                        
525         return (0);
526 }
527
528 static errr Term_flush_gtk(void)
529 {
530         gdk_flush();
531         return (0);
532 }
533
534 static errr Term_fresh_gtk(void)
535 {
536         term_data *td = (term_data*)(Term->data);
537         cairo_rectangle_t r;
538        
539         init_cairo_rect(&r, 0, 0, max_win_width(td), max_win_height(td));
540         invalidate_drawing_area(td->drawing_area, r);
541         gdk_window_process_updates(td->win->window, 1);
542         return (0);
543 }
544
545 /*
546  * Handle a "special request"
547  */
548 static errr Term_xtra_gtk(int n, int v)
549 {
550         /* Handle a subset of the legal requests */
551         switch (n)
552         {
553                 /* Make a noise */
554                 case TERM_XTRA_NOISE:
555                         return (0);
556
557                 /* Flush the output */
558                 case TERM_XTRA_FRESH:
559                         return (Term_fresh_gtk());
560
561                 /* Process random events */
562                 case TERM_XTRA_BORED:
563                         return (CheckEvent(0));
564
565                 /* Process Events */
566                 case TERM_XTRA_EVENT:
567                         return (CheckEvent(v));
568
569                 /* Flush the events */
570                 case TERM_XTRA_FLUSH:
571                         return (Term_flush_gtk());
572
573                 /* Handle change in the "level" */
574                 case TERM_XTRA_LEVEL:
575                         return (0);
576
577                 /* Clear the screen */
578                 case TERM_XTRA_CLEAR:
579                         return (Term_clear_gtk());
580
581                 /* Delay for some milliseconds */
582                 case TERM_XTRA_DELAY:
583                         if (v > 0) usleep(1000 * v);
584                         return (0);
585
586                 /* React to changes */
587                 case TERM_XTRA_REACT:
588                         return (0);
589         }
590
591         /* Unknown */
592         return (1);
593 }
594
595 static errr Term_curs_gtk(int x, int y)
596 {
597         term_data *td = (term_data*)(Term->data);
598         cairo_rectangle_t r;
599        
600         init_cairo_rect(&r, row_in_pixels(td, x)+1, col_in_pixels(td, y) + 1,  (td->actual.w) - 2, (td->actual.h) - 2);
601         cairo_cursor(td->surface, r, TERM_SLATE);
602         invalidate_drawing_area(td->drawing_area, r);
603        
604         return (0);
605 }
606
607 /*
608  * Hack -- redraw a term_data.
609  * Note that "Term_redraw()" calls "TERM_XTRA_CLEAR"
610  */
611 static void term_data_redraw(term_data *td)
612 {
613         term *old = Term;
614        
615         /* Activate the term passed to it, not term 0! */
616         Term_activate(&td->t);
617
618         Term_resize(td->cols, td->rows);
619         Term_redraw();
620         Term_fresh();
621        
622         Term_activate(old);
623 }
624
625 static void force_redraw()
626 {
627         if (game_in_progress)
628         {
629                 reset_visuals(TRUE);
630                 Term_key_push(KTRL('R'));
631         }
632 }
633 static errr CheckEvent(bool wait)
634 {
635         if (wait)
636                 gtk_main_iteration();
637         else
638                 while (gtk_events_pending())
639                         gtk_main_iteration();
640
641         return (0);
642 }
643
644 static bool save_game_gtk(void)
645 {
646         if (game_in_progress && character_generated)
647         {
648                 if (!inkey_flag)
649                 {
650                         glog( "You may not do that right now.");
651                         return(FALSE);
652                 }
653
654                 /* Hack -- Forget messages */
655                 msg_flag = FALSE;
656                
657                 /* Save the game */
658                 do_cmd_save_game();
659         }
660        
661         return(TRUE);
662 }
663
664 static void hook_quit(cptr str)
665 {
666         gtk_log_fmt(TERM_RED,"%s", str);
667         save_prefs();
668         release_memory();
669         exit(0);
670 }
671
672 gboolean quit_event_handler(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
673 {
674         if (save_game_gtk())
675         {
676                 save_prefs();
677                 quit(NULL);
678                 exit(0);
679                 return(FALSE);
680         }
681         else
682                 return(TRUE);
683 }
684
685 gboolean destroy_event_handler(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
686 {
687         quit(NULL);
688         exit(0);
689         return(FALSE);
690 }
691
692 gboolean new_event_handler(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
693 {
694         if (game_in_progress)
695         {
696                 glog( "You can't start a new game while you're still playing!");
697                 return(TRUE);
698         }
699         else
700         {
701                 /* We'll return NEWGAME to the game. */
702                 cmd.command = CMD_NEWGAME;
703                 gtk_widget_set_sensitive(widget, FALSE);
704                 return(FALSE);
705         }
706 }
707
708 static void load_font_by_name(term_data *td, cptr font_name)
709 {       
710         PangoFontDescription *temp_font;
711         char buf[80];
712         unsigned int i, j = 0;
713
714         my_strcpy(td->font.name, font_name, sizeof(td->font.name));
715
716         for (i = 0; i < strlen(font_name); i++)
717                 if (font_name[i] == ' ')
718                         j = i;
719        
720         for (i = j; i < strlen(font_name); i++)
721                 buf[i - j] = font_name[i];
722
723         temp_font = pango_font_description_from_string(font_name);
724         my_strcpy(td->font.family, pango_font_description_get_family(temp_font), sizeof(td->font.family));
725         td->font.size = atof(buf);
726         if (td->font.size == 0) td->font.size = 12;
727         get_font_size(&td->font);
728        
729         if (use_bigtile)
730         {
731                 td->actual.w = td->font.w * 2;
732                 td->actual.h = td->font.h;
733         }
734         else
735         {
736                 td->actual.w = td->font.w;
737                 td->actual.h = td->font.h;
738         }
739        
740         td->size.w = td->cols * td->font.w;
741         td->size.h = td->rows * td->font.h;
742        
743         if ((td->initialized == TRUE) && (td->win != NULL)  && (td->visible))
744         {
745                 /* Get a matrix set up to scale the graphics. */
746                 set_term_matrix(td);
747                
748                 gtk_widget_hide(td->win);
749                 set_window_size(td);
750                 gtk_widget_show(td->win);
751                
752                 term_data_redraw(td);
753         }
754 }
755
756 void set_term_font(GtkFontButton *widget, gpointer user_data)   
757 {
758         term_data *td;
759
760         const char *font_name;
761         const char *s;
762         int t;
763        
764         s = gtk_widget_get_name(GTK_WIDGET(widget));
765         sscanf(s, "term_font_%d", &t);
766
767         td = &data[t];
768
769         font_name = gtk_font_button_get_font_name(widget);
770         load_font_by_name(td, font_name);
771 }
772
773 void set_xtra_font(GtkFontButton *widget, gpointer user_data)   
774 {
775         const char *temp, *s;
776         int i, t = 0;
777         xtra_win_data *xd;
778        
779         s = gtk_widget_get_name(GTK_WIDGET(widget));
780        
781         for (i = 0; i < MAX_XTRA_WIN_DATA; i ++)
782         {
783                 xtra_win_data *xd = &xdata[i];
784                 if (s == xd->font_button_name) t = i;
785         }
786        
787         xd=&xdata[t];
788        
789         temp = gtk_font_button_get_font_name(widget);
790         my_strcpy(xd->font.name, temp, sizeof(xd->font.name));
791         get_font_size(&xd->font);
792         if (xd->text_view != NULL)
793                 gtk_widget_modify_font(GTK_WIDGET(xd->text_view), pango_font_description_from_string(xd->font.name));
794        
795         if ((xd->surface != NULL) && (xd->event != 0))
796         {
797                 event_signal(xd->event);
798         }
799 }
800
801 /*** Callbacks: savefile opening ***/
802
803 /* Filter function for the savefile list */
804 static gboolean file_open_filter(const GtkFileFilterInfo *filter_info, gpointer data)
805 {
806         const char *name = filter_info->display_name;
807
808         (void)data;
809
810         /* Count out known non-savefiles */
811         if (!strcmp(name, "Makefile") ||
812             !strcmp(name, "delete.me"))
813         {
814                 return FALSE;
815         }
816
817         /* Let it pass */
818         return TRUE;
819 }
820
821 /*
822  * Open/Save Dialog box
823  */
824 static bool save_dialog_box(bool save)
825 {
826         GtkFileFilter *filter;
827         GtkWidget *selector_wid;
828         GtkFileChooser *selector;
829         bool accepted;
830        
831         char buf[1024];
832         const char *filename;
833
834         /* Forget it if the game is in progress */
835         /* XXX Should disable the menu entry XXX */
836         if (game_in_progress && !save)
837         {
838                 glog( "You can't open a new game while you're still playing!");
839                 return(FALSE);
840         }
841                        
842         if ((!game_in_progress || !character_generated) && save)
843         {
844                 glog( "You can't save a new game unless you're still playing!");
845                 return(FALSE);
846         }
847
848         if (!inkey_flag && save)
849         {
850                 glog( "You may not do that right now.");
851                 return(FALSE);
852         }
853        
854         /* Create a new file selector dialogue box, with no parent */
855         if (!save)
856         {
857         selector_wid = gtk_file_chooser_dialog_new("Select a savefile", NULL,
858                                                GTK_FILE_CHOOSER_ACTION_OPEN,
859                                                GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
860                                                GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
861                                                NULL);
862         }
863         else
864         {
865         selector_wid = gtk_file_chooser_dialog_new("Save your game", NULL,
866                                                GTK_FILE_CHOOSER_ACTION_SAVE,
867                                                GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
868                                                GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
869                                                NULL);
870         gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (selector_wid), op_ptr->full_name);
871         }
872         /* For convenience */
873         selector = GTK_FILE_CHOOSER(selector_wid);
874         gtk_file_chooser_set_do_overwrite_confirmation (selector, TRUE);;
875        
876         /* Get the current directory (so we can find lib/save/) */
877         filename = gtk_file_chooser_get_current_folder(selector);
878         path_build(buf, sizeof buf, filename, ANGBAND_DIR_SAVE);
879         gtk_file_chooser_set_current_folder(selector, buf);
880
881         /* Restrict the showing of pointless files */
882         filter = gtk_file_filter_new();
883         gtk_file_filter_add_custom(filter, GTK_FILE_FILTER_DISPLAY_NAME, file_open_filter, NULL, NULL);
884         gtk_file_chooser_set_filter(selector, filter);
885        
886         accepted = (gtk_dialog_run(GTK_DIALOG(selector_wid)) == GTK_RESPONSE_ACCEPT);
887        
888         /* Run the dialogue */
889         if (accepted)
890         {
891                 /* Get the filename, copy it into the savefile name */
892                 filename = gtk_file_chooser_get_filename(selector);
893                 my_strcpy(savefile, filename, sizeof(savefile));
894                 game_saved = TRUE;
895         }
896                
897         /* Destroy it now that we're done with it */
898         gtk_widget_destroy(selector_wid);
899        
900         /* Done */
901         return accepted;
902 }
903
904 void open_event_handler(GtkButton *was_clicked, gpointer user_data)
905 {
906         bool accepted;
907        
908         accepted = save_dialog_box(FALSE);
909        
910         /* Set the command now so that we skip the "Open File" prompt. */
911         if (accepted) cmd.command = CMD_LOADFILE;
912        
913         /* Done */
914         return;
915 }
916
917 gboolean save_as_event_handler(GtkWidget *widget, GdkEvent *event, gpointer user_data)
918 {
919         bool accepted;
920        
921         accepted = save_dialog_box(TRUE);
922        
923         if (accepted)
924         {
925                 Term_flush();
926                 save_game_gtk();
927         }
928         /* Done */
929         return(FALSE);
930 }
931
932 gboolean save_event_handler(GtkWidget *widget, GdkEvent *event, gpointer user_data)
933 {
934         bool accepted;
935        
936         if (game_saved)
937                 do_cmd_save_game();
938         else
939         {
940                 accepted = save_dialog_box(TRUE);
941        
942                 if (accepted)
943                 {
944                         Term_flush();
945                         save_game_gtk();
946                 }
947         }
948         /* Done */
949         return(FALSE);
950 }
951
952 gboolean delete_event_handler(GtkWidget *widget, GdkEvent *event, gpointer user_data)
953 {
954         save_game_gtk();
955         return (FALSE);
956 }
957
958 gboolean keypress_event_handler(GtkWidget *widget, GdkEventKey *event, gpointer user_data)
959 {
960         int i, mc, ms, mo, mx;
961         guint modifiers;
962
963         char msg[128];
964
965         modifiers = gtk_accelerator_get_default_mod_mask ();
966        
967         /* Extract four "modifier flags" */
968         mc = ((event->state & modifiers) == GDK_CONTROL_MASK) ? TRUE : FALSE;
969         ms = ((event->state & modifiers) == GDK_SHIFT_MASK) ? TRUE : FALSE;
970         mo = ((event->state & modifiers) == GDK_MOD1_MASK) ? TRUE : FALSE;
971         mx = ((event->state & modifiers) == GDK_MOD3_MASK) ? TRUE : FALSE;
972
973         /*
974          * Hack XXX
975          * Parse shifted numeric (keypad) keys specially.
976          */
977         if (ms && (event->keyval >= GDK_KP_0) && (event->keyval <= GDK_KP_9))
978         {
979                 /* Build the macro trigger string */
980                 strnfmt(msg, sizeof(msg), "%cS_%X%c", 31, event->keyval, 13);
981
982                 /* Enqueue the "macro trigger" string */</