medit/moo/mooutils/moobigpaned.c

1030 lines
32 KiB
C

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4; coding: utf-8 -*-
*
* moobigpaned.c
*
* Copyright (C) 2004-2006 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 "moobigpaned.h"
#include MOO_MARSHALS_H
static void moo_big_paned_finalize (GObject *object);
static void moo_big_paned_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec);
static void moo_big_paned_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec);
static gboolean moo_big_paned_expose (GtkWidget *widget,
GdkEventExpose *event,
MooBigPaned *paned);
static void child_open_pane (GtkWidget *child,
guint index,
MooBigPaned *paned);
static void child_hide_pane (GtkWidget *child,
MooBigPaned *paned);
static void child_attach_pane (GtkWidget *child,
guint index,
MooBigPaned *paned);
static void child_detach_pane (GtkWidget *child,
guint index,
MooBigPaned *paned);
static void child_pane_params_changed (GtkWidget *child,
guint index,
MooBigPaned *paned);
static void child_set_pane_size (GtkWidget *child,
int size,
MooBigPaned *paned);
static gboolean check_children_order (MooBigPaned *paned);
static void handle_drag_start (MooPaned *child,
GtkWidget *pane_widget,
MooBigPaned *paned);
static void handle_drag_motion (MooPaned *child,
GtkWidget *pane_widget,
MooBigPaned *paned);
static void handle_drag_end (MooPaned *child,
GtkWidget *pane_widget,
MooBigPaned *paned);
/* MOO_TYPE_BIG_PANED */
G_DEFINE_TYPE (MooBigPaned, moo_big_paned, GTK_TYPE_FRAME)
enum {
PROP_0,
PROP_PANE_ORDER,
PROP_ENABLE_HANDLE_DRAG,
PROP_ENABLE_DETACHING,
PROP_HANDLE_CURSOR_TYPE
};
enum {
OPEN_PANE,
HIDE_PANE,
ATTACH_PANE,
DETACH_PANE,
PANE_PARAMS_CHANGED,
SET_PANE_SIZE,
NUM_SIGNALS
};
static guint signals[NUM_SIGNALS];
static void moo_big_paned_class_init (MooBigPanedClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->finalize = moo_big_paned_finalize;
gobject_class->set_property = moo_big_paned_set_property;
gobject_class->get_property = moo_big_paned_get_property;
g_object_class_install_property (gobject_class,
PROP_PANE_ORDER,
g_param_spec_pointer ("pane-order",
"pane-order",
"pane-order",
G_PARAM_READWRITE));
g_object_class_install_property (gobject_class,
PROP_ENABLE_HANDLE_DRAG,
g_param_spec_boolean ("enable-handle-drag",
"enable-handle-drag",
"enable-handle-drag",
TRUE,
G_PARAM_CONSTRUCT | G_PARAM_WRITABLE));
g_object_class_install_property (gobject_class,
PROP_ENABLE_DETACHING,
g_param_spec_boolean ("enable-detaching",
"enable-detaching",
"enable-detaching",
FALSE,
G_PARAM_CONSTRUCT | G_PARAM_WRITABLE));
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));
signals[OPEN_PANE] =
g_signal_new ("open-pane",
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (MooBigPanedClass, open_pane),
NULL, NULL,
_moo_marshal_VOID__ENUM_UINT,
G_TYPE_NONE, 2,
MOO_TYPE_PANE_POSITION, G_TYPE_UINT);
signals[HIDE_PANE] =
g_signal_new ("hide-pane",
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (MooBigPanedClass, hide_pane),
NULL, NULL,
_moo_marshal_VOID__ENUM,
G_TYPE_NONE, 1,
MOO_TYPE_PANE_POSITION);
signals[ATTACH_PANE] =
g_signal_new ("attach-pane",
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (MooBigPanedClass, attach_pane),
NULL, NULL,
_moo_marshal_VOID__ENUM_UINT,
G_TYPE_NONE, 2,
MOO_TYPE_PANE_POSITION, G_TYPE_UINT);
signals[DETACH_PANE] =
g_signal_new ("detach-pane",
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (MooBigPanedClass, detach_pane),
NULL, NULL,
_moo_marshal_VOID__ENUM_UINT,
G_TYPE_NONE, 2,
MOO_TYPE_PANE_POSITION, G_TYPE_UINT);
signals[PANE_PARAMS_CHANGED] =
g_signal_new ("pane-params-changed",
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (MooBigPanedClass, pane_params_changed),
NULL, NULL,
_moo_marshal_VOID__ENUM_UINT,
G_TYPE_NONE, 2,
MOO_TYPE_PANE_POSITION, G_TYPE_UINT);
signals[SET_PANE_SIZE] =
g_signal_new ("set-pane-size",
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (MooBigPanedClass, set_pane_size),
NULL, NULL,
_moo_marshal_VOID__INT,
G_TYPE_NONE, 1,
G_TYPE_INT);
}
#define NTH_CHILD(paned,n) paned->paned[paned->order[n]]
static void moo_big_paned_init (MooBigPaned *paned)
{
int i;
paned->drop_pos = -1;
/* XXX destroy */
for (i = 0; i < 4; ++i)
{
GtkWidget *child;
paned->paned[i] = child =
g_object_new (MOO_TYPE_PANED,
"pane-position", (MooPanePosition) i,
NULL);
g_object_ref (child);
gtk_object_sink (GTK_OBJECT (child));
gtk_widget_show (child);
g_signal_connect (child, "hide-pane",
G_CALLBACK (child_hide_pane),
paned);
g_signal_connect (child, "open-pane",
G_CALLBACK (child_open_pane),
paned);
g_signal_connect (child, "attach-pane",
G_CALLBACK (child_attach_pane),
paned);
g_signal_connect (child, "detach-pane",
G_CALLBACK (child_detach_pane),
paned);
g_signal_connect_after (child, "pane-params-changed",
G_CALLBACK (child_pane_params_changed),
paned);
g_signal_connect_after (child, "set-pane-size",
G_CALLBACK (child_set_pane_size),
paned);
g_signal_connect (child, "handle-drag-start",
G_CALLBACK (handle_drag_start),
paned);
g_signal_connect (child, "handle-drag-motion",
G_CALLBACK (handle_drag_motion),
paned);
g_signal_connect (child, "handle-drag-end",
G_CALLBACK (handle_drag_end),
paned);
}
paned->order[0] = MOO_PANE_POS_LEFT;
paned->order[1] = MOO_PANE_POS_RIGHT;
paned->order[2] = MOO_PANE_POS_TOP;
paned->order[3] = MOO_PANE_POS_BOTTOM;
paned->inner = NTH_CHILD (paned, 3);
paned->outer = NTH_CHILD (paned, 0);
gtk_container_add (GTK_CONTAINER (paned), NTH_CHILD (paned, 0));
for (i = 0; i < 3; ++i)
gtk_container_add (GTK_CONTAINER (NTH_CHILD (paned, i)), NTH_CHILD (paned, i+1));
g_assert (check_children_order (paned));
}
static gboolean check_children_order (MooBigPaned *paned)
{
int i;
if (GTK_BIN(paned)->child != NTH_CHILD (paned, 0))
return FALSE;
for (i = 0; i < 3; ++i)
if (GTK_BIN (NTH_CHILD (paned, i))->child != NTH_CHILD (paned, i+1))
return FALSE;
return TRUE;
}
void moo_big_paned_set_pane_order (MooBigPaned *paned,
int *order)
{
MooPanePosition new_order[4] = {8, 8, 8, 8};
int i;
GtkWidget *child;
g_return_if_fail (MOO_IS_BIG_PANED (paned));
g_return_if_fail (order != NULL);
for (i = 0; i < 4; ++i)
{
g_return_if_fail (new_order[i] >= 4);
g_return_if_fail (0 <= order[i] && order[i] < 4);
new_order[i] = order[i];
}
g_return_if_fail (check_children_order (paned));
for (i = 0; i < 4; ++i)
{
if (new_order[i] != paned->order[i])
break;
}
if (i == 4)
return;
child = moo_big_paned_get_child (paned);
if (child)
g_object_ref (child);
gtk_container_remove (GTK_CONTAINER (paned), NTH_CHILD (paned, 0));
for (i = 0; i < 3; ++i)
gtk_container_remove (GTK_CONTAINER (NTH_CHILD (paned, i)), NTH_CHILD (paned, i+1));
if (child)
gtk_container_remove (GTK_CONTAINER (NTH_CHILD (paned, 3)), child);
for (i = 0; i < 4; ++i)
paned->order[i] = new_order[i];
gtk_container_add (GTK_CONTAINER (paned), NTH_CHILD (paned, 0));
for (i = 0; i < 3; ++i)
gtk_container_add (GTK_CONTAINER (NTH_CHILD (paned, i)), NTH_CHILD (paned, i+1));
paned->inner = NTH_CHILD (paned, 3);
paned->outer = NTH_CHILD (paned, 0);
if (child)
{
gtk_container_add (GTK_CONTAINER (paned->inner), child);
g_object_unref (child);
}
g_assert (check_children_order (paned));
g_object_notify (G_OBJECT (paned), "pane-order");
}
static void moo_big_paned_finalize (GObject *object)
{
MooBigPaned *paned = MOO_BIG_PANED (object);
int i;
for (i = 0; i < 4; ++i)
g_object_unref (paned->paned[i]);
/* XXX */
if (paned->drop_outline)
{
gdk_window_set_user_data (paned->drop_outline, NULL);
gdk_window_destroy (paned->drop_outline);
}
G_OBJECT_CLASS (moo_big_paned_parent_class)->finalize (object);
}
GtkWidget*
moo_big_paned_new (void)
{
return g_object_new (MOO_TYPE_BIG_PANED, NULL);
}
static void
child_open_pane (GtkWidget *child,
guint index,
MooBigPaned *paned)
{
MooPanePosition pos;
g_object_get (child, "pane-position", &pos, NULL);
g_return_if_fail (paned->paned[pos] == child);
g_signal_emit (paned, signals[OPEN_PANE], 0, pos, index);
}
static void
child_hide_pane (GtkWidget *child,
MooBigPaned *paned)
{
MooPanePosition pos;
g_object_get (child, "pane-position", &pos, NULL);
g_return_if_fail (paned->paned[pos] == child);
g_signal_emit (paned, signals[HIDE_PANE], 0, pos);
}
static void
child_attach_pane (GtkWidget *child,
guint index,
MooBigPaned *paned)
{
MooPanePosition pos;
g_object_get (child, "pane-position", &pos, NULL);
g_return_if_fail (paned->paned[pos] == child);
g_signal_emit (paned, signals[ATTACH_PANE], 0, pos, index);
}
static void
child_detach_pane (GtkWidget *child,
guint index,
MooBigPaned *paned)
{
MooPanePosition pos;
g_object_get (child, "pane-position", &pos, NULL);
g_return_if_fail (paned->paned[pos] == child);
g_signal_emit (paned, signals[DETACH_PANE], 0, pos, index);
}
static void
child_pane_params_changed (GtkWidget *child,
guint index,
MooBigPaned *paned)
{
MooPanePosition pos;
g_object_get (child, "pane-position", &pos, NULL);
g_return_if_fail (paned->paned[pos] == child);
g_signal_emit (paned, signals[PANE_PARAMS_CHANGED], 0, pos, index);
}
static void
child_set_pane_size (GtkWidget *child,
int size,
MooBigPaned *paned)
{
MooPanePosition pos;
g_object_get (child, "pane-position", &pos, NULL);
g_return_if_fail (paned->paned[pos] == child);
g_signal_emit (paned, signals[SET_PANE_SIZE], 0, pos, size);
}
int moo_big_paned_insert_pane (MooBigPaned *paned,
GtkWidget *pane_widget,
MooPaneLabel *pane_label,
MooPanePosition position,
int index_)
{
g_return_val_if_fail (MOO_IS_BIG_PANED (paned), -1);
g_return_val_if_fail (GTK_IS_WIDGET (pane_widget), -1);
g_return_val_if_fail (position < 4, -1);
return moo_paned_insert_pane (MOO_PANED (paned->paned[position]),
pane_widget, pane_label, index_);
}
void moo_big_paned_add_child (MooBigPaned *paned,
GtkWidget *child)
{
g_return_if_fail (MOO_IS_BIG_PANED (paned));
gtk_container_add (GTK_CONTAINER (paned->inner), child);
}
void moo_big_paned_remove_child (MooBigPaned *paned)
{
g_return_if_fail (MOO_IS_BIG_PANED (paned));
gtk_container_remove (GTK_CONTAINER (paned->inner), GTK_BIN(paned->inner)->child);
}
GtkWidget *moo_big_paned_get_child (MooBigPaned *paned)
{
g_return_val_if_fail (MOO_IS_BIG_PANED (paned), NULL);
return GTK_BIN(paned->inner)->child;
}
gboolean
moo_big_paned_remove_pane (MooBigPaned *paned,
GtkWidget *widget)
{
MooPaned *child;
g_return_val_if_fail (MOO_IS_BIG_PANED (paned), FALSE);
g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
if (!moo_big_paned_find_pane (paned, widget, &child, NULL))
return FALSE;
return moo_paned_remove_pane (child, widget);
}
void
moo_big_paned_open_pane (MooBigPaned *paned,
GtkWidget *widget)
{
int idx;
MooPaned *child;
g_return_if_fail (MOO_IS_BIG_PANED (paned));
g_return_if_fail (GTK_IS_WIDGET (widget));
if (!moo_big_paned_find_pane (paned, widget, &child, &idx))
g_return_if_reached ();
return moo_paned_open_pane (child, idx);
}
void
moo_big_paned_hide_pane (MooBigPaned *paned,
GtkWidget *widget)
{
MooPaned *child;
g_return_if_fail (MOO_IS_BIG_PANED (paned));
g_return_if_fail (GTK_IS_WIDGET (widget));
if (!moo_big_paned_find_pane (paned, widget, &child, NULL))
g_return_if_reached ();
return moo_paned_hide_pane (child);
}
void
moo_big_paned_present_pane (MooBigPaned *paned,
GtkWidget *widget)
{
int idx;
MooPaned *child;
g_return_if_fail (MOO_IS_BIG_PANED (paned));
g_return_if_fail (GTK_IS_WIDGET (widget));
if (!moo_big_paned_find_pane (paned, widget, &child, &idx))
g_return_if_reached ();
return moo_paned_present_pane (child, idx);
}
GtkWidget*
moo_big_paned_get_button (MooBigPaned *paned,
GtkWidget *widget)
{
MooPaned *child;
g_return_val_if_fail (MOO_IS_BIG_PANED (paned), NULL);
g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
if (!moo_big_paned_find_pane (paned, widget, &child, NULL))
return NULL;
return moo_paned_get_button (child, widget);
}
gboolean
moo_big_paned_find_pane (MooBigPaned *paned,
GtkWidget *widget,
MooPaned **child_paned,
int *pane_index)
{
int i, idx;
g_return_val_if_fail (MOO_IS_BIG_PANED (paned), FALSE);
g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
for (i = 0; i < 4; ++i)
{
idx = moo_paned_get_pane_num (MOO_PANED (paned->paned[i]), widget);
if (idx >= 0)
{
if (pane_index)
*pane_index = idx;
if (child_paned)
*child_paned = MOO_PANED (paned->paned[i]);
return TRUE;
}
}
return FALSE;
}
static void moo_big_paned_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
MooBigPaned *paned = MOO_BIG_PANED (object);
int i;
switch (prop_id)
{
case PROP_PANE_ORDER:
moo_big_paned_set_pane_order (paned, g_value_get_pointer (value));
break;
case PROP_ENABLE_HANDLE_DRAG:
for (i = 0; i < 4; ++i)
g_object_set (paned->paned[i],
"enable-handle-drag",
g_value_get_boolean (value),
NULL);
break;
case PROP_ENABLE_DETACHING:
for (i = 0; i < 4; ++i)
g_object_set (paned->paned[i],
"enable-detaching",
g_value_get_boolean (value),
NULL);
break;
case PROP_HANDLE_CURSOR_TYPE:
for (i = 0; i < 4; ++i)
g_object_set (paned->paned[i], "handle-cursor-type",
(GdkCursorType) g_value_get_enum (value),
NULL);
g_object_notify (object, "handle-cursor-type");
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void moo_big_paned_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
MooBigPaned *paned = MOO_BIG_PANED (object);
GdkCursorType cursor_type;
switch (prop_id)
{
case PROP_PANE_ORDER:
g_value_set_pointer (value, paned->order);
break;
case PROP_HANDLE_CURSOR_TYPE:
g_object_get (paned->paned[0], "handle-cursor-type",
&cursor_type, NULL);
g_value_set_enum (value, cursor_type);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
GtkWidget*
moo_big_paned_get_pane (MooBigPaned *paned,
MooPanePosition position,
int index_)
{
g_return_val_if_fail (MOO_IS_BIG_PANED (paned), NULL);
g_return_val_if_fail (position < 4, NULL);
return moo_paned_get_nth_pane (MOO_PANED (paned->paned[position]), index_);
}
MooPaneParams*
moo_big_paned_get_pane_params (MooBigPaned *paned,
MooPanePosition position,
guint index_)
{
g_return_val_if_fail (MOO_IS_BIG_PANED (paned), NULL);
g_return_val_if_fail (position < 4, NULL);
return moo_paned_get_pane_params (MOO_PANED (paned->paned[position]),
index_);
}
void
moo_big_paned_set_pane_params (MooBigPaned *paned,
MooPanePosition position,
guint index_,
MooPaneParams *params)
{
g_return_if_fail (MOO_IS_BIG_PANED (paned));
g_return_if_fail (params != NULL);
g_return_if_fail (position < 4);
moo_paned_set_pane_params (MOO_PANED (paned->paned[position]),
index_, params);
}
/*****************************************************************************/
/* rearranging panes
*/
static void create_drop_outline (MooBigPaned *paned);
static int get_drop_position (MooBigPaned *paned,
MooPaned *child,
int x,
int y);
static void get_drop_area (MooBigPaned *paned,
MooPaned *active_child,
MooPanePosition position,
GdkRectangle *rect);
static void invalidate_drop_outline (MooBigPaned *paned);
static void handle_drag_start (G_GNUC_UNUSED MooPaned *child,
G_GNUC_UNUSED GtkWidget *pane_widget,
MooBigPaned *paned)
{
g_return_if_fail (GTK_WIDGET_REALIZED (paned->outer));
g_signal_connect (paned->outer, "expose-event",
G_CALLBACK (moo_big_paned_expose), paned);
paned->drop_pos = -1;
}
static void handle_drag_motion (MooPaned *child,
G_GNUC_UNUSED GtkWidget *pane_widget,
MooBigPaned *paned)
{
int pos, x, y;
g_return_if_fail (GTK_WIDGET_REALIZED (paned->outer));
gdk_window_get_pointer (paned->outer->window, &x, &y, NULL);
pos = get_drop_position (paned, child, x, y);
if (pos == paned->drop_pos)
return;
if (paned->drop_pos >= 0)
{
invalidate_drop_outline (paned);
g_assert (paned->drop_outline != NULL);
gdk_window_set_user_data (paned->drop_outline, NULL);
gdk_window_destroy (paned->drop_outline);
paned->drop_outline = NULL;
}
paned->drop_pos = pos;
if (pos >= 0)
{
get_drop_area (paned, child, pos, &paned->drop_rect);
invalidate_drop_outline (paned);
g_assert (paned->drop_outline == NULL);
create_drop_outline (paned);
}
}
static void handle_drag_end (MooPaned *child,
GtkWidget *pane_widget,
MooBigPaned *paned)
{
int pos, x, y;
MooPanePosition old_pos;
MooPaneLabel *label;
g_return_if_fail (GTK_WIDGET_REALIZED (paned->outer));
gdk_window_get_pointer (paned->outer->window, &x, &y, NULL);
pos = get_drop_position (paned, child, x, y);
if (paned->drop_pos >= 0)
{
invalidate_drop_outline (paned);
g_assert (paned->drop_outline != NULL);
gdk_window_set_user_data (paned->drop_outline, NULL);
gdk_window_destroy (paned->drop_outline);
paned->drop_outline = NULL;
}
paned->drop_pos = -1;
g_signal_handlers_disconnect_by_func (paned->outer,
(gpointer) moo_big_paned_expose,
paned);
if (pos < 0)
return;
g_object_get (child, "pane-position", &old_pos, NULL);
if ((int) old_pos == pos)
return;
label = moo_paned_get_label (child, pane_widget);
g_object_ref (pane_widget);
moo_paned_remove_pane (child, pane_widget);
g_return_if_fail (pane_widget->parent == NULL);
moo_paned_insert_pane (MOO_PANED (paned->paned[pos]), pane_widget, label, -1);
moo_paned_open_pane (MOO_PANED (paned->paned[pos]),
moo_paned_n_panes (MOO_PANED (paned->paned[pos])) - 1);
moo_pane_label_free (label);
g_object_unref (pane_widget);
}
static void get_drop_area (MooBigPaned *paned,
MooPaned *active_child,
MooPanePosition position,
GdkRectangle *rect)
{
int width, height, size = 0;
MooPanePosition active_position;
width = paned->outer->allocation.width;
height = paned->outer->allocation.height;
g_object_get (active_child, "pane-position", &active_position, NULL);
g_return_if_fail (active_position < 4);
if (active_position == position)
{
size = moo_paned_get_pane_size (active_child) +
moo_paned_get_button_box_size (active_child);
}
else
{
switch (position)
{
case MOO_PANE_POS_LEFT:
case MOO_PANE_POS_RIGHT:
size = width / 3;
break;
case MOO_PANE_POS_TOP:
case MOO_PANE_POS_BOTTOM:
size = height / 3;
break;
}
}
switch (position)
{
case MOO_PANE_POS_LEFT:
case MOO_PANE_POS_RIGHT:
rect->y = 0;
rect->width = size;
rect->height = height;
break;
case MOO_PANE_POS_TOP:
case MOO_PANE_POS_BOTTOM:
rect->x = 0;
rect->width = width;
rect->height = size;
break;
}
switch (position)
{
case MOO_PANE_POS_LEFT:
rect->x = 0;
break;
case MOO_PANE_POS_RIGHT:
rect->x = width - size;
break;
case MOO_PANE_POS_TOP:
rect->y = 0;
break;
case MOO_PANE_POS_BOTTOM:
rect->y = height - size;
break;
}
}
#define RECT_POINT_IN(rect,x,y) (x < (rect)->x + (rect)->width && \
y < (rect)->height + (rect)->y && \
x >= (rect)->x && y >= (rect)->y)
static int get_drop_position (MooBigPaned *paned,
MooPaned *child,
int x,
int y)
{
int width, height, i;
MooPanePosition position;
GdkRectangle rect;
gdk_drawable_get_size (GDK_WINDOW (paned->outer->window),
&width, &height);
if (x < 0 || x >= width || y < 0 || y >= height)
return -1;
g_object_get (child, "pane-position", &position, NULL);
g_return_val_if_fail (position < 4, -1);
get_drop_area (paned, child, position, &rect);
if (RECT_POINT_IN (&rect, x, y))
return position;
for (i = 0; i < 4; ++i)
{
if (paned->order[i] == position)
continue;
get_drop_area (paned, child, paned->order[i], &rect);
if (RECT_POINT_IN (&rect, x, y))
return paned->order[i];
}
return -1;
}
static void invalidate_drop_outline (MooBigPaned *paned)
{
GdkRectangle line;
GdkRegion *outline;
outline = gdk_region_new ();
line.x = paned->drop_rect.x;
line.y = paned->drop_rect.y;
line.width = 1;
line.height = paned->drop_rect.height;
gdk_region_union_with_rect (outline, &line);
line.x = paned->drop_rect.x;
line.y = paned->drop_rect.y + paned->drop_rect.height;
line.width = paned->drop_rect.width;
line.height = 1;
gdk_region_union_with_rect (outline, &line);
line.x = paned->drop_rect.x + paned->drop_rect.width;
line.y = paned->drop_rect.y;
line.width = 1;
line.height = paned->drop_rect.height;
gdk_region_union_with_rect (outline, &line);
line.x = paned->drop_rect.x;
line.y = paned->drop_rect.y;
line.width = paned->drop_rect.width;
line.height = 1;
gdk_region_union_with_rect (outline, &line);
gdk_window_invalidate_region (paned->outer->window, outline, TRUE);
gdk_window_invalidate_rect (paned->outer->window, &paned->drop_rect, TRUE);
gdk_region_destroy (outline);
}
static gboolean moo_big_paned_expose (GtkWidget *widget,
GdkEventExpose *event,
MooBigPaned *paned)
{
GTK_WIDGET_CLASS(G_OBJECT_GET_CLASS (widget))->expose_event (widget, event);
if (paned->drop_pos >= 0)
{
g_return_val_if_fail (paned->drop_outline != NULL, FALSE);
gdk_window_show (paned->drop_outline);
gdk_draw_rectangle (paned->drop_outline,
widget->style->fg_gc[GTK_STATE_NORMAL],
FALSE, 0, 0,
paned->drop_rect.width - 1,
paned->drop_rect.height - 1);
}
return TRUE;
}
static GdkBitmap *create_rect_mask (int width,
int height)
{
GdkBitmap *bitmap;
GdkGC *gc;
GdkColor white = {0, 0, 0, 0};
GdkColor black = {!0, !0, !0, !0};
bitmap = gdk_pixmap_new (NULL, width, height, 1);
gc = gdk_gc_new (bitmap);
gdk_gc_set_foreground (gc, &white);
gdk_draw_rectangle (bitmap, gc, TRUE, 0, 0,
width, height);
gdk_gc_set_foreground (gc, &black);
gdk_draw_rectangle (bitmap, gc, FALSE, 0, 0,
width - 1, height - 1);
g_object_unref (gc);
return bitmap;
}
static void create_drop_outline (MooBigPaned *paned)
{
static GdkWindowAttr attributes;
int attributes_mask;
GdkBitmap *mask;
g_return_if_fail (paned->drop_outline == NULL);
attributes.x = paned->drop_rect.x;
attributes.y = paned->drop_rect.y;
attributes.width = paned->drop_rect.width;
attributes.height = paned->drop_rect.height;
attributes.window_type = GDK_WINDOW_CHILD;
attributes.visual = gtk_widget_get_visual (paned->outer);
attributes.colormap = gtk_widget_get_colormap (paned->outer);
attributes.wclass = GDK_INPUT_OUTPUT;
attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
paned->drop_outline = gdk_window_new (paned->outer->window, &attributes, attributes_mask);
gdk_window_set_user_data (paned->drop_outline, paned);
mask = create_rect_mask (paned->drop_rect.width, paned->drop_rect.height);
gdk_window_shape_combine_mask (paned->drop_outline, mask, 0, 0);
g_object_unref (mask);
}