gdl-dock-paned.c 25.1 KB
Newer Older
1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2
 *
3
 * gdl-dock-paned.h
4
 *
5
 * This file is part of the GNOME Devtools Libraries.
6
 *
7
 * Copyright (C) 2002 Gustavo Girldez <gustavo.giraldez@gmx.net>
8
 *
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
#include <glib/gi18n-lib.h>
29
#include <string.h>
30
#include <gtk/gtk.h>
31 32 33

#include "gdl-dock-paned.h"

34 35 36 37 38 39 40
/**
 * SECTION:gdl-dock-paned
 * @title: GdlDockPaned
 * @short_description: Arrange dock widget in two adjustable panes
 * @stability: Unstable
 * @see_also: #GdlDockNotebook, #GdlDockMaster
 *
41 42 43 44 45
 * A #GdlDockPaned is a compound dock widget. It can dock one or two children,
 * including another compound widget like a #GdlDockPaned or a #GdlDockNotebook.
 * The children are displayed in two panes using a #GtkPaned widget.
 * A #GdlDockPaned is normally created automatically by the master when docking
 * a child on any edge: top, bottom, left or right.
46 47
 */

48

49 50 51
/* Private prototypes */

static void     gdl_dock_paned_class_init     (GdlDockPanedClass *klass);
52
static void     gdl_dock_paned_init  (GdlDockPaned      *paned);
53 54 55 56 57 58 59 60 61 62 63 64
static GObject *gdl_dock_paned_constructor    (GType              type,
                                               guint              n_construct_properties,
                                               GObjectConstructParam *construct_param);
static void     gdl_dock_paned_set_property   (GObject           *object,
                                               guint              prop_id,
                                               const GValue      *value,
                                               GParamSpec        *pspec);
static void     gdl_dock_paned_get_property   (GObject           *object,
                                               guint              prop_id,
                                               GValue            *value,
                                               GParamSpec        *pspec);

65
static void     gdl_dock_paned_destroy        (GtkWidget         *object);
66 67 68 69 70 71 72 73 74

static void     gdl_dock_paned_add            (GtkContainer      *container,
                                               GtkWidget         *widget);
static void     gdl_dock_paned_forall         (GtkContainer      *container,
                                               gboolean           include_internals,
                                               GtkCallback        callback,
                                               gpointer           callback_data);
static GType    gdl_dock_paned_child_type     (GtkContainer      *container);

75
static gboolean gdl_dock_paned_dock_request   (GdlDockObject     *object,
76
                                               gint               x,
77
                                               gint               y,
78 79 80 81 82
                                               GdlDockRequest    *request);
static void     gdl_dock_paned_dock           (GdlDockObject    *object,
                                               GdlDockObject    *requestor,
                                               GdlDockPlacement  position,
                                               GValue           *other_data);
83 84 85 86

static void     gdl_dock_paned_set_orientation (GdlDockItem    *item,
                                                GtkOrientation  orientation);

87 88 89 90 91 92
static gboolean gdl_dock_paned_child_placement (GdlDockObject    *object,
                                                GdlDockObject    *child,
                                                GdlDockPlacement *placement);


/* ----- Class variables and definitions ----- */
93

94 95 96 97 98 99
#define SPLIT_RATIO  0.3

enum {
    PROP_0,
    PROP_POSITION
};
100

101 102 103 104
struct _GdlDockPanedPrivate {
    gboolean    user_action;
    gboolean    position_changed;
};
105

106
/* ----- Private functions ----- */
107

108
G_DEFINE_TYPE (GdlDockPaned, gdl_dock_paned, GDL_TYPE_DOCK_ITEM);
109 110 111 112

static void
gdl_dock_paned_class_init (GdlDockPanedClass *klass)
{
113 114 115 116 117
    GObjectClass       *g_object_class;
    GtkWidgetClass     *widget_class;
    GtkContainerClass  *container_class;
    GdlDockObjectClass *object_class;
    GdlDockItemClass   *item_class;
118

119 120 121 122 123
    g_object_class = G_OBJECT_CLASS (klass);
    widget_class = GTK_WIDGET_CLASS (klass);
    container_class = GTK_CONTAINER_CLASS (klass);
    object_class = GDL_DOCK_OBJECT_CLASS (klass);
    item_class = GDL_DOCK_ITEM_CLASS (klass);
124

125 126
    g_object_class->set_property = gdl_dock_paned_set_property;
    g_object_class->get_property = gdl_dock_paned_get_property;
127
    g_object_class->constructor = gdl_dock_paned_constructor;
128

129
    widget_class->destroy = gdl_dock_paned_destroy;
130 131 132 133

    container_class->add = gdl_dock_paned_add;
    container_class->forall = gdl_dock_paned_forall;
    container_class->child_type = gdl_dock_paned_child_type;
134

135
    gdl_dock_object_class_set_is_compound (object_class, TRUE);
136 137 138
    object_class->dock_request = gdl_dock_paned_dock_request;
    object_class->dock = gdl_dock_paned_dock;
    object_class->child_placement = gdl_dock_paned_child_placement;
139

140
    gdl_dock_item_class_set_has_grip (item_class, FALSE);
141
    item_class->set_orientation = gdl_dock_paned_set_orientation;
142

143
    g_object_class_install_property (
144 145 146 147 148 149
        g_object_class, PROP_POSITION,
        g_param_spec_uint ("position", _("Position"),
                           _("Position of the divider in pixels"),
                           0, G_MAXINT, 0,
                           G_PARAM_READWRITE |
                           GDL_DOCK_PARAM_EXPORT | GDL_DOCK_PARAM_AFTER));
150 151

    g_type_class_add_private (object_class, sizeof (GdlDockPanedPrivate));
152
}
153

154
static void
155
gdl_dock_paned_init (GdlDockPaned *paned)
156
{
157 158 159 160 161 162
    paned->priv = G_TYPE_INSTANCE_GET_PRIVATE (paned,
                                               GDL_TYPE_DOCK_PANED,
                                               GdlDockPanedPrivate);

    paned->priv->user_action = FALSE;
    paned->priv->position_changed = FALSE;
163 164
}

165
static void
166 167
gdl_dock_paned_notify_cb (GObject    *g_object,
                          GParamSpec *pspec,
168
                          gpointer    user_data)
169 170
{
    GdlDockPaned *paned;
171

172
    g_return_if_fail (user_data != NULL && GDL_IS_DOCK_PANED (user_data));
173

174 175
    /* chain the notification to the GdlDockPaned */
    g_object_notify (G_OBJECT (user_data), pspec->name);
176

177
    paned = GDL_DOCK_PANED (user_data);
178

179 180
    if (paned->priv->user_action && !strcmp (pspec->name, "position"))
        paned->priv->position_changed = TRUE;
181 182
}

183
static gboolean
184 185 186 187 188
gdl_dock_paned_button_cb (GtkWidget      *widget,
                          GdkEventButton *event,
                          gpointer        user_data)
{
    GdlDockPaned *paned;
189

190
    g_return_val_if_fail (user_data != NULL && GDL_IS_DOCK_PANED (user_data), FALSE);
191

192 193 194
    paned = GDL_DOCK_PANED (user_data);
    if (event->button == 1) {
        if (event->type == GDK_BUTTON_PRESS)
195
            paned->priv->user_action = TRUE;
196
        else {
197 198
            paned->priv->user_action = FALSE;
            if (paned->priv->position_changed) {
199
                /* emit pending layout changed signal to track separator position */
200
                gdl_dock_object_layout_changed_notify (GDL_DOCK_OBJECT (paned));
201
                paned->priv->position_changed = FALSE;
202 203 204
            }
        }
    }
205

206 207 208
    return FALSE;
}

209
static void
210
gdl_dock_paned_create_child (GdlDockPaned   *paned,
211
                             GtkOrientation  orientation)
212
{
213
    GdlDockItem *item;
214
    GtkWidget *child;
215

216
    item = GDL_DOCK_ITEM (paned);
217

218
    /* create the container paned */
219 220
    child = gtk_paned_new (orientation);
    gdl_dock_item_set_child (item, child);
221

222
    /* get notification for propagation */
223
    g_signal_connect (child, "notify::position",
224
                      (GCallback) gdl_dock_paned_notify_cb, (gpointer) item);
225
    g_signal_connect (child, "button-press-event",
226
                      (GCallback) gdl_dock_paned_button_cb, (gpointer) item);
227
    g_signal_connect (child, "button-release-event",
228
                      (GCallback) gdl_dock_paned_button_cb, (gpointer) item);
229

230
    gtk_widget_show (child);
231 232
}

233 234 235 236
static GObject *
gdl_dock_paned_constructor (GType                  type,
                            guint                  n_construct_properties,
                            GObjectConstructParam *construct_param)
237
{
238
    GObject *g_object;
239 240 241 242

    g_object = G_OBJECT_CLASS (gdl_dock_paned_parent_class)-> constructor (type,
                                                                          n_construct_properties,
                                                                          construct_param);
243 244
    if (g_object) {
        GdlDockItem *item = GDL_DOCK_ITEM (g_object);
245

246
        if (!gdl_dock_item_get_child (item))
247
            gdl_dock_paned_create_child (GDL_DOCK_PANED (g_object),
248
                                         gdl_dock_item_get_orientation (item));
249 250 251
        /* otherwise, the orientation was set as a construction
           parameter and the child is already created */
    }
252

253
    return g_object;
254 255 256
}

static void
257 258 259 260
gdl_dock_paned_set_property (GObject        *object,
                             guint           prop_id,
                             const GValue   *value,
                             GParamSpec     *pspec)
261
{
262
    GdlDockItem *item = GDL_DOCK_ITEM (object);
263
    GtkWidget *child;
264

265
    switch (prop_id) {
266
        case PROP_POSITION:
267 268 269
            child = gdl_dock_item_get_child (item);
            if (child && GTK_IS_PANED (child))
                gtk_paned_set_position (GTK_PANED (child),
270 271 272 273 274
                                        g_value_get_uint (value));
            break;
        default:
            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
            break;
275 276 277 278
    }
}

static void
279 280 281 282
gdl_dock_paned_get_property (GObject        *object,
                             guint           prop_id,
                             GValue         *value,
                             GParamSpec     *pspec)
283
{
284
    GdlDockItem *item = GDL_DOCK_ITEM (object);
285
    GtkWidget *child;
286

287
    switch (prop_id) {
288
        case PROP_POSITION:
289 290
            child = gdl_dock_item_get_child (item);
            if (child && GTK_IS_PANED (child))
291
                g_value_set_uint (value,
292
                                  gtk_paned_get_position (GTK_PANED (child)));
293 294 295 296 297 298
            else
                g_value_set_uint (value, 0);
            break;
        default:
            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
            break;
299 300 301
    }
}

302
static void
303
gdl_dock_paned_destroy (GtkWidget *object)
304
{
305
    GdlDockItem *item = GDL_DOCK_ITEM (object);
306

307 308
    /* we need to call the virtual first, since in GdlDockDestroy our
       children dock objects are detached */
309 310
    GTK_WIDGET_CLASS (gdl_dock_paned_parent_class)->destroy (object);

311

312
    /* after that we can remove the GtkNotebook */
313
    gdl_dock_item_set_child (item, NULL);
314 315 316 317 318 319
}

static void
gdl_dock_paned_add (GtkContainer *container,
                    GtkWidget    *widget)
{
320 321
    GdlDockItem     *item;
    GdlDockPlacement pos = GDL_DOCK_NONE;
322 323
    GtkPaned        *paned;
    GtkWidget       *child1, *child2;
324

325
    g_return_if_fail (container != NULL && widget != NULL);
326 327 328
    g_return_if_fail (GDL_IS_DOCK_PANED (container));
    g_return_if_fail (GDL_IS_DOCK_ITEM (widget));

329
    item = GDL_DOCK_ITEM (container);
330
    g_return_if_fail (gdl_dock_item_get_child (item) != NULL);
331

332
    paned = GTK_PANED (gdl_dock_item_get_child (item));
333 334 335
    child1 = gtk_paned_get_child1 (paned);
    child2 = gtk_paned_get_child2 (paned);
    g_return_if_fail (!child1 || !child2);
336

337
    if (!child1)
338
        pos = gdl_dock_item_get_orientation (item) == GTK_ORIENTATION_HORIZONTAL ?
339
            GDL_DOCK_LEFT : GDL_DOCK_TOP;
340
    else if (!child2)
341
        pos = gdl_dock_item_get_orientation (item) == GTK_ORIENTATION_HORIZONTAL ?
342
            GDL_DOCK_RIGHT : GDL_DOCK_BOTTOM;
343

344 345 346 347
    if (pos != GDL_DOCK_NONE)
        gdl_dock_object_dock (GDL_DOCK_OBJECT (container),
                              GDL_DOCK_OBJECT (widget),
                              pos, NULL);
348 349 350 351 352 353 354 355
}

static void
gdl_dock_paned_forall (GtkContainer *container,
                       gboolean      include_internals,
                       GtkCallback   callback,
                       gpointer      callback_data)
{
356
    GtkWidget *child;
357 358 359 360 361

    g_return_if_fail (container != NULL);
    g_return_if_fail (GDL_IS_DOCK_PANED (container));
    g_return_if_fail (callback != NULL);

362 363
    if (include_internals) {
        /* use GdlDockItem's forall */
364
        GTK_CONTAINER_CLASS (gdl_dock_paned_parent_class)->forall
365
                           (container, include_internals, callback, callback_data);
366
    }
367
    else {
368 369 370
        child = gdl_dock_item_get_child (GDL_DOCK_ITEM (container));
        if (child)
            gtk_container_foreach (GTK_CONTAINER (child), callback, callback_data);
371 372 373
    }
}

374 375
static GType
gdl_dock_paned_child_type (GtkContainer *container)
376
{
377
    GtkWidget *child = gdl_dock_item_get_child (GDL_DOCK_ITEM (container));
378

379
    if (gtk_container_child_type (GTK_CONTAINER (child)) == G_TYPE_NONE)
380 381 382
        return G_TYPE_NONE;
    else
        return GDL_TYPE_DOCK_ITEM;
383 384 385
}

static void
386 387 388 389
gdl_dock_paned_request_foreach (GdlDockObject *object,
                                gpointer       user_data)
{
    struct {
390
        GtkWidget *parent;
391 392 393 394
        gint            x, y;
        GdlDockRequest *request;
        gboolean        may_dock;
    } *data = user_data;
395 396

    gint child_x, child_y;
397 398
    GdlDockRequest my_request;
    gboolean       may_dock;
399

400 401 402
    /* Translate parent coordinate to child coordinate */
    gtk_widget_translate_coordinates (data->parent, GTK_WIDGET (object), data->x, data->y, &child_x, &child_y);

403
    my_request = *data->request;
404
    may_dock = gdl_dock_object_dock_request (object, child_x, child_y, &my_request);
405
    if (may_dock) {
406 407
        /* Translate request coordinate back to parent coordinate */
        gtk_widget_translate_coordinates (GTK_WIDGET (object), data->parent, my_request.rect.x, my_request.rect.y, &my_request.rect.x, &my_request.rect.y);
408 409
        data->may_dock = TRUE;
        *data->request = my_request;
410 411 412 413
    }
}

static gboolean
414
gdl_dock_paned_dock_request (GdlDockObject  *object,
415
                             gint            x,
416
                             gint            y,
417
                             GdlDockRequest *request)
418
{
419 420 421
    GdlDockItem        *item;
    guint               bw;
    gint                rel_x, rel_y;
422
    GtkAllocation       alloc;
423 424
    gboolean            may_dock = FALSE;
    GdlDockRequest      my_request;
425

426
    g_return_val_if_fail (GDL_IS_DOCK_ITEM (object), FALSE);
427

428
    /* we get (x,y) in our allocation coordinates system */
429

430
    item = GDL_DOCK_ITEM (object);
431

432
    /* Get item's allocation. */
433
    gtk_widget_get_allocation (GTK_WIDGET (object), &alloc);
434
    bw = gtk_container_get_border_width (GTK_CONTAINER (object));
435

436
    /* Get coordinates relative to our window. */
437 438
    rel_x = x - alloc.x;
    rel_y = y - alloc.y;
439

440 441
    if (request)
        my_request = *request;
442

443
    /* Check if coordinates are inside the widget. */
444 445
    if (rel_x > 0 && rel_x < alloc.width &&
        rel_y > 0 && rel_y < alloc.height) {
446 447
        GtkRequisition my, other;
        gint divider = -1;
448

449 450 451 452 453 454 455 456 457
        gdl_dock_item_preferred_size (GDL_DOCK_ITEM (my_request.applicant), &other);
        gdl_dock_item_preferred_size (GDL_DOCK_ITEM (object), &my);

        /* It's inside our area. */
        may_dock = TRUE;

	/* Set docking indicator rectangle to the widget size. */
        my_request.rect.x = bw;
        my_request.rect.y = bw;
458 459
        my_request.rect.width = alloc.width - 2*bw;
        my_request.rect.height = alloc.height - 2*bw;
460 461 462 463 464 465 466 467

        my_request.target = object;

        /* See if it's in the border_width band. */
        if (rel_x < bw) {
            my_request.position = GDL_DOCK_LEFT;
            my_request.rect.width *= SPLIT_RATIO;
            divider = other.width;
468
        } else if (rel_x > alloc.width - bw) {
469 470 471 472 473 474 475 476
            my_request.position = GDL_DOCK_RIGHT;
            my_request.rect.x += my_request.rect.width * (1 - SPLIT_RATIO);
            my_request.rect.width *= SPLIT_RATIO;
            divider = MAX (0, my.width - other.width);
        } else if (rel_y < bw) {
            my_request.position = GDL_DOCK_TOP;
            my_request.rect.height *= SPLIT_RATIO;
            divider = other.height;
477
        } else if (rel_y > alloc.height - bw) {
478 479 480 481
            my_request.position = GDL_DOCK_BOTTOM;
            my_request.rect.y += my_request.rect.height * (1 - SPLIT_RATIO);
            my_request.rect.height *= SPLIT_RATIO;
            divider = MAX (0, my.height - other.height);
482

483 484
        } else { /* Otherwise try our children. */
            struct {
485
                GtkWidget *parent;
486 487 488 489 490 491
                gint            x, y;
                GdlDockRequest *request;
                gboolean        may_dock;
            } data;

            /* give them coordinates in their allocation system... the
492 493 494 495 496
               GtkPaned has its own window in Gtk 3.1.6, so our children
               allocation coordinates has to be translated to and from
               our window coordinates. It is done in the
               gdl_dock_paned_request_foreach function. */
            data.parent = GTK_WIDGET (object);
497 498 499 500
            data.x = rel_x;
            data.y = rel_y;
            data.request = &my_request;
            data.may_dock = FALSE;
501

502 503 504 505 506 507 508 509 510
            gtk_container_foreach (GTK_CONTAINER (object),
                                   (GtkCallback) gdl_dock_paned_request_foreach,
                                   &data);

            may_dock = data.may_dock;
            if (!may_dock) {
                /* the pointer is on the handle, so snap to top/bottom
                   or left/right */
                may_dock = TRUE;
511
                if (gdl_dock_item_get_orientation (item) == GTK_ORIENTATION_HORIZONTAL) {
512
                    if (rel_y < alloc.height / 2) {
513 514 515 516 517 518 519 520 521 522
                        my_request.position = GDL_DOCK_TOP;
                        my_request.rect.height *= SPLIT_RATIO;
                        divider = other.height;
                    } else {
                        my_request.position = GDL_DOCK_BOTTOM;
                        my_request.rect.y += my_request.rect.height * (1 - SPLIT_RATIO);
                        my_request.rect.height *= SPLIT_RATIO;
                        divider = MAX (0, my.height - other.height);
                    }
                } else {
523
                    if (rel_x < alloc.width / 2) {
524 525 526 527 528 529 530 531 532 533 534 535
                        my_request.position = GDL_DOCK_LEFT;
                        my_request.rect.width *= SPLIT_RATIO;
                        divider = other.width;
                    } else {
                        my_request.position = GDL_DOCK_RIGHT;
                        my_request.rect.x += my_request.rect.width * (1 - SPLIT_RATIO);
                        my_request.rect.width *= SPLIT_RATIO;
                        divider = MAX (0, my.width - other.width);
                    }
                }
            }
        }
536

537 538 539 540 541 542
        if (divider >= 0 && my_request.position != GDL_DOCK_CENTER) {
            if (G_IS_VALUE (&my_request.extra))
                g_value_unset (&my_request.extra);
            g_value_init (&my_request.extra, G_TYPE_UINT);
            g_value_set_uint (&my_request.extra, (guint) divider);
        }
543

544 545 546
        if (may_dock) {
            /* adjust returned coordinates so they are relative to
               our allocation */
547 548
            my_request.rect.x += alloc.x;
            my_request.rect.y += alloc.y;
549
        }
550 551
    }

552 553
    if (may_dock && request)
        *request = my_request;
554

555
    return may_dock;
556 557 558
}

static void
559 560 561 562
gdl_dock_paned_dock (GdlDockObject    *object,
                     GdlDockObject    *requestor,
                     GdlDockPlacement  position,
                     GValue           *other_data)
563
{
564
    GtkPaned *paned;
565
    GtkWidget *child1, *child2;
566
    gboolean  done = FALSE;
567 568 569
    gboolean  hresize = FALSE;
    gboolean  wresize = FALSE;
    gint      temp = 0;
570

571
    g_return_if_fail (GDL_IS_DOCK_PANED (object));
572
    g_return_if_fail (gdl_dock_item_get_child (GDL_DOCK_ITEM (object)) != NULL);
573

574
    paned = GTK_PANED (gdl_dock_item_get_child (GDL_DOCK_ITEM (object)));
575

576 577 578 579 580 581 582 583 584 585
    if (GDL_IS_DOCK_ITEM (requestor)) {
        g_object_get (G_OBJECT (requestor), "preferred_height", &temp, NULL);
        if (temp == -2)
            hresize = TRUE;
        temp = 0;
        g_object_get (G_OBJECT (requestor), "preferred_width", &temp, NULL);
        if (temp == -2)
            wresize = TRUE;
    }

586 587 588
    child1 = gtk_paned_get_child1 (paned);
    child2 = gtk_paned_get_child2 (paned);

589
    /* see if we can dock the item in our paned */
590
    switch (gdl_dock_item_get_orientation (GDL_DOCK_ITEM (object))) {
591
        case GTK_ORIENTATION_HORIZONTAL:
592
            if (!child1 && position == GDL_DOCK_LEFT) {
593
                gtk_paned_pack1 (paned, GTK_WIDGET (requestor), TRUE, TRUE);
594
                done = TRUE;
595
            } else if (!child2 && position == GDL_DOCK_RIGHT) {
596
                gtk_paned_pack2 (paned, GTK_WIDGET (requestor), FALSE, FALSE);
597 598 599 600
                done = TRUE;
            }
            break;
        case GTK_ORIENTATION_VERTICAL:
601
            if (!child1 && position == GDL_DOCK_TOP) {
602
                gtk_paned_pack1 (paned, GTK_WIDGET (requestor), hresize, FALSE);
603
                done = TRUE;
604
            } else if (!child2 && position == GDL_DOCK_BOTTOM) {
605
                gtk_paned_pack2 (paned, GTK_WIDGET (requestor), hresize, FALSE);
606 607 608 609 610 611
                done = TRUE;
            }
            break;
        default:
            break;
    }
612

613 614
    if (!done) {
        /* this will create another paned and reparent us there */
615 616
        GDL_DOCK_OBJECT_CLASS (gdl_dock_paned_parent_class)->dock (object, requestor, position,
                                                                   other_data);
617 618
    }
    else {
619 620
        if (gtk_widget_get_visible (GTK_WIDGET (requestor)))
            gdl_dock_item_show_grip (GDL_DOCK_ITEM (requestor));
621 622 623 624 625 626 627
    }
}

static void
gdl_dock_paned_set_orientation (GdlDockItem    *item,
                                GtkOrientation  orientation)
{
628 629
    GtkPaned    *old_paned = NULL, *new_paned;
    GtkWidget   *child1, *child2;
630

631 632
    g_return_if_fail (GDL_IS_DOCK_PANED (item));

633 634
    if (gdl_dock_item_get_child (item)) {
        old_paned = GTK_PANED (gdl_dock_item_get_child (item));
635
        g_object_ref (old_paned);
636
        gdl_dock_item_set_child (item, NULL);
637
    }
638

639
    gdl_dock_paned_create_child (GDL_DOCK_PANED (item), orientation);
640

641
    if (old_paned) {
642
        new_paned = GTK_PANED (gdl_dock_item_get_child (item));
643 644
        child1 = gtk_paned_get_child1 (old_paned);
        child2 = gtk_paned_get_child2 (old_paned);
645

646 647 648 649 650
        if (child1) {
            g_object_ref (child1);
            gtk_container_remove (GTK_CONTAINER (old_paned), child1);
            gtk_paned_pack1 (new_paned, child1, TRUE, FALSE);
            g_object_unref (child1);
651
        }
652 653 654 655 656
        if (child2) {
            g_object_ref (child2);
            gtk_container_remove (GTK_CONTAINER (old_paned), child2);
            gtk_paned_pack1 (new_paned, child2, TRUE, FALSE);
            g_object_unref (child2);
657
        }
658
    }
659

660
    GDL_DOCK_ITEM_CLASS (gdl_dock_paned_parent_class)->set_orientation (item, orientation);
661 662
}

663
static gboolean
664 665 666
gdl_dock_paned_child_placement (GdlDockObject    *object,
                                GdlDockObject    *child,
                                GdlDockPlacement *placement)
667
{
668 669 670
    GdlDockItem      *item = GDL_DOCK_ITEM (object);
    GtkPaned         *paned;
    GdlDockPlacement  pos = GDL_DOCK_NONE;
671

672 673
    if (gdl_dock_item_get_child (item)) {
        paned = GTK_PANED (gdl_dock_item_get_child (item));
674
        if (GTK_WIDGET (child) == gtk_paned_get_child1 (paned))
675
            pos = gdl_dock_item_get_orientation (item) == GTK_ORIENTATION_HORIZONTAL ?
676
                GDL_DOCK_LEFT : GDL_DOCK_TOP;
677
        else if (GTK_WIDGET (child) == gtk_paned_get_child2 (paned))
678
            pos = gdl_dock_item_get_orientation (item) == GTK_ORIENTATION_HORIZONTAL ?
679
                GDL_DOCK_RIGHT : GDL_DOCK_BOTTOM;
680 681
    }

682 683 684 685
    if (pos != GDL_DOCK_NONE) {
        if (placement)
            *placement = pos;
        return TRUE;
686
    }
687 688
    else
        return FALSE;
689 690 691
}


692
/* ----- Public interface ----- */
693

694 695 696 697
/**
 * gdl_dock_paned_new:
 * @orientation: the pane's orientation.
 *
698 699 700
 * Creates a new manual #GdlDockPaned widget. This function is seldom useful as
 * such widget is normally created and destroyed automatically when needed by
 * the master.
701 702 703
 *
 * Returns: a new #GdlDockPaned.
 */
704 705
GtkWidget *
gdl_dock_paned_new (GtkOrientation orientation)
706
{
707
    GdlDockPaned *paned;
708

709 710
    paned = GDL_DOCK_PANED (g_object_new (GDL_TYPE_DOCK_PANED,
                                          "orientation", orientation, NULL));
711
    gdl_dock_object_set_manual (GDL_DOCK_OBJECT (paned));
712

713
    return GTK_WIDGET (paned);
714 715
}