1 /* ---------------------------------------------------------------------- *
2  * dialbox.c
3  * This file is part of lincity.
4  * Lincity is copyright (c) I J Peters 1995-1997, (c) Greg Sharp 1997-2001.
5  * Portions copyright (c) Corey Keasling, 2001.
6  * ---------------------------------------------------------------------- */
7 
8 #include "lcconfig.h"
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <math.h>
12 #include <stdarg.h> /* XXX: WCK: What does configure need to know? */
13 #include "lcstring.h"
14 #include "screen.h"
15 #include "geometry.h"
16 #include "dialbox.h"
17 #include "mouse.h"
18 #include "lclib.h"
19 
20 static Dialog_Box db_entry[MAX_DBOX_ENTRIES];
21 
22 static Rect dialog_window;   /* Describes position of window on screen */
23 static Rect text_window;     /* Describes position of text area on screen */
24 
25 static Rect db_rect[MAX_DBOX_ENTRIES];    /* region of each line/button */
26 // static Rect button_rect[MAX_DBOX_ENTRIES];  /* click area for buttons, lines */
27 // static Rect line_rect[MAX_DBOX_ENTRIES];    /* offset from text_window */
28 
29 static Mouse_Handle * main_handle;
30 static Mouse_Handle * text_handle;
31 
32 static int dbn; /* number of dbox entries */
33 static int bn;  /* number of buttons */
34 static int ln;  /* number of lines */
35 
36 static int db_longest_button; /* total width of all buttons, pixels */
37 static int db_longest_line;   /* pixel width of longest line */
38 
39 static int bs; /* button spacing */
40 static int bse; /* extra spacing, to be added at beginning and end of line */
41 
42 static int color;
43 
44 static short db_up = 0;
45 static int db_return_value;
46 
47 char * db_screen_buffer; /* hold the screen we overwrite */
48 char db_screen_fresh;    /* does the buffer hold information? */
49 
50 /* Mouse handling routines: main_handler() and text_handler()
51    main_handler handles the main dialog window: the text area and border.
52    Clicks in the border are useless and ignored; text_handler takes the
53    interesting ones
54 */
55 void
main_handler(int x,int y,int button)56 main_handler(int x, int y, int button)
57 {
58 
59 }
60 
61 void
text_handler(int x,int y,int button)62 text_handler(int x, int y, int button)
63 {
64     int i;
65     for (i = 0; i < dbn; i++) {
66 	if (mouse_in_rect(&db_rect[i], x, y) && db_entry[i].retval)
67 	    dialog_close(db_entry[i].retval);
68     }
69 
70 
71 }
72 
73 /* Keypress handler: dialog_key_handler()
74    Iterate through possible hotkeys, returning if key matches.
75 */
76 
77 void
dialog_key_handler(int key)78 dialog_key_handler (int key)
79 {
80     int i;
81 
82     if (key == 0)
83 	return;
84 
85     /* CR, LF, and space all activate default button, type 2 */
86 
87     if (key == 10 || key == 13 || key == 32) {
88 	for (i = 0; i < dbn; i++)
89 	    if (db_entry[i].type == 2) {
90 		dialog_close(db_entry[i].retval);
91 		return;
92 	    }
93     } else {
94 	for (i = 0; i < dbn;  i++) {
95 	    if (key == db_entry[i].retval) {
96 		dialog_close(db_entry[i].retval);
97 		return;
98 	    }
99 	}
100     }
101 }
102 
103 
104 int
dialog_box(int arg_color,int argc,...)105 dialog_box(int arg_color, int argc, ...)
106 {
107   va_list ap;
108   int i;
109   int db_last_button = -1;
110   int key;
111   char * working_str;
112 
113   /* Try the locks */
114   if (db_up) {
115       printf("Already have a dialog box on screen!\n");
116       exit(-1);  /* GCS: I guess this must be a critical bug. */
117   } else {
118       db_up = 1; /* XXX: Need to reconcile these - don't need both flags */
119       db_flag = 1;
120   }
121 
122   bn = 0; ln = 0; dbn = 0;
123   db_longest_button = 0; db_longest_line = 0;
124   bs = 0; bse = 0;
125   color = arg_color;
126   db_screen_fresh = 0;
127 
128   va_start(ap, argc);
129 
130   /* For each argument pair, get the arguments, determine line or button,
131      calculate width/length, adjust total size accordingly, increment type
132      count. */
133 
134   for (i = 0; i < argc; i++) {
135 
136       if (dbn >= MAX_DBOX_ENTRIES) {
137 	  fprintf(stderr,"Too many buttons in dialog_box!\n"
138 		  "Tweak MAX_DBOX_ENTRIES\n");
139 	  exit(212);
140       }
141 
142     db_entry[dbn].type = (short) va_arg(ap, int);
143     db_entry[dbn].retval = (short) va_arg(ap, int);
144 
145     if (db_entry[dbn].type == 0) { /* Text strings: Chop a paragraph into
146 				    individual lines.*/
147 	char * newline;
148 	working_str = va_arg(ap, char *);
149 	do {
150 	    newline = (char *)strchr(working_str,'\n');
151 	    if (newline) {
152 		int linelen = newline - working_str;
153 		db_entry[dbn].text = (char *)lcalloc(1 + linelen);
154 		strncpy(db_entry[dbn].text,working_str,linelen);
155 		db_entry[dbn].text[linelen] = '\0';
156 		working_str = (newline + 1) != '\0' ? newline + 1 : NULL;
157 	    } else {
158 		db_entry[dbn].text = (char *)lcalloc(1 + strlen(working_str));
159 		strncpy(db_entry[dbn].text,working_str,strlen(working_str));
160 		db_entry[dbn].text[strlen(working_str)] = '\0';
161 		working_str = NULL;
162 	    }
163 
164 	    db_entry[dbn].type = 0;
165 	    db_entry[dbn].retval = 0;
166 
167 	    db_rect[dbn].w = (strlen(db_entry[dbn].text) * CHAR_WIDTH);
168 	    db_rect[dbn].h = CHAR_HEIGHT;
169 	    if (db_rect[dbn].w > db_longest_line)
170 		db_longest_line = db_rect[dbn].w;
171 
172 	    ln++;
173 	    dbn++;
174 	} while ((working_str != NULL) && (strlen(working_str) >= 1));
175     } else {
176 	db_entry[dbn].text = va_arg(ap, char *);
177 	db_rect[dbn].w = ((strlen(db_entry[dbn].text) * CHAR_WIDTH)
178 			  + (BUTTON_BORDER * 2));
179 	db_rect[dbn].h = (CHAR_HEIGHT + (BUTTON_BORDER * 2));
180 
181 	db_longest_button += db_rect[dbn].w;
182 	bn++;
183 	dbn++;
184     }
185   }
186 
187   va_end(ap);
188 
189   /* figure out how high and wide the box needs to be */
190   text_window.h =
191       ((ln * (CHAR_HEIGHT + DB_V_SPACE)) + BUTTON_HEIGHT + DB_V_SPACE);
192 
193   if ((db_longest_button + (bn * BUTTON_MIN_SPACING)) >
194       (db_longest_line + LINE_MIN_SPACING)) {
195       text_window.w = (db_longest_button + (bn * BUTTON_MIN_SPACING));
196   } else {
197       text_window.w = (db_longest_line + LINE_MIN_SPACING);
198   }
199 
200   /* Determine button spacing;
201      add some extra in front and back */
202 
203   bs = (text_window.w - db_longest_button) / bn;
204   bse = ((text_window.w - db_longest_line) % bn) / 2;
205 
206   /* Position the buttons and lines */
207 
208   for (i = 0; i < dbn; i++)
209   {
210       if (db_entry[i].type) {                                  /* Buttons */
211 	  if (db_last_button == -1)
212 	      db_rect[i].x = ((bs + bse) / 2) - BUTTON_BORDER;
213 	  else
214 	      db_rect[i].x = ((db_rect[db_last_button].x
215 			       + db_rect[db_last_button].w + bs)
216 			      - BUTTON_BORDER);
217 
218 	  db_rect[i].y = ((ln * (CHAR_HEIGHT + DB_V_SPACE) + DB_V_SPACE)
219 			  - BUTTON_BORDER);
220 
221 	  db_last_button = i;
222       } else {                                                   /* Lines */
223 	db_rect[i].x = ((text_window.w - db_rect[i].w) / 2);
224 	db_rect[i].y = (i * (CHAR_HEIGHT + DB_V_SPACE));
225       }
226   }
227 
228   /* Figure out window size */
229 
230   dialog_window.w = (text_window.w + BORDER_SIZE*2);
231   dialog_window.h = (text_window.h + BORDER_SIZE*2);
232 
233   main_handle = mouse_register(&scr.client_win,&main_handler);
234   text_handle = mouse_register(&text_window,&text_handler);
235 
236   dialog_refresh();
237 
238   db_return_value = 0;
239 
240   /* Wait for the user to click on it or press an appropriate key */
241   /* Mouse clicks arrive from the mouse handler and set db_return_value */
242 
243   while (!db_return_value)  {
244 #ifndef LC_X11
245       lc_usleep (1000); /* call_wait_event does this for X11 */
246 #endif
247 
248 #ifdef LC_X11
249       call_wait_event ();
250       key = x_key_value;
251       x_key_value = 0;
252 #elif defined (WIN32)
253       HandleMouse ();
254       key = GetKeystroke ();
255 #else
256       mouse_update ();
257       key = vga_getkey ();
258 #endif
259       if (key == 0) continue;
260 
261       if (key == 10 || key == 13 || key == ' ') /* default button */
262 	  for (i = 0; i <= dbn; i++) {
263 	      if (db_entry[i].type == 2) {
264 		  dialog_close(db_entry[i].retval);
265 		  break;
266 	      }
267 	  }
268       else
269 	  for (i = 0; i <= dbn; i++) {
270 	      if (key == db_entry[i].retval) {
271 		  dialog_close(key);
272 		  break;
273 	      }
274 	  }
275   }
276   return (db_return_value);
277 }
278 
279 
280 void
dialog_refresh(void)281 dialog_refresh(void)
282 {
283   int i;  /* Line, Button incrementors */
284   if (!db_up)
285       return;
286 
287   /* Determine screen position */
288   dialog_window.x = (scr.client_w / 2) - (dialog_window.w / 2);
289   dialog_window.y = (scr.client_h / 2) - (dialog_window.h / 2);
290 
291   text_window.x = dialog_window.x + BORDER_SIZE;
292   text_window.y = dialog_window.y + BORDER_SIZE;
293 
294   hide_mouse();
295 
296   if (screen_refreshing && db_screen_fresh) {
297       free(db_screen_buffer);
298       db_screen_fresh = 0;
299   }
300 
301   if (!db_screen_fresh) {
302       db_screen_buffer = (char *)lcalloc(dialog_window.w * dialog_window.h);
303       Fgl_getrect(&dialog_window,db_screen_buffer);
304       db_screen_fresh = 1;
305   };
306 
307 
308   /* Draw the border, and fill the background */
309   draw_bezel(dialog_window,BORDER_SIZE,color);
310 
311   Fgl_fillbox(text_window.x,text_window.y,text_window.w,text_window.h,color);
312 
313 #ifdef USE_EXPANDED_FONT
314     gl_setwritemode (WRITEMODE_MASKED | FONT_EXPANDED);
315 #else
316     Fgl_setfontcolors (color, TEXT_FG_COLOUR);
317 #endif
318 
319     /* Loop calculating line position, and drawing the line */
320     for (i = 0; i < dbn; i++)
321     {
322 
323 	if (db_entry[i].type) {
324 	    Fgl_fillbox(db_rect[i].x + text_window.x,
325 			db_rect[i].y + text_window.y,
326 			db_rect[i].w,
327 			db_rect[i].h,
328 			white(0));
329 	}
330 	Fgl_write(db_rect[i].x + text_window.x + BUTTON_BORDER,
331 		  db_rect[i].y + text_window.y + BUTTON_BORDER,
332 		  db_entry[i].text);
333     }
334 
335 #ifdef USE_EXPANDED_FONT
336     gl_setwritemode (WRITEMODE_OVERWRITE | FONT_EXPANDED);
337 #else
338     Fgl_setfontcolors (TEXT_BG_COLOUR, TEXT_FG_COLOUR);
339 #endif
340 
341   redraw_mouse();
342 
343 }
344 
345 /* dialog_close: close the mouse handle and remember we closed it;
346    save the results; put the old screen back up and remember that too. */
347 
348 void
dialog_close(int return_value)349 dialog_close(int return_value)
350 {
351     int i;
352 
353     mouse_unregister(main_handle);
354     mouse_unregister(text_handle);
355     db_up = 0;
356     db_return_value = return_value;
357 
358     for (i = 0; i < dbn; i++)
359 	if (db_entry[i].type == DB_PARA)
360 	    free(db_entry[i].text);
361 
362     if (db_screen_fresh) {
363 	Fgl_putrect(&dialog_window,db_screen_buffer);
364 	free(db_screen_buffer);
365 	db_screen_fresh = 0;
366     }
367 
368     db_flag = 0;
369 }
370 
371