--- /dev/null
+/*
+ colours.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 <errno.h>
+#include <dirent.h>
+#include <string.h>
+#include <ctype.h>
+#include <libintl.h>
+#include <locale.h>
+
+#include "chroma.h"
+#include "level.h"
+#include "util.h"
+#include "colours.h"
+#include "menu.h"
+#include "xmlparser.h"
+
+struct colours* pdisplaycolours = NULL;
+
+extern char options_colours[];
+extern int options_debug;
+extern char *piece_name[];
+
+void colours_init()
+{
+ char filename[FILENAME_MAX];
+ char directory[FILENAME_MAX];
+
+ if(pdisplaycolours != NULL)
+ colours_delete(pdisplaycolours);
+
+ pdisplaycolours = colours_load(options_colours, 0);
+
+ if(pdisplaycolours == NULL)
+ {
+ /* Revert to default */
+ getfilename("colours", directory, 0, LOCATION_SYSTEM);
+ sprintf(filename, "%s%s%s", directory, "/", COLOURS_DEFAULT);
+ pdisplaycolours = colours_load(filename, 0);
+
+ /* If we can't even load the default, use an emergency scheme */
+ if(pdisplaycolours == NULL)
+ pdisplaycolours = colours_load(NULL, 0);
+ }
+}
+
+struct colours* colours_load(char *filename, int partial)
+{
+ struct colours* pcolours;
+ struct parser* pparser = NULL;
+ int state;
+ int piece;
+ int i;
+ int foreground, background, bold, reverse;
+ char character;
+ char *text;
+ int colour;
+
+ if(filename != NULL)
+ {
+ if(!isfile(filename))
+ return NULL;
+
+ pparser = parser_new(filename);
+ if(pparser == NULL)
+ return NULL;
+ }
+
+ pcolours = (struct colours*)malloc(sizeof(struct colours));
+ pcolours->title = NULL;
+ if(pcolours == NULL)
+ return NULL;
+
+ /* Sensible defaults */
+ if(!partial)
+ {
+ for(i = 0; i < PIECE_MAX; i ++)
+ {
+ pcolours->character[i] = piecetochar(i);
+ pcolours->foreground[i] = -1;
+ pcolours->background[i] = -1;
+ pcolours->bold[i] = 0;
+ pcolours->reverse[i] = 0;
+ }
+ }
+
+ /* Emergency default colour scheme */
+ if(filename == NULL)
+ return pcolours;
+
+ /* Parse XML file */
+ /*
+ <chroma type="colours">
+ <head>
+ <title>title</title>
+ </head>
+ <pieces>
+ <piece name="piece" character="c" foreground="colour" background ="colour" bold="yes" reverse = "yes" />
+ </pieces>
+ </chroma>
+ */
+
+ enum{
+ COLOURSPARSER_BAD, /* End of bad file */
+ COLOURSPARSER_END, /* End of good file */
+ COLOURSPARSER_OUTSIDE, /* Outside <chroma> */
+ COLOURSPARSER_CHROMA, /* Inside <chroma> */
+ COLOURSPARSER_PIECES /* Inside <pieces> */
+ };
+
+ state = COLOURSPARSER_OUTSIDE;
+ piece = PIECE_UNKNOWN;
+ foreground = 7; background = 0; bold = 0; reverse = 0; character = ' ';
+
+ while(state != COLOURSPARSER_BAD && state != COLOURSPARSER_END)
+ {
+ switch(parser_parse(pparser))
+ {
+ case PARSER_END:
+ if(state == COLOURSPARSER_OUTSIDE)
+ state = COLOURSPARSER_BAD;
+ else
+ state = COLOURSPARSER_END;
+ break;
+
+ case PARSER_ELEMENT_START:
+ switch(state)
+ {
+ case COLOURSPARSER_CHROMA:
+ if(parser_match(pparser, 0, "pieces"))
+ state = COLOURSPARSER_PIECES;
+ break;
+
+ case COLOURSPARSER_PIECES:
+ if(parser_match(pparser, 0, "piece"))
+ {
+ piece = PIECE_UNKNOWN;
+ foreground = 7; background = -1;
+ bold = 0; reverse = 0;
+ character = ' ';
+ }
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case PARSER_ELEMENT_END:
+ switch(state)
+ {
+ case COLOURSPARSER_CHROMA: /* If we're only partially loading the file, end parsing at </head> */
+ if(parser_match(pparser, 0, "head"))
+ {
+ if(partial)
+ state = COLOURSPARSER_END;
+ }
+ break;
+
+ case COLOURSPARSER_PIECES:
+ if(parser_match(pparser, 0, "pieces"))
+ state = COLOURSPARSER_CHROMA;
+ if(parser_match(pparser, 0, "piece"))
+ {
+ if(piece != PIECE_UNKNOWN)
+ {
+ pcolours->character[piece] = character;
+ pcolours->foreground[piece] = foreground;
+ pcolours->background[piece] = background;
+ pcolours->bold[piece] = bold;
+ pcolours->reverse[piece] = reverse;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ break;
+
+ case PARSER_CONTENT:
+ switch(state)
+ {
+ case COLOURSPARSER_CHROMA:
+ if(parser_match(pparser, 1, "title"))
+ {
+ pcolours->title = malloc(strlen(parser_text(pparser, 0)) + 1);
+ if(pcolours->title == NULL)
+ fatal(gettext("Out of memory in colours_load()"));
+ strcpy(pcolours->title, parser_text(pparser, 0));
+ }
+
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case PARSER_ATTRIBUTE:
+ switch(state)
+ {
+ case COLOURSPARSER_OUTSIDE: if(parser_match(pparser, 2, "chroma") && parser_match(pparser, 1, "type"))
+ {
+ if(parser_match(pparser, 0, "colours") || parser_match(pparser, 0, "colors"))
+ state = COLOURSPARSER_CHROMA;
+ }
+ break;
+
+ case COLOURSPARSER_CHROMA:
+ if(parser_match(pparser, 2, "chroma") && parser_match(pparser, 1, "hidden"))
+ {
+ if(parser_match(pparser, 0, "yes"))
+ {
+ if(partial && !(options_debug & DEBUG_HIDDEN))
+ state = COLOURSPARSER_BAD;
+ }
+ }
+ if(parser_match(pparser, 2, "title") && parser_match(pparser, 1, "translate"))
+ {
+ if(parser_match(pparser, 0, "yes"))
+ pcolours->flags |= COLOURS_TRANSLATE;
+ }
+ break;
+
+ case COLOURSPARSER_PIECES:
+ if(parser_match(pparser, 2, "piece") && parser_match(pparser, 1, "name"))
+ {
+ for(i = 0; i < PIECE_UNKNOWN; i ++)
+ {
+ if(strcasecmp(parser_text(pparser, 0), piece_name[i]) == 0)
+ piece = i;
+ }
+ }
+ if(parser_match(pparser, 2, "piece") && parser_match(pparser, 1, "character"))
+ {
+ text = parser_text(pparser, 0);
+ if(text[0] != 0)
+ character = text[0];
+ }
+ if(parser_match(pparser, 2, "piece") && parser_match(pparser, 1, "bold"))
+ {
+ bold = 0;
+ if(strcmp(parser_text(pparser, 0), "yes") == 0)
+ bold = 1;
+ }
+ if(parser_match(pparser, 2, "piece") && parser_match(pparser, 1, "reverse"))
+ {
+ reverse = 0;
+ if(strcmp(parser_text(pparser, 0), "yes") == 0)
+ reverse = 1;
+ }
+ if(parser_match(pparser, 2, "piece") && (parser_match(pparser, 1, "background") || parser_match(pparser, 1, "foreground")))
+ {
+ colour = -1;
+ if(strcmp(parser_text(pparser, 0), "black") == 0)
+ colour = 0;
+ if(strcmp(parser_text(pparser, 0), "red") == 0)
+ colour = 1;
+ if(strcmp(parser_text(pparser, 0), "green") == 0)
+ colour = 2;
+ if(strcmp(parser_text(pparser, 0), "yellow") == 0)
+ colour = 3;
+ if(strcmp(parser_text(pparser, 0), "blue") == 0)
+ colour = 4;
+ if(strcmp(parser_text(pparser, 0), "magenta") == 0)
+ colour = 5;
+ if(strcmp(parser_text(pparser, 0), "cyan") == 0)
+ colour = 6;
+ if(strcmp(parser_text(pparser, 0), "white") == 0)
+ colour = 7;
+
+ if(parser_match(pparser, 1, "background"))
+ background = colour;
+ if(parser_match(pparser, 1, "foreground") && colour != -1)
+ foreground = colour;
+ }
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case PARSER_ERROR:
+ default:
+ break;
+ }
+ }
+
+ parser_delete(pparser);
+
+ if(state == COLOURSPARSER_BAD)
+ {
+ colours_delete(pcolours);
+ return NULL;
+ }
+
+ return pcolours;
+}
+
+void colours_delete(struct colours* pcolours)
+{
+ if(pcolours->title != NULL)
+ free(pcolours->title);
+
+ free(pcolours);
+}
+
+struct menu* colours_menu()
+{
+ DIR *pdir;
+ struct dirent *dentry;
+ struct menu* pmenu;
+ struct menuentry* pentry;
+ char directory[FILENAME_MAX];
+ char filename[FILENAME_MAX];
+ struct colours* pcolours;
+ int location;
+
+ pmenu = menu_new(gettext("Colour Schemes"));
+
+ menuentry_new(pmenu, gettext("Quit and return to previous menu"), 'Q', 0);
+ menuentry_new(pmenu, "", 0, MENU_SPACE);
+
+ menuentry_new(pmenu, gettext("Current colour scheme:"), 0, MENU_NOTE);
+
+ if(pdisplaycolours == NULL)
+ menuentry_new(pmenu, gettext("** NONE **"), 0, MENU_NOTE | MENU_RIGHT);
+ else if(pdisplaycolours->title == NULL)
+ menuentry_new(pmenu, gettext("[untitled colours]"), 0, MENU_NOTE | MENU_RIGHT);
+ else if(pdisplaycolours->flags & COLOURS_TRANSLATE)
+ menuentry_new(pmenu, gettext(pdisplaycolours->title), 0, MENU_NOTE | MENU_RIGHT);
+ else
+ menuentry_new(pmenu, pdisplaycolours->title, 0, MENU_NOTE | MENU_RIGHT);
+ /* Global, then user */
+ for(location = 1; location >= 0; location --)
+ {
+ getfilename("colours", directory, 0, location);
+
+ pdir = opendir(directory);
+ if(pdir == NULL)
+ continue;
+
+ while((dentry = readdir(pdir)) != NULL)
+ {
+ if(strcmp(dentry->d_name, ".") == 0)
+ continue;
+ if(strcmp(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)
+ {
+ pcolours = colours_load(filename, 1);
+ if(pcolours != NULL)
+ {
+ if(pcolours->title == NULL)
+ pentry = menuentry_newwithvalue(pmenu, gettext("[untitled colours]"), 0, MENU_SORT, filename);
+ else if(pcolours->flags & COLOURS_TRANSLATE)
+ pentry = menuentry_newwithvalue(pmenu, gettext(pcolours->title), 0, MENU_SORT, filename);
+ else
+ pentry = menuentry_newwithvalue(pmenu, pcolours->title, 0, MENU_SORT, filename);
+ colours_delete(pcolours);
+
+ if(strcmp(options_colours, filename) == 0)
+ pmenu->entry_selected = pentry;
+ }
+ }
+ }
+
+ closedir(pdir);
+
+ menu_unsort(pmenu);
+
+ if(location == 1)
+ menuentry_new(pmenu, "", 0, MENU_SPACE);
+ }
+
+ menu_assignletters(pmenu);
+
+ return pmenu;
+}