This repository has been archived on 2024-07-15. You can view files and clone it, but cannot push or open issues or pull requests.
hedera/vn/column/vn-column-image.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
));
}