Code

d9f805ffe1b3eaafb0099488629dfc763a1fdaab
[inkscape.git] / src / libgdl / gdl-dock-item.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 
2  *
3  * gdl-dock-item.c
4  *
5  * Author: Gustavo Giráldez <gustavo.giraldez@gmx.net>
6  *         Naba Kumar  <naba@gnome.org>
7  *
8  * Based on GnomeDockItem/BonoboDockItem.  Original copyright notice follows.
9  *
10  * Copyright (C) 1998 Ettore Perazzoli
11  * Copyright (C) 1998 Elliot Lee
12  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald 
13  * All rights reserved.
14  *
15  * This library is free software; you can redistribute it and/or
16  * modify it under the terms of the GNU Library General Public
17  * License as published by the Free Software Foundation; either
18  * version 2 of the License, or (at your option) any later version.
19  *
20  * This library is distributed in the hope that it will be useful,
21  * but WITHOUT ANY WARRANTY; without even the implied warranty of
22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
23  * Library General Public License for more details.
24  *
25  * You should have received a copy of the GNU Library General Public
26  * License along with this library; if not, write to the
27  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
28  * Boston, MA 02111-1307, USA.
29  */
32 #ifdef HAVE_CONFIG_H
33 #include <config.h>
34 #endif
36 #include "gdl-i18n.h"
37 #include <string.h>
38 #include <gdk/gdkkeysyms.h>
40 #include "gdl-tools.h"
41 #include "gdl-dock.h"
42 #include "gdl-dock-item.h"
43 #include "gdl-dock-item-grip.h"
44 #include "gdl-dock-notebook.h"
45 #include "gdl-dock-paned.h"
46 #include "gdl-dock-tablabel.h"
47 #include "gdl-dock-placeholder.h"
48 #include "gdl-dock-master.h"
49 #include "libgdltypebuiltins.h"
50 #include "libgdlmarshal.h"
52 #define NEW_DOCK_ITEM_RATIO 0.3
54 /* ----- Private prototypes ----- */
56 static void  gdl_dock_item_class_init    (GdlDockItemClass *class);
57 static void  gdl_dock_item_instance_init (GdlDockItem *item);
59 static GObject *gdl_dock_item_constructor (GType                  type,
60                                            guint                  n_construct_properties,
61                                            GObjectConstructParam *construct_param);
63 static void  gdl_dock_item_set_property  (GObject      *object,
64                                           guint         prop_id,
65                                           const GValue *value,
66                                           GParamSpec   *pspec);
67 static void  gdl_dock_item_get_property  (GObject      *object,
68                                           guint         prop_id,
69                                           GValue       *value,
70                                           GParamSpec   *pspec);
72 static void  gdl_dock_item_destroy       (GtkObject *object);
74 static void  gdl_dock_item_add           (GtkContainer *container,
75                                           GtkWidget    *widget);
76 static void  gdl_dock_item_remove        (GtkContainer *container,
77                                           GtkWidget    *widget);
78 static void  gdl_dock_item_forall        (GtkContainer *container,
79                                           gboolean      include_internals,
80                                           GtkCallback   callback,
81                                           gpointer      callback_data);
82 static GtkType gdl_dock_item_child_type  (GtkContainer *container);
84 static void  gdl_dock_item_size_request  (GtkWidget *widget,
85                                           GtkRequisition *requisition);
86 static void  gdl_dock_item_size_allocate (GtkWidget *widget,
87                                           GtkAllocation *allocation);
88 static void  gdl_dock_item_map           (GtkWidget *widget);
89 static void  gdl_dock_item_unmap         (GtkWidget *widget);
90 static void  gdl_dock_item_realize       (GtkWidget *widget);
91 static void  gdl_dock_item_style_set     (GtkWidget *widget,
92                                           GtkStyle  *previous_style);
93 static gint  gdl_dock_item_expose        (GtkWidget *widget,
94                                           GdkEventExpose *event);
96 static gint  gdl_dock_item_button_changed (GtkWidget *widget,
97                                            GdkEventButton *event);
98 static gint  gdl_dock_item_motion         (GtkWidget *widget,
99                                            GdkEventMotion *event);
100 static gboolean  gdl_dock_item_key_press  (GtkWidget *widget,
101                                            GdkEventKey *event);
103 static gboolean gdl_dock_item_dock_request (GdlDockObject    *object,
104                                             gint              x,
105                                             gint              y,
106                                             GdlDockRequest   *request);
107 static void     gdl_dock_item_dock         (GdlDockObject    *object,
108                                             GdlDockObject    *requestor,
109                                             GdlDockPlacement  position,
110                                             GValue           *other_data);
112 static void  gdl_dock_item_popup_menu    (GdlDockItem *item, 
113                                           guint        button,
114                                           guint32      time);
115 static void  gdl_dock_item_drag_start    (GdlDockItem *item);
116 static void  gdl_dock_item_drag_end      (GdlDockItem *item,
117                                           gboolean     cancel);
119 static void  gdl_dock_item_tab_button    (GtkWidget      *widget,
120                                           GdkEventButton *event,
121                                           gpointer        data);
122                                           
123 static void  gdl_dock_item_hide_cb       (GtkWidget   *widget,
124                                           GdlDockItem *item);
126 static void  gdl_dock_item_lock_cb       (GtkWidget   *widget,
127                                           GdlDockItem *item);
129 static void  gdl_dock_item_unlock_cb     (GtkWidget   *widget,
130                                           GdlDockItem *item);
132 static void  gdl_dock_item_showhide_grip (GdlDockItem *item);
134 static void  gdl_dock_item_real_set_orientation (GdlDockItem    *item,
135                                                  GtkOrientation  orientation);
137 static void gdl_dock_param_export_gtk_orientation (const GValue *src,
138                                                    GValue       *dst);
139 static void gdl_dock_param_import_gtk_orientation (const GValue *src,
140                                                    GValue       *dst);
144 /* ----- Class variables and definitions ----- */
146 enum {
147     PROP_0,
148     PROP_ORIENTATION,
149     PROP_RESIZE,
150     PROP_BEHAVIOR,
151     PROP_LOCKED,
152     PROP_PREFERRED_WIDTH,
153     PROP_PREFERRED_HEIGHT
154 };
156 enum {
157     DOCK_DRAG_BEGIN,
158     DOCK_DRAG_MOTION,
159     DOCK_DRAG_END,
160     LAST_SIGNAL
161 };
163 static guint gdl_dock_item_signals [LAST_SIGNAL] = { 0 };
165 #define GDL_DOCK_ITEM_GRIP_SHOWN(item) \
166     (GDL_DOCK_ITEM_HAS_GRIP (item)) 
168 struct _GdlDockItemPrivate {
169     GtkWidget *menu;
171     gboolean   grip_shown;
172     GtkWidget *grip;
173     guint      grip_size;
174     
175     GtkWidget *tab_label;
177     gint       preferred_width;
178     gint       preferred_height;
180     GdlDockPlaceholder *ph;
182     gint       start_x, start_y;
183 };
185 /* FIXME: implement the rest of the behaviors */
187 #define SPLIT_RATIO  0.4
190 /* ----- Private functions ----- */
192 GDL_CLASS_BOILERPLATE (GdlDockItem, gdl_dock_item, GdlDockObject, GDL_TYPE_DOCK_OBJECT);
194 static void
195 gdl_dock_item_class_init (GdlDockItemClass *klass)
197     static gboolean style_initialized = FALSE;
198     
199     GObjectClass       *g_object_class;
200     GtkObjectClass     *gtk_object_class;
201     GtkWidgetClass     *widget_class;
202     GtkContainerClass  *container_class;
203     GdlDockObjectClass *object_class;
204     
205     g_object_class = G_OBJECT_CLASS (klass);
206     gtk_object_class = GTK_OBJECT_CLASS (klass);
207     widget_class = GTK_WIDGET_CLASS (klass);
208     container_class = GTK_CONTAINER_CLASS (klass);
209     object_class = GDL_DOCK_OBJECT_CLASS (klass);
211     g_object_class->constructor = gdl_dock_item_constructor;
212     g_object_class->set_property = gdl_dock_item_set_property;
213     g_object_class->get_property = gdl_dock_item_get_property;
215     gtk_object_class->destroy = gdl_dock_item_destroy;
217     widget_class->realize = gdl_dock_item_realize;
218     widget_class->map = gdl_dock_item_map;
219     widget_class->unmap = gdl_dock_item_unmap;
220     widget_class->size_request = gdl_dock_item_size_request;
221     widget_class->size_allocate = gdl_dock_item_size_allocate;
222     widget_class->style_set = gdl_dock_item_style_set;
223     widget_class->expose_event = gdl_dock_item_expose;
224     widget_class->button_press_event = gdl_dock_item_button_changed;
225     widget_class->button_release_event = gdl_dock_item_button_changed;
226     widget_class->motion_notify_event = gdl_dock_item_motion;
227     widget_class->key_press_event = gdl_dock_item_key_press;
228     
229     container_class->add = gdl_dock_item_add;
230     container_class->remove = gdl_dock_item_remove;
231     container_class->forall = gdl_dock_item_forall;
232     container_class->child_type = gdl_dock_item_child_type;
233     
234     object_class->is_compound = FALSE;
236     object_class->dock_request = gdl_dock_item_dock_request;
237     object_class->dock = gdl_dock_item_dock;
239     /* properties */
241     g_object_class_install_property (
242         g_object_class, PROP_ORIENTATION,
243         g_param_spec_enum ("orientation", _("Orientation"),
244                            _("Orientation of the docking item"),
245                            GTK_TYPE_ORIENTATION,
246                            GTK_ORIENTATION_VERTICAL,
247                            G_PARAM_READWRITE | G_PARAM_CONSTRUCT |
248                            GDL_DOCK_PARAM_EXPORT));
250     /* --- register exporter/importer for GTK_ORIENTATION */
251     g_value_register_transform_func (GTK_TYPE_ORIENTATION, GDL_TYPE_DOCK_PARAM,
252                                      gdl_dock_param_export_gtk_orientation);
253     g_value_register_transform_func (GDL_TYPE_DOCK_PARAM, GTK_TYPE_ORIENTATION,
254                                      gdl_dock_param_import_gtk_orientation);
255     /* --- end of registration */
256     
257     g_object_class_install_property (
258         g_object_class, PROP_RESIZE,
259         g_param_spec_boolean ("resize", _("Resizable"),
260                               _("If set, the dock item can be resized when "
261                                 "docked in a paned"),
262                               TRUE,
263                               G_PARAM_READWRITE));
264                                      
265     g_object_class_install_property (
266         g_object_class, PROP_BEHAVIOR,
267         g_param_spec_flags ("behavior", _("Item behavior"),
268                             _("General behavior for the dock item (i.e. "
269                               "whether it can float, if it's locked, etc.)"),
270                             GDL_TYPE_DOCK_ITEM_BEHAVIOR,
271                             GDL_DOCK_ITEM_BEH_NORMAL,
272                             G_PARAM_READWRITE));
273                                      
274     g_object_class_install_property (
275         g_object_class, PROP_LOCKED,
276         g_param_spec_boolean ("locked", _("Locked"),
277                               _("If set, the dock item cannot be dragged around "
278                                 "and it doesn't show a grip"),
279                               FALSE,
280                               G_PARAM_READWRITE |
281                               GDL_DOCK_PARAM_EXPORT));
283     g_object_class_install_property (
284         g_object_class, PROP_PREFERRED_WIDTH,
285         g_param_spec_int ("preferred-width", _("Preferred width"),
286                           _("Preferred width for the dock item"),
287                           -1, G_MAXINT, -1,
288                           G_PARAM_READWRITE));
290     g_object_class_install_property (
291         g_object_class, PROP_PREFERRED_HEIGHT,
292         g_param_spec_int ("preferred-height", _("Preferred height"),
293                           _("Preferred height for the dock item"),
294                           -1, G_MAXINT, -1,
295                           G_PARAM_READWRITE));
297     /* signals */
298     
299     gdl_dock_item_signals [DOCK_DRAG_BEGIN] = 
300         g_signal_new ("dock-drag-begin",
301                       G_TYPE_FROM_CLASS (klass),
302                       G_SIGNAL_RUN_FIRST,
303                       G_STRUCT_OFFSET (GdlDockItemClass, dock_drag_begin),
304                       NULL, /* accumulator */
305                       NULL, /* accu_data */
306                       gdl_marshal_VOID__VOID,
307                       G_TYPE_NONE, 
308                       0);
310     gdl_dock_item_signals [DOCK_DRAG_MOTION] = 
311         g_signal_new ("dock-drag-motion",
312                       G_TYPE_FROM_CLASS (klass),
313                       G_SIGNAL_RUN_FIRST,
314                       G_STRUCT_OFFSET (GdlDockItemClass, dock_drag_motion),
315                       NULL, /* accumulator */
316                       NULL, /* accu_data */
317                       gdl_marshal_VOID__INT_INT,
318                       G_TYPE_NONE, 
319                       2,
320                       G_TYPE_INT,
321                       G_TYPE_INT);
323     gdl_dock_item_signals [DOCK_DRAG_END] = 
324         g_signal_new ("dock_drag_end",
325                       G_TYPE_FROM_CLASS (klass),
326                       G_SIGNAL_RUN_FIRST,
327                       G_STRUCT_OFFSET (GdlDockItemClass, dock_drag_end),
328                       NULL, /* accumulator */
329                       NULL, /* accu_data */
330                       gdl_marshal_VOID__BOOLEAN,
331                       G_TYPE_NONE, 
332                       1,
333                       G_TYPE_BOOLEAN);
335     klass->has_grip = TRUE;
336     klass->dock_drag_begin = NULL;
337     klass->dock_drag_motion = NULL;
338     klass->dock_drag_end = NULL;
339     klass->set_orientation = gdl_dock_item_real_set_orientation;
341     if (!style_initialized)
342     {
343         style_initialized = TRUE;
344         gtk_rc_parse_string (
345             "style \"gdl-dock-item-default\" {\n"
346             "xthickness = 0\n"
347             "ythickness = 0\n"
348             "}\n"
349             "class \"GdlDockItem\" "
350             "style : gtk \"gdl-dock-item-default\"\n");
351     }
354 static void
355 gdl_dock_item_instance_init (GdlDockItem *item)
357     GTK_WIDGET_UNSET_FLAGS (GTK_WIDGET (item), GTK_NO_WINDOW);
359     item->child = NULL;
360     
361     item->orientation = GTK_ORIENTATION_VERTICAL;
362     item->behavior = GDL_DOCK_ITEM_BEH_NORMAL;
364     item->resize = TRUE;
366     item->dragoff_x = item->dragoff_y = 0;
368     item->_priv = g_new0 (GdlDockItemPrivate, 1);
369     item->_priv->menu = NULL;
371     item->_priv->preferred_width = item->_priv->preferred_height = -1;
372     item->_priv->tab_label = NULL;
374     item->_priv->ph = NULL;
377 static GObject *
378 gdl_dock_item_constructor (GType                  type,
379                            guint                  n_construct_properties,
380                            GObjectConstructParam *construct_param)
382     GObject *g_object;
383     
384     g_object = GDL_CALL_PARENT_WITH_DEFAULT (G_OBJECT_CLASS, 
385                                                constructor, 
386                                                (type,
387                                                 n_construct_properties,
388                                                 construct_param),
389                                                NULL);
390     if (g_object) {
391         GdlDockItem *item = GDL_DOCK_ITEM (g_object);
393         if (GDL_DOCK_ITEM_HAS_GRIP (item)) {
394             item->_priv->grip_shown = TRUE;
395             item->_priv->grip = gdl_dock_item_grip_new (item);
396             gtk_widget_set_parent (item->_priv->grip, GTK_WIDGET (item));
397             gtk_widget_show (item->_priv->grip);
398         }
399         else {
400             item->_priv->grip_shown = FALSE;
401         }
402     }
404     return g_object;
407 static void
408 gdl_dock_item_set_property  (GObject      *g_object,
409                              guint         prop_id,
410                              const GValue *value,
411                              GParamSpec   *pspec)
413     GdlDockItem *item = GDL_DOCK_ITEM (g_object);
415     switch (prop_id) {
416         case PROP_ORIENTATION:
417             gdl_dock_item_set_orientation (item, g_value_get_enum (value));
418             break;
419         case PROP_RESIZE:
420             item->resize = g_value_get_boolean (value);
421             gtk_widget_queue_resize (GTK_WIDGET (item));
422             break;
423         case PROP_BEHAVIOR:
424         {
425             GdlDockItemBehavior old_beh = item->behavior;
426             item->behavior = g_value_get_flags (value);
428             if ((old_beh ^ item->behavior) & GDL_DOCK_ITEM_BEH_LOCKED) {
429                 if (GDL_DOCK_OBJECT_GET_MASTER (item))
430                     g_signal_emit_by_name (GDL_DOCK_OBJECT_GET_MASTER (item),
431                                            "layout-changed");
432                 g_object_notify (g_object, "locked");
433                 gdl_dock_item_showhide_grip (item);
434             }
435             
436             break;
437         }
438         case PROP_LOCKED:
439         {
440             GdlDockItemBehavior old_beh = item->behavior;
442             if (g_value_get_boolean (value))
443                 item->behavior |= GDL_DOCK_ITEM_BEH_LOCKED;
444             else
445                 item->behavior &= ~GDL_DOCK_ITEM_BEH_LOCKED;
447             if (old_beh ^ item->behavior) {
448                 gdl_dock_item_showhide_grip (item);
449                 g_object_notify (g_object, "behavior");
451                 if (GDL_DOCK_OBJECT_GET_MASTER (item))
452                     g_signal_emit_by_name (GDL_DOCK_OBJECT_GET_MASTER (item),
453                                            "layout-changed");
454             }
455             break;
456         }
457         case PROP_PREFERRED_WIDTH:
458             item->_priv->preferred_width = g_value_get_int (value);
459             break;
460         case PROP_PREFERRED_HEIGHT:
461             item->_priv->preferred_height = g_value_get_int (value);
462             break;
463         default:
464             G_OBJECT_WARN_INVALID_PROPERTY_ID (g_object, prop_id, pspec);
465             break;
466     }
469 static void
470 gdl_dock_item_get_property  (GObject      *g_object,
471                              guint         prop_id,
472                              GValue       *value,
473                              GParamSpec   *pspec)
475     GdlDockItem *item = GDL_DOCK_ITEM (g_object);
476     
477     switch (prop_id) {
478         case PROP_ORIENTATION:
479             g_value_set_enum (value, item->orientation);
480             break;
481         case PROP_RESIZE:
482             g_value_set_boolean (value, item->resize);
483             break;
484         case PROP_BEHAVIOR:
485             g_value_set_flags (value, item->behavior);
486             break;
487         case PROP_LOCKED:
488             g_value_set_boolean (value, !GDL_DOCK_ITEM_NOT_LOCKED (item));
489             break;
490         case PROP_PREFERRED_WIDTH:
491             g_value_set_int (value, item->_priv->preferred_width);
492             break;
493         case PROP_PREFERRED_HEIGHT:
494             g_value_set_int (value, item->_priv->preferred_height);
495             break;
496         default:
497             G_OBJECT_WARN_INVALID_PROPERTY_ID (g_object, prop_id, pspec);
498             break;
499     }
502 static void
503 gdl_dock_item_destroy (GtkObject *object)
505     GdlDockItem *item = GDL_DOCK_ITEM (object);
507     if (item->_priv) {
508         GdlDockItemPrivate *priv = item->_priv;
509         
510         if (priv->tab_label) {
511             gdl_dock_item_set_tablabel (item, NULL);
512         };
513         if (priv->menu) {
514             gtk_menu_detach (GTK_MENU (priv->menu));
515             priv->menu = NULL;
516         };
517         if (priv->grip) {
518             gtk_container_remove (GTK_CONTAINER (item), priv->grip);
519             priv->grip = NULL;
520         }
521         if (priv->ph) {
522             g_object_unref (priv->ph);
523             priv->ph = NULL;
524         }
525         
526         item->_priv = NULL;
527         g_free (priv);
528     }
530     GDL_CALL_PARENT (GTK_OBJECT_CLASS, destroy, (object));
533 static void 
534 gdl_dock_item_add (GtkContainer *container,
535                    GtkWidget    *widget)
537     GdlDockItem *item;
538     
539     g_return_if_fail (GDL_IS_DOCK_ITEM (container));
541     item = GDL_DOCK_ITEM (container);
542     if (GDL_IS_DOCK_OBJECT (widget)) {
543         g_warning (_("You can't add a dock object (%p of type %s) inside a %s. "
544                      "Use a GdlDock or some other compound dock object."),
545                    widget, G_OBJECT_TYPE_NAME (widget), G_OBJECT_TYPE_NAME (item));
546         return;
547     }
549     if (item->child != NULL) {
550         g_warning (_("Attempting to add a widget with type %s to a %s, "
551                      "but it can only contain one widget at a time; "
552                      "it already contains a widget of type %s"),
553                      G_OBJECT_TYPE_NAME (widget),
554                      G_OBJECT_TYPE_NAME (item),
555                      G_OBJECT_TYPE_NAME (item->child));
556         return;
557     }
559     gtk_widget_set_parent (widget, GTK_WIDGET (item));
560     item->child = widget;
563 static void  
564 gdl_dock_item_remove (GtkContainer *container,
565                       GtkWidget    *widget)
567     GdlDockItem *item;
568     gboolean     was_visible;
569     
570     g_return_if_fail (GDL_IS_DOCK_ITEM (container));
571     
572     item = GDL_DOCK_ITEM (container);
573     if (item->_priv && widget == item->_priv->grip) {
574         gboolean grip_was_visible = GTK_WIDGET_VISIBLE (widget);
575         gtk_widget_unparent (widget);
576         item->_priv->grip = NULL;
577         if (grip_was_visible)
578             gtk_widget_queue_resize (GTK_WIDGET (item));
579         return;
580     }
581     
582     if (GDL_DOCK_ITEM_IN_DRAG (item)) {
583         gdl_dock_item_drag_end (item, TRUE);
584     }
585     
586     g_return_if_fail (item->child == widget);
587     
588     was_visible = GTK_WIDGET_VISIBLE (widget);
590     gtk_widget_unparent (widget);
591     item->child = NULL;
592     
593     if (was_visible)
594         gtk_widget_queue_resize (GTK_WIDGET (container));
597 static void
598 gdl_dock_item_forall (GtkContainer *container,
599                       gboolean      include_internals,
600                       GtkCallback   callback,
601                       gpointer      callback_data)
603     GdlDockItem *item = (GdlDockItem *) container;
604     
605     g_return_if_fail (callback != NULL);
606     
607     if (include_internals && item->_priv->grip)
608         (* callback) (item->_priv->grip, callback_data);
609     
610     if (item->child)
611         (* callback) (item->child, callback_data);
614 static GtkType
615 gdl_dock_item_child_type (GtkContainer *container)
617     g_return_val_if_fail (GDL_IS_DOCK_ITEM (container), G_TYPE_NONE);
618     
619     if (!GDL_DOCK_ITEM (container)->child)
620         return GTK_TYPE_WIDGET;
621     else
622         return G_TYPE_NONE;
625 static void
626 gdl_dock_item_size_request (GtkWidget      *widget,
627                             GtkRequisition *requisition)
629     GtkRequisition  child_requisition;
630     GtkRequisition  grip_requisition;
631     GdlDockItem    *item;
633     g_return_if_fail (GDL_IS_DOCK_ITEM (widget));
634     g_return_if_fail (requisition != NULL);
636     item = GDL_DOCK_ITEM (widget);
638     /* If our child is not visible, we still request its size, since
639        we won't have any useful hint for our size otherwise.  */
640     if (item->child)
641         gtk_widget_size_request (item->child, &child_requisition);
642     else {
643         child_requisition.width = 0;
644         child_requisition.height = 0;
645     }
647     if (item->orientation == GTK_ORIENTATION_HORIZONTAL) {
648         if (GDL_DOCK_ITEM_GRIP_SHOWN (item)) {
649             gtk_widget_size_request (item->_priv->grip, &grip_requisition);
650             requisition->width = grip_requisition.width;
651         } else {
652             requisition->width = 0;
653         }
655         if (item->child) {
656             requisition->width += child_requisition.width;
657             requisition->height = child_requisition.height;
658         } else
659             requisition->height = 0;
660     } else {
661         if (GDL_DOCK_ITEM_GRIP_SHOWN (item)) {
662             gtk_widget_size_request (item->_priv->grip, &grip_requisition);
663             requisition->height = grip_requisition.height;
664         } else {
665             requisition->height = 0;
666         }
668         if (item->child) {
669             requisition->width = child_requisition.width;
670             requisition->height += child_requisition.height;
671         } else
672             requisition->width = 0;
673     }
675     requisition->width += (GTK_CONTAINER (widget)->border_width + widget->style->xthickness) * 2;
676     requisition->height += (GTK_CONTAINER (widget)->border_width + widget->style->ythickness) * 2;
678     widget->requisition = *requisition;
681 static void
682 gdl_dock_item_size_allocate (GtkWidget     *widget,
683                              GtkAllocation *allocation)
685     GdlDockItem *item;
686   
687     g_return_if_fail (GDL_IS_DOCK_ITEM (widget));
688     g_return_if_fail (allocation != NULL);
689   
690     item = GDL_DOCK_ITEM (widget);
692     widget->allocation = *allocation;
694     /* Once size is allocated, preferred size is no longer necessary */
695     item->_priv->preferred_height = -1;
696     item->_priv->preferred_width = -1;
697     
698     if (GTK_WIDGET_REALIZED (widget))
699         gdk_window_move_resize (widget->window,
700                                 widget->allocation.x,
701                                 widget->allocation.y,
702                                 widget->allocation.width,
703                                 widget->allocation.height);
705     if (item->child && GTK_WIDGET_VISIBLE (item->child)) {
706         GtkAllocation  child_allocation;
707         int            border_width;
709         border_width = GTK_CONTAINER (widget)->border_width;
711         child_allocation.x = border_width + widget->style->xthickness;
712         child_allocation.y = border_width + widget->style->ythickness;
713         child_allocation.width = allocation->width
714             - 2 * (border_width + widget->style->xthickness);
715         child_allocation.height = allocation->height
716             - 2 * (border_width + widget->style->ythickness);
717         
718         if (GDL_DOCK_ITEM_GRIP_SHOWN (item)) {
719             GtkAllocation grip_alloc = child_allocation;
720             GtkRequisition grip_req;
721             
722             gtk_widget_size_request (item->_priv->grip, &grip_req);
723             
724             if (item->orientation == GTK_ORIENTATION_HORIZONTAL) {
725                 child_allocation.x += grip_req.width;
726                 child_allocation.width -= grip_req.width;
727                 grip_alloc.width = grip_req.width;
728             } else {
729                 child_allocation.y += grip_req.height;
730                 child_allocation.height -= grip_req.height;
731                 grip_alloc.height = grip_req.height;
732             }
733             if (item->_priv->grip)
734                 gtk_widget_size_allocate (item->_priv->grip, &grip_alloc);
735         }
736         /* Allocation can't be negative */
737         if (child_allocation.width < 0)
738             child_allocation.width = 0;
739         if (child_allocation.height < 0)
740             child_allocation.height = 0;
741         gtk_widget_size_allocate (item->child, &child_allocation);
742     }
745 static void
746 gdl_dock_item_map (GtkWidget *widget)
748     GdlDockItem *item;
750     g_return_if_fail (widget != NULL);
751     g_return_if_fail (GDL_IS_DOCK_ITEM (widget));
753     GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
755     item = GDL_DOCK_ITEM (widget);
757     gdk_window_show (widget->window);
759     if (item->child
760         && GTK_WIDGET_VISIBLE (item->child)
761         && !GTK_WIDGET_MAPPED (item->child))
762         gtk_widget_map (item->child);
764     if (item->_priv->grip
765         && GTK_WIDGET_VISIBLE (item->_priv->grip)
766         && !GTK_WIDGET_MAPPED (item->_priv->grip))
767         gtk_widget_map (item->_priv->grip);
770 static void
771 gdl_dock_item_unmap (GtkWidget *widget)
773     GdlDockItem *item;
775     g_return_if_fail (widget != NULL);
776     g_return_if_fail (GDL_IS_DOCK_ITEM (widget));
778     GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
779     
780     item = GDL_DOCK_ITEM (widget);
782     gdk_window_hide (widget->window);
784     if (item->_priv->grip)
785         gtk_widget_unmap (item->_priv->grip);
788 static void
789 gdl_dock_item_realize (GtkWidget *widget)
791     GdkWindowAttr  attributes;
792     gint           attributes_mask;
793     GdlDockItem   *item;
795     g_return_if_fail (widget != NULL);
796     g_return_if_fail (GDL_IS_DOCK_ITEM (widget));
798     item = GDL_DOCK_ITEM (widget);
800     GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
802     /* widget window */
803     attributes.x = widget->allocation.x;
804     attributes.y = widget->allocation.y;
805     attributes.width = widget->allocation.width;
806     attributes.height = widget->allocation.height;
807     attributes.window_type = GDK_WINDOW_CHILD;
808     attributes.wclass = GDK_INPUT_OUTPUT;
809     attributes.visual = gtk_widget_get_visual (widget);
810     attributes.colormap = gtk_widget_get_colormap (widget);
811     attributes.event_mask = (gtk_widget_get_events (widget) |
812                              GDK_EXPOSURE_MASK |
813                              GDK_BUTTON1_MOTION_MASK |
814                              GDK_BUTTON_PRESS_MASK |
815                              GDK_BUTTON_RELEASE_MASK);
816     attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
817     widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), 
818                                      &attributes, attributes_mask);
819     gdk_window_set_user_data (widget->window, widget);
820   
821     widget->style = gtk_style_attach (widget->style, widget->window);
822     gtk_style_set_background (widget->style, widget->window, 
823                               GTK_WIDGET_STATE (item));
824     gdk_window_set_back_pixmap (widget->window, NULL, TRUE);
826     if (item->child)
827         gtk_widget_set_parent_window (item->child, widget->window);
828     
829     if (item->_priv->grip)
830         gtk_widget_set_parent_window (item->_priv->grip, widget->window);
833 static void
834 gdl_dock_item_style_set (GtkWidget *widget,
835                          GtkStyle  *previous_style)
837     g_return_if_fail (widget != NULL);
838     g_return_if_fail (GDL_IS_DOCK_ITEM (widget));
840     if (GTK_WIDGET_REALIZED (widget) && !GTK_WIDGET_NO_WINDOW (widget)) {
841         gtk_style_set_background (widget->style, widget->window,
842                                   widget->state);
843         if (GTK_WIDGET_DRAWABLE (widget))
844             gdk_window_clear (widget->window);
845     }
848 static void
849 gdl_dock_item_paint (GtkWidget      *widget,
850                      GdkEventExpose *event)
852     GdlDockItem  *item;
854     item = GDL_DOCK_ITEM (widget);
856     gtk_paint_box (widget->style,
857                    widget->window,
858                    GTK_WIDGET_STATE (widget),
859                    GTK_SHADOW_NONE,
860                    &event->area, widget,
861                    "dockitem",
862                    0, 0, -1, -1);
865 static gint
866 gdl_dock_item_expose (GtkWidget      *widget,
867                       GdkEventExpose *event)
869     g_return_val_if_fail (widget != NULL, FALSE);
870     g_return_val_if_fail (GDL_IS_DOCK_ITEM (widget), FALSE);
871     g_return_val_if_fail (event != NULL, FALSE);
873     if (GTK_WIDGET_DRAWABLE (widget) && event->window == widget->window) {
874         gdl_dock_item_paint (widget, event);
875         GDL_CALL_PARENT_GBOOLEAN(GTK_WIDGET_CLASS, expose_event, (widget,event));
876     }
877   
878     return FALSE;
881 #define EVENT_IN_GRIP_EVENT_WINDOW(ev,gr) \
882     ((gr) != NULL && (ev)->window == GDL_DOCK_ITEM_GRIP (gr)->title_window)
884 #define EVENT_IN_TABLABEL_EVENT_WINDOW(ev,tl) \
885     ((tl) != NULL && (ev)->window == GDL_DOCK_TABLABEL (tl)->event_window)
887 static gint
888 gdl_dock_item_button_changed (GtkWidget      *widget,
889                               GdkEventButton *event)
891     GdlDockItem *item;
892     gboolean     locked;
893     gboolean     event_handled;
894     gboolean     in_handle;
895     GdkCursor   *cursor;
896   
897     g_return_val_if_fail (widget != NULL, FALSE);
898     g_return_val_if_fail (GDL_IS_DOCK_ITEM (widget), FALSE);
899     g_return_val_if_fail (event != NULL, FALSE);
900     
901     item = GDL_DOCK_ITEM (widget);
903     if (!EVENT_IN_GRIP_EVENT_WINDOW (event, item->_priv->grip))
904         return FALSE;
905     
906     locked = !GDL_DOCK_ITEM_NOT_LOCKED (item);
908     event_handled = FALSE;
910     /* Check if user clicked on the drag handle. */      
911     switch (item->orientation) {
912     case GTK_ORIENTATION_HORIZONTAL:
913         in_handle = event->x < item->_priv->grip->allocation.width;
914         break;
915     case GTK_ORIENTATION_VERTICAL:
916         in_handle = event->y < item->_priv->grip->allocation.height;
917         break;
918     default:
919         in_handle = FALSE;
920         break;
921     }
923     /* Left mousebutton click on dockitem. */
924     if (!locked && event->button == 1 && event->type == GDK_BUTTON_PRESS) {
925         /* Set in_drag flag, grab pointer and call begin drag operation. */      
926         if (in_handle) {
927             item->_priv->start_x = event->x;
928             item->_priv->start_y = event->y;
930             GDL_DOCK_ITEM_SET_FLAGS (item, GDL_DOCK_IN_PREDRAG);
931             
932             cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget),
933                                                  GDK_FLEUR);
934             gdk_window_set_cursor (GDL_DOCK_ITEM_GRIP (item->_priv->grip)->title_window,
935                                    cursor);
936             gdk_cursor_unref (cursor);
937         
938             event_handled = TRUE;
939         };
940         
941     } else if (!locked &&event->type == GDK_BUTTON_RELEASE && event->button == 1) {
942         if (GDL_DOCK_ITEM_IN_DRAG (item)) {
943             /* User dropped widget somewhere. */
944             gdl_dock_item_drag_end (item, FALSE);
945             event_handled = TRUE;
946         }
947         else if (GDL_DOCK_ITEM_IN_PREDRAG (item)) {
948             GDL_DOCK_ITEM_UNSET_FLAGS (item, GDL_DOCK_IN_PREDRAG);
949             event_handled = TRUE;
950         }
952         /* we check the window since if the item was redocked it's
953            been unrealized and maybe it's not realized again yet */
954         if (GDL_DOCK_ITEM_GRIP (item->_priv->grip)->title_window) {
955             cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget),
956                                                  GDK_HAND2);
957             gdk_window_set_cursor (GDL_DOCK_ITEM_GRIP (item->_priv->grip)->title_window,
958                                    cursor);
959             gdk_cursor_unref (cursor);
960         }
962     } else if (event->button == 3 && event->type == GDK_BUTTON_PRESS && in_handle) {
963         gdl_dock_item_popup_menu (item, event->button, event->time);
964         event_handled = TRUE;           
965     }
967     return event_handled;
970 static gint
971 gdl_dock_item_motion (GtkWidget      *widget,
972                       GdkEventMotion *event)
974     GdlDockItem *item;
975     gint         new_x, new_y;
977     g_return_val_if_fail (widget != NULL, FALSE);
978     g_return_val_if_fail (GDL_IS_DOCK_ITEM (widget), FALSE);
979     g_return_val_if_fail (event != NULL, FALSE);
981     item = GDL_DOCK_ITEM (widget);
983     if (!EVENT_IN_GRIP_EVENT_WINDOW (event, item->_priv->grip))
984         return FALSE;
986     if (GDL_DOCK_ITEM_IN_PREDRAG (item)) {
987         if (gtk_drag_check_threshold (widget,
988                                       item->_priv->start_x,
989                                       item->_priv->start_y,
990                                       event->x,
991                                       event->y)) {
992             GDL_DOCK_ITEM_UNSET_FLAGS (item, GDL_DOCK_IN_PREDRAG);
993             item->dragoff_x = item->_priv->start_x;
994             item->dragoff_y = item->_priv->start_y;
996             gdl_dock_item_drag_start (item);
997         }
998     }
999     
1000     if (!GDL_DOCK_ITEM_IN_DRAG (item))
1001         return FALSE;
1003     new_x = event->x_root;
1004     new_y = event->y_root;
1005     
1006     g_signal_emit (item, gdl_dock_item_signals [DOCK_DRAG_MOTION], 
1007                    0, new_x, new_y);
1009     return TRUE;
1012 static gboolean
1013 gdl_dock_item_key_press (GtkWidget   *widget,
1014                          GdkEventKey *event)
1016     gboolean event_handled = FALSE;
1017     if (GDL_DOCK_ITEM_IN_DRAG (widget)) {
1018         if (event->keyval == GDK_Escape) {
1019             gdl_dock_item_drag_end (GDL_DOCK_ITEM (widget), TRUE);
1020             event_handled = TRUE;
1021         }
1022     }
1024     if (event_handled)
1025         return TRUE;
1026     else
1027         return GDL_CALL_PARENT_WITH_DEFAULT (GTK_WIDGET_CLASS,
1028                                                key_press_event,
1029                                                (widget, event),
1030                                                FALSE);
1033 static gboolean
1034 gdl_dock_item_dock_request (GdlDockObject  *object,
1035                             gint            x,
1036                             gint            y,
1037                             GdlDockRequest *request)
1039     GtkAllocation *alloc;
1040     gint           rel_x, rel_y;
1042     /* we get (x,y) in our allocation coordinates system */
1043     
1044     /* Get item's allocation. */
1045     alloc = &(GTK_WIDGET (object)->allocation);
1046     
1047     /* Get coordinates relative to our window. */
1048     rel_x = x - alloc->x;
1049     rel_y = y - alloc->y;
1051     /* Location is inside. */
1052     if (rel_x > 0 && rel_x < alloc->width &&
1053         rel_y > 0 && rel_y < alloc->height) {
1054         float rx, ry;
1055         GtkRequisition my, other;
1056         gint divider = -1;
1057         
1058         /* this are for calculating the extra docking parameter */
1059         gdl_dock_item_preferred_size (GDL_DOCK_ITEM (request->applicant), &other);
1060         gdl_dock_item_preferred_size (GDL_DOCK_ITEM (object), &my);
1061         
1062         /* Calculate location in terms of the available space (0-100%). */
1063         rx = (float) rel_x / alloc->width;
1064         ry = (float) rel_y / alloc->height;
1066         /* Determine dock location. */
1067         if (rx < SPLIT_RATIO) {
1068             request->position = GDL_DOCK_LEFT;
1069             divider = other.width;
1070         }
1071         else if (rx > (1 - SPLIT_RATIO)) {
1072             request->position = GDL_DOCK_RIGHT;
1073             rx = 1 - rx;
1074             divider = MAX (0, my.width - other.width);
1075         }
1076         else if (ry < SPLIT_RATIO && ry < rx) {
1077             request->position = GDL_DOCK_TOP;
1078             divider = other.height;
1079         }
1080         else if (ry > (1 - SPLIT_RATIO) && (1 - ry) < rx) {
1081             request->position = GDL_DOCK_BOTTOM;
1082             divider = MAX (0, my.height - other.height);
1083         }
1084         else
1085             request->position = GDL_DOCK_CENTER;
1087         /* Reset rectangle coordinates to entire item. */
1088         request->rect.x = 0;
1089         request->rect.y = 0;
1090         request->rect.width = alloc->width;
1091         request->rect.height = alloc->height;
1093         GdlDockItemBehavior behavior = GDL_DOCK_ITEM(object)->behavior;
1095         /* Calculate docking indicator rectangle size for new locations. Only
1096            do this when we're not over the item's current location. */
1097         if (request->applicant != object) {
1098             switch (request->position) {
1099                 case GDL_DOCK_TOP:
1100                     if (behavior & GDL_DOCK_ITEM_BEH_CANT_DOCK_TOP)
1101                         return FALSE;
1102                     request->rect.height *= SPLIT_RATIO;
1103                     break;
1104                 case GDL_DOCK_BOTTOM:
1105                     if (behavior & GDL_DOCK_ITEM_BEH_CANT_DOCK_BOTTOM)
1106                         return FALSE;
1107                     request->rect.y += request->rect.height * (1 - SPLIT_RATIO);
1108                     request->rect.height *= SPLIT_RATIO;
1109                     break;
1110                 case GDL_DOCK_LEFT:
1111                     if (behavior & GDL_DOCK_ITEM_BEH_CANT_DOCK_LEFT)
1112                         return FALSE;
1113                     request->rect.width *= SPLIT_RATIO;
1114                     break;
1115                 case GDL_DOCK_RIGHT:
1116                     if (behavior & GDL_DOCK_ITEM_BEH_CANT_DOCK_RIGHT)
1117                         return FALSE;
1118                     request->rect.x += request->rect.width * (1 - SPLIT_RATIO);
1119                     request->rect.width *= SPLIT_RATIO;
1120                     break;
1121                 case GDL_DOCK_CENTER:
1122                     if (behavior & GDL_DOCK_ITEM_BEH_CANT_DOCK_CENTER)
1123                         return FALSE;
1124                     request->rect.x = request->rect.width * SPLIT_RATIO/2;
1125                     request->rect.y = request->rect.height * SPLIT_RATIO/2;
1126                     request->rect.width = (request->rect.width *
1127                                            (1 - SPLIT_RATIO/2)) - request->rect.x;
1128                     request->rect.height = (request->rect.height *
1129                                             (1 - SPLIT_RATIO/2)) - request->rect.y;
1130                     break;
1131                 default:
1132                     break;
1133             }
1134         }
1136         /* adjust returned coordinates so they are have the same
1137            origin as our window */
1138         request->rect.x += alloc->x;
1139         request->rect.y += alloc->y;
1140         
1141         /* Set possible target location and return TRUE. */            
1142         request->target = object;
1144         /* fill-in other dock information */
1145         if (request->position != GDL_DOCK_CENTER && divider >= 0) {
1146             if (G_IS_VALUE (&request->extra))
1147                 g_value_unset (&request->extra);
1148             g_value_init (&request->extra, G_TYPE_UINT);
1149             g_value_set_uint (&request->extra, (guint) divider);
1150         }
1151         
1152         return TRUE;         
1153     }
1154     else /* No docking possible at this location. */            
1155         return FALSE;
1158 static void
1159 gdl_dock_item_dock (GdlDockObject    *object,
1160                     GdlDockObject    *requestor,
1161                     GdlDockPlacement  position,
1162                     GValue           *other_data)
1164     GdlDockObject *new_parent, *parent;
1165     gboolean       add_ourselves_first;
1167     guint          available_space=0;
1168     gint           pref_size=-1;
1169     guint          splitpos=0;
1170     GtkRequisition req, object_req, parent_req;
1171     
1172     parent = gdl_dock_object_get_parent_object (object);
1173     gdl_dock_item_preferred_size (GDL_DOCK_ITEM (requestor), &req);
1174     gdl_dock_item_preferred_size (GDL_DOCK_ITEM (object), &object_req);
1175     if (GDL_IS_DOCK_ITEM (parent))
1176         gdl_dock_item_preferred_size (GDL_DOCK_ITEM (parent), &parent_req);
1177     else
1178     {
1179         parent_req.height = GTK_WIDGET (parent)->allocation.height;
1180         parent_req.width = GTK_WIDGET (parent)->allocation.width;
1181     }
1182     
1183     /* If preferred size is not set on the requestor (perhaps a new item),
1184      * then estimate and set it. The default value (either 0 or 1 pixels) is
1185      * not any good.
1186      */
1187     switch (position) {
1188         case GDL_DOCK_TOP:
1189         case GDL_DOCK_BOTTOM:
1190             if (req.width < 2)
1191             {
1192                 req.width = object_req.width;
1193                 g_object_set (requestor, "preferred-width", req.width, NULL);
1194             }
1195             if (req.height < 2)
1196             {
1197                 req.height = NEW_DOCK_ITEM_RATIO * object_req.height;
1198                 g_object_set (requestor, "preferred-height", req.height, NULL);
1199             }
1200             if (req.width > 1)
1201                 g_object_set (object, "preferred-width", req.width, NULL);
1202             if (req.height > 1)
1203                 g_object_set (object, "preferred-height",
1204                               object_req.height - req.height, NULL);
1205             break;
1206         case GDL_DOCK_LEFT:
1207         case GDL_DOCK_RIGHT:
1208             if (req.height < 2)
1209             {
1210                 req.height = object_req.height;
1211                 g_object_set (requestor, "preferred-height", req.height, NULL);
1212             }
1213             if (req.width < 2)
1214             {
1215                 req.width = NEW_DOCK_ITEM_RATIO * object_req.width;
1216                 g_object_set (requestor, "preferred-width", req.width, NULL);
1217             }
1218             if (req.height > 1)
1219                 g_object_set (object, "preferred-height", req.height, NULL);
1220             if (req.width > 1)
1221                 g_object_set (object, "preferred-width",
1222                           object_req.width - req.width, NULL);
1223             break;
1224         case GDL_DOCK_CENTER:
1225             if (req.height < 2)
1226             {
1227                 req.height = object_req.height;
1228                 g_object_set (requestor, "preferred-height", req.height, NULL);
1229             }
1230             if (req.width < 2)
1231             {
1232                 req.width = object_req.width;
1233                 g_object_set (requestor, "preferred-width", req.width, NULL);
1234             }
1235             if (req.height > 1)
1236                 g_object_set (object, "preferred-height", req.height, NULL);
1237             if (req.width > 1)
1238                 g_object_set (object, "preferred-width", req.width, NULL);
1239             break;
1240         default: 
1241         {
1242             GEnumClass *enum_class = G_ENUM_CLASS (g_type_class_ref (GDL_TYPE_DOCK_PLACEMENT));
1243             GEnumValue *enum_value = g_enum_get_value (enum_class, position);
1244             const gchar *name = enum_value ? enum_value->value_name : NULL;
1245             
1246             g_warning (_("Unsupported docking strategy %s in dock object of type %s"),
1247                        name,  G_OBJECT_TYPE_NAME (object));
1248             g_type_class_unref (enum_class);
1249             return;
1250         }
1251     }
1252     switch (position) {
1253         case GDL_DOCK_TOP:
1254         case GDL_DOCK_BOTTOM:
1255             /* get a paned style dock object */
1256             new_parent = g_object_new (gdl_dock_object_type_from_nick ("paned"),
1257                                        "orientation", GTK_ORIENTATION_VERTICAL,
1258                                        "preferred-width", object_req.width,
1259                                        "preferred-height", object_req.height,
1260                                        NULL);
1261             add_ourselves_first = (position == GDL_DOCK_BOTTOM);
1262             if (parent)
1263                 available_space = parent_req.height;
1264             pref_size = req.height;
1265             break;
1266         case GDL_DOCK_LEFT:
1267         case GDL_DOCK_RIGHT:
1268             new_parent = g_object_new (gdl_dock_object_type_from_nick ("paned"),
1269                                        "orientation", GTK_ORIENTATION_HORIZONTAL,
1270                                        "preferred-width", object_req.width,
1271                                        "preferred-height", object_req.height,
1272                                        NULL);
1273             add_ourselves_first = (position == GDL_DOCK_RIGHT);
1274             if(parent)
1275                 available_space = parent_req.width;
1276             pref_size = req.width;
1277             break;
1278         case GDL_DOCK_CENTER:
1279             new_parent = g_object_new (gdl_dock_object_type_from_nick ("notebook"),
1280                                        "preferred-width", object_req.width,
1281                                        "preferred-height", object_req.height,
1282                                        NULL);
1283             add_ourselves_first = TRUE;
1284             break;
1285         default: 
1286         {
1287             GEnumClass *enum_class = G_ENUM_CLASS (g_type_class_ref (GDL_TYPE_DOCK_PLACEMENT));
1288             GEnumValue *enum_value = g_enum_get_value (enum_class, position);
1289             const gchar *name = enum_value ? enum_value->value_name : NULL;
1290             
1291             g_warning (_("Unsupported docking strategy %s in dock object of type %s"),
1292                        name,  G_OBJECT_TYPE_NAME (object));
1293             g_type_class_unref (enum_class);
1294             return;
1295         }
1296     }
1298     /* freeze the parent so it doesn't reduce automatically */
1299     if (parent)
1300         gdl_dock_object_freeze (parent);
1302     /* ref ourselves since we could be destroyed when detached */
1303     g_object_ref (object);
1304     GDL_DOCK_OBJECT_SET_FLAGS (object, GDL_DOCK_IN_REFLOW);
1305     gdl_dock_object_detach (object, FALSE);
1307     /* freeze the new parent, so reduce won't get called before it's
1308        actually added to our parent */
1309     gdl_dock_object_freeze (new_parent);
1310     
1311     /* bind the new parent to our master, so the following adds work */
1312     gdl_dock_object_bind (new_parent, G_OBJECT (GDL_DOCK_OBJECT_GET_MASTER (object)));
1313     
1314     /* add the objects */
1315     if (add_ourselves_first) {
1316         gtk_container_add (GTK_CONTAINER (new_parent), GTK_WIDGET (object));
1317         gtk_container_add (GTK_CONTAINER (new_parent), GTK_WIDGET (requestor));
1318         splitpos = available_space - pref_size;
1319     } else {
1320         gtk_container_add (GTK_CONTAINER (new_parent), GTK_WIDGET (requestor));
1321         gtk_container_add (GTK_CONTAINER (new_parent), GTK_WIDGET (object));
1322         splitpos = pref_size;
1323     }
1325     /* add the new parent to the parent */
1326     if (parent)
1327         gtk_container_add (GTK_CONTAINER (parent), GTK_WIDGET (new_parent));
1329     /* show automatic object */
1330     if (GTK_WIDGET_VISIBLE (object))
1331         gtk_widget_show (GTK_WIDGET (new_parent));
1332     
1333     /* use extra docking parameter */
1334     if (position != GDL_DOCK_CENTER && other_data &&
1335         G_VALUE_HOLDS (other_data, G_TYPE_UINT)) {
1336         
1337         g_object_set (G_OBJECT (new_parent),
1338                       "position", g_value_get_uint (other_data),
1339                       NULL);
1340     } else if (splitpos > 0 && splitpos < available_space) {
1341         g_object_set (G_OBJECT (new_parent), "position", splitpos, NULL);
1342     }
1343     
1344     GDL_DOCK_OBJECT_UNSET_FLAGS (object, GDL_DOCK_IN_REFLOW);
1345     g_object_unref (object);
1347     gdl_dock_object_thaw (new_parent);
1348     if (parent)
1349         gdl_dock_object_thaw (parent);
1352 static void
1353 gdl_dock_item_detach_menu (GtkWidget *widget,
1354                            GtkMenu   *menu)
1356     GdlDockItem *item;
1357    
1358     item = GDL_DOCK_ITEM (widget);
1359     item->_priv->menu = NULL;
1362 static void
1363 gdl_dock_item_popup_menu (GdlDockItem  *item, 
1364                           guint         button,
1365                           guint32       time)
1367     GtkWidget *mitem;
1369     if (!item->_priv->menu) {
1370         /* Create popup menu and attach it to the dock item */
1371         item->_priv->menu = gtk_menu_new ();
1372         gtk_menu_attach_to_widget (GTK_MENU (item->_priv->menu),
1373                                    GTK_WIDGET (item),
1374                                    gdl_dock_item_detach_menu);
1375         
1376         if (item->behavior & GDL_DOCK_ITEM_BEH_LOCKED) {
1377             /* UnLock menuitem */
1378             mitem = gtk_menu_item_new_with_label (_("UnLock"));
1379             gtk_menu_shell_append (GTK_MENU_SHELL (item->_priv->menu), 
1380                                    mitem);
1381             g_signal_connect (mitem, "activate",
1382                               G_CALLBACK (gdl_dock_item_unlock_cb), item);
1383         } else {
1384             /* Hide menuitem. */
1385             mitem = gtk_menu_item_new_with_label (_("Hide"));
1386             gtk_menu_shell_append (GTK_MENU_SHELL (item->_priv->menu), mitem);
1387             g_signal_connect (mitem, "activate", 
1388                               G_CALLBACK (gdl_dock_item_hide_cb), item);
1389             /* Lock menuitem */
1390             mitem = gtk_menu_item_new_with_label (_("Lock"));
1391             gtk_menu_shell_append (GTK_MENU_SHELL (item->_priv->menu), mitem);
1392             g_signal_connect (mitem, "activate",
1393                               G_CALLBACK (gdl_dock_item_lock_cb), item);
1394         }
1395     }
1397     /* Show popup menu. */
1398     gtk_widget_show_all (item->_priv->menu);
1399     gtk_menu_popup (GTK_MENU (item->_priv->menu), NULL, NULL, NULL, NULL, 
1400                     button, time);
1403 static void
1404 gdl_dock_item_drag_start (GdlDockItem *item)
1406     GdkCursor *fleur;
1408     if (!GTK_WIDGET_REALIZED (item))
1409         gtk_widget_realize (GTK_WIDGET (item));
1410     
1411     GDL_DOCK_ITEM_SET_FLAGS (item, GDL_DOCK_IN_DRAG);
1412             
1413     /* grab the pointer so we receive all mouse events */
1414     fleur = gdk_cursor_new (GDK_FLEUR);
1416     /* grab the keyboard & pointer */
1417     gtk_grab_add (GTK_WIDGET (item));
1418     
1419     gdk_cursor_unref (fleur);
1420             
1421     g_signal_emit (item, gdl_dock_item_signals [DOCK_DRAG_BEGIN], 0);
1424 static void
1425 gdl_dock_item_drag_end (GdlDockItem *item,
1426                         gboolean     cancel)
1428     /* Release pointer & keyboard. */
1429     gtk_grab_remove (gtk_grab_get_current ());
1430     
1431     g_signal_emit (item, gdl_dock_item_signals [DOCK_DRAG_END], 0, cancel);
1432     
1433     GDL_DOCK_ITEM_UNSET_FLAGS (item, GDL_DOCK_IN_DRAG);
1436 static void 
1437 gdl_dock_item_tab_button (GtkWidget      *widget,
1438                           GdkEventButton *event,
1439                           gpointer        data)
1441     GdlDockItem *item;
1443     item = GDL_DOCK_ITEM (data);
1445     if (!GDL_DOCK_ITEM_NOT_LOCKED (item))
1446         return;
1448     switch (event->button) {
1449     case 1:
1450         /* set dragoff_{x,y} as we the user clicked on the middle of the 
1451            drag handle */
1452         switch (item->orientation) {
1453         case GTK_ORIENTATION_HORIZONTAL:
1454             /*item->dragoff_x = item->_priv->grip_size / 2;*/
1455             item->dragoff_y = GTK_WIDGET (data)->allocation.height / 2;
1456             break;
1457         case GTK_ORIENTATION_VERTICAL:
1458             /*item->dragoff_x = GTK_WIDGET (data)->allocation.width / 2;*/
1459             item->dragoff_y = item->_priv->grip_size / 2;
1460             break;
1461         };
1462         gdl_dock_item_drag_start (item);
1463         break;
1465     case 3:
1466         gdl_dock_item_popup_menu (item, event->button, event->time);
1467         break;
1469     default:
1470         break;
1471     };
1474 static void
1475 gdl_dock_item_hide_cb (GtkWidget   *widget, 
1476                        GdlDockItem *item)
1478     GdlDockMaster *master;
1479     
1480     g_return_if_fail (item != NULL);
1482     master = GDL_DOCK_OBJECT_GET_MASTER (item);
1483     gdl_dock_item_hide_item (item);
1486 static void
1487 gdl_dock_item_lock_cb (GtkWidget   *widget,
1488                        GdlDockItem *item)
1490     g_return_if_fail (item != NULL);
1492     gdl_dock_item_lock (item);
1495 static void
1496 gdl_dock_item_unlock_cb (GtkWidget   *widget,
1497                        GdlDockItem *item)
1499     g_return_if_fail (item != NULL);
1501     gdl_dock_item_unlock (item);
1504 static void
1505 gdl_dock_item_showhide_grip (GdlDockItem *item)
1507     GdkDisplay *display;
1508     GdkCursor *cursor;
1509     
1510     gdl_dock_item_detach_menu (GTK_WIDGET (item), NULL); 
1511     display = gtk_widget_get_display (GTK_WIDGET (item));
1512     cursor = NULL;
1513     
1514     if (item->_priv->grip) {
1515         if (GDL_DOCK_ITEM_GRIP_SHOWN (item) && 
1516             GDL_DOCK_ITEM_NOT_LOCKED(item))
1517              cursor = gdk_cursor_new_for_display (display, GDK_HAND2);
1518     }
1519     if (item->_priv->grip && GDL_DOCK_ITEM_GRIP (item->_priv->grip)->title_window)
1520         gdk_window_set_cursor (GDL_DOCK_ITEM_GRIP (item->_priv->grip)->title_window, cursor);
1522     if (cursor)
1523         gdk_cursor_unref (cursor);
1524     
1525     gtk_widget_queue_resize (GTK_WIDGET (item));
1528 static void
1529 gdl_dock_item_real_set_orientation (GdlDockItem    *item,
1530                                     GtkOrientation  orientation)
1532     item->orientation = orientation;
1533     
1534     if (GTK_WIDGET_DRAWABLE (item))
1535         gtk_widget_queue_draw (GTK_WIDGET (item));
1536     gtk_widget_queue_resize (GTK_WIDGET (item));
1540 /* ----- Public interface ----- */
1542 GtkWidget *
1543 gdl_dock_item_new (const gchar         *name,
1544                    const gchar         *long_name,
1545                    GdlDockItemBehavior  behavior)
1547     GdlDockItem *item;
1549     item = GDL_DOCK_ITEM (g_object_new (GDL_TYPE_DOCK_ITEM, 
1550                                         "name", name, 
1551                                         "long-name", long_name,
1552                                         "behavior", behavior,
1553                                         NULL));
1554     GDL_DOCK_OBJECT_UNSET_FLAGS (item, GDL_DOCK_AUTOMATIC);
1555     gdl_dock_item_set_tablabel (item, gtk_label_new (long_name));
1556     return GTK_WIDGET (item);
1559 GtkWidget *
1560 gdl_dock_item_new_with_stock (const gchar         *name,
1561                               const gchar         *long_name,
1562                               const gchar         *stock_id,
1563                               GdlDockItemBehavior  behavior)
1565     GdlDockItem *item;
1567     item = GDL_DOCK_ITEM (g_object_new (GDL_TYPE_DOCK_ITEM, 
1568                                         "name", name, 
1569                                         "long-name", long_name,
1570                                         "stock-id", stock_id,
1571                                         "behavior", behavior,
1572                                         NULL));
1573     GDL_DOCK_OBJECT_UNSET_FLAGS (item, GDL_DOCK_AUTOMATIC);
1574     gdl_dock_item_set_tablabel (item, gtk_label_new (long_name));
1576     return GTK_WIDGET (item);
1579 GtkWidget *
1580 gdl_dock_item_new_with_pixbuf_icon (const gchar         *name,
1581                                     const gchar         *long_name,
1582                                     const GdkPixbuf     *pixbuf_icon,
1583                                     GdlDockItemBehavior  behavior)
1585     GdlDockItem *item;
1587     item = GDL_DOCK_ITEM (g_object_new (GDL_TYPE_DOCK_ITEM, 
1588                                         "name", name, 
1589                                         "long-name", long_name,
1590                                         "pixbuf-icon", pixbuf_icon,
1591                                         "behavior", behavior,
1592                                         NULL));
1594     GDL_DOCK_OBJECT_UNSET_FLAGS (item, GDL_DOCK_AUTOMATIC);
1595     gdl_dock_item_set_tablabel (item, gtk_label_new (long_name));
1597     return GTK_WIDGET (item);
1600 /* convenient function (and to preserve source compat) */
1601 void
1602 gdl_dock_item_dock_to (GdlDockItem      *item,
1603                        GdlDockItem      *target,
1604                        GdlDockPlacement  position,
1605                        gint              docking_param)
1607     g_return_if_fail (item != NULL);
1608     g_return_if_fail (item != target);
1609     g_return_if_fail (target != NULL || position == GDL_DOCK_FLOATING);
1610     g_return_if_fail ((item->behavior & GDL_DOCK_ITEM_BEH_NEVER_FLOATING) == 0 || position != GDL_DOCK_FLOATING);
1612     if (position == GDL_DOCK_FLOATING || !target) {
1613         GdlDockObject *controller;
1615         if (!gdl_dock_object_is_bound (GDL_DOCK_OBJECT (item))) {
1616             g_warning (_("Attempt to bind an unbound item %p"), item);
1617             return;
1618         }
1620         controller = gdl_dock_master_get_controller (GDL_DOCK_OBJECT_GET_MASTER (item));
1621         
1622         /* FIXME: save previous docking position for later
1623            re-docking... does this make sense now? */
1625         /* Create new floating dock for widget. */
1626         item->dragoff_x = item->dragoff_y = 0;
1627         gdl_dock_add_floating_item (GDL_DOCK (controller),
1628                                     item, 0, 0, -1, -1);
1630     } else
1631         gdl_dock_object_dock (GDL_DOCK_OBJECT (target),
1632                               GDL_DOCK_OBJECT (item),
1633                               position, NULL);
1636 void
1637 gdl_dock_item_set_orientation (GdlDockItem    *item,
1638                                GtkOrientation  orientation)
1640     GParamSpec *pspec;
1642     g_return_if_fail (item != NULL);
1644     if (item->orientation != orientation) {
1645         /* push the property down the hierarchy if our child supports it */
1646         if (item->child != NULL) {
1647             pspec = g_object_class_find_property (
1648                 G_OBJECT_GET_CLASS (item->child), "orientation");
1649             if (pspec && pspec->value_type == GTK_TYPE_ORIENTATION)
1650                 g_object_set (G_OBJECT (item->child),
1651                               "orientation", orientation,
1652                               NULL);
1653         };
1655         GDL_CALL_VIRTUAL (item, GDL_DOCK_ITEM_GET_CLASS, set_orientation, (item, orientation));
1656         g_object_notify (G_OBJECT (item), "orientation");
1657     }
1660 GtkWidget *
1661 gdl_dock_item_get_tablabel (GdlDockItem *item)
1663     g_return_val_if_fail (item != NULL, NULL);
1664     g_return_val_if_fail (GDL_IS_DOCK_ITEM (item), NULL);
1666     return item->_priv->tab_label;
1669 void
1670 gdl_dock_item_set_tablabel (GdlDockItem *item,
1671                             GtkWidget   *tablabel)
1673     g_return_if_fail (item != NULL);
1675     if (item->_priv->tab_label) {
1676         /* disconnect and unref the previous tablabel */
1677         if (GDL_IS_DOCK_TABLABEL (item->_priv->tab_label)) {
1678             g_signal_handlers_disconnect_matched (item->_priv->tab_label,
1679                                                   G_SIGNAL_MATCH_DATA,
1680                                                   0, 0, NULL,
1681                                                   NULL, item);
1682             g_object_set (item->_priv->tab_label, "item", NULL, NULL);
1683         }
1684         gtk_widget_unref (item->_priv->tab_label);
1685         item->_priv->tab_label = NULL;
1686     }
1687     
1688     if (tablabel) {
1689         gtk_widget_ref (tablabel);
1690         gtk_object_sink (GTK_OBJECT (tablabel));
1691         item->_priv->tab_label = tablabel;
1692         if (GDL_IS_DOCK_TABLABEL (tablabel)) {
1693             g_object_set (tablabel, "item", item, NULL);
1694             /* connect to tablabel signal */
1695             g_signal_connect (tablabel, "button_pressed_handle",
1696                               G_CALLBACK (gdl_dock_item_tab_button), item);
1697         }
1698     }
1701 void 
1702 gdl_dock_item_hide_grip (GdlDockItem *item)
1704     g_return_if_fail (item != NULL);
1705     if (item->_priv->grip_shown) {
1706         item->_priv->grip_shown = FALSE;
1707         gdl_dock_item_showhide_grip (item);
1708     };
1709     g_warning ("Grips always show unless GDL_DOCK_ITEM_BEH_NO_GRIP is set\n" );
1712 void
1713 gdl_dock_item_show_grip (GdlDockItem *item)
1715     g_return_if_fail (item != NULL);
1716     if (!item->_priv->grip_shown) {
1717         item->_priv->grip_shown = TRUE;
1718         gdl_dock_item_showhide_grip (item);
1719     };
1722 /* convenient function (and to preserve source compat) */
1723 void
1724 gdl_dock_item_bind (GdlDockItem *item,
1725                     GtkWidget   *dock)
1727     g_return_if_fail (item != NULL);
1728     g_return_if_fail (dock == NULL || GDL_IS_DOCK (dock));
1729     
1730     gdl_dock_object_bind (GDL_DOCK_OBJECT (item),
1731                           G_OBJECT (GDL_DOCK_OBJECT_GET_MASTER (dock)));
1734 /* convenient function (and to preserve source compat) */
1735 void
1736 gdl_dock_item_unbind (GdlDockItem *item)
1738     g_return_if_fail (item != NULL);
1740     gdl_dock_object_unbind (GDL_DOCK_OBJECT (item));
1743 void
1744 gdl_dock_item_hide_item (GdlDockItem *item)
1746     g_return_if_fail (item != NULL);
1748     if (!GDL_DOCK_OBJECT_ATTACHED (item))
1749         /* already hidden/detached */
1750         return;
1751        
1752     /* if the object is manual, create a new placeholder to be able to
1753        restore the position later */
1754     if (!GDL_DOCK_OBJECT_AUTOMATIC (item)) {
1755         if (item->_priv->ph)
1756             g_object_unref (item->_priv->ph); 
1757         
1758         gboolean isFloating = FALSE;
1759         gint width=0, height=0, x=0, y = 0;
1760         
1761         if (GDL_IS_DOCK (gdl_dock_object_get_parent_object (GDL_DOCK_OBJECT (item))))
1762         {
1763             GdlDock* dock = GDL_DOCK (gdl_dock_object_get_parent_object (GDL_DOCK_OBJECT (item)));
1764             g_object_get (dock,
1765                           "floating", &isFloating, 
1766                           "width", &width,
1767                           "height",&height,
1768                           "floatx",&x,
1769                           "floaty",&y,
1770                           NULL);
1771         } else {
1772             item->_priv->preferred_width=GTK_WIDGET (item)->allocation.width;
1773             item->_priv->preferred_height=GTK_WIDGET (item)->allocation.height;
1774         }
1775         item->_priv->ph = GDL_DOCK_PLACEHOLDER (
1776             g_object_new (GDL_TYPE_DOCK_PLACEHOLDER,
1777                           "sticky", FALSE,
1778                           "host", item,
1779                           "width", width,
1780                           "height", height,
1781                           "floating", isFloating,
1782                           "floatx", x,
1783                           "floaty", y,
1784                           NULL));
1785         g_object_ref (item->_priv->ph);
1786         gtk_object_sink (GTK_OBJECT (item->_priv->ph));
1787     }
1788     
1789     gdl_dock_object_freeze (GDL_DOCK_OBJECT (item));
1790     
1791     /* hide our children first, so they can also set placeholders */
1792     if (gdl_dock_object_is_compound (GDL_DOCK_OBJECT (item))) 
1793         gtk_container_foreach (GTK_CONTAINER (item),
1794                                (GtkCallback) gdl_dock_item_hide_item,
1795                                NULL);
1796     
1797     /* detach the item recursively */
1798     gdl_dock_object_detach (GDL_DOCK_OBJECT (item), TRUE);
1800     gtk_widget_hide (GTK_WIDGET (item));
1802     gdl_dock_object_thaw (GDL_DOCK_OBJECT (item));
1805 void
1806 gdl_dock_item_iconify_item (GdlDockItem *item)
1808     g_return_if_fail (item != NULL);
1809     
1810     GDL_DOCK_OBJECT_SET_FLAGS (item, GDL_DOCK_ICONIFIED);
1811     gdl_dock_item_hide_item (item);
1814 void
1815 gdl_dock_item_show_item (GdlDockItem *item)
1817     g_return_if_fail (item != NULL);
1819     GDL_DOCK_OBJECT_UNSET_FLAGS (item, GDL_DOCK_ICONIFIED);
1820     
1821     if (item->_priv->ph) {
1822         gboolean isFloating=FALSE;
1823         gint width = 0, height = 0, x= 0, y = 0;
1824         g_object_get (G_OBJECT(item->_priv->ph),
1825                       "width", &width,
1826                       "height", &height,
1827                       "floating",&isFloating,
1828                       "floatx", &x,
1829                       "floaty", &y,
1830                       NULL);
1831         if (isFloating) {
1832             GdlDockObject *controller =
1833                 gdl_dock_master_get_controller (GDL_DOCK_OBJECT_GET_MASTER (item));
1834             gdl_dock_add_floating_item (GDL_DOCK (controller),
1835                                         item, x, y, width, height);
1836         } else {
1837             gtk_container_add (GTK_CONTAINER (item->_priv->ph),
1838                                GTK_WIDGET (item));
1839         }
1840         g_object_unref (item->_priv->ph);
1841         item->_priv->ph = NULL;
1842         
1843     } else if (gdl_dock_object_is_bound (GDL_DOCK_OBJECT (item))) {
1844         GdlDockObject *toplevel;
1845         
1846         toplevel = gdl_dock_master_get_controller
1847                         (GDL_DOCK_OBJECT_GET_MASTER (item));
1848         
1849         if (item->behavior & GDL_DOCK_ITEM_BEH_NEVER_FLOATING) {
1850             g_warning("Object %s has no default position and flag GDL_DOCK_ITEM_BEH_NEVER_FLOATING is set.\n",
1851                       GDL_DOCK_OBJECT(item)->name);
1852         } else if (toplevel) {
1853             gdl_dock_object_dock (toplevel, GDL_DOCK_OBJECT (item),
1854                                   GDL_DOCK_FLOATING, NULL);
1855         } else
1856             g_warning("There is no toplevel window. GdlDockItem %s cannot be shown.\n", GDL_DOCK_OBJECT(item)->name);
1857         
1858     } else
1859         g_warning("GdlDockItem %s is not bound. It cannot be shown.\n",
1860                   GDL_DOCK_OBJECT(item)->name);
1861     
1862     gtk_widget_show (GTK_WIDGET (item));
1865 void
1866 gdl_dock_item_lock (GdlDockItem *item)
1868     g_object_set (item, "locked", TRUE, NULL);
1871 void
1872 gdl_dock_item_unlock (GdlDockItem *item)
1874     g_object_set (item, "locked", FALSE, NULL);
1877 void 
1878 gdl_dock_item_set_default_position (GdlDockItem   *item,
1879                                     GdlDockObject *reference)
1881     g_return_if_fail (item != NULL);
1883     if (item->_priv->ph) {
1884         g_object_unref (item->_priv->ph);
1885         item->_priv->ph = NULL;
1886     }
1888     if (reference && GDL_DOCK_OBJECT_ATTACHED (reference)) {
1889         if (GDL_IS_DOCK_PLACEHOLDER (reference)) {
1890             g_object_ref (reference);
1891             gtk_object_sink (GTK_OBJECT (reference));
1892             item->_priv->ph = GDL_DOCK_PLACEHOLDER (reference);
1893         } else {
1894             item->_priv->ph = GDL_DOCK_PLACEHOLDER (
1895                 g_object_new (GDL_TYPE_DOCK_PLACEHOLDER,
1896                               "sticky", TRUE,
1897                               "host", reference,
1898                               NULL));
1899             g_object_ref (item->_priv->ph);
1900             gtk_object_sink (GTK_OBJECT (item->_priv->ph));
1901         }
1902     }
1905 void 
1906 gdl_dock_item_preferred_size (GdlDockItem    *item,
1907                               GtkRequisition *req)
1909     if (!req)
1910         return;
1912     req->width = MAX (item->_priv->preferred_width,
1913                       GTK_WIDGET (item)->allocation.width);
1914     req->height = MAX (item->_priv->preferred_height,
1915                        GTK_WIDGET (item)->allocation.height);
1919 /* ----- gtk orientation type exporter/importer ----- */
1921 static void 
1922 gdl_dock_param_export_gtk_orientation (const GValue *src,
1923                                        GValue       *dst)
1925     dst->data [0].v_pointer =
1926         g_strdup_printf ("%s", (src->data [0].v_int == GTK_ORIENTATION_HORIZONTAL) ?
1927                          "horizontal" : "vertical");
1930 static void 
1931 gdl_dock_param_import_gtk_orientation (const GValue *src,
1932                                        GValue       *dst)
1934     if (!strcmp (src->data [0].v_pointer, "horizontal"))
1935         dst->data [0].v_int = GTK_ORIENTATION_HORIZONTAL;
1936     else
1937         dst->data [0].v_int = GTK_ORIENTATION_VERTICAL;