339 lines
7.7 KiB
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);
|
||
|
}
|
||
|
|