forked from AuroraMiddleware/gtk
b67c5af55b
Requires fixes to GtkContainer and GtkWindow to unmap their children, rather than just withdrawing or hiding the container window. Requires fix to GtkHandleBox to chain up to GtkContainer unmap. Historically we avoided these unmaps for efficiency reasons, but these days it's a bigger problem that there's no way for child widgets to know that one of their ancestors has become unmapped.
1509 lines
43 KiB
C
1509 lines
43 KiB
C
/* GTK - The GIMP Toolkit
|
|
* Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
|
|
* Copyright (C) 1998 Elliot Lee
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the
|
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
* Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
/*
|
|
* Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
|
|
* file for a list of people on the GTK+ Team. See the ChangeLog
|
|
* files for a list of changes. These files are distributed with
|
|
* GTK+ at ftp://ftp.gtk.org/pub/gtk/.
|
|
*/
|
|
|
|
#include "config.h"
|
|
#include <stdlib.h>
|
|
#include "gtkhandlebox.h"
|
|
#include "gtkinvisible.h"
|
|
#include "gtkmain.h"
|
|
#include "gtkmarshalers.h"
|
|
#include "gtkwindow.h"
|
|
#include "gtkprivate.h"
|
|
#include "gtkintl.h"
|
|
|
|
|
|
|
|
struct _GtkHandleBoxPrivate
|
|
{
|
|
/* Properties */
|
|
GtkPositionType handle_position;
|
|
GtkPositionType snap_edge;
|
|
GtkShadowType shadow_type;
|
|
gboolean child_detached;
|
|
/* Properties */
|
|
|
|
GtkAllocation attach_allocation;
|
|
GtkAllocation float_allocation;
|
|
|
|
GdkDevice *grab_device;
|
|
|
|
GdkWindow *bin_window; /* parent window for children */
|
|
GdkWindow *float_window;
|
|
|
|
/* Variables used during a drag
|
|
*/
|
|
gint orig_x;
|
|
gint orig_y;
|
|
|
|
guint float_window_mapped : 1;
|
|
guint in_drag : 1;
|
|
guint shrink_on_detach : 1;
|
|
};
|
|
|
|
enum {
|
|
PROP_0,
|
|
PROP_SHADOW_TYPE,
|
|
PROP_HANDLE_POSITION,
|
|
PROP_SNAP_EDGE,
|
|
PROP_SNAP_EDGE_SET,
|
|
PROP_CHILD_DETACHED
|
|
};
|
|
|
|
#define DRAG_HANDLE_SIZE 10
|
|
#define CHILDLESS_SIZE 25
|
|
#define GHOST_HEIGHT 3
|
|
#define TOLERANCE 5
|
|
|
|
enum {
|
|
SIGNAL_CHILD_ATTACHED,
|
|
SIGNAL_CHILD_DETACHED,
|
|
SIGNAL_LAST
|
|
};
|
|
|
|
/* The algorithm for docking and redocking implemented here
|
|
* has a couple of nice properties:
|
|
*
|
|
* 1) During a single drag, docking always occurs at the
|
|
* the same cursor position. This means that the users
|
|
* motions are reversible, and that you won't
|
|
* undock/dock oscillations.
|
|
*
|
|
* 2) Docking generally occurs at user-visible features.
|
|
* The user, once they figure out to redock, will
|
|
* have useful information about doing it again in
|
|
* the future.
|
|
*
|
|
* Please try to preserve these properties if you
|
|
* change the algorithm. (And the current algorithm
|
|
* is far from ideal). Briefly, the current algorithm
|
|
* for deciding whether the handlebox is docked or not:
|
|
*
|
|
* 1) The decision is done by comparing two rectangles - the
|
|
* allocation if the widget at the start of the drag,
|
|
* and the boundary of hb->bin_window at the start of
|
|
* of the drag offset by the distance that the cursor
|
|
* has moved.
|
|
*
|
|
* 2) These rectangles must have one edge, the "snap_edge"
|
|
* of the handlebox, aligned within TOLERANCE.
|
|
*
|
|
* 3) On the other dimension, the extents of one rectangle
|
|
* must be contained in the extents of the other,
|
|
* extended by tolerance. That is, either we can have:
|
|
*
|
|
* <-TOLERANCE-|--------bin_window--------------|-TOLERANCE->
|
|
* <--------float_window-------------------->
|
|
*
|
|
* or we can have:
|
|
*
|
|
* <-TOLERANCE-|------float_window--------------|-TOLERANCE->
|
|
* <--------bin_window-------------------->
|
|
*/
|
|
|
|
static void gtk_handle_box_set_property (GObject *object,
|
|
guint param_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec);
|
|
static void gtk_handle_box_get_property (GObject *object,
|
|
guint param_id,
|
|
GValue *value,
|
|
GParamSpec *pspec);
|
|
static void gtk_handle_box_map (GtkWidget *widget);
|
|
static void gtk_handle_box_unmap (GtkWidget *widget);
|
|
static void gtk_handle_box_realize (GtkWidget *widget);
|
|
static void gtk_handle_box_unrealize (GtkWidget *widget);
|
|
static void gtk_handle_box_style_set (GtkWidget *widget,
|
|
GtkStyle *previous_style);
|
|
static void gtk_handle_box_size_request (GtkWidget *widget,
|
|
GtkRequisition *requisition);
|
|
static void gtk_handle_box_get_preferred_width (GtkWidget *widget,
|
|
gint *minimum,
|
|
gint *natural);
|
|
static void gtk_handle_box_get_preferred_height (GtkWidget *widget,
|
|
gint *minimum,
|
|
gint *natural);
|
|
static void gtk_handle_box_size_allocate (GtkWidget *widget,
|
|
GtkAllocation *real_allocation);
|
|
static void gtk_handle_box_add (GtkContainer *container,
|
|
GtkWidget *widget);
|
|
static void gtk_handle_box_remove (GtkContainer *container,
|
|
GtkWidget *widget);
|
|
static gboolean gtk_handle_box_draw (GtkWidget *widget,
|
|
cairo_t *cr);
|
|
static gboolean gtk_handle_box_button_press (GtkWidget *widget,
|
|
GdkEventButton *event);
|
|
static gboolean gtk_handle_box_motion (GtkWidget *widget,
|
|
GdkEventMotion *event);
|
|
static gboolean gtk_handle_box_delete_event (GtkWidget *widget,
|
|
GdkEventAny *event);
|
|
static void gtk_handle_box_reattach (GtkHandleBox *hb);
|
|
static void gtk_handle_box_end_drag (GtkHandleBox *hb,
|
|
guint32 time);
|
|
|
|
static guint handle_box_signals[SIGNAL_LAST] = { 0 };
|
|
|
|
G_DEFINE_TYPE (GtkHandleBox, gtk_handle_box, GTK_TYPE_BIN)
|
|
|
|
static void
|
|
gtk_handle_box_class_init (GtkHandleBoxClass *class)
|
|
{
|
|
GObjectClass *gobject_class;
|
|
GtkWidgetClass *widget_class;
|
|
GtkContainerClass *container_class;
|
|
|
|
gobject_class = (GObjectClass *) class;
|
|
widget_class = (GtkWidgetClass *) class;
|
|
container_class = (GtkContainerClass *) class;
|
|
|
|
gobject_class->set_property = gtk_handle_box_set_property;
|
|
gobject_class->get_property = gtk_handle_box_get_property;
|
|
|
|
g_object_class_install_property (gobject_class,
|
|
PROP_SHADOW_TYPE,
|
|
g_param_spec_enum ("shadow-type",
|
|
P_("Shadow type"),
|
|
P_("Appearance of the shadow that surrounds the container"),
|
|
GTK_TYPE_SHADOW_TYPE,
|
|
GTK_SHADOW_OUT,
|
|
GTK_PARAM_READWRITE));
|
|
|
|
g_object_class_install_property (gobject_class,
|
|
PROP_HANDLE_POSITION,
|
|
g_param_spec_enum ("handle-position",
|
|
P_("Handle position"),
|
|
P_("Position of the handle relative to the child widget"),
|
|
GTK_TYPE_POSITION_TYPE,
|
|
GTK_POS_LEFT,
|
|
GTK_PARAM_READWRITE));
|
|
|
|
g_object_class_install_property (gobject_class,
|
|
PROP_SNAP_EDGE,
|
|
g_param_spec_enum ("snap-edge",
|
|
P_("Snap edge"),
|
|
P_("Side of the handlebox that's lined up with the docking point to dock the handlebox"),
|
|
GTK_TYPE_POSITION_TYPE,
|
|
GTK_POS_TOP,
|
|
GTK_PARAM_READWRITE));
|
|
|
|
g_object_class_install_property (gobject_class,
|
|
PROP_SNAP_EDGE_SET,
|
|
g_param_spec_boolean ("snap-edge-set",
|
|
P_("Snap edge set"),
|
|
P_("Whether to use the value from the snap_edge property or a value derived from handle_position"),
|
|
FALSE,
|
|
GTK_PARAM_READWRITE));
|
|
|
|
g_object_class_install_property (gobject_class,
|
|
PROP_CHILD_DETACHED,
|
|
g_param_spec_boolean ("child-detached",
|
|
P_("Child Detached"),
|
|
P_("A boolean value indicating whether the handlebox's child is attached or detached."),
|
|
FALSE,
|
|
GTK_PARAM_READABLE));
|
|
|
|
widget_class->map = gtk_handle_box_map;
|
|
widget_class->unmap = gtk_handle_box_unmap;
|
|
widget_class->realize = gtk_handle_box_realize;
|
|
widget_class->unrealize = gtk_handle_box_unrealize;
|
|
widget_class->style_set = gtk_handle_box_style_set;
|
|
widget_class->get_preferred_width = gtk_handle_box_get_preferred_width;
|
|
widget_class->get_preferred_height = gtk_handle_box_get_preferred_height;
|
|
widget_class->size_allocate = gtk_handle_box_size_allocate;
|
|
widget_class->draw = gtk_handle_box_draw;
|
|
widget_class->button_press_event = gtk_handle_box_button_press;
|
|
widget_class->delete_event = gtk_handle_box_delete_event;
|
|
|
|
container_class->add = gtk_handle_box_add;
|
|
container_class->remove = gtk_handle_box_remove;
|
|
|
|
class->child_attached = NULL;
|
|
class->child_detached = NULL;
|
|
|
|
handle_box_signals[SIGNAL_CHILD_ATTACHED] =
|
|
g_signal_new (I_("child-attached"),
|
|
G_OBJECT_CLASS_TYPE (gobject_class),
|
|
G_SIGNAL_RUN_FIRST,
|
|
G_STRUCT_OFFSET (GtkHandleBoxClass, child_attached),
|
|
NULL, NULL,
|
|
_gtk_marshal_VOID__OBJECT,
|
|
G_TYPE_NONE, 1,
|
|
GTK_TYPE_WIDGET);
|
|
handle_box_signals[SIGNAL_CHILD_DETACHED] =
|
|
g_signal_new (I_("child-detached"),
|
|
G_OBJECT_CLASS_TYPE (gobject_class),
|
|
G_SIGNAL_RUN_FIRST,
|
|
G_STRUCT_OFFSET (GtkHandleBoxClass, child_detached),
|
|
NULL, NULL,
|
|
_gtk_marshal_VOID__OBJECT,
|
|
G_TYPE_NONE, 1,
|
|
GTK_TYPE_WIDGET);
|
|
|
|
g_type_class_add_private (gobject_class, sizeof (GtkHandleBoxPrivate));
|
|
}
|
|
|
|
static void
|
|
gtk_handle_box_init (GtkHandleBox *handle_box)
|
|
{
|
|
GtkHandleBoxPrivate *priv;
|
|
|
|
handle_box->priv = G_TYPE_INSTANCE_GET_PRIVATE (handle_box,
|
|
GTK_TYPE_HANDLE_BOX,
|
|
GtkHandleBoxPrivate);
|
|
priv = handle_box->priv;
|
|
|
|
gtk_widget_set_has_window (GTK_WIDGET (handle_box), TRUE);
|
|
|
|
priv->bin_window = NULL;
|
|
priv->float_window = NULL;
|
|
priv->shadow_type = GTK_SHADOW_OUT;
|
|
priv->handle_position = GTK_POS_LEFT;
|
|
priv->float_window_mapped = FALSE;
|
|
priv->child_detached = FALSE;
|
|
priv->in_drag = FALSE;
|
|
priv->shrink_on_detach = TRUE;
|
|
priv->snap_edge = -1;
|
|
}
|
|
|
|
static void
|
|
gtk_handle_box_set_property (GObject *object,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
GtkHandleBox *handle_box = GTK_HANDLE_BOX (object);
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_SHADOW_TYPE:
|
|
gtk_handle_box_set_shadow_type (handle_box, g_value_get_enum (value));
|
|
break;
|
|
case PROP_HANDLE_POSITION:
|
|
gtk_handle_box_set_handle_position (handle_box, g_value_get_enum (value));
|
|
break;
|
|
case PROP_SNAP_EDGE:
|
|
gtk_handle_box_set_snap_edge (handle_box, g_value_get_enum (value));
|
|
break;
|
|
case PROP_SNAP_EDGE_SET:
|
|
if (!g_value_get_boolean (value))
|
|
gtk_handle_box_set_snap_edge (handle_box, (GtkPositionType)-1);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gtk_handle_box_get_property (GObject *object,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
GtkHandleBox *handle_box = GTK_HANDLE_BOX (object);
|
|
GtkHandleBoxPrivate *priv = handle_box->priv;
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_SHADOW_TYPE:
|
|
g_value_set_enum (value, priv->shadow_type);
|
|
break;
|
|
case PROP_HANDLE_POSITION:
|
|
g_value_set_enum (value, priv->handle_position);
|
|
break;
|
|
case PROP_SNAP_EDGE:
|
|
g_value_set_enum (value,
|
|
(priv->snap_edge == -1 ?
|
|
GTK_POS_TOP : priv->snap_edge));
|
|
break;
|
|
case PROP_SNAP_EDGE_SET:
|
|
g_value_set_boolean (value, priv->snap_edge != -1);
|
|
break;
|
|
case PROP_CHILD_DETACHED:
|
|
g_value_set_boolean (value, priv->child_detached);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
GtkWidget*
|
|
gtk_handle_box_new (void)
|
|
{
|
|
return g_object_new (GTK_TYPE_HANDLE_BOX, NULL);
|
|
}
|
|
|
|
static void
|
|
gtk_handle_box_map (GtkWidget *widget)
|
|
{
|
|
GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
|
|
GtkHandleBoxPrivate *priv = hb->priv;
|
|
GtkBin *bin = GTK_BIN (widget);
|
|
GtkWidget *child;
|
|
|
|
gtk_widget_set_mapped (widget, TRUE);
|
|
|
|
child = gtk_bin_get_child (bin);
|
|
if (child != NULL &&
|
|
gtk_widget_get_visible (child) &&
|
|
!gtk_widget_get_mapped (child))
|
|
gtk_widget_map (child);
|
|
|
|
if (priv->child_detached && !priv->float_window_mapped)
|
|
{
|
|
gdk_window_show (priv->float_window);
|
|
priv->float_window_mapped = TRUE;
|
|
}
|
|
|
|
gdk_window_show (priv->bin_window);
|
|
gdk_window_show (gtk_widget_get_window (widget));
|
|
}
|
|
|
|
static void
|
|
gtk_handle_box_unmap (GtkWidget *widget)
|
|
{
|
|
GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
|
|
GtkHandleBoxPrivate *priv = hb->priv;
|
|
|
|
gtk_widget_set_mapped (widget, FALSE);
|
|
|
|
gdk_window_hide (gtk_widget_get_window (widget));
|
|
if (priv->float_window_mapped)
|
|
{
|
|
gdk_window_hide (priv->float_window);
|
|
priv->float_window_mapped = FALSE;
|
|
}
|
|
|
|
GTK_WIDGET_CLASS (gtk_handle_box_parent_class)->unmap (widget);
|
|
}
|
|
|
|
static void
|
|
gtk_handle_box_realize (GtkWidget *widget)
|
|
{
|
|
GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
|
|
GtkHandleBoxPrivate *priv = hb->priv;
|
|
GtkAllocation allocation;
|
|
GtkRequisition requisition;
|
|
GtkStateType state;
|
|
GtkStyle *style;
|
|
GtkWidget *child;
|
|
GdkWindow *window;
|
|
GdkWindowAttr attributes;
|
|
gint attributes_mask;
|
|
|
|
gtk_widget_set_realized (widget, TRUE);
|
|
|
|
gtk_widget_get_allocation (widget, &allocation);
|
|
|
|
attributes.x = allocation.x;
|
|
attributes.y = allocation.y;
|
|
attributes.width = allocation.width;
|
|
attributes.height = allocation.height;
|
|
attributes.window_type = GDK_WINDOW_CHILD;
|
|
attributes.wclass = GDK_INPUT_OUTPUT;
|
|
attributes.visual = gtk_widget_get_visual (widget);
|
|
attributes.event_mask = (gtk_widget_get_events (widget)
|
|
| GDK_EXPOSURE_MASK);
|
|
attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
|
|
|
|
window = gdk_window_new (gtk_widget_get_parent_window (widget),
|
|
&attributes, attributes_mask);
|
|
gtk_widget_set_window (widget, window);
|
|
gdk_window_set_user_data (window, widget);
|
|
|
|
attributes.x = 0;
|
|
attributes.y = 0;
|
|
attributes.width = allocation.width;
|
|
attributes.height = allocation.height;
|
|
attributes.window_type = GDK_WINDOW_CHILD;
|
|
attributes.event_mask = (gtk_widget_get_events (widget) |
|
|
GDK_EXPOSURE_MASK |
|
|
GDK_BUTTON1_MOTION_MASK |
|
|
GDK_POINTER_MOTION_HINT_MASK |
|
|
GDK_BUTTON_PRESS_MASK |
|
|
GDK_BUTTON_RELEASE_MASK);
|
|
attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
|
|
|
|
priv->bin_window = gdk_window_new (window,
|
|
&attributes, attributes_mask);
|
|
gdk_window_set_user_data (priv->bin_window, widget);
|
|
|
|
child = gtk_bin_get_child (GTK_BIN (hb));
|
|
if (child)
|
|
gtk_widget_set_parent_window (child, priv->bin_window);
|
|
|
|
gtk_widget_get_preferred_size (widget, &requisition, NULL);
|
|
|
|
attributes.x = 0;
|
|
attributes.y = 0;
|
|
attributes.width = requisition.width;
|
|
attributes.height = requisition.height;
|
|
attributes.window_type = GDK_WINDOW_TOPLEVEL;
|
|
attributes.wclass = GDK_INPUT_OUTPUT;
|
|
attributes.visual = gtk_widget_get_visual (widget);
|
|
attributes.event_mask = (gtk_widget_get_events (widget) |
|
|
GDK_KEY_PRESS_MASK |
|
|
GDK_ENTER_NOTIFY_MASK |
|
|
GDK_LEAVE_NOTIFY_MASK |
|
|
GDK_FOCUS_CHANGE_MASK |
|
|
GDK_STRUCTURE_MASK);
|
|
attributes.type_hint = GDK_WINDOW_TYPE_HINT_TOOLBAR;
|
|
attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_TYPE_HINT;
|
|
priv->float_window = gdk_window_new (gtk_widget_get_root_window (widget),
|
|
&attributes, attributes_mask);
|
|
gdk_window_set_user_data (priv->float_window, widget);
|
|
gdk_window_set_decorations (priv->float_window, 0);
|
|
gdk_window_set_type_hint (priv->float_window, GDK_WINDOW_TYPE_HINT_TOOLBAR);
|
|
|
|
gtk_widget_style_attach (widget);
|
|
style = gtk_widget_get_style (widget);
|
|
state = gtk_widget_get_state (widget);
|
|
gtk_style_set_background (style, window, state);
|
|
gtk_style_set_background (style, priv->bin_window, state);
|
|
gtk_style_set_background (style, priv->float_window, state);
|
|
}
|
|
|
|
static void
|
|
gtk_handle_box_unrealize (GtkWidget *widget)
|
|
{
|
|
GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
|
|
GtkHandleBoxPrivate *priv = hb->priv;
|
|
|
|
gdk_window_set_user_data (priv->bin_window, NULL);
|
|
gdk_window_destroy (priv->bin_window);
|
|
priv->bin_window = NULL;
|
|
gdk_window_set_user_data (priv->float_window, NULL);
|
|
gdk_window_destroy (priv->float_window);
|
|
priv->float_window = NULL;
|
|
|
|
GTK_WIDGET_CLASS (gtk_handle_box_parent_class)->unrealize (widget);
|
|
}
|
|
|
|
static void
|
|
gtk_handle_box_style_set (GtkWidget *widget,
|
|
GtkStyle *previous_style)
|
|
{
|
|
GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
|
|
GtkHandleBoxPrivate *priv = hb->priv;
|
|
|
|
if (gtk_widget_get_realized (widget) &&
|
|
gtk_widget_get_has_window (widget))
|
|
{
|
|
GtkStateType state;
|
|
GtkStyle *style;
|
|
|
|
style = gtk_widget_get_style (widget);
|
|
state = gtk_widget_get_state (widget);
|
|
|
|
gtk_style_set_background (style, gtk_widget_get_window (widget), state);
|
|
gtk_style_set_background (style, priv->bin_window, state);
|
|
gtk_style_set_background (style, priv->float_window, state);
|
|
}
|
|
}
|
|
|
|
static int
|
|
effective_handle_position (GtkHandleBox *hb)
|
|
{
|
|
GtkHandleBoxPrivate *priv = hb->priv;
|
|
int handle_position;
|
|
|
|
if (gtk_widget_get_direction (GTK_WIDGET (hb)) == GTK_TEXT_DIR_LTR)
|
|
handle_position = priv->handle_position;
|
|
else
|
|
{
|
|
switch (priv->handle_position)
|
|
{
|
|
case GTK_POS_LEFT:
|
|
handle_position = GTK_POS_RIGHT;
|
|
break;
|
|
case GTK_POS_RIGHT:
|
|
handle_position = GTK_POS_LEFT;
|
|
break;
|
|
default:
|
|
handle_position = priv->handle_position;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return handle_position;
|
|
}
|
|
|
|
static void
|
|
gtk_handle_box_size_request (GtkWidget *widget,
|
|
GtkRequisition *requisition)
|
|
{
|
|
GtkBin *bin = GTK_BIN (widget);
|
|
GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
|
|
GtkHandleBoxPrivate *priv = hb->priv;
|
|
GtkRequisition child_requisition;
|
|
GtkWidget *child;
|
|
gint handle_position;
|
|
|
|
handle_position = effective_handle_position (hb);
|
|
|
|
if (handle_position == GTK_POS_LEFT ||
|
|
handle_position == GTK_POS_RIGHT)
|
|
{
|
|
requisition->width = DRAG_HANDLE_SIZE;
|
|
requisition->height = 0;
|
|
}
|
|
else
|
|
{
|
|
requisition->width = 0;
|
|
requisition->height = DRAG_HANDLE_SIZE;
|
|
}
|
|
|
|
child = gtk_bin_get_child (bin);
|
|
/* if our child is not visible, we still request its size, since we
|
|
* won't have any useful hint for our size otherwise.
|
|
*/
|
|
if (child)
|
|
{
|
|
gtk_widget_get_preferred_size (child, &child_requisition, NULL);
|
|
}
|
|
else
|
|
{
|
|
child_requisition.width = 0;
|
|
child_requisition.height = 0;
|
|
}
|
|
|
|
if (priv->child_detached)
|
|
{
|
|
/* FIXME: This doesn't work currently */
|
|
if (!priv->shrink_on_detach)
|
|
{
|
|
if (handle_position == GTK_POS_LEFT ||
|
|
handle_position == GTK_POS_RIGHT)
|
|
requisition->height += child_requisition.height;
|
|
else
|
|
requisition->width += child_requisition.width;
|
|
}
|
|
else
|
|
{
|
|
if (handle_position == GTK_POS_LEFT ||
|
|
handle_position == GTK_POS_RIGHT)
|
|
requisition->height += gtk_widget_get_style (widget)->ythickness;
|
|
else
|
|
requisition->width += gtk_widget_get_style (widget)->xthickness;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
guint border_width;
|
|
|
|
border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
|
|
requisition->width += border_width * 2;
|
|
requisition->height += border_width * 2;
|
|
|
|
if (child)
|
|
{
|
|
requisition->width += child_requisition.width;
|
|
requisition->height += child_requisition.height;
|
|
}
|
|
else
|
|
{
|
|
requisition->width += CHILDLESS_SIZE;
|
|
requisition->height += CHILDLESS_SIZE;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
gtk_handle_box_get_preferred_width (GtkWidget *widget,
|
|
gint *minimum,
|
|
gint *natural)
|
|
{
|
|
GtkRequisition requisition;
|
|
|
|
gtk_handle_box_size_request (widget, &requisition);
|
|
|
|
*minimum = *natural = requisition.width;
|
|
}
|
|
|
|
static void
|
|
gtk_handle_box_get_preferred_height (GtkWidget *widget,
|
|
gint *minimum,
|
|
gint *natural)
|
|
{
|
|
GtkRequisition requisition;
|
|
|
|
gtk_handle_box_size_request (widget, &requisition);
|
|
|
|
*minimum = *natural = requisition.height;
|
|
}
|
|
|
|
|
|
static void
|
|
gtk_handle_box_size_allocate (GtkWidget *widget,
|
|
GtkAllocation *allocation)
|
|
{
|
|
GtkBin *bin = GTK_BIN (widget);
|
|
GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
|
|
GtkHandleBoxPrivate *priv = hb->priv;
|
|
GtkRequisition child_requisition;
|
|
GtkWidget *child;
|
|
gint handle_position;
|
|
|
|
handle_position = effective_handle_position (hb);
|
|
|
|
child = gtk_bin_get_child (bin);
|
|
|
|
if (child)
|
|
{
|
|
gtk_widget_get_preferred_size (child, &child_requisition, NULL);
|
|
}
|
|
else
|
|
{
|
|
child_requisition.width = 0;
|
|
child_requisition.height = 0;
|
|
}
|
|
|
|
gtk_widget_set_allocation (widget, allocation);
|
|
|
|
if (gtk_widget_get_realized (widget))
|
|
gdk_window_move_resize (gtk_widget_get_window (widget),
|
|
allocation->x, allocation->y,
|
|
allocation->width, allocation->height);
|
|
|
|
if (child != NULL && gtk_widget_get_visible (child))
|
|
{
|
|
GtkAllocation child_allocation;
|
|
guint border_width;
|
|
|
|
border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
|
|
|
|
child_allocation.x = border_width;
|
|
child_allocation.y = border_width;
|
|
if (handle_position == GTK_POS_LEFT)
|
|
child_allocation.x += DRAG_HANDLE_SIZE;
|
|
else if (handle_position == GTK_POS_TOP)
|
|
child_allocation.y += DRAG_HANDLE_SIZE;
|
|
|
|
if (priv->child_detached)
|
|
{
|
|
guint float_width;
|
|
guint float_height;
|
|
|
|
child_allocation.width = child_requisition.width;
|
|
child_allocation.height = child_requisition.height;
|
|
|
|
float_width = child_allocation.width + 2 * border_width;
|
|
float_height = child_allocation.height + 2 * border_width;
|
|
|
|
if (handle_position == GTK_POS_LEFT ||
|
|
handle_position == GTK_POS_RIGHT)
|
|
float_width += DRAG_HANDLE_SIZE;
|
|
else
|
|
float_height += DRAG_HANDLE_SIZE;
|
|
|
|
if (gtk_widget_get_realized (widget))
|
|
{
|
|
gdk_window_resize (priv->float_window,
|
|
float_width,
|
|
float_height);
|
|
gdk_window_move_resize (priv->bin_window,
|
|
0,
|
|
0,
|
|
float_width,
|
|
float_height);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
child_allocation.width = MAX (1, (gint) allocation->width - 2 * border_width);
|
|
child_allocation.height = MAX (1, (gint) allocation->height - 2 * border_width);
|
|
|
|
if (handle_position == GTK_POS_LEFT ||
|
|
handle_position == GTK_POS_RIGHT)
|
|
child_allocation.width -= DRAG_HANDLE_SIZE;
|
|
else
|
|
child_allocation.height -= DRAG_HANDLE_SIZE;
|
|
|
|
if (gtk_widget_get_realized (widget))
|
|
gdk_window_move_resize (priv->bin_window,
|
|
0,
|
|
0,
|
|
allocation->width,
|
|
allocation->height);
|
|
}
|
|
|
|
gtk_widget_size_allocate (child, &child_allocation);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gtk_handle_box_draw_ghost (GtkHandleBox *hb,
|
|
cairo_t *cr)
|
|
{
|
|
GtkWidget *widget = GTK_WIDGET (hb);
|
|
GtkStateType state;
|
|
GtkStyle *style;
|
|
GdkWindow *window;
|
|
guint x;
|
|
guint y;
|
|
guint width;
|
|
guint height;
|
|
gint allocation_width;
|
|
gint allocation_height;
|
|
gint handle_position;
|
|
|
|
handle_position = effective_handle_position (hb);
|
|
allocation_width = gtk_widget_get_allocated_width (widget);
|
|
allocation_height = gtk_widget_get_allocated_height (widget);
|
|
|
|
if (handle_position == GTK_POS_LEFT ||
|
|
handle_position == GTK_POS_RIGHT)
|
|
{
|
|
x = handle_position == GTK_POS_LEFT ? 0 : allocation_width - DRAG_HANDLE_SIZE;
|
|
y = 0;
|
|
width = DRAG_HANDLE_SIZE;
|
|
height = allocation_height;
|
|
}
|
|
else
|
|
{
|
|
x = 0;
|
|
y = handle_position == GTK_POS_TOP ? 0 : allocation_height - DRAG_HANDLE_SIZE;
|
|
width = allocation_width;
|
|
height = DRAG_HANDLE_SIZE;
|
|
}
|
|
|
|
style = gtk_widget_get_style (widget);
|
|
window = gtk_widget_get_window (widget);
|
|
state = gtk_widget_get_state (widget);
|
|
|
|
gtk_paint_shadow (style,
|
|
cr,
|
|
state,
|
|
GTK_SHADOW_ETCHED_IN,
|
|
widget, "handle",
|
|
x,
|
|
y,
|
|
width,
|
|
height);
|
|
if (handle_position == GTK_POS_LEFT ||
|
|
handle_position == GTK_POS_RIGHT)
|
|
gtk_paint_hline (style,
|
|
cr,
|
|
state,
|
|
widget, "handlebox",
|
|
handle_position == GTK_POS_LEFT ? DRAG_HANDLE_SIZE : 0,
|
|
handle_position == GTK_POS_LEFT ? allocation_width : allocation_width - DRAG_HANDLE_SIZE,
|
|
allocation_height / 2);
|
|
else
|
|
gtk_paint_vline (style,
|
|
cr,
|
|
state,
|
|
widget, "handlebox",
|
|
handle_position == GTK_POS_TOP ? DRAG_HANDLE_SIZE : 0,
|
|
handle_position == GTK_POS_TOP ? allocation_height : allocation_height - DRAG_HANDLE_SIZE,
|
|
allocation_width / 2);
|
|
}
|
|
|
|
void
|
|
gtk_handle_box_set_shadow_type (GtkHandleBox *handle_box,
|
|
GtkShadowType type)
|
|
{
|
|
GtkHandleBoxPrivate *priv;
|
|
|
|
g_return_if_fail (GTK_IS_HANDLE_BOX (handle_box));
|
|
|
|
priv = handle_box->priv;
|
|
|
|
if ((GtkShadowType) priv->shadow_type != type)
|
|
{
|
|
priv->shadow_type = type;
|
|
g_object_notify (G_OBJECT (handle_box), "shadow-type");
|
|
gtk_widget_queue_resize (GTK_WIDGET (handle_box));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* gtk_handle_box_get_shadow_type:
|
|
* @handle_box: a #GtkHandleBox
|
|
*
|
|
* Gets the type of shadow drawn around the handle box. See
|
|
* gtk_handle_box_set_shadow_type().
|
|
*
|
|
* Return value: the type of shadow currently drawn around the handle box.
|
|
**/
|
|
GtkShadowType
|
|
gtk_handle_box_get_shadow_type (GtkHandleBox *handle_box)
|
|
{
|
|
g_return_val_if_fail (GTK_IS_HANDLE_BOX (handle_box), GTK_SHADOW_ETCHED_OUT);
|
|
|
|
return handle_box->priv->shadow_type;
|
|
}
|
|
|
|
void
|
|
gtk_handle_box_set_handle_position (GtkHandleBox *handle_box,
|
|
GtkPositionType position)
|
|
{
|
|
GtkHandleBoxPrivate *priv;
|
|
|
|
g_return_if_fail (GTK_IS_HANDLE_BOX (handle_box));
|
|
|
|
priv = handle_box->priv;
|
|
|
|
if ((GtkPositionType) priv->handle_position != position)
|
|
{
|
|
priv->handle_position = position;
|
|
g_object_notify (G_OBJECT (handle_box), "handle-position");
|
|
gtk_widget_queue_resize (GTK_WIDGET (handle_box));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* gtk_handle_box_get_handle_position:
|
|
* @handle_box: a #GtkHandleBox
|
|
*
|
|
* Gets the handle position of the handle box. See
|
|
* gtk_handle_box_set_handle_position().
|
|
*
|
|
* Return value: the current handle position.
|
|
**/
|
|
GtkPositionType
|
|
gtk_handle_box_get_handle_position (GtkHandleBox *handle_box)
|
|
{
|
|
g_return_val_if_fail (GTK_IS_HANDLE_BOX (handle_box), GTK_POS_LEFT);
|
|
|
|
return handle_box->priv->handle_position;
|
|
}
|
|
|
|
void
|
|
gtk_handle_box_set_snap_edge (GtkHandleBox *handle_box,
|
|
GtkPositionType edge)
|
|
{
|
|
GtkHandleBoxPrivate *priv;
|
|
|
|
g_return_if_fail (GTK_IS_HANDLE_BOX (handle_box));
|
|
|
|
priv = handle_box->priv;
|
|
|
|
if (priv->snap_edge != edge)
|
|
{
|
|
priv->snap_edge = edge;
|
|
|
|
g_object_freeze_notify (G_OBJECT (handle_box));
|
|
g_object_notify (G_OBJECT (handle_box), "snap-edge");
|
|
g_object_notify (G_OBJECT (handle_box), "snap-edge-set");
|
|
g_object_thaw_notify (G_OBJECT (handle_box));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* gtk_handle_box_get_snap_edge:
|
|
* @handle_box: a #GtkHandleBox
|
|
*
|
|
* Gets the edge used for determining reattachment of the handle box. See
|
|
* gtk_handle_box_set_snap_edge().
|
|
*
|
|
* Return value: the edge used for determining reattachment, or (GtkPositionType)-1 if this
|
|
* is determined (as per default) from the handle position.
|
|
**/
|
|
GtkPositionType
|
|
gtk_handle_box_get_snap_edge (GtkHandleBox *handle_box)
|
|
{
|
|
g_return_val_if_fail (GTK_IS_HANDLE_BOX (handle_box), (GtkPositionType)-1);
|
|
|
|
return handle_box->priv->snap_edge;
|
|
}
|
|
|
|
/**
|
|
* gtk_handle_box_get_child_detached:
|
|
* @handle_box: a #GtkHandleBox
|
|
*
|
|
* Whether the handlebox's child is currently detached.
|
|
*
|
|
* Return value: %TRUE if the child is currently detached, otherwise %FALSE
|
|
*
|
|
* Since: 2.14
|
|
**/
|
|
gboolean
|
|
gtk_handle_box_get_child_detached (GtkHandleBox *handle_box)
|
|
{
|
|
g_return_val_if_fail (GTK_IS_HANDLE_BOX (handle_box), FALSE);
|
|
|
|
return handle_box->priv->child_detached;
|
|
}
|
|
|
|
static void
|
|
gtk_handle_box_paint (GtkWidget *widget,
|
|
cairo_t *cr)
|
|
{
|
|
GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
|
|
GtkHandleBoxPrivate *priv = hb->priv;
|
|
GtkBin *bin = GTK_BIN (widget);
|
|
GtkWidget *child;
|
|
gint width, height;
|
|
GdkRectangle rect;
|
|
gint handle_position;
|
|
GtkOrientation handle_orientation;
|
|
|
|
handle_position = effective_handle_position (hb);
|
|
|
|
width = gdk_window_get_width (priv->bin_window);
|
|
height = gdk_window_get_height (priv->bin_window);
|
|
|
|
gtk_paint_box (gtk_widget_get_style (widget),
|
|
cr,
|
|
gtk_widget_get_state (widget),
|
|
priv->shadow_type,
|
|
widget, "handlebox_bin",
|
|
0, 0, width, height);
|
|
|
|
switch (handle_position)
|
|
{
|
|
case GTK_POS_LEFT:
|
|
rect.x = 0;
|
|
rect.y = 0;
|
|
rect.width = DRAG_HANDLE_SIZE;
|
|
rect.height = height;
|
|
handle_orientation = GTK_ORIENTATION_VERTICAL;
|
|
break;
|
|
case GTK_POS_RIGHT:
|
|
rect.x = width - DRAG_HANDLE_SIZE;
|
|
rect.y = 0;
|
|
rect.width = DRAG_HANDLE_SIZE;
|
|
rect.height = height;
|
|
handle_orientation = GTK_ORIENTATION_VERTICAL;
|
|
break;
|
|
case GTK_POS_TOP:
|
|
rect.x = 0;
|
|
rect.y = 0;
|
|
rect.width = width;
|
|
rect.height = DRAG_HANDLE_SIZE;
|
|
handle_orientation = GTK_ORIENTATION_HORIZONTAL;
|
|
break;
|
|
case GTK_POS_BOTTOM:
|
|
rect.x = 0;
|
|
rect.y = height - DRAG_HANDLE_SIZE;
|
|
rect.width = width;
|
|
rect.height = DRAG_HANDLE_SIZE;
|
|
handle_orientation = GTK_ORIENTATION_HORIZONTAL;
|
|
break;
|
|
default:
|
|
g_assert_not_reached ();
|
|
break;
|
|
}
|
|
|
|
gtk_paint_handle (gtk_widget_get_style (widget), cr,
|
|
GTK_STATE_NORMAL, GTK_SHADOW_OUT,
|
|
widget, "handlebox",
|
|
rect.x, rect.y, rect.width, rect.height,
|
|
handle_orientation);
|
|
|
|
child = gtk_bin_get_child (bin);
|
|
if (child != NULL && gtk_widget_get_visible (child))
|
|
GTK_WIDGET_CLASS (gtk_handle_box_parent_class)->draw (widget, cr);
|
|
}
|
|
|
|
static gboolean
|
|
gtk_handle_box_draw (GtkWidget *widget,
|
|
cairo_t *cr)
|
|
{
|
|
GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
|
|
GtkHandleBoxPrivate *priv = hb->priv;
|
|
|
|
if (gtk_cairo_should_draw_window (cr, gtk_widget_get_window (widget)))
|
|
{
|
|
if (priv->child_detached)
|
|
gtk_handle_box_draw_ghost (hb, cr);
|
|
}
|
|
else if (gtk_cairo_should_draw_window (cr, priv->bin_window))
|
|
gtk_handle_box_paint (widget, cr);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static GtkWidget *
|
|
gtk_handle_box_get_invisible (void)
|
|
{
|
|
static GtkWidget *handle_box_invisible = NULL;
|
|
|
|
if (!handle_box_invisible)
|
|
{
|
|
handle_box_invisible = gtk_invisible_new ();
|
|
gtk_widget_show (handle_box_invisible);
|
|
}
|
|
|
|
return handle_box_invisible;
|
|
}
|
|
|
|
static gboolean
|
|
gtk_handle_box_grab_event (GtkWidget *widget,
|
|
GdkEvent *event,
|
|
GtkHandleBox *hb)
|
|
{
|
|
GtkHandleBoxPrivate *priv = hb->priv;
|
|
|
|
switch (event->type)
|
|
{
|
|
case GDK_BUTTON_RELEASE:
|
|
if (priv->in_drag) /* sanity check */
|
|
{
|
|
gtk_handle_box_end_drag (hb, event->button.time);
|
|
return TRUE;
|
|
}
|
|
break;
|
|
|
|
case GDK_MOTION_NOTIFY:
|
|
return gtk_handle_box_motion (GTK_WIDGET (hb), (GdkEventMotion *)event);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
gtk_handle_box_button_press (GtkWidget *widget,
|
|
GdkEventButton *event)
|
|
{
|
|
GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
|
|
GtkHandleBoxPrivate *priv = hb->priv;
|
|
gboolean event_handled;
|
|
GdkCursor *fleur;
|
|
gint handle_position;
|
|
|
|
handle_position = effective_handle_position (hb);
|
|
|
|
event_handled = FALSE;
|
|
if ((event->button == 1) &&
|
|
(event->type == GDK_BUTTON_PRESS || event->type == GDK_2BUTTON_PRESS))
|
|
{
|
|
GtkWidget *child;
|
|
gboolean in_handle;
|
|
|
|
if (event->window != priv->bin_window)
|
|
return FALSE;
|
|
|
|
child = gtk_bin_get_child (GTK_BIN (hb));
|
|
|
|
if (child)
|
|
{
|
|
GtkAllocation child_allocation;
|
|
guint border_width;
|
|
|
|
gtk_widget_get_allocation (child, &child_allocation);
|
|
border_width = gtk_container_get_border_width (GTK_CONTAINER (hb));
|
|
|
|
switch (handle_position)
|
|
{
|
|
case GTK_POS_LEFT:
|
|
in_handle = event->x < DRAG_HANDLE_SIZE;
|
|
break;
|
|
case GTK_POS_TOP:
|
|
in_handle = event->y < DRAG_HANDLE_SIZE;
|
|
break;
|
|
case GTK_POS_RIGHT:
|
|
in_handle = event->x > 2 * border_width + child_allocation.width;
|
|
break;
|
|
case GTK_POS_BOTTOM:
|
|
in_handle = event->y > 2 * border_width + child_allocation.height;
|
|
break;
|
|
default:
|
|
in_handle = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
in_handle = FALSE;
|
|
event_handled = TRUE;
|
|
}
|
|
|
|
if (in_handle)
|
|
{
|
|
if (event->type == GDK_BUTTON_PRESS) /* Start a drag */
|
|
{
|
|
GtkWidget *invisible = gtk_handle_box_get_invisible ();
|
|
GdkWindow *window;
|
|
gint root_x, root_y;
|
|
|
|
gtk_invisible_set_screen (GTK_INVISIBLE (invisible),
|
|
gtk_widget_get_screen (GTK_WIDGET (hb)));
|
|
gdk_window_get_origin (priv->bin_window, &root_x, &root_y);
|
|
|
|
priv->orig_x = event->x_root;
|
|
priv->orig_y = event->y_root;
|
|
|
|
priv->float_allocation.x = root_x - event->x_root;
|
|
priv->float_allocation.y = root_y - event->y_root;
|
|
priv->float_allocation.width = gdk_window_get_width (priv->bin_window);
|
|
priv->float_allocation.height = gdk_window_get_height (priv->bin_window);
|
|
|
|
window = gtk_widget_get_window (widget);
|
|
if (gdk_window_is_viewable (window))
|
|
{
|
|
gdk_window_get_origin (window, &root_x, &root_y);
|
|
|
|
priv->attach_allocation.x = root_x;
|
|
priv->attach_allocation.y = root_y;
|
|
priv->attach_allocation.width = gdk_window_get_width (window);
|
|
priv->attach_allocation.height = gdk_window_get_height (window);
|
|
}
|
|
else
|
|
{
|
|
priv->attach_allocation.x = -1;
|
|
priv->attach_allocation.y = -1;
|
|
priv->attach_allocation.width = 0;
|
|
priv->attach_allocation.height = 0;
|
|
}
|
|
priv->in_drag = TRUE;
|
|
priv->grab_device = event->device;
|
|
fleur = gdk_cursor_new_for_display (gtk_widget_get_display (widget),
|
|
GDK_FLEUR);
|
|
if (gdk_device_grab (event->device,
|
|
gtk_widget_get_window (invisible),
|
|
GDK_OWNERSHIP_WINDOW,
|
|
FALSE,
|
|
(GDK_BUTTON1_MOTION_MASK |
|
|
GDK_POINTER_MOTION_HINT_MASK |
|
|
GDK_BUTTON_RELEASE_MASK),
|
|
fleur,
|
|
event->time) != GDK_GRAB_SUCCESS)
|
|
{
|
|
priv->in_drag = FALSE;
|
|
priv->grab_device = NULL;
|
|
}
|
|
else
|
|
{
|
|
gtk_device_grab_add (invisible, priv->grab_device, TRUE);
|
|
g_signal_connect (invisible, "event",
|
|
G_CALLBACK (gtk_handle_box_grab_event), hb);
|
|
}
|
|
|
|
gdk_cursor_unref (fleur);
|
|
event_handled = TRUE;
|
|
}
|
|
else if (priv->child_detached) /* Double click */
|
|
{
|
|
gtk_handle_box_reattach (hb);
|
|
}
|
|
}
|
|
}
|
|
|
|
return event_handled;
|
|
}
|
|
|
|
static gboolean
|
|
gtk_handle_box_motion (GtkWidget *widget,
|
|
GdkEventMotion *event)
|
|
{
|
|
GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
|
|
GtkHandleBoxPrivate *priv = hb->priv;
|
|
GtkWidget *child;
|
|
gint new_x, new_y;
|
|
gint snap_edge;
|
|
gboolean is_snapped = FALSE;
|
|
gint handle_position;
|
|
GdkGeometry geometry;
|
|
GdkScreen *screen, *pointer_screen;
|
|
|
|
if (!priv->in_drag)
|
|
return FALSE;
|
|
handle_position = effective_handle_position (hb);
|
|
|
|
/* Calculate the attachment point on the float, if the float
|
|
* were detached
|
|
*/
|
|
new_x = 0;
|
|
new_y = 0;
|
|
screen = gtk_widget_get_screen (widget);
|
|
gdk_display_get_device_state (gdk_screen_get_display (screen),
|
|
event->device,
|
|
&pointer_screen,
|
|
&new_x, &new_y, NULL);
|
|
if (pointer_screen != screen)
|
|
{
|
|
new_x = priv->orig_x;
|
|
new_y = priv->orig_y;
|
|
}
|
|
|
|
new_x += priv->float_allocation.x;
|
|
new_y += priv->float_allocation.y;
|
|
|
|
snap_edge = priv->snap_edge;
|
|
if (snap_edge == -1)
|
|
snap_edge = (handle_position == GTK_POS_LEFT ||
|
|
handle_position == GTK_POS_RIGHT) ?
|
|
GTK_POS_TOP : GTK_POS_LEFT;
|
|
|
|
if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
|
|
switch (snap_edge)
|
|
{
|
|
case GTK_POS_LEFT:
|
|
snap_edge = GTK_POS_RIGHT;
|
|
break;
|
|
case GTK_POS_RIGHT:
|
|
snap_edge = GTK_POS_LEFT;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* First, check if the snapped edge is aligned
|
|
*/
|
|
switch (snap_edge)
|
|
{
|
|
case GTK_POS_TOP:
|
|
is_snapped = abs (priv->attach_allocation.y - new_y) < TOLERANCE;
|
|
break;
|
|
case GTK_POS_BOTTOM:
|
|
is_snapped = abs (priv->attach_allocation.y + (gint)priv->attach_allocation.height -
|
|
new_y - (gint)priv->float_allocation.height) < TOLERANCE;
|
|
break;
|
|
case GTK_POS_LEFT:
|
|
is_snapped = abs (priv->attach_allocation.x - new_x) < TOLERANCE;
|
|
break;
|
|
case GTK_POS_RIGHT:
|
|
is_snapped = abs (priv->attach_allocation.x + (gint)priv->attach_allocation.width -
|
|
new_x - (gint)priv->float_allocation.width) < TOLERANCE;
|
|
break;
|
|
}
|
|
|
|
/* Next, check if coordinates in the other direction are sufficiently
|
|
* aligned
|
|
*/
|
|
if (is_snapped)
|
|
{
|
|
gint float_pos1 = 0; /* Initialize to suppress warnings */
|
|
gint float_pos2 = 0;
|
|
gint attach_pos1 = 0;
|
|
gint attach_pos2 = 0;
|
|
|
|
switch (snap_edge)
|
|
{
|
|
case GTK_POS_TOP:
|
|
case GTK_POS_BOTTOM:
|
|
attach_pos1 = priv->attach_allocation.x;
|
|
attach_pos2 = priv->attach_allocation.x + priv->attach_allocation.width;
|
|
float_pos1 = new_x;
|
|
float_pos2 = new_x + priv->float_allocation.width;
|
|
break;
|
|
case GTK_POS_LEFT:
|
|
case GTK_POS_RIGHT:
|
|
attach_pos1 = priv->attach_allocation.y;
|
|
attach_pos2 = priv->attach_allocation.y + priv->attach_allocation.height;
|
|
float_pos1 = new_y;
|
|
float_pos2 = new_y + priv->float_allocation.height;
|
|
break;
|
|
}
|
|
|
|
is_snapped = ((attach_pos1 - TOLERANCE < float_pos1) &&
|
|
(attach_pos2 + TOLERANCE > float_pos2)) ||
|
|
((float_pos1 - TOLERANCE < attach_pos1) &&
|
|
(float_pos2 + TOLERANCE > attach_pos2));
|
|
}
|
|
|
|
child = gtk_bin_get_child (GTK_BIN (hb));
|
|
|
|
if (is_snapped)
|
|
{
|
|
if (priv->child_detached)
|
|
{
|
|
priv->child_detached = FALSE;
|
|
gdk_window_hide (priv->float_window);
|
|
gdk_window_reparent (priv->bin_window, gtk_widget_get_window (widget), 0, 0);
|
|
priv->float_window_mapped = FALSE;
|
|
g_signal_emit (hb,
|
|
handle_box_signals[SIGNAL_CHILD_ATTACHED],
|
|
0,
|
|
child);
|
|
|
|
gtk_widget_queue_resize (widget);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
gint width, height;
|
|
|
|
width = gdk_window_get_width (priv->float_window);
|
|
height = gdk_window_get_height (priv->float_window);
|
|
|
|
switch (handle_position)
|
|
{
|
|
case GTK_POS_LEFT:
|
|
new_y += ((gint)priv->float_allocation.height - height) / 2;
|
|
break;
|
|
case GTK_POS_RIGHT:
|
|
new_x += (gint)priv->float_allocation.width - width;
|
|
new_y += ((gint)priv->float_allocation.height - height) / 2;
|
|
break;
|
|
case GTK_POS_TOP:
|
|
new_x += ((gint)priv->float_allocation.width - width) / 2;
|
|
break;
|
|
case GTK_POS_BOTTOM:
|
|
new_x += ((gint)priv->float_allocation.width - width) / 2;
|
|
new_y += (gint)priv->float_allocation.height - height;
|
|
break;
|
|
}
|
|
|
|
if (priv->child_detached)
|
|
{
|
|
gdk_window_move (priv->float_window, new_x, new_y);
|
|
gdk_window_raise (priv->float_window);
|
|
}
|
|
else
|
|
{
|
|
guint border_width;
|
|
GtkRequisition child_requisition;
|
|
|
|
priv->child_detached = TRUE;
|
|
|
|
if (child)
|
|
{
|
|
gtk_widget_get_preferred_size (child, &child_requisition, NULL);
|
|
}
|
|
else
|
|
{
|
|
child_requisition.width = 0;
|
|
child_requisition.height = 0;
|
|
}
|
|
|
|
border_width = gtk_container_get_border_width (GTK_CONTAINER (hb));
|
|
width = child_requisition.width + 2 * border_width;
|
|
height = child_requisition.height + 2 * border_width;
|
|
|
|
if (handle_position == GTK_POS_LEFT || handle_position == GTK_POS_RIGHT)
|
|
width += DRAG_HANDLE_SIZE;
|
|
else
|
|
height += DRAG_HANDLE_SIZE;
|
|
|
|
gdk_window_move_resize (priv->float_window, new_x, new_y, width, height);
|
|
gdk_window_reparent (priv->bin_window, priv->float_window, 0, 0);
|
|
gdk_window_set_geometry_hints (priv->float_window, &geometry, GDK_HINT_POS);
|
|
gdk_window_show (priv->float_window);
|
|
priv->float_window_mapped = TRUE;
|
|
#if 0
|
|
/* this extra move is necessary if we use decorations, or our
|
|
* window manager insists on decorations.
|
|
*/
|
|
gdk_display_sync (gtk_widget_get_display (widget));
|
|
gdk_window_move (priv->float_window, new_x, new_y);
|
|
gdk_display_sync (gtk_widget_get_display (widget));
|
|
#endif /* 0 */
|
|
g_signal_emit (hb,
|
|
handle_box_signals[SIGNAL_CHILD_DETACHED],
|
|
0,
|
|
child);
|
|
|
|
gtk_widget_queue_resize (widget);
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
gtk_handle_box_add (GtkContainer *container,
|
|
GtkWidget *widget)
|
|
{
|
|
GtkHandleBoxPrivate *priv = GTK_HANDLE_BOX (container)->priv;
|
|
|
|
gtk_widget_set_parent_window (widget, priv->bin_window);
|
|
GTK_CONTAINER_CLASS (gtk_handle_box_parent_class)->add (container, widget);
|
|
}
|
|
|
|
static void
|
|
gtk_handle_box_remove (GtkContainer *container,
|
|
GtkWidget *widget)
|
|
{
|
|
GTK_CONTAINER_CLASS (gtk_handle_box_parent_class)->remove (container, widget);
|
|
|
|
gtk_handle_box_reattach (GTK_HANDLE_BOX (container));
|
|
}
|
|
|
|
static gint
|
|
gtk_handle_box_delete_event (GtkWidget *widget,
|
|
GdkEventAny *event)
|
|
{
|
|
GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
|
|
GtkHandleBoxPrivate *priv = hb->priv;
|
|
|
|
if (event->window == priv->float_window)
|
|
{
|
|
gtk_handle_box_reattach (hb);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
gtk_handle_box_reattach (GtkHandleBox *hb)
|
|
{
|
|
GtkHandleBoxPrivate *priv = hb->priv;
|
|
GtkWidget *child;
|
|
GtkWidget *widget = GTK_WIDGET (hb);
|
|
|
|
if (priv->child_detached)
|
|
{
|
|
priv->child_detached = FALSE;
|
|
if (gtk_widget_get_realized (widget))
|
|
{
|
|
gdk_window_hide (priv->float_window);
|
|
gdk_window_reparent (priv->bin_window, gtk_widget_get_window (widget),
|
|
0, 0);
|
|
|
|
child = gtk_bin_get_child (GTK_BIN (hb));
|
|
if (child)
|
|
g_signal_emit (hb,
|
|
handle_box_signals[SIGNAL_CHILD_ATTACHED],
|
|
0,
|
|
child);
|
|
|
|
}
|
|
priv->float_window_mapped = FALSE;
|
|
}
|
|
if (priv->in_drag)
|
|
gtk_handle_box_end_drag (hb, GDK_CURRENT_TIME);
|
|
|
|
gtk_widget_queue_resize (GTK_WIDGET (hb));
|
|
}
|
|
|
|
static void
|
|
gtk_handle_box_end_drag (GtkHandleBox *hb,
|
|
guint32 time)
|
|
{
|
|
GtkHandleBoxPrivate *priv = hb->priv;
|
|
GtkWidget *invisible = gtk_handle_box_get_invisible ();
|
|
|
|
priv->in_drag = FALSE;
|
|
|
|
gtk_device_grab_remove (invisible, priv->grab_device);
|
|
gdk_device_ungrab (priv->grab_device, time);
|
|
g_signal_handlers_disconnect_by_func (invisible,
|
|
G_CALLBACK (gtk_handle_box_grab_event),
|
|
hb);
|
|
|
|
priv->grab_device = NULL;
|
|
}
|