diff src/main.c @ 9:d907d608745f

Sync to GQview 1.5.9 release. ######## DO NOT BASE ENHANCEMENTS OR TRANSLATION UPDATES ON CODE IN THIS CVS! This CVS is never up to date with current development and is provided solely for reference purposes, please use the latest official release package when making any changes or translation updates. ########
author gqview
date Sat, 26 Feb 2005 00:13:35 +0000
parents c0e337a01cb7
children 6d50eaba8e4b
line wrap: on
line diff
--- a/src/main.c	Sat Feb 26 00:07:07 2005 +0000
+++ b/src/main.c	Sat Feb 26 00:13:35 2005 +0000
@@ -1,127 +1,275 @@
 /*
- * GQview image viewer
- * (C)2000 John Ellis
+ * GQview
+ * (C) 2004 John Ellis
  *
  * Author: John Ellis
  *
+ * This software is released under the GNU General Public License (GNU GPL).
+ * Please read the included file COPYING for more information.
+ * This software comes with no warranty of any kind, use at your own risk!
  */
 
+
 #include "gqview.h"
+
+#include "cache.h"
+#include "collect.h"
+#include "collect-io.h"
+#include "dnd.h"
+#include "editors.h"
+#include "filelist.h"
+#include "img-view.h"
+#include "layout.h"
+#include "layout_image.h"
+#include "menu.h"
+#include "preferences.h"
+#include "rcfile.h"
+#include "remote.h"
+#include "similar.h"
+#include "slideshow.h"
+#include "utilops.h"
+#include "ui_bookmark.h"
+#include "ui_help.h"
+#include "ui_fileops.h"
+#include "ui_tabcomp.h"
+#include "ui_utildlg.h"
+
 #include <gdk/gdkkeysyms.h> /* for keyboard values */
 
-static void parse_command_line(int argc, char *argv[], gchar **path, gchar **file);
-static void setup_default_options();
+#include "icons/icon.xpm"
+
+
+#include <math.h>
+
+
+static RemoteConnection *gqview_remote = NULL;
+static CollectionData *gqview_command_collection = NULL;
+
 
 /*
  *-----------------------------------------------------------------------------
- * path manipulation routines (public)
+ * misc (public)
  *-----------------------------------------------------------------------------
  */ 
 
-gchar *filename_from_path(char *t)
+typedef struct _WindowIconData WindowIconData;
+struct _WindowIconData
 {
-	char *p;
+	const char **icon;
+	gchar *path;
+};
 
-	p = t + strlen(t);
-	while(p > t && p[0] != '/') p--;
-	if (p[0] == '/') p++;
-	return p;
-}
-
-gchar *remove_level_from_path(gchar *path)
+static void window_set_icon_cb(GtkWidget *widget, gpointer data)
 {
-	gchar *new_path;
-	gchar *ptr = path;
-	gint p;
-
-	if (!path) return NULL;
+	WindowIconData *wid = data;
+	GdkPixbuf *pb;
+	GdkPixmap *pixmap;
+	GdkBitmap *mask;
 
-	p = strlen(path) - 1;
-	if (p < 0) return NULL;
-	while(ptr[p] != '/' && p > 0) p--;
-	if (p == 0 && ptr[p] == '/') p++;
-	new_path = g_strndup(path, (guint)p);
-	return new_path;
+	if (wid->icon)
+		{
+		pb = gdk_pixbuf_new_from_xpm_data(wid->icon);
+		}
+	else
+		{
+		pb = gdk_pixbuf_new_from_file(wid->path, NULL);
+		}
+
+	g_free(wid->path);
+	g_free(wid);
+
+	if (!pb) return;
+
+	gdk_pixbuf_render_pixmap_and_mask(pb, &pixmap, &mask, 128);
+	gdk_pixbuf_unref(pb);
+
+	gdk_window_set_icon(widget->window, NULL, pixmap, mask);
+	/* apparently, gdk_window_set_icon does not ref the pixmap and mask, so don't unref it (leak?) */
 }
 
-void parse_out_relatives(gchar *path)
+void window_set_icon(GtkWidget *window, const char **icon, const gchar *file)
 {
-	gint s, t;
+	WindowIconData *wid;
+
+	if (!icon && !file) icon = (const char **)icon_xpm;
 
-	if (!path) return;
+	wid = g_new0(WindowIconData, 1);
+	wid->icon = icon;
+	wid->path = g_strdup(file);
 
-	s = t = 0;
+	g_signal_connect(G_OBJECT(window), "realize",
+			 G_CALLBACK(window_set_icon_cb), wid);
+}
 
-	while (path[s] != '\0')
-		{
-		if (path[s] == '/' && path[s+1] == '.' && (path[s+2] == '/' || path[s+2] == '\0') )
-			{
-			s += 2;
-			}
-		else if (path[s] == '/' && path[s+1] == '.' && path[s+2] == '.' && (path[s+3] == '/' || path[s+3] == '\0') )
-			{
-			s += 3;
-			if (t > 0) t--;
-			while (path[t] != '/' && t > 0) t--;
-			}
-		else
-			{
-			if (s != t) path[t] = path[s];
-			t++;
-			s++;
-			}
-		}
-	if (t == 0 && path[t] == '/') t++;
-	if (t > 1 && path[t-1] == '/') t--;
-	path[t] = '\0';
+gint window_maximized(GtkWidget *window)
+{
+	GdkWindowState state;
+
+	if (!window || !window->window) return FALSE;
+
+	state = gdk_window_get_state(window->window);
+	return (state & GDK_WINDOW_STATE_MAXIMIZED);
+}
+
+gdouble get_zoom_increment(void)
+{
+	return ((zoom_increment != 0) ? (gdouble)zoom_increment / 10.0 : 1.0);
 }
 
 /*
  *-----------------------------------------------------------------------------
- * external editor start routines (public)
+ * Open  browser with the help Documentation
+ *-----------------------------------------------------------------------------
+ */
+
+static gchar *command_result(const gchar *binary, const gchar *command)
+{
+	gchar *result = NULL;
+	FILE *f;
+	char buf[2048];
+	int l;
+
+	if (!binary) return NULL;
+	if (!file_in_path(binary)) return NULL;
+
+	if (!command) return g_strdup(binary);
+	if (command[0] == '!') return g_strdup(command + 1);
+
+	f = popen(command, "r");
+	if (!f) return NULL;
+
+	while ((l = fread(buf, sizeof(char), sizeof(buf), f)) > 0)
+		{
+		if (!result)
+			{
+			int n = 0;
+
+			while (n < l && buf[n] != '\n' && buf[n] != '\r') n++;
+			if (n > 0) result = g_strndup(buf, n);
+			}
+		}
+
+	pclose(f);
+
+	return result;
+}
+
+static void help_browser_command(const gchar *command, const gchar *path)
+{
+	gchar *result;
+	gchar *buf;
+	gchar *begin;
+	gchar *end;
+
+	if (!command || !path) return;
+
+	if (debug) printf("Help command pre \"%s\", \"%s\"\n", command, path);
+
+	buf = g_strdup(command);
+	begin = strstr(buf, "%s");
+	if (begin)
+		{
+		*begin = '\0';
+		end = begin + 2;
+		begin = buf;
+
+		result = g_strdup_printf("%s%s%s &", begin, path, end);
+		}
+	else
+		{
+		result = g_strdup_printf("%s \"%s\" &", command, path);
+		}
+	g_free(buf);
+
+	if (debug) printf("Help command post [%s]\n", result);
+
+	system(result);
+
+	g_free(result);
+}
+
+/*
+ * each set of 2 strings is one browser:
+ *   the 1st is the binary to look for in the path
+ *   the 2nd has 3 capabilities:
+ *        NULL     exec binary with html file path as command line
+ *        string   exec string and use results for command line
+ *        !string  use text following ! as command line, replacing optional %s with html file path
+*/
+static gchar *html_browsers[] =
+{
+	/* Redhat has a nifty htmlview script to start the user's preferred browser */
+	"htmlview",	NULL,
+	/* GNOME 2 */
+	"gconftool-2",	"gconftool-2 -g /desktop/gnome/url-handlers/http/command",
+	/* KDE */
+	"kfmclient",	"!kfmclient exec \"%s\"",
+	/* use fallbacks */
+	"firefox",	NULL,
+	"mozilla",	NULL,
+	"konqueror",	NULL,
+	"netscape",	NULL,
+	NULL,		NULL
+};
+
+static void help_browser_run(void)
+{
+	gchar *result = NULL;
+	gint i;
+
+	i = 0;
+	while (!result && html_browsers[i])
+		{
+		result = command_result(html_browsers[i], html_browsers[i+1]);
+		i += 2;
+		}
+
+	if (!result)
+		{
+		printf("Unable to detect an installed browser.\n");
+		return;
+		}
+
+	help_browser_command(result, GQVIEW_HTMLDIR "/index.html");
+
+	g_free(result);
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * help window
  *-----------------------------------------------------------------------------
  */ 
 
-void start_editor_from_file(gint n, gchar *path)
+static GtkWidget *help_window = NULL;
+
+static void help_window_destroy_cb(GtkWidget *window, gpointer data)
 {
-	gchar *cmd;
-	if (!path) return;
-	cmd = g_strdup_printf("%s \"%s\" &", editor_command[n], path);
-	printf(_("GQview running: %s\n"),cmd);
-	system(cmd);
-	g_free(cmd);
-}
-
-void start_editor_from_image(gint n)
-{
-	start_editor_from_file(n, image_get_path());
+	help_window = NULL;
 }
 
-void start_editor_from_list(gint n)
+void help_window_show(const gchar *key)
 {
-	gchar *cmd;
-	gchar *buf;
-	GList *list = file_get_selected_list();
-	GList *work;
-	if (!list) return;
-	work = list;
-	cmd = g_strconcat(editor_command[n], " ", NULL);
-	while(work)
+	if (key && strcmp(key, "html_contents") == 0)
+		{
+		help_browser_run();
+		return;
+		}
+
+	if (help_window)
 		{
-		buf = cmd;
-		cmd = g_strconcat(buf, "\"", work->data, "\" ", NULL);
-		g_free(buf);
-		work = work->next;
+		gtk_window_present(GTK_WINDOW(help_window));
+		if (key) help_window_set_key(help_window, key);
+		return;
 		}
-	buf = cmd;
-	cmd = g_strconcat(buf, "&", NULL);
-	g_free(buf);
-	printf(_("GQview running: %s\n"),cmd);
-	system(cmd);
-	g_free(cmd);
-	free_selected_list(list);
+
+	help_window = help_window_new(_("Help - GQview"), "GQview", "help",
+                                      GQVIEW_HELPDIR "/README", key);
+	g_signal_connect(G_OBJECT(help_window), "destroy",
+			 G_CALLBACK(help_window_destroy_cb), NULL);
 }
 
+
 /*
  *-----------------------------------------------------------------------------
  * keyboard functions
@@ -134,6 +282,16 @@
 	static guint32 time_old = 0;
 	static guint keyval_old = 0;
 
+	if (event->state & GDK_CONTROL_MASK)
+		{
+		if (*x < 0) *x = G_MININT / 2;
+		if (*x > 0) *x = G_MAXINT / 2;
+		if (*y < 0) *y = G_MININT / 2;
+		if (*y > 0) *y = G_MAXINT / 2;
+
+		return;
+		}
+
 	if (progressive_key_scrolling)
 		{
 		guint32 time_diff;
@@ -157,103 +315,494 @@
 	*y = *y * delta;
 }
 
-gint key_press_cb(GtkWidget *widget, GdkEventKey *event)
+
+/*
+ *-----------------------------------------------------------------------------
+ * remote functions
+ *-----------------------------------------------------------------------------
+ */
+
+static void gr_image_next(const gchar *text, gpointer data)
+{
+	layout_image_next(NULL);
+}
+
+static void gr_image_prev(const gchar *text, gpointer data)
+{
+	layout_image_prev(NULL);
+}
+
+static void gr_image_first(const gchar *text, gpointer data)
+{
+	layout_image_first(NULL);
+}
+
+static void gr_image_last(const gchar *text, gpointer data)
+{
+	layout_image_last(NULL);
+}
+
+static void gr_fullscreen_toggle(const gchar *text, gpointer data)
+{
+	layout_image_full_screen_toggle(NULL);
+}
+
+static void gr_fullscreen_start(const gchar *text, gpointer data)
 {
-	gint stop_signal = FALSE;
-	gint x = 0;
-	gint y = 0;
+	layout_image_full_screen_start(NULL);
+}
+
+static void gr_fullscreen_stop(const gchar *text, gpointer data)
+{
+	layout_image_full_screen_stop(NULL);
+}
+
+static void gr_slideshow_start_rec(const gchar *text, gpointer data)
+{
+	GList *list;
+
+	list = path_list_recursive(text);
+	if (!list) return;
+printf("length: %d\n", g_list_length(list));
+	layout_image_slideshow_stop(NULL);
+	layout_image_slideshow_start_from_list(NULL, list);
+}
 
-	if (GTK_WIDGET_HAS_FOCUS(path_entry))
+static void gr_slideshow_toggle(const gchar *text, gpointer data)
+{
+	layout_image_slideshow_toggle(NULL);
+}
+
+static void gr_slideshow_start(const gchar *text, gpointer data)
+{
+	layout_image_slideshow_start(NULL);
+}
+
+static void gr_slideshow_stop(const gchar *text, gpointer data)
+{
+	layout_image_slideshow_stop(NULL);
+}
+
+static void gr_slideshow_delay(const gchar *text, gpointer data)
+{
+	gdouble n;
+
+	n = strtod(text, NULL);
+	if (n < SLIDESHOW_MIN_SECONDS || n > SLIDESHOW_MAX_SECONDS)
 		{
-		if (event->keyval == GDK_Escape)
-			gtk_entry_set_text(GTK_ENTRY(path_entry), current_path);
-		return stop_signal;
+		gchar *buf;
+
+		buf = g_strdup_printf("Remote slideshow delay out of range (%.1f to %.1f)\n",
+				      SLIDESHOW_MIN_SECONDS, SLIDESHOW_MAX_SECONDS);
+		print_term(buf);
+		g_free(buf);
+
+		return;
+		}
+	slideshow_delay = (gint)(n * 10.0 + 0.01);
+}
+
+static void gr_tools_show(const gchar *text, gpointer data)
+{
+	gint popped;
+	gint hidden;
+
+	if (layout_tools_float_get(NULL, &popped, &hidden) && hidden)
+		{
+		layout_tools_float_set(NULL, popped, FALSE);
 		}
+}
 
-	if (full_screen_window || GTK_WIDGET_HAS_FOCUS(main_image->viewport))
+static void gr_tools_hide(const gchar *text, gpointer data)
+{
+	gint popped;
+	gint hidden;
+
+	if (layout_tools_float_get(NULL, &popped, &hidden) && !hidden)
 		{
-		switch (event->keyval)
+		layout_tools_float_set(NULL, popped, TRUE);
+		}
+}
+
+static gint gr_quit_idle_cb(gpointer data)
+{
+	exit_gqview();
+
+	return FALSE;
+}
+
+static void gr_quit(const gchar *text, gpointer data)
+{
+	/* schedule exit when idle, if done from within a
+	 * remote handler remote_close will crash
+	 */
+	g_idle_add(gr_quit_idle_cb, NULL);
+}
+
+static void gr_file_load(const gchar *text, gpointer data)
+{
+	if (isfile(text))
+		{
+		if (file_extension_match(text, ".gqv"))
 			{
-			case GDK_Left:
-				x -= 1;
-				stop_signal = TRUE;
-				break;
-			case GDK_Right:
-				x += 1;
-				stop_signal = TRUE;
-				break;
-			case GDK_Up:
-				y -= 1;
-				stop_signal = TRUE;
-				break;
-			case GDK_Down:
-				y += 1;
-				stop_signal = TRUE;
-				break;
-			case GDK_BackSpace:
-				file_prev_image();
-				stop_signal = TRUE;
-				break;
-			case GDK_space:
-				file_next_image();
-				stop_signal = TRUE;
-				break;
+			collection_window_new(text);
+			}
+		else
+			{
+			layout_set_path(NULL, text);
 			}
 		}
+	else if (isdir(text))
+		{
+		layout_set_path(NULL, text);
+		}
+	else
+		{
+		printf("remote sent filename that does not exist:\"%s\"\n", text);
+		}
+}
+
+static void gr_file_view(const gchar *text, gpointer data)
+{
+	view_window_new(text);
+}
+
+static void gr_list_clear(const gchar *text, gpointer data)
+{
+	if (gqview_command_collection) collection_unref(gqview_command_collection);
+	gqview_command_collection = NULL;
+}
+
+static void gr_list_add(const gchar *text, gpointer data)
+{
+	gint new = TRUE;
+
+	if (!gqview_command_collection)
+		{
+		CollectionData *cd;
+
+		cd = collection_new("");
+
+		g_free(cd->path);
+		cd->path = NULL;
+		g_free(cd->name);
+		cd->name = g_strdup(_("Command line"));
+
+		gqview_command_collection = cd;
+		}
+	else
+		{
+		new = (!collection_get_first(gqview_command_collection));
+		}
 
-	switch (event->keyval)
+	if (collection_add(gqview_command_collection, text, FALSE) && new)
+		{
+		layout_image_set_collection(NULL, gqview_command_collection,
+					    collection_get_first(gqview_command_collection));
+		}
+}
+
+static void gr_raise(const gchar *text, gpointer data)
+{
+	LayoutWindow *lw = NULL;
+
+	if (layout_valid(&lw))
+		{
+		gtk_window_present(GTK_WINDOW(lw->window));
+		}
+}
+
+typedef struct _RemoteCommandEntry RemoteCommandEntry;
+struct _RemoteCommandEntry {
+	gchar *opt_s;
+	gchar *opt_l;
+	void (*func)(const gchar *text, gpointer data);
+	gint needs_extra;
+	gint prefer_command_line;
+	gchar *description;
+};
+
+static RemoteCommandEntry remote_commands[] = {
+	/* short, long                  callback,               extra, prefer,description */
+	{ "-n", "--next",               gr_image_next,          FALSE, FALSE, N_("next image") },
+	{ "-b", "--back",               gr_image_prev,          FALSE, FALSE, N_("previous image") },
+	{ NULL, "--first",              gr_image_first,         FALSE, FALSE, N_("first image") },
+        { NULL, "--last",               gr_image_last,          FALSE, FALSE, N_("last image") },
+	{ "-f", "--fullscreen",         gr_fullscreen_toggle,   FALSE, TRUE,  N_("toggle full screen") },
+	{ "-fs","--fullscreen-start",   gr_fullscreen_start,    FALSE, FALSE, N_("start full screen") },
+	{ "-fS","--fullscreen-stop",    gr_fullscreen_stop,     FALSE, FALSE, N_("stop full screen") },
+	{ "-s", "--slideshow",          gr_slideshow_toggle,	FALSE, TRUE,  N_("toggle slide show") },
+	{ "-ss","--slideshow-start",    gr_slideshow_start,     FALSE, FALSE, N_("start slide show") },
+	{ "-sS","--slideshow-stop",     gr_slideshow_stop,      FALSE, FALSE, N_("stop slide show") },
+	{ "-sr","--slideshow-recurse",  gr_slideshow_start_rec, TRUE,  FALSE, N_("start recursive slide show") },
+	{ "-d", "--delay=",             gr_slideshow_delay,     TRUE,  FALSE, N_("set slide show delay in seconds") },
+	{ "+t", "--tools-show",         gr_tools_show,          FALSE, TRUE,  N_("show tools") },
+	{ "-t", "--tools-hide",	        gr_tools_hide,          FALSE, TRUE,  N_("hide tools") },
+	{ "-q", "--quit",               gr_quit,                FALSE, FALSE, N_("quit") },
+	{ NULL, "file:",                gr_file_load,           TRUE,  FALSE, N_("open file") },
+	{ NULL, "view:",		gr_file_view,		TRUE,  FALSE, N_("open file in new window") },
+	{ NULL, "--list-clear",         gr_list_clear,          FALSE, FALSE, NULL },
+	{ NULL, "--list-add:",          gr_list_add,            TRUE,  FALSE, NULL },
+	{ NULL, "raise",		gr_raise,		FALSE, FALSE, NULL },
+	{ NULL, NULL, NULL, FALSE, FALSE, NULL }
+};
+
+static RemoteCommandEntry *gqview_remote_command_find(const gchar *text, const gchar **offset)
+{
+	gint match = FALSE;
+	gint i;
+
+	i = 0;
+	while (!match && remote_commands[i].func != NULL)
 		{
-		case '+':
-			image_adjust_zoom(1);
-			break;
-		case GDK_Page_Up:
-			file_prev_image();
-			stop_signal = TRUE;
-			break;
-		case GDK_Page_Down:
-			file_next_image();
-			stop_signal = TRUE;
-			break;
-		case GDK_Home:
-			file_first_image();
-			stop_signal = TRUE;
-			break;
-		case GDK_End:
-			file_last_image();
-			stop_signal = TRUE;
-			break;
-		case GDK_Delete:
-			file_util_delete(image_get_path(), NULL);
-			stop_signal = TRUE;
-			break;
-		case GDK_Escape:
-			interrupt_thumbs();
-			stop_signal = TRUE;
-			break;
-		case 'Q': case 'q':
-			if (event->state == 0 || (event->state & GDK_MODIFIER_MASK) == GDK_LOCK_MASK)
+		if (remote_commands[i].needs_extra)
+			{
+			if (remote_commands[i].opt_s &&
+			    strncmp(remote_commands[i].opt_s, text, strlen(remote_commands[i].opt_s)) == 0)
+				{
+				if (offset) *offset = text + strlen(remote_commands[i].opt_s);
+				return &remote_commands[i];
+				}
+			else if (remote_commands[i].opt_l &&
+				 strncmp(remote_commands[i].opt_l, text, strlen(remote_commands[i].opt_l)) == 0)
+				{
+				if (offset) *offset = text + strlen(remote_commands[i].opt_l);
+				return &remote_commands[i];
+				}
+			}
+		else
+			{
+			if ((remote_commands[i].opt_s && strcmp(remote_commands[i].opt_s, text) == 0) ||
+			    (remote_commands[i].opt_l && strcmp(remote_commands[i].opt_l, text) == 0))
 				{
-				exit_gqview();
-				return FALSE;
+				if (offset) *offset = text;
+				return &remote_commands[i];
 				}
-			break;
+			}
+
+		i++;
+		}
+
+	return NULL;
+}
+
+static void gqview_remote_cb(RemoteConnection *rc, const gchar *text, gpointer data)
+{
+	RemoteCommandEntry *entry;
+	const gchar *offset;
+
+	entry = gqview_remote_command_find(text, &offset);
+	if (entry && entry->func)
+		{
+		entry->func(offset, data);
+		}
+	else
+		{
+		printf("unknown remote command:%s\n", text);
+		}
+}
+
+static void gqview_remote_help(void)
+{
+	gint i;
+
+	print_term(_("Remote command list:\n"));
+
+	i = 0;
+	while (remote_commands[i].func != NULL)
+		{
+		if (remote_commands[i].description)
+			{
+			gchar *buf;
+
+			buf = g_strdup_printf("  %-3s%s %-20s %s\n",
+				(remote_commands[i].opt_s) ? remote_commands[i].opt_s : "",
+				(remote_commands[i].opt_s && remote_commands[i].opt_l) ? "," : " ",
+				(remote_commands[i].opt_l) ? remote_commands[i].opt_l : "",
+				_(remote_commands[i].description));
+
+			print_term(buf);
+			g_free(buf);
+			}
+		i++;
+		}
+}
+
+static GList *gqview_remote_build_list(GList *list, int argc, char *argv[])
+{
+	gint i;
+
+	i = 1;
+	while (i < argc)
+		{
+		RemoteCommandEntry *entry;
+
+		entry = gqview_remote_command_find(argv[i], NULL);
+		if (entry)
+			{
+			list = g_list_append(list, argv[i]);
+			}
+		i++;
 		}
 
-	if (event->state & GDK_SHIFT_MASK)
+	return list;
+}
+
+static void gqview_remote_control(const gchar *arg_exec, GList *remote_list, const gchar *path,
+				  GList *cmd_list, GList *collection_list)
+{
+	RemoteConnection *rc;
+	gint started = FALSE;
+	gchar *buf;
+
+	buf = g_strconcat(homedir(), "/", GQVIEW_RC_DIR, "/.command", NULL);
+	rc = remote_client_open(buf);
+	if (!rc)
+		{
+		GString *command;
+		GList *work;
+		gint retry_count = 12;
+		gint blank = FALSE;
+
+		print_term(_("Remote GQview not running, starting..."));
+		command = g_string_new(arg_exec);
+
+		work = remote_list;
+		while (work)
+			{
+			gchar *text;
+			RemoteCommandEntry *entry;
+
+			text = work->data;
+			work = work->next;
+
+			entry = gqview_remote_command_find(text, NULL);
+			if (entry)
+				{
+				if (entry->prefer_command_line)
+					{
+					remote_list = g_list_remove(remote_list, text);
+					g_string_append(command, " ");
+					g_string_append(command, text);
+					}
+				if (entry->opt_l && strcmp(entry->opt_l, "file:") == 0)
+					{
+					blank = TRUE;
+					}
+				}
+			}
+
+		if (blank || cmd_list || path) g_string_append(command, " --blank");
+		if (debug) g_string_append(command, " --debug");
+
+		g_string_append(command, " &");
+		system(command->str);
+		g_string_free(command, TRUE);
+
+		while (!rc && retry_count > 0)
+			{
+			usleep((retry_count > 10) ? 500000 : 1000000);
+			rc = remote_client_open(buf);
+			if (!rc) print_term(".");
+			retry_count--;
+			}
+
+		print_term("\n");
+
+		started = TRUE;
+		}
+	g_free(buf);
+
+	if (rc)
 		{
-		x *= 3;
-		y *= 3;
+		GList *work;
+		const gchar *prefix;
+		gint use_path = TRUE;
+		gint sent = FALSE;
+
+		work = remote_list;
+		while (work)
+			{
+			gchar *text;
+			RemoteCommandEntry *entry;
+
+			text = work->data;
+			work = work->next;
+
+			entry = gqview_remote_command_find(text, NULL);
+			if (entry &&
+			    entry->opt_l &&
+			    strcmp(entry->opt_l, "file:") == 0) use_path = FALSE;
+
+			remote_client_send(rc, text);
+
+			sent = TRUE;
+			}
+
+		if (cmd_list && cmd_list->next)
+			{
+			prefix = "--list-add:";
+			remote_client_send(rc, "--list-clear");
+			}
+		else
+			{
+			prefix = "file:";
+			}
+
+		work = cmd_list;
+		while (work)
+			{
+			const gchar *name;
+			gchar *text;
+
+			name = work->data;
+			work = work->next;
+
+			text = g_strconcat(prefix, name, NULL);
+			remote_client_send(rc, text);
+			g_free(text);
+
+			sent = TRUE;
+			}
+
+		if (path && !cmd_list && use_path)
+			{
+			gchar *text;
+
+			text = g_strdup_printf("file:%s", path);
+			remote_client_send(rc, text);
+			g_free(text);
+
+			sent = TRUE;
+			}
+
+		work = collection_list;
+		while (work)
+			{
+			const gchar *name;
+			gchar *text;
+
+			name = work->data;
+			work = work->next;
+
+			text = g_strdup_printf("file:%s", name);
+			remote_client_send(rc, text);
+			g_free(text);
+
+			sent = TRUE;
+			}
+
+		if (!started && !sent)
+			{
+			remote_client_send(rc, "raise");
+			}
+		}
+	else
+		{
+		print_term(_("Remote not available\n"));
 		}
 
-	if (x != 0 || y!= 0)
-		{
-		keyboard_scroll_calc(&x, &y, event);
-		image_scroll(x, y);
-		}
-
-	if (stop_signal) gtk_signal_emit_stop_by_name(GTK_OBJECT(widget), "key_press_event");
-
-	return stop_signal;
+	_exit(0);
 }
 
 /*
@@ -262,11 +811,39 @@
  *-----------------------------------------------------------------------------
  */ 
 
+static gint startup_blank = FALSE;
 static gint startup_full_screen = FALSE;
 static gint startup_in_slideshow = FALSE;
+static gint startup_command_line_collection = FALSE;
 
-static void parse_command_line(int argc, char *argv[], gchar **path, gchar **file)
+
+static void parse_command_line_add_file(const gchar *new_path, gchar **path, gchar **file,
+				        GList **list, GList **collection_list)
 {
+	gchar *path_parsed;
+
+	path_parsed = g_strdup(new_path);
+	parse_out_relatives(path_parsed);
+
+	if (file_extension_match(new_path, ".gqv"))
+		{
+		*collection_list = g_list_append(*collection_list, path_parsed);
+		}
+	else
+		{
+		if (!*path) *path = remove_level_from_path(path_parsed);
+		if (!*file) *file = g_strdup(path_parsed);
+		*list = g_list_append(*list, path_parsed);
+		}
+}
+
+static void parse_command_line(int argc, char *argv[], gchar **path, gchar **file,
+			       GList **cmd_list, GList **collection_list)
+{
+	GList *list = NULL;
+	GList *remote_list = NULL;
+	gint remote_do = FALSE;
+
 	if (argc > 1)
 		{
 		gint i;
@@ -274,8 +851,8 @@
 		i = 1;
 		while (i < argc)
 			{
-			gchar *cmd_line = argv[i];
-			gchar *cmd_all = g_strconcat(base_dir, "/", cmd_line, NULL);
+			const gchar *cmd_line = argv[i];
+			gchar *cmd_all = concat_dir_and_file(base_dir, cmd_line);
 
 			if (!*path && cmd_line[0] == '/' && isdir(cmd_line))
 				{
@@ -285,33 +862,34 @@
 				{
 				*path = g_strdup(cmd_all);
 				}
-			else if (!*file && cmd_line[0] == '/' && isfile(cmd_line))
+			else if (cmd_line[0] == '/' && isfile(cmd_line))
 				{
-				g_free(*path);
-				*path = remove_level_from_path(cmd_line);
-				*file = g_strdup(cmd_line);
+				parse_command_line_add_file(cmd_line, path, file, &list, collection_list);
 				}
-			else if (!*file && isfile(cmd_all))
+			else if (isfile(cmd_all))
 				{
-				g_free(*path);
-				*path = remove_level_from_path(cmd_all);
-				*file = g_strdup(cmd_all);
+				parse_command_line_add_file(cmd_all, path, file, &list, collection_list);
 				}
 			else if (strcmp(cmd_line, "--debug") == 0)
 				{
-				debug = TRUE;
-				printf("debugging output enabled\n");
+				/* we now increment the debug state for verbosity */
+				debug++;
+				printf("debugging output enabled (level %d)\n", debug);
 				}
 			else if (strcmp(cmd_line, "+t") == 0 ||
 				 strcmp(cmd_line, "--with-tools") == 0)
 				{
 				tools_float = FALSE;
 				tools_hidden = FALSE;
+
+				remote_list = g_list_append(remote_list, "+t");
 				}
 			else if (strcmp(cmd_line, "-t") == 0 ||
 				 strcmp(cmd_line, "--without-tools") == 0)
 				{
 				tools_hidden = TRUE;
+
+				remote_list = g_list_append(remote_list, "-t");
 				}
 			else if (strcmp(cmd_line, "-f") == 0 ||
 				 strcmp(cmd_line, "--fullscreen") == 0)
@@ -323,24 +901,76 @@
 				{
 				startup_in_slideshow = TRUE;
 				}
+			else if (strcmp(cmd_line, "-l") == 0 ||
+				 strcmp(cmd_line, "--list") == 0)
+				{
+				startup_command_line_collection = TRUE;
+				}
+			else if (strcmp(cmd_line, "-r") == 0 ||
+				 strcmp(cmd_line, "--remote") == 0)
+				{
+				if (!remote_do)
+					{
+					remote_do = TRUE;
+					remote_list = gqview_remote_build_list(remote_list, argc, argv);
+					}
+				}
+			else if (strcmp(cmd_line, "-rh") == 0 ||
+				 strcmp(cmd_line, "--remote-help") == 0)
+				{
+				gqview_remote_help();
+				exit (0);
+				}
+			else if (strcmp(cmd_line, "--blank") == 0)
+				{
+				startup_blank = TRUE;
+				}
+			else if (strcmp(cmd_line, "-v") == 0 ||
+				 strcmp(cmd_line, "--version") == 0)
+				{
+				printf("GQview %s\n", VERSION);
+				exit (0);
+				}
+			else if (strcmp(cmd_line, "--alternate") == 0)
+				{
+				/* enable faster experimental algorithm */
+				printf("Alternate similarity algorithm enabled\n");
+				image_sim_alternate_set(TRUE);
+				}
 			else if (strcmp(cmd_line, "-h") == 0 ||
 				 strcmp(cmd_line, "--help") == 0)
 				{
-				printf("GQview version %s\n", VERSION);
-				printf(_("Usage: gqview [options] [path]\n\n"));
-				printf(_("valid options are:\n"));
-				printf(_("  +t, --with-tools           force show of tools\n"));
-				printf(_("  -t, --without-tools        force hide of tools\n"));
-				printf(_("  -f, --fullscreen           start in full screen mode\n"));
-				printf(_("  -s, --slideshow            start in slideshow mode\n"));
-				printf(_("  --debug                    turn on debug output\n"));
-				printf(_("  -h, --help                 show this message\n\n"));
+				printf("GQview %s\n", VERSION);
+				print_term(_("Usage: gqview [options] [path]\n\n"));
+				print_term(_("valid options are:\n"));
+				print_term(_("  +t, --with-tools           force show of tools\n"));
+				print_term(_("  -t, --without-tools        force hide of tools\n"));
+				print_term(_("  -f, --fullscreen           start in full screen mode\n"));
+				print_term(_("  -s, --slideshow            start in slideshow mode\n"));
+				print_term(_("  -l, --list                 open collection window for command line\n"));
+				print_term(_("  -r, --remote               send following commands to open window\n"));
+				print_term(_("  -rh,--remote-help          print remote command list\n"));
+				print_term(_("  --debug                    turn on debug output\n"));
+				print_term(_("  -v, --version              print version info\n"));
+				print_term(_("  -h, --help                 show this message\n\n"));
+				
+#if 0
+				/* these options are not officially supported!
+				 * only for testing new features, no need to translate them */
+				print_term(  "  --alternate                use alternate similarity algorithm\n");
+#endif
+				
 				exit (0);
 				}
-			else 
+			else if (!remote_do)
 				{
-				printf(_("invalid or ignored: %s\nUse -help for options\n"), cmd_line);
+				gchar *buf;
+
+				buf = g_strdup_printf(_("invalid or ignored: %s\nUse --help for options\n"), cmd_line);
+				print_term(buf);
+				g_free(buf);
 				}
+
 			g_free(cmd_all);
 			i++;
 			}
@@ -348,6 +978,22 @@
 		parse_out_relatives(*path);
 		parse_out_relatives(*file);
 		}
+
+	if (remote_do)
+		{
+		gqview_remote_control(argv[0], remote_list, *path, list, *collection_list);
+		}
+	g_list_free(remote_list);
+
+	if (list && list->next)
+		{
+		*cmd_list = list;
+		}
+	else
+		{
+		path_list_free(list);
+		*cmd_list = NULL;
+		}
 }
 
 /*
@@ -356,100 +1002,365 @@
  *-----------------------------------------------------------------------------
  */ 
 
-static void setup_default_options()
+#define RC_HISTORY_NAME "history"
+
+static void keys_load(void)
+{
+	gchar *path;
+
+	path = g_strconcat(homedir(), "/", GQVIEW_RC_DIR, "/", RC_HISTORY_NAME, NULL);
+	history_list_load(path);
+	g_free(path);
+}
+
+static void keys_save(void)
+{
+	gchar *path;
+
+	path = g_strconcat(homedir(), "/", GQVIEW_RC_DIR, "/", RC_HISTORY_NAME, NULL);
+	history_list_save(path);
+	g_free(path);
+}
+
+static void check_for_home_path(gchar *path)
 {
+	gchar *buf;
+
+	buf = g_strconcat(homedir(), "/", path, NULL);
+	if (!isdir(buf))
+		{
+		gchar *tmp;
+
+		tmp = g_strdup_printf(_("Creating GQview dir:%s\n"), buf);
+		print_term(tmp);
+		g_free(tmp);
+
+		if (!mkdir_utf8(buf, 0755))
+			{
+			tmp = g_strdup_printf(_("Could not create dir:%s\n"), buf);
+			print_term(tmp);
+			g_free(tmp);
+			}
+		}
+	g_free(buf);
+}
+
+static void setup_default_options(void)
+{
+	gchar *path;
 	gint i;
 
-	for(i=0; i<8; i++)
+	for (i = 0; i < GQVIEW_EDITOR_SLOTS; i++)
 		{
 		editor_name[i] = NULL;
 		editor_command[i] = NULL;
 		}
 
-	editor_name[0] = g_strdup(_("The Gimp"));
-	editor_command[0] = g_strdup("gimp");
-
-	editor_name[1] = g_strdup(_("Electric Eyes"));
-	editor_command[1] = g_strdup("ee");
+	editor_reset_defaults();
 
-	editor_name[2] = g_strdup(_("XV"));
-	editor_command[2] = g_strdup("xv");
+	bookmark_add_default(_("Home"), homedir());
+	path = concat_dir_and_file(homedir(), "Desktop");
+	bookmark_add_default(_("Desktop"), path);
+	g_free(path);
+	path = concat_dir_and_file(homedir(), GQVIEW_RC_DIR_COLLECTIONS);
+	bookmark_add_default(_("Collections"), path);
+	g_free(path);
 
-	editor_name[3] = g_strdup(_("Xpaint"));
-	editor_command[3] = g_strdup("xpaint");
-
-	custom_filter = g_strdup(".eim;");
+	g_free(safe_delete_path);
+	safe_delete_path = concat_dir_and_file(homedir(), GQVIEW_RC_DIR_TRASH);
 }
 
-void exit_gqview()
+static void exit_gqview_final(void)
 {
-	full_screen_stop();
+	gchar *path;
+	gchar *pathl;
+	LayoutWindow *lw = NULL;
+
+	remote_close(gqview_remote);
+
+	collect_manager_flush();
 
-	gdk_window_get_position (mainwindow->window, &main_window_x, &main_window_y);
-	gdk_window_get_size(mainwindow->window, &main_window_w, &main_window_h);
+	if (layout_valid(&lw))
+		{
+		main_window_maximized =  window_maximized(lw->window);
+		if (!main_window_maximized)
+			{
+			layout_geometry_get(NULL, &main_window_x, &main_window_y,
+					    &main_window_w, &main_window_h);
+			}
+		}
 
-	if (toolwindow)
-		{
-		gdk_window_get_position (toolwindow->window, &float_window_x, &float_window_y);
-		gdk_window_get_size(toolwindow->window, &float_window_w, &float_window_h);
-		}
+	layout_geometry_get_dividers(NULL, &window_hdivider_pos, &window_vdivider_pos);
+
+	layout_views_get(NULL, &layout_view_tree, &layout_view_icons);
+
+	thumbnails_enabled = layout_thumb_get(NULL);
+	layout_sort_get(NULL, &file_sort_method, &file_sort_ascending);
+
+	layout_geometry_get_tools(NULL, &float_window_x, &float_window_y,
+				  &float_window_w, &float_window_h, &float_window_divider);
+	layout_tools_float_get(NULL, &tools_float, &tools_hidden);
+	toolbar_hidden = layout_toolbar_hidden(NULL);
+
 	save_options();
+	keys_save();
+
+	path = g_strconcat(homedir(), "/", GQVIEW_RC_DIR, "/accels", NULL);
+	pathl = path_from_utf8(path);
+	gtk_accel_map_save(pathl);
+	g_free(pathl);
+	g_free(path);
 
 	gtk_main_quit();
 }
 
+static GenericDialog *exit_dialog = NULL;
+
+static void exit_confirm_cancel_cb(GenericDialog *gd, gpointer data)
+{
+	exit_dialog = NULL;
+	generic_dialog_close(gd);
+}
+
+static void exit_confirm_exit_cb(GenericDialog *gd, gpointer data)
+{
+	exit_dialog = NULL;
+	generic_dialog_close(gd);
+	exit_gqview_final();
+}
+
+static gint exit_confirm_dlg(void)
+{
+	GtkWidget *parent;
+	LayoutWindow *lw;
+
+	if (exit_dialog)
+		{
+		gtk_window_present(GTK_WINDOW(exit_dialog->dialog));
+		return TRUE;
+		}
+
+	if (!collection_window_modified_exists()) return FALSE;
+
+	parent = NULL;
+	lw = NULL;
+	if (layout_valid(&lw))
+		{
+		parent = lw->window;
+		}
+
+	exit_dialog = generic_dialog_new(_("GQview - exit"),
+				"GQview", "exit", parent, FALSE,
+				exit_confirm_cancel_cb, NULL);
+	generic_dialog_add_message(exit_dialog, GTK_STOCK_DIALOG_QUESTION,
+				   _("Quit GQview"), _("Collections have been modified. Quit anyway?"));
+	generic_dialog_add_button(exit_dialog, GTK_STOCK_QUIT, NULL, exit_confirm_exit_cb, TRUE);
+
+	gtk_widget_show(exit_dialog->dialog);
+
+	return TRUE;
+}
+
+void exit_gqview(void)
+{
+	layout_image_full_screen_stop(NULL);
+
+	if (exit_confirm_dlg()) return;
+
+	exit_gqview_final();
+}
+
 int main (int argc, char *argv[])
 {
+	LayoutWindow *lw;
+	gchar *path = NULL;
 	gchar *cmd_path = NULL;
 	gchar *cmd_file = NULL;
+	GList *cmd_list = NULL;
+	GList *collection_list = NULL;
+	CollectionData *first_collection = NULL;
+	gchar *buf;
+	gchar *bufl;
 
 	/* setup locale, i18n */
 	gtk_set_locale();
 	bindtextdomain (PACKAGE, LOCALEDIR);
+	bind_textdomain_codeset (PACKAGE, "UTF-8");
 	textdomain (PACKAGE);
 
 	/* setup random seed for random slideshow */
-        srand (time (0));
-
-	gtk_init (&argc, &argv);
-	gdk_imlib_init();
+        srand(time(NULL));
 
-	/* push the correct color depths to gtk, (for 8-bit psuedo color displays)
-	 * they should be popped, too, I guess...
-	 */
-	gtk_widget_push_visual(gdk_imlib_get_visual());
-	gtk_widget_push_colormap(gdk_imlib_get_colormap());
+#if 0
+	printf("GQview %s, This is a beta release.\n", VERSION);
+#endif
 
+	layout_order = g_strdup("123");
 	setup_default_options();
 	load_options();
 
-	parse_command_line(argc, argv, &cmd_path, &cmd_file);
+	parse_command_line(argc, argv, &cmd_path, &cmd_file, &cmd_list, &collection_list);
+
+	gtk_init (&argc, &argv);
+
+	if (gtk_major_version < GTK_MAJOR_VERSION ||
+	    (gtk_major_version == GTK_MAJOR_VERSION && gtk_minor_version < GTK_MINOR_VERSION) )
+		{
+		gchar *msg;
+		print_term("!!! This is a friendly warning.\n");
+		print_term("!!! The version of GTK+ in use now is older than when GQview was compiled.\n");
+		msg = g_strdup_printf("!!!  compiled with GTK+-%d.%d\n", GTK_MAJOR_VERSION, GTK_MINOR_VERSION);
+		print_term(msg);
+		g_free(msg);
+		msg = g_strdup_printf("!!!   running with GTK+-%d.%d\n", gtk_major_version, gtk_minor_version);
+		print_term(msg);
+		g_free(msg);
+		print_term("!!! GQview may quit unexpectedly with a relocation error.\n");
+		}
+
+	check_for_home_path(GQVIEW_RC_DIR);
+	check_for_home_path(GQVIEW_RC_DIR_COLLECTIONS);
+	check_for_home_path(GQVIEW_CACHE_RC_THUMB);
+	check_for_home_path(GQVIEW_CACHE_RC_METADATA);
+
+	keys_load();
+	filter_add_defaults();
+	filter_rebuild();
+
+	buf = g_strconcat(homedir(), "/", GQVIEW_RC_DIR, "/accels", NULL);
+	bufl = path_from_utf8(buf);
+	gtk_accel_map_load(bufl);
+	g_free(bufl);
+	g_free(buf);
 
-	if (cmd_path)
-		current_path = g_strdup(cmd_path);
+	if (startup_blank)
+		{
+		g_free(cmd_path);
+		cmd_path = NULL;
+		g_free(cmd_file);
+		cmd_file = NULL;
+		path_list_free(cmd_list);
+		cmd_list = NULL;
+		path_list_free(collection_list);
+		collection_list = NULL;
+
+		path = NULL;
+		}
+	else if (cmd_path)
+		{
+		path = g_strdup(cmd_path);
+		}
 	else if (startup_path_enable && startup_path && isdir(startup_path))
-		current_path = g_strdup(startup_path);
+		{
+		path = g_strdup(startup_path);
+		}
 	else
-		current_path = get_current_dir();
+		{
+		path = get_current_dir();
+		}
+
+	lw = layout_new(NULL, tools_float, tools_hidden);
+	layout_sort_set(lw, file_sort_method, file_sort_ascending);
+
+	if (collection_list && !startup_command_line_collection)
+		{
+		GList *work;
+
+		work = collection_list;
+		while (work)
+			{
+			CollectWindow *cw;
+			const gchar *path;
 
-	create_main_window();
-	update_edit_menus(mainwindow_accel_grp);
-	rebuild_file_filter();
-	filelist_refresh();
+			path = work->data;
+			work = work->next;
+
+			cw = collection_window_new(path);
+			if (!first_collection && cw) first_collection = cw->cd;
+			}
+		}
+
+	if (cmd_list ||
+	    (startup_command_line_collection && collection_list))
+		{
+		CollectionData *cd;
+		GList *work;
+
+		if (startup_command_line_collection)
+			{
+			CollectWindow *cw;
+
+			cw = collection_window_new("");
+			cd = cw->cd;
+			}
+		else
+			{
+			cd = collection_new("");	/* if we pass NULL, untitled counter is falsely increm. */
+			gqview_command_collection = cd;
+			}
+
+		g_free(cd->path);
+		cd->path = NULL;
+		g_free(cd->name);
+		cd->name = g_strdup(_("Command line"));
+
+		collection_path_changed(cd);
 
-	init_dnd();
+		work = cmd_list;
+		while (work)
+			{
+			collection_add(cd, (gchar *)work->data, FALSE);
+			work = work->next;
+			}
+
+		work = collection_list;
+		while (work)
+			{
+			collection_load(cd, (gchar *)work->data, TRUE);
+			work = work->next;
+			}
+
+		layout_set_path(lw, path);
+		if (cd->list) layout_image_set_collection(lw, cd, cd->list->data);
 
-	while(gtk_events_pending()) gtk_main_iteration();
-	image_change_to(cmd_file);
+		/* mem leak, we never unref this collection when !startup_command_line_collection
+		 * (the image view of the main window does not hold a ref to the collection)
+		 * this is sort of unavoidable, for if it did hold a ref, next/back
+		 * may not work as expected when closing collection windows.
+		 *
+		 * collection_unref(cd);
+		 */
+
+		}
+	else if (cmd_file)
+		{
+		layout_set_path(lw, cmd_file);
+		}
+	else
+		{
+		layout_set_path(lw, path);
+		if (first_collection)
+			{
+			layout_image_set_collection(lw, first_collection,
+						    collection_get_first(first_collection));
+			}
+		}
 
 	g_free(cmd_path);
 	g_free(cmd_file);
+	path_list_free(cmd_list);
+	path_list_free(collection_list);
+	g_free(path);
 
-	if (startup_full_screen) full_screen_toggle();
-	if (startup_in_slideshow) slideshow_start();
+	if (startup_full_screen) layout_image_full_screen_start(lw);
+	if (startup_in_slideshow) layout_image_slideshow_start(lw);
+
+	buf = g_strconcat(homedir(), "/", GQVIEW_RC_DIR, "/.command", NULL);
+	gqview_remote = remote_server_open(buf);
+	remote_server_subscribe(gqview_remote, gqview_remote_cb, NULL);
+	g_free(buf);
 
 	gtk_main ();
 	return 0;
 }
 
-