Code

Set merge from trunk
[inkscape.git] / src / libgdl / gdl-dock-placeholder.c
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;
99     
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;
108     
109     /* connected signal handlers */
110     guint             host_detach_handler;
111     guint             host_dock_handler;
112         
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)
128     GObjectClass       *g_object_class;
129     GtkObjectClass     *gtk_object_class;
130     GtkContainerClass  *container_class;
131     GdlDockObjectClass *object_class;
132     
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;
140     
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));
148     
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));
155     
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));
166     
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));
174     
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 for 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 for dock when floating"),
200                                 -1, G_MAXINT, -1,
201                                 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
202                                 GDL_DOCK_PARAM_EXPORT));
203     
204         
205     gtk_object_class->destroy = gdl_dock_placeholder_destroy;
206     container_class->add = gdl_dock_placeholder_add;
207     
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;
215 static void 
216 gdl_dock_placeholder_instance_init (GdlDockPlaceholder *ph)
218     GTK_WIDGET_SET_FLAGS (ph, GTK_NO_WINDOW);
219     GTK_WIDGET_UNSET_FLAGS (ph, GTK_CAN_FOCUS);
220     
221     ph->_priv = g_new0 (GdlDockPlaceholderPrivate, 1);
224 static void 
225 gdl_dock_placeholder_set_property (GObject      *g_object,
226                                    guint         prop_id,
227                                    const GValue *value,
228                                    GParamSpec   *pspec)
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     }
268 static void 
269 gdl_dock_placeholder_get_property (GObject    *g_object,
270                                    guint       prop_id,
271                                    GValue     *value,
272                                    GParamSpec *pspec)
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     }
316 static void
317 gdl_dock_placeholder_destroy (GtkObject *object)
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));
331 static void 
332 gdl_dock_placeholder_add (GtkContainer *container,
333                           GtkWidget    *widget)
335     GdlDockPlaceholder *ph;
336     GdlDockPlacement    pos = GDL_DOCK_TOP;   /* default position */
337     
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;
344     
345     gdl_dock_object_dock (GDL_DOCK_OBJECT (ph), GDL_DOCK_OBJECT (widget),
346                           pos, NULL);
349 static void
350 gdl_dock_placeholder_detach (GdlDockObject *object,
351                              gboolean       recursive)
353     GdlDockPlaceholder *ph = GDL_DOCK_PLACEHOLDER (object);
355     /* disconnect handlers */
356     disconnect_host (ph);
357     
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);
365 static void 
366 gdl_dock_placeholder_reduce (GdlDockObject *object)
368     /* placeholders are not reduced */
369     return;
372 static void
373 find_biggest_dock_item (GtkContainer *container, GtkWidget **biggest_child,
374                         gint *biggest_child_area)
376     GList *children, *child;
377     
378     children = gtk_container_get_children (GTK_CONTAINER (container));
379     child = children;
380     while (child) {
381         gint area;
382         GtkWidget *child_widget;
383         
384         child_widget = GTK_WIDGET (child->data);
385         
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;
393         
394         if (area > *biggest_child_area) {
395             *biggest_child_area = area;
396             *biggest_child = child_widget;
397         }
398         child = g_list_next (child);
399     }
402 static void
403 attempt_to_dock_on_host (GdlDockPlaceholder *ph, GdlDockObject *host,
404                          GdlDockObject *requestor, GdlDockPlacement placement,
405                          gpointer other_data)
407     GdlDockObject *parent;
408     gint host_width = GTK_WIDGET (host)->allocation.width;
409     gint host_height = GTK_WIDGET (host)->allocation.height;
410     
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;
422         
423         find_biggest_dock_item (GTK_CONTAINER (host), &biggest_child,
424                                 &biggest_child_area);
425         
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     }
437     
438     parent = gdl_dock_object_get_parent_object (requestor);
439     
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;
451                 
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;
466                 
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     }
478 static void 
479 gdl_dock_placeholder_dock (GdlDockObject    *object,
480                            GdlDockObject    *requestor,
481                            GdlDockPlacement  position,
482                            GValue           *other_data)
484     GdlDockPlaceholder *ph = GDL_DOCK_PLACEHOLDER (object);
485     
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;
492         
493         if (!gdl_dock_object_is_bound (GDL_DOCK_OBJECT (ph))) {
494             g_warning ("%s",_("Attempt to dock a dock object to an unbound placeholder"));
495             return;
496         }
497         
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     }
505 #ifdef PLACEHOLDER_DEBUG
506 static void
507 print_placement_stack (GdlDockPlaceholder *ph)
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);
524     
525     g_string_free (message, TRUE);
526     g_type_class_unref (enum_class);
528 #endif
530 static void 
531 gdl_dock_placeholder_present (GdlDockObject *object,
532                               GdlDockObject *child)
534     /* do nothing */
535     return;
538 /* ----- Public interface ----- */ 
539                                                                    
540 GtkWidget * 
541 gdl_dock_placeholder_new (gchar            *name,
542                           GdlDockObject    *object,
543                           GdlDockPlacement  position,
544                           gboolean          sticky)
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     }
567     
568     return GTK_WIDGET (ph);
571 static void 
572 gdl_dock_placeholder_weak_notify (gpointer data,
573                                   GObject *old_object)
575     GdlDockPlaceholder *ph;
576     
577     g_return_if_fail (data != NULL && GDL_IS_DOCK_PLACEHOLDER (data));
579     ph = GDL_DOCK_PLACEHOLDER (data);
580     
581 #ifdef PLACEHOLDER_DEBUG
582     g_message ("The placeholder just lost its host, ph = %p", ph);
583 #endif
584     
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;
588     
589     /* We didn't get a detach signal from the host. Detach from the 
590     supposedly dead host (consequently attaching to the controller) */
591     
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
601 static void
602 detach_cb (GdlDockObject *object,
603            gboolean       recursive,
604            gpointer       user_data)
606     GdlDockPlaceholder *ph;
607     GdlDockObject      *new_host, *obj;
609     g_return_if_fail (user_data != NULL && GDL_IS_DOCK_PLACEHOLDER (user_data));
610     
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     }
622     
623     /* skip sticky objects */
624     if (ph->_priv->sticky)
625         return;
626     
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;
633     
634     while (new_host) {
635         GdlDockPlacement pos = GDL_DOCK_NONE;
636         
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;
650         
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));
665         
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
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)
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;
700         
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);
712                 
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);
720                 
721                 break;
722             }
723         }
724         g_list_free (children);
725     }
728 static void 
729 dock_cb (GdlDockObject    *object,
730          GdlDockObject    *requestor,
731          GdlDockPlacement  position,
732          GValue           *other_data,
733          gpointer          user_data)
735     GdlDockPlacement    pos = GDL_DOCK_NONE;
736     GdlDockPlaceholder *ph;
737     
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);
741     
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
758 static void
759 disconnect_host (GdlDockPlaceholder *ph)
761     if (!ph->_priv->host)
762         return;
763     
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;
775     
776 #ifdef PLACEHOLDER_DEBUG
777     g_message ("Host just disconnected!, ph = %p", ph);
778 #endif
781 static void
782 connect_host (GdlDockPlaceholder *ph,
783               GdlDockObject      *new_host)
785     if (ph->_priv->host)
786         disconnect_host (ph);
787     
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);
797     
798     ph->_priv->host_dock_handler =
799         g_signal_connect (ph->_priv->host,
800                           "dock",
801                           (GCallback) dock_cb,
802                           (gpointer) ph);
803     
804 #ifdef PLACEHOLDER_DEBUG
805     g_message ("Host just connected!, ph = %p", ph);
806 #endif
809 void
810 gdl_dock_placeholder_attach (GdlDockPlaceholder *ph,
811                              GdlDockObject      *object)
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);
816     
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);
822         
823     gdl_dock_object_freeze (GDL_DOCK_OBJECT (ph));
824     
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);
830     
831     GDL_DOCK_OBJECT_SET_FLAGS (ph, GDL_DOCK_ATTACHED);
832     
833     gdl_dock_object_thaw (GDL_DOCK_OBJECT (ph));