ti83-sdk/tool/tilem-src/gui/config.c

339 lines
7.7 KiB
C

/*
* TilEm II
*
* Copyright (c) 2010-2011 Thibault Duponchelle
* Copyright (c) 2011 Benjamin Moody
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <gtk/gtk.h>
#include <ticalcs.h>
#include <tilem.h>
#include "gui.h"
#include "files.h"
#include "msgbox.h"
#ifndef CONFIG_FILE
#define CONFIG_FILE "config.ini"
#endif
#define MAX_RECENT_FILES 10
/* Store a filename in a GKeyFile. Any control characters or
non-UTF-8 filenames are stored in octal. Note that
g_key_file_set/get_string() can't be used because they only allow
UTF-8 */
static void key_file_set_filename(GKeyFile *gkf, const char *group,
const char *key, const char *value)
{
char *escaped;
const char *p;
char *q;
gunichar uc;
int b;
q = escaped = g_new(char, strlen(value) * 4 + 1);
while (*value != 0) {
uc = g_utf8_get_char_validated(value, -1);
if (uc < 0x20 || uc == 0x7F || !g_unichar_validate(uc)) {
b = (unsigned char) *value;
q[0] = '\\';
q[1] = '0' + (b >> 6);
q[2] = '0' + ((b >> 3) & 7);
q[3] = '0' + (b & 7);
q += 4;
value++;
}
else if (uc == '\\') {
q[0] = q[1] = '\\';
q += 2;
value++;
}
else {
p = g_utf8_next_char(value);
while (value != p)
*q++ = *value++;
}
}
*q = 0;
g_key_file_set_value(gkf, group, key, escaped);
g_free(escaped);
}
/* Retrieve a filename from a GKeyFile. */
static char *key_file_get_filename(GKeyFile *gkf, const char *group,
const char *key, GError **error)
{
char *value, *unescaped;
value = g_key_file_get_value(gkf, group, key, error);
if (!value)
return NULL;
unescaped = g_strcompress(value);
g_free(value);
return unescaped;
}
/* Load and parse the configuration file. */
static GKeyFile *load_config(gboolean writable)
{
static gboolean warned;
GKeyFile *gkf;
GKeyFileFlags flags;
char *cfname, *dname;
GError *err = NULL;
gkf = g_key_file_new();
cfname = get_shared_file_path(CONFIG_FILE, NULL);
if (!cfname)
return gkf;
if (writable)
flags = (G_KEY_FILE_KEEP_COMMENTS
| G_KEY_FILE_KEEP_TRANSLATIONS);
else
flags = 0;
if (!g_key_file_load_from_file(gkf, cfname, flags, &err)) {
/* don't bother the user more than once */
if (!warned) {
dname = g_filename_display_name(cfname);
messagebox02(NULL, GTK_MESSAGE_ERROR,
"Unable to read settings",
"An error occurred while reading %s: %s",
dname, err->message);
g_free(dname);
warned = TRUE;
}
g_error_free(err);
}
g_free(cfname);
return gkf;
}
/* Save the configuration file. */
static void save_config(GKeyFile *gkf)
{
static gboolean warned;
char *cfname, *dname;
char *data;
gsize length;
GError *err = NULL;
data = g_key_file_to_data(gkf, &length, NULL);
cfname = get_config_file_path(CONFIG_FILE, NULL);
if (!g_file_set_contents(cfname, data, length, &err)) {
/* don't bother the user more than once */
if (!warned) {
dname = g_filename_display_name(cfname);
messagebox02(NULL, GTK_MESSAGE_ERROR,
"Unable to save settings",
"An error occurred while writing %s: %s",
dname, err->message);
g_free(dname);
warned = TRUE;
}
g_error_free(err);
}
g_free(cfname);
g_free(data);
}
/* Retrieve settings from the configuration file. */
void tilem_config_get(const char *group, const char *option, ...)
{
va_list ap;
GKeyFile *gkf;
const char *type, *defvalue;
GError *err = NULL;
char *key, *p;
char **strp;
int *intp;
double *dblp;
GdkColor *colorp;
g_return_if_fail(group != NULL);
g_return_if_fail(option != NULL);
gkf = load_config(FALSE);
va_start(ap, option);
while (option != NULL) {
type = strrchr(option, '/');
if (type == NULL || type[1] == 0
|| (type[2] != 0 && type[2] != '=')) {
g_critical("invalid argument\n");
break;
}
if (type[2] == '=')
defvalue = &type[3];
else
defvalue = NULL;
key = g_strndup(option, type - option);
if (type[1] == 'f') {
strp = va_arg(ap, char **);
*strp = key_file_get_filename(gkf, group, key, &err);
if (err && defvalue)
*strp = g_strdup(defvalue);
}
else if (type[1] == 's') {
strp = va_arg(ap, char **);
*strp = g_key_file_get_string(gkf, group, key, &err);
if (err && defvalue)
*strp = g_strdup(defvalue);
}
else if (type[1] == 'i') {
intp = va_arg(ap, int *);
*intp = g_key_file_get_integer(gkf, group, key, &err);
if (err && defvalue)
*intp = g_ascii_strtoll(defvalue, NULL, 10);
}
else if (type[1] == 'r') {
dblp = va_arg(ap, double *);
*dblp = g_key_file_get_double(gkf, group, key, &err);
if (err && defvalue)
*dblp = g_ascii_strtod(defvalue, NULL);
}
else if (type[1] == 'b') {
intp = va_arg(ap, int *);
*intp = g_key_file_get_boolean(gkf, group, key, &err);
if (err && defvalue)
*intp = g_ascii_strtoll(defvalue, NULL, 10);
}
else if (type[1] == 'c') {
colorp = va_arg(ap, GdkColor *);
p = g_key_file_get_string(gkf, group, key, &err);
if (p == NULL || !gdk_color_parse(p, colorp)) {
if (defvalue) {
gdk_color_parse(defvalue, colorp);
}
else {
colorp->red = 0;
colorp->green = 0;
colorp->blue = 0;
}
}
g_free(p);
}
else {
g_critical("invalid argument\n");
g_free(key);
break;
}
g_clear_error(&err);
g_free(key);
option = va_arg(ap, const char *);
}
va_end(ap);
g_key_file_free(gkf);
}
/* Save settings to the configuration file. */
void tilem_config_set(const char *group, const char *option, ...)
{
va_list ap;
GKeyFile *gkf;
const char *type;
char *key;
const char *strv;
int intv;
double dblv;
const GdkColor *colorv;
char *p;
g_return_if_fail(group != NULL);
g_return_if_fail(option != NULL);
gkf = load_config(TRUE);
va_start(ap, option);
while (option != NULL) {
type = strrchr(option, '/');
if (type == NULL || type[1] == 0 || type[2] != 0) {
g_critical("invalid argument\n");
break;
}
key = g_strndup(option, type - option);
if (type[1] == 'f') {
strv = va_arg(ap, const char *);
key_file_set_filename(gkf, group, key, strv);
}
else if (type[1] == 's') {
strv = va_arg(ap, const char *);
g_key_file_set_string(gkf, group, key, strv);
}
else if (type[1] == 'i') {
intv = va_arg(ap, int);
g_key_file_set_integer(gkf, group, key, intv);
}
else if (type[1] == 'r') {
dblv = va_arg(ap, double);
g_key_file_set_double(gkf, group, key, dblv);
}
else if (type[1] == 'b') {
intv = va_arg(ap, int);
g_key_file_set_boolean(gkf, group, key, !!intv);
}
else if (type[1] == 'c') {
colorv = va_arg(ap, const GdkColor *);
p = g_strdup_printf("#%02x%02x%02x",
colorv->red >> 8,
colorv->green >> 8,
colorv->blue >> 8);
g_key_file_set_string(gkf, group, key, p);
g_free(p);
}
else {
g_critical("invalid argument\n");
g_free(key);
break;
}
g_free(key);
option = va_arg(ap, const char *);
}
va_end(ap);
save_config(gkf);
g_key_file_free(gkf);
}