1 /**********************************************************************
2  Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
3    This program is free software; you can redistribute it and/or modify
4    it under the terms of the GNU General Public License as published by
5    the Free Software Foundation; either version 2, or (at your option)
6    any later version.
7 
8    This program is distributed in the hope that it will be useful,
9    but WITHOUT ANY WARRANTY; without even the implied warranty of
10    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11    GNU General Public License for more details.
12 ***********************************************************************/
13 
14 #ifdef HAVE_CONFIG_H
15 #include <fc_config.h>
16 #endif
17 
18 #include <stdio.h>
19 #include <stdlib.h>
20 
21 #include <X11/Intrinsic.h>
22 #include <X11/StringDefs.h>
23 #include <X11/Xaw/Form.h>
24 #include <X11/Xaw/Label.h>
25 #include <X11/Xaw/SimpleMenu.h>
26 #include <X11/Xaw/Command.h>
27 #include <X11/Xaw/List.h>
28 #include <X11/Xaw/Viewport.h>
29 
30 #include "fcintl.h"
31 #include "game.h"
32 #include "map.h"
33 #include "mem.h"
34 #include "packets.h"
35 #include "player.h"
36 
37 #include "chatline.h"
38 #include "citydlg.h"
39 #include "gui_main.h"
40 #include "gui_stuff.h"
41 #include "mapview.h"
42 #include "options.h"
43 
44 #include "messagewin.h"
45 
46 static Widget meswin_dialog_shell;
47 static Widget meswin_form;
48 static Widget meswin_label;
49 static Widget meswin_list;
50 static Widget meswin_viewport;
51 static Widget meswin_close_command;
52 static Widget meswin_goto_command;
53 static Widget meswin_popcity_command;
54 static bool meswin_dialog_shell_is_raised;
55 
56 static void create_meswin_dialog(bool raise);
57 static void meswin_scroll_down(void);
58 static void meswin_close_callback(Widget w, XtPointer client_data,
59 				  XtPointer call_data);
60 static void meswin_list_callback(Widget w, XtPointer client_data,
61 				 XtPointer call_data);
62 static void meswin_goto_callback(Widget w, XtPointer client_data,
63 				 XtPointer call_data);
64 static void meswin_popcity_callback(Widget w, XtPointer client_data,
65 				    XtPointer call_data);
66 
67 static CONST_FOR_XAW_LIST_CHANGE char *dummy_message_list[] = {
68   "                                                        ", 0 };
69 
70 #define N_MSG_VIEW 24 		/* max before scrolling happens */
71 
72 /****************************************************************
73 popup the dialog 10% inside the main-window
74 *****************************************************************/
meswin_dialog_popup(bool raise)75 void meswin_dialog_popup(bool raise)
76 {
77   int updated = 0;
78 
79   meswin_dialog_shell_is_raised = raise;
80 
81   if(!meswin_dialog_shell) {
82     create_meswin_dialog(raise);
83     updated = 1;		/* create_ calls update_ */
84   }
85 
86   if (raise) {
87     XtSetSensitive(main_form, FALSE);
88   }
89 
90   xaw_set_relative_position(toplevel, meswin_dialog_shell, 25, 25);
91   XtPopup(meswin_dialog_shell, XtGrabNone);
92   if (!updated) {
93     real_meswin_dialog_update(NULL);
94   }
95 
96   /* Is this necessary here?
97    * from popup_city_report_dialog():
98    * force refresh of viewport so the scrollbar is added.
99    * Buggy sun athena requires this
100    */
101   XtVaSetValues(meswin_viewport, XtNforceBars, True, NULL);
102 
103   meswin_scroll_down();
104 }
105 
106 /****************************************************************
107   Closes the message window dialog.
108 *****************************************************************/
meswin_dialog_popdown(void)109 void meswin_dialog_popdown(void)
110 {
111   if (meswin_dialog_shell) {
112     if (meswin_dialog_shell_is_raised) {
113       XtSetSensitive(main_form, TRUE);
114     }
115     XtDestroyWidget(meswin_dialog_shell);
116     meswin_dialog_shell = 0;
117   }
118 }
119 
120 /****************************************************************
121 ...
122 *****************************************************************/
meswin_dialog_is_open(void)123 bool meswin_dialog_is_open(void)
124 {
125   return meswin_dialog_shell != NULL;
126 }
127 
128 /* This is set when we are creating the message window and it is not popped
129  * up yet.  We can't scroll, since Xaw may crash - see PR#2794. */
130 static bool creating = FALSE;
131 
132 /****************************************************************
133 ...
134 *****************************************************************/
create_meswin_dialog(bool raise)135 static void create_meswin_dialog(bool raise)
136 {
137   creating = TRUE;
138 
139   meswin_dialog_shell =
140     I_IN(I_T(XtCreatePopupShell("meswinpopup",
141 				raise ? transientShellWidgetClass
142 				: topLevelShellWidgetClass,
143 				toplevel, NULL, 0)));
144 
145   meswin_form = XtVaCreateManagedWidget("meswinform",
146 				       formWidgetClass,
147 				       meswin_dialog_shell, NULL);
148 
149   meswin_label =
150     I_L(XtVaCreateManagedWidget("meswinlabel", labelWidgetClass,
151 				meswin_form, NULL));
152 
153   meswin_viewport = XtVaCreateManagedWidget("meswinviewport",
154 					    viewportWidgetClass,
155 					    meswin_form,
156 					    NULL);
157 
158   meswin_list = XtVaCreateManagedWidget("meswinlist",
159 					listWidgetClass,
160 					meswin_viewport,
161 					XtNlist,
162 					(XtArgVal)dummy_message_list,
163 					NULL);
164 
165   meswin_close_command =
166     I_L(XtVaCreateManagedWidget("meswinclosecommand", commandWidgetClass,
167 				meswin_form, NULL));
168 
169   meswin_goto_command =
170     I_L(XtVaCreateManagedWidget("meswingotocommand", commandWidgetClass,
171 				meswin_form,
172 				XtNsensitive, False,
173 				NULL));
174 
175   meswin_popcity_command =
176     I_L(XtVaCreateManagedWidget("meswinpopcitycommand", commandWidgetClass,
177 				meswin_form,
178 				XtNsensitive, False,
179 				NULL));
180 
181   XtAddCallback(meswin_list, XtNcallback, meswin_list_callback,
182 		NULL);
183 
184   XtAddCallback(meswin_close_command, XtNcallback, meswin_close_callback,
185 		NULL);
186 
187   XtAddCallback(meswin_goto_command, XtNcallback, meswin_goto_callback,
188 		NULL);
189 
190   XtAddCallback(meswin_popcity_command, XtNcallback, meswin_popcity_callback,
191 		NULL);
192 
193   real_meswin_dialog_update(NULL);
194 
195   XtRealizeWidget(meswin_dialog_shell);
196 
197   XSetWMProtocols(display, XtWindow(meswin_dialog_shell),
198 		  &wm_delete_window, 1);
199   XtOverrideTranslations(meswin_dialog_shell,
200       XtParseTranslationTable("<Message>WM_PROTOCOLS: msg-close-messages()"));
201 
202   creating = FALSE;
203 }
204 
205 /**************************************************************************
206  This scrolls the messages window down to the bottom.
207  NOTE: it seems this must not be called until _after_ meswin_dialog_shell
208  is ...? realized, popped up, ... something.
209  Its a toss-up whether we _should_ scroll the window down:
210  Against: user will likely want to read from the top and scroll down manually.
211  For: if we don't scroll down, new messages which appear at the bottom
212  (including combat results etc) will be easily missed.
213 **************************************************************************/
meswin_scroll_down(void)214 static void meswin_scroll_down(void)
215 {
216   Dimension height;
217   int pos;
218 
219   if (!meswin_dialog_shell || creating) {
220     return;
221   }
222   if (meswin_get_num_messages() <= N_MSG_VIEW) {
223     return;
224   }
225 
226   XtVaGetValues(meswin_list, XtNheight, &height, NULL);
227   pos = (((double) (meswin_get_num_messages() - 1))
228          / meswin_get_num_messages()) * height;
229   XawViewportSetCoordinates(meswin_viewport, 0, pos);
230 }
231 
232 /**************************************************************************
233 ...
234 **************************************************************************/
real_meswin_dialog_update(void * unused)235 void real_meswin_dialog_update(void *unused)
236 {
237   Dimension height, iheight, width, oldheight, newheight;
238   int i, num = meswin_get_num_messages();
239 
240   XawFormDoLayout(meswin_form, False);
241 
242   XtVaGetValues(meswin_viewport, XtNheight, &oldheight, NULL);
243 
244   if (num == 0) {
245     XawListChange(meswin_list, dummy_message_list, 1, 0, True);
246   } else {
247     /* strings will not be freed */
248     static CONST_FOR_XAW_LIST_CHANGE char **strings = NULL;
249 
250     strings = fc_realloc(strings, num * sizeof(char *));
251 
252     for (i = 0; i < num; i++) {
253       strings[i] = meswin_get_message(i)->descr;
254     }
255 
256     XawListChange(meswin_list, strings, num, 0, True);
257   }
258 
259   /* Much of the following copied from city_report_dialog_update() */
260   XtVaGetValues(meswin_list, XtNlongest, &i, NULL);
261   width = i + 10;
262   /* I don't know the proper way to set the width of this viewport widget.
263      Someone who knows is more than welcome to fix this */
264   XtVaSetValues(meswin_viewport, XtNwidth, width + 15, NULL);
265   XtVaSetValues(meswin_label, XtNwidth, width + 15, NULL);
266 
267   /* Seems have to do this here so we get the correct height below. */
268   XawFormDoLayout(meswin_form, True);
269 
270   if (num <= N_MSG_VIEW) {
271     XtVaGetValues(meswin_list, XtNheight, &height, NULL);
272     if ((oldheight == 0) || (num == 0)) {
273       XtVaSetValues(meswin_viewport, XtNheight, height, NULL);
274     } else {
275       XtVaGetValues(meswin_form, XtNheight, &newheight, NULL);
276       newheight = newheight + height - oldheight;
277       XtVaSetValues(meswin_form, XtNheight, newheight, NULL);
278     }
279   } else {
280     XtVaGetValues(meswin_list, XtNheight, &height, NULL);
281     XtVaGetValues(meswin_list, XtNinternalHeight, &iheight, NULL);
282     height -= (iheight * 2);
283     height /= num;
284     height *= N_MSG_VIEW;
285     height += (iheight * 2);
286     if (height != oldheight) {
287       if (oldheight == 0) {
288 	XtVaSetValues(meswin_viewport, XtNheight, height, NULL);
289       } else {
290 	XtVaGetValues(meswin_form, XtNheight, &newheight, NULL);
291 	newheight = newheight + height - oldheight;
292 	XtVaSetValues(meswin_form, XtNheight, newheight, NULL);
293       }
294     }
295     meswin_scroll_down();
296   }
297 
298   XtSetSensitive(meswin_goto_command, FALSE);
299   XtSetSensitive(meswin_popcity_command, FALSE);
300 }
301 
302 /**************************************************************************
303 ...
304 **************************************************************************/
meswin_list_callback(Widget w,XtPointer client_data,XtPointer call_data)305 static void meswin_list_callback(Widget w, XtPointer client_data,
306 				 XtPointer call_data)
307 {
308   XawListReturnStruct *ret = XawListShowCurrent(meswin_list);
309 
310   if ((ret->list_index != XAW_LIST_NONE)
311       && (meswin_get_num_messages() != 0)) {
312     const struct message *message = meswin_get_message(ret->list_index);
313 
314     XtSetSensitive(meswin_goto_command, message->location_ok ? True : False);
315     XtSetSensitive(meswin_popcity_command, message->city_ok ? True : False);
316   } else {
317     XtSetSensitive(meswin_goto_command, False);
318     XtSetSensitive(meswin_popcity_command, False);
319   }
320 }
321 
322 /**************************************************************************
323 ...
324 **************************************************************************/
meswin_close_callback(Widget w,XtPointer client_data,XtPointer call_data)325 static void meswin_close_callback(Widget w, XtPointer client_data,
326 				  XtPointer call_data)
327 {
328   meswin_dialog_popdown();
329 }
330 
331 /****************************************************************
332 ...
333 *****************************************************************/
meswin_msg_close(Widget w)334 void meswin_msg_close(Widget w)
335 {
336   meswin_close_callback(w, NULL, NULL);
337 }
338 
339 /**************************************************************************
340 ...
341 **************************************************************************/
meswin_goto_callback(Widget w,XtPointer client_data,XtPointer call_data)342 static void meswin_goto_callback(Widget w, XtPointer client_data,
343 				 XtPointer call_data)
344 {
345   XawListReturnStruct *ret = XawListShowCurrent(meswin_list);
346 
347   if (ret->list_index != XAW_LIST_NONE) {
348     meswin_goto(ret->list_index);
349   }
350 }
351 
352 /**************************************************************************
353 ...
354 **************************************************************************/
meswin_popcity_callback(Widget w,XtPointer client_data,XtPointer call_data)355 static void meswin_popcity_callback(Widget w, XtPointer client_data,
356 				    XtPointer call_data)
357 {
358   XawListReturnStruct *ret = XawListShowCurrent(meswin_list);
359 
360   if (ret->list_index != XAW_LIST_NONE) {
361     meswin_popup_city(ret->list_index);
362   }
363 }
364