1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3 * Copyright (C) 1998 Elliot Lee
4 *
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.
9 *
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.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 /*
20 * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
21 * file for a list of people on the GTK+ Team. See the ChangeLog
22 * files for a list of changes. These files are distributed with
23 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
24 */
25
26 #include "config.h"
27
28 #include <stdlib.h>
29
30 #define GDK_DISABLE_DEPRECATION_WARNINGS
31
32 #include "gtkhandlebox.h"
33 #include "gtkinvisible.h"
34 #include "gtkmain.h"
35 #include "gtkmarshalers.h"
36 #include "gtkrender.h"
37 #include "gtkwindow.h"
38 #include "gtktypebuiltins.h"
39 #include "gtkprivate.h"
40 #include "gtkintl.h"
41
42
43 /**
44 * SECTION:gtkhandlebox
45 * @Short_description: a widget for detachable window portions
46 * @Title: GtkHandleBox
47 *
48 * The #GtkHandleBox widget allows a portion of a window to be "torn
49 * off". It is a bin widget which displays its child and a handle that
50 * the user can drag to tear off a separate window (the “float
51 * window”) containing the child widget. A thin
52 * “ghost” is drawn in the original location of the
53 * handlebox. By dragging the separate window back to its original
54 * location, it can be reattached.
55 *
56 * When reattaching, the ghost and float window, must be aligned
57 * along one of the edges, the “snap edge”.
58 * This either can be specified by the application programmer
59 * explicitly, or GTK+ will pick a reasonable default based
60 * on the handle position.
61 *
62 * To make detaching and reattaching the handlebox as minimally confusing
63 * as possible to the user, it is important to set the snap edge so that
64 * the snap edge does not move when the handlebox is deattached. For
65 * instance, if the handlebox is packed at the bottom of a VBox, then
66 * when the handlebox is detached, the bottom edge of the handlebox's
67 * allocation will remain fixed as the height of the handlebox shrinks,
68 * so the snap edge should be set to %GTK_POS_BOTTOM.
69 *
70 * > #GtkHandleBox has been deprecated. It is very specialized, lacks features
71 * > to make it useful and most importantly does not fit well into modern
72 * > application design. Do not use it. There is no replacement.
73 */
74
75
76 struct _GtkHandleBoxPrivate
77 {
78 /* Properties */
79 GtkPositionType handle_position;
80 gint snap_edge;
81 GtkShadowType shadow_type;
82 gboolean child_detached;
83 /* Properties */
84
85 GtkAllocation attach_allocation;
86 GtkAllocation float_allocation;
87
88 GdkDevice *grab_device;
89
90 GdkWindow *bin_window; /* parent window for children */
91 GdkWindow *float_window;
92
93 /* Variables used during a drag
94 */
95 gint orig_x;
96 gint orig_y;
97
98 guint float_window_mapped : 1;
99 guint in_drag : 1;
100 guint shrink_on_detach : 1;
101 };
102
103 enum {
104 PROP_0,
105 PROP_SHADOW_TYPE,
106 PROP_HANDLE_POSITION,
107 PROP_SNAP_EDGE,
108 PROP_SNAP_EDGE_SET,
109 PROP_CHILD_DETACHED
110 };
111
112 #define DRAG_HANDLE_SIZE 10
113 #define CHILDLESS_SIZE 25
114 #define GHOST_HEIGHT 3
115 #define TOLERANCE 5
116
117 enum {
118 SIGNAL_CHILD_ATTACHED,
119 SIGNAL_CHILD_DETACHED,
120 SIGNAL_LAST
121 };
122
123 /* The algorithm for docking and redocking implemented here
124 * has a couple of nice properties:
125 *
126 * 1) During a single drag, docking always occurs at the
127 * the same cursor position. This means that the users
128 * motions are reversible, and that you won't
129 * undock/dock oscillations.
130 *
131 * 2) Docking generally occurs at user-visible features.
132 * The user, once they figure out to redock, will
133 * have useful information about doing it again in
134 * the future.
135 *
136 * Please try to preserve these properties if you
137 * change the algorithm. (And the current algorithm
138 * is far from ideal). Briefly, the current algorithm
139 * for deciding whether the handlebox is docked or not:
140 *
141 * 1) The decision is done by comparing two rectangles - the
142 * allocation if the widget at the start of the drag,
143 * and the boundary of hb->bin_window at the start of
144 * of the drag offset by the distance that the cursor
145 * has moved.
146 *
147 * 2) These rectangles must have one edge, the “snap_edge”
148 * of the handlebox, aligned within TOLERANCE.
149 *
150 * 3) On the other dimension, the extents of one rectangle
151 * must be contained in the extents of the other,
152 * extended by tolerance. That is, either we can have:
153 *
154 * <-TOLERANCE-|--------bin_window--------------|-TOLERANCE->
155 * <--------float_window-------------------->
156 *
157 * or we can have:
158 *
159 * <-TOLERANCE-|------float_window--------------|-TOLERANCE->
160 * <--------bin_window-------------------->
161 */
162
163 static void gtk_handle_box_set_property (GObject *object,
164 guint param_id,
165 const GValue *value,
166 GParamSpec *pspec);
167 static void gtk_handle_box_get_property (GObject *object,
168 guint param_id,
169 GValue *value,
170 GParamSpec *pspec);
171 static void gtk_handle_box_map (GtkWidget *widget);
172 static void gtk_handle_box_unmap (GtkWidget *widget);
173 static void gtk_handle_box_realize (GtkWidget *widget);
174 static void gtk_handle_box_unrealize (GtkWidget *widget);
175 static void gtk_handle_box_style_updated (GtkWidget *widget);
176 static void gtk_handle_box_size_request (GtkWidget *widget,
177 GtkRequisition *requisition);
178 static void gtk_handle_box_get_preferred_width (GtkWidget *widget,
179 gint *minimum,
180 gint *natural);
181 static void gtk_handle_box_get_preferred_height (GtkWidget *widget,
182 gint *minimum,
183 gint *natural);
184 static void gtk_handle_box_size_allocate (GtkWidget *widget,
185 GtkAllocation *real_allocation);
186 static void gtk_handle_box_add (GtkContainer *container,
187 GtkWidget *widget);
188 static void gtk_handle_box_remove (GtkContainer *container,
189 GtkWidget *widget);
190 static gboolean gtk_handle_box_draw (GtkWidget *widget,
191 cairo_t *cr);
192 static gboolean gtk_handle_box_button_press (GtkWidget *widget,
193 GdkEventButton *event);
194 static gboolean gtk_handle_box_motion (GtkWidget *widget,
195 GdkEventMotion *event);
196 static gboolean gtk_handle_box_delete_event (GtkWidget *widget,
197 GdkEventAny *event);
198 static void gtk_handle_box_reattach (GtkHandleBox *hb);
199 static void gtk_handle_box_end_drag (GtkHandleBox *hb,
200 guint32 time);
201
202 static guint handle_box_signals[SIGNAL_LAST] = { 0 };
203
G_DEFINE_TYPE_WITH_PRIVATE(GtkHandleBox,gtk_handle_box,GTK_TYPE_BIN)204 G_DEFINE_TYPE_WITH_PRIVATE (GtkHandleBox, gtk_handle_box, GTK_TYPE_BIN)
205
206 static void
207 gtk_handle_box_class_init (GtkHandleBoxClass *class)
208 {
209 GObjectClass *gobject_class;
210 GtkWidgetClass *widget_class;
211 GtkContainerClass *container_class;
212
213 gobject_class = (GObjectClass *) class;
214 widget_class = (GtkWidgetClass *) class;
215 container_class = (GtkContainerClass *) class;
216
217 gobject_class->set_property = gtk_handle_box_set_property;
218 gobject_class->get_property = gtk_handle_box_get_property;
219
220 g_object_class_install_property (gobject_class,
221 PROP_SHADOW_TYPE,
222 g_param_spec_enum ("shadow-type",
223 P_("Shadow type"),
224 P_("Appearance of the shadow that surrounds the container"),
225 GTK_TYPE_SHADOW_TYPE,
226 GTK_SHADOW_OUT,
227 GTK_PARAM_READWRITE));
228
229 g_object_class_install_property (gobject_class,
230 PROP_HANDLE_POSITION,
231 g_param_spec_enum ("handle-position",
232 P_("Handle position"),
233 P_("Position of the handle relative to the child widget"),
234 GTK_TYPE_POSITION_TYPE,
235 GTK_POS_LEFT,
236 GTK_PARAM_READWRITE));
237
238 g_object_class_install_property (gobject_class,
239 PROP_SNAP_EDGE,
240 g_param_spec_enum ("snap-edge",
241 P_("Snap edge"),
242 P_("Side of the handlebox that's lined up with the docking point to dock the handlebox"),
243 GTK_TYPE_POSITION_TYPE,
244 GTK_POS_TOP,
245 GTK_PARAM_READWRITE));
246
247 g_object_class_install_property (gobject_class,
248 PROP_SNAP_EDGE_SET,
249 g_param_spec_boolean ("snap-edge-set",
250 P_("Snap edge set"),
251 P_("Whether to use the value from the snap_edge property or a value derived from handle_position"),
252 FALSE,
253 GTK_PARAM_READWRITE));
254
255 g_object_class_install_property (gobject_class,
256 PROP_CHILD_DETACHED,
257 g_param_spec_boolean ("child-detached",
258 P_("Child Detached"),
259 P_("A boolean value indicating whether the handlebox's child is attached or detached."),
260 FALSE,
261 GTK_PARAM_READABLE));
262
263 widget_class->map = gtk_handle_box_map;
264 widget_class->unmap = gtk_handle_box_unmap;
265 widget_class->realize = gtk_handle_box_realize;
266 widget_class->unrealize = gtk_handle_box_unrealize;
267 widget_class->style_updated = gtk_handle_box_style_updated;
268 widget_class->get_preferred_width = gtk_handle_box_get_preferred_width;
269 widget_class->get_preferred_height = gtk_handle_box_get_preferred_height;
270 widget_class->size_allocate = gtk_handle_box_size_allocate;
271 widget_class->draw = gtk_handle_box_draw;
272 widget_class->button_press_event = gtk_handle_box_button_press;
273 widget_class->delete_event = gtk_handle_box_delete_event;
274
275 container_class->add = gtk_handle_box_add;
276 container_class->remove = gtk_handle_box_remove;
277
278 class->child_attached = NULL;
279 class->child_detached = NULL;
280
281 /**
282 * GtkHandleBox::child-attached:
283 * @handlebox: the object which received the signal.
284 * @widget: the child widget of the handlebox.
285 * (this argument provides no extra information
286 * and is here only for backwards-compatibility)
287 *
288 * This signal is emitted when the contents of the
289 * handlebox are reattached to the main window.
290 *
291 * Deprecated: 3.4: #GtkHandleBox has been deprecated.
292 */
293 handle_box_signals[SIGNAL_CHILD_ATTACHED] =
294 g_signal_new (I_("child-attached"),
295 G_OBJECT_CLASS_TYPE (gobject_class),
296 G_SIGNAL_RUN_FIRST,
297 G_STRUCT_OFFSET (GtkHandleBoxClass, child_attached),
298 NULL, NULL,
299 NULL,
300 G_TYPE_NONE, 1,
301 GTK_TYPE_WIDGET);
302
303 /**
304 * GtkHandleBox::child-detached:
305 * @handlebox: the object which received the signal.
306 * @widget: the child widget of the handlebox.
307 * (this argument provides no extra information
308 * and is here only for backwards-compatibility)
309 *
310 * This signal is emitted when the contents of the
311 * handlebox are detached from the main window.
312 *
313 * Deprecated: 3.4: #GtkHandleBox has been deprecated.
314 */
315 handle_box_signals[SIGNAL_CHILD_DETACHED] =
316 g_signal_new (I_("child-detached"),
317 G_OBJECT_CLASS_TYPE (gobject_class),
318 G_SIGNAL_RUN_FIRST,
319 G_STRUCT_OFFSET (GtkHandleBoxClass, child_detached),
320 NULL, NULL,
321 NULL,
322 G_TYPE_NONE, 1,
323 GTK_TYPE_WIDGET);
324 }
325
326 static void
gtk_handle_box_init(GtkHandleBox * handle_box)327 gtk_handle_box_init (GtkHandleBox *handle_box)
328 {
329 GtkHandleBoxPrivate *priv;
330 GtkStyleContext *context;
331
332 handle_box->priv = gtk_handle_box_get_instance_private (handle_box);
333 priv = handle_box->priv;
334
335 gtk_widget_set_has_window (GTK_WIDGET (handle_box), TRUE);
336
337 priv->bin_window = NULL;
338 priv->float_window = NULL;
339 priv->shadow_type = GTK_SHADOW_OUT;
340 priv->handle_position = GTK_POS_LEFT;
341 priv->float_window_mapped = FALSE;
342 priv->child_detached = FALSE;
343 priv->in_drag = FALSE;
344 priv->shrink_on_detach = TRUE;
345 priv->snap_edge = -1;
346
347 context = gtk_widget_get_style_context (GTK_WIDGET (handle_box));
348 gtk_style_context_add_class (context, GTK_STYLE_CLASS_DOCK);
349 }
350
351 static void
gtk_handle_box_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)352 gtk_handle_box_set_property (GObject *object,
353 guint prop_id,
354 const GValue *value,
355 GParamSpec *pspec)
356 {
357 GtkHandleBox *handle_box = GTK_HANDLE_BOX (object);
358
359 switch (prop_id)
360 {
361 case PROP_SHADOW_TYPE:
362 gtk_handle_box_set_shadow_type (handle_box, g_value_get_enum (value));
363 break;
364 case PROP_HANDLE_POSITION:
365 gtk_handle_box_set_handle_position (handle_box, g_value_get_enum (value));
366 break;
367 case PROP_SNAP_EDGE:
368 gtk_handle_box_set_snap_edge (handle_box, g_value_get_enum (value));
369 break;
370 case PROP_SNAP_EDGE_SET:
371 if (!g_value_get_boolean (value))
372 gtk_handle_box_set_snap_edge (handle_box, (GtkPositionType)-1);
373 break;
374 default:
375 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
376 break;
377 }
378 }
379
380 static void
gtk_handle_box_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)381 gtk_handle_box_get_property (GObject *object,
382 guint prop_id,
383 GValue *value,
384 GParamSpec *pspec)
385 {
386 GtkHandleBox *handle_box = GTK_HANDLE_BOX (object);
387 GtkHandleBoxPrivate *priv = handle_box->priv;
388
389 switch (prop_id)
390 {
391 case PROP_SHADOW_TYPE:
392 g_value_set_enum (value, priv->shadow_type);
393 break;
394 case PROP_HANDLE_POSITION:
395 g_value_set_enum (value, priv->handle_position);
396 break;
397 case PROP_SNAP_EDGE:
398 g_value_set_enum (value,
399 (priv->snap_edge == -1 ?
400 GTK_POS_TOP : priv->snap_edge));
401 break;
402 case PROP_SNAP_EDGE_SET:
403 g_value_set_boolean (value, priv->snap_edge != -1);
404 break;
405 case PROP_CHILD_DETACHED:
406 g_value_set_boolean (value, priv->child_detached);
407 break;
408 default:
409 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
410 break;
411 }
412 }
413
414 /**
415 * gtk_handle_box_new:
416 *
417 * Create a new handle box.
418 *
419 * Returns: a new #GtkHandleBox.
420 *
421 * Deprecated: 3.4: #GtkHandleBox has been deprecated.
422 */
423 GtkWidget*
gtk_handle_box_new(void)424 gtk_handle_box_new (void)
425 {
426 return g_object_new (GTK_TYPE_HANDLE_BOX, NULL);
427 }
428
429 static void
gtk_handle_box_map(GtkWidget * widget)430 gtk_handle_box_map (GtkWidget *widget)
431 {
432 GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
433 GtkHandleBoxPrivate *priv = hb->priv;
434 GtkBin *bin = GTK_BIN (widget);
435 GtkWidget *child;
436
437 gtk_widget_set_mapped (widget, TRUE);
438
439 child = gtk_bin_get_child (bin);
440 if (child != NULL &&
441 gtk_widget_get_visible (child) &&
442 !gtk_widget_get_mapped (child))
443 gtk_widget_map (child);
444
445 if (priv->child_detached && !priv->float_window_mapped)
446 {
447 gdk_window_show (priv->float_window);
448 priv->float_window_mapped = TRUE;
449 }
450
451 gdk_window_show (priv->bin_window);
452 gdk_window_show (gtk_widget_get_window (widget));
453 }
454
455 static void
gtk_handle_box_unmap(GtkWidget * widget)456 gtk_handle_box_unmap (GtkWidget *widget)
457 {
458 GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
459 GtkHandleBoxPrivate *priv = hb->priv;
460
461 gtk_widget_set_mapped (widget, FALSE);
462
463 gdk_window_hide (gtk_widget_get_window (widget));
464 if (priv->float_window_mapped)
465 {
466 gdk_window_hide (priv->float_window);
467 priv->float_window_mapped = FALSE;
468 }
469
470 GTK_WIDGET_CLASS (gtk_handle_box_parent_class)->unmap (widget);
471 }
472
473 static void
gtk_handle_box_realize(GtkWidget * widget)474 gtk_handle_box_realize (GtkWidget *widget)
475 {
476 GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
477 GtkHandleBoxPrivate *priv = hb->priv;
478 GtkAllocation allocation;
479 GtkRequisition requisition;
480 GtkStyleContext *context;
481 GtkWidget *child;
482 GdkWindow *window;
483 GdkWindowAttr attributes;
484 gint attributes_mask;
485
486 gtk_widget_set_realized (widget, TRUE);
487
488 gtk_widget_get_allocation (widget, &allocation);
489
490 attributes.x = allocation.x;
491 attributes.y = allocation.y;
492 attributes.width = allocation.width;
493 attributes.height = allocation.height;
494 attributes.window_type = GDK_WINDOW_CHILD;
495 attributes.wclass = GDK_INPUT_OUTPUT;
496 attributes.visual = gtk_widget_get_visual (widget);
497 attributes.event_mask = gtk_widget_get_events (widget);
498 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
499
500 window = gdk_window_new (gtk_widget_get_parent_window (widget),
501 &attributes, attributes_mask);
502 gtk_widget_set_window (widget, window);
503 gdk_window_set_user_data (window, widget);
504
505 attributes.x = 0;
506 attributes.y = 0;
507 attributes.width = allocation.width;
508 attributes.height = allocation.height;
509 attributes.window_type = GDK_WINDOW_CHILD;
510 attributes.event_mask = (gtk_widget_get_events (widget)
511 | GDK_BUTTON1_MOTION_MASK
512 | GDK_POINTER_MOTION_HINT_MASK
513 | GDK_BUTTON_PRESS_MASK
514 | GDK_BUTTON_RELEASE_MASK);
515 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
516
517 priv->bin_window = gdk_window_new (window,
518 &attributes, attributes_mask);
519 gdk_window_set_user_data (priv->bin_window, widget);
520
521 child = gtk_bin_get_child (GTK_BIN (hb));
522 if (child)
523 gtk_widget_set_parent_window (child, priv->bin_window);
524
525 gtk_widget_get_preferred_size (widget, &requisition, NULL);
526
527 attributes.x = 0;
528 attributes.y = 0;
529 attributes.width = requisition.width;
530 attributes.height = requisition.height;
531 attributes.window_type = GDK_WINDOW_TOPLEVEL;
532 attributes.wclass = GDK_INPUT_OUTPUT;
533 attributes.visual = gtk_widget_get_visual (widget);
534 attributes.event_mask = (gtk_widget_get_events (widget)
535 | GDK_KEY_PRESS_MASK
536 | GDK_ENTER_NOTIFY_MASK
537 | GDK_LEAVE_NOTIFY_MASK
538 | GDK_FOCUS_CHANGE_MASK
539 | GDK_STRUCTURE_MASK);
540 attributes.type_hint = GDK_WINDOW_TYPE_HINT_TOOLBAR;
541 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_TYPE_HINT;
542 priv->float_window = gdk_window_new (gdk_screen_get_root_window (gtk_widget_get_screen (widget)),
543 &attributes, attributes_mask);
544 gdk_window_set_user_data (priv->float_window, widget);
545 gdk_window_set_decorations (priv->float_window, 0);
546 gdk_window_set_type_hint (priv->float_window, GDK_WINDOW_TYPE_HINT_TOOLBAR);
547
548 context = gtk_widget_get_style_context (widget);
549 gtk_style_context_set_background (context, window);
550 gtk_style_context_set_background (context, priv->bin_window);
551 gtk_style_context_set_background (context, priv->float_window);
552 }
553
554 static void
gtk_handle_box_unrealize(GtkWidget * widget)555 gtk_handle_box_unrealize (GtkWidget *widget)
556 {
557 GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
558 GtkHandleBoxPrivate *priv = hb->priv;
559
560 gdk_window_set_user_data (priv->bin_window, NULL);
561 gdk_window_destroy (priv->bin_window);
562 priv->bin_window = NULL;
563 gdk_window_set_user_data (priv->float_window, NULL);
564 gdk_window_destroy (priv->float_window);
565 priv->float_window = NULL;
566
567 GTK_WIDGET_CLASS (gtk_handle_box_parent_class)->unrealize (widget);
568 }
569
570 static void
gtk_handle_box_style_updated(GtkWidget * widget)571 gtk_handle_box_style_updated (GtkWidget *widget)
572 {
573 GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
574 GtkHandleBoxPrivate *priv = hb->priv;
575
576 GTK_WIDGET_CLASS (gtk_handle_box_parent_class)->style_updated (widget);
577
578 if (gtk_widget_get_realized (widget) &&
579 gtk_widget_get_has_window (widget))
580 {
581 GtkStateFlags state;
582 GtkStyleContext *context;
583
584 context = gtk_widget_get_style_context (widget);
585 state = gtk_widget_get_state_flags (widget);
586
587 gtk_style_context_save (context);
588 gtk_style_context_set_state (context, state);
589
590 gtk_style_context_set_background (context, gtk_widget_get_window (widget));
591 gtk_style_context_set_background (context, priv->bin_window);
592 gtk_style_context_set_background (context, priv->float_window);
593
594 gtk_style_context_restore (context);
595 }
596 }
597
598 static int
effective_handle_position(GtkHandleBox * hb)599 effective_handle_position (GtkHandleBox *hb)
600 {
601 GtkHandleBoxPrivate *priv = hb->priv;
602 int handle_position;
603
604 if (gtk_widget_get_direction (GTK_WIDGET (hb)) == GTK_TEXT_DIR_LTR)
605 handle_position = priv->handle_position;
606 else
607 {
608 switch (priv->handle_position)
609 {
610 case GTK_POS_LEFT:
611 handle_position = GTK_POS_RIGHT;
612 break;
613 case GTK_POS_RIGHT:
614 handle_position = GTK_POS_LEFT;
615 break;
616 default:
617 handle_position = priv->handle_position;
618 break;
619 }
620 }
621
622 return handle_position;
623 }
624
625 static void
gtk_handle_box_size_request(GtkWidget * widget,GtkRequisition * requisition)626 gtk_handle_box_size_request (GtkWidget *widget,
627 GtkRequisition *requisition)
628 {
629 GtkBin *bin = GTK_BIN (widget);
630 GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
631 GtkHandleBoxPrivate *priv = hb->priv;
632 GtkRequisition child_requisition;
633 GtkWidget *child;
634 gint handle_position;
635
636 handle_position = effective_handle_position (hb);
637
638 if (handle_position == GTK_POS_LEFT ||
639 handle_position == GTK_POS_RIGHT)
640 {
641 requisition->width = DRAG_HANDLE_SIZE;
642 requisition->height = 0;
643 }
644 else
645 {
646 requisition->width = 0;
647 requisition->height = DRAG_HANDLE_SIZE;
648 }
649
650 child = gtk_bin_get_child (bin);
651 /* if our child is not visible, we still request its size, since we
652 * won't have any useful hint for our size otherwise.
653 */
654 if (child)
655 {
656 gtk_widget_get_preferred_size (child, &child_requisition, NULL);
657 }
658 else
659 {
660 child_requisition.width = 0;
661 child_requisition.height = 0;
662 }
663
664 if (priv->child_detached)
665 {
666 /* FIXME: This doesn't work currently */
667 if (!priv->shrink_on_detach)
668 {
669 if (handle_position == GTK_POS_LEFT ||
670 handle_position == GTK_POS_RIGHT)
671 requisition->height += child_requisition.height;
672 else
673 requisition->width += child_requisition.width;
674 }
675 else
676 {
677 GtkStyleContext *context;
678 GtkStateFlags state;
679 GtkBorder padding;
680
681 context = gtk_widget_get_style_context (widget);
682 state = gtk_widget_get_state_flags (widget);
683 gtk_style_context_get_padding (context, state, &padding);
684
685 if (handle_position == GTK_POS_LEFT ||
686 handle_position == GTK_POS_RIGHT)
687 requisition->height += padding.top;
688 else
689 requisition->width += padding.left;
690 }
691 }
692 else
693 {
694 guint border_width;
695
696 border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
697 requisition->width += border_width * 2;
698 requisition->height += border_width * 2;
699
700 if (child)
701 {
702 requisition->width += child_requisition.width;
703 requisition->height += child_requisition.height;
704 }
705 else
706 {
707 requisition->width += CHILDLESS_SIZE;
708 requisition->height += CHILDLESS_SIZE;
709 }
710 }
711 }
712
713 static void
gtk_handle_box_get_preferred_width(GtkWidget * widget,gint * minimum,gint * natural)714 gtk_handle_box_get_preferred_width (GtkWidget *widget,
715 gint *minimum,
716 gint *natural)
717 {
718 GtkRequisition requisition;
719
720 gtk_handle_box_size_request (widget, &requisition);
721
722 *minimum = *natural = requisition.width;
723 }
724
725 static void
gtk_handle_box_get_preferred_height(GtkWidget * widget,gint * minimum,gint * natural)726 gtk_handle_box_get_preferred_height (GtkWidget *widget,
727 gint *minimum,
728 gint *natural)
729 {
730 GtkRequisition requisition;
731
732 gtk_handle_box_size_request (widget, &requisition);
733
734 *minimum = *natural = requisition.height;
735 }
736
737
738 static void
gtk_handle_box_size_allocate(GtkWidget * widget,GtkAllocation * allocation)739 gtk_handle_box_size_allocate (GtkWidget *widget,
740 GtkAllocation *allocation)
741 {
742 GtkBin *bin = GTK_BIN (widget);
743 GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
744 GtkHandleBoxPrivate *priv = hb->priv;
745 GtkRequisition child_requisition;
746 GtkWidget *child;
747 gint handle_position;
748
749 handle_position = effective_handle_position (hb);
750
751 child = gtk_bin_get_child (bin);
752
753 if (child)
754 {
755 gtk_widget_get_preferred_size (child, &child_requisition, NULL);
756 }
757 else
758 {
759 child_requisition.width = 0;
760 child_requisition.height = 0;
761 }
762
763 gtk_widget_set_allocation (widget, allocation);
764
765 if (gtk_widget_get_realized (widget))
766 gdk_window_move_resize (gtk_widget_get_window (widget),
767 allocation->x, allocation->y,
768 allocation->width, allocation->height);
769
770 if (child != NULL && gtk_widget_get_visible (child))
771 {
772 GtkAllocation child_allocation;
773 guint border_width;
774
775 border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
776
777 child_allocation.x = border_width;
778 child_allocation.y = border_width;
779 if (handle_position == GTK_POS_LEFT)
780 child_allocation.x += DRAG_HANDLE_SIZE;
781 else if (handle_position == GTK_POS_TOP)
782 child_allocation.y += DRAG_HANDLE_SIZE;
783
784 if (priv->child_detached)
785 {
786 guint float_width;
787 guint float_height;
788
789 child_allocation.width = child_requisition.width;
790 child_allocation.height = child_requisition.height;
791
792 float_width = child_allocation.width + 2 * border_width;
793 float_height = child_allocation.height + 2 * border_width;
794
795 if (handle_position == GTK_POS_LEFT ||
796 handle_position == GTK_POS_RIGHT)
797 float_width += DRAG_HANDLE_SIZE;
798 else
799 float_height += DRAG_HANDLE_SIZE;
800
801 if (gtk_widget_get_realized (widget))
802 {
803 gdk_window_resize (priv->float_window,
804 float_width,
805 float_height);
806 gdk_window_move_resize (priv->bin_window,
807 0,
808 0,
809 float_width,
810 float_height);
811 }
812 }
813 else
814 {
815 child_allocation.width = MAX (1, (gint) allocation->width - 2 * border_width);
816 child_allocation.height = MAX (1, (gint) allocation->height - 2 * border_width);
817
818 if (handle_position == GTK_POS_LEFT ||
819 handle_position == GTK_POS_RIGHT)
820 child_allocation.width -= DRAG_HANDLE_SIZE;
821 else
822 child_allocation.height -= DRAG_HANDLE_SIZE;
823
824 if (gtk_widget_get_realized (widget))
825 gdk_window_move_resize (priv->bin_window,
826 0,
827 0,
828 allocation->width,
829 allocation->height);
830 }
831
832 gtk_widget_size_allocate (child, &child_allocation);
833 }
834 }
835
836 static void
gtk_handle_box_draw_ghost(GtkHandleBox * hb,cairo_t * cr)837 gtk_handle_box_draw_ghost (GtkHandleBox *hb,
838 cairo_t *cr)
839 {
840 GtkWidget *widget = GTK_WIDGET (hb);
841 GtkStateFlags state;
842 GtkStyleContext *context;
843 guint x;
844 guint y;
845 guint width;
846 guint height;
847 gint allocation_width;
848 gint allocation_height;
849 gint handle_position;
850
851 handle_position = effective_handle_position (hb);
852 allocation_width = gtk_widget_get_allocated_width (widget);
853 allocation_height = gtk_widget_get_allocated_height (widget);
854
855 if (handle_position == GTK_POS_LEFT ||
856 handle_position == GTK_POS_RIGHT)
857 {
858 x = handle_position == GTK_POS_LEFT ? 0 : allocation_width - DRAG_HANDLE_SIZE;
859 y = 0;
860 width = DRAG_HANDLE_SIZE;
861 height = allocation_height;
862 }
863 else
864 {
865 x = 0;
866 y = handle_position == GTK_POS_TOP ? 0 : allocation_height - DRAG_HANDLE_SIZE;
867 width = allocation_width;
868 height = DRAG_HANDLE_SIZE;
869 }
870
871 context = gtk_widget_get_style_context (widget);
872 state = gtk_widget_get_state_flags (widget);
873
874 gtk_style_context_save (context);
875 gtk_style_context_set_state (context, state);
876
877 gtk_render_background (context, cr, x, y, width, height);
878 gtk_render_frame (context, cr, x, y, width, height);
879
880 if (handle_position == GTK_POS_LEFT ||
881 handle_position == GTK_POS_RIGHT)
882 gtk_render_line (context, cr,
883 handle_position == GTK_POS_LEFT ? DRAG_HANDLE_SIZE : 0,
884 allocation_height / 2,
885 handle_position == GTK_POS_LEFT ? allocation_width : allocation_width - DRAG_HANDLE_SIZE,
886 allocation_height / 2);
887 else
888 gtk_render_line (context, cr,
889 allocation_width / 2,
890 handle_position == GTK_POS_TOP ? DRAG_HANDLE_SIZE : 0,
891 allocation_width / 2,
892 handle_position == GTK_POS_TOP ? allocation_height : allocation_height - DRAG_HANDLE_SIZE);
893
894 gtk_style_context_restore (context);
895 }
896
897 /**
898 * gtk_handle_box_set_shadow_type:
899 * @handle_box: a #GtkHandleBox
900 * @type: the shadow type.
901 *
902 * Sets the type of shadow to be drawn around the border
903 * of the handle box.
904 *
905 * Deprecated: 3.4: #GtkHandleBox has been deprecated.
906 */
907 void
gtk_handle_box_set_shadow_type(GtkHandleBox * handle_box,GtkShadowType type)908 gtk_handle_box_set_shadow_type (GtkHandleBox *handle_box,
909 GtkShadowType type)
910 {
911 GtkHandleBoxPrivate *priv;
912
913 g_return_if_fail (GTK_IS_HANDLE_BOX (handle_box));
914
915 priv = handle_box->priv;
916
917 if ((GtkShadowType) priv->shadow_type != type)
918 {
919 priv->shadow_type = type;
920 g_object_notify (G_OBJECT (handle_box), "shadow-type");
921 gtk_widget_queue_resize (GTK_WIDGET (handle_box));
922 }
923 }
924
925 /**
926 * gtk_handle_box_get_shadow_type:
927 * @handle_box: a #GtkHandleBox
928 *
929 * Gets the type of shadow drawn around the handle box. See
930 * gtk_handle_box_set_shadow_type().
931 *
932 * Returns: the type of shadow currently drawn around the handle box.
933 *
934 * Deprecated: 3.4: #GtkHandleBox has been deprecated.
935 **/
936 GtkShadowType
gtk_handle_box_get_shadow_type(GtkHandleBox * handle_box)937 gtk_handle_box_get_shadow_type (GtkHandleBox *handle_box)
938 {
939 g_return_val_if_fail (GTK_IS_HANDLE_BOX (handle_box), GTK_SHADOW_ETCHED_OUT);
940
941 return handle_box->priv->shadow_type;
942 }
943
944 /**
945 * gtk_handle_box_set_handle_position:
946 * @handle_box: a #GtkHandleBox
947 * @position: the side of the handlebox where the handle should be drawn.
948 *
949 * Sets the side of the handlebox where the handle is drawn.
950 *
951 * Deprecated: 3.4: #GtkHandleBox has been deprecated.
952 */
953 void
gtk_handle_box_set_handle_position(GtkHandleBox * handle_box,GtkPositionType position)954 gtk_handle_box_set_handle_position (GtkHandleBox *handle_box,
955 GtkPositionType position)
956 {
957 GtkHandleBoxPrivate *priv;
958
959 g_return_if_fail (GTK_IS_HANDLE_BOX (handle_box));
960
961 priv = handle_box->priv;
962
963 if ((GtkPositionType) priv->handle_position != position)
964 {
965 priv->handle_position = position;
966 g_object_notify (G_OBJECT (handle_box), "handle-position");
967 gtk_widget_queue_resize (GTK_WIDGET (handle_box));
968 }
969 }
970
971 /**
972 * gtk_handle_box_get_handle_position:
973 * @handle_box: a #GtkHandleBox
974 *
975 * Gets the handle position of the handle box. See
976 * gtk_handle_box_set_handle_position().
977 *
978 * Returns: the current handle position.
979 *
980 * Deprecated: 3.4: #GtkHandleBox has been deprecated.
981 **/
982 GtkPositionType
gtk_handle_box_get_handle_position(GtkHandleBox * handle_box)983 gtk_handle_box_get_handle_position (GtkHandleBox *handle_box)
984 {
985 g_return_val_if_fail (GTK_IS_HANDLE_BOX (handle_box), GTK_POS_LEFT);
986
987 return handle_box->priv->handle_position;
988 }
989
990 /**
991 * gtk_handle_box_set_snap_edge:
992 * @handle_box: a #GtkHandleBox
993 * @edge: the snap edge, or -1 to unset the value; in which
994 * case GTK+ will try to guess an appropriate value
995 * in the future.
996 *
997 * Sets the snap edge of a handlebox. The snap edge is
998 * the edge of the detached child that must be aligned
999 * with the corresponding edge of the “ghost” left
1000 * behind when the child was detached to reattach
1001 * the torn-off window. Usually, the snap edge should
1002 * be chosen so that it stays in the same place on
1003 * the screen when the handlebox is torn off.
1004 *
1005 * If the snap edge is not set, then an appropriate value
1006 * will be guessed from the handle position. If the
1007 * handle position is %GTK_POS_RIGHT or %GTK_POS_LEFT,
1008 * then the snap edge will be %GTK_POS_TOP, otherwise
1009 * it will be %GTK_POS_LEFT.
1010 *
1011 * Deprecated: 3.4: #GtkHandleBox has been deprecated.
1012 */
1013 void
gtk_handle_box_set_snap_edge(GtkHandleBox * handle_box,GtkPositionType edge)1014 gtk_handle_box_set_snap_edge (GtkHandleBox *handle_box,
1015 GtkPositionType edge)
1016 {
1017 GtkHandleBoxPrivate *priv;
1018
1019 g_return_if_fail (GTK_IS_HANDLE_BOX (handle_box));
1020
1021 priv = handle_box->priv;
1022
1023 if (priv->snap_edge != edge)
1024 {
1025 priv->snap_edge = edge;
1026
1027 g_object_freeze_notify (G_OBJECT (handle_box));
1028 g_object_notify (G_OBJECT (handle_box), "snap-edge");
1029 g_object_notify (G_OBJECT (handle_box), "snap-edge-set");
1030 g_object_thaw_notify (G_OBJECT (handle_box));
1031 }
1032 }
1033
1034 /**
1035 * gtk_handle_box_get_snap_edge:
1036 * @handle_box: a #GtkHandleBox
1037 *
1038 * Gets the edge used for determining reattachment of the handle box.
1039 * See gtk_handle_box_set_snap_edge().
1040 *
1041 * Returns: the edge used for determining reattachment, or
1042 * (GtkPositionType)-1 if this is determined (as per default)
1043 * from the handle position.
1044 *
1045 * Deprecated: 3.4: #GtkHandleBox has been deprecated.
1046 **/
1047 GtkPositionType
gtk_handle_box_get_snap_edge(GtkHandleBox * handle_box)1048 gtk_handle_box_get_snap_edge (GtkHandleBox *handle_box)
1049 {
1050 g_return_val_if_fail (GTK_IS_HANDLE_BOX (handle_box), (GtkPositionType)-1);
1051
1052 return (GtkPositionType)handle_box->priv->snap_edge;
1053 }
1054
1055 /**
1056 * gtk_handle_box_get_child_detached:
1057 * @handle_box: a #GtkHandleBox
1058 *
1059 * Whether the handlebox’s child is currently detached.
1060 *
1061 * Returns: %TRUE if the child is currently detached, otherwise %FALSE
1062 *
1063 * Since: 2.14
1064 *
1065 * Deprecated: 3.4: #GtkHandleBox has been deprecated.
1066 **/
1067 gboolean
gtk_handle_box_get_child_detached(GtkHandleBox * handle_box)1068 gtk_handle_box_get_child_detached (GtkHandleBox *handle_box)
1069 {
1070 g_return_val_if_fail (GTK_IS_HANDLE_BOX (handle_box), FALSE);
1071
1072 return handle_box->priv->child_detached;
1073 }
1074
1075 static void
gtk_handle_box_paint(GtkWidget * widget,cairo_t * cr)1076 gtk_handle_box_paint (GtkWidget *widget,
1077 cairo_t *cr)
1078 {
1079 GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
1080 GtkHandleBoxPrivate *priv = hb->priv;
1081 GtkBin *bin = GTK_BIN (widget);
1082 GtkStyleContext *context;
1083 GtkStateFlags state;
1084 GtkWidget *child;
1085 gint width, height;
1086 GdkRectangle rect;
1087 gint handle_position;
1088
1089 handle_position = effective_handle_position (hb);
1090
1091 width = gdk_window_get_width (priv->bin_window);
1092 height = gdk_window_get_height (priv->bin_window);
1093
1094 context = gtk_widget_get_style_context (widget);
1095 state = gtk_widget_get_state_flags (widget);
1096
1097 gtk_style_context_save (context);
1098 gtk_style_context_set_state (context, state);
1099
1100 gtk_render_background (context, cr, 0, 0, width, height);
1101 gtk_render_frame (context, cr, 0, 0, width, height);
1102
1103 switch (handle_position)
1104 {
1105 case GTK_POS_LEFT:
1106 rect.x = 0;
1107 rect.y = 0;
1108 rect.width = DRAG_HANDLE_SIZE;
1109 rect.height = height;
1110 break;
1111 case GTK_POS_RIGHT:
1112 rect.x = width - DRAG_HANDLE_SIZE;
1113 rect.y = 0;
1114 rect.width = DRAG_HANDLE_SIZE;
1115 rect.height = height;
1116 break;
1117 case GTK_POS_TOP:
1118 rect.x = 0;
1119 rect.y = 0;
1120 rect.width = width;
1121 rect.height = DRAG_HANDLE_SIZE;
1122 break;
1123 case GTK_POS_BOTTOM:
1124 rect.x = 0;
1125 rect.y = height - DRAG_HANDLE_SIZE;
1126 rect.width = width;
1127 rect.height = DRAG_HANDLE_SIZE;
1128 break;
1129 default:
1130 g_assert_not_reached ();
1131 break;
1132 }
1133
1134 gtk_render_handle (context, cr,
1135 rect.x, rect.y, rect.width, rect.height);
1136
1137 child = gtk_bin_get_child (bin);
1138 if (child != NULL && gtk_widget_get_visible (child))
1139 GTK_WIDGET_CLASS (gtk_handle_box_parent_class)->draw (widget, cr);
1140
1141 gtk_style_context_restore (context);
1142 }
1143
1144 static gboolean
gtk_handle_box_draw(GtkWidget * widget,cairo_t * cr)1145 gtk_handle_box_draw (GtkWidget *widget,
1146 cairo_t *cr)
1147 {
1148 GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
1149 GtkHandleBoxPrivate *priv = hb->priv;
1150
1151 if (gtk_cairo_should_draw_window (cr, gtk_widget_get_window (widget)))
1152 {
1153 if (priv->child_detached)
1154 gtk_handle_box_draw_ghost (hb, cr);
1155 }
1156 else if (gtk_cairo_should_draw_window (cr, priv->bin_window))
1157 gtk_handle_box_paint (widget, cr);
1158
1159 return FALSE;
1160 }
1161
1162 static GtkWidget *
gtk_handle_box_get_invisible(void)1163 gtk_handle_box_get_invisible (void)
1164 {
1165 static GtkWidget *handle_box_invisible = NULL;
1166
1167 if (!handle_box_invisible)
1168 {
1169 handle_box_invisible = gtk_invisible_new ();
1170 gtk_widget_show (handle_box_invisible);
1171 }
1172
1173 return handle_box_invisible;
1174 }
1175
1176 static gboolean
gtk_handle_box_grab_event(GtkWidget * widget,GdkEvent * event,GtkHandleBox * hb)1177 gtk_handle_box_grab_event (GtkWidget *widget,
1178 GdkEvent *event,
1179 GtkHandleBox *hb)
1180 {
1181 GtkHandleBoxPrivate *priv = hb->priv;
1182
1183 switch (event->type)
1184 {
1185 case GDK_BUTTON_RELEASE:
1186 if (priv->in_drag) /* sanity check */
1187 {
1188 gtk_handle_box_end_drag (hb, event->button.time);
1189 return TRUE;
1190 }
1191 break;
1192
1193 case GDK_MOTION_NOTIFY:
1194 return gtk_handle_box_motion (GTK_WIDGET (hb), (GdkEventMotion *)event);
1195 break;
1196
1197 default:
1198 break;
1199 }
1200
1201 return FALSE;
1202 }
1203
1204 static gboolean
gtk_handle_box_button_press(GtkWidget * widget,GdkEventButton * event)1205 gtk_handle_box_button_press (GtkWidget *widget,
1206 GdkEventButton *event)
1207 {
1208 GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
1209 GtkHandleBoxPrivate *priv = hb->priv;
1210 gboolean event_handled;
1211 GdkCursor *fleur;
1212 gint handle_position;
1213
1214 handle_position = effective_handle_position (hb);
1215
1216 event_handled = FALSE;
1217 if ((event->button == GDK_BUTTON_PRIMARY) &&
1218 (event->type == GDK_BUTTON_PRESS || event->type == GDK_2BUTTON_PRESS))
1219 {
1220 GtkWidget *child;
1221 gboolean in_handle;
1222
1223 if (event->window != priv->bin_window)
1224 return FALSE;
1225
1226 child = gtk_bin_get_child (GTK_BIN (hb));
1227
1228 if (child)
1229 {
1230 GtkAllocation child_allocation;
1231 guint border_width;
1232
1233 gtk_widget_get_allocation (child, &child_allocation);
1234 border_width = gtk_container_get_border_width (GTK_CONTAINER (hb));
1235
1236 switch (handle_position)
1237 {
1238 case GTK_POS_LEFT:
1239 in_handle = event->x < DRAG_HANDLE_SIZE;
1240 break;
1241 case GTK_POS_TOP:
1242 in_handle = event->y < DRAG_HANDLE_SIZE;
1243 break;
1244 case GTK_POS_RIGHT:
1245 in_handle = event->x > 2 * border_width + child_allocation.width;
1246 break;
1247 case GTK_POS_BOTTOM:
1248 in_handle = event->y > 2 * border_width + child_allocation.height;
1249 break;
1250 default:
1251 in_handle = FALSE;
1252 break;
1253 }
1254 }
1255 else
1256 {
1257 in_handle = FALSE;
1258 event_handled = TRUE;
1259 }
1260
1261 if (in_handle)
1262 {
1263 if (event->type == GDK_BUTTON_PRESS) /* Start a drag */
1264 {
1265 GtkWidget *invisible = gtk_handle_box_get_invisible ();
1266 GdkWindow *window;
1267 gint root_x, root_y;
1268
1269 gtk_invisible_set_screen (GTK_INVISIBLE (invisible),
1270 gtk_widget_get_screen (GTK_WIDGET (hb)));
1271 gdk_window_get_origin (priv->bin_window, &root_x, &root_y);
1272
1273 priv->orig_x = event->x_root;
1274 priv->orig_y = event->y_root;
1275
1276 priv->float_allocation.x = root_x - event->x_root;
1277 priv->float_allocation.y = root_y - event->y_root;
1278 priv->float_allocation.width = gdk_window_get_width (priv->bin_window);
1279 priv->float_allocation.height = gdk_window_get_height (priv->bin_window);
1280
1281 window = gtk_widget_get_window (widget);
1282 if (gdk_window_is_viewable (window))
1283 {
1284 gdk_window_get_origin (window, &root_x, &root_y);
1285
1286 priv->attach_allocation.x = root_x;
1287 priv->attach_allocation.y = root_y;
1288 priv->attach_allocation.width = gdk_window_get_width (window);
1289 priv->attach_allocation.height = gdk_window_get_height (window);
1290 }
1291 else
1292 {
1293 priv->attach_allocation.x = -1;
1294 priv->attach_allocation.y = -1;
1295 priv->attach_allocation.width = 0;
1296 priv->attach_allocation.height = 0;
1297 }
1298 priv->in_drag = TRUE;
1299 priv->grab_device = event->device;
1300 fleur = gdk_cursor_new_for_display (gtk_widget_get_display (widget),
1301 GDK_FLEUR);
1302 if (gdk_device_grab (event->device,
1303 gtk_widget_get_window (invisible),
1304 GDK_OWNERSHIP_WINDOW,
1305 FALSE,
1306 (GDK_BUTTON1_MOTION_MASK |
1307 GDK_POINTER_MOTION_HINT_MASK |
1308 GDK_BUTTON_RELEASE_MASK),
1309 fleur,
1310 event->time) != GDK_GRAB_SUCCESS)
1311 {
1312 priv->in_drag = FALSE;
1313 priv->grab_device = NULL;
1314 }
1315 else
1316 {
1317 gtk_device_grab_add (invisible, priv->grab_device, TRUE);
1318 g_signal_connect (invisible, "event",
1319 G_CALLBACK (gtk_handle_box_grab_event), hb);
1320 }
1321
1322 g_object_unref (fleur);
1323 event_handled = TRUE;
1324 }
1325 else if (priv->child_detached) /* Double click */
1326 {
1327 gtk_handle_box_reattach (hb);
1328 }
1329 }
1330 }
1331
1332 return event_handled;
1333 }
1334
1335 static gboolean
gtk_handle_box_motion(GtkWidget * widget,GdkEventMotion * event)1336 gtk_handle_box_motion (GtkWidget *widget,
1337 GdkEventMotion *event)
1338 {
1339 GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
1340 GtkHandleBoxPrivate *priv = hb->priv;
1341 GtkWidget *child;
1342 gint new_x, new_y;
1343 gint snap_edge;
1344 gboolean is_snapped = FALSE;
1345 gint handle_position;
1346 GdkGeometry geometry;
1347 GdkScreen *screen, *pointer_screen;
1348
1349 if (!priv->in_drag)
1350 return FALSE;
1351 handle_position = effective_handle_position (hb);
1352
1353 /* Calculate the attachment point on the float, if the float
1354 * were detached
1355 */
1356 new_x = 0;
1357 new_y = 0;
1358 screen = gtk_widget_get_screen (widget);
1359 gdk_device_get_position (event->device,
1360 &pointer_screen,
1361 &new_x, &new_y);
1362 if (pointer_screen != screen)
1363 {
1364 new_x = priv->orig_x;
1365 new_y = priv->orig_y;
1366 }
1367
1368 new_x += priv->float_allocation.x;
1369 new_y += priv->float_allocation.y;
1370
1371 snap_edge = priv->snap_edge;
1372 if (snap_edge == -1)
1373 snap_edge = (handle_position == GTK_POS_LEFT ||
1374 handle_position == GTK_POS_RIGHT) ?
1375 GTK_POS_TOP : GTK_POS_LEFT;
1376
1377 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
1378 switch (snap_edge)
1379 {
1380 case GTK_POS_LEFT:
1381 snap_edge = GTK_POS_RIGHT;
1382 break;
1383 case GTK_POS_RIGHT:
1384 snap_edge = GTK_POS_LEFT;
1385 break;
1386 default:
1387 break;
1388 }
1389
1390 /* First, check if the snapped edge is aligned
1391 */
1392 switch (snap_edge)
1393 {
1394 case GTK_POS_TOP:
1395 is_snapped = abs (priv->attach_allocation.y - new_y) < TOLERANCE;
1396 break;
1397 case GTK_POS_BOTTOM:
1398 is_snapped = abs (priv->attach_allocation.y + (gint)priv->attach_allocation.height -
1399 new_y - (gint)priv->float_allocation.height) < TOLERANCE;
1400 break;
1401 case GTK_POS_LEFT:
1402 is_snapped = abs (priv->attach_allocation.x - new_x) < TOLERANCE;
1403 break;
1404 case GTK_POS_RIGHT:
1405 is_snapped = abs (priv->attach_allocation.x + (gint)priv->attach_allocation.width -
1406 new_x - (gint)priv->float_allocation.width) < TOLERANCE;
1407 break;
1408 }
1409
1410 /* Next, check if coordinates in the other direction are sufficiently
1411 * aligned
1412 */
1413 if (is_snapped)
1414 {
1415 gint float_pos1 = 0; /* Initialize to suppress warnings */
1416 gint float_pos2 = 0;
1417 gint attach_pos1 = 0;
1418 gint attach_pos2 = 0;
1419
1420 switch (snap_edge)
1421 {
1422 case GTK_POS_TOP:
1423 case GTK_POS_BOTTOM:
1424 attach_pos1 = priv->attach_allocation.x;
1425 attach_pos2 = priv->attach_allocation.x + priv->attach_allocation.width;
1426 float_pos1 = new_x;
1427 float_pos2 = new_x + priv->float_allocation.width;
1428 break;
1429 case GTK_POS_LEFT:
1430 case GTK_POS_RIGHT:
1431 attach_pos1 = priv->attach_allocation.y;
1432 attach_pos2 = priv->attach_allocation.y + priv->attach_allocation.height;
1433 float_pos1 = new_y;
1434 float_pos2 = new_y + priv->float_allocation.height;
1435 break;
1436 }
1437
1438 is_snapped = ((attach_pos1 - TOLERANCE < float_pos1) &&
1439 (attach_pos2 + TOLERANCE > float_pos2)) ||
1440 ((float_pos1 - TOLERANCE < attach_pos1) &&
1441 (float_pos2 + TOLERANCE > attach_pos2));
1442 }
1443
1444 child = gtk_bin_get_child (GTK_BIN (hb));
1445
1446 if (is_snapped)
1447 {
1448 if (priv->child_detached)
1449 {
1450 priv->child_detached = FALSE;
1451 gdk_window_hide (priv->float_window);
1452 gdk_window_reparent (priv->bin_window, gtk_widget_get_window (widget), 0, 0);
1453 priv->float_window_mapped = FALSE;
1454 g_signal_emit (hb,
1455 handle_box_signals[SIGNAL_CHILD_ATTACHED],
1456 0,
1457 child);
1458
1459 gtk_widget_queue_resize (widget);
1460 }
1461 }
1462 else
1463 {
1464 gint width, height;
1465
1466 width = gdk_window_get_width (priv->float_window);
1467 height = gdk_window_get_height (priv->float_window);
1468
1469 switch (handle_position)
1470 {
1471 case GTK_POS_LEFT:
1472 new_y += ((gint)priv->float_allocation.height - height) / 2;
1473 break;
1474 case GTK_POS_RIGHT:
1475 new_x += (gint)priv->float_allocation.width - width;
1476 new_y += ((gint)priv->float_allocation.height - height) / 2;
1477 break;
1478 case GTK_POS_TOP:
1479 new_x += ((gint)priv->float_allocation.width - width) / 2;
1480 break;
1481 case GTK_POS_BOTTOM:
1482 new_x += ((gint)priv->float_allocation.width - width) / 2;
1483 new_y += (gint)priv->float_allocation.height - height;
1484 break;
1485 }
1486
1487 if (priv->child_detached)
1488 {
1489 gdk_window_move (priv->float_window, new_x, new_y);
1490 gdk_window_raise (priv->float_window);
1491 }
1492 else
1493 {
1494 guint border_width;
1495 GtkRequisition child_requisition;
1496
1497 priv->child_detached = TRUE;
1498
1499 if (child)
1500 {
1501 gtk_widget_get_preferred_size (child, &child_requisition, NULL);
1502 }
1503 else
1504 {
1505 child_requisition.width = 0;
1506 child_requisition.height = 0;
1507 }
1508
1509 border_width = gtk_container_get_border_width (GTK_CONTAINER (hb));
1510 width = child_requisition.width + 2 * border_width;
1511 height = child_requisition.height + 2 * border_width;
1512
1513 if (handle_position == GTK_POS_LEFT || handle_position == GTK_POS_RIGHT)
1514 width += DRAG_HANDLE_SIZE;
1515 else
1516 height += DRAG_HANDLE_SIZE;
1517
1518 gdk_window_move_resize (priv->float_window, new_x, new_y, width, height);
1519 gdk_window_reparent (priv->bin_window, priv->float_window, 0, 0);
1520 gdk_window_set_geometry_hints (priv->float_window, &geometry, GDK_HINT_POS);
1521 gdk_window_show (priv->float_window);
1522 priv->float_window_mapped = TRUE;
1523 #if 0
1524 /* this extra move is necessary if we use decorations, or our
1525 * window manager insists on decorations.
1526 */
1527 gdk_display_sync (gtk_widget_get_display (widget));
1528 gdk_window_move (priv->float_window, new_x, new_y);
1529 gdk_display_sync (gtk_widget_get_display (widget));
1530 #endif /* 0 */
1531 g_signal_emit (hb,
1532 handle_box_signals[SIGNAL_CHILD_DETACHED],
1533 0,
1534 child);
1535
1536 gtk_widget_queue_resize (widget);
1537 }
1538 }
1539
1540 return TRUE;
1541 }
1542
1543 static void
gtk_handle_box_add(GtkContainer * container,GtkWidget * widget)1544 gtk_handle_box_add (GtkContainer *container,
1545 GtkWidget *widget)
1546 {
1547 GtkHandleBoxPrivate *priv = GTK_HANDLE_BOX (container)->priv;
1548
1549 gtk_widget_set_parent_window (widget, priv->bin_window);
1550 GTK_CONTAINER_CLASS (gtk_handle_box_parent_class)->add (container, widget);
1551 }
1552
1553 static void
gtk_handle_box_remove(GtkContainer * container,GtkWidget * widget)1554 gtk_handle_box_remove (GtkContainer *container,
1555 GtkWidget *widget)
1556 {
1557 GTK_CONTAINER_CLASS (gtk_handle_box_parent_class)->remove (container, widget);
1558
1559 gtk_handle_box_reattach (GTK_HANDLE_BOX (container));
1560 }
1561
1562 static gint
gtk_handle_box_delete_event(GtkWidget * widget,GdkEventAny * event)1563 gtk_handle_box_delete_event (GtkWidget *widget,
1564 GdkEventAny *event)
1565 {
1566 GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
1567 GtkHandleBoxPrivate *priv = hb->priv;
1568
1569 if (event->window == priv->float_window)
1570 {
1571 gtk_handle_box_reattach (hb);
1572
1573 return TRUE;
1574 }
1575
1576 return FALSE;
1577 }
1578
1579 static void
gtk_handle_box_reattach(GtkHandleBox * hb)1580 gtk_handle_box_reattach (GtkHandleBox *hb)
1581 {
1582 GtkHandleBoxPrivate *priv = hb->priv;
1583 GtkWidget *child;
1584 GtkWidget *widget = GTK_WIDGET (hb);
1585
1586 if (priv->child_detached)
1587 {
1588 priv->child_detached = FALSE;
1589 if (gtk_widget_get_realized (widget))
1590 {
1591 gdk_window_hide (priv->float_window);
1592 gdk_window_reparent (priv->bin_window, gtk_widget_get_window (widget),
1593 0, 0);
1594
1595 child = gtk_bin_get_child (GTK_BIN (hb));
1596 if (child)
1597 g_signal_emit (hb,
1598 handle_box_signals[SIGNAL_CHILD_ATTACHED],
1599 0,
1600 child);
1601
1602 }
1603 priv->float_window_mapped = FALSE;
1604 }
1605 if (priv->in_drag)
1606 gtk_handle_box_end_drag (hb, GDK_CURRENT_TIME);
1607
1608 gtk_widget_queue_resize (GTK_WIDGET (hb));
1609 }
1610
1611 static void
gtk_handle_box_end_drag(GtkHandleBox * hb,guint32 time)1612 gtk_handle_box_end_drag (GtkHandleBox *hb,
1613 guint32 time)
1614 {
1615 GtkHandleBoxPrivate *priv = hb->priv;
1616 GtkWidget *invisible = gtk_handle_box_get_invisible ();
1617
1618 priv->in_drag = FALSE;
1619
1620 gtk_device_grab_remove (invisible, priv->grab_device);
1621 gdk_device_ungrab (priv->grab_device, time);
1622 g_signal_handlers_disconnect_by_func (invisible,
1623 G_CALLBACK (gtk_handle_box_grab_event),
1624 hb);
1625
1626 priv->grab_device = NULL;
1627 }
1628