/*
* Copyright (c) 2010 Mike Qin <mikeandmore@gmail.com>
*
* The contents of this file are subject to the terms of either the GNU Lesser
* General Public License Version 2.1 only ("LGPL") or the Common Development and
* Distribution License ("CDDL")(collectively, the "License"). You may not use this
* file except in compliance with the License. You can obtain a copy of the CDDL at
* http://www.opensource.org/licenses/cddl1.php and a copy of the LGPLv2.1 at
* http://www.opensource.org/licenses/lgpl-license.php. See the License for the
* specific language governing permissions and limitations under the License. When
* distributing the software, include this License Header Notice in each file and
* include the full text of the License in the License file as well as the
* following notice:
*
* NOTICE PURSUANT TO SECTION 9 OF THE COMMON DEVELOPMENT AND DISTRIBUTION LICENSE
* (CDDL)
* For Covered Software in this distribution, this License shall be governed by the
* laws of the State of California (excluding conflict-of-law provisions).
* Any litigation relating to this License shall be subject to the jurisdiction of
* the Federal Courts of the Northern District of California and the state courts
* of the State of California, with venue lying in Santa Clara County, California.
*
* Contributor(s):
*
* If you wish your version of this file to be governed by only the CDDL or only
* the LGPL Version 2.1, indicate your decision by adding "[Contributor]" elects to
* include this software in this distribution under the [CDDL or LGPL Version 2.1]
* license." If you don't indicate a single choice of license, a recipient has the
* option to distribute your version of this file under either the CDDL or the LGPL
* Version 2.1, or to extend the choice of license to its licensees as provided
* above. However, if you add LGPL Version 2.1 code and therefore, elected the LGPL
* Version 2 license, then the option applies only if the new code is made subject
* to such option by the copyright holder.
*/
#include <X11/X.h>
#include <X11/Xproto.h>
#include <X11/Xlib.h>
#include <X11/keysym.h>
#include <X11/Xutil.h>
#include <sys/stat.h>
#include "common.h"
#include "xmisc.h"
#include "settings.h"
typedef void (*serialize_func_t)(char* str, void* data);
static const char* setting_names[] = {
"trigger_key",
"eng_key",
"icbar_pos",
"preedit_opacity",
"preedit_color",
"preedit_font",
"preedit_font_color",
"shuangpin",
"shuangpin_scheme",
"candidates_size",
"page_minus_plus",
"page_comma_period",
"page_paren",
"fuzzy_inner_segmentation",
"fuzzy_segmentation",
"cancel_on_backspace",
"smart_punct",
"skin_name",
NULL
};
static void* setting_data[MAX_KEY];
static size_t setting_size[MAX_KEY];
static serialize_func_t setting_enc[MAX_KEY];
static serialize_func_t setting_dec[MAX_KEY];
static void
__varchar_enc(char* str, void* data)
{
strncpy(str, data, 128);
}
static void
__varchar_dec(char* str, void* data)
{
strncpy(data, str, 128);
}
static void
__double_enc(char* str, void* data)
{
double* ptr = data;
snprintf(str, 256, "%.2lf", *ptr);
}
static void
__double_dec(char* str, void* data)
{
double* ptr = data;
sscanf(str, "%lf", ptr);
}
static void
__int_enc(char* str, void* data)
{
int* ptr = data;
snprintf(str, 256, "%d", *ptr);
}
static void
__int_dec(char* str, void* data)
{
int* ptr = data;
sscanf(str, "%d", ptr);
}
static void
__position_enc(char* str, void* data)
{
position_t* pos = data;
snprintf(str, 256, "%d,%d", pos->x, pos->y);
}
static void
__position_dec(char* str, void* data)
{
position_t* pos = data;
sscanf(str, "%d,%d", &pos->x, &pos->y);
}
static const int kmap_keysym[] =
{
XK_Shift_L, XK_Shift_R, XK_Control_L,
XK_Control_R, XK_Meta_L, XK_Meta_R,
XK_space,
};
static const char* kmap_text[] =
{
"Shift_L", "Shift_R", "Control_L",
"Control_R", "Meta_L", "Meta_R", "space",
};
const int nkmap = 7;
static void
__hotkey_enc(char* str, void* data)
{
hotkey_t* hk = data;
if (hk->modifiers & ControlMask) {
strncat(str, "Control+", 255);
}
if (hk->modifiers & ShiftMask) {
strncat(str, "Shift+", 255);
}
char keyname[256];
memset(keyname, 0, sizeof(char) * 256);
int i;
for (i = 0; i < nkmap + 1; i++) {
if (i == nkmap) {
keyname[0] = hk->keysym - 0x0020;
break;
} else if (hk->keysym == kmap_keysym[i]) {
strncpy(keyname, kmap_text[i], 255);
break;
}
}
strncat(str, keyname, 255);
}
static void
__hotkey_dec(char* str, void* data)
{
hotkey_t* hk = data;
hk->keysym = 0;
hk->modifiers = 0;
char* last_ptr = str;
char text[256];
while (1) {
char* ptr = strchr(last_ptr, '+');
memset(text, 0, sizeof(char) * 256);
if (ptr == NULL)
strcpy(text, last_ptr);
else
strncpy(text, last_ptr, (ptr - last_ptr) * sizeof(char));
if (ptr == NULL) {
int i;
for (i = 0; i < nkmap + 1; i++) {
if (i == nkmap) {
hk->keysym = text[0] + 0x0020;
break;
} else if (strcmp(text, kmap_text[i]) == 0) {
hk->keysym = kmap_keysym[i];
break;
}
}
break;
} else {
if (strcmp(text, "Control") == 0)
hk->modifiers |= ControlMask;
if (strcmp(text, "Shift") == 0)
hk->modifiers |= ShiftMask;
}
last_ptr = ptr + 1;
}
}
static void
__init_default_values()
{
hotkey_t hk;
position_t pos;
double d;
varchar str;
int i;
/* trigger key */
hk.modifiers = ControlMask;
hk.keysym = XK_space;
settings_set(TRIGGER_KEY, &hk);
/* eng key */
hk.modifiers = 0;
hk.keysym = XK_Shift_L;
settings_set(ENG_KEY, &hk);
get_screen_size(&(pos.x), &(pos.y));
pos.x -= 200;
pos.y -= 70;
settings_set(ICBAR_POS, &pos);
/* preedit opacity */
settings_set_double(PREEDIT_OPACITY, 1.0);
settings_set_string(PREEDIT_COLOR, "#FFFFB3");
settings_set_string(PREEDIT_FONT, "Sans 10");
settings_set_string(PREEDIT_FONT_COLOR, "#000000");
settings_set_int(SHUANGPIN, 0);
settings_set_string(SHUANGPIN_SCHEME, "MS2003");
settings_set_int(CANDIDATES_SIZE, 10);
/* page up and down trigger */
settings_set_int(PAGE_MINUS_PLUS, 1);
settings_set_int(PAGE_COMMA_PERIOD, 0);
settings_set_int(PAGE_PAREN, 0);
/* fuzzy segmentation */
settings_set_int(FUZZY_SEGMENTATION, 0);
settings_set_int(FUZZY_INNER_SEGMENTATION, 0);
/* cancel on backspace */
settings_set_int(CANCEL_ON_BACKSPACE, 1);
/* smart punctuation */
settings_set_int(SMART_PUNCT, 0);
/* skin support */
settings_set_string(SKIN_NAME, "classic");
}
#define REGISTER(k, type, efunc, dfunc) \
do { \
setting_data[k] = malloc(sizeof(type)); \
setting_size[k] = sizeof(type); \
setting_enc[k] = efunc; \
setting_dec[k] = dfunc; \
} while(0) \
void
settings_init()
{
memset(setting_data, 0, sizeof(void*) * MAX_KEY);
REGISTER(TRIGGER_KEY, hotkey_t, __hotkey_enc, __hotkey_dec);
REGISTER(ENG_KEY, hotkey_t, __hotkey_enc, __hotkey_dec);
REGISTER(ICBAR_POS, position_t, __position_enc, __position_dec);
REGISTER(PREEDIT_OPACITY, double, __double_enc, __double_dec);
REGISTER(PREEDIT_COLOR, varchar, __varchar_enc, __varchar_dec);
REGISTER(PREEDIT_FONT, varchar, __varchar_enc, __varchar_dec);
REGISTER(PREEDIT_FONT_COLOR, varchar, __varchar_enc, __varchar_dec);
REGISTER(SHUANGPIN, int, __int_enc, __int_dec);
REGISTER(SHUANGPIN_SCHEME, varchar, __varchar_enc, __varchar_dec);
REGISTER(CANDIDATES_SIZE, int, __int_enc, __int_dec);
REGISTER(PAGE_MINUS_PLUS, int, __int_enc, __int_dec);
REGISTER(PAGE_COMMA_PERIOD, int, __int_enc, __int_dec);
REGISTER(PAGE_PAREN, int, __int_enc, __int_dec);
REGISTER(FUZZY_SEGMENTATION, int, __int_enc, __int_dec);
REGISTER(FUZZY_INNER_SEGMENTATION, int, __int_enc, __int_dec);
REGISTER(CANCEL_ON_BACKSPACE, int, __int_enc, __int_dec);
REGISTER(SMART_PUNCT, int, __int_enc, __int_dec);
REGISTER(SKIN_NAME, varchar, __varchar_enc, __varchar_dec);
__init_default_values();
}
void
settings_destroy()
{
int i = 0;
while (setting_names[i] != NULL) {
if (setting_data[i] != NULL)
free(setting_data[i]);
i++;
}
}
#define SETTING_FILE ".sunpinyin/xim_config"
#define DEFAULT_SETTING_FILE SUNPINYIN_XIM_SETTING_DIR"/xim_config_default"
void
settings_load()
{
char path[256];
char line[256];
snprintf(path, 256, "%s/%s", getenv("HOME"), SETTING_FILE);
FILE *fp = fopen(path, "r");
if (fp == NULL) {
char config_dir[256];
snprintf(config_dir, 256, "%s/.sunpinyin", getenv("HOME"));
mkdir(config_dir, S_IRWXU);
settings_save();
if ((fp = fopen(path, "r")) == NULL)
return;
}
while (1) {
memset(line, 0, sizeof(char) * 256);
if (fgets(line, 256, fp) == NULL)
break;
if (line[0] == 0)
break;
/* strip the last \n */
line[strlen(line) - 1] = 0;
/* bypass the comment */
if (line[0] == '#')
continue;
char* ptr = strchr(line, '=');
int i = 0;
while (setting_names[i] != NULL) {
if (strncmp(line, setting_names[i], ptr - line) == 0) {
serialize_func_t func = setting_dec[i];
func(ptr + 1, setting_data[i]);
}
i++;
}
}
fclose(fp);
}
void
settings_save()
{
char path[256];
char line[256];
snprintf(path, 256, "%s/%s", getenv("HOME"), SETTING_FILE);
FILE *fp = fopen(path, "w");
if (fp == NULL) {
LOG("settings can't be saved");
return;
}
int i = 0;
while (setting_names[i] != NULL) {
memset(line, 0, sizeof(char) * 256);
serialize_func_t func = setting_enc[i];
func(line, setting_data[i]);
fprintf(fp, "%s=%s\n", setting_names[i], line);
i++;
}
fclose(fp);
}
int settings_get_int(setting_key_t key)
{
int ret = 0;
settings_get(key, &ret);
return ret;
}
double settings_get_double(setting_key_t key)
{
double ret = 0;
settings_get(key, &ret);
return ret;
}
void
settings_get(setting_key_t key, void* data)
{
if (setting_data[key] == NULL) {
LOG("invalid setting key %d to get", key);
return;
}
memcpy(data, setting_data[key], setting_size[key]);
}
void
settings_set_int(setting_key_t key, int value)
{
settings_set(key, &value);
}
void
settings_set_double(setting_key_t key, double value)
{
settings_set(key, &value);
}
void
settings_set_string(setting_key_t key, const char* str)
{
varchar vchar;
strncpy(vchar, str, sizeof(varchar));
settings_set(key, vchar);
}
void
settings_set(setting_key_t key, void* data)
{
if (setting_data[key] == NULL) {
LOG("invalid setting key %d to set", key);
return;
}
memcpy(setting_data[key], data, setting_size[key]);
}