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:
		
							parent
							
								
									4755033781
								
							
						
					
					
						commit
						81d702ed66
					
				
							
								
								
									
										19
									
								
								README.adoc
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								README.adoc
									
									
									
									
									
								
							| @ -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 | ||||
| '.ifo' file.  When sdtui discovers this field while reading the dictionary, it | ||||
| 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 | ||||
| ------------ | ||||
|  | ||||
							
								
								
									
										197
									
								
								src/sdtui.c
									
									
									
									
									
								
							
							
						
						
									
										197
									
								
								src/sdtui.c
									
									
									
									
									
								
							| @ -63,6 +63,29 @@ unichar_width (gunichar ch) | ||||
| 
 | ||||
| // --- 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.
 | ||||
| typedef struct view_entry               ViewEntry; | ||||
| /// Encloses application data.
 | ||||
| @ -88,6 +111,7 @@ struct application | ||||
| 	StardictDict  * dict;               ///< The current dictionary
 | ||||
| 	guint           show_help : 1;      ///< Whether help can be shown
 | ||||
| 	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
 | ||||
| 	guint           top_offset;         ///< Offset into the top entry
 | ||||
| @ -104,8 +128,13 @@ struct application | ||||
| 	guint           selection_timer;    ///< Selection watcher timeout timer
 | ||||
| 	gint            selection_interval; ///< Selection watcher timer interval
 | ||||
| 	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 | ||||
| { | ||||
| 	gboolean show_version;              ///< Output version information and quit
 | ||||
| @ -221,6 +250,112 @@ rearm_selection_watcher (Application *self) | ||||
| } | ||||
| #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.
 | ||||
| static void | ||||
| 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->center_search = TRUE; | ||||
| 	self->underline_last = TRUE; | ||||
| 
 | ||||
| 	self->top_position = 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
 | ||||
| 
 | ||||
| 	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.
 | ||||
| @ -503,14 +675,14 @@ app_add_utf8_string (Application *self, const gchar *str, chtype attrs, int n) | ||||
| static void | ||||
| app_redraw_top (Application *self) | ||||
| { | ||||
| 	attrset (A_REVERSE); | ||||
| 	mvwhline (stdscr, 0, 0, A_REVERSE, COLS); | ||||
| 	attrset (APP_ATTR (HEADER)); | ||||
| 	mvwhline (stdscr, 0, 0, APP_ATTR (HEADER), COLS); | ||||
| 	gsize indent = app_add_utf8_string (self, PROJECT_NAME "  ", A_BOLD, -1); | ||||
| 	app_add_utf8_string (self, stardict_info_get_book_name | ||||
| 		(stardict_dict_get_info (self->dict)), 0, COLS - indent); | ||||
| 
 | ||||
| 	attrset (A_UNDERLINE); | ||||
| 	mvwhline (stdscr, 1, 0, A_UNDERLINE, COLS); | ||||
| 	attrset (APP_ATTR (SEARCH)); | ||||
| 	mvwhline (stdscr, 1, 0, APP_ATTR (SEARCH), COLS); | ||||
| 	indent = app_add_utf8_string (self, self->search_label, 0, -1); | ||||
| 
 | ||||
| 	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); | ||||
| 		for (; k < ve->definitions_length; k++) | ||||
| 		{ | ||||
| 			int attrs = 0; | ||||
| 			if (shown == self->selected)          attrs |= A_REVERSE; | ||||
| 			if (k + 1 == ve->definitions_length)  attrs |= A_UNDERLINE; | ||||
| 			int attrs = ((self->top_position + i) & 1) | ||||
| 				? APP_ATTR (ODD) : APP_ATTR (EVEN); | ||||
| 			if (shown == self->selected)      attrs |= A_REVERSE; | ||||
| 			gboolean last = k + 1 == ve->definitions_length; | ||||
| 			if (last && self->underline_last) attrs |= A_UNDERLINE; | ||||
| 
 | ||||
| 			attrset (attrs); | ||||
| 
 | ||||
| 			RowBuffer buf; | ||||
| @ -1630,13 +1805,7 @@ G_GNUC_END_IGNORE_DEPRECATIONS | ||||
| 
 | ||||
| 	Application app; | ||||
| 	app_init (&app, &options, argv[1]); | ||||
| 
 | ||||
| 	TERMO_CHECK_VERSION; | ||||
| 	if (!(app.tk = termo_new (STDIN_FILENO, NULL, 0))) | ||||
| 		abort (); | ||||
| 
 | ||||
| 	if (!initscr () || nonl () == ERR) | ||||
| 		abort (); | ||||
| 	app_init_terminal (&app); | ||||
| 	app_redraw (&app); | ||||
| 
 | ||||
| 	// g_unix_signal_add() cannot handle SIGWINCH
 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user