chiark / gitweb /
Update upstream source from tag 'upstream/1.18'
[chroma-debian.git] / menu.c
diff --git a/menu.c b/menu.c
new file mode 100644 (file)
index 0000000..cb491f5
--- /dev/null
+++ b/menu.c
@@ -0,0 +1,808 @@
+/*  
+    menu.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 <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <time.h>
+#include <string.h>
+#include <libintl.h>
+#include <locale.h>
+
+#include "chroma.h"
+#include "menu.h"
+#include "level.h"
+#include "util.h"
+#include "xmlparser.h"
+
+int menu_levelentries(struct menu* pmenu, char *directory, int flags);
+void level_play(struct level* plevelgame);
+
+extern struct level* plevelcurrent;
+
+extern int options_debug;
+#ifdef XOR_COMPATIBILITY
+extern int options_xor_options;
+#endif
+#ifdef ENIGMA_COMPATIBILITY
+extern int options_enigma_options;
+#endif
+
+struct menu *menu_new(char *title)
+{
+    struct menu* pmenu;
+
+    pmenu = (struct menu*)malloc(sizeof(struct menu));
+
+    if(pmenu == NULL)
+        fatal(gettext("Out of memory in menu_new()"));
+
+    pmenu->title = (char *)malloc(strlen(title) + 1);
+
+    if(pmenu->title == NULL)
+        fatal(gettext("Out of memory in menu_new()"));
+     
+    strcpy(pmenu->title, title);
+
+    pmenu->entry_first = NULL;
+    pmenu->entry_last = NULL;
+    pmenu->entry_selected = NULL;
+    pmenu->display_first = NULL;
+    pmenu->display_last = NULL;
+    pmenu->offset = 0;
+    pmenu->redraw = MENUREDRAW_ALL;
+    pmenu->logo = 0;
+
+    return pmenu;
+}
+
+void menu_delete(struct menu *pmenu)
+{
+    struct menuentry *pentry;
+    struct menuentry *pentrytmp;
+
+    pentry = pmenu->entry_first;
+
+    while(pentry != NULL)
+    {
+        pentrytmp = pentry;
+        pentry = pentry->next;
+
+        if(pentrytmp->text != NULL)
+            free(pentrytmp->text);
+        if(pentrytmp->text2 != NULL)
+            free(pentrytmp->text2);
+        if(pentrytmp->text3 != NULL)
+            free(pentrytmp->text3);
+        if(pentrytmp->text4 != NULL)
+            free(pentrytmp->text4);
+        if(pentrytmp->value != NULL)
+            free(pentrytmp->value);
+
+        free(pentrytmp);
+    }
+
+    if(pmenu->title != NULL)
+        free(pmenu->title);
+
+    free(pmenu);
+}
+
+void menu_unsort(struct menu *pmenu)
+{
+    struct menuentry* pentry;
+
+    if(pmenu == NULL)
+        return;
+
+    pentry = pmenu->entry_first;
+    while(pentry != NULL)
+    {
+        pentry->flags &= ~(MENU_SORT | MENU_TOPSORT);
+        pentry = pentry->next;
+    }
+}
+
+void menu_assignletters(struct menu *pmenu)
+{
+    struct menuentry* pentry;
+    struct menuentry* pentry_before;
+    char c;
+
+    if(pmenu == NULL)
+        return;
+
+    c = '1';
+
+    pentry = pmenu->entry_first;
+    while(pentry != NULL)
+    {
+        if(pentry->key == 0 && !(pentry->flags & (MENU_GREY | MENU_NOTE | MENU_SPACE)))
+        {
+            pentry_before = pmenu->entry_first;
+            while(pentry_before != NULL && pentry_before != pentry)
+            {
+                if(pentry_before->key == c)
+                {
+                    c ++;
+
+                    /* Valid keys are 1-9 A-P R-Z. Q is deliberately omitted. */
+                    if(c == '9' + 1)
+                        c = 'A';
+                    if(c == 'Q')
+                        c ++;
+                    if(c == 'Z' + 1)
+                        return;
+
+                    pentry_before = pmenu->entry_first;
+                }
+                else
+                    pentry_before = pentry_before ->next;
+            }
+            pentry->key = c;
+        }
+        pentry = pentry->next;
+    }
+}
+
+struct menuentry* menuentry_new(struct menu *pmenu, char *text, char key, int flags)
+{
+    return menuentry_newwithvalue(pmenu, text, key, flags, NULL);
+}
+
+struct menuentry* menuentry_newwithvalue(struct menu *pmenu, char *text, char key, int flags, char *value)
+{
+    struct menuentry* pentry;
+    struct menuentry* pentry_before;
+
+    pentry = (struct menuentry*)malloc(sizeof(struct menuentry));
+
+    if(pentry == NULL)
+        fatal(gettext("Out of memory in menuentry_newwithvalue()"));
+    
+    if(flags & MENU_SORT)
+    {
+        pentry_before = pmenu->entry_first;
+        while(pentry_before != NULL)
+        {
+            if((pentry_before->flags & MENU_SORT) && (!(pentry_before->flags & MENU_TOPSORT)) && pentry_before->value != NULL)
+            {
+                if(strcmp(pentry_before->value, value) > 0)
+                    break;
+            }
+            pentry_before = pentry_before->next;
+        }
+    }
+    else if(flags & MENU_TOPSORT)
+    {
+        pentry_before = pmenu->entry_first;
+        while(pentry_before != NULL)
+        {
+            if((pentry_before->flags & MENU_TOPSORT) && pentry_before->value != NULL)
+            {
+                if(strcmp(pentry_before->value, value) > 0)
+                    break;
+            }
+
+            if(pentry_before->flags & MENU_SORT && !(pentry_before->flags & MENU_TOPSORT))
+                break;
+
+            pentry_before = pentry_before->next;
+        }
+    }
+    else
+        pentry_before = NULL;
+
+    /* If pentry_before is NULL, we add to the bottom of the list.
+       Otherwise, we add before pentry_before.
+     */
+    if(pentry_before == NULL)
+    {
+        if(pmenu->entry_first == NULL)
+            pmenu->entry_first = pentry;
+
+        pentry->previous = pmenu->entry_last;
+        pentry->next = NULL;
+
+        if(pmenu->entry_last != NULL)
+            pmenu->entry_last->next = pentry;
+
+        pmenu->entry_last = pentry;
+    }
+    else
+    {
+        if(pentry_before == pmenu->entry_first)
+            pmenu->entry_first = pentry;
+        pentry->previous = pentry_before->previous;
+        pentry->next = pentry_before;
+        if(pentry_before->previous != NULL)
+            pentry_before->previous->next = pentry;
+        pentry_before->previous = pentry;
+    }
+
+    pentry->text = (char *)malloc(strlen(text) + 1);
+
+    if(pentry->text == NULL)
+        fatal(gettext("Out of memory in menuentry_newwithvalue()"));
+
+    strcpy(pentry->text, text);
+
+    pentry->redraw = 0;
+
+    pentry->flags = flags;
+
+    pentry->key = key;
+
+    pentry->value = NULL;
+    if(value != NULL)
+    {
+        pentry->value = (char *)malloc(strlen(value) + 1);
+
+        if(pentry->value == NULL)
+        fatal(gettext("Out of memory in menuentry_newwithvalue()"));
+
+        strcpy(pentry->value, value);
+    }
+
+    pentry->text2 = NULL;
+    pentry->text3 = NULL;
+    pentry->text4 = NULL;
+
+    return pentry;
+}
+
+void menuentry_value(struct menuentry* pentry, char *value)
+{
+    if(pentry->value != NULL)
+        free(pentry->value);
+
+    pentry->value = (char *)malloc(strlen(value) + 1);
+
+    if(pentry->value == NULL)
+        fatal(gettext("Out of memory in menuentry_value()"));
+
+    strcpy(pentry->value, value);
+}
+
+void menuentry_text(struct menuentry* pentry, char *text)
+{
+    if(pentry->text != NULL)
+        free(pentry->text);
+
+    pentry->text = (char *)malloc(strlen(text) + 1);
+
+    if(pentry->text == NULL)
+        fatal(gettext("Out of memory in menuentry_text()"));
+
+    strcpy(pentry->text, text);
+}
+
+void menuentry_extratext(struct menuentry* pentry, char *text2, char *text3, char *text4)
+{
+    if(pentry->text2 != NULL)
+        free(pentry->text2);
+    if(pentry->text3 != NULL)
+        free(pentry->text3);
+    if(pentry->text4 != NULL)
+        free(pentry->text4);
+
+    if(text2 != NULL)
+    {
+        pentry->text2 = (char *)malloc(strlen(text2) + 1);
+        if(pentry->text2 == NULL)
+            fatal(gettext("Out of memory in menuentry_extratext()"));
+
+        strcpy(pentry->text2, text2);
+    }
+    else
+        pentry->text2 = NULL;
+
+    if(text3 != NULL)
+    {
+        pentry->text3 = (char *)malloc(strlen(text3) + 1);
+        if(pentry->text3 == NULL)
+            fatal(gettext("Out of memory in menuentry_extratext()"));
+
+        strcpy(pentry->text3, text3);
+    }
+    else
+        pentry->text3 = NULL;
+
+    if(text4 != NULL)
+    {
+        pentry->text4 = (char *)malloc(strlen(text4) + 1);
+        if(pentry->text4 == NULL)
+            fatal(gettext("Out of memory in menuentry_extratext()"));
+
+        strcpy(pentry->text4, text4);
+    }
+    else
+        pentry->text4 = NULL;
+}
+
+void menuentry_delete(struct menu* pmenu, struct menuentry* pentry)
+{
+    struct menuentry* pentry_replace;
+
+    pentry_replace = pentry->next;
+    if(pentry_replace == NULL)
+        pentry_replace = pentry->previous;
+
+    if(pmenu->entry_first == pentry)
+        pmenu->entry_first = pentry_replace;
+    if(pmenu->display_first == pentry)
+        pmenu->display_first = pentry_replace;
+    if(pmenu->entry_last == pentry)
+        pmenu->entry_last = pentry_replace;
+    if(pmenu->entry_selected == pentry)
+        pmenu->entry_selected = pentry_replace;
+    if(pentry->previous != NULL)
+        pentry->previous->next = pentry->next;
+    if(pentry->next != NULL)
+        pentry->next->previous = pentry->previous;
+
+    if(pentry->text != NULL)
+        free(pentry->text);
+    if(pentry->text2 != NULL)
+        free(pentry->text2);
+    if(pentry->text3 != NULL)
+        free(pentry->text3);
+    if(pentry->text4 != NULL)
+        free(pentry->text4);
+    if(pentry->value != NULL)
+        free(pentry->value);
+
+    free(pentry);
+}
+
+int menu_levelselector(char *directory_levels, char *chosen, char *title, int flags)
+{
+    struct menu* pmenu;
+    struct level* plevel;
+    int flags_child;
+    int result;
+    char directory[FILENAME_MAX];
+
+    pmenu = menu_new(title);
+
+    if(flags & LEVELMENU_RETURN)
+        menuentry_new(pmenu, gettext("Return to previous menu"), 'Q', 0);
+
+    if(flags & LEVELMENU_NEW)
+        menuentry_new(pmenu, gettext("Save as new position"), 'S', 0);
+
+    if(flags & LEVELMENU_CREATE)
+        menuentry_new(pmenu, gettext("Create new level"), 'C', 0);
+
+    if(flags & LEVELMENU_IMPORT)
+    {
+        getfilename("", directory, 0, LOCATION_DOCUMENTS);
+        if(isdirectory(directory))
+            menuentry_newwithvalue(pmenu, gettext("Import a position"), 'I', 0, directory);
+    }
+
+    if(flags & LEVELMENU_SAVED)
+    {
+        getfilename("saved", directory, 0, LOCATION_LOCAL);
+        if(isdirectory(directory))
+            menuentry_newwithvalue(pmenu, gettext("Saved Positions"), 'S', MENU_BOLD, directory);
+    }
+
+    if(flags & LEVELMENU_SOLVED)
+    {
+        getfilename("solved", directory, 0, LOCATION_LOCAL);
+        if(isdirectory(directory))
+            menuentry_newwithvalue(pmenu, gettext("Solved Positions"), 'O', MENU_BOLD, directory);
+    }
+
+    if(flags & LEVELMENU_MAIN)
+    {
+        getfilename("construct", directory, 0, LOCATION_LOCAL);
+        if(isdirectory(directory))
+            menuentry_newwithvalue(pmenu, gettext("Levels under Construction"), 'C', MENU_BOLD, directory);
+    }
+
+    /* Separator only if needed */
+    if(pmenu->entry_first != NULL)
+        menuentry_new(pmenu, "", 0, MENU_TOPSORT | MENU_SPACE);
+
+    if(flags & LEVELMENU_DELETE)
+        menuentry_new(pmenu, gettext("[DELETE] to delete a position"), 0, MENU_NOTE | MENU_CENTRE);
+
+    if(flags & LEVELMENU_MAIN)
+    {
+        getfilename("levels", directory, 0, LOCATION_SYSTEM);
+        if(menu_levelentries(pmenu, directory, flags))
+            menuentry_newwithvalue(pmenu, "", 0, MENU_SORT | MENU_SPACE, "");
+
+        menu_unsort(pmenu);
+
+        /* Separator only if needed */
+        if(pmenu->entry_last != NULL && !(pmenu->entry_last->flags & MENU_SPACE))
+            menuentry_newwithvalue(pmenu, "", 0, MENU_SPACE, "");
+
+            getfilename("levels", directory, 0, LOCATION_LOCAL);
+        if(menu_levelentries(pmenu, directory, flags))
+            menuentry_newwithvalue(pmenu, "", 0, MENU_SORT | MENU_SPACE, "");
+    }
+    else
+    {
+        if(menu_levelentries(pmenu, directory_levels, flags))
+            menuentry_newwithvalue(pmenu, "", 0, MENU_SORT | MENU_SPACE, "");
+    }
+
+    menu_assignletters(pmenu);
+
+    while(1)
+    {
+        result = menu_process(pmenu);
+
+        if(result == MENU_QUIT)
+        {
+            if(chosen != NULL)
+                strcpy(chosen, "");
+        break;
+        }
+
+        if(result == MENU_SELECT)
+        {
+            if(pmenu->entry_selected->key == '+' && flags & LEVELMENU_FILTER)
+            {
+                result = menu_levelselector(directory_levels, chosen, title, flags & ~LEVELMENU_FILTER);
+                if(result == MENU_SELECT)
+                    break;
+            }
+            else if(pmenu->entry_selected->value != NULL)
+            {
+                if(isfile(pmenu->entry_selected->value))
+                {
+                    if(flags & LEVELMENU_GAME)
+                    {
+                        plevel = level_load(pmenu->entry_selected->value, 0);
+                        if(plevel != NULL)
+                            level_play(plevel);
+                        /* plevel is deleted by level_play */
+                    }
+                    else
+                    {
+                        if(chosen != NULL)
+                            strcpy(chosen, pmenu->entry_selected->value);
+                        break;
+                    }
+                }
+                else if(isdirectory(pmenu->entry_selected->value))
+                {
+                    flags_child = flags & (LEVELMENU_NEW | LEVELMENU_DELETE | LEVELMENU_FILENAMES | LEVELMENU_MOVES | LEVELMENU_GAME | LEVELMENU_FILTER);
+                    flags_child |= LEVELMENU_RETURN;
+
+                    /* Fix flags for "saved", "solved" and "construct" */
+                    if(pmenu->entry_selected->key == 'S')
+                        flags_child |= LEVELMENU_MOVES | LEVELMENU_DELETE;
+                    if(pmenu->entry_selected->key == 'C')
+                        flags_child |= LEVELMENU_MOVES | LEVELMENU_DELETE;
+                    if(pmenu->entry_selected->key == 'O')
+                        flags_child |= LEVELMENU_MOVES | LEVELMENU_DELETE;
+
+                    result = menu_levelselector(pmenu->entry_selected->value, chosen, pmenu->entry_selected->text, flags_child);
+
+                    if(result == MENU_SELECT)
+                        break;
+
+                    result = MENU_NULL;
+                }
+            }
+            else
+            {
+                if(chosen != NULL)
+                    strcpy(chosen, "");
+
+                if(pmenu->entry_selected->key == 'Q')
+                    result = MENU_QUIT;
+
+                break;
+            }
+        }
+
+        if(result == MENU_DELETE)
+        {
+            if(pmenu->entry_selected->value != NULL)
+                unlink(pmenu->entry_selected->value);
+            menuentry_delete(pmenu, pmenu->entry_selected);
+        }
+    }
+
+    menu_delete(pmenu);
+
+    return result;
+}
+
+int menu_levelentries(struct menu* pmenu, char *directory, int flags)
+{
+    DIR *pdir;
+    struct dirent *dentry;
+    struct menuentry* pentry;
+    struct level* plevel;
+    struct stat filestat;
+    int flags_child;
+    int submenus;
+    int subfiles;
+    int state;
+    int hidden;
+    int filtered;
+
+    char text_date[256];
+    char text_moves[256];
+    char text_stars[256];
+    char *text_title;
+    char *text_subtitle;
+
+    char filename[FILENAME_MAX];
+    char titlefilename[FILENAME_MAX];
+
+    struct parser* pparser;
+
+    submenus = 0;
+    subfiles = 0;
+    filtered = 0;
+
+    pdir = opendir(directory);
+
+    if(pdir == NULL)
+    return 0;
+
+    while((dentry = readdir(pdir)) != NULL)
+    {
+        if(strcmp(dentry->d_name, ".") == 0)
+            continue;
+        if(strcmp(dentry->d_name, "..") == 0)
+            continue; 
+
+        if(dentry->d_name[0] == '.')
+            continue;
+
+        sprintf(filename, "%s%s%s", directory, "/", dentry->d_name);
+
+        if(isfile(filename) && strlen(filename) > 7 && strcmp(filename + strlen(filename) - 7, ".chroma") == 0)
+        {
+            plevel = level_load(filename, 1);
+
+            if(plevel != NULL)
+            { 
+            flags_child = MENU_SORT;
+            if(flags & (LEVELMENU_MOVES | LEVELMENU_FILENAMES))
+                    flags_child |= MENU_DOUBLE;
+            if(flags & LEVELMENU_DELETE)
+                flags_child |= MENU_DELETABLE;
+
+            if(plevel->title == NULL || strcmp(plevel->title, "") == 0)
+                pentry = menuentry_newwithvalue(pmenu, gettext("[untitled level]"), 0, flags_child, filename);
+            else
+            {
+                if(strncmp(plevel->title, "chroma", 6) == 0)
+                pentry = menuentry_newwithvalue(pmenu, gettext(plevel->title), 0, flags_child, filename);
+                else
+                pentry = menuentry_newwithvalue(pmenu, plevel->title, 0, flags_child, filename);
+            }
+
+            if(plevel->stars_exploded)
+                sprintf(text_stars, gettext("%d lost"), plevel->stars_exploded);
+            else
+                sprintf(text_stars, "%d / %d", plevel->stars_caught, plevel->stars_total);
+            if(plevel->flags & LEVELFLAG_SOLVED && !(plevel->flags & LEVELFLAG_FAILED))
+                sprintf(text_stars, gettext("solved"));
+
+            if(plevel->flags & LEVELFLAG_FAILED)
+                sprintf(text_moves, gettext("failed"));
+            else
+                sprintf(text_moves, gettext("%d moves"), plevel->moves);
+
+            if(!stat(filename, &filestat))
+            {
+                sprintf(text_date, "%s", ctime(&filestat.st_mtime));
+                if(strlen(text_date) > 0)
+                    text_date[strlen(text_date) - 1] = 0;
+            }
+            else
+                strcpy(text_date, "");
+
+            if(flags & LEVELMENU_FILENAMES)
+                menuentry_extratext(pentry, NULL, text_date, dentry->d_name);
+            else if(flags & LEVELMENU_MOVES)
+                menuentry_extratext(pentry, text_stars, text_date, text_moves);
+            else
+            {
+                /* Not really needed except for development */
+                if(plevel->flags & LEVELFLAG_SOLVED && !(plevel->flags & LEVELFLAG_FAILED))
+                    menuentry_extratext(pentry, gettext("solved"), NULL, NULL);
+            }
+
+            if((flags & LEVELMENU_FILTER) && plevelcurrent != NULL)
+            {
+                if((plevelcurrent->title != NULL && strcmp(plevelcurrent->title, pentry->text) != 0)
+                        || (plevelcurrent->title == NULL && plevel->title != NULL))
+                {
+                    filtered ++;
+                    menuentry_delete(pmenu, pentry);
+                    continue;
+                }
+            }
+
+#ifdef XOR_COMPATIBILITY
+            if(plevel->mode == MODE_XOR)
+                options_xor_options = 1;
+#endif
+#ifdef ENIGMA_COMPATIBILITY
+            if(plevel->mode == MODE_ENIGMA)
+                options_enigma_options = 1;
+#endif
+                subfiles ++;
+            }
+
+            level_delete(plevel);
+        }
+        else if(isdirectory(filename))
+        {
+            flags_child = MENU_TOPSORT | MENU_BOLD;
+            if(flags & (LEVELMENU_MOVES | LEVELMENU_FILENAMES))
+                    flags_child |= MENU_DOUBLE;
+
+            /* Read the title information from set.chroma, if it exists */
+            snprintf(titlefilename, FILENAME_MAX, "%s/%s", filename, "set.chroma");
+            text_title = NULL;
+            text_subtitle = NULL;
+
+            hidden = 0;
+
+            pparser = parser_new(titlefilename);
+
+            enum {
+                LEVELSETPARSER_END,
+                LEVELSETPARSER_OUTSIDE,
+                LEVELSETPARSER_CHROMA
+            };
+
+            state = LEVELSETPARSER_OUTSIDE;
+
+            while(state != LEVELSETPARSER_END)
+            {
+                switch(parser_parse(pparser))
+                {
+                    case PARSER_END:
+                        state = LEVELSETPARSER_END;
+                        break;
+
+                    case PARSER_ELEMENT_START:
+                        break;
+
+                    case PARSER_ELEMENT_END:
+                        switch(state)
+                        {
+                            case LEVELSETPARSER_CHROMA:
+                                /* We only read the file's header here */
+                                if(parser_match(pparser, 0, "head"))
+                                    state = LEVELSETPARSER_END;
+                                break;
+                        }
+                        break;
+
+                    case PARSER_CONTENT:
+                        switch(state)
+                        {   
+                            case LEVELSETPARSER_CHROMA:
+                                if(parser_match(pparser, 1, "title"))
+                                {   
+                                    text_title = malloc(strlen(parser_text(pparser, 0)) + 1);
+                                    if(text_title == NULL)
+                                        fatal(gettext("Out of memory in menu_levelentries()")); 
+                                    strcpy(text_title, parser_text(pparser, 0));
+                                }
+                                if(parser_match(pparser, 1, "subtitle"))
+                                {   
+                                    text_subtitle = malloc(strlen(parser_text(pparser, 0)) + 1);
+                                    if(text_title == NULL)
+                                        fatal(gettext("Out of memory in menu_levelentries()")); 
+                                    strcpy(text_subtitle, parser_text(pparser, 0));
+                                }
+                                break;
+                        }
+                        break;
+
+                    case PARSER_ATTRIBUTE:
+                        switch(state)
+                        {   
+                            case LEVELSETPARSER_OUTSIDE:
+                                if(parser_match(pparser, 2, "chroma") && parser_match(pparser, 1, "type"))
+                                {   
+                                    if(parser_match(pparser, 0, "set"))
+                                        state = LEVELSETPARSER_CHROMA;
+                                }
+                                break;
+
+                            case LEVELSETPARSER_CHROMA:
+                                if(parser_match(pparser, 2, "chroma") && parser_match(pparser, 1, "hidden"))
+                                {   
+                                    if(parser_match(pparser, 0, "yes"))
+                                        hidden = 1;
+                                }
+                                break;
+                        }
+                        break;
+
+                    case PARSER_ERROR:
+                        state = LEVELSETPARSER_END;
+                        break;
+                }
+            }
+
+            parser_delete(pparser);
+
+            if(!hidden || options_debug & DEBUG_HIDDEN)
+            {
+                if(text_title != NULL)
+                {
+                    if(strncmp(text_title, "chroma", 6) == 0)
+                        pentry = menuentry_newwithvalue(pmenu, gettext(text_title), 0, flags_child | (text_subtitle != NULL ? MENU_DOUBLE : 0), filename);
+                    else
+                        pentry = menuentry_newwithvalue(pmenu, text_title, 0, flags_child | (text_subtitle != NULL ? MENU_DOUBLE : 0), filename);
+
+                }
+                else
+                pentry = menuentry_newwithvalue(pmenu, dentry->d_name, 0, flags_child, filename);
+
+                if(flags & LEVELMENU_FILENAMES)
+                    menuentry_extratext(pentry, NULL, NULL, dentry->d_name);
+                else
+                {
+                    if(text_title != NULL && strncmp(text_title, "chroma", 6) == 0)
+                        menuentry_extratext(pentry, NULL, NULL, gettext(text_subtitle));
+                    else
+                        menuentry_extratext(pentry, NULL, NULL, text_subtitle);
+                }
+                if(text_title != NULL)
+                    free(text_title);
+                if(text_subtitle != NULL)
+                    free(text_subtitle);
+
+                submenus ++;
+            }
+        }
+    }
+
+    closedir(pdir);
+
+    if(filtered != 0)
+    {
+        if(submenus != 0 || subfiles != 0)
+            menuentry_new(pmenu, "", 0, MENU_SPACE);
+        pentry = menuentry_new(pmenu, gettext("Show positions for other levels"), '+', 0);
+        sprintf(text_date, filtered == 1 ? gettext("(%d other)") : gettext("(%d others)"), filtered);
+        menuentry_extratext(pentry, text_date, NULL, NULL);
+    }
+
+    if(submenus !=0 && subfiles != 0)
+        return 1;
+    else
+        return 0;
+
+}
+