diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 17 | ||||
-rw-r--r-- | src/Makefile.in | 303 | ||||
-rw-r--r-- | src/ci_string.h | 41 | ||||
-rw-r--r-- | src/gtkcompletionline.cc | 1187 | ||||
-rw-r--r-- | src/gtkcompletionline.h | 90 | ||||
-rw-r--r-- | src/history.cc | 167 | ||||
-rw-r--r-- | src/history.h | 71 | ||||
-rw-r--r-- | src/main.cc | 669 | ||||
-rw-r--r-- | src/main.h | 13 | ||||
-rw-r--r-- | src/prefs.cc | 177 | ||||
-rw-r--r-- | src/prefs.h | 45 |
11 files changed, 2780 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..168be3c --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,17 @@ +## Process this file with automake to produce Makefile.in + +INCLUDES = \ + @GTK_CFLAGS@ @STLPORT_CXXFLAGS@ + +bin_PROGRAMS = gmrun + +gmrun_SOURCES = \ + gtkcompletionline.cc gtkcompletionline.h \ + history.cc history.h \ + main.cc main.h \ + prefs.cc prefs.h ci_string.h + +# gmrun_LDFLAGS = -s + +gmrun_LDADD = @GTK_LIBS@ @STLPORT_LDFLAGS@ + diff --git a/src/Makefile.in b/src/Makefile.in new file mode 100644 index 0000000..7e46523 --- /dev/null +++ b/src/Makefile.in @@ -0,0 +1,303 @@ +# Makefile.in generated automatically by automake 1.4-p6 from Makefile.am + +# Copyright (C) 1994, 1995-8, 1999, 2001 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include + +DESTDIR = + +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ + +top_builddir = .. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ $(AM_INSTALL_PROGRAM_FLAGS) +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +transform = @program_transform_name@ + +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +CC = @CC@ +CXX = @CXX@ +GLIB_CFLAGS = @GLIB_CFLAGS@ +GLIB_LIBS = @GLIB_LIBS@ +GTK_CFLAGS = @GTK_CFLAGS@ +GTK_LIBS = @GTK_LIBS@ +MAKEINFO = @MAKEINFO@ +PACKAGE = @PACKAGE@ +PACKAGE_DATA_DIR = @PACKAGE_DATA_DIR@ +PKG_CONFIG = @PKG_CONFIG@ +PREFIX = @PREFIX@ +STLPORT_CXXFLAGS = @STLPORT_CXXFLAGS@ +STLPORT_INCDIR = @STLPORT_INCDIR@ +STLPORT_INCLUDES = @STLPORT_INCLUDES@ +STLPORT_LDFLAGS = @STLPORT_LDFLAGS@ +STLPORT_LIBDIR = @STLPORT_LIBDIR@ +VERSION = @VERSION@ + +INCLUDES = @GTK_CFLAGS@ @STLPORT_CXXFLAGS@ + + +bin_PROGRAMS = gmrun + +gmrun_SOURCES = gtkcompletionline.cc gtkcompletionline.h history.cc history.h main.cc main.h prefs.cc prefs.h ci_string.h + + +# gmrun_LDFLAGS = -s + +gmrun_LDADD = @GTK_LIBS@ @STLPORT_LDFLAGS@ +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_HEADER = ../config.h +CONFIG_CLEAN_FILES = +PROGRAMS = $(bin_PROGRAMS) + + +DEFS = @DEFS@ -I. -I$(srcdir) -I.. +CPPFLAGS = @CPPFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ +gmrun_OBJECTS = gtkcompletionline.o history.o main.o prefs.o +gmrun_DEPENDENCIES = +gmrun_LDFLAGS = +CXXFLAGS = @CXXFLAGS@ +CXXCOMPILE = $(CXX) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(LDFLAGS) -o $@ +CFLAGS = @CFLAGS@ +COMPILE = $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ +DIST_COMMON = Makefile.am Makefile.in + + +DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST) + +TAR = tar +GZIP_ENV = --best +SOURCES = $(gmrun_SOURCES) +OBJECTS = $(gmrun_OBJECTS) + +all: all-redirect +.SUFFIXES: +.SUFFIXES: .S .c .cc .o .s +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && $(AUTOMAKE) --gnu --include-deps src/Makefile + +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) \ + && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status + + +mostlyclean-binPROGRAMS: + +clean-binPROGRAMS: + -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS) + +distclean-binPROGRAMS: + +maintainer-clean-binPROGRAMS: + +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + $(mkinstalldirs) $(DESTDIR)$(bindir) + @list='$(bin_PROGRAMS)'; for p in $$list; do \ + if test -f $$p; then \ + echo " $(INSTALL_PROGRAM) $$p $(DESTDIR)$(bindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`"; \ + $(INSTALL_PROGRAM) $$p $(DESTDIR)$(bindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`; \ + else :; fi; \ + done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + list='$(bin_PROGRAMS)'; for p in $$list; do \ + rm -f $(DESTDIR)$(bindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`; \ + done + +.c.o: + $(COMPILE) -c $< + +.s.o: + $(COMPILE) -c $< + +.S.o: + $(COMPILE) -c $< + +mostlyclean-compile: + -rm -f *.o core *.core + +clean-compile: + +distclean-compile: + -rm -f *.tab.c + +maintainer-clean-compile: + +gmrun: $(gmrun_OBJECTS) $(gmrun_DEPENDENCIES) + @rm -f gmrun + $(CXXLINK) $(gmrun_LDFLAGS) $(gmrun_OBJECTS) $(gmrun_LDADD) $(LIBS) +.cc.o: + $(CXXCOMPILE) -c $< + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) + list='$(SOURCES) $(HEADERS)'; \ + unique=`for i in $$list; do echo $$i; done | \ + awk ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + here=`pwd` && cd $(srcdir) \ + && mkid -f$$here/ID $$unique $(LISP) + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS)'; \ + unique=`for i in $$list; do echo $$i; done | \ + awk ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \ + || (cd $(srcdir) && etags $(ETAGS_ARGS) $$tags $$unique $(LISP) -o $$here/TAGS) + +mostlyclean-tags: + +clean-tags: + +distclean-tags: + -rm -f TAGS ID + +maintainer-clean-tags: + +distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir) + +subdir = src + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + d=$(srcdir); \ + if test -d $$d/$$file; then \ + cp -pr $$d/$$file $(distdir)/$$file; \ + else \ + test -f $(distdir)/$$file \ + || ln $$d/$$file $(distdir)/$$file 2> /dev/null \ + || cp -p $$d/$$file $(distdir)/$$file || :; \ + fi; \ + done +gtkcompletionline.o: gtkcompletionline.cc gtkcompletionline.h history.h +history.o: history.cc history.h prefs.h ci_string.h +main.o: main.cc gtkcompletionline.h history.h prefs.h ci_string.h +prefs.o: prefs.cc prefs.h ci_string.h ../config.h + +info-am: +info: info-am +dvi-am: +dvi: dvi-am +check-am: all-am +check: check-am +installcheck-am: +installcheck: installcheck-am +install-exec-am: install-binPROGRAMS +install-exec: install-exec-am + +install-data-am: +install-data: install-data-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am +install: install-am +uninstall-am: uninstall-binPROGRAMS +uninstall: uninstall-am +all-am: Makefile $(PROGRAMS) +all-redirect: all-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) AM_INSTALL_PROGRAM_FLAGS=-s install +installdirs: + $(mkinstalldirs) $(DESTDIR)$(bindir) + + +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) + -rm -f config.cache config.log stamp-h stamp-h[0-9]* + +maintainer-clean-generic: +mostlyclean-am: mostlyclean-binPROGRAMS mostlyclean-compile \ + mostlyclean-tags mostlyclean-generic + +mostlyclean: mostlyclean-am + +clean-am: clean-binPROGRAMS clean-compile clean-tags clean-generic \ + mostlyclean-am + +clean: clean-am + +distclean-am: distclean-binPROGRAMS distclean-compile distclean-tags \ + distclean-generic clean-am + +distclean: distclean-am + +maintainer-clean-am: maintainer-clean-binPROGRAMS \ + maintainer-clean-compile maintainer-clean-tags \ + maintainer-clean-generic distclean-am + @echo "This command is intended for maintainers to use;" + @echo "it deletes files that may require special tools to rebuild." + +maintainer-clean: maintainer-clean-am + +.PHONY: mostlyclean-binPROGRAMS distclean-binPROGRAMS clean-binPROGRAMS \ +maintainer-clean-binPROGRAMS uninstall-binPROGRAMS install-binPROGRAMS \ +mostlyclean-compile distclean-compile clean-compile \ +maintainer-clean-compile tags mostlyclean-tags distclean-tags \ +clean-tags maintainer-clean-tags distdir info-am info dvi-am dvi check \ +check-am installcheck-am installcheck install-exec-am install-exec \ +install-data-am install-data install-am install uninstall-am uninstall \ +all-redirect all-am all installdirs mostlyclean-generic \ +distclean-generic clean-generic maintainer-clean-generic clean \ +mostlyclean distclean maintainer-clean + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/ci_string.h b/src/ci_string.h new file mode 100644 index 0000000..aed3001 --- /dev/null +++ b/src/ci_string.h @@ -0,0 +1,41 @@ +// $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 <string> +#include <ctype.h> + +struct ci_char_traits : public std::char_traits<char> +{ + 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<char, ci_char_traits> ci_string; + +#endif // __CI_STRING_H__ diff --git a/src/gtkcompletionline.cc b/src/gtkcompletionline.cc new file mode 100644 index 0000000..374deee --- /dev/null +++ b/src/gtkcompletionline.cc @@ -0,0 +1,1187 @@ +/***************************************************************************** + * $Id: gtkcompletionline.cc,v 1.33 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 <gdk/gdkkeysyms.h> +#include <gtk/gtksignal.h> +#include <gtk/gtkclist.h> +#include <gtk/gtkwindow.h> +#include <gtk/gtkscrolledwindow.h> +#include <gtk/gtkmain.h> + +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <dirent.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> + +#include <iostream> +#include <set> +#include <sstream> +#include <string> +#include <vector> +using namespace std; + +#include "gtkcompletionline.h" + +static int on_row_selected_handler = 0; +static int on_key_press_handler = 0; + +/* GLOBALS */ + +/* signals */ +enum { + UNIQUE, + NOTUNIQUE, + INCOMPLETE, + RUNWITHTERM, + SEARCH_MODE, + SEARCH_LETTER, + SEARCH_NOT_FOUND, + EXT_HANDLER, + CANCEL, + LAST_SIGNAL +}; + +#define GEN_COMPLETION_OK 1 +#define GEN_CANT_COMPLETE 2 +#define GEN_NOT_UNIQUE 3 + +static guint gtk_completion_line_signals[LAST_SIGNAL]; + +typedef set<string> StrSet; +typedef vector<string> StrList; + +static StrSet path; +static StrSet execs; +static StrSet dirlist; +static string prefix; +static int g_show_dot_files; + +/* callbacks */ +static void gtk_completion_line_class_init(GtkCompletionLineClass *klass); +static void gtk_completion_line_init(GtkCompletionLine *object); + +static gboolean +on_key_press(GtkCompletionLine *cl, GdkEventKey *event, gpointer data); + +/* get_type */ +guint gtk_completion_line_get_type(void) +{ + static guint type = 0; + if (type == 0) + { + GtkTypeInfo type_info = + { + "GtkCompletionLine", + sizeof(GtkCompletionLine), + sizeof(GtkCompletionLineClass), + (GtkClassInitFunc)gtk_completion_line_class_init, + (GtkObjectInitFunc)gtk_completion_line_init, + /*(GtkArgSetFunc)*/NULL /* reserved */, + /*(GtkArgGetFunc)*/NULL /* reserved */ + }; + type = gtk_type_unique(gtk_entry_get_type(), &type_info); + } + return type; +} + +/* class_init */ +static void +gtk_completion_line_class_init(GtkCompletionLineClass *klass) +{ + GtkObjectClass *object_class; + + object_class = (GtkObjectClass*)klass; + + gtk_completion_line_signals[UNIQUE] = + gtk_signal_new("unique", + GTK_RUN_FIRST, G_TYPE_FROM_CLASS(object_class), + GTK_SIGNAL_OFFSET(GtkCompletionLineClass, + unique), + gtk_signal_default_marshaller, GTK_TYPE_NONE, 0); + + gtk_completion_line_signals[NOTUNIQUE] = + gtk_signal_new("notunique", + GTK_RUN_FIRST, G_TYPE_FROM_CLASS(object_class), + GTK_SIGNAL_OFFSET(GtkCompletionLineClass, + notunique), + gtk_signal_default_marshaller, GTK_TYPE_NONE, 0); + + gtk_completion_line_signals[INCOMPLETE] = + gtk_signal_new("incomplete", + GTK_RUN_FIRST, G_TYPE_FROM_CLASS(object_class), + GTK_SIGNAL_OFFSET(GtkCompletionLineClass, + incomplete), + gtk_signal_default_marshaller, GTK_TYPE_NONE, 0); + + gtk_completion_line_signals[RUNWITHTERM] = + gtk_signal_new("runwithterm", + GTK_RUN_FIRST, G_TYPE_FROM_CLASS(object_class), + GTK_SIGNAL_OFFSET(GtkCompletionLineClass, + runwithterm), + gtk_signal_default_marshaller, GTK_TYPE_NONE, 0); + + gtk_completion_line_signals[SEARCH_MODE] = + gtk_signal_new("search_mode", + GTK_RUN_FIRST, G_TYPE_FROM_CLASS(object_class), + GTK_SIGNAL_OFFSET(GtkCompletionLineClass, + search_mode), + gtk_signal_default_marshaller, GTK_TYPE_NONE, 0); + + gtk_completion_line_signals[SEARCH_NOT_FOUND] = + gtk_signal_new("search_not_found", + GTK_RUN_FIRST, G_TYPE_FROM_CLASS(object_class), + GTK_SIGNAL_OFFSET(GtkCompletionLineClass, + search_not_found), + gtk_signal_default_marshaller, GTK_TYPE_NONE, 0); + + gtk_completion_line_signals[SEARCH_LETTER] = + gtk_signal_new("search_letter", + GTK_RUN_FIRST, G_TYPE_FROM_CLASS(object_class), + GTK_SIGNAL_OFFSET(GtkCompletionLineClass, + search_letter), + gtk_signal_default_marshaller, GTK_TYPE_NONE, 0); + + gtk_completion_line_signals[EXT_HANDLER] = + gtk_signal_new("ext_handler", + GTK_RUN_FIRST, G_TYPE_FROM_CLASS(object_class), + GTK_SIGNAL_OFFSET(GtkCompletionLineClass, + ext_handler), + gtk_marshal_NONE__POINTER, GTK_TYPE_NONE, 1, + GTK_TYPE_POINTER); + + gtk_completion_line_signals[CANCEL] = + gtk_signal_new("cancel", + GTK_RUN_FIRST, G_TYPE_FROM_CLASS(object_class), + GTK_SIGNAL_OFFSET(GtkCompletionLineClass, + ext_handler), + gtk_marshal_NONE__POINTER, GTK_TYPE_NONE, 1, + GTK_TYPE_POINTER); + + //gtk_object_class_add_signals(object_class, + // gtk_completion_line_signals, LAST_SIGNAL); + + klass->unique = NULL; + klass->notunique = NULL; + klass->incomplete = NULL; + klass->runwithterm = NULL; + klass->search_mode = NULL; + klass->search_letter = NULL; + klass->search_not_found = NULL; + klass->ext_handler = NULL; + klass->cancel = NULL; +} + +/* init */ +static void +gtk_completion_line_init(GtkCompletionLine *object) +{ + /* Add object initialization / creation stuff here */ + + object->where = NULL; + object->cmpl = NULL; + object->win_compl = NULL; + object->list_compl = NULL; + object->hist_search_mode = GCL_SEARCH_OFF; + object->hist_word = new string; + object->tabtimeout = 0; + object->show_dot_files = 0; + + on_key_press_handler = + gtk_signal_connect(GTK_OBJECT(object), "key_press_event", + GTK_SIGNAL_FUNC(on_key_press), NULL); + gtk_signal_connect(GTK_OBJECT(object), "key_release_event", + GTK_SIGNAL_FUNC(on_key_press), NULL); + + object->hist = new HistoryFile(); + + object->first_key = 1; +} + +void gtk_completion_line_last_history_item(GtkCompletionLine* object) { + const char *last_item = object->hist->last_item(); + 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_select_region(GTK_ENTRY(object), 0, strlen(txt)); + } +} + +string quote_string(const string& str) +{ + string res; + const char* i = str.c_str(); + while (*i) { + char c = *i++; + switch (c) { + case ' ': + res += '\\'; + default: + res += c; + } + } + return res; +} + +static void +get_token(istream& is, string& s) +{ + s.clear(); + bool escaped = false; + while (!is.eof()) { + char c = is.get(); + if (is.eof()) + break; + if (escaped) { + s += c; + escaped = false; + } else if (c == '\\') { + // s += c; + escaped = true; + } else if (::isspace(c)) { + while (::isspace(c) && !is.eof()) c = is.get(); + if (!is.eof()) + is.unget(); + break; + } else { + s += c; + } + } +} + +int +get_words(GtkCompletionLine *object, vector<string>& words) +{ + string content(gtk_entry_get_text(GTK_ENTRY(object))); + int pos_in_text = gtk_editable_get_position(GTK_EDITABLE(object)); + int pos = 0; + { + string::iterator i = content.begin() + pos_in_text; + if (i != content.end()) + content.insert(i, ' '); + } + istringstream ss(content); + + while (!ss.eof()) { + string s; + // ss >> s; + get_token(ss, s); + words.push_back(s); + if (ss.eof()) break; + if (ss.tellg() < pos_in_text && ss.tellg() >= 0) + ++pos; + } + + return pos; +} + +int +set_words(GtkCompletionLine *object, const vector<string>& words, int pos = -1) +{ + ostringstream ss; + if (pos == -1) + pos = words.size() - 1; + int where = 0; + + vector<string>::const_iterator + i = words.begin(), + i_end = words.end(); + + while (i != i_end) { + ss << quote_string(*i++); + if (i != i_end) + ss << ' '; + if (!pos && !where) + where = ss.tellp(); + else + --pos; + } + ss << ends; + + if (words.size() == 1) { + const string& s = words.back(); + size_t pos = s.rfind('.'); + if (pos != string::npos) + gtk_signal_emit_by_name( + GTK_OBJECT(object), "ext_handler", s.substr(pos + 1).c_str()); + else + gtk_signal_emit_by_name(GTK_OBJECT(object), "ext_handler", NULL); + } + + gtk_entry_set_text(GTK_ENTRY(object), + g_locale_to_utf8 (ss.str().c_str(), -1, NULL, NULL, NULL)); + gtk_editable_set_position(GTK_EDITABLE(object), where); + return where; +} + +static void +generate_path() +{ + char *path_cstr = (char*)getenv("PATH"); + + istringstream path_ss(path_cstr); + string tmp; + + path.clear(); + while (!path_ss.eof()) { + tmp = ""; + do { + char c; + c = path_ss.get(); + if (c == ':' || path_ss.eof()) break; + else tmp += c; + } while (true); + if (tmp.length() != 0) + path.insert(tmp); + } +} + +static int +select_executables_only(const struct dirent* dent) +{ + int len = strlen(dent->d_name); + int lenp = prefix.length(); + + if (dent->d_name[0] == '.') { + if (!g_show_dot_files) + return 0; + if (dent->d_name[1] == '\0') + return 0; + if ((dent->d_name[1] == '.') && (dent->d_name[2] == '\0')) + return 0; + } + if (dent->d_name[len - 1] == '~') + return 0; + if (lenp == 0) + return 1; + if (lenp > len) + return 0; + + if (strncmp(dent->d_name, prefix.c_str(), lenp) == 0) + return 1; + + return 0; +} + +int my_alphasort(const void* va, const void* vb) { + const struct dirent** a = (const struct dirent**)va; + const struct dirent** b = (const struct dirent**)vb; + + const char* s1 = (*a)->d_name; + const char* s2 = (*b)->d_name; + + int l1 = strlen(s1); + int l2 = strlen(s2); + int result = strcmp(s1, s2); + + if (result == 0) return 0; + + if (l1 < l2) { + int res2 = strncmp(s1, s2, l1); + if (res2 == 0) return -1; + } else { + int res2 = strncmp(s1, s2, l2); + if (res2 == 0) return 1; + } + + return result; +} + +static void +generate_execs() +{ + execs.clear(); + + for (StrSet::iterator i = path.begin(); i != path.end(); i++) { + struct dirent **eps; + int n = scandir(i->c_str(), &eps, select_executables_only, my_alphasort); + if (n >= 0) { + for (int j = 0; j < n; j++) { + execs.insert(eps[j]->d_name); + free(eps[j]); + } + free(eps); + } + } +} + +static int +generate_completion_from_execs(GtkCompletionLine *object) +{ + g_list_foreach(object->cmpl, (GFunc)g_string_free, NULL); + g_list_free(object->cmpl); + object->cmpl = NULL; + + for (StrSet::const_iterator i = execs.begin(); i != execs.end(); i++) { + GString *the_fucking_gstring = g_string_new(i->c_str()); + object->cmpl = g_list_append(object->cmpl, the_fucking_gstring); + } + + return 0; +} + +static string +get_common_part(const char *p1, const char *p2) +{ + string ret; + + while (*p1 == *p2 && *p1 != '\0' && *p2 != '\0') { + ret += *p1; + p1++; + p2++; + } + + return ret; +} + +static int +complete_common(GtkCompletionLine *object) +{ + GList *l; + GList *ls = object->cmpl; + vector<string> words; + int pos = get_words(object, words); + words[pos] = ((GString*)ls->data)->str; + + ls = g_list_next(ls); + while (ls != NULL) { + words[pos] = get_common_part(words[pos].c_str(), + ((GString*)ls->data)->str); + ls = g_list_next(ls); + } + + set_words(object, words, pos); + + ls = object->cmpl; + l = ls; +/* + if (words[pos] == ((GString*)(l->data))->str) { + ls = g_list_remove_link(ls, l); + ls = g_list_append(ls, l->data); + g_list_free_1(l); + object->cmpl = ls; + } +*/ + return 0; +} + +static int +generate_dirlist(const char *what) +{ + char *str = strdup(what); + char *p = str + 1; + char *filename = str; + string dest("/"); + int n; + + while (*p != '\0') { + dest += *p; + if (*p == '/') { + DIR* dir = opendir(dest.c_str()); + if (!dir) + goto dirty; + closedir(dir); + filename = p; + } + ++p; + } + + *filename = '\0'; + filename++; + dest = str; + dest += '/'; + + dirlist.clear(); + struct dirent **eps; + prefix = filename; + n = scandir(dest.c_str(), &eps, select_executables_only, my_alphasort); + if (n >= 0) { + for (int j = 0; j < n; j++) { + { + string foo(dest); + foo += eps[j]->d_name; + struct stat filestatus; + stat(foo.c_str(), &filestatus); + if (S_ISDIR(filestatus.st_mode)) foo += '/'; + dirlist.insert(foo); + } + free(eps[j]); + } + free(eps); + } + + free(str); + return GEN_COMPLETION_OK; + + dirty: + free(str); + return GEN_CANT_COMPLETE; +} + +static int +generate_completion_from_dirlist(GtkCompletionLine *object) +{ + g_list_foreach(object->cmpl, (GFunc)g_string_free, NULL); + g_list_free(object->cmpl); + object->cmpl = NULL; + + for (StrSet::const_iterator i = dirlist.begin(); i != dirlist.end(); i++) { + GString *the_fucking_gstring = g_string_new(i->c_str()); + object->cmpl = g_list_append(object->cmpl, the_fucking_gstring); + } + + return 0; +} + +static int +parse_tilda(GtkCompletionLine *object) +{ + string text = gtk_entry_get_text(GTK_ENTRY(object)); + gint where = (gint)text.find("~"); + if (where != string::npos) { + if ((where > 0) && (text[where - 1] != ' ')) + return 0; + if (where < text.size() - 1 && text[where + 1] != '/') { + // FIXME: Parse another user's home + } else { + string home = g_get_home_dir(); + size_t i = home.length() - 1; + while ((i >= 0) && (home[i] == '/')) + home.erase(i--); + gtk_editable_insert_text(GTK_EDITABLE(object), home.c_str(), home.length(), &where); + gtk_editable_delete_text(GTK_EDITABLE(object), where, where + 1); + } + } + + return 0; +} + +static void +complete_from_list(GtkCompletionLine *object) +{ + parse_tilda(object); + vector<string> words; + int pos = get_words(object, words); + + prefix = words[pos]; + + if (object->win_compl != NULL) { + object->where = (GList*)gtk_clist_get_row_data( + GTK_CLIST(object->list_compl), object->list_compl_items_where); + words[pos] = ((GString*)object->where->data)->str; + int current_pos = set_words(object, words, pos); + gtk_entry_select_region(GTK_ENTRY(object), + object->pos_in_text, current_pos); + int &item = object->list_compl_items_where; + gtk_clist_select_row(GTK_CLIST(object->list_compl), item, 0); + gtk_clist_moveto(GTK_CLIST(object->list_compl), item, 0, 0.5, 0.0); + } else { + words[pos] = ((GString*)object->where->data)->str; + object->pos_in_text = gtk_editable_get_position(GTK_EDITABLE(object)); + int current_pos = set_words(object, words, pos); + gtk_entry_select_region(GTK_ENTRY(object), + object->pos_in_text, current_pos); + object->where = g_list_next(object->where); + } +} + +static void +on_row_selected(GtkWidget *ls, gint row, gint col, GdkEvent *ev, gpointer data) +{ + GtkCompletionLine *cl = GTK_COMPLETION_LINE(data); + + cl->list_compl_items_where = row; + gtk_signal_handler_block(GTK_OBJECT(cl->list_compl), + on_row_selected_handler); + complete_from_list(cl); + gtk_signal_handler_unblock(GTK_OBJECT(cl->list_compl), + on_row_selected_handler); +} + +/* + +static void +select_appropiate(GtkCompletionLine *object) +{ + for (int i = 0; i < object->list_compl_nr_rows; ++i) { + char *text; + gtk_clist_get_text(GTK_CLIST(object->list_compl), i, 0, &text); + if (strncmp(prefix.c_str(), text, prefix.length())) { + gtk_signal_handler_block(GTK_OBJECT(object->list_compl), + on_row_selected_handler); + gtk_clist_select_row(GTK_CLIST(object->list_compl), i, 0); + object->list_compl_items_where = i; + gtk_signal_handler_unblock(GTK_OBJECT(object->list_compl), + on_row_selected_handler); + break; + } + } +} + +static void +get_prefix(GtkCompletionLine *object) +{ + parse_tilda(object); + vector<string> words; + int pos = get_words(object, words); + prefix = words[pos]; +} + +*/ + +static int +complete_line(GtkCompletionLine *object) +{ + parse_tilda(object); + vector<string> words; + int pos = get_words(object, words); + prefix = words[pos]; + + g_show_dot_files = object->show_dot_files; + if (prefix[0] != '/') { + if (object->where == NULL) { + generate_path(); + generate_execs(); + generate_completion_from_execs(object); + object->where = NULL; + } + } else if (object->where == NULL) { + generate_dirlist(prefix.c_str()); + generate_completion_from_dirlist(object); + object->where = NULL; + } + + if (object->cmpl != NULL) { + complete_common(object); + object->where = object->cmpl; + } + + // FUCK C! C++ Rules! + if (object->where != NULL) { + if (object->win_compl != NULL) { + int &item = object->list_compl_items_where; + ++item; + if (item >= object->list_compl_nr_rows) + item = object->list_compl_nr_rows - 1; + } + complete_from_list(object); + } else if (object->cmpl != NULL) { + complete_common(object); + object->where = object->cmpl; + } + + GList *ls = object->cmpl; + + if (g_list_length(ls) == 1) { + gtk_signal_emit_by_name(GTK_OBJECT(object), "unique"); + return GEN_COMPLETION_OK; + } else if (g_list_length(ls) == 0 || ls == NULL) { + gtk_signal_emit_by_name(GTK_OBJECT(object), "incomplete"); + return GEN_CANT_COMPLETE; + } else if (g_list_length(ls) > 1) { + gtk_signal_emit_by_name(GTK_OBJECT(object), "notunique"); + + vector<string> words; + int pos = get_words(object, words); + + if (words[pos] == ((GString*)ls->data)->str) { + + if (object->win_compl == NULL) { + object->win_compl = gtk_window_new(GTK_WINDOW_POPUP); + gtk_widget_set_name(object->win_compl, "Msh_Run_Window"); + + /*gtk_window_set_position(GTK_WINDOW(object->win_compl), + GTK_WIN_POS_MOUSE);*/ + + gtk_window_set_policy(GTK_WINDOW(object->win_compl), + FALSE, FALSE, TRUE); + + object->list_compl = gtk_clist_new(1); + + on_row_selected_handler = + gtk_signal_connect(GTK_OBJECT(object->list_compl), "select_row", + GTK_SIGNAL_FUNC(on_row_selected), object); + + gtk_signal_handler_block(GTK_OBJECT(object->list_compl), + on_row_selected_handler); + + GList *p = ls; + object->list_compl_nr_rows = 0; + while (p) { + char *tmp[2]; + tmp[0] = ((GString*)p->data)->str; + tmp[1] = NULL; + int row = gtk_clist_append(GTK_CLIST(object->list_compl), tmp); + gtk_clist_set_row_data(GTK_CLIST(object->list_compl), row, p); + object->list_compl_nr_rows++; + p = g_list_next(p); + } + + GtkWidget *scroll = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scroll), GTK_SHADOW_OUT); + gtk_widget_show(scroll); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW (scroll), + GTK_POLICY_NEVER, + GTK_POLICY_AUTOMATIC); + + gtk_container_set_border_width(GTK_CONTAINER(object->list_compl), 2); + gtk_container_add(GTK_CONTAINER (scroll), object->list_compl); + + object->list_compl_items_where = 0; + + gtk_widget_show(object->list_compl); + int w = gtk_clist_optimal_column_width(GTK_CLIST(object->list_compl), 0); + gtk_widget_set_usize(scroll, w + 40, 150); + + gtk_container_add(GTK_CONTAINER(object->win_compl), scroll); + + GdkWindow *top = gtk_widget_get_parent_window(GTK_WIDGET(object)); + int x, y; + gdk_window_get_position(top, &x, &y); + x += GTK_WIDGET(object)->allocation.x; + y += GTK_WIDGET(object)->allocation.y + + GTK_WIDGET(object)->allocation.height; + + // gtk_widget_popup(object->win_compl, x, y); + gtk_window_move(GTK_WINDOW(object->win_compl), x, y); + gtk_widget_show(object->win_compl); + + gtk_clist_select_row(GTK_CLIST(object->list_compl), + object->list_compl_items_where, 0); + + gtk_signal_handler_unblock(GTK_OBJECT(object->list_compl), + on_row_selected_handler); + } + + return GEN_COMPLETION_OK; + } + return GEN_NOT_UNIQUE; + } + + return GEN_COMPLETION_OK; +} + +GtkWidget * +gtk_completion_line_new() +{ + return GTK_WIDGET(gtk_type_new(gtk_completion_line_get_type())); +} + +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 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 +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) { + gtk_signal_emit_by_name(GTK_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)); + gtk_entry_select_region(GTK_ENTRY(cl), + i, i + cl->hist_word->length()); + gtk_signal_emit_by_name(GTK_OBJECT(cl), "search_letter"); + return 1; + } + } + histext = cl->hist->prev_to_first(); + if (histext == NULL) { + gtk_signal_emit_by_name(GTK_OBJECT(cl), "search_not_found"); + break; + } + } + } else { + gtk_entry_select_region(GTK_ENTRY(cl), 0, 0); + gtk_signal_emit_by_name(GTK_OBJECT(cl), "search_letter"); + } + + return 0; +} + +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) { + gtk_signal_emit_by_name(GTK_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)); + gtk_entry_select_region(GTK_ENTRY(cl), + i, i + cl->hist_word->length()); + gtk_signal_emit_by_name(GTK_OBJECT(cl), "search_letter"); + return 1; + } + } + histext = cl->hist->next_to_last(); + if (histext == NULL) { + gtk_signal_emit_by_name(GTK_OBJECT(cl), "search_not_found"); + break; + } + } + } else { + gtk_entry_select_region(GTK_ENTRY(cl), 0, 0); + gtk_signal_emit_by_name(GTK_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 int +inverse_search_history(GtkCompletionLine* cl, bool avance, bool begin) +{ + switch (cl->hist_search_mode) { + case GCL_SEARCH_FWD: + return search_back_history(cl, avance, begin); + + case GCL_SEARCH_REW: + case GCL_SEARCH_BEG: + 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)); + gtk_entry_select_region(GTK_ENTRY(cl), pos, pos); + cl->hist_search_mode = GCL_SEARCH_OFF; + gtk_signal_emit_by_name(GTK_OBJECT(cl), "search_mode"); + cl->hist->reset_position(); +} + +#define STOP_PRESS \ + (gtk_signal_emit_stop_by_name(GTK_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 gint tab_pressed(GtkCompletionLine* cl) +{ + if (MODE_SRC) + search_off(cl); + complete_line(cl); + return FALSE; +} + +static void +clear_selection(GtkCompletionLine* cl) +{ + int pos = gtk_editable_get_position(GTK_EDITABLE(cl)); + gtk_editable_select_region(GTK_EDITABLE(cl), pos, pos); +} + +static gboolean +on_key_press(GtkCompletionLine *cl, GdkEventKey *event, gpointer data) +{ + static gint tt_id = -1; + + switch (event->type) { + + case GDK_KEY_PRESS: + + + switch (event->keyval) { + + case GDK_Control_R: + case GDK_Control_L: + case GDK_Shift_R: + case GDK_Shift_L: + case GDK_Alt_R: + case GDK_Alt_L: + break; + + case GDK_Tab: + if (tt_id != -1) { + gtk_timeout_remove(tt_id); + tt_id = -1; + } + tab_pressed(cl); + STOP_PRESS; + return TRUE; + + case GDK_Up: + if (cl->win_compl != NULL) { + int &item = cl->list_compl_items_where; + item--; + if (item < 0) { + item = 0; + } else { + complete_from_list(cl); + } + } else { + up_history(cl); + } + if (MODE_SRC) { + search_off(cl); + } + STOP_PRESS; + return TRUE; + + case GDK_space: + { + cl->first_key = 0; + bool search = MODE_SRC; + if (search) + search_off(cl); + if (cl->win_compl != NULL) { + gtk_widget_destroy(cl->win_compl); + cl->win_compl = NULL; + if (!search) { + int pos = gtk_editable_get_position(GTK_EDITABLE(cl)); + gtk_entry_select_region(GTK_ENTRY(cl), pos, pos); + } + } + } + return FALSE; + + case GDK_Down: + if (cl->win_compl != NULL) { + int &item = cl->list_compl_items_where; + item++; + if (item >= cl->list_compl_nr_rows) { + item = cl->list_compl_nr_rows - 1; + } else { + complete_from_list(cl); + } + } else { + down_history(cl); + } + if (MODE_SRC) { + search_off(cl); + } + STOP_PRESS; + return TRUE; + + case GDK_Return: + if (cl->win_compl != NULL) { + gtk_widget_destroy(cl->win_compl); + cl->win_compl = NULL; + } + if (event->state & GDK_CONTROL_MASK) { + gtk_signal_emit_by_name(GTK_OBJECT(cl), "runwithterm"); + } else { + gtk_signal_emit_by_name(GTK_OBJECT(cl), "activate"); + } + STOP_PRESS; + return TRUE; + + case GDK_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(); + gtk_signal_emit_by_name(GTK_OBJECT(cl), "search_mode"); + STOP_PRESS; + return true; + } else goto ordinary; + + case GDK_R: + case GDK_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(); + gtk_signal_emit_by_name(GTK_OBJECT(cl), "search_mode"); + } + STOP_PRESS; + return TRUE; + } else goto ordinary; + + case GDK_S: + case GDK_s: + if (event->state & GDK_CONTROL_MASK) { + if (MODE_SRC) { + search_forward_history(cl, true, MODE_BEG); + } else { + cl->hist_search_mode = GCL_SEARCH_FWD; + cl->hist_word->clear(); + cl->hist->reset_position(); + gtk_signal_emit_by_name(GTK_OBJECT(cl), "search_mode"); + } + STOP_PRESS; + return TRUE; + } else goto ordinary; + + case GDK_BackSpace: + if (MODE_SRC) { + if (!cl->hist_word->empty()) { + cl->hist_word->erase(cl->hist_word->length() - 1); + gtk_signal_emit_by_name(GTK_OBJECT(cl), "search_letter"); + } + STOP_PRESS; + return TRUE; + } + return FALSE; + + case GDK_Home: + case GDK_End: + clear_selection(cl); + goto ordinary; + + case GDK_Escape: + if (MODE_SRC) { + search_off(cl); + } else if (cl->win_compl != NULL) { + gtk_widget_destroy(cl->win_compl); + cl->win_compl = NULL; + } else { + // user cancelled + gtk_signal_emit_by_name(GTK_OBJECT(cl), "cancel"); + } + STOP_PRESS; + return TRUE; + + case GDK_G: + case GDK_g: + if (event->state & GDK_CONTROL_MASK) { + search_off(cl); + if (MODE_SRC) + STOP_PRESS; + return TRUE; + } else goto ordinary; + + case GDK_E: + case GDK_e: + if (event->state & GDK_CONTROL_MASK) { + search_off(cl); + if (MODE_SRC) + clear_selection(cl); + } + goto ordinary; + + ordinary: + default: + cl->first_key = 0; + if (cl->win_compl != NULL) { + gtk_widget_destroy(cl->win_compl); + cl->win_compl = NULL; + } + cl->where = NULL; + if (MODE_SRC) { + 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; + } else + search_off(cl); + } + if (cl->tabtimeout != 0) { + if (tt_id != -1) { + gtk_timeout_remove(tt_id); + tt_id = -1; + } + if (::isprint(*event->string)) + tt_id = gtk_timeout_add(cl->tabtimeout, + GtkFunction(tab_pressed), cl); + } + break; + } + break; + + default: + break; + } + 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 *** +// End: *** diff --git a/src/gtkcompletionline.h b/src/gtkcompletionline.h new file mode 100644 index 0000000..5e14cd7 --- /dev/null +++ b/src/gtkcompletionline.h @@ -0,0 +1,90 @@ +/***************************************************************************** + * $Id: gtkcompletionline.h,v 1.12 2003/11/16 10:43:32 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. + *****************************************************************************/ + + +#ifndef __GTKCOMPLETIONLINE_H__ +#define __GTKCOMPLETIONLINE_H__ + +#include <gtk/gtkentry.h> + +#include <string> + +#include "history.h" + +extern "C++" { + +#define GTK_COMPLETION_LINE(obj) \ + GTK_CHECK_CAST(obj, gtk_completion_line_get_type(), GtkCompletionLine) +#define GTK_COMPLETION_LINE_CLASS(klass) \ + GTK_CHECK_CLASS_CAST(klass, gtk_completion_line_get_type(), GtkCompletionLineClass) +#define IS_GTK_COMPLETION_LINE(obj) \ + GTK_CHECK_TYPE(obj, gtk_completion_line_get_type()) + + 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; + GtkWidget *win_compl; + GtkWidget *list_compl; + int list_compl_items_where; + int list_compl_nr_rows; + int pos_in_text; + + GList *cmpl; + GList *where; + + HistoryFile *hist; + GCL_SEARCH_MODE hist_search_mode; + std::string *hist_word; + + int first_key; + int tabtimeout; + int show_dot_files; + }; + + struct _GtkCompletionLineClass + { + GtkEntryClass parent_class; + /* add your CLASS members here */ + + void (* unique)(GtkCompletionLine *cl); + void (* notunique)(GtkCompletionLine *cl); + void (* incomplete)(GtkCompletionLine *cl); + void (* runwithterm)(GtkCompletionLine *cl); + void (* search_mode)(GtkCompletionLine *cl); + void (* search_letter)(GtkCompletionLine *cl); + void (* search_not_found)(GtkCompletionLine *cl); + void (* ext_handler)(GtkCompletionLine *cl); + void (* cancel)(GtkCompletionLine *cl); + }; + + guint gtk_completion_line_get_type(void); + GtkWidget *gtk_completion_line_new(); + + void gtk_completion_line_last_history_item(GtkCompletionLine*); +} + +#endif /* __GTKCOMPLETIONLINE_H__ */ + +// Local Variables: *** +// mode: c++ *** +// c-basic-offset: 2 *** +// End: *** diff --git a/src/history.cc b/src/history.cc new file mode 100644 index 0000000..61afba0 --- /dev/null +++ b/src/history.cc @@ -0,0 +1,167 @@ +/***************************************************************************** + * $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 <glib.h> +#include <cstdlib> +#include <cstdio> +#include <iostream> +#include <fstream> +#include <algorithm> +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; + + while (!f.eof()) { + char line_text[256]; + string line_str; + + f.getline(line_text, sizeof(line_text)); + if (*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 < 0 || 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 (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 (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 new file mode 100644 index 0000000..9f1d4b9 --- /dev/null +++ b/src/history.h @@ -0,0 +1,71 @@ +/***************************************************************************** + * $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 + * + * 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 __HISTORY_H__ +#define __HISTORY_H__ + +#include <vector> +#include <string> +using namespace std; + +class HistoryFile +{ + protected: + int m_file_entries; + string m_filename; + string m_default; + bool m_default_set; + int m_current; + + typedef vector<string> StrArray; + StrArray history; + + public: + HistoryFile(); + ~HistoryFile(); + + void append(const char *entry); + void set_default(const char *defstr); + void clear_default(); + + void reset_position(); + + const char * operator [] (size_t index); + + const char * prev(); + const char * next(); + + const char * prev_to_first(); + const char * next_to_last(); + + void sync_the_file(); + + inline const char* last_item() { + return history.empty() ? 0 : history.back().c_str(); + } + + inline const char* first_item() { + return history.empty() ? 0 : history.front().c_str(); + } + + 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; + }; +}; + +#endif // __HISTORY_H__ 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: *** diff --git a/src/main.h b/src/main.h new file mode 100644 index 0000000..e02475b --- /dev/null +++ b/src/main.h @@ -0,0 +1,13 @@ +/***************************************************************************** + * $Id: main.h,v 1.2 2001/05/16 14:39: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. + *****************************************************************************/ + + + diff --git a/src/prefs.cc b/src/prefs.cc new file mode 100644 index 0000000..f9e0485 --- /dev/null +++ b/src/prefs.cc @@ -0,0 +1,177 @@ +/***************************************************************************** + * $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 <fstream> +#include <iostream> +#include <stdio.h> + +#include <list> + +#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 = PACKAGE_DATA_DIR"/"; + 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<string>& 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 new file mode 100644 index 0000000..0732a51 --- /dev/null +++ b/src/prefs.h @@ -0,0 +1,45 @@ +/***************************************************************************** + * $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 <map> +#include <list> + +class Prefs +{ + public: + typedef std::map<ci_string, std::string> CONFIG; + typedef std::pair<ci_string, std::string> 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<string>& val) const; + bool get_ext_handler(const std::string& ext, std::string& val) const; +}; + +extern Prefs configuration; + +#endif /* __PREFS_H__ */ |