1 /*
2     cursesmenudisplay.c
3 
4     Copyright (C) 2010-2019 Amf
5 
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10 
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15 
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 */
20 
21 #include <stdlib.h>
22 #include <ctype.h>
23 #include <string.h>
24 
25 #ifdef CHROMA_CURSES_HEADER
26 #include CHROMA_CURSES_HEADER
27 #else
28 #ifdef __WIN32__
29 #include <curses.h>
30 #else
31 #include <ncurses.h>
32 #endif
33 #endif
34 
35 #include "chroma.h"
36 #include "menu.h"
37 #include "actions.h"
38 #include "level.h"
39 #include "display.h"
40 #include "util.h"
41 
42 #define MAX_WIDTH 65
43 
44 extern int display_size_x, display_size_y;
45 extern int actions[KEY_MAX];
46 
47 extern short colourpair_menu;
48 extern short colourpair_menugrey;
49 extern short colourpair_red;
50 extern short colourpair_green;
51 extern short colourpair_yellow;
52 extern short colourpair_blue;
53 extern short colourpair_cyan;
54 extern short colourpair_magenta;
55 extern short colourpair_cyan;
56 extern short colourpair_white;
57 
58 int menu_offset;
59 int menu_width;
60 int menu_height_notes;
61 int menu_height_entries;
62 int menu_y_min;
63 int menu_y_max;
64 int menu_y_logo_top;
65 int menu_y_logo_bottom;
66 int menu_scroll_y_min;
67 int menu_scroll_y_max;
68 int menu_scroll_top;
69 int menu_scroll_bottom;
70 
menu_entryheight(struct menuentry * pentry)71 int menu_entryheight(struct menuentry *pentry)
72 {
73     if(pentry->flags & MENU_INVISIBLE)
74         return 0;
75     if(pentry->flags & MENU_DOUBLE)
76         return 2;
77     return 1;
78 }
79 
menu_displayentry(struct menu * pmenu,struct menuentry * pentry,int y,int selected)80 void menu_displayentry(struct menu *pmenu, struct menuentry *pentry, int y, int selected)
81 {
82     char buffer[MAX_WIDTH + 1];
83     int x, i;
84 
85     if(menu_width < 1)
86         return;
87 
88     if(pentry->flags & MENU_INVISIBLE)
89         return;
90 
91     if(pentry->flags & (MENU_GREY | MENU_NOTE | MENU_TEXT))
92         selected = 0;
93 
94     /* Plot first line, if visible */
95     if((y >= menu_y_min && y < menu_y_max) || (pentry->flags & MENU_NOTE))
96     {
97         /* Plot key press */
98         attron(COLOR_PAIR(colourpair_white));
99         if(pentry->key != 0 && pentry->key != MENU_KEY_ANY)
100         {
101             mvprintw(y, menu_offset - 4, "[ ] ");
102             attron(A_BOLD);
103             mvprintw(y, menu_offset - 3, "%c", pentry->key);
104             attroff(A_BOLD);
105         }
106         else
107             mvprintw(y, menu_offset - 4, "    ");
108         attroff(COLOR_PAIR(colourpair_white));
109 
110         /* Determine colour */
111         if(pentry->flags & (MENU_GREY | MENU_SPACE))
112             attron(COLOR_PAIR(colourpair_menugrey));
113         else if(pentry->flags & MENU_NOTE)
114         {
115             if(pmenu->logo == 0)
116                 attron(COLOR_PAIR(colourpair_menugrey));
117         }
118         else if(pentry->flags & MENU_TEXT)
119             attron(COLOR_PAIR(colourpair_white));
120         else
121             attron(COLOR_PAIR(colourpair_menu));
122         if(selected)
123             attron(A_REVERSE);
124 
125         /* Blank line */
126         move(y, menu_offset);
127         for(i = 0; i < menu_width; i ++)
128             addch(' ');
129 
130         /* Plot right hand side text */
131         if(pentry->text2 != NULL)
132         {
133             utf8strncpy(buffer, pentry->text2, menu_width - 2);
134             x = menu_offset + menu_width - 1 - utf8strlen(buffer);
135             mvprintw(y, x, "%s", buffer);
136         }
137 
138         /* Plot main text */
139         if(pentry->text != NULL)
140         {
141             utf8strncpy(buffer, pentry->text, menu_width - 2);
142 
143             x = menu_offset + 1;
144             if(pentry->flags & MENU_RIGHT)
145                 x = menu_offset + menu_width - utf8strlen(buffer);
146             if(pentry->flags & MENU_CENTRE)
147                 x = menu_offset + ((menu_width - utf8strlen(buffer))/2);
148 
149             if(pentry->flags & MENU_BOLD)
150                 attron(A_BOLD);
151             mvprintw(y, x, "%s", buffer);
152             if(pentry->flags & MENU_BOLD)
153                 attroff(A_BOLD);
154         }
155 
156         if(selected)
157             attroff(A_REVERSE);
158         if(pentry->flags & (MENU_GREY | MENU_NOTE | MENU_SPACE))
159             attroff(COLOR_PAIR(colourpair_menugrey));
160         else if(pentry->flags & MENU_TEXT)
161             attroff(COLOR_PAIR(colourpair_white));
162         else
163             attroff(COLOR_PAIR(colourpair_menu));
164     }
165 
166     if(!(pentry->flags & MENU_DOUBLE))
167         return;
168 
169     y ++;
170 
171     /* Plot second line, if visible */
172     if((y >= menu_y_min && y < menu_y_max) || (pentry->flags & MENU_NOTE))
173     {
174         /* Blank key press area */
175         attron(COLOR_PAIR(colourpair_white));
176         mvprintw(y, menu_offset - 4, "    ");
177         attroff(COLOR_PAIR(colourpair_white));
178 
179         /* Determine colour */
180         if(pentry->flags & (MENU_GREY | MENU_NOTE | MENU_SPACE))
181             attron(COLOR_PAIR(colourpair_menugrey));
182         else
183             attron(COLOR_PAIR(colourpair_menu));
184         if(selected)
185             attron(A_REVERSE);
186 
187         /* Blank line */
188         move(y, menu_offset);
189         for(i = 0; i < menu_width; i ++)
190             addch(' ');
191 
192         /* Plot right hand side text */
193         if(pentry->text4 != NULL)
194         {
195             utf8strncpy(buffer, pentry->text4, menu_width - 2);
196 
197             x = menu_offset + menu_width - 1 - utf8strlen(buffer);
198             if(pentry->flags & MENU_EDITING)
199                 attron(COLOR_PAIR(colourpair_white));
200             mvprintw(y, x, "%s", buffer);
201             if(pentry->flags & MENU_EDITING)
202                 attroff(COLOR_PAIR(colourpair_white));
203         }
204 
205         /* Plot left hand side text */
206         if(pentry->text3 != NULL)
207         {
208             utf8strncpy(buffer, pentry->text3, menu_width - 2);
209 
210             x = menu_offset + 1;
211             mvprintw(y, x, "%s", buffer);
212         }
213 
214         if(selected)
215             attroff(A_REVERSE);
216         if(pentry->flags & (MENU_GREY | MENU_SPACE))
217             attroff(COLOR_PAIR(colourpair_menugrey));
218         else if(pentry->flags & MENU_NOTE)
219         {
220             if(pmenu->logo == 0)
221                 attroff(COLOR_PAIR(colourpair_menugrey));
222         }
223         else
224             attroff(COLOR_PAIR(colourpair_menu));
225     }
226 }
227 
menu_display(struct menu * pmenu,int redraw)228 void menu_display(struct menu *pmenu, int redraw)
229 {
230     struct menuentry *pentry;
231     int x, y, selected;
232     char title[] = "chroma";
233     char buffer[256];
234     int i;
235     int state;
236     int y_editing;
237     short cp;
238     int j;
239 
240                 /* 012345678901234567890123456789012345678901 */
241     char logo[] = "       8                                  "
242                   "       8                                  "
243                   ".oPYo. 8oPYo. oPYo. .oPYo. ooYoYo. .oPYo. "
244                   "8    ' 8    8 8  `' 8    8 8' 8  8 .oooo8 "
245                   "8    . 8    8 8     8    8 8  8  8 8    8 "
246                   "`YooP' 8    8 8     `YooP' 8  8  8 `YooP8 ";
247 
248 #define LOGO_HEIGHT 7
249 
250     /* Calculate various widths */
251     menu_width = display_size_x - 2;
252     if(menu_width > MAX_WIDTH)
253         menu_width = MAX_WIDTH;
254 
255     menu_offset = (display_size_x - menu_width) / 2;
256     if(menu_offset < 0)
257         menu_offset = 0;
258 
259     menu_width -= 5;
260     menu_offset += 4;
261 
262     /* Calculate various heights */
263     menu_height_notes = 0;
264     menu_height_entries = 0;
265 
266     pentry = pmenu->entry_first;
267     while(pentry != NULL)
268     {
269         if(pentry->flags & MENU_NOTE)
270             menu_height_notes += menu_entryheight(pentry);
271         else
272             menu_height_entries += menu_entryheight(pentry);
273         pentry = pentry->next;
274     }
275 
276     /* Add an extra line to pad the notes out, if there are any */
277     if(menu_height_notes != 0)
278         menu_height_notes += 1;
279 
280     if(pmenu->logo)
281     {
282         menu_y_logo_top = (display_size_y - menu_height_entries - LOGO_HEIGHT) / 2;
283         if(menu_y_logo_top < 0)
284             menu_y_logo_top = 0;
285         menu_y_logo_bottom = menu_y_logo_top + LOGO_HEIGHT;
286         menu_y_min = display_size_y - menu_height_entries - 1;
287         if(menu_y_min < menu_y_logo_bottom)
288             menu_y_min = menu_y_logo_bottom;
289         menu_y_max = display_size_y - 1;
290     }
291     else
292     {
293         menu_y_min = 4;
294         menu_y_max = display_size_y - menu_height_notes - 1;
295         if(pmenu->title != NULL)
296             menu_y_min += 2;
297     }
298 
299     /* If a full redraw, clear the screen and plot the title */
300     if(redraw == MENUREDRAW_ALL)
301     {
302     clear();
303     curs_set(0);
304 
305         if(pmenu->logo)
306         {
307             x = (display_size_x - 42) / 2;
308             y = menu_y_logo_top;
309             cp = colourpair_red;
310 
311             move(y, x);
312 
313             for(i = 0; i < strlen(logo); i ++)
314             {
315                 addch(logo[i] | A_BOLD | COLOR_PAIR(cp));
316                 switch(i % 42)
317                 {
318                     case 6:
319                         cp = colourpair_yellow;
320                         break;
321                     case 13:
322                         cp = colourpair_green;
323                         break;
324                     case 18:
325                         cp = colourpair_cyan;
326                         break;
327                     case 25:
328                         cp = colourpair_blue;
329                         break;
330                     case 33:
331                         cp = colourpair_magenta;
332                         break;
333                     case 41:
334                         y ++;
335                         move(y, x);
336                         cp = colourpair_red;
337                         break;
338                 }
339             }
340 
341             y ++;
342 
343         }
344         else
345         {
346             /* Display game title */
347             x = (display_size_x / 2) - strlen(title);
348 
349             attron(A_BOLD);
350             for(i = 0; i < strlen(title); i ++)
351             {
352                 if(i == 0) attron(COLOR_PAIR(colourpair_red));
353                 if(i == 1) attron(COLOR_PAIR(colourpair_yellow));
354                 if(i == 2) attron(COLOR_PAIR(colourpair_green));
355                 if(i == 3) attron(COLOR_PAIR(colourpair_cyan));
356                 if(i == 4) attron(COLOR_PAIR(colourpair_blue));
357                 if(i == 5) attron(COLOR_PAIR(colourpair_magenta));
358 
359                 sprintf(buffer, "%c", title[i]);
360                 mvprintw(2, x, "%s", buffer);
361                 x +=2;
362             }
363             attroff(COLOR_PAIR(colourpair_red));
364             attroff(A_BOLD);
365 
366             /* Display menu title */
367             y = 4;
368             x = (display_size_x / 2) - utf8strlen(pmenu->title);
369 
370             attron(A_BOLD);
371             attroff(COLOR_PAIR(colourpair_white));
372             /* If the title is too long, plot normally */
373             if(x < 0)
374             {
375                 x = (display_size_x - utf8strlen(pmenu->title)) / 2;
376                 mvprintw(y, x, "%s", pmenu->title);
377             }
378             else
379             {
380                 /* Otherwise, spread it out */
381                 for(i = 0; i < strlen(pmenu->title); i ++)
382                 {
383             j = 0;
384             buffer[j] = pmenu->title[i]; j ++;
385             while((pmenu->title[i + j] & 0xc0) == 0x80)
386             {
387             buffer[j] = pmenu->title[i + j]; j ++;
388             }
389             buffer[j] = 0;
390                     mvprintw(y, x, "%s", buffer);
391             x += 2;  i += j - 1;
392                 }
393             }
394             attroff(A_BOLD);
395         }
396     }
397 
398     /* Keep scroll bar within reasonable limits */
399     if(pmenu->offset > (menu_height_entries - (menu_y_max - menu_y_min)))
400         pmenu->offset = menu_height_entries - (menu_y_max - menu_y_min);
401     if(pmenu->offset < 0)
402         pmenu->offset = 0;
403 
404     /* Display scrollbar, if needed */
405     if(menu_height_entries > (menu_y_max - menu_y_min) || pmenu->offset != 0)
406     {
407         if(redraw >= MENUREDRAW_ENTRIES)
408         {
409             menu_scroll_y_min = menu_y_min + 1;
410             menu_scroll_y_max = menu_y_max - 1;
411             menu_scroll_top = menu_scroll_y_min + ((menu_scroll_y_max - menu_scroll_y_min) * pmenu->offset / menu_height_entries);
412             menu_scroll_bottom = menu_scroll_top + ((menu_scroll_y_max - menu_scroll_y_min) * (1 + menu_y_max - menu_y_min) / menu_height_entries);
413 
414             attron(COLOR_PAIR(colourpair_menugrey));
415             x = menu_offset + menu_width + 1;
416             mvaddch(menu_y_min, x, '^');
417             mvaddch(menu_y_max - 1, x, 'v');
418 
419             attron(COLOR_PAIR(colourpair_white));
420             for(y = menu_y_min + 1; y < menu_y_max - 1; y ++)
421             {
422                 /* >= and <= to guarantee at least one character scrollbar */
423                 if(y >= menu_scroll_top && y <= menu_scroll_bottom)
424                 {
425                 attron(A_REVERSE);
426                 mvaddch(y, x, '|');
427                 attroff(A_REVERSE);
428                 }
429                 else
430                 mvaddch(y, x, '.');
431             }
432         }
433     }
434 
435     /* Display notes */
436     if((menu_height_notes != 0) && redraw >= MENUREDRAW_ENTRIES)
437     {
438         if(pmenu->logo)
439             y = menu_y_logo_bottom;
440         else
441             y = menu_y_max + 1;
442 
443         pentry = pmenu->entry_first;
444         while(pentry != NULL)
445         {
446             if(pentry->flags & MENU_NOTE)
447             {
448                 menu_displayentry(pmenu, pentry, y, 0);
449                 y += menu_entryheight(pentry);
450             }
451             pentry = pentry->next;
452         }
453     }
454 
455     /* Select an entry if the selected one is not on screen */
456     if(pmenu->entry_selected == NULL)
457         pmenu->entry_selected = pmenu->entry_first;
458     y = menu_y_min - pmenu->offset;
459     pentry = pmenu->entry_first;
460     pmenu->display_first = NULL;
461     pmenu->display_last = NULL;
462     state = 1;
463     while(pentry != NULL)
464     {
465         /* Don't count notes */
466         if(pentry->flags & MENU_NOTE)
467         {
468             pentry = pentry->next;
469             continue;
470         }
471         /* Is the entry off the top of the screen? */
472         if(y < (menu_y_min - menu_entryheight(pentry)))
473         {
474             if(pentry == pmenu->entry_selected)
475                 state = -1;
476             y += menu_entryheight(pentry);
477             pentry = pentry->next;
478             continue;
479         }
480         /* Stop processing once we hit the bottom of the screen */
481         if(y > menu_y_max)
482         {
483             pentry = NULL;
484             continue;
485         }
486         if(pentry == pmenu->entry_selected)
487             state = 0;
488 
489         if(pmenu->display_first == NULL)
490             pmenu->display_first = pentry->next;
491         pmenu->display_last = pentry->previous;
492 
493         y += menu_entryheight(pentry);
494         pentry = pentry->next;
495     }
496     if(state == -1)
497         pmenu->entry_selected = pmenu->display_first;
498     if(state == 1)
499         pmenu->entry_selected = pmenu->display_last;
500     if(state != 0 && redraw < MENUREDRAW_ENTRIES)
501         redraw = MENUREDRAW_ENTRIES;
502 
503     /* Display entries */
504     pentry = pmenu->entry_first;
505     y = menu_y_min - pmenu->offset;
506     y_editing = 0;
507     while(pentry != NULL)
508     {
509         /* Don't display notes */
510         if(pentry->flags & MENU_NOTE)
511         {
512             pentry = pentry->next;
513             continue;
514         }
515         /* Don't display entries off the top of the screen */
516         if(y < (menu_y_min - menu_entryheight(pentry)))
517         {
518             y += menu_entryheight(pentry);
519             pentry = pentry->next;
520             continue;
521         }
522         /* Stop processing once we hit the bottom of the screen */
523         if(y > menu_y_max)
524         {
525             pentry = NULL;
526             continue;
527         }
528         selected = (pmenu->entry_selected == pentry) ? 1 : 0;
529 
530         if(redraw >= MENUREDRAW_ENTRIES ||
531                 (redraw >= MENUREDRAW_CHANGED && pentry->redraw))
532             menu_displayentry(pmenu, pentry, y, selected);
533         if(pentry->flags & MENU_EDITING)
534             y_editing = y + 1;
535 
536 
537         pentry->redraw = 0;
538         y += menu_entryheight(pentry);
539         pentry = pentry->next;
540     }
541 
542     /* Display cursor if we're editing a text field */
543     if(y_editing)
544     {
545     curs_set(1);
546         move(y_editing, menu_offset + menu_width - 2);
547     }
548     else
549     curs_set(0);
550 
551     /* Redraw the screen */
552     refresh();
553 }
554 
menu_process(struct menu * pmenu)555 int menu_process(struct menu* pmenu)
556 {
557     int ok;
558     int redraw;
559     int c;
560     struct menuentry* pentry;
561     char *buffer;
562 
563     redraw = pmenu->redraw;
564 
565     ok = 0;
566     while(!ok)
567     {
568         if(redraw != MENUREDRAW_NONE)
569         {
570             menu_display(pmenu, redraw);
571             redraw = MENUREDRAW_NONE;
572         }
573 
574         c = getch();
575 
576         /* Are we editing a text field? */
577         if(pmenu->entry_selected != NULL && pmenu->entry_selected->flags & MENU_EDITING)
578         {
579             switch(c)
580             {
581                 case KEY_RESIZE:
582                     getmaxyx(stdscr, display_size_y, display_size_x);
583                     redraw = MENUREDRAW_ALL;
584                     break;
585 
586                 case '\n':
587                 case '\r':
588                 case 27:
589                 case KEY_UP:
590                 case KEY_DOWN:
591                 case KEY_PPAGE:
592                 case KEY_NPAGE:
593                     pmenu->entry_selected->flags -= MENU_EDITING;
594                     pmenu->entry_selected->redraw = 1;
595                     redraw = MENUREDRAW_CHANGED;
596                     break;
597 
598                 case KEY_BACKSPACE:
599                 case KEY_DC:
600                 case 8:
601                     if(strlen(pmenu->entry_selected->text4) == 0)
602                         break;
603 
604                     buffer = malloc(strlen(pmenu->entry_selected->text4) + 1);
605                     if(buffer != NULL)
606                     {
607                         strcpy(buffer, pmenu->entry_selected->text4);
608 
609                         /* not particularly efficient, but does the job of deleting one UTF8 character */
610                         while(strlen(buffer) > 0 && ((buffer[strlen(buffer) - 1] & 0xc0) == 0x80))
611                             buffer[strlen(buffer) - 1] = 0;
612                         buffer[strlen(buffer) - 1] = 0;
613                     }
614                     menuentry_extratext(pmenu->entry_selected, NULL, NULL, buffer);
615                     free(buffer);
616 
617                     pmenu->entry_selected->redraw = 1;
618                     redraw = MENUREDRAW_CHANGED;
619                     break;
620 
621                 default:
622                     if(c > 31 && c != 127)
623                     {
624                         buffer = malloc(strlen(pmenu->entry_selected->text4) + 2);
625                         if(buffer != NULL)
626                         {
627                             strcpy(buffer, pmenu->entry_selected->text4);
628                             buffer[strlen(buffer) + 1] = 0;
629                             buffer[strlen(buffer)] = c;
630                         }
631                         menuentry_extratext(pmenu->entry_selected, NULL, NULL, buffer);
632                         free(buffer);
633 
634                         pmenu->entry_selected->redraw = 1;
635                         redraw = MENUREDRAW_CHANGED;
636                         break;
637                     }
638                     break;
639             }
640             continue;
641         }
642 
643         /* Not a text field, but a regular entry */
644         /* First, check if the key corresponds to an entry */
645         if(c >= 0 && c < 127)
646             c = toupper(c);
647         pentry = pmenu->entry_first;
648         while(pentry != NULL)
649         {
650             if(pentry->key == c)
651             {
652                 if(!(pentry->flags & (MENU_GREY | MENU_INVISIBLE | MENU_SPACE)))
653                 {
654                     if(pmenu->entry_selected != NULL)
655                         pmenu->entry_selected->redraw = 1;
656                     pmenu->entry_selected = pentry;
657                     pentry->redraw = 1;
658 
659                     if(pmenu->entry_selected->flags & MENU_EDITABLE)
660                     {
661                         pmenu->entry_selected->flags |= MENU_EDITING;
662                         pmenu->entry_selected->redraw = 1;
663                         redraw = MENUREDRAW_CHANGED;
664                     }
665                     else
666                         ok = MENU_SELECT;
667                     break;
668                 }
669             }
670             pentry = pentry->next;
671         }
672 
673         if(ok == MENU_SELECT)
674             continue;
675 
676         if(c < 0)
677             continue;
678 
679         /* Otherwise, see if it corresponds to an action */
680         switch(actions[c])
681         {
682             case ACTION_UP:
683                 pentry = pmenu->entry_selected;
684                 while(pentry != NULL)
685                 {
686                     if(pentry == pmenu->display_first || pentry->flags & MENU_TEXT)
687                         redraw = MENUREDRAW_ENTRIES;
688 
689                     pentry = pentry->previous;
690 
691                     if(pentry == NULL)
692                         break;
693                     if(redraw != MENUREDRAW_NONE)
694                         pmenu->offset -= menu_entryheight(pentry);
695                     if(!(pentry->flags & (MENU_GREY | MENU_NOTE | MENU_SPACE)))
696                         break;
697                 }
698                 if(pentry != NULL)
699                 {
700                     pentry->redraw = 1;
701                     pmenu->entry_selected->redraw = 1;
702                     pmenu->entry_selected = pentry;
703                     if(redraw == MENUREDRAW_NONE)
704                         redraw = MENUREDRAW_CHANGED;
705                 }
706                 break;
707 
708             case ACTION_DOWN:
709                 pentry = pmenu->entry_selected;
710                 while(pentry != NULL)
711                 {
712                     if(pentry == pmenu->display_last || pentry->flags & MENU_TEXT)
713                         redraw = MENUREDRAW_ENTRIES;
714 
715                     pentry = pentry->next;
716 
717                     if(pentry == NULL)
718                         break;
719                     if(redraw != MENUREDRAW_NONE)
720                         pmenu->offset += menu_entryheight(pentry);
721                     if(!(pentry->flags & (MENU_GREY | MENU_NOTE | MENU_SPACE)))
722                         break;
723                 }
724                 if(pentry != NULL)
725                 {
726                     pentry->redraw = 1;
727                     pmenu->entry_selected->redraw = 1;
728                     pmenu->entry_selected = pentry;
729                     if(redraw == MENUREDRAW_NONE)
730                         redraw = MENUREDRAW_CHANGED;
731                 }
732                 break;
733 
734             case ACTION_LEFT:
735                 if(pmenu->entry_selected != NULL && pmenu->entry_selected->flags & MENU_SCROLLABLE)
736                     ok = MENU_SCROLLLEFT;
737                 break;
738 
739             case ACTION_RIGHT:
740                 if(pmenu->entry_selected != NULL && pmenu->entry_selected->flags & MENU_SCROLLABLE)
741                     ok = MENU_SCROLLRIGHT;
742                 break;
743 
744             case ACTION_PAGE_UP:
745                 pmenu->offset -= (menu_y_max - menu_y_min);
746                 redraw = MENUREDRAW_ENTRIES;
747                 break;
748 
749             case ACTION_PAGE_DOWN:
750                 pmenu->offset += (menu_y_max - menu_y_min);
751                 redraw = MENUREDRAW_ENTRIES;
752                 break;
753 
754             case ACTION_ENTER:
755                 if(pmenu->entry_selected != NULL)
756                 {
757                    if(pmenu->entry_selected->flags & MENU_EDITABLE)
758                    {
759                        pmenu->entry_selected->flags |= MENU_EDITING;
760                        pmenu->entry_selected->redraw = 1;
761                        redraw = MENUREDRAW_CHANGED;
762                    }
763                    else
764                        ok = MENU_SELECT;
765                 }
766                 break;
767 
768             case ACTION_DELETE:
769                 if(pmenu->entry_selected != NULL && pmenu->entry_selected->flags & MENU_DELETABLE)
770                     ok = MENU_DELETE;
771                 break;
772 
773             case ACTION_QUIT:
774                 ok = MENU_QUIT;
775                 break;
776 
777             case ACTION_REDRAW:
778                 getmaxyx(stdscr, display_size_y, display_size_x);
779                 redraw = MENUREDRAW_ALL;
780                 break;
781 
782             case ACTION_HIDE:
783                 display_hide();
784                 redraw = MENUREDRAW_ALL;
785                 break;
786 
787             default:
788                 /* See if there is an "any key" entry */
789                 pentry = pmenu->entry_first;
790                 while(pentry != NULL)
791                 {
792                     if(pentry->key == MENU_KEY_ANY)
793                     {
794                         if(!(pentry->flags & (MENU_GREY | MENU_INVISIBLE | MENU_SPACE)))
795                         {
796                             if(pmenu->entry_selected != NULL)
797                                 pmenu->entry_selected->redraw = 1;
798                             pmenu->entry_selected = pentry;
799                             pentry->redraw = 1;
800 
801                             ok = MENU_SELECT;
802                             break;
803                         }
804                     }
805                     pentry = pentry->next;
806                 }
807                 break;
808         }
809     }
810 
811     /* Redraw the whole menu when we next process it */
812     pmenu->redraw = MENUREDRAW_ALL;
813 
814     if(ok == MENU_SELECT)
815     {
816         if(pmenu->entry_selected->flags & MENU_SCROLLABLE)
817             ok = MENU_SCROLLRIGHT;
818     }
819     /* unless this is a scrollable entry */
820     if(ok == MENU_SCROLLLEFT || ok == MENU_SCROLLRIGHT)
821     {
822         pmenu->redraw = MENUREDRAW_CHANGED;
823         pmenu->entry_selected->redraw = 1;
824     }
825 
826     return ok;
827 }
828 
menu_addfile(struct menu * pmenu,char * filename)829 int menu_addfile(struct menu *pmenu, char *filename)
830 {
831     FILE *file;
832     char word[256], buffer[4096];
833     char c;
834     int i;
835     int ok, wok;
836 
837     file = fopen(filename, "r");
838     if(file == NULL)
839     return 0;
840 
841     ok = 0;
842     strcpy(buffer, "");
843     while(!ok)
844     {
845     wok = 0; i = 0;
846     while(!wok)
847     {
848         c = fgetc(file);
849         if(c == 0 || c == 13 || c == 10 || c == 32 || c == 9 || feof(file) || i == 254)
850         wok = 1;
851         else
852             word[i++] = c;
853     }
854     word[i] = 0;
855 
856     /* We assume menu_width is defined by the time we get here - a safe
857        assumption provided this isn't the first menu we see. */
858     if(utf8strlen(buffer) + utf8strlen(word) < menu_width - 2 && !(strcmp(word, "") == 0 && (c == 10 || c == 13)) && !(strncmp(word, "===", 3) == 0))
859     {
860         if(buffer[0] != 0 && word[0] != 0)
861             strcat(buffer, " ");
862         strcat(buffer, word);
863     }
864     else
865     {
866         menuentry_new(pmenu, buffer, 0, MENU_TEXT);
867         if(strcmp(word, "") == 0 && (c == 10 || c ==13))
868         menuentry_new(pmenu, "", 0, MENU_TEXT);
869             if(strncmp(word, "===", 3) == 0)
870             {
871                 strcpy(buffer, "");
872                 pmenu->entry_last->flags |= MENU_GREY;
873             }
874             else
875             strcpy(buffer, word);
876     }
877 
878     if(feof(file))
879         ok = 1;
880     }
881 
882     menuentry_new(pmenu, buffer, 0, MENU_TEXT);
883 
884     fclose(file);
885 
886     return 1;
887 }
888