df068d03f6f1dffd9b14eedf72fd625dfdbd587b
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 *
3 * gdl-dock-placeholder.c - Placeholders for docking items
4 *
5 * This file is part of the GNOME Devtools Libraries.
6 *
7 * Copyright (C) 2002 Gustavo Giráldez <gustavo.giraldez@gmx.net>
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 */
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
28 #include "gdl-i18n.h"
30 #include "gdl-tools.h"
31 #include "gdl-dock-placeholder.h"
32 #include "gdl-dock-item.h"
33 #include "gdl-dock-master.h"
34 #include "libgdltypebuiltins.h"
37 #undef PLACEHOLDER_DEBUG
39 /* ----- Private prototypes ----- */
41 static void gdl_dock_placeholder_class_init (GdlDockPlaceholderClass *klass);
42 static void gdl_dock_placeholder_instance_init (GdlDockPlaceholder *ph);
44 static void gdl_dock_placeholder_set_property (GObject *g_object,
45 guint prop_id,
46 const GValue *value,
47 GParamSpec *pspec);
48 static void gdl_dock_placeholder_get_property (GObject *g_object,
49 guint prop_id,
50 GValue *value,
51 GParamSpec *pspec);
53 static void gdl_dock_placeholder_destroy (GtkObject *object);
55 static void gdl_dock_placeholder_add (GtkContainer *container,
56 GtkWidget *widget);
58 static void gdl_dock_placeholder_detach (GdlDockObject *object,
59 gboolean recursive);
60 static void gdl_dock_placeholder_reduce (GdlDockObject *object);
61 static void gdl_dock_placeholder_dock (GdlDockObject *object,
62 GdlDockObject *requestor,
63 GdlDockPlacement position,
64 GValue *other_data);
66 static void gdl_dock_placeholder_weak_notify (gpointer data,
67 GObject *old_object);
69 static void disconnect_host (GdlDockPlaceholder *ph);
70 static void connect_host (GdlDockPlaceholder *ph,
71 GdlDockObject *new_host);
72 static void do_excursion (GdlDockPlaceholder *ph);
74 static void gdl_dock_placeholder_present (GdlDockObject *object,
75 GdlDockObject *child);
77 static void detach_cb (GdlDockObject *object,
78 gboolean recursive,
79 gpointer user_data);
81 /* ----- Private variables and data structures ----- */
83 enum {
84 PROP_0,
85 PROP_STICKY,
86 PROP_HOST,
87 PROP_NEXT_PLACEMENT,
88 PROP_WIDTH,
89 PROP_HEIGHT,
90 PROP_FLOATING,
91 PROP_FLOAT_X,
92 PROP_FLOAT_Y
93 };
95 struct _GdlDockPlaceholderPrivate {
96 /* current object this placeholder is pinned to */
97 GdlDockObject *host;
98 gboolean sticky;
100 /* when the placeholder is moved up the hierarchy, this stack
101 keeps track of the necessary dock positions needed to get the
102 placeholder to the original position */
103 GSList *placement_stack;
105 /* Width and height of the attachments */
106 gint width;
107 gint height;
109 /* connected signal handlers */
110 guint host_detach_handler;
111 guint host_dock_handler;
113 /* Window Coordinates if Dock was floating */
114 gboolean floating;
115 gint floatx;
116 gint floaty;
117 };
120 /* ----- Private interface ----- */
122 GDL_CLASS_BOILERPLATE (GdlDockPlaceholder, gdl_dock_placeholder,
123 GdlDockObject, GDL_TYPE_DOCK_OBJECT);
125 static void
126 gdl_dock_placeholder_class_init (GdlDockPlaceholderClass *klass)
127 {
128 GObjectClass *g_object_class;
129 GtkObjectClass *gtk_object_class;
130 GtkContainerClass *container_class;
131 GdlDockObjectClass *object_class;
133 g_object_class = G_OBJECT_CLASS (klass);
134 gtk_object_class = GTK_OBJECT_CLASS (klass);
135 container_class = GTK_CONTAINER_CLASS (klass);
136 object_class = GDL_DOCK_OBJECT_CLASS (klass);
138 g_object_class->get_property = gdl_dock_placeholder_get_property;
139 g_object_class->set_property = gdl_dock_placeholder_set_property;
141 g_object_class_install_property (
142 g_object_class, PROP_STICKY,
143 g_param_spec_boolean ("sticky", _("Sticky"),
144 _("Whether the placeholder will stick to its host or "
145 "move up the hierarchy when the host is redocked"),
146 FALSE,
147 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
149 g_object_class_install_property (
150 g_object_class, PROP_HOST,
151 g_param_spec_object ("host", _("Host"),
152 _("The dock object this placeholder is attached to"),
153 GDL_TYPE_DOCK_OBJECT,
154 G_PARAM_READWRITE));
156 /* this will return the top of the placement stack */
157 g_object_class_install_property (
158 g_object_class, PROP_NEXT_PLACEMENT,
159 g_param_spec_enum ("next-placement", _("Next placement"),
160 _("The position an item will be docked to our host if a "
161 "request is made to dock to us"),
162 GDL_TYPE_DOCK_PLACEMENT,
163 GDL_DOCK_CENTER,
164 G_PARAM_READWRITE |
165 GDL_DOCK_PARAM_EXPORT | GDL_DOCK_PARAM_AFTER));
167 g_object_class_install_property (
168 g_object_class, PROP_WIDTH,
169 g_param_spec_int ("width", _("Width"),
170 _("Width for the widget when it's attached to the placeholder"),
171 -1, G_MAXINT, -1,
172 G_PARAM_READWRITE | G_PARAM_CONSTRUCT |
173 GDL_DOCK_PARAM_EXPORT));
175 g_object_class_install_property (
176 g_object_class, PROP_HEIGHT,
177 g_param_spec_int ("height", _("Height"),
178 _("Height for the widget when it's attached to the placeholder"),
179 -1, G_MAXINT, -1,
180 G_PARAM_READWRITE | G_PARAM_CONSTRUCT |
181 GDL_DOCK_PARAM_EXPORT));
182 g_object_class_install_property (
183 g_object_class, PROP_FLOATING,
184 g_param_spec_boolean ("floating", _("Floating Toplevel"),
185 _("Whether the placeholder is standing in for a "
186 "floating toplevel dock"),
187 FALSE,
188 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
189 g_object_class_install_property (
190 g_object_class, PROP_FLOAT_X,
191 g_param_spec_int ("floatx", _("X-Coordinate"),
192 _("X-Coordinate fow dock when floating"),
193 -1, G_MAXINT, -1,
194 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
195 GDL_DOCK_PARAM_EXPORT));
196 g_object_class_install_property (
197 g_object_class, PROP_FLOAT_Y,
198 g_param_spec_int ("floaty", _("Y-Coordinate"),
199 _("Y-Coordinate fow dock when floating"),
200 -1, G_MAXINT, -1,
201 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
202 GDL_DOCK_PARAM_EXPORT));
205 gtk_object_class->destroy = gdl_dock_placeholder_destroy;
206 container_class->add = gdl_dock_placeholder_add;
208 object_class->is_compound = FALSE;
209 object_class->detach = gdl_dock_placeholder_detach;
210 object_class->reduce = gdl_dock_placeholder_reduce;
211 object_class->dock = gdl_dock_placeholder_dock;
212 object_class->present = gdl_dock_placeholder_present;
213 }
215 static void
216 gdl_dock_placeholder_instance_init (GdlDockPlaceholder *ph)
217 {
218 GTK_WIDGET_SET_FLAGS (ph, GTK_NO_WINDOW);
219 GTK_WIDGET_UNSET_FLAGS (ph, GTK_CAN_FOCUS);
221 ph->_priv = g_new0 (GdlDockPlaceholderPrivate, 1);
222 }
224 static void
225 gdl_dock_placeholder_set_property (GObject *g_object,
226 guint prop_id,
227 const GValue *value,
228 GParamSpec *pspec)
229 {
230 GdlDockPlaceholder *ph = GDL_DOCK_PLACEHOLDER (g_object);
232 switch (prop_id) {
233 case PROP_STICKY:
234 if (ph->_priv)
235 ph->_priv->sticky = g_value_get_boolean (value);
236 break;
237 case PROP_HOST:
238 gdl_dock_placeholder_attach (ph, g_value_get_object (value));
239 break;
240 case PROP_NEXT_PLACEMENT:
241 if (ph->_priv) {
242 ph->_priv->placement_stack =
243 g_slist_prepend (ph->_priv->placement_stack,
244 GINT_TO_POINTER (g_value_get_enum (value)));
245 }
246 break;
247 case PROP_WIDTH:
248 ph->_priv->width = g_value_get_int (value);
249 break;
250 case PROP_HEIGHT:
251 ph->_priv->height = g_value_get_int (value);
252 break;
253 case PROP_FLOATING:
254 ph->_priv->floating = g_value_get_boolean (value);
255 break;
256 case PROP_FLOAT_X:
257 ph->_priv->floatx = g_value_get_int (value);
258 break;
259 case PROP_FLOAT_Y:
260 ph->_priv->floaty = g_value_get_int (value);
261 break;
262 default:
263 G_OBJECT_WARN_INVALID_PROPERTY_ID (g_object, prop_id, pspec);
264 break;
265 }
266 }
268 static void
269 gdl_dock_placeholder_get_property (GObject *g_object,
270 guint prop_id,
271 GValue *value,
272 GParamSpec *pspec)
273 {
274 GdlDockPlaceholder *ph = GDL_DOCK_PLACEHOLDER (g_object);
276 switch (prop_id) {
277 case PROP_STICKY:
278 if (ph->_priv)
279 g_value_set_boolean (value, ph->_priv->sticky);
280 else
281 g_value_set_boolean (value, FALSE);
282 break;
283 case PROP_HOST:
284 if (ph->_priv)
285 g_value_set_object (value, ph->_priv->host);
286 else
287 g_value_set_object (value, NULL);
288 break;
289 case PROP_NEXT_PLACEMENT:
290 if (ph->_priv && ph->_priv->placement_stack)
291 g_value_set_enum (value, (GdlDockPlacement) ph->_priv->placement_stack->data);
292 else
293 g_value_set_enum (value, GDL_DOCK_CENTER);
294 break;
295 case PROP_WIDTH:
296 g_value_set_int (value, ph->_priv->width);
297 break;
298 case PROP_HEIGHT:
299 g_value_set_int (value, ph->_priv->height);
300 break;
301 case PROP_FLOATING:
302 g_value_set_boolean (value, ph->_priv->floating);
303 break;
304 case PROP_FLOAT_X:
305 g_value_set_int (value, ph->_priv->floatx);
306 break;
307 case PROP_FLOAT_Y:
308 g_value_set_int (value, ph->_priv->floaty);
309 break;
310 default:
311 G_OBJECT_WARN_INVALID_PROPERTY_ID (g_object, prop_id, pspec);
312 break;
313 }
314 }
316 static void
317 gdl_dock_placeholder_destroy (GtkObject *object)
318 {
319 GdlDockPlaceholder *ph = GDL_DOCK_PLACEHOLDER (object);
321 if (ph->_priv) {
322 if (ph->_priv->host)
323 gdl_dock_placeholder_detach (GDL_DOCK_OBJECT (object), FALSE);
324 g_free (ph->_priv);
325 ph->_priv = NULL;
326 }
328 GDL_CALL_PARENT (GTK_OBJECT_CLASS, destroy, (object));
329 }
331 static void
332 gdl_dock_placeholder_add (GtkContainer *container,
333 GtkWidget *widget)
334 {
335 GdlDockPlaceholder *ph;
336 GdlDockPlacement pos = GDL_DOCK_CENTER; /* default position */
338 g_return_if_fail (GDL_IS_DOCK_PLACEHOLDER (container));
339 g_return_if_fail (GDL_IS_DOCK_ITEM (widget));
341 ph = GDL_DOCK_PLACEHOLDER (container);
342 if (ph->_priv->placement_stack)
343 pos = (GdlDockPlacement) ph->_priv->placement_stack->data;
345 gdl_dock_object_dock (GDL_DOCK_OBJECT (ph), GDL_DOCK_OBJECT (widget),
346 pos, NULL);
347 }
349 static void
350 gdl_dock_placeholder_detach (GdlDockObject *object,
351 gboolean recursive)
352 {
353 GdlDockPlaceholder *ph = GDL_DOCK_PLACEHOLDER (object);
355 /* disconnect handlers */
356 disconnect_host (ph);
358 /* free the placement stack */
359 g_slist_free (ph->_priv->placement_stack);
360 ph->_priv->placement_stack = NULL;
362 GDL_DOCK_OBJECT_UNSET_FLAGS (object, GDL_DOCK_ATTACHED);
363 }
365 static void
366 gdl_dock_placeholder_reduce (GdlDockObject *object)
367 {
368 /* placeholders are not reduced */
369 return;
370 }
372 static void
373 find_biggest_dock_item (GtkContainer *container, GtkWidget **biggest_child,
374 gint *biggest_child_area)
375 {
376 GList *children, *child;
378 children = gtk_container_get_children (GTK_CONTAINER (container));
379 child = children;
380 while (child) {
381 gint area;
382 GtkWidget *child_widget;
384 child_widget = GTK_WIDGET (child->data);
386 if (gdl_dock_object_is_compound (GDL_DOCK_OBJECT(child_widget))) {
387 find_biggest_dock_item (GTK_CONTAINER (child_widget),
388 biggest_child, biggest_child_area);
389 child = g_list_next (child);
390 continue;
391 }
392 area = child_widget->allocation.width * child_widget->allocation.height;
394 if (area > *biggest_child_area) {
395 *biggest_child_area = area;
396 *biggest_child = child_widget;
397 }
398 child = g_list_next (child);
399 }
400 }
402 static void
403 attempt_to_dock_on_host (GdlDockPlaceholder *ph, GdlDockObject *host,
404 GdlDockObject *requestor, GdlDockPlacement placement,
405 gpointer other_data)
406 {
407 GdlDockObject *parent;
408 gint host_width = GTK_WIDGET (host)->allocation.width;
409 gint host_height = GTK_WIDGET (host)->allocation.height;
411 if (placement != GDL_DOCK_CENTER || !GDL_IS_DOCK_PANED (host)) {
412 /* we simply act as a proxy for our host */
413 gdl_dock_object_dock (host, requestor,
414 placement, other_data);
415 } else {
416 /* If the requested pos is center, we have to make sure that it
417 * does not colapses existing paned items. Find the larget item
418 * which is not a paned item to dock to.
419 */
420 GtkWidget *biggest_child = NULL;
421 gint biggest_child_area = 0;
423 find_biggest_dock_item (GTK_CONTAINER (host), &biggest_child,
424 &biggest_child_area);
426 if (biggest_child) {
427 /* we simply act as a proxy for our host */
428 gdl_dock_object_dock (GDL_DOCK_OBJECT (biggest_child), requestor,
429 placement, other_data);
430 } else {
431 g_warning ("No suitable child found! Should not be here!");
432 /* we simply act as a proxy for our host */
433 gdl_dock_object_dock (GDL_DOCK_OBJECT (host), requestor,
434 placement, other_data);
435 }
436 }
438 parent = gdl_dock_object_get_parent_object (requestor);
440 /* Restore dock item's dimention */
441 switch (placement) {
442 case GDL_DOCK_LEFT:
443 if (ph->_priv->width > 0) {
444 g_object_set (G_OBJECT (parent), "position",
445 ph->_priv->width, NULL);
446 }
447 break;
448 case GDL_DOCK_RIGHT:
449 if (ph->_priv->width > 0) {
450 gint complementary_width = host_width - ph->_priv->width;
452 if (complementary_width > 0)
453 g_object_set (G_OBJECT (parent), "position",
454 complementary_width, NULL);
455 }
456 break;
457 case GDL_DOCK_TOP:
458 if (ph->_priv->height > 0) {
459 g_object_set (G_OBJECT (parent), "position",
460 ph->_priv->height, NULL);
461 }
462 break;
463 case GDL_DOCK_BOTTOM:
464 if (ph->_priv->height > 0) {
465 gint complementary_height = host_height - ph->_priv->height;
467 if (complementary_height > 0)
468 g_object_set (G_OBJECT (parent), "position",
469 complementary_height, NULL);
470 }
471 break;
472 default:
473 /* nothing */
474 break;
475 }
476 }
478 static void
479 gdl_dock_placeholder_dock (GdlDockObject *object,
480 GdlDockObject *requestor,
481 GdlDockPlacement position,
482 GValue *other_data)
483 {
484 GdlDockPlaceholder *ph = GDL_DOCK_PLACEHOLDER (object);
486 if (ph->_priv->host) {
487 attempt_to_dock_on_host (ph, ph->_priv->host, requestor,
488 position, other_data);
489 }
490 else {
491 GdlDockObject *toplevel;
493 if (!gdl_dock_object_is_bound (GDL_DOCK_OBJECT (ph))) {
494 g_warning (_("Attempt to dock a dock object to an unbound placeholder"));
495 return;
496 }
498 /* dock the item as a floating of the controller */
499 toplevel = gdl_dock_master_get_controller (GDL_DOCK_OBJECT_GET_MASTER (ph));
500 gdl_dock_object_dock (toplevel, requestor,
501 GDL_DOCK_FLOATING, NULL);
502 }
503 }
505 #ifdef PLACEHOLDER_DEBUG
506 static void
507 print_placement_stack (GdlDockPlaceholder *ph)
508 {
509 GSList *s = ph->_priv->placement_stack;
510 GEnumClass *enum_class = G_ENUM_CLASS (g_type_class_ref (GDL_TYPE_DOCK_PLACEMENT));
511 GEnumValue *enum_value;
512 gchar *name;
513 GString *message;
515 message = g_string_new (NULL);
516 g_string_printf (message, "[%p] host: %p (%s), stack: ",
517 ph, ph->_priv->host, G_OBJECT_TYPE_NAME (ph->_priv->host));
518 for (; s; s = s->next) {
519 enum_value = g_enum_get_value (enum_class, (GdlDockPlacement) s->data);
520 name = enum_value ? enum_value->value_name : NULL;
521 g_string_append_printf (message, "%s, ", name);
522 }
523 g_message ("%s", message->str);
525 g_string_free (message, TRUE);
526 g_type_class_unref (enum_class);
527 }
528 #endif
530 static void
531 gdl_dock_placeholder_present (GdlDockObject *object,
532 GdlDockObject *child)
533 {
534 /* do nothing */
535 return;
536 }
538 /* ----- Public interface ----- */
540 GtkWidget *
541 gdl_dock_placeholder_new (gchar *name,
542 GdlDockObject *object,
543 GdlDockPlacement position,
544 gboolean sticky)
545 {
546 GdlDockPlaceholder *ph;
548 ph = GDL_DOCK_PLACEHOLDER (g_object_new (GDL_TYPE_DOCK_PLACEHOLDER,
549 "name", name,
550 "sticky", sticky,
551 NULL));
552 GDL_DOCK_OBJECT_UNSET_FLAGS (ph, GDL_DOCK_AUTOMATIC);
554 if (object) {
555 gdl_dock_placeholder_attach (ph, object);
556 if (position == GDL_DOCK_NONE)
557 position = GDL_DOCK_CENTER;
558 g_object_set (G_OBJECT (ph), "next-placement", position, NULL);
559 if (GDL_IS_DOCK (object)) {
560 /* the top placement will be consumed by the toplevel
561 dock, so add a dummy placement */
562 g_object_set (G_OBJECT (ph), "next-placement", GDL_DOCK_CENTER, NULL);
563 }
564 /* try a recursion */
565 do_excursion (ph);
566 }
568 return GTK_WIDGET (ph);
569 }
571 static void
572 gdl_dock_placeholder_weak_notify (gpointer data,
573 GObject *old_object)
574 {
575 GdlDockPlaceholder *ph;
577 g_return_if_fail (data != NULL && GDL_IS_DOCK_PLACEHOLDER (data));
579 ph = GDL_DOCK_PLACEHOLDER (data);
581 #ifdef PLACEHOLDER_DEBUG
582 g_message ("The placeholder just lost its host, ph = %p", ph);
583 #endif
585 /* we shouldn't get here, so perform an emergency detach. instead
586 we should have gotten a detach signal from our host */
587 ph->_priv->host = NULL;
589 /* We didn't get a detach signal from the host. Detach from the
590 supposedly dead host (consequently attaching to the controller) */
592 detach_cb (NULL, TRUE, data);
593 #if 0
594 /* free the placement stack */
595 g_slist_free (ph->_priv->placement_stack);
596 ph->_priv->placement_stack = NULL;
597 GDL_DOCK_OBJECT_UNSET_FLAGS (ph, GDL_DOCK_ATTACHED);
598 #endif
599 }
601 static void
602 detach_cb (GdlDockObject *object,
603 gboolean recursive,
604 gpointer user_data)
605 {
606 GdlDockPlaceholder *ph;
607 GdlDockObject *new_host, *obj;
609 g_return_if_fail (user_data != NULL && GDL_IS_DOCK_PLACEHOLDER (user_data));
611 /* we go up in the hierarchy and we store the hinted placement in
612 * the placement stack so we can rebuild the docking layout later
613 * when we get the host's dock signal. */
615 ph = GDL_DOCK_PLACEHOLDER (user_data);
616 obj = ph->_priv->host;
617 if (obj != object) {
618 g_warning (_("Got a detach signal from an object (%p) who is not "
619 "our host %p"), object, ph->_priv->host);
620 return;
621 }
623 /* skip sticky objects */
624 if (ph->_priv->sticky)
625 return;
627 if (obj)
628 /* go up in the hierarchy */
629 new_host = gdl_dock_object_get_parent_object (obj);
630 else
631 /* Detaching from the dead host */
632 new_host = NULL;
634 while (new_host) {
635 GdlDockPlacement pos = GDL_DOCK_NONE;
637 /* get placement hint from the new host */
638 if (gdl_dock_object_child_placement (new_host, obj, &pos)) {
639 ph->_priv->placement_stack = g_slist_prepend (
640 ph->_priv->placement_stack, (gpointer) pos);
641 }
642 else {
643 g_warning (_("Something weird happened while getting the child "
644 "placement for %p from parent %p"), obj, new_host);
645 }
647 if (!GDL_DOCK_OBJECT_IN_DETACH (new_host))
648 /* we found a "stable" dock object */
649 break;
651 obj = new_host;
652 new_host = gdl_dock_object_get_parent_object (obj);
653 }
655 /* disconnect host */
656 disconnect_host (ph);
658 if (!new_host) {
659 #ifdef PLACEHOLDER_DEBUG
660 g_message ("Detaching from the toplevel. Assignaing to controller");
661 #endif
662 /* the toplevel was detached: we attach ourselves to the
663 controller with an initial placement of floating */
664 new_host = gdl_dock_master_get_controller (GDL_DOCK_OBJECT_GET_MASTER (ph));
666 /*
667 ph->_priv->placement_stack = g_slist_prepend (
668 ph->_priv->placement_stack, (gpointer) GDL_DOCK_FLOATING);
669 */
670 }
671 if (new_host)
672 connect_host (ph, new_host);
674 #ifdef PLACEHOLDER_DEBUG
675 print_placement_stack (ph);
676 #endif
677 }
679 /**
680 * do_excursion:
681 * @ph: placeholder object
682 *
683 * Tries to shrink the placement stack by examining the host's
684 * children and see if any of them matches the placement which is at
685 * the top of the stack. If this is the case, it tries again with the
686 * new host.
687 **/
688 static void
689 do_excursion (GdlDockPlaceholder *ph)
690 {
691 if (ph->_priv->host &&
692 !ph->_priv->sticky &&
693 ph->_priv->placement_stack &&
694 gdl_dock_object_is_compound (ph->_priv->host)) {
696 GdlDockPlacement pos, stack_pos =
697 (GdlDockPlacement) ph->_priv->placement_stack->data;
698 GList *children, *l;
699 GdlDockObject *host = ph->_priv->host;
701 children = gtk_container_get_children (GTK_CONTAINER (host));
702 for (l = children; l; l = l->next) {
703 pos = stack_pos;
704 gdl_dock_object_child_placement (GDL_DOCK_OBJECT (host),
705 GDL_DOCK_OBJECT (l->data),
706 &pos);
707 if (pos == stack_pos) {
708 /* remove the stack position */
709 ph->_priv->placement_stack =
710 g_slist_remove_link (ph->_priv->placement_stack,
711 ph->_priv->placement_stack);
713 /* connect to the new host */
714 disconnect_host (ph);
715 connect_host (ph, GDL_DOCK_OBJECT (l->data));
717 /* recurse... */
718 if (!GDL_DOCK_OBJECT_IN_REFLOW (l->data))
719 do_excursion (ph);
721 break;
722 }
723 }
724 g_list_free (children);
725 }
726 }
728 static void
729 dock_cb (GdlDockObject *object,
730 GdlDockObject *requestor,
731 GdlDockPlacement position,
732 GValue *other_data,
733 gpointer user_data)
734 {
735 GdlDockPlacement pos = GDL_DOCK_NONE;
736 GdlDockPlaceholder *ph;
738 g_return_if_fail (user_data != NULL && GDL_IS_DOCK_PLACEHOLDER (user_data));
739 ph = GDL_DOCK_PLACEHOLDER (user_data);
740 g_return_if_fail (ph->_priv->host == object);
742 /* see if the given position is compatible for the stack's top
743 element */
744 if (!ph->_priv->sticky && ph->_priv->placement_stack) {
745 pos = (GdlDockPlacement) ph->_priv->placement_stack->data;
746 if (gdl_dock_object_child_placement (object, requestor, &pos)) {
747 if (pos == (GdlDockPlacement) ph->_priv->placement_stack->data) {
748 /* the position is compatible: excurse down */
749 do_excursion (ph);
750 }
751 }
752 }
753 #ifdef PLACEHOLDER_DEBUG
754 print_placement_stack (ph);
755 #endif
756 }
758 static void
759 disconnect_host (GdlDockPlaceholder *ph)
760 {
761 if (!ph->_priv->host)
762 return;
764 if (ph->_priv->host_detach_handler)
765 g_signal_handler_disconnect (ph->_priv->host, ph->_priv->host_detach_handler);
766 if (ph->_priv->host_dock_handler)
767 g_signal_handler_disconnect (ph->_priv->host, ph->_priv->host_dock_handler);
768 ph->_priv->host_detach_handler = 0;
769 ph->_priv->host_dock_handler = 0;
771 /* remove weak ref to object */
772 g_object_weak_unref (G_OBJECT (ph->_priv->host),
773 gdl_dock_placeholder_weak_notify, ph);
774 ph->_priv->host = NULL;
776 #ifdef PLACEHOLDER_DEBUG
777 g_message ("Host just disconnected!, ph = %p", ph);
778 #endif
779 }
781 static void
782 connect_host (GdlDockPlaceholder *ph,
783 GdlDockObject *new_host)
784 {
785 if (ph->_priv->host)
786 disconnect_host (ph);
788 ph->_priv->host = new_host;
789 g_object_weak_ref (G_OBJECT (ph->_priv->host),
790 gdl_dock_placeholder_weak_notify, ph);
792 ph->_priv->host_detach_handler =
793 g_signal_connect (ph->_priv->host,
794 "detach",
795 (GCallback) detach_cb,
796 (gpointer) ph);
798 ph->_priv->host_dock_handler =
799 g_signal_connect (ph->_priv->host,
800 "dock",
801 (GCallback) dock_cb,
802 (gpointer) ph);
804 #ifdef PLACEHOLDER_DEBUG
805 g_message ("Host just connected!, ph = %p", ph);
806 #endif
807 }
809 void
810 gdl_dock_placeholder_attach (GdlDockPlaceholder *ph,
811 GdlDockObject *object)
812 {
813 g_return_if_fail (ph != NULL && GDL_IS_DOCK_PLACEHOLDER (ph));
814 g_return_if_fail (ph->_priv != NULL);
815 g_return_if_fail (object != NULL);
817 /* object binding */
818 if (!gdl_dock_object_is_bound (GDL_DOCK_OBJECT (ph)))
819 gdl_dock_object_bind (GDL_DOCK_OBJECT (ph), object->master);
821 g_return_if_fail (GDL_DOCK_OBJECT (ph)->master == object->master);
823 gdl_dock_object_freeze (GDL_DOCK_OBJECT (ph));
825 /* detach from previous host first */
826 if (ph->_priv->host)
827 gdl_dock_object_detach (GDL_DOCK_OBJECT (ph), FALSE);
829 connect_host (ph, object);
831 GDL_DOCK_OBJECT_SET_FLAGS (ph, GDL_DOCK_ATTACHED);
833 gdl_dock_object_thaw (GDL_DOCK_OBJECT (ph));
834 }