3988 lines
123 KiB
C
3988 lines
123 KiB
C
/*
|
|
* moopaned.c
|
|
*
|
|
* Copyright (C) 2004-2007 by Yevgen Muntyan <muntyan@math.tamu.edu>
|
|
*
|
|
* 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 "mooutils/mooutils-gobject.h"
|
|
#include <string.h>
|
|
#include <gdk/gdkkeysyms.h>
|
|
#include <gtk/gtk.h>
|
|
|
|
|
|
#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 */
|
|
gpointer 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 */
|
|
gpointer 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);
|
|
|
|
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);
|
|
|
|
_moo_stock_init ();
|
|
|
|
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;
|
|
}
|
|
|
|
MOO_OBJECT_REF_SINK (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);
|
|
MOO_OBJECT_REF_SINK (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 (paned->priv->focus_child,
|
|
&paned->priv->focus_child);
|
|
|
|
paned->priv->focus_child = find_focus_child (paned);
|
|
|
|
if (paned->priv->focus_child)
|
|
g_object_add_weak_pointer (paned->priv->focus_child,
|
|
&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 (old_focus_pane->focus_child,
|
|
&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 (old_focus_pane->focus_child,
|
|
&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 (G_UNLIKELY (!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;
|
|
MOO_OBJECT_REF_SINK (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;
|
|
MOO_OBJECT_REF_SINK (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 (pane->focus_child, &pane->focus_child);
|
|
pane->focus_child = find_focus (pane->child);
|
|
if (pane->focus_child)
|
|
g_object_add_weak_pointer (pane->focus_child, &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 (G_UNLIKELY (!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 (G_UNLIKELY (!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;
|
|
}
|