/* * moopaned.c * * Copyright (C) 2004-2006 by Yevgen Muntyan * * 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. * * See COPYING file that comes with this distribution. */ #include "mooutils/moomarshals.h" #include "mooutils/moopaned.h" #include "mooutils/moostock.h" #include "mooutils/mooutils-misc.h" #include "mooutils/moocompat.h" #include #include #include #define MIN_PANE_SIZE 10 #define SPACING_IN_BUTTON 4 typedef enum { FOCUS_NONE = 0, FOCUS_CHILD, FOCUS_PANE, FOCUS_BUTTON } FocusPosition; typedef struct { MooPaneLabel *label; GtkWidget *frame; GtkWidget *handle; GtkWidget *child; GtkWidget *child_holder; GtkWidget *button; GtkWidget *sticky_button; GtkWidget *detach_button; /* XXX weak pointer */ GtkWidget *focus_child; GtkWidget *window; GtkWidget *keep_on_top_button; GtkWidget *window_child_holder; MooPaneParams *params; guint detachable : 1; } Pane; struct _MooPanedPrivate { MooPanePosition pane_position; GdkWindow *handle_window; GdkWindow *pane_window; gboolean raise_pane; /* need to raise pane window in size_allocate */ /* XXX weak pointer */ GtkWidget *focus_child; /* focused grandchild of bin->child */ Pane *focus_pane; FocusPosition focus; gboolean button_real_focus; /* button was focused by keyboard navigation */ gboolean dont_move_focus; /* do not try to move focus in open_pane/hide_pane */ Pane *current_pane; GSList *panes; gboolean close_on_child_focus; int position; gboolean button_box_visible; int button_box_size; gboolean handle_visible; int handle_size; gboolean pane_widget_visible; int pane_widget_size; gboolean enable_border; int border_size; gboolean sticky; gboolean handle_prelit; gboolean in_drag; int drag_start; gboolean enable_handle_drag; gboolean handle_in_drag; gboolean handle_button_pressed; int handle_drag_start_x; int handle_drag_start_y; GdkCursorType handle_cursor_type; guint enable_detaching : 1; guint params_changed_blocked : 1; }; static void moo_paned_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); static void moo_paned_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); static GObject *moo_paned_constructor (GType type, guint n_construct_properties, GObjectConstructParam *construct_properties); static void moo_paned_destroy (GtkObject *object); static void moo_paned_realize (GtkWidget *widget); static void moo_paned_unrealize (GtkWidget *widget); static void moo_paned_map (GtkWidget *widget); static void moo_paned_unmap (GtkWidget *widget); static gboolean moo_paned_focus (GtkWidget *widget, GtkDirectionType direction); static void moo_paned_set_focus_child (GtkContainer *container, GtkWidget *widget); static void moo_paned_size_request (GtkWidget *widget, GtkRequisition *requisition); static void moo_paned_size_allocate (GtkWidget *widget, GtkAllocation *allocation); static gboolean moo_paned_expose (GtkWidget *widget, GdkEventExpose *event); static gboolean moo_paned_motion (GtkWidget *widget, GdkEventMotion *event); static gboolean moo_paned_enter (GtkWidget *widget, GdkEventCrossing *event); static gboolean moo_paned_leave (GtkWidget *widget, GdkEventCrossing *event); static gboolean moo_paned_button_press (GtkWidget *widget, GdkEventButton *event); static gboolean moo_paned_button_release (GtkWidget *widget, GdkEventButton *event); static gboolean moo_paned_key_press (GtkWidget *widget, GdkEventKey *event); static int pane_index (MooPaned *paned, Pane *pane); static Pane *get_nth_pane (MooPaned *paned, guint index_); static void moo_paned_open_pane_real (MooPaned *paned, guint index); static void moo_paned_hide_pane_real (MooPaned *paned); static void moo_paned_attach_pane_real (MooPaned *paned, guint index); static void moo_paned_detach_pane_real (MooPaned *paned, guint index); static void moo_paned_set_pane_size_real(MooPaned *paned, int size); static void moo_paned_forall (GtkContainer *container, gboolean include_internals, GtkCallback callback, gpointer callback_data); static void moo_paned_add (GtkContainer *container, GtkWidget *widget); static void moo_paned_remove (GtkContainer *container, GtkWidget *widget); static void realize_handle (MooPaned *paned); static void realize_pane (MooPaned *paned); static void draw_handle (MooPaned *paned, GdkEventExpose *event); static void draw_border (MooPaned *paned, GdkEventExpose *event); static void button_box_visible_notify (MooPaned *paned); static void button_toggled (GtkToggleButton *button, MooPaned *paned); static void sticky_button_toggled (GtkToggleButton *button, MooPaned *paned); static void detach_button_clicked (GtkWidget *button, MooPaned *paned); static void attach_button_clicked (GtkWidget *button, MooPaned *paned); static void keep_on_top_button_toggled (GtkToggleButton *button, MooPaned *paned); static gboolean handle_button_press (GtkWidget *widget, GdkEventButton *event, MooPaned *paned); static gboolean handle_button_release (GtkWidget *widget, GdkEventButton *event, MooPaned *paned); static gboolean handle_motion (GtkWidget *widget, GdkEventMotion *event, MooPaned *paned); static gboolean handle_expose (GtkWidget *widget, GdkEventExpose *event, MooPaned *paned); static void handle_realize (GtkWidget *widget, MooPaned *paned); static void moo_paned_set_handle_cursor_type (MooPaned *paned, GdkCursorType cursor_type, gboolean really_set); static void moo_paned_set_enable_detaching (MooPaned *paned, gboolean enable); static gboolean pane_window_configure (GtkWindow *window, GdkEventConfigure *event, MooPaned *paned); static Pane *pane_new (MooPaneLabel *label); static void pane_free (Pane *pane); static void pane_params_changed (MooPaned *paned, Pane *pane); static void block_params_changed (MooPaned *paned); static void unblock_params_changed (MooPaned *paned); /* MOO_TYPE_PANED */ G_DEFINE_TYPE (MooPaned, moo_paned, GTK_TYPE_BIN) enum { PROP_0, PROP_PANE_POSITION, PROP_CLOSE_PANE_ON_CHILD_FOCUS, PROP_STICKY_PANE, PROP_ENABLE_HANDLE_DRAG, PROP_HANDLE_CURSOR_TYPE, PROP_ENABLE_DETACHING, PROP_ENABLE_BORDER }; enum { OPEN_PANE, HIDE_PANE, ATTACH_PANE, DETACH_PANE, SET_PANE_SIZE, HANDLE_DRAG_START, HANDLE_DRAG_MOTION, HANDLE_DRAG_END, PANE_PARAMS_CHANGED, NUM_SIGNALS }; static guint signals[NUM_SIGNALS]; static void moo_paned_class_init (MooPanedClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GtkObjectClass *gtkobject_class = GTK_OBJECT_CLASS (klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass); moo_create_stock_items (); g_type_class_add_private (klass, sizeof (MooPanedPrivate)); gobject_class->set_property = moo_paned_set_property; gobject_class->get_property = moo_paned_get_property; gobject_class->constructor = moo_paned_constructor; gtkobject_class->destroy = moo_paned_destroy; widget_class->realize = moo_paned_realize; widget_class->unrealize = moo_paned_unrealize; widget_class->map = moo_paned_map; widget_class->unmap = moo_paned_unmap; widget_class->expose_event = moo_paned_expose; widget_class->size_request = moo_paned_size_request; widget_class->size_allocate = moo_paned_size_allocate; widget_class->motion_notify_event = moo_paned_motion; widget_class->enter_notify_event = moo_paned_enter; widget_class->leave_notify_event = moo_paned_leave; widget_class->button_press_event = moo_paned_button_press; widget_class->button_release_event = moo_paned_button_release; widget_class->focus = moo_paned_focus; widget_class->key_press_event = moo_paned_key_press; container_class->forall = moo_paned_forall; container_class->set_focus_child = moo_paned_set_focus_child; container_class->remove = moo_paned_remove; container_class->add = moo_paned_add; klass->open_pane = moo_paned_open_pane_real; klass->hide_pane = moo_paned_hide_pane_real; klass->attach_pane = moo_paned_attach_pane_real; klass->detach_pane = moo_paned_detach_pane_real; klass->set_pane_size = moo_paned_set_pane_size_real; g_object_class_install_property (gobject_class, PROP_PANE_POSITION, g_param_spec_enum ("pane-position", "pane-position", "pane-position", MOO_TYPE_PANE_POSITION, MOO_PANE_POS_LEFT, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (gobject_class, PROP_CLOSE_PANE_ON_CHILD_FOCUS, g_param_spec_boolean ("close-pane-on-child-focus", "close-pane-on-child-focus", "close-pane-on-child-focus", TRUE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); g_object_class_install_property (gobject_class, PROP_STICKY_PANE, g_param_spec_boolean ("sticky-pane", "sticky-pane", "sticky-pane", FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); g_object_class_install_property (gobject_class, PROP_ENABLE_HANDLE_DRAG, g_param_spec_boolean ("enable-handle-drag", "enable-handle-drag", "enable-handle-drag", FALSE, G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, PROP_ENABLE_DETACHING, g_param_spec_boolean ("enable-detaching", "enable-detaching", "enable-detaching", FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); g_object_class_install_property (gobject_class, PROP_HANDLE_CURSOR_TYPE, g_param_spec_enum ("handle-cursor-type", "handle-cursor-type", "handle-cursor-type", GDK_TYPE_CURSOR_TYPE, GDK_HAND2, G_PARAM_CONSTRUCT | G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, PROP_ENABLE_BORDER, g_param_spec_boolean ("enable-border", "enable-border", "enable-border", TRUE, G_PARAM_CONSTRUCT | G_PARAM_READWRITE)); gtk_widget_class_install_style_property (widget_class, g_param_spec_int ("handle-size", "handle-size", "handle-size", 0, G_MAXINT, 5, G_PARAM_READABLE)); gtk_widget_class_install_style_property (widget_class, g_param_spec_int ("button-spacing", "button-spacing", "button-spacing", 0, G_MAXINT, 0, G_PARAM_READABLE)); signals[OPEN_PANE] = g_signal_new ("open-pane", G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (MooPanedClass, open_pane), NULL, NULL, _moo_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); signals[HIDE_PANE] = g_signal_new ("hide-pane", G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (MooPanedClass, hide_pane), NULL, NULL, _moo_marshal_VOID__VOID, G_TYPE_NONE, 0); signals[ATTACH_PANE] = g_signal_new ("attach-pane", G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (MooPanedClass, attach_pane), NULL, NULL, _moo_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); signals[DETACH_PANE] = g_signal_new ("detach-pane", G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (MooPanedClass, detach_pane), NULL, NULL, _moo_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); signals[SET_PANE_SIZE] = g_signal_new ("set-pane-size", G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (MooPanedClass, set_pane_size), NULL, NULL, _moo_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT); signals[HANDLE_DRAG_START] = g_signal_new ("handle-drag-start", G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (MooPanedClass, handle_drag_start), NULL, NULL, _moo_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GTK_TYPE_WIDGET); signals[HANDLE_DRAG_MOTION] = g_signal_new ("handle-drag-motion", G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (MooPanedClass, handle_drag_motion), NULL, NULL, _moo_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GTK_TYPE_WIDGET); signals[HANDLE_DRAG_END] = g_signal_new ("handle-drag-end", G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (MooPanedClass, handle_drag_end), NULL, NULL, _moo_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GTK_TYPE_WIDGET); signals[PANE_PARAMS_CHANGED] = g_signal_new ("pane-params-changed", G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (MooPanedClass, pane_params_changed), NULL, NULL, _moo_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); } static void moo_paned_init (MooPaned *paned) { GTK_WIDGET_UNSET_FLAGS (paned, GTK_NO_WINDOW); paned->priv = G_TYPE_INSTANCE_GET_PRIVATE (paned, MOO_TYPE_PANED, MooPanedPrivate); paned->button_box = NULL; paned->priv->pane_position = -1; paned->priv->handle_window = NULL; paned->priv->pane_window = NULL; paned->priv->current_pane = NULL; paned->priv->panes = NULL; paned->priv->enable_border = TRUE; paned->priv->button_box_visible = FALSE; paned->priv->button_box_size = 0; paned->priv->handle_visible = FALSE; paned->priv->handle_size = 0; paned->priv->pane_widget_visible = FALSE; paned->priv->pane_widget_size = 0; paned->priv->sticky = FALSE; paned->priv->position = -1; paned->priv->handle_prelit = FALSE; paned->priv->in_drag = FALSE; paned->priv->drag_start = -1; } static GObject* moo_paned_constructor (GType type, guint n_construct_properties, GObjectConstructParam *construct_properties) { GObject *object; MooPaned *paned; int button_spacing; object = G_OBJECT_CLASS(moo_paned_parent_class)->constructor (type, n_construct_properties, construct_properties); paned = MOO_PANED (object); gtk_widget_style_get (GTK_WIDGET (paned), "button-spacing", &button_spacing, NULL); switch (paned->priv->pane_position) { case MOO_PANE_POS_LEFT: case MOO_PANE_POS_RIGHT: paned->button_box = gtk_vbox_new (FALSE, button_spacing); break; case MOO_PANE_POS_TOP: case MOO_PANE_POS_BOTTOM: paned->button_box = gtk_hbox_new (FALSE, button_spacing); break; default: g_warning ("%s: invalid 'pane-position' property value '%u'," "falling back to MOO_PANE_POS_LEFT", G_STRLOC, paned->priv->pane_position); paned->priv->pane_position = MOO_PANE_POS_LEFT; paned->button_box = gtk_vbox_new (FALSE, button_spacing); break; } gtk_object_sink (g_object_ref (paned->button_box)); gtk_widget_set_parent (paned->button_box, GTK_WIDGET (paned)); gtk_widget_show (paned->button_box); g_signal_connect_swapped (paned->button_box, "notify::visible", G_CALLBACK (button_box_visible_notify), paned); return object; } static void moo_paned_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MooPaned *paned = MOO_PANED (object); switch (prop_id) { case PROP_PANE_POSITION: paned->priv->pane_position = g_value_get_enum (value); break; case PROP_CLOSE_PANE_ON_CHILD_FOCUS: paned->priv->close_on_child_focus = g_value_get_boolean (value); g_object_notify (object, "close-pane-on-child-focus"); break; case PROP_ENABLE_BORDER: paned->priv->enable_border = g_value_get_boolean (value); gtk_widget_queue_resize (GTK_WIDGET (paned)); g_object_notify (object, "enable_border"); break; case PROP_STICKY_PANE: moo_paned_set_sticky_pane (paned, g_value_get_boolean (value)); break; case PROP_ENABLE_HANDLE_DRAG: paned->priv->enable_handle_drag = g_value_get_boolean (value); if (!paned->priv->enable_handle_drag) moo_paned_set_handle_cursor_type (paned, 0, FALSE); else moo_paned_set_handle_cursor_type (paned, paned->priv->handle_cursor_type, TRUE); g_object_notify (object, "enable-handle-drag"); break; case PROP_ENABLE_DETACHING: moo_paned_set_enable_detaching (paned, g_value_get_boolean (value)); break; case PROP_HANDLE_CURSOR_TYPE: if (paned->priv->enable_handle_drag) moo_paned_set_handle_cursor_type (paned, g_value_get_enum (value), TRUE); else paned->priv->handle_cursor_type = g_value_get_enum (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void moo_paned_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { MooPaned *paned = MOO_PANED (object); switch (prop_id) { case PROP_PANE_POSITION: g_value_set_enum (value, paned->priv->pane_position); break; case PROP_CLOSE_PANE_ON_CHILD_FOCUS: g_value_set_boolean (value, paned->priv->close_on_child_focus); break; case PROP_STICKY_PANE: g_value_set_boolean (value, paned->priv->sticky); break; case PROP_ENABLE_HANDLE_DRAG: g_value_set_boolean (value, paned->priv->enable_handle_drag); break; case PROP_ENABLE_BORDER: g_value_set_boolean (value, paned->priv->enable_border); break; case PROP_ENABLE_DETACHING: g_value_set_boolean (value, paned->priv->enable_detaching ? TRUE : FALSE); break; case PROP_HANDLE_CURSOR_TYPE: g_value_set_enum (value, paned->priv->handle_cursor_type); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void moo_paned_destroy (GtkObject *object) { GSList *l; MooPaned *paned = MOO_PANED (object); for (l = paned->priv->panes; l != NULL; l = l->next) { Pane *pane = l->data; gtk_widget_destroy (pane->child); pane->child = NULL; if (pane->window) { gtk_widget_destroy (pane->window); pane->window = NULL; } } GTK_OBJECT_CLASS(moo_paned_parent_class)->destroy (object); for (l = paned->priv->panes; l != NULL; l = l->next) pane_free (l->data); g_slist_free (paned->priv->panes); paned->priv->panes = NULL; } GtkWidget* moo_paned_new (MooPanePosition pane_position) { return GTK_WIDGET (g_object_new (MOO_TYPE_PANED, "pane-position", pane_position, NULL)); } static void moo_paned_realize (GtkWidget *widget) { static GdkWindowAttr attributes; gint attributes_mask; MooPaned *paned; paned = MOO_PANED (widget); GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED); attributes.x = widget->allocation.x; attributes.y = widget->allocation.y; attributes.width = widget->allocation.width; attributes.height = widget->allocation.height; attributes.window_type = GDK_WINDOW_CHILD; attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK; attributes.visual = gtk_widget_get_visual (widget); attributes.colormap = gtk_widget_get_colormap (widget); attributes.wclass = GDK_INPUT_OUTPUT; attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP; widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask); gdk_window_set_user_data (widget->window, widget); widget->style = gtk_style_attach (widget->style, widget->window); gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL); realize_pane (paned); } static void realize_handle (MooPaned *paned) { static GdkWindowAttr attributes; gint attributes_mask; GtkWidget *widget = GTK_WIDGET (paned); switch (paned->priv->pane_position) { case MOO_PANE_POS_LEFT: case MOO_PANE_POS_RIGHT: attributes.y = 0; attributes.width = paned->priv->handle_size; attributes.height = widget->allocation.height; break; case MOO_PANE_POS_TOP: case MOO_PANE_POS_BOTTOM: attributes.x = 0; attributes.width = widget->allocation.width; attributes.height = paned->priv->handle_size; break; } switch (paned->priv->pane_position) { case MOO_PANE_POS_LEFT: attributes.x = paned->priv->pane_widget_size; break; case MOO_PANE_POS_RIGHT: attributes.x = 0; break; case MOO_PANE_POS_TOP: attributes.y = paned->priv->pane_widget_size; break; case MOO_PANE_POS_BOTTOM: attributes.y = 0; break; } attributes.window_type = GDK_WINDOW_CHILD; attributes.event_mask = gtk_widget_get_events (widget) | GDK_POINTER_MOTION_HINT_MASK | GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_EXPOSURE_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK; attributes.visual = gtk_widget_get_visual (widget); attributes.colormap = gtk_widget_get_colormap (widget); attributes.wclass = GDK_INPUT_OUTPUT; switch (paned->priv->pane_position) { case MOO_PANE_POS_LEFT: case MOO_PANE_POS_RIGHT: attributes.cursor = gdk_cursor_new (GDK_SB_H_DOUBLE_ARROW); break; case MOO_PANE_POS_TOP: case MOO_PANE_POS_BOTTOM: attributes.cursor = gdk_cursor_new (GDK_SB_V_DOUBLE_ARROW); break; } attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP | GDK_WA_CURSOR; paned->priv->handle_window = gdk_window_new (paned->priv->pane_window, &attributes, attributes_mask); gdk_window_set_user_data (paned->priv->handle_window, widget); gtk_style_set_background (widget->style, paned->priv->handle_window, GTK_STATE_NORMAL); gdk_cursor_unref (attributes.cursor); } static void get_pane_window_rect (MooPaned *paned, GdkRectangle *rect) { switch (paned->priv->pane_position) { case MOO_PANE_POS_LEFT: case MOO_PANE_POS_RIGHT: rect->width = paned->priv->pane_widget_size + paned->priv->handle_size; rect->height = GTK_WIDGET(paned)->allocation.height; rect->y = 0; break; case MOO_PANE_POS_TOP: case MOO_PANE_POS_BOTTOM: rect->height = paned->priv->pane_widget_size + paned->priv->handle_size; rect->width = GTK_WIDGET(paned)->allocation.width; rect->x = 0; break; } switch (paned->priv->pane_position) { case MOO_PANE_POS_LEFT: rect->x = paned->priv->button_box_size; break; case MOO_PANE_POS_RIGHT: rect->x = GTK_WIDGET(paned)->allocation.width - rect->width - paned->priv->button_box_size; break; case MOO_PANE_POS_TOP: rect->y = paned->priv->button_box_size; break; case MOO_PANE_POS_BOTTOM: rect->y = GTK_WIDGET(paned)->allocation.height - rect->height - paned->priv->button_box_size; break; } } static void realize_pane (MooPaned *paned) { static GdkWindowAttr attributes; gint attributes_mask; GtkWidget *widget = GTK_WIDGET (paned); GdkRectangle rect; get_pane_window_rect (paned, &rect); attributes.x = rect.x; attributes.y = rect.y; attributes.width = rect.width; attributes.height = rect.height; attributes.window_type = GDK_WINDOW_CHILD; attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK; attributes.visual = gtk_widget_get_visual (widget); attributes.colormap = gtk_widget_get_colormap (widget); attributes.wclass = GDK_INPUT_OUTPUT; attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP; paned->priv->pane_window = gdk_window_new (widget->window, &attributes, attributes_mask); gdk_window_set_user_data (paned->priv->pane_window, widget); gtk_style_set_background (widget->style, paned->priv->pane_window, GTK_STATE_NORMAL); realize_handle (paned); } static void moo_paned_unrealize (GtkWidget *widget) { MooPaned *paned = MOO_PANED (widget); if (paned->priv->handle_window) { gdk_window_set_user_data (paned->priv->handle_window, NULL); gdk_window_destroy (paned->priv->handle_window); paned->priv->handle_window = NULL; paned->priv->handle_visible = FALSE; paned->priv->handle_size = 0; } if (paned->priv->pane_window) { gdk_window_set_user_data (paned->priv->pane_window, NULL); gdk_window_destroy (paned->priv->pane_window); paned->priv->pane_window = NULL; paned->priv->pane_widget_visible = FALSE; paned->priv->pane_widget_size = 0; } GTK_WIDGET_CLASS(moo_paned_parent_class)->unrealize (widget); } static void add_button_box_requisition (MooPaned *paned, GtkRequisition *requisition, GtkRequisition *child_requisition) { switch (paned->priv->pane_position) { case MOO_PANE_POS_LEFT: case MOO_PANE_POS_RIGHT: requisition->width += child_requisition->width; requisition->height = MAX (child_requisition->height, requisition->height); paned->priv->button_box_size = child_requisition->width; break; case MOO_PANE_POS_TOP: case MOO_PANE_POS_BOTTOM: requisition->height += child_requisition->height; requisition->width = MAX (child_requisition->width, requisition->width); paned->priv->button_box_size = child_requisition->height; break; } } static void add_handle_requisition (MooPaned *paned, GtkRequisition *requisition) { gtk_widget_style_get (GTK_WIDGET (paned), "handle_size", &paned->priv->handle_size, NULL); switch (paned->priv->pane_position) { case MOO_PANE_POS_LEFT: case MOO_PANE_POS_RIGHT: requisition->width += paned->priv->handle_size; break; case MOO_PANE_POS_TOP: case MOO_PANE_POS_BOTTOM: requisition->height += paned->priv->handle_size; break; } } static void add_pane_widget_requisition (MooPaned *paned, GtkRequisition *requisition, GtkRequisition *child_requisition) { switch (paned->priv->pane_position) { case MOO_PANE_POS_LEFT: case MOO_PANE_POS_RIGHT: requisition->height = MAX (child_requisition->height, requisition->height); if (paned->priv->sticky) { requisition->width += child_requisition->width; } else { requisition->width = MAX (child_requisition->width + paned->priv->button_box_size + paned->priv->handle_size, requisition->width); } break; case MOO_PANE_POS_TOP: case MOO_PANE_POS_BOTTOM: requisition->width = MAX (child_requisition->width, requisition->width); if (paned->priv->sticky) { requisition->height += child_requisition->height; } else { requisition->height = MAX (child_requisition->height + paned->priv->button_box_size + paned->priv->handle_size, requisition->height); } break; } switch (paned->priv->pane_position) { case MOO_PANE_POS_LEFT: case MOO_PANE_POS_RIGHT: paned->priv->pane_widget_size = MAX (paned->priv->position, child_requisition->width); paned->priv->position = paned->priv->pane_widget_size; break; case MOO_PANE_POS_TOP: case MOO_PANE_POS_BOTTOM: paned->priv->pane_widget_size = MAX (paned->priv->position, child_requisition->height); paned->priv->position = paned->priv->pane_widget_size; break; } } static void moo_paned_size_request (GtkWidget *widget, GtkRequisition *requisition) { GtkBin *bin = GTK_BIN (widget); MooPaned *paned = MOO_PANED (widget); GtkRequisition child_requisition; requisition->width = 0; requisition->height = 0; if (bin->child && GTK_WIDGET_VISIBLE (bin->child)) { gtk_widget_size_request (bin->child, &child_requisition); requisition->width += child_requisition.width; requisition->height += child_requisition.height; } if (paned->priv->button_box_visible) { gtk_widget_size_request (paned->button_box, &child_requisition); add_button_box_requisition (paned, requisition, &child_requisition); } else { paned->priv->button_box_size = 0; } if (paned->priv->handle_visible) add_handle_requisition (paned, requisition); else paned->priv->handle_size = 0; if (paned->priv->pane_widget_visible) { gtk_widget_size_request (paned->priv->current_pane->frame, &child_requisition); add_pane_widget_requisition (paned, requisition, &child_requisition); } else { paned->priv->pane_widget_size = 0; } if (paned->priv->enable_border) { switch (paned->priv->pane_position) { case MOO_PANE_POS_LEFT: case MOO_PANE_POS_RIGHT: paned->priv->border_size = widget->style->xthickness; requisition->width += paned->priv->border_size; break; case MOO_PANE_POS_TOP: case MOO_PANE_POS_BOTTOM: paned->priv->border_size = widget->style->ythickness; requisition->height += paned->priv->border_size; break; } } else { paned->priv->border_size = 0; } } static void get_pane_widget_allocation (MooPaned *paned, GtkAllocation *allocation) { switch (paned->priv->pane_position) { case MOO_PANE_POS_LEFT: allocation->x = 0; allocation->y = 0; allocation->width = paned->priv->pane_widget_size; allocation->height = GTK_WIDGET(paned)->allocation.height; break; case MOO_PANE_POS_RIGHT: allocation->x = paned->priv->handle_size; allocation->y = 0; allocation->width = paned->priv->pane_widget_size; allocation->height = GTK_WIDGET(paned)->allocation.height; break; case MOO_PANE_POS_TOP: allocation->x = 0; allocation->y = 0; allocation->width = GTK_WIDGET(paned)->allocation.width; allocation->height = paned->priv->pane_widget_size; break; case MOO_PANE_POS_BOTTOM: allocation->x = 0; allocation->y = paned->priv->handle_size; allocation->width = GTK_WIDGET(paned)->allocation.width; allocation->height = paned->priv->pane_widget_size; break; } } static void get_button_box_allocation (MooPaned *paned, GtkAllocation *allocation) { switch (paned->priv->pane_position) { case MOO_PANE_POS_LEFT: case MOO_PANE_POS_RIGHT: allocation->y = 0; allocation->height = GTK_WIDGET(paned)->allocation.height; allocation->width = paned->priv->button_box_size; break; case MOO_PANE_POS_TOP: case MOO_PANE_POS_BOTTOM: allocation->x = 0; allocation->width = GTK_WIDGET(paned)->allocation.width; allocation->height = paned->priv->button_box_size; break; } switch (paned->priv->pane_position) { case MOO_PANE_POS_LEFT: allocation->x = 0; break; case MOO_PANE_POS_RIGHT: allocation->x = GTK_WIDGET(paned)->allocation.width - allocation->width; break; case MOO_PANE_POS_TOP: allocation->y = 0; break; case MOO_PANE_POS_BOTTOM: allocation->y = GTK_WIDGET(paned)->allocation.height - allocation->height; break; } } static void get_bin_child_allocation (MooPaned *paned, GtkAllocation *allocation) { switch (paned->priv->pane_position) { case MOO_PANE_POS_LEFT: case MOO_PANE_POS_RIGHT: allocation->y = 0; allocation->height = GTK_WIDGET(paned)->allocation.height; allocation->width = GTK_WIDGET(paned)->allocation.width - paned->priv->button_box_size - paned->priv->border_size; break; case MOO_PANE_POS_TOP: case MOO_PANE_POS_BOTTOM: allocation->x = 0; allocation->width = GTK_WIDGET(paned)->allocation.width; allocation->height = GTK_WIDGET(paned)->allocation.height - paned->priv->button_box_size - paned->priv->border_size; break; } switch (paned->priv->pane_position) { case MOO_PANE_POS_LEFT: allocation->x = paned->priv->button_box_size + paned->priv->border_size; break; case MOO_PANE_POS_RIGHT: allocation->x = 0; break; case MOO_PANE_POS_TOP: allocation->y = paned->priv->button_box_size + paned->priv->border_size; break; case MOO_PANE_POS_BOTTOM: allocation->y = 0; break; } if (paned->priv->sticky) { int add = paned->priv->handle_size + paned->priv->pane_widget_size; switch (paned->priv->pane_position) { case MOO_PANE_POS_LEFT: allocation->x += add; allocation->width -= add; break; case MOO_PANE_POS_RIGHT: allocation->width -= add; break; case MOO_PANE_POS_TOP: allocation->y += add; allocation->height -= add; break; case MOO_PANE_POS_BOTTOM: allocation->height -= add; break; } } } static void clamp_handle_size (MooPaned *paned) { switch (paned->priv->pane_position) { case MOO_PANE_POS_LEFT: case MOO_PANE_POS_RIGHT: paned->priv->handle_size = CLAMP (paned->priv->handle_size, 0, GTK_WIDGET(paned)->allocation.width); break; case MOO_PANE_POS_TOP: case MOO_PANE_POS_BOTTOM: paned->priv->handle_size = CLAMP (paned->priv->handle_size, 0, GTK_WIDGET(paned)->allocation.height); break; } } static void clamp_button_box_size (MooPaned *paned) { switch (paned->priv->pane_position) { case MOO_PANE_POS_LEFT: case MOO_PANE_POS_RIGHT: paned->priv->button_box_size = CLAMP (paned->priv->button_box_size, 0, GTK_WIDGET(paned)->allocation.width - paned->priv->handle_size); break; case MOO_PANE_POS_TOP: case MOO_PANE_POS_BOTTOM: paned->priv->button_box_size = CLAMP (paned->priv->button_box_size, 0, GTK_WIDGET(paned)->allocation.height - paned->priv->handle_size); break; } } static void clamp_child_requisition (MooPaned *paned, GtkRequisition *requisition) { switch (paned->priv->pane_position) { case MOO_PANE_POS_LEFT: case MOO_PANE_POS_RIGHT: requisition->width = CLAMP (requisition->width, 0, GTK_WIDGET(paned)->allocation.width - paned->priv->handle_size - paned->priv->button_box_size - paned->priv->border_size); break; case MOO_PANE_POS_TOP: case MOO_PANE_POS_BOTTOM: requisition->height = CLAMP (requisition->height, 0, GTK_WIDGET(paned)->allocation.height - paned->priv->handle_size - paned->priv->button_box_size - paned->priv->border_size); break; } } static void clamp_pane_widget_size (MooPaned *paned, GtkRequisition *child_requisition) { int min_size; int max_size = 0; switch (paned->priv->pane_position) { case MOO_PANE_POS_LEFT: case MOO_PANE_POS_RIGHT: max_size = GTK_WIDGET(paned)->allocation.width - paned->priv->handle_size - paned->priv->button_box_size; if (paned->priv->sticky) max_size -= child_requisition->width; break; case MOO_PANE_POS_TOP: case MOO_PANE_POS_BOTTOM: max_size = GTK_WIDGET(paned)->allocation.height - paned->priv->handle_size - paned->priv->button_box_size; if (paned->priv->sticky) max_size -= child_requisition->height; break; } min_size = CLAMP (MIN_PANE_SIZE, 0, max_size); paned->priv->pane_widget_size = CLAMP (paned->priv->pane_widget_size, min_size, max_size); } static void get_handle_window_rect (MooPaned *paned, GdkRectangle *rect) { switch (paned->priv->pane_position) { case MOO_PANE_POS_LEFT: case MOO_PANE_POS_RIGHT: rect->y = 0; rect->width = paned->priv->handle_size; rect->height = GTK_WIDGET(paned)->allocation.height; break; case MOO_PANE_POS_TOP: case MOO_PANE_POS_BOTTOM: rect->x = 0; rect->height = paned->priv->handle_size; rect->width = GTK_WIDGET(paned)->allocation.width; break; } switch (paned->priv->pane_position) { case MOO_PANE_POS_LEFT: rect->x = paned->priv->pane_widget_size; break; case MOO_PANE_POS_RIGHT: rect->x = 0; break; case MOO_PANE_POS_TOP: rect->y = paned->priv->pane_widget_size; break; case MOO_PANE_POS_BOTTOM: rect->y = 0; break; } } static void moo_paned_size_allocate (GtkWidget *widget, GtkAllocation *allocation) { GtkBin *bin; MooPaned *paned; GtkAllocation child_allocation; GtkRequisition child_requisition = {0, 0}; widget->allocation = *allocation; bin = GTK_BIN (widget); paned = MOO_PANED (widget); if (!paned->priv->handle_visible) paned->priv->handle_size = 0; if (!paned->priv->button_box_visible) paned->priv->button_box_size = 0; if (!paned->priv->pane_widget_visible) paned->priv->pane_widget_size = 0; if (bin->child && GTK_WIDGET_VISIBLE (bin->child)) gtk_widget_get_child_requisition (bin->child, &child_requisition); if (paned->priv->handle_visible) clamp_handle_size (paned); if (paned->priv->button_box_visible) clamp_button_box_size (paned); clamp_child_requisition (paned, &child_requisition); if (paned->priv->pane_widget_visible) { clamp_pane_widget_size (paned, &child_requisition); paned->priv->position = paned->priv->pane_widget_size; } if (GTK_WIDGET_REALIZED (widget)) gdk_window_move_resize (widget->window, allocation->x, allocation->y, allocation->width, allocation->height); if (paned->priv->button_box_visible) { get_button_box_allocation (paned, &child_allocation); gtk_widget_size_allocate (paned->button_box, &child_allocation); } if (bin->child) { get_bin_child_allocation (paned, &child_allocation); gtk_widget_size_allocate (bin->child, &child_allocation); } if (GTK_WIDGET_REALIZED (widget)) { GdkRectangle rect; if (paned->priv->pane_widget_visible) { get_pane_window_rect (paned, &rect); gdk_window_move_resize (paned->priv->pane_window, rect.x, rect.y, rect.width, rect.height); #if 0 if (paned->priv->raise_pane) #endif { gdk_window_raise (paned->priv->pane_window); paned->priv->raise_pane = FALSE; } } if (paned->priv->handle_visible) { get_handle_window_rect (paned, &rect); gdk_window_move_resize (paned->priv->handle_window, rect.x, rect.y, rect.width, rect.height); } } if (paned->priv->pane_widget_visible) { get_pane_widget_allocation (paned, &child_allocation); gtk_widget_size_allocate (paned->priv->current_pane->frame, &child_allocation); } } static void moo_paned_map (GtkWidget *widget) { MooPaned *paned = MOO_PANED (widget); gdk_window_show (widget->window); GTK_WIDGET_CLASS(moo_paned_parent_class)->map (widget); if (paned->priv->handle_visible) { gdk_window_show (paned->priv->pane_window); gdk_window_show (paned->priv->handle_window); } } static void moo_paned_unmap (GtkWidget *widget) { MooPaned *paned = MOO_PANED (widget); if (paned->priv->handle_window) gdk_window_hide (paned->priv->handle_window); if (paned->priv->pane_window) gdk_window_hide (paned->priv->pane_window); GTK_WIDGET_CLASS(moo_paned_parent_class)->unmap (widget); } static void moo_paned_forall (GtkContainer *container, gboolean include_internals, GtkCallback callback, gpointer callback_data) { MooPaned *paned = MOO_PANED (container); GtkBin *bin = GTK_BIN (container); GSList *l; if (bin->child) callback (bin->child, callback_data); if (include_internals) { callback (paned->button_box, callback_data); for (l = paned->priv->panes; l != NULL; l = l->next) callback (((Pane*)l->data)->frame, callback_data); } } static gboolean moo_paned_expose (GtkWidget *widget, GdkEventExpose *event) { MooPaned *paned = MOO_PANED (widget); if (paned->priv->button_box_visible) gtk_container_propagate_expose (GTK_CONTAINER (paned), paned->button_box, event); if (GTK_BIN(paned)->child && GTK_WIDGET_DRAWABLE (GTK_BIN(paned)->child)) gtk_container_propagate_expose (GTK_CONTAINER (paned), GTK_BIN(paned)->child, event); if (paned->priv->pane_widget_visible) gtk_container_propagate_expose (GTK_CONTAINER (paned), paned->priv->current_pane->frame, event); if (paned->priv->handle_visible && event->window == paned->priv->handle_window) draw_handle (paned, event); if (paned->priv->button_box_visible && !paned->priv->pane_widget_visible && paned->priv->border_size && event->window == widget->window) draw_border (paned, event); return TRUE; } #if 0 /* TODO */ static GdkEventExpose *clip_bin_child_event (MooPaned *paned, GdkEventExpose *event) { GtkAllocation child_alloc; GdkRegion *child_rect; GdkEventExpose *child_event; get_bin_child_allocation (paned, &child_alloc); child_rect = gdk_region_rectangle ((GdkRectangle*) &child_alloc); child_event = (GdkEventExpose*) gdk_event_copy ((GdkEvent*) event); gdk_region_intersect (child_event->region, child_rect); gdk_region_get_clipbox (child_event->region, &child_event->area); gdk_region_destroy (child_rect); return child_event; } #endif static void moo_paned_add (GtkContainer *container, GtkWidget *child) { GtkBin *bin = GTK_BIN (container); g_return_if_fail (GTK_IS_WIDGET (child)); if (bin->child != NULL) { g_warning ("Attempting to add a widget with type %s to a %s, " "but as a GtkBin subclass a %s can only contain one widget at a time; " "it already contains a widget of type %s", g_type_name (G_OBJECT_TYPE (child)), g_type_name (G_OBJECT_TYPE (bin)), g_type_name (G_OBJECT_TYPE (bin)), g_type_name (G_OBJECT_TYPE (bin->child))); return; } gtk_widget_set_parent (child, GTK_WIDGET (bin)); bin->child = child; } static void moo_paned_remove (GtkContainer *container, GtkWidget *widget) { MooPaned *paned = MOO_PANED (container); g_return_if_fail (widget == GTK_BIN(paned)->child); GTK_CONTAINER_CLASS(moo_paned_parent_class)->remove (container, widget); } static void draw_handle (MooPaned *paned, GdkEventExpose *event) { GtkWidget *widget = GTK_WIDGET (paned); GtkStateType state; GdkRectangle area = {0, 0, 0, 0}; GtkOrientation orientation = GTK_ORIENTATION_VERTICAL; int shadow_size = 0; switch (paned->priv->pane_position) { case MOO_PANE_POS_LEFT: case MOO_PANE_POS_RIGHT: shadow_size = widget->style->xthickness; area.width = paned->priv->handle_size; area.height = widget->allocation.height; if (area.width <= 3*shadow_size) shadow_size = 0; area.x += shadow_size; area.width -= shadow_size; orientation = GTK_ORIENTATION_VERTICAL; break; case MOO_PANE_POS_TOP: case MOO_PANE_POS_BOTTOM: shadow_size = widget->style->ythickness; area.width = widget->allocation.width; area.height = paned->priv->handle_size; if (area.height <= 3 * shadow_size) shadow_size = 0; area.y += shadow_size; area.height -= shadow_size; orientation = GTK_ORIENTATION_HORIZONTAL; break; } if (gtk_widget_is_focus (widget)) state = GTK_STATE_SELECTED; else if (paned->priv->handle_prelit) state = GTK_STATE_PRELIGHT; else state = GTK_WIDGET_STATE (widget); gtk_paint_handle (widget->style, paned->priv->handle_window, state, GTK_SHADOW_NONE, &event->area, widget, "paned", area.x, area.y, area.width, area.height, orientation); if (shadow_size) { if (orientation == GTK_ORIENTATION_VERTICAL) { area.x -= shadow_size; area.width = shadow_size; gtk_paint_vline (widget->style, paned->priv->handle_window, GTK_STATE_NORMAL, &event->area, widget, "moo-paned", area.y, area.y + area.height, area.x); area.x = paned->priv->handle_size - shadow_size; gtk_paint_vline (widget->style, paned->priv->handle_window, GTK_STATE_NORMAL, &event->area, widget, "moo-paned", area.y, area.y + area.height, area.x); } else { area.y -= shadow_size; area.height = shadow_size; gtk_paint_hline (widget->style, paned->priv->handle_window, GTK_STATE_NORMAL, &event->area, widget, "moo-paned", area.x, area.x + area.width, area.y); area.y = paned->priv->handle_size - shadow_size; gtk_paint_hline (widget->style, paned->priv->handle_window, GTK_STATE_NORMAL, &event->area, widget, "moo-paned", area.x, area.x + area.width, area.y); } } } static void draw_border (MooPaned *paned, GdkEventExpose *event) { GdkRectangle rect; GtkWidget *widget = GTK_WIDGET (paned); rect.x = paned->priv->button_box_size; rect.y = paned->priv->button_box_size; switch (paned->priv->pane_position) { case MOO_PANE_POS_RIGHT: rect.x = widget->allocation.width - paned->priv->button_box_size - paned->priv->border_size; case MOO_PANE_POS_LEFT: rect.y = 0; rect.height = widget->allocation.height; rect.width = paned->priv->border_size; gtk_paint_vline (widget->style, widget->window, GTK_STATE_NORMAL, &event->area, widget, "moo-paned", rect.y, rect.y + rect.height, rect.x); break; case MOO_PANE_POS_BOTTOM: rect.y = widget->allocation.height - paned->priv->button_box_size - paned->priv->border_size; case MOO_PANE_POS_TOP: rect.x = 0; rect.width = widget->allocation.width; rect.height = paned->priv->border_size; gtk_paint_hline (widget->style, widget->window, GTK_STATE_NORMAL, &event->area, widget, "moo-paned", rect.x, rect.x + rect.width, rect.y); break; } } void moo_paned_set_sticky_pane (MooPaned *paned, gboolean sticky) { GSList *l; if (sticky) sticky = TRUE; g_return_if_fail (MOO_IS_PANED (paned)); if (paned->priv->sticky != sticky && GTK_WIDGET_REALIZED (paned)) gtk_widget_queue_resize (GTK_WIDGET (paned)); paned->priv->sticky = sticky; for (l = paned->priv->panes; l != NULL; l = l->next) gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (((Pane*)l->data)->sticky_button), sticky); /* this is needed because bin child may move its window on top of the pane window */ if (!sticky && paned->priv->current_pane) paned->priv->raise_pane = TRUE; g_object_notify (G_OBJECT (paned), "sticky-pane"); } GtkWidget* moo_paned_get_nth_pane (MooPaned *paned, guint n) { Pane *pane; g_return_val_if_fail (MOO_IS_PANED (paned), NULL); pane = get_nth_pane (paned, n); g_return_val_if_fail (pane != NULL, NULL); return pane->child; } int moo_paned_get_pane_num (MooPaned *paned, GtkWidget *widget) { Pane *pane; g_return_val_if_fail (MOO_IS_PANED (paned), -1); g_return_val_if_fail (GTK_IS_WIDGET (widget), -1); pane = g_object_get_data (G_OBJECT (widget), "moo-pane"); if (pane) return pane_index (paned, pane); else return -1; } MooPaneLabel* moo_paned_get_label (MooPaned *paned, GtkWidget *pane_widget) { Pane *pane; g_return_val_if_fail (MOO_IS_PANED (paned), NULL); g_return_val_if_fail (GTK_IS_WIDGET (pane_widget), NULL); pane = g_object_get_data (G_OBJECT (pane_widget), "moo-pane"); g_return_val_if_fail (pane != NULL, NULL); return moo_pane_label_copy (pane->label); } GtkWidget* moo_paned_get_button (MooPaned *paned, GtkWidget *pane_widget) { Pane *pane; g_return_val_if_fail (MOO_IS_PANED (paned), NULL); g_return_val_if_fail (GTK_IS_WIDGET (pane_widget), NULL); pane = g_object_get_data (G_OBJECT (pane_widget), "moo-pane"); g_return_val_if_fail (pane != NULL, NULL); return pane->button; } static gboolean moo_paned_motion (GtkWidget *widget, G_GNUC_UNUSED GdkEventMotion *event) { MooPaned *paned = MOO_PANED (widget); if (paned->priv->in_drag) { int size = 0; GtkRequisition requisition; gtk_widget_get_child_requisition (paned->priv->current_pane->frame, &requisition); switch (paned->priv->pane_position) { case MOO_PANE_POS_LEFT: case MOO_PANE_POS_RIGHT: gdk_window_get_pointer (widget->window, &size, NULL, NULL); if (paned->priv->pane_position == MOO_PANE_POS_RIGHT) size = widget->allocation.width - size; size -= (paned->priv->drag_start + paned->priv->button_box_size); size = CLAMP (size, requisition.width, widget->allocation.width - paned->priv->button_box_size - paned->priv->handle_size); break; case MOO_PANE_POS_TOP: case MOO_PANE_POS_BOTTOM: gdk_window_get_pointer (widget->window, NULL, &size, NULL); if (paned->priv->pane_position == MOO_PANE_POS_BOTTOM) size = widget->allocation.height - size; size -= (paned->priv->drag_start + paned->priv->button_box_size); size = CLAMP (size, requisition.height, widget->allocation.height - paned->priv->button_box_size - paned->priv->handle_size); break; } if (size != paned->priv->pane_widget_size) moo_paned_set_pane_size (paned, size); } return FALSE; } static void get_handle_rect (MooPaned *paned, GdkRectangle *rect) { rect->x = rect->y = 0; switch (paned->priv->pane_position) { case MOO_PANE_POS_LEFT: case MOO_PANE_POS_RIGHT: rect->width = paned->priv->handle_size; rect->height = GTK_WIDGET(paned)->allocation.height; break; case MOO_PANE_POS_TOP: case MOO_PANE_POS_BOTTOM: rect->height = paned->priv->handle_size; rect->width = GTK_WIDGET(paned)->allocation.width; break; } } static gboolean moo_paned_enter (GtkWidget *widget, GdkEventCrossing *event) { MooPaned *paned = MOO_PANED (widget); GdkRectangle rect; if (event->window == paned->priv->handle_window && !paned->priv->in_drag) { paned->priv->handle_prelit = TRUE; get_handle_rect (paned, &rect); gdk_window_invalidate_rect (paned->priv->handle_window, &rect, FALSE); return TRUE; } return FALSE; } static gboolean moo_paned_leave (GtkWidget *widget, GdkEventCrossing *event) { MooPaned *paned = MOO_PANED (widget); GdkRectangle rect; if (event->window == paned->priv->handle_window && !paned->priv->in_drag) { paned->priv->handle_prelit = FALSE; get_handle_rect (paned, &rect); gdk_window_invalidate_rect (paned->priv->handle_window, &rect, FALSE); return TRUE; } return FALSE; } static gboolean moo_paned_button_press (GtkWidget *widget, GdkEventButton *event) { MooPaned *paned = MOO_PANED (widget); if (!paned->priv->in_drag && (event->window == paned->priv->handle_window) && (event->button == 1) && paned->priv->pane_widget_visible) { paned->priv->in_drag = TRUE; /* This is copied from gtkpaned.c */ gdk_pointer_grab (paned->priv->handle_window, FALSE, GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON1_MOTION_MASK | GDK_BUTTON_RELEASE_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK, NULL, NULL, event->time); switch (paned->priv->pane_position) { case MOO_PANE_POS_LEFT: case MOO_PANE_POS_RIGHT: paned->priv->drag_start = event->x; break; case MOO_PANE_POS_TOP: case MOO_PANE_POS_BOTTOM: paned->priv->drag_start = event->y; break; } return TRUE; } return FALSE; } static gboolean moo_paned_button_release (GtkWidget *widget, GdkEventButton *event) { MooPaned *paned = MOO_PANED (widget); if (paned->priv->in_drag && (event->button == 1)) { paned->priv->in_drag = FALSE; paned->priv->drag_start = -1; gdk_display_pointer_ungrab (gtk_widget_get_display (widget), event->time); return TRUE; } return FALSE; } int moo_paned_get_pane_size (MooPaned *paned) { g_return_val_if_fail (MOO_IS_PANED (paned), 0); return paned->priv->position; } int moo_paned_get_button_box_size (MooPaned *paned) { g_return_val_if_fail (MOO_IS_PANED (paned), 0); return paned->priv->button_box_size; } static gboolean invalidate_predicate (GdkWindow *window, gpointer data) { gpointer user_data; gdk_window_get_user_data (window, &user_data); return (user_data == data); } static void moo_paned_set_pane_size_real (MooPaned *paned, int size) { GdkRegion *region; GtkWidget *widget; GdkRectangle rect; g_return_if_fail (MOO_IS_PANED (paned)); if (!GTK_WIDGET_REALIZED (paned)) { paned->priv->position = size; return; } widget = GTK_WIDGET (paned); switch (paned->priv->pane_position) { case MOO_PANE_POS_LEFT: case MOO_PANE_POS_RIGHT: size = CLAMP (size, 0, widget->allocation.width - paned->priv->button_box_size - paned->priv->handle_size); break; case MOO_PANE_POS_TOP: case MOO_PANE_POS_BOTTOM: size = CLAMP (size, 0, widget->allocation.height - paned->priv->button_box_size - paned->priv->handle_size); break; } if (size == paned->priv->position) return; paned->priv->position = size; if (!paned->priv->pane_widget_visible) return; /* button box redrawing is too slow */ if (!paned->priv->button_box_visible) { gtk_widget_queue_resize (widget); return; } rect.x = widget->allocation.x; rect.y = widget->allocation.y; rect.width = widget->allocation.width; rect.height = widget->allocation.height; switch (paned->priv->pane_position) { case MOO_PANE_POS_LEFT: rect.x += paned->priv->button_box_size; rect.width -= paned->priv->button_box_size; break; case MOO_PANE_POS_RIGHT: rect.width -= paned->priv->button_box_size; break; case MOO_PANE_POS_TOP: rect.y += paned->priv->button_box_size; rect.height -= paned->priv->button_box_size; break; case MOO_PANE_POS_BOTTOM: rect.height -= paned->priv->button_box_size; break; } region = gdk_region_rectangle (&rect); gdk_window_invalidate_maybe_recurse (widget->window, region, invalidate_predicate, widget); gdk_region_destroy (region); gtk_widget_queue_resize_no_redraw (widget); } void moo_paned_set_pane_size (MooPaned *paned, int size) { g_return_if_fail (MOO_IS_PANED (paned)); g_signal_emit (paned, signals[SET_PANE_SIZE], 0, size); } static void button_box_visible_notify (MooPaned *paned) { gboolean visible = GTK_WIDGET_VISIBLE (paned->button_box); if (paned->priv->button_box_visible == visible) return; if (paned->priv->panes) paned->priv->button_box_visible = visible; if (GTK_WIDGET_REALIZED (paned)) gtk_widget_queue_resize (GTK_WIDGET (paned)); } static GtkWidget *moo_pane_label_get_widget (MooPaneLabel *label, MooPanePosition position) { GtkWidget *box = NULL; GtkWidget *text = NULL; GtkWidget *icon = NULL; g_return_val_if_fail (label != NULL, NULL); g_return_val_if_fail (position < 4, NULL); if (label->label) { text = gtk_label_new (label->label); switch (position) { case MOO_PANE_POS_LEFT: gtk_label_set_angle (GTK_LABEL (text), 90); break; case MOO_PANE_POS_RIGHT: gtk_label_set_angle (GTK_LABEL (text), 270); break; default: break; } } if (label->icon_widget) icon = label->icon_widget; else if (label->icon_stock_id) icon = gtk_image_new_from_stock (label->icon_stock_id, GTK_ICON_SIZE_MENU); else if (label->icon_pixbuf) icon = gtk_image_new_from_pixbuf (label->icon_pixbuf); if (icon) gtk_widget_show (icon); if (text) gtk_widget_show (text); if (icon && text) { switch (position) { case MOO_PANE_POS_LEFT: case MOO_PANE_POS_RIGHT: box = gtk_vbox_new (FALSE, SPACING_IN_BUTTON); break; default: box = gtk_hbox_new (FALSE, SPACING_IN_BUTTON); break; } gtk_widget_show (box); switch (position) { case MOO_PANE_POS_LEFT: gtk_box_pack_start (GTK_BOX (box), text, FALSE, FALSE, 0); gtk_box_pack_start (GTK_BOX (box), icon, FALSE, FALSE, 0); break; default: gtk_box_pack_start (GTK_BOX (box), icon, FALSE, FALSE, 0); gtk_box_pack_start (GTK_BOX (box), text, FALSE, FALSE, 0); break; } return box; } if (icon) return icon; if (text) return text; g_warning ("%s: empty label", G_STRLOC); box = gtk_vbox_new (FALSE, SPACING_IN_BUTTON); gtk_widget_show (box); return box; } static GtkWidget *create_button (Pane *pane, GtkWidget *toolbar, const char *tip, gboolean toggle, const char *stock_id) { GtkWidget *button; GtkWidget *icon; if (toggle) button = gtk_toggle_button_new (); else button = gtk_button_new (); g_object_set_data (G_OBJECT (button), "moo-pane", pane); gtk_button_set_focus_on_click (GTK_BUTTON (button), FALSE); gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE); _moo_widget_set_tooltip (button, tip); icon = gtk_image_new_from_stock (stock_id, MOO_ICON_SIZE_REAL_SMALL); gtk_container_add (GTK_CONTAINER (button), icon); gtk_box_pack_end (GTK_BOX (toolbar), button, FALSE, FALSE, 0); gtk_widget_show_all (button); return button; } static GtkWidget *create_frame_widget (MooPaned *paned, Pane *pane, gboolean embedded) { GtkWidget *vbox, *toolbar, *separator, *handle, *hbox = NULL, *child_holder; vbox = gtk_vbox_new (FALSE, 0); gtk_widget_show (vbox); toolbar = gtk_hbox_new (FALSE, 0); handle = gtk_event_box_new (); gtk_widget_show (handle); gtk_box_pack_start (GTK_BOX (toolbar), handle, TRUE, TRUE, 3); if (embedded) { pane->handle = handle; g_signal_connect (handle, "realize", G_CALLBACK (handle_realize), paned); } if (embedded) { GtkWidget *close_button; close_button = create_button (pane, toolbar, "Hide pane", FALSE, MOO_STOCK_CLOSE); pane->sticky_button = create_button (pane, toolbar, "Sticky", TRUE, MOO_STOCK_STICKY); pane->detach_button = create_button (pane, toolbar, "Detach pane", FALSE, MOO_STOCK_DETACH); if (!paned->priv->enable_detaching || !pane->detachable) gtk_widget_hide (pane->detach_button); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (pane->sticky_button), paned->priv->sticky); g_signal_connect_swapped (close_button, "clicked", G_CALLBACK (moo_paned_hide_pane), paned); } else { GtkWidget *attach_button; attach_button = create_button (pane, toolbar, "Attach", FALSE, MOO_STOCK_ATTACH); pane->keep_on_top_button = create_button (pane, toolbar, "Keep on top", TRUE, MOO_STOCK_KEEP_ON_TOP); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (pane->keep_on_top_button), pane->params->keep_on_top); g_object_set_data (G_OBJECT (attach_button), "moo-pane", pane); g_signal_connect (attach_button, "clicked", G_CALLBACK (attach_button_clicked), paned); } gtk_widget_show (toolbar); gtk_box_pack_start (GTK_BOX (vbox), toolbar, FALSE, FALSE, 0); separator = gtk_hseparator_new (); gtk_widget_show (separator); gtk_box_pack_start (GTK_BOX (vbox), separator, FALSE, FALSE, 0); child_holder = gtk_vbox_new (FALSE, 0); gtk_widget_show (child_holder); gtk_box_pack_start (GTK_BOX (vbox), child_holder, TRUE, TRUE, 0); if (embedded) pane->child_holder = child_holder; else pane->window_child_holder = child_holder; switch (paned->priv->pane_position) { case MOO_PANE_POS_LEFT: case MOO_PANE_POS_RIGHT: hbox = gtk_hbox_new (FALSE, 0); separator = gtk_vseparator_new (); gtk_widget_show (separator); break; case MOO_PANE_POS_TOP: case MOO_PANE_POS_BOTTOM: hbox = gtk_vbox_new (FALSE, 0); separator = gtk_hseparator_new (); gtk_widget_show (separator); break; } switch (paned->priv->pane_position) { case MOO_PANE_POS_LEFT: case MOO_PANE_POS_TOP: gtk_box_pack_start (GTK_BOX (hbox), separator, FALSE, FALSE, 0); gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0); break; case MOO_PANE_POS_RIGHT: case MOO_PANE_POS_BOTTOM: gtk_box_pack_end (GTK_BOX (hbox), separator, FALSE, FALSE, 0); gtk_box_pack_end (GTK_BOX (hbox), vbox, TRUE, TRUE, 0); break; } return hbox; } int moo_paned_insert_pane (MooPaned *paned, GtkWidget *pane_widget, MooPaneLabel *pane_label, int position) { GtkWidget *button, *label_widget; Pane *pane; g_return_val_if_fail (MOO_IS_PANED (paned), -1); g_return_val_if_fail (GTK_IS_WIDGET (pane_widget), -1); g_return_val_if_fail (pane_label != NULL, -1); g_return_val_if_fail (pane_widget->parent == NULL, -1); button = gtk_toggle_button_new (); gtk_widget_show (button); gtk_button_set_focus_on_click (GTK_BUTTON (button), FALSE); label_widget = moo_pane_label_get_widget (pane_label, paned->priv->pane_position); gtk_container_add (GTK_CONTAINER (button), label_widget); gtk_widget_show (label_widget); if (position < 0 || position > (int) moo_paned_n_panes (paned)) position = moo_paned_n_panes (paned); gtk_container_add_with_properties (GTK_CONTAINER (paned->button_box), button, "expand", FALSE, "fill", FALSE, "pack-type", GTK_PACK_START, "position", position, NULL); pane = pane_new (pane_label); pane->frame = create_frame_widget (paned, pane, TRUE); gtk_object_sink (g_object_ref (pane->frame)); if (GTK_WIDGET_REALIZED (paned)) gtk_widget_set_parent_window (pane->frame, paned->priv->pane_window); gtk_widget_set_parent (pane->frame, GTK_WIDGET (paned)); pane->child = pane_widget; gtk_widget_show (pane->child); gtk_box_pack_start (GTK_BOX (pane->child_holder), pane->child, TRUE, TRUE, 0); pane->button = button; paned->priv->panes = g_slist_insert (paned->priv->panes, pane, position); g_object_set_data (G_OBJECT (button), "moo-pane", pane); g_object_set_data (G_OBJECT (pane->child), "moo-pane", pane); g_object_set_data (G_OBJECT (pane->frame), "moo-pane", pane); g_object_set_data (G_OBJECT (pane->handle), "moo-pane", pane); g_signal_connect (button, "toggled", G_CALLBACK (button_toggled), paned); g_signal_connect (pane->sticky_button, "toggled", G_CALLBACK (sticky_button_toggled), paned); g_signal_connect (pane->detach_button, "clicked", G_CALLBACK (detach_button_clicked), paned); g_signal_connect (pane->handle, "button-press-event", G_CALLBACK (handle_button_press), paned); g_signal_connect (pane->handle, "button-release-event", G_CALLBACK (handle_button_release), paned); g_signal_connect (pane->handle, "motion-notify-event", G_CALLBACK (handle_motion), paned); g_signal_connect (pane->handle, "expose-event", G_CALLBACK (handle_expose), paned); gtk_widget_show (paned->button_box); paned->priv->button_box_visible = TRUE; if (GTK_WIDGET_VISIBLE (paned)) gtk_widget_queue_resize (GTK_WIDGET (paned)); return position; } static Pane* pane_new (MooPaneLabel *label) { Pane *pane = g_new0 (Pane, 1); pane->detachable = TRUE; pane->label = moo_pane_label_copy (label); pane->params = moo_pane_params_new (); return pane; } static void pane_free (Pane *pane) { if (pane) { moo_pane_label_free (pane->label); moo_pane_params_free (pane->params); g_free (pane); } } gboolean moo_paned_remove_pane (MooPaned *paned, GtkWidget *pane_widget) { Pane *pane; GtkWidget *label; g_return_val_if_fail (MOO_IS_PANED (paned), FALSE); g_return_val_if_fail (GTK_IS_WIDGET (pane_widget), FALSE); pane = g_object_get_data (G_OBJECT (pane_widget), "moo-pane"); g_return_val_if_fail (pane != NULL, FALSE); g_return_val_if_fail (pane->child == pane_widget, FALSE); g_return_val_if_fail (g_slist_find (paned->priv->panes, pane) != NULL, FALSE); if (paned->priv->current_pane == pane) moo_paned_hide_pane (paned); if (pane->params->detached) { block_params_changed (paned); moo_paned_attach_pane (paned, g_slist_index (paned->priv->panes, pane)); unblock_params_changed (paned); } g_object_set_data (G_OBJECT (pane->button), "moo-pane", NULL); g_object_set_data (G_OBJECT (pane->child), "moo-pane", NULL); g_object_set_data (G_OBJECT (pane->frame), "moo-pane", NULL); g_object_set_data (G_OBJECT (pane->handle), "moo-pane", NULL); if (pane->window) gtk_widget_destroy (pane->window); pane->window = NULL; g_signal_handlers_disconnect_by_func (pane->button, (gpointer) button_toggled, paned); g_signal_handlers_disconnect_by_func (pane->sticky_button, (gpointer) sticky_button_toggled, paned); g_signal_handlers_disconnect_by_func (pane->detach_button, (gpointer) detach_button_clicked, paned); g_signal_handlers_disconnect_by_func (pane->handle, (gpointer) handle_button_press, paned); g_signal_handlers_disconnect_by_func (pane->handle, (gpointer) handle_button_release, paned); g_signal_handlers_disconnect_by_func (pane->handle, (gpointer) handle_motion, paned); g_signal_handlers_disconnect_by_func (pane->handle, (gpointer) handle_expose, paned); g_signal_handlers_disconnect_by_func (pane->handle, (gpointer) handle_realize, paned); label = gtk_bin_get_child (GTK_BIN (pane->button)); gtk_container_remove (GTK_CONTAINER (pane->button), label); gtk_container_remove (GTK_CONTAINER (paned->button_box), pane->button); paned->priv->panes = g_slist_remove (paned->priv->panes, pane); gtk_container_remove (GTK_CONTAINER (pane->child_holder), pane->child); gtk_widget_unparent (pane->frame); pane_free (pane); if (!moo_paned_n_panes (paned)) { paned->priv->handle_visible = FALSE; paned->priv->handle_size = 0; if (paned->priv->pane_window) gdk_window_hide (paned->priv->pane_window); gtk_widget_hide (paned->button_box); paned->priv->button_box_visible = FALSE; } gtk_widget_queue_resize (GTK_WIDGET (paned)); return TRUE; } guint moo_paned_n_panes (MooPaned *paned) { g_return_val_if_fail (MOO_IS_PANED (paned), 0); return g_slist_length (paned->priv->panes); } static GtkWidget *find_focus (GtkWidget *widget) { GtkWidget *focus_child, *window; if (!widget) return NULL; window = gtk_widget_get_toplevel (widget); if (!GTK_IS_WINDOW (window)) return NULL; focus_child = gtk_window_get_focus (GTK_WINDOW (window)); if (focus_child && gtk_widget_is_ancestor (focus_child, widget)) return focus_child; else return NULL; } static GtkWidget *find_focus_child (MooPaned *paned) { return find_focus (GTK_BIN(paned)->child); } static GtkWidget *find_pane_focus_child (Pane *pane) { g_return_val_if_fail (pane != NULL && pane->child != NULL, NULL); return find_focus (pane->child); } static void moo_paned_set_focus_child (GtkContainer *container, GtkWidget *widget) { MooPaned *paned = MOO_PANED (container); FocusPosition new_focus = FOCUS_NONE; Pane *new_focus_pane = NULL; Pane *old_focus_pane = paned->priv->focus_pane; GSList *l; if (widget) { if (widget == GTK_BIN(paned)->child) new_focus = FOCUS_CHILD; else if (widget == paned->button_box) new_focus = FOCUS_BUTTON; if (!new_focus) { for (l = paned->priv->panes; l != NULL; l = l->next) { Pane *pane = l->data; if (widget == pane->frame) { new_focus_pane = pane; new_focus = FOCUS_PANE; break; } } } if (!new_focus) { g_critical ("%s: oops", G_STRLOC); GTK_CONTAINER_CLASS(moo_paned_parent_class)->set_focus_child (container, widget); paned->priv->focus = FOCUS_NONE; paned->priv->focus_pane = NULL; g_return_if_reached (); } } if (new_focus != FOCUS_BUTTON) paned->priv->button_real_focus = FALSE; switch (paned->priv->focus) { case FOCUS_NONE: case FOCUS_BUTTON: break; case FOCUS_CHILD: if (new_focus != FOCUS_CHILD) { if (paned->priv->focus_child) g_object_remove_weak_pointer (G_OBJECT (paned->priv->focus_child), (gpointer*) &paned->priv->focus_child); paned->priv->focus_child = find_focus_child (paned); if (paned->priv->focus_child) g_object_add_weak_pointer (G_OBJECT (paned->priv->focus_child), (gpointer*) &paned->priv->focus_child); } break; case FOCUS_PANE: g_assert (old_focus_pane != NULL); if (new_focus_pane != old_focus_pane) { if (old_focus_pane->focus_child) g_object_remove_weak_pointer (G_OBJECT (old_focus_pane->focus_child), (gpointer*) &old_focus_pane->focus_child); old_focus_pane->focus_child = find_pane_focus_child (old_focus_pane); if (old_focus_pane->focus_child) g_object_add_weak_pointer (G_OBJECT (old_focus_pane->focus_child), (gpointer*) &old_focus_pane->focus_child); } break; } GTK_CONTAINER_CLASS(moo_paned_parent_class)->set_focus_child (container, widget); paned->priv->focus = new_focus; paned->priv->focus_pane = new_focus_pane; if (new_focus == FOCUS_CHILD && paned->priv->close_on_child_focus && !paned->priv->sticky) { paned->priv->dont_move_focus = TRUE; moo_paned_hide_pane (paned); paned->priv->dont_move_focus = FALSE; } } static gboolean focus_to_child (MooPaned *paned, GtkDirectionType direction) { return GTK_BIN(paned)->child && gtk_widget_child_focus (GTK_BIN(paned)->child, direction); } static gboolean focus_to_pane (MooPaned *paned, GtkDirectionType direction) { Pane *pane = paned->priv->current_pane; if (!pane) return FALSE; if (find_focus (pane->frame)) { return gtk_widget_child_focus (pane->frame, direction); } else { return gtk_widget_child_focus (pane->child, direction) || gtk_widget_child_focus (pane->frame, direction); } } static gboolean focus_to_button (MooPaned *paned, GtkDirectionType direction) { if (gtk_widget_child_focus (paned->button_box, direction)) { paned->priv->button_real_focus = TRUE; return TRUE; } else { return FALSE; } } static gboolean moo_left_paned_focus (MooPaned *paned, GtkDirectionType direction) { switch (paned->priv->focus) { case FOCUS_NONE: switch (direction) { case GTK_DIR_LEFT: case GTK_DIR_UP: return focus_to_child (paned, direction) || (paned->priv->current_pane && focus_to_pane (paned, direction)) || focus_to_button (paned, direction); case GTK_DIR_RIGHT: case GTK_DIR_DOWN: return focus_to_button (paned, direction) || focus_to_child (paned, direction); default: g_return_val_if_reached (FALSE); } case FOCUS_CHILD: if (focus_to_child (paned, direction)) return TRUE; switch (direction) { case GTK_DIR_LEFT: return (paned->priv->current_pane && focus_to_pane (paned, direction)) || focus_to_button (paned, direction); default: return FALSE; } case FOCUS_PANE: if (focus_to_pane (paned, direction)) return TRUE; switch (direction) { case GTK_DIR_LEFT: return focus_to_button (paned, direction); case GTK_DIR_RIGHT: return focus_to_child (paned, direction); default: return FALSE; } case FOCUS_BUTTON: if (focus_to_button (paned, direction)) return TRUE; switch (direction) { case GTK_DIR_RIGHT: return focus_to_pane (paned, direction) || focus_to_child (paned, direction); default: return FALSE; } } g_return_val_if_reached (FALSE); } static gboolean moo_left_paned_tab_focus(MooPaned *paned, gboolean forward, GtkDirectionType direction) { if (forward) { switch (paned->priv->focus) { case FOCUS_NONE: return focus_to_button (paned, direction) || focus_to_child (paned, direction); case FOCUS_CHILD: return focus_to_child (paned, direction); case FOCUS_PANE: return focus_to_pane (paned, direction) || focus_to_child (paned, direction); case FOCUS_BUTTON: return focus_to_button (paned, direction) || focus_to_pane (paned, direction) || focus_to_child (paned, direction); } } else { switch (paned->priv->focus) { case FOCUS_NONE: case FOCUS_CHILD: return focus_to_child (paned, direction) || focus_to_pane (paned, direction) || focus_to_button (paned, direction); case FOCUS_PANE: return focus_to_pane (paned, direction) || focus_to_button (paned, direction); case FOCUS_BUTTON: return focus_to_button (paned, direction); } } g_return_val_if_reached (FALSE); } static gboolean moo_top_paned_tab_focus (MooPaned *paned, GtkDirectionType direction) { switch (paned->priv->focus) { case FOCUS_NONE: return focus_to_button (paned, direction) || focus_to_child (paned, direction); case FOCUS_BUTTON: return focus_to_button (paned, direction) || focus_to_pane (paned, direction) || focus_to_child (paned, direction); case FOCUS_PANE: return focus_to_pane (paned, direction) || focus_to_child (paned, direction); case FOCUS_CHILD: return focus_to_child (paned, direction); } g_return_val_if_reached (FALSE); } static gboolean moo_paned_focus (GtkWidget *widget, GtkDirectionType direction) { MooPaned *paned = MOO_PANED (widget); gboolean invert = FALSE; gboolean flip = FALSE; paned->priv->button_real_focus = FALSE; if (direction == GTK_DIR_TAB_FORWARD || direction == GTK_DIR_TAB_BACKWARD) { switch (paned->priv->pane_position) { case MOO_PANE_POS_RIGHT: return moo_left_paned_tab_focus (paned, direction != GTK_DIR_TAB_FORWARD, direction); case MOO_PANE_POS_LEFT: return moo_left_paned_tab_focus (paned, direction == GTK_DIR_TAB_FORWARD, direction); case MOO_PANE_POS_BOTTOM: case MOO_PANE_POS_TOP: return moo_top_paned_tab_focus (paned, direction); } } switch (paned->priv->pane_position) { case MOO_PANE_POS_RIGHT: invert = TRUE; /* fall through */ case MOO_PANE_POS_LEFT: break; case MOO_PANE_POS_BOTTOM: invert = TRUE; /* fall through */ case MOO_PANE_POS_TOP: flip = TRUE; break; } if (invert) { switch (direction) { case GTK_DIR_RIGHT: direction = GTK_DIR_LEFT; break; case GTK_DIR_LEFT: direction = GTK_DIR_RIGHT; break; case GTK_DIR_UP: direction = GTK_DIR_DOWN; break; case GTK_DIR_DOWN: direction = GTK_DIR_UP; break; default: g_return_val_if_reached (FALSE); } } if (flip) { switch (direction) { case GTK_DIR_RIGHT: direction = GTK_DIR_DOWN; break; case GTK_DIR_DOWN: direction = GTK_DIR_RIGHT; break; case GTK_DIR_LEFT: direction = GTK_DIR_UP; break; case GTK_DIR_UP: direction = GTK_DIR_LEFT; break; default: g_return_val_if_reached (FALSE); } } return moo_left_paned_focus (paned, direction); } static void sticky_button_toggled (GtkToggleButton *button, MooPaned *paned) { gboolean active = gtk_toggle_button_get_active (button); if (active != paned->priv->sticky) moo_paned_set_sticky_pane (paned, active); } static void detach_button_clicked (G_GNUC_UNUSED GtkWidget *button, MooPaned *paned) { int index_ = pane_index (paned, paned->priv->current_pane); g_return_if_fail (index_ >= 0); moo_paned_detach_pane (paned, index_); } void moo_paned_present_pane (MooPaned *paned, guint index_) { Pane *pane; g_return_if_fail (MOO_IS_PANED (paned)); g_return_if_fail (index_ < moo_paned_n_panes (paned)); pane = get_nth_pane (paned, index_); g_return_if_fail (pane != NULL); if (paned->priv->current_pane == pane) { paned->priv->dont_move_focus = FALSE; if (!find_focus (pane->child)) { if (pane->focus_child) { gtk_widget_grab_focus (pane->focus_child); } else if (!gtk_widget_child_focus (pane->child, GTK_DIR_TAB_FORWARD)) { paned->priv->button_real_focus = FALSE; gtk_widget_grab_focus (pane->button); } } return; } else if (pane->params->detached) { gtk_window_present (GTK_WINDOW (pane->window)); } else { moo_paned_open_pane (paned, index_); } } static void moo_paned_open_pane_real(MooPaned *paned, guint index) { Pane *pane; FocusPosition old_focus; g_return_if_fail (index < moo_paned_n_panes (paned)); pane = get_nth_pane (paned, index); g_return_if_fail (pane != NULL); if (paned->priv->current_pane == pane) return; old_focus = paned->priv->focus; if (paned->priv->current_pane) { Pane *old_pane = paned->priv->current_pane; paned->priv->current_pane = NULL; gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (old_pane->button), FALSE); gtk_widget_hide (old_pane->frame); } if (GTK_WIDGET_REALIZED (paned)) { /* XXX move this to realize */ gtk_widget_set_parent_window (pane->frame, paned->priv->pane_window); } if (GTK_WIDGET_MAPPED (paned)) { gdk_window_show (paned->priv->pane_window); gdk_window_show (paned->priv->handle_window); } paned->priv->current_pane = pane; gtk_widget_show (pane->frame); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (pane->button), TRUE); if (pane->params->detached) moo_paned_attach_pane (paned, index); paned->priv->handle_visible = TRUE; paned->priv->pane_widget_visible = TRUE; if (paned->priv->position > 0) paned->priv->pane_widget_size = paned->priv->position; /* XXX it's wrong, it should look if button was clicked */ if (!paned->priv->dont_move_focus && (old_focus != FOCUS_BUTTON || !paned->priv->button_real_focus)) { if (pane->focus_child) { gtk_widget_grab_focus (pane->focus_child); } else if (!gtk_widget_child_focus (pane->child, GTK_DIR_TAB_FORWARD)) { paned->priv->button_real_focus = FALSE; gtk_widget_grab_focus (pane->button); } } gtk_widget_queue_resize (GTK_WIDGET (paned)); } /* XXX invalidate space under the pane */ static void moo_paned_hide_pane_real(MooPaned *paned) { if (paned->priv->current_pane) { GtkWidget *button = paned->priv->current_pane->button; FocusPosition old_focus = paned->priv->focus; gtk_widget_hide (paned->priv->current_pane->frame); if (GTK_WIDGET_REALIZED (paned)) { gdk_window_hide (paned->priv->handle_window); gdk_window_hide (paned->priv->pane_window); } paned->priv->current_pane = NULL; paned->priv->pane_widget_visible = FALSE; paned->priv->handle_visible = FALSE; gtk_widget_queue_resize (GTK_WIDGET (paned)); /* XXX it's wrong, it should look if button was clicked */ if (!paned->priv->dont_move_focus && old_focus != FOCUS_NONE && (old_focus != FOCUS_BUTTON || !paned->priv->button_real_focus)) { if (paned->priv->focus_child) { gtk_widget_grab_focus (paned->priv->focus_child); } else if (!GTK_BIN(paned)->child || !gtk_widget_child_focus (GTK_BIN(paned)->child, GTK_DIR_TAB_FORWARD)) { if (GTK_WIDGET_VISIBLE (button)) gtk_widget_grab_focus (button); } else { GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (paned)); gtk_widget_child_focus (toplevel, GTK_DIR_TAB_FORWARD); } } gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), FALSE); } } static void button_toggled (GtkToggleButton *button, MooPaned *paned) { Pane *pane; int index; if (!gtk_toggle_button_get_active (button)) { if (paned->priv->current_pane && paned->priv->current_pane->button == GTK_WIDGET (button)) { moo_paned_hide_pane (paned); } } else if (!paned->priv->current_pane || paned->priv->current_pane->button != GTK_WIDGET (button)) { pane = g_object_get_data (G_OBJECT (button), "moo-pane"); g_return_if_fail (pane != NULL); index = pane_index (paned, pane); g_return_if_fail (index >= 0); moo_paned_open_pane (paned, index); } } void moo_paned_hide_pane (MooPaned *paned) { g_return_if_fail (MOO_IS_PANED (paned)); g_signal_emit (paned, signals[HIDE_PANE], 0); } void moo_paned_open_pane (MooPaned *paned, guint index) { g_signal_emit (paned, signals[OPEN_PANE], 0, index); } static int pane_index (MooPaned *paned, Pane *pane) { return g_slist_index (paned->priv->panes, pane); } static Pane *get_nth_pane (MooPaned *paned, guint index_) { return g_slist_nth_data (paned->priv->panes, index_); } int moo_paned_get_open_pane (MooPaned *paned) { g_return_val_if_fail (MOO_IS_PANED (paned), -1); if (!paned->priv->current_pane) return -1; else return pane_index (paned, paned->priv->current_pane); } gboolean moo_paned_is_open (MooPaned *paned) { return moo_paned_get_open_pane (paned) != -1; } GType moo_pane_position_get_type (void) { static GType type = 0; if (!type) { static const GEnumValue values[] = { { MOO_PANE_POS_LEFT, (char*) "MOO_PANE_POS_LEFT", (char*) "left" }, { MOO_PANE_POS_RIGHT, (char*) "MOO_PANE_POS_RIGHT", (char*) "right" }, { MOO_PANE_POS_TOP, (char*) "MOO_PANE_POS_TOP", (char*) "top" }, { MOO_PANE_POS_BOTTOM, (char*) "MOO_PANE_POS_BOTTOM", (char*) "bottom" }, { 0, NULL, NULL } }; type = g_enum_register_static ("MooPanePosition", values); } return type; } GSList *moo_paned_get_panes (MooPaned *paned) { GSList *list = NULL, *l; Pane *pane; g_return_val_if_fail (MOO_IS_PANED (paned), NULL); for (l = paned->priv->panes; l != NULL; l = l->next) { pane = l->data; list = g_slist_prepend (list, pane->child); } return g_slist_reverse (list); } static gboolean handle_button_press (GtkWidget *widget, GdkEventButton *event, MooPaned *paned) { #if 1 GdkCursor *cursor; #endif if (event->button != 1 || event->type != GDK_BUTTON_PRESS) return FALSE; if (!paned->priv->enable_handle_drag) return FALSE; g_return_val_if_fail (!paned->priv->handle_in_drag, FALSE); g_return_val_if_fail (!paned->priv->handle_button_pressed, FALSE); paned->priv->handle_button_pressed = TRUE; paned->priv->handle_drag_start_x = event->x; paned->priv->handle_drag_start_y = event->y; #if 1 cursor = gdk_cursor_new (paned->priv->handle_cursor_type); g_return_val_if_fail (cursor != NULL, TRUE); gdk_window_set_cursor (widget->window, cursor); gdk_cursor_unref (cursor); #endif return TRUE; } static gboolean handle_motion (GtkWidget *widget, GdkEventMotion *event, MooPaned *paned) { Pane *pane; if (!paned->priv->handle_button_pressed) return FALSE; pane = g_object_get_data (G_OBJECT (widget), "moo-pane"); g_return_val_if_fail (pane != NULL && pane->child != NULL, FALSE); if (!paned->priv->handle_in_drag) { if (!gtk_drag_check_threshold (widget, paned->priv->handle_drag_start_x, paned->priv->handle_drag_start_y, event->x, event->y)) return FALSE; paned->priv->handle_in_drag = TRUE; g_signal_emit (paned, signals[HANDLE_DRAG_START], 0, pane->child); } g_signal_emit (paned, signals[HANDLE_DRAG_MOTION], 0, pane->child); return TRUE; } static gboolean handle_button_release (GtkWidget *widget, G_GNUC_UNUSED GdkEventButton *event, MooPaned *paned) { Pane *pane; if (paned->priv->handle_button_pressed) { #if 1 gdk_window_set_cursor (widget->window, NULL); #endif paned->priv->handle_button_pressed = FALSE; } if (!paned->priv->handle_in_drag) return FALSE; paned->priv->handle_in_drag = FALSE; pane = g_object_get_data (G_OBJECT (widget), "moo-pane"); g_return_val_if_fail (pane != NULL && pane->child != NULL, FALSE); g_signal_emit (paned, signals[HANDLE_DRAG_END], 0, pane->child); return TRUE; } #define HANDLE_HEIGHT 12 static gboolean handle_expose (GtkWidget *widget, GdkEventExpose *event, MooPaned *paned) { int height; if (!paned->priv->enable_handle_drag) return FALSE; height = MIN (widget->allocation.height, HANDLE_HEIGHT); gtk_paint_handle (widget->style, widget->window, widget->state, GTK_SHADOW_ETCHED_IN, &event->area, widget, "moo-pane-handle", 0, (widget->allocation.height - height) / 2, widget->allocation.width, height, GTK_ORIENTATION_HORIZONTAL); return TRUE; } static void handle_realize (GtkWidget *widget, MooPaned *paned) { #if 0 GdkCursor *cursor; #endif g_return_if_fail (widget->window != NULL); g_return_if_fail (MOO_IS_PANED (paned)); if (!paned->priv->enable_handle_drag) return; #if 0 cursor = gdk_cursor_new (paned->priv->handle_cursor_type); g_return_if_fail (cursor != NULL); gdk_window_set_cursor (widget->window, cursor); gdk_cursor_unref (cursor); #endif } static void moo_paned_set_handle_cursor_type (MooPaned *paned, GdkCursorType cursor_type, gboolean set) { GSList *l; GdkCursor *cursor = NULL; for (l = paned->priv->panes; l != NULL; l = l->next) { Pane *pane = l->data; if (pane->handle && pane->handle->window) { #if 0 if (set && !cursor) { cursor = gdk_cursor_new (cursor_type); g_return_if_fail (cursor != NULL); } gdk_window_set_cursor (pane->handle->window, cursor); #endif } } if (set) { paned->priv->handle_cursor_type = cursor_type; g_object_notify (G_OBJECT (paned), "handle-cursor-type"); } if (cursor) gdk_cursor_unref (cursor); } static void label_icon_destroyed (GtkWidget *icon, MooPaneLabel *label) { g_return_if_fail (label->icon_widget == icon); g_object_unref (label->icon_widget); label->icon_widget = NULL; } MooPaneLabel* moo_pane_label_new (const char *stock_id, GdkPixbuf *pixbuf, GtkWidget *icon, const char *text, const char *window_title) { MooPaneLabel *label = g_new0 (MooPaneLabel, 1); label->icon_stock_id = g_strdup (stock_id); label->label = g_strdup (text); label->window_title = g_strdup (window_title); if (pixbuf) label->icon_pixbuf = g_object_ref (pixbuf); if (icon) { label->icon_widget = icon; g_object_ref (icon); gtk_object_sink (GTK_OBJECT (icon)); g_signal_connect (icon, "destroy", G_CALLBACK (label_icon_destroyed), label); } return label; } MooPaneLabel* moo_pane_label_copy (MooPaneLabel *label) { MooPaneLabel *copy; g_return_val_if_fail (label != NULL, NULL); copy = g_new0 (MooPaneLabel, 1); copy->icon_stock_id = g_strdup (label->icon_stock_id); copy->label = g_strdup (label->label); copy->window_title = g_strdup (label->window_title); if (label->icon_pixbuf) copy->icon_pixbuf = g_object_ref (label->icon_pixbuf); if (label->icon_widget) { copy->icon_widget = label->icon_widget; g_object_ref (copy->icon_widget); gtk_object_sink (GTK_OBJECT (copy->icon_widget)); g_signal_connect (copy->icon_widget, "destroy", G_CALLBACK (label_icon_destroyed), copy); } return copy; } void moo_pane_label_free (MooPaneLabel *label) { if (label) { g_free (label->icon_stock_id); g_free (label->label); g_free (label->window_title); if (label->icon_pixbuf) g_object_unref (label->icon_pixbuf); if (label->icon_widget) { g_signal_handlers_disconnect_by_func (label->icon_widget, (gpointer) label_icon_destroyed, label); g_object_unref (label->icon_widget); } g_free (label); } } static gboolean pane_window_delete_event (GtkWidget *window, G_GNUC_UNUSED GdkEvent *event, MooPaned *paned) { Pane *pane; int index_; g_return_val_if_fail (MOO_IS_PANED (paned), FALSE); pane = g_object_get_data (G_OBJECT (window), "moo-pane"); g_return_val_if_fail (pane != NULL, FALSE); index_ = g_slist_index (paned->priv->panes, pane); g_assert (index_ >= 0); g_assert (pane->params->detached); g_assert (pane->window == window); moo_paned_attach_pane (paned, index_); return TRUE; } /* XXX gtk_widget_reparent() doesn't work here for some reasons */ /* shouldn't it work now, as I fixed GTK_NO_WINDOW flag? */ static void reparent (GtkWidget *widget, GtkWidget *old_container, GtkWidget *new_container) { g_object_ref (widget); gtk_container_remove (GTK_CONTAINER (old_container), widget); gtk_container_add (GTK_CONTAINER (new_container), widget); g_object_unref (widget); } void moo_paned_detach_pane (MooPaned *paned, guint index_) { g_return_if_fail (MOO_IS_PANED (paned)); g_signal_emit (paned, signals[DETACH_PANE], 0, index_); } static void create_pane_window (MooPaned *paned, Pane *pane) { int width = -1; int height = -1; GtkWidget *frame; GtkWindow *window; if (pane->window) return; pane->window = gtk_window_new (GTK_WINDOW_TOPLEVEL); window = GTK_WINDOW (pane->window); if (pane->label->window_title) gtk_window_set_title (window, pane->label->window_title); if (pane->label->icon_pixbuf) gtk_window_set_icon (window, pane->label->icon_pixbuf); else if (pane->label->icon_stock_id) _moo_window_set_icon_from_stock (window, pane->label->icon_stock_id); switch (paned->priv->pane_position) { case MOO_PANE_POS_LEFT: case MOO_PANE_POS_RIGHT: width = paned->priv->position; height = GTK_WIDGET(paned)->allocation.height; break; case MOO_PANE_POS_TOP: case MOO_PANE_POS_BOTTOM: height = paned->priv->position; width = GTK_WIDGET(paned)->allocation.width; break; } gtk_window_set_default_size (window, width, height); g_signal_connect (window, "delete-event", G_CALLBACK (pane_window_delete_event), paned); frame = create_frame_widget (paned, pane, FALSE); gtk_widget_show (frame); gtk_container_add (GTK_CONTAINER (pane->window), frame); g_object_set_data (G_OBJECT (pane->window), "moo-pane", pane); g_object_set_data (G_OBJECT (pane->keep_on_top_button), "moo-pane", pane); g_signal_connect (pane->keep_on_top_button, "toggled", G_CALLBACK (keep_on_top_button_toggled), paned); g_signal_connect (pane->window, "configure-event", G_CALLBACK (pane_window_configure), paned); } static void moo_paned_detach_pane_real (MooPaned *paned, guint index_) { Pane *pane; GtkWidget *widget = GTK_WIDGET (paned); gboolean visible; pane = get_nth_pane (paned, index_); g_return_if_fail (pane != NULL); if (pane->params->detached) return; if (pane == paned->priv->current_pane) moo_paned_hide_pane (paned); pane->params->detached = TRUE; create_pane_window (paned, pane); reparent (pane->child, pane->child_holder, pane->window_child_holder); if (pane->params->keep_on_top) { GtkWidget *parent = gtk_widget_get_toplevel (widget); if (GTK_IS_WINDOW (parent)) gtk_window_set_transient_for (GTK_WINDOW (pane->window), GTK_WINDOW (parent)); } else { gtk_window_set_transient_for (GTK_WINDOW (pane->window), NULL); } if (pane->focus_child) gtk_widget_grab_focus (pane->focus_child); else gtk_widget_child_focus (pane->child, GTK_DIR_TAB_FORWARD); g_object_get (pane->window, "visible", &visible, NULL); if (!visible && pane->params->window_position.width > 0 && pane->params->window_position.height > 0) { gtk_window_move (GTK_WINDOW (pane->window), pane->params->window_position.x, pane->params->window_position.y); gtk_window_set_default_size (GTK_WINDOW (pane->window), pane->params->window_position.width, pane->params->window_position.height); } gtk_window_present (GTK_WINDOW (pane->window)); gtk_widget_queue_resize (GTK_WIDGET (paned)); pane_params_changed (paned, pane); } void moo_paned_attach_pane (MooPaned *paned, guint index_) { g_return_if_fail (MOO_IS_PANED (paned)); g_signal_emit (paned, signals[ATTACH_PANE], 0, index_); } static void moo_paned_attach_pane_real (MooPaned *paned, guint index_) { Pane *pane; pane = get_nth_pane (paned, index_); g_return_if_fail (pane != NULL); if (!pane->params->detached) return; pane->params->detached = FALSE; if (pane->focus_child) g_object_remove_weak_pointer (G_OBJECT (pane->focus_child), (gpointer*) &pane->focus_child); pane->focus_child = find_focus (pane->child); if (pane->focus_child) g_object_add_weak_pointer (G_OBJECT (pane->focus_child), (gpointer*) &pane->focus_child); reparent (pane->child, pane->window_child_holder, pane->child_holder); gtk_widget_hide (pane->window); gtk_widget_queue_resize (GTK_WIDGET (paned)); pane_params_changed (paned, pane); } static void moo_paned_set_enable_detaching (MooPaned *paned, gboolean enable) { GSList *l; if ((paned->priv->enable_detaching && enable) || (!paned->priv->enable_detaching && !enable)) return; paned->priv->enable_detaching = enable ? TRUE : FALSE; for (l = paned->priv->panes; l != NULL; l = l->next) { Pane *pane = l->data; gboolean visible = enable && pane->detachable; g_object_set (pane->detach_button, "visible", visible, NULL); } g_object_notify (G_OBJECT (paned), "enable-detaching"); } static void attach_button_clicked (GtkWidget *button, MooPaned *paned) { Pane *pane; int index_; g_return_if_fail (MOO_IS_PANED (paned)); pane = g_object_get_data (G_OBJECT (button), "moo-pane"); index_ = pane_index (paned, pane); g_return_if_fail (index_ >= 0); moo_paned_attach_pane (paned, index_); paned->priv->dont_move_focus = TRUE; moo_paned_open_pane (paned, index_); paned->priv->dont_move_focus = TRUE; if (pane->focus_child) gtk_widget_grab_focus (pane->focus_child); else if (!gtk_widget_child_focus (pane->child, GTK_DIR_TAB_FORWARD)) gtk_widget_grab_focus (pane->button); } static void keep_on_top_button_toggled (GtkToggleButton *button, MooPaned *paned) { Pane *pane; gboolean active; g_return_if_fail (MOO_IS_PANED (paned)); pane = g_object_get_data (G_OBJECT (button), "moo-pane"); g_return_if_fail (pane_index (paned, pane) >= 0); active = gtk_toggle_button_get_active (button); pane->params->keep_on_top = active; if (pane->params->keep_on_top) { GtkWidget *parent = gtk_widget_get_toplevel (GTK_WIDGET (paned)); if (GTK_IS_WINDOW (parent)) gtk_window_set_transient_for (GTK_WINDOW (pane->window), GTK_WINDOW (parent)); } else { gtk_window_set_transient_for (GTK_WINDOW (pane->window), NULL); } pane_params_changed (paned, pane); } static gboolean pane_window_configure (GtkWindow *window, GdkEventConfigure *event, MooPaned *paned) { Pane *pane = g_object_get_data (G_OBJECT (window), "moo-pane"); g_return_val_if_fail (g_slist_find (paned->priv->panes, pane) != NULL, FALSE); pane->params->window_position.x = event->x; pane->params->window_position.y = event->y; pane->params->window_position.width = event->width; pane->params->window_position.height = event->height; pane_params_changed (paned, pane); return FALSE; } MooPaneParams* moo_pane_params_new (void) { return g_new0 (MooPaneParams, 1); } MooPaneParams* moo_pane_params_copy (MooPaneParams *params) { MooPaneParams *copy; g_return_val_if_fail (params != NULL, NULL); copy = g_new (MooPaneParams, 1); memcpy (copy, params, sizeof (MooPaneParams)); return copy; } void moo_pane_params_free (MooPaneParams *params) { g_free (params); } GType moo_pane_label_get_type (void) { static GType type = 0; if (!type) type = g_boxed_type_register_static ("MooPaneLabel", (GBoxedCopyFunc) moo_pane_label_copy, (GBoxedFreeFunc) moo_pane_label_free); return type; } GType moo_pane_params_get_type (void) { static GType type = 0; if (!type) type = g_boxed_type_register_static ("MooPaneParams", (GBoxedCopyFunc) moo_pane_params_copy, (GBoxedFreeFunc) moo_pane_params_free); return type; } static void pane_params_changed (MooPaned *paned, Pane *pane) { if (!paned->priv->params_changed_blocked) g_signal_emit (paned, signals[PANE_PARAMS_CHANGED], 0, (guint) pane_index (paned, pane)); } static void block_params_changed (MooPaned *paned) { paned->priv->params_changed_blocked = TRUE; } static void unblock_params_changed (MooPaned *paned) { paned->priv->params_changed_blocked = FALSE; } MooPaneParams* moo_paned_get_pane_params (MooPaned *paned, guint index_) { Pane *pane; g_return_val_if_fail (MOO_IS_PANED (paned), NULL); pane = get_nth_pane (paned, index_); g_return_val_if_fail (pane != NULL, NULL); return moo_pane_params_copy (pane->params); } void moo_paned_set_pane_params (MooPaned *paned, guint index_, MooPaneParams *params) { Pane *pane; MooPaneParams *old_params; g_return_if_fail (MOO_IS_PANED (paned)); g_return_if_fail (params != NULL); pane = get_nth_pane (paned, index_); g_return_if_fail (pane != NULL); old_params = pane->params; pane->params = moo_pane_params_copy (params); if (old_params->detached != params->detached) { pane->params->detached = old_params->detached; if (old_params->detached) moo_paned_attach_pane (paned, index_); else moo_paned_detach_pane (paned, index_); } moo_pane_params_free (old_params); } static gboolean moo_paned_key_press (GtkWidget *widget, GdkEventKey *event) { static int delta = 5; int add = 0; guint mask = GDK_SHIFT_MASK | GDK_CONTROL_MASK | GDK_MOD1_MASK; MooPaned *paned = MOO_PANED (widget); if ((event->state & mask) == (GDK_CONTROL_MASK | GDK_MOD1_MASK)) { switch (event->keyval) { case GDK_Up: case GDK_KP_Up: if (paned->priv->pane_position == MOO_PANE_POS_TOP) add = -delta; else if (paned->priv->pane_position == MOO_PANE_POS_BOTTOM) add = delta; break; case GDK_Down: case GDK_KP_Down: if (paned->priv->pane_position == MOO_PANE_POS_TOP) add = delta; else if (paned->priv->pane_position == MOO_PANE_POS_BOTTOM) add = -delta; break; case GDK_Left: case GDK_KP_Left: if (paned->priv->pane_position == MOO_PANE_POS_LEFT) add = -delta; else if (paned->priv->pane_position == MOO_PANE_POS_RIGHT) add = delta; break; case GDK_Right: case GDK_KP_Right: if (paned->priv->pane_position == MOO_PANE_POS_LEFT) add = delta; else if (paned->priv->pane_position == MOO_PANE_POS_RIGHT) add = -delta; break; } } if (!add) return GTK_WIDGET_CLASS(moo_paned_parent_class)->key_press_event (widget, event); moo_paned_set_pane_size (paned, moo_paned_get_pane_size (paned) + add); return TRUE; }