From 05511520d9c41f3439aae018211eab1137e6f28d Mon Sep 17 00:00:00 2001 From: wdlkmpx Date: Sun, 13 Sep 2020 00:16:42 +0800 Subject: [PATCH] *** handle configuration and history with C code *** This is a major change that may lead to a complete C application. Deleted files: - prefs.cc - prefs.h - history.cc - history.h New files: - config_prefs.c - config_prefs.h - history.c - history.h gmrun was mostly broken, I'm not sure when the app became so broken but it's better now. The code is more readable and more stable. Various bugs were fixed, it would take many lines to explain the changes, they were just so dramatic --- README.md | 7 +- src/Makefile.am | 7 +- src/ci_string.h | 41 ----- src/config_prefs.c | 356 +++++++++++++++++++++++++++++++++++++++ src/config_prefs.h | 41 +++++ src/gtkcompletionline.cc | 331 +++++++++++++++--------------------- src/gtkcompletionline.h | 10 +- src/history.c | 308 +++++++++++++++++++++++++++++++++ src/history.cc | 158 ----------------- src/history.h | 114 +++++++------ src/main.cc | 343 +++++++++++++++++-------------------- src/prefs.cc | 178 -------------------- src/prefs.h | 45 ----- 13 files changed, 1060 insertions(+), 879 deletions(-) delete mode 100644 src/ci_string.h create mode 100644 src/config_prefs.c create mode 100644 src/config_prefs.h create mode 100644 src/history.c delete mode 100644 src/history.cc delete mode 100644 src/prefs.cc delete mode 100644 src/prefs.h diff --git a/README.md b/README.md index 7062267..e6e5c32 100644 --- a/README.md +++ b/README.md @@ -24,10 +24,7 @@ Features * CTRL-Enter runs the command in a terminal. CTRL-Enter without any command starts a new terminal. - * You can use CTRL-R / CTRL-S to search through history, much like in bash - or Emacs. Also, pressing "!" as the first character enters some special - search mode, finding those history entries that begin with the entered - text. + * You can use CTRL-R / CTRL-S to search through history. * URL handlers allowing you to enter lines like "http://www.google.com" to start your favorite browser on www.google.com. @@ -39,8 +36,6 @@ Features it in the configuration file. The default handler for ".txt" files is, of course, Emacs. But you can easily change that, you... you VIM user! - * UTF-8 support (added in 0.9.2) - Requirements ------------- diff --git a/src/Makefile.am b/src/Makefile.am index ee8a7ca..2e0d43e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -4,11 +4,10 @@ AM_CPPFLAGS = @GTK_CFLAGS@ bin_PROGRAMS = gmrun -gmrun_SOURCES = \ +gmrun_SOURCES = config_prefs.c \ + history.c \ gtkcompletionline.cc gtkcompletionline.h \ - history.cc history.h \ - main.cc \ - prefs.cc prefs.h ci_string.h + main.cc # gmrun_LDFLAGS = -s diff --git a/src/ci_string.h b/src/ci_string.h deleted file mode 100644 index 48ba538..0000000 --- a/src/ci_string.h +++ /dev/null @@ -1,41 +0,0 @@ -// $Id: ci_string.h,v 1.2 2001/05/16 14:39:31 mishoo Exp $ - -// This is a very nice class, probably because it's not coded by me ;=]~ -// Provide a case-insensitive std::string-like class. - -#ifndef __CI_STRING_H__ -#define __CI_STRING_H__ - -#include -#include - -struct ci_char_traits : public std::char_traits -{ - static bool eq( char c1, char c2 ) { - return ::tolower(c1) == ::tolower(c2); - } - - static bool ne( char c1, char c2 ) { - return ::tolower(c1) != ::tolower(c2); - } - - static bool lt( char c1, char c2 ) { - return ::tolower(c1) < ::tolower(c2); - } - - static int compare( const char* s1, - const char* s2, - size_t n ) { - return strncasecmp( s1, s2, n ); - } - - static const char* - find( const char* s, int n, char a ) { - while ( n-- > 0 && ::tolower(*s) != ::tolower(a) ) ++s; - return s; - } -}; - -typedef std::basic_string ci_string; - -#endif // __CI_STRING_H__ diff --git a/src/config_prefs.c b/src/config_prefs.c new file mode 100644 index 0000000..7c789a2 --- /dev/null +++ b/src/config_prefs.c @@ -0,0 +1,356 @@ +/* + * This is free and unencumbered software released into the public domain. + * + * For more information, please refer to + */ + +/* + * Read config file formatted like this: + * + * key1 = value1 + * key2=value2 + * + * It's possible to open several files with the same config + * The keys will be updated with thew new values. + * 'key' is not case sensitive + */ + +#include "config_prefs.h" + +#define APP_CONFIG_FILE "gmrunrc" + +// ============================================================ +// PRIVATE +// ============================================================ + +static GList * PrefsGList = NULL; +static GList * ExtensionGList = NULL; + +struct _pref_item +{ + char * key; + char * value; +}; +typedef struct _pref_item pref_item; + + +static void pref_item_free (pref_item * item) +{ + if (item) { + if (item->key) g_free (item->key); + if (item->value) g_free (item->value); + g_free (item); + } +} + +static void pref_item_free_cb (gpointer data) +{ + pref_item_free ((pref_item *) data); +} + + +static GList * config_find_key (GList * list, const char * key) +{ + GList *i; + pref_item * item; + + if (!key || !*key) { /* ignore empty keys (strings) */ + return (NULL); + } + + for (i = list; i; i = i->next) + { + item = (pref_item *) (i->data); + if (strcasecmp (key, item->key) == 0) { + return (i); + } + } + return (NULL); /* key not found */ +} + + +static void config_replace_key (GList ** list, pref_item * item) +{ + GList * found = config_find_key (*list, item->key); + if (found) { + /* only update found item */ + pref_item * found_item = (pref_item *) (found->data); + if (strcmp (found_item->value, item->value) == 0) { + pref_item_free (item); + return; /* values are equal, nothing to update */ + } + g_free (found_item->value); + found_item->value = g_strdup (item->value); + pref_item_free (item); + } else { + /* append item */ + *list = g_list_append (*list, (gpointer) item); + } +} + + +/** get value, it's always a string **/ +static char * config_get_item_value (GList * list, const char * key) +{ + GList * ret; + pref_item * item; + + ret = config_find_key (list, key); + if (ret) { + item = (pref_item *) (ret->data); + return (item->value); + } + return (NULL); /* key not found */ +} + + +static void config_load_from_file (const char * filename, GList ** out_list) +{ + FILE *fp; + char buf[1024]; + + char * stripped; + char ** keyvalue; + pref_item * item; + + fp = fopen (filename, "r"); + if (!fp) { + return; + } + + /* Read file line by line */ + while (fgets (buf, sizeof (buf), fp)) + { + stripped = buf; + while (*stripped && *stripped <= 0x20) { // 32 = space + stripped++; + } + if (strlen (stripped) < 3 || *stripped == '#') { + continue; + } + if (!strchr (stripped, '=')) { + continue; + } + + item = (pref_item *) g_malloc0 (sizeof (pref_item)); + keyvalue = g_strsplit (stripped, "=", 2); + item->key = g_strstrip (keyvalue[0]); + item->value = g_strstrip (keyvalue[1]); + + if (!*item->key || !*item->value) { + g_strfreev (keyvalue); + g_free (item); + continue; + } + + /// fprintf (stderr, "### %s = %s\n", key, value); + /* Insert or replace item */ + config_replace_key (out_list, item); + } + + fclose (fp); + return; +} + + +static void create_extension_handler_list (void) +{ + GList * i; + pref_item * item, * item_out; + char ** str_vector; + + for (i = PrefsGList; i; i = i->next) + { + item = (pref_item *) (i->data); + if (strncasecmp (item->key, "EXT:", 4) == 0) + { + int w; + str_vector = g_strsplit (item->key + 4, ",", 0); + for (w = 0; str_vector[w]; w++) + { + item_out = (pref_item *) g_malloc0 (sizeof (pref_item)); + item_out->key = g_strdup (str_vector[w]); + item_out->value = g_strdup (item->value); + config_replace_key (&ExtensionGList, item_out); + } + g_strfreev (str_vector); + } + } +} + + +static char * replace_variable (char * txt) /* config_get_string_expanded() */ +{ + // pre${variable}post : ${Terminal} -e ... + char * pre = NULL, * post = NULL; + char * variable = NULL; + char * variable_value = NULL; + char * new_text = NULL; + char * p, * p2; + + if (strlen (txt) < 5) { // at least ${xx} + return (NULL); + } + p = strstr (txt, "${"); + if (!p) { + return (NULL); // syntax error + } + if (!strchr (p + 3, '}')) { + return (NULL); // syntax error + } + + if (txt[0] != '$' && txt[1] != '$') { + pre = g_strdup (txt); + p2 = strchr (pre, '$'); + if (p2) p2 = 0; + } + + variable = g_strdup (p + 2); // variable start + p2 = strchr (variable, '}'); // variable end + *p2 = 0; // `Terminal` + post = p2 + 1; // ` -e ...` + + variable_value = config_get_item_value (PrefsGList, variable); // xterm + + if (variable_value) { + if (pre) { + // pre xterm -e ... + new_text = g_strconcat (pre, variable_value, post, NULL); + } else { + // xterm -e ... + new_text = g_strconcat (variable_value, post, NULL); + } + } + + if (pre) g_free (pre); + if (variable) g_free (variable); + + return (new_text); +} + + +// ============================================================ +// PUBLIC +// ============================================================ + +void config_init () +{ + if (PrefsGList) { + return; + } + char config_file[512]; + char * HOME; + + snprintf (config_file, sizeof (config_file), "/etc/%s", APP_CONFIG_FILE); + config_load_from_file (config_file, &PrefsGList); + + HOME = getenv ("HOME"); + if (HOME) { + snprintf (config_file, sizeof (config_file), "%s/.%s", HOME, APP_CONFIG_FILE); + config_load_from_file (config_file, &PrefsGList); + } + + create_extension_handler_list (); +} + + +void config_destroy () +{ + if (PrefsGList) { + g_list_free_full (PrefsGList, pref_item_free_cb); + g_list_free_full (ExtensionGList, pref_item_free_cb); + PrefsGList = NULL; + ExtensionGList = NULL; + } +} + + +void config_reload () +{ + config_destroy (); + config_init (); +} + + +void config_print () +{ + GList * i; + pref_item * item; + for (i = PrefsGList; i; i = i->next) + { + item = (pref_item *) (i->data); + printf ("%s = %s\n", item->key, item->value); + } + for (i = ExtensionGList; i; i = i->next) + { + item = (pref_item *) (i->data); + printf ("%s = %s\n", item->key, item->value); + } +} + + +gboolean config_get_int (const char * key, int * out_int) +{ + char * value; + value = config_get_item_value (PrefsGList, key); + if (value) { + *out_int = (int) strtoll (value, NULL, 0); + return TRUE; + } else { + *out_int = -1; + return FALSE; + } +} + + +// returns a string that must be freed with g_free +gboolean config_get_string_expanded (const char * key, char ** out_str) +{ + char * value1, * value2, * value = NULL; + + value1 = config_get_item_value (PrefsGList, key); + if (value1 && strstr (value1, "${")) { + value2 = replace_variable (value1); + value = value2; + // expand variable up to 2 times + if (value2 && strstr (value2, "${")) { + value = replace_variable (value2); + g_free (value2); + } + } else { + value = g_strdup (value1); + } + + if (value) { + *out_str = value; + return TRUE; + } else { + *out_str = NULL; + return FALSE; + } +} + + +gboolean config_get_string (const char * key, char ** out_str) + { + char * value; + value = config_get_item_value (PrefsGList, key); + if (value) { + *out_str = value; + return TRUE; + } else { + *out_str = NULL; + return FALSE; + } +} + + +char * config_get_handler_for_extension (const char * extension) +{ + char * handler; + if (extension && *extension == '.') { + extension++; // .html -> html + } + handler = config_get_item_value (ExtensionGList, extension); + return (handler); +} diff --git a/src/config_prefs.h b/src/config_prefs.h new file mode 100644 index 0000000..fa10f03 --- /dev/null +++ b/src/config_prefs.h @@ -0,0 +1,41 @@ +/* + * This is free and unencumbered software released into the public domain. + * + * For more information, please refer to + */ + +#ifndef __CONFIG_PREFS_H +#define __CONFIG_PREFS_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "gtkcompat.h" // glib-compat.h +#include +#include +#include +#include + +void config_init (); +void config_destroy (); +void config_reload (); +void config_print (); + +gboolean config_get_int (const char * key, int * out_int); + +/// changes string pointer (must not be freed) +gboolean config_get_string (const char * key, char ** out_str); + +/// allocates a string that must be freed with g_free +gboolean config_get_string_expanded (const char * key, char ** out_str); + +/// returns a constant string (must not be freed) +char * config_get_handler_for_extension (const char * extension); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/gtkcompletionline.cc b/src/gtkcompletionline.cc index 0ef393c..7d0af0c 100644 --- a/src/gtkcompletionline.cc +++ b/src/gtkcompletionline.cc @@ -31,8 +31,11 @@ using namespace std; +#include "config_prefs.h" #include "gtkcompletionline.h" +#define HISTORY_FILE ".gmrun_history" + static int on_cursor_changed_handler = 0; static int on_key_press_handler = 0; static guint timeout_id = 0; @@ -60,7 +63,6 @@ enum { static guint gtk_completion_line_signals[LAST_SIGNAL]; typedef set StrSet; -typedef vector StrList; static StrSet path; static StrSet execs; @@ -80,15 +82,17 @@ G_DEFINE_TYPE_EXTENDED (GtkCompletionLine, /* type name */ GTK_TYPE_ENTRY, /* GType of the parent type */ (GTypeFlags)0, NULL); +static void gtk_completion_line_finalize (GObject *object); // see also https://developer.gnome.org/gobject/stable/GTypeModule.html#G-DEFINE-DYNAMIC-TYPE:CAPS /* class_init */ static void gtk_completion_line_class_init (GtkCompletionLineClass *klass) { - GtkWidgetClass *object_class; + GtkWidgetClass * object_class = GTK_WIDGET_CLASS (klass); guint s; - object_class = (GtkWidgetClass*)klass; + GObjectClass * basic_class = G_OBJECT_CLASS (klass); + basic_class->finalize = gtk_completion_line_finalize; s = g_signal_new ("unique", G_TYPE_FROM_CLASS (object_class), @@ -182,7 +186,7 @@ static void gtk_completion_line_class_init (GtkCompletionLineClass *klass) s = g_signal_new ("cancel", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET (GtkCompletionLineClass, ext_handler), + G_STRUCT_OFFSET (GtkCompletionLineClass, cancel), NULL, NULL, g_cclosure_marshal_VOID__POINTER, @@ -212,7 +216,7 @@ static void gtk_completion_line_init (GtkCompletionLine *self) self->list_compl = NULL; self->sort_list_compl = NULL; self->tree_compl = NULL; - self->hist_search_mode = GCL_SEARCH_OFF; + self->hist_search_mode = FALSE; self->hist_word = new string; self->tabtimeout = 0; self->show_dot_files = 0; @@ -226,18 +230,41 @@ static void gtk_completion_line_init (GtkCompletionLine *self) g_signal_connect(G_OBJECT(self), "scroll-event", G_CALLBACK(on_scroll), NULL); - self->hist = new HistoryFile(); + char * HOME = getenv ("HOME"); + char history_file[512] = ""; + if (HOME) { + snprintf (history_file, sizeof (history_file), "%s/%s", HOME, HISTORY_FILE); + } + + int HIST_MAX_SIZE; + if (!config_get_int ("History", &HIST_MAX_SIZE)) + HIST_MAX_SIZE = 20; + + self->hist = history_new (history_file, HIST_MAX_SIZE); + // hacks for prev/next will be applied + history_unset_current (self->hist); self->first_key = 1; } -void gtk_completion_line_last_history_item(GtkCompletionLine* object) { - const char *last_item = object->hist->last_item(); +static void gtk_completion_line_finalize (GObject *object) +{ + GtkCompletionLine * self = GTK_COMPLETION_LINE (object); + if (self->hist) { + history_save (self->hist); + //history_print (self->hist); //debug + history_destroy (self->hist); + self->hist = NULL; + } + G_OBJECT_CLASS (gtk_completion_line_parent_class)->finalize (object); +} + +// ==================================================================== + +void gtk_completion_line_last_history_item (GtkCompletionLine* object) { + const char *last_item = history_last (object->hist); if (last_item) { - object->hist->set_default(""); - const char* txt = object->hist->prev(); - gtk_entry_set_text(GTK_ENTRY(object), - g_locale_to_utf8 (txt, -1, NULL, NULL, NULL)); + gtk_entry_set_text (GTK_ENTRY(object), last_item); } } @@ -319,12 +346,9 @@ int set_words(GtkCompletionLine *object, const vector& words, int pos = g_regex_unref (regex); if (words.size() == 1) { - const string& s = words.back(); - size_t pos = s.rfind('.'); - if (pos != string::npos) - g_signal_emit_by_name(G_OBJECT(object), "ext_handler", s.substr(pos + 1).c_str()); - else - g_signal_emit_by_name(G_OBJECT(object), "ext_handler", NULL); + const char * dot = NULL; + dot = strrchr (words.back().c_str(), '.'); + g_signal_emit_by_name (G_OBJECT(object), "ext_handler", dot); } gtk_entry_set_text(GTK_ENTRY(object), @@ -772,135 +796,91 @@ gtk_completion_line_new() static void up_history(GtkCompletionLine* cl) { - cl->hist->set_default(gtk_entry_get_text(GTK_ENTRY(cl))); - gtk_entry_set_text(GTK_ENTRY(cl), - g_locale_to_utf8 (cl->hist->prev(), -1, NULL, NULL, NULL)); + static int pause = 0; + const char * text_up; + if (pause == 1) { + text_up = history_last (cl->hist); + pause = 0; + } else { + text_up = history_prev (cl->hist); + if (!text_up) { // empty, set a flag, next time we'll get something + pause = 1; + text_up = ""; + } + } + if (text_up) { + gtk_entry_set_text (GTK_ENTRY(cl), text_up); + } } static void down_history(GtkCompletionLine* cl) { - cl->hist->set_default(gtk_entry_get_text(GTK_ENTRY(cl))); - gtk_entry_set_text(GTK_ENTRY(cl), - g_locale_to_utf8 (cl->hist->next(), -1, NULL, NULL, NULL)); + static int pause = 0; + const char * text_down; + if (pause == 1) { + text_down = history_first (cl->hist); + pause = 0; + } else { + text_down = history_next (cl->hist); + if (!text_down) { // empty, set a flag, next time we'll get something + pause = 1; + text_down = ""; + } + } + if (text_down) { + gtk_entry_set_text (GTK_ENTRY(cl), text_down); + } } +static void search_off (GtkCompletionLine* cl) +{ + int pos = gtk_editable_get_position (GTK_EDITABLE(cl)); + cl->hist_search_mode = FALSE; + g_signal_emit_by_name (G_OBJECT(cl), "search_mode"); + history_unset_current (cl->hist); +} + + static int -search_back_history(GtkCompletionLine* cl, bool avance, bool begin) -{ - if (!cl->hist_word->empty()) { - const char * histext; - if (avance) { - histext = cl->hist->prev_to_first(); - if (histext == NULL) { - g_signal_emit_by_name(G_OBJECT(cl), "search_not_found"); - return 0; - } - } else { - histext = gtk_entry_get_text(GTK_ENTRY(cl)); - } - while (true) { - string s = histext; - string::size_type i; - i = s.find(*cl->hist_word); - if (i != string::npos && !(begin && i != 0)) { - const char *tmp = gtk_entry_get_text(GTK_ENTRY(cl)); - if (!(avance && strcmp(tmp, histext) == 0)) { - gtk_entry_set_text(GTK_ENTRY(cl), - g_locale_to_utf8 (histext, -1, NULL, NULL, NULL)); - g_signal_emit_by_name(G_OBJECT(cl), "search_letter"); +search_history (GtkCompletionLine* cl) +{ // must only be called if cl->hist_search_mode = TRUE + /* a key is pressed and added to cl->hist_word */ + if (!cl->hist_word->empty()) + { + const char * history_current_item; + const char * search_str; + int search_str_len; + history_current_item = history_first (cl->hist); + search_str = cl->hist_word->c_str(); + search_str_len = strlen (search_str); + + while (true) + { + const char * s; + s = strstr (history_current_item, search_str); + if (s) { + if (strncmp (history_current_item, search_str, search_str_len) == 0) { + gtk_entry_set_text (GTK_ENTRY(cl), history_current_item); + g_signal_emit_by_name (G_OBJECT(cl), "search_letter"); return 1; } } - histext = cl->hist->prev_to_first(); - if (histext == NULL) { - g_signal_emit_by_name(G_OBJECT(cl), "search_not_found"); + history_current_item = history_next (cl->hist); + if (history_current_item == NULL) { + g_signal_emit_by_name (G_OBJECT(cl), "search_not_found"); break; } } - } else { - g_signal_emit_by_name(G_OBJECT(cl), "search_letter"); } + g_signal_emit_by_name (G_OBJECT(cl), "search_letter"); - return 0; + return 1; } -static int search_forward_history(GtkCompletionLine* cl, bool avance, bool begin) -{ - if (!cl->hist_word->empty()) { - const char * histext; - if (avance) { - histext = cl->hist->next_to_last(); - if (histext == NULL) { - g_signal_emit_by_name(G_OBJECT(cl), "search_not_found"); - return 0; - } - } else { - histext = gtk_entry_get_text(GTK_ENTRY(cl)); - } - while (true) { - string s = histext; - string::size_type i; - i = s.find(*cl->hist_word); - if (i != string::npos && !(begin && i != 0)) { - const char *tmp = gtk_entry_get_text(GTK_ENTRY(cl)); - if (!(avance && strcmp(tmp, histext) == 0)) { - gtk_entry_set_text(GTK_ENTRY(cl), - g_locale_to_utf8 (histext, -1, NULL, NULL, NULL)); - g_signal_emit_by_name(G_OBJECT(cl), "search_letter"); - return 1; - } - } - histext = cl->hist->next_to_last(); - if (histext == NULL) { - g_signal_emit_by_name(G_OBJECT(cl), "search_not_found"); - break; - } - } - } else { - g_signal_emit_by_name(G_OBJECT(cl), "search_letter"); - } - - return 0; -} - -static int search_history(GtkCompletionLine* cl, bool avance, bool begin) -{ - switch (cl->hist_search_mode) { - case GCL_SEARCH_REW: - case GCL_SEARCH_BEG: - return search_back_history(cl, avance, begin); - - case GCL_SEARCH_FWD: - return search_forward_history(cl, avance, begin); - - default: - return -1; - } -} - -static void search_off(GtkCompletionLine* cl) -{ - int pos = gtk_editable_get_position(GTK_EDITABLE(cl)); - cl->hist_search_mode = GCL_SEARCH_OFF; - g_signal_emit_by_name(G_OBJECT(cl), "search_mode"); - cl->hist->reset_position(); -} - -#define STOP_PRESS \ - (g_signal_stop_emission_by_name(G_OBJECT(cl), "key_press_event")) -#define MODE_BEG \ - (cl->hist_search_mode == GCL_SEARCH_BEG) -#define MODE_REW \ - (cl->hist_search_mode == GCL_SEARCH_REW) -#define MODE_FWD \ - (cl->hist_search_mode == GCL_SEARCH_FWD) -#define MODE_SRC \ - (cl->hist_search_mode != GCL_SEARCH_OFF) - static guint tab_pressed(GtkCompletionLine* cl) { - if (MODE_SRC) + if (cl->hist_search_mode == TRUE) search_off(cl); complete_line(cl); timeout_id = 0; @@ -945,7 +925,7 @@ on_scroll(GtkCompletionLine *cl, GdkEventScroll *event, gpointer data) } else { up_history(cl); } - if (MODE_SRC) { + if (cl->hist_search_mode == TRUE) { search_off(cl); } return TRUE; @@ -959,7 +939,7 @@ on_scroll(GtkCompletionLine *cl, GdkEventScroll *event, gpointer data) } else { down_history(cl); } - if (MODE_SRC) { + if (cl->hist_search_mode == TRUE) { search_off(cl); } return TRUE; @@ -969,7 +949,7 @@ on_scroll(GtkCompletionLine *cl, GdkEventScroll *event, gpointer data) static gboolean on_key_press(GtkCompletionLine *cl, GdkEventKey *event, gpointer data) -{ +{ // https://developer.gnome.org/gtk2/stable/GtkWidget.html#GtkWidget-key-press-event if (event->type == GDK_KEY_PRESS) { switch (event->keyval) { @@ -987,8 +967,7 @@ on_key_press(GtkCompletionLine *cl, GdkEventKey *event, gpointer data) timeout_id = 0; } tab_pressed(cl); - STOP_PRESS; - return TRUE; + return TRUE; /* stop signal emission */ case GDK_KEY_Up: if (cl->win_compl != NULL) { @@ -1011,16 +990,15 @@ on_key_press(GtkCompletionLine *cl, GdkEventKey *event, gpointer data) } else { up_history(cl); } - if (MODE_SRC) { + if (cl->hist_search_mode == TRUE) { search_off(cl); } - STOP_PRESS; - return TRUE; + return TRUE; /* stop signal emission */ case GDK_KEY_space: { cl->first_key = 0; - bool search = MODE_SRC; + bool search = cl->hist_search_mode; if (search) search_off(cl); if (cl->win_compl != NULL) { @@ -1043,11 +1021,10 @@ on_key_press(GtkCompletionLine *cl, GdkEventKey *event, gpointer data) } else { down_history(cl); } - if (MODE_SRC) { + if (cl->hist_search_mode == TRUE) { search_off(cl); } - STOP_PRESS; - return TRUE; + return TRUE; /* stop signal emission */ case GDK_KEY_Return: if (cl->win_compl != NULL) { @@ -1059,61 +1036,30 @@ on_key_press(GtkCompletionLine *cl, GdkEventKey *event, gpointer data) } else { g_signal_emit_by_name(G_OBJECT(cl), "activate"); } - STOP_PRESS; - return TRUE; - - case GDK_KEY_exclam: - if (!MODE_BEG) { - if (!MODE_SRC) - gtk_editable_delete_selection(GTK_EDITABLE(cl)); - const char *tmp = gtk_entry_get_text(GTK_ENTRY(cl)); - if (!(*tmp == '\0' || cl->first_key)) - goto ordinary; - cl->hist_search_mode = GCL_SEARCH_BEG; - cl->hist_word->clear(); - g_signal_emit_by_name(G_OBJECT(cl), "search_mode"); - STOP_PRESS; - return true; - } else goto ordinary; - - case GDK_KEY_R: - case GDK_KEY_r: - if (event->state & GDK_CONTROL_MASK) { - if (MODE_SRC) { - search_back_history(cl, true, MODE_BEG); - } else { - cl->hist_search_mode = GCL_SEARCH_REW; - cl->hist_word->clear(); - cl->hist->reset_position(); - g_signal_emit_by_name(G_OBJECT(cl), "search_mode"); - } - STOP_PRESS; - return TRUE; - } else goto ordinary; + return TRUE; /* stop signal emission */ case GDK_KEY_S: case GDK_KEY_s: + case GDK_KEY_R: + case GDK_KEY_r: if (event->state & GDK_CONTROL_MASK) { - if (MODE_SRC) { - search_forward_history(cl, true, MODE_BEG); - } else { - cl->hist_search_mode = GCL_SEARCH_FWD; + if (cl->hist_search_mode == FALSE) { + history_first (cl->hist); + cl->hist_search_mode = TRUE; cl->hist_word->clear(); - cl->hist->reset_position(); g_signal_emit_by_name(G_OBJECT(cl), "search_mode"); } - STOP_PRESS; - return TRUE; + return TRUE; /* stop signal emission */ } else goto ordinary; case GDK_KEY_BackSpace: - if (MODE_SRC) { + if (cl->hist_search_mode == TRUE) { if (!cl->hist_word->empty()) { cl->hist_word->erase(cl->hist_word->length() - 1); + search_history(cl); g_signal_emit_by_name(G_OBJECT(cl), "search_letter"); } - STOP_PRESS; - return TRUE; + return TRUE; /* stop signal emission */ } return FALSE; @@ -1123,7 +1069,7 @@ on_key_press(GtkCompletionLine *cl, GdkEventKey *event, gpointer data) goto ordinary; case GDK_KEY_Escape: - if (MODE_SRC) { + if (cl->hist_search_mode == TRUE) { search_off(cl); } else if (cl->win_compl != NULL) { gtk_widget_destroy(cl->win_compl); @@ -1132,23 +1078,20 @@ on_key_press(GtkCompletionLine *cl, GdkEventKey *event, gpointer data) // user cancelled g_signal_emit_by_name(G_OBJECT(cl), "cancel"); } - STOP_PRESS; - return TRUE; + return TRUE; /* stop signal emission */ case GDK_KEY_G: case GDK_KEY_g: if (event->state & GDK_CONTROL_MASK) { search_off(cl); - if (MODE_SRC) - STOP_PRESS; - return TRUE; + return TRUE; /* stop signal emission */ } else goto ordinary; case GDK_KEY_E: case GDK_KEY_e: if (event->state & GDK_CONTROL_MASK) { search_off(cl); - if (MODE_SRC) + if (cl->hist_search_mode == TRUE) clear_selection(cl); } goto ordinary; @@ -1161,13 +1104,11 @@ on_key_press(GtkCompletionLine *cl, GdkEventKey *event, gpointer data) cl->win_compl = NULL; } cl->where = NULL; - if (MODE_SRC) { + if (cl->hist_search_mode == TRUE) { if (event->length > 0) { *cl->hist_word += event->string; - if (search_history(cl, false, MODE_BEG) <= 0) - cl->hist_word->erase(cl->hist_word->length() - 1); - STOP_PRESS; - return TRUE; + search_history(cl); + return TRUE; /* stop signal emission */ } else search_off(cl); } @@ -1187,12 +1128,6 @@ on_key_press(GtkCompletionLine *cl, GdkEventKey *event, gpointer data) return FALSE; } -#undef STOP_PRESS -#undef MODE_BEG -#undef MODE_REW -#undef MODE_FWD -#undef MODE_SRC - // Local Variables: *** // mode: c++ *** // c-basic-offset: 2 *** diff --git a/src/gtkcompletionline.h b/src/gtkcompletionline.h index be691a7..0882774 100644 --- a/src/gtkcompletionline.h +++ b/src/gtkcompletionline.h @@ -31,14 +31,6 @@ extern "C++" { typedef struct _GtkCompletionLine GtkCompletionLine; typedef struct _GtkCompletionLineClass GtkCompletionLineClass; - enum GCL_SEARCH_MODE - { - GCL_SEARCH_OFF = 0, - GCL_SEARCH_REW = 1, - GCL_SEARCH_FWD = 2, - GCL_SEARCH_BEG = 3 - }; - struct _GtkCompletionLine { GtkEntry parent; @@ -53,7 +45,7 @@ extern "C++" { GList *where; /* current row pointer ??? */ HistoryFile *hist; - GCL_SEARCH_MODE hist_search_mode; + gboolean hist_search_mode; std::string *hist_word; int first_key; diff --git a/src/history.c b/src/history.c new file mode 100644 index 0000000..9e92669 --- /dev/null +++ b/src/history.c @@ -0,0 +1,308 @@ +/* + * This is free and unencumbered software released into the public domain. + * + * For more information, please refer to + */ + +/* + * Generic implementation of HistoryFile + * HOWTO: see example at the end of the file + */ + +#include "history.h" + +// ============================================================ +// PRIVATE +// ============================================================ + +struct _Whistory +{ + long int index; + unsigned int count; + unsigned int max; + char * filename; + GList * list; + GList * current; +}; + +static void _history_clear (HistoryFile * history) +{ + // keep history->filename (destroyed in _history_free()) + if (history->list) { + GList * i ; + for (i = history->list; i; i = i->next) { + if (i->data) g_free (i->data); + } + g_list_free (history->list); + history->list = NULL; + } + history->index = 0; + history->count = 0; +} + + +static void _history_free (HistoryFile * history) +{ + _history_clear (history); + if (history->filename) { + g_free (history->filename); + history->filename = NULL; + } + g_free (history); +} + + +static void _history_load_from_file (HistoryFile * history, const char * filename) +{ + FILE *fp; + char buf[1024]; + char * p, * item; + size_t len; + unsigned int count = 0; + unsigned int max = history->max; + GList * out_list = NULL; + + fp = fopen (filename, "r"); + if (!fp) { + return; + } + + /* Read file line by line */ + while (fgets (buf, sizeof (buf), fp)) + { + p = buf; + while (*p && *p <= 0x20) { // 32 = space [ignore spaces] + p++; + } + if (!*p) { + continue; + } + + item = g_strdup (p); + len = strlen (buf); + item[len-1] = 0; + if (max > 0 && count >= max) { + break; + } + count++; + out_list = g_list_prepend (out_list, (gpointer) item); + } + + if (out_list) { + out_list = g_list_reverse (out_list); + history->index = 1; + } + + history->count = count; + history->list = out_list; + history->current = history->list; // current = 1st item + fclose (fp); + return; +} + + +void _history_write_to_file (HistoryFile * history, const char * filename) +{ + FILE *fp; + fp = fopen (filename, "w"); + if (!fp) { + return; + } + unsigned int count = 0; + unsigned int max = history->max; + + GList * i; + for (i = history->list; i; i = i->next) + { + if (max > 0 && count >= max) { + break; + } + count++; + fprintf (fp, "%s\n", (char *) (i->data)); + } + + fclose (fp); + return; +} + + +// ============================================================ +// PUBLIC +// ============================================================ + +HistoryFile * history_new (char * filename, unsigned int maxcount) +{ + HistoryFile * history = g_malloc0 (sizeof (HistoryFile)); + history->max = maxcount; + if (filename || *filename) { + history->filename = g_strdup (filename); + _history_load_from_file (history, filename); + } + return (history); +} + +void history_save (HistoryFile * history) +{ + if (history && history->filename) { + _history_write_to_file (history, history->filename); + } else { + fprintf (stderr, "history_save(): history or filename is NULL\n"); + } +} + +void history_destroy (HistoryFile * history) +{ + if (history) { + _history_free (history); + } +} + +void history_reload (HistoryFile * history) +{ + if (history) { + _history_clear (history); + if (history->filename) { + _history_load_from_file (history, history->filename); + } + } +} + + +void history_print (HistoryFile * history) +{ + if (history) { + unsigned int count = 0; + GList * i; + for (i = history->list; i; i = i->next) { + count++; + printf ("[%d] %s\n", count, (char *) (i->data)); + } + printf ("-- list internal count: [%d]\n", history->count); + } +} + + +// some apps might want to handle prev/next in a special way +void history_unset_current (HistoryFile * history) +{ + if (history) { + history->current = NULL; + history->index = -1; + } +} + + +const char * history_get_current (HistoryFile * history) +{ + if (history) return ((char*) (history->current->data)); + else return (NULL); +} + + +int history_get_current_index (HistoryFile * history) +{ + if (history) return (history->index); + else return (-1); +} + + +const char * history_next (HistoryFile * history) +{ + if (history->current && history->current->next) { + history->current = history->current->next; + history->index++; + return ((char *) (history->current->data)); + } + return (NULL); +} + + +const char * history_prev (HistoryFile * history) +{ + if (history->current && history->current->prev) { + history->current = history->current->prev; + history->index--; + return ((char *) (history->current->data)); + } + return (NULL); +} + + +const char * history_first (HistoryFile * history) +{ + if (history->list) { + history->current = history->list; + history->index = 1; + return ((char *) (history->current->data)); + } + return (NULL); +} + + +const char * history_last (HistoryFile * history) +{ + if (history->list) { + history->current = g_list_last (history->list); + return ((char *) (history->current->data)); + history->index = history->count; + } + return (NULL); +} + + +void history_append (HistoryFile * history, const char * text) +{ + if (!text || !*text) { + return; + } + + unsigned int remove_first = 0; + if (history->max > 0 + && history->count > 1 + && history->count >= history->max) { + remove_first = 1; + } + + char * new_item = g_strdup (text); + history->list = g_list_append (history->list, (gpointer) new_item); + if (history->count < history->max) { + history->count++; + } + + if (remove_first && history->list->next) { + gpointer data = history->list->data; + history->list = g_list_remove (history->list, data); + } +} + +// ============================================================ +// example +// ============================================================ + +/* +// gcc -o history history.c -Wall -g -O2 `pkg-config --cflags glib-2.0` `pkg-config --libs glib-2.0` + +int main (int argc, char ** argv) +{ + + #define HISTORY_FILE ".gmrun_history" + char * HOME = getenv ("HOME"); + char history_file[512] = ""; + if (HOME) { + snprintf (history_file, sizeof (history_file), "%s/%s", HOME, HISTORY_FILE); + } + + HistoryFile * history; + history = history_new (history_file, 10); + + history_print (history); + history_append (history, "new line"); + history_print (history); + history_append (history, "soldier dream"); + history_print (history); + + history_destroy (history); + history = NULL; + return (0); +} +*/ diff --git a/src/history.cc b/src/history.cc deleted file mode 100644 index 768dc67..0000000 --- a/src/history.cc +++ /dev/null @@ -1,158 +0,0 @@ -/***************************************************************************** - * $Id: history.cc,v 1.10 2002/08/17 13:19:31 mishoo Exp $ - * Copyright (C) 2000, Mishoo - * Author: Mihai Bazon Email: mishoo@fenrir.infoiasi.ro - * - * Distributed under the terms of the GNU General Public License. You are - * free to use/modify/distribute this program as long as you comply to the - * terms of the GNU General Public License, version 2 or above, at your - * option, and provided that this copyright notice remains intact. - *****************************************************************************/ - - -#include -#include -#include -#include -#include -#include -using namespace std; - -#include "history.h" -#include "prefs.h" - -HistoryFile::HistoryFile() -{ - m_file_entries = 0; - m_filename = g_get_home_dir(); - m_filename += "/.gmrun_history"; - m_current = 0; - m_default_set = false; - read_the_file(); -} - -HistoryFile::~HistoryFile() -{} - -void HistoryFile::read_the_file() -{ - const char *filename = m_filename.c_str(); - ifstream f(filename); - if (!f) return; - - string line_text; - - while (!f.eof()) { - string line_str; - - getline(f,line_text); - line_str = line_text; - history.push_back(line_str); - } - - m_file_entries = history.size(); - m_current = m_file_entries; -} - -void HistoryFile::sync_the_file() -{ - const char *filename = m_filename.c_str(); - - int HIST_MAX_SIZE; - - if (!configuration.get_int("History", HIST_MAX_SIZE)) - HIST_MAX_SIZE = 20; - - ofstream f(filename, ios::out); - - int start = 0; - if (history.size() > (size_t)HIST_MAX_SIZE) - start = history.size() - HIST_MAX_SIZE; - - for (size_t i = start; i < history.size(); i++) - if (history[i].length() != 0) - f << history[i] << endl; - - f.flush(); -} - -void HistoryFile::append(const char *entry) -{ - std::string ent = std::string(entry); - if (!history.empty()) { - StrArray::reverse_iterator i; -#ifdef DEBUG - for_each(history.begin(), history.end(), DumpString(cerr)); -#endif - i = find(history.rbegin(), history.rend(), ent); - if (i != history.rend()) { -#ifdef DEBUG - cerr << "erasing "<< ent << endl; -#endif - history.erase(remove(history.begin(), history.end(), ent)); - } - } - history.push_back(ent); -} - -void HistoryFile::set_default(const char *defstr) -{ - if (!m_default_set) { - m_default = defstr; - m_default_set = true; - } -} - -const char * HistoryFile::operator [] (size_t index) -{ - if (index >= history.size()) { - return m_default.c_str(); - } - - return history[index].c_str(); -} - -const char * -HistoryFile::prev() -{ - const char *ret = (*this)[--m_current]; - if (m_current < 0) m_current = history.size(); - return ret; -} - -const char * -HistoryFile::next() -{ - const char *ret = (*this)[++m_current]; - if ((guint)m_current >= history.size()) m_current = -1; - return ret; -} - -const char * -HistoryFile::prev_to_first() -{ - if (m_current > 0) { - return (*this)[--m_current]; - } else { - return NULL; - } -} - -const char * HistoryFile::next_to_last() -{ - if ((guint)m_current < history.size()) { - return (*this)[++m_current]; - } else { - return NULL; - } -} - -void HistoryFile::clear_default() -{ - m_default_set = false; -} - -void HistoryFile::reset_position() -{ - m_current = m_file_entries; -} diff --git a/src/history.h b/src/history.h index d5dd5f2..b505ce1 100644 --- a/src/history.h +++ b/src/history.h @@ -1,71 +1,77 @@ -/***************************************************************************** - * $Id: history.h,v 1.11 2002/08/17 13:19:31 mishoo Exp $ - * Copyright (C) 2000, Mishoo - * Author: Mihai Bazon Email: mishoo@fenrir.infoiasi.ro + +/* + * This is free and unencumbered software released into the public domain. * - * Distributed under the terms of the GNU General Public License. You are - * free to use/modify/distribute this program as long as you comply to the - * terms of the GNU General Public License, version 2 or above, at your - * option, and provided that this copyright notice remains intact. - *****************************************************************************/ + * For more information, please refer to + */ +/* + * Generic implementation of HistoryFile + */ -#ifndef __HISTORY_H__ -#define __HISTORY_H__ +#ifndef __HISTORY_H +#define __HISTORY_H -#include -#include -using namespace std; - -class HistoryFile +#ifdef __cplusplus +extern "C" { - protected: - int m_file_entries; - string m_filename; - string m_default; - bool m_default_set; - int m_current; +#endif - typedef vector StrArray; - StrArray history; +#include +#include +#include +#include - public: - HistoryFile(); - ~HistoryFile(); +typedef struct _Whistory HistoryFile; - void append(const char *entry); - void set_default(const char *defstr); - void clear_default(); +/// create a new HistoryFile +/// the history is initialized using filename and the filename is stored +/// maxcount > 0 sets a maximun item count +HistoryFile * history_new (char * filename, unsigned int maxcount); - void reset_position(); +/// history is saved to file +void history_save (HistoryFile * history); - const char * operator [] (size_t index); +/// history is destroyed, you must variable to NULL +void history_destroy (HistoryFile * history); - const char * prev(); - const char * next(); +/// clear history and reload from file +void history_reload (HistoryFile * history); - const char * prev_to_first(); - const char * next_to_last(); +/// print history, good for debugging +void history_print (HistoryFile * history); - void sync_the_file(); +/// some apps might want to handle prev/next in a special way +void history_unset_current (HistoryFile * history); - inline const char* last_item() { - return history.empty() ? 0 : history.back().c_str(); - } +/// returns string with the current history item +const char * history_get_current (HistoryFile * history); - inline const char* first_item() { - return history.empty() ? 0 : history.front().c_str(); - } +/// returns current index: valid index > 0 +/// assume unser current item if index <= 0 +int history_get_current_index (HistoryFile * history); - protected: - void read_the_file(); - private: - struct DumpString { - DumpString(std::ostream& o) : _out(o) {} - void operator()(std::string& str) { _out << str << endl; } - private: - std::ostream& _out; - }; -}; +/// moves position to the next entry and returns text +/// if there is no next entry, the position is not moved and returns NULL +const char * history_next (HistoryFile * history); -#endif // __HISTORY_H__ +/// moves position to the previous entry and returns text +/// if there is no next entry, the position is not moved and returns NULL +const char * history_prev (HistoryFile * history); + +/// moves position to the first entry en returns text +/// if there are no entries, it returns NULL +const char * history_first (HistoryFile * history); + +/// moves position to the last entry en returns text +/// if there are no entries, it returns NULL +const char * history_last (HistoryFile * history); + +/// append item to history +void history_append (HistoryFile * history, const char * text); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/main.cc b/src/main.cc index 701fc5b..f41baec 100644 --- a/src/main.cc +++ b/src/main.cc @@ -15,15 +15,6 @@ #include #include -#include -#include -#include -#include -#include -#include - -using namespace std; - #ifdef MTRACE #include #endif @@ -32,7 +23,7 @@ using namespace std; #include #include "gtkcompletionline.h" -#include "prefs.h" +#include "config_prefs.h" enum { @@ -44,10 +35,7 @@ enum static void gmrun_exit (void); GtkAllocation window_geom = { -1, -1, -1, -1 }; - -// defined in gtkcompletionline.cc -int get_words(GtkCompletionLine *object, vector& words); -string quote_string(const string& str); +GtkWidget * compline; struct gigi { @@ -98,9 +86,11 @@ static void set_info_text_color (GtkWidget *w, const char *text, int spec) } static void -run_the_command(const std::string& command, struct gigi* g) +run_the_command(const char * cmd, struct gigi* g) { - const char * cmd = command.c_str(); +#if DEBUG + fprintf (stderr, "command: %s\n", cmd); +#endif GError * error = NULL; gboolean success; int argc; @@ -130,57 +120,44 @@ run_the_command(const std::string& command, struct gigi* g) static void on_ext_handler(GtkCompletionLine *cl, const char* ext, struct gigi* g) { - string cmd; - if (ext && configuration.get_ext_handler(ext, cmd)) { - string str("Handler: "); - size_t pos = cmd.find_first_of(" \t"); - if (pos == string::npos) - str += cmd; - else - str += cmd.substr(0, pos); - gtk_label_set_text(GTK_LABEL(g->w2), str.c_str()); - gtk_widget_show(g->w2); - // gtk_timeout_add(1000, GSourceFunc(search_off_timeout), g); - } else { - search_off_timeout(g); + if (!ext) { + search_off_timeout (g); + return; + } + const char * handler = config_get_handler_for_extension (ext); + if (handler) { + char * tmp = g_strconcat ("Handler: ", handler, NULL); + gtk_label_set_text (GTK_LABEL(g->w2), tmp); + gtk_widget_show (g->w2); + g_free (tmp); } } -static void on_compline_runwithterm(GtkCompletionLine *cl, struct gigi* g) +static void on_compline_runwithterm (GtkCompletionLine *cl, struct gigi* g) { - string command(g_locale_from_utf8 (gtk_entry_get_text(GTK_ENTRY(cl)), - -1, - NULL, - NULL, - NULL)); - string tmp; - string term; + char cmd[512]; + char * term; + char * entry_text = g_strdup (gtk_entry_get_text (GTK_ENTRY(cl))); + g_strstrip (entry_text); - string::size_type i; - i = command.find_first_not_of(" \t"); - - if (i != string::npos) { - if (!configuration.get_string("TermExec", term)) { - term = "xterm -e"; - } - tmp = term; - tmp += " "; - tmp += command; - } else { - if (!configuration.get_string("Terminal", term)) { - tmp = "xterm"; + if (*entry_text) { + if (config_get_string_expanded ("TermExec", &term)) { + snprintf (cmd, sizeof (cmd), "%s %s", term, entry_text); + g_free (term); } else { - tmp = term; + snprintf (cmd, sizeof (cmd), "xterm -e %s", entry_text); + } + } else { + if (config_get_string ("Terminal", &term)) { + strncpy (cmd, term, sizeof (cmd)); + } else { + strncpy (cmd, "xterm", sizeof (cmd)); } } -#ifdef DEBUG - cerr << tmp << endl; -#endif - - cl->hist->append(command.c_str()); - cl->hist->sync_the_file(); - run_the_command(tmp.c_str(), g); + history_append (cl->hist, cmd); + run_the_command (cmd, g); + g_free (entry_text); } static gint search_off_timeout(struct gigi *g) @@ -215,7 +192,7 @@ on_compline_incomplete(GtkCompletionLine *cl, struct gigi *g) static void on_search_mode(GtkCompletionLine *cl, struct gigi *g) { - if (cl->hist_search_mode != GCL_SEARCH_OFF) { + if (cl->hist_search_mode == TRUE) { gtk_widget_show(g->w2); gtk_label_set_text(GTK_LABEL(g->w1), "Search:"); gtk_label_set_text(GTK_LABEL(g->w2), cl->hist_word->c_str()); @@ -236,6 +213,7 @@ static gint search_fail_timeout(struct gigi *g) { set_info_text_color(g->w1, "Search:", W_TEXT_STYLE_NOTUNIQUE); + g_search_off_timeout_id = 0; return FALSE; } @@ -246,144 +224,134 @@ on_search_not_found(GtkCompletionLine *cl, struct gigi *g) add_search_off_timeout(1000, g, GSourceFunc(search_fail_timeout)); } -static bool url_check(GtkCompletionLine *cl, struct gigi *g) +static bool url_check(GtkCompletionLine *cl, struct gigi *g, char * entry_text) { - string text(g_locale_from_utf8 (gtk_entry_get_text(GTK_ENTRY(cl)), - -1, - NULL, - NULL, - NULL)); + // + // http : //www.fsf.org + // + // config: URL_ + // handler %s (format 1) = run handler with + // handler %u (format 2) = run handler with + char * cmd; + char * tmp, * delim, * p; + char * url, * url_type, * full_url, * chosen_url; + char * url_handler; + char * config_key; - string::size_type i; - string::size_type sp; + delim = strchr (entry_text, ':'); + if (!delim || !*(delim+1)) { + return FALSE; + } + tmp = g_strdup (entry_text); + delim = strchr (tmp, ':'); + *delim = 0; + url_type = tmp; // http + url = delim + 1; // //www.fsf.org + full_url = entry_text; - sp = text.find_first_not_of(" \t"); - if (sp == string::npos) return true; - text = text.substr(sp); - - sp = text.find_first_of(" \t"); - i = text.find(":"); - - if (i != string::npos && i < sp) { - // URL entered... - string url(text.substr(i + 1)); - string url_type(text.substr(0, i)); - string url_handler; - - if (configuration.get_string(string("URL_") + url_type, url_handler)) { - string::size_type j = 0; - - do { - j = url_handler.find("%s", j); - if (j != string::npos) { - url_handler.replace(j, 2, url); - } - } while (j != string::npos); - - j = 0; - do { - j = url_handler.find("%u", j); - if (j != string::npos) { - url_handler.replace(j, 2, text); - } - } while (j != string::npos); - - cl->hist->append(text.c_str()); - cl->hist->sync_the_file(); - run_the_command(url_handler.c_str(), g); - return true; + config_key = g_strconcat ("URL_", url_type); + if (config_get_string_expanded (config_key, &url_handler)) + { + chosen_url = url; + p = strchr (url_handler, '%'); + if (p) { // handler %s + p++; + if (*p == 'u') { // handler %u + *p = 's'; // convert %u to %s (for printf) + chosen_url = full_url; + } + cmd = g_strdup_printf (url_handler, chosen_url); } else { - set_info_text_color(g->w1, - (string("No URL handler for [") + url_type + "]").c_str(), - W_TEXT_STYLE_NOTFOUND); - add_search_off_timeout(1000, g); - return true; + cmd = g_strconcat (url_handler, " ", url, NULL); } + g_free (url_handler); } - return false; + if (cmd) { + history_append (cl->hist, cmd); + run_the_command (cmd, g); + g_free (cmd); + } else { + g_free (tmp); + tmp = g_strconcat ("No URL handler for [", config_key, "]", NULL); + set_info_text_color (g->w1, tmp, W_TEXT_STYLE_NOTFOUND); + add_search_off_timeout (1000, g); + } + + g_free (entry_text); + g_free (config_key); + g_free (tmp); + return TRUE; } -static bool ext_check(GtkCompletionLine *cl, struct gigi *g) +static bool ext_check (GtkCompletionLine *cl, struct gigi *g, char * entry_text) { - vector words; - get_words(cl, words); - vector::const_iterator - i = words.begin(), - i_end = words.end(); - - - GRegex *regex = g_regex_new (" ", G_REGEX_OPTIMIZE, G_REGEX_MATCH_NOTEMPTY, NULL); - while (i != i_end) { - gchar *quoted = g_regex_replace_literal ( - regex, (*i++).c_str(), -1, 0, "\\ ", G_REGEX_MATCH_NOTEMPTY, NULL); - const string w = quoted; - if (w[0] == '/') { - // absolute path, check for extension - size_t pos = w.rfind('.'); - if (pos != string::npos) { - // we have extension - string ext = w.substr(pos + 1); - string ext_handler; - if (configuration.get_ext_handler(ext, ext_handler)) { - // we have the handler - pos = ext_handler.find("%s"); - if (pos != string::npos) - ext_handler.replace(pos, 2, w); - cl->hist->append(w.c_str()); - cl->hist->sync_the_file(); - run_the_command(ext_handler.c_str(), g); - return true; - } - } - } - g_free(quoted); - // FIXME: for now we check only one entry - break; + // example: file.html -> `xdg-open %s` -> `xdg-open file.html` + char * cmd; + char * ext = strrchr (entry_text, '.'); + char * handler_format = NULL; + if (ext) { + handler_format = config_get_handler_for_extension (ext); } - g_regex_unref(regex); - return false; + if (handler_format) { + if (strchr (handler_format, '%')) { // xdg-open %s + cmd = g_strdup_printf (handler_format, entry_text); + } else { // xdg-open + cmd = g_strconcat (handler_format, " ", entry_text, NULL); + } + history_append (cl->hist, cmd); + run_the_command (cmd, g); + + g_free (entry_text); + g_free (cmd); + return TRUE; + } + + return FALSE; } -static void on_compline_activated(GtkCompletionLine *cl, struct gigi *g) +static void on_compline_activated (GtkCompletionLine *cl, struct gigi *g) { - if (url_check(cl, g)) + char * entry_text = g_strdup (gtk_entry_get_text (GTK_ENTRY(cl))); + g_strstrip (entry_text); + + if (url_check(cl, g, entry_text)) return; - if (ext_check(cl, g)) + if (ext_check(cl, g, entry_text)) return; - string command = g_locale_from_utf8 (gtk_entry_get_text(GTK_ENTRY(cl)), - -1, - NULL, - NULL, - NULL); + char cmd[512]; + char * AlwaysInTerm = NULL; + char ** term_progs = NULL; + char * selected_term_prog = NULL; - string::size_type i; - i = command.find_first_not_of(" \t"); - - if (i != string::npos) { - string::size_type j = command.find_first_of(" \t", i); - string progname = command.substr(i, j - i); - list term_progs; - if (configuration.get_string_list("AlwaysInTerm", term_progs)) { -#ifdef DEBUG - cerr << "---" << std::endl; - std::copy(term_progs.begin(), term_progs.end(), - std::ostream_iterator(cerr, "\n")); - cerr << "---" << std::endl; -#endif - list::const_iterator w = - std::find(term_progs.begin(), term_progs.end(), progname); - if (w != term_progs.end()) { - on_compline_runwithterm(cl, g); - return; + if (config_get_string ("AlwaysInTerm", &AlwaysInTerm)) + { + term_progs = g_strsplit (AlwaysInTerm, " ", 0); + int i; + for (i = 0; term_progs[i]; i++) { + if (strcmp (term_progs[i], entry_text) == 0) { + selected_term_prog = g_strdup (term_progs[i]); + break; } } - cl->hist->append(command.c_str()); - cl->hist->sync_the_file(); - run_the_command(command, g); + g_strfreev (term_progs); } + + if (selected_term_prog) { + char * TermExec; + config_get_string_expanded ("TermExec", &TermExec); + snprintf (cmd, sizeof (cmd), "%s %s", TermExec, selected_term_prog); + g_free (selected_term_prog); + g_free (TermExec); + } else { + strncpy (cmd, entry_text, sizeof (cmd)); + } + g_free (entry_text); + + history_append (cl->hist, cmd); + run_the_command (cmd, g); } // ============================================================= @@ -391,7 +359,7 @@ static void on_compline_activated(GtkCompletionLine *cl, struct gigi *g) static void gmrun_activate(void) { GtkWidget *dialog, * main_vbox; - GtkWidget *compline; + GtkWidget *label_search; struct gigi g; @@ -428,11 +396,11 @@ static void gmrun_activate(void) gtk_box_pack_start (GTK_BOX (main_vbox), compline, TRUE, TRUE, 0); // don't show files starting with "." by default - if (!configuration.get_int("ShowDotFiles", GTK_COMPLETION_LINE(compline)->show_dot_files)) + if (config_get_int ("ShowDotFiles", &(GTK_COMPLETION_LINE(compline)->show_dot_files))) { GTK_COMPLETION_LINE(compline)->show_dot_files = 0; - { - int tmp; - if (configuration.get_int("TabTimeout", tmp)) + } + int tmp; + if (config_get_int ("TabTimeout", &tmp)) { ((GtkCompletionLine*)compline)->tabtimeout = tmp; } @@ -464,7 +432,7 @@ static void gmrun_activate(void) G_CALLBACK(on_ext_handler), &g); int shows_last_history_item; - if (!configuration.get_int("ShowLast", shows_last_history_item)) { + if (!config_get_int ("ShowLast", &shows_last_history_item)) { shows_last_history_item = 0; } if (shows_last_history_item) { @@ -484,8 +452,8 @@ static void gmrun_activate(void) gtk_window_set_default_size (GTK_WINDOW (dialog), window_geom.width, window_geom.height); } else { - /* default width = 400 */ - gtk_window_set_default_size (GTK_WINDOW (dialog), 400, -1); + /* default width = 450 */ + gtk_window_set_default_size (GTK_WINDOW (dialog), 450, -1); } // window icon @@ -537,9 +505,9 @@ static void parse_command_line (int argc, char ** argv) if (!geometry_str) { // --geometry was not specified, see config file - std::string geomstr; - if (configuration.get_string("Geometry", geomstr)) { - geometry_str = g_strdup (geomstr.c_str()); + char * geomstr; + if (config_get_string ("Geometry", &geomstr)) { + geometry_str = g_strdup (geomstr); } } @@ -591,6 +559,8 @@ static void parse_command_line (int argc, char ** argv) void gmrun_exit(void) { + gtk_widget_destroy (compline); + config_destroy (); gtk_main_quit (); } @@ -603,6 +573,7 @@ int main(int argc, char **argv) gtk_init(&argc, &argv); + config_init (); parse_command_line (argc, argv); gmrun_activate (); diff --git a/src/prefs.cc b/src/prefs.cc deleted file mode 100644 index cc9136a..0000000 --- a/src/prefs.cc +++ /dev/null @@ -1,178 +0,0 @@ -/***************************************************************************** - * $Id: prefs.cc,v 1.9 2002/08/16 10:48:22 mishoo Exp $ - * Copyright (C) 2000, Mishoo - * Author: Mihai Bazon Email: mishoo@fenrir.infoiasi.ro - * - * Distributed under the terms of the GNU General Public License. You are - * free to use/modify/distribute this program as long as you comply to the - * terms of the GNU General Public License, version 2 or above, at your - * option, and provided that this copyright notice remains intact. - *****************************************************************************/ - - -#include -#include -#include -#include - -#include - -#define GMRUNRC "gmrunrc" - -using std::ifstream; -using std::getline; -using std::string; -using std::list; - -#include "prefs.h" -#include "config.h" - -Prefs configuration; - -Prefs::Prefs() -{ - string file_name = "/etc/"; - file_name += GMRUNRC; - init(file_name); - - file_name = getenv("HOME"); - if (!file_name.empty()) { - string::iterator i = file_name.end() - 1; - if (*i == '/') file_name.erase(i); - file_name += "/."; - file_name += GMRUNRC; - init(file_name); - } -} - -Prefs::~Prefs() -{} - -bool Prefs::get_string(const string& key, string& val) const -{ - CONFIG::const_iterator i; - i = vals_.find(ci_string(key.c_str())); - if (i != vals_.end()) { - val = process(i->second); - return true; - } else { - return false; - } -} - -bool Prefs::get_ext_handler(const std::string& ext, std::string& val) const -{ - CONFIG::const_iterator i; - i = exts_.find(ci_string(ext.c_str())); - if (i != exts_.end()) { - val = process(i->second); - return true; - } else { - return false; - } -} - -bool Prefs::get_int(const std::string& key, int& val) const -{ - string sval; - if (get_string(key, sval)) { - int ret; - if (sscanf(sval.c_str(), "%d", &ret) == 1) { - val = ret; - return true; - } else { - return false; - } - } else { - return false; - } -} - -bool Prefs::init(const string& file_name) -{ - ifstream f(file_name.c_str()); - - if (!f.good() || f.eof()) return false; - - while (f.good() && !f.eof()) { - string line; - char key[0x100]; - char val[0x100]; - - getline(f, line); - if (f.eof()) break; - string::size_type i = line.find_first_not_of(" \t"); - if (i == string::npos) continue; - if (line[i] == '#') continue; - - sscanf(line.c_str(), "%255[a-zA-Z_0-9:,;] = %255[^\n]", key, val); - if (strncmp(key, "EXT:", 4) == 0) { - string k(key + 4); - size_t i = 0; - while (i != string::npos) { - string ext; - size_t j = k.find(',', i); - if (j != string::npos) { - ext = k.substr(i, j - i); - i = j + 1; - } else { - ext = k.substr(i); - i = string::npos; - } - if (ext.length() > 0) { -#ifdef DEBUG - std::cerr << "Extension: " << ext << " - " << val << std::endl; -#endif - exts_[ext.c_str()] = val; - } - } - } else { - vals_[key] = val; - } - -#ifdef DEBUG - std::cerr << "Key: " << key << ", val: " << vals_[key] << std::endl; -#endif - } - - return true; -} - -string Prefs::process(const std::string& cmd) const -{ - string::size_type i = cmd.find("${"); - string::size_type j = cmd.find("}", i); - - if (i == string::npos || j == string::npos) { - return cmd; - } - - string val; - if (!get_string(cmd.substr(i + 2, j - i - 2), val)) return cmd; - string ret(cmd); - ret.replace(i, j - i + 1, val); - return process(ret); -} - -bool Prefs::get_string_list(const string& key, list& val) const -{ - string sval; - if (get_string(key, sval)) { - const char *q, *p = sval.c_str(); - for (q = sval.c_str(); *q; ++q) { - if (::isspace(*q)) { - string s(p, q - p); - val.push_back(s); - while (*q && ::isspace(*q)) ++q; - p = q; - } - } - if (p != q) { - string s(p, q - p); - val.push_back(s); - } - return (val.size() > 0); - } - return false; -} - diff --git a/src/prefs.h b/src/prefs.h deleted file mode 100644 index fb4250d..0000000 --- a/src/prefs.h +++ /dev/null @@ -1,45 +0,0 @@ -/***************************************************************************** - * $Id: prefs.h,v 1.6 2002/08/16 10:30:18 mishoo Exp $ - * Copyright (C) 2000, Mishoo - * Author: Mihai Bazon Email: mishoo@fenrir.infoiasi.ro - * - * Distributed under the terms of the GNU General Public License. You are - * free to use/modify/distribute this program as long as you comply to the - * terms of the GNU General Public License, version 2 or above, at your - * option, and provided that this copyright notice remains intact. - *****************************************************************************/ - - -#ifndef __PREFS_H__ -#define __PREFS_H__ - -#include "ci_string.h" -#include -#include - -class Prefs -{ - public: - typedef std::map CONFIG; - typedef std::pair PAIR; - - private: - CONFIG vals_; - CONFIG exts_; - - bool init(const std::string& file_name); - string process(const std::string& cmd) const; - - public: - Prefs(); - ~Prefs(); - - bool get_string(const std::string& key, std::string& val) const; - bool get_int(const std::string& key, int& val) const; - bool get_string_list(const std::string& ket, std::list& val) const; - bool get_ext_handler(const std::string& ext, std::string& val) const; -}; - -extern Prefs configuration; - -#endif /* __PREFS_H__ */