Files
gimp/app/widgets/gimpcontainertreeview.c
Michael Natterer 55e8528eff app: GimpContainerTreeView: keep a reference on tree_view->model
during the lifetime of the widget. Leaving that up to the GtkTreeView
was working for mysterious reasons even during destruction of the
widget. It's safer and cleaner this way.
2014-06-06 22:48:35 +02:00

1423 lines
53 KiB
C

/* GIMP - The GNU Image Manipulation Program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* gimpcontainertreeview.c
* Copyright (C) 2003-2010 Michael Natterer <mitch@gimp.org>
*
* 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 "config.h"
#include <string.h>
#include <gegl.h>
#include <gdk/gdkkeysyms.h>
#include <gtk/gtk.h>
#include "libgimpwidgets/gimpwidgets.h"
#include "widgets-types.h"
#include "core/gimpcontainer.h"
#include "core/gimpcontext.h"
#include "core/gimpmarshal.h"
#include "core/gimpviewable.h"
#include "gimpcellrendererviewable.h"
#include "gimpcontainertreestore.h"
#include "gimpcontainertreeview.h"
#include "gimpcontainertreeview-dnd.h"
#include "gimpcontainertreeview.h"
#include "gimpcontainertreeview-private.h"
#include "gimpcontainerview.h"
#include "gimpdnd.h"
#include "gimpviewrenderer.h"
#include "gimpwidgets-utils.h"
enum
{
EDIT_NAME,
LAST_SIGNAL
};
static void gimp_container_tree_view_view_iface_init (GimpContainerViewInterface *iface);
static void gimp_container_tree_view_constructed (GObject *object);
static void gimp_container_tree_view_finalize (GObject *object);
static void gimp_container_tree_view_unmap (GtkWidget *widget);
static gboolean gimp_container_tree_view_popup_menu (GtkWidget *widget);
static void gimp_container_tree_view_set_container (GimpContainerView *view,
GimpContainer *container);
static void gimp_container_tree_view_set_context (GimpContainerView *view,
GimpContext *context);
static void gimp_container_tree_view_set_selection_mode(GimpContainerView *view,
GtkSelectionMode mode);
static gpointer gimp_container_tree_view_insert_item (GimpContainerView *view,
GimpViewable *viewable,
gpointer parent_insert_data,
gint index);
static void gimp_container_tree_view_remove_item (GimpContainerView *view,
GimpViewable *viewable,
gpointer insert_data);
static void gimp_container_tree_view_reorder_item (GimpContainerView *view,
GimpViewable *viewable,
gint new_index,
gpointer insert_data);
static void gimp_container_tree_view_rename_item (GimpContainerView *view,
GimpViewable *viewable,
gpointer insert_data);
static gboolean gimp_container_tree_view_select_item (GimpContainerView *view,
GimpViewable *viewable,
gpointer insert_data);
static void gimp_container_tree_view_clear_items (GimpContainerView *view);
static void gimp_container_tree_view_set_view_size (GimpContainerView *view);
static void gimp_container_tree_view_real_edit_name (GimpContainerTreeView *tree_view);
static void gimp_container_tree_view_name_started (GtkCellRendererText *cell,
GtkCellEditable *editable,
const gchar *path_str,
GimpContainerTreeView *tree_view);
static void gimp_container_tree_view_name_canceled (GtkCellRendererText *cell,
GimpContainerTreeView *tree_view);
static void gimp_container_tree_view_selection_changed (GtkTreeSelection *sel,
GimpContainerTreeView *tree_view);
static gboolean gimp_container_tree_view_button_press (GtkWidget *widget,
GdkEventButton *bevent,
GimpContainerTreeView *tree_view);
static gboolean gimp_container_tree_view_tooltip (GtkWidget *widget,
gint x,
gint y,
gboolean keyboard_tip,
GtkTooltip *tooltip,
GimpContainerTreeView *tree_view);
static GimpViewable *gimp_container_tree_view_drag_viewable (GtkWidget *widget,
GimpContext **context,
gpointer data);
static GdkPixbuf *gimp_container_tree_view_drag_pixbuf (GtkWidget *widget,
gpointer data);
static gboolean gimp_container_tree_view_get_selected_single (GimpContainerTreeView *tree_view,
GtkTreeIter *iter);
static gint gimp_container_tree_view_get_selected (GimpContainerView *view,
GList **items);
static void gimp_container_tree_view_row_expanded (GtkTreeView *tree_view,
GtkTreeIter *iter,
GtkTreePath *path,
GimpContainerTreeView *view);
static void gimp_container_tree_view_expand_rows (GtkTreeModel *model,
GtkTreeView *view,
GtkTreeIter *parent);
G_DEFINE_TYPE_WITH_CODE (GimpContainerTreeView, gimp_container_tree_view,
GIMP_TYPE_CONTAINER_BOX,
G_IMPLEMENT_INTERFACE (GIMP_TYPE_CONTAINER_VIEW,
gimp_container_tree_view_view_iface_init))
#define parent_class gimp_container_tree_view_parent_class
static GimpContainerViewInterface *parent_view_iface = NULL;
static guint tree_view_signals[LAST_SIGNAL] = { 0 };
static void
gimp_container_tree_view_class_init (GimpContainerTreeViewClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
GtkBindingSet *binding_set;
object_class->constructed = gimp_container_tree_view_constructed;
object_class->finalize = gimp_container_tree_view_finalize;
widget_class->unmap = gimp_container_tree_view_unmap;
widget_class->popup_menu = gimp_container_tree_view_popup_menu;
klass->edit_name = gimp_container_tree_view_real_edit_name;
klass->drop_possible = gimp_container_tree_view_real_drop_possible;
klass->drop_viewable = gimp_container_tree_view_real_drop_viewable;
klass->drop_color = NULL;
klass->drop_uri_list = NULL;
klass->drop_svg = NULL;
klass->drop_component = NULL;
klass->drop_pixbuf = NULL;
tree_view_signals[EDIT_NAME] =
g_signal_new ("edit-name",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
G_STRUCT_OFFSET (GimpContainerTreeViewClass, edit_name),
NULL, NULL,
gimp_marshal_VOID__VOID,
G_TYPE_NONE, 0);
binding_set = gtk_binding_set_by_class (klass);
gtk_binding_entry_add_signal (binding_set, GDK_KEY_F2, 0,
"edit-name", 0);
g_type_class_add_private (klass, sizeof (GimpContainerTreeViewPriv));
}
static void
gimp_container_tree_view_view_iface_init (GimpContainerViewInterface *iface)
{
parent_view_iface = g_type_interface_peek_parent (iface);
if (! parent_view_iface)
parent_view_iface = g_type_default_interface_peek (GIMP_TYPE_CONTAINER_VIEW);
iface->set_container = gimp_container_tree_view_set_container;
iface->set_context = gimp_container_tree_view_set_context;
iface->set_selection_mode = gimp_container_tree_view_set_selection_mode;
iface->insert_item = gimp_container_tree_view_insert_item;
iface->remove_item = gimp_container_tree_view_remove_item;
iface->reorder_item = gimp_container_tree_view_reorder_item;
iface->rename_item = gimp_container_tree_view_rename_item;
iface->select_item = gimp_container_tree_view_select_item;
iface->clear_items = gimp_container_tree_view_clear_items;
iface->set_view_size = gimp_container_tree_view_set_view_size;
iface->get_selected = gimp_container_tree_view_get_selected;
iface->insert_data_free = (GDestroyNotify) gtk_tree_iter_free;
}
static void
gimp_container_tree_view_init (GimpContainerTreeView *tree_view)
{
GimpContainerBox *box = GIMP_CONTAINER_BOX (tree_view);
tree_view->priv = G_TYPE_INSTANCE_GET_PRIVATE (tree_view,
GIMP_TYPE_CONTAINER_TREE_VIEW,
GimpContainerTreeViewPriv);
gimp_container_tree_store_columns_init (tree_view->model_columns,
&tree_view->n_model_columns);
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (box->scrolled_win),
GTK_SHADOW_IN);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (box->scrolled_win),
GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
}
static void
gimp_container_tree_view_constructed (GObject *object)
{
GimpContainerTreeView *tree_view = GIMP_CONTAINER_TREE_VIEW (object);
GimpContainerView *view = GIMP_CONTAINER_VIEW (object);
GimpContainerBox *box = GIMP_CONTAINER_BOX (object);
G_OBJECT_CLASS (parent_class)->constructed (object);
tree_view->model = gimp_container_tree_store_new (view,
tree_view->n_model_columns,
tree_view->model_columns);
tree_view->view = g_object_new (GTK_TYPE_TREE_VIEW,
"model", tree_view->model,
"search-column", GIMP_CONTAINER_TREE_STORE_COLUMN_NAME,
"enable-search", FALSE,
"headers-visible", FALSE,
"has-tooltip", TRUE,
"show-expanders", GIMP_CONTAINER_VIEW_GET_INTERFACE (view)->model_is_tree,
NULL);
gtk_container_add (GTK_CONTAINER (box->scrolled_win),
GTK_WIDGET (tree_view->view));
gtk_widget_show (GTK_WIDGET (tree_view->view));
gimp_container_view_set_dnd_widget (view, GTK_WIDGET (tree_view->view));
tree_view->main_column = gtk_tree_view_column_new ();
gtk_tree_view_insert_column (tree_view->view, tree_view->main_column, 0);
gtk_tree_view_set_expander_column (tree_view->view, tree_view->main_column);
gtk_tree_view_set_enable_tree_lines (tree_view->view, TRUE);
tree_view->renderer_cell = gimp_cell_renderer_viewable_new ();
gtk_tree_view_column_pack_start (tree_view->main_column,
tree_view->renderer_cell,
FALSE);
gtk_tree_view_column_set_attributes (tree_view->main_column,
tree_view->renderer_cell,
"renderer", GIMP_CONTAINER_TREE_STORE_COLUMN_RENDERER,
NULL);
tree_view->priv->name_cell = gtk_cell_renderer_text_new ();
g_object_set (tree_view->priv->name_cell, "xalign", 0.0, NULL);
gtk_tree_view_column_pack_end (tree_view->main_column,
tree_view->priv->name_cell,
FALSE);
gtk_tree_view_column_set_attributes (tree_view->main_column,
tree_view->priv->name_cell,
"text", GIMP_CONTAINER_TREE_STORE_COLUMN_NAME,
"attributes", GIMP_CONTAINER_TREE_STORE_COLUMN_NAME_ATTRIBUTES,
"sensitive", GIMP_CONTAINER_TREE_STORE_COLUMN_NAME_SENSITIVE,
NULL);
g_signal_connect (tree_view->priv->name_cell, "editing-started",
G_CALLBACK (gimp_container_tree_view_name_started),
tree_view);
g_signal_connect (tree_view->priv->name_cell, "editing-canceled",
G_CALLBACK (gimp_container_tree_view_name_canceled),
tree_view);
gimp_container_tree_view_add_renderer_cell (tree_view,
tree_view->renderer_cell);
tree_view->priv->selection = gtk_tree_view_get_selection (tree_view->view);
g_signal_connect (tree_view->priv->selection, "changed",
G_CALLBACK (gimp_container_tree_view_selection_changed),
tree_view);
g_signal_connect (tree_view->view, "drag-failed",
G_CALLBACK (gimp_container_tree_view_drag_failed),
tree_view);
g_signal_connect (tree_view->view, "drag-leave",
G_CALLBACK (gimp_container_tree_view_drag_leave),
tree_view);
g_signal_connect (tree_view->view, "drag-motion",
G_CALLBACK (gimp_container_tree_view_drag_motion),
tree_view);
g_signal_connect (tree_view->view, "drag-drop",
G_CALLBACK (gimp_container_tree_view_drag_drop),
tree_view);
g_signal_connect (tree_view->view, "drag-data-received",
G_CALLBACK (gimp_container_tree_view_drag_data_received),
tree_view);
g_signal_connect (tree_view->view, "query-tooltip",
G_CALLBACK (gimp_container_tree_view_tooltip),
tree_view);
}
static void
gimp_container_tree_view_finalize (GObject *object)
{
GimpContainerTreeView *tree_view = GIMP_CONTAINER_TREE_VIEW (object);
if (tree_view->model)
{
g_object_unref (tree_view->model);
tree_view->model = NULL;
}
if (tree_view->priv->toggle_cells)
{
g_list_free (tree_view->priv->toggle_cells);
tree_view->priv->toggle_cells = NULL;
}
if (tree_view->priv->renderer_cells)
{
g_list_free (tree_view->priv->renderer_cells);
tree_view->priv->renderer_cells = NULL;
}
if (tree_view->priv->editable_cells)
{
g_list_free (tree_view->priv->editable_cells);
tree_view->priv->editable_cells = NULL;
}
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gimp_container_tree_view_unmap (GtkWidget *widget)
{
GimpContainerTreeView *tree_view = GIMP_CONTAINER_TREE_VIEW (widget);
if (tree_view->priv->scroll_timeout_id)
{
g_source_remove (tree_view->priv->scroll_timeout_id);
tree_view->priv->scroll_timeout_id = 0;
}
GTK_WIDGET_CLASS (parent_class)->unmap (widget);
}
static void
gimp_container_tree_view_menu_position (GtkMenu *menu,
gint *x,
gint *y,
gpointer data)
{
GimpContainerTreeView *tree_view = GIMP_CONTAINER_TREE_VIEW (data);
GtkWidget *widget = GTK_WIDGET (tree_view->view);
GtkAllocation allocation;
GtkTreeIter selected_iter;
gtk_widget_get_allocation (widget, &allocation);
gdk_window_get_origin (gtk_widget_get_window (widget), x, y);
if (! gtk_widget_get_has_window (widget))
{
*x += allocation.x;
*y += allocation.y;
}
if (gimp_container_tree_view_get_selected_single (tree_view, &selected_iter))
{
GtkTreePath *path;
GdkRectangle cell_rect;
gint center;
path = gtk_tree_model_get_path (tree_view->model, &selected_iter);
gtk_tree_view_get_cell_area (tree_view->view, path,
tree_view->main_column, &cell_rect);
gtk_tree_path_free (path);
center = cell_rect.y + cell_rect.height / 2;
center = CLAMP (center, 0, allocation.height);
*x += allocation.width / 2;
*y += center;
}
else
{
GtkStyle *style = gtk_widget_get_style (widget);
*x += style->xthickness;
*y += style->ythickness;
}
gimp_menu_position (menu, x, y);
}
static gboolean
gimp_container_tree_view_popup_menu (GtkWidget *widget)
{
return gimp_editor_popup_menu (GIMP_EDITOR (widget),
gimp_container_tree_view_menu_position,
widget);
}
GtkWidget *
gimp_container_tree_view_new (GimpContainer *container,
GimpContext *context,
gint view_size,
gint view_border_width)
{
GimpContainerTreeView *tree_view;
GimpContainerView *view;
g_return_val_if_fail (container == NULL || GIMP_IS_CONTAINER (container),
NULL);
g_return_val_if_fail (context == NULL || GIMP_IS_CONTEXT (context), NULL);
g_return_val_if_fail (view_size > 0 &&
view_size <= GIMP_VIEWABLE_MAX_PREVIEW_SIZE, NULL);
g_return_val_if_fail (view_border_width >= 0 &&
view_border_width <= GIMP_VIEW_MAX_BORDER_WIDTH,
NULL);
tree_view = g_object_new (GIMP_TYPE_CONTAINER_TREE_VIEW, NULL);
view = GIMP_CONTAINER_VIEW (tree_view);
gimp_container_view_set_view_size (view, view_size, view_border_width);
if (container)
gimp_container_view_set_container (view, container);
if (context)
gimp_container_view_set_context (view, context);
return GTK_WIDGET (tree_view);
}
GtkCellRenderer *
gimp_container_tree_view_get_name_cell (GimpContainerTreeView *tree_view)
{
g_return_val_if_fail (GIMP_IS_CONTAINER_TREE_VIEW (tree_view), NULL);
return tree_view->priv->name_cell;
}
void
gimp_container_tree_view_set_main_column_title (GimpContainerTreeView *tree_view,
const gchar *title)
{
g_return_if_fail (GIMP_IS_CONTAINER_TREE_VIEW (tree_view));
gtk_tree_view_column_set_title (tree_view->main_column,
title);
}
void
gimp_container_tree_view_add_toggle_cell (GimpContainerTreeView *tree_view,
GtkCellRenderer *cell)
{
g_return_if_fail (GIMP_IS_CONTAINER_TREE_VIEW (tree_view));
g_return_if_fail (GIMP_IS_CELL_RENDERER_TOGGLE (cell));
tree_view->priv->toggle_cells = g_list_prepend (tree_view->priv->toggle_cells,
cell);
}
void
gimp_container_tree_view_add_renderer_cell (GimpContainerTreeView *tree_view,
GtkCellRenderer *cell)
{
g_return_if_fail (GIMP_IS_CONTAINER_TREE_VIEW (tree_view));
g_return_if_fail (GIMP_IS_CELL_RENDERER_VIEWABLE (cell));
tree_view->priv->renderer_cells = g_list_prepend (tree_view->priv->renderer_cells,
cell);
gimp_container_tree_store_add_renderer_cell (GIMP_CONTAINER_TREE_STORE (tree_view->model),
cell);
}
void
gimp_container_tree_view_set_dnd_drop_to_empty (GimpContainerTreeView *tree_view,
gboolean dnd_drop_to_empty)
{
g_return_if_fail (GIMP_IS_CONTAINER_TREE_VIEW (tree_view));
tree_view->priv->dnd_drop_to_empty = dnd_drop_to_empty;
}
void
gimp_container_tree_view_connect_name_edited (GimpContainerTreeView *tree_view,
GCallback callback,
gpointer data)
{
g_return_if_fail (GIMP_IS_CONTAINER_TREE_VIEW (tree_view));
g_return_if_fail (callback != NULL);
g_object_set (tree_view->priv->name_cell,
"mode", GTK_CELL_RENDERER_MODE_EDITABLE,
"editable", TRUE,
NULL);
if (! g_list_find (tree_view->priv->editable_cells, tree_view->priv->name_cell))
tree_view->priv->editable_cells = g_list_prepend (tree_view->priv->editable_cells,
tree_view->priv->name_cell);
g_signal_connect (tree_view->priv->name_cell, "edited",
callback,
data);
}
/* GimpContainerView methods */
static void
gimp_container_tree_view_set_container (GimpContainerView *view,
GimpContainer *container)
{
GimpContainerTreeView *tree_view = GIMP_CONTAINER_TREE_VIEW (view);
GimpContainer *old_container;
old_container = gimp_container_view_get_container (view);
if (old_container)
{
tree_view->priv->dnd_renderer = NULL;
g_signal_handlers_disconnect_by_func (tree_view->view,
gimp_container_tree_view_row_expanded,
tree_view);
if (! container)
{
if (gimp_dnd_viewable_source_remove (GTK_WIDGET (tree_view->view),
gimp_container_get_children_type (old_container)))
{
if (GIMP_VIEWABLE_CLASS (g_type_class_peek (gimp_container_get_children_type (old_container)))->get_size)
gimp_dnd_pixbuf_source_remove (GTK_WIDGET (tree_view->view));
gtk_drag_source_unset (GTK_WIDGET (tree_view->view));
}
g_signal_handlers_disconnect_by_func (tree_view->view,
gimp_container_tree_view_button_press,
tree_view);
}
}
else if (container)
{
if (gimp_dnd_drag_source_set_by_type (GTK_WIDGET (tree_view->view),
GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,
gimp_container_get_children_type (container),
GDK_ACTION_COPY))
{
gimp_dnd_viewable_source_add (GTK_WIDGET (tree_view->view),
gimp_container_get_children_type (container),
gimp_container_tree_view_drag_viewable,
tree_view);
if (GIMP_VIEWABLE_CLASS (g_type_class_peek (gimp_container_get_children_type (container)))->get_size)
gimp_dnd_pixbuf_source_add (GTK_WIDGET (tree_view->view),
gimp_container_tree_view_drag_pixbuf,
tree_view);
}
/* connect button_press_event after DND so we can keep the list from
* selecting the item on button2
*/
g_signal_connect (tree_view->view, "button-press-event",
G_CALLBACK (gimp_container_tree_view_button_press),
tree_view);
}
parent_view_iface->set_container (view, container);
if (container)
{
gimp_container_tree_view_expand_rows (tree_view->model,
tree_view->view,
NULL);
g_signal_connect (tree_view->view,
"row-collapsed",
G_CALLBACK (gimp_container_tree_view_row_expanded),
tree_view);
g_signal_connect (tree_view->view,
"row-expanded",
G_CALLBACK (gimp_container_tree_view_row_expanded),
tree_view);
}
gtk_tree_view_columns_autosize (tree_view->view);
}
static void
gimp_container_tree_view_set_context (GimpContainerView *view,
GimpContext *context)
{
GimpContainerTreeView *tree_view = GIMP_CONTAINER_TREE_VIEW (view);
if (tree_view->model)
gimp_container_tree_store_set_context (GIMP_CONTAINER_TREE_STORE (tree_view->model),
context);
parent_view_iface->set_context (view, context);
}
static void
gimp_container_tree_view_set_selection_mode (GimpContainerView *view,
GtkSelectionMode mode)
{
GimpContainerTreeView *tree_view = GIMP_CONTAINER_TREE_VIEW (view);
gtk_tree_selection_set_mode (tree_view->priv->selection, mode);
parent_view_iface->set_selection_mode (view, mode);
}
static gpointer
gimp_container_tree_view_insert_item (GimpContainerView *view,
GimpViewable *viewable,
gpointer parent_insert_data,
gint index)
{
GimpContainerTreeView *tree_view = GIMP_CONTAINER_TREE_VIEW (view);
GtkTreeIter *iter;
iter = gimp_container_tree_store_insert_item (GIMP_CONTAINER_TREE_STORE (tree_view->model),
viewable,
parent_insert_data,
index);
return iter;
}
static void
gimp_container_tree_view_remove_item (GimpContainerView *view,
GimpViewable *viewable,
gpointer insert_data)
{
GimpContainerTreeView *tree_view = GIMP_CONTAINER_TREE_VIEW (view);
GtkTreeIter *iter = (GtkTreeIter *) insert_data;
gimp_container_tree_store_remove_item (GIMP_CONTAINER_TREE_STORE (tree_view->model),
viewable,
iter);
if (iter)
gtk_tree_view_columns_autosize (tree_view->view);
}
static void
gimp_container_tree_view_reorder_item (GimpContainerView *view,
GimpViewable *viewable,
gint new_index,
gpointer insert_data)
{
GimpContainerTreeView *tree_view = GIMP_CONTAINER_TREE_VIEW (view);
GtkTreeIter *iter = (GtkTreeIter *) insert_data;
gboolean selected = FALSE;
if (iter)
{
GtkTreeIter selected_iter;
selected = gimp_container_tree_view_get_selected_single (tree_view,
&selected_iter);
if (selected)
{
GimpViewRenderer *renderer;
gtk_tree_model_get (tree_view->model, &selected_iter,
GIMP_CONTAINER_TREE_STORE_COLUMN_RENDERER, &renderer,
-1);
if (renderer->viewable != viewable)
selected = FALSE;
g_object_unref (renderer);
}
}
gimp_container_tree_store_reorder_item (GIMP_CONTAINER_TREE_STORE (tree_view->model),
viewable,
new_index,
iter);
if (selected)
gimp_container_view_select_item (view, viewable);
}
static void
gimp_container_tree_view_rename_item (GimpContainerView *view,
GimpViewable *viewable,
gpointer insert_data)
{
GimpContainerTreeView *tree_view = GIMP_CONTAINER_TREE_VIEW (view);
GtkTreeIter *iter = (GtkTreeIter *) insert_data;
if (gimp_container_tree_store_rename_item (GIMP_CONTAINER_TREE_STORE (tree_view->model),
viewable,
iter))
{
gtk_tree_view_columns_autosize (tree_view->view);
}
}
static gboolean
gimp_container_tree_view_select_item (GimpContainerView *view,
GimpViewable *viewable,
gpointer insert_data)
{
GimpContainerTreeView *tree_view = GIMP_CONTAINER_TREE_VIEW (view);
if (viewable && insert_data)
{
GtkTreePath *path;
GtkTreePath *parent_path;
GtkTreeIter *iter = (GtkTreeIter *) insert_data;
path = gtk_tree_model_get_path (tree_view->model, iter);
parent_path = gtk_tree_path_copy (path);
if (gtk_tree_path_up (parent_path))
gtk_tree_view_expand_to_path (tree_view->view, parent_path);
gtk_tree_path_free (parent_path);
g_signal_handlers_block_by_func (tree_view->priv->selection,
gimp_container_tree_view_selection_changed,
tree_view);
gtk_tree_view_set_cursor (tree_view->view, path, NULL, FALSE);
g_signal_handlers_unblock_by_func (tree_view->priv->selection,
gimp_container_tree_view_selection_changed,
tree_view);
gtk_tree_view_scroll_to_cell (tree_view->view, path,
NULL, FALSE, 0.0, 0.0);
gtk_tree_path_free (path);
}
else if (insert_data == NULL)
{
/* viewable == NULL && insert_data != NULL means multiple selection.
* viewable == NULL && insert_data == NULL means no selection. */
gtk_tree_selection_unselect_all (tree_view->priv->selection);
}
return TRUE;
}
static void
gimp_container_tree_view_clear_items (GimpContainerView *view)
{
GimpContainerTreeView *tree_view = GIMP_CONTAINER_TREE_VIEW (view);
gimp_container_tree_store_clear_items (GIMP_CONTAINER_TREE_STORE (tree_view->model));
parent_view_iface->clear_items (view);
}
static void
gimp_container_tree_view_set_view_size (GimpContainerView *view)
{
GimpContainerTreeView *tree_view = GIMP_CONTAINER_TREE_VIEW (view);
GtkWidget *tree_widget;
GList *list;
gint view_size;
gint border_width;
view_size = gimp_container_view_get_view_size (view, &border_width);
if (tree_view->model)
gimp_container_tree_store_set_view_size (GIMP_CONTAINER_TREE_STORE (tree_view->model));
tree_widget = GTK_WIDGET (tree_view->view);
if (! tree_widget)
return;
for (list = tree_view->priv->toggle_cells; list; list = g_list_next (list))
{
gchar *icon_name;
GtkIconSize icon_size;
g_object_get (list->data, "icon-name", &icon_name, NULL);
if (icon_name)
{
GtkStyle *style = gtk_widget_get_style (tree_widget);
icon_size = gimp_get_icon_size (tree_widget,
icon_name,
GTK_ICON_SIZE_BUTTON,
view_size -
2 * style->xthickness,
view_size -
2 * style->ythickness);
g_object_set (list->data, "stock-size", icon_size, NULL);
g_free (icon_name);
}
}
gtk_tree_view_columns_autosize (tree_view->view);
}
/* GimpContainerTreeView methods */
static void
gimp_container_tree_view_real_edit_name (GimpContainerTreeView *tree_view)
{
GtkTreeIter selected_iter;
if (g_list_find (tree_view->priv->editable_cells,
tree_view->priv->name_cell) &&
gimp_container_tree_view_get_selected_single (tree_view,
&selected_iter))
{
GtkTreePath *path;
path = gtk_tree_model_get_path (tree_view->model, &selected_iter);
gtk_tree_view_set_cursor_on_cell (tree_view->view, path,
tree_view->main_column,
tree_view->priv->name_cell,
TRUE);
gtk_tree_path_free (path);
}
}
/* callbacks */
static void
gimp_container_tree_view_name_started (GtkCellRendererText *cell,
GtkCellEditable *editable,
const gchar *path_str,
GimpContainerTreeView *tree_view)
{
GtkTreePath *path;
GtkTreeIter iter;
path = gtk_tree_path_new_from_string (path_str);
if (gtk_tree_model_get_iter (tree_view->model, &iter, path))
{
GimpViewRenderer *renderer;
const gchar *real_name;
gtk_tree_model_get (tree_view->model, &iter,
GIMP_CONTAINER_TREE_STORE_COLUMN_RENDERER, &renderer,
-1);
real_name = gimp_object_get_name (renderer->viewable);
g_object_unref (renderer);
gtk_entry_set_text (GTK_ENTRY (editable), real_name);
}
gtk_tree_path_free (path);
}
static void
gimp_container_tree_view_name_canceled (GtkCellRendererText *cell,
GimpContainerTreeView *tree_view)
{
GtkTreeIter iter;
if (gimp_container_tree_view_get_selected_single (tree_view, &iter))
{
GimpViewRenderer *renderer;
gchar *name;
gtk_tree_model_get (tree_view->model, &iter,
GIMP_CONTAINER_TREE_STORE_COLUMN_RENDERER, &renderer,
-1);
name = gimp_viewable_get_description (renderer->viewable, NULL);
gtk_tree_store_set (GTK_TREE_STORE (tree_view->model), &iter,
GIMP_CONTAINER_TREE_STORE_COLUMN_NAME, name,
-1);
g_free (name);
g_object_unref (renderer);
}
}
static void
gimp_container_tree_view_selection_changed (GtkTreeSelection *selection,
GimpContainerTreeView *tree_view)
{
GimpContainerView *view = GIMP_CONTAINER_VIEW (tree_view);
GList *items;
gimp_container_tree_view_get_selected (view, &items);
gimp_container_view_multi_selected (view, items);
g_list_free (items);
}
static GtkCellRenderer *
gimp_container_tree_view_find_click_cell (GtkWidget *widget,
GList *cells,
GtkTreeViewColumn *column,
GdkRectangle *column_area,
gint tree_x,
gint tree_y)
{
GList *list;
gboolean rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
for (list = cells; list; list = g_list_next (list))
{
GtkCellRenderer *renderer = list->data;
gint start;
gint width;
if (gtk_cell_renderer_get_visible (renderer) &&
gtk_tree_view_column_cell_get_position (column, renderer,
&start, &width))
{
gint xpad, ypad;
gint x;
gtk_cell_renderer_get_padding (renderer, &xpad, &ypad);
if (rtl)
x = column_area->x + column_area->width - start - width;
else
x = start + column_area->x;
if (tree_x >= x + xpad &&
tree_x < x + width - xpad)
{
return renderer;
}
}
}
return NULL;
}
static gboolean
gimp_container_tree_view_button_press (GtkWidget *widget,
GdkEventButton *bevent,
GimpContainerTreeView *tree_view)
{
GimpContainerView *container_view = GIMP_CONTAINER_VIEW (tree_view);
GtkTreeViewColumn *column;
GtkTreePath *path;
tree_view->priv->dnd_renderer = NULL;
if (! gtk_widget_has_focus (widget))
gtk_widget_grab_focus (widget);
if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget),
bevent->x, bevent->y,
&path, &column, NULL, NULL))
{
GimpViewRenderer *renderer;
GimpCellRendererToggle *toggled_cell = NULL;
GimpCellRendererViewable *clicked_cell = NULL;
GtkCellRenderer *edit_cell = NULL;
GdkRectangle column_area;
GtkTreeIter iter;
gboolean handled = TRUE;
gboolean multisel_mode;
multisel_mode = (gtk_tree_selection_get_mode (tree_view->priv->selection)
== GTK_SELECTION_MULTIPLE);
gtk_tree_model_get_iter (tree_view->model, &iter, path);
gtk_tree_model_get (tree_view->model, &iter,
GIMP_CONTAINER_TREE_STORE_COLUMN_RENDERER, &renderer,
-1);
tree_view->priv->dnd_renderer = renderer;
gtk_tree_view_get_cell_area (tree_view->view, path,
column, &column_area);
gtk_tree_view_column_cell_set_cell_data (column,
tree_view->model,
&iter,
FALSE, FALSE);
if (bevent->button == 1 &&
gtk_tree_model_iter_has_child (tree_view->model, &iter) &&
column == gtk_tree_view_get_expander_column (tree_view->view))
{
GList *cells;
cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
if (! gimp_container_tree_view_find_click_cell (widget,
cells,
column, &column_area,
bevent->x, bevent->y))
{
/* we didn't click on any cell, but we clicked on empty
* space in the expander column of a row that has
* children; let GtkTreeView process the button press
* to maybe handle a click on an expander.
*/
g_list_free (cells);
gtk_tree_path_free (path);
g_object_unref (renderer);
return FALSE;
}
g_list_free (cells);
}
toggled_cell = (GimpCellRendererToggle *)
gimp_container_tree_view_find_click_cell (widget,
tree_view->priv->toggle_cells,
column, &column_area,
bevent->x, bevent->y);
if (! toggled_cell)
clicked_cell = (GimpCellRendererViewable *)
gimp_container_tree_view_find_click_cell (widget,
tree_view->priv->renderer_cells,
column, &column_area,
bevent->x, bevent->y);
if (! toggled_cell && ! clicked_cell)
edit_cell =
gimp_container_tree_view_find_click_cell (widget,
tree_view->priv->editable_cells,
column, &column_area,
bevent->x, bevent->y);
g_object_ref (tree_view);
if (gdk_event_triggers_context_menu ((GdkEvent *) bevent))
{
if (gimp_container_view_item_selected (container_view,
renderer->viewable))
{
if (gimp_container_view_get_container (container_view))
gimp_container_view_item_context (container_view,
renderer->viewable);
}
}
else if (bevent->button == 1)
{
if (bevent->type == GDK_BUTTON_PRESS)
{
/* don't select item if a toggle was clicked */
if (! toggled_cell)
{
gchar *path_str = gtk_tree_path_to_string (path);
handled = FALSE;
if (clicked_cell)
handled =
gimp_cell_renderer_viewable_pre_clicked (clicked_cell,
path_str,
bevent->state);
if (! handled)
{
if (multisel_mode)
{
/* let parent do the work */
}
else
{
handled =
gimp_container_view_item_selected (container_view,
renderer->viewable);
}
}
g_free (path_str);
}
/* a callback invoked by selecting the item may have
* destroyed us, so check if the container is still there
*/
if (gimp_container_view_get_container (container_view))
{
/* another row may have been set by selecting */
gtk_tree_view_column_cell_set_cell_data (column,
tree_view->model,
&iter,
FALSE, FALSE);
if (toggled_cell || clicked_cell)
{
gchar *path_str = gtk_tree_path_to_string (path);
if (toggled_cell)
{
gimp_cell_renderer_toggle_clicked (toggled_cell,
path_str,
bevent->state);
}
else if (clicked_cell)
{
gimp_cell_renderer_viewable_clicked (clicked_cell,
path_str,
bevent->state);
}
g_free (path_str);
}
}
}
else if (bevent->type == GDK_2BUTTON_PRESS)
{
gboolean success = TRUE;
/* don't select item if a toggle was clicked */
if (! toggled_cell)
success = gimp_container_view_item_selected (container_view,
renderer->viewable);
if (success)
{
if (edit_cell)
{
gtk_tree_view_set_cursor_on_cell (tree_view->view, path,
column, edit_cell, TRUE);
}
else if (! toggled_cell &&
! (bevent->state & gimp_get_all_modifiers_mask ()))
{
/* Only activate if we're not in a toggled cell
* and no modifier keys are pressed
*/
gimp_container_view_item_activated (container_view,
renderer->viewable);
}
}
}
}
else if (bevent->button == 2)
{
if (bevent->type == GDK_BUTTON_PRESS)
{
if (clicked_cell)
{
gchar *path_str = gtk_tree_path_to_string (path);
gimp_cell_renderer_viewable_clicked (clicked_cell,
path_str,
bevent->state);
g_free (path_str);
}
}
}
g_object_unref (tree_view);
gtk_tree_path_free (path);
g_object_unref (renderer);
return multisel_mode ? handled : TRUE;
}
else
{
if (gdk_event_triggers_context_menu ((GdkEvent *) bevent))
{
gimp_editor_popup_menu (GIMP_EDITOR (tree_view), NULL, NULL);
}
return TRUE;
}
}
static gboolean
gimp_container_tree_view_tooltip (GtkWidget *widget,
gint x,
gint y,
gboolean keyboard_tip,
GtkTooltip *tooltip,
GimpContainerTreeView *tree_view)
{
GimpViewRenderer *renderer;
GtkTreeIter iter;
GtkTreePath *path;
gboolean show_tip = FALSE;
if (! gtk_tree_view_get_tooltip_context (GTK_TREE_VIEW (widget), &x, &y,
keyboard_tip,
NULL, &path, &iter))
return FALSE;
gtk_tree_model_get (tree_view->model, &iter,
GIMP_CONTAINER_TREE_STORE_COLUMN_RENDERER, &renderer,
-1);
if (renderer)
{
gchar *desc;
gchar *tip;
desc = gimp_viewable_get_description (renderer->viewable, &tip);
if (tip)
{
gtk_tooltip_set_text (tooltip, tip);
gtk_tree_view_set_tooltip_row (GTK_TREE_VIEW (widget), tooltip, path);
show_tip = TRUE;
g_free (tip);
}
g_free (desc);
g_object_unref (renderer);
}
gtk_tree_path_free (path);
return show_tip;
}
static GimpViewable *
gimp_container_tree_view_drag_viewable (GtkWidget *widget,
GimpContext **context,
gpointer data)
{
GimpContainerTreeView *tree_view = GIMP_CONTAINER_TREE_VIEW (data);
if (context)
*context = gimp_container_view_get_context (GIMP_CONTAINER_VIEW (data));
if (tree_view->priv->dnd_renderer)
return tree_view->priv->dnd_renderer->viewable;
return NULL;
}
static GdkPixbuf *
gimp_container_tree_view_drag_pixbuf (GtkWidget *widget,
gpointer data)
{
GimpContainerTreeView *tree_view = GIMP_CONTAINER_TREE_VIEW (data);
GimpViewRenderer *renderer = tree_view->priv->dnd_renderer;
gint width;
gint height;
if (renderer && gimp_viewable_get_size (renderer->viewable, &width, &height))
return gimp_viewable_get_new_pixbuf (renderer->viewable,
renderer->context,
width, height);
return NULL;
}
static gboolean
gimp_container_tree_view_get_selected_single (GimpContainerTreeView *tree_view,
GtkTreeIter *iter)
{
GtkTreeSelection *selection;
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view->view));
if (gtk_tree_selection_count_selected_rows (selection) == 1)
{
GList *selected_rows;
selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL);
gtk_tree_model_get_iter (GTK_TREE_MODEL (tree_view->model), iter,
(GtkTreePath *) selected_rows->data);
g_list_free_full (selected_rows, (GDestroyNotify) gtk_tree_path_free);
return TRUE;
}
else
{
return FALSE;
}
}
static gint
gimp_container_tree_view_get_selected (GimpContainerView *view,
GList **items)
{
GimpContainerTreeView *tree_view = GIMP_CONTAINER_TREE_VIEW (view);
GtkTreeSelection *selection;
gint selected_count;
GList *selected_rows;
GList *current_row;
GtkTreeIter iter;
GimpViewRenderer *renderer;
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view->view));
selected_count = gtk_tree_selection_count_selected_rows (selection);
if (items == NULL)
{
/* just provide selected count */
return selected_count;
}
selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL);
*items = NULL;
for (current_row = selected_rows;
current_row;
current_row = g_list_next (current_row))
{
gtk_tree_model_get_iter (GTK_TREE_MODEL (tree_view->model), &iter,
(GtkTreePath *) current_row->data);
gtk_tree_model_get (tree_view->model, &iter,
GIMP_CONTAINER_TREE_STORE_COLUMN_RENDERER, &renderer,
-1);
if (renderer->viewable)
*items = g_list_prepend (*items, renderer->viewable);
g_object_unref (renderer);
}
g_list_free_full (selected_rows, (GDestroyNotify) gtk_tree_path_free);
*items = g_list_reverse (*items);
return selected_count;
}
static void
gimp_container_tree_view_row_expanded (GtkTreeView *tree_view,
GtkTreeIter *iter,
GtkTreePath *path,
GimpContainerTreeView *view)
{
GimpViewRenderer *renderer;
gtk_tree_model_get (view->model, iter,
GIMP_CONTAINER_TREE_STORE_COLUMN_RENDERER, &renderer,
-1);
if (renderer)
{
gboolean expanded = gtk_tree_view_row_expanded (tree_view, path);
gimp_viewable_set_expanded (renderer->viewable,
expanded);
if (expanded)
{
g_signal_handlers_block_by_func (tree_view,
gimp_container_tree_view_row_expanded,
view);
gimp_container_tree_view_expand_rows (view->model, tree_view, iter);
g_signal_handlers_unblock_by_func (tree_view,
gimp_container_tree_view_row_expanded,
view);
}
g_object_unref (renderer);
}
}
static void
gimp_container_tree_view_expand_rows (GtkTreeModel *model,
GtkTreeView *view,
GtkTreeIter *parent)
{
GtkTreeIter iter;
if (gtk_tree_model_iter_children (model, &iter, parent))
do
if (gtk_tree_model_iter_has_child (model, &iter))
{
GimpViewRenderer *renderer;
gtk_tree_model_get (model, &iter,
GIMP_CONTAINER_TREE_STORE_COLUMN_RENDERER, &renderer,
-1);
if (renderer)
{
GtkTreePath *path = gtk_tree_model_get_path (model, &iter);
if (gimp_viewable_get_expanded (renderer->viewable))
gtk_tree_view_expand_row (view, path, FALSE);
else
gtk_tree_view_collapse_row (view, path);
gtk_tree_path_free (path);
g_object_unref (renderer);
}
gimp_container_tree_view_expand_rows (model, view, &iter);
}
while (gtk_tree_model_iter_next (model, &iter));
}