Add a configuration file

Now you can have pretty colors w/o having to hardcode them.
The selection is rather limited for now, though.

Search "centering" can now be disabled by default as well.
This commit is contained in:
Přemysl Eric Janouch 2016-09-26 16:19:36 +02:00
parent 4755033781
commit 81d702ed66
Signed by: p
GPG Key ID: B715679E3A361BE6
2 changed files with 201 additions and 15 deletions

View File

@ -66,7 +66,24 @@ As the original StarDict is a bit of a clusterfuck with regard to collation of
dictionary entries, I had to introduce an additional `collation` field into the dictionary entries, I had to introduce an additional `collation` field into the
'.ifo' file. When sdtui discovers this field while reading the dictionary, it '.ifo' file. When sdtui discovers this field while reading the dictionary, it
automatically reorders the index according to that locale (e.g. "cs_CZ"). automatically reorders the index according to that locale (e.g. "cs_CZ").
This operation may take a little while. This operation may take a little while, in the order of seconds.
Configuration
-------------
To get a nicer look in 256color terminals, create ~/.config/sdtui/sdtui.conf
with the following. Note that it is intended for black-on-white terminals.
....
[Settings]
center-search = true
underline-last = false
[Colors]
header = reverse
search = ul
even = 16 231
odd = 16 255
....
Dictionaries Dictionaries
------------ ------------

View File

@ -63,6 +63,29 @@ unichar_width (gunichar ch)
// --- Application ------------------------------------------------------------- // --- Application -------------------------------------------------------------
#define ATTRIBUTE_TABLE(XX) \
XX( HEADER, "header", -1, -1, A_REVERSE ) \
XX( SEARCH, "search", -1, -1, A_UNDERLINE ) \
XX( EVEN, "even", -1, -1, 0 ) \
XX( ODD, "odd", -1, -1, 0 )
enum
{
#define XX(name, config, fg_, bg_, attrs_) ATTRIBUTE_ ## name,
ATTRIBUTE_TABLE (XX)
#undef XX
ATTRIBUTE_COUNT
};
struct attrs
{
short fg; ///< Foreground color index
short bg; ///< Background color index
chtype attrs; ///< Other attributes
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/// Data relating to one entry within the dictionary. /// Data relating to one entry within the dictionary.
typedef struct view_entry ViewEntry; typedef struct view_entry ViewEntry;
/// Encloses application data. /// Encloses application data.
@ -88,6 +111,7 @@ struct application
StardictDict * dict; ///< The current dictionary StardictDict * dict; ///< The current dictionary
guint show_help : 1; ///< Whether help can be shown guint show_help : 1; ///< Whether help can be shown
guint center_search : 1; ///< Whether to center the search guint center_search : 1; ///< Whether to center the search
guint underline_last : 1; ///< Underline the last definition
guint32 top_position; ///< Index of the topmost dict. entry guint32 top_position; ///< Index of the topmost dict. entry
guint top_offset; ///< Offset into the top entry guint top_offset; ///< Offset into the top entry
@ -104,8 +128,13 @@ struct application
guint selection_timer; ///< Selection watcher timeout timer guint selection_timer; ///< Selection watcher timeout timer
gint selection_interval; ///< Selection watcher timer interval gint selection_interval; ///< Selection watcher timer interval
gchar * selection_contents; ///< Selection contents gchar * selection_contents; ///< Selection contents
struct attrs attrs[ATTRIBUTE_COUNT];
}; };
/// Shortcut to retrieve named terminal attributes
#define APP_ATTR(name) self->attrs[ATTRIBUTE_ ## name].attrs
struct app_options struct app_options
{ {
gboolean show_version; ///< Output version information and quit gboolean show_version; ///< Output version information and quit
@ -221,6 +250,112 @@ rearm_selection_watcher (Application *self)
} }
#endif // WITH_GTK #endif // WITH_GTK
/// Load configuration for a color using a subset of git config colors.
static void
app_load_color (Application *self, GKeyFile *kf, const gchar *name, int id)
{
gchar *value = g_key_file_get_string (kf, "Colors", name, NULL);
if (!value)
return;
struct attrs attrs = { -1, -1, 0 };
gchar **values = g_strsplit (value, " ", 0);
gint colors = 0;
for (gchar **it = values; *it; it++)
{
gchar *end = NULL;
gint64 n = g_ascii_strtoll (*it, &end, 10);
if (*it != end && !*end && n >= G_MINSHORT && n <= G_MAXSHORT)
{
if (colors == 0) attrs.fg = n;
if (colors == 1) attrs.bg = n;
colors++;
}
else if (!strcmp (*it, "bold")) attrs.attrs |= A_BOLD;
else if (!strcmp (*it, "dim")) attrs.attrs |= A_DIM;
else if (!strcmp (*it, "ul")) attrs.attrs |= A_UNDERLINE;
else if (!strcmp (*it, "blink")) attrs.attrs |= A_BLINK;
else if (!strcmp (*it, "reverse")) attrs.attrs |= A_REVERSE;
else if (!strcmp (*it, "italic")) attrs.attrs |= A_ITALIC;
}
g_strfreev (values);
g_free (value);
self->attrs[id] = attrs;
}
static void
app_load_config_values (Application *self, GKeyFile *kf)
{
GError *e;
const gchar *settings = "Settings";
e = NULL;
bool center_search =
g_key_file_get_boolean (kf, settings, "center-search", &e);
if (e)
g_error_free (e);
else
self->center_search = center_search;
e = NULL;
bool underline_last =
g_key_file_get_boolean (kf, settings, "underline-last", &e);
if (e)
g_error_free (e);
else
self->underline_last = underline_last;
#define XX(name, config, fg_, bg_, attrs_) \
app_load_color (self, kf, config, ATTRIBUTE_ ## name);
ATTRIBUTE_TABLE (XX)
#undef XX
}
static void
app_load_config (Application *self)
{
GKeyFile *kf = g_key_file_new ();
GPtrArray *paths = g_ptr_array_new ();
g_ptr_array_add (paths, (gpointer) g_get_user_config_dir ());
for (const gchar *const *system = g_get_system_config_dirs ();
*system; system++)
g_ptr_array_add (paths, (gpointer) *system);
g_ptr_array_add (paths, NULL);
// XXX: if there are dashes in the final path component,
// the function tries to replace them with directory separators,
// which is completely undocumented
GError *e = NULL;
g_key_file_load_from_dirs (kf,
PROJECT_NAME G_DIR_SEPARATOR_S PROJECT_NAME ".conf",
(const gchar **) paths->pdata, NULL, 0, &e);
g_ptr_array_free (paths, TRUE);
// TODO: proper error handling showing all relevant information;
// we can afford that here since the terminal hasn't been initialized yet
if (e)
{
if (e->code != G_KEY_FILE_ERROR_NOT_FOUND)
g_error ("%s: %s\n", _("Cannot load configuration"), e->message);
g_error_free (e);
}
else
app_load_config_values (self, kf);
g_key_file_free (kf);
}
static void
app_init_attrs (Application *self)
{
#define XX(name, config, fg_, bg_, attrs_) \
self->attrs[ATTRIBUTE_ ## name].fg = fg_; \
self->attrs[ATTRIBUTE_ ## name].bg = bg_; \
self->attrs[ATTRIBUTE_ ## name].attrs = attrs_;
ATTRIBUTE_TABLE (XX)
#undef XX
}
/// Initialize the application core. /// Initialize the application core.
static void static void
app_init (Application *self, AppOptions *options, const gchar *filename) app_init (Application *self, AppOptions *options, const gchar *filename)
@ -255,6 +390,7 @@ app_init (Application *self, AppOptions *options, const gchar *filename)
self->show_help = TRUE; self->show_help = TRUE;
self->center_search = TRUE; self->center_search = TRUE;
self->underline_last = TRUE;
self->top_position = 0; self->top_position = 0;
self->top_offset = 0; self->top_offset = 0;
@ -279,6 +415,42 @@ app_init (Application *self, AppOptions *options, const gchar *filename)
#endif // G_BYTE_ORDER != G_LITTLE_ENDIAN #endif // G_BYTE_ORDER != G_LITTLE_ENDIAN
app_reload_view (self); app_reload_view (self);
app_init_attrs (self);
app_load_config (self);
}
static void
app_init_terminal (Application *self)
{
TERMO_CHECK_VERSION;
if (!(self->tk = termo_new (STDIN_FILENO, NULL, 0)))
abort ();
if (!initscr () || nonl () == ERR)
abort ();
// By default we don't use any colors so they're not required...
if (start_color () == ERR
|| use_default_colors () == ERR
|| COLOR_PAIRS <= ATTRIBUTE_COUNT)
return;
gboolean failed = false;
for (int a = 0; a < ATTRIBUTE_COUNT; a++)
{
if (self->attrs[a].fg >= COLORS || self->attrs[a].fg < -1
|| self->attrs[a].bg >= COLORS || self->attrs[a].bg < -1)
{
failed = true;
continue;
}
init_pair (a + 1, self->attrs[a].fg, self->attrs[a].bg);
self->attrs[a].attrs |= COLOR_PAIR (a + 1);
}
// ...thus we can reset back to defaults even after initializing some
if (failed)
app_init_attrs (self);
} }
/// Free any resources used by the application. /// Free any resources used by the application.
@ -503,14 +675,14 @@ app_add_utf8_string (Application *self, const gchar *str, chtype attrs, int n)
static void static void
app_redraw_top (Application *self) app_redraw_top (Application *self)
{ {
attrset (A_REVERSE); attrset (APP_ATTR (HEADER));
mvwhline (stdscr, 0, 0, A_REVERSE, COLS); mvwhline (stdscr, 0, 0, APP_ATTR (HEADER), COLS);
gsize indent = app_add_utf8_string (self, PROJECT_NAME " ", A_BOLD, -1); gsize indent = app_add_utf8_string (self, PROJECT_NAME " ", A_BOLD, -1);
app_add_utf8_string (self, stardict_info_get_book_name app_add_utf8_string (self, stardict_info_get_book_name
(stardict_dict_get_info (self->dict)), 0, COLS - indent); (stardict_dict_get_info (self->dict)), 0, COLS - indent);
attrset (A_UNDERLINE); attrset (APP_ATTR (SEARCH));
mvwhline (stdscr, 1, 0, A_UNDERLINE, COLS); mvwhline (stdscr, 1, 0, APP_ATTR (SEARCH), COLS);
indent = app_add_utf8_string (self, self->search_label, 0, -1); indent = app_add_utf8_string (self, self->search_label, 0, -1);
gchar *input_utf8 = g_ucs4_to_utf8 gchar *input_utf8 = g_ucs4_to_utf8
@ -616,9 +788,12 @@ app_redraw_view (Application *self)
ViewEntry *ve = g_ptr_array_index (self->entries, i); ViewEntry *ve = g_ptr_array_index (self->entries, i);
for (; k < ve->definitions_length; k++) for (; k < ve->definitions_length; k++)
{ {
int attrs = 0; int attrs = ((self->top_position + i) & 1)
if (shown == self->selected) attrs |= A_REVERSE; ? APP_ATTR (ODD) : APP_ATTR (EVEN);
if (k + 1 == ve->definitions_length) attrs |= A_UNDERLINE; if (shown == self->selected) attrs |= A_REVERSE;
gboolean last = k + 1 == ve->definitions_length;
if (last && self->underline_last) attrs |= A_UNDERLINE;
attrset (attrs); attrset (attrs);
RowBuffer buf; RowBuffer buf;
@ -1630,13 +1805,7 @@ G_GNUC_END_IGNORE_DEPRECATIONS
Application app; Application app;
app_init (&app, &options, argv[1]); app_init (&app, &options, argv[1]);
app_init_terminal (&app);
TERMO_CHECK_VERSION;
if (!(app.tk = termo_new (STDIN_FILENO, NULL, 0)))
abort ();
if (!initscr () || nonl () == ERR)
abort ();
app_redraw (&app); app_redraw (&app);
// g_unix_signal_add() cannot handle SIGWINCH // g_unix_signal_add() cannot handle SIGWINCH