/* 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 * * pikadockbook.c * Copyright (C) 2001-2007 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 #undef GTK_DISABLE_DEPRECATED #include #include "libpikabase/pikabase.h" #include "libpikawidgets/pikawidgets.h" #include "widgets-types.h" #include "config/pikaguiconfig.h" #include "core/pika.h" #include "core/pikacontext.h" #include "menus/menus.h" #include "pikaactiongroup.h" #include "pikadialogfactory.h" #include "pikadnd.h" #include "pikadock.h" #include "pikadockable.h" #include "pikadockbook.h" #include "pikadocked.h" #include "pikadockcontainer.h" #include "pikadockwindow.h" #include "pikahelp-ids.h" #include "pikamenufactory.h" #include "pikapanedbox.h" #include "pikastringaction.h" #include "pikauimanager.h" #include "pikaview.h" #include "pikawidgets-utils.h" #include "pika-log.h" #include "pika-intl.h" #define DEFAULT_TAB_ICON_SIZE GTK_ICON_SIZE_BUTTON #define DND_WIDGET_ICON_SIZE GTK_ICON_SIZE_BUTTON #define MENU_WIDGET_SPACING 4 #define TAB_HOVER_TIMEOUT 500 #define PIKA_DOCKABLE_DETACH_REF_KEY "pika-dockable-detach-ref" enum { DOCKABLE_ADDED, DOCKABLE_REMOVED, DOCKABLE_REORDERED, LAST_SIGNAL }; typedef struct { PikaDockbookDragCallback callback; gpointer data; } PikaDockbookDragCallbackData; struct _PikaDockbookPrivate { PikaDock *dock; PikaUIManager *ui_manager; guint tab_hover_timeout; PikaDockable *tab_hover_dockable; PikaPanedBox *drag_handler; GtkWidget *menu_button; }; static void pika_dockbook_style_updated (GtkWidget *widget); static void pika_dockbook_drag_begin (GtkWidget *widget, GdkDragContext *context); static void pika_dockbook_drag_end (GtkWidget *widget, GdkDragContext *context); static gboolean pika_dockbook_drag_motion (GtkWidget *widget, GdkDragContext *context, gint x, gint y, guint time); static gboolean pika_dockbook_drag_drop (GtkWidget *widget, GdkDragContext *context, gint x, gint y, guint time); static gboolean pika_dockbook_popup_menu (GtkWidget *widget); static GtkNotebook *pika_dockbook_create_window (GtkNotebook *notebook, GtkWidget *page, gint x, gint y); static void pika_dockbook_page_added (GtkNotebook *notebook, GtkWidget *child, guint page_num); static void pika_dockbook_page_removed (GtkNotebook *notebook, GtkWidget *child, guint page_num); static void pika_dockbook_page_reordered (GtkNotebook *notebook, GtkWidget *child, guint page_num); static gboolean pika_dockbook_menu_button_press (PikaDockbook *dockbook, GdkEventButton *bevent, GtkWidget *button); static gboolean pika_dockbook_show_menu (PikaDockbook *dockbook); static void pika_dockbook_menu_end (PikaDockable *dockable); static void pika_dockbook_tab_locked_notify (PikaDockable *dockable, GParamSpec *pspec, PikaDockbook *dockbook); static void pika_dockbook_help_func (const gchar *help_id, gpointer help_data); G_DEFINE_TYPE_WITH_PRIVATE (PikaDockbook, pika_dockbook, GTK_TYPE_NOTEBOOK) #define parent_class pika_dockbook_parent_class static guint dockbook_signals[LAST_SIGNAL] = { 0 }; static const GtkTargetEntry dialog_target_table[] = { PIKA_TARGET_NOTEBOOK_TAB }; static GList *drag_callbacks = NULL; static void pika_dockbook_class_init (PikaDockbookClass *klass) { GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); GtkNotebookClass *notebook_class = GTK_NOTEBOOK_CLASS (klass); dockbook_signals[DOCKABLE_ADDED] = g_signal_new ("dockable-added", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (PikaDockbookClass, dockable_added), NULL, NULL, NULL, G_TYPE_NONE, 1, PIKA_TYPE_DOCKABLE); dockbook_signals[DOCKABLE_REMOVED] = g_signal_new ("dockable-removed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (PikaDockbookClass, dockable_removed), NULL, NULL, NULL, G_TYPE_NONE, 1, PIKA_TYPE_DOCKABLE); dockbook_signals[DOCKABLE_REORDERED] = g_signal_new ("dockable-reordered", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (PikaDockbookClass, dockable_reordered), NULL, NULL, NULL, G_TYPE_NONE, 1, PIKA_TYPE_DOCKABLE); widget_class->style_updated = pika_dockbook_style_updated; widget_class->drag_begin = pika_dockbook_drag_begin; widget_class->drag_end = pika_dockbook_drag_end; widget_class->drag_motion = pika_dockbook_drag_motion; widget_class->drag_drop = pika_dockbook_drag_drop; widget_class->popup_menu = pika_dockbook_popup_menu; notebook_class->create_window = pika_dockbook_create_window; notebook_class->page_added = pika_dockbook_page_added; notebook_class->page_removed = pika_dockbook_page_removed; notebook_class->page_reordered = pika_dockbook_page_reordered; klass->dockable_added = NULL; klass->dockable_removed = NULL; klass->dockable_reordered = NULL; gtk_widget_class_install_style_property (widget_class, g_param_spec_enum ("tab-icon-size", NULL, NULL, GTK_TYPE_ICON_SIZE, DEFAULT_TAB_ICON_SIZE, PIKA_PARAM_READABLE)); } static void pika_dockbook_init (PikaDockbook *dockbook) { GtkNotebook *notebook = GTK_NOTEBOOK (dockbook); GtkWidget *image; dockbook->p = pika_dockbook_get_instance_private (dockbook); /* Various init */ gtk_notebook_popup_enable (notebook); gtk_notebook_set_scrollable (notebook, TRUE); gtk_notebook_set_show_border (notebook, FALSE); gtk_notebook_set_show_tabs (notebook, TRUE); gtk_notebook_set_group_name (notebook, "pika-dockbook"); gtk_drag_dest_set (GTK_WIDGET (dockbook), 0, dialog_target_table, G_N_ELEMENTS (dialog_target_table), GDK_ACTION_MOVE); /* Menu button */ dockbook->p->menu_button = gtk_button_new (); gtk_widget_set_can_focus (dockbook->p->menu_button, FALSE); gtk_button_set_relief (GTK_BUTTON (dockbook->p->menu_button), GTK_RELIEF_NONE); gtk_notebook_set_action_widget (notebook, dockbook->p->menu_button, GTK_PACK_END); gtk_widget_show (dockbook->p->menu_button); image = gtk_image_new_from_icon_name (PIKA_ICON_MENU_LEFT, GTK_ICON_SIZE_MENU); gtk_image_set_pixel_size (GTK_IMAGE (image), 12); gtk_image_set_from_icon_name (GTK_IMAGE (image), PIKA_ICON_MENU_LEFT, GTK_ICON_SIZE_MENU); gtk_container_add (GTK_CONTAINER (dockbook->p->menu_button), image); gtk_widget_show (image); pika_help_set_help_data (dockbook->p->menu_button, _("Configure this tab"), PIKA_HELP_DOCK_TAB_MENU); g_signal_connect_swapped (dockbook->p->menu_button, "button-press-event", G_CALLBACK (pika_dockbook_menu_button_press), dockbook); } static void pika_dockbook_style_updated (GtkWidget *widget) { PikaDockbook *dockbook = PIKA_DOCKBOOK (widget); PikaContext *context; GtkWidget *tab_widget; GList *children; GList *iter; GTK_WIDGET_CLASS (parent_class)->style_updated (widget); if (! dockbook->p->dock || ! (context = pika_dock_get_context (dockbook->p->dock))) return; children = gtk_container_get_children (GTK_CONTAINER (dockbook)); for (iter = children; iter; iter = g_list_next (iter)) { PikaDockable *dockable = PIKA_DOCKABLE (iter->data); tab_widget = pika_dockbook_create_tab_widget (dockbook, dockable); gtk_notebook_set_tab_label (GTK_NOTEBOOK (dockbook), GTK_WIDGET (dockable), tab_widget); } g_list_free (children); pika_dock_invalidate_geometry (PIKA_DOCK (dockbook->p->dock)); } static void pika_dockbook_drag_begin (GtkWidget *widget, GdkDragContext *context) { GList *iter; if (GTK_WIDGET_CLASS (parent_class)->drag_begin) GTK_WIDGET_CLASS (parent_class)->drag_begin (widget, context); iter = drag_callbacks; while (iter) { PikaDockbookDragCallbackData *callback_data = iter->data; iter = g_list_next (iter); callback_data->callback (context, TRUE, callback_data->data); } } static void pika_dockbook_drag_end (GtkWidget *widget, GdkDragContext *context) { GList *iter; iter = drag_callbacks; while (iter) { PikaDockbookDragCallbackData *callback_data = iter->data; iter = g_list_next (iter); callback_data->callback (context, FALSE, callback_data->data); } if (GTK_WIDGET_CLASS (parent_class)->drag_end) GTK_WIDGET_CLASS (parent_class)->drag_end (widget, context); } static gboolean pika_dockbook_drag_motion (GtkWidget *widget, GdkDragContext *context, gint x, gint y, guint time) { PikaDockbook *dockbook = PIKA_DOCKBOOK (widget); if (pika_paned_box_will_handle_drag (dockbook->p->drag_handler, widget, context, x, y, time)) { gdk_drag_status (context, 0, time); return FALSE; } return GTK_WIDGET_CLASS (parent_class)->drag_motion (widget, context, x, y, time); } static gboolean pika_dockbook_drag_drop (GtkWidget *widget, GdkDragContext *context, gint x, gint y, guint time) { PikaDockbook *dockbook = PIKA_DOCKBOOK (widget); if (pika_paned_box_will_handle_drag (dockbook->p->drag_handler, widget, context, x, y, time)) { return FALSE; } if (widget == gtk_drag_get_source_widget (context)) { GList *children = gtk_container_get_children (GTK_CONTAINER (widget)); gint n_children = g_list_length (children); g_list_free (children); /* we dragged the only tab, and want to drop it back on the same * notebook, this would remove and add the page, causing the * dockbook to be destroyed in the process because it becomes * empty */ if (n_children == 1) return FALSE; } return GTK_WIDGET_CLASS (parent_class)->drag_drop (widget, context, x, y, time); } static gboolean pika_dockbook_popup_menu (GtkWidget *widget) { return pika_dockbook_show_menu (PIKA_DOCKBOOK (widget)); } static GtkNotebook * pika_dockbook_create_window (GtkNotebook *notebook, GtkWidget *page, gint x, gint y) { PikaDockbook *dockbook = PIKA_DOCKBOOK (notebook); PikaDialogFactory *dialog_factory; PikaMenuFactory *menu_factory; PikaDockWindow *src_dock_window; PikaDock *src_dock; GtkWidget *new_dock; PikaDockWindow *new_dock_window; GtkWidget *new_dockbook; src_dock = pika_dockbook_get_dock (dockbook); src_dock_window = pika_dock_window_from_dock (src_dock); dialog_factory = pika_dock_get_dialog_factory (src_dock); menu_factory = menus_get_global_menu_factory (pika_dialog_factory_get_context (dialog_factory)->pika); new_dock = pika_dock_with_window_new (dialog_factory, pika_widget_get_monitor (page), FALSE); new_dock_window = pika_dock_window_from_dock (PIKA_DOCK (new_dock)); gtk_window_set_position (GTK_WINDOW (new_dock_window), GTK_WIN_POS_MOUSE); if (src_dock_window) pika_dock_window_setup (new_dock_window, src_dock_window); new_dockbook = pika_dockbook_new (menu_factory); pika_dock_add_book (PIKA_DOCK (new_dock), PIKA_DOCKBOOK (new_dockbook), 0); gtk_widget_show (GTK_WIDGET (new_dock_window)); gtk_widget_show (new_dock); return GTK_NOTEBOOK (new_dockbook); } static void pika_dockbook_page_added (GtkNotebook *notebook, GtkWidget *child, guint page_num) { PikaDockbook *dockbook = PIKA_DOCKBOOK (notebook); PikaDockable *dockable = PIKA_DOCKABLE (child); GtkWidget *tab_widget; GtkWidget *menu_widget; PIKA_LOG (DND, "PikaDockable %p added to PikaDockbook %p", dockable, dockbook); tab_widget = pika_dockbook_create_tab_widget (dockbook, dockable); /* For the notebook right-click menu, always use the icon style */ menu_widget = pika_dockable_create_tab_widget (dockable, pika_dock_get_context (dockbook->p->dock), PIKA_TAB_STYLE_ICON_BLURB, GTK_ICON_SIZE_MENU); gtk_notebook_set_tab_label (notebook, child, tab_widget); gtk_notebook_set_menu_label (notebook, child, menu_widget); if (! pika_dockable_get_locked (dockable)) { gtk_notebook_set_tab_reorderable (notebook, child, TRUE); gtk_notebook_set_tab_detachable (notebook, child, TRUE); } pika_dockable_set_dockbook (dockable, dockbook); pika_dockable_set_context (dockable, pika_dock_get_context (dockbook->p->dock)); g_signal_connect (dockable, "notify::locked", G_CALLBACK (pika_dockbook_tab_locked_notify), dockbook); gtk_widget_show (child); gtk_notebook_set_current_page (notebook, page_num); g_signal_emit (dockbook, dockbook_signals[DOCKABLE_ADDED], 0, dockable); } static void pika_dockbook_page_removed (GtkNotebook *notebook, GtkWidget *child, guint page_num) { PikaDockbook *dockbook = PIKA_DOCKBOOK (notebook); PikaDockable *dockable = PIKA_DOCKABLE (child); PIKA_LOG (DND, "PikaDockable removed %p from PikaDockbook %p", dockable, dockbook); g_signal_handlers_disconnect_by_func (dockable, G_CALLBACK (pika_dockbook_tab_locked_notify), dockbook); pika_dockable_set_dockbook (dockable, NULL); pika_dockable_set_context (dockable, NULL); g_signal_emit (dockbook, dockbook_signals[DOCKABLE_REMOVED], 0, dockable); if (dockbook->p->dock) { GList *children = gtk_container_get_children (GTK_CONTAINER (dockbook)); if (! children) pika_dock_remove_book (dockbook->p->dock, dockbook); g_list_free (children); } } static void pika_dockbook_page_reordered (GtkNotebook *notebook, GtkWidget *child, guint page_num) { } static gboolean pika_dockbook_menu_button_press (PikaDockbook *dockbook, GdkEventButton *bevent, GtkWidget *button) { gboolean handled = FALSE; if (bevent->button == 1 && bevent->type == GDK_BUTTON_PRESS) handled = pika_dockbook_show_menu (dockbook); return handled; } static gboolean pika_dockbook_show_menu (PikaDockbook *dockbook) { PikaUIManager *dockbook_ui_manager; PikaUIManager *dialog_ui_manager; const gchar *dialog_ui_path; gpointer dialog_popup_data; PikaDockable *dockable; gint page_num; dockbook_ui_manager = dockbook->p->ui_manager; if (! dockbook_ui_manager) return FALSE; page_num = gtk_notebook_get_current_page (GTK_NOTEBOOK (dockbook)); dockable = PIKA_DOCKABLE (gtk_notebook_get_nth_page (GTK_NOTEBOOK (dockbook), page_num)); if (! dockable) return FALSE; dialog_ui_manager = pika_dockable_get_menu (dockable, &dialog_ui_path, &dialog_popup_data); /* an action callback may destroy both dockable and dockbook, so * reference them for pika_dockbook_menu_end() */ g_object_ref (dockable); g_object_set_data_full (G_OBJECT (dockable), PIKA_DOCKABLE_DETACH_REF_KEY, g_object_ref (dockbook), g_object_unref); if (dialog_ui_manager) pika_ui_manager_update (dialog_ui_manager, dialog_popup_data); pika_ui_manager_update (dockbook_ui_manager, dockable); pika_ui_manager_ui_popup_at_widget (dockbook_ui_manager, "/dockable-popup", dialog_ui_manager, dialog_ui_path, dockbook->p->menu_button, GDK_GRAVITY_WEST, GDK_GRAVITY_NORTH_EAST, NULL, (GDestroyNotify) pika_dockbook_menu_end, dockable); return TRUE; } static void pika_dockbook_menu_end (PikaDockable *dockable) { /* release pika_dockbook_show_menu()'s references */ g_object_set_data (G_OBJECT (dockable), PIKA_DOCKABLE_DETACH_REF_KEY, NULL); g_object_unref (dockable); } /* public functions */ GtkWidget * pika_dockbook_new (PikaMenuFactory *menu_factory) { PikaDockbook *dockbook; g_return_val_if_fail (PIKA_IS_MENU_FACTORY (menu_factory), NULL); dockbook = g_object_new (PIKA_TYPE_DOCKBOOK, NULL); dockbook->p->ui_manager = pika_menu_factory_get_manager (menu_factory, "", dockbook); pika_help_connect (GTK_WIDGET (dockbook), pika_dockbook_help_func, PIKA_HELP_DOCK, dockbook, NULL); return GTK_WIDGET (dockbook); } PikaDock * pika_dockbook_get_dock (PikaDockbook *dockbook) { g_return_val_if_fail (PIKA_IS_DOCKBOOK (dockbook), NULL); return dockbook->p->dock; } void pika_dockbook_set_dock (PikaDockbook *dockbook, PikaDock *dock) { PikaContext *context; g_return_if_fail (PIKA_IS_DOCKBOOK (dockbook)); g_return_if_fail (dock == NULL || PIKA_IS_DOCK (dock)); if (dockbook->p->dock && (context = pika_dock_get_context (dockbook->p->dock)) != NULL) { g_signal_handlers_disconnect_by_func (PIKA_GUI_CONFIG (context->pika->config), G_CALLBACK (pika_dockbook_style_updated), dockbook); } dockbook->p->dock = dock; if (dockbook->p->dock && (context = pika_dock_get_context (dockbook->p->dock)) != NULL) { g_signal_connect_object (PIKA_GUI_CONFIG (context->pika->config), "notify::theme", G_CALLBACK (pika_dockbook_style_updated), dockbook, G_CONNECT_AFTER | G_CONNECT_SWAPPED); g_signal_connect_object (PIKA_GUI_CONFIG (context->pika->config), "notify::override-theme-icon-size", G_CALLBACK (pika_dockbook_style_updated), dockbook, G_CONNECT_AFTER | G_CONNECT_SWAPPED); g_signal_connect_object (PIKA_GUI_CONFIG (context->pika->config), "notify::custom-icon-size", G_CALLBACK (pika_dockbook_style_updated), dockbook, G_CONNECT_AFTER | G_CONNECT_SWAPPED); } } /** * pika_dockbook_add_from_dialog_factory: * @dockbook: The #DockBook * @identifiers: The dockable identifier(s) * * Add a dockable from the dialog factory associated with the dockbook. **/ GtkWidget * pika_dockbook_add_from_dialog_factory (PikaDockbook *dockbook, const gchar *identifiers) { GtkWidget *dockable; PikaDock *dock; gchar *identifier; gchar *p; g_return_val_if_fail (PIKA_IS_DOCKBOOK (dockbook), NULL); g_return_val_if_fail (identifiers != NULL, NULL); identifier = g_strdup (identifiers); p = strchr (identifier, '|'); if (p) *p = '\0'; dock = pika_dockbook_get_dock (dockbook); dockable = pika_dialog_factory_dockable_new (pika_dock_get_dialog_factory (dock), dock, identifier, -1); g_free (identifier); /* Maybe pika_dialog_factory_dockable_new() returned an already * existing singleton dockable, so check if it already is * attached to a dockbook. */ if (dockable && ! pika_dockable_get_dockbook (PIKA_DOCKABLE (dockable))) gtk_notebook_append_page (GTK_NOTEBOOK (dockbook), dockable, NULL); return dockable; } /** * pika_dockbook_update_with_context: * @dockbook: * @context: * * Set @context on all dockables in @dockbook. **/ void pika_dockbook_update_with_context (PikaDockbook *dockbook, PikaContext *context) { GList *children = gtk_container_get_children (GTK_CONTAINER (dockbook)); GList *iter; for (iter = children; iter; iter = g_list_next (iter)) { PikaDockable *dockable = PIKA_DOCKABLE (iter->data); pika_dockable_set_context (dockable, context); } g_list_free (children); } GtkWidget * pika_dockbook_create_tab_widget (PikaDockbook *dockbook, PikaDockable *dockable) { GtkWidget *tab_widget; PikaDockWindow *dock_window; PikaAction *action = NULL; GtkIconSize tab_size = DEFAULT_TAB_ICON_SIZE; gtk_widget_style_get (GTK_WIDGET (dockbook), "tab-icon-size", &tab_size, NULL); tab_widget = pika_dockable_create_tab_widget (dockable, pika_dock_get_context (dockbook->p->dock), pika_dockable_get_tab_style (dockable), tab_size); if (PIKA_IS_VIEW (tab_widget)) { GtkWidget *event_box; event_box = gtk_event_box_new (); gtk_event_box_set_visible_window (GTK_EVENT_BOX (event_box), FALSE); gtk_event_box_set_above_child (GTK_EVENT_BOX (event_box), TRUE); gtk_container_add (GTK_CONTAINER (event_box), tab_widget); gtk_widget_show (tab_widget); tab_widget = event_box; } /* EEK */ dock_window = pika_dock_window_from_dock (dockbook->p->dock); if (dock_window && pika_dock_container_get_ui_manager (PIKA_DOCK_CONTAINER (dock_window))) { const gchar *dialog_id; dialog_id = g_object_get_data (G_OBJECT (dockable), "pika-dialog-identifier"); if (dialog_id) { PikaDockContainer *dock_container; PikaActionGroup *group; dock_container = PIKA_DOCK_CONTAINER (dock_window); group = pika_ui_manager_get_action_group (pika_dock_container_get_ui_manager (dock_container), "dialogs"); if (group) { GList *actions; GList *list; actions = pika_action_group_list_actions (group); for (list = actions; list; list = g_list_next (list)) { if (PIKA_IS_STRING_ACTION (list->data) && strstr (PIKA_STRING_ACTION (list->data)->value, dialog_id)) { action = list->data; break; } } g_list_free (actions); } } } if (action) pika_widget_set_accel_help (tab_widget, action); else pika_help_set_help_data (tab_widget, pika_dockable_get_blurb (dockable), pika_dockable_get_help_id (dockable)); return tab_widget; } /** * pika_dockable_set_drag_handler: * @dockable: * @handler: * * Set a drag handler that will be asked if it will handle drag events * before the dockbook handles the event itself. **/ void pika_dockbook_set_drag_handler (PikaDockbook *dockbook, PikaPanedBox *drag_handler) { g_return_if_fail (PIKA_IS_DOCKBOOK (dockbook)); dockbook->p->drag_handler = drag_handler; } void pika_dockbook_add_drag_callback (PikaDockbookDragCallback callback, gpointer data) { PikaDockbookDragCallbackData *callback_data; callback_data = g_slice_new (PikaDockbookDragCallbackData); callback_data->callback = callback; callback_data->data = data; drag_callbacks = g_list_prepend (drag_callbacks, callback_data); } void pika_dockbook_remove_drag_callback (PikaDockbookDragCallback callback, gpointer data) { GList *iter; iter = drag_callbacks; while (iter) { PikaDockbookDragCallbackData *callback_data = iter->data; GList *next = g_list_next (iter); if (callback_data->callback == callback && callback_data->data == data) { g_slice_free (PikaDockbookDragCallbackData, callback_data); drag_callbacks = g_list_delete_link (drag_callbacks, iter); } iter = next; } } /* private functions */ static void pika_dockbook_tab_locked_notify (PikaDockable *dockable, GParamSpec *pspec, PikaDockbook *dockbook) { gboolean locked = pika_dockable_get_locked (dockable); gtk_notebook_set_tab_reorderable (GTK_NOTEBOOK (dockbook), GTK_WIDGET (dockable), ! locked); gtk_notebook_set_tab_detachable (GTK_NOTEBOOK (dockbook), GTK_WIDGET (dockable), ! locked); } static void pika_dockbook_help_func (const gchar *help_id, gpointer help_data) { PikaDockbook *dockbook = PIKA_DOCKBOOK (help_data); GtkWidget *dockable; gint page_num; page_num = gtk_notebook_get_current_page (GTK_NOTEBOOK (dockbook)); dockable = gtk_notebook_get_nth_page (GTK_NOTEBOOK (dockbook), page_num); if (PIKA_IS_DOCKABLE (dockable)) pika_standard_help_func (pika_dockable_get_help_id (PIKA_DOCKABLE (dockable)), NULL); else pika_standard_help_func (PIKA_HELP_DOCK, NULL); }