/* PIKA - Photo and Image Kooker Application * a rebranding of The GNU Image Manipulation Program (created with heckimp) * A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio * * Original copyright, applying to most contents (license remains unchanged): * Copyright (C) 1995 Spencer Kimball and Peter Mattis * * pikacontainereditor.c * Copyright (C) 2001-2011 Michael Natterer * * 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 . */ #include "config.h" #include #include #include "libpikabase/pikabase.h" #include "libpikawidgets/pikawidgets.h" #include "widgets-types.h" #include "core/pikaasyncset.h" #include "core/pikacontext.h" #include "core/pikalist.h" #include "core/pikaviewable.h" #include "pikacontainereditor.h" #include "pikacontainericonview.h" #include "pikacontainertreeview.h" #include "pikacontainerview.h" #include "pikadocked.h" #include "pikamenufactory.h" #include "pikaviewrenderer.h" #include "pikauimanager.h" enum { PROP_0, PROP_VIEW_TYPE, PROP_CONTAINER, PROP_CONTEXT, PROP_VIEW_SIZE, PROP_VIEW_BORDER_WIDTH, PROP_MENU_FACTORY, PROP_MENU_IDENTIFIER, PROP_UI_PATH }; struct _PikaContainerEditorPrivate { PikaViewType view_type; PikaContainer *container; PikaContext *context; gint view_size; gint view_border_width; PikaMenuFactory *menu_factory; gchar *menu_identifier; gchar *ui_path; GtkWidget *busy_box; GBinding *async_set_binding; }; static void pika_container_editor_docked_iface_init (PikaDockedInterface *iface); static void pika_container_editor_constructed (GObject *object); static void pika_container_editor_dispose (GObject *object); static void pika_container_editor_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void pika_container_editor_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static gboolean pika_container_editor_select_items (PikaContainerView *view, GList *items, GList *paths, PikaContainerEditor *editor); static void pika_container_editor_activate_item (GtkWidget *widget, PikaViewable *viewable, gpointer insert_data, PikaContainerEditor *editor); static GtkWidget * pika_container_editor_get_preview (PikaDocked *docked, PikaContext *context, GtkIconSize size); static void pika_container_editor_set_context (PikaDocked *docked, PikaContext *context); static PikaUIManager * pika_container_editor_get_menu(PikaDocked *docked, const gchar **ui_path, gpointer *popup_data); static gboolean pika_container_editor_has_button_bar (PikaDocked *docked); static void pika_container_editor_set_show_button_bar (PikaDocked *docked, gboolean show); static gboolean pika_container_editor_get_show_button_bar (PikaDocked *docked); G_DEFINE_TYPE_WITH_CODE (PikaContainerEditor, pika_container_editor, GTK_TYPE_BOX, G_ADD_PRIVATE (PikaContainerEditor) G_IMPLEMENT_INTERFACE (PIKA_TYPE_DOCKED, pika_container_editor_docked_iface_init)) #define parent_class pika_container_editor_parent_class static void pika_container_editor_class_init (PikaContainerEditorClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->constructed = pika_container_editor_constructed; object_class->dispose = pika_container_editor_dispose; object_class->set_property = pika_container_editor_set_property; object_class->get_property = pika_container_editor_get_property; klass->select_item = NULL; klass->activate_item = NULL; g_object_class_install_property (object_class, PROP_VIEW_TYPE, g_param_spec_enum ("view-type", NULL, NULL, PIKA_TYPE_VIEW_TYPE, PIKA_VIEW_TYPE_LIST, PIKA_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_CONTAINER, g_param_spec_object ("container", NULL, NULL, PIKA_TYPE_CONTAINER, PIKA_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_CONTEXT, g_param_spec_object ("context", NULL, NULL, PIKA_TYPE_CONTEXT, PIKA_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_VIEW_SIZE, g_param_spec_int ("view-size", NULL, NULL, 1, PIKA_VIEWABLE_MAX_PREVIEW_SIZE, PIKA_VIEW_SIZE_MEDIUM, PIKA_PARAM_READWRITE | G_PARAM_CONSTRUCT)); g_object_class_install_property (object_class, PROP_VIEW_BORDER_WIDTH, g_param_spec_int ("view-border-width", NULL, NULL, 0, PIKA_VIEW_MAX_BORDER_WIDTH, 1, PIKA_PARAM_READWRITE | G_PARAM_CONSTRUCT)); g_object_class_install_property (object_class, PROP_MENU_FACTORY, g_param_spec_object ("menu-factory", NULL, NULL, PIKA_TYPE_MENU_FACTORY, PIKA_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_MENU_IDENTIFIER, g_param_spec_string ("menu-identifier", NULL, NULL, NULL, PIKA_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_UI_PATH, g_param_spec_string ("ui-path", NULL, NULL, NULL, PIKA_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); } static void pika_container_editor_docked_iface_init (PikaDockedInterface *iface) { iface->get_preview = pika_container_editor_get_preview; iface->set_context = pika_container_editor_set_context; iface->get_menu = pika_container_editor_get_menu; iface->has_button_bar = pika_container_editor_has_button_bar; iface->set_show_button_bar = pika_container_editor_set_show_button_bar; iface->get_show_button_bar = pika_container_editor_get_show_button_bar; } static void pika_container_editor_init (PikaContainerEditor *editor) { gtk_orientable_set_orientation (GTK_ORIENTABLE (editor), GTK_ORIENTATION_VERTICAL); editor->priv = pika_container_editor_get_instance_private (editor); } static void pika_container_editor_constructed (GObject *object) { PikaContainerEditor *editor = PIKA_CONTAINER_EDITOR (object); G_OBJECT_CLASS (parent_class)->constructed (object); pika_assert (PIKA_IS_CONTAINER (editor->priv->container)); pika_assert (PIKA_IS_CONTEXT (editor->priv->context)); switch (editor->priv->view_type) { case PIKA_VIEW_TYPE_GRID: editor->view = PIKA_CONTAINER_VIEW (pika_container_icon_view_new (editor->priv->container, editor->priv->context, editor->priv->view_size, editor->priv->view_border_width)); break; case PIKA_VIEW_TYPE_LIST: editor->view = PIKA_CONTAINER_VIEW (pika_container_tree_view_new (editor->priv->container, editor->priv->context, editor->priv->view_size, editor->priv->view_border_width)); break; default: pika_assert_not_reached (); } if (PIKA_IS_LIST (editor->priv->container)) pika_container_view_set_reorderable (PIKA_CONTAINER_VIEW (editor->view), ! pika_list_get_sort_func (PIKA_LIST (editor->priv->container))); if (editor->priv->menu_factory && editor->priv->menu_identifier && editor->priv->ui_path) { pika_editor_create_menu (PIKA_EDITOR (editor->view), editor->priv->menu_factory, editor->priv->menu_identifier, editor->priv->ui_path, editor); } gtk_box_pack_start (GTK_BOX (editor), GTK_WIDGET (editor->view), TRUE, TRUE, 0); gtk_widget_show (GTK_WIDGET (editor->view)); editor->priv->busy_box = pika_busy_box_new (NULL); gtk_box_pack_start (GTK_BOX (editor), editor->priv->busy_box, TRUE, TRUE, 0); g_object_bind_property (editor->priv->busy_box, "visible", editor->view, "visible", G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN); /* Connect "select-items" with G_CONNECT_AFTER because it's a * RUN_LAST signal and the default handler selecting the row must * run before signal connections. See bug #784176. */ g_signal_connect_object (editor->view, "select-items", G_CALLBACK (pika_container_editor_select_items), editor, G_CONNECT_AFTER); g_signal_connect_object (editor->view, "activate-item", G_CALLBACK (pika_container_editor_activate_item), editor, 0); /* g_signal_connect_object (editor->view, "context-item", XXX maybe listen to popup-menu? */ /* G_CALLBACK (pika_container_editor_context_item), */ /* editor, 0); */ { GList *objects = NULL; PikaObject *object = pika_context_get_by_type (editor->priv->context, pika_container_get_children_type (editor->priv->container)); if (object) objects = g_list_prepend (objects, object); pika_container_editor_select_items (editor->view, objects, NULL, editor); g_list_free (objects); } } static void pika_container_editor_dispose (GObject *object) { PikaContainerEditor *editor = PIKA_CONTAINER_EDITOR (object); pika_container_editor_bind_to_async_set (editor, NULL, NULL); g_clear_object (&editor->priv->container); g_clear_object (&editor->priv->context); g_clear_object (&editor->priv->menu_factory); g_clear_pointer (&editor->priv->menu_identifier, g_free); g_clear_pointer (&editor->priv->ui_path, g_free); G_OBJECT_CLASS (parent_class)->dispose (object); } static void pika_container_editor_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { PikaContainerEditor *editor = PIKA_CONTAINER_EDITOR (object); switch (property_id) { case PROP_VIEW_TYPE: editor->priv->view_type = g_value_get_enum (value); break; case PROP_CONTAINER: editor->priv->container = g_value_dup_object (value); break; case PROP_CONTEXT: editor->priv->context = g_value_dup_object (value); break; case PROP_VIEW_SIZE: editor->priv->view_size = g_value_get_int (value); break; case PROP_VIEW_BORDER_WIDTH: editor->priv->view_border_width = g_value_get_int (value); break; case PROP_MENU_FACTORY: editor->priv->menu_factory = g_value_dup_object (value); break; case PROP_MENU_IDENTIFIER: editor->priv->menu_identifier = g_value_dup_string (value); break; case PROP_UI_PATH: editor->priv->ui_path = g_value_dup_string (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void pika_container_editor_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { PikaContainerEditor *editor = PIKA_CONTAINER_EDITOR (object); switch (property_id) { case PROP_VIEW_TYPE: g_value_set_enum (value, editor->priv->view_type); break; case PROP_CONTAINER: g_value_set_object (value, editor->priv->container); break; case PROP_CONTEXT: g_value_set_object (value, editor->priv->context); break; case PROP_VIEW_SIZE: g_value_set_int (value, editor->priv->view_size); break; case PROP_VIEW_BORDER_WIDTH: g_value_set_int (value, editor->priv->view_border_width); break; case PROP_MENU_FACTORY: g_value_set_object (value, editor->priv->menu_factory); break; case PROP_MENU_IDENTIFIER: g_value_set_string (value, editor->priv->menu_identifier); break; case PROP_UI_PATH: g_value_set_string (value, editor->priv->ui_path); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } GtkSelectionMode pika_container_editor_get_selection_mode (PikaContainerEditor *editor) { return pika_container_view_get_selection_mode (PIKA_CONTAINER_VIEW (editor->view)); } void pika_container_editor_set_selection_mode (PikaContainerEditor *editor, GtkSelectionMode mode) { pika_container_view_set_selection_mode (PIKA_CONTAINER_VIEW (editor->view), mode); } /* private functions */ static gboolean pika_container_editor_select_items (PikaContainerView *view, GList *items, GList *paths, PikaContainerEditor *editor) { PikaContainerEditorClass *klass = PIKA_CONTAINER_EDITOR_GET_CLASS (editor); PikaViewable *viewable = NULL; /* XXX Right now a PikaContainerEditor only supports 1 item selected * at once. Let's see later if we want to allow more. */ /*g_return_val_if_fail (g_list_length (items) < 2, FALSE);*/ if (items) viewable = items->data; if (klass->select_item) klass->select_item (editor, viewable); if (editor->priv->container) { const gchar *signal_name; GType children_type; children_type = pika_container_get_children_type (editor->priv->container); signal_name = pika_context_type_to_signal_name (children_type); if (signal_name) pika_context_set_by_type (editor->priv->context, children_type, PIKA_OBJECT (viewable)); } if (pika_editor_get_ui_manager (PIKA_EDITOR (editor->view))) pika_ui_manager_update (pika_editor_get_ui_manager (PIKA_EDITOR (editor->view)), pika_editor_get_popup_data (PIKA_EDITOR (editor->view))); return TRUE; } static void pika_container_editor_activate_item (GtkWidget *widget, PikaViewable *viewable, gpointer insert_data, PikaContainerEditor *editor) { PikaContainerEditorClass *klass = PIKA_CONTAINER_EDITOR_GET_CLASS (editor); if (klass->activate_item) klass->activate_item (editor, viewable); } static GtkWidget * pika_container_editor_get_preview (PikaDocked *docked, PikaContext *context, GtkIconSize size) { PikaContainerEditor *editor = PIKA_CONTAINER_EDITOR (docked); return pika_docked_get_preview (PIKA_DOCKED (editor->view), context, size); } static void pika_container_editor_set_context (PikaDocked *docked, PikaContext *context) { PikaContainerEditor *editor = PIKA_CONTAINER_EDITOR (docked); pika_docked_set_context (PIKA_DOCKED (editor->view), context); } static PikaUIManager * pika_container_editor_get_menu (PikaDocked *docked, const gchar **ui_path, gpointer *popup_data) { PikaContainerEditor *editor = PIKA_CONTAINER_EDITOR (docked); return pika_docked_get_menu (PIKA_DOCKED (editor->view), ui_path, popup_data); } static gboolean pika_container_editor_has_button_bar (PikaDocked *docked) { PikaContainerEditor *editor = PIKA_CONTAINER_EDITOR (docked); return pika_docked_has_button_bar (PIKA_DOCKED (editor->view)); } static void pika_container_editor_set_show_button_bar (PikaDocked *docked, gboolean show) { PikaContainerEditor *editor = PIKA_CONTAINER_EDITOR (docked); pika_docked_set_show_button_bar (PIKA_DOCKED (editor->view), show); } static gboolean pika_container_editor_get_show_button_bar (PikaDocked *docked) { PikaContainerEditor *editor = PIKA_CONTAINER_EDITOR (docked); return pika_docked_get_show_button_bar (PIKA_DOCKED (editor->view)); } void pika_container_editor_bind_to_async_set (PikaContainerEditor *editor, PikaAsyncSet *async_set, const gchar *message) { g_return_if_fail (PIKA_IS_CONTAINER_EDITOR (editor)); g_return_if_fail (async_set == NULL || PIKA_IS_ASYNC_SET (async_set)); g_return_if_fail (async_set == NULL || message != NULL); if (! async_set && ! editor->priv->async_set_binding) return; g_clear_object (&editor->priv->async_set_binding); if (async_set) { pika_busy_box_set_message (PIKA_BUSY_BOX (editor->priv->busy_box), message); editor->priv->async_set_binding = g_object_bind_property ( async_set, "empty", editor->priv->busy_box, "visible", G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN); } else { gtk_widget_hide (editor->priv->busy_box); } }