diff options
Diffstat (limited to 'src/main.cc')
-rw-r--r-- | src/main.cc | 669 |
1 files changed, 669 insertions, 0 deletions
diff --git a/src/main.cc b/src/main.cc new file mode 100644 index 0000000..dc4b034 --- /dev/null +++ b/src/main.cc @@ -0,0 +1,669 @@ +/***************************************************************************** + * $Id: main.cc,v 1.26 2003/11/16 10:55:07 andreas99 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 <gtk/gtk.h> +#include <gdk/gdkkeysyms.h> + +#include <string> +#include <iostream> +#include <sstream> +#include <vector> +#include <algorithm> +#include <iterator> +#include <popt.h> + +using namespace std; + +#ifdef MTRACE +#include <mcheck.h> +#endif + +#include <unistd.h> +#include <errno.h> + +#include "gtkcompletionline.h" +#include "prefs.h" + +// defined in gtkcompletionline.cc +int get_words(GtkCompletionLine *object, vector<string>& words); +string quote_string(const string& str); + +struct gigi +{ + GtkWidget *w1; + GtkWidget *w2; +}; + +/// BEGIN: TIMEOUT MANAGEMENT + +static gint search_off_timeout(struct gigi *g); + +static guint g_search_off_timeout_id = 0; + +static void remove_search_off_timeout() +{ + if (g_search_off_timeout_id) { + gtk_timeout_remove(g_search_off_timeout_id); + g_search_off_timeout_id = 0; + } +} + +static void add_search_off_timeout(guint32 timeout, struct gigi *g, GtkFunction func = 0) +{ + remove_search_off_timeout(); + if (!func) + func = GtkFunction(search_off_timeout); + g_search_off_timeout_id = gtk_timeout_add(timeout, func, g); +} + +/// END: TIMEOUT MANAGEMENT + +GtkStyle* style_normal(GtkWidget *w) +{ + static GtkStyle *style; + + if (!style) { + style = gtk_style_copy(gtk_widget_get_style(w)); + style->fg[GTK_STATE_NORMAL] = (GdkColor){0, 0x0000, 0x0000, 0x0000}; + gtk_style_ref(style); + } + return style; +} + +GtkStyle* style_notfound(GtkWidget *w) +{ + static GtkStyle *style; + + if (!style) { + style = gtk_style_copy(gtk_widget_get_style(w)); + style->fg[GTK_STATE_NORMAL] = (GdkColor){0, 0xFFFF, 0x0000, 0x0000}; + gtk_style_ref(style); + } + return style; +} + +GtkStyle* style_notunique(GtkWidget *w) +{ + static GtkStyle *style; + + if (!style) { + style = gtk_style_copy(gtk_widget_get_style(w)); + style->fg[GTK_STATE_NORMAL] = (GdkColor){0, 0x0000, 0x0000, 0xFFFF}; + gtk_style_ref(style); + } + return style; +} + +GtkStyle* style_unique(GtkWidget *w) +{ + static GtkStyle *style; + + if (!style) { + style = gtk_style_copy(gtk_widget_get_style(w)); + style->fg[GTK_STATE_NORMAL] = (GdkColor){0, 0x0000, 0xFFFF, 0x0000}; + gtk_style_ref(style); + } + return style; +} + +static void +run_with_system(const std::string& command, struct gigi* g) +{ + string cmd(command); + cmd += '&'; + int ret = system(cmd.c_str()); +#ifdef DEBUG + cerr << "System exit code: " << ret << endl; +#endif + if (ret != -1) + gtk_main_quit(); + else { + string error("ERROR: "); + error += strerror(errno); +#ifdef DEBUG + cerr << error << endl; +#endif + gtk_label_set_text(GTK_LABEL(g->w1), error.c_str()); + gtk_widget_set_style(g->w1, style_notfound(g->w1)); + add_search_off_timeout(2000, g); + } +} + +#ifdef USE_SYSTEM + +// this version uses the "system" libc function to execute commands. +static void +run_the_command(const std::string& command, struct gigi* g) +{ + run_with_system(command, g); +} + +#else + +// a more elaborate function to avoid system.. though I think that most will +// prefer the above one. I don't even remember why I coded this... but let it +// be there. +static void +run_the_command(const std::string& command, struct gigi* g) +{ + string prog; + std::vector<char*> argv; + + string cmd = command + ' '; + istringstream iss(cmd); +#ifdef DEBUG + cerr << cmd << endl; +#endif + char what_quote = '"'; + enum TYPE_CONTEXT { + CT_NORMAL = 0, + CT_QUOTE, + CT_ESCAPE + }; + TYPE_CONTEXT context = CT_NORMAL; + TYPE_CONTEXT save_context = CT_NORMAL; + string tmp; + char c; + + while (!iss.eof()) { + c = (char)iss.get(); + if (iss.eof()) { + break; + } + + switch (c) { + case '\\': + if (context != CT_ESCAPE) { + save_context = context; + context = CT_ESCAPE; + } else { + goto ordinary; + } + break; + + case '\'': + case '"': + if (context == CT_ESCAPE) { + goto ordinary; + } else { + if (context == CT_QUOTE) { + if (what_quote == c) { + context = CT_NORMAL; + } else { + goto ordinary; + } + } else { + context = CT_QUOTE; + what_quote = c; + } + } + break; + + case ' ': + if (context == CT_ESCAPE || context == CT_QUOTE) { + goto ordinary; + } else if (!tmp.empty()) { + if (prog.empty()) { + prog = tmp; + } + char *p = (char*)malloc(tmp.length() + 1); + memcpy(p, tmp.c_str(), tmp.length() + 1); + argv.push_back(p); + tmp.clear(); + } + break; + + ordinary: + default: + if (context == CT_ESCAPE) { + context = save_context; + tmp += c; + } else if (context == CT_QUOTE) { + tmp += c; + } else if (c != ' ') { + tmp += c; + } + } + } + argv.push_back(NULL); + +#ifdef DEBUG + for (vector<char*>::iterator i = argv.begin(); i != argv.end(); ++i) { + if (*i) { + cerr << "'" << *i << "'" << endl; + } + } +#endif + + execvp(prog.c_str(), (char**)&(*argv.begin())); + string error("ERROR: "); + error += strerror(errno); +#ifdef DEBUG + cerr << error << endl; +#endif + + gtk_label_set_text(GTK_LABEL(g->w1), error.c_str()); + gtk_widget_set_style(g->w1, style_notfound(g->w1)); + add_search_off_timeout(2000, g); +} +#endif + +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, GtkFunction(search_off_timeout), g); + } else { + search_off_timeout(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; + + 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"; + } else { + tmp = term; + } + } + +#ifdef DEBUG + cerr << tmp << endl; +#endif + + cl->hist->append(command.c_str()); + cl->hist->sync_the_file(); + run_with_system(tmp.c_str(), g); +} + +static gint +search_off_timeout(struct gigi *g) +{ + gtk_label_set_text(GTK_LABEL(g->w1), "Run program:"); + gtk_widget_set_style(g->w1, style_normal(g->w1)); + gtk_widget_hide(g->w2); + return FALSE; +} + +static void +on_compline_unique(GtkCompletionLine *cl, struct gigi *g) +{ + gtk_label_set_text(GTK_LABEL(g->w1), "unique"); + gtk_widget_set_style(g->w1, style_unique(g->w1)); + add_search_off_timeout(1000, g); +} + +static void +on_compline_notunique(GtkCompletionLine *cl, struct gigi *g) +{ + gtk_label_set_text(GTK_LABEL(g->w1), "not unique"); + gtk_widget_set_style(g->w1, style_notunique(g->w1)); + add_search_off_timeout(1000, g); +} + +static void +on_compline_incomplete(GtkCompletionLine *cl, struct gigi *g) +{ + gtk_label_set_text(GTK_LABEL(g->w1), "not found"); + gtk_widget_set_style(g->w1, style_notfound(g->w1)); + add_search_off_timeout(1000, g); +} + +static void +on_search_mode(GtkCompletionLine *cl, struct gigi *g) +{ + if (cl->hist_search_mode != GCL_SEARCH_OFF) { + 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()); + } else { + gtk_widget_hide(g->w2); + gtk_label_set_text(GTK_LABEL(g->w1), "Search OFF"); + add_search_off_timeout(1000, g); + } +} + +static void +on_search_letter(GtkCompletionLine *cl, GtkWidget *label) +{ + gtk_label_set_text(GTK_LABEL(label), cl->hist_word->c_str()); +} + +static gint +search_fail_timeout(struct gigi *g) +{ + gtk_label_set_text(GTK_LABEL(g->w1), "Search:"); + gtk_widget_set_style(g->w2, style_notunique(g->w2)); + + return FALSE; +} + +static void +on_search_not_found(GtkCompletionLine *cl, struct gigi *g) +{ + gtk_label_set_text(GTK_LABEL(g->w1), "Not Found!"); + gtk_widget_set_style(g->w2, style_notfound(g->w2)); + add_search_off_timeout(1000, g, GtkFunction(search_fail_timeout)); +} + +static bool +url_check(GtkCompletionLine *cl, struct gigi *g) +{ + string text(g_locale_from_utf8 (gtk_entry_get_text(GTK_ENTRY(cl)), + -1, + NULL, + NULL, + NULL)); + + string::size_type i; + string::size_type sp; + + 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; + } else { + gtk_label_set_text(GTK_LABEL(g->w1), + (string("No URL handler for [") + + url_type + "]").c_str()); + gtk_widget_set_style(g->w1, style_notfound(g->w1)); + add_search_off_timeout(1000, g); + return true; + } + } + + return false; +} + +static bool +ext_check(GtkCompletionLine *cl, struct gigi *g) +{ + vector<string> words; + get_words(cl, words); + vector<string>::const_iterator + i = words.begin(), + i_end = words.end(); + + while (i != i_end) { + const string& w = quote_string(*i++); + 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; + } + } + } + // FIXME: for now we check only one entry + break; + } + + return false; +} + +static void +on_compline_activated(GtkCompletionLine *cl, struct gigi *g) +{ + if (url_check(cl, g)) + return; + if (ext_check(cl, g)) + return; + + string command = g_locale_from_utf8 (gtk_entry_get_text(GTK_ENTRY(cl)), + -1, + NULL, + NULL, + 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<string> 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<string>(cerr, "\n")); + cerr << "---" << std::endl; +#endif + list<string>::const_iterator w = + std::find(term_progs.begin(), term_progs.end(), progname); + if (w != term_progs.end()) { + on_compline_runwithterm(cl, g); + return; + } + } + cl->hist->append(command.c_str()); + cl->hist->sync_the_file(); + run_the_command(command, g); + } +} + +int main(int argc, char **argv) +{ + GtkWidget *win; + GtkWidget *compline; + GtkWidget *label_search; + struct gigi g; + +#ifdef MTRACE + mtrace(); +#endif + + gtk_init(&argc, &argv); + + win = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_widget_realize(win); + gdk_window_set_decorations(win->window, GDK_DECOR_BORDER); + gtk_widget_set_name(win, "Msh_Run_Window"); + gtk_window_set_title(GTK_WINDOW(win), "Execute program feat. completion"); + gtk_window_set_policy(GTK_WINDOW(win), FALSE, FALSE, TRUE); + // gtk_window_set_position(GTK_WINDOW(win), GTK_WIN_POS_CENTER); + gtk_container_set_border_width(GTK_CONTAINER(win), 4); + gtk_signal_connect(GTK_OBJECT(win), "destroy", + GTK_SIGNAL_FUNC(gtk_main_quit), NULL); + + GtkWidget *hbox = gtk_vbox_new(FALSE, 2); + gtk_widget_show(hbox); + gtk_container_add(GTK_CONTAINER(win), hbox); + + GtkWidget *hhbox = gtk_hbox_new(FALSE, 2); + gtk_widget_show(hhbox); + gtk_box_pack_start(GTK_BOX(hbox), hhbox, FALSE, FALSE, 0); + + GtkWidget *label = gtk_label_new("Run program:"); + gtk_widget_show(label); + gtk_misc_set_alignment(GTK_MISC(label), 0.0, 1.0); + gtk_misc_set_padding(GTK_MISC(label), 10, 0); + gtk_box_pack_start(GTK_BOX(hhbox), label, FALSE, FALSE, 0); + + label_search = gtk_label_new(""); + gtk_widget_show(label_search); + gtk_misc_set_alignment(GTK_MISC(label_search), 1.0, 0.5); + gtk_misc_set_padding(GTK_MISC(label_search), 10, 0); + gtk_box_pack_start(GTK_BOX(hhbox), label_search, TRUE, TRUE, 0); + + compline = gtk_completion_line_new(); + gtk_widget_set_name(compline, "Msh_Run_Compline"); + int prefs_width; + if (!configuration.get_int("Width", prefs_width)) + prefs_width = 500; + + // don't show files starting with "." by default + if (!configuration.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)) + ((GtkCompletionLine*)compline)->tabtimeout = tmp; + } + + g.w1 = label; + g.w2 = label_search; + + gtk_widget_set_usize(compline, prefs_width, -2); + gtk_signal_connect(GTK_OBJECT(compline), "cancel", + GTK_SIGNAL_FUNC(gtk_main_quit), NULL); + gtk_signal_connect(GTK_OBJECT(compline), "activate", + GTK_SIGNAL_FUNC(on_compline_activated), &g); + gtk_signal_connect(GTK_OBJECT(compline), "runwithterm", + GTK_SIGNAL_FUNC(on_compline_runwithterm), &g); + + gtk_signal_connect(GTK_OBJECT(compline), "unique", + GTK_SIGNAL_FUNC(on_compline_unique), &g); + gtk_signal_connect(GTK_OBJECT(compline), "notunique", + GTK_SIGNAL_FUNC(on_compline_notunique), &g); + gtk_signal_connect(GTK_OBJECT(compline), "incomplete", + GTK_SIGNAL_FUNC(on_compline_incomplete), &g); + + gtk_signal_connect(GTK_OBJECT(compline), "search_mode", + GTK_SIGNAL_FUNC(on_search_mode), &g); + gtk_signal_connect(GTK_OBJECT(compline), "search_not_found", + GTK_SIGNAL_FUNC(on_search_not_found), &g); + gtk_signal_connect(GTK_OBJECT(compline), "search_letter", + GTK_SIGNAL_FUNC(on_search_letter), label_search); + + gtk_signal_connect(GTK_OBJECT(compline), "ext_handler", + GTK_SIGNAL_FUNC(on_ext_handler), &g); + gtk_widget_show(compline); + + int shows_last_history_item; + if (!configuration.get_int("ShowLast", shows_last_history_item)) { + shows_last_history_item = 0; + } + if (shows_last_history_item) { + gtk_completion_line_last_history_item(GTK_COMPLETION_LINE(compline)); + } + + gtk_box_pack_start(GTK_BOX(hbox), compline, TRUE, TRUE, 0); + + int prefs_top = 80; + int prefs_left = 100; + configuration.get_int("Top", prefs_top); + configuration.get_int("Left", prefs_left); + + // parse commandline options + gboolean geo_parsed; + char geo_option[30] = ""; + char *geoptr; + poptContext context; + int option; + + geoptr = geo_option; + + struct poptOption options[] = { + { "geometry", 'g', POPT_ARG_STRING | POPT_ARGFLAG_ONEDASH, + &geoptr, 0, "This option specifies the initial " + "size and location of the window.", NULL }, + POPT_AUTOHELP + { NULL, '\0', 0, NULL, 0 } + }; + + context = poptGetContext("popt1", argc, (const char**) argv, options, 0); + option = poptGetNextOpt (context); + + if (strcmp (geoptr, "")) + { + geo_parsed = gtk_window_parse_geometry (GTK_WINDOW (win), + geoptr); + } + else + { + gtk_widget_set_uposition(win, prefs_left, prefs_top); + } + + gtk_widget_show(win); + + gtk_window_set_focus(GTK_WINDOW(win), compline); + + gtk_main(); +} + +// Local Variables: *** +// mode: c++ *** +// c-basic-offset: 2 *** +// End: *** |