1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3 * Copyright (C) 1998 Elliot Lee
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
22 * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
23 * file for a list of people on the GTK+ Team. See the ChangeLog
24 * files for a list of changes. These files are distributed with
25 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
30 #include "gtkhandlebox.h"
31 #include "gtkinvisible.h"
33 #include "gtkmarshalers.h"
34 #include "gtkwindow.h"
35 #include "gtkprivate.h"
39 typedef struct _GtkHandleBoxPrivate GtkHandleBoxPrivate;
41 struct _GtkHandleBoxPrivate
56 #define DRAG_HANDLE_SIZE 10
57 #define CHILDLESS_SIZE 25
58 #define GHOST_HEIGHT 3
62 SIGNAL_CHILD_ATTACHED,
63 SIGNAL_CHILD_DETACHED,
67 /* The algorithm for docking and redocking implemented here
68 * has a couple of nice properties:
70 * 1) During a single drag, docking always occurs at the
71 * the same cursor position. This means that the users
72 * motions are reversible, and that you won't
73 * undock/dock oscillations.
75 * 2) Docking generally occurs at user-visible features.
76 * The user, once they figure out to redock, will
77 * have useful information about doing it again in
80 * Please try to preserve these properties if you
81 * change the algorithm. (And the current algorithm
82 * is far from ideal). Briefly, the current algorithm
83 * for deciding whether the handlebox is docked or not:
85 * 1) The decision is done by comparing two rectangles - the
86 * allocation if the widget at the start of the drag,
87 * and the boundary of hb->bin_window at the start of
88 * of the drag offset by the distance that the cursor
91 * 2) These rectangles must have one edge, the "snap_edge"
92 * of the handlebox, aligned within TOLERANCE.
94 * 3) On the other dimension, the extents of one rectangle
95 * must be contained in the extents of the other,
96 * extended by tolerance. That is, either we can have:
98 * <-TOLERANCE-|--------bin_window--------------|-TOLERANCE->
99 * <--------float_window-------------------->
103 * <-TOLERANCE-|------float_window--------------|-TOLERANCE->
104 * <--------bin_window-------------------->
107 static void gtk_handle_box_set_property (GObject *object,
111 static void gtk_handle_box_get_property (GObject *object,
115 static void gtk_handle_box_destroy (GtkObject *object);
116 static void gtk_handle_box_map (GtkWidget *widget);
117 static void gtk_handle_box_unmap (GtkWidget *widget);
118 static void gtk_handle_box_realize (GtkWidget *widget);
119 static void gtk_handle_box_unrealize (GtkWidget *widget);
120 static void gtk_handle_box_style_set (GtkWidget *widget,
121 GtkStyle *previous_style);
122 static void gtk_handle_box_size_request (GtkWidget *widget,
123 GtkRequisition *requisition);
124 static void gtk_handle_box_size_allocate (GtkWidget *widget,
125 GtkAllocation *real_allocation);
126 static void gtk_handle_box_add (GtkContainer *container,
128 static void gtk_handle_box_remove (GtkContainer *container,
130 static void gtk_handle_box_draw_ghost (GtkHandleBox *hb);
131 static void gtk_handle_box_paint (GtkWidget *widget,
132 GdkEventExpose *event,
134 static gint gtk_handle_box_expose (GtkWidget *widget,
135 GdkEventExpose *event);
136 static gint gtk_handle_box_button_changed (GtkWidget *widget,
137 GdkEventButton *event);
138 static gint gtk_handle_box_motion (GtkWidget *widget,
139 GdkEventMotion *event);
140 static gint gtk_handle_box_delete_event (GtkWidget *widget,
142 static void gtk_handle_box_reattach (GtkHandleBox *hb);
143 static void gtk_handle_box_end_drag (GtkHandleBox *hb,
146 static guint handle_box_signals[SIGNAL_LAST] = { 0 };
148 G_DEFINE_TYPE (GtkHandleBox, gtk_handle_box, GTK_TYPE_BIN)
151 gtk_handle_box_class_init (GtkHandleBoxClass *class)
153 GObjectClass *gobject_class;
154 GtkObjectClass *object_class;
155 GtkWidgetClass *widget_class;
156 GtkContainerClass *container_class;
158 gobject_class = (GObjectClass *) class;
159 object_class = (GtkObjectClass *) class;
160 widget_class = (GtkWidgetClass *) class;
161 container_class = (GtkContainerClass *) class;
163 gobject_class->set_property = gtk_handle_box_set_property;
164 gobject_class->get_property = gtk_handle_box_get_property;
166 g_object_class_install_property (gobject_class,
168 g_param_spec_enum ("shadow", NULL,
169 P_("Deprecated property, use shadow_type instead"),
170 GTK_TYPE_SHADOW_TYPE,
171 GTK_SHADOW_ETCHED_OUT,
172 GTK_PARAM_READWRITE));
173 g_object_class_install_property (gobject_class,
175 g_param_spec_enum ("shadow-type",
177 P_("Appearance of the shadow that surrounds the container"),
178 GTK_TYPE_SHADOW_TYPE,
179 GTK_SHADOW_ETCHED_OUT,
180 GTK_PARAM_READWRITE));
182 g_object_class_install_property (gobject_class,
183 PROP_HANDLE_POSITION,
184 g_param_spec_enum ("handle-position",
185 P_("Handle position"),
186 P_("Position of the handle relative to the child widget"),
187 GTK_TYPE_POSITION_TYPE,
189 GTK_PARAM_READWRITE));
191 g_object_class_install_property (gobject_class,
193 g_param_spec_enum ("snap-edge",
195 P_("Side of the handlebox that's lined up with the docking point to dock the handlebox"),
196 GTK_TYPE_POSITION_TYPE,
198 GTK_PARAM_READWRITE));
200 g_object_class_install_property (gobject_class,
202 g_param_spec_boolean ("snap-edge-set",
204 P_("Whether to use the value from the snap_edge property or a value derived from handle_position"),
206 GTK_PARAM_READWRITE));
208 object_class->destroy = gtk_handle_box_destroy;
210 widget_class->map = gtk_handle_box_map;
211 widget_class->unmap = gtk_handle_box_unmap;
212 widget_class->realize = gtk_handle_box_realize;
213 widget_class->unrealize = gtk_handle_box_unrealize;
214 widget_class->style_set = gtk_handle_box_style_set;
215 widget_class->size_request = gtk_handle_box_size_request;
216 widget_class->size_allocate = gtk_handle_box_size_allocate;
217 widget_class->expose_event = gtk_handle_box_expose;
218 widget_class->button_press_event = gtk_handle_box_button_changed;
219 widget_class->delete_event = gtk_handle_box_delete_event;
221 container_class->add = gtk_handle_box_add;
222 container_class->remove = gtk_handle_box_remove;
224 class->child_attached = NULL;
225 class->child_detached = NULL;
227 handle_box_signals[SIGNAL_CHILD_ATTACHED] =
228 g_signal_new (I_("child_attached"),
229 G_OBJECT_CLASS_TYPE (gobject_class),
231 G_STRUCT_OFFSET (GtkHandleBoxClass, child_attached),
233 _gtk_marshal_VOID__OBJECT,
236 handle_box_signals[SIGNAL_CHILD_DETACHED] =
237 g_signal_new (I_("child_detached"),
238 G_OBJECT_CLASS_TYPE (gobject_class),
240 G_STRUCT_OFFSET (GtkHandleBoxClass, child_detached),
242 _gtk_marshal_VOID__OBJECT,
246 g_type_class_add_private (gobject_class, sizeof (GtkHandleBoxPrivate));
249 static GtkHandleBoxPrivate *
250 gtk_handle_box_get_private (GtkHandleBox *hb)
252 return G_TYPE_INSTANCE_GET_PRIVATE (hb, GTK_TYPE_HANDLE_BOX, GtkHandleBoxPrivate);
256 gtk_handle_box_init (GtkHandleBox *handle_box)
258 GTK_WIDGET_UNSET_FLAGS (handle_box, GTK_NO_WINDOW);
260 handle_box->bin_window = NULL;
261 handle_box->float_window = NULL;
262 handle_box->shadow_type = GTK_SHADOW_OUT;
263 handle_box->handle_position = GTK_POS_LEFT;
264 handle_box->float_window_mapped = FALSE;
265 handle_box->child_detached = FALSE;
266 handle_box->in_drag = FALSE;
267 handle_box->shrink_on_detach = TRUE;
268 handle_box->snap_edge = -1;
272 gtk_handle_box_set_property (GObject *object,
277 GtkHandleBox *handle_box = GTK_HANDLE_BOX (object);
282 case PROP_SHADOW_TYPE:
283 gtk_handle_box_set_shadow_type (handle_box, g_value_get_enum (value));
285 case PROP_HANDLE_POSITION:
286 gtk_handle_box_set_handle_position (handle_box, g_value_get_enum (value));
289 gtk_handle_box_set_snap_edge (handle_box, g_value_get_enum (value));
291 case PROP_SNAP_EDGE_SET:
292 if (!g_value_get_boolean (value))
293 gtk_handle_box_set_snap_edge (handle_box, (GtkPositionType)-1);
296 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
302 gtk_handle_box_get_property (GObject *object,
307 GtkHandleBox *handle_box = GTK_HANDLE_BOX (object);
312 case PROP_SHADOW_TYPE:
313 g_value_set_enum (value, handle_box->shadow_type);
315 case PROP_HANDLE_POSITION:
316 g_value_set_enum (value, handle_box->handle_position);
319 g_value_set_enum (value,
320 (handle_box->snap_edge == -1 ?
321 GTK_POS_TOP : handle_box->snap_edge));
323 case PROP_SNAP_EDGE_SET:
324 g_value_set_boolean (value, handle_box->snap_edge != -1);
327 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
333 gtk_handle_box_new (void)
335 return g_object_new (GTK_TYPE_HANDLE_BOX, NULL);
339 gtk_handle_box_destroy (GtkObject *object)
341 if (GTK_OBJECT_CLASS (gtk_handle_box_parent_class)->destroy)
342 (* GTK_OBJECT_CLASS (gtk_handle_box_parent_class)->destroy) (object);
346 gtk_handle_box_map (GtkWidget *widget)
351 GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
353 bin = GTK_BIN (widget);
354 hb = GTK_HANDLE_BOX (widget);
357 GTK_WIDGET_VISIBLE (bin->child) &&
358 !GTK_WIDGET_MAPPED (bin->child))
359 gtk_widget_map (bin->child);
361 if (hb->child_detached && !hb->float_window_mapped)
363 gdk_window_show (hb->float_window);
364 hb->float_window_mapped = TRUE;
367 gdk_window_show (hb->bin_window);
368 gdk_window_show (widget->window);
372 gtk_handle_box_unmap (GtkWidget *widget)
376 GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
378 hb = GTK_HANDLE_BOX (widget);
380 gdk_window_hide (widget->window);
381 if (hb->float_window_mapped)
383 gdk_window_hide (hb->float_window);
384 hb->float_window_mapped = FALSE;
389 gtk_handle_box_realize (GtkWidget *widget)
391 GdkWindowAttr attributes;
392 gint attributes_mask;
395 hb = GTK_HANDLE_BOX (widget);
397 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
399 attributes.x = widget->allocation.x;
400 attributes.y = widget->allocation.y;
401 attributes.width = widget->allocation.width;
402 attributes.height = widget->allocation.height;
403 attributes.window_type = GDK_WINDOW_CHILD;
404 attributes.wclass = GDK_INPUT_OUTPUT;
405 attributes.visual = gtk_widget_get_visual (widget);
406 attributes.colormap = gtk_widget_get_colormap (widget);
407 attributes.event_mask = (gtk_widget_get_events (widget)
408 | GDK_EXPOSURE_MASK);
409 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
410 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
411 gdk_window_set_user_data (widget->window, widget);
415 attributes.width = widget->allocation.width;
416 attributes.height = widget->allocation.height;
417 attributes.window_type = GDK_WINDOW_CHILD;
418 attributes.event_mask = (gtk_widget_get_events (widget) |
420 GDK_BUTTON1_MOTION_MASK |
421 GDK_POINTER_MOTION_HINT_MASK |
422 GDK_BUTTON_PRESS_MASK |
423 GDK_BUTTON_RELEASE_MASK);
424 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
425 hb->bin_window = gdk_window_new (widget->window, &attributes, attributes_mask);
426 gdk_window_set_user_data (hb->bin_window, widget);
427 if (GTK_BIN (hb)->child)
428 gtk_widget_set_parent_window (GTK_BIN (hb)->child, hb->bin_window);
432 attributes.width = widget->requisition.width;
433 attributes.height = widget->requisition.height;
434 attributes.window_type = GDK_WINDOW_TOPLEVEL;
435 attributes.wclass = GDK_INPUT_OUTPUT;
436 attributes.visual = gtk_widget_get_visual (widget);
437 attributes.colormap = gtk_widget_get_colormap (widget);
438 attributes.event_mask = (gtk_widget_get_events (widget) |
440 GDK_ENTER_NOTIFY_MASK |
441 GDK_LEAVE_NOTIFY_MASK |
442 GDK_FOCUS_CHANGE_MASK |
444 attributes.type_hint = GDK_WINDOW_TYPE_HINT_TOOLBAR;
445 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP | GDK_WA_TYPE_HINT;
446 hb->float_window = gdk_window_new (gtk_widget_get_root_window (widget),
447 &attributes, attributes_mask);
448 gdk_window_set_user_data (hb->float_window, widget);
449 gdk_window_set_decorations (hb->float_window, 0);
450 gdk_window_set_type_hint (hb->float_window, GDK_WINDOW_TYPE_HINT_TOOLBAR);
452 widget->style = gtk_style_attach (widget->style, widget->window);
453 gtk_style_set_background (widget->style, widget->window, GTK_WIDGET_STATE (hb));
454 gtk_style_set_background (widget->style, hb->bin_window, GTK_WIDGET_STATE (hb));
455 gtk_style_set_background (widget->style, hb->float_window, GTK_WIDGET_STATE (hb));
456 gdk_window_set_back_pixmap (widget->window, NULL, TRUE);
460 gtk_handle_box_unrealize (GtkWidget *widget)
462 GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
464 gdk_window_set_user_data (hb->bin_window, NULL);
465 gdk_window_destroy (hb->bin_window);
466 hb->bin_window = NULL;
467 gdk_window_set_user_data (hb->float_window, NULL);
468 gdk_window_destroy (hb->float_window);
469 hb->float_window = NULL;
471 if (GTK_WIDGET_CLASS (gtk_handle_box_parent_class)->unrealize)
472 (* GTK_WIDGET_CLASS (gtk_handle_box_parent_class)->unrealize) (widget);
476 gtk_handle_box_style_set (GtkWidget *widget,
477 GtkStyle *previous_style)
479 GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
481 if (GTK_WIDGET_REALIZED (widget) &&
482 !GTK_WIDGET_NO_WINDOW (widget))
484 gtk_style_set_background (widget->style, widget->window,
486 gtk_style_set_background (widget->style, hb->bin_window, widget->state);
487 gtk_style_set_background (widget->style, hb->float_window, widget->state);
492 effective_handle_position (GtkHandleBox *hb)
496 if (gtk_widget_get_direction (GTK_WIDGET (hb)) == GTK_TEXT_DIR_LTR)
497 handle_position = hb->handle_position;
500 switch (hb->handle_position)
503 handle_position = GTK_POS_RIGHT;
506 handle_position = GTK_POS_LEFT;
509 handle_position = hb->handle_position;
514 return handle_position;
518 gtk_handle_box_size_request (GtkWidget *widget,
519 GtkRequisition *requisition)
523 GtkRequisition child_requisition;
524 gint handle_position;
526 bin = GTK_BIN (widget);
527 hb = GTK_HANDLE_BOX (widget);
529 handle_position = effective_handle_position (hb);
531 if (handle_position == GTK_POS_LEFT ||
532 handle_position == GTK_POS_RIGHT)
534 requisition->width = DRAG_HANDLE_SIZE;
535 requisition->height = 0;
539 requisition->width = 0;
540 requisition->height = DRAG_HANDLE_SIZE;
543 /* if our child is not visible, we still request its size, since we
544 * won't have any useful hint for our size otherwise.
547 gtk_widget_size_request (bin->child, &child_requisition);
550 child_requisition.width = 0;
551 child_requisition.height = 0;
554 if (hb->child_detached)
556 /* FIXME: This doesn't work currently */
557 if (!hb->shrink_on_detach)
559 if (handle_position == GTK_POS_LEFT ||
560 handle_position == GTK_POS_RIGHT)
561 requisition->height += child_requisition.height;
563 requisition->width += child_requisition.width;
567 if (handle_position == GTK_POS_LEFT ||
568 handle_position == GTK_POS_RIGHT)
569 requisition->height += widget->style->ythickness;
571 requisition->width += widget->style->xthickness;
576 requisition->width += GTK_CONTAINER (widget)->border_width * 2;
577 requisition->height += GTK_CONTAINER (widget)->border_width * 2;
581 requisition->width += child_requisition.width;
582 requisition->height += child_requisition.height;
586 requisition->width += CHILDLESS_SIZE;
587 requisition->height += CHILDLESS_SIZE;
593 gtk_handle_box_size_allocate (GtkWidget *widget,
594 GtkAllocation *allocation)
598 GtkRequisition child_requisition;
599 gint handle_position;
601 bin = GTK_BIN (widget);
602 hb = GTK_HANDLE_BOX (widget);
604 handle_position = effective_handle_position (hb);
607 gtk_widget_get_child_requisition (bin->child, &child_requisition);
610 child_requisition.width = 0;
611 child_requisition.height = 0;
614 widget->allocation = *allocation;
616 if (GTK_WIDGET_REALIZED (hb))
617 gdk_window_move_resize (widget->window,
618 widget->allocation.x,
619 widget->allocation.y,
620 widget->allocation.width,
621 widget->allocation.height);
624 if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
626 GtkAllocation child_allocation;
629 border_width = GTK_CONTAINER (widget)->border_width;
631 child_allocation.x = border_width;
632 child_allocation.y = border_width;
633 if (handle_position == GTK_POS_LEFT)
634 child_allocation.x += DRAG_HANDLE_SIZE;
635 else if (handle_position == GTK_POS_TOP)
636 child_allocation.y += DRAG_HANDLE_SIZE;
638 if (hb->child_detached)
643 child_allocation.width = child_requisition.width;
644 child_allocation.height = child_requisition.height;
646 float_width = child_allocation.width + 2 * border_width;
647 float_height = child_allocation.height + 2 * border_width;
649 if (handle_position == GTK_POS_LEFT ||
650 handle_position == GTK_POS_RIGHT)
651 float_width += DRAG_HANDLE_SIZE;
653 float_height += DRAG_HANDLE_SIZE;
655 if (GTK_WIDGET_REALIZED (hb))
657 gdk_window_resize (hb->float_window,
660 gdk_window_move_resize (hb->bin_window,
669 child_allocation.width = MAX (1, (gint)widget->allocation.width - 2 * border_width);
670 child_allocation.height = MAX (1, (gint)widget->allocation.height - 2 * border_width);
672 if (handle_position == GTK_POS_LEFT ||
673 handle_position == GTK_POS_RIGHT)
674 child_allocation.width -= DRAG_HANDLE_SIZE;
676 child_allocation.height -= DRAG_HANDLE_SIZE;
678 if (GTK_WIDGET_REALIZED (hb))
679 gdk_window_move_resize (hb->bin_window,
682 widget->allocation.width,
683 widget->allocation.height);
686 gtk_widget_size_allocate (bin->child, &child_allocation);
691 gtk_handle_box_draw_ghost (GtkHandleBox *hb)
698 gint handle_position;
700 widget = GTK_WIDGET (hb);
702 handle_position = effective_handle_position (hb);
703 if (handle_position == GTK_POS_LEFT ||
704 handle_position == GTK_POS_RIGHT)
706 x = handle_position == GTK_POS_LEFT ? 0 : widget->allocation.width - DRAG_HANDLE_SIZE;
708 width = DRAG_HANDLE_SIZE;
709 height = widget->allocation.height;
714 y = handle_position == GTK_POS_TOP ? 0 : widget->allocation.height - DRAG_HANDLE_SIZE;
715 width = widget->allocation.width;
716 height = DRAG_HANDLE_SIZE;
718 gtk_paint_shadow (widget->style,
720 GTK_WIDGET_STATE (widget),
721 GTK_SHADOW_ETCHED_IN,
722 NULL, widget, "handle",
727 if (handle_position == GTK_POS_LEFT ||
728 handle_position == GTK_POS_RIGHT)
729 gtk_paint_hline (widget->style,
731 GTK_WIDGET_STATE (widget),
732 NULL, widget, "handlebox",
733 handle_position == GTK_POS_LEFT ? DRAG_HANDLE_SIZE : 0,
734 handle_position == GTK_POS_LEFT ? widget->allocation.width : widget->allocation.width - DRAG_HANDLE_SIZE,
735 widget->allocation.height / 2);
737 gtk_paint_vline (widget->style,
739 GTK_WIDGET_STATE (widget),
740 NULL, widget, "handlebox",
741 handle_position == GTK_POS_TOP ? DRAG_HANDLE_SIZE : 0,
742 handle_position == GTK_POS_TOP ? widget->allocation.height : widget->allocation.height - DRAG_HANDLE_SIZE,
743 widget->allocation.width / 2);
747 draw_textured_frame (GtkWidget *widget, GdkWindow *window, GdkRectangle *rect, GtkShadowType shadow,
748 GdkRectangle *clip, GtkOrientation orientation)
750 gtk_paint_handle (widget->style, window, GTK_STATE_NORMAL, shadow,
751 clip, widget, "handlebox",
752 rect->x, rect->y, rect->width, rect->height,
757 gtk_handle_box_set_shadow_type (GtkHandleBox *handle_box,
760 g_return_if_fail (GTK_IS_HANDLE_BOX (handle_box));
762 if ((GtkShadowType) handle_box->shadow_type != type)
764 handle_box->shadow_type = type;
765 g_object_notify (G_OBJECT (handle_box), "shadow-type");
766 gtk_widget_queue_resize (GTK_WIDGET (handle_box));
771 * gtk_handle_box_get_shadow_type:
772 * @handle_box: a #GtkHandleBox
774 * Gets the type of shadow drawn around the handle box. See
775 * gtk_handle_box_set_shadow_type().
777 * Return value: the type of shadow currently drawn around the handle box.
780 gtk_handle_box_get_shadow_type (GtkHandleBox *handle_box)
782 g_return_val_if_fail (GTK_IS_HANDLE_BOX (handle_box), GTK_SHADOW_ETCHED_OUT);
784 return handle_box->shadow_type;
788 gtk_handle_box_set_handle_position (GtkHandleBox *handle_box,
789 GtkPositionType position)
791 g_return_if_fail (GTK_IS_HANDLE_BOX (handle_box));
793 if ((GtkPositionType) handle_box->handle_position != position)
795 handle_box->handle_position = position;
796 g_object_notify (G_OBJECT (handle_box), "handle-position");
797 gtk_widget_queue_resize (GTK_WIDGET (handle_box));
802 * gtk_handle_box_get_handle_position:
803 * @handle_box: a #GtkHandleBox
805 * Gets the handle position of the handle box. See
806 * gtk_handle_box_set_handle_position().
808 * Return value: the current handle position.
811 gtk_handle_box_get_handle_position (GtkHandleBox *handle_box)
813 g_return_val_if_fail (GTK_IS_HANDLE_BOX (handle_box), GTK_POS_LEFT);
815 return handle_box->handle_position;
819 gtk_handle_box_set_snap_edge (GtkHandleBox *handle_box,
820 GtkPositionType edge)
822 g_return_if_fail (GTK_IS_HANDLE_BOX (handle_box));
824 if (handle_box->snap_edge != edge)
826 handle_box->snap_edge = edge;
828 g_object_freeze_notify (G_OBJECT (handle_box));
829 g_object_notify (G_OBJECT (handle_box), "snap-edge");
830 g_object_notify (G_OBJECT (handle_box), "snap-edge-set");
831 g_object_thaw_notify (G_OBJECT (handle_box));
836 * gtk_handle_box_get_snap_edge:
837 * @handle_box: a #GtkHandleBox
839 * Gets the edge used for determining reattachment of the handle box. See
840 * gtk_handle_box_set_snap_edge().
842 * Return value: the edge used for determining reattachment, or (GtkPositionType)-1 if this
843 * is determined (as per default) from the handle position.
846 gtk_handle_box_get_snap_edge (GtkHandleBox *handle_box)
848 g_return_val_if_fail (GTK_IS_HANDLE_BOX (handle_box), (GtkPositionType)-1);
850 return handle_box->snap_edge;
854 gtk_handle_box_paint (GtkWidget *widget,
856 GdkEventExpose *event,
864 gint handle_position;
865 GtkOrientation handle_orientation;
867 bin = GTK_BIN (widget);
868 hb = GTK_HANDLE_BOX (widget);
870 handle_position = effective_handle_position (hb);
872 gdk_drawable_get_size (hb->bin_window, &width, &height);
875 gtk_paint_box (widget->style,
877 GTK_WIDGET_STATE (widget),
879 area, widget, "handlebox_bin",
882 gtk_paint_box (widget->style,
884 GTK_WIDGET_STATE (widget),
886 &event->area, widget, "handlebox_bin",
889 /* We currently draw the handle _above_ the relief of the handlebox.
890 * it could also be drawn on the same level...
892 hb->handle_position == GTK_POS_LEFT ? DRAG_HANDLE_SIZE : 0,
893 hb->handle_position == GTK_POS_TOP ? DRAG_HANDLE_SIZE : 0,
897 switch (handle_position)
902 rect.width = DRAG_HANDLE_SIZE;
903 rect.height = height;
904 handle_orientation = GTK_ORIENTATION_VERTICAL;
907 rect.x = width - DRAG_HANDLE_SIZE;
909 rect.width = DRAG_HANDLE_SIZE;
910 rect.height = height;
911 handle_orientation = GTK_ORIENTATION_VERTICAL;
917 rect.height = DRAG_HANDLE_SIZE;
918 handle_orientation = GTK_ORIENTATION_HORIZONTAL;
922 rect.y = height - DRAG_HANDLE_SIZE;
924 rect.height = DRAG_HANDLE_SIZE;
925 handle_orientation = GTK_ORIENTATION_HORIZONTAL;
928 g_assert_not_reached ();
932 if (gdk_rectangle_intersect (event ? &event->area : area, &rect, &dest))
933 draw_textured_frame (widget, hb->bin_window, &rect,
935 event ? &event->area : area,
938 if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
939 (* GTK_WIDGET_CLASS (gtk_handle_box_parent_class)->expose_event) (widget, event);
943 gtk_handle_box_expose (GtkWidget *widget,
944 GdkEventExpose *event)
948 if (GTK_WIDGET_DRAWABLE (widget))
950 hb = GTK_HANDLE_BOX (widget);
952 if (event->window == widget->window)
954 if (hb->child_detached)
955 gtk_handle_box_draw_ghost (hb);
958 gtk_handle_box_paint (widget, event, NULL);
965 gtk_handle_box_get_invisible (void)
967 static GtkWidget *handle_box_invisible = NULL;
969 if (!handle_box_invisible)
971 handle_box_invisible = gtk_invisible_new ();
972 gtk_widget_show (handle_box_invisible);
975 return handle_box_invisible;
979 gtk_handle_box_grab_event (GtkWidget *widget,
985 case GDK_BUTTON_RELEASE:
986 if (hb->in_drag) /* sanity check */
988 gtk_handle_box_end_drag (hb, event->button.time);
993 case GDK_MOTION_NOTIFY:
994 return gtk_handle_box_motion (GTK_WIDGET (hb), (GdkEventMotion *)event);
1005 gtk_handle_box_button_changed (GtkWidget *widget,
1006 GdkEventButton *event)
1009 gboolean event_handled;
1011 gint handle_position;
1013 hb = GTK_HANDLE_BOX (widget);
1015 handle_position = effective_handle_position (hb);
1017 event_handled = FALSE;
1018 if ((event->button == 1) &&
1019 (event->type == GDK_BUTTON_PRESS || event->type == GDK_2BUTTON_PRESS))
1024 if (event->window != hb->bin_window)
1027 child = GTK_BIN (hb)->child;
1031 switch (handle_position)
1034 in_handle = event->x < DRAG_HANDLE_SIZE;
1037 in_handle = event->y < DRAG_HANDLE_SIZE;
1040 in_handle = event->x > 2 * GTK_CONTAINER (hb)->border_width + child->allocation.width;
1042 case GTK_POS_BOTTOM:
1043 in_handle = event->y > 2 * GTK_CONTAINER (hb)->border_width + child->allocation.height;
1053 event_handled = TRUE;
1058 if (event->type == GDK_BUTTON_PRESS) /* Start a drag */
1060 GtkHandleBoxPrivate *private = gtk_handle_box_get_private (hb);
1061 GtkWidget *invisible = gtk_handle_box_get_invisible ();
1062 gint desk_x, desk_y;
1063 gint root_x, root_y;
1066 gdk_window_get_deskrelative_origin (hb->bin_window, &desk_x, &desk_y);
1067 gdk_window_get_origin (hb->bin_window, &root_x, &root_y);
1068 gdk_drawable_get_size (hb->bin_window, &width, &height);
1070 private->orig_x = event->x_root;
1071 private->orig_y = event->y_root;
1073 hb->float_allocation.x = root_x - event->x_root;
1074 hb->float_allocation.y = root_y - event->y_root;
1075 hb->float_allocation.width = width;
1076 hb->float_allocation.height = height;
1078 hb->deskoff_x = desk_x - root_x;
1079 hb->deskoff_y = desk_y - root_y;
1081 if (gdk_window_is_viewable (widget->window))
1083 gdk_window_get_origin (widget->window, &root_x, &root_y);
1084 gdk_drawable_get_size (widget->window, &width, &height);
1086 hb->attach_allocation.x = root_x;
1087 hb->attach_allocation.y = root_y;
1088 hb->attach_allocation.width = width;
1089 hb->attach_allocation.height = height;
1093 hb->attach_allocation.x = -1;
1094 hb->attach_allocation.y = -1;
1095 hb->attach_allocation.width = 0;
1096 hb->attach_allocation.height = 0;
1099 fleur = gdk_cursor_new_for_display (gtk_widget_get_display (widget),
1101 if (gdk_pointer_grab (invisible->window,
1103 (GDK_BUTTON1_MOTION_MASK |
1104 GDK_POINTER_MOTION_HINT_MASK |
1105 GDK_BUTTON_RELEASE_MASK),
1110 hb->in_drag = FALSE;
1114 gtk_grab_add (invisible);
1115 g_signal_connect (invisible, "event",
1116 G_CALLBACK (gtk_handle_box_grab_event), hb);
1119 gdk_cursor_unref (fleur);
1120 event_handled = TRUE;
1122 else if (hb->child_detached) /* Double click */
1124 gtk_handle_box_reattach (hb);
1129 return event_handled;
1133 gtk_handle_box_motion (GtkWidget *widget,
1134 GdkEventMotion *event)
1136 GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
1139 gboolean is_snapped = FALSE;
1140 gint handle_position;
1141 GdkGeometry geometry;
1142 GdkScreen *screen, *pointer_screen;
1146 handle_position = effective_handle_position (hb);
1148 /* Calculate the attachment point on the float, if the float
1153 screen = gtk_widget_get_screen (widget);
1154 gdk_display_get_pointer (gdk_screen_get_display (screen),
1156 &new_x, &new_y, NULL);
1157 if (pointer_screen != screen)
1159 GtkHandleBoxPrivate *private = gtk_handle_box_get_private (hb);
1161 new_x = private->orig_x;
1162 new_y = private->orig_y;
1165 new_x += hb->float_allocation.x;
1166 new_y += hb->float_allocation.y;
1168 snap_edge = hb->snap_edge;
1169 if (snap_edge == -1)
1170 snap_edge = (handle_position == GTK_POS_LEFT ||
1171 handle_position == GTK_POS_RIGHT) ?
1172 GTK_POS_TOP : GTK_POS_LEFT;
1174 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
1178 snap_edge = GTK_POS_RIGHT;
1181 snap_edge = GTK_POS_LEFT;
1187 /* First, check if the snapped edge is aligned
1192 is_snapped = abs (hb->attach_allocation.y - new_y) < TOLERANCE;
1194 case GTK_POS_BOTTOM:
1195 is_snapped = abs (hb->attach_allocation.y + (gint)hb->attach_allocation.height -
1196 new_y - (gint)hb->float_allocation.height) < TOLERANCE;
1199 is_snapped = abs (hb->attach_allocation.x - new_x) < TOLERANCE;
1202 is_snapped = abs (hb->attach_allocation.x + (gint)hb->attach_allocation.width -
1203 new_x - (gint)hb->float_allocation.width) < TOLERANCE;
1207 /* Next, check if coordinates in the other direction are sufficiently
1212 gint float_pos1 = 0; /* Initialize to suppress warnings */
1213 gint float_pos2 = 0;
1214 gint attach_pos1 = 0;
1215 gint attach_pos2 = 0;
1220 case GTK_POS_BOTTOM:
1221 attach_pos1 = hb->attach_allocation.x;
1222 attach_pos2 = hb->attach_allocation.x + hb->attach_allocation.width;
1224 float_pos2 = new_x + hb->float_allocation.width;
1228 attach_pos1 = hb->attach_allocation.y;
1229 attach_pos2 = hb->attach_allocation.y + hb->attach_allocation.height;
1231 float_pos2 = new_y + hb->float_allocation.height;
1235 is_snapped = ((attach_pos1 - TOLERANCE < float_pos1) &&
1236 (attach_pos2 + TOLERANCE > float_pos2)) ||
1237 ((float_pos1 - TOLERANCE < attach_pos1) &&
1238 (float_pos2 + TOLERANCE > attach_pos2));
1243 if (hb->child_detached)
1245 hb->child_detached = FALSE;
1246 gdk_window_hide (hb->float_window);
1247 gdk_window_reparent (hb->bin_window, widget->window, 0, 0);
1248 hb->float_window_mapped = FALSE;
1250 handle_box_signals[SIGNAL_CHILD_ATTACHED],
1252 GTK_BIN (hb)->child);
1254 gtk_widget_queue_resize (widget);
1261 gdk_drawable_get_size (hb->float_window, &width, &height);
1262 new_x += hb->deskoff_x;
1263 new_y += hb->deskoff_y;
1265 switch (handle_position)
1268 new_y += ((gint)hb->float_allocation.height - height) / 2;
1271 new_x += (gint)hb->float_allocation.width - width;
1272 new_y += ((gint)hb->float_allocation.height - height) / 2;
1275 new_x += ((gint)hb->float_allocation.width - width) / 2;
1277 case GTK_POS_BOTTOM:
1278 new_x += ((gint)hb->float_allocation.width - width) / 2;
1279 new_y += (gint)hb->float_allocation.height - height;
1283 if (hb->child_detached)
1285 gdk_window_move (hb->float_window, new_x, new_y);
1286 gdk_window_raise (hb->float_window);
1292 GtkRequisition child_requisition;
1294 hb->child_detached = TRUE;
1296 if (GTK_BIN (hb)->child)
1297 gtk_widget_get_child_requisition (GTK_BIN (hb)->child, &child_requisition);
1300 child_requisition.width = 0;
1301 child_requisition.height = 0;
1304 width = child_requisition.width + 2 * GTK_CONTAINER (hb)->border_width;
1305 height = child_requisition.height + 2 * GTK_CONTAINER (hb)->border_width;
1307 if (handle_position == GTK_POS_LEFT || handle_position == GTK_POS_RIGHT)
1308 width += DRAG_HANDLE_SIZE;
1310 height += DRAG_HANDLE_SIZE;
1312 gdk_window_move_resize (hb->float_window, new_x, new_y, width, height);
1313 gdk_window_reparent (hb->bin_window, hb->float_window, 0, 0);
1314 gdk_window_set_geometry_hints (hb->float_window, &geometry, GDK_HINT_POS);
1315 gdk_window_show (hb->float_window);
1316 hb->float_window_mapped = TRUE;
1318 /* this extra move is necessary if we use decorations, or our
1319 * window manager insists on decorations.
1321 gdk_display_sync (gtk_widget_get_display (widget));
1322 gdk_window_move (hb->float_window, new_x, new_y);
1323 gdk_display_sync (gtk_widget_get_display (widget));
1326 handle_box_signals[SIGNAL_CHILD_DETACHED],
1328 GTK_BIN (hb)->child);
1329 gtk_handle_box_draw_ghost (hb);
1331 gtk_widget_queue_resize (widget);
1339 gtk_handle_box_add (GtkContainer *container,
1342 gtk_widget_set_parent_window (widget, GTK_HANDLE_BOX (container)->bin_window);
1343 GTK_CONTAINER_CLASS (gtk_handle_box_parent_class)->add (container, widget);
1347 gtk_handle_box_remove (GtkContainer *container,
1350 GTK_CONTAINER_CLASS (gtk_handle_box_parent_class)->remove (container, widget);
1352 gtk_handle_box_reattach (GTK_HANDLE_BOX (container));
1356 gtk_handle_box_delete_event (GtkWidget *widget,
1359 GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
1361 if (event->window == hb->float_window)
1363 gtk_handle_box_reattach (hb);
1372 gtk_handle_box_reattach (GtkHandleBox *hb)
1374 GtkWidget *widget = GTK_WIDGET (hb);
1376 if (hb->child_detached)
1378 hb->child_detached = FALSE;
1379 if (GTK_WIDGET_REALIZED (hb))
1381 gdk_window_hide (hb->float_window);
1382 gdk_window_reparent (hb->bin_window, widget->window, 0, 0);
1384 if (GTK_BIN (hb)->child)
1386 handle_box_signals[SIGNAL_CHILD_ATTACHED],
1388 GTK_BIN (hb)->child);
1391 hb->float_window_mapped = FALSE;
1394 gtk_handle_box_end_drag (hb, GDK_CURRENT_TIME);
1396 gtk_widget_queue_resize (GTK_WIDGET (hb));
1400 gtk_handle_box_end_drag (GtkHandleBox *hb,
1403 GtkWidget *invisible = gtk_handle_box_get_invisible ();
1405 hb->in_drag = FALSE;
1407 gtk_grab_remove (invisible);
1408 gdk_pointer_ungrab (time);
1409 g_signal_handlers_disconnect_by_func (invisible,
1410 G_CALLBACK (gtk_handle_box_grab_event),
1414 #define __GTK_HANDLE_BOX_C__
1415 #include "gtkaliasdef.c"