diff --git a/projects/lookuplet/Makefile.am b/projects/lookuplet/Makefile.am index 20228d0b..d6ef333d 100644 --- a/projects/lookuplet/Makefile.am +++ b/projects/lookuplet/Makefile.am @@ -1,29 +1,4 @@ # -# $Id: Makefile.am,v 1.1 2000/12/10 23:38:39 mdb Exp $ +# $Id: Makefile.am,v 1.2 2001/02/24 02:35:20 mdb Exp $ SUBDIRS = src - -# Edit these target directories if they differ on your system. If you -# change anything, run "autoremake", then continue with installation as -# usual. - -BINDIR = /usr/bin -DESKTOPDIR = /usr/share/applets/Network -CORBADIR = /etc/CORBA/servers -PIXMAPDIR = /usr/share/pixmaps - -install: - $(INSTALL_PROGRAM) src/lookuplet $(BINDIR)/lookuplet - $(INSTALL_PROGRAM) src/lookuplet.gnorba $(CORBADIR)/lookuplet.gnorba - $(INSTALL_PROGRAM) src/lookuplet.desktop $(DESKTOPDIR)/lookuplet.desktop - $(INSTALL_PROGRAM) src/gnome-lookuplet.xpm $(PIXMAPDIR)/gnome-lookuplet.xpm - -uninstall: - rm \ - $(BINDIR)/lookuplet \ - $(CORBADIR)/lookuplet.gnorba \ - $(DESKTOPDIR)/lookuplet.desktop \ - $(PIXMAPDIR)/gnome-lookuplet.xpm - -clean: - rm src/lookuplet diff --git a/projects/lookuplet/src/Makefile.am b/projects/lookuplet/src/Makefile.am index 8da120ec..53dbf65f 100644 --- a/projects/lookuplet/src/Makefile.am +++ b/projects/lookuplet/src/Makefile.am @@ -1,21 +1,39 @@ # -# $Id: Makefile.am,v 1.1 2000/12/10 23:38:39 mdb Exp $ +# $Id: Makefile.am,v 1.2 2001/02/24 02:35:20 mdb Exp $ -AUTOMAKE_OPTIONS = no-dependencies - -INCLUDES = \ - $(GNOME_INCLUDEDIR) +INCLUDES = \ + -I. \ + -I$(srcdir) \ + -I$(top_srcdir)/intl -I$(top_builddir)/intl \ + -DGNOMELOCALEDIR=\""$(datadir)/locale"\" \ + -I$(includedir) $(GNOME_INCLUDEDIR) bin_PROGRAMS = lookuplet -bin_DIR = ../install.d/ lookuplet_SOURCES = \ - launcher.c \ lookuplet.c \ - querybox.c + launcher.c \ + querybox.c \ + preferences.c \ + binding.c \ + keysym-util.c lookuplet_LDADD = \ $(GNOME_LIBDIR) \ - $(GTK_LIBS) \ - $(GNOMEGNORBA_LIBS) \ - -lpanel_applet + $(GNOME_APPLETS_LIBS) \ + $(INTLLIBS) + +# currently the installation of these bitmaps wouldn't be necessary +bitmapsdir = $(datadir)/pixmaps +bitmaps_DATA = gnome-lookuplet.xpm + +EXTRA_DIST = \ + lookuplet.desktop \ + lookuplet.gnorba \ + $(bitmaps_DATA) + +sysdir = $(datadir)/applets/Network +sys_DATA = lookuplet.desktop + +gnorbadir = $(sysconfdir)/CORBA/servers +gnorba_DATA = lookuplet.gnorba diff --git a/projects/lookuplet/src/c/binding.c b/projects/lookuplet/src/c/binding.c new file mode 100644 index 00000000..0f112511 --- /dev/null +++ b/projects/lookuplet/src/c/binding.c @@ -0,0 +1,193 @@ +/** + * $Id: binding.c,v 1.1 2001/02/24 02:35:20 mdb Exp $ + */ + +#include +#include "binding.h" + +static gchar* DEFAULT_BINDING_KEYS[] = +{ + "Control-m", + "Control-g", + "Control-d", + "Control-Shift-d", + "Control-f", +}; + +static LkBindingType DEFAULT_BINDING_TYPES[] = +{ + URL, + URL, + EXEC, + URL, + URL, +}; + +static gchar* DEFAULT_BINDING_NAMES[] = +{ + "MetaCrawler search", + "Google search", + "Dictionary lookup", + "Debian package search", + "Freshmeat search", +}; + +static gchar* DEFAULT_BINDING_ARGUMENTS[] = +{ + "http://search.metacrawler.com/crawler?general=%U", + "http://www.google.com/search?client=googlet&q=%U", + "gdict -a %T", + "http://cgi.debian.org/cgi-bin/search_contents.pl?word=%U&case=insensitive&version=unstable&arch=i386&directories=yes", + "http://freshmeat.net/search/?q=%U", +}; + +#define DEFAULT_BINDING_COUNT \ +(sizeof(DEFAULT_BINDING_TYPES)/sizeof(LkBindingType)) + +LkBinding* +lk_binding_new (void) +{ + LkBinding* binding = (LkBinding*)g_malloc(sizeof(LkBinding)); + binding->key = g_strdup(""); + binding->name = g_strdup(""); + binding->argument = g_strdup(""); + return binding; +} + +LkBinding* +lk_binding_new_with_values (const gchar* key, LkBindingType type, + const gchar* name, const gchar* argument) +{ + LkBinding* binding = (LkBinding*)g_malloc(sizeof(LkBinding)); + binding->key = g_strdup(key); + binding->type = type; + binding->name = g_strdup(name); + binding->argument = g_strdup(argument); + return binding; +} + +void +lk_binding_destroy (LkBinding* binding) +{ + g_free(binding->key); + g_free(binding->name); + g_free(binding->argument); + g_free(binding); +} + +void +lk_binding_init (LkBinding* binding, const gchar* key, + LkBindingType type, const gchar* name, const gchar* argument) +{ + g_free(binding->key); + binding->key = g_strdup(key); + binding->type = type; + g_free(binding->name); + binding->name = g_strdup(name); + g_free(binding->argument); + binding->argument = g_strdup(argument); +} + +static gchar* +config_get_string (const gchar* field, gint index) +{ + gchar gkey[100]; + gchar* value; + + /* construct the path */ + g_snprintf(gkey, sizeof(gkey), + "lookuplet/bindings/%s_%.2u", field, index); + + /* fetch the value and deal with non-existent values */ + value = gnome_config_get_string(gkey); + if (value == NULL) { + value = g_strdup(""); + } + + return value; +} + +void +lk_binding_load_bindings (GPtrArray* bindings) +{ + gchar gkey[100]; + int count, i; + + gnome_config_push_prefix("/lookuplet/"); + count = gnome_config_get_int("lookuplet/bindings/count"); + + /* if we have bindings defined, load those */ + if (count > 0) { + for (i = 0; i < count; i++) { + gchar* key, *name, *argument; + LkBindingType type = URL; + + key = config_get_string("key", i); + g_snprintf(gkey, sizeof(gkey), "lookuplet/bindings/type_%.2u", i); + type = (LkBindingType)gnome_config_get_int(gkey); + name = config_get_string("name", i); + argument = config_get_string("arg", i); + + g_ptr_array_add(bindings, lk_binding_new_with_values( + key, type, name, argument)); + + g_free(key); + g_free(name); + g_free(argument); + } + + } else { + /* otherwise use the default bindings */ + for (i = 0; i < DEFAULT_BINDING_COUNT; i++) { + LkBinding* binding = + lk_binding_new_with_values(DEFAULT_BINDING_KEYS[i], + DEFAULT_BINDING_TYPES[i], + DEFAULT_BINDING_NAMES[i], + DEFAULT_BINDING_ARGUMENTS[i]); + g_ptr_array_add(bindings, binding); + } + } + + gnome_config_pop_prefix(); +} + +void +lk_binding_save_bindings (GPtrArray* bindings) +{ + gchar gkey[100]; + int i; + + gnome_config_push_prefix("/lookuplet/"); + + /* save the total number of bindings */ + gnome_config_set_int("lookuplet/bindings/count", bindings->len); + + /* save each binding */ + for (i = 0; i < bindings->len; i++) { + LkBinding* binding = LK_BINDING(g_ptr_array_index(bindings, i)); + + g_snprintf(gkey, sizeof(gkey), + "lookuplet/bindings/key_%.2u", i); + gnome_config_set_string(gkey, binding->key); + + g_snprintf(gkey, sizeof(gkey), + "lookuplet/bindings/type_%.2u", i); + gnome_config_set_int(gkey, binding->type); + + g_snprintf(gkey, sizeof(gkey), + "lookuplet/bindings/name_%.2u", i); + gnome_config_set_string(gkey, binding->name); + + g_snprintf(gkey, sizeof(gkey), + "lookuplet/bindings/arg_%.2u", i); + gnome_config_set_string(gkey, binding->argument); + } + + /* write our new settings to disk */ + gnome_config_sync(); + + /* cargo cult programming! i dunno, the other guy did this too */ + gnome_config_drop_all(); + + gnome_config_pop_prefix(); +} diff --git a/projects/lookuplet/src/c/binding.h b/projects/lookuplet/src/c/binding.h new file mode 100644 index 00000000..a4447200 --- /dev/null +++ b/projects/lookuplet/src/c/binding.h @@ -0,0 +1,51 @@ +/** + * $Id: binding.h,v 1.1 2001/02/24 02:35:20 mdb Exp $ + */ + +#ifndef _BINDING_H_ +#define _BINDING_H_ + +#include + +#define MAX_BINDING_LENGTH 250 + +typedef enum _LkBindingType LkBindingType; +typedef struct _LkBinding LkBinding; + +enum _LkBindingType +{ + URL = 0, + EXEC = 1 +}; + +struct _LkBinding +{ + gchar* key; + LkBindingType type; + gchar* name; + gchar* argument; +}; + +#define LK_BINDING(b) ((LkBinding*)b) + +extern LkBinding* +lk_binding_new (void); + +extern LkBinding* +lk_binding_new_with_values (const gchar* key, LkBindingType type, + const gchar* name, const gchar* argument); + +extern void +lk_binding_destroy (LkBinding* binding); + +extern void +lk_binding_init (LkBinding* binding, const gchar* key, + LkBindingType type, const gchar* name, const gchar* argument); + +extern void +lk_binding_load_bindings (GPtrArray* bindings); + +extern void +lk_binding_save_bindings (GPtrArray* bindings); + +#endif /* _BINDING_H_ */ diff --git a/projects/lookuplet/src/c/keysym-util.c b/projects/lookuplet/src/c/keysym-util.c new file mode 100644 index 00000000..99f7ed57 --- /dev/null +++ b/projects/lookuplet/src/c/keysym-util.c @@ -0,0 +1,148 @@ +/** + * $Id: keysym-util.c,v 1.1 2001/02/24 02:35:20 mdb Exp $ + */ + +#include + +#include "keysym-util.h" + +static gboolean +string_empty (const char* string) +{ + return (string == NULL || + string[0] == '\0'); +} + +gboolean +convert_string_to_keysym_state (const char* string, + guint* keysym, + guint* state) +{ + char* s, *p; + + g_return_val_if_fail(keysym != NULL, FALSE); + g_return_val_if_fail(state != NULL, FALSE); + + *state = 0; + *keysym = 0; + + if (string_empty(string) || + strcmp(string, "Disabled") == 0 || + strcmp(string, _("Disabled")) == 0) { + return FALSE; + } + + s = g_strdup(string); + + gdk_error_trap_push(); + + p = strtok(s, "-"); + + while (p != NULL) { + if (strcmp(p, "Control")==0) { + *state |= GDK_CONTROL_MASK; + } else if (strcmp(p, "Lock")==0) { + *state |= GDK_LOCK_MASK; + } else if (strcmp(p, "Shift")==0) { + *state |= GDK_SHIFT_MASK; + } else if (strcmp(p, "Mod1")==0) { + *state |= GDK_MOD1_MASK; + } else if (strcmp(p, "Mod2")==0) { + *state |= GDK_MOD2_MASK; + } else if (strcmp(p, "Mod3")==0) { + *state |= GDK_MOD3_MASK; + } else if (strcmp(p, "Mod4")==0) { + *state |= GDK_MOD4_MASK; + } else if (strcmp(p, "Mod5")==0) { + *state |= GDK_MOD5_MASK; + } else { + *keysym = gdk_keyval_from_name(p); + if (*keysym == 0) { + gdk_flush(); + gdk_error_trap_pop(); + g_free(s); + return FALSE; + } + } + p = strtok(NULL, "-"); + } + + gdk_flush(); + gdk_error_trap_pop(); + g_free(s); + + return (*keysym != 0); +} + +char* +convert_keysym_state_to_string (guint keysym, + guint state) +{ + GString* gs; + char* sep = ""; + char* key; + + if (keysym == 0) { + return g_strdup(_("Disabled")); + } + + gdk_error_trap_push(); + key = gdk_keyval_name(keysym); + gdk_flush(); + gdk_error_trap_pop(); + + if (key == NULL) { + return NULL; + } + + gs = g_string_new(NULL); + + if (state & GDK_CONTROL_MASK) { + g_string_append(gs, "Control"); + sep = "-"; + } + if (state & GDK_LOCK_MASK) { + g_string_append(gs, sep); + g_string_append(gs, "Lock"); + sep = "-"; + } + if (state & GDK_SHIFT_MASK) { + g_string_append(gs, sep); + g_string_append(gs, "Shift"); + sep = "-"; + } + if (state & GDK_MOD1_MASK) { + g_string_append(gs, sep); + g_string_append(gs, "Mod1"); + sep = "-"; + } + if (state & GDK_MOD2_MASK) { + g_string_append(gs, sep); + g_string_append(gs, "Mod2"); + sep = "-"; + } + if (state & GDK_MOD3_MASK) { + g_string_append(gs, sep); + g_string_append(gs, "Mod3"); + sep = "-"; + } + if (state & GDK_MOD4_MASK) { + g_string_append(gs, sep); + g_string_append(gs, "Mod4"); + sep = "-"; + } + if (state & GDK_MOD5_MASK) { + g_string_append(gs, sep); + g_string_append(gs, "Mod5"); + sep = "-"; + } + + g_string_append(gs, sep); + g_string_append(gs, key); + + { + char *ret = gs->str; + g_string_free(gs, FALSE); + return ret; + } +} diff --git a/projects/lookuplet/src/c/keysym-util.h b/projects/lookuplet/src/c/keysym-util.h new file mode 100644 index 00000000..183031c7 --- /dev/null +++ b/projects/lookuplet/src/c/keysym-util.h @@ -0,0 +1,19 @@ +/** + * $Id: keysym-util.h,v 1.1 2001/02/24 02:35:20 mdb Exp $ + */ + +#ifndef _KEYSYM_UTIL_H_ +#define _KEYSYM_UTIL_H_ + +#include + +gboolean +convert_string_to_keysym_state (const char* string, + guint* keysym, + guint* state); + +char* +convert_keysym_state_to_string (guint keysym, + guint state); + +#endif /* _KEYSYM_UTIL_H_ */ diff --git a/projects/lookuplet/src/c/launcher.c b/projects/lookuplet/src/c/launcher.c index 17acd569..ef46e78a 100644 --- a/projects/lookuplet/src/c/launcher.c +++ b/projects/lookuplet/src/c/launcher.c @@ -1,5 +1,5 @@ /** - * $Id: launcher.c,v 1.1 2000/12/10 23:38:39 mdb Exp $ + * $Id: launcher.c,v 1.2 2001/02/24 02:35:20 mdb Exp $ */ #include @@ -11,62 +11,8 @@ #include "launcher.h" -#define DEFAULT_QUERY "http://search.metacrawler.com/crawler?general=%U" - -/** - * This hashtable stores the mappings from qualifier keys to URL templates - * for use when we launch a URL for a particular set of terms. - */ -static GHashTable* _qualmap; - -/** - * Removes and frees any previous entry registered with the supplied key - * before inserting the new key/value pair. - */ -static void -g_hash_table_insert_safe (GHashTable* table, gchar* key, gchar* value) -{ - gpointer oldkey, oldvalue; - - if (g_hash_table_lookup_extended(table, key, &oldkey, &oldvalue)) { - g_free(oldkey); - g_free(oldvalue); - } - - g_hash_table_insert(table, key, value); -} - -/** - * Loads up the mappings for the launcher from the configuration - * repository. - */ -void -lookuplet_launcher_init (void) -{ - _qualmap = g_hash_table_new(g_str_hash, g_str_equal); - - /* for now, just insert some bogus config parameters */ - g_hash_table_insert_safe(_qualmap, - g_strdup("Ctrl-s"), g_strdup(DEFAULT_QUERY)); -} - -static void -free_table_entry (gpointer key, gpointer value, gpointer rock) -{ - g_free(key); - g_free(value); -} - -/** - * Frees the mapping information used by the launcher. - */ -void -lookuplet_launcher_cleanup (void) -{ - g_hash_table_foreach(_qualmap, free_table_entry, NULL); - g_hash_table_destroy(_qualmap); - _qualmap = NULL; -} +#define DEFAULT_COMMAND "%T" +#define DEFAULT_TYPE URL #define ESC_CHAR '%' @@ -157,36 +103,47 @@ guint g_string_replace (GString* source, const gchar* before, * qualifier. */ gboolean -lookuplet_launcher_launch (const char* terms, const char* qualifier) +lk_launcher_launch (const LkBinding* binding, const gchar* terms) { - gchar* uterms; - gchar* template; + char cwdbuf[1024]; + GString* cmd; + gchar* uterms, *command; + LkBindingType type; - uterms = url_encode(terms); + /* figure out what we're supposed to do */ + if (binding == NULL) { + command = DEFAULT_COMMAND; + type = DEFAULT_TYPE; - /* if no qualifier was supplied, we use the default template; - * otherwise we get the template from the mapping table */ - if (qualifier == NULL) { - template = DEFAULT_QUERY; } else { - template = g_hash_table_lookup(_qualmap, qualifier); - } - - /* if we found a template, construct the actual URL from it by - * replacing instances of %T with the terms and %U with the URL - * encoded terms. */ - if (template != NULL) { - GString* url = g_string_new(template); - /* first replace %U because we know that won't result in any '%T's - * being inserted into the string because the url encoded terms - * encode all %s */ - g_string_replace(url, "%U", uterms); - g_string_replace(url, "%T", terms); - gnome_url_show(url->str); - g_string_free(url, TRUE); + command = binding->argument; + type = binding->type; } + /* construct the actual command from it by replacing instances of %T + * with the terms and %U with the URL encoded terms. */ + cmd = g_string_new(command); + /* first replace %U because we know that won't result in any '%T's + * being inserted into the string because the url encoded terms + * encode all %s */ + uterms = url_encode(terms); + g_string_replace(cmd, "%U", uterms); g_free(uterms); + g_string_replace(cmd, "%T", terms); + + /* now we either invoke a program or launch a URL */ + switch (type) { + case EXEC: + gnome_execute_shell(getcwd(cwdbuf, 1024), cmd->str); + break; + + default: + case URL: + gnome_url_show(cmd->str); + break; + } + + g_string_free(cmd, TRUE); return TRUE; } diff --git a/projects/lookuplet/src/c/launcher.h b/projects/lookuplet/src/c/launcher.h index d89b7911..1b9a8a43 100644 --- a/projects/lookuplet/src/c/launcher.h +++ b/projects/lookuplet/src/c/launcher.h @@ -1,5 +1,5 @@ /** - * $Id: launcher.h,v 1.1 2000/12/10 23:38:39 mdb Exp $ + * $Id: launcher.h,v 1.2 2001/02/24 02:35:20 mdb Exp $ */ #ifndef _LAUNCHER_H_ @@ -7,13 +7,9 @@ #include -extern void -lookuplet_launcher_init (void); - -extern void -lookuplet_launcher_cleanup (void); +#include "binding.h" extern gboolean -lookuplet_launcher_launch (const char* terms, const char* qualifier); +lk_launcher_launch (const LkBinding* binding, const gchar* terms); #endif /* _LAUNCHER_H_ */ diff --git a/projects/lookuplet/src/c/lookuplet.c b/projects/lookuplet/src/c/lookuplet.c index 4a6d9334..9d38b3d1 100644 --- a/projects/lookuplet/src/c/lookuplet.c +++ b/projects/lookuplet/src/c/lookuplet.c @@ -1,41 +1,53 @@ /** - * $Id: lookuplet.c,v 1.1 2000/12/10 23:38:39 mdb Exp $ + * $Id: lookuplet.c,v 1.2 2001/02/24 02:35:20 mdb Exp $ */ #include #include -#include +#include "entry.h" #include "querybox.h" +#include "preferences.h" + +static void +exit_lookuplet (GtkWidget* widget, gpointer data) +{ + gtk_main_quit(); +} int main (int argc, char** argv) { - GtkWidget* applet; + GtkWidget* window; GtkWidget* contents; /* initialize the i18n stuff */ bindtextdomain(PACKAGE, GNOMELOCALEDIR); textdomain(PACKAGE); - /* intialize, this will basically set up the applet, corba and - call gnome_init */ - applet_widget_init("lookuplet", NULL, argc, argv, NULL, 0, NULL); + /* initialize gtk */ + gtk_init(&argc, &argv); - /* create a new applet_widget */ - applet = applet_widget_new("lookuplet"); - if (applet == NULL) { - g_error("Can't create applet!\n"); - } + /* initialize our preferences */ + lk_prefs_init(); + + /* create a new window */ + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_signal_connect(GTK_OBJECT(window), "destroy", + GTK_SIGNAL_FUNC(exit_lookuplet), NULL); /* create our contents and set them up real proper like */ - contents = lookuplet_querybox_create(); - applet_widget_add(APPLET_WIDGET(applet), contents); - gtk_widget_show(contents); - gtk_widget_show(applet); + contents = lk_querybox_create(); + gtk_container_add(GTK_CONTAINER(window), contents); + gtk_container_set_border_width(GTK_CONTAINER(window), GNOME_PAD_SMALL); + gtk_widget_show_all(contents); + gtk_widget_show(window); - /* special corba main loop */ - applet_widget_gtk_main(); + /* initialize the querybox (which handles about everything) */ + lk_querybox_init(); + + /* main loop */ + gtk_main(); return 0; } diff --git a/projects/lookuplet/src/c/preferences.c b/projects/lookuplet/src/c/preferences.c new file mode 100644 index 00000000..6f2d4e75 --- /dev/null +++ b/projects/lookuplet/src/c/preferences.c @@ -0,0 +1,438 @@ +/** + * $Id: preferences.c,v 1.1 2001/02/24 02:35:20 mdb Exp $ + */ + +#include +#include + +#include "keysym-util.h" +#include "binding.h" +#include "preferences.h" + +static GtkWidget* _grabDialog; +static GtkWidget* _bindList; +static GtkWidget* _prefBox = NULL; +static GPtrArray* _bindings; + +static GtkWidget* _keyEntry; +static LkBindingType _type; +static GtkWidget* _nameEntry; +static GtkWidget* _argEntry; +static GtkWidget* _edit; + +static gint _selection = -1; + +static GdkFilterReturn +grab_key_filter (GdkXEvent* gdk_xevent, GdkEvent* event, gpointer data) +{ + XEvent* xevent = (XEvent*)gdk_xevent; + GtkEntry* entry; + char* key; + + /* skip non-keypress events */ + if (xevent->type != KeyPress && xevent->type != KeyRelease) { + return GDK_FILTER_CONTINUE; + } + + /* convert the keysym into a string and stick it into the text entry + * that the user would otherwise type into */ + key = convert_keysym_state_to_string(event->key.keyval, event->key.state); + entry = GTK_ENTRY(data); + gtk_entry_set_text(entry, key ? key : ""); + g_free(key); + + /* clean up after ourselves */ + gdk_keyboard_ungrab(GDK_CURRENT_TIME); + gtk_widget_destroy(_grabDialog); + _grabDialog = NULL; + gdk_window_remove_filter(GDK_ROOT_PARENT(), grab_key_filter, data); + + return GDK_FILTER_REMOVE; +} + +static void +grab_button_pressed (GtkButton* button, gpointer data) +{ + GtkWidget* frame; + GtkWidget* box; + GtkWidget* label; + + _grabDialog = gtk_window_new(GTK_WINDOW_POPUP); + + gdk_keyboard_grab(GDK_ROOT_PARENT(), TRUE, GDK_CURRENT_TIME); + gdk_window_add_filter(GDK_ROOT_PARENT(), grab_key_filter, data); + + gtk_window_set_policy(GTK_WINDOW(_grabDialog), FALSE, FALSE, TRUE); + gtk_window_set_position(GTK_WINDOW(_grabDialog), GTK_WIN_POS_CENTER); + gtk_window_set_modal(GTK_WINDOW(_grabDialog), TRUE); + + frame = gtk_frame_new(NULL); + gtk_container_add(GTK_CONTAINER(_grabDialog), frame); + + box = gtk_hbox_new(FALSE, 0); + gtk_container_set_border_width(GTK_CONTAINER(box), GNOME_PAD_BIG); + gtk_container_add(GTK_CONTAINER(frame), box); + + label = gtk_label_new(_("Press a key...")); + gtk_container_add(GTK_CONTAINER(box), label); + + gtk_widget_show_all(_grabDialog); +} + +static void +pop_down_edit_binding (GtkWidget* button, gpointer arg) +{ + _keyEntry = NULL; + _nameEntry = NULL; + _argEntry = NULL; + gtk_widget_destroy(GTK_WIDGET(arg)); +} + +static void +select_type (GtkWidget* menuitem, gpointer data) +{ + _type = (LkBindingType)GPOINTER_TO_INT(data); +} + +static void +row_selected (GtkCList* clist, gint row, gint column, GdkEventButton* event, + gpointer user_data) +{ + _selection = row; + gtk_widget_set_sensitive(_edit, TRUE); +} + +static void +row_deselected (GtkCList* clist, gint row, gint column, GdkEventButton* event, + gpointer user_data) +{ + _selection = -1; + gtk_widget_set_sensitive(_edit, FALSE); +} + +static void +populate_binding (GtkWidget* button, gpointer arg) +{ + LkBinding* binding = LK_BINDING(arg); + gchar* key = gtk_entry_get_text(GTK_ENTRY(_keyEntry)); + gchar* name = gtk_entry_get_text(GTK_ENTRY(_nameEntry)); + gchar* argument = gtk_entry_get_text(GTK_ENTRY(_argEntry)); + + /* only update the binding if we modified something */ + if (strcmp(key, binding->key) || _type != binding->type || + strcmp(name, binding->name) || strcmp(argument, binding->argument)) { + /* update the binding instance */ + lk_binding_init(binding, key, _type, name, argument); + /* let the prefs system know that something has changed */ + gnome_property_box_changed(GNOME_PROPERTY_BOX(_prefBox)); + } +} + +static void +lk_prefs_edit_binding (LkBinding* binding, GtkSignalFunc okedFunc, + GtkSignalFunc cancelledFunc) +{ + GtkWidget* dialog; + GtkWidget* vbox, *hbox; + GtkWidget* label, *button; + GtkWidget* optmenu, *menu, *menuitem; + + gchar* types[] = { _("URL"), _("Exec"), NULL }; + int i; + + dialog = gtk_dialog_new(); + gtk_window_set_modal(GTK_WINDOW(dialog), TRUE); + vbox = GTK_DIALOG(dialog)->vbox; + gtk_container_set_border_width(GTK_CONTAINER(vbox), + GNOME_PAD_SMALL); + gtk_box_set_spacing(GTK_BOX(vbox), GNOME_PAD_SMALL); + + /* create the key line */ + hbox = gtk_hbox_new(FALSE, GNOME_PAD_SMALL); + /* create the key input */ + label = gtk_label_new(_("Key:")); + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); + _keyEntry = gtk_entry_new(); + gtk_entry_set_text(GTK_ENTRY(_keyEntry), binding->key); + gtk_box_pack_start(GTK_BOX(hbox), _keyEntry, FALSE, FALSE, 0); + button = gtk_button_new_with_label(_("Grab...")); + gtk_signal_connect(GTK_OBJECT(button), "clicked", + grab_button_pressed, _keyEntry); + gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0); + /* create the name input */ + label = gtk_label_new(_("Name:")); + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); + _nameEntry = gtk_entry_new(); + gtk_entry_set_text(GTK_ENTRY(_nameEntry), binding->name); + gtk_box_pack_start(GTK_BOX(hbox), _nameEntry, FALSE, FALSE, 0); + /* pack the line into the dialog */ + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + + /* create the command line */ + hbox = gtk_hbox_new(FALSE, GNOME_PAD_SMALL); + optmenu = gtk_option_menu_new(); + menu = gtk_menu_new(); + for (i = 0; types[i] != NULL; i++) { + menuitem = gtk_menu_item_new_with_label(types[i]); + gtk_signal_connect(GTK_OBJECT(menuitem), "activate", + select_type, GINT_TO_POINTER(i)); + gtk_widget_show(menuitem); + gtk_menu_append(GTK_MENU(menu), menuitem); + } + gtk_option_menu_set_menu(GTK_OPTION_MENU(optmenu), menu); + gtk_option_menu_set_history(GTK_OPTION_MENU(optmenu), + (int)binding->type); + _type = binding->type; + gtk_box_pack_start(GTK_BOX(hbox), optmenu, FALSE, FALSE, 0); + + _argEntry = gtk_entry_new_with_max_length(MAX_BINDING_LENGTH); + gtk_entry_set_text(GTK_ENTRY(_argEntry), binding->argument); + gtk_box_pack_start(GTK_BOX(hbox), _argEntry, TRUE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + + /* create and wire up the cancel button */ + hbox = GTK_DIALOG(dialog)->action_area; + button = gtk_button_new_with_label(_("Cancel")); + gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0); + if (cancelledFunc != NULL) { + gtk_signal_connect(GTK_OBJECT(button), "clicked", + cancelledFunc, binding); + } + gtk_signal_connect(GTK_OBJECT(button), "clicked", + pop_down_edit_binding, dialog); + + /* create and wire up the ok button */ + button = gtk_button_new_with_label(_("OK")); + gtk_signal_connect(GTK_OBJECT(button), "clicked", + populate_binding, binding); + if (okedFunc != NULL) { + gtk_signal_connect(GTK_OBJECT(button), "clicked", + okedFunc, binding); + } + gtk_signal_connect(GTK_OBJECT(button), "clicked", + pop_down_edit_binding, dialog); + gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0); + + gtk_window_set_default_size(GTK_WINDOW(dialog), 400, 10); + gtk_widget_show_all(dialog); +} + +static void +add_binding_oked (GtkWidget* button, gpointer arg) +{ + LkBinding* binding = LK_BINDING(arg); + gchar* data[2]; + + /* add this binding to our bindings array */ + g_ptr_array_add(_bindings, binding); + + /* and add it to the bindings list */ + data[0] = binding->key; + data[1] = binding->name; + gtk_clist_append(GTK_CLIST(_bindList), data); + gtk_clist_columns_autosize(GTK_CLIST(_bindList)); + + /* indicate our change of properties */ + gnome_property_box_changed(GNOME_PROPERTY_BOX(_prefBox)); +} + +static void +add_binding_cancelled (GtkWidget* button, gpointer arg) +{ + lk_binding_destroy(LK_BINDING(arg)); +} + +static void +add_clicked (GtkButton* button, gpointer user_data) +{ + LkBinding* binding = lk_binding_new(); + lk_prefs_edit_binding(binding, add_binding_oked, add_binding_cancelled); +} + +static void +edit_binding_oked (GtkWidget* button, gpointer arg) +{ + LkBinding* binding = LK_BINDING(arg); + + /* update the table display (the _selection is guaranteed not to have + * changed because the edit dialog is modal) */ + gtk_clist_set_text(GTK_CLIST(_bindList), _selection, 0, binding->key); + gtk_clist_set_text(GTK_CLIST(_bindList), _selection, 1, binding->name); + gtk_clist_columns_autosize(GTK_CLIST(_bindList)); +} + +static void +edit_clicked (GtkButton* button, gpointer user_data) +{ + /* only do something if there's a selection */ + if (_selection != -1) { + LkBinding* binding = + LK_BINDING(g_ptr_array_index(_bindings, _selection)); + /* make sure things aren't crazily out of sync */ + if (binding != NULL) { + lk_prefs_edit_binding(binding, edit_binding_oked, NULL); + } + } +} + +static void +delete_clicked (GtkButton* button, gpointer user_data) +{ + /* only do something if there's a selection */ + if (_selection != -1) { + /* remove the binding from our bindings list */ + LkBinding* binding = + LK_BINDING(g_ptr_array_remove_index(_bindings, _selection)); + /* make sure things are crazily out of sync */ + if (binding != NULL) { + lk_binding_destroy(binding); + } + /* and remove that row from the clist */ + gtk_clist_remove(GTK_CLIST(_bindList), _selection); + /* clear out the selection */ + _selection = -1; + /* let the world know we've made changes */ + gnome_property_box_changed(GNOME_PROPERTY_BOX(_prefBox)); + } +} + +void +lk_prefs_init (void) +{ + if (_bindings == NULL) { + _bindings = g_ptr_array_new(); + + /* load up our bindings */ + lk_binding_load_bindings(_bindings); + } +} + +GPtrArray* +lk_prefs_get_bindings (void) +{ + return _bindings; +} + +void +lk_prefs_cleanup (void) +{ + int i; + + /* free our preferences panel */ + gtk_widget_destroy(_prefBox); + _prefBox = NULL; + + for (i = 0; i < _bindings->len; i++) { + lk_binding_destroy(LK_BINDING(g_ptr_array_index(_bindings, i))); + } + g_ptr_array_free(_bindings, FALSE); +} + +static void +populate_bindings () +{ + gchar* data[2]; + int i; + + /* clear out any old stuff */ + gtk_clist_clear(GTK_CLIST(_bindList)); + + /* add an entry for each binding */ + for (i = 0; i < _bindings->len; i++) { + LkBinding* binding = LK_BINDING(g_ptr_array_index(_bindings, i)); + data[0] = binding->key; + data[1] = binding->name; + gtk_clist_append(GTK_CLIST(_bindList), data); + } + + /* fit the display to the contents */ + gtk_clist_columns_autosize(GTK_CLIST(_bindList)); +} + +static void +apply_preferences (GnomePropertyBox* pbox, gint page, gpointer data) +{ + /* save our bindings */ + lk_binding_save_bindings(_bindings); +} + +void +lk_prefs_display () +{ + GtkWidget* vbox, *bbox, *scrolled; + GtkWidget* label; + GtkWidget* add, *delete; + + gchar* titles[] = { _("Key"), _("Name") }; + + /* just display the existing window if it's already open */ + if (_prefBox != NULL) { + populate_bindings(); + gdk_window_show(GTK_WIDGET(_prefBox)->window); + gdk_window_raise(GTK_WIDGET(_prefBox)->window); + return; + } + + /* create and configure our prefs ui */ + _prefBox = gnome_property_box_new(); + gtk_window_set_title(GTK_WINDOW(_prefBox), _("Lookuplet Properties")); + gtk_signal_connect(GTK_OBJECT(_prefBox), "destroy", + gtk_widget_destroyed, &_prefBox);; + + vbox = gtk_vbox_new(FALSE, GNOME_PAD_SMALL); + gtk_container_set_border_width(GTK_CONTAINER(vbox), + GNOME_PAD_SMALL); + + label = gtk_label_new(_("Configure bindings...")); + gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT); + gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0); + + /* create a scrolled window in which our clist will live */ + scrolled = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + _bindList = gtk_clist_new_with_titles(2, titles); + gtk_clist_set_selection_mode(GTK_CLIST(_bindList), GTK_SELECTION_SINGLE); + gtk_signal_connect(GTK_OBJECT(_bindList), "select-row", + GTK_SIGNAL_FUNC(row_selected), NULL); + gtk_signal_connect(GTK_OBJECT(_bindList), "unselect-row", + GTK_SIGNAL_FUNC(row_deselected), NULL); + gtk_container_add(GTK_CONTAINER(scrolled), _bindList); + + /* instruct the scrolled window not to assume teeny dimensions */ + gtk_widget_set_usize(scrolled, 300, 200); + + populate_bindings(); + gtk_box_pack_start(GTK_BOX(vbox), scrolled, TRUE, TRUE, 0); + + /* create the control buttons */ + bbox = gtk_hbox_new(FALSE, GNOME_PAD_SMALL); + delete = gtk_button_new_with_label(_("Delete")); + gtk_box_pack_end(GTK_BOX(bbox), delete, FALSE, FALSE, 0); + _edit = gtk_button_new_with_label(_("Edit...")); + gtk_widget_set_sensitive(_edit, FALSE); + gtk_box_pack_end(GTK_BOX(bbox), _edit, FALSE, FALSE, 0); + add = gtk_button_new_with_label(_("Add...")); + gtk_box_pack_end(GTK_BOX(bbox), add, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 0); + + /* wire them up */ + gtk_signal_connect(GTK_OBJECT(add), "clicked", + GTK_SIGNAL_FUNC(add_clicked), NULL); + gtk_signal_connect(GTK_OBJECT(_edit), "clicked", + GTK_SIGNAL_FUNC(edit_clicked), NULL); + gtk_signal_connect(GTK_OBJECT(delete), "clicked", + GTK_SIGNAL_FUNC(delete_clicked), NULL); + + /* add our page to the property box */ + gnome_property_box_append_page(GNOME_PROPERTY_BOX(_prefBox), + vbox, gtk_label_new(_("Bindings"))); + + /* wire up some signals */ + gtk_signal_connect(GTK_OBJECT(_prefBox), "apply", + GTK_SIGNAL_FUNC(apply_preferences), NULL); + + /* and finally show our property box */ + gtk_widget_show_all(_prefBox); +} diff --git a/projects/lookuplet/src/c/preferences.h b/projects/lookuplet/src/c/preferences.h new file mode 100644 index 00000000..af86585a --- /dev/null +++ b/projects/lookuplet/src/c/preferences.h @@ -0,0 +1,22 @@ +/** + * $Id: preferences.h,v 1.1 2001/02/24 02:35:20 mdb Exp $ + */ + +#ifndef _PREFERENCES_H_ +#define _PREFERENCES_H_ + +#include + +extern void +lk_prefs_init (void); + +extern GPtrArray* +lk_prefs_get_bindings (void); + +extern void +lk_prefs_cleanup (void); + +extern void +lk_prefs_display (); + +#endif /* _PREFERENCES_H_ */ diff --git a/projects/lookuplet/src/c/querybox.c b/projects/lookuplet/src/c/querybox.c index 54af7168..8d016e66 100644 --- a/projects/lookuplet/src/c/querybox.c +++ b/projects/lookuplet/src/c/querybox.c @@ -1,58 +1,145 @@ /** - * $Id: querybox.c,v 1.1 2000/12/10 23:38:39 mdb Exp $ + * $Id: querybox.c,v 1.2 2001/02/24 02:35:20 mdb Exp $ */ #include #include #include +#include "binding.h" #include "launcher.h" +#include "keysym-util.h" +#include "preferences.h" #include "querybox.h" -#define BORDER 1 -#define SPACER 2 +static GtkWidget* _query; + +static void +selection_received (GtkWidget* widget, GtkSelectionData* selection_data, + gpointer data) +{ + /* make sure some selection was provided */ + if (selection_data->length > 0) { + /* select the text in the entry so that it can be easily replaced */ + gtk_editable_select_region(GTK_EDITABLE(_query), 0, + selection_data->length); + } +} + +static gint +key_pressed (GtkWidget* widget, GdkEvent* event, gpointer callback_data) +{ + GdkEventKey* ek = (GdkEventKey*)event; + GPtrArray* bindings; + gint handled = FALSE, i; + gchar* keystr; + + /* ignore plain or shifted keystrokes */ + if (ek->state <= 1) { + return FALSE; + } + + /* otherwise convert the key combo to a name and look it up */ + keystr = convert_keysym_state_to_string(ek->keyval, ek->state); + bindings = lk_prefs_get_bindings(); + + g_print("key pressed: %s (%d %d)\n", keystr, ek->state, ek->keyval); + + for (i = 0; i < bindings->len; i++) { + LkBinding* binding = LK_BINDING(g_ptr_array_index(bindings, i)); + if (!strcmp(keystr, binding->key)) { + /* get the terms from the query box */ + gchar* search_text = + gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1); + /* launch the appropriate thing */ + lk_launcher_launch(binding, search_text); + g_free(search_text); + /* clear out the contents of the entry box */ + gtk_entry_set_text(GTK_ENTRY(widget), ""); + handled = TRUE; + break; + } + } + + g_free(keystr); + + return handled; +} + +static void +prefs_clicked (GtkWidget* widget, gpointer data) +{ + lk_prefs_display(); +} static void launch (GtkWidget* widget, gpointer data) { - gchar* search_text = gtk_entry_get_text(GTK_ENTRY(widget)); - /* pass the contents of the querybox on to the launcher for display * (specifying NULL for the qualifier since they just pressed return * instead of pressing some special key combination) */ - lookuplet_launcher_launch(search_text, NULL); + gchar* search_text = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1); + lk_launcher_launch(NULL, search_text); + g_free(search_text); /* clear out the contents of the entry box */ gtk_entry_set_text(GTK_ENTRY(widget), ""); } GtkWidget* -lookuplet_querybox_create () +lk_querybox_create (void) { - GtkWidget* vbox; + GtkWidget* hbox; GtkWidget* label; - GtkWidget* query; + GtkWidget* prefs; /* create the widget we are going to put on the applet */ label = gtk_label_new(_("Query")); - gtk_widget_show(label); /* make the query text box and attach the "activate" signal handler */ - query = gtk_entry_new(); + _query = gtk_entry_new(); /* Connect "return" in the search box to the launch func. */ - gtk_signal_connect(GTK_OBJECT(query), "activate", + gtk_signal_connect(GTK_OBJECT(_query), "activate", GTK_SIGNAL_FUNC(launch), NULL); + /* create our preferences button */ + prefs = gtk_button_new_with_label(_("Prefs")); + gtk_signal_connect (GTK_OBJECT(prefs), "clicked", + GTK_SIGNAL_FUNC(prefs_clicked), NULL); + /* create a vertical box to hold our query input box and the label */ - vbox = gtk_hbox_new(FALSE, SPACER); + hbox = gtk_hbox_new(FALSE, GNOME_PAD_SMALL); - /* pack the query box and label into the vbox */ - gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, SPACER); - gtk_box_pack_start(GTK_BOX(vbox), query, FALSE, FALSE, SPACER); + /* pack the query box and label into the hbox */ + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox), _query, TRUE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(hbox), prefs, FALSE, FALSE, 0); - gtk_widget_show(query); - gtk_widget_show(label); - - return vbox; + return hbox; +} + +void +lk_querybox_init (void) +{ + static GdkAtom targets_atom = GDK_NONE; + + /* wire up our key press event handler */ + gtk_signal_connect(GTK_OBJECT(_query), "key_press_event", + GTK_SIGNAL_FUNC(key_pressed), NULL); + + /* focus the text entry box */ + gtk_widget_grab_focus(_query); + + /* register a signal handler that will select the selection when it + arrives */ + gtk_signal_connect_after(GTK_OBJECT(_query), "selection_received", + GTK_SIGNAL_FUNC(selection_received), NULL); + + /* request the selection as type "STRING" */ + if (targets_atom == GDK_NONE) { + targets_atom = gdk_atom_intern ("STRING", FALSE); + } + gtk_selection_convert(_query, GDK_SELECTION_PRIMARY, targets_atom, + GDK_CURRENT_TIME); } diff --git a/projects/lookuplet/src/c/querybox.h b/projects/lookuplet/src/c/querybox.h index 157d3534..f715fd2f 100644 --- a/projects/lookuplet/src/c/querybox.h +++ b/projects/lookuplet/src/c/querybox.h @@ -1,11 +1,16 @@ /** - * $Id: querybox.h,v 1.1 2000/12/10 23:38:39 mdb Exp $ + * $Id: querybox.h,v 1.2 2001/02/24 02:35:20 mdb Exp $ */ #ifndef _QUERYBOX_H_ #define _QUERYBOX_H_ +#include + extern GtkWidget* -lookuplet_querybox_create (void); +lk_querybox_create (void); + +extern void +lk_querybox_init (void); #endif /* _QUERYBOX_H_ */