diff src/layout_image.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
children 17acca639a86
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/layout_image.c	Sat Feb 26 00:13:35 2005 +0000
@@ -0,0 +1,1437 @@
+/*
+ * 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 "layout_image.h"
+
+#include "collect.h"
+#include "dnd.h"
+#include "editors.h"
+#include "filelist.h"
+#include "fullscreen.h"
+#include "image.h"
+#include "image-overlay.h"
+#include "img-view.h"
+#include "info.h"
+#include "layout.h"
+#include "layout_util.h"
+#include "menu.h"
+#include "pixbuf_util.h"
+#include "utilops.h"
+#include "slideshow.h"
+#include "ui_bookmark.h"
+#include "ui_fileops.h"
+#include "ui_menu.h"
+
+#include <gdk/gdkkeysyms.h> /* for keyboard values */
+
+
+static GtkWidget *layout_image_pop_menu(LayoutWindow *lw);
+static void layout_image_set_buttons(LayoutWindow *lw);
+
+/*
+ *----------------------------------------------------------------------------
+ * full screen overlay
+ *----------------------------------------------------------------------------
+ */
+
+static void layout_image_overlay_set(LayoutWindow *lw, gint enable)
+{
+	lw->full_screen_overlay_on = enable;
+
+	if (!lw->full_screen) return;
+
+	if (enable)
+		{
+		if (lw->full_screen_overlay_id == -1)
+			{
+			lw->full_screen_overlay_id = image_overlay_info_enable(lw->image);
+			}
+		}
+	else
+		{
+		if (lw->full_screen_overlay_id != -1)
+			{
+			image_overlay_info_disable(lw->image, lw->full_screen_overlay_id);
+			lw->full_screen_overlay_id = -1;
+			}
+		}
+}
+
+void layout_image_overlay_update(LayoutWindow *lw)
+{
+	if (!lw || !lw->full_screen) return;
+	if (lw->full_screen_overlay_id != -1) image_overlay_update(lw->image, lw->full_screen_overlay_id);
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * full screen
+ *----------------------------------------------------------------------------
+ */
+
+static void layout_image_fullscreen_menu_pos_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
+{
+	LayoutWindow *lw = data;
+
+	if (!lw->full_screen) return;
+
+	gdk_window_get_origin(lw->full_screen->imd->image->window, x, y);
+	popup_menu_position_clamp(menu, x, y, 0);
+}
+
+static void layout_image_full_screen_menu_popup(LayoutWindow *lw)
+{
+	GtkWidget *menu;
+
+	menu = layout_image_pop_menu(lw);
+	gtk_menu_popup(GTK_MENU(menu), NULL, NULL, layout_image_fullscreen_menu_pos_cb, lw, 0, GDK_CURRENT_TIME);
+}
+
+static gint layout_image_full_screen_key_press_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
+{
+	LayoutWindow *lw = data;
+	gint stop_signal = FALSE;
+	gint x = 0;
+	gint y = 0;
+
+	switch (event->keyval)
+		{
+		case GDK_Left: case GDK_KP_Left:
+			x -= 1;
+			stop_signal = TRUE;
+			break;
+		case GDK_Right: case GDK_KP_Right:
+			x += 1;
+			stop_signal = TRUE;
+			break;
+		case GDK_Up: case GDK_KP_Up:
+			y -= 1;
+			stop_signal = TRUE;
+			break;
+		case GDK_Down: case GDK_KP_Down:
+			y += 1;
+			stop_signal = TRUE;
+			break;
+		}
+
+	if ( !(event->state & GDK_CONTROL_MASK) )
+	    switch (event->keyval)
+		{
+		case '+': case '=': case GDK_KP_Add:
+			layout_image_zoom_adjust(lw, get_zoom_increment());
+			break;
+		case '-': case GDK_KP_Subtract:
+			layout_image_zoom_adjust(lw, -get_zoom_increment());
+			break;
+		case 'X': case 'x': case GDK_KP_Multiply:
+			layout_image_zoom_set(lw, 0.0);
+			break;
+		case 'Z': case 'z': case GDK_KP_Divide:
+		case '1':
+			layout_image_zoom_set(lw, 1.0);
+			break;
+		case '2':
+			layout_image_zoom_set(lw, 2.0);
+			break;
+		case '3':
+			layout_image_zoom_set(lw, 3.0);
+			break;
+		case '4':
+			layout_image_zoom_set(lw, 4.0);
+			break;
+		case '7':
+			layout_image_zoom_set(lw, -4.0);
+			break;
+		case '8':
+			layout_image_zoom_set(lw, -3.0);
+			break;
+		case '9':
+			layout_image_zoom_set(lw, -2.0);
+			break;
+		case 'W': case 'w':
+			layout_image_zoom_set_fill_geometry(lw, FALSE);
+			break;
+		case 'H': case 'h':
+			layout_image_zoom_set_fill_geometry(lw, TRUE);
+			break;
+		case GDK_Page_Up: case GDK_KP_Page_Up:
+		case GDK_BackSpace:
+		case 'B': case 'b':
+			layout_image_prev(lw);
+			stop_signal = TRUE;
+			break;
+		case GDK_Page_Down: case GDK_KP_Page_Down:
+		case GDK_space:
+		case 'N': case 'n':
+			layout_image_next(lw);
+			stop_signal = TRUE;
+			break;
+		case GDK_Home: case GDK_KP_Home:
+			layout_image_first(lw);
+			stop_signal = TRUE;
+			break;
+		case GDK_End: case GDK_KP_End:
+			layout_image_last(lw);
+			stop_signal = TRUE;
+			break;
+		case ']':
+			layout_image_alter(lw, ALTER_ROTATE_90);
+			stop_signal = TRUE;
+			break;
+		case '[':
+			layout_image_alter(lw, ALTER_ROTATE_90_CC);
+			stop_signal = TRUE;
+			break;
+		case GDK_Delete: case GDK_KP_Delete:
+			if (enable_delete_key)
+				{
+				file_util_delete(layout_image_get_path(lw), NULL, widget);
+				stop_signal = TRUE;
+				}
+			break;
+		case GDK_Escape:
+			layout_image_full_screen_stop(lw);
+			stop_signal = TRUE;
+			break;
+		case 'R': case 'r':
+			layout_refresh(lw);
+			break;
+		case 'S': case 's':
+			layout_image_slideshow_toggle(lw);
+			break;
+		case 'P': case 'p':
+			layout_image_slideshow_pause_toggle(lw);
+			break;
+		case 'F': case 'f':
+		case 'V': case 'v':
+			layout_image_full_screen_stop(lw);
+			break;
+		case GDK_Menu:
+		case GDK_F10:
+			layout_image_full_screen_menu_popup(lw);
+			stop_signal = TRUE;
+			break;
+		case 'I': case 'i':
+			layout_image_overlay_set(lw, !(lw->full_screen_overlay_on));
+			break;
+		}
+
+	if (event->state & GDK_CONTROL_MASK)
+		{
+		gint n = -1;
+		switch (event->keyval)
+			{
+			case '1':
+				n = 0;
+				break;
+			case '2':
+				n = 1;
+				break;
+			case '3':
+				n = 2;
+				break;
+			case '4':
+				n = 3;
+				break;
+			case '5':
+				n = 4;
+				break;
+			case '6':
+				n = 5;
+				break;
+			case '7':
+				n = 6;
+				break;
+			case '8':
+				n = 7;
+				break;
+			case '9':
+				n = 8;
+				break;
+			case '0':
+				n = 9;
+				break;
+			case 'C': case 'c':
+				file_util_copy(layout_image_get_path(lw), NULL, NULL, widget);
+				break;
+			case 'M': case 'm':
+				file_util_move(layout_image_get_path(lw), NULL, NULL, widget);
+				break;
+			case 'R': case 'r':
+				file_util_rename(layout_image_get_path(lw), NULL, widget);
+				break;
+			case 'D': case 'd':
+				file_util_delete(layout_image_get_path(lw), NULL, widget);
+				break;
+			case 'P': case 'p':
+				info_window_new(layout_image_get_path(lw), NULL);
+				break;
+			case 'Q': case 'q':
+				exit_gqview();
+				return FALSE;
+				break;
+			}
+		if (n != -1)
+			{
+			layout_image_full_screen_stop(lw);
+			start_editor_from_file(n, layout_image_get_path(lw));
+			}
+		}
+	else if (event->state & GDK_SHIFT_MASK)
+		{
+		switch (event->keyval)
+			{
+			case 'R': case 'r':
+				layout_image_alter(lw, ALTER_ROTATE_180);
+				stop_signal = TRUE;
+				break;
+			case 'M': case 'm':
+				layout_image_alter(lw, ALTER_MIRROR);
+				stop_signal = TRUE;
+				break;
+			case 'F': case 'f':
+				layout_image_alter(lw, ALTER_FLIP);
+				stop_signal = TRUE;
+				break;
+			default:
+				break;
+			}
+		x *= 3;
+		y *= 3;
+		}
+
+	if (x != 0 || y!= 0)
+		{
+		keyboard_scroll_calc(&x, &y, event);
+		layout_image_scroll(lw, x, y);
+		}
+
+	return stop_signal;
+}
+
+static void layout_image_full_screen_stop_func(FullScreenData *fs, gpointer data)
+{
+	LayoutWindow *lw = data;
+
+	/* restore image window */
+	lw->image = fs->normal_imd;
+
+	if (lw->slideshow)
+		{
+		lw->slideshow->imd = lw->image;
+		}
+
+	lw->full_screen = NULL;
+	lw->full_screen_overlay_id = -1;
+}
+
+void layout_image_full_screen_start(LayoutWindow *lw)
+{
+	if (!layout_valid(&lw)) return;
+
+	if (lw->full_screen) return;
+
+	lw->full_screen = fullscreen_start(lw->window, lw->image,
+					   layout_image_full_screen_stop_func, lw);
+
+	/* set to new image window */
+	lw->image = lw->full_screen->imd;
+
+	if (lw->slideshow)
+		{
+		lw->slideshow->imd = lw->image;
+		}
+
+	layout_image_set_buttons(lw);
+
+	g_signal_connect(G_OBJECT(lw->full_screen->window), "key_press_event",
+			 G_CALLBACK(layout_image_full_screen_key_press_cb), lw);
+
+#if 0
+	gtk_widget_set_sensitive(lw->window, FALSE);
+	if (lw->tools) gtk_widget_set_sensitive(lw->tools, FALSE);
+#endif
+
+	layout_image_overlay_set(lw, lw->full_screen_overlay_on);
+}
+
+void layout_image_full_screen_stop(LayoutWindow *lw)
+{
+	if (!layout_valid(&lw)) return;
+	if (!lw->full_screen) return;
+
+	fullscreen_stop(lw->full_screen);
+
+#if 0
+	gtk_widget_set_sensitive(lw->window, TRUE);
+	if (lw->tools) gtk_widget_set_sensitive(lw->tools, TRUE);
+#endif
+}
+
+void layout_image_full_screen_toggle(LayoutWindow *lw)
+{
+	if (!layout_valid(&lw)) return;
+	if (lw->full_screen)
+		{
+		layout_image_full_screen_stop(lw);
+		}
+	else
+		{
+		layout_image_full_screen_start(lw);
+		}
+}
+
+gint layout_image_full_screen_active(LayoutWindow *lw)
+{
+	if (!layout_valid(&lw)) return FALSE;
+
+	return (lw->full_screen != NULL);
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * slideshow
+ *----------------------------------------------------------------------------
+ */
+
+static void layout_image_slideshow_next(LayoutWindow *lw)
+{
+	if (lw->slideshow) slideshow_next(lw->slideshow);
+}
+
+static void layout_image_slideshow_prev(LayoutWindow *lw)
+{
+	if (lw->slideshow) slideshow_prev(lw->slideshow);
+}
+
+static void layout_image_slideshow_stop_func(SlideShowData *ss, gpointer data)
+{
+	LayoutWindow *lw = data;
+
+	lw->slideshow = NULL;
+	layout_status_update_info(lw, NULL);
+}
+
+void layout_image_slideshow_start(LayoutWindow *lw)
+{
+	CollectionData *cd;
+	CollectInfo *info;
+
+	if (!layout_valid(&lw)) return;
+	if (lw->slideshow) return;
+
+	cd = image_get_collection(lw->image, &info);
+
+	if (cd && info)
+		{
+		lw->slideshow = slideshow_start_from_collection(lw->image, cd,
+				layout_image_slideshow_stop_func, lw, info);
+		}
+	else
+		{
+		lw->slideshow = slideshow_start(lw->image, lw,
+				layout_list_get_index(lw, layout_image_get_path(lw)),
+				layout_image_slideshow_stop_func, lw);
+		}
+
+	layout_status_update_info(lw, NULL);
+}
+
+/* note that slideshow will take ownership of the list, do not free it */
+void layout_image_slideshow_start_from_list(LayoutWindow *lw, GList *list)
+{
+	if (!layout_valid(&lw)) return;
+
+	if (lw->slideshow || !list)
+		{
+		path_list_free(list);
+		return;
+		}
+
+	lw->slideshow = slideshow_start_from_path_list(lw->image, list,
+						       layout_image_slideshow_stop_func, lw);
+
+	layout_status_update_info(lw, NULL);
+}
+
+void layout_image_slideshow_stop(LayoutWindow *lw)
+{
+	if (!layout_valid(&lw)) return;
+
+	if (!lw->slideshow) return;
+
+	slideshow_free(lw->slideshow);
+	/* the stop_func sets lw->slideshow to NULL for us */
+}
+
+void layout_image_slideshow_toggle(LayoutWindow *lw)
+{
+	if (!layout_valid(&lw)) return;
+
+	if (lw->slideshow)
+		{
+		layout_image_slideshow_stop(lw);
+		}
+	else
+		{
+		layout_image_slideshow_start(lw);
+		}
+}
+
+gint layout_image_slideshow_active(LayoutWindow *lw)
+{
+	if (!layout_valid(&lw)) return FALSE;
+
+	return (lw->slideshow != NULL);
+}
+
+gint layout_image_slideshow_pause_toggle(LayoutWindow *lw)
+{
+	gint ret;
+
+	if (!layout_valid(&lw)) return FALSE;
+
+	ret = slideshow_pause_toggle(lw->slideshow);
+
+	layout_status_update_info(lw, NULL);
+
+	return ret;
+}
+
+gint layout_image_slideshow_paused(LayoutWindow *lw)
+{
+	if (!layout_valid(&lw)) return FALSE;
+
+	return (slideshow_paused(lw->slideshow));
+}
+
+static gint layout_image_slideshow_continue_check(LayoutWindow *lw)
+{
+	if (!lw->slideshow) return FALSE;
+
+	if (!slideshow_should_continue(lw->slideshow))
+		{
+		layout_image_slideshow_stop(lw);
+		return FALSE;
+		}
+
+	return TRUE;
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * pop-up menus
+ *----------------------------------------------------------------------------
+ */
+
+static void li_pop_menu_zoom_in_cb(GtkWidget *widget, gpointer data)
+{
+	LayoutWindow *lw = data;
+
+	image_zoom_adjust(lw->image, get_zoom_increment());
+}
+
+static void li_pop_menu_zoom_out_cb(GtkWidget *widget, gpointer data)
+{
+	LayoutWindow *lw = data;
+	image_zoom_adjust(lw->image, -get_zoom_increment());
+}
+
+static void li_pop_menu_zoom_1_1_cb(GtkWidget *widget, gpointer data)
+{
+	LayoutWindow *lw = data;
+
+	image_zoom_set(lw->image, 1.0);
+}
+
+static void li_pop_menu_zoom_fit_cb(GtkWidget *widget, gpointer data)
+{
+	LayoutWindow *lw = data;
+
+	image_zoom_set(lw->image, 0.0);
+}
+
+static void li_pop_menu_edit_cb(GtkWidget *widget, gpointer data)
+{
+	LayoutWindow *lw;
+	gint n;
+
+	lw = submenu_item_get_data(widget);
+	n = GPOINTER_TO_INT(data);
+
+	layout_image_full_screen_stop(lw);
+
+	start_editor_from_file(n, layout_image_get_path(lw));
+}
+
+static void li_pop_menu_wallpaper_cb(GtkWidget *widget, gpointer data)
+{
+	LayoutWindow *lw = data;
+
+	layout_image_to_root(lw);
+}
+
+static void li_pop_menu_alter_cb(GtkWidget *widget, gpointer data)
+{
+	LayoutWindow *lw = data;
+	AlterType type;
+
+	lw = submenu_item_get_data(widget);
+	type = (AlterType)GPOINTER_TO_INT(data);
+
+	image_alter(lw->image, type);
+}
+
+static void li_pop_menu_info_cb(GtkWidget *widget, gpointer data)
+{
+	LayoutWindow *lw = data;
+
+	info_window_new(layout_image_get_path(lw), NULL);
+}
+
+static void li_pop_menu_new_cb(GtkWidget *widget, gpointer data)
+{
+	LayoutWindow *lw = data;
+
+	view_window_new(layout_image_get_path(lw));
+}
+
+static GtkWidget *li_pop_menu_click_parent(GtkWidget *widget, LayoutWindow *lw)
+{
+	GtkWidget *menu;
+	GtkWidget *parent;
+
+	menu = gtk_widget_get_toplevel(widget);
+	if (!menu) return NULL;
+
+	parent = g_object_get_data(G_OBJECT(menu), "click_parent");
+
+	if (!parent && lw->full_screen)
+		{
+		parent = lw->full_screen->imd->widget;
+		}
+
+	return parent;
+}
+
+static void li_pop_menu_copy_cb(GtkWidget *widget, gpointer data)
+{
+	LayoutWindow *lw = data;
+
+	file_util_copy(layout_image_get_path(lw), NULL, NULL,
+		       li_pop_menu_click_parent(widget, lw));
+}
+
+static void li_pop_menu_move_cb(GtkWidget *widget, gpointer data)
+{
+	LayoutWindow *lw = data;
+
+	file_util_move(layout_image_get_path(lw), NULL, NULL,
+		       li_pop_menu_click_parent(widget, lw));
+}
+
+static void li_pop_menu_rename_cb(GtkWidget *widget, gpointer data)
+{
+	LayoutWindow *lw = data;
+
+	file_util_rename(layout_image_get_path(lw), NULL,
+			 li_pop_menu_click_parent(widget, lw));
+}
+
+static void li_pop_menu_delete_cb(GtkWidget *widget, gpointer data)
+{
+	LayoutWindow *lw = data;
+
+	file_util_delete(layout_image_get_path(lw), NULL,
+			 li_pop_menu_click_parent(widget, lw));
+}
+
+static void li_pop_menu_slide_start_cb(GtkWidget *widget, gpointer data)
+{
+	LayoutWindow *lw = data;
+
+	layout_image_slideshow_start(lw);
+}
+
+static void li_pop_menu_slide_stop_cb(GtkWidget *widget, gpointer data)
+{
+	LayoutWindow *lw = data;
+
+	layout_image_slideshow_stop(lw);
+}
+
+static void li_pop_menu_slide_pause_cb(GtkWidget *widget, gpointer data)
+{
+	LayoutWindow *lw = data;
+
+	layout_image_slideshow_pause_toggle(lw);
+}
+
+static void li_pop_menu_full_screen_cb(GtkWidget *widget, gpointer data)
+{
+	LayoutWindow *lw = data;
+
+	layout_image_full_screen_toggle(lw);
+}
+
+static void li_pop_menu_hide_cb(GtkWidget *widget, gpointer data)
+{
+	LayoutWindow *lw = data;
+
+	layout_tools_hide_toggle(lw);
+}
+
+static GtkWidget *layout_image_pop_menu(LayoutWindow *lw)
+{
+	GtkWidget *menu;
+	GtkWidget *item;
+	GtkWidget *submenu;
+	const gchar *path;
+	gint fullscreen;
+
+	path = layout_image_get_path(lw);
+	fullscreen = layout_image_full_screen_active(lw);
+
+	menu = popup_menu_short_lived();
+
+	menu_item_add_stock(menu, _("Zoom _in"), GTK_STOCK_ZOOM_IN, G_CALLBACK(li_pop_menu_zoom_in_cb), lw);
+	menu_item_add_stock(menu, _("Zoom _out"), GTK_STOCK_ZOOM_OUT, G_CALLBACK(li_pop_menu_zoom_out_cb), lw);
+	menu_item_add_stock(menu, _("Zoom _1:1"), GTK_STOCK_ZOOM_100, G_CALLBACK(li_pop_menu_zoom_1_1_cb), lw);
+	menu_item_add_stock(menu, _("Fit image to _window"), GTK_STOCK_ZOOM_FIT, G_CALLBACK(li_pop_menu_zoom_fit_cb), lw);
+	menu_item_add_divider(menu);
+
+	submenu = submenu_add_edit(menu, &item, G_CALLBACK(li_pop_menu_edit_cb), lw);
+	if (!path) gtk_widget_set_sensitive(item, FALSE);
+	menu_item_add_divider(submenu);
+	menu_item_add(submenu, _("Set as _wallpaper"), G_CALLBACK(li_pop_menu_wallpaper_cb), lw);
+
+	item = submenu_add_alter(menu, G_CALLBACK(li_pop_menu_alter_cb), lw);
+
+	item = menu_item_add_stock(menu, _("_Properties"), GTK_STOCK_PROPERTIES, G_CALLBACK(li_pop_menu_info_cb), lw);
+	if (!path) gtk_widget_set_sensitive(item, FALSE);
+
+	item = menu_item_add_stock(menu, _("View in _new window"), GTK_STOCK_NEW, G_CALLBACK(li_pop_menu_new_cb), lw);
+	if (!path || fullscreen) gtk_widget_set_sensitive(item, FALSE);
+
+	menu_item_add_divider(menu);
+
+	item = menu_item_add_stock(menu, _("_Copy..."), GTK_STOCK_COPY, G_CALLBACK(li_pop_menu_copy_cb), lw);
+	if (!path) gtk_widget_set_sensitive(item, FALSE);
+	item = menu_item_add(menu, _("_Move..."), G_CALLBACK(li_pop_menu_move_cb), lw);
+	if (!path) gtk_widget_set_sensitive(item, FALSE);
+	item = menu_item_add(menu, _("_Rename..."), G_CALLBACK(li_pop_menu_rename_cb), lw);
+	if (!path) gtk_widget_set_sensitive(item, FALSE);
+	item = menu_item_add_stock(menu, _("_Delete..."), GTK_STOCK_DELETE, G_CALLBACK(li_pop_menu_delete_cb), lw);
+	if (!path) gtk_widget_set_sensitive(item, FALSE);
+
+	menu_item_add_divider(menu);
+
+	if (layout_image_slideshow_active(lw))
+		{
+		menu_item_add(menu, _("_Stop slideshow"), G_CALLBACK(li_pop_menu_slide_stop_cb), lw);
+		if (layout_image_slideshow_paused(lw))
+			{
+			item = menu_item_add(menu, _("Continue slides_how"),
+					     G_CALLBACK(li_pop_menu_slide_pause_cb), lw);
+			}
+		else
+			{
+			item = menu_item_add(menu, _("Pause slides_how"),
+					     G_CALLBACK(li_pop_menu_slide_pause_cb), lw);
+			}
+		}
+	else
+                {
+		menu_item_add(menu, _("_Start slideshow"), G_CALLBACK(li_pop_menu_slide_start_cb), lw);
+		item = menu_item_add(menu, _("Pause slides_how"), G_CALLBACK(li_pop_menu_slide_pause_cb), lw);
+		gtk_widget_set_sensitive(item, FALSE);
+		}
+
+	if (!fullscreen)
+		{
+		menu_item_add(menu, _("_Full screen"), G_CALLBACK(li_pop_menu_full_screen_cb), lw);
+		}
+	else
+		{
+		menu_item_add(menu, _("Exit _full screen"), G_CALLBACK(li_pop_menu_full_screen_cb), lw);
+		}
+
+	menu_item_add_divider(menu);
+
+	item = menu_item_add_check(menu, _("Hide file _list"), lw->tools_hidden,
+				   G_CALLBACK(li_pop_menu_hide_cb), lw);
+	if (fullscreen) gtk_widget_set_sensitive(item, FALSE);
+
+	return menu;
+}
+
+static void layout_image_menu_pos_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
+{
+	LayoutWindow *lw = data;
+
+	gdk_window_get_origin(lw->image->image->window, x, y);
+	popup_menu_position_clamp(menu, x, y, 0);
+}
+
+void layout_image_menu_popup(LayoutWindow *lw)
+{
+	GtkWidget *menu;
+
+	menu = layout_image_pop_menu(lw);
+	gtk_menu_popup(GTK_MENU(menu), NULL, NULL, layout_image_menu_pos_cb, lw, 0, GDK_CURRENT_TIME);
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * dnd
+ *----------------------------------------------------------------------------
+ */
+
+static void layout_image_dnd_receive(GtkWidget *widget, GdkDragContext *context,
+				     gint x, gint y,
+				     GtkSelectionData *selection_data, guint info,
+				     guint time, gpointer data)
+{
+	LayoutWindow *lw = data;
+
+	if (info == TARGET_URI_LIST || info == TARGET_APP_COLLECTION_MEMBER)
+		{
+		CollectionData *source;
+		GList *list;
+		GList *info_list;
+
+		if (info == TARGET_URI_LIST)
+			{
+			list = uri_list_from_text(selection_data->data, TRUE);
+			source = NULL;
+			info_list = NULL;
+			}
+		else
+			{
+			source = collection_from_dnd_data((gchar *)selection_data->data, &list, &info_list);
+			}
+
+		if (list)
+			{
+			gchar *path;
+
+			path = list->data;
+
+			if (isfile(path))
+				{
+				gchar *base;
+				gint row;
+
+				base = remove_level_from_path(path);
+				if (strcmp(base, layout_get_path(lw)) != 0)
+					{
+					layout_set_path(lw, base);
+					}
+				g_free(base);
+
+				row = layout_list_get_index(lw, path);
+				if (source && info_list)
+					{
+					layout_image_set_collection(lw, source, info_list->data);
+					}
+				else if (row == -1)
+					{
+					layout_image_set_path(lw, path);
+					}
+				else
+					{
+					layout_image_set_index(lw, row);
+					}
+				}
+			else if (isdir(path))
+				{
+				layout_set_path(lw, path);
+				layout_image_set_path(lw, NULL);
+				}
+			}
+
+		path_list_free(list);
+		g_list_free(info_list);
+		}
+}
+
+static void layout_image_dnd_get(GtkWidget *widget, GdkDragContext *context,
+				 GtkSelectionData *selection_data, guint info,
+				 guint time, gpointer data)
+{
+	LayoutWindow *lw = data;
+	const gchar *path;
+
+	path = layout_image_get_path(lw);
+
+	if (path)
+		{
+		gchar *text = NULL;
+		gint len;
+		gint plain_text;
+		GList *list;
+
+		switch (info)
+			{
+			case TARGET_URI_LIST:
+				plain_text = FALSE;
+				break;
+			case TARGET_TEXT_PLAIN:
+			default:
+				plain_text = TRUE;
+				break;
+			}
+		list = g_list_append(NULL, (gchar *)path);
+		text = uri_text_from_list(list, &len, plain_text);
+		g_list_free(list);
+		if (text)
+			{
+			gtk_selection_data_set (selection_data, selection_data->target,
+						8, text, len);
+			g_free(text);
+			}
+		}
+	else
+		{
+		gtk_selection_data_set (selection_data, selection_data->target,
+					8, NULL, 0);
+		}
+}
+
+static void layout_image_dnd_end(GtkWidget *widget, GdkDragContext *context, gpointer data)
+{
+	LayoutWindow *lw = data;
+	if (context->action == GDK_ACTION_MOVE)
+		{
+		const gchar *path;
+		gint row;
+
+		path = layout_image_get_path(lw);
+		row = layout_list_get_index(lw, path);
+		if (row < 0) return;
+
+		if (!isfile(path))
+			{
+			if (row < layout_list_count(lw, NULL) - 1)
+				{
+				layout_image_next(lw);
+				}
+			else
+				{
+				layout_image_prev(lw);
+				}
+			}
+		layout_refresh(lw);
+		}
+}
+
+static void layout_image_dnd_init(LayoutWindow *lw)
+{
+	gtk_drag_source_set(lw->image->image, GDK_BUTTON2_MASK,
+			    dnd_file_drag_types, dnd_file_drag_types_count,
+			    GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
+	g_signal_connect(G_OBJECT(lw->image->image), "drag_data_get",
+			 G_CALLBACK(layout_image_dnd_get), lw);
+	g_signal_connect(G_OBJECT(lw->image->image), "drag_end",
+			 G_CALLBACK(layout_image_dnd_end), lw);
+
+	gtk_drag_dest_set(lw->image->image,
+			  GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
+			  dnd_file_drop_types, dnd_file_drop_types_count,
+                          GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
+	g_signal_connect(G_OBJECT(lw->image->image), "drag_data_received",
+			 G_CALLBACK(layout_image_dnd_receive), lw);
+}
+
+
+/*
+ *----------------------------------------------------------------------------
+ * misc
+ *----------------------------------------------------------------------------
+ */
+
+void layout_image_to_root(LayoutWindow *lw)
+{
+	image_to_root_window(lw->image, (image_zoom_get(lw->image) == 0));
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * manipulation + accessors
+ *----------------------------------------------------------------------------
+ */
+
+void layout_image_scroll(LayoutWindow *lw, gint x, gint y)
+{
+	if (!layout_valid(&lw)) return;
+
+	image_scroll(lw->image, x, y);
+}
+
+void layout_image_zoom_adjust(LayoutWindow *lw, gdouble increment)
+{
+	if (!layout_valid(&lw)) return;
+
+	image_zoom_adjust(lw->image, increment);
+}
+
+void layout_image_zoom_set(LayoutWindow *lw, gdouble zoom)
+{
+	if (!layout_valid(&lw)) return;
+
+	image_zoom_set(lw->image, zoom);
+}
+
+void layout_image_zoom_set_fill_geometry(LayoutWindow *lw, gint vertical)
+{
+	if (!layout_valid(&lw)) return;
+
+	image_zoom_set_fill_geometry(lw->image, vertical);
+}
+
+void layout_image_alter(LayoutWindow *lw, AlterType type)
+{
+	if (!layout_valid(&lw)) return;
+
+	image_alter(lw->image, type);
+}
+
+const gchar *layout_image_get_path(LayoutWindow *lw)
+{
+	if (!layout_valid(&lw)) return NULL;
+
+	return image_get_path(lw->image);
+}
+
+const gchar *layout_image_get_name(LayoutWindow *lw)
+{
+	if (!layout_valid(&lw)) return NULL;
+
+	return image_get_name(lw->image);
+}
+
+CollectionData *layout_image_get_collection(LayoutWindow *lw, CollectInfo **info)
+{
+	if (!layout_valid(&lw)) return NULL;
+
+	return image_get_collection(lw->image, info);
+}
+
+gint layout_image_get_index(LayoutWindow *lw)
+{
+	return layout_list_get_index(lw, image_get_path(lw->image));
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * image changers
+ *----------------------------------------------------------------------------
+ */
+
+void layout_image_set_path(LayoutWindow *lw, const gchar *path)
+{
+	if (!layout_valid(&lw)) return;
+
+	image_change_path(lw->image, path, image_zoom_get_default(lw->image, zoom_mode));
+
+	layout_list_sync_path(lw, path);
+	layout_image_slideshow_continue_check(lw);
+	layout_bars_new_image(lw);
+}
+
+void layout_image_set_with_ahead(LayoutWindow *lw, const gchar *path, const gchar *read_ahead_path)
+{
+	if (!layout_valid(&lw)) return;
+
+	if (path)
+		{
+		const gchar *old_path;
+
+		old_path = layout_image_get_path(lw);
+		if (old_path && strcmp(path, old_path) == 0) return;
+		}
+
+	layout_image_set_path(lw, path);
+	if (enable_read_ahead) image_prebuffer_set(lw->image, read_ahead_path);
+}
+
+void layout_image_set_index(LayoutWindow *lw, gint index)
+{
+	const gchar *path;
+	const gchar *read_ahead_path;
+	gint old;
+
+	if (!layout_valid(&lw)) return;
+
+	old = layout_list_get_index(lw, layout_image_get_path(lw));
+	path = layout_list_get_path(lw, index);
+
+	if (old > index)
+		{
+		read_ahead_path = layout_list_get_path(lw, index - 1);
+		}
+	else
+		{
+		read_ahead_path = layout_list_get_path(lw, index + 1);
+		}
+
+	layout_image_set_with_ahead(lw, path, read_ahead_path);
+}
+
+static void layout_image_set_collection_real(LayoutWindow *lw, CollectionData *cd, CollectInfo *info, gint forward)
+{
+	if (!layout_valid(&lw)) return;
+
+	image_change_from_collection(lw->image, cd, info, image_zoom_get_default(lw->image, zoom_mode));
+	if (enable_read_ahead)
+		{
+		CollectInfo *r_info;
+		if (forward)
+			{
+			r_info = collection_next_by_info(cd, info);
+			if (!r_info) r_info = collection_prev_by_info(cd, info);
+			}
+		else
+			{
+			r_info = collection_prev_by_info(cd, info);
+			if (!r_info) r_info = collection_next_by_info(cd, info);
+			}
+		if (r_info) image_prebuffer_set(lw->image, r_info->path);
+		}
+
+	layout_image_slideshow_continue_check(lw);
+	layout_bars_new_image(lw);
+}
+
+void layout_image_set_collection(LayoutWindow *lw, CollectionData *cd, CollectInfo *info)
+{
+	layout_image_set_collection_real(lw, cd, info, TRUE);
+	layout_list_sync_path(lw, layout_image_get_path(lw));
+}
+
+void layout_image_refresh(LayoutWindow *lw)
+{
+	if (!layout_valid(&lw)) return;
+
+	image_reload(lw->image);
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * list walkers
+ *----------------------------------------------------------------------------
+ */
+
+void layout_image_next(LayoutWindow *lw)
+{
+	gint current;
+	CollectionData *cd;
+	CollectInfo *info;
+
+	if (!layout_valid(&lw)) return;
+
+	if (layout_image_slideshow_active(lw))
+		{
+		layout_image_slideshow_next(lw);
+		return;
+		}
+
+	cd = image_get_collection(lw->image, &info);
+
+	if (cd && info)
+		{
+		info = collection_next_by_info(cd, info);
+		if (info) layout_image_set_collection_real(lw, cd, info, TRUE);
+		return;
+		}
+
+	current = layout_image_get_index(lw);
+
+	if (current >= 0)
+		{
+		if (current < layout_list_count(lw, NULL) - 1)
+			{
+			layout_image_set_index(lw, current + 1);
+			}
+		}
+	else
+		{
+		layout_image_set_index(lw, 0);
+		}
+}
+
+void layout_image_prev(LayoutWindow *lw)
+{
+	gint current;
+	CollectionData *cd;
+	CollectInfo *info;
+
+	if (!layout_valid(&lw)) return;
+
+	if (layout_image_slideshow_active(lw))
+		{
+		layout_image_slideshow_prev(lw);
+		return;
+		}
+
+	cd = image_get_collection(lw->image, &info);
+
+	if (cd && info)
+		{
+		info = collection_prev_by_info(cd, info);
+		if (info) layout_image_set_collection_real(lw, cd, info, FALSE);
+		return;
+		}
+
+	current = layout_image_get_index(lw);
+
+	if (current >= 0)
+		{
+		if (current > 0)
+			{
+			layout_image_set_index(lw, current - 1);
+			}
+		}
+	else
+		{
+		layout_image_set_index(lw, layout_list_count(lw, NULL) - 1);
+		}
+}
+
+void layout_image_first(LayoutWindow *lw)
+{
+	gint current;
+	CollectionData *cd;
+	CollectInfo *info;
+
+	if (!layout_valid(&lw)) return;
+
+	cd = image_get_collection(lw->image, &info);
+
+	if (cd && info)
+		{
+		CollectInfo *new;
+		new = collection_get_first(cd);
+		if (new != info) layout_image_set_collection_real(lw, cd, new, TRUE);
+		return;
+		}
+
+	current = layout_image_get_index(lw);
+	if (current != 0 && layout_list_count(lw, NULL) > 0)
+		{
+		layout_image_set_index(lw, 0);
+		}
+}
+
+void layout_image_last(LayoutWindow *lw)
+{
+	gint current;
+	gint count;
+	CollectionData *cd;
+	CollectInfo *info;
+
+	if (!layout_valid(&lw)) return;
+
+	cd = image_get_collection(lw->image, &info);
+
+	if (cd && info)
+		{
+		CollectInfo *new;
+		new = collection_get_last(cd);
+		if (new != info) layout_image_set_collection_real(lw, cd, new, FALSE);
+		return;
+		}
+
+	current = layout_image_get_index(lw);
+	count = layout_list_count(lw, NULL);
+	if (current != count - 1 && count > 0)
+		{
+		layout_image_set_index(lw, count - 1);
+		}
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * mouse callbacks
+ *----------------------------------------------------------------------------
+ */
+
+static void layout_image_button_cb(ImageWindow *imd, gint button, guint32 time,
+				   gdouble x, gdouble y, guint state, gpointer data)
+{
+	LayoutWindow *lw = data;
+	GtkWidget *menu;
+
+	switch (button)
+		{
+		case 1:
+			layout_image_next(lw);
+			break;
+		case 2:
+			layout_image_prev(lw);
+			break;
+		case 3:
+			menu = layout_image_pop_menu(lw);
+			if (imd == lw->image)
+				{
+				g_object_set_data(G_OBJECT(menu), "click_parent", imd->widget);
+				}
+			gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, time);
+			break;
+		default:
+			break;
+		}
+}
+
+static void layout_image_scroll_cb(ImageWindow *imd, GdkScrollDirection direction, guint32 time,
+				   gdouble x, gdouble y, guint state, gpointer data)
+{
+	LayoutWindow *lw = data;
+
+	if (state & GDK_CONTROL_MASK)
+		{
+		switch (direction)
+			{
+			case GDK_SCROLL_UP:
+				image_zoom_adjust_at_point(imd, get_zoom_increment(), x, y);
+				break;
+			case GDK_SCROLL_DOWN:
+				image_zoom_adjust_at_point(imd, -get_zoom_increment(), x, y);
+				break;
+			default:
+				break;
+			}
+		}
+	else if ( (state & GDK_SHIFT_MASK) != (mousewheel_scrolls))
+		{
+		switch (direction)
+			{
+			case GDK_SCROLL_UP:
+				image_scroll(imd, 0, -MOUSEWHEEL_SCROLL_SIZE);
+				break;
+			case GDK_SCROLL_DOWN:
+				image_scroll(imd, 0, MOUSEWHEEL_SCROLL_SIZE);
+				break;
+			case GDK_SCROLL_LEFT:
+				image_scroll(imd, -MOUSEWHEEL_SCROLL_SIZE, 0);
+				break;
+			case GDK_SCROLL_RIGHT:
+				image_scroll(imd, MOUSEWHEEL_SCROLL_SIZE, 0);
+				break;
+			default:
+				break;
+			}
+		}
+	else
+		{
+		switch (direction)
+			{
+			case GDK_SCROLL_UP:
+				layout_image_prev(lw);
+				break;
+			case GDK_SCROLL_DOWN:
+				layout_image_next(lw);
+				break;
+			default:
+				break;
+			}
+		}
+}
+
+static void layout_image_set_buttons(LayoutWindow *lw)
+{
+	image_set_button_func(lw->image, layout_image_button_cb, lw);
+	image_set_scroll_func(lw->image, layout_image_scroll_cb, lw);
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * setup
+ *----------------------------------------------------------------------------
+ */
+
+static void layout_image_update_cb(ImageWindow *imd, gpointer data)
+{
+	LayoutWindow *lw = data;
+	layout_status_update_image(lw);
+}
+
+GtkWidget *layout_image_new(LayoutWindow *lw, const gchar *path)
+{
+	if (!lw->image) 
+		{
+		lw->image = image_new( (!lw->tools_float && !lw->tools_hidden) );
+		if (black_window_background) image_background_set_black(lw->image, TRUE);
+		image_set_update_func(lw->image, layout_image_update_cb, lw);
+		layout_image_set_buttons(lw);
+		layout_image_dnd_init(lw);
+
+		image_attach_window(lw->image, lw->window, NULL, "GQview", FALSE);
+
+		image_auto_refresh(lw->image, 0);
+		}
+
+	return lw->image->widget;
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * maintenance (for rename, move, remove)
+ *-----------------------------------------------------------------------------
+ */
+
+void layout_image_maint_renamed(LayoutWindow *lw, const gchar *source, const gchar *dest)
+{
+	const gchar *img_path;
+
+	img_path = layout_image_get_path(lw);
+	if (img_path && strcmp(img_path, source) == 0)
+		{
+		image_set_path(lw->image, dest);
+		layout_bars_maint_renamed(lw);
+		}
+}
+
+void layout_image_maint_removed(LayoutWindow *lw, const gchar *path)
+{
+	const gchar *img_path;
+
+	img_path = layout_image_get_path(lw);
+	if (img_path && strcmp(img_path, path) == 0)
+		{
+		CollectionData *cd;
+		CollectInfo *info;
+
+		cd = image_get_collection(lw->image, &info);
+		if (cd && info)
+			{
+			CollectInfo *new;
+
+			new = collection_next_by_info(cd, info);
+			if (!new) new = collection_prev_by_info(cd, info);
+
+			if (new)
+				{
+				layout_image_set_collection(lw, cd, new);
+				return;
+				}
+			}
+
+		layout_image_set_path(lw, NULL);
+		}
+}
+
+void layout_image_maint_moved(LayoutWindow *lw, const gchar *source, const gchar *dest)
+{
+	layout_image_maint_renamed(lw, source, dest);
+}
+