1 /* Tower Toppler - Nebulus
2 * Copyright (C) 2000-2012 Andreas R�ver
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 */
18
19 #include "menusys.h"
20
21 #include "string.h"
22 #include "screen.h"
23 #include "sprites.h"
24 #include "configuration.h"
25 #include "keyb.h"
26
27 #include <ctype.h>
28
29 static menubg_callback_proc menu_background_proc = NULL;
30
set_men_bgproc(menubg_callback_proc proc)31 void set_men_bgproc(menubg_callback_proc proc) {
32 menu_background_proc = proc;
33 }
34
new_menu_system(const char * title,menuopt_callback_proc pr,int molen,int ystart)35 _menusystem *new_menu_system(const char *title, menuopt_callback_proc pr, int molen, int ystart)
36 {
37 _menusystem *ms = new _menusystem;
38
39 if (ms) {
40 memset(ms->title, '\0', MENUTITLELEN);
41 if (title) {
42 memcpy(ms->title, title, (strlen(title) < MENUTITLELEN) ? strlen(title) + 1 : (MENUTITLELEN-1));
43 }
44
45 ms->numoptions = 0;
46 ms->moption = NULL;
47 ms->mstate = 0;
48 ms->mproc = pr;
49 ms->maxoptlen = molen;
50 ms->exitmenu = false;
51 ms->wraparound = false;
52 ms->curr_mtime = ms->hilited = 0;
53 ms->ystart = ystart;
54 ms->key = SDLK_UNKNOWN;
55 ms->mtime = ms->yhilitpos = ms->opt_steal_control = -1;
56 ms->timeproc = NULL;
57 }
58
59 return ms;
60 }
61
62 _menusystem *
add_menu_option(_menusystem * ms,const char * name,menuopt_callback_proc pr,SDLKey quickkey,menuoptflags flags,int state)63 add_menu_option(_menusystem *ms,
64 const char *name,
65 menuopt_callback_proc pr,
66 SDLKey quickkey,
67 menuoptflags flags,
68 int state) {
69 _menuoption *tmp;
70 int olen = 0;
71
72 if (!ms) return ms;
73
74 tmp = new _menuoption[ms->numoptions+1];
75
76 if (!tmp) return ms;
77
78 memcpy(tmp, ms->moption, sizeof(_menuoption)*ms->numoptions);
79 delete [] ms->moption;
80
81 memset(tmp[ms->numoptions].oname, '\0', MENUOPTIONLEN);
82
83 /* if no name, but has callback proc, query name from it. */
84 if (!name && pr) name = (*pr) (NULL);
85
86 if (name) {
87 olen = strlen(name);
88 memcpy(tmp[ms->numoptions].oname, name, (olen < MENUOPTIONLEN) ? olen + 1 : (MENUOPTIONLEN-1));
89 }
90
91 tmp[ms->numoptions].oproc = pr;
92 tmp[ms->numoptions].ostate = state;
93 tmp[ms->numoptions].oflags = flags;
94 tmp[ms->numoptions].quickkey = quickkey;
95
96 ms->moption = tmp;
97 ms->numoptions++;
98 if (name)
99 olen = scr_textlength(name);
100 else
101 olen = 0;
102 if (ms->maxoptlen < olen) ms->maxoptlen = olen;
103
104 return ms;
105 }
106
107 void
free_menu_system(_menusystem * ms)108 free_menu_system(_menusystem *ms)
109 {
110 if (!ms) return;
111
112 delete [] ms->moption;
113 ms->numoptions = 0;
114 ms->mstate = 0;
115 ms->mproc = NULL;
116 delete ms;
117 }
118
draw_background(_menusystem * ms)119 static void draw_background(_menusystem *ms)
120 {
121 while (!ms->mproc && ms->parent) ms = ms->parent;
122
123 if (ms->mproc)
124 (*ms->mproc)(ms);
125 else if (menu_background_proc)
126 (*menu_background_proc) ();
127 }
128
129
130 void
draw_menu_system(_menusystem * ms,Uint16 dx,Uint16 dy)131 draw_menu_system(_menusystem *ms, Uint16 dx, Uint16 dy)
132 {
133 static int color_r = 0, color_g = 20, color_b = 70;
134
135 if (!ms) return;
136
137 int y, offs = 0, len, realy, minx, miny, maxx, maxy, scrlen,
138 newhilite = -1, yz, titlehei;
139 bool has_title = (ms->title) && (strlen(ms->title) != 0);
140
141 if (ms->wraparound) {
142 if (ms->hilited < 0)
143 ms->hilited = ms->numoptions - 1;
144 else if (ms->hilited >= ms->numoptions)
145 ms->hilited = 0;
146 } else {
147 if (ms->hilited < 0)
148 ms->hilited = 0;
149 else if (ms->hilited >= ms->numoptions)
150 ms->hilited = ms->numoptions - 1;
151 }
152
153 draw_background(ms);
154
155 titlehei = 0;
156
157 if (has_title) {
158 int pos = 0;
159 int start = 0;
160 int len = strlen(ms->title);
161
162 while (pos <= len) {
163
164 if ((ms->title[pos] == '\n') || (ms->title[pos] == 0)) {
165
166 bool end = ms->title[pos] == 0;
167
168 ms->title[pos] = 0;
169 scr_writetext_center(ms->ystart + titlehei * FONTHEI, ms->title + start);
170 titlehei ++;
171
172 if (!end)
173 ms->title[pos] = '\n';
174
175 start = pos + 1;
176 }
177 pos++;
178 }
179
180 titlehei++;
181 }
182
183
184
185 /* TODO: Calculate offs from ms->hilited.
186 * TODO: Put slider if more options than fits in screen.
187 */
188
189 yz = ms->ystart + (titlehei) * FONTHEI;
190
191 for (y = 0; (yz+y+1 < SCREENHEI) && (y+offs < ms->numoptions); y++) {
192 realy = yz + y * FONTHEI;
193 len = strlen(ms->moption[y+offs].oname);
194 scrlen = scr_textlength(ms->moption[y+offs].oname, len);
195 minx = (SCREENWID - scrlen) / 2;
196 miny = realy;
197 maxx = (SCREENWID + scrlen) / 2;
198 maxy = realy + FONTHEI;
199 if (len) {
200 if (dx >= minx && dx <= maxx && dy >= miny && dy <= maxy) {
201 newhilite = y + offs;
202 ms->curr_mtime = 0;
203 }
204 if (y + offs == ms->hilited) {
205 if (ms->yhilitpos == -1) {
206 ms->yhilitpos = miny;
207 } else {
208 if (ms->yhilitpos < miny) {
209 ms->yhilitpos += ((miny - ms->yhilitpos + 3) / 4)+1;
210 if (ms->yhilitpos > miny) ms->yhilitpos = miny;
211 } else if (ms->yhilitpos > miny) {
212 ms->yhilitpos -= ((ms->yhilitpos - miny + 3) / 4)+1;
213 if (ms->yhilitpos < miny) ms->yhilitpos = miny;
214 }
215 }
216 scr_putbar((SCREENWID - ms->maxoptlen - 8) / 2, ms->yhilitpos - 3,
217 ms->maxoptlen + 8, FONTHEI + 3,
218 color_r, color_g, color_b, (config.use_alpha_darkening())?128:255);
219 }
220 }
221 }
222
223 maxy = y;
224
225 for (y = 0; y < maxy; y++) {
226 if (strlen(ms->moption[y+offs].oname)) {
227 miny = ms->ystart + (y + titlehei)*FONTHEI;
228 if ((ms->moption[y+offs].oflags & MOF_LEFT))
229 scr_writetext((SCREENWID - ms->maxoptlen) / 2 + 4, miny,
230 ms->moption[y+offs].oname);
231 else
232 if ((ms->moption[y+offs].oflags & MOF_RIGHT))
233 scr_writetext((SCREENWID + ms->maxoptlen) / 2 - 4
234 - scr_textlength(ms->moption[y+offs].oname), miny,
235 ms->moption[y+offs].oname);
236 else
237 scr_writetext_center(miny, ms->moption[y+offs].oname);
238 }
239 }
240
241 if (newhilite >= 0) ms->hilited = newhilite;
242
243 scr_color_ramp(&color_r, &color_g, &color_b);
244
245 scr_swap();
246 dcl_wait();
247 }
248
249 void
menu_system_caller(_menusystem * ms)250 menu_system_caller(_menusystem *ms)
251 {
252 const char *tmpbuf = (*ms->moption[ms->hilited].oproc) (ms);
253 if (tmpbuf) {
254 int olen = strlen(tmpbuf);
255 memset(ms->moption[ms->hilited].oname, '\0', MENUOPTIONLEN);
256 memcpy(ms->moption[ms->hilited].oname, tmpbuf,
257 (olen < MENUOPTIONLEN) ? olen + 1 : (MENUOPTIONLEN-1));
258 olen = scr_textlength(tmpbuf);
259 if (ms->maxoptlen < olen) ms->maxoptlen = olen;
260 ms->key = SDLK_UNKNOWN;
261 }
262 }
263
264
265 _menusystem *
run_menu_system(_menusystem * ms,_menusystem * parent)266 run_menu_system(_menusystem *ms, _menusystem *parent)
267 {
268 Uint16 x,y;
269 ttkey bttn;
270 bool stolen = false;
271
272 if (!ms) return ms;
273
274 ms->parent = parent;
275
276 /* find the first option with text */
277 if (!strlen(ms->moption[ms->hilited].oname))
278 do {
279 ms->hilited = (ms->hilited + 1) % ms->numoptions;
280 } while (!strlen(ms->moption[ms->hilited].oname));
281
282 (void)key_sdlkey();
283
284 do {
285
286 stolen = false;
287 bttn = no_key;
288 x = y = 0;
289
290 ms->key = SDLK_UNKNOWN;
291
292 if ((ms->curr_mtime++ >= ms->mtime) && ms->timeproc) {
293 (void) (*ms->timeproc) (ms);
294 ms->curr_mtime = 0;
295 }
296
297 if ((ms->opt_steal_control >= 0) &&
298 (ms->opt_steal_control < ms->numoptions) &&
299 ms->moption[ms->opt_steal_control].oproc) {
300 ms->key = key_sdlkey();
301 ms->hilited = ms->opt_steal_control;
302 stolen = true;
303 } else {
304 if (!config.fullscreen() && !key_mouse(&x, &y, &bttn) && bttn)
305 ms->key = key_conv2sdlkey(bttn, false);
306 else ms->key = key_sdlkey();
307 }
308
309 draw_menu_system(ms, x, y);
310
311 if ((ms->key != SDLK_UNKNOWN) || stolen) {
312
313 if (!stolen) {
314 ms->curr_mtime = 0;
315 for (int tmpz = 0; tmpz < ms->numoptions; tmpz++)
316 if (ms->moption[tmpz].quickkey == ms->key) {
317 ms->hilited = tmpz;
318 ms->key = SDLK_UNKNOWN;
319 break;
320 }
321 }
322
323 if ((((ms->moption[ms->hilited].oflags & MOF_PASSKEYS)) || stolen) &&
324 (ms->moption[ms->hilited].oproc) && ((ms->key != SDLK_UNKNOWN) || stolen)) {
325 menu_system_caller(ms);
326 }
327 if (!stolen) {
328 switch (key_sdlkey2conv(ms->key, false)) {
329 case down_key:
330 if (ms->wraparound) {
331 do {
332 ms->hilited = (ms->hilited + 1) % ms->numoptions;
333 } while (!strlen(ms->moption[ms->hilited].oname));
334 } else {
335 if (ms->hilited < ms->numoptions) {
336 ms->hilited++;
337 if (!strlen(ms->moption[ms->hilited].oname)) ms->hilited++;
338 }
339 }
340 break;
341 case up_key:
342 if (ms->wraparound) {
343 do {
344 ms->hilited--;
345 if (ms->hilited < 0) ms->hilited = ms->numoptions - 1;
346 } while (!strlen(ms->moption[ms->hilited].oname));
347 } else {
348 int tmpz = ms->hilited;
349 if (ms->hilited > 0) {
350 do {
351 if (ms->hilited < 0) {
352 ms->hilited = tmpz;
353 break;
354 }
355 ms->hilited--;
356 } while (!strlen(ms->moption[ms->hilited].oname));
357 }
358 }
359 break;
360 case fire_key:
361 if ((ms->hilited >= 0) && (ms->hilited < ms->numoptions) &&
362 ms->moption[ms->hilited].oproc) {
363 const char *tmpbuf = (*ms->moption[ms->hilited].oproc) (ms);
364 if (tmpbuf) {
365 int olen = strlen(tmpbuf);
366 memset(ms->moption[ms->hilited].oname, '\0', MENUOPTIONLEN);
367 memcpy(ms->moption[ms->hilited].oname, tmpbuf,
368 (olen < MENUOPTIONLEN) ? olen + 1 : (MENUOPTIONLEN-1));
369 olen = scr_textlength(tmpbuf);
370 if (ms->maxoptlen < olen) ms->maxoptlen = olen;
371 }
372 break;
373 }
374 case break_key : ms->exitmenu = true; break;
375 default:
376 break;
377 }
378 }
379 }
380 } while (!ms->exitmenu);
381 return ms;
382 }
383
men_info(char * s,long timeout,int fire)384 void men_info(char *s, long timeout, int fire) {
385 bool ende = false;
386 do {
387 if (menu_background_proc) (*menu_background_proc) ();
388 scr_writetext_center((SCREENHEI / 5), s);
389 if (fire)
390 scr_writetext_center((SCREENHEI / 5) + 2 * FONTHEI, (fire == 1) ? _("Press fire") : _("Press space"));
391 scr_swap();
392 dcl_wait();
393 if (timeout > 0) timeout--;
394 if (!timeout) ende = true;
395 else if ((fire == 2) && (key_chartyped() == ' ')) ende = true;
396 else if ((fire != 2) && key_keypressed(fire_key)) ende = true;
397 } while (!ende);
398 (void)key_sdlkey();
399 }
400
401 static int input_box_cursor_state = 0;
402
403 void
draw_input_box(int x,int y,int len,int cursor,char * txt)404 draw_input_box(int x, int y, int len, int cursor, char *txt)
405 {
406 static int col_r = 0, col_g = 200, col_b = 120;
407 int nlen = len, slen = len;
408 int arrows = 0;
409
410 if ((len+3)*FONTMAXWID > SCREENWID)
411 nlen = (SCREENWID / FONTMAXWID) - 3;
412
413 if (x < 0) x = (SCREENWID / 2) - nlen * (FONTMAXWID / 2);
414 if (x < 0) x = 0;
415 if (y < 0) y = (SCREENHEI / 2) - (FONTHEI / 2);
416
417 scr_putbar(x, y, nlen * FONTMAXWID, FONTHEI, 0, 0, 0, (config.use_alpha_darkening())?128:255);
418
419 if (scr_textlength(txt) >= nlen*FONTMAXWID) {
420 while ((cursor >= 0) &&
421 (scr_textlength(txt, cursor+(nlen/2)) >= (nlen)*FONTMAXWID)) {
422 cursor--;
423 txt++;
424 arrows = 1;
425 }
426 }
427 if (scr_textlength(txt) >= nlen*FONTMAXWID) {
428 arrows |= 2;
429 while ((slen > 0) && (scr_textlength(txt, slen) >= nlen*FONTMAXWID)) slen--;
430 }
431
432 scr_writetext(x+1,y, txt, slen);
433
434 if ((input_box_cursor_state & 4) && (cursor >= 0))
435 scr_putbar(x + scr_textlength(txt, cursor) + 1, y, FONTMINWID, FONTHEI,
436 col_r, col_g, col_b, (config.use_alpha_darkening())?128:255);
437 scr_putrect(x,y, nlen * FONTMAXWID, FONTHEI, col_r, col_g, col_b, 255);
438
439 if ((arrows & 1)) scr_writetext(x-FONTMAXWID,y, "\x08"); //fontptrright
440 if ((arrows & 2)) scr_writetext(x+(nlen*FONTMAXWID),y, "\x06"); //fontptrleft
441
442 input_box_cursor_state++;
443
444 scr_color_ramp(&col_r, &col_g, &col_b);
445 }
446
men_input(char * origs,int max_len,int xpos,int ypos,const char * allowed)447 bool men_input(char *origs, int max_len, int xpos, int ypos, const char *allowed) {
448 SDLKey sdlinp;
449 char inpc;
450 ttkey inptt;
451 static int pos = strlen(origs);
452 int ztmp;
453 static char s[256];
454 static bool copy_origs = true;
455 bool restore_origs = false;
456 bool ende = false;
457
458 if ((strlen(origs) >= 256)) return true;
459
460 if (copy_origs) {
461 strcpy(s, origs);
462 copy_origs = false;
463 pos = strlen(origs);
464 }
465
466 (void)key_readkey();
467
468 if (menu_background_proc) (*menu_background_proc) ();
469
470 draw_input_box(xpos,ypos, max_len, pos, s);
471 scr_swap();
472 dcl_wait();
473
474 key_keydatas(sdlinp, inptt, inpc);
475
476 switch (sdlinp) {
477 case SDLK_RIGHT: if ((unsigned)pos < strlen(s)) pos++; break;
478 case SDLK_LEFT: if (pos > 0) pos--; break;
479 case SDLK_ESCAPE:if (strlen(s)) {
480 s[0] = '\0';
481 pos = 0;
482 restore_origs = false;
483 } else {
484 restore_origs = true;
485 ende = true;
486 }
487 break;
488 case SDLK_RETURN: restore_origs = false; copy_origs = true; ende = true;
489 break;
490 case SDLK_DELETE:
491 if (strlen(s) >= (unsigned)pos) {
492 for (ztmp = pos; ztmp < max_len-1; ztmp++) s[ztmp] = s[ztmp+1];
493 s[ztmp] = '\0';
494 }
495 break;
496 case SDLK_BACKSPACE:
497 if (pos > 0) {
498 if (pos <= max_len) {
499 for (ztmp = pos-1; ztmp < max_len-1; ztmp++) s[ztmp] = s[ztmp+1];
500 s[ztmp] = '\0';
501 }
502 pos--;
503 }
504 break;
505 default:
506 if (pos >= max_len || (inpc < ' ')) break;
507 if (allowed) {
508 if (!strchr(allowed, inpc)) {
509 if (strchr(allowed, toupper(inpc))) inpc = toupper(inpc);
510 else
511 if (strchr(allowed, tolower(inpc))) inpc = tolower(inpc);
512 else break;
513 }
514 } else {
515 if (inpc < ' ' || inpc > 'z') break;
516 }
517 if ((strlen(s) >= (unsigned)pos) &&
518 (strlen(s) < (unsigned)max_len)) {
519 for (ztmp = max_len-1; ztmp >= pos; ztmp--) s[ztmp+1] = s[ztmp];
520 s[pos] = inpc;
521 s[max_len] = '\0';
522 pos++;
523 }
524 break;
525 }
526 if (ende) {
527 if (!restore_origs) strcpy(origs, s);
528 s[0] = 0;
529 copy_origs = true;
530 } else {
531 copy_origs = false;
532 }
533 return ende;
534 }
535
536 _menusystem *
set_menu_system_timeproc(_menusystem * ms,long t,menuopt_callback_proc pr)537 set_menu_system_timeproc(_menusystem *ms, long t, menuopt_callback_proc pr)
538 {
539 if (!ms) return ms;
540
541 ms->timeproc = pr;
542 ms->mtime = t;
543
544 return ms;
545 }
546
547 static const char *
men_yn_option_yes(_menusystem * ms)548 men_yn_option_yes(_menusystem *ms)
549 {
550 if (ms) {
551 ms->mstate = 1;
552 ms->exitmenu = true;
553 return NULL;
554 } else return _("Yes");
555 }
556
557 static const char *
men_yn_option_no(_menusystem * ms)558 men_yn_option_no(_menusystem *ms)
559 {
560 if (ms) {
561 ms->mstate = 0;
562 ms->exitmenu = true;
563 return NULL;
564 } else return _("No");
565 }
566
men_yn(char * s,bool defchoice,menuopt_callback_proc pr)567 unsigned char men_yn(char *s, bool defchoice, menuopt_callback_proc pr) {
568 _menusystem *ms = new_menu_system(s, pr, 0, SCREENHEI / 5);
569
570 bool doquit = false;
571
572 if (!ms) return defchoice;
573
574 ms = add_menu_option(ms, NULL, men_yn_option_no, SDLK_n);
575 ms = add_menu_option(ms, NULL, men_yn_option_yes, SDLK_y);
576
577 ms->mstate = defchoice ? 1 : 0;
578
579 ms = run_menu_system(ms, 0);
580
581 doquit = (ms->mstate != 0);
582
583 free_menu_system(ms);
584
585 return ((doquit == 0) ? 0 : 1);
586 }
587
588