1 /* $Header: /home/jcb/MahJong/newmj/RCS/gui-dial.c,v 12.15 2020/05/16 15:46:02 jcb Exp $
2  * gui-dial.c
3  * dialog box functions.
4  */
5 /****************** COPYRIGHT STATEMENT **********************
6  * This file is Copyright (c) 2000 by J. C. Bradfield.       *
7  * Distribution and use is governed by the LICENCE file that *
8  * accompanies this file.                                    *
9  * The moral rights of the author are asserted.              *
10  *                                                           *
11  ***************** DISCLAIMER OF WARRANTY ********************
12  * This code is not warranted fit for any purpose. See the   *
13  * LICENCE file for further information.                     *
14  *                                                           *
15  *************************************************************/
16 
17 #include "gui.h"
18 #ifdef GTK2
19 #define GTK_WINDOW_DIALOG GTK_WINDOW_TOPLEVEL
20 #endif
21 
22 static const char rcs_id[] = "$Header: /home/jcb/MahJong/newmj/RCS/gui-dial.c,v 12.15 2020/05/16 15:46:02 jcb Exp $";
23 
24 static void continue_callback(GtkWidget *w, gpointer data);
25 static void turn_callback(GtkWidget *w, gpointer data);
26 
27 static void debug_report_query_popup(void);
28 
29 /* useful things */
30 const char *windnames[] = { "none", "East", "South", "West", "North" };
31 const char *shortwindnames[] = { " ", "E", "S", "W", "N" };
32 
33 /* check box to keep keyboard focus in message window */
34 static GtkWidget *mfocus;
35 
36 /* This grabs focus, unless the chat window is in the main window
37    and has focus. This stops focus being lost from the chat entry
38    box every time a dialog pops up. (Requested by users, to avoid
39    accidental discards.
40    Moreover, if the appropriate checkbox is set, it
41    keeps the focus in the message window.
42 */
43 static void grab_focus_if_appropriate(GtkWidget *w);
44 
45 /* Used sordidly and internally */
46 static GtkRequisition discard_req = { 0, 0};
47 /* Why an array? So I can pass pointers around */
48 DiscardDialog discard_dialog[1];
49 
50 /* dialog box for specifying chows */
51 GtkWidget *chow_dialog;
52 /* Stores the three TileSetBoxes */
53 static TileSetBox chowtsbs[3];
54 /* and the three buttons */
55 static GtkWidget *chowbuttons[3];
56 
57 /* dialog box for declaring specials */
58 GtkWidget *ds_dialog;
59 
60 /* dialog box for continuing with next hand */
61 GtkWidget *continue_dialog;
62 
63 /* for end of game */
64 GtkWidget *end_dialog;
65 
66 /* dialog for opening connection */
67 GtkWidget *open_dialog;
68 GtkWidget *openmenuentry, *newgamemenuentry, *resumegamemenuentry,
69   *savemenuentry, *saveasmenuentry, *closemenuentry, *gameoptionsmenuentry;
70 /* entry for showing warnings window */
71 static GtkWidget *warningentry;
72 
73 /* dialog box for action when it's our turn.
74    Actions: Discard  Kong  Mah Jong
75    As of 11.80, Kong includes adding a tile to an existing pung
76 */
77 GtkWidget *turn_dialog;
78 GtkWidget *turn_dialog_label; /* used to display number of tiles left */
79 
80 /* dialog box for closed sets when scoring.
81    Actions: Eyes  Chow  Pung  Done
82 */
83 GtkWidget *scoring_dialog;
84 
85 /* window for game status display */
86 GtkWidget *status_window;
87 
88 /* window for "about" information */
89 GtkWidget *about_window;
90 /* window to nag for donations */
91 GtkWidget *nag_window;
92 
93 /* an array of text widgets for displaying scores etc.
94    Element 4 is for settlements.
95    The others are for each player: currently, I think
96    these should be table relative.
97 */
98 GtkWidget *scoring_notebook;
99 GtkWidget *textpages[5];
100 GtkWidget *textlabels[5]; /* labels for the pages */
101 GtkWidget *textwindow; /* and the window for it */
102 
103 /* Window for scoring history */
104 GtkWidget *scorehistorywindow;
105 GtkWidget *scorehistorytext;
106 
107 /* The window for messages, and the display text widget */
108 GtkWidget *messagewindow, *messagetext;
109 
110 #ifdef GTK2
111 GtkTextBuffer *messagetextbuf;
112 GtkTextIter messagetextiter;
113 #endif
114 
115 /* Warning window */
116 GtkWidget *warningwindow, *warningtext;
117 
118 #ifdef GTK2
119 GtkTextBuffer *warningtextbuf;
120 GtkTextIter warningtextiter;
121 #endif
122 
123 /* The Save As.. dialog */
124 GtkWidget *save_window;
125 /* and its text entry widget */
126 GtkWidget *save_text;
127 
128 /* Password entry dialog */
129 GtkWidget *password_window;
130 /* and its text entry widget */
131 GtkWidget *password_text;
132 
133 /* The window for display options */
134 GtkWidget *display_option_dialog = NULL;
135 
136 /* the window for updating the game options */
137 GtkWidget *game_option_dialog = NULL;
138 GtkWidget *game_option_panel = NULL;
139 /* and some of its buttons */
140 GtkWidget *game_option_apply_button = NULL;
141 GtkWidget *game_option_prefs_button = NULL;
142 
143 /* and a very similar one for option preferences */
144 GtkWidget *game_prefs_dialog = NULL;
145 GtkWidget *game_prefs_panel = NULL;
146 
147 /* and one for playing preferences */
148 GtkWidget *playing_prefs_dialog = NULL;
149 
150 /* and one for debugging options */
151 static GtkWidget *debug_options_dialog = NULL;
152 
153 /* message entry widget */
154 static GtkWidget *message_entry = NULL;
155 
156 /* time at which progress bar was started */
157 static struct timeval pstart;
158 static int pinterval = 25; /* timeout interval in ms */
159 static intptr_t pbar_timeout_instance = 0; /* track dead timeouts */
160 static GtkWidget *pbar;
161 
162 /* timeout handler for the dialog progress bar */
pbar_timeout(gpointer instance)163 static int pbar_timeout(gpointer instance) {
164   int timeleft;
165   struct timeval timenow;
166 
167   if ( pbar_timeout_instance != (intptr_t) instance ) return FALSE; /* dead timeout */
168   if ( ! GTK_WIDGET_VISIBLE(discard_dialog->widget) ) return FALSE;
169   gettimeofday(&timenow,NULL);
170   timeleft = ptimeout-(1000*(timenow.tv_sec-pstart.tv_sec)
171 	      +(timenow.tv_usec-pstart.tv_usec)/1000);
172   if ( timeleft <= 0 ) {
173     /* we should not hide the claim dialog: the timeout is really
174        controlled by the server, not us */
175     /* However, if we are supposed to be handling timeouts locally,
176        we'd better send a noclaim! */
177     if ( local_timeouts ) {
178       disc_callback(NULL,(gpointer)NoClaim);
179     }
180     return FALSE;
181   }
182   gtk_progress_bar_update(GTK_PROGRESS_BAR(pbar),1.0-(timeleft+0.0)/(ptimeout+0.0));
183   return TRUE;
184 }
185 
186 /* popup the discard dialog. Arguments:
187    Tile, player whence it came (as an ori),
188    mode = 0 (normal), 1 (claiming tile for mah jong), 2 (claiming from kong) */
discard_dialog_popup(Tile t,int ori,int mode)189 void discard_dialog_popup(Tile t, int ori, int mode) {
190   int i;
191   char buf[128];
192   static Tile lastt; static int lastori, lastmode;
193 
194   /* So that we don't work if it's already popped up: */
195   if ( GTK_WIDGET_VISIBLE(discard_dialog->widget)
196        && t == lastt && lastori == ori && lastmode == mode ) return;
197   lastt = t; lastori = ori; lastmode = mode;
198 
199   if ( mode != discard_dialog->mode ) {
200     discard_dialog->mode = mode;
201     if ( mode == 0 ) {
202       gtk_widget_show(discard_dialog->noclaim);
203       gtk_widget_hide(discard_dialog->eyes);
204       gtk_widget_show(discard_dialog->chow);
205       gtk_widget_show(discard_dialog->pung);
206       gtk_widget_hide(discard_dialog->special);
207       gtk_widget_show(discard_dialog->kong);
208       gtk_widget_show(discard_dialog->mahjong);
209       gtk_widget_hide(discard_dialog->robkong);
210     } else if ( mode == 1 ) {
211       gtk_widget_hide(discard_dialog->noclaim);
212       gtk_widget_show(discard_dialog->eyes);
213       gtk_widget_show(discard_dialog->chow);
214       gtk_widget_show(discard_dialog->pung);
215       gtk_widget_show(discard_dialog->special);
216       gtk_widget_hide(discard_dialog->kong);
217       gtk_widget_hide(discard_dialog->mahjong);
218       gtk_widget_hide(discard_dialog->robkong);
219     } else {
220       gtk_widget_show(discard_dialog->noclaim);
221       gtk_widget_hide(discard_dialog->eyes);
222       gtk_widget_hide(discard_dialog->chow);
223       gtk_widget_hide(discard_dialog->pung);
224       gtk_widget_hide(discard_dialog->special);
225       gtk_widget_hide(discard_dialog->kong);
226       gtk_widget_hide(discard_dialog->mahjong);
227       gtk_widget_show(discard_dialog->robkong);
228     }
229   }
230   if ( mode == 0 ) grab_focus_if_appropriate(discard_dialog->noclaim);
231 
232   /* set the appropriate tile */
233   for ( i=1 ; i < 4 ; i++ ) {
234     if ( i == ori ) {
235       button_set_tile(discard_dialog->tiles[i],t,i);
236       gtk_widget_show(discard_dialog->tiles[i]);
237     } else {
238       gtk_widget_hide(discard_dialog->tiles[i]);
239     }
240   }
241   gtk_widget_hide(discard_dialog->tilename);
242   if ( mode == 1 ) {
243     gtk_label_set_text(GTK_LABEL(discard_dialog->tilename),
244 		     "Claim discard for:");
245   } else {
246     /* if not showing wall, say how many tiles left */
247     if ( ! showwall ) {
248       if ( ori == 1 ) sprintf(buf,"(%d tiles left) %s",
249 			      the_game->wall.live_end-the_game->wall.live_used,
250 			      tile_name(the_game->tile));
251       else sprintf(buf,"%s (%d tiles left)",
252 		   tile_name(the_game->tile),
253 		   the_game->wall.live_end-the_game->wall.live_used);
254     } else {
255       strcpy(buf,tile_name(the_game->tile));
256     }
257     gtk_label_set_text(GTK_LABEL(discard_dialog->tilename),buf);
258   }
259   if ( mode == 0 ) {
260     static const gfloat xal[] = { 0.5,1.0,0.5,0.0 };
261     gtk_misc_set_alignment(GTK_MISC(discard_dialog->tilename),
262 			  xal[ori],0.5);
263   } else {
264     gtk_misc_set_alignment(GTK_MISC(discard_dialog->tilename),
265 			  0.5,0.5);
266   }
267   gtk_widget_show(discard_dialog->tilename);
268   dialog_popup(discard_dialog->widget,DPCentred);
269   /* and start the progress bar timeout if appropriate */
270   if ( the_game->state != MahJonging && ptimeout > 0 ) {
271     gtk_widget_show(pbar);
272     gettimeofday(&pstart,NULL);
273     /* we may as well calculate an appropriate value of pbar_timeout
274        each time... we want it to update every half pixel, or 40 times
275        a second, whichever is slower */
276     if ( pbar->allocation.width > 1 ) {
277       /* in case it isn't realized yet */
278       pinterval = ptimeout/(2*pbar->allocation.width);
279     }
280     if ( pinterval < 25 ) pinterval = 25;
281     gtk_timeout_add(pinterval,pbar_timeout,(gpointer) ++pbar_timeout_instance);
282   } else {
283     gtk_widget_hide(pbar);
284   }
285 }
286 
287 /* this macro generates a variable for an accelerator group,
288    and a function to add or remove it. The macro argument is included
289    in the name of the variable and function */
290 #define ACCELFUN(NAME) \
291 static GtkAccelGroup *NAME ## _accel;\
292 static void add_or_remove_ ## NAME ## _accels(GtkWidget *w UNUSED, gpointer data)\
293 {\
294   static int there = 0;\
295   if ( NAME ## _accel == NULL ) return;\
296   if ( data ) {\
297     if (GTK_HAS_FOCUS & GTK_WIDGET_FLAGS(message_entry)) return;\
298     if ( ! there )\
299       gtk_window_add_accel_group(GTK_WINDOW(topwindow),NAME ## _accel);\
300     there = 1;\
301   } else {\
302     if ( there )\
303       gtk_window_remove_accel_group(GTK_WINDOW(topwindow),NAME ## _accel);\
304     there = 0;\
305   }\
306 }
307 
308 /* an accelerator group  for the discard dialog */
ACCELFUN(discard)309 ACCELFUN(discard)
310 
311 /* initialize it */
312 /* Structure:
313    If the dialog is in the middle:
314       lefttile opptile righttile
315            tile name
316          progress bar
317       Pass/Draw Chow Pung Kong MahJong
318    otherwise:
319       tilename   progress bar
320          buttons
321 */
322 void discard_dialog_init(void) {
323   GtkWidget *box, *tilebox, *left, *opp, *right, *lbl,
324       *butbox, *but, *pixm;
325   DiscardDialog *dd = &discard_dialog[0];
326 
327   if ( dd->widget ) {
328     gtk_widget_destroy(dd->widget);
329     dd->widget = NULL;
330   }
331 
332   switch ( dialogs_position ) {
333   case DialogsCentral:
334   case DialogsUnspecified:
335     /* event box so there's a window to have background */
336     dd->widget = gtk_event_box_new();
337     gtk_fixed_put(GTK_FIXED(discard_area),dd->widget,0,0);
338     /* it'll be moved later */
339     break;
340   case DialogsBelow:
341     dd->widget = gtk_event_box_new();
342     gtk_box_pack_start(GTK_BOX(dialoglowerbox),dd->widget,1,0,0);
343     /* show it, so that the top window includes it when first mapped */
344     gtk_widget_show(dd->widget);
345     break;
346   case DialogsPopup:
347     dd->widget = gtk_window_new(GTK_WINDOW_DIALOG);
348     gtk_signal_connect (GTK_OBJECT (dd->widget), "delete_event",
349                                GTK_SIGNAL_FUNC (gtk_widget_hide), NULL);
350   }
351 
352   box = gtk_vbox_new(0,dialog_vert_spacing);
353   gtk_widget_show(box);
354   gtk_container_add(GTK_CONTAINER(dd->widget),box);
355   dd->mode = -1; /* so that personality will be set */
356   gtk_container_set_border_width(GTK_CONTAINER(box),
357 				 dialog_border_width);
358 
359   tilebox = gtk_hbox_new(0,0);
360   if ( dialogs_position != DialogsBelow ) gtk_widget_show(tilebox);
361 
362   left = gtk_button_new();
363   GTK_WIDGET_UNSET_FLAGS(left,GTK_CAN_FOCUS);
364   gtk_widget_show(left);
365   pixm = gtk_pixmap_new(tilepixmaps[3][HiddenTile],NULL);
366   gtk_widget_show(pixm);
367   gtk_container_add(GTK_CONTAINER(left),pixm);
368   opp = gtk_button_new();
369   GTK_WIDGET_UNSET_FLAGS(opp,GTK_CAN_FOCUS);
370   gtk_widget_show(opp);
371   pixm = gtk_pixmap_new(tilepixmaps[2][HiddenTile],NULL);
372   gtk_widget_show(pixm);
373   gtk_container_add(GTK_CONTAINER(opp),pixm);
374   right = gtk_button_new();
375   GTK_WIDGET_UNSET_FLAGS(right,GTK_CAN_FOCUS);
376   gtk_widget_show(right);
377   pixm = gtk_pixmap_new(tilepixmaps[1][HiddenTile],NULL);
378   gtk_widget_show(pixm);
379   gtk_container_add(GTK_CONTAINER(right),pixm);
380   gtk_box_pack_start(GTK_BOX(tilebox),left,0,0,0);
381   gtk_box_pack_start(GTK_BOX(tilebox),opp,1,0,0);
382   gtk_box_pack_end(GTK_BOX(tilebox),right,0,0,0);
383   dd->tiles[1] = right;
384   dd->tiles[2] = opp;
385   dd->tiles[3] = left;
386 
387   lbl = gtk_label_new("name of tile");
388   gtk_widget_show(lbl);
389   dd->tilename = lbl;
390 
391   butbox = gtk_hbox_new(1,dialog_button_spacing); /* homogeneous, spaced */
392   gtk_widget_show(butbox);
393 
394   /* accelerators for discard dialog actions */
395   discard_accel = gtk_accel_group_new();
396 
397   but = gtk_button_new_with_label("No claim");
398   gtk_widget_show(but);
399   gtk_box_pack_start(GTK_BOX(butbox),but,1,1,0);
400   gtk_signal_connect(GTK_OBJECT(but),"clicked",
401     GTK_SIGNAL_FUNC(disc_callback),(gpointer)NoClaim);
402   dd->noclaim = but;
403 #ifdef GTK2
404   gtk_widget_add_accelerator(GTK_WIDGET(but),"clicked",discard_accel,GDK_n,0,0);
405 #else
406   gtk_accel_group_add(discard_accel,GDK_n,0,0,GTK_OBJECT(but),"clicked");
407 #endif
408   gtk_label_set_pattern(GTK_LABEL(GTK_BIN(but)->child),"_");
409 
410   but = gtk_button_new_with_label("Eyes");
411   GTK_WIDGET_UNSET_FLAGS(but,GTK_CAN_FOCUS);
412   /* not shown in normal state */
413   gtk_box_pack_start(GTK_BOX(butbox),but,1,1,0);
414   gtk_signal_connect(GTK_OBJECT(but),"clicked"
415     ,GTK_SIGNAL_FUNC(disc_callback),(gpointer)PairClaim);
416   dd->eyes = but;
417 #ifdef GTK2
418   gtk_widget_add_accelerator(GTK_WIDGET(but),"clicked",discard_accel,GDK_e,0,0);
419 #else
420   gtk_accel_group_add(discard_accel,GDK_e,0,0,GTK_OBJECT(but),"clicked");
421 #endif
422   gtk_label_set_pattern(GTK_LABEL(GTK_BIN(but)->child),"_");
423 
424   but = gtk_button_new_with_label("Chow");
425   GTK_WIDGET_UNSET_FLAGS(but,GTK_CAN_FOCUS);
426   gtk_widget_show(but);
427   gtk_box_pack_start(GTK_BOX(butbox),but,1,1,0);
428   gtk_signal_connect(GTK_OBJECT(but),"clicked"
429     ,GTK_SIGNAL_FUNC(disc_callback),(gpointer)ChowClaim);
430   dd->chow = but;
431 #ifdef GTK2
432   gtk_widget_add_accelerator(GTK_WIDGET(but),"clicked",discard_accel,GDK_c,0,0);
433 #else
434   gtk_accel_group_add(discard_accel,GDK_c,0,0,GTK_OBJECT(but),"clicked");
435 #endif
436   gtk_label_set_pattern(GTK_LABEL(GTK_BIN(but)->child),"_");
437 
438   but = gtk_button_new_with_label("Pung");
439   GTK_WIDGET_UNSET_FLAGS(but,GTK_CAN_FOCUS);
440   gtk_widget_show(but);
441   gtk_box_pack_start(GTK_BOX(butbox),but,1,1,0);
442   gtk_signal_connect(GTK_OBJECT(but),"clicked",GTK_SIGNAL_FUNC(disc_callback),(gpointer)PungClaim);
443   dd->pung = but;
444 #ifdef GTK2
445   gtk_widget_add_accelerator(GTK_WIDGET(but),"clicked",discard_accel,GDK_p,0,0);
446 #else
447   gtk_accel_group_add(discard_accel,GDK_p,0,0,GTK_OBJECT(but),"clicked");
448 #endif
449   gtk_label_set_pattern(GTK_LABEL(GTK_BIN(but)->child),"_");
450 
451   but = gtk_button_new_with_label("Special Hand");
452   GTK_WIDGET_UNSET_FLAGS(but,GTK_CAN_FOCUS);
453   /* gtk_widget_show(but); */ /* don't show this; uses other space */
454   gtk_box_pack_start(GTK_BOX(butbox),but,1,1,0);
455   gtk_signal_connect(GTK_OBJECT(but),"clicked",GTK_SIGNAL_FUNC(disc_callback),(gpointer)SpecialSetClaim);
456   dd->special = but;
457 #ifdef GTK2
458   gtk_widget_add_accelerator(GTK_WIDGET(but),"clicked",discard_accel,GDK_s,0,0);
459 #else
460   gtk_accel_group_add(discard_accel,GDK_s,0,0,GTK_OBJECT(but),"clicked");
461 #endif
462   gtk_label_set_pattern(GTK_LABEL(GTK_BIN(but)->child),"_");
463 
464   but = gtk_button_new_with_label("Kong");
465   GTK_WIDGET_UNSET_FLAGS(but,GTK_CAN_FOCUS);
466   gtk_widget_show(but);
467   gtk_box_pack_start(GTK_BOX(butbox),but,1,1,0);
468   gtk_signal_connect(GTK_OBJECT(but),"clicked",GTK_SIGNAL_FUNC(disc_callback),(gpointer)KongClaim);
469   dd->kong = but;
470 #ifdef GTK2
471   gtk_widget_add_accelerator(GTK_WIDGET(but),"clicked",discard_accel,GDK_k,0,0);
472 #else
473   gtk_accel_group_add(discard_accel,GDK_k,0,0,GTK_OBJECT(but),"clicked");
474 #endif
475   gtk_label_set_pattern(GTK_LABEL(GTK_BIN(but)->child),"_");
476 
477   but = gtk_button_new_with_label("Mah Jong");
478   GTK_WIDGET_UNSET_FLAGS(but,GTK_CAN_FOCUS);
479   gtk_widget_show(but);
480   gtk_box_pack_start(GTK_BOX(butbox),but,1,1,0);
481   gtk_signal_connect(GTK_OBJECT(but),"clicked",GTK_SIGNAL_FUNC(disc_callback),(gpointer)MahJongClaim);
482   dd->mahjong = but;
483 #ifdef GTK2
484   gtk_widget_add_accelerator(GTK_WIDGET(but),"clicked",discard_accel,GDK_m,0,0);
485 #else
486   gtk_accel_group_add(discard_accel,GDK_m,0,0,GTK_OBJECT(but),"clicked");
487 #endif
488   gtk_label_set_pattern(GTK_LABEL(GTK_BIN(but)->child),"_");
489 
490   but = gtk_button_new_with_label("Rob the Kong - Mah Jong!");
491   GTK_WIDGET_UNSET_FLAGS(but,GTK_CAN_FOCUS);
492   /* gtk_widget_show(but); */ /* don't show this; it uses the space of others */
493   gtk_box_pack_start(GTK_BOX(butbox),but,1,1,0);
494   gtk_signal_connect(GTK_OBJECT(but),"clicked",GTK_SIGNAL_FUNC(disc_callback),(gpointer)MahJongClaim);
495   dd->robkong = but;
496 #ifdef GTK2
497  gtk_widget_add_accelerator(GTK_WIDGET(but),"clicked",discard_accel,GDK_r,0,0);
498 #else
499   gtk_accel_group_add(discard_accel,GDK_r,0,0,GTK_OBJECT(but),"clicked");
500 #endif
501   gtk_label_set_pattern(GTK_LABEL(GTK_BIN(but)->child),"_");
502 
503   pbar = gtk_progress_bar_new();
504   gtk_widget_show(pbar);
505 
506   /* These are packed in reverse order so they float to the bottom */
507   gtk_box_pack_end(GTK_BOX(box),butbox,0,0,0);
508   gtk_box_pack_end(GTK_BOX(box),lbl,0,0,0);
509   gtk_box_pack_end(GTK_BOX(box),pbar,0,0,0);
510   gtk_box_pack_end(GTK_BOX(box),tilebox,0,0,0);
511 
512   /* OK, now ask its size: store the result, and keep it
513      this size for ever more */
514   gtk_widget_size_request(dd->widget,&discard_req);
515   gtk_widget_set_usize(dd->widget,discard_req.width,discard_req.height);
516 
517   /* we have to ensure that the accelerators are only available when
518      the widget is popped up */
519   gtk_signal_connect(GTK_OBJECT(discard_dialog->widget),"hide",GTK_SIGNAL_FUNC(add_or_remove_discard_accels),(gpointer)0);
520   gtk_signal_connect(GTK_OBJECT(discard_dialog->widget),"show",GTK_SIGNAL_FUNC(add_or_remove_discard_accels),(gpointer)1);
521 
522 }
523 
524 static GtkWidget *turn_dialog_discard_button;
525 static GtkWidget *turn_dialog_calling_button;
526 
turn_dialog_popup(void)527 void turn_dialog_popup(void) {
528   /* only original call is allowed, so hide the calling
529      button after first discard */
530   if ( pflag(our_player,NoDiscard) )
531     gtk_widget_show(turn_dialog_calling_button);
532   else
533     gtk_widget_hide(turn_dialog_calling_button);
534   /* if not showing wall, put tiles left in label */
535   if ( ! showwall ) {
536     char buf[128];
537     sprintf(buf,"(%d tiles left)  Select tile and:",
538 	    the_game->wall.live_end-the_game->wall.live_used);
539     gtk_label_set_text(GTK_LABEL(turn_dialog_label),buf);
540   } else {
541     gtk_label_set_text(GTK_LABEL(turn_dialog_label),"Select tile and:");
542   }
543   dialog_popup(turn_dialog,DPOnDiscardOnce);
544   grab_focus_if_appropriate(turn_dialog_discard_button);
545 }
546 
547 /* callback when we toggle one of our tiles.
548    data is our index number.
549    If data also has bit 7 set, force the tile active.
550    If called with NULL widget, clear selection
551 */
552 
conc_callback(GtkWidget * w,gpointer data)553 void conc_callback(GtkWidget *w, gpointer data) {
554   int active;
555   int i;
556   int force=0, index=-1;
557   static GtkWidget *selected = NULL; /* for radiogroup functionality */
558 
559   active = (w && gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w)));
560   if ( active ) index = ((int)(intptr_t)data)& 127;
561   force = (w && (((int)(intptr_t)data) & 128));
562 
563 
564   if ( w && just_doubleclicked == w) force = 1;
565 
566   /* make sure all other tiles are unselected, if we're active,
567      or if we're called to clear: we don't just rely
568      on the selected variable, since under some circumstances we
569      can end up with two tiles active, by accident as it were */
570   /* FIXME: this relies on induced callbacks being executed synchronously */
571   if ( active || w == NULL) {
572     for ( i = 0; i<14; i++ ) {
573       if ( pdisps[0].conc[i] && pdisps[0].conc[i] != w) {
574 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pdisps[0].conc[i]),
575 				     FALSE);
576       }
577     }
578   }
579   selected_button = index;
580   selected = NULL;
581   if ( active ) selected = w;
582 
583   if ( w == NULL ) return;
584 
585   /* change the label on the discard button to be declare if
586      this is a flower */
587   gtk_label_set_text(GTK_LABEL(GTK_BIN(turn_dialog_discard_button)->child),
588     is_special(our_player->concealed[index]) ? "Declare" : "Discard");
589 
590   if ( force ) {
591     /* if it's not active, set it active: we'll then
592        be invoked normally, so we just return now. */
593     if ( ! active ) {
594       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w),TRUE);
595       return;
596     }
597   }
598 
599   /* if we were double clicked, invoke the turn callback directly */
600   if ( w && just_doubleclicked == w) {
601     just_doubleclicked = 0;
602     turn_callback(w,(gpointer)PMsgDiscard);
603   }
604 }
605 
606 /* This detects doubleclicks on the concealed buttons */
doubleclicked(GtkWidget * w,GdkEventButton * eb,gpointer data UNUSED)607 gint doubleclicked(GtkWidget *w, GdkEventButton *eb,gpointer data UNUSED) {
608   if ( eb->type != GDK_2BUTTON_PRESS ) return FALSE;
609   /* This is disgusting. We set a global doubleclicked flag,
610      which is noticed by the toggle callback */
611   just_doubleclicked = w;
612   return FALSE;
613 }
614 
615 /* callback attached to the buttons of the discarding dialog.
616    They pass PMsgDiscard, PMsgDeclareClosedKong,
617    PMsgAddToPung, or PMsgMahJong.
618    As of 11.80, PMsgDeclareClosedKong may mean that or AddToPung,
619    and we must work out which here.
620    Passed PMsgDiscard + 1000000 to declare calling.
621    Also invoked by the declaring special callback:
622     with DeclareSpecial to declare a special, Kong if appropriate,
623     and NoClaim to indicate the end of declaration.
624    Also invoked by scoring dialog with appropriate values */
625 
turn_callback(GtkWidget * w UNUSED,gpointer data)626 static void turn_callback(GtkWidget *w UNUSED, gpointer data) {
627   PMsgUnion m;
628   Tile selected_tile;
629 
630   /* it is possible for this to be invoked when it shouldn't be,
631      for example double-clicking on a tile (or hitting space on a tile?)
632      when no dialog is up. So let's check that one of possible dialogs
633      is up */
634   if ( ! (GTK_WIDGET_VISIBLE(turn_dialog)
635 	 || GTK_WIDGET_VISIBLE(ds_dialog)
636 	 || GTK_WIDGET_VISIBLE(scoring_dialog) ) ) return;
637 
638   selected_tile = (selected_button < 0) ? HiddenTile : our_player->concealed[selected_button];
639 
640   m.type = (PlayerMsgType)data;
641 
642   if ( m.type == PMsgMahJong ) {
643     // set the discard to 0 for cleanliness.
644     m.mahjong.discard = 0;
645     send_packet(&m);
646     return;
647   }
648   if ( m.type == PMsgDeclareClosedKong ) {
649     if ( player_can_declare_closed_kong(our_player,selected_tile) ) {
650       /* that's fine */
651     } else {
652       /* assume they mean add to pung */
653       m.type = PMsgAddToPung;
654     }
655   }
656   if ( m.type == PMsgShowTiles
657        || m.type == PMsgFormClosedSpecialSet ) {
658     closed_set_in_progress = 1;
659     send_packet(&m);
660     return;
661   }
662 
663   if ( the_game->state == DeclaringSpecials
664        && m.type == PMsgNoClaim ) {
665     selected_tile = HiddenTile;
666     m.type = PMsgDeclareSpecial;
667     conc_callback(NULL,NULL); /* clear the selection */
668   }
669 
670   /* in declaring specials, use this to finish */
671   if ( selected_tile == HiddenTile ) {
672     if ( the_game->state == DeclaringSpecials )
673       m.type = PMsgDeclareSpecial;
674     else {
675       error_dialog_popup("No tile selected!");
676       return;
677     }
678   }
679   if ( is_special(selected_tile) )
680     m.type = PMsgDeclareSpecial;
681 
682   switch ( m.type ) {
683   case PMsgDeclareSpecial:
684     m.declarespecial.tile = selected_tile;
685     break;
686   case PMsgDiscard:
687     if ( selected_button >= 0 )
688       player_set_discard_hint(our_player,selected_button);
689     m.discard.tile = selected_tile;
690     m.discard.calling = 0;
691     break;
692   case PMsgDiscard+1000000:
693     if ( selected_button >= 0 )
694       player_set_discard_hint(our_player,selected_button);
695     m.type = PMsgDiscard;
696     m.discard.tile = selected_tile;
697     m.discard.calling = 1;
698     break;
699   case PMsgDeclareClosedKong:
700     m.declareclosedkong.tile = selected_tile;
701     break;
702   case PMsgAddToPung:
703     m.addtopung.tile = selected_tile;
704     break;
705   case PMsgFormClosedPair:
706     m.formclosedpair.tile = selected_tile;
707     closed_set_in_progress = 1;
708     break;
709   case PMsgFormClosedChow:
710     m.formclosedchow.tile = selected_tile;
711     closed_set_in_progress = 1;
712     break;
713   case PMsgFormClosedPung:
714     m.formclosedpung.tile = selected_tile;
715     closed_set_in_progress = 1;
716     break;
717   default:
718     warn("bad type in turn_callback");
719     return;
720   }
721   send_packet(&m);
722 }
723 
724 /* callback when one of the discard dialog buttons is clicked */
disc_callback(GtkWidget * w UNUSED,gpointer data)725 void disc_callback(GtkWidget *w UNUSED, gpointer data) {
726   PMsgUnion m;
727 
728   switch ( (intptr_t) data) {
729   case NoClaim:
730     m.type = PMsgNoClaim;
731     m.noclaim.discard = the_game->serial;
732     break;
733   case ChowClaim:
734     m.type = PMsgChow;
735     m.chow.discard = the_game->serial;
736     m.chow.cpos = AnyPos; /* worry about it later */
737     break;
738   case PairClaim:
739     m.type = PMsgPair;
740     break;
741   case SpecialSetClaim:
742     m.type = PMsgSpecialSet;
743     break;
744   case PungClaim:
745     m.type = PMsgPung;
746     m.pung.discard = the_game->serial;
747     break;
748   case KongClaim:
749     m.type = PMsgKong;
750     m.kong.discard = the_game->serial;
751     break;
752   case MahJongClaim:
753     m.type = PMsgMahJong;
754     m.mahjong.discard = the_game->serial;
755     break;
756   default:
757     warn("disc callback called with unexpected data");
758     return;
759   }
760 
761   /* If the server has a protocol version before 1050, we mustn't
762      send an AnyPos while mahjonging, as it doesn't know how to
763      handle it; so pop up the chow dialog directly */
764   if ( server_pversion < 1050 && the_game->state == MahJonging && m.type == PMsgChow ) {
765     do_chow(NULL,(gpointer)AnyPos);
766   } else {
767     send_packet(&m);
768   }
769 }
770 
ACCELFUN(chow)771 ACCELFUN(chow)
772 
773   /* Now create the Chow dialog box:
774      Structure: three boxes, each containing a tileset showing
775      a possible chow. Below that, a label "which chow?".
776      Below that, three buttons to select.
777   */
778 void chow_dialog_init(void)  {
779   GtkWidget *box,*u,*v; int i;
780 
781   if ( chow_dialog ) {
782     gtk_widget_destroy(chow_dialog);
783     chow_dialog = NULL;
784   }
785 
786   switch ( dialogs_position ) {
787   case DialogsCentral:
788   case DialogsUnspecified:
789     chow_dialog = gtk_event_box_new();
790     gtk_fixed_put(GTK_FIXED(discard_area),chow_dialog,0,0);
791     break;
792   case DialogsPopup:
793   case DialogsBelow:
794     chow_dialog = gtk_window_new(GTK_WINDOW_DIALOG);
795     gtk_signal_connect (GTK_OBJECT (chow_dialog), "delete_event",GTK_SIGNAL_FUNC(gtk_widget_hide), NULL);
796     /* This one is allowed to shrink, and should */
797     gtk_window_set_policy(GTK_WINDOW(chow_dialog),1,1,1);
798   }
799   box = gtk_vbox_new(0,dialog_vert_spacing);
800   gtk_container_set_border_width(GTK_CONTAINER(box),
801 				 dialog_border_width);
802   gtk_widget_show(box);
803   gtk_container_add(GTK_CONTAINER(chow_dialog),box);
804   u = gtk_hbox_new(0,dialog_button_spacing);
805   gtk_widget_show(u);
806   gtk_box_pack_start(GTK_BOX(box),u,0,0,0);
807   for (i=0;i<3;i++) {
808     v = gtk_hbox_new(0,0);
809     gtk_widget_show(v);
810     gtk_box_pack_start(GTK_BOX(u),v,0,0,0);
811     chowtsbs[i].widget = v;
812     tilesetbox_init(&chowtsbs[i],0,GTK_SIGNAL_FUNC(do_chow),(gpointer)(intptr_t)i);
813   }
814   u = gtk_label_new("Which chow?");
815   gtk_widget_show(u);
816   gtk_box_pack_start(GTK_BOX(box),u,0,0,0);
817   u = gtk_hbox_new(1,dialog_button_spacing);
818   gtk_widget_show(u);
819   gtk_box_pack_start(GTK_BOX(box),u,0,0,0);
820 
821   chow_accel = gtk_accel_group_new();
822 
823   for (i=0;i<3;i++) {
824     static int keys[] = { GDK_l, GDK_m, GDK_u };
825     v = gtk_button_new_with_label(player_print_ChowPosition(i));
826     GTK_WIDGET_UNSET_FLAGS(v,GTK_CAN_FOCUS);
827     gtk_widget_show(v);
828     gtk_box_pack_start(GTK_BOX(u),v,1,1,0);
829     chowbuttons[i] = v;
830     gtk_signal_connect(GTK_OBJECT(v),"clicked",GTK_SIGNAL_FUNC(do_chow),(gpointer)(intptr_t)i);
831 #ifdef GTK2
832  gtk_widget_add_accelerator(GTK_WIDGET(v),"clicked",chow_accel,keys[i],0,0);
833 #else
834     gtk_accel_group_add(chow_accel,keys[i],0,0,GTK_OBJECT(v),"clicked");
835 #endif
836     gtk_label_set_pattern(GTK_LABEL(GTK_BIN(v)->child),"_");
837   }
838 
839   gtk_signal_connect(GTK_OBJECT(chow_dialog),"hide",GTK_SIGNAL_FUNC(add_or_remove_chow_accels),(gpointer)0);
840   gtk_signal_connect(GTK_OBJECT(chow_dialog),"show",GTK_SIGNAL_FUNC(add_or_remove_chow_accels),(gpointer)1);
841 
842 }
843 
844 /* now create the declaring specials dialog.
845    Contains a button for declare (special), kong, and done.
846    If no flowers, just kongs.
847 */
848 static GtkWidget *ds_dialog_declare_button;
849 static GtkWidget *ds_dialog_finish_button;
850 
ACCELFUN(ds)851 ACCELFUN(ds)
852 
853 void ds_dialog_init(void) {
854   GtkWidget *box, *bbox, *w;
855 
856   if ( ds_dialog ) {
857     gtk_widget_destroy(ds_dialog);
858     ds_dialog = NULL;
859   }
860 
861   switch ( dialogs_position ) {
862   case DialogsCentral:
863   case DialogsUnspecified:
864     ds_dialog = gtk_event_box_new();
865     gtk_fixed_put(GTK_FIXED(discard_area),ds_dialog,0,0);
866     break;
867   case DialogsBelow:
868     ds_dialog = gtk_event_box_new();
869     gtk_box_pack_start(GTK_BOX(dialoglowerbox),ds_dialog,1,0,0);
870     break;
871   case DialogsPopup:
872     ds_dialog = gtk_window_new(GTK_WINDOW_DIALOG);
873     gtk_signal_connect (GTK_OBJECT (ds_dialog), "delete_event",GTK_SIGNAL_FUNC(gtk_widget_hide), NULL);
874   }
875 
876   box = gtk_vbox_new(0,dialog_vert_spacing);
877   gtk_container_set_border_width(GTK_CONTAINER(box),
878 				 dialog_border_width);
879   gtk_widget_show(box);
880   gtk_container_add(GTK_CONTAINER(ds_dialog),box);
881 
882   bbox = gtk_hbox_new(1,dialog_button_spacing);
883   gtk_widget_show(bbox);
884   gtk_box_pack_end(GTK_BOX(box),bbox,0,0,0);
885 
886   if ( game_get_option_value(the_game,GOFlowers,NULL).optbool )
887     w = gtk_label_new("Declare Flowers/Seasons (and kongs)\nSelect tile and:");
888   else
889     w = gtk_label_new("Declare kongs\nSelect tile and:");
890   gtk_widget_show(w);
891   gtk_box_pack_end(GTK_BOX(box),w,0,0,0);
892 
893   ds_accel = gtk_accel_group_new();
894 
895   if ( game_get_option_value(the_game,GOFlowers,NULL).optbool ) {
896     ds_dialog_declare_button = w = gtk_button_new_with_label("Declare");
897     gtk_widget_show(w);
898     gtk_box_pack_start(GTK_BOX(bbox),w,1,1,0);
899     gtk_signal_connect(GTK_OBJECT(w),"clicked",GTK_SIGNAL_FUNC(turn_callback),(gpointer)(intptr_t)PMsgDeclareSpecial);
900 #ifdef GTK2
901  gtk_widget_add_accelerator(GTK_WIDGET(w),"clicked",ds_accel,GDK_d,0,0);
902 #else
903     gtk_accel_group_add(ds_accel,GDK_d,0,0,GTK_OBJECT(w),"clicked");
904 #endif
905     gtk_label_set_pattern(GTK_LABEL(GTK_BIN(w)->child),"_");
906   }
907 
908   w = gtk_button_new_with_label("Kong");
909   gtk_widget_show(w);
910   gtk_box_pack_start(GTK_BOX(bbox),w,1,1,0);
911   gtk_signal_connect(GTK_OBJECT(w),"clicked",GTK_SIGNAL_FUNC(turn_callback),(gpointer)(intptr_t)PMsgDeclareClosedKong);
912 #ifdef GTK2
913  gtk_widget_add_accelerator(GTK_WIDGET(w),"clicked",ds_accel,GDK_k,0,0);
914 #else
915   gtk_accel_group_add(ds_accel,GDK_k,0,0,GTK_OBJECT(w),"clicked");
916 #endif
917   gtk_label_set_pattern(GTK_LABEL(GTK_BIN(w)->child),"_");
918 
919   ds_dialog_finish_button = w = gtk_button_new_with_label("Finish");
920   gtk_widget_show(w);
921   gtk_box_pack_start(GTK_BOX(bbox),w,1,1,0);
922   gtk_signal_connect(GTK_OBJECT(w),"clicked",GTK_SIGNAL_FUNC(turn_callback),(gpointer)(intptr_t)PMsgNoClaim);
923 #ifdef GTK2
924  gtk_widget_add_accelerator(GTK_WIDGET(w),"clicked",ds_accel,GDK_f,0,0);
925 #else
926   gtk_accel_group_add(ds_accel,GDK_f,0,0,GTK_OBJECT(w),"clicked");
927 #endif
928   gtk_label_set_pattern(GTK_LABEL(GTK_BIN(w)->child),"_");
929 
930   gtk_signal_connect(GTK_OBJECT(ds_dialog),"hide",GTK_SIGNAL_FUNC(add_or_remove_ds_accels),(gpointer)0);
931   gtk_signal_connect(GTK_OBJECT(ds_dialog),"show",GTK_SIGNAL_FUNC(add_or_remove_ds_accels),(gpointer)1);
932 }
933 
ds_dialog_popup(void)934 void ds_dialog_popup(void) {
935   dialog_popup(ds_dialog,DPCentredOnce);
936   if ( game_get_option_value(the_game,GOFlowers,NULL).optbool )
937     grab_focus_if_appropriate(ds_dialog_declare_button);
938   else
939     grab_focus_if_appropriate(ds_dialog_finish_button);
940 }
941 /* dialog to ask to continue with next hand */
942 static GtkWidget *continue_dialog_continue_button;
943 static GtkWidget *continue_dialog_label;
944 
continue_dialog_init(void)945 void continue_dialog_init(void) {
946   GtkWidget *box, *w;
947 
948   if ( continue_dialog ) {
949     gtk_widget_destroy(continue_dialog);
950     continue_dialog = NULL;
951   }
952 
953   switch ( dialogs_position ) {
954   case DialogsCentral:
955   case DialogsUnspecified:
956     continue_dialog = gtk_event_box_new();
957     gtk_fixed_put(GTK_FIXED(discard_area),continue_dialog,0,0);
958     break;
959   case DialogsBelow:
960     continue_dialog = gtk_event_box_new();
961     gtk_box_pack_start(GTK_BOX(dialoglowerbox),continue_dialog,1,0,0);
962     break;
963   case DialogsPopup:
964     continue_dialog = gtk_window_new(GTK_WINDOW_DIALOG);
965     gtk_signal_connect (GTK_OBJECT (continue_dialog), "delete_event",GTK_SIGNAL_FUNC(gtk_widget_hide), NULL);
966   }
967 
968   box = gtk_vbox_new(0,dialog_vert_spacing);
969   gtk_container_set_border_width(GTK_CONTAINER(box),
970 				 dialog_border_width);
971   gtk_widget_show(box);
972   gtk_container_add(GTK_CONTAINER(continue_dialog),box);
973 
974   continue_dialog_continue_button = w = gtk_button_new_with_label("Ready");
975   gtk_widget_show(w);
976   gtk_box_pack_end(GTK_BOX(box),w,0,0,0);
977   gtk_signal_connect(GTK_OBJECT(w),"clicked",GTK_SIGNAL_FUNC(continue_callback),(gpointer)(intptr_t)PMsgDiscard);
978 
979   /* created and packed second so things float to bottom */
980 
981   continue_dialog_label = w = gtk_label_new("Here is some dummy text to space");
982   gtk_widget_show(w);
983   gtk_box_pack_end(GTK_BOX(box),w,0,0,0);
984 
985 }
986 
continue_dialog_popup(void)987 void continue_dialog_popup(void) {
988   static char buf[256];
989   /* the text of the display depends on whether we've said we're ready */
990   if ( ! the_game->active ) {
991     strcpy(buf,"Waiting for game to (re)start");
992     gtk_label_set_text(GTK_LABEL(continue_dialog_label),buf);
993     gtk_widget_hide(continue_dialog_continue_button);
994   }
995   else if ( the_game->ready[our_seat] ) {
996     strcpy(buf,"Waiting for others ");
997     strcat(buf,the_game->paused);
998     gtk_label_set_text(GTK_LABEL(continue_dialog_label),buf);
999     gtk_widget_hide(continue_dialog_continue_button);
1000   } else {
1001     strcpy(buf,"Ready ");
1002     strcat(buf,the_game->paused);
1003     strcat(buf,"?");
1004     gtk_label_set_text(GTK_LABEL(continue_dialog_label),buf);
1005     gtk_widget_show(continue_dialog_continue_button);
1006     grab_focus_if_appropriate(continue_dialog_continue_button);
1007   }
1008   dialog_popup(continue_dialog,DPCentredOnce);
1009 }
1010 
continue_callback(GtkWidget * w UNUSED,gpointer data UNUSED)1011 static void continue_callback(GtkWidget *w UNUSED, gpointer data UNUSED) {
1012   PMsgReadyMsg rm;
1013   rm.type = PMsgReady;
1014   send_packet(&rm);
1015 }
1016 
1017 /* dialog to ask to close at end of game */
1018 static GtkWidget *end_dialog_end_button;
1019 static GtkWidget *end_dialog_label;
1020 
1021 static void end_callback(GtkWidget *w UNUSED, gpointer data UNUSED);
1022 
end_dialog_init(void)1023 void end_dialog_init(void) {
1024   GtkWidget *box, *w;
1025 
1026   if ( end_dialog ) {
1027     gtk_widget_destroy(end_dialog);
1028     end_dialog = NULL;
1029   }
1030 
1031   switch ( dialogs_position ) {
1032   case DialogsCentral:
1033   case DialogsUnspecified:
1034     end_dialog = gtk_event_box_new();
1035     gtk_fixed_put(GTK_FIXED(discard_area),end_dialog,0,0);
1036     break;
1037   case DialogsBelow:
1038     end_dialog = gtk_event_box_new();
1039     gtk_box_pack_start(GTK_BOX(dialoglowerbox),end_dialog,1,0,0);
1040     break;
1041   case DialogsPopup:
1042     end_dialog = gtk_window_new(GTK_WINDOW_DIALOG);
1043     gtk_signal_connect (GTK_OBJECT (end_dialog), "delete_event",GTK_SIGNAL_FUNC(gtk_widget_hide), NULL);
1044   }
1045 
1046   box = gtk_vbox_new(0,dialog_vert_spacing);
1047   gtk_container_set_border_width(GTK_CONTAINER(box),
1048 				 dialog_border_width);
1049   gtk_widget_show(box);
1050   gtk_container_add(GTK_CONTAINER(end_dialog),box);
1051 
1052   end_dialog_end_button = w = gtk_button_new_with_label("Done");
1053   gtk_widget_show(w);
1054   gtk_box_pack_end(GTK_BOX(box),w,0,0,0);
1055   gtk_signal_connect(GTK_OBJECT(w),"clicked",GTK_SIGNAL_FUNC(end_callback),(gpointer)(intptr_t)PMsgDiscard);
1056 
1057   /* created and packed second so things float to bottom */
1058 
1059   end_dialog_label = w = gtk_label_new("GAME OVER");
1060   gtk_widget_show(w);
1061   gtk_box_pack_end(GTK_BOX(box),w,0,0,0);
1062 
1063 }
1064 
end_dialog_popup(void)1065 void end_dialog_popup(void) {
1066   grab_focus_if_appropriate(end_dialog_end_button);
1067   dialog_popup(end_dialog,DPCentredOnce);
1068 }
1069 
end_callback(GtkWidget * w UNUSED,gpointer data UNUSED)1070 static void end_callback(GtkWidget *w UNUSED, gpointer data UNUSED) {
1071   close_connection(0);
1072   gtk_widget_hide(end_dialog);
1073   status_showraise();
1074 }
1075 
1076 /* how many are up; used for positioning - not multi-thread safe! */
1077 static int num_error_dialogs = 0;
1078 
1079 /* function called to close an error dialog */
kill_error(GtkObject * data)1080 static void kill_error(GtkObject *data) {
1081   gtk_widget_destroy(GTK_WIDGET(data));
1082   num_error_dialogs--;
1083 }
1084 
1085 /* popup an error box (new for each message),
1086    or an info box (which doesn't have error tile in it)' */
_error_dialog_popup(char * msg,int iserror)1087 static void _error_dialog_popup(char *msg,int iserror) {
1088   GtkWidget *box, *hbox, *w, *error_dialog, *error_message;
1089 
1090   if ( topwindow == NULL ) {
1091     warn("error_dialog_popup: windows not initialized");
1092     return;
1093   }
1094 
1095   if ( num_error_dialogs >= 10 ) {
1096     warn("Too many error dialogs up to print error: %s",msg);
1097     return;
1098   }
1099 
1100   error_dialog = gtk_window_new(GTK_WINDOW_DIALOG);
1101   gtk_signal_connect_object(GTK_OBJECT (error_dialog), "delete_event",GTK_SIGNAL_FUNC(gtk_widget_destroy),
1102 			    GTK_OBJECT(error_dialog));
1103   gtk_container_set_border_width(GTK_CONTAINER(error_dialog),
1104 				 dialog_border_width);
1105 
1106   box = gtk_vbox_new(0,dialog_vert_spacing);
1107   gtk_widget_show(box);
1108   gtk_container_add(GTK_CONTAINER(error_dialog),box);
1109 
1110   hbox = gtk_hbox_new(0,dialog_button_spacing);
1111   gtk_widget_show(hbox);
1112   gtk_box_pack_start(GTK_BOX(box),hbox,0,0,0);
1113 
1114   if ( iserror ) {
1115     w = gtk_pixmap_new(tilepixmaps[0][ErrorTile],NULL);
1116     gtk_widget_show(w);
1117     gtk_box_pack_start(GTK_BOX(hbox),w,0,0,0);
1118   }
1119 
1120   error_message = gtk_label_new(msg);
1121   gtk_widget_show(error_message);
1122   gtk_box_pack_start(GTK_BOX(hbox),error_message,0,0,0);
1123 
1124   w = gtk_button_new_with_label("OK");
1125   gtk_widget_show(w);
1126   gtk_box_pack_start(GTK_BOX(box),w,0,0,0);
1127   gtk_signal_connect_object(GTK_OBJECT(w),"clicked",GTK_SIGNAL_FUNC(kill_error),GTK_OBJECT(error_dialog));
1128   gtk_window_set_focus(GTK_WINDOW(error_dialog),w);
1129 
1130   dialog_popup(error_dialog,DPErrorPos);
1131   num_error_dialogs++;
1132   gdk_window_raise(error_dialog->window);
1133 }
1134 
error_dialog_popup(char * msg)1135 void error_dialog_popup(char *msg) {
1136   _error_dialog_popup(msg,1);
1137 }
1138 
info_dialog_popup(char * msg)1139 void info_dialog_popup(char *msg) {
1140   _error_dialog_popup(msg,0);
1141 }
1142 
1143 
1144 
1145 GtkWidget *openfile, *openhost, *openport,
1146   *openfiletext, *openhosttext, *openporttext, *openidtext, *opennametext,
1147 #ifdef GTK2
1148   *opengamefilechooser,
1149 #endif
1150   *opengamefile, *opengamefiletext, *opentimeout;
1151 
1152 GtkWidget *openallowdisconnectbutton,*opensaveonexitbutton,*openrandomseatsbutton,
1153   *openplayercheckboxes[3],*openplayernames[3],*openplayeroptions[3],*opentimeoutspinbutton;
1154 static GtkWidget *opengamepanel,*openplayeroptionboxes[3],
1155   *openconnectbutton,*openstartbutton, *openresumebutton, *openhosttoggle, *openunixtoggle;
1156 
1157 /* callback used in next function */
openbut_callback(GtkWidget * w,gpointer data)1158 static void openbut_callback(GtkWidget *w, gpointer data) {
1159   int active = GTK_TOGGLE_BUTTON(w)->active;
1160 
1161   switch ( (int)(intptr_t)data ) {
1162   case 1:
1163     if ( active ) {
1164       /* selected network */
1165       gtk_widget_set_sensitive(openfile,0);
1166       gtk_widget_set_sensitive(openhost,1);
1167       gtk_widget_set_sensitive(openport,1);
1168     }
1169     break;
1170   case 2:
1171     if ( active ) {
1172       /* selected Unix socket */
1173       gtk_widget_set_sensitive(openfile,1);
1174       gtk_widget_set_sensitive(openhost,0);
1175       gtk_widget_set_sensitive(openport,0);
1176     }
1177     break;
1178   case 10:
1179   case 11:
1180   case 12:
1181     gtk_widget_set_sensitive(openplayeroptionboxes[((int)(intptr_t)data)-10],active);
1182     break;
1183   }
1184 }
1185 
1186 #ifdef GTK2
1187 /* callback for the file chooser dialog used below */
chooser_callback(GtkDialog * w,gint rid,gpointer userdata)1188 static void chooser_callback(GtkDialog *w, gint rid, gpointer userdata) {
1189   if ( rid == GTK_RESPONSE_ACCEPT ) {
1190     gchar *f = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(opengamefilechooser));
1191     gchar *buf;
1192     if ( f != NULL && (buf = g_filename_to_utf8(f,-1,NULL,NULL,NULL)) != NULL ) {
1193       gchar cbuf[2*PATH_MAX+1];
1194       getcwd(cbuf,2*PATH_MAX);
1195       gchar *b = buf;
1196       if ( strncmp(cbuf,buf,strlen(cbuf)) == 0 ) {
1197 	b = buf + strlen(cbuf);
1198 #ifdef WIN32
1199 	if ( *b == '\\' ) b++;
1200 #else
1201 	if ( *b == '/' ) b++;
1202 #endif
1203       }
1204       gtk_entry_set_text(GTK_ENTRY(opengamefiletext),b);
1205     } else {
1206       error_dialog_popup("Unexpected error choosing file!");
1207     }
1208     gtk_widget_hide(opengamefilechooser);
1209     g_free(f);
1210     g_free(buf);
1211   } else {
1212     gtk_widget_hide(opengamefilechooser);
1213   }
1214 }
1215 
1216 #endif
1217 
1218 /* the dialog for opening a connection.
1219    Arguments are initial values for id, name
1220    text entry fields, */
open_dialog_init(char * idt,char * nt)1221 void open_dialog_init(char *idt, char *nt) {
1222   /* Layout of the box:
1223      x  Connect to host
1224      x  Use Unix socket
1225      Hostname
1226      ........
1227      Port
1228      ....
1229      Filename
1230      ....
1231      Player ID
1232      ....
1233      Name
1234      ....
1235      OPEN CANCEL
1236   */
1237   int i;
1238   GtkWidget *box, *bbox, *but1, *but2, *w1, *w2;
1239   open_dialog = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1240   gtk_window_set_policy(GTK_WINDOW(open_dialog),FALSE,FALSE,TRUE);
1241   gtk_signal_connect (GTK_OBJECT (open_dialog), "delete_event",GTK_SIGNAL_FUNC(gtk_widget_hide), NULL);
1242   gtk_container_set_border_width(GTK_CONTAINER(open_dialog),
1243 				 dialog_border_width);
1244 
1245   box = gtk_vbox_new(0,dialog_vert_spacing);
1246   gtk_widget_show(box);
1247   gtk_container_add(GTK_CONTAINER(open_dialog),box);
1248 
1249   w1 = gtk_hbox_new(0,dialog_button_spacing);
1250   gtk_widget_show(w1);
1251   gtk_box_pack_start(GTK_BOX(box),w1,0,0,0);
1252   openhosttoggle = but1 = gtk_radio_button_new_with_label(NULL,"Internet server");
1253   GTK_WIDGET_UNSET_FLAGS(but1,GTK_CAN_FOCUS);
1254 #ifndef WIN32
1255   gtk_widget_show(but1);
1256 #endif
1257   gtk_box_pack_start(GTK_BOX(w1),but1,0,0,0);
1258   openunixtoggle = but2 = gtk_radio_button_new_with_label(gtk_radio_button_group(GTK_RADIO_BUTTON(but1)),"Unix socket server");
1259   GTK_WIDGET_UNSET_FLAGS(but2,GTK_CAN_FOCUS);
1260 #ifndef WIN32
1261   gtk_widget_show(but2);
1262 #endif
1263   gtk_box_pack_end(GTK_BOX(w1),but2,0,0,0);
1264 
1265   gtk_signal_connect(GTK_OBJECT(but1),"toggled",GTK_SIGNAL_FUNC(openbut_callback),(gpointer)1);
1266   gtk_signal_connect(GTK_OBJECT(but2),"toggled",GTK_SIGNAL_FUNC(openbut_callback),(gpointer)2);
1267 
1268   w2 = gtk_hbox_new(0,dialog_button_spacing);
1269   gtk_widget_show(w2);
1270   gtk_box_pack_start(GTK_BOX(box),w2,0,0,0);
1271   openhost = gtk_hbox_new(0,0);
1272   gtk_widget_show(openhost);
1273   gtk_box_pack_start(GTK_BOX(w2),openhost,0,0,0);
1274   w1 = gtk_label_new("Host: ");
1275   gtk_widget_show(w1);
1276   gtk_box_pack_start(GTK_BOX(openhost),w1,0,0,0);
1277   openhosttext = gtk_entry_new();
1278   gtk_widget_show(openhosttext);
1279   gtk_box_pack_start(GTK_BOX(openhost),openhosttext,0,0,0);
1280 
1281   openport = gtk_hbox_new(0,0);
1282   gtk_widget_show(openport);
1283   gtk_box_pack_start(GTK_BOX(w2),openport,0,0,0);
1284   w1 = gtk_label_new("Port: ");
1285   gtk_widget_show(w1);
1286   gtk_box_pack_start(GTK_BOX(openport),w1,0,0,0);
1287   openporttext = gtk_entry_new_with_max_length(5);
1288   gtk_widget_set_usize(openporttext,75,0);
1289   gtk_widget_show(openporttext);
1290   gtk_box_pack_start(GTK_BOX(openport),openporttext,0,0,0);
1291 
1292   openfile = gtk_hbox_new(0,0);
1293 #ifndef WIN32
1294   gtk_widget_show(openfile);
1295 #endif
1296   gtk_box_pack_start(GTK_BOX(box),openfile,0,0,0);
1297   w1 = gtk_label_new("Socket file: ");
1298   gtk_widget_show(w1);
1299   gtk_box_pack_start(GTK_BOX(openfile),w1,0,0,0);
1300   openfiletext = gtk_entry_new();
1301   gtk_widget_show(openfiletext);
1302   gtk_box_pack_start(GTK_BOX(openfile),openfiletext,0,0,0);
1303 
1304   opengamefile = gtk_hbox_new(0,0);
1305   gtk_widget_show(opengamefile);
1306   gtk_box_pack_start(GTK_BOX(box),opengamefile,0,0,0);
1307   w1 = gtk_label_new("Saved game file: ");
1308   gtk_widget_show(w1);
1309   gtk_box_pack_start(GTK_BOX(opengamefile),w1,0,0,0);
1310   opengamefiletext = gtk_entry_new();
1311   gtk_widget_show(opengamefiletext);
1312   gtk_box_pack_start(GTK_BOX(opengamefile),opengamefiletext,0,0,0);
1313 
1314 #ifdef GTK2
1315   opengamefilechooser =
1316     gtk_file_chooser_dialog_new("Resume from...",
1317       GTK_WINDOW(open_dialog),
1318       GTK_FILE_CHOOSER_ACTION_OPEN,
1319       GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1320       GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
1321       NULL);
1322   gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(opengamefilechooser),1);
1323   char fbuf[PATH_MAX+1];
1324   if ( getcwd(fbuf,PATH_MAX) != NULL ) {
1325     gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(opengamefilechooser),
1326       fbuf);
1327   }
1328   GtkFileFilter *filter = gtk_file_filter_new();
1329   gtk_file_filter_add_pattern(filter, "*.mjs");
1330   gtk_file_filter_set_name(filter,".mjs files");
1331   GtkFileFilter *afilter = gtk_file_filter_new();
1332   gtk_file_filter_add_pattern(afilter, "*");
1333   gtk_file_filter_set_name(afilter,"All files");
1334   gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(opengamefilechooser),filter);
1335   gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(opengamefilechooser),afilter);
1336   gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(opengamefilechooser),filter);
1337   gtk_signal_connect(GTK_OBJECT(opengamefilechooser),"response",
1338     GTK_SIGNAL_FUNC(chooser_callback),NULL);
1339 
1340   w1 = gtk_button_new_with_label("Browse...");
1341   gtk_widget_show(w1);
1342   gtk_box_pack_start(GTK_BOX(opengamefile),w1,0,0,0);
1343   gtk_signal_connect_object(GTK_OBJECT(w1),"clicked",GTK_SIGNAL_FUNC(gtk_widget_show),GTK_OBJECT(opengamefilechooser));
1344 #endif
1345 
1346   w2 = gtk_hbox_new(0,0);
1347   gtk_widget_show(w2);
1348   gtk_box_pack_start(GTK_BOX(box),w2,0,0,0);
1349   w1 = gtk_label_new("Player ID: ");
1350   gtk_widget_show(w1);
1351   gtk_box_pack_start(GTK_BOX(w2),w1,0,0,0);
1352   openidtext = gtk_entry_new();
1353   gtk_widget_show(openidtext);
1354   gtk_entry_set_text(GTK_ENTRY(openidtext),idt);
1355   gtk_box_pack_start(GTK_BOX(w2),openidtext,0,0,0);
1356 
1357   w2 = gtk_hbox_new(0,0);
1358   gtk_widget_show(w2);
1359   gtk_box_pack_start(GTK_BOX(box),w2,0,0,0);
1360   w1 = gtk_label_new("Name: ");
1361   gtk_widget_show(w1);
1362   gtk_box_pack_start(GTK_BOX(w2),w1,0,0,0);
1363   opennametext = gtk_entry_new();
1364   gtk_widget_show(opennametext);
1365   if ( ! nt || !nt[0] ) nt = getenv("LOGNAME");
1366   if ( ! nt ) nt = getlogin(); /* may need to be in sysdep.c */
1367   gtk_entry_set_text(GTK_ENTRY(opennametext),nt);
1368   gtk_box_pack_start(GTK_BOX(w2),opennametext,0,0,0);
1369 
1370   /* Now some stuff for when this panel is in its personality as
1371      a start game panel */
1372   opengamepanel = bbox = gtk_vbox_new(0,dialog_vert_spacing);
1373   /* gtk_widget_show(bbox); */
1374   gtk_box_pack_start(GTK_BOX(box),bbox,0,0,0);
1375 
1376   for ( i = 0 ; i < 3 ; i++ ) {
1377     static const char *playerlabs[] = { "Second player:" , "Third player:",
1378 					"Fourth player:" };
1379 
1380     w2 = gtk_hbox_new(0,dialog_button_spacing);
1381     gtk_widget_show(w2);
1382     gtk_box_pack_start(GTK_BOX(bbox),w2,0,0,0);
1383     w1 = gtk_label_new(playerlabs[i]);
1384     gtk_widget_show(w1);
1385     gtk_box_pack_start(GTK_BOX(w2),w1,0,0,0);
1386     openplayercheckboxes[i] = w1 =
1387       gtk_check_button_new_with_label("Start computer player   ");
1388     gtk_widget_show(w1);
1389     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w1),1);
1390     gtk_box_pack_end(GTK_BOX(w2),w1,0,0,0);
1391     gtk_signal_connect(GTK_OBJECT(w1),"toggled",GTK_SIGNAL_FUNC(openbut_callback),(gpointer)(intptr_t)(10+i));
1392     openplayeroptionboxes[i] = w2 = gtk_hbox_new(0,dialog_button_spacing);
1393     gtk_widget_show(w2);
1394     gtk_box_pack_start(GTK_BOX(bbox),w2,0,0,0);
1395     w1 = gtk_label_new(" Name:");
1396     gtk_widget_show(w1);
1397     gtk_box_pack_start(GTK_BOX(w2),w1,0,0,0);
1398     openplayernames[i] = w1 = gtk_entry_new();
1399     gtk_entry_set_text(GTK_ENTRY(w1),robot_names[i]);
1400     gtk_widget_show(w1);
1401     gtk_box_pack_start(GTK_BOX(w2),w1,0,0,0);
1402     w1 = gtk_label_new("  Options:");
1403     gtk_widget_show(w1);
1404     gtk_box_pack_start(GTK_BOX(w2),w1,0,0,0);
1405     openplayeroptions[i] = w1 = gtk_entry_new();
1406     gtk_entry_set_text(GTK_ENTRY(w1),robot_options[i]);
1407     gtk_widget_show(w1);
1408     gtk_box_pack_start(GTK_BOX(w2),w1,0,0,0);
1409   }
1410 
1411   openallowdisconnectbutton = w1 =
1412     gtk_check_button_new_with_label("Allow disconnection from game");
1413   gtk_widget_show(w1);
1414   gtk_box_pack_start(GTK_BOX(bbox),w1,0,0,0);
1415 
1416   opensaveonexitbutton = w1 =
1417     gtk_check_button_new_with_label("Save game state on exit");
1418   gtk_widget_show(w1);
1419   gtk_box_pack_start(GTK_BOX(bbox),w1,0,0,0);
1420 
1421   openrandomseatsbutton = w1 =
1422     gtk_check_button_new_with_label("Seat players randomly");
1423   gtk_widget_show(w1);
1424   gtk_box_pack_start(GTK_BOX(bbox),w1,0,0,0);
1425 
1426   opentimeoutspinbutton = w1 =
1427     gtk_spin_button_new(GTK_ADJUSTMENT(gtk_adjustment_new(1.0*game_get_option_entry_from_table(&prefs_table,GOTimeout,NULL)->value.optnat,0.0,300.0,1.0,10.0,0.0)),
1428 			0.0,0);
1429   gtk_widget_show(w1);
1430   opentimeout = w2 = gtk_hbox_new(0,dialog_button_spacing);
1431   gtk_widget_show(w2);
1432   gtk_box_pack_start(GTK_BOX(w2),w1,0,0,0);
1433   w1 = gtk_label_new("seconds allowed for claims");
1434   gtk_widget_show(w1);
1435   gtk_box_pack_start(GTK_BOX(w2),w1,0,0,0);
1436   gtk_box_pack_start(GTK_BOX(bbox),w2,0,0,0);
1437 
1438   w1 = gtk_hbox_new(0,dialog_button_spacing);
1439   gtk_widget_show(w1);
1440   gtk_box_pack_start(GTK_BOX(box),w1,0,0,0);
1441   openconnectbutton = w2 = gtk_button_new_with_label("Connect");
1442   gtk_widget_show(w2);
1443   gtk_box_pack_start(GTK_BOX(w1),w2,0,0,0);
1444   gtk_signal_connect(GTK_OBJECT(w2),"clicked",GTK_SIGNAL_FUNC(open_connection),0);
1445   openstartbutton = w2 = gtk_button_new_with_label("Start Game");
1446   /* gtk_widget_show(w2); */
1447   gtk_box_pack_start(GTK_BOX(w1),w2,0,0,0);
1448   gtk_signal_connect(GTK_OBJECT(w2),"clicked",GTK_SIGNAL_FUNC(open_connection),(gpointer)1);
1449   openresumebutton = w2 = gtk_button_new_with_label("Resume Game");
1450   /* gtk_widget_show(w2); */
1451   gtk_box_pack_start(GTK_BOX(w1),w2,0,0,0);
1452   gtk_signal_connect(GTK_OBJECT(w2),"clicked",GTK_SIGNAL_FUNC(open_connection),(gpointer)2);
1453   w2 = gtk_button_new_with_label("Cancel");
1454   gtk_widget_show(w2);
1455   gtk_box_pack_end(GTK_BOX(w1),w2,0,0,0);
1456   gtk_signal_connect_object(GTK_OBJECT(w2),"clicked",GTK_SIGNAL_FUNC(close_saving_posn),GTK_OBJECT(open_dialog));
1457   /* initialize dialog values */
1458   open_dialog_popup(NULL,(gpointer)-1);
1459 }
1460 
1461 /* if data is -1, just set the values of the open dialog fields,
1462    to be picked up by do_connect */
open_dialog_popup(GtkWidget * w UNUSED,gpointer data)1463 void open_dialog_popup(GtkWidget *w UNUSED, gpointer data) {
1464   int new = 0, join = 0, resume = 0;
1465   char buf[256];
1466   char ht[256], pt[10], ft[256], idt[10];
1467   int usehost = 1;
1468   ht[0] = pt[0] = idt[0] = ft[0] = 0;
1469   if ( strchr(address,':') ) {
1470     /* grrr */
1471     if ( address[0] == ':' ) {
1472       strcpy(ht,"localhost");
1473       strcpy(pt,address+1);
1474     } else {
1475       sscanf(address,"%[^:]:%s",ht,pt);
1476     }
1477   } else {
1478     strcpy(ft,address);
1479     usehost = 0;
1480   }
1481   /* set the default id to be our current id */
1482   sprintf(buf,"%d",our_id);
1483 
1484   if ( (int)(intptr_t)data == 1 ) new = 1;
1485   if ( (int)(intptr_t)data == 0 ) join = 1;
1486   if ( (int)(intptr_t)data == 2 ) resume = 1;
1487   gtk_entry_set_text(GTK_ENTRY(openidtext),buf);
1488   /* set the host and port etc from the address */
1489   gtk_widget_set_sensitive(openhost,usehost);
1490   gtk_entry_set_text(GTK_ENTRY(openhosttext),ht);
1491   gtk_widget_set_sensitive(openport,usehost);
1492   gtk_entry_set_text(GTK_ENTRY(openporttext),pt);
1493   gtk_widget_set_sensitive(openfile,!usehost);
1494   gtk_entry_set_text(GTK_ENTRY(openfiletext),ft);
1495 
1496   if ( (int)(intptr_t)data == -1 ) return;
1497 
1498   if ( join ) {
1499     gtk_widget_show(openhost);
1500   } else {
1501     gtk_widget_hide(openhost);
1502     gtk_entry_set_text(GTK_ENTRY(openhosttext),"localhost");
1503   }
1504 
1505   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(usehost ? openhosttoggle : openunixtoggle),1);
1506 
1507 
1508   if ( resume ) gtk_widget_show(opengamefile);
1509   else gtk_widget_hide(opengamefile);
1510   if ( new || resume ) gtk_widget_show(opengamepanel);
1511   else gtk_widget_hide(opengamepanel);
1512   if ( new ) gtk_widget_show(opentimeout);
1513   else gtk_widget_hide(opentimeout);
1514   if ( new ) gtk_widget_show(openrandomseatsbutton);
1515   else gtk_widget_hide(openrandomseatsbutton);
1516   if ( new ) gtk_widget_show(openstartbutton);
1517   else gtk_widget_hide(openstartbutton);
1518   if ( join ) gtk_widget_show(openconnectbutton);
1519   else gtk_widget_hide(openconnectbutton);
1520   if ( resume ) gtk_widget_show(openresumebutton);
1521   else gtk_widget_hide(openresumebutton);
1522 
1523   dialog_popup(open_dialog,DPCentredOnce);
1524 
1525   if ( new ) gtk_widget_grab_focus(openstartbutton);
1526   if ( join ) gtk_widget_grab_focus(openconnectbutton);
1527   if ( resume ) gtk_widget_grab_focus(openresumebutton);
1528 }
1529 
1530 
ACCELFUN(turn)1531 ACCELFUN(turn)
1532 
1533 /* the turn dialog: buttons for Discard (also declares specs),
1534    Kong (concealed or adding to pung), Mah Jong */
1535 void turn_dialog_init(void) {
1536   GtkWidget *box, *butbox, *w;
1537 
1538   if ( turn_dialog ) {
1539     gtk_widget_destroy(turn_dialog);
1540     turn_dialog = NULL;
1541   }
1542 
1543   switch ( dialogs_position ) {
1544   case DialogsCentral:
1545   case DialogsUnspecified:
1546     turn_dialog = gtk_event_box_new();
1547     gtk_fixed_put(GTK_FIXED(discard_area),turn_dialog,0,0);
1548     break;
1549   case DialogsBelow:
1550     turn_dialog = gtk_event_box_new();
1551     gtk_box_pack_start(GTK_BOX(dialoglowerbox),turn_dialog,1,0,0);
1552     break;
1553   case DialogsPopup:
1554     turn_dialog = gtk_window_new(GTK_WINDOW_DIALOG);
1555     gtk_signal_connect (GTK_OBJECT (turn_dialog), "delete_event",GTK_SIGNAL_FUNC(gtk_widget_hide), NULL);
1556   }
1557 
1558   box = gtk_vbox_new(0,dialog_vert_spacing);
1559   gtk_container_set_border_width(GTK_CONTAINER(box),
1560 				 dialog_border_width);
1561   gtk_widget_show(box);
1562   gtk_container_add(GTK_CONTAINER(turn_dialog),box);
1563 
1564   butbox = gtk_hbox_new(1,dialog_button_spacing);
1565   gtk_widget_show(butbox);
1566   gtk_box_pack_end(GTK_BOX(box),butbox,0,0,0);
1567 
1568   turn_dialog_label = w = gtk_label_new("Select tile and:");
1569   gtk_widget_show(w);
1570   gtk_box_pack_end(GTK_BOX(box),w,0,0,0);
1571 
1572   turn_accel = gtk_accel_group_new();
1573 
1574   w = gtk_button_new_with_label("Discard");
1575   gtk_widget_show(w);
1576   gtk_box_pack_start(GTK_BOX(butbox),w,1,1,0);
1577   /* so other function can set it */
1578   turn_dialog_discard_button = w;
1579   gtk_signal_connect(GTK_OBJECT(w),"clicked",GTK_SIGNAL_FUNC(turn_callback),(gpointer)(intptr_t)PMsgDiscard);
1580 #ifdef GTK2
1581  gtk_widget_add_accelerator(GTK_WIDGET(w),"clicked",turn_accel,GDK_d,0,0);
1582 #else
1583   gtk_accel_group_add(turn_accel,GDK_d,0,0,GTK_OBJECT(w),"clicked");
1584 #endif
1585   gtk_label_set_pattern(GTK_LABEL(GTK_BIN(w)->child),"_");
1586 
1587   w = gtk_button_new_with_label("& Calling");
1588   gtk_widget_show(w);
1589   gtk_box_pack_start(GTK_BOX(butbox),w,1,1,0);
1590   /* so other function can set it */
1591   turn_dialog_calling_button = w;
1592   /* this assumes knowledge that protocol enums don't go above
1593      1000000 */
1594   gtk_signal_connect(GTK_OBJECT(w),"clicked",GTK_SIGNAL_FUNC(turn_callback),(gpointer)(intptr_t)(PMsgDiscard+1000000));
1595 #ifdef GTK2
1596  gtk_widget_add_accelerator(GTK_WIDGET(w),"clicked",turn_accel,GDK_c,0,0);
1597 #else
1598   gtk_accel_group_add(turn_accel,GDK_c,0,0,GTK_OBJECT(w),"clicked");
1599 #endif
1600   gtk_label_set_pattern(GTK_LABEL(GTK_BIN(w)->child),"  _");
1601 
1602   w = gtk_button_new_with_label("Kong");
1603   GTK_WIDGET_UNSET_FLAGS(w,GTK_CAN_FOCUS);
1604   gtk_widget_show(w);
1605   gtk_box_pack_start(GTK_BOX(butbox),w,1,1,0);
1606   gtk_signal_connect(GTK_OBJECT(w),"clicked",GTK_SIGNAL_FUNC(turn_callback),(gpointer)(intptr_t)PMsgDeclareClosedKong);
1607 #ifdef GTK2
1608  gtk_widget_add_accelerator(GTK_WIDGET(w),"clicked",turn_accel,GDK_k,0,0);
1609 #else
1610   gtk_accel_group_add(turn_accel,GDK_k,0,0,GTK_OBJECT(w),"clicked");
1611 #endif
1612   gtk_label_set_pattern(GTK_LABEL(GTK_BIN(w)->child),"_");
1613 
1614   w = gtk_button_new_with_label("Mah Jong!");
1615   GTK_WIDGET_UNSET_FLAGS(w,GTK_CAN_FOCUS);
1616   gtk_widget_show(w);
1617   gtk_box_pack_start(GTK_BOX(butbox),w,1,1,0);
1618   gtk_signal_connect(GTK_OBJECT(w),"clicked",GTK_SIGNAL_FUNC(turn_callback),(gpointer)(intptr_t)PMsgMahJong);
1619 #ifdef GTK2
1620  gtk_widget_add_accelerator(GTK_WIDGET(w),"clicked",turn_accel,GDK_m,0,0);
1621 #else
1622   gtk_accel_group_add(turn_accel,GDK_m,0,0,GTK_OBJECT(w),"clicked");
1623 #endif
1624   gtk_label_set_pattern(GTK_LABEL(GTK_BIN(w)->child),"_");
1625 
1626   gtk_signal_connect(GTK_OBJECT(turn_dialog),"hide",GTK_SIGNAL_FUNC(add_or_remove_turn_accels),(gpointer)0);
1627   gtk_signal_connect(GTK_OBJECT(turn_dialog),"show",GTK_SIGNAL_FUNC(add_or_remove_turn_accels),(gpointer)1);
1628 
1629 }
1630 
1631 /* dialog for scoring phase: forming closed sets */
1632 static GtkWidget *scoring_done, *scoring_special;
1633 
ACCELFUN(scoring)1634 ACCELFUN(scoring)
1635 
1636 void scoring_dialog_init(void) {
1637   GtkWidget *box, *butbox, *w;
1638 
1639   if ( scoring_dialog ) {
1640     gtk_widget_destroy(scoring_dialog);
1641     scoring_dialog = NULL;
1642   }
1643 
1644   switch ( dialogs_position ) {
1645   case DialogsCentral:
1646   case DialogsUnspecified:
1647     scoring_dialog = gtk_event_box_new();
1648     gtk_fixed_put(GTK_FIXED(discard_area),scoring_dialog,0,0);
1649     break;
1650   case DialogsBelow:
1651     scoring_dialog = gtk_event_box_new();
1652     gtk_box_pack_start(GTK_BOX(dialoglowerbox),scoring_dialog,1,0,0);
1653     break;
1654   case DialogsPopup:
1655     scoring_dialog = gtk_window_new(GTK_WINDOW_DIALOG);
1656     gtk_signal_connect (GTK_OBJECT (scoring_dialog), "delete_event",GTK_SIGNAL_FUNC(gtk_widget_hide), NULL);
1657   }
1658 
1659   box = gtk_vbox_new(0,dialog_vert_spacing);
1660   gtk_container_set_border_width(GTK_CONTAINER(box),
1661 				 dialog_border_width);
1662   gtk_widget_show(box);
1663   gtk_container_add(GTK_CONTAINER(scoring_dialog),box);
1664 
1665   butbox = gtk_hbox_new(1,dialog_button_spacing);
1666   gtk_widget_show(butbox);
1667   gtk_box_pack_end(GTK_BOX(box),butbox,0,0,0);
1668 
1669   w = gtk_label_new("Declare concealed sets\nSelect 1st tile and:");
1670   gtk_widget_show(w);
1671   gtk_box_pack_end(GTK_BOX(box),w,0,0,0);
1672 
1673   scoring_accel = gtk_accel_group_new();
1674 
1675   w = gtk_button_new_with_label("Eyes");
1676   GTK_WIDGET_UNSET_FLAGS(w,GTK_CAN_FOCUS);
1677   gtk_widget_show(w);
1678   gtk_box_pack_start(GTK_BOX(butbox),w,1,1,0);
1679   gtk_signal_connect(GTK_OBJECT(w),"clicked",GTK_SIGNAL_FUNC(turn_callback),(gpointer)(intptr_t)PMsgFormClosedPair);
1680 #ifdef GTK2
1681  gtk_widget_add_accelerator(GTK_WIDGET(w),"clicked",scoring_accel,GDK_e,0,0);
1682 #else
1683   gtk_accel_group_add(scoring_accel,GDK_e,0,0,GTK_OBJECT(w),"clicked");
1684 #endif
1685   gtk_label_set_pattern(GTK_LABEL(GTK_BIN(w)->child),"_");
1686 
1687 
1688   w = gtk_button_new_with_label("Chow");
1689   GTK_WIDGET_UNSET_FLAGS(w,GTK_CAN_FOCUS);
1690   gtk_widget_show(w);
1691   gtk_box_pack_start(GTK_BOX(butbox),w,1,1,0);
1692   gtk_signal_connect(GTK_OBJECT(w),"clicked",GTK_SIGNAL_FUNC(turn_callback),(gpointer)(intptr_t)PMsgFormClosedChow);
1693 #ifdef GTK2
1694  gtk_widget_add_accelerator(GTK_WIDGET(w),"clicked",scoring_accel,GDK_c,0,0);
1695 #else
1696   gtk_accel_group_add(scoring_accel,GDK_c,0,0,GTK_OBJECT(w),"clicked");
1697 #endif
1698   gtk_label_set_pattern(GTK_LABEL(GTK_BIN(w)->child),"_");
1699 
1700   w = gtk_button_new_with_label("Pung");
1701   GTK_WIDGET_UNSET_FLAGS(w,GTK_CAN_FOCUS);
1702   gtk_widget_show(w);
1703   gtk_box_pack_start(GTK_BOX(butbox),w,1,1,0);
1704   gtk_signal_connect(GTK_OBJECT(w),"clicked",GTK_SIGNAL_FUNC(turn_callback),(gpointer)(intptr_t)PMsgFormClosedPung);
1705 #ifdef GTK2
1706  gtk_widget_add_accelerator(GTK_WIDGET(w),"clicked",scoring_accel,GDK_p,0,0);
1707 #else
1708   gtk_accel_group_add(scoring_accel,GDK_p,0,0,GTK_OBJECT(w),"clicked");
1709 #endif
1710   gtk_label_set_pattern(GTK_LABEL(GTK_BIN(w)->child),"_");
1711 
1712   scoring_special = w = gtk_button_new_with_label("Special Hand");
1713   GTK_WIDGET_UNSET_FLAGS(w,GTK_CAN_FOCUS);
1714   gtk_widget_show(w);
1715   gtk_box_pack_start(GTK_BOX(butbox),w,1,1,0);
1716   gtk_signal_connect(GTK_OBJECT(w),"clicked",GTK_SIGNAL_FUNC(turn_callback),(gpointer)(intptr_t)PMsgFormClosedSpecialSet);
1717 #ifdef GTK2
1718  gtk_widget_add_accelerator(GTK_WIDGET(w),"clicked",scoring_accel,GDK_s,0,0);
1719 #else
1720   gtk_accel_group_add(scoring_accel,GDK_s,0,0,GTK_OBJECT(w),"clicked");
1721 #endif
1722   gtk_label_set_pattern(GTK_LABEL(GTK_BIN(w)->child),"_");
1723 
1724   scoring_done = w = gtk_button_new_with_label("Finished");
1725   GTK_WIDGET_UNSET_FLAGS(w,GTK_CAN_FOCUS);
1726   /* gtk_widget_show(w); */ /* uses same space as special hand */
1727   gtk_box_pack_start(GTK_BOX(butbox),w,1,1,0);
1728   gtk_signal_connect(GTK_OBJECT(w),"clicked",GTK_SIGNAL_FUNC(turn_callback),(gpointer)(intptr_t)PMsgShowTiles);
1729 #ifdef GTK2
1730  gtk_widget_add_accelerator(GTK_WIDGET(w),"clicked",scoring_accel,GDK_f,0,0);
1731 #else
1732   gtk_accel_group_add(scoring_accel,GDK_f,0,0,GTK_OBJECT(w),"clicked");
1733 #endif
1734   gtk_label_set_pattern(GTK_LABEL(GTK_BIN(w)->child),"_");
1735 
1736   gtk_signal_connect(GTK_OBJECT(scoring_dialog),"hide",GTK_SIGNAL_FUNC(add_or_remove_scoring_accels),(gpointer)0);
1737   gtk_signal_connect(GTK_OBJECT(scoring_dialog),"show",GTK_SIGNAL_FUNC(add_or_remove_scoring_accels),(gpointer)1);
1738 
1739 }
1740 
scoring_dialog_popup(void)1741 void scoring_dialog_popup(void) {
1742   if ( the_game->player == our_seat ) {
1743     gtk_widget_show(scoring_special);
1744     gtk_widget_hide(scoring_done);
1745   } else {
1746     gtk_widget_hide(scoring_special);
1747     gtk_widget_show(scoring_done);
1748   }
1749   dialog_popup(scoring_dialog,DPCentredOnce);
1750 }
1751 
1752 /* used above - it removes all the accelerators when the focus
1753    enters the chat widget, to avoid accidents */
mentry_focus_callback(GtkWidget * w UNUSED,GdkEventFocus e UNUSED,gpointer u UNUSED)1754 static gint mentry_focus_callback(GtkWidget *w UNUSED,GdkEventFocus e UNUSED,gpointer u UNUSED) {
1755   add_or_remove_discard_accels(NULL,0);
1756   add_or_remove_chow_accels(NULL,0);
1757   add_or_remove_ds_accels(NULL,0);
1758   add_or_remove_turn_accels(NULL,0);
1759   add_or_remove_scoring_accels(NULL,0);
1760   return 0;
1761 }
1762 
1763 /* close a widget, saving its position for next open */
close_saving_posn(GtkWidget * w)1764 void close_saving_posn(GtkWidget *w) {
1765   gint x,y;
1766   gdk_window_get_deskrelative_origin(w->window,&x,&y);
1767   gtk_widget_set_uposition(w,x,y);
1768   /* gtk2 seems to lose the information over unmap/map */
1769   gtk_object_set_data(GTK_OBJECT(w),"position-set",(gpointer)1);
1770   gtk_object_set_data(GTK_OBJECT(w),"position-x",(gpointer)(intptr_t)x);
1771   gtk_object_set_data(GTK_OBJECT(w),"position-y",(gpointer)(intptr_t)y);
1772   gtk_widget_hide(w);
1773 }
1774 
1775 /* the textwindow for scoring information etc */
textwindow_init(void)1776 void textwindow_init(void) {
1777   int i;
1778   GtkWidget
1779 #ifndef GTK2
1780     *sbar,
1781 #endif
1782     *obox, *box, *tmp, *lbl,*textw;
1783 
1784   textwindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1785   gtk_signal_connect (GTK_OBJECT (textwindow), "delete_event",GTK_SIGNAL_FUNC(gtk_widget_hide), NULL);
1786   /* must allow shrinking */
1787   gtk_window_set_policy(GTK_WINDOW(textwindow),TRUE,TRUE,FALSE);
1788   gtk_window_set_title(GTK_WINDOW(textwindow),"Scoring calculations");
1789 
1790   // we used to set the size here, but I think that's better done
1791   // on popup
1792 
1793   obox = gtk_vbox_new(0,dialog_vert_spacing);
1794   gtk_container_set_border_width(GTK_CONTAINER(obox),dialog_border_width);
1795   gtk_widget_show(obox);
1796   gtk_container_add(GTK_CONTAINER(textwindow),obox);
1797 
1798   scoring_notebook = gtk_notebook_new();
1799   GTK_WIDGET_UNSET_FLAGS(scoring_notebook,GTK_CAN_FOCUS);
1800   gtk_notebook_set_homogeneous_tabs(GTK_NOTEBOOK(scoring_notebook),1);
1801   gtk_widget_show(scoring_notebook);
1802   gtk_box_pack_start(GTK_BOX(obox),scoring_notebook,1,1,0);
1803 
1804   for (i=0;i<5;i++) {
1805 #ifdef GTK2
1806     textw = gtk_text_view_new();
1807     gtk_text_view_set_editable(GTK_TEXT_VIEW(textw),FALSE);
1808     gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(textw),FALSE);
1809     gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(textw),GTK_WRAP_WORD_CHAR);
1810 #else
1811     GtkStyle *s;
1812     textw = gtk_text_new(NULL,NULL);
1813 #endif
1814     gtk_widget_show(textw);
1815     textpages[i] = textw;
1816 #ifdef GTK2
1817     box = gtk_scrolled_window_new(NULL,NULL);
1818     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(box),GTK_POLICY_NEVER,GTK_POLICY_AUTOMATIC);
1819     gtk_container_add(GTK_CONTAINER(box),textw);
1820 #else
1821     sbar = gtk_vscrollbar_new(GTK_TEXT (textw)->vadj);
1822     gtk_widget_show(sbar);
1823     box = gtk_hbox_new(0,0);
1824     gtk_box_pack_start(GTK_BOX(box),sbar,0,0,0);
1825     gtk_box_pack_start(GTK_BOX(box),textw,1,1,0);
1826 #endif
1827     gtk_widget_show(box);
1828     lbl = gtk_label_new((i==4) ? "Settlement" : "");
1829     gtk_widget_show(lbl);
1830     textlabels[i] = lbl;
1831     gtk_notebook_append_page(GTK_NOTEBOOK(scoring_notebook),box,lbl);
1832     gtk_widget_realize(textw);
1833 #ifndef GTK2
1834     if ( fixed_font ) {
1835       s = gtk_style_copy(gtk_widget_get_style(textw));
1836       s->font = fixed_font;
1837       gtk_widget_set_style(textw,s);
1838     }
1839 #endif
1840   }
1841 
1842   tmp = gtk_button_new_with_label("Close");
1843   gtk_signal_connect_object(GTK_OBJECT(tmp),"clicked",GTK_SIGNAL_FUNC(close_saving_posn),GTK_OBJECT(textwindow));
1844   gtk_widget_show(tmp);
1845   gtk_box_pack_end(GTK_BOX(obox),tmp,0,0,0);
1846   gtk_widget_grab_focus(tmp);
1847 }
1848 
1849 /* the textwindow for scoring history */
1850 
scorehistory_init(void)1851 void scorehistory_init(void) {
1852   int i;
1853   GtkWidget
1854 #ifndef GTK2
1855     *sbar,
1856 #endif
1857     *obox, *box, *tmp, *lbl,*textw;
1858 
1859   scorehistorywindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1860   gtk_signal_connect (GTK_OBJECT (scorehistorywindow), "delete_event",GTK_SIGNAL_FUNC(gtk_widget_hide), NULL);
1861   /* must allow shrinking */
1862   gtk_window_set_policy(GTK_WINDOW(scorehistorywindow),TRUE,TRUE,FALSE);
1863   gtk_window_set_title(GTK_WINDOW(scorehistorywindow),"Scoring history");
1864 
1865   gtk_widget_set_usize(scorehistorywindow,0,400);
1866 
1867   obox = gtk_vbox_new(0,dialog_vert_spacing);
1868   gtk_container_set_border_width(GTK_CONTAINER(obox),dialog_border_width);
1869   gtk_widget_show(obox);
1870   gtk_container_add(GTK_CONTAINER(scorehistorywindow),obox);
1871 
1872 #ifdef GTK2
1873   scorehistorytext = textw = gtk_text_view_new();
1874   gtk_text_view_set_editable(GTK_TEXT_VIEW(textw),FALSE);
1875   gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(textw),FALSE);
1876   gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(textw),GTK_WRAP_NONE);
1877 #else
1878   GtkStyle *s;
1879   scorehistorytext = textw = gtk_text_new(NULL,NULL);
1880   gtk_text_set_word_wrap(GTK_TEXT(textw),FALSE);
1881   gtk_text_set_editable(GTK_TEXT(textw),FALSE);
1882 #endif
1883   gtk_widget_show(textw);
1884 #ifdef GTK2
1885   box = gtk_scrolled_window_new(NULL,NULL);
1886   gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(box),GTK_POLICY_NEVER,GTK_POLICY_AUTOMATIC);
1887   gtk_container_add(GTK_CONTAINER(box),textw);
1888 #else
1889   sbar = gtk_vscrollbar_new(GTK_TEXT (textw)->vadj);
1890   gtk_widget_show(sbar);
1891   box = gtk_hbox_new(0,0);
1892   gtk_box_pack_start(GTK_BOX(box),sbar,0,0,0);
1893   gtk_box_pack_start(GTK_BOX(box),textw,1,1,0);
1894 #endif
1895   gtk_box_pack_start(GTK_BOX(obox),box,1,1,0);
1896   gtk_widget_show(box);
1897   gtk_widget_realize(textw);
1898 #ifndef GTK2
1899   if ( fixed_font ) {
1900     s = gtk_style_copy(gtk_widget_get_style(textw));
1901     s->font = fixed_font;
1902     gtk_widget_set_style(textw,s);
1903   }
1904 #endif
1905 
1906   tmp = gtk_button_new_with_label("Close");
1907   gtk_signal_connect_object(GTK_OBJECT(tmp),"clicked",GTK_SIGNAL_FUNC(close_saving_posn),GTK_OBJECT(scorehistorywindow));
1908   gtk_widget_show(tmp);
1909   gtk_box_pack_end(GTK_BOX(obox),tmp,0,0,0);
1910   gtk_widget_grab_focus(tmp);
1911 }
1912 
1913 /* the callback when user hits return in the message composition window */
mentry_callback(GtkWidget * widget UNUSED,GtkWidget * mentry)1914 static void mentry_callback(GtkWidget *widget UNUSED,GtkWidget *mentry) {
1915   const gchar *mentry_text;
1916   PMsgSendMessageMsg smm;
1917   mentry_text = gtk_entry_get_text(GTK_ENTRY(mentry));
1918   smm.type = PMsgSendMessage;
1919   smm.addressee = 0; /* broadcast only at present ... */
1920   smm.text = (char *)mentry_text;
1921   send_packet(&smm);
1922   gtk_entry_set_text(GTK_ENTRY(mentry),"");
1923 }
1924 
1925 static gint mentry_focus_callback(GtkWidget *w,GdkEventFocus e,gpointer u);
1926 
1927 /* the window for messages */
messagewindow_init(void)1928 void messagewindow_init(void) {
1929   GtkWidget *obox, *box, *tmp, *mentry, *label;
1930 #ifndef GTK2
1931   GtkWidget *sbar;
1932   GtkStyle *s;
1933 #endif
1934 
1935   if ( !info_windows_in_main ) {
1936     messagewindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1937     gtk_signal_connect (GTK_OBJECT (messagewindow), "delete_event",GTK_SIGNAL_FUNC(gtk_widget_hide), NULL);
1938     /* must allow shrinking */
1939     gtk_window_set_policy(GTK_WINDOW(messagewindow),TRUE,TRUE,FALSE);
1940     gtk_window_set_title(GTK_WINDOW(messagewindow),"Messages");
1941     /* reasonable size is ... */
1942     gtk_window_set_default_size(GTK_WINDOW(messagewindow),400,300);
1943   }
1944 
1945   obox = gtk_vbox_new(0,info_windows_in_main ? 0 : dialog_vert_spacing);
1946   if ( !info_windows_in_main ) {
1947     gtk_container_set_border_width(GTK_CONTAINER(obox),dialog_border_width);
1948   }
1949   gtk_widget_show(obox);
1950   if ( info_windows_in_main ) {
1951     messagewindow = obox;
1952     gtk_box_pack_end(GTK_BOX(info_box),messagewindow,1,1,0);
1953   } else {
1954     gtk_container_add(GTK_CONTAINER(messagewindow),obox);
1955   }
1956 
1957 #ifdef GTK2
1958   messagetext = gtk_text_view_new();
1959   gtk_text_view_set_editable(GTK_TEXT_VIEW(messagetext),FALSE);
1960   gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(messagetext),FALSE);
1961   gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(messagetext),GTK_WRAP_WORD_CHAR);
1962   messagetextbuf = gtk_text_view_get_buffer (GTK_TEXT_VIEW (messagetext));
1963   gtk_text_buffer_get_iter_at_offset (messagetextbuf, &messagetextiter, 0);
1964 #else
1965   messagetext = gtk_text_new(NULL,NULL);
1966 #endif
1967   if ( info_windows_in_main ) {
1968     gtk_widget_set_usize(messagetext,0,50);
1969   }
1970   gtk_widget_show(messagetext);
1971 #ifdef GTK2
1972   box = gtk_scrolled_window_new(NULL,NULL);
1973   gtk_container_add(GTK_CONTAINER(box),messagetext);
1974   gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(box),GTK_POLICY_NEVER,GTK_POLICY_AUTOMATIC);
1975 #else
1976   sbar = gtk_vscrollbar_new(GTK_TEXT (messagetext)->vadj);
1977   gtk_widget_show(sbar);
1978   box = gtk_hbox_new(0,0);
1979   gtk_box_pack_start(GTK_BOX(box),sbar,0,0,0);
1980   gtk_box_pack_start(GTK_BOX(box),messagetext,1,1,0);
1981 #endif
1982   gtk_widget_show(box);
1983   gtk_box_pack_start(GTK_BOX(obox),box,1,1,0);
1984   gtk_widget_realize(messagetext);
1985 #ifndef GTK2
1986   if ( fixed_font ) {
1987     s = gtk_style_copy(gtk_widget_get_style(messagetext));
1988       s->font = fixed_font;
1989     gtk_widget_set_style(messagetext,s);
1990   }
1991 #endif
1992 
1993   GTK_WIDGET_UNSET_FLAGS(messagetext,GTK_CAN_FOCUS); /* entry widget shd focus */
1994 
1995   label = gtk_label_new(info_windows_in_main ? "Chat:" : "Send message:");
1996   GTK_WIDGET_UNSET_FLAGS(label,GTK_CAN_FOCUS);
1997   gtk_misc_set_alignment(GTK_MISC(label),0.0,0.5);
1998   gtk_widget_show(label);
1999 
2000   /* the thing for sending messages */
2001   message_entry = mentry = gtk_entry_new();
2002   gtk_widget_show(mentry);
2003   gtk_signal_connect(GTK_OBJECT(mentry),"activate",GTK_SIGNAL_FUNC(mentry_callback),mentry);
2004   gtk_signal_connect(GTK_OBJECT(mentry),"focus-in-event",GTK_SIGNAL_FUNC(mentry_focus_callback),mentry);
2005 
2006   if ( !info_windows_in_main ) {
2007     gtk_box_pack_start(GTK_BOX(obox),label,0,0,0);
2008     gtk_box_pack_start(GTK_BOX(obox),mentry,0,0,0);
2009   } else {
2010     GtkWidget *w = gtk_hbox_new(0,0);
2011     gtk_widget_show(w);
2012     gtk_box_pack_start(GTK_BOX(w),label,0,0,0);
2013     gtk_box_pack_start(GTK_BOX(w),mentry,0,0,0);
2014     mfocus = gtk_check_button_new_with_label("keep cursor here");
2015     GTK_WIDGET_UNSET_FLAGS(mfocus,GTK_CAN_FOCUS);
2016     gtk_widget_show(mfocus);
2017     gtk_box_pack_start(GTK_BOX(w),mfocus,0,0,0);
2018     gtk_box_pack_start(GTK_BOX(obox),w,0,0,0);
2019   }
2020 
2021 
2022   if ( !info_windows_in_main ) {
2023     tmp = gtk_button_new_with_label("Close");
2024     GTK_WIDGET_UNSET_FLAGS(tmp,GTK_CAN_FOCUS); /* entry widget shd focus */
2025     gtk_signal_connect_object(GTK_OBJECT(tmp),"clicked",GTK_SIGNAL_FUNC(close_saving_posn),GTK_OBJECT(messagewindow));
2026     gtk_widget_show(tmp);
2027     gtk_box_pack_end(GTK_BOX(obox),tmp,0,0,0);
2028     /* god knows how focus would work if this happened in the topwindow! */
2029     gtk_widget_grab_focus(mentry);
2030   }
2031 }
2032 
2033 /* the window for showing warnings */
warningwindow_init(void)2034 void warningwindow_init(void) {
2035   GtkWidget *obox, *box, *tmp, *label;
2036 #ifndef GTK2
2037   GtkWidget *sbar;
2038   GtkStyle *s;
2039 #endif
2040 
2041   if ( warningwindow ) { gtk_widget_destroy(warningwindow); }
2042   warningwindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2043   gtk_signal_connect (GTK_OBJECT (warningwindow), "delete_event",GTK_SIGNAL_FUNC(gtk_widget_hide), NULL);
2044   /* must allow shrinking */
2045   gtk_window_set_policy(GTK_WINDOW(warningwindow),TRUE,TRUE,FALSE);
2046   gtk_window_set_title(GTK_WINDOW(warningwindow),"Warnings");
2047   /* reasonable size is ... */
2048   gtk_window_set_default_size(GTK_WINDOW(warningwindow),400,300);
2049 
2050   obox = gtk_vbox_new(0,dialog_vert_spacing);
2051   gtk_container_set_border_width(GTK_CONTAINER(obox),dialog_border_width);
2052   gtk_widget_show(obox);
2053   gtk_container_add(GTK_CONTAINER(warningwindow),obox);
2054 
2055   label = gtk_label_new(
2056     "Apart from when a player disconnects,\n"
2057       "warnings usually indicate a bug or other\n"
2058       "unexpected situation. If in doubt,\n"
2059       "please mail mahjong@stevens-bradfield.com\n"
2060       "with the warning text and a description\n"
2061       "of the situation in which it occurred.");
2062   GTK_WIDGET_UNSET_FLAGS(label,GTK_CAN_FOCUS);
2063   gtk_widget_show(label);
2064   gtk_box_pack_start(GTK_BOX(obox),label,0,0,0);
2065 
2066 #ifdef GTK2
2067   warningtext = gtk_text_view_new();
2068   warningtextbuf = gtk_text_view_get_buffer (GTK_TEXT_VIEW (warningtext));
2069   gtk_text_buffer_get_iter_at_offset (warningtextbuf, &warningtextiter, 0);
2070 #else
2071   warningtext = gtk_text_new(NULL,NULL);
2072 #endif
2073   gtk_widget_show(warningtext);
2074 #ifdef GTK2
2075   box = gtk_scrolled_window_new(NULL,NULL);
2076   gtk_container_add(GTK_CONTAINER(box),warningtext);
2077   gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(box),GTK_POLICY_NEVER,GTK_POLICY_AUTOMATIC);
2078 #else
2079   sbar = gtk_vscrollbar_new(GTK_TEXT (warningtext)->vadj);
2080   gtk_widget_show(sbar);
2081   box = gtk_hbox_new(0,0);
2082   gtk_box_pack_start(GTK_BOX(box),sbar,0,0,0);
2083   gtk_box_pack_start(GTK_BOX(box),warningtext,1,1,0);
2084 #endif
2085   gtk_widget_show(box);
2086   gtk_box_pack_start(GTK_BOX(obox),box,1,1,0);
2087   gtk_widget_realize(warningtext);
2088 #ifndef GTK2
2089   if ( fixed_font ) {
2090     s = gtk_style_copy(gtk_widget_get_style(warningtext));
2091       s->font = fixed_font;
2092     gtk_widget_set_style(warningtext,s);
2093   }
2094 #endif
2095 
2096   GTK_WIDGET_UNSET_FLAGS(warningtext,GTK_CAN_FOCUS);
2097 
2098   box = gtk_hbox_new(0,0);
2099   gtk_widget_show(box);
2100   gtk_box_pack_end(GTK_BOX(obox),box,0,0,0);
2101 
2102   tmp = gtk_button_new_with_label("Clear Warnings");
2103   gtk_signal_connect_object(GTK_OBJECT(tmp),"clicked",GTK_SIGNAL_FUNC(warning_clear),NULL);
2104   gtk_widget_show(tmp);
2105   gtk_box_pack_start(GTK_BOX(box),tmp,1,1,0);
2106 
2107   tmp = gtk_button_new_with_label("Close");
2108   gtk_signal_connect_object(GTK_OBJECT(tmp),"clicked",GTK_SIGNAL_FUNC(close_saving_posn),GTK_OBJECT(warningwindow));
2109   gtk_widget_show(tmp);
2110   gtk_box_pack_end(GTK_BOX(box),tmp,1,1,0);
2111 
2112   /* check for cached warnings */
2113   log_msg_add(0,NULL);
2114 }
2115 
warningraise(void)2116 static void warningraise(void) {
2117   showraise(warningwindow);
2118 }
2119 
2120 static int warning_numbers = 0;
2121 
2122 
warning_clear(void)2123 void warning_clear(void) {
2124   warning_numbers = 0;
2125 #ifdef GTK2
2126   gtk_text_buffer_set_text(GTK_TEXT_BUFFER(warningtextbuf),"",0);
2127   gtk_text_buffer_get_iter_at_offset (warningtextbuf, &warningtextiter, 0);
2128 #else
2129   gtk_editable_delete_text(GTK_EDITABLE(warningtext),0,-1);
2130 #endif
2131   gtk_widget_hide(warningentry);
2132   close_saving_posn(warningwindow);
2133 }
2134 
2135 static GtkWidget *debug_options_reporting; /* query dialog */
2136 
file_report(char * warning)2137 static void file_report(char *warning) {
2138   SOCKET fd;
2139   static int in_progress = 0;
2140   static int waiting_permission = 0;
2141   /* don't enter this routine twice.
2142      However, if we're waiting for permission, the permission is
2143      given by calling this routine with a non-null or null argument.
2144   */
2145   if ( in_progress && ! waiting_permission ) return;
2146   if ( debug_reports == DebugReportsNever ) return;
2147   in_progress = 1;
2148   if ( debug_reports != DebugReportsAlways ) {
2149     static char saved_warning[50000];
2150     if ( waiting_permission  ) {
2151       waiting_permission = 0;
2152       gtk_widget_hide(debug_options_reporting);
2153       control_server_processing(1);
2154       if ( warning ) {
2155 	// permission received
2156 	warning = saved_warning;
2157       } else {
2158 	// permission denied
2159 	in_progress = 0;
2160 	return;
2161       }
2162     } else {
2163       // need to ask for permission
2164       waiting_permission = 1;
2165       // save the warning text
2166       strmcpy(saved_warning,warning,49999);
2167       // disable processing of input from the server, to avoid confusion
2168       control_server_processing(0);
2169       debug_report_query_popup();
2170       return;
2171     }
2172   }
2173   // now file the report
2174   fd = plain_connect_to_host("www.stevens-bradfield.com:9000");
2175   if ( fd == INVALID_SOCKET ) {
2176     warn("unable to file error report");
2177   } else {
2178     static char header[] = "From: <nobody@nobody.nobody>\n"
2179       "To: mahjong@stevens-bradfield.com\n"
2180       "Subject: XMJ internal error report\n"
2181       "\n"
2182       "XMJ version: ";
2183     char msg[1024];
2184     int n;
2185     put_data(fd,header,strlen(header));
2186     put_data(fd,(char *)version,strlen(version));
2187     put_data(fd,"\n",1);
2188     put_data(fd,warning,strlen(warning));
2189     put_data(fd,NULL,0);
2190     n = get_data(fd,msg,1023);
2191     if ( n > 0 ) {
2192       info_dialog_popup(msg);
2193     }
2194     plain_close_socket(fd);
2195   }
2196   in_progress = 0;
2197 }
2198 
2199 
2200 /* a second argument of NULL just checks for stashed warnings to be
2201    transferred to the window */
log_msg_add(LogLevel l,char * warning)2202 int log_msg_add(LogLevel l,char *warning) {
2203   static char buf[1024] = "";
2204   if ( warning && l >= LogWarning) warning_numbers++;
2205   /* if the warning window is not currently available,
2206      cache the warning until it is */
2207   if ( warningwindow == NULL ) {
2208     if ( warning ) {
2209       strncat(buf,warning,1023-strlen(buf));
2210     }
2211   } else {
2212     if ( buf[0] ) {
2213 #ifdef GTK2
2214       gtk_text_buffer_insert(warningtextbuf,&warningtextiter,buf,-1);
2215       gtk_text_buffer_get_end_iter(warningtextbuf,&warningtextiter);
2216 #else
2217       gtk_text_insert(GTK_TEXT(warningtext),NULL,NULL,NULL,buf,-1);
2218 #endif
2219       buf[0] = 0;
2220     }
2221     if ( warning ) {
2222 #ifdef GTK2
2223       gtk_text_buffer_insert(warningtextbuf,&warningtextiter,warning,-1);
2224       gtk_text_buffer_get_end_iter(warningtextbuf,&warningtextiter);
2225 #else
2226       gtk_text_insert(GTK_TEXT(warningtext),NULL,NULL,NULL,warning,-1);
2227 #endif
2228     }
2229     if ( warning_numbers > 0 ) gtk_widget_show(warningentry);
2230     /* if the log level exceeds warning, try to file a report,
2231        and give user a message */
2232     if ( l > LogWarning ) {
2233       file_report(warning);
2234       error_dialog_popup("The program has encountered an internal error.\n"
2235 	"This will probably cause it to crash very soon. Sorry!");
2236     }
2237   }
2238   /* tell warn to print the warning anyway */
2239   return 0;
2240 }
2241 
2242 /* callback for saving as */
save_callback(GtkWidget * w UNUSED,gpointer data UNUSED)2243 static void save_callback(GtkWidget *w UNUSED, gpointer data UNUSED) {
2244   PMsgSaveStateMsg m;
2245 
2246   m.type = PMsgSaveState;
2247   m.filename = (char *)gtk_entry_get_text(GTK_ENTRY(save_text));
2248   send_packet(&m);
2249   close_saving_posn(save_window);
2250 }
2251 
2252 /* window for Save As ... function */
save_init(void)2253 static void save_init(void) {
2254   GtkWidget *box, *bbox, *w1, *tmp;
2255 
2256 
2257   save_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2258   gtk_signal_connect (GTK_OBJECT (save_window), "delete_event",GTK_SIGNAL_FUNC(gtk_widget_hide), NULL);
2259   gtk_container_set_border_width(GTK_CONTAINER(save_window),
2260 				 dialog_border_width);
2261 
2262   bbox = gtk_vbox_new(0,0);
2263   gtk_widget_show(bbox);
2264   gtk_container_add(GTK_CONTAINER(save_window),bbox);
2265 
2266   w1 = gtk_label_new("Save as:");
2267   gtk_widget_show(w1);
2268   gtk_box_pack_start(GTK_BOX(bbox),w1,0,0,0);
2269 
2270   box = gtk_hbox_new(0,0);
2271   gtk_widget_show(box);
2272   gtk_box_pack_start(GTK_BOX(bbox),box,0,0,0);
2273 
2274   save_text = gtk_entry_new();
2275   gtk_widget_show(save_text);
2276   gtk_box_pack_start(GTK_BOX(box),save_text,0,0,0);
2277 
2278   w1 = gtk_label_new(".mjs");
2279   gtk_widget_show(w1);
2280   gtk_box_pack_start(GTK_BOX(box),w1,0,0,0);
2281 
2282   box = gtk_hbox_new(0,0);
2283   gtk_widget_show(box);
2284   gtk_box_pack_start(GTK_BOX(bbox),box,0,0,0);
2285 
2286   tmp = gtk_button_new_with_label("Save");
2287   gtk_signal_connect_object(GTK_OBJECT(tmp),"clicked",GTK_SIGNAL_FUNC(save_callback),0);
2288   gtk_widget_show(tmp);
2289   gtk_box_pack_start(GTK_BOX(box),tmp,1,1,0);
2290 
2291   tmp = gtk_button_new_with_label("Cancel");
2292   gtk_signal_connect_object(GTK_OBJECT(tmp),"clicked",GTK_SIGNAL_FUNC(close_saving_posn),GTK_OBJECT(save_window));
2293   gtk_widget_show(tmp);
2294   gtk_box_pack_start(GTK_BOX(box),tmp,1,1,0);
2295 
2296 }
2297 
2298 /* callback for password */
password_callback(GtkWidget * w UNUSED,gpointer data UNUSED)2299 static void password_callback(GtkWidget *w UNUSED, gpointer data UNUSED) {
2300   PMsgAuthInfoMsg m;
2301 
2302   m.type = PMsgAuthInfo;
2303   strcpy(m.authtype,"basic");
2304   m.authdata = (char *)gtk_entry_get_text(GTK_ENTRY(password_text));
2305   send_packet(&m);
2306   close_saving_posn(password_window);
2307 }
2308 
2309 /* window for password request */
password_init(void)2310 static void password_init(void) {
2311   GtkWidget *box, *bbox, *w1, *tmp;
2312 
2313   password_window = gtk_window_new(GTK_WINDOW_DIALOG);
2314   gtk_signal_connect (GTK_OBJECT (password_window), "delete_event",GTK_SIGNAL_FUNC(gtk_widget_hide), NULL);
2315   gtk_container_set_border_width(GTK_CONTAINER(password_window),
2316 				 dialog_border_width);
2317 
2318   bbox = gtk_vbox_new(0,0);
2319   gtk_widget_show(bbox);
2320   gtk_container_add(GTK_CONTAINER(password_window),bbox);
2321 
2322   w1 = gtk_label_new("Password required:");
2323   gtk_widget_show(w1);
2324   gtk_box_pack_start(GTK_BOX(bbox),w1,0,0,0);
2325 
2326   box = gtk_hbox_new(0,0);
2327   gtk_widget_show(box);
2328   gtk_box_pack_start(GTK_BOX(bbox),box,0,0,0);
2329 
2330   password_text = gtk_entry_new();
2331   gtk_widget_show(password_text);
2332   gtk_box_pack_start(GTK_BOX(box),password_text,0,0,0);
2333 
2334   box = gtk_hbox_new(0,0);
2335   gtk_widget_show(box);
2336   gtk_box_pack_start(GTK_BOX(bbox),box,0,0,0);
2337 
2338   tmp = gtk_button_new_with_label("OK");
2339   gtk_signal_connect_object(GTK_OBJECT(tmp),"clicked",GTK_SIGNAL_FUNC(password_callback),0);
2340   gtk_widget_show(tmp);
2341   gtk_box_pack_start(GTK_BOX(box),tmp,1,1,0);
2342 
2343   gtk_widget_show(tmp);
2344   gtk_box_pack_start(GTK_BOX(box),tmp,1,1,0);
2345 
2346 }
2347 
2348 
2349 /* radio buttons for dialog positions */
2350 static GtkWidget *display_option_dialog_dialogposn[DialogsPopup+1];
2351 /* checkbox for animation */
2352 static GtkWidget *display_option_dialog_animation;
2353 /* checkbox for nopopups */
2354 static GtkWidget *display_option_dialog_nopopups;
2355 /* checkbox for tiletips */
2356 static GtkWidget *display_option_dialog_tiletips;
2357 /* checkbox for rotatelabels */
2358 static GtkWidget *display_option_dialog_rotatelabels;
2359 /* checkbox for thinking_claim */
2360 static GtkWidget *display_option_dialog_thinking_claim;
2361 /* checkbox for alert_mahjong */
2362 static GtkWidget *display_option_dialog_alert_mahjong;
2363 /* check box for iconify option */
2364 static GtkWidget *display_option_dialog_iconify;
2365 /* check box for info in main */
2366 static GtkWidget *display_option_dialog_info_in_main;
2367 /* radiobuttons for show wall */
2368 static GtkWidget *display_option_dialog_showwall[3];
2369 static GtkWidget *display_option_dialog_tileset, *display_option_dialog_tileset_path;
2370 /* text entry for size */
2371 static GtkWidget *display_option_size_entry;
2372 /* radio buttons for sort tiles */
2373 static GtkWidget *display_option_dialog_sort_tiles[3];
2374 #ifdef GTK2
2375 /* text entry for gtk2rc file */
2376 static GtkWidget *display_option_dialog_gtk2_rcfile;
2377 /* checkbox entry for use_system_gtkrc */
2378 static GtkWidget *display_option_dialog_use_system_gtkrc;
2379 #endif
2380 
2381 static void display_option_dialog_apply(void);
2382 static void display_option_dialog_save(void);
2383 static void display_option_dialog_refresh(void);
2384 
2385 /* things used below */
2386 static GtkWidget *mfontwindow;
2387 static GtkWidget *mfont_selector;
mfontsel_callback(GtkWidget * w UNUSED,gpointer data)2388 static void mfontsel_callback(GtkWidget *w UNUSED, gpointer data) {
2389   if ( data ) {
2390     /* use default */
2391     main_font_name[0] = 0;
2392   } else {
2393     /* use selection */
2394     strmcpy(main_font_name,gtk_font_selection_get_font_name(GTK_FONT_SELECTION(mfont_selector)),256);
2395   }
2396   gtk_widget_hide(mfontwindow);
2397 }
2398 
2399 static GtkWidget *tfontwindow;
2400 static GtkWidget *tfont_selector;
tfontsel_callback(GtkWidget * w UNUSED,gpointer data)2401 static void tfontsel_callback(GtkWidget *w UNUSED, gpointer data) {
2402   if ( data ) {
2403     /* use default */
2404     text_font_name[0] = 0;
2405   } else {
2406     /* use selection */
2407     strmcpy(text_font_name,gtk_font_selection_get_font_name(GTK_FONT_SELECTION(tfont_selector)),256);
2408   }
2409   gtk_widget_hide(tfontwindow);
2410 }
2411 
2412 static GtkWidget *tcolwindow;
2413 static GtkWidget *tcolor_selector;
tcolsel_callback(GtkWidget * w UNUSED,gpointer data)2414 static void tcolsel_callback(GtkWidget *w UNUSED, gpointer data) {
2415   if ( data ) {
2416     /* use default */
2417     table_colour_name[0] = 0;
2418   } else {
2419     /* use selection */
2420     gdouble c[4];
2421     gtk_color_selection_get_color(GTK_COLOR_SELECTION(tcolor_selector),c);
2422 #ifdef GTK2
2423     sprintf(table_colour_name,"#%04X%04X%04X",(int)(0xFFFF*c[0]),(int)(0xFFFF*c[1]),(int)(0xFFFF*c[2]));
2424 #else
2425     sprintf(table_colour_name,"rgb:%04X/%04X/%04X",(int)(0xFFFF*c[0]),(int)(0xFFFF*c[1]),(int)(0xFFFF*c[2]));
2426 #endif
2427   }
2428   gtk_widget_hide(tcolwindow);
2429 }
2430 
2431 /* window for display options */
display_option_dialog_init(void)2432 static void display_option_dialog_init(void) {
2433   GtkWidget *box, *bbox, *hbox, *but1, *but2, *but3, *w1, *w2;
2434 
2435   display_option_dialog = gtk_window_new(GTK_WINDOW_DIALOG);
2436   gtk_signal_connect (GTK_OBJECT (display_option_dialog), "delete_event",GTK_SIGNAL_FUNC(gtk_widget_hide), NULL);
2437   gtk_container_set_border_width(GTK_CONTAINER(display_option_dialog),
2438 				 dialog_border_width);
2439 
2440   box = gtk_vbox_new(0,dialog_vert_spacing);
2441   gtk_widget_show(box);
2442   gtk_container_add(GTK_CONTAINER(display_option_dialog),box);
2443 
2444   bbox = gtk_vbox_new(0,0);
2445   gtk_widget_show(bbox);
2446   gtk_box_pack_start(GTK_BOX(box),bbox,0,0,0);
2447 
2448   w1 = gtk_label_new("Position of action dialogs:");
2449   gtk_widget_show(w1);
2450   gtk_box_pack_start(GTK_BOX(bbox),w1,0,0,0);
2451 
2452   hbox = gtk_hbox_new(0,dialog_button_spacing);
2453   gtk_widget_show(hbox);
2454   gtk_box_pack_start(GTK_BOX(bbox),hbox,0,0,0);
2455 
2456   but1 = gtk_radio_button_new_with_label(NULL,"central");
2457   gtk_widget_show(but1);
2458   gtk_box_pack_start(GTK_BOX(hbox),but1,0,0,0);
2459   display_option_dialog_dialogposn[DialogsCentral] = but1 ;
2460 
2461   but2 = gtk_radio_button_new_with_label(gtk_radio_button_group(GTK_RADIO_BUTTON(but1)),"below");
2462   gtk_widget_show(but2);
2463   gtk_box_pack_start(GTK_BOX(hbox),but2,0,0,0);
2464   display_option_dialog_dialogposn[DialogsBelow] = but2;
2465 
2466   but3 = gtk_radio_button_new_with_label(gtk_radio_button_group(GTK_RADIO_BUTTON(but1)),"popup");
2467   gtk_widget_show(but3);
2468   gtk_box_pack_start(GTK_BOX(hbox),but3,0,0,0);
2469   display_option_dialog_dialogposn[DialogsPopup] = but3;
2470 
2471   /* animation */
2472   display_option_dialog_animation = but1 =
2473     gtk_check_button_new_with_label("Animation");
2474   gtk_widget_show(but1);
2475   gtk_box_pack_start(GTK_BOX(box),but1,0,0,0);
2476 
2477   /* info in main */
2478   display_option_dialog_info_in_main = but1 =
2479     gtk_check_button_new_with_label("Display status and messages in main window");
2480   gtk_widget_show(but1);
2481   gtk_box_pack_start(GTK_BOX(box),but1,0,0,0);
2482 
2483   /* nopopups */
2484   display_option_dialog_nopopups = but1 =
2485     gtk_check_button_new_with_label("Don't popup scoring/message windows");
2486   gtk_widget_show(but1);
2487   gtk_box_pack_start(GTK_BOX(box),but1,0,0,0);
2488 
2489   /* tiletips */
2490   display_option_dialog_tiletips = but1 =
2491     gtk_check_button_new_with_label("Tiletips always shown");
2492   gtk_widget_show(but1);
2493   gtk_box_pack_start(GTK_BOX(box),but1,0,0,0);
2494 
2495   /* rotatelabels */
2496   display_option_dialog_rotatelabels = but1 =
2497     gtk_check_button_new_with_label("Rotate player info text");
2498   gtk_widget_show(but1);
2499   gtk_box_pack_start(GTK_BOX(box),but1,0,0,0);
2500 
2501   /* thinking_claim */
2502   display_option_dialog_thinking_claim = but1 =
2503     gtk_check_button_new_with_label("Show when players are thinking");
2504   gtk_widget_show(but1);
2505   gtk_box_pack_start(GTK_BOX(box),but1,0,0,0);
2506 
2507   /* mah-jong */
2508   display_option_dialog_alert_mahjong = but1 =
2509     gtk_check_button_new_with_label("Alert on possible mah-jong");
2510   gtk_widget_show(but1);
2511   gtk_box_pack_start(GTK_BOX(box),but1,0,0,0);
2512 
2513   /* display size */
2514   hbox = gtk_hbox_new(0,dialog_button_spacing);
2515   gtk_widget_show(hbox);
2516   gtk_box_pack_start(GTK_BOX(box),hbox,0,0,0);
2517   w1 = gtk_label_new("Display size (in tile-widths):");
2518   gtk_widget_show(w1);
2519   gtk_box_pack_start(GTK_BOX(hbox),w1,0,0,0);
2520   w1 = gtk_combo_new();
2521   gtk_entry_set_max_length(GTK_ENTRY(GTK_COMBO(w1)->entry),6);
2522   gtk_widget_show(w1);
2523   gtk_box_pack_start(GTK_BOX(hbox),w1,0,0,0);
2524   { GList *gl = NULL;
2525   gl = g_list_append(gl,"19");
2526   gl = g_list_append(gl,"18");
2527   gl = g_list_append(gl,"17");
2528   gl = g_list_append(gl,"16");
2529   gl = g_list_append(gl,"15");
2530   gl = g_list_append(gl,"14");
2531   gl = g_list_append(gl,"(auto)");
2532   gtk_combo_set_popdown_strings(GTK_COMBO(w1),gl);
2533   }
2534   gtk_entry_set_editable(GTK_ENTRY(GTK_COMBO(w1)->entry),FALSE);
2535   display_option_size_entry = GTK_COMBO(w1)->entry;
2536 
2537   /* showwall setting */
2538   bbox = gtk_vbox_new(0,0);
2539   gtk_widget_show(bbox);
2540   gtk_box_pack_start(GTK_BOX(box),bbox,0,0,0);
2541 
2542   w1 = gtk_label_new("Show the wall:");
2543   gtk_widget_show(w1);
2544   gtk_box_pack_start(GTK_BOX(bbox),w1,0,0,0);
2545 
2546   hbox = gtk_hbox_new(0,dialog_button_spacing);
2547   gtk_widget_show(hbox);
2548   gtk_box_pack_start(GTK_BOX(bbox),hbox,0,0,0);
2549 
2550   but1 = gtk_radio_button_new_with_label(NULL,"always");
2551   gtk_widget_show(but1);
2552   gtk_box_pack_start(GTK_BOX(hbox),but1,0,0,0);
2553   display_option_dialog_showwall[1] = but1 ;
2554 
2555   but2 = gtk_radio_button_new_with_label(gtk_radio_button_group(GTK_RADIO_BUTTON(but1)),"when room");
2556   gtk_widget_show(but2);
2557   gtk_box_pack_start(GTK_BOX(hbox),but2,0,0,0);
2558   display_option_dialog_showwall[2] = but2;
2559 
2560   but3 = gtk_radio_button_new_with_label(gtk_radio_button_group(GTK_RADIO_BUTTON(but1)),"never");
2561   gtk_widget_show(but3);
2562   gtk_box_pack_start(GTK_BOX(hbox),but3,0,0,0);
2563   display_option_dialog_showwall[0] = but3;
2564 
2565   /* sort tiles setting */
2566   bbox = gtk_vbox_new(0,0);
2567   gtk_widget_show(bbox);
2568   gtk_box_pack_start(GTK_BOX(box),bbox,0,0,0);
2569 
2570   w1 = gtk_label_new("Sort tiles in hand:");
2571   gtk_widget_show(w1);
2572   gtk_box_pack_start(GTK_BOX(bbox),w1,0,0,0);
2573 
2574   hbox = gtk_hbox_new(0,dialog_button_spacing);
2575   gtk_widget_show(hbox);
2576   gtk_box_pack_start(GTK_BOX(bbox),hbox,0,0,0);
2577 
2578   but1 = gtk_radio_button_new_with_label(NULL,"always");
2579   gtk_widget_show(but1);
2580   gtk_box_pack_start(GTK_BOX(hbox),but1,0,0,0);
2581   display_option_dialog_sort_tiles[SortAlways] = but1 ;
2582 
2583   but2 = gtk_radio_button_new_with_label(gtk_radio_button_group(GTK_RADIO_BUTTON(but1)),"during deal");
2584   gtk_widget_show(but2);
2585   gtk_box_pack_start(GTK_BOX(hbox),but2,0,0,0);
2586   display_option_dialog_sort_tiles[SortDeal] = but2;
2587 
2588   but3 = gtk_radio_button_new_with_label(gtk_radio_button_group(GTK_RADIO_BUTTON(but1)),"never");
2589   gtk_widget_show(but3);
2590   gtk_box_pack_start(GTK_BOX(hbox),but3,0,0,0);
2591   display_option_dialog_sort_tiles[SortNever] = but3;
2592 
2593   /* iconify */
2594   display_option_dialog_iconify = but1 =
2595     gtk_check_button_new_with_label("Iconify all windows with main");
2596   gtk_widget_show(but1);
2597   gtk_box_pack_start(GTK_BOX(box),but1,0,0,0);
2598 
2599   /* tileset name */
2600   bbox = gtk_hbox_new(0,0);
2601   gtk_widget_show(bbox);
2602   gtk_box_pack_start(GTK_BOX(box),bbox,0,0,0);
2603 
2604   w1 = gtk_label_new("Tileset: ");
2605   gtk_widget_show(w1);
2606   gtk_box_pack_start(GTK_BOX(bbox),w1,0,0,0);
2607 
2608   display_option_dialog_tileset = w1 = gtk_entry_new();
2609   gtk_widget_show(w1);
2610   gtk_box_pack_start(GTK_BOX(bbox),w1,0,0,0);
2611 
2612   /* tileset path */
2613   bbox = gtk_hbox_new(0,0);
2614   gtk_widget_show(bbox);
2615   gtk_box_pack_start(GTK_BOX(box),bbox,0,0,0);
2616 
2617   w1 = gtk_label_new("Tileset Path: ");
2618   gtk_widget_show(w1);
2619   gtk_box_pack_start(GTK_BOX(bbox),w1,0,0,0);
2620 
2621   display_option_dialog_tileset_path = w1 = gtk_entry_new();
2622   gtk_widget_show(w1);
2623   gtk_box_pack_start(GTK_BOX(bbox),w1,0,0,0);
2624 
2625   /* deal with the selection of the system font */
2626   { GtkWidget *box, *tmp, *w, *fsdial;
2627     if ( mfontwindow ) { gtk_widget_destroy(mfontwindow); }
2628     mfontwindow = gtk_window_new(GTK_WINDOW_DIALOG);
2629     gtk_window_set_title(GTK_WINDOW(mfontwindow),"Main font selection");
2630     box = gtk_vbox_new(0,dialog_vert_spacing);
2631     gtk_widget_show(box);
2632     gtk_container_add(GTK_CONTAINER(mfontwindow),box);
2633     mfont_selector = fsdial = gtk_font_selection_new();
2634     gtk_widget_show(fsdial);
2635     gtk_font_selection_set_preview_text(GTK_FONT_SELECTION(fsdial),
2636       "I hope you're enjoying the game");
2637     gtk_box_pack_start(GTK_BOX(box),fsdial,0,0,0);
2638     tmp = gtk_hbox_new(1,dialog_button_spacing);
2639     gtk_widget_show(tmp);
2640     gtk_box_pack_start(GTK_BOX(box),tmp,0,0,0);
2641 
2642     w = gtk_button_new_with_label("Select");
2643     gtk_widget_show(w);
2644     gtk_box_pack_start(GTK_BOX(tmp),w,1,1,0);
2645     gtk_signal_connect(GTK_OBJECT(w),"clicked",GTK_SIGNAL_FUNC(mfontsel_callback),(gpointer) 0);
2646 
2647     w = gtk_button_new_with_label("Use default");
2648     gtk_widget_show(w);
2649     gtk_box_pack_start(GTK_BOX(tmp),w,1,1,0);
2650     gtk_signal_connect(GTK_OBJECT(w),"clicked",GTK_SIGNAL_FUNC(mfontsel_callback),(gpointer) 1);
2651 
2652     w = gtk_button_new_with_label("Cancel");
2653     gtk_widget_show(w);
2654     gtk_box_pack_start(GTK_BOX(tmp),w,1,1,0);
2655     gtk_signal_connect_object(GTK_OBJECT(w),"clicked",GTK_SIGNAL_FUNC(gtk_widget_hide),GTK_OBJECT(mfontwindow));
2656   }
2657 
2658   w1 = gtk_button_new_with_label("Main font selection...");
2659   gtk_widget_show(w1);
2660   gtk_box_pack_start(GTK_BOX(box),w1,0,0,0);
2661   gtk_signal_connect_object(GTK_OBJECT(w1),"clicked",GTK_SIGNAL_FUNC(showraise),GTK_OBJECT(mfontwindow));
2662 
2663   /* deal with the selection of the text font */
2664   { GtkWidget *box, *tmp, *w, *fsdial;
2665     if ( tfontwindow ) { gtk_widget_destroy(tfontwindow); }
2666     tfontwindow = gtk_window_new(GTK_WINDOW_DIALOG);
2667     gtk_window_set_title(GTK_WINDOW(tfontwindow),"Text font selection");
2668     box = gtk_vbox_new(0,dialog_vert_spacing);
2669     gtk_widget_show(box);
2670     gtk_container_add(GTK_CONTAINER(tfontwindow),box);
2671     tfont_selector = fsdial = gtk_font_selection_new();
2672     gtk_widget_show(fsdial);
2673     gtk_font_selection_set_preview_text(GTK_FONT_SELECTION(fsdial),
2674       "I hope you're enjoying the game");
2675     gtk_box_pack_start(GTK_BOX(box),fsdial,0,0,0);
2676     tmp = gtk_hbox_new(1,dialog_button_spacing);
2677     gtk_widget_show(tmp);
2678     gtk_box_pack_start(GTK_BOX(box),tmp,0,0,0);
2679 
2680     w = gtk_button_new_with_label("Select");
2681     gtk_widget_show(w);
2682     gtk_box_pack_start(GTK_BOX(tmp),w,1,1,0);
2683     gtk_signal_connect(GTK_OBJECT(w),"clicked",GTK_SIGNAL_FUNC(tfontsel_callback),(gpointer) 0);
2684 
2685     w = gtk_button_new_with_label("Use default");
2686     gtk_widget_show(w);
2687     gtk_box_pack_start(GTK_BOX(tmp),w,1,1,0);
2688     gtk_signal_connect(GTK_OBJECT(w),"clicked",GTK_SIGNAL_FUNC(tfontsel_callback),(gpointer) 1);
2689 
2690     w = gtk_button_new_with_label("Cancel");
2691     gtk_widget_show(w);
2692     gtk_box_pack_start(GTK_BOX(tmp),w,1,1,0);
2693     gtk_signal_connect_object(GTK_OBJECT(w),"clicked",GTK_SIGNAL_FUNC(gtk_widget_hide),GTK_OBJECT(tfontwindow));
2694   }
2695 
2696   w1 = gtk_button_new_with_label("Text font selection...");
2697   gtk_widget_show(w1);
2698   gtk_box_pack_start(GTK_BOX(box),w1,0,0,0);
2699   gtk_signal_connect_object(GTK_OBJECT(w1),"clicked",GTK_SIGNAL_FUNC(showraise),GTK_OBJECT(tfontwindow));
2700 
2701   /* deal with selection of table colour */
2702   { GtkWidget *box, *tmp, *w, *csdial;
2703     if ( tcolwindow ) { gtk_widget_destroy(tcolwindow); }
2704     tcolwindow = gtk_window_new(GTK_WINDOW_DIALOG);
2705     gtk_window_set_title(GTK_WINDOW(tcolwindow),"Table colour selection");
2706     box = gtk_vbox_new(0,dialog_vert_spacing);
2707     gtk_widget_show(box);
2708     gtk_container_add(GTK_CONTAINER(tcolwindow),box);
2709     tcolor_selector = csdial = gtk_color_selection_new();
2710     gtk_widget_show(csdial);
2711     gtk_box_pack_start(GTK_BOX(box),csdial,0,0,0);
2712     tmp = gtk_hbox_new(1,dialog_button_spacing);
2713     gtk_widget_show(tmp);
2714     gtk_box_pack_start(GTK_BOX(box),tmp,0,0,0);
2715 
2716     w = gtk_button_new_with_label("Select");
2717     gtk_widget_show(w);
2718     gtk_box_pack_start(GTK_BOX(tmp),w,1,1,0);
2719     gtk_signal_connect(GTK_OBJECT(w),"clicked",GTK_SIGNAL_FUNC(tcolsel_callback),(gpointer) 0);
2720 
2721     w = gtk_button_new_with_label("Use default");
2722     gtk_widget_show(w);
2723     gtk_box_pack_start(GTK_BOX(tmp),w,1,1,0);
2724     gtk_signal_connect(GTK_OBJECT(w),"clicked",GTK_SIGNAL_FUNC(tcolsel_callback),(gpointer) 1);
2725 
2726     w = gtk_button_new_with_label("Cancel");
2727     gtk_widget_show(w);
2728     gtk_box_pack_start(GTK_BOX(tmp),w,1,1,0);
2729     gtk_signal_connect_object(GTK_OBJECT(w),"clicked",GTK_SIGNAL_FUNC(gtk_widget_hide),GTK_OBJECT(tcolwindow));
2730   }
2731 
2732 
2733   w1 = gtk_button_new_with_label("Table colour selection...");
2734   gtk_widget_show(w1);
2735   gtk_box_pack_start(GTK_BOX(box),w1,0,0,0);
2736   gtk_signal_connect_object(GTK_OBJECT(w1),"clicked",GTK_SIGNAL_FUNC(showraise),GTK_OBJECT(tcolwindow));
2737 
2738 #ifdef GTK2
2739   /* gtk2rcfile */
2740   bbox = gtk_hbox_new(0,0);
2741   gtk_widget_show(bbox);
2742   gtk_box_pack_start(GTK_BOX(box),bbox,0,0,0);
2743 
2744   w1 = gtk_label_new("Gtk2 Rcfile: ");
2745   gtk_widget_show(w1);
2746   gtk_box_pack_start(GTK_BOX(bbox),w1,0,0,0);
2747 
2748   display_option_dialog_gtk2_rcfile = w1 = gtk_entry_new();
2749   gtk_widget_show(w1);
2750   gtk_box_pack_start(GTK_BOX(bbox),w1,0,0,0);
2751 
2752   /* use_system_gtkrc  */
2753   display_option_dialog_use_system_gtkrc = but1 =
2754     gtk_check_button_new_with_label("Use system gtkrc");
2755   gtk_widget_show(but1);
2756   gtk_box_pack_start(GTK_BOX(box),but1,0,0,0);
2757 
2758 #endif
2759 
2760   /* apply, save and close buttons */
2761   w1 = gtk_hbox_new(1,dialog_button_spacing);
2762   gtk_widget_show(w1);
2763   gtk_box_pack_end(GTK_BOX(box),w1,0,0,0);
2764 
2765   w2 = gtk_button_new_with_label("Save & Apply");
2766   gtk_widget_show(w2);
2767   gtk_box_pack_start(GTK_BOX(w1),w2,1,1,0);
2768   gtk_signal_connect(GTK_OBJECT(w2),"clicked",GTK_SIGNAL_FUNC(display_option_dialog_save),NULL);
2769 
2770   w2 = gtk_button_new_with_label("Apply (no save)");
2771   gtk_widget_show(w2);
2772   gtk_box_pack_start(GTK_BOX(w1),w2,1,1,0);
2773   gtk_signal_connect(GTK_OBJECT(w2),"clicked",GTK_SIGNAL_FUNC(display_option_dialog_apply),NULL);
2774 
2775   w2 = gtk_button_new_with_label("Cancel");
2776   gtk_widget_show(w2);
2777   gtk_box_pack_start(GTK_BOX(w1),w2,1,1,0);
2778   gtk_signal_connect_object(GTK_OBJECT(w2),"clicked",GTK_SIGNAL_FUNC(gtk_widget_hide),GTK_OBJECT(display_option_dialog));
2779 }
2780 
2781 static char old_main_font_name[256];
2782 static char old_text_font_name[256];
2783 static char old_table_colour_name[256];
2784 
2785 /* make the panel match reality */
display_option_dialog_refresh(void)2786 static void display_option_dialog_refresh(void) {
2787   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(display_option_dialog_dialogposn[dialogs_position]),1);
2788   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(display_option_dialog_animation),animate);
2789   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(display_option_dialog_nopopups),nopopups);
2790   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(display_option_dialog_info_in_main),info_windows_in_main);
2791   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(display_option_dialog_tiletips),tiletips);
2792   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(display_option_dialog_rotatelabels),rotatelabels);
2793   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(display_option_dialog_thinking_claim),thinking_claim);
2794   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(display_option_dialog_alert_mahjong),alert_mahjong);
2795   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(display_option_dialog_showwall[pref_showwall == -1 ? 2 : pref_showwall]),1);
2796   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(display_option_dialog_sort_tiles[sort_tiles]),1);
2797   if ( display_size == 0 ) {
2798     gtk_entry_set_text(GTK_ENTRY(display_option_size_entry),"(auto)");
2799   } else {
2800     char buf[5];
2801     sprintf(buf,"%d",display_size);
2802     gtk_entry_set_text(GTK_ENTRY(display_option_size_entry),buf);
2803   }
2804   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(display_option_dialog_iconify),iconify_dialogs_with_main);
2805   gtk_entry_set_text(GTK_ENTRY(display_option_dialog_tileset),
2806     tileset ? tileset : "");
2807 #ifdef GTK2
2808   gtk_entry_set_text(GTK_ENTRY(display_option_dialog_gtk2_rcfile),
2809     gtk2_rcfile);
2810   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(display_option_dialog_use_system_gtkrc),use_system_gtkrc);
2811 #endif
2812   gtk_entry_set_text(GTK_ENTRY(display_option_dialog_tileset_path),
2813     tileset_path ? tileset_path : "");
2814   strmcpy(old_main_font_name,main_font_name,256);
2815   if ( main_font_name[0] ) {
2816     gtk_font_selection_set_font_name(GTK_FONT_SELECTION(mfont_selector),
2817       main_font_name);
2818   }
2819   strmcpy(old_text_font_name,text_font_name,256);
2820   if ( text_font_name[0] ) {
2821     gtk_font_selection_set_font_name(GTK_FONT_SELECTION(tfont_selector),
2822       text_font_name);
2823   } else if ( fallback_text_font_name[0] ) {
2824     gtk_font_selection_set_font_name(GTK_FONT_SELECTION(tfont_selector),
2825       fallback_text_font_name);
2826   }
2827   strmcpy(old_table_colour_name,table_colour_name,256);
2828 }
2829 
2830 /* apply the selected options */
display_option_dialog_apply(void)2831 static void display_option_dialog_apply(void) {
2832   int i;
2833   unsigned int newdp = dialogs_position;
2834   int old;
2835   char *newt;
2836   GtkWidget *wwindow;
2837   int restart = 0; /* set to 1 if we need to recreate the display */
2838 
2839   /* if we produce any warnings now, and then restart, they won't be seen
2840      in the gui, since the warning window will be destroyed.
2841      So disable the warning window, and reinstate it later */
2842   wwindow = warningwindow;
2843   warningwindow = NULL;
2844 
2845   for (i=DialogsCentral; i <= DialogsPopup; i++) {
2846     if ( GTK_TOGGLE_BUTTON(display_option_dialog_dialogposn[i])->active )
2847       newdp = i;
2848   }
2849   if ( newdp != dialogs_position ) restart = 1;
2850   dialogs_position = newdp;
2851   set_animation(GTK_TOGGLE_BUTTON(display_option_dialog_animation)->active);
2852   old = info_windows_in_main;
2853   info_windows_in_main = GTK_TOGGLE_BUTTON(display_option_dialog_info_in_main)->active;
2854   if ( old != info_windows_in_main ) restart = 1;
2855   nopopups = GTK_TOGGLE_BUTTON(display_option_dialog_nopopups)->active;
2856   tiletips = GTK_TOGGLE_BUTTON(display_option_dialog_tiletips)->active;
2857   int old_rotatelabels = rotatelabels;
2858   rotatelabels = GTK_TOGGLE_BUTTON(display_option_dialog_rotatelabels)->active;
2859   if ( old_rotatelabels != rotatelabels ) {
2860     gtk_label_set_angle(pdisps[3].infolab, rotatelabels ? 270 : 0);
2861     gtk_label_set_angle(pdisps[1].infolab, rotatelabels ? 90 : 0);
2862   }
2863   thinking_claim =  GTK_TOGGLE_BUTTON(display_option_dialog_thinking_claim)->active;
2864   alert_mahjong =  GTK_TOGGLE_BUTTON(display_option_dialog_alert_mahjong)->active;
2865   iconify_dialogs_with_main = GTK_TOGGLE_BUTTON(display_option_dialog_iconify)->active;
2866   i =
2867     (GTK_TOGGLE_BUTTON(display_option_dialog_showwall[0])->active ? 0 :
2868      GTK_TOGGLE_BUTTON(display_option_dialog_showwall[1])->active ? 1 : -1 );
2869   if ( pref_showwall != i ) {
2870     restart = 1;
2871     showwall = pref_showwall = i;
2872   }
2873   for (i=0;i<3
2874 	 && !GTK_TOGGLE_BUTTON(display_option_dialog_sort_tiles[i])->active;
2875        i++);
2876   if ( i < 3 ) sort_tiles = i;
2877   old = display_size;
2878   newt = (char *)gtk_entry_get_text(GTK_ENTRY(display_option_size_entry));
2879   if ( strcmp(newt,"(auto)") == 0 ) {
2880     display_size = 0;
2881   } else {
2882     sscanf(newt,"%d",&display_size);
2883   }
2884   if ( display_size != old ) restart = 1;
2885   newt = (char *)gtk_entry_get_text(GTK_ENTRY(display_option_dialog_tileset));
2886   if ( ! tileset ) tileset = "";
2887   if ( strcmp(newt,tileset) != 0 ) {
2888     restart = 1;
2889     tileset = newt;
2890   }
2891   newt = (char *)gtk_entry_get_text(GTK_ENTRY(display_option_dialog_tileset_path));
2892   if ( ! tileset_path ) tileset_path = "";
2893   if ( strcmp(newt,tileset_path) != 0 ) {
2894     restart = 1;
2895     tileset_path = newt;
2896   }
2897 #ifdef GTK2
2898   newt = (char *)gtk_entry_get_text(GTK_ENTRY(display_option_dialog_gtk2_rcfile));
2899   if ( strcmp(newt,gtk2_rcfile) != 0 ) {
2900     /* we can't restart for this one */
2901     error_dialog_popup("Restart xmj for gtk2_rcfile change to take effect");
2902     strmcpy(gtk2_rcfile,newt,sizeof(gtk2_rcfile));
2903   }
2904   old = use_system_gtkrc;
2905   use_system_gtkrc = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(display_option_dialog_use_system_gtkrc));
2906   if ( old != use_system_gtkrc ) {
2907     /* we can't restart for this one */
2908     error_dialog_popup("Restart xmj for use_system_gtkrc change to take effect");
2909   }
2910 #endif
2911   if ( strncmp(old_main_font_name,main_font_name,256) != 0 ) {
2912 #ifdef GTK2
2913     char c[300];
2914     strcpy(c,"gtk-font-name = \"");
2915     char *d = c + strlen(c);
2916     strmcpy(d,main_font_name,256);
2917     strcat(c,"\"");
2918     gtk_rc_parse_string(c);
2919     gtk_rc_reset_styles(gtk_settings_get_default());
2920 #else
2921     restart = 1;
2922 #endif
2923   }
2924   if ( strncmp(old_text_font_name,text_font_name,256) != 0 ) {
2925 #ifdef GTK2
2926     char c[300];
2927     strcpy(c,"style \"text\" { font_name = \"");
2928     char *d = c + strlen(c);
2929     strmcpy(d,text_font_name,256);
2930     strcat(c,"\" }");
2931     gtk_rc_parse_string(c);
2932     gtk_rc_reset_styles(gtk_settings_get_default());
2933 #else
2934     restart = 1;
2935 #endif
2936   }
2937   if ( strncmp(old_table_colour_name,table_colour_name,256) != 0 ) {
2938 #ifdef GTK2
2939     char c[300];
2940     strcpy(c,"style \"table\" { bg[NORMAL] = \"");
2941     char *d = c + strlen(c);
2942     strmcpy(d,table_colour_name,256);
2943     strcat(c,"\" }");
2944     gtk_rc_parse_string(c);
2945     gtk_rc_reset_styles(gtk_settings_get_default());
2946 #else
2947     restart = 1;
2948 #endif
2949   }
2950   close_saving_posn(display_option_dialog);
2951   if ( restart ) {
2952     /* need to destroy the warning window ourselves, since
2953        we've nulled it */
2954     if ( wwindow ) gtk_widget_destroy(wwindow);
2955     destroy_display();
2956     create_display();
2957   } else {
2958     /* re-instate warnings */
2959     warningwindow = wwindow;
2960     log_msg_add(0,NULL);
2961   }
2962 }
2963 
2964 /* save options */
display_option_dialog_save(void)2965 static void display_option_dialog_save(void) {
2966   /* first apply */
2967   display_option_dialog_apply();
2968   /* then save */
2969   if ( read_or_update_rcfile(NULL,XmjrcNone,XmjrcDisplay) == 0 ) {
2970     error_dialog_popup("Error updating rc file");
2971   }
2972 }
2973 
2974 /* various widgets needed in the playing prefs dialog */
2975 static GtkWidget *playing_prefs_auto_specials;
2976 static GtkWidget *playing_prefs_auto_losing;
2977 static GtkWidget *playing_prefs_auto_winning;
2978 
2979 static void playing_prefs_dialog_apply(void);
2980 static void playing_prefs_dialog_save(void);
2981 static void playing_prefs_dialog_refresh(void);
2982 
2983 /* dialog for specifying playing preferences */
playing_prefs_dialog_init(void)2984 static void playing_prefs_dialog_init(void) {
2985   GtkWidget *box, *but1, *w1, *w2;
2986 
2987   playing_prefs_dialog = gtk_window_new(GTK_WINDOW_DIALOG);
2988   gtk_signal_connect (GTK_OBJECT (playing_prefs_dialog), "delete_event",GTK_SIGNAL_FUNC(gtk_widget_hide), NULL);
2989   gtk_container_set_border_width(GTK_CONTAINER(playing_prefs_dialog),
2990 				 dialog_border_width);
2991 
2992   box = gtk_vbox_new(0,dialog_vert_spacing);
2993   gtk_widget_show(box);
2994   gtk_container_add(GTK_CONTAINER(playing_prefs_dialog),box);
2995 
2996   w1 = gtk_label_new("Automatic declarations:");
2997   gtk_widget_show(w1);
2998   gtk_box_pack_start(GTK_BOX(box),w1,0,0,0);
2999 
3000   /* declaring specials */
3001   playing_prefs_auto_specials = but1 =
3002     gtk_check_button_new_with_label("flowers and seasons");
3003   gtk_widget_show(but1);
3004   gtk_box_pack_start(GTK_BOX(box),but1,0,0,0);
3005 
3006   /* declaring losing hands */
3007   playing_prefs_auto_losing = but1 =
3008     gtk_check_button_new_with_label("losing hands");
3009   gtk_widget_show(but1);
3010   gtk_box_pack_start(GTK_BOX(box),but1,0,0,0);
3011 
3012   /* declaring winning hands */
3013   playing_prefs_auto_winning = but1 =
3014     gtk_check_button_new_with_label("winning hands");
3015   gtk_widget_show(but1);
3016   gtk_box_pack_start(GTK_BOX(box),but1,0,0,0);
3017 
3018   /* apply, save and close buttons */
3019   w1 = gtk_hbox_new(0,dialog_button_spacing);
3020   gtk_widget_show(w1);
3021   gtk_box_pack_end(GTK_BOX(box),w1,0,0,0);
3022 
3023   w2 = gtk_button_new_with_label("Save & Apply");
3024   gtk_widget_show(w2);
3025   gtk_box_pack_start(GTK_BOX(w1),w2,0,0,0);
3026   gtk_signal_connect(GTK_OBJECT(w2),"clicked",GTK_SIGNAL_FUNC(playing_prefs_dialog_save),NULL);
3027 
3028   w2 = gtk_button_new_with_label("Apply (no save)");
3029   gtk_widget_show(w2);
3030   gtk_box_pack_start(GTK_BOX(w1),w2,0,0,0);
3031   gtk_signal_connect(GTK_OBJECT(w2),"clicked",GTK_SIGNAL_FUNC(playing_prefs_dialog_apply),NULL);
3032 
3033   w2 = gtk_button_new_with_label("Cancel");
3034   gtk_widget_show(w2);
3035   gtk_box_pack_start(GTK_BOX(w1),w2,0,0,0);
3036   gtk_signal_connect_object(GTK_OBJECT(w2),"clicked",GTK_SIGNAL_FUNC(gtk_widget_hide),GTK_OBJECT(playing_prefs_dialog));
3037 }
3038 
3039 /* make the playing prefs panel match reality */
playing_prefs_dialog_refresh(void)3040 static void playing_prefs_dialog_refresh(void) {
3041   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(playing_prefs_auto_specials),
3042     playing_auto_declare_specials);
3043   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(playing_prefs_auto_losing),
3044     playing_auto_declare_losing);
3045   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(playing_prefs_auto_winning),
3046     playing_auto_declare_winning);
3047 
3048   gtk_widget_hide(playing_prefs_dialog);
3049 }
3050 
3051 /* apply the selected playing options */
playing_prefs_dialog_apply(void)3052 static void playing_prefs_dialog_apply(void) {
3053   playing_auto_declare_specials = GTK_TOGGLE_BUTTON(playing_prefs_auto_specials)->active;
3054   playing_auto_declare_losing = GTK_TOGGLE_BUTTON(playing_prefs_auto_losing)->active;
3055   playing_auto_declare_winning = GTK_TOGGLE_BUTTON(playing_prefs_auto_winning)->active;
3056   gtk_widget_hide(playing_prefs_dialog);
3057 }
3058 
3059 /* save playing options */
playing_prefs_dialog_save(void)3060 static void playing_prefs_dialog_save(void) {
3061   /* first apply */
3062   playing_prefs_dialog_apply();
3063   /* then save */
3064   if ( read_or_update_rcfile(NULL,XmjrcNone,XmjrcPlaying) == 0 ) {
3065     error_dialog_popup("Error updating rc file");
3066   }
3067 }
3068 
3069 /* debug options dialog */
3070 
3071 static void debug_options_dialog_apply(void);
3072 static void debug_options_dialog_save(void);
3073 static void debug_options_dialog_refresh(void);
3074 
3075 static GtkWidget *debug_options_report_buttons[DebugReportsAlways+1];
3076 
3077 static void debug_options_dialog_init(void);
3078 
debug_report_query_popup(void)3079 static void debug_report_query_popup(void) {
3080   if ( ! debug_options_dialog ) debug_options_dialog_init();
3081   dialog_popup(debug_options_reporting,DPCentred);
3082 }
3083 
debug_options_dialog_init(void)3084 static void debug_options_dialog_init(void) {
3085   GtkWidget *box, *but0, *but1, *but2, *but3, *w1, *w2;
3086 
3087   debug_options_dialog = gtk_window_new(GTK_WINDOW_DIALOG);
3088   gtk_signal_connect (GTK_OBJECT (debug_options_dialog), "delete_event",GTK_SIGNAL_FUNC(gtk_widget_hide), NULL);
3089   gtk_container_set_border_width(GTK_CONTAINER(debug_options_dialog),
3090 				 dialog_border_width);
3091 
3092   box = gtk_vbox_new(0,dialog_vert_spacing);
3093   gtk_widget_show(box);
3094   gtk_container_add(GTK_CONTAINER(debug_options_dialog),box);
3095   w1 = gtk_label_new("Fault reporting:\n"
3096     "When certain internal errors occur, this program\n"
3097     "can file a debugging report over the Internet to the author.\n"
3098     "This report includes the current game status; it may include\n"
3099     "the entire game history. Therefore the report may contain\n"
3100     "sensitive information such as your username and password for the\n"
3101     "www.TUMJ.com game server.\n"
3102     "When may a debugging report be sent over the Internet?");
3103   gtk_widget_show(w1);
3104   gtk_box_pack_start(GTK_BOX(box),w1,0,0,0);
3105 
3106   but0 = gtk_radio_button_new_with_label(NULL,"unspecified");
3107   /* gtk_widget_show(but0); */
3108   gtk_box_pack_start(GTK_BOX(box),but0,0,0,0);
3109   debug_options_report_buttons[DebugReportsUnspecified] = but0;
3110 
3111   but1 = gtk_radio_button_new_with_label(gtk_radio_button_group(GTK_RADIO_BUTTON(but0)),"never");
3112   gtk_widget_show(but1);
3113   gtk_box_pack_start(GTK_BOX(box),but1,0,0,0);
3114   debug_options_report_buttons[DebugReportsNever] = but1;
3115 
3116   but2 = gtk_radio_button_new_with_label(gtk_radio_button_group(GTK_RADIO_BUTTON(but0)),"ask each time");
3117   gtk_widget_show(but2);
3118   gtk_box_pack_start(GTK_BOX(box),but2,0,0,0);
3119   debug_options_report_buttons[DebugReportsAsk] = but2;
3120 
3121   but3 = gtk_radio_button_new_with_label(gtk_radio_button_group(GTK_RADIO_BUTTON(but0)),"always");
3122   gtk_widget_show(but3);
3123   gtk_box_pack_start(GTK_BOX(box),but3,0,0,0);
3124   debug_options_report_buttons[DebugReportsAlways] = but3;
3125 
3126   /* apply, save and close buttons */
3127   w1 = gtk_hbox_new(0,dialog_button_spacing);
3128   gtk_widget_show(w1);
3129   gtk_box_pack_end(GTK_BOX(box),w1,0,0,0);
3130 
3131   w2 = gtk_button_new_with_label("Save & Apply");
3132   gtk_widget_show(w2);
3133   gtk_box_pack_start(GTK_BOX(w1),w2,0,0,0);
3134   gtk_signal_connect(GTK_OBJECT(w2),"clicked",GTK_SIGNAL_FUNC(debug_options_dialog_save),NULL);
3135 
3136   w2 = gtk_button_new_with_label("Apply (no save)");
3137   gtk_widget_show(w2);
3138   gtk_box_pack_start(GTK_BOX(w1),w2,0,0,0);
3139   gtk_signal_connect(GTK_OBJECT(w2),"clicked",GTK_SIGNAL_FUNC(debug_options_dialog_apply),NULL);
3140 
3141   w2 = gtk_button_new_with_label("Cancel");
3142   gtk_widget_show(w2);
3143   gtk_box_pack_start(GTK_BOX(w1),w2,0,0,0);
3144   gtk_signal_connect_object(GTK_OBJECT(w2),"clicked",GTK_SIGNAL_FUNC(gtk_widget_hide),GTK_OBJECT(debug_options_dialog));
3145 
3146 
3147   /* Now create the dialog that is popped up each time (if necessary)
3148      to ask whether to file a report */
3149   if ( debug_options_reporting ) {
3150     gtk_widget_destroy(debug_options_reporting);
3151   }
3152 
3153   debug_options_reporting =  gtk_window_new(GTK_WINDOW_DIALOG);
3154   gtk_signal_connect (GTK_OBJECT (debug_options_reporting), "delete_event",GTK_SIGNAL_FUNC(gtk_widget_hide), NULL);
3155   gtk_container_set_border_width(GTK_CONTAINER(debug_options_reporting),
3156 				 dialog_border_width);
3157 
3158   box = gtk_vbox_new(0,dialog_vert_spacing);
3159   gtk_widget_show(box);
3160   gtk_container_add(GTK_CONTAINER(debug_options_reporting),box);
3161   w1 = gtk_label_new("This program has encountered an unexpected error.\n"
3162     "It can file a debugging report over the Internet to the author.\n"
3163     "This report includes the current game status; it may include\n"
3164     "the entire game history. Therefore the report may contain\n"
3165     "sensitive information such as your username and password for the\n"
3166     "www.TUMJ.com game server.\n"
3167     "May the report be sent?");
3168   gtk_widget_show(w1);
3169   gtk_box_pack_start(GTK_BOX(box),w1,0,0,0);
3170 
3171   w1 = gtk_hbox_new(0,dialog_button_spacing);
3172   gtk_widget_show(w1);
3173   gtk_box_pack_end(GTK_BOX(box),w1,0,0,0);
3174 
3175   w2 = gtk_button_new_with_label("Send report");
3176   gtk_widget_show(w2);
3177   gtk_box_pack_start(GTK_BOX(w1),w2,0,0,0);
3178   gtk_signal_connect_object(GTK_OBJECT(w2),"clicked",GTK_SIGNAL_FUNC(file_report),(gpointer)1);
3179 
3180   w2 = gtk_button_new_with_label("Cancel");
3181   gtk_widget_show(w2);
3182   gtk_box_pack_start(GTK_BOX(w1),w2,0,0,0);
3183   gtk_signal_connect_object(GTK_OBJECT(w2),"clicked",GTK_SIGNAL_FUNC(file_report),NULL);
3184 
3185 }
3186 
debug_options_dialog_refresh(void)3187 static void debug_options_dialog_refresh(void) {
3188   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(debug_options_report_buttons[debug_reports]),1);
3189 }
3190 
debug_options_dialog_apply(void)3191 static void debug_options_dialog_apply(void) {
3192   int i;
3193   for ( i=DebugReportsNever; i <= DebugReportsAlways; i++ ) {
3194     if ( GTK_TOGGLE_BUTTON(debug_options_report_buttons[i])->active )
3195       debug_reports = i;
3196   }
3197   gtk_widget_hide(debug_options_dialog);
3198 }
3199 
debug_options_dialog_save(void)3200 static void debug_options_dialog_save(void) {
3201   /* first apply */
3202   debug_options_dialog_apply();
3203   /* then save */
3204   if ( read_or_update_rcfile(NULL,XmjrcNone,XmjrcMisc) == 0 ) {
3205     error_dialog_popup("Error updating rc file");
3206   }
3207 }
3208 
3209 
3210 
3211 /* callback used below */
enabler_callback(GtkWidget * w UNUSED,gpointer data)3212 static void enabler_callback(GtkWidget *w UNUSED, gpointer data)
3213 {
3214   GameOptionEntry *goe = data;
3215   goe->enabled = ! goe->enabled;
3216   make_or_refresh_option_updater(goe,0);
3217 }
3218 
3219 /* ditto */
make_sensitive(GtkWidget * w)3220 static void make_sensitive(GtkWidget *w)
3221 {
3222   gtk_widget_set_sensitive(w,1);
3223 }
3224 
3225 /* given a GameOptionEntry, create
3226    or refresh an updater widget, stored in the userdata
3227    field of the entry.
3228    Second arg says if this is preference panel (or option panel)
3229 */
make_or_refresh_option_updater(GameOptionEntry * goe,int prefsp)3230 GtkWidget *make_or_refresh_option_updater(GameOptionEntry *goe,int prefsp)
3231 {
3232   /*      description of option
3233           current value   new value  update
3234   */
3235   GtkWidget *vbox = NULL, *hbox = NULL, *ohbox, *w1 = NULL, *w2,
3236     *old, *lim, *halflim, *nolim, *resetter;
3237   GtkAdjustment *adj;
3238   int dbls, pts;
3239   int centilims;
3240 
3241   old = goe->userdata;
3242   if ( ! old ) {
3243     resetter = gtk_button_new_with_label("Reset"); /* needed early */
3244   } else {
3245     resetter = (GtkWidget *)gtk_object_get_data(GTK_OBJECT(old),"reset");
3246   }
3247   if ( ! old ) {
3248     vbox = gtk_vbox_new(0,5);
3249     gtk_widget_show(vbox);
3250     if ( prefsp ) {
3251       w2 = gtk_hbox_new(0,5);
3252       gtk_widget_show(w2);
3253       gtk_box_pack_start(GTK_BOX(vbox),w2,0,0,0);
3254       w1 = gtk_button_new_with_label("Add pref");
3255       GTK_WIDGET_UNSET_FLAGS(w1,GTK_CAN_FOCUS);
3256       gtk_object_set_data(GTK_OBJECT(vbox),"enabled",(gpointer) w1);
3257       gtk_signal_connect(GTK_OBJECT(w1),"clicked",GTK_SIGNAL_FUNC(enabler_callback),(gpointer) goe);
3258       gtk_widget_show(w1);
3259       gtk_box_pack_start(GTK_BOX(w2),w1,0,0,0);
3260       w1 = gtk_label_new(goe->desc);
3261       gtk_widget_show(w1);
3262       gtk_box_pack_start(GTK_BOX(w2),w1,0,0,0);
3263     } else {
3264       w1 = gtk_label_new(goe->desc);
3265       gtk_widget_show(w1);
3266       gtk_box_pack_start(GTK_BOX(vbox),w1,0,0,0);
3267     }
3268     if ( prefsp ) {
3269       ohbox = gtk_hbox_new(0,0);
3270       gtk_widget_show(ohbox);
3271       gtk_box_pack_start(GTK_BOX(vbox),ohbox,0,0,0);
3272       hbox = gtk_hbox_new(0,0);
3273       gtk_object_set_data(GTK_OBJECT(vbox),"hbox",(gpointer) hbox);
3274       gtk_widget_show(hbox);
3275       gtk_box_pack_start(GTK_BOX(ohbox),hbox,1,1,0);
3276     } else {
3277       hbox = gtk_hbox_new(0,0);
3278       gtk_widget_show(hbox);
3279       gtk_box_pack_start(GTK_BOX(vbox),hbox,0,0,0);
3280     }
3281   } else {
3282     vbox = old;
3283   }
3284   switch ( goe->type ) {
3285   case GOTBool:
3286     if ( ! old ) {
3287       w1 = gtk_hbox_new(0,0);
3288       gtk_widget_show(w1);
3289       gtk_box_pack_start(GTK_BOX(hbox),w1,0,0,5);
3290       w2 = gtk_check_button_new_with_label("on/off");
3291       gtk_signal_connect_object(GTK_OBJECT(w2),"toggled",GTK_SIGNAL_FUNC(make_sensitive),GTK_OBJECT(resetter));
3292       gtk_widget_show(w2);
3293       gtk_box_pack_start(GTK_BOX(w1),w2,0,0,5);
3294       gtk_object_set_data(GTK_OBJECT(vbox),"checkbox",w2);
3295     } else {
3296       w2 = gtk_object_get_data(GTK_OBJECT(vbox),"checkbox");
3297     }
3298     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w2),
3299 				 goe->value.optbool);
3300     break;
3301   case GOTNat:
3302   case GOTInt:
3303     if ( ! old ) {
3304       w1 = gtk_hbox_new(0,0);
3305       gtk_widget_show(w1);
3306       gtk_box_pack_start(GTK_BOX(hbox),w1,0,0,5);
3307       adj = (GtkAdjustment *)gtk_adjustment_new(goe->value.optint,
3308 						(goe->type == GOTNat ) ? 0 : -1000000,1000000,
3309 						1,10,0);
3310       w2 = gtk_spin_button_new(adj,1.0,0);
3311       gtk_widget_set_usize(w2,70,0);
3312       gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(w2),1);
3313       gtk_signal_connect_object(GTK_OBJECT(w2),"changed",GTK_SIGNAL_FUNC(make_sensitive),GTK_OBJECT(resetter));
3314       gtk_widget_show(w2);
3315       gtk_box_pack_start(GTK_BOX(w1),w2,0,0,5);
3316       gtk_object_set_data(GTK_OBJECT(vbox),"spinbutton",w2);
3317     } else {
3318       w2 = gtk_object_get_data(GTK_OBJECT(vbox),"spinbutton");
3319       gtk_spin_button_set_value(GTK_SPIN_BUTTON(w2),goe->value.optint);
3320     }
3321     break;
3322   case GOTString:
3323     if ( ! old ) {
3324       w1 = gtk_hbox_new(0,0);
3325       gtk_widget_show(w1);
3326       gtk_box_pack_start(GTK_BOX(hbox),w1,0,0,5);
3327       w2 = gtk_entry_new();
3328       gtk_signal_connect_object(GTK_OBJECT(w2),"changed",GTK_SIGNAL_FUNC(make_sensitive),GTK_OBJECT(resetter));
3329       gtk_widget_show(w2);
3330       gtk_box_pack_start(GTK_BOX(w1),w2,0,0,5);
3331       gtk_object_set_data(GTK_OBJECT(vbox),"entry",w2);
3332     } else {
3333       w2 = gtk_object_get_data(GTK_OBJECT(vbox),"entry");
3334     }
3335     gtk_entry_set_text(GTK_ENTRY(w2),goe->value.optstring);
3336     break;
3337   case GOTScore:
3338     pts = goe->value.optscore % 10000;
3339     dbls = (goe->value.optscore / 10000) % 100;
3340     centilims = goe->value.optscore/1000000;
3341     if ( ! old ) {
3342       w1 = gtk_hbox_new(0,0);
3343       gtk_widget_show(w1);
3344       gtk_box_pack_start(GTK_BOX(hbox),w1,0,0,5);
3345       lim = w2 = gtk_radio_button_new_with_label(NULL,"lim");
3346       gtk_signal_connect_object(GTK_OBJECT(w2),"toggled",GTK_SIGNAL_FUNC(make_sensitive),GTK_OBJECT(resetter));
3347       gtk_widget_show(w2);
3348       gtk_box_pack_start(GTK_BOX(w1),w2,0,0,5);
3349       gtk_object_set_data(GTK_OBJECT(vbox),"lim",w2);
3350       halflim = w2 = gtk_radio_button_new_with_label(gtk_radio_button_group (GTK_RADIO_BUTTON(lim)),"1/2 lim");
3351       gtk_signal_connect_object(GTK_OBJECT(w2),"toggled",GTK_SIGNAL_FUNC(make_sensitive),GTK_OBJECT(resetter));
3352       gtk_widget_show(w2);
3353       gtk_box_pack_start(GTK_BOX(w1),w2,0,0,5);
3354       gtk_object_set_data(GTK_OBJECT(vbox),"halflim",w2);
3355       nolim = w2 = gtk_radio_button_new_with_label(gtk_radio_button_group (GTK_RADIO_BUTTON(lim)),"");
3356       gtk_signal_connect_object(GTK_OBJECT(w2),"toggled",GTK_SIGNAL_FUNC(make_sensitive),GTK_OBJECT(resetter));
3357       gtk_widget_show(w2);
3358       gtk_box_pack_start(GTK_BOX(w1),w2,0,0,5);
3359       gtk_object_set_data(GTK_OBJECT(vbox),"nolim",w2);
3360     } else {
3361       lim = gtk_object_get_data(GTK_OBJECT(vbox),"lim");
3362       halflim = gtk_object_get_data(GTK_OBJECT(vbox),"halflim");
3363       nolim = gtk_object_get_data(GTK_OBJECT(vbox),"nolim");
3364     }
3365     switch ( centilims ) {
3366     case 100:
3367       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(lim),1);
3368       break;
3369     case 50:
3370       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(halflim),1);
3371       break;
3372     case 0:
3373       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(nolim),1);
3374       break;
3375     default:
3376       warn("Unexpected fraction (%d) of a limit in score; setting to limit",
3377 	   centilims);
3378       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(lim),1);
3379       break;
3380     }
3381 
3382     if ( ! old ) {
3383       adj = (GtkAdjustment *)gtk_adjustment_new(dbls,
3384 						0,100,
3385 						1,10,0);
3386       w2 = gtk_spin_button_new(adj,1.0,0);
3387       gtk_widget_set_usize(w2,40,0);
3388       gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(w2),1);
3389       gtk_signal_connect_object(GTK_OBJECT(w2),"changed",GTK_SIGNAL_FUNC(make_sensitive),GTK_OBJECT(resetter));
3390       gtk_widget_show(w2);
3391       gtk_box_pack_start(GTK_BOX(w1),w2,0,0,5);
3392       gtk_object_set_data(GTK_OBJECT(vbox),"dbls",w2);
3393       w2 = gtk_label_new("dbls");
3394       gtk_widget_show(w2);
3395       gtk_box_pack_start(GTK_BOX(w1),w2,0,0,0);
3396     } else {
3397       w2 = gtk_object_get_data(GTK_OBJECT(vbox),"dbls");
3398       gtk_spin_button_set_value(GTK_SPIN_BUTTON(w2),dbls);
3399     }
3400 
3401     if ( ! old ) {
3402       adj = (GtkAdjustment *)gtk_adjustment_new(pts,
3403 						0,10000,
3404 						1,10,0);
3405       w2 = gtk_spin_button_new(adj,1.0,0);
3406       gtk_widget_set_usize(w2,60,0);
3407       gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(w2),1);
3408       gtk_signal_connect_object(GTK_OBJECT(w2),"changed",GTK_SIGNAL_FUNC(make_sensitive),GTK_OBJECT(resetter));
3409       gtk_widget_show(w2);
3410       gtk_box_pack_start(GTK_BOX(w1),w2,0,0,5);
3411       gtk_object_set_data(GTK_OBJECT(vbox),"pts",w2);
3412       w2 = gtk_label_new("pts");
3413       gtk_widget_show(w2);
3414       gtk_box_pack_start(GTK_BOX(w1),w2,0,0,0);
3415     } else {
3416       w2 = gtk_object_get_data(GTK_OBJECT(vbox),"pts");
3417       gtk_spin_button_set_value(GTK_SPIN_BUTTON(w2),pts);
3418     }
3419     break;
3420   }
3421   if ( ! old ) {
3422     /* The button is created at the top so we can feed it to callbacks *
3423        w1 = gtk_button_new_with_label("Reset");
3424     */
3425     w1 = resetter;
3426     gtk_widget_show(w1);
3427     gtk_box_pack_end(GTK_BOX(hbox),w1,0,0,5);
3428     gtk_object_set_data(GTK_OBJECT(vbox),"reset",w1);
3429     gtk_signal_connect(GTK_OBJECT(w1),"clicked",GTK_SIGNAL_FUNC(option_reset_callback),
3430 		       (gpointer) goe);
3431   }
3432   if ( ! old ) goe->userdata = vbox;
3433   /* If there's an enabled button, deal with it */
3434   w1 = (GtkWidget *)gtk_object_get_data(GTK_OBJECT(vbox),"enabled");
3435   if ( w1 ) {
3436     hbox = (GtkWidget *)gtk_object_get_data(GTK_OBJECT(vbox),"hbox");
3437     if ( goe->enabled ) {
3438       gtk_widget_show(hbox);
3439       gtk_label_set_text(GTK_LABEL(GTK_BIN(w1)->child),"Remove pref");
3440     } else {
3441       gtk_widget_hide(hbox);
3442       gtk_label_set_text(GTK_LABEL(GTK_BIN(w1)->child),"Add pref");
3443     }
3444   }
3445   /* make the reset button insensitive */
3446   gtk_widget_set_sensitive(resetter,0);
3447   return vbox;
3448 }
3449 
3450 /* given an optiontable, pack a list of updating widgets into
3451    a vbox; or just refresh if already there (2nd arg)
3452    If third arg is 1, this is prefs panel, else options panel
3453  */
build_or_refresh_optionprefs_panel(GameOptionTable * got,GtkWidget * panel,int prefsp)3454 static GtkWidget *build_or_refresh_optionprefs_panel(GameOptionTable *got, GtkWidget *panel, int prefsp)
3455 {
3456   int i;
3457   GtkWidget *vbox,*hs,*u;
3458   int first = 1;
3459 
3460   if ( ! got ) {
3461     /* destroy the panel's children, if any */
3462     if ( panel ) {
3463       gtk_container_foreach(GTK_CONTAINER(panel),(GtkCallback) gtk_widget_destroy,NULL);
3464     }
3465   }
3466 
3467   if ( ! panel ) {
3468     vbox = gtk_vbox_new(0,0);
3469     gtk_widget_show(vbox);
3470   } else {
3471     vbox = panel;
3472   }
3473   for ( i = 0 ; got && i < got->numoptions; i++ ) {
3474     /* if this is actually a filler unknown option, skip it */
3475     if ( got->options[i].name[0] == 0 ) continue;
3476     /* likewise for end */
3477     if ( got->options[i].option == GOEnd ) continue;
3478     /* if this is the options panel, and the option is not enabled, skip */
3479     if ( ! prefsp && ! got->options[i].enabled ) continue;
3480     u = got->options[i].userdata;
3481     if ( ! first && u == NULL ) {
3482       hs = gtk_hseparator_new();
3483       gtk_widget_show(hs);
3484       gtk_box_pack_start(GTK_BOX(vbox),hs,0,0,0);
3485     } else first = 0;
3486     if ( u ) {
3487       make_or_refresh_option_updater(&got->options[i],prefsp);
3488     } else {
3489       u = make_or_refresh_option_updater(&got->options[i],prefsp);
3490       gtk_box_pack_start(GTK_BOX(vbox),u,0,0,5);
3491     }
3492   }
3493   return vbox;
3494 }
3495 
build_or_refresh_option_panel(GameOptionTable * got,GtkWidget * panel)3496 GtkWidget *build_or_refresh_option_panel(GameOptionTable *got, GtkWidget *panel)
3497 {
3498   return build_or_refresh_optionprefs_panel(got,panel,0);
3499 }
3500 
build_or_refresh_prefs_panel(GameOptionTable * got,GtkWidget * panel)3501 GtkWidget *build_or_refresh_prefs_panel(GameOptionTable *got, GtkWidget *panel)
3502 {
3503   return build_or_refresh_optionprefs_panel(got,panel,1);
3504 }
3505 
option_reset_callback(GtkWidget * w UNUSED,gpointer data)3506 void option_reset_callback(GtkWidget *w UNUSED, gpointer data)
3507 {
3508   make_or_refresh_option_updater((GameOptionEntry *)data,0);
3509 }
3510 
option_updater_callback(GtkWidget * w UNUSED,gpointer data UNUSED)3511 void option_updater_callback(GtkWidget *w UNUSED, gpointer data UNUSED)
3512 {
3513   GameOptionTable *got;
3514   PMsgSetGameOptionMsg psgom;
3515   char buf[1024];
3516   int centilims,dbls,pts;
3517   int i, changed, val;
3518 
3519   if ( ! the_game ) return;
3520   got = &the_game->option_table;
3521   for ( i = 0 ; i < got->numoptions ; i++ ) {
3522     w = got->options[i].userdata;
3523     if ( ! w ) continue;
3524     changed = 0;
3525     psgom.type = PMsgSetGameOption;
3526     strncpy(psgom.optname,got->options[i].name,16);
3527     switch ( got->options[i].type ) {
3528     case GOTBool:
3529       val = GTK_TOGGLE_BUTTON(gtk_object_get_data(GTK_OBJECT(w),"checkbox"))->active;
3530       psgom.optvalue = val ? "1" : "0";
3531       if ( val != got->options[i].value.optbool ) changed = 1;
3532       break;
3533     case GOTNat:
3534     case GOTInt:
3535       gtk_spin_button_update(GTK_SPIN_BUTTON(gtk_object_get_data(GTK_OBJECT(w),"spinbutton")));
3536       val = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(gtk_object_get_data(GTK_OBJECT(w),"spinbutton")));
3537       sprintf(buf,"%d",val);
3538       psgom.optvalue = buf;
3539       if ( val != got->options[i].value.optint ) changed = 1;
3540       break;
3541     case GOTString:
3542       psgom.optvalue =
3543 	(char *)gtk_entry_get_text(GTK_ENTRY(gtk_object_get_data(GTK_OBJECT(w),"entry")));
3544       if ( strcmp(psgom.optvalue,got->options[i].value.optstring) != 0 ) changed = 1;
3545       break;
3546     case GOTScore:
3547       centilims = 100*GTK_TOGGLE_BUTTON(gtk_object_get_data(GTK_OBJECT(w),"lim"))->active;
3548       centilims += 50 * GTK_TOGGLE_BUTTON(gtk_object_get_data(GTK_OBJECT(w),"halflim"))->active;
3549       gtk_spin_button_update(GTK_SPIN_BUTTON(gtk_object_get_data(GTK_OBJECT(w),"dbls")));
3550       dbls = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(gtk_object_get_data(GTK_OBJECT(w),"dbls")));
3551       gtk_spin_button_update(GTK_SPIN_BUTTON(gtk_object_get_data(GTK_OBJECT(w),"pts")));
3552       pts = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(gtk_object_get_data(GTK_OBJECT(w),"pts")));
3553       val = 1000000*centilims + 10000 * dbls + pts;
3554       sprintf(buf,"%d",val);
3555       psgom.optvalue = buf;
3556       if ( val != got->options[i].value.optscore ) changed = 1;
3557       break;
3558     }
3559     if ( changed ) send_packet(&psgom);
3560   }
3561   gtk_widget_hide(game_option_dialog);
3562 }
3563 
prefs_updater_callback(GtkWidget * w UNUSED,gpointer data UNUSED)3564 void prefs_updater_callback(GtkWidget *w UNUSED, gpointer data UNUSED)
3565 {
3566   GameOptionTable *got;
3567   char buf[1024];
3568   int centilims,dbls,pts;
3569   int i, changed, val;
3570   char *vals;
3571 
3572   got = &prefs_table;
3573   for ( i = 0 ; i < got->numoptions ; i++ ) {
3574     w = got->options[i].userdata;
3575     if ( ! w ) continue;
3576     changed = 0;
3577     switch ( got->options[i].type ) {
3578     case GOTBool:
3579       val = GTK_TOGGLE_BUTTON(gtk_object_get_data(GTK_OBJECT(w),"checkbox"))->active;
3580       if ( val != got->options[i].value.optbool ) changed = 1;
3581       got->options[i].value.optbool = val;
3582       break;
3583     case GOTNat:
3584     case GOTInt:
3585       gtk_spin_button_update(GTK_SPIN_BUTTON(gtk_object_get_data(GTK_OBJECT(w),"spinbutton")));
3586       val = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(gtk_object_get_data(GTK_OBJECT(w),"spinbutton")));
3587       sprintf(buf,"%d",val);
3588       if ( val != got->options[i].value.optint ) changed = 1;
3589       got->options[i].value.optint = val;
3590       break;
3591     case GOTString:
3592       vals =
3593 	(char *)gtk_entry_get_text(GTK_ENTRY(gtk_object_get_data(GTK_OBJECT(w),"entry")));
3594       if ( strcmp(vals,got->options[i].value.optstring) != 0 ) changed = 1;
3595       strmcpy(got->options[i].value.optstring,vals,128);
3596       break;
3597     case GOTScore:
3598       centilims = 100*GTK_TOGGLE_BUTTON(gtk_object_get_data(GTK_OBJECT(w),"lim"))->active;
3599       centilims += 50*GTK_TOGGLE_BUTTON(gtk_object_get_data(GTK_OBJECT(w),"halflim"))->active;
3600       gtk_spin_button_update(GTK_SPIN_BUTTON(gtk_object_get_data(GTK_OBJECT(w),"dbls")));
3601       dbls = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(gtk_object_get_data(GTK_OBJECT(w),"dbls")));
3602       gtk_spin_button_update(GTK_SPIN_BUTTON(gtk_object_get_data(GTK_OBJECT(w),"pts")));
3603       pts = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(gtk_object_get_data(GTK_OBJECT(w),"pts")));
3604       val = 1000000*centilims + 10000 * dbls + pts;
3605       if ( val != got->options[i].value.optscore ) changed = 1;
3606       got->options[i].value.optscore = val;
3607       break;
3608     }
3609     if ( changed ) make_or_refresh_option_updater(&got->options[i],1);
3610   }
3611   /* now actually save it */
3612   read_or_update_rcfile(NULL,XmjrcNone,XmjrcGame);
3613   /* and close */
3614   gtk_widget_hide(game_prefs_dialog);
3615 }
3616 
apply_game_prefs_callback(GtkWidget * w UNUSED,gpointer data UNUSED)3617 static void apply_game_prefs_callback(GtkWidget *w UNUSED, gpointer data UNUSED)
3618 {
3619   apply_game_prefs();
3620   gtk_widget_hide(game_prefs_dialog);
3621 }
3622 
3623 
game_option_init(void)3624 void game_option_init(void)
3625 {
3626   GtkWidget *sbar, *obox, *bbox, *tmp;
3627 
3628   game_option_dialog = gtk_window_new(GTK_WINDOW_TOPLEVEL);
3629   gtk_signal_connect (GTK_OBJECT (game_option_dialog), "delete_event",GTK_SIGNAL_FUNC(gtk_widget_hide), NULL);
3630   /* must allow shrinking */
3631   gtk_window_set_policy(GTK_WINDOW(game_option_dialog),TRUE,TRUE,FALSE);
3632   gtk_window_set_title(GTK_WINDOW(game_option_dialog),"Current Game Options");
3633   /* reasonable size is ... */
3634   gtk_window_set_default_size(GTK_WINDOW(game_option_dialog),450,400);
3635 
3636   obox = gtk_vbox_new(0,dialog_vert_spacing);
3637   gtk_container_set_border_width(GTK_CONTAINER(obox),dialog_border_width);
3638   gtk_widget_show(obox);
3639   gtk_container_add(GTK_CONTAINER(game_option_dialog),obox);
3640 
3641   game_option_panel = build_or_refresh_option_panel(the_game ?
3642 						     &the_game->option_table : NULL,
3643 						     game_option_panel);
3644   sbar = gtk_scrolled_window_new(NULL,NULL);
3645   gtk_widget_show(sbar);
3646   gtk_box_pack_start(GTK_BOX(obox),sbar,1,1,0);
3647   gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(sbar),
3648 					game_option_panel);
3649   gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sbar),
3650 				 GTK_POLICY_NEVER,GTK_POLICY_AUTOMATIC);
3651 
3652   bbox = gtk_hbox_new(1,dialog_button_spacing);
3653   gtk_widget_show(bbox);
3654   gtk_box_pack_end(GTK_BOX(obox),bbox,0,0,0);
3655 
3656   tmp = gtk_button_new_with_label("Close");
3657   GTK_WIDGET_UNSET_FLAGS(tmp,GTK_CAN_FOCUS); /* entry widget shd focus */
3658   gtk_signal_connect_object(GTK_OBJECT(tmp),"clicked",GTK_SIGNAL_FUNC(close_saving_posn),GTK_OBJECT(game_option_dialog));
3659   gtk_widget_show(tmp);
3660   gtk_box_pack_end(GTK_BOX(bbox),tmp,1,1,0);
3661 
3662   game_option_prefs_button = tmp = gtk_button_new_with_label("Apply preferences");
3663   GTK_WIDGET_UNSET_FLAGS(tmp,GTK_CAN_FOCUS); /* entry widget shd focus */
3664   gtk_signal_connect(GTK_OBJECT(tmp),"clicked",GTK_SIGNAL_FUNC(apply_game_prefs_callback),NULL);
3665   gtk_widget_show(tmp);
3666   gtk_box_pack_end(GTK_BOX(bbox),tmp,1,1,0);
3667 
3668   game_option_apply_button = tmp = gtk_button_new_with_label("Apply changes");
3669   GTK_WIDGET_UNSET_FLAGS(tmp,GTK_CAN_FOCUS); /* entry widget shd focus */
3670   gtk_signal_connect(GTK_OBJECT(tmp),"clicked",GTK_SIGNAL_FUNC(option_updater_callback),NULL);
3671   gtk_widget_show(tmp);
3672   gtk_box_pack_end(GTK_BOX(bbox),tmp,1,1,0);
3673 }
3674 
game_prefs_init(void)3675 void game_prefs_init(void)
3676 {
3677   GtkWidget *sbar, *obox, *bbox, *tmp;
3678 
3679   game_prefs_dialog = gtk_window_new(GTK_WINDOW_TOPLEVEL);
3680   gtk_signal_connect (GTK_OBJECT (game_prefs_dialog), "delete_event",GTK_SIGNAL_FUNC(gtk_widget_hide), NULL);
3681   /* must allow shrinking */
3682   gtk_window_set_policy(GTK_WINDOW(game_prefs_dialog),TRUE,TRUE,FALSE);
3683   gtk_window_set_title(GTK_WINDOW(game_prefs_dialog),"Game Preferences");
3684   /* reasonable size is ... */
3685   gtk_window_set_default_size(GTK_WINDOW(game_prefs_dialog),450,400);
3686 
3687   obox = gtk_vbox_new(0,dialog_vert_spacing);
3688   gtk_container_set_border_width(GTK_CONTAINER(obox),dialog_border_width);
3689   gtk_widget_show(obox);
3690   gtk_container_add(GTK_CONTAINER(game_prefs_dialog),obox);
3691 
3692   game_prefs_panel = build_or_refresh_prefs_panel(&prefs_table,
3693 						     game_prefs_panel);
3694   sbar = gtk_scrolled_window_new(NULL,NULL);
3695   gtk_widget_show(sbar);
3696   gtk_box_pack_start(GTK_BOX(obox),sbar,1,1,0);
3697   gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(sbar),
3698 					game_prefs_panel);
3699   gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sbar),
3700 				 GTK_POLICY_NEVER,GTK_POLICY_AUTOMATIC);
3701 
3702   bbox = gtk_hbox_new(1,dialog_button_spacing);
3703   gtk_widget_show(bbox);
3704   gtk_box_pack_end(GTK_BOX(obox),bbox,0,0,0);
3705 
3706   tmp = gtk_button_new_with_label("Cancel");
3707   GTK_WIDGET_UNSET_FLAGS(tmp,GTK_CAN_FOCUS); /* entry widget shd focus */
3708   gtk_signal_connect_object(GTK_OBJECT(tmp),"clicked",GTK_SIGNAL_FUNC(close_saving_posn),GTK_OBJECT(game_prefs_dialog));
3709   gtk_widget_show(tmp);
3710   gtk_box_pack_end(GTK_BOX(bbox),tmp,1,1,0);
3711 
3712   tmp = gtk_button_new_with_label("Save changes");
3713   GTK_WIDGET_UNSET_FLAGS(tmp,GTK_CAN_FOCUS); /* entry widget shd focus */
3714   gtk_signal_connect(GTK_OBJECT(tmp),"clicked",GTK_SIGNAL_FUNC(prefs_updater_callback),NULL);
3715   gtk_widget_show(tmp);
3716   gtk_box_pack_end(GTK_BOX(bbox),tmp,1,1,0);
3717 }
3718 
3719 /* set the dialog position, changing if necessary */
set_dialog_posn(DialogPosition p)3720 void set_dialog_posn(DialogPosition p) {
3721   if ( p != dialogs_position ) {
3722     /* if the new position is not below, we need to allow the top
3723        window to shrink down */
3724     if ( p != DialogsBelow ) {
3725       gtk_window_set_policy(GTK_WINDOW(topwindow),1,1,1);
3726     } else {
3727       /* don't let it shrink */
3728       gtk_window_set_policy(GTK_WINDOW(topwindow),0,1,0);
3729     }
3730     dialogs_position = p;
3731     create_dialogs();
3732   }
3733 }
3734 
set_animation(int a)3735 void set_animation(int a) {
3736   animate = a;
3737   /* request appropriate pause time */
3738   if ( !monitor && the_game ) {
3739     PMsgSetPlayerOptionMsg spom;
3740       spom.type = PMsgSetPlayerOption;
3741     spom.option = PODelayTime;
3742     spom.ack = 0;
3743     spom.value = animate ? 10 : 5; /* 0.5 or 1 second min delay */
3744     spom.text = NULL;
3745     send_packet(&spom);
3746   }
3747 }
3748 /* do_chow: if there's only one possible chow, do it, otherwise
3749    put up a dialog box to choose. Sneakily, this is defined as
3750    a callback, so that the choosing buttons can call this specifying
3751    the chow pos, and the main program can call it with AnyPos.
3752 */
3753 
do_chow(GtkWidget * widg UNUSED,gpointer data)3754 void do_chow(GtkWidget *widg UNUSED, gpointer data) {
3755   ChowPosition cpos = (ChowPosition)data;
3756   PMsgChowMsg cm;
3757   int i,n,j;
3758   static int lastdiscard;
3759   TileSet ts;
3760 
3761   cm.type = PMsgChow;
3762   cm.discard = the_game->serial;
3763 
3764   if ( !monitor && cpos == AnyPos ) {
3765     /* we want to avoid working and popping up again if
3766        for some reason we're already popped up. (So that
3767        this procedure is idempotent.) */
3768     if ( GTK_WIDGET_VISIBLE(chow_dialog) && cm.discard == lastdiscard )
3769       return;
3770     lastdiscard = cm.discard;
3771     ts.type = Chow;
3772     /* set up the boxes */
3773     for (n=0,i=0,j=0;i<3;i++) {
3774       if ( player_can_chow(our_player,the_game->tile,i) ) {
3775 	ts.tile = the_game->tile - i;
3776 	tilesetbox_set(&chowtsbs[i],&ts,0);
3777 	tilesetbox_highlight_nth(&chowtsbs[i],i);
3778 	gtk_widget_show(chowbuttons[i]);
3779 	n++; j = i;
3780       } else {
3781 	gtk_widget_hide(chowtsbs[i].widget);
3782 	gtk_widget_hide(chowbuttons[i]);
3783       }
3784     }
3785     /* if there is only one possibility, don't bother to ask.
3786        Also if there is no possibility: we'll then get an error
3787        from the server, which saves us having to worry about it */
3788     if ( n <= 1 ) {
3789       cpos = j;
3790     } else {
3791       /* pop down the discard dialog */
3792       gtk_widget_hide(discard_dialog->widget);
3793       dialog_popup(chow_dialog,DPCentred);
3794     }
3795   }
3796 
3797   /* Now we might have found the position */
3798   if ( cpos != AnyPos ) {
3799     cm.cpos = cpos;
3800     send_packet(&cm);
3801     /* if the chowpending flag isn't set, we must be working with an
3802        old server and be in the mahjonging state. Pop ourselves down,
3803        so that if something goes wrong it's the discard dialog that
3804        comes back up */
3805     if ( ! the_game->chowpending ) gtk_widget_hide(chow_dialog);
3806   }
3807 }
3808 
3809 /* Generic popup centered over main window.
3810    Positioning is given by
3811    DPCentred - centered over main window
3812    DPOnDiscard - bottom left corner in same place as discard dialog
3813    DPErrorPos - for error dialogs: centred over top of main window,
3814     and offset by a multiple of num_error_dialogs
3815    DPNone - don't touch the positioning at all '
3816    DPCentredOnce - centre it on first popup, then don't fiddle '
3817    DPOnDiscardOnce - on discard dialog first time, then don't fiddle '
3818    If the widget is not a window, then DPCentredOnce and DPNone
3819    are equivalent to DPCentred, and DPOnDiscardOnce is equivalent to
3820    DPOnDiscard.
3821 */
dialog_popup(GtkWidget * dialog,DPPosn posn)3822 void dialog_popup(GtkWidget *dialog,DPPosn posn) {
3823   gint x,y,w,h;
3824   GtkRequisition r = { 0, 0};
3825 
3826   /* So that we don't work if it's already popped up: */
3827   if ( GTK_WIDGET_VISIBLE(dialog) ) return;
3828 
3829   /* if the position has been set, don't mess */
3830   if ( GTK_IS_WINDOW(dialog)
3831     && gtk_object_get_data(GTK_OBJECT(dialog),"position-set") ) {
3832     gtk_widget_set_uposition(dialog,
3833       (gint)(intptr_t)gtk_object_get_data(GTK_OBJECT(dialog),"position-x"),
3834       (gint)(intptr_t)gtk_object_get_data(GTK_OBJECT(dialog),"position-y"));
3835     gtk_widget_show(dialog);
3836     // This ought to work, but seems to confuse my wm
3837     //gtk_window_present(GTK_WINDOW(dialog));
3838     return;
3839   }
3840 
3841   if ( ! GTK_IS_WINDOW(dialog) ) {
3842     if ( posn == DPCentredOnce ) posn = DPCentred;
3843     if ( posn == DPOnDiscardOnce ) posn = DPOnDiscard;
3844     if ( posn == DPNone ) posn = DPCentred;
3845   }
3846 
3847   /* get size of discard widget if necessary */
3848   if ( (posn == DPOnDiscard || posn == DPOnDiscardOnce)
3849        && discard_req.width == 0 ) {
3850     gtk_widget_size_request(discard_dialog->widget,&discard_req);
3851     gtk_widget_set_usize(discard_dialog->widget,
3852 			 discard_req.width,
3853 			 discard_req.height);
3854   }
3855 
3856   gtk_widget_size_request(dialog,&r);
3857 
3858   if ( GTK_IS_WINDOW(dialog) ) {
3859     gdk_window_get_size(topwindow->window,&w,&h);
3860     gdk_window_get_deskrelative_origin(topwindow->window,&x,&y);
3861   } else {
3862     w = discard_area_alloc.width;
3863     h = discard_area_alloc.height;
3864     x = y = 0;
3865   }
3866 
3867   if ( dialogs_position != DialogsBelow || GTK_IS_WINDOW(dialog) ) {
3868     if ( posn == DPOnDiscard ) {
3869       if ( GTK_IS_WINDOW(dialog) )
3870 	gtk_widget_set_uposition(dialog,
3871 	  x + w/2 - r.width/2
3872 	  - (discard_req.width - r.width)/2,
3873 	  y + h/2 - r.height/2
3874 	  + (discard_req.height - r.height)/2);
3875       else
3876 	gtk_fixed_move(GTK_FIXED(discard_area),dialog,
3877 	  x + w/2 - r.width/2
3878 	  - (discard_req.width - r.width)/2,
3879 	  y + h/2 - r.height/2
3880 	  + (discard_req.height - r.height)/2);
3881     } else if ( posn == DPOnDiscardOnce ) {
3882       if ( gtk_object_get_data(GTK_OBJECT(dialog),"position-set") ) {
3883 	/* do nothing */
3884       } else {
3885 	if ( GTK_IS_WINDOW(dialog) )
3886 	  gtk_widget_set_uposition(dialog,
3887 	    x + w/2 - r.width/2
3888 	    - (discard_req.width - r.width)/2,
3889 	    y + h/2 - r.height/2
3890 	    + (discard_req.height - r.height)/2);
3891 	else
3892 	  gtk_fixed_move(GTK_FIXED(discard_area),dialog,
3893 	    x + w/2 - r.width/2
3894 	    - (discard_req.width - r.width)/2,
3895 	    y + h/2 - r.height/2
3896 	    + (discard_req.height - r.height)/2);
3897 	gtk_object_set_data(GTK_OBJECT(dialog),"position-set",(gpointer)1);
3898       }
3899     } else if ( posn == DPCentred ) {
3900       if ( GTK_IS_WINDOW(dialog) )
3901 	gtk_widget_set_uposition(dialog,
3902 	  x + w/2 - r.width/2,
3903 	  y + h/2 - r.height/2);
3904       else
3905 	gtk_fixed_move(GTK_FIXED(discard_area),dialog,
3906 	  x + w/2 - r.width/2,
3907 	  y + h/2 - r.height/2);
3908     } else if ( posn == DPCentredOnce ) {
3909       if ( gtk_object_get_data(GTK_OBJECT(dialog),"position-set") ) {
3910 	/* do nothing */
3911 #ifdef GTK2
3912 	if ( GTK_IS_WINDOW(dialog) ) {
3913 	  gtk_widget_set_uposition(dialog,
3914 	    (gint)(intptr_t)gtk_object_get_data(GTK_OBJECT(dialog),"position-x"),
3915 	    (gint)(intptr_t)gtk_object_get_data(GTK_OBJECT(dialog),"position-y"));
3916 	}
3917 #endif
3918       } else {
3919 	if ( GTK_IS_WINDOW(dialog) )
3920 	  gtk_widget_set_uposition(dialog,
3921 	    x + w/2 - r.width/2,
3922 	    y + h/2 - r.height/2);
3923 	else
3924 	  gtk_fixed_move(GTK_FIXED(discard_area),dialog,
3925 	    x + w/2 - r.width/2,
3926 	    y + h/2 - r.height/2);
3927 	gtk_object_set_data(GTK_OBJECT(dialog),"position-set",(gpointer)1);
3928       }
3929     } else if ( posn == DPErrorPos ) {
3930       if ( GTK_IS_WINDOW(dialog) )
3931 	gtk_widget_set_uposition(dialog,
3932 	  x + w/2 - r.width/2,
3933 	  y + h/4 - r.height/2 + 10*num_error_dialogs);
3934       else
3935 	 gtk_fixed_move(GTK_FIXED(discard_area),dialog,
3936 	   x + w/2 - r.width/2,
3937 	   y + h/4 - r.height/2 + 10*num_error_dialogs);
3938     }
3939   }
3940   gtk_widget_show(dialog);
3941   if ( GTK_IS_WINDOW(dialog) ) {
3942     // This ought to work, but seems to confuse my wm
3943     // gtk_window_present(GTK_WINDOW(dialog));
3944   } else {
3945     // This ought to work, but seems to confuse my wm
3946     // gtk_window_present(GTK_WINDOW(topwindow));
3947   }
3948 }
3949 
3950 /* window to display game status.
3951      0      1    2...  3....
3952    We are WIND; id: n  Name: name
3953      total score: nnn
3954    and for right, opposite, left
3955 */
3956 static char *status_poses[] = { "We are ", "Right is ", "Opp. is ", "Left is " };
3957 static GtkWidget *status_poslabels[4];
3958 static GtkWidget *status_windlabels[4];
3959 static GtkWidget *status_idlabels[4];
3960 static GtkWidget *status_namelabels[4];
3961 static GtkWidget *status_scorelabels[4];
3962 static GtkWidget *status_pwind;
3963 static GtkWidget *status_status;
3964 static GtkWidget *status_suspended;
3965 static GtkWidget *status_tilesleft;
3966 /* and for the short version */
3967 static GtkWidget *status_chairs[4];
3968 
status_init(void)3969 void status_init(void) {
3970   int i;
3971   GtkWidget *table, *w;
3972 
3973   if ( info_windows_in_main ) {
3974     status_window = gtk_hbox_new(0,0);
3975     gtk_widget_show(status_window);
3976     gtk_box_pack_start(GTK_BOX(info_box),status_window,0,0,0);
3977   } else {
3978     status_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
3979     gtk_signal_connect(GTK_OBJECT (status_window), "delete_event",GTK_SIGNAL_FUNC(gtk_widget_hide), NULL);
3980     gtk_window_set_title(GTK_WINDOW(status_window),"Game information");
3981   }
3982   gtk_container_set_border_width(GTK_CONTAINER(status_window),
3983     dialog_border_width);
3984 
3985   /* this is what we used to do, and will still do for separate
3986      windows */
3987   if ( ! info_windows_in_main ) {
3988     table = gtk_table_new(12,4,0);
3989     gtk_widget_show(table);
3990     gtk_container_add(GTK_CONTAINER(status_window),table);
3991     for ( i = 0 ; i < 4 ; i++ ) {
3992       status_poslabels[i] = w = gtk_label_new(status_poses[i]);
3993       gtk_widget_show(w);
3994       gtk_label_set_justify(GTK_LABEL(w),GTK_JUSTIFY_LEFT);
3995       gtk_table_attach_defaults(GTK_TABLE(table),w,
3996 	0,1,2*i,2*i+1);
3997       status_windlabels[i] = w = gtk_label_new("none");
3998       gtk_widget_show(w);
3999       gtk_label_set_justify(GTK_LABEL(w),GTK_JUSTIFY_LEFT);
4000       gtk_table_attach_defaults(GTK_TABLE(table),w,
4001 	1,2,2*i,2*i+1);
4002       status_idlabels[i] = w = gtk_label_new("  ID: 0");
4003       gtk_widget_show(w);
4004       gtk_label_set_justify(GTK_LABEL(w),GTK_JUSTIFY_LEFT);
4005       gtk_table_attach_defaults(GTK_TABLE(table),w,
4006 	2,3,2*i,2*i+1);
4007       status_namelabels[i] = w = gtk_label_new("  Name: unknown");
4008       gtk_widget_show(w);
4009       gtk_label_set_justify(GTK_LABEL(w),GTK_JUSTIFY_LEFT);
4010       gtk_table_attach_defaults(GTK_TABLE(table),w,
4011 	3,4,2*i,2*i+1);
4012       status_scorelabels[i] = w = gtk_label_new("total score:     0");
4013       gtk_widget_show(w);
4014       gtk_label_set_justify(GTK_LABEL(w),GTK_JUSTIFY_LEFT);
4015       gtk_table_attach_defaults(GTK_TABLE(table),w,
4016 	1,4,2*i+1,2*i+2);
4017 
4018     }
4019     status_pwind = w = gtk_label_new("Prevailing wind: none");
4020     gtk_widget_show(w);
4021     gtk_table_attach_defaults(GTK_TABLE(table),w,
4022       0,4,8,9);
4023 
4024     status_status = w = gtk_label_new("no game");
4025     gtk_widget_show(w);
4026     gtk_table_attach_defaults(GTK_TABLE(table),w,
4027       0,4,9,10);
4028 
4029     status_tilesleft = w = gtk_label_new("");
4030     gtk_widget_show(w);
4031     gtk_table_attach_defaults(GTK_TABLE(table),w,
4032       0,4,10,11);
4033 
4034     w = gtk_button_new_with_label("Close");
4035     gtk_widget_show(w);
4036     gtk_table_attach_defaults(GTK_TABLE(table),w,
4037       0,4,11,12);
4038     gtk_signal_connect_object(GTK_OBJECT(w),"clicked",GTK_SIGNAL_FUNC(close_saving_posn),GTK_OBJECT(status_window));
4039 
4040   } else {
4041     /* and this is for the new in-window information */
4042     table = gtk_table_new(4,7,0);
4043     gtk_widget_show(table);
4044     gtk_container_add(GTK_CONTAINER(status_window),table);
4045     /* opposite player */
4046     status_chairs[2] = w = gtk_label_new("(seat empty)");
4047     gtk_widget_show(w);
4048     gtk_table_attach_defaults(GTK_TABLE(table),w,1,4,0,1);
4049     /* left player */
4050     status_chairs[3] = w = gtk_label_new("(seat empty)");
4051     gtk_label_set_justify(GTK_LABEL(w),GTK_JUSTIFY_LEFT);
4052     gtk_widget_show(w);
4053     gtk_table_attach_defaults(GTK_TABLE(table),w,0,3,1,2);
4054     /* right player */
4055     status_chairs[1] = w = gtk_label_new("(seat empty)");
4056     gtk_label_set_justify(GTK_LABEL(w),GTK_JUSTIFY_RIGHT);
4057     gtk_widget_show(w);
4058     gtk_table_attach_defaults(GTK_TABLE(table),w,2,5,2,3);
4059     /* us */
4060     status_chairs[0] = w = gtk_label_new("(seat empty)");
4061     gtk_widget_show(w);
4062     gtk_table_attach_defaults(GTK_TABLE(table),w,1,4,3,4);
4063     /* spacing */
4064     w = gtk_label_new("     ");
4065     gtk_widget_show(w);
4066     gtk_table_attach_defaults(GTK_TABLE(table),w,5,6,0,1);
4067     /* round */
4068     status_pwind = w = gtk_label_new("     ");
4069     gtk_widget_show(w);
4070     gtk_table_attach_defaults(GTK_TABLE(table),w,6,7,0,1);
4071     /* game status */
4072     status_status = w = gtk_label_new("no game");
4073     gtk_widget_show(w);
4074     gtk_table_attach_defaults(GTK_TABLE(table),w,6,7,1,2);
4075     /* tiles left */
4076     status_tilesleft = w = gtk_label_new("");
4077     gtk_widget_show(w);
4078     gtk_table_attach_defaults(GTK_TABLE(table),w,6,7,2,3);
4079     /* game suspended */
4080     status_suspended = w = gtk_label_new("");
4081     gtk_widget_show(w);
4082     gtk_table_attach_defaults(GTK_TABLE(table),w,6,7,3,4);
4083   }
4084 }
4085 
status_update(int game_over)4086 void status_update(int game_over) {
4087   int i,s;
4088   const char *pn;
4089   PlayerP p;
4090   static char buf[256];
4091 
4092   if ( ! the_game ) return;
4093 
4094   for ( i=0 ; i < 4 ; i++ ) {
4095     s = (our_seat+i)%NUM_SEATS;
4096     p = the_game->players[s];
4097     if ( !info_windows_in_main ) {
4098       gtk_label_set_text(GTK_LABEL(status_windlabels[i]),
4099 	windnames[p->wind]);
4100       sprintf(buf,"  ID: %d",p->id);
4101       gtk_label_set_text(GTK_LABEL(status_idlabels[i]),buf);
4102       snprintf(buf,256,"  Name: %s",p->name);
4103       gtk_label_set_text(GTK_LABEL(status_namelabels[i]),buf);
4104       sprintf(buf,"total score: %5d",p->cumulative_score);
4105       gtk_label_set_text(GTK_LABEL(status_scorelabels[i]),buf);
4106     } else {
4107       snprintf(buf,256,p->hand_score >= 0 ? "(%s) %s[%d]: %d (%d)"
4108 	: "(%s) %s[%d]: %d",
4109 	shortwindnames[p->wind],p->name,p->id,p->cumulative_score,
4110 	p->hand_score);
4111       gtk_label_set_text(GTK_LABEL(status_chairs[i]),buf);
4112     }
4113 #ifdef GTK2
4114     if ( showwall ) {
4115       snprintf(buf,256,p->hand_score >= 0 ? "%s [%d]\n%s\nTotal: %d\nThis hand: %d" : "%s [%d]\n%s\nTotal: %d\nThis hand:   ",
4116 	p->name,p->id,windnames[p->wind],p->cumulative_score,
4117 	p->hand_score);
4118       gtk_label_set_text(pdisps[i].infolab,buf);
4119     }
4120 #endif
4121   }
4122 
4123   sprintf(buf,info_windows_in_main ? "%s round" : "Prevailing wind: %s",
4124     windnames[the_game->round]);
4125   gtk_label_set_text(GTK_LABEL(status_pwind),buf);
4126 
4127   pn = (info_windows_in_main ? shortwindnames : windnames)[the_game->player+1]; /* +1 for seat to wind */
4128   switch (the_game->state) {
4129   case Dealing:
4130     sprintf(buf,"Dealing");
4131     break;
4132   case DeclaringSpecials:
4133     sprintf(buf,"%s declaring specials",pn);
4134     break;
4135   case Discarding:
4136     sprintf(buf,"%s to discard",pn);
4137     break;
4138   case Discarded:
4139     sprintf(buf,"%s has discarded",pn);
4140     break;
4141   case MahJonging:
4142     sprintf(buf,"%s has Mah Jong",pn);
4143     break;
4144   case HandComplete:
4145     sprintf(buf,"Hand finished");
4146     break;
4147   }
4148   if ( !info_windows_in_main ) {
4149     if ( !the_game->active ) strcat(buf," (Play suspended)");
4150   }
4151   if ( game_over ) strcpy(buf,"GAME OVER");
4152   gtk_label_set_text(GTK_LABEL(status_status),buf);
4153   if ( ! info_windows_in_main ) {
4154     sprintf(buf,"%3d tiles left + %2d dead tiles",
4155       the_game->wall.live_end-the_game->wall.live_used,
4156       the_game->wall.dead_end-the_game->wall.live_end);
4157   } else {
4158     sprintf(buf,"%3d tiles left + %2d dead tiles",
4159       the_game->wall.live_end-the_game->wall.live_used,
4160       the_game->wall.dead_end-the_game->wall.live_end);
4161   }
4162   gtk_label_set_text(GTK_LABEL(status_tilesleft),buf);
4163   if ( info_windows_in_main ) {
4164     gtk_label_set_text(GTK_LABEL(status_suspended),
4165       the_game->active ? "" : "Play suspended");
4166   }
4167 }
4168 
showraise(GtkWidget * w)4169 void showraise(GtkWidget *w) {
4170   if ( GTK_IS_WINDOW(w)
4171     && gtk_object_get_data(GTK_OBJECT(w),"position-set") ) {
4172     gtk_widget_set_uposition(w,
4173       (gint)(intptr_t)gtk_object_get_data(GTK_OBJECT(w),"position-x"),
4174       (gint)(intptr_t)gtk_object_get_data(GTK_OBJECT(w),"position-y"));
4175   }
4176   gtk_widget_show(w);
4177   gdk_window_raise(w->window);
4178 #ifndef GTK2
4179   /* I forget why this is there - but it seems to break position
4180      saving etc. in gtk2 */
4181   gdk_window_show(w->window);
4182 #endif
4183 }
4184 
about_init(void)4185 static void about_init(void) {
4186   GtkWidget *closebutton, *textw, *vbox;
4187   static char *abouttxt =
4188 "This is  xmj , part of the Mah-Jong for Unix (etc)\n"
4189 "set of programs.\n"
4190 "Copyright (c) J. C. Bradfield 2000-2014.\n"
4191 "Distributed under the Gnu General Public License, version 2.\n"
4192 "This is version " VERSION " (protocol version " STRINGIFY(PROTOCOL_VERSION) ").\n"
4193 "User documentation is in the  xmj  manual page.\n"
4194 "Latest versions, information etc. may be found at\n"
4195 " http://mahjong.julianbradfield.org/  .\n"
4196 "Comments and suggestions should be mailed to\n"
4197 " mahjong@stevens-bradfield.com" ;
4198 
4199   about_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
4200   gtk_signal_connect (GTK_OBJECT (about_window), "delete_event",GTK_SIGNAL_FUNC(gtk_widget_hide), NULL);
4201   gtk_window_set_title(GTK_WINDOW(about_window),"About xmj");
4202   gtk_container_set_border_width(GTK_CONTAINER(about_window),
4203 				 dialog_border_width);
4204 
4205   vbox = gtk_vbox_new(0,dialog_vert_spacing);
4206   gtk_widget_show(vbox);
4207   gtk_container_add(GTK_CONTAINER(about_window),vbox);
4208 
4209 #ifdef GTK2
4210   GtkTextBuffer *textwbuf;
4211   GtkTextIter textwiter;
4212 
4213   textw = gtk_text_view_new();
4214   textwbuf = gtk_text_view_get_buffer (GTK_TEXT_VIEW (textw));
4215   gtk_text_buffer_get_iter_at_offset (textwbuf, &textwiter, 0);
4216 #else
4217   textw = gtk_text_new(NULL,NULL);
4218 #endif
4219   // let it choose its own
4220   // gtk_widget_set_usize(textw,300,200);
4221 #ifdef GTK2
4222   gtk_text_buffer_insert(textwbuf,&textwiter,abouttxt,-1);
4223 #else
4224   gtk_text_insert(GTK_TEXT(textw),NULL,NULL,NULL,abouttxt,-1);
4225 #endif
4226   gtk_widget_show(textw);
4227   GTK_WIDGET_UNSET_FLAGS(textw,GTK_CAN_FOCUS);
4228   gtk_box_pack_start(GTK_BOX(vbox),textw,0,0,0);
4229 
4230   closebutton = gtk_button_new_with_label("Close");
4231   gtk_widget_show(closebutton);
4232   gtk_signal_connect_object(GTK_OBJECT(closebutton),"clicked",GTK_SIGNAL_FUNC(close_saving_posn),GTK_OBJECT(about_window));
4233   gtk_box_pack_start(GTK_BOX(vbox),closebutton,0,0,0);
4234 
4235 }
4236 
nag_callback(GtkWidget * widg UNUSED,gpointer data)4237 static void nag_callback(GtkWidget *widg UNUSED, gpointer data) {
4238   nag_state = (int)(intptr_t) data;
4239   read_or_update_rcfile(NULL,XmjrcNone,XmjrcMisc);
4240   gtk_widget_hide(nag_window);
4241 }
4242 
nag_init(void)4243 static void nag_init(void) {
4244   GtkWidget *yesbutton, *maybebutton, *nobutton, *textw, *vbox;
4245 
4246 #ifdef GTK2
4247   GtkTextBuffer *textwbuf;
4248   GtkTextIter textwiter;
4249 #endif
4250 
4251   char buf[1024];
4252   static char *nagtxt = ""
4253 "Congratulations: you've completed %d full games using\n"
4254 "this set of programs.\n"
4255 "That suggests that you are getting some enjoyment\n"
4256 "out of them.\n"
4257 "\n"
4258 "If you haven't already, perhaps you would like to\n"
4259 "think about making a small donation to me by way of thanks?\n"
4260 "\n"
4261 "You can do this with a credit card via the home page\n"
4262 "hhtp://www.stevens-bradfield.com/MahJong/";
4263 
4264   nag_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
4265   gtk_signal_connect (GTK_OBJECT (nag_window), "delete_event",GTK_SIGNAL_FUNC(gtk_widget_hide), NULL);
4266   gtk_window_set_title(GTK_WINDOW(nag_window),"mu4 juan1");
4267   gtk_container_set_border_width(GTK_CONTAINER(nag_window),
4268 				 dialog_border_width);
4269 
4270   vbox = gtk_vbox_new(0,dialog_vert_spacing);
4271   gtk_widget_show(vbox);
4272   gtk_container_add(GTK_CONTAINER(nag_window),vbox);
4273 
4274 #ifdef GTK2
4275   textw = gtk_text_view_new();
4276 #else
4277   textw = gtk_text_new(NULL,NULL);
4278 #endif
4279   // Better not to set this, I think
4280   //gtk_widget_set_usize(textw,400,200);
4281   sprintf(buf,nagtxt,completed_games);
4282 #ifdef GTK2
4283   textwbuf = gtk_text_view_get_buffer (GTK_TEXT_VIEW (textw));
4284   gtk_text_buffer_get_iter_at_offset (textwbuf, &textwiter, 0);
4285   gtk_text_buffer_insert(textwbuf,&textwiter,buf,-1);
4286 #else
4287   gtk_text_insert(GTK_TEXT(textw),NULL,NULL,NULL,buf,-1);
4288 #endif
4289   gtk_widget_show(textw);
4290   GTK_WIDGET_UNSET_FLAGS(textw,GTK_CAN_FOCUS);
4291   gtk_box_pack_start(GTK_BOX(vbox),textw,0,0,0);
4292 
4293   yesbutton = gtk_button_new_with_label("Yes, I have/will");
4294   gtk_widget_show(yesbutton);
4295   gtk_signal_connect(GTK_OBJECT(yesbutton),"clicked",GTK_SIGNAL_FUNC(nag_callback),(gpointer)2);
4296   gtk_box_pack_start(GTK_BOX(vbox),yesbutton,0,0,0);
4297 
4298   maybebutton = gtk_button_new_with_label("Maybe: remind me again later");
4299   gtk_widget_show(maybebutton);
4300   gtk_signal_connect(GTK_OBJECT(maybebutton),"clicked",GTK_SIGNAL_FUNC(nag_callback),(gpointer)0);
4301   gtk_box_pack_start(GTK_BOX(vbox),maybebutton,0,0,0);
4302 
4303   nobutton = gtk_button_new_with_label("No, I won't");
4304   gtk_widget_show(nobutton);
4305   gtk_signal_connect(GTK_OBJECT(nobutton),"clicked",GTK_SIGNAL_FUNC(nag_callback),(gpointer)1);
4306   gtk_box_pack_start(GTK_BOX(vbox),nobutton,0,0,0);
4307 
4308 }
4309 
nag_popup(void)4310 void nag_popup(void) {
4311   if ( ! nag_window ) nag_init();
4312   dialog_popup(nag_window,DPCentred);
4313 }
4314 
4315 /* create the dialogs that depend on --dialog-XXX */
create_dialogs(void)4316 void create_dialogs(void) {
4317 
4318   if ( dialogs_position == DialogsBelow ) {
4319     GtkWidget *t;
4320     t = gtk_event_box_new(); /* so there's a window to have a style */
4321     gtk_widget_show(t);
4322     dialoglowerbox = gtk_hbox_new(0,0);
4323     gtk_widget_show(dialoglowerbox);
4324     gtk_container_add(GTK_CONTAINER(t),dialoglowerbox);
4325     gtk_box_pack_end(GTK_BOX(outerframe),t,0,0,0);
4326     gtk_widget_show(dialoglowerbox->parent);
4327   }
4328 
4329   /* create the discard dialog */
4330   discard_dialog_init();
4331 
4332   /* and the chow dialog */
4333   chow_dialog_init();
4334 
4335   /* and the declaring specials dialog */
4336   ds_dialog_init();
4337 
4338   /* and create the turn dialog */
4339   turn_dialog_init();
4340 
4341   /* now create the scoring dialog */
4342   scoring_dialog_init();
4343 
4344   /* and the continue dialog */
4345   continue_dialog_init();
4346 
4347   /* and the end dialog */
4348   end_dialog_init();
4349 
4350 }
4351 
4352 /* and destroy them */
destroy_dialogs(void)4353 void destroy_dialogs(void)
4354 {
4355 #define zapit(w) if ( w ) gtk_widget_destroy(w) ; w = NULL ;
4356   zapit(discard_dialog->widget);
4357   zapit(chow_dialog);
4358   zapit(ds_dialog);
4359   zapit(turn_dialog);
4360   zapit(scoring_dialog);
4361   zapit(continue_dialog);
4362 }
4363 
scorehistory_showraise(void)4364 static void scorehistory_showraise(void) {
4365   if ( ! scorehistorywindow ) scorehistory_init();
4366   showraise(scorehistorywindow);
4367 }
scoring_showraise(void)4368 static void scoring_showraise(void) {
4369   if ( ! textwindow ) textwindow_init();
4370   showraise(textwindow);
4371 }
message_showraise(void)4372 static void message_showraise(void) {
4373   if ( ! messagewindow ) messagewindow_init();
4374   showraise(messagewindow);
4375 }
about_showraise(void)4376 static void about_showraise(void) {
4377   if ( ! about_window ) about_init();
4378   showraise(about_window);
4379 }
4380 
save_showraise(void)4381 static void save_showraise(void) {
4382   if ( ! save_window ) save_init();
4383   showraise(save_window);
4384 }
4385 
password_showraise(void)4386 void password_showraise(void) {
4387   if ( ! password_window ) password_init();
4388   showraise(password_window);
4389 }
4390 
status_showraise(void)4391 void status_showraise(void) {
4392   if (!nopopups) {
4393     showraise(status_window);
4394   }
4395   status_update(0) ;
4396 }
4397 
display_option_dialog_popup(void)4398 static void display_option_dialog_popup(void) {
4399   if ( ! display_option_dialog ) display_option_dialog_init();
4400   display_option_dialog_refresh();
4401   dialog_popup(display_option_dialog,DPCentredOnce) ;
4402 }
4403 
playing_prefs_dialog_popup(void)4404 static void playing_prefs_dialog_popup(void) {
4405   if ( ! playing_prefs_dialog ) playing_prefs_dialog_init();
4406   playing_prefs_dialog_refresh();
4407   dialog_popup(playing_prefs_dialog,DPCentredOnce) ;
4408 }
4409 
debug_options_dialog_popup(void)4410 void debug_options_dialog_popup(void) {
4411   if ( ! debug_options_dialog ) debug_options_dialog_init();
4412   debug_options_dialog_refresh();
4413   dialog_popup(debug_options_dialog,DPCentredOnce) ;
4414 }
4415 
game_option_popup(void)4416 static void game_option_popup(void) {
4417   int b;
4418   if ( ! the_game ) return;
4419   if ( ! game_option_dialog ) game_option_init();
4420   game_option_panel = build_or_refresh_option_panel(&the_game->option_table,
4421 						    game_option_panel);
4422   /* we can't apply options if somebody else is the manager */
4423   b = (the_game->manager == 0 || the_game->manager == our_id);
4424   gtk_widget_set_sensitive(game_option_apply_button,b);
4425   gtk_widget_set_sensitive(game_option_prefs_button,b);
4426   dialog_popup(game_option_dialog,DPNone);
4427 }
4428 
game_prefs_popup(void)4429 static void game_prefs_popup(void) {
4430   if ( ! game_prefs_dialog ) game_prefs_init();
4431   read_or_update_rcfile(NULL,XmjrcGame,XmjrcNone);
4432   game_prefs_panel = build_or_refresh_prefs_panel(&prefs_table,game_prefs_panel);
4433   dialog_popup(game_prefs_dialog,DPNone);
4434 }
4435 
save_state(void)4436 static void save_state(void) {
4437   PMsgSaveStateMsg m;
4438   m.type = PMsgSaveState;
4439   m.filename = NULL;
4440   send_packet(&m);
4441 }
4442 
4443 /* see comment at declaration */
grab_focus_if_appropriate(GtkWidget * w)4444 static void grab_focus_if_appropriate(GtkWidget *w) {
4445   if ( info_windows_in_main ) {
4446     if ( GTK_TOGGLE_BUTTON(mfocus)->active ) {
4447       gtk_widget_grab_focus(message_entry);
4448     } else if ( ! (GTK_HAS_FOCUS & GTK_WIDGET_FLAGS(message_entry)) ) {
4449       gtk_widget_grab_focus(w);
4450     }
4451   } else {
4452     gtk_widget_grab_focus(w);
4453   }
4454 }
4455 
4456 #ifdef GTK2
4457 #define NULLEX , NULL
4458 #else
4459 #define NULLEX
4460 #endif
4461 /* the menu items */
4462 static GtkItemFactoryEntry menu_items[] = {
4463   { "/_Game", NULL, NULL, 0, "<Branch>" NULLEX},
4464   { "/Game/_New local game...", NULL, GTK_SIGNAL_FUNC(open_dialog_popup), 1, NULL NULLEX},
4465   { "/Game/_Join server...", NULL, GTK_SIGNAL_FUNC(open_dialog_popup), 0, NULL NULLEX},
4466   { "/Game/_Resume game...", NULL, GTK_SIGNAL_FUNC(open_dialog_popup), 2, NULL NULLEX},
4467   { "/Game/_Save", NULL, save_state, 0, NULL NULLEX},
4468   { "/Game/Save _as...", NULL, save_showraise, 0, NULL NULLEX},
4469   { "/Game/_Close", NULL, GTK_SIGNAL_FUNC(close_connection), 0, NULL NULLEX},
4470   { "/Game/_Quit", NULL, GTK_SIGNAL_FUNC(exit), 0, NULL NULLEX},
4471   { "/_Show", NULL, NULL, 0, "<Branch>" NULLEX},
4472   { "/Show/_Scoring info", NULL, scoring_showraise, 0, NULL NULLEX},
4473   { "/Show/_Game info", NULL, status_showraise, 0, NULL NULLEX},
4474   { "/Show/_Messages", NULL, message_showraise, 0, NULL NULLEX},
4475   { "/Show/Scoring _history", NULL, scorehistory_showraise, 0, NULL NULLEX},
4476   { "/Show/_Warnings", NULL, warningraise, 0, NULL NULLEX},
4477   { "/_Options",NULL,NULL,0,"<Branch>" NULLEX},
4478   { "/Options/_Display Options...", NULL, display_option_dialog_popup,0,NULL NULLEX},
4479   { "/Options/_Playing Preferences...",NULL, playing_prefs_dialog_popup,0,NULL NULLEX},
4480   { "/Options/_Game Option Preferences...", NULL, game_prefs_popup,0,NULL NULLEX},
4481   { "/Options/_Current Game Options...", NULL, game_option_popup,0,NULL NULLEX},
4482   { "/Options/_Debugging Options...", NULL, debug_options_dialog_popup,0,NULL NULLEX},
4483 #ifdef GTK2
4484   /* a button in the title bar doesn't quite work properly in gtk2.
4485      It seems somehow to lose a click. But since ItemFactory is deprecated,
4486      there's no chance of a fix. */
4487   { "/Show _Warnings", NULL, NULL,0,"<Branch>" NULLEX},
4488   { "/Show _Warnings/Show _Warnings", NULL, warningraise,0,NULL NULLEX},
4489 #else
4490   { "/Show _Warnings", NULL, warningraise,0,NULL NULLEX},
4491 #endif
4492   { "/_About", NULL, NULL, 0, "<LastBranch>" NULLEX},
4493   { "/About/_About xmj", NULL, about_showraise, 0, NULL NULLEX},
4494 };
4495 
4496 static const int nmenu_items = sizeof(menu_items)/sizeof(GtkItemFactoryEntry);
4497 
4498 /* create the menubar, and return it */
menubar_create(void)4499 GtkWidget *menubar_create(void) {
4500   GtkItemFactory *item_factory;
4501   GtkAccelGroup *accel;
4502   GtkWidget *m;
4503   int connected = the_game && the_game->fd;
4504 
4505   accel = gtk_accel_group_new();
4506   item_factory = gtk_item_factory_new(GTK_TYPE_MENU_BAR,
4507 				      "<main>",accel);
4508   gtk_item_factory_create_items(item_factory,nmenu_items,
4509 				menu_items,NULL);
4510   gtk_window_add_accel_group(GTK_WINDOW(topwindow),accel);
4511 
4512   m = gtk_item_factory_get_widget(item_factory,"<main>");
4513   gtk_widget_show(m);
4514   openmenuentry = gtk_item_factory_get_widget(item_factory,
4515 					      "/Game/Join server...");
4516   assert(openmenuentry);
4517   gtk_widget_set_sensitive(openmenuentry,!connected);
4518   newgamemenuentry = gtk_item_factory_get_widget(item_factory,
4519 					      "/Game/New local game...");
4520   assert(newgamemenuentry);
4521   gtk_widget_set_sensitive(newgamemenuentry,!connected);
4522   resumegamemenuentry = gtk_item_factory_get_widget(item_factory,
4523 					      "/Game/Resume game...");
4524   assert(resumegamemenuentry);
4525   gtk_widget_set_sensitive(resumegamemenuentry,!connected);
4526   savemenuentry = gtk_item_factory_get_widget(item_factory,
4527 					      "/Game/Save");
4528   assert(savemenuentry);
4529   gtk_widget_set_sensitive(savemenuentry,connected);
4530   saveasmenuentry = gtk_item_factory_get_widget(item_factory,
4531 					      "/Game/Save as...");
4532   assert(saveasmenuentry);
4533   gtk_widget_set_sensitive(saveasmenuentry,connected);
4534   closemenuentry = gtk_item_factory_get_widget(item_factory,"/Game/Close");
4535   assert(closemenuentry);
4536   gtk_widget_set_sensitive(closemenuentry,connected);
4537   gameoptionsmenuentry = gtk_item_factory_get_widget(item_factory,"/Options/Current Game Options...");
4538   gtk_widget_set_sensitive(gameoptionsmenuentry,connected);
4539 
4540   warningentry = gtk_item_factory_get_item(item_factory,
4541     "/Show Warnings");
4542   assert(warningentry);
4543   /* This doesn't actually work. But we'll leave it here
4544      in case it works in gtk 2.0 */
4545   gtk_menu_item_right_justify(GTK_MENU_ITEM(warningentry));
4546   gtk_widget_set_name(warningentry,"warningentry");
4547 
4548   /* it's very tedious simply to set this to have red text */
4549   {
4550 #ifdef GTK2
4551     GdkColor c;
4552     gdk_color_parse("red",&c);
4553     gtk_widget_modify_fg(gtk_bin_get_child(GTK_BIN(warningentry)),GTK_STATE_NORMAL,&c);
4554 #else
4555     GtkStyle *s;
4556     s = gtk_style_copy(gtk_widget_get_default_style());
4557     gdk_color_parse("red",&(s->fg[GTK_STATE_NORMAL]));
4558     gtk_widget_set_style(GTK_BIN(warningentry)->child,s);
4559 #endif
4560   }
4561 
4562   gtk_widget_hide(warningentry);
4563 
4564   /* if the relevant windows are in the main window, zap the menu entries */
4565   if ( info_windows_in_main ) {
4566     gtk_widget_destroy(gtk_item_factory_get_widget(item_factory,"/Show/Game info"));
4567     gtk_widget_destroy(gtk_item_factory_get_widget(item_factory,"/Show/Messages"));
4568   }
4569 
4570   return m;
4571 }
4572 
4573 
4574