diff options
author | Claudio Saavedra <csaavedra@igalia.com> | 2012-01-25 01:21:24 +0800 |
---|---|---|
committer | Claudio Saavedra <csaavedra@igalia.com> | 2012-09-01 02:33:59 +0800 |
commit | 5793ceba3440c18f43cd0deb281db7ed9b7bc53b (patch) | |
tree | 7a8a1b4e8b56764749a1a3f6389d40b806409d88 | |
parent | b447f3c0080796b087386e013c75a3613f79d86b (diff) | |
download | gsoc2013-epiphany-5793ceba3440c18f43cd0deb281db7ed9b7bc53b.tar gsoc2013-epiphany-5793ceba3440c18f43cd0deb281db7ed9b7bc53b.tar.gz gsoc2013-epiphany-5793ceba3440c18f43cd0deb281db7ed9b7bc53b.tar.bz2 gsoc2013-epiphany-5793ceba3440c18f43cd0deb281db7ed9b7bc53b.tar.lz gsoc2013-epiphany-5793ceba3440c18f43cd0deb281db7ed9b7bc53b.tar.xz gsoc2013-epiphany-5793ceba3440c18f43cd0deb281db7ed9b7bc53b.tar.zst gsoc2013-epiphany-5793ceba3440c18f43cd0deb281db7ed9b7bc53b.zip |
Add GdMainView for use in the overview
This widget courtesy of gnome-documents
https://bugzilla.gnome.org/show_bug.cgi?id=455173
-rw-r--r-- | lib/widgets/Makefile.am | 12 | ||||
-rw-r--r-- | lib/widgets/gd-main-icon-view.c | 204 | ||||
-rw-r--r-- | lib/widgets/gd-main-icon-view.h | 74 | ||||
-rw-r--r-- | lib/widgets/gd-main-list-view.c | 224 | ||||
-rw-r--r-- | lib/widgets/gd-main-list-view.h | 80 | ||||
-rw-r--r-- | lib/widgets/gd-main-view-generic.c | 155 | ||||
-rw-r--r-- | lib/widgets/gd-main-view-generic.h | 97 | ||||
-rw-r--r-- | lib/widgets/gd-main-view.c | 725 | ||||
-rw-r--r-- | lib/widgets/gd-main-view.h | 103 | ||||
-rw-r--r-- | lib/widgets/gd-toggle-pixbuf-renderer.c | 198 | ||||
-rw-r--r-- | lib/widgets/gd-toggle-pixbuf-renderer.h | 75 | ||||
-rw-r--r-- | lib/widgets/gd-two-lines-renderer.c | 528 | ||||
-rw-r--r-- | lib/widgets/gd-two-lines-renderer.h | 75 |
13 files changed, 2550 insertions, 0 deletions
diff --git a/lib/widgets/Makefile.am b/lib/widgets/Makefile.am index 8a7e54b81..71980752c 100644 --- a/lib/widgets/Makefile.am +++ b/lib/widgets/Makefile.am @@ -29,6 +29,18 @@ libephywidgets_la_SOURCES = \ ephy-urls-view.h \ ephy-zoom-action.h \ ephy-zoom-action.c \ + gd-main-icon-view.c \ + gd-main-icon-view.h \ + gd-main-list-view.c \ + gd-main-list-view.h \ + gd-main-view-generic.c \ + gd-main-view-generic.h \ + gd-main-view.c \ + gd-main-view.h \ + gd-toggle-pixbuf-renderer.c \ + gd-toggle-pixbuf-renderer.h \ + gd-two-lines-renderer.c \ + gd-two-lines-renderer.h \ nautilus-floating-bar.c \ nautilus-floating-bar.h \ totem-glow-button.c \ diff --git a/lib/widgets/gd-main-icon-view.c b/lib/widgets/gd-main-icon-view.c new file mode 100644 index 000000000..4cc14c6cd --- /dev/null +++ b/lib/widgets/gd-main-icon-view.c @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2011 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2 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 Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: Cosimo Cecchi <cosimoc@redhat.com> + * + */ + +#include "gd-main-icon-view.h" +#include "gd-main-view.h" +#include "gd-main-view-generic.h" +#include "gd-toggle-pixbuf-renderer.h" +#include "gd-two-lines-renderer.h" + +#include <math.h> +#include <glib/gi18n.h> + +#define VIEW_ITEM_WIDTH 140 +#define VIEW_ITEM_WRAP_WIDTH 128 +#define VIEW_COLUMN_SPACING 20 +#define VIEW_MARGIN 16 + +struct _GdMainIconViewPrivate { + GtkCellRenderer *pixbuf_cell; + gboolean selection_mode; +}; + +static void gd_main_view_generic_iface_init (GdMainViewGenericIface *iface); +G_DEFINE_TYPE_WITH_CODE (GdMainIconView, gd_main_icon_view, GTK_TYPE_ICON_VIEW, + G_IMPLEMENT_INTERFACE (GD_TYPE_MAIN_VIEW_GENERIC, + gd_main_view_generic_iface_init)) + +static GtkTreePath* +get_source_row (GdkDragContext *context) +{ + GtkTreeRowReference *ref; + + ref = g_object_get_data (G_OBJECT (context), "gtk-icon-view-source-row"); + + if (ref) + return gtk_tree_row_reference_get_path (ref); + else + return NULL; +} + +static void +gd_main_icon_view_drag_data_get (GtkWidget *widget, + GdkDragContext *drag_context, + GtkSelectionData *data, + guint info, + guint time) +{ + GdMainIconView *self = GD_MAIN_ICON_VIEW (widget); + GtkTreeModel *model = gtk_icon_view_get_model (GTK_ICON_VIEW (self)); + + if (info != 0) + return; + + _gd_main_view_generic_dnd_common (model, self->priv->selection_mode, + get_source_row (drag_context), data); + + GTK_WIDGET_CLASS (gd_main_icon_view_parent_class)->drag_data_get (widget, drag_context, + data, info, time); +} + +static void +gd_main_icon_view_constructed (GObject *obj) +{ + GdMainIconView *self = GD_MAIN_ICON_VIEW (obj); + GtkCellRenderer *cell; + const GtkTargetEntry targets[] = { + { "text/uri-list", GTK_TARGET_OTHER_APP, 0 } + }; + + G_OBJECT_CLASS (gd_main_icon_view_parent_class)->constructed (obj); + + gtk_widget_set_hexpand (GTK_WIDGET (self), TRUE); + gtk_widget_set_vexpand (GTK_WIDGET (self), TRUE); + gtk_icon_view_set_selection_mode (GTK_ICON_VIEW (self), GTK_SELECTION_NONE); + + g_object_set (self, + "column-spacing", VIEW_COLUMN_SPACING, + "margin", VIEW_MARGIN, + NULL); + + self->priv->pixbuf_cell = cell = gd_toggle_pixbuf_renderer_new (); + g_object_set (cell, + "xalign", 0.5, + "yalign", 0.5, + NULL); + + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (self), cell, FALSE); + gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (self), cell, + "active", GD_MAIN_COLUMN_SELECTED); + gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (self), cell, + "pixbuf", GD_MAIN_COLUMN_ICON); + + cell = gd_two_lines_renderer_new (); + g_object_set (cell, + "alignment", PANGO_ALIGN_CENTER, + "wrap-mode", PANGO_WRAP_WORD_CHAR, + "wrap-width", VIEW_ITEM_WRAP_WIDTH, + "text-lines", 3, + NULL); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (self), cell, FALSE); + gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (self), cell, + "text", GD_MAIN_COLUMN_TITLE); + gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (self), cell, + "line-two", GD_MAIN_COLUMN_AUTHOR); + + gtk_icon_view_enable_model_drag_source (GTK_ICON_VIEW (self), + GDK_BUTTON1_MASK, + targets, 1, + GDK_ACTION_COPY); +} + +static void +gd_main_icon_view_class_init (GdMainIconViewClass *klass) +{ + GObjectClass *oclass = G_OBJECT_CLASS (klass); + GtkWidgetClass *wclass = GTK_WIDGET_CLASS (klass); + + oclass->constructed = gd_main_icon_view_constructed; + wclass->drag_data_get = gd_main_icon_view_drag_data_get; + + gtk_widget_class_install_style_property (wclass, + g_param_spec_int ("check-icon-size", + "Check icon size", + "Check icon size", + -1, G_MAXINT, 40, + G_PARAM_READWRITE)); + + g_type_class_add_private (klass, sizeof (GdMainIconViewPrivate)); +} + +static void +gd_main_icon_view_init (GdMainIconView *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GD_TYPE_MAIN_ICON_VIEW, GdMainIconViewPrivate); +} + +static GtkTreePath * +gd_main_icon_view_get_path_at_pos (GdMainViewGeneric *mv, + gint x, + gint y) +{ + return gtk_icon_view_get_path_at_pos (GTK_ICON_VIEW (mv), x, y); +} + +static void +gd_main_icon_view_set_selection_mode (GdMainViewGeneric *mv, + gboolean selection_mode) +{ + GdMainIconView *self = GD_MAIN_ICON_VIEW (mv); + + self->priv->selection_mode = selection_mode; + + g_object_set (self->priv->pixbuf_cell, + "toggle-visible", selection_mode, + NULL); + gtk_widget_queue_draw (GTK_WIDGET (self)); +} + +static void +gd_main_icon_view_scroll_to_path (GdMainViewGeneric *mv, + GtkTreePath *path) +{ + gtk_icon_view_scroll_to_path (GTK_ICON_VIEW (mv), path, TRUE, 0.5, 0.5); +} + +static void +gd_main_icon_view_set_model (GdMainViewGeneric *mv, + GtkTreeModel *model) +{ + gtk_icon_view_set_model (GTK_ICON_VIEW (mv), model); +} + +static void +gd_main_view_generic_iface_init (GdMainViewGenericIface *iface) +{ + iface->set_model = gd_main_icon_view_set_model; + iface->get_path_at_pos = gd_main_icon_view_get_path_at_pos; + iface->scroll_to_path = gd_main_icon_view_scroll_to_path; + iface->set_selection_mode = gd_main_icon_view_set_selection_mode; +} + +GtkWidget * +gd_main_icon_view_new (void) +{ + return g_object_new (GD_TYPE_MAIN_ICON_VIEW, NULL); +} diff --git a/lib/widgets/gd-main-icon-view.h b/lib/widgets/gd-main-icon-view.h new file mode 100644 index 000000000..7370b2af8 --- /dev/null +++ b/lib/widgets/gd-main-icon-view.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2011 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2 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 Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: Cosimo Cecchi <cosimoc@redhat.com> + * + */ + +#ifndef __GD_MAIN_ICON_VIEW_H__ +#define __GD_MAIN_ICON_VIEW_H__ + +#include <glib-object.h> +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +#define GD_TYPE_MAIN_ICON_VIEW gd_main_icon_view_get_type() + +#define GD_MAIN_ICON_VIEW(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + GD_TYPE_MAIN_ICON_VIEW, GdMainIconView)) + +#define GD_MAIN_ICON_VIEW_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), \ + GD_TYPE_MAIN_ICON_VIEW, GdMainIconViewClass)) + +#define GD_IS_MAIN_ICON_VIEW(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + GD_TYPE_MAIN_ICON_VIEW)) + +#define GD_IS_MAIN_ICON_VIEW_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + GD_TYPE_MAIN_ICON_VIEW)) + +#define GD_MAIN_ICON_VIEW_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + GD_TYPE_MAIN_ICON_VIEW, GdMainIconViewClass)) + +typedef struct _GdMainIconView GdMainIconView; +typedef struct _GdMainIconViewClass GdMainIconViewClass; +typedef struct _GdMainIconViewPrivate GdMainIconViewPrivate; + +struct _GdMainIconView +{ + GtkIconView parent; + + GdMainIconViewPrivate *priv; +}; + +struct _GdMainIconViewClass +{ + GtkIconViewClass parent_class; +}; + +GType gd_main_icon_view_get_type (void) G_GNUC_CONST; + +GtkWidget * gd_main_icon_view_new (void); + +G_END_DECLS + +#endif /* __GD_MAIN_ICON_VIEW_H__ */ diff --git a/lib/widgets/gd-main-list-view.c b/lib/widgets/gd-main-list-view.c new file mode 100644 index 000000000..ef14051f4 --- /dev/null +++ b/lib/widgets/gd-main-list-view.c @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2011 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2 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 Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: Cosimo Cecchi <cosimoc@redhat.com> + * + */ + +#include "gd-main-list-view.h" +#include "gd-main-view.h" +#include "gd-main-view-generic.h" +#include "gd-two-lines-renderer.h" + +#include <glib/gi18n.h> + +struct _GdMainListViewPrivate { + GtkTreeViewColumn *tree_col; + GtkCellRenderer *selection_cell; + + gboolean selection_mode; +}; + +static void gd_main_view_generic_iface_init (GdMainViewGenericIface *iface); +G_DEFINE_TYPE_WITH_CODE (GdMainListView, gd_main_list_view, GTK_TYPE_TREE_VIEW, + G_IMPLEMENT_INTERFACE (GD_TYPE_MAIN_VIEW_GENERIC, + gd_main_view_generic_iface_init)) + +static GtkTreePath* +get_source_row (GdkDragContext *context) +{ + GtkTreeRowReference *ref = + g_object_get_data (G_OBJECT (context), "gtk-tree-view-source-row"); + + if (ref) + return gtk_tree_row_reference_get_path (ref); + else + return NULL; +} + +static void +gd_main_list_view_drag_data_get (GtkWidget *widget, + GdkDragContext *drag_context, + GtkSelectionData *data, + guint info, + guint time) +{ + GdMainListView *self = GD_MAIN_LIST_VIEW (widget); + GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (self)); + + if (info != 0) + return; + + _gd_main_view_generic_dnd_common (model, + self->priv->selection_mode, + get_source_row (drag_context), data); + + GTK_WIDGET_CLASS (gd_main_list_view_parent_class)->drag_data_get (widget, drag_context, + data, info, time); +} + +static void +gd_main_list_view_constructed (GObject *obj) +{ + GdMainListView *self = GD_MAIN_LIST_VIEW (obj); + GtkCellRenderer *cell; + GtkTreeSelection *selection; + const GtkTargetEntry targets[] = { + { "text/uri-list", GTK_TARGET_OTHER_APP, 0 } + }; + + G_OBJECT_CLASS (gd_main_list_view_parent_class)->constructed (obj); + + gtk_widget_set_hexpand (GTK_WIDGET (self), TRUE); + gtk_widget_set_vexpand (GTK_WIDGET (self), TRUE); + + g_object_set (self, + "headers-visible", FALSE, + "enable-search", FALSE, + NULL); + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self)); + gtk_tree_selection_set_mode (selection, GTK_SELECTION_NONE); + + self->priv->tree_col = gtk_tree_view_column_new (); + gtk_tree_view_append_column (GTK_TREE_VIEW (self), self->priv->tree_col); + + self->priv->selection_cell = cell = gtk_cell_renderer_toggle_new (); + g_object_set (cell, + "visible", FALSE, + "xpad", 12, + "xalign", 1.0, + NULL); + gtk_tree_view_column_pack_start (self->priv->tree_col, cell, FALSE); + gtk_tree_view_column_add_attribute (self->priv->tree_col, cell, + "active", GD_MAIN_COLUMN_SELECTED); + + cell = gtk_cell_renderer_pixbuf_new (); + g_object_set (cell, + "xalign", 0.5, + "yalign", 0.5, + "xpad", 12, + "ypad", 2, + NULL); + gtk_tree_view_column_pack_start (self->priv->tree_col, cell, FALSE); + gtk_tree_view_column_add_attribute (self->priv->tree_col, cell, + "pixbuf", GD_MAIN_COLUMN_ICON); + + cell = gd_two_lines_renderer_new (); + g_object_set (cell, + "xalign", 0.0, + "wrap-mode", PANGO_WRAP_WORD_CHAR, + "xpad", 12, + "text-lines", 2, + NULL); + gtk_tree_view_column_pack_start (self->priv->tree_col, cell, TRUE); + gtk_tree_view_column_add_attribute (self->priv->tree_col, cell, + "text", GD_MAIN_COLUMN_TITLE); + gtk_tree_view_column_add_attribute (self->priv->tree_col, cell, + "line-two", GD_MAIN_COLUMN_AUTHOR); + + gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (self), + GDK_BUTTON1_MASK, + targets, 1, + GDK_ACTION_COPY); +} + +static void +gd_main_list_view_class_init (GdMainListViewClass *klass) +{ + GObjectClass *oclass = G_OBJECT_CLASS (klass); + GtkWidgetClass *wclass = GTK_WIDGET_CLASS (klass); + + oclass->constructed = gd_main_list_view_constructed; + wclass->drag_data_get = gd_main_list_view_drag_data_get; + + g_type_class_add_private (klass, sizeof (GdMainListViewPrivate)); +} + +static void +gd_main_list_view_init (GdMainListView *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GD_TYPE_MAIN_LIST_VIEW, GdMainListViewPrivate); +} + +static GtkTreePath * +gd_main_list_view_get_path_at_pos (GdMainViewGeneric *mv, + gint x, + gint y) +{ + GtkTreePath *path = NULL; + + gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (mv), x, y, &path, + NULL, NULL, NULL); + + return path; +} + +static void +gd_main_list_view_set_selection_mode (GdMainViewGeneric *mv, + gboolean selection_mode) +{ + GdMainListView *self = GD_MAIN_LIST_VIEW (mv); + + self->priv->selection_mode = selection_mode; + + g_object_set (self->priv->selection_cell, + "visible", selection_mode, + NULL); + gtk_tree_view_column_queue_resize (self->priv->tree_col); +} + +static void +gd_main_list_view_scroll_to_path (GdMainViewGeneric *mv, + GtkTreePath *path) +{ + gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (mv), path, NULL, TRUE, 0.5, 0.5); +} + +static void +gd_main_list_view_set_model (GdMainViewGeneric *mv, + GtkTreeModel *model) +{ + gtk_tree_view_set_model (GTK_TREE_VIEW (mv), model); +} + +static void +gd_main_view_generic_iface_init (GdMainViewGenericIface *iface) +{ + iface->set_model = gd_main_list_view_set_model; + iface->get_path_at_pos = gd_main_list_view_get_path_at_pos; + iface->scroll_to_path = gd_main_list_view_scroll_to_path; + iface->set_selection_mode = gd_main_list_view_set_selection_mode; +} + +void +gd_main_list_view_add_renderer (GdMainListView *self, + GtkCellRenderer *renderer, + GtkTreeCellDataFunc func, + gpointer user_data, + GDestroyNotify destroy) +{ + gtk_tree_view_column_pack_start (self->priv->tree_col, renderer, FALSE); + gtk_tree_view_column_set_cell_data_func (self->priv->tree_col, renderer, + func, user_data, destroy); +} + +GtkWidget * +gd_main_list_view_new (void) +{ + return g_object_new (GD_TYPE_MAIN_LIST_VIEW, NULL); +} diff --git a/lib/widgets/gd-main-list-view.h b/lib/widgets/gd-main-list-view.h new file mode 100644 index 000000000..317e9c4bf --- /dev/null +++ b/lib/widgets/gd-main-list-view.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2011 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2 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 Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: Cosimo Cecchi <cosimoc@redhat.com> + * + */ + +#ifndef __GD_MAIN_LIST_VIEW_H__ +#define __GD_MAIN_LIST_VIEW_H__ + +#include <glib-object.h> +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +#define GD_TYPE_MAIN_LIST_VIEW gd_main_list_view_get_type() + +#define GD_MAIN_LIST_VIEW(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + GD_TYPE_MAIN_LIST_VIEW, GdMainListView)) + +#define GD_MAIN_LIST_VIEW_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), \ + GD_TYPE_MAIN_LIST_VIEW, GdMainListViewClass)) + +#define GD_IS_MAIN_LIST_VIEW(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + GD_TYPE_MAIN_LIST_VIEW)) + +#define GD_IS_MAIN_LIST_VIEW_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + GD_TYPE_MAIN_LIST_VIEW)) + +#define GD_MAIN_LIST_VIEW_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + GD_TYPE_MAIN_LIST_VIEW, GdMainListViewClass)) + +typedef struct _GdMainListView GdMainListView; +typedef struct _GdMainListViewClass GdMainListViewClass; +typedef struct _GdMainListViewPrivate GdMainListViewPrivate; + +struct _GdMainListView +{ + GtkTreeView parent; + + GdMainListViewPrivate *priv; +}; + +struct _GdMainListViewClass +{ + GtkTreeViewClass parent_class; +}; + +GType gd_main_list_view_get_type (void) G_GNUC_CONST; + +GtkWidget * gd_main_list_view_new (void); + +void gd_main_list_view_add_renderer (GdMainListView *self, + GtkCellRenderer *renderer, + GtkTreeCellDataFunc func, + gpointer user_data, + GDestroyNotify destroy); + +G_END_DECLS + +#endif /* __GD_MAIN_LIST_VIEW_H__ */ diff --git a/lib/widgets/gd-main-view-generic.c b/lib/widgets/gd-main-view-generic.c new file mode 100644 index 000000000..df8d75685 --- /dev/null +++ b/lib/widgets/gd-main-view-generic.c @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2011 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2 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 Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: Cosimo Cecchi <cosimoc@redhat.com> + * + */ + +#include "gd-main-view.h" +#include "gd-main-view-generic.h" + +typedef GdMainViewGenericIface GdMainViewGenericInterface; +G_DEFINE_INTERFACE (GdMainViewGeneric, gd_main_view_generic, GTK_TYPE_WIDGET) + +static void +gd_main_view_generic_default_init (GdMainViewGenericInterface *iface) +{ + /* nothing */ +} + +/** + * gd_main_view_generic_set_model: + * @self: + * @model: (allow-none): + * + */ +void +gd_main_view_generic_set_model (GdMainViewGeneric *self, + GtkTreeModel *model) +{ + GdMainViewGenericInterface *iface; + + iface = GD_MAIN_VIEW_GENERIC_GET_IFACE (self); + + (* iface->set_model) (self, model); +} + +GtkTreePath * +gd_main_view_generic_get_path_at_pos (GdMainViewGeneric *self, + gint x, + gint y) +{ + GdMainViewGenericInterface *iface; + + iface = GD_MAIN_VIEW_GENERIC_GET_IFACE (self); + + return (* iface->get_path_at_pos) (self, x, y); +} + +void +gd_main_view_generic_set_selection_mode (GdMainViewGeneric *self, + gboolean selection_mode) +{ + GdMainViewGenericInterface *iface; + + iface = GD_MAIN_VIEW_GENERIC_GET_IFACE (self); + + (* iface->set_selection_mode) (self, selection_mode); +} + +void +gd_main_view_generic_scroll_to_path (GdMainViewGeneric *self, + GtkTreePath *path) +{ + GdMainViewGenericInterface *iface; + + iface = GD_MAIN_VIEW_GENERIC_GET_IFACE (self); + + (* iface->scroll_to_path) (self, path); +} + +static gboolean +build_selection_uris_foreach (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer user_data) +{ + GPtrArray *ptr_array = user_data; + gchar *uri; + gboolean is_selected; + + gtk_tree_model_get (model, iter, + GD_MAIN_COLUMN_URI, &uri, + GD_MAIN_COLUMN_SELECTED, &is_selected, + -1); + + if (is_selected) + g_ptr_array_add (ptr_array, uri); + else + g_free (uri); + + return FALSE; +} + +static gchar ** +model_get_selection_uris (GtkTreeModel *model) +{ + GPtrArray *ptr_array = g_ptr_array_new (); + + gtk_tree_model_foreach (model, + build_selection_uris_foreach, + ptr_array); + + g_ptr_array_add (ptr_array, NULL); + return (gchar **) g_ptr_array_free (ptr_array, FALSE); +} + +void +_gd_main_view_generic_dnd_common (GtkTreeModel *model, + gboolean selection_mode, + GtkTreePath *path, + GtkSelectionData *data) +{ + gchar **uris; + + if (selection_mode) + { + uris = model_get_selection_uris (model); + } + else + { + GtkTreeIter iter; + gboolean res; + gchar *uri = NULL; + + if (path != NULL) + { + res = gtk_tree_model_get_iter (model, &iter, path); + if (res) + gtk_tree_model_get (model, &iter, + GD_MAIN_COLUMN_URI, &uri, + -1); + } + + uris = g_new0 (gchar *, 2); + uris[0] = uri; + uris[1] = NULL; + } + + gtk_selection_data_set_uris (data, uris); + g_strfreev (uris); +} diff --git a/lib/widgets/gd-main-view-generic.h b/lib/widgets/gd-main-view-generic.h new file mode 100644 index 000000000..d47cd109f --- /dev/null +++ b/lib/widgets/gd-main-view-generic.h @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2011 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2 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 Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: Cosimo Cecchi <cosimoc@redhat.com> + * + */ + +#ifndef __GD_MAIN_VIEW_GENERIC_H__ +#define __GD_MAIN_VIEW_GENERIC_H__ + +#include <glib-object.h> + +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +#define GD_TYPE_MAIN_VIEW_GENERIC gd_main_view_generic_get_type() + +#define GD_MAIN_VIEW_GENERIC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + GD_TYPE_MAIN_VIEW_GENERIC, GdMainViewGeneric)) + +#define GD_MAIN_VIEW_GENERIC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), \ + GD_TYPE_MAIN_VIEW_GENERIC, GdMainViewGenericIface)) + +#define GD_IS_MAIN_VIEW_GENERIC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + GD_TYPE_MAIN_VIEW_GENERIC)) + +#define GD_IS_MAIN_VIEW_GENERIC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + GD_TYPE_MAIN_VIEW_GENERIC)) + +#define GD_MAIN_VIEW_GENERIC_GET_IFACE(obj) \ + (G_TYPE_INSTANCE_GET_INTERFACE ((obj), \ + GD_TYPE_MAIN_VIEW_GENERIC, GdMainViewGenericIface)) + +typedef struct _GdMainViewGeneric GdMainViewGeneric; +typedef struct _GdMainViewGenericIface GdMainViewGenericIface; + +struct _GdMainViewGenericIface +{ + GTypeInterface base_iface; + + /* signals */ + void (* selection_changed) (GdMainViewGeneric *self); + + /* vtable */ + void (* set_model) (GdMainViewGeneric *self, + GtkTreeModel *model); + + GtkTreePath * (* get_path_at_pos) (GdMainViewGeneric *self, + gint x, + gint y); + void (* scroll_to_path) (GdMainViewGeneric *self, + GtkTreePath *path); + void (* set_selection_mode) (GdMainViewGeneric *self, + gboolean selection_mode); +}; + +GType gd_main_view_generic_get_type (void) G_GNUC_CONST; + +void gd_main_view_generic_set_model (GdMainViewGeneric *self, + GtkTreeModel *model); + +void gd_main_view_generic_scroll_to_path (GdMainViewGeneric *self, + GtkTreePath *path); +void gd_main_view_generic_set_selection_mode (GdMainViewGeneric *self, + gboolean selection_mode); +GtkTreePath * gd_main_view_generic_get_path_at_pos (GdMainViewGeneric *self, + gint x, + gint y); + +/* private */ +void _gd_main_view_generic_dnd_common (GtkTreeModel *model, + gboolean selection_mode, + GtkTreePath *path, + GtkSelectionData *data); + +G_END_DECLS + +#endif /* __GD_MAIN_VIEW_GENERIC_H__ */ diff --git a/lib/widgets/gd-main-view.c b/lib/widgets/gd-main-view.c new file mode 100644 index 000000000..8c3545e99 --- /dev/null +++ b/lib/widgets/gd-main-view.c @@ -0,0 +1,725 @@ +/* + * Copyright (c) 2011 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2 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 Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: Cosimo Cecchi <cosimoc@redhat.com> + * + */ + +#include "gd-main-view.h" + +#include "gd-main-view-generic.h" +#include "gd-main-icon-view.h" +#include "gd-main-list-view.h" + +#define MAIN_VIEW_TYPE_INITIAL -1 +#define MAIN_VIEW_DND_ICON_OFFSET 20 + +struct _GdMainViewPrivate { + GdMainViewType current_type; + gboolean selection_mode; + + GtkWidget *current_view; + GtkTreeModel *model; + + gchar *button_press_item_path; +}; + +enum { + PROP_VIEW_TYPE = 1, + PROP_SELECTION_MODE, + PROP_MODEL, + NUM_PROPERTIES +}; + +enum { + ITEM_ACTIVATED = 1, + SELECTION_MODE_REQUEST, + VIEW_SELECTION_CHANGED, + NUM_SIGNALS +}; + +static GParamSpec *properties[NUM_PROPERTIES] = { NULL, }; +static guint signals[NUM_SIGNALS] = { 0, }; + +G_DEFINE_TYPE (GdMainView, gd_main_view, GTK_TYPE_SCROLLED_WINDOW) + +static void +gd_main_view_dispose (GObject *obj) +{ + GdMainView *self = GD_MAIN_VIEW (obj); + + g_clear_object (&self->priv->model); + + G_OBJECT_CLASS (gd_main_view_parent_class)->dispose (obj); +} + +static void +gd_main_view_finalize (GObject *obj) +{ + GdMainView *self = GD_MAIN_VIEW (obj); + + g_free (self->priv->button_press_item_path); + + G_OBJECT_CLASS (gd_main_view_parent_class)->finalize (obj); +} + +static void +gd_main_view_init (GdMainView *self) +{ + GtkStyleContext *context; + + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GD_TYPE_MAIN_VIEW, GdMainViewPrivate); + + /* so that we get constructed with the right view even at startup */ + self->priv->current_type = MAIN_VIEW_TYPE_INITIAL; + + gtk_widget_set_hexpand (GTK_WIDGET (self), TRUE); + gtk_widget_set_vexpand (GTK_WIDGET (self), TRUE); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (self), GTK_SHADOW_IN); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (self), + GTK_POLICY_NEVER, + GTK_POLICY_AUTOMATIC); + + context = gtk_widget_get_style_context (GTK_WIDGET (self)); + gtk_style_context_add_class (context, "documents-scrolledwin"); +} + +static void +gd_main_view_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GdMainView *self = GD_MAIN_VIEW (object); + + switch (property_id) + { + case PROP_VIEW_TYPE: + g_value_set_int (value, gd_main_view_get_view_type (self)); + break; + case PROP_SELECTION_MODE: + g_value_set_boolean (value, gd_main_view_get_selection_mode (self)); + break; + case PROP_MODEL: + g_value_set_object (value, gd_main_view_get_model (self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gd_main_view_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GdMainView *self = GD_MAIN_VIEW (object); + + switch (property_id) + { + case PROP_VIEW_TYPE: + gd_main_view_set_view_type (self, g_value_get_int (value)); + break; + case PROP_SELECTION_MODE: + gd_main_view_set_selection_mode (self, g_value_get_boolean (value)); + break; + case PROP_MODEL: + gd_main_view_set_model (self, g_value_get_object (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gd_main_view_class_init (GdMainViewClass *klass) +{ + GObjectClass *oclass = G_OBJECT_CLASS (klass); + + oclass->get_property = gd_main_view_get_property; + oclass->set_property = gd_main_view_set_property; + oclass->dispose = gd_main_view_dispose; + oclass->finalize = gd_main_view_finalize; + + properties[PROP_VIEW_TYPE] = + g_param_spec_int ("view-type", + "View type", + "View type", + GD_MAIN_VIEW_ICON, + GD_MAIN_VIEW_LIST, + GD_MAIN_VIEW_ICON, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); + + properties[PROP_SELECTION_MODE] = + g_param_spec_boolean ("selection-mode", + "Selection mode", + "Whether the view is in selection mode", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS); + + properties[PROP_MODEL] = + g_param_spec_object ("model", + "Model", + "The GtkTreeModel", + GTK_TYPE_TREE_MODEL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS); + + signals[ITEM_ACTIVATED] = + g_signal_new ("item-activated", + GD_TYPE_MAIN_VIEW, + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, NULL, + G_TYPE_NONE, 2, + G_TYPE_STRING, + GTK_TYPE_TREE_PATH); + + signals[SELECTION_MODE_REQUEST] = + g_signal_new ("selection-mode-request", + GD_TYPE_MAIN_VIEW, + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, NULL, + G_TYPE_NONE, 0); + + signals[VIEW_SELECTION_CHANGED] = + g_signal_new ("view-selection-changed", + GD_TYPE_MAIN_VIEW, + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, NULL, + G_TYPE_NONE, 0); + + g_type_class_add_private (klass, sizeof (GdMainViewPrivate)); + g_object_class_install_properties (oclass, NUM_PROPERTIES, properties); +} + +static GdkPixbuf * +gd_main_view_get_counter_icon (GdMainView *self, + GdkPixbuf *base, + gint number) +{ + GtkStyleContext *context; + cairo_t *cr, *emblem_cr; + cairo_surface_t *surface, *emblem_surface; + GdkPixbuf *retval; + gint width, height; + gint layout_width, layout_height; + gint emblem_size; + gdouble scale; + gchar *str; + PangoLayout *layout; + PangoAttrList *attr_list; + PangoAttribute *attr; + const PangoFontDescription *desc; + GdkRGBA color; + + context = gtk_widget_get_style_context (GTK_WIDGET (self)); + gtk_style_context_save (context); + gtk_style_context_add_class (context, "documents-counter"); + + width = gdk_pixbuf_get_width (base); + height = gdk_pixbuf_get_height (base); + + surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, + width, height); + cr = cairo_create (surface); + gdk_cairo_set_source_pixbuf (cr, base, 0, 0); + cairo_paint (cr); + + emblem_size = MIN (width / 2, height / 2); + emblem_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, + emblem_size, emblem_size); + emblem_cr = cairo_create (emblem_surface); + gtk_render_background (context, emblem_cr, + 0, 0, emblem_size, emblem_size); + + if (number > 99) + number = 99; + if (number < -99) + number = -99; + + str = g_strdup_printf ("%d", number); + layout = gtk_widget_create_pango_layout (GTK_WIDGET (self), str); + g_free (str); + + pango_layout_get_pixel_size (layout, &layout_width, &layout_height); + + /* scale the layout to be 0.5 of the size still available for drawing */ + scale = (emblem_size * 0.50) / (MAX (layout_width, layout_height)); + attr_list = pango_attr_list_new (); + + attr = pango_attr_scale_new (scale); + pango_attr_list_insert (attr_list, attr); + pango_layout_set_attributes (layout, attr_list); + + desc = gtk_style_context_get_font (context, 0); + pango_layout_set_font_description (layout, desc); + + gtk_style_context_get_color (context, 0, &color); + gdk_cairo_set_source_rgba (emblem_cr, &color); + + /* update these values */ + pango_layout_get_pixel_size (layout, &layout_width, &layout_height); + + cairo_move_to (emblem_cr, + emblem_size / 2 - layout_width / 2, + emblem_size / 2 - layout_height / 2); + + pango_cairo_show_layout (emblem_cr, layout); + + g_object_unref (layout); + pango_attr_list_unref (attr_list); + cairo_destroy (emblem_cr); + + cairo_set_source_surface (cr, emblem_surface, + width - emblem_size, height - emblem_size); + cairo_paint (cr); + cairo_destroy (cr); + + retval = gdk_pixbuf_get_from_surface (surface, + 0, 0, + width, height); + + cairo_surface_destroy (emblem_surface); + cairo_surface_destroy (surface); + gtk_style_context_restore (context); + + return retval; +} + +static GdMainViewGeneric * +get_generic (GdMainView *self) +{ + if (self->priv->current_view != NULL) + return GD_MAIN_VIEW_GENERIC (self->priv->current_view); + + return NULL; +} + +static gboolean +on_button_release_selection_mode (GdMainView *self, + GdkEventButton *event, + gboolean entered_mode, + GtkTreePath *path) +{ + gboolean selected; + GtkTreeIter iter; + + if (!gtk_tree_model_get_iter (self->priv->model, &iter, path)) + return FALSE; + + gtk_tree_model_get (self->priv->model, &iter, + GD_MAIN_COLUMN_SELECTED, &selected, + -1); + + if (selected && !entered_mode) + gtk_list_store_set (GTK_LIST_STORE (self->priv->model), &iter, + GD_MAIN_COLUMN_SELECTED, FALSE, + -1); + else if (!selected) + gtk_list_store_set (GTK_LIST_STORE (self->priv->model), &iter, + GD_MAIN_COLUMN_SELECTED, TRUE, + -1); + + g_signal_emit (self, signals[VIEW_SELECTION_CHANGED], 0); + + return FALSE; +} + +static gboolean +on_button_release_view_mode (GdMainView *self, + GdkEventButton *event, + GtkTreePath *path) +{ + GtkTreeIter iter; + gchar *id; + + if (self->priv->model == NULL) + return FALSE; + + if (!gtk_tree_model_get_iter (self->priv->model, &iter, path)) + return FALSE; + + gtk_tree_model_get (self->priv->model, &iter, + GD_MAIN_COLUMN_ID, &id, + -1); + + g_signal_emit (self, signals[ITEM_ACTIVATED], 0, id, path); + g_free (id); + + return FALSE; +} + +static gboolean +on_button_release_event (GtkWidget *view, + GdkEventButton *event, + gpointer user_data) +{ + GdMainView *self = user_data; + GdMainViewGeneric *generic = get_generic (self); + GtkTreePath *path; + gchar *button_release_item_path; + gboolean entered_mode = FALSE, selection_mode; + gboolean res, same_item = FALSE; + + /* eat double/triple click events */ + if (event->type != GDK_BUTTON_RELEASE) + return TRUE; + + path = gd_main_view_generic_get_path_at_pos (generic, event->x, event->y); + + if (path != NULL) + { + button_release_item_path = gtk_tree_path_to_string (path); + if (g_strcmp0 (self->priv->button_press_item_path, button_release_item_path) == 0) + same_item = TRUE; + + g_free (button_release_item_path); + } + + g_free (self->priv->button_press_item_path); + self->priv->button_press_item_path = NULL; + + if (!same_item) + { + res = FALSE; + goto out; + } + + selection_mode = self->priv->selection_mode; + + if (!selection_mode) + { + if ((event->button == 3) || + ((event->button == 1) && (event->state & GDK_CONTROL_MASK))) + { + g_signal_emit (self, signals[SELECTION_MODE_REQUEST], 0); + selection_mode = TRUE; + entered_mode = TRUE; + } + } + + if (selection_mode) + res = on_button_release_selection_mode (self, event, entered_mode, path); + else + res = on_button_release_view_mode (self, event, path); + + out: + gtk_tree_path_free (path); + return res; +} + +static gboolean +on_button_press_event (GtkWidget *view, + GdkEventButton *event, + gpointer user_data) +{ + GdMainView *self = user_data; + GdMainViewGeneric *generic = get_generic (self); + GtkTreePath *path; + GList *selection, *l; + GtkTreePath *sel_path; + gboolean found = FALSE; + + path = gd_main_view_generic_get_path_at_pos (generic, event->x, event->y); + + if (path != NULL) + self->priv->button_press_item_path = gtk_tree_path_to_string (path); + + if (!self->priv->selection_mode || + path == NULL) + { + gtk_tree_path_free (path); + return FALSE; + } + + selection = gd_main_view_get_selection (self); + + for (l = selection; l != NULL; l = l->next) + { + sel_path = l->data; + if (gtk_tree_path_compare (path, sel_path) == 0) + { + found = TRUE; + break; + } + } + + if (selection != NULL) + g_list_free_full (selection, (GDestroyNotify) gtk_tree_path_free); + + /* if we did not find the item in the selection, block + * drag and drop, while in selection mode + */ + return !found; +} + +static void +on_drag_begin (GdMainViewGeneric *generic, + GdkDragContext *drag_context, + gpointer user_data) +{ + GdMainView *self = user_data; + + if (self->priv->button_press_item_path != NULL) + { + gboolean res; + GtkTreeIter iter; + GdkPixbuf *icon = NULL; + GtkTreePath *path; + + path = gtk_tree_path_new_from_string (self->priv->button_press_item_path); + res = gtk_tree_model_get_iter (self->priv->model, + &iter, path); + if (res) + gtk_tree_model_get (self->priv->model, &iter, + GD_MAIN_COLUMN_ICON, &icon, + -1); + + if (self->priv->selection_mode && + icon != NULL) + { + GList *selection; + GdkPixbuf *counter; + + selection = gd_main_view_get_selection (self); + + if (g_list_length (selection) > 1) + { + counter = gd_main_view_get_counter_icon (self, icon, g_list_length (selection)); + g_clear_object (&icon); + icon = counter; + } + + if (selection != NULL) + g_list_free_full (selection, (GDestroyNotify) gtk_tree_path_free); + } + + if (icon != NULL) + { + gtk_drag_set_icon_pixbuf (drag_context, icon, + MAIN_VIEW_DND_ICON_OFFSET, MAIN_VIEW_DND_ICON_OFFSET); + g_object_unref (icon); + } + + gtk_tree_path_free (path); + } +} + +static void +gd_main_view_apply_model (GdMainView *self) +{ + GdMainViewGeneric *generic = get_generic (self); + gd_main_view_generic_set_model (generic, self->priv->model); +} + +static gboolean +clear_selection_list_foreach (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer user_data) +{ + gtk_list_store_set (GTK_LIST_STORE (model), iter, + GD_MAIN_COLUMN_SELECTED, FALSE, + -1); + + return FALSE; +} + +static void +gd_main_view_apply_selection_mode (GdMainView *self) +{ + GdMainViewGeneric *generic = get_generic (self); + + gd_main_view_generic_set_selection_mode (generic, self->priv->selection_mode); + + if (!self->priv->selection_mode && + self->priv->model != NULL) + { + gtk_tree_model_foreach (self->priv->model, + clear_selection_list_foreach, + self); + g_signal_emit (self, signals[VIEW_SELECTION_CHANGED], 0); + } +} + +static void +gd_main_view_rebuild (GdMainView *self) +{ + GtkStyleContext *context; + + if (self->priv->current_view != NULL) + gtk_widget_destroy (self->priv->current_view); + + if (self->priv->current_type == GD_MAIN_VIEW_ICON) + self->priv->current_view = gd_main_icon_view_new (); + else + self->priv->current_view = gd_main_list_view_new (); + + context = gtk_widget_get_style_context (self->priv->current_view); + gtk_style_context_add_class (context, "documents-main-view"); + + gtk_container_add (GTK_CONTAINER (self), self->priv->current_view); + + g_signal_connect (self->priv->current_view, "button-press-event", + G_CALLBACK (on_button_press_event), self); + g_signal_connect (self->priv->current_view, "button-release-event", + G_CALLBACK (on_button_release_event), self); + g_signal_connect_after (self->priv->current_view, "drag-begin", + G_CALLBACK (on_drag_begin), self); + + gd_main_view_apply_selection_mode (self); + gd_main_view_apply_model (self); + + gtk_widget_show_all (GTK_WIDGET (self)); +} + +GdMainView * +gd_main_view_new (GdMainViewType type) +{ + return g_object_new (GD_TYPE_MAIN_VIEW, + "view-type", type, + NULL); +} + +void +gd_main_view_set_view_type (GdMainView *self, + GdMainViewType type) +{ + if (type != self->priv->current_type) + { + self->priv->current_type = type; + gd_main_view_rebuild (self); + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_VIEW_TYPE]); + } +} + +GdMainViewType +gd_main_view_get_view_type (GdMainView *self) +{ + return self->priv->current_type; +} + +void +gd_main_view_set_selection_mode (GdMainView *self, + gboolean selection_mode) +{ + if (selection_mode != self->priv->selection_mode) + { + self->priv->selection_mode = selection_mode; + gd_main_view_apply_selection_mode (self); + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SELECTION_MODE]); + } +} + +GdMainViewType +gd_main_view_get_selection_mode (GdMainView *self) +{ + return self->priv->selection_mode; +} + +/** + * gd_main_view_set_model: + * @self: + * @model: (allow-none): + * + */ +void +gd_main_view_set_model (GdMainView *self, + GtkTreeModel *model) +{ + if (model != self->priv->model) + { + g_clear_object (&self->priv->model); + + if (model) + self->priv->model = g_object_ref (model); + else + self->priv->model = NULL; + + gd_main_view_apply_model (self); + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODEL]); + } +} + +/** + * gd_main_view_get_model: + * @self: + * + * Returns: (transfer none): + */ +GtkTreeModel * +gd_main_view_get_model (GdMainView *self) +{ + return self->priv->model; +} + +/** + * gd_main_view_get_generic_view: + * @self: + * + * Returns: (transfer none): + */ +GtkWidget * +gd_main_view_get_generic_view (GdMainView *self) +{ + return self->priv->current_view; +} + +static gboolean +build_selection_list_foreach (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer user_data) +{ + GList **sel = user_data; + gboolean is_selected; + + gtk_tree_model_get (model, iter, + GD_MAIN_COLUMN_SELECTED, &is_selected, + -1); + + if (is_selected) + *sel = g_list_prepend (*sel, gtk_tree_path_copy (path)); + + return FALSE; +} + +/** + * gd_main_view_get_selection: + * @self: + * + * Returns: (element-type GtkTreePath) (transfer full): + */ +GList * +gd_main_view_get_selection (GdMainView *self) +{ + GList *retval = NULL; + + gtk_tree_model_foreach (self->priv->model, + build_selection_list_foreach, + &retval); + + return g_list_reverse (retval); +} diff --git a/lib/widgets/gd-main-view.h b/lib/widgets/gd-main-view.h new file mode 100644 index 000000000..b6906a10f --- /dev/null +++ b/lib/widgets/gd-main-view.h @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2011 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2 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 Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: Cosimo Cecchi <cosimoc@redhat.com> + * + */ + +#ifndef __GD_MAIN_VIEW_H__ +#define __GD_MAIN_VIEW_H__ + +#include <glib-object.h> + +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +#define GD_TYPE_MAIN_VIEW gd_main_view_get_type() + +#define GD_MAIN_VIEW(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + GD_TYPE_MAIN_VIEW, GdMainView)) + +#define GD_MAIN_VIEW_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), \ + GD_TYPE_MAIN_VIEW, GdMainViewIface)) + +#define GD_IS_MAIN_VIEW(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + GD_TYPE_MAIN_VIEW)) + +#define GD_IS_MAIN_VIEW_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + GD_TYPE_MAIN_VIEW)) + +#define GD_MAIN_VIEW_GET_IFACE(obj) \ + (G_TYPE_INSTANCE_GET_INTERFACE ((obj), \ + GD_TYPE_MAIN_VIEW, GdMainViewIface)) + +typedef struct _GdMainView GdMainView; +typedef struct _GdMainViewClass GdMainViewClass; +typedef struct _GdMainViewPrivate GdMainViewPrivate; + +typedef enum { + GD_MAIN_COLUMN_ID, + GD_MAIN_COLUMN_URI, + GD_MAIN_COLUMN_TITLE, + GD_MAIN_COLUMN_AUTHOR, + GD_MAIN_COLUMN_ICON, + GD_MAIN_COLUMN_MTIME, + GD_MAIN_COLUMN_SELECTED +} GdMainColumns; + +typedef enum { + GD_MAIN_VIEW_ICON, + GD_MAIN_VIEW_LIST +} GdMainViewType; + +struct _GdMainView { + GtkScrolledWindow parent; + + GdMainViewPrivate *priv; +}; + +struct _GdMainViewClass { + GtkScrolledWindowClass parent_class; +}; + +GType gd_main_view_get_type (void) G_GNUC_CONST; + +GdMainView * gd_main_view_new (GdMainViewType type); +void gd_main_view_set_view_type (GdMainView *self, + GdMainViewType type); +GdMainViewType gd_main_view_get_view_type (GdMainView *self); + +void gd_main_view_set_selection_mode (GdMainView *self, + gboolean selection_mode); +GdMainViewType gd_main_view_get_selection_mode (GdMainView *self); + +GList * gd_main_view_get_selection (GdMainView *self); + +GtkTreeModel * gd_main_view_get_model (GdMainView *self); +void gd_main_view_set_model (GdMainView *self, + GtkTreeModel *model); + +GtkWidget * gd_main_view_get_generic_view (GdMainView *self); + +G_END_DECLS + +#endif /* __GD_MAIN_VIEW_H__ */ diff --git a/lib/widgets/gd-toggle-pixbuf-renderer.c b/lib/widgets/gd-toggle-pixbuf-renderer.c new file mode 100644 index 000000000..3267938bf --- /dev/null +++ b/lib/widgets/gd-toggle-pixbuf-renderer.c @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2011 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2 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 Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: Cosimo Cecchi <cosimoc@redhat.com> + * + */ + +#include "gd-toggle-pixbuf-renderer.h" + +G_DEFINE_TYPE (GdTogglePixbufRenderer, gd_toggle_pixbuf_renderer, GTK_TYPE_CELL_RENDERER_PIXBUF); + +enum { + PROP_ACTIVE = 1, + PROP_TOGGLE_VISIBLE, + NUM_PROPERTIES +}; + +static GParamSpec *properties[NUM_PROPERTIES] = { NULL, }; + +struct _GdTogglePixbufRendererPrivate { + gboolean active; + gboolean toggle_visible; +}; + +static void +gd_toggle_pixbuf_renderer_render (GtkCellRenderer *cell, + cairo_t *cr, + GtkWidget *widget, + const GdkRectangle *background_area, + const GdkRectangle *cell_area, + GtkCellRendererState flags) +{ + gint icon_size = -1; + gint check_x, check_y, x_offset, xpad, ypad; + GtkStyleContext *context; + GdTogglePixbufRenderer *self = GD_TOGGLE_PIXBUF_RENDERER (cell); + GtkTextDirection direction; + + GTK_CELL_RENDERER_CLASS (gd_toggle_pixbuf_renderer_parent_class)->render + (cell, cr, widget, + background_area, cell_area, flags); + + if (!self->priv->toggle_visible) + return; + + gtk_cell_renderer_get_padding (cell, &xpad, &ypad); + direction = gtk_widget_get_direction (widget); + gtk_widget_style_get (widget, + "check-icon-size", &icon_size, + NULL); + + if (icon_size == -1) + icon_size = 40; + + if (direction == GTK_TEXT_DIR_RTL) + x_offset = xpad; + else + x_offset = cell_area->width - icon_size - xpad; + + check_x = cell_area->x + x_offset; + check_y = cell_area->y + cell_area->height - icon_size - ypad; + + context = gtk_widget_get_style_context (widget); + gtk_style_context_save (context); + gtk_style_context_add_class (context, GTK_STYLE_CLASS_CHECK); + + if (self->priv->active) + gtk_style_context_set_state (context, GTK_STATE_FLAG_ACTIVE); + + gtk_render_check (context, cr, + check_x, check_y, + icon_size, icon_size); + + gtk_style_context_restore (context); +} + +static void +gd_toggle_pixbuf_renderer_get_size (GtkCellRenderer *cell, + GtkWidget *widget, + const GdkRectangle *cell_area, + gint *x_offset, + gint *y_offset, + gint *width, + gint *height) +{ + gint icon_size; + + gtk_widget_style_get (widget, + "check-icon-size", &icon_size, + NULL); + + GTK_CELL_RENDERER_CLASS (gd_toggle_pixbuf_renderer_parent_class)->get_size + (cell, widget, cell_area, + x_offset, y_offset, width, height); + + *width += icon_size / 4; +} + +static void +gd_toggle_pixbuf_renderer_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GdTogglePixbufRenderer *self = GD_TOGGLE_PIXBUF_RENDERER (object); + + switch (property_id) + { + case PROP_ACTIVE: + g_value_set_boolean (value, self->priv->active); + break; + case PROP_TOGGLE_VISIBLE: + g_value_set_boolean (value, self->priv->toggle_visible); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gd_toggle_pixbuf_renderer_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GdTogglePixbufRenderer *self = GD_TOGGLE_PIXBUF_RENDERER (object); + + switch (property_id) + { + case PROP_ACTIVE: + self->priv->active = g_value_get_boolean (value); + break; + case PROP_TOGGLE_VISIBLE: + self->priv->toggle_visible = g_value_get_boolean (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gd_toggle_pixbuf_renderer_class_init (GdTogglePixbufRendererClass *klass) +{ + GObjectClass *oclass = G_OBJECT_CLASS (klass); + GtkCellRendererClass *crclass = GTK_CELL_RENDERER_CLASS (klass); + + crclass->render = gd_toggle_pixbuf_renderer_render; + crclass->get_size = gd_toggle_pixbuf_renderer_get_size; + oclass->get_property = gd_toggle_pixbuf_renderer_get_property; + oclass->set_property = gd_toggle_pixbuf_renderer_set_property; + + properties[PROP_ACTIVE] = + g_param_spec_boolean ("active", + "Active", + "Whether the cell renderer is active", + FALSE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); + properties[PROP_TOGGLE_VISIBLE] = + g_param_spec_boolean ("toggle-visible", + "Toggle visible", + "Whether to draw the toggle indicator", + FALSE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); + + g_type_class_add_private (klass, sizeof (GdTogglePixbufRendererPrivate)); + g_object_class_install_properties (oclass, NUM_PROPERTIES, properties); +} + +static void +gd_toggle_pixbuf_renderer_init (GdTogglePixbufRenderer *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GD_TYPE_TOGGLE_PIXBUF_RENDERER, + GdTogglePixbufRendererPrivate); +} + +GtkCellRenderer * +gd_toggle_pixbuf_renderer_new (void) +{ + return g_object_new (GD_TYPE_TOGGLE_PIXBUF_RENDERER, NULL); +} diff --git a/lib/widgets/gd-toggle-pixbuf-renderer.h b/lib/widgets/gd-toggle-pixbuf-renderer.h new file mode 100644 index 000000000..fe54cf462 --- /dev/null +++ b/lib/widgets/gd-toggle-pixbuf-renderer.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2011 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2 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 Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: Cosimo Cecchi <cosimoc@redhat.com> + * + */ + +#ifndef _GD_TOGGLE_PIXBUF_RENDERER_H +#define _GD_TOGGLE_PIXBUF_RENDERER_H + +#include <glib-object.h> + +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +#define GD_TYPE_TOGGLE_PIXBUF_RENDERER gd_toggle_pixbuf_renderer_get_type() + +#define GD_TOGGLE_PIXBUF_RENDERER(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + GD_TYPE_TOGGLE_PIXBUF_RENDERER, GdTogglePixbufRenderer)) + +#define GD_TOGGLE_PIXBUF_RENDERER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), \ + GD_TYPE_TOGGLE_PIXBUF_RENDERER, GdTogglePixbufRendererClass)) + +#define GD_IS_TOGGLE_PIXBUF_RENDERER(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + GD_TYPE_TOGGLE_PIXBUF_RENDERER)) + +#define GD_IS_TOGGLE_PIXBUF_RENDERER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + GD_TYPE_TOGGLE_PIXBUF_RENDERER)) + +#define GD_TOGGLE_PIXBUF_RENDERER_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + GD_TYPE_TOGGLE_PIXBUF_RENDERER, GdTogglePixbufRendererClass)) + +typedef struct _GdTogglePixbufRenderer GdTogglePixbufRenderer; +typedef struct _GdTogglePixbufRendererClass GdTogglePixbufRendererClass; +typedef struct _GdTogglePixbufRendererPrivate GdTogglePixbufRendererPrivate; + +struct _GdTogglePixbufRenderer +{ + GtkCellRendererPixbuf parent; + + GdTogglePixbufRendererPrivate *priv; +}; + +struct _GdTogglePixbufRendererClass +{ + GtkCellRendererPixbufClass parent_class; +}; + +GType gd_toggle_pixbuf_renderer_get_type (void) G_GNUC_CONST; + +GtkCellRenderer *gd_toggle_pixbuf_renderer_new (void); + +G_END_DECLS + +#endif /* _GD_TOGGLE_PIXBUF_RENDERER_H */ diff --git a/lib/widgets/gd-two-lines-renderer.c b/lib/widgets/gd-two-lines-renderer.c new file mode 100644 index 000000000..38d2c9063 --- /dev/null +++ b/lib/widgets/gd-two-lines-renderer.c @@ -0,0 +1,528 @@ +/* + * Copyright (c) 2011 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2 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 Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: Cosimo Cecchi <cosimoc@redhat.com> + * + */ + +#include "gd-two-lines-renderer.h" +#include <string.h> + +G_DEFINE_TYPE (GdTwoLinesRenderer, gd_two_lines_renderer, GTK_TYPE_CELL_RENDERER_TEXT) + +struct _GdTwoLinesRendererPrivate { + gchar *line_two; + gint text_lines; +}; + +enum { + PROP_TEXT_LINES = 1, + PROP_LINE_TWO, + NUM_PROPERTIES +}; + +static GParamSpec *properties[NUM_PROPERTIES] = { NULL, }; + +static PangoLayout * +create_layout_with_attrs (GtkWidget *widget, + GdTwoLinesRenderer *self, + PangoEllipsizeMode ellipsize) +{ + PangoLayout *layout; + gint wrap_width; + PangoWrapMode wrap_mode; + PangoAlignment alignment; + + g_object_get (self, + "wrap-width", &wrap_width, + "wrap-mode", &wrap_mode, + "alignment", &alignment, + NULL); + + layout = pango_layout_new (gtk_widget_get_pango_context (widget)); + + pango_layout_set_ellipsize (layout, ellipsize); + pango_layout_set_wrap (layout, wrap_mode); + pango_layout_set_alignment (layout, alignment); + + if (wrap_width != -1) + pango_layout_set_width (layout, wrap_width * PANGO_SCALE); + + return layout; +} + +static void +gd_two_lines_renderer_prepare_layouts (GdTwoLinesRenderer *self, + GtkWidget *widget, + PangoLayout **layout_one, + PangoLayout **layout_two) +{ + PangoLayout *line_one; + PangoLayout *line_two = NULL; + gchar *text = NULL; + + g_object_get (self, + "text", &text, + NULL); + + line_one = create_layout_with_attrs (widget, self, PANGO_ELLIPSIZE_MIDDLE); + + if (self->priv->line_two == NULL || + g_strcmp0 (self->priv->line_two, "") == 0) + { + pango_layout_set_height (line_one, - (self->priv->text_lines)); + + if (text != NULL) + pango_layout_set_text (line_one, text, -1); + } + else + { + line_two = create_layout_with_attrs (widget, self, PANGO_ELLIPSIZE_END); + + pango_layout_set_height (line_one, - (self->priv->text_lines - 1)); + pango_layout_set_height (line_two, -1); + pango_layout_set_text (line_two, self->priv->line_two, -1); + + if (text != NULL) + pango_layout_set_text (line_one, text, -1); + } + + if (layout_one) + *layout_one = line_one; + if (layout_two) + *layout_two = line_two; + + g_free (text); +} + +static void +gd_two_lines_renderer_get_size (GtkCellRenderer *cell, + GtkWidget *widget, + PangoLayout *layout_1, + PangoLayout *layout_2, + gint *width, + gint *height, + const GdkRectangle *cell_area, + gint *x_offset_1, + gint *x_offset_2, + gint *y_offset) +{ + GdTwoLinesRenderer *self = GD_TWO_LINES_RENDERER (cell); + gint xpad, ypad; + PangoLayout *layout_one, *layout_two; + GdkRectangle layout_one_rect, layout_two_rect, layout_union; + + if (layout_1 == NULL) + { + gd_two_lines_renderer_prepare_layouts (self, widget, &layout_one, &layout_two); + } + else + { + layout_one = g_object_ref (layout_1); + + if (layout_2 != NULL) + layout_two = g_object_ref (layout_2); + else + layout_two = NULL; + } + + gtk_cell_renderer_get_padding (cell, &xpad, &ypad); + pango_layout_get_pixel_extents (layout_one, NULL, (PangoRectangle *) &layout_one_rect); + + if (layout_two != NULL) + { + pango_layout_get_pixel_extents (layout_two, NULL, (PangoRectangle *) &layout_two_rect); + + layout_union.width = MAX(layout_one_rect.width, layout_two_rect.width); + layout_union.height = layout_one_rect.height + layout_two_rect.height; + } + else + { + layout_union = layout_one_rect; + } + + if (cell_area) + { + gfloat xalign, yalign; + + gtk_cell_renderer_get_alignment (cell, &xalign, &yalign); + + layout_union.width = MIN (layout_union.width, cell_area->width - 2 * xpad); + layout_union.height = MIN (layout_union.height, cell_area->height - 2 * ypad); + + if (x_offset_1) + { + if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL && + pango_layout_get_alignment (layout_one) != PANGO_ALIGN_CENTER) + *x_offset_1 = (1.0 - xalign) * (cell_area->width - (layout_one_rect.width + (2 * xpad))); + else + *x_offset_1 = xalign * (cell_area->width - (layout_one_rect.width + (2 * xpad))); + + *x_offset_1 = MAX (*x_offset_1, 0); + } + if (x_offset_2) + { + if (layout_two != NULL) + { + if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL && + pango_layout_get_alignment (layout_two) != PANGO_ALIGN_CENTER) + *x_offset_2 = (1.0 - xalign) * (cell_area->width - (layout_two_rect.width + (2 * xpad))); + else + *x_offset_2 = xalign * (cell_area->width - (layout_two_rect.width + (2 * xpad))); + + *x_offset_2 = MAX (*x_offset_2, 0); + } + else + { + *x_offset_2 = 0; + } + } + + if (y_offset) + { + *y_offset = yalign * (cell_area->height - (layout_union.height + (2 * ypad))); + *y_offset = MAX (*y_offset, 0); + } + } + else + { + if (x_offset_1) *x_offset_1 = 0; + if (x_offset_2) *x_offset_2 = 0; + if (y_offset) *y_offset = 0; + } + + g_clear_object (&layout_one); + g_clear_object (&layout_two); + + if (height) + *height = ypad * 2 + layout_union.height; + + if (width) + *width = xpad * 2 + layout_union.width; +} + +static void +gd_two_lines_renderer_render (GtkCellRenderer *cell, + cairo_t *cr, + GtkWidget *widget, + const GdkRectangle *background_area, + const GdkRectangle *cell_area, + GtkCellRendererState flags) +{ + GdTwoLinesRenderer *self = GD_TWO_LINES_RENDERER (cell); + GtkStyleContext *context; + gint line_one_height; + GtkStateFlags state; + GdkRectangle render_area = *cell_area; + gint xpad, ypad, x_offset_1, x_offset_2, y_offset; + PangoLayout *layout_one, *layout_two; + + context = gtk_widget_get_style_context (widget); + gd_two_lines_renderer_prepare_layouts (self, widget, &layout_one, &layout_two); + gd_two_lines_renderer_get_size (cell, widget, + layout_one, layout_two, + NULL, NULL, + cell_area, + &x_offset_1, &x_offset_2, &y_offset); + + gtk_cell_renderer_get_padding (cell, &xpad, &ypad); + + render_area.x += xpad + x_offset_1; + render_area.y += ypad; + + pango_layout_set_width (layout_one, + (cell_area->width - x_offset_1 - 2 * xpad) * PANGO_SCALE); + + gtk_render_layout (context, cr, + render_area.x, + render_area.y, + layout_one); + + if (layout_two != NULL) + { + pango_layout_get_pixel_size (layout_one, + NULL, &line_one_height); + + gtk_style_context_save (context); + gtk_style_context_add_class (context, "dim-label"); + + state = gtk_cell_renderer_get_state (cell, widget, flags); + gtk_style_context_set_state (context, state); + + render_area.x += - x_offset_1 + x_offset_2; + render_area.y += line_one_height; + pango_layout_set_width (layout_two, + (cell_area->width - x_offset_2 - 2 * xpad) * PANGO_SCALE); + + gtk_render_layout (context, cr, + render_area.x, + render_area.y, + layout_two); + + gtk_style_context_restore (context); + } + + g_clear_object (&layout_one); + g_clear_object (&layout_two); +} + +static void +gd_two_lines_renderer_get_preferred_width (GtkCellRenderer *cell, + GtkWidget *widget, + gint *minimum_size, + gint *natural_size) +{ + PangoContext *context; + PangoFontMetrics *metrics; + const PangoFontDescription *font_desc; + GtkStyleContext *style_context; + gint nat_width, min_width; + gint xpad, char_width, wrap_width, text_width; + gint width_chars, ellipsize_chars; + + g_object_get (cell, + "xpad", &xpad, + "width-chars", &width_chars, + "wrap-width", &wrap_width, + NULL); + style_context = gtk_widget_get_style_context (widget); + gtk_cell_renderer_get_padding (cell, &xpad, NULL); + + gd_two_lines_renderer_get_size (cell, widget, + NULL, NULL, + &text_width, NULL, + NULL, + NULL, NULL, NULL); + + /* Fetch the average size of a charachter */ + context = gtk_widget_get_pango_context (widget); + font_desc = gtk_style_context_get_font (style_context, 0); + metrics = pango_context_get_metrics (context, font_desc, + pango_context_get_language (context)); + + char_width = pango_font_metrics_get_approximate_char_width (metrics); + + pango_font_metrics_unref (metrics); + + /* enforce minimum width for ellipsized labels at ~3 chars */ + ellipsize_chars = 3; + + /* If no width-chars set, minimum for wrapping text will be the wrap-width */ + if (wrap_width > -1) + min_width = xpad * 2 + MIN (text_width, wrap_width); + else + min_width = xpad * 2 + + MIN (text_width, + (PANGO_PIXELS (char_width) * MAX (width_chars, ellipsize_chars))); + + if (width_chars > 0) + nat_width = xpad * 2 + + MAX ((PANGO_PIXELS (char_width) * width_chars), text_width); + else + nat_width = xpad * 2 + text_width; + + nat_width = MAX (nat_width, min_width); + + if (minimum_size) + *minimum_size = min_width; + + if (natural_size) + *natural_size = nat_width; +} + +static void +gd_two_lines_renderer_get_preferred_height_for_width (GtkCellRenderer *cell, + GtkWidget *widget, + gint width, + gint *minimum_size, + gint *natural_size) +{ + gint text_height; + gint ypad; + + gd_two_lines_renderer_get_size (cell, widget, + NULL, NULL, + NULL, &text_height, + NULL, + NULL, NULL, NULL); + + gtk_cell_renderer_get_padding (cell, NULL, &ypad); + text_height += 2 * ypad; + + if (minimum_size != NULL) + *minimum_size = text_height; + + if (natural_size != NULL) + *natural_size = text_height; +} + +static void +gd_two_lines_renderer_get_preferred_height (GtkCellRenderer *cell, + GtkWidget *widget, + gint *minimum_size, + gint *natural_size) +{ + gint min_width; + + gtk_cell_renderer_get_preferred_width (cell, widget, &min_width, NULL); + gd_two_lines_renderer_get_preferred_height_for_width (cell, widget, min_width, + minimum_size, natural_size); +} + +static void +gd_two_lines_renderer_get_aligned_area (GtkCellRenderer *cell, + GtkWidget *widget, + GtkCellRendererState flags, + const GdkRectangle *cell_area, + GdkRectangle *aligned_area) +{ + gint x_offset_1, x_offset_2, y_offset; + + gd_two_lines_renderer_get_size (cell, widget, + NULL, NULL, + &aligned_area->width, &aligned_area->height, + cell_area, + &x_offset_1, &x_offset_2, &y_offset); + + aligned_area->x = cell_area->x + MAX (x_offset_1, x_offset_2); + aligned_area->y = cell_area->y; +} + +static void +gd_two_lines_renderer_set_line_two (GdTwoLinesRenderer *self, + const gchar *line_two) +{ + if (g_strcmp0 (self->priv->line_two, line_two) == 0) + return; + + g_free (self->priv->line_two); + self->priv->line_two = g_strdup (line_two); + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_LINE_TWO]); +} + +static void +gd_two_lines_renderer_set_text_lines (GdTwoLinesRenderer *self, + gint text_lines) +{ + if (self->priv->text_lines == text_lines) + return; + + self->priv->text_lines = text_lines; + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_TEXT_LINES]); +} + +static void +gd_two_lines_renderer_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GdTwoLinesRenderer *self = GD_TWO_LINES_RENDERER (object); + + switch (property_id) + { + case PROP_TEXT_LINES: + gd_two_lines_renderer_set_text_lines (self, g_value_get_int (value)); + break; + case PROP_LINE_TWO: + gd_two_lines_renderer_set_line_two (self, g_value_get_string (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gd_two_lines_renderer_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GdTwoLinesRenderer *self = GD_TWO_LINES_RENDERER (object); + + switch (property_id) + { + case PROP_TEXT_LINES: + g_value_set_int (value, self->priv->text_lines); + break; + case PROP_LINE_TWO: + g_value_set_string (value, self->priv->line_two); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gd_two_lines_renderer_finalize (GObject *object) +{ + GdTwoLinesRenderer *self = GD_TWO_LINES_RENDERER (object); + + g_free (self->priv->line_two); + + G_OBJECT_CLASS (gd_two_lines_renderer_parent_class)->finalize (object); +} + +static void +gd_two_lines_renderer_class_init (GdTwoLinesRendererClass *klass) +{ + GtkCellRendererClass *cclass = GTK_CELL_RENDERER_CLASS (klass); + GObjectClass *oclass = G_OBJECT_CLASS (klass); + + cclass->render = gd_two_lines_renderer_render; + cclass->get_preferred_width = gd_two_lines_renderer_get_preferred_width; + cclass->get_preferred_height = gd_two_lines_renderer_get_preferred_height; + cclass->get_preferred_height_for_width = gd_two_lines_renderer_get_preferred_height_for_width; + cclass->get_aligned_area = gd_two_lines_renderer_get_aligned_area; + + oclass->set_property = gd_two_lines_renderer_set_property; + oclass->get_property = gd_two_lines_renderer_get_property; + oclass->finalize = gd_two_lines_renderer_finalize; + + properties[PROP_TEXT_LINES] = + g_param_spec_int ("text-lines", + "Lines of text", + "The total number of lines to be displayed", + 2, G_MAXINT, 2, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + properties[PROP_LINE_TWO] = + g_param_spec_string ("line-two", + "Second line", + "Second line", + NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + g_type_class_add_private (klass, sizeof (GdTwoLinesRendererPrivate)); + g_object_class_install_properties (oclass, NUM_PROPERTIES, properties); +} + +static void +gd_two_lines_renderer_init (GdTwoLinesRenderer *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GD_TYPE_TWO_LINES_RENDERER, + GdTwoLinesRendererPrivate); +} + +GtkCellRenderer * +gd_two_lines_renderer_new (void) +{ + return g_object_new (GD_TYPE_TWO_LINES_RENDERER, NULL); +} diff --git a/lib/widgets/gd-two-lines-renderer.h b/lib/widgets/gd-two-lines-renderer.h new file mode 100644 index 000000000..23bf70f08 --- /dev/null +++ b/lib/widgets/gd-two-lines-renderer.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2011 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2 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 Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: Cosimo Cecchi <cosimoc@redhat.com> + * + */ + +#ifndef _GD_TWO_LINES_RENDERER_H +#define _GD_TWO_LINES_RENDERER_H + +#include <glib-object.h> + +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +#define GD_TYPE_TWO_LINES_RENDERER gd_two_lines_renderer_get_type() + +#define GD_TWO_LINES_RENDERER(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + GD_TYPE_TWO_LINES_RENDERER, GdTwoLinesRenderer)) + +#define GD_TWO_LINES_RENDERER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), \ + GD_TYPE_TWO_LINES_RENDERER, GdTwoLinesRendererClass)) + +#define GD_IS_TWO_LINES_RENDERER(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + GD_TYPE_TWO_LINES_RENDERER)) + +#define GD_IS_TWO_LINES_RENDERER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + GD_TYPE_TWO_LINES_RENDERER)) + +#define GD_TWO_LINES_RENDERER_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + GD_TYPE_TWO_LINES_RENDERER, GdTwoLinesRendererClass)) + +typedef struct _GdTwoLinesRenderer GdTwoLinesRenderer; +typedef struct _GdTwoLinesRendererClass GdTwoLinesRendererClass; +typedef struct _GdTwoLinesRendererPrivate GdTwoLinesRendererPrivate; + +struct _GdTwoLinesRenderer +{ + GtkCellRendererText parent; + + GdTwoLinesRendererPrivate *priv; +}; + +struct _GdTwoLinesRendererClass +{ + GtkCellRendererTextClass parent_class; +}; + +GType gd_two_lines_renderer_get_type (void) G_GNUC_CONST; + +GtkCellRenderer *gd_two_lines_renderer_new (void); + +G_END_DECLS + +#endif /* _GD_TWO_LINES_RENDERER_H */ |