1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
3
* gdl-dock-placeholder.c - Placeholders for docking items
5
* This file is part of the GNOME Devtools Libraries.
7
* Copyright (C) 2002 Gustavo Gir�ldez <gustavo.giraldez@gmx.net>
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.
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.
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
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,
48
static void gdl_dock_placeholder_get_property (GObject *g_object,
53
static void gdl_dock_placeholder_destroy (GtkObject *object);
55
static void gdl_dock_placeholder_add (GtkContainer *container,
58
static void gdl_dock_placeholder_detach (GdlDockObject *object,
60
static void gdl_dock_placeholder_reduce (GdlDockObject *object);
61
static void gdl_dock_placeholder_dock (GdlDockObject *object,
62
GdlDockObject *requestor,
63
GdlDockPlacement position,
66
static void gdl_dock_placeholder_weak_notify (gpointer data,
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,
81
/* ----- Private variables and data structures ----- */
95
struct _GdlDockPlaceholderPrivate {
96
/* current object this placeholder is pinned to */
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 */
109
/* connected signal handlers */
110
guint host_detach_handler;
111
guint host_dock_handler;
113
/* Window Coordinates if Dock was floating */
120
/* ----- Private interface ----- */
122
GDL_CLASS_BOILERPLATE (GdlDockPlaceholder, gdl_dock_placeholder,
123
GdlDockObject, GDL_TYPE_DOCK_OBJECT);
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;
133
g_object_class = G_OBJECT_CLASS (klass);
134
gtk_object_class = GTK_OBJECT_CLASS (klass);
135
container_class = GTK_CONTAINER_CLASS (klass);
136
object_class = GDL_DOCK_OBJECT_CLASS (klass);
138
g_object_class->get_property = gdl_dock_placeholder_get_property;
139
g_object_class->set_property = gdl_dock_placeholder_set_property;
141
g_object_class_install_property (
142
g_object_class, PROP_STICKY,
143
g_param_spec_boolean ("sticky", _("Sticky"),
144
_("Whether the placeholder will stick to its host or "
145
"move up the hierarchy when the host is redocked"),
147
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
149
g_object_class_install_property (
150
g_object_class, PROP_HOST,
151
g_param_spec_object ("host", _("Host"),
152
_("The dock object this placeholder is attached to"),
153
GDL_TYPE_DOCK_OBJECT,
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,
165
GDL_DOCK_PARAM_EXPORT | GDL_DOCK_PARAM_AFTER));
167
g_object_class_install_property (
168
g_object_class, PROP_WIDTH,
169
g_param_spec_int ("width", _("Width"),
170
_("Width for the widget when it's attached to the placeholder"),
172
G_PARAM_READWRITE | G_PARAM_CONSTRUCT |
173
GDL_DOCK_PARAM_EXPORT));
175
g_object_class_install_property (
176
g_object_class, PROP_HEIGHT,
177
g_param_spec_int ("height", _("Height"),
178
_("Height for the widget when it's attached to the placeholder"),
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"),
188
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
189
g_object_class_install_property (
190
g_object_class, PROP_FLOAT_X,
191
g_param_spec_int ("floatx", _("X-Coordinate"),
192
_("X-Coordinate fow dock when floating"),
194
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
195
GDL_DOCK_PARAM_EXPORT));
196
g_object_class_install_property (
197
g_object_class, PROP_FLOAT_Y,
198
g_param_spec_int ("floaty", _("Y-Coordinate"),
199
_("Y-Coordinate fow dock when floating"),
201
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
202
GDL_DOCK_PARAM_EXPORT));
205
gtk_object_class->destroy = gdl_dock_placeholder_destroy;
206
container_class->add = gdl_dock_placeholder_add;
208
object_class->is_compound = FALSE;
209
object_class->detach = gdl_dock_placeholder_detach;
210
object_class->reduce = gdl_dock_placeholder_reduce;
211
object_class->dock = gdl_dock_placeholder_dock;
212
object_class->present = gdl_dock_placeholder_present;
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);
221
ph->_priv = g_new0 (GdlDockPlaceholderPrivate, 1);
225
gdl_dock_placeholder_set_property (GObject *g_object,
230
GdlDockPlaceholder *ph = GDL_DOCK_PLACEHOLDER (g_object);
235
ph->_priv->sticky = g_value_get_boolean (value);
238
gdl_dock_placeholder_attach (ph, g_value_get_object (value));
240
case PROP_NEXT_PLACEMENT:
242
ph->_priv->placement_stack =
243
g_slist_prepend (ph->_priv->placement_stack,
244
GINT_TO_POINTER (g_value_get_enum (value)));
248
ph->_priv->width = g_value_get_int (value);
251
ph->_priv->height = g_value_get_int (value);
254
ph->_priv->floating = g_value_get_boolean (value);
257
ph->_priv->floatx = g_value_get_int (value);
260
ph->_priv->floaty = g_value_get_int (value);
263
G_OBJECT_WARN_INVALID_PROPERTY_ID (g_object, prop_id, pspec);
269
gdl_dock_placeholder_get_property (GObject *g_object,
274
GdlDockPlaceholder *ph = GDL_DOCK_PLACEHOLDER (g_object);
279
g_value_set_boolean (value, ph->_priv->sticky);
281
g_value_set_boolean (value, FALSE);
285
g_value_set_object (value, ph->_priv->host);
287
g_value_set_object (value, NULL);
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);
293
g_value_set_enum (value, GDL_DOCK_CENTER);
296
g_value_set_int (value, ph->_priv->width);
299
g_value_set_int (value, ph->_priv->height);
302
g_value_set_boolean (value, ph->_priv->floating);
305
g_value_set_int (value, ph->_priv->floatx);
308
g_value_set_int (value, ph->_priv->floaty);
311
G_OBJECT_WARN_INVALID_PROPERTY_ID (g_object, prop_id, pspec);
317
gdl_dock_placeholder_destroy (GtkObject *object)
319
GdlDockPlaceholder *ph = GDL_DOCK_PLACEHOLDER (object);
323
gdl_dock_placeholder_detach (GDL_DOCK_OBJECT (object), FALSE);
328
GDL_CALL_PARENT (GTK_OBJECT_CLASS, destroy, (object));
332
gdl_dock_placeholder_add (GtkContainer *container,
335
GdlDockPlaceholder *ph;
336
GdlDockPlacement pos = GDL_DOCK_CENTER; /* default position */
338
g_return_if_fail (GDL_IS_DOCK_PLACEHOLDER (container));
339
g_return_if_fail (GDL_IS_DOCK_ITEM (widget));
341
ph = GDL_DOCK_PLACEHOLDER (container);
342
if (ph->_priv->placement_stack)
343
pos = (GdlDockPlacement) ph->_priv->placement_stack->data;
345
gdl_dock_object_dock (GDL_DOCK_OBJECT (ph), GDL_DOCK_OBJECT (widget),
350
gdl_dock_placeholder_detach (GdlDockObject *object,
353
GdlDockPlaceholder *ph = GDL_DOCK_PLACEHOLDER (object);
355
/* disconnect handlers */
356
disconnect_host (ph);
358
/* free the placement stack */
359
g_slist_free (ph->_priv->placement_stack);
360
ph->_priv->placement_stack = NULL;
362
GDL_DOCK_OBJECT_UNSET_FLAGS (object, GDL_DOCK_ATTACHED);
366
gdl_dock_placeholder_reduce (GdlDockObject *object)
368
/* placeholders are not reduced */
373
find_biggest_dock_item (GtkContainer *container, GtkWidget **biggest_child,
374
gint *biggest_child_area)
376
GList *children, *child;
378
children = gtk_container_get_children (GTK_CONTAINER (container));
382
GtkWidget *child_widget;
384
child_widget = GTK_WIDGET (child->data);
386
if (gdl_dock_object_is_compound (GDL_DOCK_OBJECT(child_widget))) {
387
find_biggest_dock_item (GTK_CONTAINER (child_widget),
388
biggest_child, biggest_child_area);
389
child = g_list_next (child);
392
area = child_widget->allocation.width * child_widget->allocation.height;
394
if (area > *biggest_child_area) {
395
*biggest_child_area = area;
396
*biggest_child = child_widget;
398
child = g_list_next (child);
403
attempt_to_dock_on_host (GdlDockPlaceholder *ph, GdlDockObject *host,
404
GdlDockObject *requestor, GdlDockPlacement placement,
407
GdlDockObject *parent;
408
gint host_width = GTK_WIDGET (host)->allocation.width;
409
gint host_height = GTK_WIDGET (host)->allocation.height;
411
if (placement != GDL_DOCK_CENTER || !GDL_IS_DOCK_PANED (host)) {
412
/* we simply act as a proxy for our host */
413
gdl_dock_object_dock (host, requestor,
414
placement, other_data);
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.
420
GtkWidget *biggest_child = NULL;
421
gint biggest_child_area = 0;
423
find_biggest_dock_item (GTK_CONTAINER (host), &biggest_child,
424
&biggest_child_area);
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);
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);
438
parent = gdl_dock_object_get_parent_object (requestor);
440
/* Restore dock item's dimention */
443
if (ph->_priv->width > 0) {
444
g_object_set (G_OBJECT (parent), "position",
445
ph->_priv->width, NULL);
449
if (ph->_priv->width > 0) {
450
gint complementary_width = host_width - ph->_priv->width;
452
if (complementary_width > 0)
453
g_object_set (G_OBJECT (parent), "position",
454
complementary_width, NULL);
458
if (ph->_priv->height > 0) {
459
g_object_set (G_OBJECT (parent), "position",
460
ph->_priv->height, NULL);
463
case GDL_DOCK_BOTTOM:
464
if (ph->_priv->height > 0) {
465
gint complementary_height = host_height - ph->_priv->height;
467
if (complementary_height > 0)
468
g_object_set (G_OBJECT (parent), "position",
469
complementary_height, NULL);
479
gdl_dock_placeholder_dock (GdlDockObject *object,
480
GdlDockObject *requestor,
481
GdlDockPlacement position,
484
GdlDockPlaceholder *ph = GDL_DOCK_PLACEHOLDER (object);
486
if (ph->_priv->host) {
487
attempt_to_dock_on_host (ph, ph->_priv->host, requestor,
488
position, other_data);
491
GdlDockObject *toplevel;
493
if (!gdl_dock_object_is_bound (GDL_DOCK_OBJECT (ph))) {
494
g_warning (_("Attempt to dock a dock object to an unbound placeholder"));
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);
505
#ifdef PLACEHOLDER_DEBUG
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;
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);
523
g_message ("%s", message->str);
525
g_string_free (message, TRUE);
526
g_type_class_unref (enum_class);
531
gdl_dock_placeholder_present (GdlDockObject *object,
532
GdlDockObject *child)
538
/* ----- Public interface ----- */
541
gdl_dock_placeholder_new (gchar *name,
542
GdlDockObject *object,
543
GdlDockPlacement position,
546
GdlDockPlaceholder *ph;
548
ph = GDL_DOCK_PLACEHOLDER (g_object_new (GDL_TYPE_DOCK_PLACEHOLDER,
552
GDL_DOCK_OBJECT_UNSET_FLAGS (ph, GDL_DOCK_AUTOMATIC);
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);
564
/* try a recursion */
568
return GTK_WIDGET (ph);
572
gdl_dock_placeholder_weak_notify (gpointer data,
575
GdlDockPlaceholder *ph;
577
g_return_if_fail (data != NULL && GDL_IS_DOCK_PLACEHOLDER (data));
579
ph = GDL_DOCK_PLACEHOLDER (data);
581
#ifdef PLACEHOLDER_DEBUG
582
g_message ("The placeholder just lost its host, ph = %p", ph);
585
/* we shouldn't get here, so perform an emergency detach. instead
586
we should have gotten a detach signal from our host */
587
ph->_priv->host = NULL;
589
/* We didn't get a detach signal from the host. Detach from the
590
supposedly dead host (consequently attaching to the controller) */
592
detach_cb (NULL, TRUE, data);
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);
602
detach_cb (GdlDockObject *object,
606
GdlDockPlaceholder *ph;
607
GdlDockObject *new_host, *obj;
609
g_return_if_fail (user_data != NULL && GDL_IS_DOCK_PLACEHOLDER (user_data));
611
/* we go up in the hierarchy and we store the hinted placement in
612
* the placement stack so we can rebuild the docking layout later
613
* when we get the host's dock signal. */
615
ph = GDL_DOCK_PLACEHOLDER (user_data);
616
obj = ph->_priv->host;
618
g_warning (_("Got a detach signal from an object (%p) who is not "
619
"our host %p"), object, ph->_priv->host);
623
/* skip sticky objects */
624
if (ph->_priv->sticky)
628
/* go up in the hierarchy */
629
new_host = gdl_dock_object_get_parent_object (obj);
631
/* Detaching from the dead host */
635
GdlDockPlacement pos = GDL_DOCK_NONE;
637
/* get placement hint from the new host */
638
if (gdl_dock_object_child_placement (new_host, obj, &pos)) {
639
ph->_priv->placement_stack = g_slist_prepend (
640
ph->_priv->placement_stack, (gpointer) pos);
643
g_warning (_("Something weird happened while getting the child "
644
"placement for %p from parent %p"), obj, new_host);
647
if (!GDL_DOCK_OBJECT_IN_DETACH (new_host))
648
/* we found a "stable" dock object */
652
new_host = gdl_dock_object_get_parent_object (obj);
655
/* disconnect host */
656
disconnect_host (ph);
659
#ifdef PLACEHOLDER_DEBUG
660
g_message ("Detaching from the toplevel. Assignaing to controller");
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));
667
ph->_priv->placement_stack = g_slist_prepend (
668
ph->_priv->placement_stack, (gpointer) GDL_DOCK_FLOATING);
672
connect_host (ph, new_host);
674
#ifdef PLACEHOLDER_DEBUG
675
print_placement_stack (ph);
681
* @ph: placeholder object
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
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;
699
GdlDockObject *host = ph->_priv->host;
701
children = gtk_container_get_children (GTK_CONTAINER (host));
702
for (l = children; l; l = l->next) {
704
gdl_dock_object_child_placement (GDL_DOCK_OBJECT (host),
705
GDL_DOCK_OBJECT (l->data),
707
if (pos == stack_pos) {
708
/* remove the stack position */
709
ph->_priv->placement_stack =
710
g_slist_remove_link (ph->_priv->placement_stack,
711
ph->_priv->placement_stack);
713
/* connect to the new host */
714
disconnect_host (ph);
715
connect_host (ph, GDL_DOCK_OBJECT (l->data));
718
if (!GDL_DOCK_OBJECT_IN_REFLOW (l->data))
724
g_list_free (children);
729
dock_cb (GdlDockObject *object,
730
GdlDockObject *requestor,
731
GdlDockPlacement position,
735
GdlDockPlacement pos = GDL_DOCK_NONE;
736
GdlDockPlaceholder *ph;
738
g_return_if_fail (user_data != NULL && GDL_IS_DOCK_PLACEHOLDER (user_data));
739
ph = GDL_DOCK_PLACEHOLDER (user_data);
740
g_return_if_fail (ph->_priv->host == object);
742
/* see if the given position is compatible for the stack's top
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 */
753
#ifdef PLACEHOLDER_DEBUG
754
print_placement_stack (ph);
759
disconnect_host (GdlDockPlaceholder *ph)
761
if (!ph->_priv->host)
764
if (ph->_priv->host_detach_handler)
765
g_signal_handler_disconnect (ph->_priv->host, ph->_priv->host_detach_handler);
766
if (ph->_priv->host_dock_handler)
767
g_signal_handler_disconnect (ph->_priv->host, ph->_priv->host_dock_handler);
768
ph->_priv->host_detach_handler = 0;
769
ph->_priv->host_dock_handler = 0;
771
/* remove weak ref to object */
772
g_object_weak_unref (G_OBJECT (ph->_priv->host),
773
gdl_dock_placeholder_weak_notify, ph);
774
ph->_priv->host = NULL;
776
#ifdef PLACEHOLDER_DEBUG
777
g_message ("Host just disconnected!, ph = %p", ph);
782
connect_host (GdlDockPlaceholder *ph,
783
GdlDockObject *new_host)
786
disconnect_host (ph);
788
ph->_priv->host = new_host;
789
g_object_weak_ref (G_OBJECT (ph->_priv->host),
790
gdl_dock_placeholder_weak_notify, ph);
792
ph->_priv->host_detach_handler =
793
g_signal_connect (ph->_priv->host,
795
(GCallback) detach_cb,
798
ph->_priv->host_dock_handler =
799
g_signal_connect (ph->_priv->host,
804
#ifdef PLACEHOLDER_DEBUG
805
g_message ("Host just connected!, ph = %p", ph);
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);
818
if (!gdl_dock_object_is_bound (GDL_DOCK_OBJECT (ph)))
819
gdl_dock_object_bind (GDL_DOCK_OBJECT (ph), object->master);
821
g_return_if_fail (GDL_DOCK_OBJECT (ph)->master == object->master);
823
gdl_dock_object_freeze (GDL_DOCK_OBJECT (ph));
825
/* detach from previous host first */
827
gdl_dock_object_detach (GDL_DOCK_OBJECT (ph), FALSE);
829
connect_host (ph, object);
831
GDL_DOCK_OBJECT_SET_FLAGS (ph, GDL_DOCK_ATTACHED);
833
gdl_dock_object_thaw (GDL_DOCK_OBJECT (ph));