/* $Id$ */
/* Copyright (c) 2011-2018 Pierre Pronchery <khorben@defora.org> */
/* This file is part of DeforaOS Desktop Browser */
/* Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY ITS AUTHORS AND CONTRIBUTORS "AS IS" AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */



#include <System.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <libintl.h>
#include "Browser.h"
#define _(string) gettext(string)
#define N_(string) (string)

#ifndef max
# define max(a, b) ((a) > (b) ? (a) : (b))
#endif
#ifndef min
# define min(a, b) ((a) < (b) ? (a) : (b))
#endif


/* Preview */
/* private */
/* types */
typedef enum _PreviewImageHow
{
	PREVIEW_IMAGE_HOW_FIT = 0,
	PREVIEW_IMAGE_HOW_ORIGINAL,
	PREVIEW_IMAGE_HOW_SCALE
} PreviewImageHow;

typedef struct _BrowserPlugin
{
	BrowserPluginHelper * helper;

	char * path;
	guint source;

	/* widgets */
	GtkWidget * widget;
	GtkWidget * name;
	GtkWidget * toolbar;
	GtkToolItem * open;
	GtkToolItem * edit;
	GtkToolItem * copy;
	GtkToolItem * select_all;
	GtkToolItem * zoom_100;
	GtkToolItem * zoom_fit;
	GtkToolItem * zoom_out;
	GtkToolItem * zoom_in;
	GtkWidget * view_image;
	GtkWidget * view_image_image;
	PreviewImageHow view_image_how;
	gint view_image_height;
	gint view_image_width;
	gdouble view_image_scale;
	GtkWidget * view_text;
	GtkTextBuffer * view_text_tbuf;
} Preview;


/* constants */
#define PREVIEW_IMAGE_SIZE_DEFAULT	96


/* prototypes */
/* plug-in */
static Preview * _preview_init(BrowserPluginHelper * helper);
static void _preview_destroy(Preview * preview);
static GtkWidget * _preview_get_widget(Preview * preview);
static void _preview_refresh(Preview * preview, GList * selection);

/* accessors */
static void _preview_get_image_size(Preview * preview, gint * width,
		gint * height);
static void _preview_get_widget_size(Preview * preview, gint * width,
		gint * height);

/* callbacks */
static void _preview_on_copy(gpointer data);
static void _preview_on_edit(gpointer data);
static gboolean _preview_on_idle_image(gpointer data);
static gboolean _preview_on_idle_text(gpointer data);
static void _preview_on_open(gpointer data);
static void _preview_on_select_all(gpointer data);
static void _preview_on_zoom_100(gpointer data);
static void _preview_on_zoom_fit(gpointer data);
static void _preview_on_zoom_in(gpointer data);
static void _preview_on_zoom_out(gpointer data);


/* public */
/* variables */
BrowserPluginDefinition plugin =
{
	N_("Preview"),
	"gtk-find",
	NULL,
	_preview_init,
	_preview_destroy,
	_preview_get_widget,
	_preview_refresh
};


/* private */
/* functions */
/* preview_init */
static Preview * _preview_init(BrowserPluginHelper * helper)
{
	Preview * preview;
	PangoFontDescription * font;
	GtkWidget * vbox;
	GtkWidget * widget;
	String const * p;

	if((preview = object_new(sizeof(*preview))) == NULL)
		return NULL;
	preview->helper = helper;
	preview->path = NULL;
	preview->source = 0;
	if((p = helper->config_get(helper->browser, "preview", "size")) != NULL)
		preview->view_image_how = strtol(p, NULL, 0);
	else
		preview->view_image_how = PREVIEW_IMAGE_HOW_FIT;
	/* widgets */
	vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 4);
	preview->widget = vbox;
	/* toolbar */
	preview->toolbar = gtk_toolbar_new();
	gtk_widget_set_no_show_all(preview->toolbar, TRUE);
	/* mime */
	preview->open = gtk_tool_button_new_from_stock(GTK_STOCK_OPEN);
	g_signal_connect_swapped(preview->open, "clicked", G_CALLBACK(
				_preview_on_open), preview);
	gtk_toolbar_insert(GTK_TOOLBAR(preview->toolbar), preview->open, -1);
	preview->edit = gtk_tool_button_new_from_stock(GTK_STOCK_EDIT);
	g_signal_connect_swapped(preview->edit, "clicked", G_CALLBACK(
				_preview_on_edit), preview);
	gtk_toolbar_insert(GTK_TOOLBAR(preview->toolbar), preview->edit, -1);
	/* copy */
	preview->copy = gtk_tool_button_new_from_stock(GTK_STOCK_COPY);
	g_signal_connect_swapped(preview->copy, "clicked", G_CALLBACK(
				_preview_on_copy), preview);
	gtk_toolbar_insert(GTK_TOOLBAR(preview->toolbar), preview->copy, -1);
	/* select all */
#if GTK_CHECK_VERSION(2, 10, 0)
	preview->select_all = gtk_tool_button_new_from_stock(
			GTK_STOCK_SELECT_ALL);
#else
	widget = gtk_image_new_from_icon_name("edit-select-all",
			gtk_toolbar_get_icon_size(GTK_TOOLBAR(
					preview->toolbar)));
	preview->select_all = gtk_tool_button_new(widget, "Select all");
#endif
	g_signal_connect_swapped(preview->select_all, "clicked", G_CALLBACK(
				_preview_on_select_all), preview);
	gtk_toolbar_insert(GTK_TOOLBAR(preview->toolbar), preview->select_all,
			-1);
	/* zoom */
	preview->zoom_100 = gtk_tool_button_new_from_stock(GTK_STOCK_ZOOM_100);
	g_signal_connect_swapped(preview->zoom_100, "clicked", G_CALLBACK(
				_preview_on_zoom_100), preview);
	gtk_toolbar_insert(GTK_TOOLBAR(preview->toolbar), preview->zoom_100,
			-1);
	preview->zoom_fit = gtk_tool_button_new_from_stock(GTK_STOCK_ZOOM_FIT);
	g_signal_connect_swapped(preview->zoom_fit, "clicked", G_CALLBACK(
				_preview_on_zoom_fit), preview);
	gtk_toolbar_insert(GTK_TOOLBAR(preview->toolbar), preview->zoom_fit,
			-1);
	preview->zoom_out = gtk_tool_button_new_from_stock(GTK_STOCK_ZOOM_OUT);
	g_signal_connect_swapped(preview->zoom_out, "clicked", G_CALLBACK(
				_preview_on_zoom_out), preview);
	gtk_toolbar_insert(GTK_TOOLBAR(preview->toolbar), preview->zoom_out,
			-1);
	preview->zoom_in = gtk_tool_button_new_from_stock(GTK_STOCK_ZOOM_IN);
	g_signal_connect_swapped(preview->zoom_in, "clicked", G_CALLBACK(
				_preview_on_zoom_in), preview);
	gtk_toolbar_insert(GTK_TOOLBAR(preview->toolbar), preview->zoom_in, -1);
	gtk_box_pack_start(GTK_BOX(vbox), preview->toolbar, FALSE, TRUE, 0);
	/* name */
	preview->name = gtk_label_new(NULL);
	gtk_label_set_ellipsize(GTK_LABEL(preview->name),
			PANGO_ELLIPSIZE_MIDDLE);
#if GTK_CHECK_VERSION(3, 0, 0)
	g_object_set(preview->name, "halign", GTK_ALIGN_START, NULL);
#else
	gtk_misc_set_alignment(GTK_MISC(preview->name), 0.0, 0.5);
#endif
	font = pango_font_description_new();
	pango_font_description_set_weight(font, PANGO_WEIGHT_BOLD);
#if GTK_CHECK_VERSION(3, 0, 0)
	gtk_widget_override_font(preview->name, font);
#else
	gtk_widget_modify_font(preview->name, font);
#endif
	pango_font_description_free(font);
	if((p = helper->config_get(helper->browser, "preview", "label")) != NULL
			&& strtol(p, NULL, 0) == 0)
		gtk_widget_set_no_show_all(preview->name, TRUE);
	gtk_box_pack_start(GTK_BOX(vbox), preview->name, FALSE, TRUE, 0);
	/* image */
	preview->view_image = gtk_scrolled_window_new(NULL, NULL);
	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(preview->view_image),
			GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
	gtk_widget_set_no_show_all(preview->view_image, TRUE);
	preview->view_image_image = gtk_image_new();
	gtk_widget_show(preview->view_image_image);
#if GTK_CHECK_VERSION(3, 0, 0)
	gtk_container_add(GTK_CONTAINER(preview->view_image),
			preview->view_image_image);
#else
	gtk_scrolled_window_add_with_viewport(
			GTK_SCROLLED_WINDOW(preview->view_image),
			preview->view_image_image);
#endif
	preview->view_image_scale = 1.0;
	preview->view_image_height = -1;
	preview->view_image_width = -1;
	gtk_box_pack_start(GTK_BOX(vbox), preview->view_image, TRUE, TRUE, 0);
	/* text */
	preview->view_text = gtk_scrolled_window_new(NULL, NULL);
	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(preview->view_text),
			GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
	gtk_widget_set_no_show_all(preview->view_text, TRUE);
	font = pango_font_description_new();
	pango_font_description_set_family(font, "monospace");
	widget = gtk_text_view_new();
	preview->view_text_tbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(
				widget));
	gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(widget), FALSE);
	gtk_text_view_set_editable(GTK_TEXT_VIEW(widget), FALSE);
	gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(widget), GTK_WRAP_WORD_CHAR);
#if GTK_CHECK_VERSION(3, 0, 0)
	gtk_widget_override_font(widget, font);
#else
	gtk_widget_modify_font(widget, font);
#endif
	gtk_widget_show(widget);
	pango_font_description_free(font);
#if GTK_CHECK_VERSION(3, 0, 0)
	gtk_container_add(GTK_CONTAINER(preview->view_text), widget);
#else
	gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(
				preview->view_text), widget);
#endif
	gtk_box_pack_start(GTK_BOX(vbox), preview->view_text, TRUE, TRUE, 0);
	gtk_widget_show_all(vbox);
	return preview;
}


/* preview_destroy */
static void _preview_destroy(Preview * preview)
{
	if(preview->source != 0)
		g_source_remove(preview->source);
	free(preview->path);
	object_delete(preview);
}


/* preview_get_widget */
static GtkWidget * _preview_get_widget(Preview * preview)
{
	return preview->widget;
}


/* preview_refresh */
static void _refresh_mime(Preview * preview, Mime * mime, char const * type);
static int _refresh_name(Preview * preview, char const * path);
static void _refresh_reset(Preview * preview);

static void _preview_refresh(Preview * preview, GList * selection)
{
	char * path = (selection != NULL) ? selection->data : NULL;
	Mime * mime = preview->helper->get_mime(preview->helper->browser);
	struct stat st;
	char const image[6] = "image/";
	char const text[5] = "text/";
	char const * types[] = { "application/x-perl",
		"application/x-shellscript",
		"application/xml",
		"application/xslt+xml" };
	char const * type;
	size_t i;

	_refresh_reset(preview);
	if(path == NULL)
		return;
	if(_refresh_name(preview, path) != 0)
		return;
	/* ignore directories */
	if(lstat(path, &st) == 0 && S_ISDIR(st.st_mode))
		return;
	/* XXX avoid stat() and use vfs_mime_type() instead */
	if((type = mime_type(mime, path)) == NULL)
		return;
	_refresh_mime(preview, mime, type);
	if(strncmp(type, image, sizeof(image)) == 0)
		preview->source = g_idle_add(_preview_on_idle_image, preview);
	else if(strncmp(type, text, sizeof(text)) == 0)
		preview->source = g_idle_add(_preview_on_idle_text, preview);
	else
		for(i = 0; i < sizeof(types) / sizeof(*types); i++)
			if(strcmp(types[i], type) == 0)
			{
				preview->source = g_idle_add(
						_preview_on_idle_text, preview);
				break;
			}
}

static void _refresh_mime(Preview * preview, Mime * mime, char const * type)
{
	if(mime_get_handler(mime, type, "open") != NULL)
	{
		gtk_widget_show(GTK_WIDGET(preview->open));
		gtk_widget_show(preview->toolbar);
	}
	if(mime_get_handler(mime, type, "edit") != NULL)
	{
		gtk_widget_show(GTK_WIDGET(preview->edit));
		gtk_widget_show(preview->toolbar);
	}
}

static int _refresh_name(Preview * preview, char const * path)
{
	BrowserPluginHelper * helper = preview->helper;
	gchar * p;

	free(preview->path);
	if((preview->path = strdup(path)) == NULL)
		return -helper->error(helper->browser, strerror(errno), 1);
	p = g_filename_display_basename(path);
	gtk_label_set_text(GTK_LABEL(preview->name), p);
	g_free(p);
	return 0;
}

static void _refresh_reset(Preview * preview)
{
	if(preview->source != 0)
		g_source_remove(preview->source);
	preview->source = 0;
	preview->view_image_width = -1;
	preview->view_image_height = -1;
	gtk_widget_hide(preview->toolbar);
	gtk_widget_hide(GTK_WIDGET(preview->open));
	gtk_widget_hide(GTK_WIDGET(preview->edit));
	gtk_widget_hide(GTK_WIDGET(preview->copy));
	gtk_widget_hide(GTK_WIDGET(preview->select_all));
	gtk_widget_hide(GTK_WIDGET(preview->zoom_100));
	gtk_widget_hide(GTK_WIDGET(preview->zoom_fit));
	gtk_widget_hide(GTK_WIDGET(preview->zoom_out));
	gtk_widget_hide(GTK_WIDGET(preview->zoom_in));
	gtk_widget_hide(preview->view_image);
	gtk_widget_hide(preview->view_text);
}


/* accessors */
/* preview_get_image_size */
static void _preview_get_image_size(Preview * preview, gint * width,
		gint * height)
{
	BrowserPluginHelper * helper = preview->helper;
	GdkPixbuf * pixbuf;
	GError * error = NULL;

	/* check if we have cached the values */
	if(preview->view_image_width > 0
			&& preview->view_image_height > 0)
	{
		*width = preview->view_image_width;
		*height = preview->view_image_height;
#ifdef DEBUG
		fprintf(stderr, "DEBUG: %s() %dx%d (cached)\n", __func__,
				*width, *height);
#endif
		return;
	}
	*width = PREVIEW_IMAGE_SIZE_DEFAULT;
	*height = PREVIEW_IMAGE_SIZE_DEFAULT;
	if(preview->path != NULL
			&& (pixbuf = gdk_pixbuf_new_from_file(preview->path,
					&error)) != NULL)
	{
		*width = gdk_pixbuf_get_width(pixbuf);
		*height = gdk_pixbuf_get_height(pixbuf);
		g_object_unref(pixbuf);
		/* cache the values */
		preview->view_image_width = *width;
		preview->view_image_height = *height;
	}
	if(error != NULL)
	{
		helper->error(NULL, error->message, 1);
		g_error_free(error);
	}
#ifdef DEBUG
	fprintf(stderr, "DEBUG: %s() %dx%d\n", __func__, *width, *height);
#endif
}


/* preview_get_widget_size */
static void _preview_get_widget_size(Preview * preview, gint * width,
		gint * height)
{
#if GTK_CHECK_VERSION(2, 14, 0)
	GdkWindow * window;

	if((window = gtk_widget_get_window(preview->view_image)) != NULL)
	{
# if GTK_CHECK_VERSION(2, 24, 0)
		*width = gdk_window_get_width(window);
		*height = gdk_window_get_height(window);
# else
		gdk_drawable_get_size(GDK_DRAWABLE(window), width, height);
# endif
	}
	else
#endif
	{
		*width = PREVIEW_IMAGE_SIZE_DEFAULT;
		*height = PREVIEW_IMAGE_SIZE_DEFAULT;
	}
#ifdef DEBUG
	fprintf(stderr, "DEBUG: %s() %dx%d\n", __func__, *width, *height);
#endif
}


/* callbacks */
/* preview_on_copy */
static void _preview_on_copy(gpointer data)
{
	Preview * preview = data;
	GtkClipboard * clipboard;

	clipboard = gtk_widget_get_clipboard(preview->view_text,
			GDK_SELECTION_CLIPBOARD);
	gtk_text_buffer_copy_clipboard(preview->view_text_tbuf, clipboard);
}


/* preview_on_edit */
static void _preview_on_edit(gpointer data)
{
	Preview * preview = data;
	Mime * mime = preview->helper->get_mime(preview->helper->browser);

	if(preview->path != NULL)
		mime_action(mime, "edit", preview->path);
}


/* preview_on_idle_image */
static void _idle_image_100(Preview * preview);
static void _idle_image_fit(Preview * preview);
static void _idle_image_load(Preview * preview, gint height, gint width);
static void _idle_image_load_animation(Preview * preview);
static void _idle_image_load_pixbuf(Preview * preview, gint width, gint height);
static void _idle_image_scale(Preview * preview);

static gboolean _preview_on_idle_image(gpointer data)
{
	Preview * preview = data;

	preview->source = 0;
	/* show the toolbar */
	gtk_widget_show(GTK_WIDGET(preview->zoom_100));
	gtk_widget_show(GTK_WIDGET(preview->zoom_fit));
	gtk_widget_show(GTK_WIDGET(preview->zoom_out));
	gtk_widget_show(GTK_WIDGET(preview->zoom_in));
	gtk_widget_show(preview->toolbar);
	switch(preview->view_image_how)
	{
		case PREVIEW_IMAGE_HOW_FIT:
			_idle_image_fit(preview);
			break;
		case PREVIEW_IMAGE_HOW_ORIGINAL:
			_idle_image_100(preview);
			break;
		case PREVIEW_IMAGE_HOW_SCALE:
		default:
			_idle_image_scale(preview);
			break;
	}
	gtk_widget_show(preview->view_image);
	return FALSE;
}

static void _idle_image_100(Preview * preview)
{
	_idle_image_load(preview, 0, 0);
}

static void _idle_image_fit(Preview * preview)
{
	gint iwidth;
	gint iheight;
	gint wwidth;
	gint wheight;

	_preview_get_image_size(preview, &iwidth, &iheight);
	_preview_get_widget_size(preview, &wwidth, &wheight);
	/* scale the image accordingly */
	preview->view_image_scale = min((gdouble)wwidth / (gdouble)iwidth,
			(gdouble)wheight / (gdouble)iheight);
	_idle_image_load(preview, 0, 0);
}

static void _idle_image_load(Preview * preview, gint height, gint width)
{
#ifdef DEBUG
	fprintf(stderr, "DEBUG: %s(%d, %d)\n", __func__, width, height);
#endif
	if(width <= 0 && height <= 0 && preview->view_image_scale == 1.0)
		_idle_image_load_animation(preview);
	else
	{
		if(width <= 0 || height <= 0)
			_preview_get_image_size(preview, &width, &height);
		_idle_image_load_pixbuf(preview,
				width * preview->view_image_scale,
				height * preview->view_image_scale);
	}
}

static void _idle_image_load_animation(Preview * preview)
{
	BrowserPluginHelper * helper = preview->helper;
	GdkPixbufAnimation * pixbuf;
	GError * error = NULL;

	if((pixbuf = gdk_pixbuf_animation_new_from_file(preview->path, &error))
			== NULL)
	{
		helper->error(helper->browser, error->message, 1);
		g_error_free(error);
		return;
	}
	if(error != NULL)
	{
		helper->error(NULL, error->message, 1);
		g_error_free(error);
	}
	gtk_image_set_from_animation(GTK_IMAGE(preview->view_image_image),
			pixbuf);
	g_object_unref(pixbuf);
}

static void _idle_image_load_pixbuf(Preview * preview, gint width, gint height)
{
	BrowserPluginHelper * helper = preview->helper;
	GdkPixbuf * pixbuf;
	GError * error = NULL;

	if(width <= 0)
		width = 1;
	if(height <= 0)
		height = 1;
#if GTK_CHECK_VERSION(2, 6, 0)
	if((pixbuf = gdk_pixbuf_new_from_file_at_scale(preview->path, width,
					height, TRUE, &error)) == NULL)
#else
	if((pixbuf = gdk_pixbuf_new_from_file_at_size(preview->path, width,
					height, &error)) == NULL)
#endif
	{
		helper->error(helper->browser, error->message, 1);
		g_error_free(error);
		return;
	}
	if(error != NULL)
	{
		helper->error(NULL, error->message, 1);
		g_error_free(error);
	}
	gtk_image_set_from_pixbuf(GTK_IMAGE(preview->view_image_image), pixbuf);
	g_object_unref(pixbuf);
}

static void _idle_image_scale(Preview * preview)
{
	_idle_image_load(preview, 0, 0);
}


/* preview_on_idle_text */
static gboolean _preview_on_idle_text(gpointer data)
{
	Preview * preview = data;
	BrowserPluginHelper * helper = preview->helper;
	GtkTextBuffer * tbuf;
	GtkTextIter iter;
	int fd;
	char buf[256];
	ssize_t s;
	String const * p = NULL;

	preview->source = 0;
	gtk_widget_show(GTK_WIDGET(preview->copy));
	gtk_widget_show(GTK_WIDGET(preview->select_all));
	gtk_widget_show(preview->toolbar);
	tbuf = preview->view_text_tbuf;
	gtk_text_buffer_set_text(tbuf, "", 0);
	gtk_text_buffer_get_end_iter(tbuf, &iter);
	if((fd = open(preview->path, O_RDONLY)) < 0)
	{
		helper->error(helper->browser, strerror(errno), 1);
		return FALSE;
	}
	/* FIXME use a GIOChannel instead */
	while((s = read(fd, buf, sizeof(buf))) > 0)
	{
		if(p == NULL)
		{
			if((p = helper->config_get(helper->browser, "preview",
							"ellipsize")) == NULL
					|| strtol(p, NULL, 0) != 0)
			{
				if(s == sizeof(buf))
				{
					buf[sizeof(buf) - 3] = '.';
					buf[sizeof(buf) - 2] = '.';
					buf[sizeof(buf) - 1] = '.';
				}
				gtk_text_buffer_set_text(tbuf, buf, s);
				break;
			}
		}
		gtk_text_buffer_insert(preview->view_text_tbuf, &iter, buf, s);
	}
	close(fd);
	gtk_widget_show(preview->view_text);
	return FALSE;
}


/* preview_on_open */
static void _preview_on_open(gpointer data)
{
	Preview * preview = data;
	Mime * mime = preview->helper->get_mime(preview->helper->browser);

	if(preview->path != NULL)
		mime_action(mime, "open", preview->path);
}


/* preview_on_select_all */
static void _preview_on_select_all(gpointer data)
{
	Preview * preview = data;
	GtkTextIter start;
	GtkTextIter end;

	gtk_text_buffer_get_start_iter(preview->view_text_tbuf, &start);
	gtk_text_buffer_get_end_iter(preview->view_text_tbuf, &end);
	gtk_text_buffer_select_range(preview->view_text_tbuf, &start, &end);
}


/* preview_on_zoom_100 */
static void _preview_on_zoom_100(gpointer data)
{
	Preview * preview = data;

	preview->view_image_how = PREVIEW_IMAGE_HOW_ORIGINAL;
	preview->view_image_scale = 1.0;
	if(preview->source != 0)
		g_source_remove(preview->source);
	/* XXX may not always be an image */
	preview->source = g_idle_add(_preview_on_idle_image, preview);
}


/* preview_on_zoom_fit */
static void _preview_on_zoom_fit(gpointer data)
{
	Preview * preview = data;

	preview->view_image_how = PREVIEW_IMAGE_HOW_FIT;
	if(preview->source != 0)
		g_source_remove(preview->source);
	/* XXX may not always be an image */
	preview->source = g_idle_add(_preview_on_idle_image, preview);
}


/* preview_on_zoom_in */
static void _preview_on_zoom_in(gpointer data)
{
	Preview * preview = data;

	preview->view_image_how = PREVIEW_IMAGE_HOW_SCALE;
	preview->view_image_scale *= 1.25;
	if(preview->view_image_scale > 4.0)
		preview->view_image_scale = 4.0;
	if(preview->source != 0)
		g_source_remove(preview->source);
	/* XXX may not always be an image */
	preview->source = g_idle_add(_preview_on_idle_image, preview);
}


/* preview_on_zoom_out */
static void _preview_on_zoom_out(gpointer data)
{
	Preview * preview = data;

	preview->view_image_how = PREVIEW_IMAGE_HOW_SCALE;
	preview->view_image_scale /= 1.25;
	if(preview->view_image_scale < 0.1)
		preview->view_image_scale = 0.1;
	if(preview->source != 0)
		g_source_remove(preview->source);
	/* XXX may not always be an image */
	preview->source = g_idle_add(_preview_on_idle_image, preview);
}
