
2003-09-30 Michael Natterer <mitch@gimp.org> * app/widgets/gimpcontainergridview.c * app/widgets/gimpcontainertreeview.c: implement GtkWidget::popup_menu() and pop up the menu from the selected item. If there is no selected item, return FALSE to makeGimpDockable pop up the menu from its menu button.
1019 lines
34 KiB
C
1019 lines
34 KiB
C
/* The GIMP -- an image manipulation program
|
|
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
|
*
|
|
* gimpcontainertreeview.c
|
|
* Copyright (C) 2003 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 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 General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <string.h>
|
|
|
|
#include <gtk/gtk.h>
|
|
|
|
#include "widgets-types.h"
|
|
|
|
#include "base/temp-buf.h"
|
|
|
|
#include "core/gimpcontainer.h"
|
|
#include "core/gimpcontext.h"
|
|
#include "core/gimpviewable.h"
|
|
|
|
#include "gimpcellrenderertoggle.h"
|
|
#include "gimpcellrendererviewable.h"
|
|
#include "gimpcontainertreeview.h"
|
|
#include "gimpcontainertreeview-dnd.h"
|
|
#include "gimpdnd.h"
|
|
#include "gimpitemfactory.h"
|
|
#include "gimppreviewrenderer.h"
|
|
#include "gimpwidgets-utils.h"
|
|
|
|
|
|
enum
|
|
{
|
|
COLUMN_RENDERER,
|
|
COLUMN_NAME,
|
|
COLUMN_NAME_ATTRIBUTES,
|
|
NUM_COLUMNS
|
|
};
|
|
|
|
|
|
static void gimp_container_tree_view_class_init (GimpContainerTreeViewClass *klass);
|
|
static void gimp_container_tree_view_init (GimpContainerTreeView *view);
|
|
|
|
static GObject *gimp_container_tree_view_constructor (GType type,
|
|
guint n_params,
|
|
GObjectConstructParam *params);
|
|
static gboolean gimp_container_tree_view_popup_menu (GtkWidget *widget);
|
|
static void gimp_container_tree_view_set_container (GimpContainerView *view,
|
|
GimpContainer *container);
|
|
|
|
static gpointer gimp_container_tree_view_insert_item (GimpContainerView *view,
|
|
GimpViewable *viewable,
|
|
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 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_preview_size (GimpContainerView *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 void gimp_container_tree_view_renderer_update (GimpPreviewRenderer *renderer,
|
|
GimpContainerTreeView *tree_view);
|
|
static void gimp_container_tree_view_name_changed (GimpObject *object,
|
|
GimpContainerTreeView *tree_view);
|
|
|
|
static GimpViewable * gimp_container_tree_view_drag_viewable (GtkWidget *widget,
|
|
gpointer data);
|
|
|
|
|
|
static GimpContainerViewClass *parent_class = NULL;
|
|
|
|
|
|
GType
|
|
gimp_container_tree_view_get_type (void)
|
|
{
|
|
static GType view_type = 0;
|
|
|
|
if (! view_type)
|
|
{
|
|
static const GTypeInfo view_info =
|
|
{
|
|
sizeof (GimpContainerTreeViewClass),
|
|
NULL, /* base_init */
|
|
NULL, /* base_finalize */
|
|
(GClassInitFunc) gimp_container_tree_view_class_init,
|
|
NULL, /* class_finalize */
|
|
NULL, /* class_data */
|
|
sizeof (GimpContainerTreeView),
|
|
0, /* n_preallocs */
|
|
(GInstanceInitFunc) gimp_container_tree_view_init,
|
|
};
|
|
|
|
view_type = g_type_register_static (GIMP_TYPE_CONTAINER_VIEW,
|
|
"GimpContainerTreeView",
|
|
&view_info, 0);
|
|
}
|
|
|
|
return view_type;
|
|
}
|
|
|
|
static void
|
|
gimp_container_tree_view_class_init (GimpContainerTreeViewClass *klass)
|
|
{
|
|
GObjectClass *object_class;
|
|
GtkWidgetClass *widget_class;
|
|
GimpContainerViewClass *container_view_class;
|
|
|
|
object_class = G_OBJECT_CLASS (klass);
|
|
widget_class = GTK_WIDGET_CLASS (klass);
|
|
container_view_class = GIMP_CONTAINER_VIEW_CLASS (klass);
|
|
|
|
parent_class = g_type_class_peek_parent (klass);
|
|
|
|
object_class->constructor = gimp_container_tree_view_constructor;
|
|
|
|
widget_class->popup_menu = gimp_container_tree_view_popup_menu;
|
|
|
|
container_view_class->set_container = gimp_container_tree_view_set_container;
|
|
container_view_class->insert_item = gimp_container_tree_view_insert_item;
|
|
container_view_class->remove_item = gimp_container_tree_view_remove_item;
|
|
container_view_class->reorder_item = gimp_container_tree_view_reorder_item;
|
|
container_view_class->select_item = gimp_container_tree_view_select_item;
|
|
container_view_class->clear_items = gimp_container_tree_view_clear_items;
|
|
container_view_class->set_preview_size = gimp_container_tree_view_set_preview_size;
|
|
|
|
container_view_class->insert_data_free = (GDestroyNotify) g_free;
|
|
|
|
klass->drop_possible = gimp_container_tree_view_real_drop_possible;
|
|
klass->drop = gimp_container_tree_view_real_drop;
|
|
}
|
|
|
|
static void
|
|
gimp_container_tree_view_init (GimpContainerTreeView *tree_view)
|
|
{
|
|
GimpContainerView *view;
|
|
|
|
view = GIMP_CONTAINER_VIEW (tree_view);
|
|
|
|
tree_view->n_model_columns = NUM_COLUMNS;
|
|
|
|
tree_view->model_columns[COLUMN_RENDERER] = GIMP_TYPE_PREVIEW_RENDERER;
|
|
tree_view->model_columns[COLUMN_NAME] = G_TYPE_STRING;
|
|
tree_view->model_columns[COLUMN_NAME_ATTRIBUTES] = PANGO_TYPE_ATTR_LIST;
|
|
|
|
tree_view->model_column_renderer = COLUMN_RENDERER;
|
|
tree_view->model_column_name = COLUMN_NAME;
|
|
tree_view->model_column_name_attributes = COLUMN_NAME_ATTRIBUTES;
|
|
|
|
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (view->scrolled_win),
|
|
GTK_SHADOW_IN);
|
|
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (view->scrolled_win),
|
|
GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
|
|
}
|
|
|
|
static GObject *
|
|
gimp_container_tree_view_constructor (GType type,
|
|
guint n_params,
|
|
GObjectConstructParam *params)
|
|
{
|
|
GimpContainerTreeView *tree_view;
|
|
GimpContainerView *view;
|
|
GtkListStore *list;
|
|
GObject *object;
|
|
|
|
object = G_OBJECT_CLASS (parent_class)->constructor (type, n_params, params);
|
|
|
|
tree_view = GIMP_CONTAINER_TREE_VIEW (object);
|
|
view = GIMP_CONTAINER_VIEW (object);
|
|
|
|
list = gtk_list_store_newv (tree_view->n_model_columns,
|
|
tree_view->model_columns);
|
|
tree_view->model = GTK_TREE_MODEL (list);
|
|
|
|
tree_view->view =
|
|
GTK_TREE_VIEW (gtk_tree_view_new_with_model (tree_view->model));
|
|
g_object_unref (list);
|
|
|
|
gtk_tree_view_set_headers_visible (tree_view->view, FALSE);
|
|
gtk_container_add (GTK_CONTAINER (view->scrolled_win),
|
|
GTK_WIDGET (tree_view->view));
|
|
gtk_widget_show (GTK_WIDGET (tree_view->view));
|
|
|
|
GIMP_CONTAINER_VIEW (tree_view)->dnd_widget = 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);
|
|
|
|
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", COLUMN_RENDERER,
|
|
NULL);
|
|
|
|
tree_view->name_cell = gtk_cell_renderer_text_new ();
|
|
g_object_set (tree_view->name_cell, "xalign", 0.0, NULL);
|
|
gtk_tree_view_column_pack_end (tree_view->main_column,
|
|
tree_view->name_cell,
|
|
FALSE);
|
|
|
|
gtk_tree_view_column_set_attributes (tree_view->main_column,
|
|
tree_view->name_cell,
|
|
"text", COLUMN_NAME,
|
|
"attributes", COLUMN_NAME_ATTRIBUTES,
|
|
NULL);
|
|
|
|
tree_view->renderer_cells = g_list_prepend (tree_view->renderer_cells,
|
|
tree_view->renderer_cell);
|
|
|
|
tree_view->selection = gtk_tree_view_get_selection (tree_view->view);
|
|
|
|
g_signal_connect (tree_view->selection, "changed",
|
|
G_CALLBACK (gimp_container_tree_view_selection_changed),
|
|
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);
|
|
|
|
gtk_tree_view_set_search_column (tree_view->view, COLUMN_NAME);
|
|
gtk_tree_view_set_enable_search (tree_view->view, TRUE);
|
|
|
|
return object;
|
|
}
|
|
|
|
static void
|
|
gimp_container_tree_view_menu_position (GtkMenu *menu,
|
|
gint *x,
|
|
gint *y,
|
|
gpointer data)
|
|
{
|
|
GimpContainerTreeView *tree_view;
|
|
GtkWidget *widget;
|
|
GtkTreeIter selected_iter;
|
|
GtkRequisition requisition;
|
|
GdkScreen *screen;
|
|
|
|
tree_view = GIMP_CONTAINER_TREE_VIEW (data);
|
|
widget = GTK_WIDGET (tree_view->view);
|
|
|
|
gdk_window_get_origin (widget->window, x, y);
|
|
|
|
if (GTK_WIDGET_NO_WINDOW (widget))
|
|
{
|
|
*x += widget->allocation.x;
|
|
*y += widget->allocation.y;
|
|
}
|
|
|
|
if (gtk_tree_selection_get_selected (tree_view->selection, NULL,
|
|
&selected_iter))
|
|
{
|
|
GtkTreePath *path;
|
|
GdkRectangle cell_rect;
|
|
|
|
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);
|
|
|
|
*x += widget->allocation.width / 2;
|
|
*y += cell_rect.y + cell_rect.height / 2;
|
|
}
|
|
else
|
|
{
|
|
*x += widget->style->xthickness;
|
|
*y += widget->style->ythickness;
|
|
}
|
|
|
|
gtk_widget_size_request (GTK_WIDGET (menu), &requisition);
|
|
|
|
screen = gtk_widget_get_screen (GTK_WIDGET (menu));
|
|
|
|
if (*x + requisition.width > gdk_screen_get_width (screen))
|
|
*x -= requisition.width;
|
|
|
|
if (*x < 0)
|
|
*x = 0;
|
|
|
|
if (*y + requisition.height > gdk_screen_get_height (screen))
|
|
*y -= requisition.height;
|
|
|
|
if (*y < 0)
|
|
*y = 0;
|
|
}
|
|
|
|
static gboolean
|
|
gimp_container_tree_view_popup_menu (GtkWidget *widget)
|
|
{
|
|
GimpContainerTreeView *tree_view;
|
|
GimpEditor *editor;
|
|
|
|
tree_view = GIMP_CONTAINER_TREE_VIEW (widget);
|
|
editor = GIMP_EDITOR (widget);
|
|
|
|
if (editor->item_factory &&
|
|
gtk_tree_selection_get_selected (tree_view->selection, NULL, NULL))
|
|
{
|
|
gimp_item_factory_popup_with_data (editor->item_factory,
|
|
editor->item_factory_data,
|
|
gimp_container_tree_view_menu_position,
|
|
editor,
|
|
NULL);
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
GtkWidget *
|
|
gimp_container_tree_view_new (GimpContainer *container,
|
|
GimpContext *context,
|
|
gint preview_size,
|
|
gint preview_border_width,
|
|
gboolean reorderable)
|
|
{
|
|
GimpContainerTreeView *tree_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 (preview_size > 0 &&
|
|
preview_size <= GIMP_VIEWABLE_MAX_PREVIEW_SIZE, NULL);
|
|
g_return_val_if_fail (preview_border_width >= 0 &&
|
|
preview_border_width <= GIMP_PREVIEW_MAX_BORDER_WIDTH,
|
|
NULL);
|
|
|
|
tree_view = g_object_new (GIMP_TYPE_CONTAINER_TREE_VIEW, NULL);
|
|
|
|
gimp_container_view_construct (GIMP_CONTAINER_VIEW (tree_view),
|
|
container, context,
|
|
preview_size, preview_border_width,
|
|
reorderable);
|
|
|
|
return GTK_WIDGET (tree_view);
|
|
}
|
|
|
|
static void
|
|
gimp_container_tree_view_set (GimpContainerTreeView *tree_view,
|
|
GtkTreeIter *iter,
|
|
GimpViewable *viewable)
|
|
{
|
|
GimpContainerView *view;
|
|
GimpPreviewRenderer *renderer;
|
|
gchar *name;
|
|
|
|
view = GIMP_CONTAINER_VIEW (tree_view);
|
|
|
|
name = gimp_viewable_get_description (viewable, NULL);
|
|
|
|
renderer = gimp_preview_renderer_new (G_TYPE_FROM_INSTANCE (viewable),
|
|
view->preview_size,
|
|
view->preview_border_width,
|
|
FALSE);
|
|
gimp_preview_renderer_set_viewable (renderer, viewable);
|
|
gimp_preview_renderer_remove_idle (renderer);
|
|
|
|
g_signal_connect (renderer, "update",
|
|
G_CALLBACK (gimp_container_tree_view_renderer_update),
|
|
tree_view);
|
|
|
|
gtk_list_store_set (GTK_LIST_STORE (tree_view->model), iter,
|
|
COLUMN_RENDERER, renderer,
|
|
COLUMN_NAME, name,
|
|
-1);
|
|
|
|
g_object_unref (renderer);
|
|
g_free (name);
|
|
}
|
|
|
|
/* GimpContainerView methods */
|
|
|
|
static void
|
|
gimp_container_tree_view_set_container (GimpContainerView *view,
|
|
GimpContainer *container)
|
|
{
|
|
GimpContainerTreeView *tree_view;
|
|
|
|
tree_view = GIMP_CONTAINER_TREE_VIEW (view);
|
|
|
|
if (view->container)
|
|
{
|
|
gimp_container_remove_handler (view->container,
|
|
tree_view->name_changed_handler_id);
|
|
|
|
if (! container)
|
|
{
|
|
if (gimp_dnd_viewable_source_unset (GTK_WIDGET (tree_view->view),
|
|
view->container->children_type))
|
|
{
|
|
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,
|
|
container->children_type,
|
|
GDK_ACTION_COPY))
|
|
{
|
|
gimp_dnd_viewable_source_set (GTK_WIDGET (tree_view->view),
|
|
container->children_type,
|
|
gimp_container_tree_view_drag_viewable,
|
|
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);
|
|
}
|
|
|
|
GIMP_CONTAINER_VIEW_CLASS (parent_class)->set_container (view, container);
|
|
|
|
if (view->container)
|
|
{
|
|
GimpViewableClass *viewable_class;
|
|
|
|
viewable_class = g_type_class_ref (container->children_type);
|
|
|
|
tree_view->name_changed_handler_id =
|
|
gimp_container_add_handler (view->container,
|
|
viewable_class->name_changed_signal,
|
|
G_CALLBACK (gimp_container_tree_view_name_changed),
|
|
tree_view);
|
|
|
|
g_type_class_unref (viewable_class);
|
|
}
|
|
}
|
|
|
|
static gpointer
|
|
gimp_container_tree_view_insert_item (GimpContainerView *view,
|
|
GimpViewable *viewable,
|
|
gint index)
|
|
{
|
|
GimpContainerTreeView *tree_view;
|
|
GtkTreeIter *iter;
|
|
|
|
tree_view = GIMP_CONTAINER_TREE_VIEW (view);
|
|
|
|
iter = g_new0 (GtkTreeIter, 1);
|
|
|
|
if (index == -1)
|
|
gtk_list_store_append (GTK_LIST_STORE (tree_view->model), iter);
|
|
else
|
|
gtk_list_store_insert (GTK_LIST_STORE (tree_view->model), iter, index);
|
|
|
|
gimp_container_tree_view_set (tree_view, iter, viewable);
|
|
|
|
return (gpointer) iter;
|
|
}
|
|
|
|
static void
|
|
gimp_container_tree_view_remove_item (GimpContainerView *view,
|
|
GimpViewable *viewable,
|
|
gpointer insert_data)
|
|
{
|
|
GimpContainerTreeView *tree_view;
|
|
GtkTreeIter *iter;
|
|
|
|
tree_view = GIMP_CONTAINER_TREE_VIEW (view);
|
|
|
|
iter = (GtkTreeIter *) insert_data;
|
|
|
|
if (iter)
|
|
gtk_list_store_remove (GTK_LIST_STORE (tree_view->model), iter);
|
|
}
|
|
|
|
static void
|
|
gimp_container_tree_view_reorder_item (GimpContainerView *view,
|
|
GimpViewable *viewable,
|
|
gint new_index,
|
|
gpointer insert_data)
|
|
{
|
|
GimpContainerTreeView *tree_view;
|
|
GtkTreeIter *iter;
|
|
|
|
tree_view = GIMP_CONTAINER_TREE_VIEW (view);
|
|
|
|
iter = (GtkTreeIter *) insert_data;
|
|
|
|
if (iter)
|
|
{
|
|
GtkTreePath *path;
|
|
GtkTreeIter selected_iter;
|
|
gboolean selected;
|
|
|
|
selected = gtk_tree_selection_get_selected (tree_view->selection,
|
|
NULL, &selected_iter);
|
|
|
|
if (selected)
|
|
{
|
|
GimpPreviewRenderer *renderer;
|
|
|
|
gtk_tree_model_get (tree_view->model, &selected_iter,
|
|
COLUMN_RENDERER, &renderer,
|
|
-1);
|
|
|
|
if (renderer->viewable != viewable)
|
|
selected = FALSE;
|
|
|
|
g_object_unref (renderer);
|
|
}
|
|
|
|
if (new_index == -1 || new_index == view->container->num_children - 1)
|
|
{
|
|
gtk_list_store_move_before (GTK_LIST_STORE (tree_view->model),
|
|
iter, NULL);
|
|
}
|
|
else if (new_index == 0)
|
|
{
|
|
gtk_list_store_move_after (GTK_LIST_STORE (tree_view->model),
|
|
iter, NULL);
|
|
}
|
|
else
|
|
{
|
|
GtkTreeIter place_iter;
|
|
gint old_index;
|
|
|
|
path = gtk_tree_model_get_path (tree_view->model, iter);
|
|
old_index = gtk_tree_path_get_indices (path)[0];
|
|
gtk_tree_path_free (path);
|
|
|
|
if (new_index != old_index)
|
|
{
|
|
path = gtk_tree_path_new_from_indices (new_index, -1);
|
|
gtk_tree_model_get_iter (tree_view->model, &place_iter, path);
|
|
gtk_tree_path_free (path);
|
|
|
|
if (new_index > old_index)
|
|
gtk_list_store_move_after (GTK_LIST_STORE (tree_view->model),
|
|
iter, &place_iter);
|
|
else
|
|
gtk_list_store_move_before (GTK_LIST_STORE (tree_view->model),
|
|
iter, &place_iter);
|
|
}
|
|
}
|
|
|
|
if (selected)
|
|
gimp_container_view_select_item (view, viewable);
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
gimp_container_tree_view_select_item (GimpContainerView *view,
|
|
GimpViewable *viewable,
|
|
gpointer insert_data)
|
|
{
|
|
GimpContainerTreeView *tree_view;
|
|
GtkTreeIter *iter;
|
|
|
|
tree_view = GIMP_CONTAINER_TREE_VIEW (view);
|
|
|
|
iter = (GtkTreeIter *) insert_data;
|
|
|
|
if (iter)
|
|
{
|
|
GtkTreePath *path;
|
|
|
|
path = gtk_tree_model_get_path (tree_view->model, iter);
|
|
|
|
g_signal_handlers_block_by_func (tree_view->selection,
|
|
gimp_container_tree_view_selection_changed,
|
|
tree_view);
|
|
|
|
#ifdef __GNUC__
|
|
#warning FIXME: remove this hack as soon as we depend on GTK+ 2.2.2
|
|
#endif
|
|
if (! GTK_CHECK_VERSION (2, 2, 2) &&
|
|
tree_view->main_column->editable_widget)
|
|
gtk_cell_editable_remove_widget (tree_view->main_column->editable_widget);
|
|
|
|
gtk_tree_view_set_cursor (tree_view->view, path, NULL, FALSE);
|
|
|
|
#ifdef __GNUC__
|
|
#warning FIXME: remove this hack as soon as #115871 is fixed
|
|
#endif
|
|
/* gtk_tree_view_set_cursor() should be sufficient actually... */
|
|
gtk_tree_selection_select_iter (tree_view->selection, iter);
|
|
|
|
g_signal_handlers_unblock_by_func (tree_view->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
|
|
{
|
|
gtk_tree_selection_unselect_all (tree_view->selection);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
gimp_container_tree_view_clear_items (GimpContainerView *view)
|
|
{
|
|
GimpContainerTreeView *tree_view;
|
|
|
|
tree_view = GIMP_CONTAINER_TREE_VIEW (view);
|
|
|
|
gtk_list_store_clear (GTK_LIST_STORE (tree_view->model));
|
|
|
|
GIMP_CONTAINER_VIEW_CLASS (parent_class)->clear_items (view);
|
|
}
|
|
|
|
static void
|
|
gimp_container_tree_view_set_preview_size (GimpContainerView *view)
|
|
{
|
|
GimpContainerTreeView *tree_view;
|
|
GtkWidget *tree_widget;
|
|
GtkTreeIter iter;
|
|
gboolean iter_valid;
|
|
GList *list;
|
|
|
|
tree_view = GIMP_CONTAINER_TREE_VIEW (view);
|
|
|
|
tree_widget = GTK_WIDGET (tree_view->view);
|
|
|
|
for (list = tree_view->toggle_cells; list; list = g_list_next (list))
|
|
{
|
|
gchar *stock_id;
|
|
GtkIconSize icon_size;
|
|
|
|
g_object_get (list->data, "stock-id", &stock_id, NULL);
|
|
|
|
if (stock_id)
|
|
{
|
|
icon_size = gimp_get_icon_size (tree_widget,
|
|
stock_id,
|
|
GTK_ICON_SIZE_BUTTON,
|
|
view->preview_size -
|
|
2 * tree_widget->style->xthickness,
|
|
view->preview_size -
|
|
2 * tree_widget->style->ythickness);
|
|
|
|
g_object_set (list->data, "stock-size", icon_size, NULL);
|
|
|
|
g_free (stock_id);
|
|
}
|
|
}
|
|
|
|
for (iter_valid = gtk_tree_model_get_iter_first (tree_view->model, &iter);
|
|
iter_valid;
|
|
iter_valid = gtk_tree_model_iter_next (tree_view->model, &iter))
|
|
{
|
|
GimpPreviewRenderer *renderer;
|
|
|
|
gtk_tree_model_get (tree_view->model, &iter,
|
|
COLUMN_RENDERER, &renderer,
|
|
-1);
|
|
|
|
gimp_preview_renderer_set_size (renderer,
|
|
view->preview_size,
|
|
view->preview_border_width);
|
|
|
|
g_object_unref (renderer);
|
|
}
|
|
|
|
gtk_tree_view_columns_autosize (tree_view->view);
|
|
}
|
|
|
|
|
|
/* callbacks */
|
|
|
|
static void
|
|
gimp_container_tree_view_selection_changed (GtkTreeSelection *selection,
|
|
GimpContainerTreeView *tree_view)
|
|
{
|
|
GtkTreeIter iter;
|
|
|
|
if (gtk_tree_selection_get_selected (selection, NULL, &iter))
|
|
{
|
|
GimpPreviewRenderer *renderer;
|
|
|
|
gtk_tree_model_get (tree_view->model, &iter,
|
|
COLUMN_RENDERER, &renderer,
|
|
-1);
|
|
|
|
gimp_container_view_item_selected (GIMP_CONTAINER_VIEW (tree_view),
|
|
renderer->viewable);
|
|
g_object_unref (renderer);
|
|
}
|
|
}
|
|
|
|
static GtkCellRenderer *
|
|
gimp_container_tree_view_find_click_cell (GList *cells,
|
|
GtkTreeViewColumn *column,
|
|
GdkRectangle *column_area,
|
|
gint tree_x,
|
|
gint tree_y)
|
|
{
|
|
GtkCellRenderer *renderer;
|
|
GList *list;
|
|
|
|
for (list = cells; list; list = g_list_next (list))
|
|
{
|
|
gint start_pos;
|
|
gint width;
|
|
|
|
renderer = (GtkCellRenderer *) list->data;
|
|
|
|
if (renderer->visible &&
|
|
gtk_tree_view_column_cell_get_position (column, renderer,
|
|
&start_pos, &width) &&
|
|
width > 0 &&
|
|
column_area->x + start_pos + renderer->xpad <= tree_x &&
|
|
column_area->x + start_pos + renderer->xpad + width - 1 >= tree_x)
|
|
{
|
|
#if 0
|
|
g_print ("click on cell at %d (%d width) (%d column->x) (%d tree_x)\n",
|
|
start_pos, width, column_area->x, tree_x);
|
|
#endif
|
|
|
|
return renderer;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static gboolean
|
|
gimp_container_tree_view_button_press (GtkWidget *widget,
|
|
GdkEventButton *bevent,
|
|
GimpContainerTreeView *tree_view)
|
|
{
|
|
GimpContainerView *container_view;
|
|
GtkTreeViewColumn *column;
|
|
GtkTreePath *path;
|
|
|
|
container_view = GIMP_CONTAINER_VIEW (tree_view);
|
|
|
|
tree_view->dnd_viewable = 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))
|
|
{
|
|
GimpPreviewRenderer *renderer;
|
|
GimpCellRendererToggle *toggled_cell = NULL;
|
|
GimpCellRendererViewable *clicked_cell = NULL;
|
|
GtkCellRenderer *edit_cell = NULL;
|
|
GdkRectangle column_area;
|
|
gint tree_x;
|
|
gint tree_y;
|
|
GtkTreeIter iter;
|
|
|
|
gtk_tree_model_get_iter (tree_view->model, &iter, path);
|
|
|
|
gtk_tree_model_get (tree_view->model, &iter,
|
|
COLUMN_RENDERER, &renderer,
|
|
-1);
|
|
|
|
tree_view->dnd_viewable = renderer->viewable;
|
|
|
|
gtk_tree_view_widget_to_tree_coords (tree_view->view,
|
|
bevent->x, bevent->y,
|
|
&tree_x, &tree_y);
|
|
gtk_tree_view_get_background_area (tree_view->view, path,
|
|
column, &column_area);
|
|
|
|
gtk_tree_view_column_cell_set_cell_data (column,
|
|
tree_view->model,
|
|
&iter,
|
|
FALSE, FALSE);
|
|
|
|
toggled_cell = (GimpCellRendererToggle *)
|
|
gimp_container_tree_view_find_click_cell (tree_view->toggle_cells,
|
|
column, &column_area,
|
|
tree_x, tree_y);
|
|
|
|
if (! toggled_cell)
|
|
clicked_cell = (GimpCellRendererViewable *)
|
|
gimp_container_tree_view_find_click_cell (tree_view->renderer_cells,
|
|
column, &column_area,
|
|
tree_x, tree_y);
|
|
|
|
if (! toggled_cell && ! clicked_cell)
|
|
edit_cell =
|
|
gimp_container_tree_view_find_click_cell (tree_view->editable_cells,
|
|
column, &column_area,
|
|
tree_x, tree_y);
|
|
|
|
g_object_ref (tree_view);
|
|
|
|
switch (bevent->button)
|
|
{
|
|
case 1:
|
|
if (bevent->type == GDK_BUTTON_PRESS)
|
|
{
|
|
gboolean success = TRUE;
|
|
|
|
#ifdef __GNUC__
|
|
#warning FIXME: remove this hack as soon as we depend on GTK+ 2.2.2
|
|
#endif
|
|
if (! GTK_CHECK_VERSION (2, 2, 2) &&
|
|
tree_view->main_column->editable_widget)
|
|
gtk_cell_editable_remove_widget (tree_view->main_column->editable_widget);
|
|
|
|
/* don't select item if a toggle was clicked */
|
|
if (! toggled_cell)
|
|
success = gimp_container_view_item_selected (container_view,
|
|
renderer->viewable);
|
|
|
|
/* a callback invoked by selecting the item may have
|
|
* destroyed us, so check if the container is still there
|
|
*/
|
|
if (container_view->container)
|
|
{
|
|
/* 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;
|
|
|
|
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)
|
|
{
|
|
#ifdef __GNUC__
|
|
#warning FIXME: remove this hack as soon as we depend on GTK+ 2.2.2
|
|
#endif
|
|
if (! GTK_CHECK_VERSION (2, 2, 2) &&
|
|
column->editable_widget)
|
|
gtk_cell_editable_remove_widget (column->editable_widget);
|
|
|
|
#ifdef __GNUC__
|
|
#warning FIXME: make sure the orig text gets restored when cancelling editing
|
|
#endif
|
|
if (edit_cell == tree_view->name_cell)
|
|
{
|
|
const gchar *real_name;
|
|
|
|
real_name =
|
|
gimp_object_get_name (GIMP_OBJECT (renderer->viewable));
|
|
|
|
gtk_list_store_set (GTK_LIST_STORE (tree_view->model),
|
|
&iter,
|
|
tree_view->model_column_name,
|
|
real_name,
|
|
-1);
|
|
}
|
|
|
|
gtk_tree_view_set_cursor_on_cell (tree_view->view, path,
|
|
column, edit_cell, TRUE);
|
|
}
|
|
else if (! toggled_cell) /* ignore double click on toggles */
|
|
{
|
|
gimp_container_view_item_activated (container_view,
|
|
renderer->viewable);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 2:
|
|
break;
|
|
|
|
case 3:
|
|
if (gimp_container_view_item_selected (container_view,
|
|
renderer->viewable))
|
|
{
|
|
if (container_view->container)
|
|
gimp_container_view_item_context (GIMP_CONTAINER_VIEW (tree_view),
|
|
renderer->viewable);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
g_object_unref (tree_view);
|
|
|
|
gtk_tree_path_free (path);
|
|
g_object_unref (renderer);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
gimp_container_tree_view_renderer_update (GimpPreviewRenderer *renderer,
|
|
GimpContainerTreeView *tree_view)
|
|
{
|
|
GimpContainerView *view;
|
|
GtkTreeIter *iter;
|
|
|
|
view = GIMP_CONTAINER_VIEW (tree_view);
|
|
|
|
iter = g_hash_table_lookup (view->hash_table, renderer->viewable);
|
|
|
|
if (iter)
|
|
{
|
|
GtkTreePath *path;
|
|
|
|
path = gtk_tree_model_get_path (tree_view->model, iter);
|
|
|
|
gtk_tree_model_row_changed (tree_view->model, path, iter);
|
|
|
|
gtk_tree_path_free (path);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gimp_container_tree_view_name_changed (GimpObject *object,
|
|
GimpContainerTreeView *tree_view)
|
|
{
|
|
GimpContainerView *view;
|
|
GtkTreeIter *iter;
|
|
|
|
view = GIMP_CONTAINER_VIEW (tree_view);
|
|
|
|
iter = g_hash_table_lookup (view->hash_table, object);
|
|
|
|
if (iter)
|
|
{
|
|
gchar *name;
|
|
|
|
name = gimp_viewable_get_description (GIMP_VIEWABLE (object), NULL);
|
|
|
|
gtk_list_store_set (GTK_LIST_STORE (tree_view->model), iter,
|
|
COLUMN_NAME, name,
|
|
-1);
|
|
|
|
g_free (name);
|
|
}
|
|
}
|
|
|
|
static GimpViewable *
|
|
gimp_container_tree_view_drag_viewable (GtkWidget *widget,
|
|
gpointer data)
|
|
{
|
|
GimpContainerTreeView *tree_view;
|
|
|
|
tree_view = GIMP_CONTAINER_TREE_VIEW (data);
|
|
|
|
return tree_view->dnd_viewable;
|
|
}
|