chiark / gitweb /
Update upstream source from tag 'upstream/1.18'
[chroma-debian.git] / cursesmenudisplay.c
diff --git a/cursesmenudisplay.c b/cursesmenudisplay.c
new file mode 100644 (file)
index 0000000..fdb36eb
--- /dev/null
@@ -0,0 +1,887 @@
+/*  
+    cursesmenudisplay.c
+
+    Copyright (C) 2010-2019 Amf
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version. 
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+
+#ifdef CHROMA_CURSES_HEADER
+#include CHROMA_CURSES_HEADER
+#else
+#ifdef __WIN32__
+#include <curses.h>
+#else
+#include <ncurses.h>
+#endif
+#endif
+
+#include "chroma.h"
+#include "menu.h"
+#include "actions.h"
+#include "level.h"
+#include "display.h"
+#include "util.h"
+
+#define MAX_WIDTH 65
+
+extern int display_size_x, display_size_y;
+extern int actions[KEY_MAX];
+
+extern short colourpair_menu;
+extern short colourpair_menugrey;
+extern short colourpair_red;
+extern short colourpair_green;
+extern short colourpair_yellow;
+extern short colourpair_blue;
+extern short colourpair_cyan;
+extern short colourpair_magenta;
+extern short colourpair_cyan;
+extern short colourpair_white;
+
+int menu_offset;
+int menu_width;
+int menu_height_notes;
+int menu_height_entries;
+int menu_y_min;
+int menu_y_max;
+int menu_y_logo_top;
+int menu_y_logo_bottom;
+int menu_scroll_y_min;
+int menu_scroll_y_max;
+int menu_scroll_top;
+int menu_scroll_bottom;
+
+int menu_entryheight(struct menuentry *pentry)
+{
+    if(pentry->flags & MENU_INVISIBLE)
+        return 0;
+    if(pentry->flags & MENU_DOUBLE)
+        return 2;
+    return 1;
+}
+
+void menu_displayentry(struct menu *pmenu, struct menuentry *pentry, int y, int selected)
+{
+    char buffer[MAX_WIDTH + 1];
+    int x, i;
+
+    if(menu_width < 1)
+        return;
+
+    if(pentry->flags & MENU_INVISIBLE)
+        return;
+
+    if(pentry->flags & (MENU_GREY | MENU_NOTE | MENU_TEXT))
+        selected = 0;
+
+    /* Plot first line, if visible */
+    if((y >= menu_y_min && y < menu_y_max) || (pentry->flags & MENU_NOTE))
+    {
+        /* Plot key press */
+        attron(COLOR_PAIR(colourpair_white));
+        if(pentry->key != 0 && pentry->key != MENU_KEY_ANY)
+        {   
+            mvprintw(y, menu_offset - 4, "[ ] ");
+            attron(A_BOLD);
+            mvprintw(y, menu_offset - 3, "%c", pentry->key);
+            attroff(A_BOLD);
+        }
+        else
+            mvprintw(y, menu_offset - 4, "    ");
+        attroff(COLOR_PAIR(colourpair_white));
+
+        /* Determine colour */
+        if(pentry->flags & (MENU_GREY | MENU_SPACE))
+            attron(COLOR_PAIR(colourpair_menugrey));
+        else if(pentry->flags & MENU_NOTE)
+        {
+            if(pmenu->logo == 0)
+                attron(COLOR_PAIR(colourpair_menugrey));
+        }
+        else if(pentry->flags & MENU_TEXT)
+            attron(COLOR_PAIR(colourpair_white));
+        else
+            attron(COLOR_PAIR(colourpair_menu));
+        if(selected)
+            attron(A_REVERSE);
+
+        /* Blank line */
+        move(y, menu_offset);
+        for(i = 0; i < menu_width; i ++)
+            addch(' ');
+
+        /* Plot right hand side text */
+        if(pentry->text2 != NULL)
+        {   
+            utf8strncpy(buffer, pentry->text2, menu_width - 2);
+            x = menu_offset + menu_width - 1 - utf8strlen(buffer);
+            mvprintw(y, x, buffer);
+        }
+
+        /* Plot main text */
+        if(pentry->text != NULL)
+        {
+            utf8strncpy(buffer, pentry->text, menu_width - 2);
+
+            x = menu_offset + 1;
+            if(pentry->flags & MENU_RIGHT)
+                x = menu_offset + menu_width - utf8strlen(buffer);
+            if(pentry->flags & MENU_CENTRE)
+                x = menu_offset + ((menu_width - utf8strlen(buffer))/2);
+
+            if(pentry->flags & MENU_BOLD)
+                attron(A_BOLD);
+            mvprintw(y, x, buffer);
+            if(pentry->flags & MENU_BOLD)
+                attroff(A_BOLD);
+        }
+
+        if(selected)
+            attroff(A_REVERSE);
+        if(pentry->flags & (MENU_GREY | MENU_NOTE | MENU_SPACE))
+            attroff(COLOR_PAIR(colourpair_menugrey));
+        else if(pentry->flags & MENU_TEXT)
+            attroff(COLOR_PAIR(colourpair_white));
+        else
+            attroff(COLOR_PAIR(colourpair_menu));
+    }
+
+    if(!(pentry->flags & MENU_DOUBLE))
+        return;
+
+    y ++;
+
+    /* Plot second line, if visible */
+    if((y >= menu_y_min && y < menu_y_max) || (pentry->flags & MENU_NOTE))
+    {
+        /* Blank key press area */
+        attron(COLOR_PAIR(colourpair_white));
+        mvprintw(y, menu_offset - 4, "    ");
+        attroff(COLOR_PAIR(colourpair_white));
+
+        /* Determine colour */
+        if(pentry->flags & (MENU_GREY | MENU_NOTE | MENU_SPACE))
+            attron(COLOR_PAIR(colourpair_menugrey));
+        else
+            attron(COLOR_PAIR(colourpair_menu));
+        if(selected)
+            attron(A_REVERSE);
+
+        /* Blank line */
+        move(y, menu_offset);
+        for(i = 0; i < menu_width; i ++)
+            addch(' ');
+
+        /* Plot right hand side text */
+        if(pentry->text4 != NULL)
+        {   
+            utf8strncpy(buffer, pentry->text4, menu_width - 2);
+
+            x = menu_offset + menu_width - 1 - utf8strlen(buffer);
+            if(pentry->flags & MENU_EDITING)
+                attron(COLOR_PAIR(colourpair_white));
+            mvprintw(y, x, buffer);
+            if(pentry->flags & MENU_EDITING)
+                attroff(COLOR_PAIR(colourpair_white));
+        }
+
+        /* Plot left hand side text */
+        if(pentry->text3 != NULL)
+        {   
+            utf8strncpy(buffer, pentry->text3, menu_width - 2);
+
+            x = menu_offset + 1;
+            mvprintw(y, x, buffer);
+        }
+
+        if(selected)
+            attroff(A_REVERSE);
+        if(pentry->flags & (MENU_GREY | MENU_SPACE))
+            attroff(COLOR_PAIR(colourpair_menugrey));
+        else if(pentry->flags & MENU_NOTE)
+        {
+            if(pmenu->logo == 0)
+                attroff(COLOR_PAIR(colourpair_menugrey));
+        }
+        else
+            attroff(COLOR_PAIR(colourpair_menu));
+    }
+}
+
+void menu_display(struct menu *pmenu, int redraw)
+{
+    struct menuentry *pentry;
+    int x, y, selected;
+    char title[] = "chroma";
+    char buffer[256];
+    int i;
+    int state;
+    int y_editing;
+    short cp;
+    int j;
+
+                /* 012345678901234567890123456789012345678901 */
+    char logo[] = "       8                                  "
+                  "       8                                  "
+                  ".oPYo. 8oPYo. oPYo. .oPYo. ooYoYo. .oPYo. "
+                  "8    ' 8    8 8  `' 8    8 8' 8  8 .oooo8 "
+                  "8    . 8    8 8     8    8 8  8  8 8    8 "
+                  "`YooP' 8    8 8     `YooP' 8  8  8 `YooP8 ";
+
+#define LOGO_HEIGHT 7
+
+    /* Calculate various widths */
+    menu_width = display_size_x - 2;
+    if(menu_width > MAX_WIDTH)
+        menu_width = MAX_WIDTH;
+
+    menu_offset = (display_size_x - menu_width) / 2;
+    if(menu_offset < 0)
+        menu_offset = 0;
+
+    menu_width -= 5;
+    menu_offset += 4;
+
+    /* Calculate various heights */
+    menu_height_notes = 0;
+    menu_height_entries = 0;
+
+    pentry = pmenu->entry_first;
+    while(pentry != NULL)
+    {   
+        if(pentry->flags & MENU_NOTE)
+            menu_height_notes += menu_entryheight(pentry);
+        else
+            menu_height_entries += menu_entryheight(pentry);
+        pentry = pentry->next;
+    }
+
+    /* Add an extra line to pad the notes out, if there are any */
+    if(menu_height_notes != 0)
+        menu_height_notes += 1;
+
+    if(pmenu->logo)
+    {   
+        menu_y_logo_top = (display_size_y - menu_height_entries - LOGO_HEIGHT) / 2;
+        if(menu_y_logo_top < 0)
+            menu_y_logo_top = 0;
+        menu_y_logo_bottom = menu_y_logo_top + LOGO_HEIGHT;
+        menu_y_min = display_size_y - menu_height_entries - 1;
+        if(menu_y_min < menu_y_logo_bottom)
+            menu_y_min = menu_y_logo_bottom;
+        menu_y_max = display_size_y - 1;
+    }
+    else
+    {   
+        menu_y_min = 4;
+        menu_y_max = display_size_y - menu_height_notes - 1;
+        if(pmenu->title != NULL)
+            menu_y_min += 2;
+    }
+
+    /* If a full redraw, clear the screen and plot the title */
+    if(redraw == MENUREDRAW_ALL)
+    { 
+    clear();
+    curs_set(0); 
+
+        if(pmenu->logo)
+        {
+            x = (display_size_x - 42) / 2;
+            y = menu_y_logo_top;
+            cp = colourpair_red;
+
+            move(y, x);
+
+            for(i = 0; i < strlen(logo); i ++)
+            {
+                addch(logo[i] | A_BOLD | COLOR_PAIR(cp));
+                switch(i % 42)
+                {
+                    case 6:
+                        cp = colourpair_yellow;
+                        break;
+                    case 13:
+                        cp = colourpair_green;
+                        break;
+                    case 18:
+                        cp = colourpair_cyan;
+                        break;
+                    case 25:
+                        cp = colourpair_blue;
+                        break;
+                    case 33:
+                        cp = colourpair_magenta;
+                        break;
+                    case 41:
+                        y ++;
+                        move(y, x);
+                        cp = colourpair_red;
+                        break;
+                }
+            }
+
+            y ++;
+
+        }
+        else
+        {
+            /* Display game title */
+            x = (display_size_x / 2) - strlen(title);
+
+            attron(A_BOLD);
+            for(i = 0; i < strlen(title); i ++)
+            {   
+                if(i == 0) attron(COLOR_PAIR(colourpair_red));
+                if(i == 1) attron(COLOR_PAIR(colourpair_yellow));
+                if(i == 2) attron(COLOR_PAIR(colourpair_green));
+                if(i == 3) attron(COLOR_PAIR(colourpair_cyan));
+                if(i == 4) attron(COLOR_PAIR(colourpair_blue));
+                if(i == 5) attron(COLOR_PAIR(colourpair_magenta));
+
+                sprintf(buffer, "%c", title[i]);
+                mvprintw(2, x, buffer);
+                x +=2;
+            }
+            attroff(COLOR_PAIR(colourpair_red));
+            attroff(A_BOLD);
+
+            /* Display menu title */
+            y = 4;
+            x = (display_size_x / 2) - utf8strlen(pmenu->title);
+
+            attron(A_BOLD);
+            attroff(COLOR_PAIR(colourpair_white));
+            /* If the title is too long, plot normally */
+            if(x < 0)
+            {
+                x = (display_size_x - utf8strlen(pmenu->title)) / 2;
+                mvprintw(y, x, pmenu->title);
+            }
+            else
+            {
+                /* Otherwise, spread it out */
+                for(i = 0; i < strlen(pmenu->title); i ++)
+                {
+            j = 0;
+            buffer[j] = pmenu->title[i]; j ++;
+            while((pmenu->title[i + j] & 0xc0) == 0x80)
+            {
+            buffer[j] = pmenu->title[i + j]; j ++;
+            }
+            buffer[j] = 0;
+                    mvprintw(y, x, buffer);
+            x += 2;  i += j - 1;
+                }
+            }
+            attroff(A_BOLD);
+        }
+    }
+
+    /* Keep scroll bar within reasonable limits */
+    if(pmenu->offset > (menu_height_entries - (menu_y_max - menu_y_min)))
+        pmenu->offset = menu_height_entries - (menu_y_max - menu_y_min);
+    if(pmenu->offset < 0)
+        pmenu->offset = 0;
+
+    /* Display scrollbar, if needed */
+    if(menu_height_entries > (menu_y_max - menu_y_min) || pmenu->offset != 0)
+    {
+        if(redraw >= MENUREDRAW_ENTRIES)
+        {
+            menu_scroll_y_min = menu_y_min + 1;
+            menu_scroll_y_max = menu_y_max - 1;
+            menu_scroll_top = menu_scroll_y_min + ((menu_scroll_y_max - menu_scroll_y_min) * pmenu->offset / menu_height_entries);
+            menu_scroll_bottom = menu_scroll_top + ((menu_scroll_y_max - menu_scroll_y_min) * (1 + menu_y_max - menu_y_min) / menu_height_entries);
+
+            attron(COLOR_PAIR(colourpair_menugrey));
+            x = menu_offset + menu_width + 1;
+            mvaddch(menu_y_min, x, '^');
+            mvaddch(menu_y_max - 1, x, 'v');
+
+            attron(COLOR_PAIR(colourpair_white));
+            for(y = menu_y_min + 1; y < menu_y_max - 1; y ++)
+            {
+                /* >= and <= to guarantee at least one character scrollbar */
+                if(y >= menu_scroll_top && y <= menu_scroll_bottom)
+                {
+                attron(A_REVERSE);
+                mvaddch(y, x, '|');
+                attroff(A_REVERSE);
+                }
+                else
+                mvaddch(y, x, '.');
+            }
+        }
+    }
+
+    /* Display notes */
+    if((menu_height_notes != 0) && redraw >= MENUREDRAW_ENTRIES)
+    {
+        if(pmenu->logo)
+            y = menu_y_logo_bottom;
+        else
+            y = menu_y_max + 1;
+
+        pentry = pmenu->entry_first;
+        while(pentry != NULL)
+        {
+            if(pentry->flags & MENU_NOTE)
+            {
+                menu_displayentry(pmenu, pentry, y, 0);
+                y += menu_entryheight(pentry);
+            }
+            pentry = pentry->next;
+        }
+    }
+
+    /* Select an entry if the selected one is not on screen */
+    if(pmenu->entry_selected == NULL)
+        pmenu->entry_selected = pmenu->entry_first;
+    y = menu_y_min - pmenu->offset;
+    pentry = pmenu->entry_first;
+    pmenu->display_first = NULL;
+    pmenu->display_last = NULL;
+    state = 1;
+    while(pentry != NULL)
+    {
+        /* Don't count notes */
+        if(pentry->flags & MENU_NOTE)
+        {
+            pentry = pentry->next;
+            continue;
+        }
+        /* Is the entry off the top of the screen? */
+        if(y < (menu_y_min - menu_entryheight(pentry)))
+        {
+            if(pentry == pmenu->entry_selected)
+                state = -1;
+            y += menu_entryheight(pentry);
+            pentry = pentry->next;
+            continue;
+        }
+        /* Stop processing once we hit the bottom of the screen */
+        if(y > menu_y_max)
+        {
+            pentry = NULL;
+            continue;
+        }
+        if(pentry == pmenu->entry_selected)
+            state = 0;
+        
+        if(pmenu->display_first == NULL)
+            pmenu->display_first = pentry->next;
+        pmenu->display_last = pentry->previous;
+
+        y += menu_entryheight(pentry);
+        pentry = pentry->next;
+    }
+    if(state == -1)
+        pmenu->entry_selected = pmenu->display_first;
+    if(state == 1)
+        pmenu->entry_selected = pmenu->display_last;
+    if(state != 0 && redraw < MENUREDRAW_ENTRIES)
+        redraw = MENUREDRAW_ENTRIES;
+
+    /* Display entries */
+    pentry = pmenu->entry_first;
+    y = menu_y_min - pmenu->offset;
+    y_editing = 0;
+    while(pentry != NULL)
+    {
+        /* Don't display notes */
+        if(pentry->flags & MENU_NOTE)
+        {
+            pentry = pentry->next;
+            continue;
+        }
+        /* Don't display entries off the top of the screen */
+        if(y < (menu_y_min - menu_entryheight(pentry)))
+        {
+            y += menu_entryheight(pentry);
+            pentry = pentry->next;
+            continue;
+        }
+        /* Stop processing once we hit the bottom of the screen */
+        if(y > menu_y_max)
+        {
+            pentry = NULL;
+            continue;
+        }
+        selected = (pmenu->entry_selected == pentry) ? 1 : 0;
+
+        if(redraw >= MENUREDRAW_ENTRIES ||
+                (redraw >= MENUREDRAW_CHANGED && pentry->redraw))
+            menu_displayentry(pmenu, pentry, y, selected);
+        if(pentry->flags & MENU_EDITING)
+            y_editing = y + 1;
+
+
+        pentry->redraw = 0;
+        y += menu_entryheight(pentry);
+        pentry = pentry->next;
+    }
+
+    /* Display cursor if we're editing a text field */
+    if(y_editing)
+    {
+    curs_set(1); 
+        move(y_editing, menu_offset + menu_width - 2);
+    }
+    else
+    curs_set(0); 
+
+    /* Redraw the screen */
+    refresh();
+}
+
+int menu_process(struct menu* pmenu)
+{
+    int ok;
+    int redraw;
+    int c;
+    struct menuentry* pentry;
+    char *buffer;
+
+    redraw = pmenu->redraw;
+
+    ok = 0;
+    while(!ok)
+    {   
+        if(redraw != MENUREDRAW_NONE)
+        {   
+            menu_display(pmenu, redraw);
+            redraw = MENUREDRAW_NONE;
+        }
+
+        c = getch();
+
+        /* Are we editing a text field? */
+        if(pmenu->entry_selected != NULL && pmenu->entry_selected->flags & MENU_EDITING)
+        {  
+            switch(c)
+            {  
+                case KEY_RESIZE:
+                    getmaxyx(stdscr, display_size_y, display_size_x);
+                    redraw = MENUREDRAW_ALL;
+                    break;
+
+                case '\n':
+                case '\r':
+                case 27:
+                case KEY_UP:
+                case KEY_DOWN:
+                case KEY_PPAGE:
+                case KEY_NPAGE:
+                    pmenu->entry_selected->flags -= MENU_EDITING;
+                    pmenu->entry_selected->redraw = 1;
+                    redraw = MENUREDRAW_CHANGED;
+                    break;
+
+                case KEY_BACKSPACE:
+                case KEY_DC:
+                case 8:
+                    if(strlen(pmenu->entry_selected->text4) == 0)
+                        break;
+
+                    buffer = malloc(strlen(pmenu->entry_selected->text4) + 1);
+                    if(buffer != NULL)
+                    {  
+                        strcpy(buffer, pmenu->entry_selected->text4);
+
+                        /* not particularly efficient, but does the job of deleting one UTF8 character */
+                        while(strlen(buffer) > 0 && ((buffer[strlen(buffer) - 1] & 0xc0) == 0x80))
+                            buffer[strlen(buffer) - 1] = 0;
+                        buffer[strlen(buffer) - 1] = 0;
+                    }
+                    menuentry_extratext(pmenu->entry_selected, NULL, NULL, buffer);
+                    free(buffer);
+
+                    pmenu->entry_selected->redraw = 1;
+                    redraw = MENUREDRAW_CHANGED;
+                    break;
+
+                default:
+                    if(c > 31 && c != 127)
+                    {  
+                        buffer = malloc(strlen(pmenu->entry_selected->text4) + 2);
+                        if(buffer != NULL)
+                        {  
+                            strcpy(buffer, pmenu->entry_selected->text4);
+                            buffer[strlen(buffer) + 1] = 0;
+                            buffer[strlen(buffer)] = c;
+                        }
+                        menuentry_extratext(pmenu->entry_selected, NULL, NULL, buffer);
+                        free(buffer);
+
+                        pmenu->entry_selected->redraw = 1;
+                        redraw = MENUREDRAW_CHANGED;
+                        break;
+                    }
+                    break;
+            }
+            continue;
+        }
+
+        /* Not a text field, but a regular entry */
+        /* First, check if the key corresponds to an entry */
+        if(c >= 0 && c < 127)
+            c = toupper(c);
+        pentry = pmenu->entry_first;
+        while(pentry != NULL)
+        {  
+            if(pentry->key == c)
+            {   
+                if(!(pentry->flags & (MENU_GREY | MENU_INVISIBLE | MENU_SPACE)))
+                {
+                    if(pmenu->entry_selected != NULL)
+                        pmenu->entry_selected->redraw = 1;
+                    pmenu->entry_selected = pentry;
+                    pentry->redraw = 1;
+
+                    if(pmenu->entry_selected->flags & MENU_EDITABLE)
+                    {
+                        pmenu->entry_selected->flags |= MENU_EDITING;
+                        pmenu->entry_selected->redraw = 1;
+                        redraw = MENUREDRAW_CHANGED;
+                    }
+                    else
+                        ok = MENU_SELECT;
+                    break;
+                }
+            }
+            pentry = pentry->next;
+        }
+
+        if(ok == MENU_SELECT)
+            continue;
+
+        if(c < 0)
+            continue;
+
+        /* Otherwise, see if it corresponds to an action */
+        switch(actions[c])
+        {   
+            case ACTION_UP:
+                pentry = pmenu->entry_selected;
+                while(pentry != NULL)
+                {   
+                    if(pentry == pmenu->display_first || pentry->flags & MENU_TEXT)
+                        redraw = MENUREDRAW_ENTRIES;
+
+                    pentry = pentry->previous;
+
+                    if(pentry == NULL)
+                        break;
+                    if(redraw != MENUREDRAW_NONE)
+                        pmenu->offset -= menu_entryheight(pentry);
+                    if(!(pentry->flags & (MENU_GREY | MENU_NOTE | MENU_SPACE)))
+                        break;
+                }
+                if(pentry != NULL)
+                {
+                    pentry->redraw = 1;
+                    pmenu->entry_selected->redraw = 1;
+                    pmenu->entry_selected = pentry;
+                    if(redraw == MENUREDRAW_NONE)
+                        redraw = MENUREDRAW_CHANGED;
+                }
+                break;
+
+            case ACTION_DOWN:
+                pentry = pmenu->entry_selected;
+                while(pentry != NULL)
+                {
+                    if(pentry == pmenu->display_last || pentry->flags & MENU_TEXT)
+                        redraw = MENUREDRAW_ENTRIES;
+
+                    pentry = pentry->next;
+
+                    if(pentry == NULL)
+                        break;
+                    if(redraw != MENUREDRAW_NONE)
+                        pmenu->offset += menu_entryheight(pentry);
+                    if(!(pentry->flags & (MENU_GREY | MENU_NOTE | MENU_SPACE)))
+                        break;
+                }
+                if(pentry != NULL)
+                {
+                    pentry->redraw = 1;
+                    pmenu->entry_selected->redraw = 1;
+                    pmenu->entry_selected = pentry;
+                    if(redraw == MENUREDRAW_NONE)
+                        redraw = MENUREDRAW_CHANGED;
+                }
+                break;
+
+            case ACTION_LEFT:
+                if(pmenu->entry_selected != NULL && pmenu->entry_selected->flags & MENU_SCROLLABLE)
+                    ok = MENU_SCROLLLEFT;
+                break;
+
+            case ACTION_RIGHT:
+                if(pmenu->entry_selected != NULL && pmenu->entry_selected->flags & MENU_SCROLLABLE)
+                    ok = MENU_SCROLLRIGHT;
+                break;
+
+            case ACTION_PAGE_UP:
+                pmenu->offset -= (menu_y_max - menu_y_min);
+                redraw = MENUREDRAW_ENTRIES;
+                break;
+
+            case ACTION_PAGE_DOWN:
+                pmenu->offset += (menu_y_max - menu_y_min);
+                redraw = MENUREDRAW_ENTRIES;
+                break;
+
+            case ACTION_ENTER:
+                if(pmenu->entry_selected != NULL)
+                {  
+                   if(pmenu->entry_selected->flags & MENU_EDITABLE)
+                   {   
+                       pmenu->entry_selected->flags |= MENU_EDITING;
+                       pmenu->entry_selected->redraw = 1;
+                       redraw = MENUREDRAW_CHANGED;
+                   }
+                   else
+                       ok = MENU_SELECT;
+                }
+                break;
+
+            case ACTION_DELETE:
+                if(pmenu->entry_selected != NULL && pmenu->entry_selected->flags & MENU_DELETABLE)
+                    ok = MENU_DELETE;
+                break;
+
+            case ACTION_QUIT:
+                ok = MENU_QUIT;
+                break;
+
+            case ACTION_REDRAW:
+                getmaxyx(stdscr, display_size_y, display_size_x);
+                redraw = MENUREDRAW_ALL;
+                break;
+
+            case ACTION_HIDE:
+                display_hide();
+                redraw = MENUREDRAW_ALL;
+                break;
+
+            default:
+                /* See if there is an "any key" entry */
+                pentry = pmenu->entry_first;
+                while(pentry != NULL)
+                {    
+                    if(pentry->key == MENU_KEY_ANY)
+                    {   
+                        if(!(pentry->flags & (MENU_GREY | MENU_INVISIBLE | MENU_SPACE)))
+                        {   
+                            if(pmenu->entry_selected != NULL)
+                                pmenu->entry_selected->redraw = 1;
+                            pmenu->entry_selected = pentry;
+                            pentry->redraw = 1;
+
+                            ok = MENU_SELECT;
+                            break;
+                        }
+                    }
+                    pentry = pentry->next;
+                }
+                break;
+        }
+    }
+
+    /* Redraw the whole menu when we next process it */
+    pmenu->redraw = MENUREDRAW_ALL;
+
+    if(ok == MENU_SELECT)
+    {   
+        if(pmenu->entry_selected->flags & MENU_SCROLLABLE)
+            ok = MENU_SCROLLRIGHT;
+    }
+    /* unless this is a scrollable entry */
+    if(ok == MENU_SCROLLLEFT || ok == MENU_SCROLLRIGHT)
+    {   
+        pmenu->redraw = MENUREDRAW_CHANGED;
+        pmenu->entry_selected->redraw = 1;
+    }
+
+    return ok;
+}
+
+int menu_addfile(struct menu *pmenu, char *filename)
+{
+    FILE *file;
+    char word[256], buffer[4096];
+    char c;
+    int i;
+    int ok, wok;
+
+    file = fopen(filename, "r");
+    if(file == NULL)
+    return 0;
+
+    ok = 0;
+    strcpy(buffer, "");
+    while(!ok)
+    {
+    wok = 0; i = 0;
+    while(!wok)
+    {
+        c = fgetc(file);
+        if(c == 0 || c == 13 || c == 10 || c == 32 || c == 9 || feof(file) || i == 254)
+        wok = 1;
+        else
+            word[i++] = c;
+    }
+    word[i] = 0;
+
+    /* We assume menu_width is defined by the time we get here - a safe
+       assumption provided this isn't the first menu we see. */
+    if(utf8strlen(buffer) + utf8strlen(word) < menu_width - 2 && !(strcmp(word, "") == 0 && (c == 10 || c == 13)) && !(strncmp(word, "===", 3) == 0))
+    {
+        if(buffer[0] != 0 && word[0] != 0)
+            strcat(buffer, " ");
+        strcat(buffer, word);
+    }
+    else
+    {
+        menuentry_new(pmenu, buffer, 0, MENU_TEXT);
+        if(strcmp(word, "") == 0 && (c == 10 || c ==13))
+        menuentry_new(pmenu, "", 0, MENU_TEXT);
+            if(strncmp(word, "===", 3) == 0)
+            {
+                strcpy(buffer, "");
+                pmenu->entry_last->flags |= MENU_GREY;
+            }
+            else
+            strcpy(buffer, word);
+    }
+
+    if(feof(file))
+        ok = 1;
+    }
+
+    menuentry_new(pmenu, buffer, 0, MENU_TEXT);
+
+    fclose(file);
+
+    return 1;
+}