gdl-dock-placeholder.c 29.3 KB
Newer Older
1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 3 4 5 6 7 8
 *
 * gdl-dock-placeholder.c - Placeholders for docking items
 *
 * This file is part of the GNOME Devtools Libraries.
 *
 * Copyright (C) 2002 Gustavo Girldez <gustavo.giraldez@gmx.net>
 *
9 10 11 12
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.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 17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
18
 *
19 20 21
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
22 23
 */

24 25 26 27
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

28

29
#include <glib/gi18n-lib.h>
30

31 32
#undef GDL_DISABLE_DEPRECATED

33 34
#include "gdl-dock-placeholder.h"
#include "gdl-dock-item.h"
35
#include "gdl-dock-paned.h"
36
#include "gdl-dock-master.h"
37 38
#include "libgdltypebuiltins.h"

39 40 41 42 43 44 45 46 47 48
/**
 * SECTION:gdl-dock-placeholder
 * @title: GdlDockPlaceHolder
 * @short_description: A widget marking a docking place.
 * @see_also:
 * @stability: Unstable
 *
 * A dock placeholder is a widget allowing to keep track of a docking place.
 * Unfortunately, all the details of the initial goal have been forgotten and
 * the code has still some issues.
49
 *
50 51 52
 * In GDL 3.6, this part has been deprecated. Placeholder widgets are not
 * used anymore. Instead, closed widgets are hidden but kept in the widget
 * hierarchy.
53
 */
54

55 56
#undef PLACEHOLDER_DEBUG

57 58 59 60 61 62 63 64 65 66 67 68 69
/* ----- Private prototypes ----- */

static void     gdl_dock_placeholder_class_init     (GdlDockPlaceholderClass *klass);

static void     gdl_dock_placeholder_set_property   (GObject                 *g_object,
                                                     guint                    prop_id,
                                                     const GValue            *value,
                                                     GParamSpec              *pspec);
static void     gdl_dock_placeholder_get_property   (GObject                 *g_object,
                                                     guint                    prop_id,
                                                     GValue                  *value,
                                                     GParamSpec              *pspec);

70
static void     gdl_dock_placeholder_dispose        (GObject                 *object);
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93

static void     gdl_dock_placeholder_add            (GtkContainer            *container,
                                                     GtkWidget               *widget);

static void     gdl_dock_placeholder_detach         (GdlDockObject           *object,
                                                     gboolean                 recursive);
static void     gdl_dock_placeholder_reduce         (GdlDockObject           *object);
static void     gdl_dock_placeholder_dock           (GdlDockObject           *object,
                                                     GdlDockObject           *requestor,
                                                     GdlDockPlacement         position,
                                                     GValue                  *other_data);

static void     gdl_dock_placeholder_weak_notify    (gpointer                 data,
                                                     GObject                 *old_object);

static void     disconnect_host                     (GdlDockPlaceholder      *ph);
static void     connect_host                        (GdlDockPlaceholder      *ph,
                                                     GdlDockObject           *new_host);
static void     do_excursion                        (GdlDockPlaceholder      *ph);

static void     gdl_dock_placeholder_present        (GdlDockObject           *object,
                                                     GdlDockObject           *child);

94 95 96
static void     detach_cb                           (GdlDockObject           *object,
                                                     gboolean                 recursive,
                                                     gpointer                 user_data);
97 98 99 100 101 102 103

/* ----- Private variables and data structures ----- */

enum {
    PROP_0,
    PROP_STICKY,
    PROP_HOST,
104 105
    PROP_NEXT_PLACEMENT,
    PROP_WIDTH,
106
    PROP_HEIGHT,
107 108 109
    PROP_FLOATING,
    PROP_FLOAT_X,
    PROP_FLOAT_Y
110 111 112 113 114 115
};

struct _GdlDockPlaceholderPrivate {
    /* current object this placeholder is pinned to */
    GdlDockObject    *host;
    gboolean          sticky;
116

117 118 119 120 121
    /* when the placeholder is moved up the hierarchy, this stack
       keeps track of the necessary dock positions needed to get the
       placeholder to the original position */
    GSList           *placement_stack;

122 123 124
    /* Width and height of the attachments */
    gint              width;
    gint              height;
125

126 127 128
    /* connected signal handlers */
    guint             host_detach_handler;
    guint             host_dock_handler;
129 130 131 132 133

    /* Window Coordinates if Dock was floating */
    gboolean          floating;
    gint              floatx;
    gint              floaty;
134 135 136 137 138
};


/* ----- Private interface ----- */

139
G_DEFINE_TYPE (GdlDockPlaceholder, gdl_dock_placeholder, GDL_TYPE_DOCK_OBJECT);
140

141
static void
142 143
gdl_dock_placeholder_class_init (GdlDockPlaceholderClass *klass)
{
144
    GObjectClass       *object_class;
145
    GtkContainerClass  *container_class;
146
    GdlDockObjectClass *dock_object_class;
147

148
    object_class = G_OBJECT_CLASS (klass);
149
    container_class = GTK_CONTAINER_CLASS (klass);
150 151 152 153
    dock_object_class = GDL_DOCK_OBJECT_CLASS (klass);

    object_class->get_property = gdl_dock_placeholder_get_property;
    object_class->set_property = gdl_dock_placeholder_set_property;
154
    object_class->dispose = gdl_dock_placeholder_dispose;
155 156 157

    container_class->add = gdl_dock_placeholder_add;

158
    gdl_dock_object_class_set_is_compound (dock_object_class, FALSE);
159 160 161 162
    dock_object_class->detach = gdl_dock_placeholder_detach;
    dock_object_class->reduce = gdl_dock_placeholder_reduce;
    dock_object_class->dock = gdl_dock_placeholder_dock;
    dock_object_class->present = gdl_dock_placeholder_present;
163 164

    g_object_class_install_property (
165
        object_class, PROP_STICKY,
166
        g_param_spec_boolean ("sticky", _("Sticky"),
167 168 169 170 171
                              _("Whether the placeholder will stick to its host or "
                                "move up the hierarchy when the host is redocked"),
                              FALSE,
                              G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));

172
    g_object_class_install_property (
173
        object_class, PROP_HOST,
174
        g_param_spec_object ("host", _("Host"),
175 176 177
                            _("The dock object this placeholder is attached to"),
                            GDL_TYPE_DOCK_OBJECT,
                            G_PARAM_READWRITE));
178

179 180
    /* this will return the top of the placement stack */
    g_object_class_install_property (
181
        object_class, PROP_NEXT_PLACEMENT,
182
        g_param_spec_enum ("next-placement", _("Next placement"),
183 184 185 186 187 188
         					_("The position an item will be docked to our host if a "
           					"request is made to dock to us"),
                           	GDL_TYPE_DOCK_PLACEMENT,
                           	GDL_DOCK_CENTER,
                           	G_PARAM_READWRITE |
                           	GDL_DOCK_PARAM_EXPORT | GDL_DOCK_PARAM_AFTER));
189

190
    g_object_class_install_property (
191
        object_class, PROP_WIDTH,
192
        g_param_spec_int ("width", _("Width"),
193 194 195 196
                          	_("Width for the widget when it's attached to the placeholder"),
                          	-1, G_MAXINT, -1,
                          	G_PARAM_READWRITE | G_PARAM_CONSTRUCT |
                          	GDL_DOCK_PARAM_EXPORT));
197

198
    g_object_class_install_property (
199
        object_class, PROP_HEIGHT,
200
        g_param_spec_int ("height", _("Height"),
201 202 203 204
                     		_("Height for the widget when it's attached to the placeholder"),
                          	-1, G_MAXINT, -1,
                          	G_PARAM_READWRITE | G_PARAM_CONSTRUCT |
                          	GDL_DOCK_PARAM_EXPORT));
205 206 207
    g_object_class_install_property (
        object_class, PROP_FLOATING,
        g_param_spec_boolean ("floating", _("Floating Toplevel"),
208 209 210 211
                            _("Whether the placeholder is standing in for a "
                            "floating toplevel dock"),
                            FALSE,
                            G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
212 213
    g_object_class_install_property (
        object_class, PROP_FLOAT_X,
214 215
        g_param_spec_int ("floatx", _("X Coordinate"),
                          	_("X coordinate for dock when floating"),
216 217 218
                          	-1, G_MAXINT, -1,
                          	G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
                          	GDL_DOCK_PARAM_EXPORT));
219 220
    g_object_class_install_property (
        object_class, PROP_FLOAT_Y,
221 222
        g_param_spec_int ("floaty", _("Y Coordinate"),
                          	_("Y coordinate for dock when floating"),
223 224 225
                          	-1, G_MAXINT, -1,
                          	G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
                          	GDL_DOCK_PARAM_EXPORT));
226 227

    g_type_class_add_private (object_class, sizeof (GdlDockPlaceholderPrivate));
228 229
}

230
static void
231
gdl_dock_placeholder_init (GdlDockPlaceholder *ph)
232
{
233 234 235 236
    ph->priv = G_TYPE_INSTANCE_GET_PRIVATE (ph,
                                            GDL_TYPE_DOCK_PLACEHOLDER,
                                            GdlDockPlaceholderPrivate);

237 238
    gtk_widget_set_has_window (GTK_WIDGET (ph), FALSE);
    gtk_widget_set_can_focus (GTK_WIDGET (ph), FALSE);
239 240
}

241
static void
242
gdl_dock_placeholder_set_property (GObject      *g_object,
243 244 245
                                   guint         prop_id,
                                   const GValue *value,
                                   GParamSpec   *pspec)
246 247 248 249
{
    GdlDockPlaceholder *ph = GDL_DOCK_PLACEHOLDER (g_object);

    switch (prop_id) {
250
        case PROP_STICKY:
251 252
            if (ph->priv)
                ph->priv->sticky = g_value_get_boolean (value);
253 254
            break;
        case PROP_HOST:
255
            gdl_dock_placeholder_attach (ph, g_value_get_object (value));
256
            break;
257
        case PROP_NEXT_PLACEMENT:
258 259 260
            if (ph->priv) {
            	ph->priv->placement_stack =
            			g_slist_prepend (ph->priv->placement_stack,
261
                                     GINT_TO_POINTER (g_value_get_enum (value)));
262 263
            }
            break;
264
        case PROP_WIDTH:
265
            ph->priv->width = g_value_get_int (value);
266 267
            break;
        case PROP_HEIGHT:
268
            ph->priv->height = g_value_get_int (value);
269
            break;
270
		case PROP_FLOATING:
271
			ph->priv->floating = g_value_get_boolean (value);
272 273
			break;
		case PROP_FLOAT_X:
274
			ph->priv->floatx = g_value_get_int (value);
275 276
			break;
		case PROP_FLOAT_Y:
277
			ph->priv->floaty = g_value_get_int (value);
278
			break;
279
        default:
280 281
			G_OBJECT_WARN_INVALID_PROPERTY_ID (g_object, prop_id, pspec);
			break;
282 283 284
    }
}

285
static void
286
gdl_dock_placeholder_get_property (GObject    *g_object,
287 288 289
                                   guint       prop_id,
                                   GValue     *value,
                                   GParamSpec *pspec)
290 291 292 293
{
    GdlDockPlaceholder *ph = GDL_DOCK_PLACEHOLDER (g_object);

    switch (prop_id) {
294
        case PROP_STICKY:
295 296
            if (ph->priv)
                g_value_set_boolean (value, ph->priv->sticky);
297 298
            else
                g_value_set_boolean (value, FALSE);
299 300
            break;
        case PROP_HOST:
301 302
            if (ph->priv)
                g_value_set_object (value, ph->priv->host);
303 304
            else
                g_value_set_object (value, NULL);
305 306
            break;
        case PROP_NEXT_PLACEMENT:
307 308
            if (ph->priv && ph->priv->placement_stack)
                g_value_set_enum (value, (GdlDockPlacement) ph->priv->placement_stack->data);
309 310
            else
                g_value_set_enum (value, GDL_DOCK_CENTER);
311 312
            break;
        case PROP_WIDTH:
313
            g_value_set_int (value, ph->priv->width);
314 315
            break;
        case PROP_HEIGHT:
316
            g_value_set_int (value, ph->priv->height);
317 318
            break;
		case PROP_FLOATING:
319
			g_value_set_boolean (value, ph->priv->floating);
320 321
			break;
		case PROP_FLOAT_X:
322
            g_value_set_int (value, ph->priv->floatx);
323 324
            break;
        case PROP_FLOAT_Y:
325
            g_value_set_int (value, ph->priv->floaty);
326 327 328 329
            break;
        default:
            G_OBJECT_WARN_INVALID_PROPERTY_ID (g_object, prop_id, pspec);
            break;
330 331 332 333
    }
}

static void
334
gdl_dock_placeholder_dispose (GObject *object)
335 336 337
{
    GdlDockPlaceholder *ph = GDL_DOCK_PLACEHOLDER (object);

338 339
    if (ph->priv->host)
        gdl_dock_placeholder_detach (GDL_DOCK_OBJECT (object), FALSE);
340

341
    G_OBJECT_CLASS (gdl_dock_placeholder_parent_class)->dispose (object);
342 343
}

344
static void
345 346 347 348 349
gdl_dock_placeholder_add (GtkContainer *container,
                          GtkWidget    *widget)
{
    GdlDockPlaceholder *ph;
    GdlDockPlacement    pos = GDL_DOCK_CENTER;   /* default position */
350

351 352 353 354
    g_return_if_fail (GDL_IS_DOCK_PLACEHOLDER (container));
    g_return_if_fail (GDL_IS_DOCK_ITEM (widget));

    ph = GDL_DOCK_PLACEHOLDER (container);
355 356
    if (ph->priv->placement_stack)
        pos = (GdlDockPlacement) ph->priv->placement_stack->data;
357

358 359 360 361 362 363 364 365 366 367 368 369
    gdl_dock_object_dock (GDL_DOCK_OBJECT (ph), GDL_DOCK_OBJECT (widget),
                          pos, NULL);
}

static void
gdl_dock_placeholder_detach (GdlDockObject *object,
                             gboolean       recursive)
{
    GdlDockPlaceholder *ph = GDL_DOCK_PLACEHOLDER (object);

    /* disconnect handlers */
    disconnect_host (ph);
370

371
    /* free the placement stack */
372 373
    g_slist_free (ph->priv->placement_stack);
    ph->priv->placement_stack = NULL;
374 375 376 377

    GDL_DOCK_OBJECT_UNSET_FLAGS (object, GDL_DOCK_ATTACHED);
}

378
static void
379 380 381 382 383 384
gdl_dock_placeholder_reduce (GdlDockObject *object)
{
    /* placeholders are not reduced */
    return;
}

385 386 387 388 389
static void
find_biggest_dock_item (GtkContainer *container, GtkWidget **biggest_child,
                        gint *biggest_child_area)
{
    GList *children, *child;
390
    GtkAllocation allocation;
391

392 393 394 395 396
    children = gtk_container_get_children (GTK_CONTAINER (container));
    child = children;
    while (child) {
        gint area;
        GtkWidget *child_widget;
397

398
        child_widget = GTK_WIDGET (child->data);
399

400 401 402 403 404 405
        if (gdl_dock_object_is_compound (GDL_DOCK_OBJECT(child_widget))) {
            find_biggest_dock_item (GTK_CONTAINER (child_widget),
                                    biggest_child, biggest_child_area);
            child = g_list_next (child);
            continue;
        }
406 407
        gtk_widget_get_allocation (child_widget, &allocation);
        area = allocation.width * allocation.height;
408

409 410 411 412 413 414 415 416 417 418 419 420 421 422
        if (area > *biggest_child_area) {
            *biggest_child_area = area;
            *biggest_child = child_widget;
        }
        child = g_list_next (child);
    }
}

static void
attempt_to_dock_on_host (GdlDockPlaceholder *ph, GdlDockObject *host,
                         GdlDockObject *requestor, GdlDockPlacement placement,
                         gpointer other_data)
{
    GdlDockObject *parent;
423 424 425 426 427 428 429
    GtkAllocation  allocation;
    gint host_width;
    gint host_height;

    gtk_widget_get_allocation (GTK_WIDGET (host), &allocation);
    host_width = allocation.width;
    host_height = allocation.height;
430

431 432 433 434 435 436 437 438 439 440 441
    if (placement != GDL_DOCK_CENTER || !GDL_IS_DOCK_PANED (host)) {
        /* we simply act as a proxy for our host */
        gdl_dock_object_dock (host, requestor,
                              placement, other_data);
    } else {
        /* If the requested pos is center, we have to make sure that it
         * does not colapses existing paned items. Find the larget item
         * which is not a paned item to dock to.
         */
        GtkWidget *biggest_child = NULL;
        gint biggest_child_area = 0;
442

443 444
        find_biggest_dock_item (GTK_CONTAINER (host), &biggest_child,
                                &biggest_child_area);
445

446 447 448 449 450 451 452 453 454 455 456
        if (biggest_child) {
            /* we simply act as a proxy for our host */
            gdl_dock_object_dock (GDL_DOCK_OBJECT (biggest_child), requestor,
                                  placement, other_data);
        } else {
            g_warning ("No suitable child found! Should not be here!");
            /* we simply act as a proxy for our host */
            gdl_dock_object_dock (GDL_DOCK_OBJECT (host), requestor,
                                  placement, other_data);
        }
    }
457

458
    parent = gdl_dock_object_get_parent_object (requestor);
459

460 461 462
    /* Restore dock item's dimention */
    switch (placement) {
        case GDL_DOCK_LEFT:
463
            if (ph->priv->width > 0) {
464
                g_object_set (G_OBJECT (parent), "position",
465
                              ph->priv->width, NULL);
466 467 468
            }
            break;
        case GDL_DOCK_RIGHT:
469 470
            if (ph->priv->width > 0) {
                gint complementary_width = host_width - ph->priv->width;
471

472 473 474 475 476 477
                if (complementary_width > 0)
                    g_object_set (G_OBJECT (parent), "position",
                                  complementary_width, NULL);
            }
            break;
        case GDL_DOCK_TOP:
478
            if (ph->priv->height > 0) {
479
                g_object_set (G_OBJECT (parent), "position",
480
                              ph->priv->height, NULL);
481 482 483
            }
            break;
        case GDL_DOCK_BOTTOM:
484 485
            if (ph->priv->height > 0) {
                gint complementary_height = host_height - ph->priv->height;
486

487 488 489 490 491 492 493 494 495 496 497
                if (complementary_height > 0)
                    g_object_set (G_OBJECT (parent), "position",
                                  complementary_height, NULL);
            }
            break;
        default:
            /* nothing */
            break;
    }
}

498
static void
499
gdl_dock_placeholder_dock (GdlDockObject    *object,
500 501 502
                           GdlDockObject    *requestor,
                           GdlDockPlacement  position,
                           GValue           *other_data)
503 504
{
    GdlDockPlaceholder *ph = GDL_DOCK_PLACEHOLDER (object);
505

506 507
    if (ph->priv->host) {
        attempt_to_dock_on_host (ph, ph->priv->host, requestor,
508
                                 position, other_data);
509 510 511
    }
    else {
        GdlDockObject *toplevel;
512

513
        if (!gdl_dock_object_is_bound (GDL_DOCK_OBJECT (ph))) {
514
            g_warning ("%s", _("Attempt to dock a dock object to an unbound placeholder"));
515 516
            return;
        }
517

518
        /* dock the item as a floating of the controller */
519
        toplevel = gdl_dock_object_get_controller (GDL_DOCK_OBJECT (ph));
520 521 522 523 524
        gdl_dock_object_dock (toplevel, requestor,
                              GDL_DOCK_FLOATING, NULL);
    }
}

525
#ifdef PLACEHOLDER_DEBUG
526 527 528
static void
print_placement_stack (GdlDockPlaceholder *ph)
{
529
    GSList *s = ph->priv->placement_stack;
530 531 532 533 534 535 536
    GEnumClass *enum_class = G_ENUM_CLASS (g_type_class_ref (GDL_TYPE_DOCK_PLACEMENT));
    GEnumValue *enum_value;
    gchar *name;
    GString *message;

    message = g_string_new (NULL);
    g_string_printf (message, "[%p] host: %p (%s), stack: ",
537
                     ph, ph->priv->host, G_OBJECT_TYPE_NAME (ph->priv->host));
538 539 540 541 542 543
    for (; s; s = s->next) {
        enum_value = g_enum_get_value (enum_class, (GdlDockPlacement) s->data);
        name = enum_value ? enum_value->value_name : NULL;
        g_string_append_printf (message, "%s, ", name);
    }
    g_message ("%s", message->str);
544

545 546 547
    g_string_free (message, TRUE);
    g_type_class_unref (enum_class);
}
548
#endif
549

550
static void
551 552 553 554 555 556 557
gdl_dock_placeholder_present (GdlDockObject *object,
                              GdlDockObject *child)
{
    /* do nothing */
    return;
}

558 559
/* ----- Public interface ----- */

560 561 562 563 564 565
/**
 * gdl_dock_placeholder_new:
 * @name: Unique name for identifying the dock object.
 * @object: Corresponding #GdlDockObject
 * @position: The position to dock a new item in @object
 * @sticky: %TRUE if the placeholder move with the @object
566
 *
567 568 569
 * Creates a new dock placeholder at @object place. This is a kind of marker
 * allowing you to dock new items later at this place. It is not completely
 * working though.
570
 *
571
 * Returns: The newly created placeholder.
572
 *
573
 * Deprecated: 3.6:
574
 */
575
GtkWidget *
576
gdl_dock_placeholder_new (const gchar     *name,
577 578 579 580 581 582 583 584 585
                          GdlDockObject    *object,
                          GdlDockPlacement  position,
                          gboolean          sticky)
{
    GdlDockPlaceholder *ph;

    ph = GDL_DOCK_PLACEHOLDER (g_object_new (GDL_TYPE_DOCK_PLACEHOLDER,
                                             "name", name,
                                             "sticky", sticky,
586 587
                                             "next-placement", position,
                                             "host", object,
588
                                             NULL));
589
    GDL_DOCK_OBJECT_UNSET_FLAGS (ph, GDL_DOCK_AUTOMATIC);
590

591 592 593
    return GTK_WIDGET (ph);
}

594
static void
595 596 597 598
gdl_dock_placeholder_weak_notify (gpointer data,
                                  GObject *old_object)
{
    GdlDockPlaceholder *ph;
599

600 601 602
    g_return_if_fail (data != NULL && GDL_IS_DOCK_PLACEHOLDER (data));

    ph = GDL_DOCK_PLACEHOLDER (data);
603

604 605 606
#ifdef PLACEHOLDER_DEBUG
    g_message ("The placeholder just lost its host, ph = %p", ph);
#endif
607

608 609
    /* we shouldn't get here, so perform an emergency detach. instead
       we should have gotten a detach signal from our host */
610
    ph->priv->host = NULL;
611 612

    /* We didn't get a detach signal from the host. Detach from the
613
    supposedly dead host (consequently attaching to the controller) */
614

615 616
    detach_cb (NULL, TRUE, data);
#if 0
617
    /* free the placement stack */
618 619
    g_slist_free (ph->priv->placement_stack);
    ph->priv->placement_stack = NULL;
620
    GDL_DOCK_OBJECT_UNSET_FLAGS (ph, GDL_DOCK_ATTACHED);
621
#endif
622 623 624 625 626 627 628 629 630 631 632
}

static void
detach_cb (GdlDockObject *object,
           gboolean       recursive,
           gpointer       user_data)
{
    GdlDockPlaceholder *ph;
    GdlDockObject      *new_host, *obj;

    g_return_if_fail (user_data != NULL && GDL_IS_DOCK_PLACEHOLDER (user_data));
633

634 635 636 637 638
    /* we go up in the hierarchy and we store the hinted placement in
     * the placement stack so we can rebuild the docking layout later
     * when we get the host's dock signal.  */

    ph = GDL_DOCK_PLACEHOLDER (user_data);
639
    obj = ph->priv->host;
640 641
    if (obj != object) {
        g_warning (_("Got a detach signal from an object (%p) who is not "
642
                     "our host %p"), object, ph->priv->host);
643 644
        return;
    }
645

646
    /* skip sticky objects */
647
    if (ph->priv->sticky)
648
        return;
649

650 651 652 653 654 655
    if (obj)
        /* go up in the hierarchy */
        new_host = gdl_dock_object_get_parent_object (obj);
    else
        /* Detaching from the dead host */
        new_host = NULL;
656

657 658
    while (new_host) {
        GdlDockPlacement pos = GDL_DOCK_NONE;
659

660 661
        /* get placement hint from the new host */
        if (gdl_dock_object_child_placement (new_host, obj, &pos)) {
662 663
            ph->priv->placement_stack = g_slist_prepend (
                ph->priv->placement_stack, (gpointer) pos);
664 665 666 667 668 669 670 671 672
        }
        else {
            g_warning (_("Something weird happened while getting the child "
                         "placement for %p from parent %p"), obj, new_host);
        }

        if (!GDL_DOCK_OBJECT_IN_DETACH (new_host))
            /* we found a "stable" dock object */
            break;
673

674 675 676 677 678 679 680 681
        obj = new_host;
        new_host = gdl_dock_object_get_parent_object (obj);
    }

    /* disconnect host */
    disconnect_host (ph);

    if (!new_host) {
682 683 684
#ifdef PLACEHOLDER_DEBUG
        g_message ("Detaching from the toplevel. Assignaing to controller");
#endif
685 686
        /* the toplevel was detached: we attach ourselves to the
           controller with an initial placement of floating */
687
        new_host = gdl_dock_object_get_controller (GDL_DOCK_OBJECT (ph));
688

689
        /*
690 691
        ph->priv->placement_stack = g_slist_prepend (
        ph->priv->placement_stack, (gpointer) GDL_DOCK_FLOATING);
692
        */
693 694 695 696
    }
    if (new_host)
        connect_host (ph, new_host);

697
#ifdef PLACEHOLDER_DEBUG
698
    print_placement_stack (ph);
699
#endif
700 701 702 703 704 705 706 707 708 709
}

/**
 * do_excursion:
 * @ph: placeholder object
 *
 * Tries to shrink the placement stack by examining the host's
 * children and see if any of them matches the placement which is at
 * the top of the stack.  If this is the case, it tries again with the
 * new host.
710
 *
711
 * Deprecated: 3.6:
712 713 714 715
 **/
static void
do_excursion (GdlDockPlaceholder *ph)
{
716 717 718 719
    if (ph->priv->host &&
        !ph->priv->sticky &&
        ph->priv->placement_stack &&
        gdl_dock_object_is_compound (ph->priv->host)) {
720 721

        GdlDockPlacement pos, stack_pos =
722
            (GdlDockPlacement) ph->priv->placement_stack->data;
723
        GList           *children, *l;
724
        GdlDockObject   *host = ph->priv->host;
725

726 727 728 729 730 731 732 733
        children = gtk_container_get_children (GTK_CONTAINER (host));
        for (l = children; l; l = l->next) {
            pos = stack_pos;
            gdl_dock_object_child_placement (GDL_DOCK_OBJECT (host),
                                             GDL_DOCK_OBJECT (l->data),
                                             &pos);
            if (pos == stack_pos) {
                /* remove the stack position */
734 735 736
                ph->priv->placement_stack =
                    g_slist_remove_link (ph->priv->placement_stack,
                                         ph->priv->placement_stack);
737

738 739 740 741 742 743 744
                /* connect to the new host */
                disconnect_host (ph);
                connect_host (ph, GDL_DOCK_OBJECT (l->data));

                /* recurse... */
                if (!GDL_DOCK_OBJECT_IN_REFLOW (l->data))
                    do_excursion (ph);
745

746 747 748 749 750 751 752
                break;
            }
        }
        g_list_free (children);
    }
}

753
static void
754 755 756 757 758 759 760 761
dock_cb (GdlDockObject    *object,
         GdlDockObject    *requestor,
         GdlDockPlacement  position,
         GValue           *other_data,
         gpointer          user_data)
{
    GdlDockPlacement    pos = GDL_DOCK_NONE;
    GdlDockPlaceholder *ph;
762

763 764
    g_return_if_fail (user_data != NULL && GDL_IS_DOCK_PLACEHOLDER (user_data));
    ph = GDL_DOCK_PLACEHOLDER (user_data);
765
    g_return_if_fail (ph->priv->host == object);
766

767 768
    /* see if the given position is compatible for the stack's top
       element */
769 770
    if (!ph->priv->sticky && ph->priv->placement_stack) {
        pos = (GdlDockPlacement) ph->priv->placement_stack->data;
771
        if (gdl_dock_object_child_placement (object, requestor, &pos)) {
772
            if (pos == (GdlDockPlacement) ph->priv->placement_stack->data) {
773 774 775 776 777
                /* the position is compatible: excurse down */
                do_excursion (ph);
            }
        }
    }
778
#ifdef PLACEHOLDER_DEBUG
779
    print_placement_stack (ph);
780
#endif
781 782 783 784 785
}

static void
disconnect_host (GdlDockPlaceholder *ph)
{
786
    if (!ph->priv->host)
787
        return;
788

789 790 791 792 793 794
    if (ph->priv->host_detach_handler)
        g_signal_handler_disconnect (ph->priv->host, ph->priv->host_detach_handler);
    if (ph->priv->host_dock_handler)
        g_signal_handler_disconnect (ph->priv->host, ph->priv->host_dock_handler);
    ph->priv->host_detach_handler = 0;
    ph->priv->host_dock_handler = 0;
795 796

    /* remove weak ref to object */
797
    g_object_weak_unref (G_OBJECT (ph->priv->host),
798
                         gdl_dock_placeholder_weak_notify, ph);
799
    ph->priv->host = NULL;
800

801 802 803
#ifdef PLACEHOLDER_DEBUG
    g_message ("Host just disconnected!, ph = %p", ph);
#endif
804 805 806 807 808 809
}

static void
connect_host (GdlDockPlaceholder *ph,
              GdlDockObject      *new_host)
{
810
    if (ph->priv->host)
811
        disconnect_host (ph);
812

813 814
    ph->priv->host = new_host;
    g_object_weak_ref (G_OBJECT (ph->priv->host),
815 816
                       gdl_dock_placeholder_weak_notify, ph);

817 818
    ph->priv->host_detach_handler =
        g_signal_connect (ph->priv->host,
819 820 821
                          "detach",
                          (GCallback) detach_cb,
                          (gpointer) ph);
822

823 824
    ph->priv->host_dock_handler =
        g_signal_connect (ph->priv->host,
825 826 827
                          "dock",
                          (GCallback) dock_cb,
                          (gpointer) ph);
828

829 830 831
#ifdef PLACEHOLDER_DEBUG
    g_message ("Host just connected!, ph = %p", ph);
#endif
832 833
}

834 835 836 837
/**
 * gdl_dock_placeholder_attach:
 * @ph: The #GdlDockPlaceholder object
 * @object: A new #GdlDockObject
838
 *
839
 * Move the placeholder to the position of @object.
840
 *
841
 * Deprecated: 3.6:
842
 */
843 844 845 846 847
void
gdl_dock_placeholder_attach (GdlDockPlaceholder *ph,
                             GdlDockObject      *object)
{
    g_return_if_fail (ph != NULL && GDL_IS_DOCK_PLACEHOLDER (ph));
848
    g_return_if_fail (ph->priv != NULL);
849
    g_return_if_fail (object != NULL);
850

851 852
    /* object binding */
    if (!gdl_dock_object_is_bound (GDL_DOCK_OBJECT (ph)))
853
        gdl_dock_object_bind (GDL_DOCK_OBJECT (ph), object->deprecated_master);
854

855
    g_return_if_fail (GDL_DOCK_OBJECT (ph)->deprecated_master == object->deprecated_master);
856

857
    gdl_dock_object_freeze (GDL_DOCK_OBJECT (ph));
858

859
    /* detach from previous host first */
860
    if (ph->priv->host)
861 862 863
        gdl_dock_object_detach (GDL_DOCK_OBJECT (ph), FALSE);

    connect_host (ph, object);
864

865
    GDL_DOCK_OBJECT_SET_FLAGS (ph, GDL_DOCK_ATTACHED);
866

867 868
    gdl_dock_object_thaw (GDL_DOCK_OBJECT (ph));
}