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__ */