542 lines
14 KiB
C
542 lines
14 KiB
C
/*
|
|
* Copyright (C) 2012 - Juan Ferrer Toribio
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "vn-column-image.h"
|
|
#include "../vn-list-model.h"
|
|
|
|
#define LOAD_IMAGE _IMAGE_DIR"/load.gif"
|
|
|
|
G_DEFINE_TYPE (VnColumnImage, vn_column_image, VN_TYPE_COLUMN);
|
|
|
|
typedef struct
|
|
{
|
|
VnColumnImage * self;
|
|
GtkTreeModel * model;
|
|
GtkTreeIter * iter;
|
|
GtkCellRenderer * cell;
|
|
gchar * name;
|
|
}
|
|
DownloadData;
|
|
|
|
typedef struct
|
|
{
|
|
gchar * path;
|
|
gboolean error;
|
|
gint tooltip_size;
|
|
GtkWidget * image;
|
|
}
|
|
TooltipData;
|
|
|
|
VnColumn * vn_column_image_new ()
|
|
{
|
|
return g_object_new (VN_TYPE_COLUMN_IMAGE, NULL);
|
|
}
|
|
|
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++ Private
|
|
|
|
static void vn_column_image_download_error (VnColumnImage * self, const GError * error)
|
|
{
|
|
g_object_set (VN_COLUMN (self)->cell, "icon-name", "image-missing", NULL);
|
|
}
|
|
|
|
static void free_object (gpointer object)
|
|
{
|
|
if (object != NULL)
|
|
g_object_unref (object);
|
|
}
|
|
|
|
static void free_tooltip_data (TooltipData * data)
|
|
{
|
|
if (data)
|
|
{
|
|
g_free (data->path);
|
|
|
|
if (data->image)
|
|
g_object_unref (data->image);
|
|
}
|
|
|
|
g_free (data);
|
|
}
|
|
|
|
static void vn_column_image_on_tooltip_size (GdkPixbufLoader * loader, gint width,
|
|
gint height, gpointer tsize)
|
|
{
|
|
gint h, w, size = GPOINTER_TO_INT (tsize);
|
|
|
|
if (width > size || height > size)
|
|
{
|
|
if (width >= height)
|
|
{
|
|
w = size;
|
|
h = height * w / width;
|
|
}
|
|
else
|
|
{
|
|
h = size;
|
|
w = width * h / height;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
w = width;
|
|
h = height;
|
|
}
|
|
|
|
gdk_pixbuf_loader_set_size (loader, w, h);
|
|
}
|
|
|
|
static void vn_column_image_on_download_tooltip (DbFileLoader * fl,
|
|
GBytes * bytes, const GError * error, TooltipData * data)
|
|
{
|
|
if (error)
|
|
data->image = g_object_ref_sink (gtk_image_new_from_icon_name
|
|
("image-missing", GTK_ICON_SIZE_MENU));
|
|
else if (bytes && data)
|
|
{
|
|
gsize size;
|
|
GError * err = NULL;
|
|
const guchar * raw_data = g_bytes_get_data (bytes, &size);
|
|
GdkPixbufLoader * loader = gdk_pixbuf_loader_new ();
|
|
|
|
if (data->tooltip_size > 0)
|
|
g_signal_connect (loader, "size-prepared",
|
|
G_CALLBACK (vn_column_image_on_tooltip_size),
|
|
GINT_TO_POINTER (data->tooltip_size));
|
|
|
|
if (gdk_pixbuf_loader_write (loader, raw_data, size, &err)
|
|
&& gdk_pixbuf_loader_close (loader, &err))
|
|
{
|
|
GdkPixbuf * pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
|
|
|
|
if (data->image && G_IS_OBJECT (data->image))
|
|
g_object_unref (data->image);
|
|
|
|
data->image = g_object_ref_sink (gtk_image_new_from_pixbuf (pixbuf));
|
|
}
|
|
else
|
|
{
|
|
gdk_pixbuf_loader_close (loader, NULL);
|
|
g_error_free (err);
|
|
}
|
|
|
|
g_object_unref (loader);
|
|
}
|
|
|
|
gtk_tooltip_trigger_tooltip_query (gdk_display_get_default());
|
|
}
|
|
|
|
static gboolean vn_column_image_on_query_tooltip (GtkTreeView * view,
|
|
gint x, gint y, gboolean k, GtkTooltip * tip, VnColumnImage * self)
|
|
{
|
|
gboolean ret = FALSE;
|
|
TooltipData * data;
|
|
GtkTreeIter iter;
|
|
GtkTreePath * path = NULL;
|
|
|
|
if (gtk_tree_view_get_tooltip_context (view, &x, &y, k, NULL, &path, &iter))
|
|
{
|
|
gint wx, wy;
|
|
GdkRectangle rect;
|
|
|
|
gtk_tree_view_convert_bin_window_to_widget_coords (view, x, y, &wx, &wy);
|
|
gtk_tree_view_get_background_area
|
|
(view, path, GTK_TREE_VIEW_COLUMN (self), &rect);
|
|
|
|
if (!(rect.x < wx && wx < rect.x + rect.width))
|
|
{
|
|
gtk_tree_path_free (path);
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
return FALSE;
|
|
|
|
if (self->tooltips
|
|
&& (data = g_hash_table_lookup (self->tooltips, iter.user_data))
|
|
&& !data->error && data->path)
|
|
{
|
|
gtk_tree_view_set_tooltip_cell (view, tip, path,
|
|
GTK_TREE_VIEW_COLUMN (self), VN_COLUMN (self)->cell);
|
|
|
|
if (!data->image)
|
|
{
|
|
data->image = g_object_ref_sink (gtk_image_new_from_file (LOAD_IMAGE));
|
|
gtk_tooltip_set_custom (tip, data->image);
|
|
|
|
db_file_loader_download (self->loader, data->path,
|
|
(DbFileLoaderCallbackFunc) vn_column_image_on_download_tooltip, data);
|
|
}
|
|
else
|
|
gtk_tooltip_set_custom (tip, data->image);
|
|
|
|
ret = TRUE;
|
|
}
|
|
|
|
gtk_tree_path_free (path);
|
|
return ret;
|
|
}
|
|
|
|
static GdkPixbuf * vn_column_image_set_image (VnColumnImage * self,
|
|
GtkCellRenderer * cell, GBytes * bytes, gboolean pix)
|
|
{
|
|
gsize size;
|
|
GdkPixbuf * pixbuf = NULL;
|
|
GError * error = NULL;
|
|
const guchar * raw_data = g_bytes_get_data (bytes, &size);
|
|
|
|
if (raw_data)
|
|
{
|
|
GdkPixbufLoader * loader = gdk_pixbuf_loader_new ();
|
|
|
|
if (gdk_pixbuf_loader_write (loader, raw_data, size, &error)
|
|
&& gdk_pixbuf_loader_close (loader, &error))
|
|
{
|
|
pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
|
|
g_object_set (cell, "pixbuf", pixbuf, NULL);
|
|
}
|
|
else
|
|
{
|
|
gdk_pixbuf_loader_close (loader, NULL);
|
|
vn_column_image_download_error (self, error);
|
|
g_error_free (error);
|
|
}
|
|
|
|
g_object_unref (loader);
|
|
}
|
|
|
|
return pix && pixbuf ? g_object_ref (pixbuf) : NULL;
|
|
}
|
|
|
|
static void vn_column_image_on_download (DbFileLoader * self, GBytes * bytes,
|
|
const GError * error, DownloadData * data)
|
|
{
|
|
GtkTreePath * path;
|
|
|
|
if (error)
|
|
vn_column_image_download_error (data->self, error);
|
|
else if (bytes && vn_list_model_iter_is_valid (data->iter, data->model))
|
|
{
|
|
if (data->self->loaded)
|
|
g_hash_table_replace (data->self->loaded, g_strdup (data->name),
|
|
vn_column_image_set_image (data->self, data->cell, bytes, TRUE));
|
|
|
|
path = gtk_tree_model_get_path (data->model, data->iter);
|
|
gtk_tree_model_row_changed (data->model, path, data->iter);
|
|
gtk_tree_path_free (path);
|
|
}
|
|
|
|
g_object_unref (data->self);
|
|
g_object_unref (data->model);
|
|
gtk_tree_iter_free (data->iter);
|
|
g_free (data->name);
|
|
g_free (data);
|
|
}
|
|
|
|
static void vn_column_image_set_value (VnColumnImage * self, GtkTreeModel * model,
|
|
GtkTreeIter * iter, GtkCellRenderer * cell, const GValue * value)
|
|
{
|
|
GType type = G_VALUE_TYPE (value);
|
|
|
|
if (type == GVN_TYPE_NULL)
|
|
g_object_set (cell, "pixbuf", NULL, "icon-name", NULL, NULL);
|
|
else if (type == G_TYPE_BYTES)
|
|
{
|
|
GBytes * bytes = g_value_get_boxed (value);
|
|
|
|
if (bytes)
|
|
vn_column_image_set_image (self, cell, bytes, FALSE);
|
|
else
|
|
vn_column_image_download_error (self, NULL);
|
|
}
|
|
else if (type == G_TYPE_STRING)
|
|
{
|
|
DownloadData * data;
|
|
gchar * cell_name;
|
|
const gchar * name = g_value_get_string (value);
|
|
GtkTreeView * view = GTK_TREE_VIEW
|
|
(gtk_tree_view_column_get_tree_view (GTK_TREE_VIEW_COLUMN (self)));
|
|
|
|
if (view != self->tree_view)
|
|
{
|
|
if (GTK_IS_TREE_VIEW (self->tree_view))
|
|
g_signal_handlers_disconnect_by_func (self->tree_view,
|
|
vn_column_image_on_query_tooltip, self);
|
|
|
|
g_signal_connect (view, "query-tooltip",
|
|
G_CALLBACK (vn_column_image_on_query_tooltip), self);
|
|
g_object_set (view, "has-tooltip", TRUE, NULL);
|
|
self->tree_view = view;
|
|
}
|
|
|
|
if (self->loaded)
|
|
{
|
|
if (g_hash_table_contains (self->loaded, name))
|
|
{
|
|
GdkPixbuf * pixbuf = g_hash_table_lookup (self->loaded, name);
|
|
|
|
if (pixbuf)
|
|
g_object_set (cell, "pixbuf", pixbuf, NULL);
|
|
else
|
|
vn_column_image_download_error (self, NULL);
|
|
|
|
if (self->tooltips
|
|
&& !g_hash_table_contains (self->tooltips, iter->user_data))
|
|
{
|
|
TooltipData * data = g_new (TooltipData, 1);
|
|
data->path = g_strconcat ("/", self->tooltip_path, "/", name, NULL);
|
|
data->error = FALSE;
|
|
data->tooltip_size = self->tooltip_size;
|
|
data->image = NULL;
|
|
|
|
g_hash_table_insert (self->tooltips, iter->user_data, data);
|
|
}
|
|
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
gint view_x, view_y;
|
|
GdkRectangle view_rect, cell_rect;
|
|
GtkTreePath * path = gtk_tree_model_get_path (model, iter);
|
|
|
|
gtk_tree_view_get_cell_area
|
|
(view, path, GTK_TREE_VIEW_COLUMN (self), &cell_rect);
|
|
gtk_tree_path_free (path);
|
|
gtk_tree_view_get_visible_rect (view, &view_rect);
|
|
gtk_tree_view_convert_tree_to_bin_window_coords
|
|
(view, view_rect.x, view_rect.y, &view_x, &view_y);
|
|
|
|
if (!(view_x <= cell_rect.x && cell_rect.x <= view_x + view_rect.width
|
|
&& view_y <= cell_rect.y && cell_rect.y <= view_y + view_rect.height))
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
self->loaded = g_hash_table_new_full
|
|
((GHashFunc) g_str_hash, (GEqualFunc) g_str_equal,
|
|
(GDestroyNotify) g_free, (GDestroyNotify) free_object);
|
|
|
|
cell_name =
|
|
/*self->external_loader ?
|
|
g_strconcat ("/", self->path, "/", name, NULL):*/
|
|
g_strdup (name);
|
|
|
|
g_hash_table_insert (self->loaded, g_strdup (cell_name), NULL);
|
|
|
|
if (!self->loader)
|
|
self->loader = db_file_loader_new (self->host, self->path);
|
|
|
|
if (self->tooltip_path && !self->tooltips)
|
|
self->tooltips = g_hash_table_new_full
|
|
((GHashFunc) g_direct_hash, (GEqualFunc) g_direct_equal,
|
|
(GDestroyNotify) NULL, (GDestroyNotify) free_tooltip_data);
|
|
|
|
data = g_new (DownloadData, 1);
|
|
data->self = g_object_ref (self);
|
|
data->model = g_object_ref (model);
|
|
data->iter = gtk_tree_iter_copy (iter);
|
|
data->cell = cell;
|
|
data->name = cell_name;
|
|
|
|
db_file_loader_download (self->loader, cell_name,
|
|
(DbFileLoaderCallbackFunc) vn_column_image_on_download, data);
|
|
}
|
|
}
|
|
|
|
static void vn_column_image_on_model_changed (VnColumnImage * self)
|
|
{
|
|
if (self->loader)
|
|
db_file_loader_cancel_all (self->loader);
|
|
|
|
if (self->loaded)
|
|
g_hash_table_destroy (self->loaded);
|
|
|
|
if (self->tooltips)
|
|
g_hash_table_destroy (self->tooltips);
|
|
|
|
self->loaded = NULL;
|
|
self->tooltips = NULL;
|
|
}
|
|
|
|
static void vn_column_image_set_editable (VnColumn * self, gboolean editable){}
|
|
|
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++ Property
|
|
|
|
enum
|
|
{
|
|
PROP_HOST = 1
|
|
,PROP_PATH
|
|
,PROP_TOOLTIP_PATH
|
|
,PROP_TOOLTIP_SIZE
|
|
,PROP_FILE_LOADER
|
|
};
|
|
|
|
static void vn_column_image_set_property (VnColumnImage * self, guint id,
|
|
const GValue * value, GParamSpec * pspec)
|
|
{
|
|
switch (id)
|
|
{
|
|
case PROP_HOST:
|
|
g_free (self->host);
|
|
self->host = g_value_dup_string (value);
|
|
break;
|
|
case PROP_PATH:
|
|
g_free (self->path);
|
|
self->path = g_value_dup_string (value);
|
|
break;
|
|
case PROP_TOOLTIP_PATH:
|
|
g_free (self->tooltip_path);
|
|
self->tooltip_path = g_value_dup_string (value);
|
|
break;
|
|
case PROP_TOOLTIP_SIZE:
|
|
self->tooltip_size = g_value_get_int (value);
|
|
break;
|
|
case PROP_FILE_LOADER:
|
|
self->loader = g_value_dup_object (value);
|
|
if (self->loader)
|
|
self->external_loader = TRUE;
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (self, id, pspec);
|
|
}
|
|
}
|
|
|
|
static void vn_column_image_get_property (VnColumnImage * self, guint id,
|
|
GValue * value, GParamSpec * pspec)
|
|
{
|
|
switch (id)
|
|
{
|
|
case PROP_HOST:
|
|
g_value_set_string (value, self->host);
|
|
break;
|
|
case PROP_PATH:
|
|
g_value_set_string (value, self->path);
|
|
break;
|
|
case PROP_TOOLTIP_PATH:
|
|
g_value_set_string (value, self->tooltip_path);
|
|
break;
|
|
case PROP_TOOLTIP_SIZE:
|
|
g_value_set_int (value, self->tooltip_size);
|
|
break;
|
|
case PROP_FILE_LOADER:
|
|
g_value_set_object (value, self->loader);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (self, id, pspec);
|
|
}
|
|
}
|
|
|
|
//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class
|
|
|
|
static void vn_column_image_init (VnColumnImage * self)
|
|
{
|
|
GtkCellRenderer * cell = gtk_cell_renderer_pixbuf_new ();
|
|
VN_COLUMN_GET_CLASS (self)->set_renderer (VN_COLUMN (self), cell);
|
|
|
|
self->host = NULL;
|
|
self->path = NULL;
|
|
self->tooltip_path = NULL;
|
|
self->loader = NULL;
|
|
self->external_loader = FALSE;
|
|
self->loaded = NULL;
|
|
self->tooltips = NULL;
|
|
self->tree_view = NULL;
|
|
}
|
|
|
|
static void vn_column_image_finalize (VnColumnImage * self)
|
|
{
|
|
g_free (self->host);
|
|
g_free (self->path);
|
|
g_free (self->tooltip_path);
|
|
|
|
if (self->loader)
|
|
{
|
|
db_file_loader_cancel_all (self->loader);
|
|
g_object_unref (self->loader);
|
|
}
|
|
|
|
if (self->loaded)
|
|
g_hash_table_destroy (self->loaded);
|
|
|
|
if (self->tooltips)
|
|
g_hash_table_destroy (self->tooltips);
|
|
|
|
if (GTK_IS_TREE_VIEW (self->tree_view))
|
|
g_signal_handlers_disconnect_by_func (self->tree_view,
|
|
vn_column_image_on_query_tooltip, self);
|
|
|
|
G_OBJECT_CLASS (vn_column_image_parent_class)->finalize (G_OBJECT (self));
|
|
}
|
|
|
|
static void vn_column_image_class_init (VnColumnImageClass * klass)
|
|
{
|
|
GObjectClass * k = G_OBJECT_CLASS (klass);
|
|
VnColumnClass * col_k = VN_COLUMN_CLASS (klass);
|
|
k->finalize = (GObjectFinalizeFunc) vn_column_image_finalize;
|
|
k->set_property = (GObjectSetPropertyFunc) vn_column_image_set_property;
|
|
k->get_property = (GObjectGetPropertyFunc) vn_column_image_get_property;
|
|
col_k->set_value = (VnColumnSetValueFunc) vn_column_image_set_value;
|
|
col_k->set_editable = (VnColumnSetEditableFunc) vn_column_image_set_editable;
|
|
col_k->model_changed = (VnColumnModelChangedFunc) vn_column_image_on_model_changed;
|
|
|
|
g_object_class_install_property (k, PROP_HOST,
|
|
g_param_spec_string ("host"
|
|
,_("Host")
|
|
,_("The host web server name to get the images")
|
|
,NULL
|
|
,G_PARAM_CONSTRUCT | G_PARAM_READWRITE
|
|
));
|
|
|
|
g_object_class_install_property (k, PROP_PATH,
|
|
g_param_spec_string ("path"
|
|
,_("Path")
|
|
,_("Base path from the host where the images will be downloaded")
|
|
,NULL
|
|
,G_PARAM_CONSTRUCT | G_PARAM_READWRITE
|
|
));
|
|
|
|
g_object_class_install_property (k, PROP_TOOLTIP_PATH,
|
|
g_param_spec_string ("tooltip-path"
|
|
,_("Tooltip path")
|
|
,_("Prefix for the path of the images to be shown in the tooltip. "
|
|
"Starting after the path of the column and appending the name "
|
|
"on each cell")
|
|
,NULL
|
|
,G_PARAM_CONSTRUCT | G_PARAM_READWRITE
|
|
));
|
|
|
|
g_object_class_install_property (k, PROP_TOOLTIP_SIZE,
|
|
g_param_spec_int ("tooltip-size"
|
|
,_("Tooltip size")
|
|
,_("Size of the bigger side of the tooltip images, the another "
|
|
"side will be scaled accordingly and smaller images won't be "
|
|
"scaled")
|
|
,-1
|
|
,G_MAXINT
|
|
,300
|
|
,G_PARAM_CONSTRUCT | G_PARAM_READWRITE
|
|
));
|
|
|
|
g_object_class_install_property (k, PROP_FILE_LOADER,
|
|
g_param_spec_object ("file-loader"
|
|
,_("File loader")
|
|
,_("An optional file loader, if it's NULL the column will create one")
|
|
,DB_TYPE_FILE_LOADER
|
|
,G_PARAM_CONSTRUCT | G_PARAM_READWRITE
|
|
));
|
|
}
|