Code

Allow GdlDockItems to take focus, and grab focus on present. Various
[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);
358     GTK_WIDGET_SET_FLAGS (GTK_WIDGET (item), GTK_CAN_FOCUS);
360     item->child = NULL;
361     
362     item->orientation = GTK_ORIENTATION_VERTICAL;
363     item->behavior = GDL_DOCK_ITEM_BEH_NORMAL;
365     item->resize = TRUE;
367     item->dragoff_x = item->dragoff_y = 0;
369     item->_priv = g_new0 (GdlDockItemPrivate, 1);
370     item->_priv->menu = NULL;
372     item->_priv->preferred_width = item->_priv->preferred_height = -1;
373     item->_priv->tab_label = NULL;
375     item->_priv->ph = NULL;
378 static GObject *
379 gdl_dock_item_constructor (GType                  type,
380                            guint                  n_construct_properties,
381                            GObjectConstructParam *construct_param)
383     GObject *g_object;
384     
385     g_object = GDL_CALL_PARENT_WITH_DEFAULT (G_OBJECT_CLASS, 
386                                                constructor, 
387                                                (type,
388                                                 n_construct_properties,
389                                                 construct_param),
390                                                NULL);
391     if (g_object) {
392         GdlDockItem *item = GDL_DOCK_ITEM (g_object);
394         if (GDL_DOCK_ITEM_HAS_GRIP (item)) {
395             item->_priv->grip_shown = TRUE;
396             item->_priv->grip = gdl_dock_item_grip_new (item);
397             gtk_widget_set_parent (item->_priv->grip, GTK_WIDGET (item));
398             gtk_widget_show (item->_priv->grip);
399         }
400         else {
401             item->_priv->grip_shown = FALSE;
402         }
403     }
405     return g_object;
408 static void
409 gdl_dock_item_set_property  (GObject      *g_object,
410                              guint         prop_id,
411                              const GValue *value,
412                              GParamSpec   *pspec)
414     GdlDockItem *item = GDL_DOCK_ITEM (g_object);
416     switch (prop_id) {
417         case PROP_ORIENTATION:
418             gdl_dock_item_set_orientation (item, g_value_get_enum (value));
419             break;
420         case PROP_RESIZE:
421             item->resize = g_value_get_boolean (value);
422             gtk_widget_queue_resize (GTK_WIDGET (item));
423             break;
424         case PROP_BEHAVIOR:
425         {
426             GdlDockItemBehavior old_beh = item->behavior;
427             item->behavior = g_value_get_flags (value);
429             if ((old_beh ^ item->behavior) & GDL_DOCK_ITEM_BEH_LOCKED) {
430                 if (GDL_DOCK_OBJECT_GET_MASTER (item))
431                     g_signal_emit_by_name (GDL_DOCK_OBJECT_GET_MASTER (item),
432                                            "layout-changed");
433                 g_object_notify (g_object, "locked");
434                 gdl_dock_item_showhide_grip (item);
435             }
436             
437             break;
438         }
439         case PROP_LOCKED:
440         {
441             GdlDockItemBehavior old_beh = item->behavior;
443             if (g_value_get_boolean (value))
444                 item->behavior |= GDL_DOCK_ITEM_BEH_LOCKED;
445             else
446                 item->behavior &= ~GDL_DOCK_ITEM_BEH_LOCKED;
448             if (old_beh ^ item->behavior) {
449                 gdl_dock_item_showhide_grip (item);
450                 g_object_notify (g_object, "behavior");
452                 if (GDL_DOCK_OBJECT_GET_MASTER (item))
453                     g_signal_emit_by_name (GDL_DOCK_OBJECT_GET_MASTER (item),
454                                            "layout-changed");
455             }
456             break;
457         }
458         case PROP_PREFERRED_WIDTH:
459             item->_priv->preferred_width = g_value_get_int (value);
460             break;
461         case PROP_PREFERRED_HEIGHT:
462             item->_priv->preferred_height = g_value_get_int (value);
463             break;
464         default:
465             G_OBJECT_WARN_INVALID_PROPERTY_ID (g_object, prop_id, pspec);
466             break;
467     }
470 static void
471 gdl_dock_item_get_property  (GObject      *g_object,
472                              guint         prop_id,
473                              GValue       *value,
474                              GParamSpec   *pspec)
476     GdlDockItem *item = GDL_DOCK_ITEM (g_object);
477     
478     switch (prop_id) {
479         case PROP_ORIENTATION:
480             g_value_set_enum (value, item->orientation);
481             break;
482         case PROP_RESIZE:
483             g_value_set_boolean (value, item->resize);
484             break;
485         case PROP_BEHAVIOR:
486             g_value_set_flags (value, item->behavior);
487             break;
488         case PROP_LOCKED:
489             g_value_set_boolean (value, !GDL_DOCK_ITEM_NOT_LOCKED (item));
490             break;
491         case PROP_PREFERRED_WIDTH:
492             g_value_set_int (value, item->_priv->preferred_width);
493             break;
494         case PROP_PREFERRED_HEIGHT:
495             g_value_set_int (value, item->_priv->preferred_height);
496             break;
497         default:
498             G_OBJECT_WARN_INVALID_PROPERTY_ID (g_object, prop_id, pspec);
499             break;
500     }
503 static void
504 gdl_dock_item_destroy (GtkObject *object)
506     GdlDockItem *item = GDL_DOCK_ITEM (object);
508     if (item->_priv) {
509         GdlDockItemPrivate *priv = item->_priv;
510         
511         if (priv->tab_label) {
512             gdl_dock_item_set_tablabel (item, NULL);
513         };
514         if (priv->menu) {
515             gtk_menu_detach (GTK_MENU (priv->menu));
516             priv->menu = NULL;
517         };
518         if (priv->grip) {
519             gtk_container_remove (GTK_CONTAINER (item), priv->grip);
520             priv->grip = NULL;
521         }
522         if (priv->ph) {
523             g_object_unref (priv->ph);
524             priv->ph = NULL;
525         }
526         
527         item->_priv = NULL;
528         g_free (priv);
529     }
531     GDL_CALL_PARENT (GTK_OBJECT_CLASS, destroy, (object));
534 static void 
535 gdl_dock_item_add (GtkContainer *container,
536                    GtkWidget    *widget)
538     GdlDockItem *item;
539     
540     g_return_if_fail (GDL_IS_DOCK_ITEM (container));
542     item = GDL_DOCK_ITEM (container);
543     if (GDL_IS_DOCK_OBJECT (widget)) {
544         g_warning (_("You can't add a dock object (%p of type %s) inside a %s. "
545                      "Use a GdlDock or some other compound dock object."),
546                    widget, G_OBJECT_TYPE_NAME (widget), G_OBJECT_TYPE_NAME (item));
547         return;
548     }
550     if (item->child != NULL) {
551         g_warning (_("Attempting to add a widget with type %s to a %s, "
552                      "but it can only contain one widget at a time; "
553                      "it already contains a widget of type %s"),
554                      G_OBJECT_TYPE_NAME (widget),
555                      G_OBJECT_TYPE_NAME (item),
556                      G_OBJECT_TYPE_NAME (item->child));
557         return;
558     }
560     gtk_widget_set_parent (widget, GTK_WIDGET (item));
561     item->child = widget;
564 static void  
565 gdl_dock_item_remove (GtkContainer *container,
566                       GtkWidget    *widget)
568     GdlDockItem *item;
569     gboolean     was_visible;
570     
571     g_return_if_fail (GDL_IS_DOCK_ITEM (container));
572     
573     item = GDL_DOCK_ITEM (container);
574     if (item->_priv && widget == item->_priv->grip) {
575         gboolean grip_was_visible = GTK_WIDGET_VISIBLE (widget);
576         gtk_widget_unparent (widget);
577         item->_priv->grip = NULL;
578         if (grip_was_visible)
579             gtk_widget_queue_resize (GTK_WIDGET (item));
580         return;
581     }
582     
583     if (GDL_DOCK_ITEM_IN_DRAG (item)) {
584         gdl_dock_item_drag_end (item, TRUE);
585     }
586     
587     g_return_if_fail (item->child == widget);
588     
589     was_visible = GTK_WIDGET_VISIBLE (widget);
591     gtk_widget_unparent (widget);
592     item->child = NULL;
593     
594     if (was_visible)
595         gtk_widget_queue_resize (GTK_WIDGET (container));
598 static void
599 gdl_dock_item_forall (GtkContainer *container,
600                       gboolean      include_internals,
601                       GtkCallback   callback,
602                       gpointer      callback_data)
604     GdlDockItem *item = (GdlDockItem *) container;
605     
606     g_return_if_fail (callback != NULL);
607     
608     if (include_internals && item->_priv->grip)
609         (* callback) (item->_priv->grip, callback_data);
610     
611     if (item->child)
612         (* callback) (item->child, callback_data);
615 static GtkType
616 gdl_dock_item_child_type (GtkContainer *container)
618     g_return_val_if_fail (GDL_IS_DOCK_ITEM (container), G_TYPE_NONE);
619     
620     if (!GDL_DOCK_ITEM (container)->child)
621         return GTK_TYPE_WIDGET;
622     else
623         return G_TYPE_NONE;
626 static void
627 gdl_dock_item_size_request (GtkWidget      *widget,
628                             GtkRequisition *requisition)
630     GtkRequisition  child_requisition;
631     GtkRequisition  grip_requisition;
632     GdlDockItem    *item;
634     g_return_if_fail (GDL_IS_DOCK_ITEM (widget));
635     g_return_if_fail (requisition != NULL);
637     item = GDL_DOCK_ITEM (widget);
639     /* If our child is not visible, we still request its size, since
640        we won't have any useful hint for our size otherwise.  */
641     if (item->child)
642         gtk_widget_size_request (item->child, &child_requisition);
643     else {
644         child_requisition.width = 0;
645         child_requisition.height = 0;
646     }
648     if (item->orientation == GTK_ORIENTATION_HORIZONTAL) {
649         if (GDL_DOCK_ITEM_GRIP_SHOWN (item)) {
650             gtk_widget_size_request (item->_priv->grip, &grip_requisition);
651             requisition->width = grip_requisition.width;
652         } else {
653             requisition->width = 0;
654         }
656         if (item->child) {
657             requisition->width += child_requisition.width;
658             requisition->height = child_requisition.height;
659         } else
660             requisition->height = 0;
661     } else {
662         if (GDL_DOCK_ITEM_GRIP_SHOWN (item)) {
663             gtk_widget_size_request (item->_priv->grip, &grip_requisition);
664             requisition->height = grip_requisition.height;
665         } else {
666             requisition->height = 0;
667         }
669         if (item->child) {
670             requisition->width = child_requisition.width;
671             requisition->height += child_requisition.height;
672         } else
673             requisition->width = 0;
674     }
676     requisition->width += (GTK_CONTAINER (widget)->border_width + widget->style->xthickness) * 2;
677     requisition->height += (GTK_CONTAINER (widget)->border_width + widget->style->ythickness) * 2;
679     widget->requisition = *requisition;
682 static void
683 gdl_dock_item_size_allocate (GtkWidget     *widget,
684                              GtkAllocation *allocation)
686     GdlDockItem *item;
687   
688     g_return_if_fail (GDL_IS_DOCK_ITEM (widget));
689     g_return_if_fail (allocation != NULL);
690   
691     item = GDL_DOCK_ITEM (widget);
693     widget->allocation = *allocation;
695     /* Once size is allocated, preferred size is no longer necessary */
696     item->_priv->preferred_height = -1;
697     item->_priv->preferred_width = -1;
698     
699     if (GTK_WIDGET_REALIZED (widget))
700         gdk_window_move_resize (widget->window,
701                                 widget->allocation.x,
702                                 widget->allocation.y,
703                                 widget->allocation.width,
704                                 widget->allocation.height);
706     if (item->child && GTK_WIDGET_VISIBLE (item->child)) {
707         GtkAllocation  child_allocation;
708         int            border_width;
710         border_width = GTK_CONTAINER (widget)->border_width;
712         child_allocation.x = border_width + widget->style->xthickness;
713         child_allocation.y = border_width + widget->style->ythickness;
714         child_allocation.width = allocation->width
715             - 2 * (border_width + widget->style->xthickness);
716         child_allocation.height = allocation->height
717             - 2 * (border_width + widget->style->ythickness);
718         
719         if (GDL_DOCK_ITEM_GRIP_SHOWN (item)) {
720             GtkAllocation grip_alloc = child_allocation;
721             GtkRequisition grip_req;
722             
723             gtk_widget_size_request (item->_priv->grip, &grip_req);
724             
725             if (item->orientation == GTK_ORIENTATION_HORIZONTAL) {
726                 child_allocation.x += grip_req.width;
727                 child_allocation.width -= grip_req.width;
728                 grip_alloc.width = grip_req.width;
729             } else {
730                 child_allocation.y += grip_req.height;
731                 child_allocation.height -= grip_req.height;
732                 grip_alloc.height = grip_req.height;
733             }
734             if (item->_priv->grip)
735                 gtk_widget_size_allocate (item->_priv->grip, &grip_alloc);
736         }
737         /* Allocation can't be negative */
738         if (child_allocation.width < 0)
739             child_allocation.width = 0;
740         if (child_allocation.height < 0)
741             child_allocation.height = 0;
742         gtk_widget_size_allocate (item->child, &child_allocation);
743     }
746 static void
747 gdl_dock_item_map (GtkWidget *widget)
749     GdlDockItem *item;
751     g_return_if_fail (widget != NULL);
752     g_return_if_fail (GDL_IS_DOCK_ITEM (widget));
754     GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
756     item = GDL_DOCK_ITEM (widget);
758     gdk_window_show (widget->window);
760     if (item->child
761         && GTK_WIDGET_VISIBLE (item->child)
762         && !GTK_WIDGET_MAPPED (item->child))
763         gtk_widget_map (item->child);
765     if (item->_priv->grip
766         && GTK_WIDGET_VISIBLE (item->_priv->grip)
767         && !GTK_WIDGET_MAPPED (item->_priv->grip))
768         gtk_widget_map (item->_priv->grip);
771 static void
772 gdl_dock_item_unmap (GtkWidget *widget)
774     GdlDockItem *item;
776     g_return_if_fail (widget != NULL);
777     g_return_if_fail (GDL_IS_DOCK_ITEM (widget));
779     GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
780     
781     item = GDL_DOCK_ITEM (widget);
783     gdk_window_hide (widget->window);
785     if (item->_priv->grip)
786         gtk_widget_unmap (item->_priv->grip);
789 static void
790 gdl_dock_item_realize (GtkWidget *widget)
792     GdkWindowAttr  attributes;
793     gint           attributes_mask;
794     GdlDockItem   *item;
796     g_return_if_fail (widget != NULL);
797     g_return_if_fail (GDL_IS_DOCK_ITEM (widget));
799     item = GDL_DOCK_ITEM (widget);
801     GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
803     /* widget window */
804     attributes.x = widget->allocation.x;
805     attributes.y = widget->allocation.y;
806     attributes.width = widget->allocation.width;
807     attributes.height = widget->allocation.height;
808     attributes.window_type = GDK_WINDOW_CHILD;
809     attributes.wclass = GDK_INPUT_OUTPUT;
810     attributes.visual = gtk_widget_get_visual (widget);
811     attributes.colormap = gtk_widget_get_colormap (widget);
812     attributes.event_mask = (gtk_widget_get_events (widget) |
813                              GDK_EXPOSURE_MASK |
814                              GDK_BUTTON1_MOTION_MASK |
815                              GDK_BUTTON_PRESS_MASK |
816                              GDK_BUTTON_RELEASE_MASK);
817     attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
818     widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), 
819                                      &attributes, attributes_mask);
820     gdk_window_set_user_data (widget->window, widget);
821   
822     widget->style = gtk_style_attach (widget->style, widget->window);
823     gtk_style_set_background (widget->style, widget->window, 
824                               GTK_WIDGET_STATE (item));
825     gdk_window_set_back_pixmap (widget->window, NULL, TRUE);
827     if (item->child)
828         gtk_widget_set_parent_window (item->child, widget->window);
829     
830     if (item->_priv->grip)
831         gtk_widget_set_parent_window (item->_priv->grip, widget->window);
834 static void
835 gdl_dock_item_style_set (GtkWidget *widget,
836                          GtkStyle  *previous_style)
838     g_return_if_fail (widget != NULL);
839     g_return_if_fail (GDL_IS_DOCK_ITEM (widget));
841     if (GTK_WIDGET_REALIZED (widget) && !GTK_WIDGET_NO_WINDOW (widget)) {
842         gtk_style_set_background (widget->style, widget->window,
843                                   widget->state);
844         if (GTK_WIDGET_DRAWABLE (widget))
845             gdk_window_clear (widget->window);
846     }
849 static void
850 gdl_dock_item_paint (GtkWidget      *widget,
851                      GdkEventExpose *event)
853     GdlDockItem  *item;
855     item = GDL_DOCK_ITEM (widget);
857     gtk_paint_box (widget->style,
858                    widget->window,
859                    GTK_WIDGET_STATE (widget),
860                    GTK_SHADOW_NONE,
861                    &event->area, widget,
862                    "dockitem",
863                    0, 0, -1, -1);
866 static gint
867 gdl_dock_item_expose (GtkWidget      *widget,
868                       GdkEventExpose *event)
870     g_return_val_if_fail (widget != NULL, FALSE);
871     g_return_val_if_fail (GDL_IS_DOCK_ITEM (widget), FALSE);
872     g_return_val_if_fail (event != NULL, FALSE);
874     if (GTK_WIDGET_DRAWABLE (widget) && event->window == widget->window) {
875         gdl_dock_item_paint (widget, event);
876         GDL_CALL_PARENT_GBOOLEAN(GTK_WIDGET_CLASS, expose_event, (widget,event));
877     }
878   
879     return FALSE;
882 #define EVENT_IN_GRIP_EVENT_WINDOW(ev,gr) \
883     ((gr) != NULL && (ev)->window == GDL_DOCK_ITEM_GRIP (gr)->title_window)
885 #define EVENT_IN_TABLABEL_EVENT_WINDOW(ev,tl) \
886     ((tl) != NULL && (ev)->window == GDL_DOCK_TABLABEL (tl)->event_window)
888 static gint
889 gdl_dock_item_button_changed (GtkWidget      *widget,
890                               GdkEventButton *event)
892     GdlDockItem *item;
893     gboolean     locked;
894     gboolean     event_handled;
895     gboolean     in_handle;
896     GdkCursor   *cursor;
897   
898     g_return_val_if_fail (widget != NULL, FALSE);
899     g_return_val_if_fail (GDL_IS_DOCK_ITEM (widget), FALSE);
900     g_return_val_if_fail (event != NULL, FALSE);
901     
902     item = GDL_DOCK_ITEM (widget);
904     if (!EVENT_IN_GRIP_EVENT_WINDOW (event, item->_priv->grip))
905         return FALSE;
906     
907     locked = !GDL_DOCK_ITEM_NOT_LOCKED (item);
909     event_handled = FALSE;
911     /* Check if user clicked on the drag handle. */      
912     switch (item->orientation) {
913     case GTK_ORIENTATION_HORIZONTAL:
914         in_handle = event->x < item->_priv->grip->allocation.width;
915         break;
916     case GTK_ORIENTATION_VERTICAL:
917         in_handle = event->y < item->_priv->grip->allocation.height;
918         break;
919     default:
920         in_handle = FALSE;
921         break;
922     }
924     /* Left mousebutton click on dockitem. */
925     if (!locked && event->button == 1 && event->type == GDK_BUTTON_PRESS) {
926         /* Set in_drag flag, grab pointer and call begin drag operation. */      
927         if (in_handle) {
928             item->_priv->start_x = event->x;
929             item->_priv->start_y = event->y;
931             GDL_DOCK_ITEM_SET_FLAGS (item, GDL_DOCK_IN_PREDRAG);
932             
933             cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget),
934                                                  GDK_FLEUR);
935             gdk_window_set_cursor (GDL_DOCK_ITEM_GRIP (item->_priv->grip)->title_window,
936                                    cursor);
937             gdk_cursor_unref (cursor);
938         
939             event_handled = TRUE;
940         };
941         
942     } else if (!locked &&event->type == GDK_BUTTON_RELEASE && event->button == 1) {
943         if (GDL_DOCK_ITEM_IN_DRAG (item)) {
944             /* User dropped widget somewhere. */
945             gdl_dock_item_drag_end (item, FALSE);
946             event_handled = TRUE;
947         }
948         else if (GDL_DOCK_ITEM_IN_PREDRAG (item)) {
949             GDL_DOCK_ITEM_UNSET_FLAGS (item, GDL_DOCK_IN_PREDRAG);
950             event_handled = TRUE;
951         }
953         /* we check the window since if the item was redocked it's
954            been unrealized and maybe it's not realized again yet */
955         if (GDL_DOCK_ITEM_GRIP (item->_priv->grip)->title_window) {
956             cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget),
957                                                  GDK_HAND2);
958             gdk_window_set_cursor (GDL_DOCK_ITEM_GRIP (item->_priv->grip)->title_window,
959                                    cursor);
960             gdk_cursor_unref (cursor);
961         }
963     } else if (event->button == 3 && event->type == GDK_BUTTON_PRESS && in_handle) {
964         gdl_dock_item_popup_menu (item, event->button, event->time);
965         event_handled = TRUE;           
966     }
968     return event_handled;
971 static gint
972 gdl_dock_item_motion (GtkWidget      *widget,
973                       GdkEventMotion *event)
975     GdlDockItem *item;
976     gint         new_x, new_y;
978     g_return_val_if_fail (widget != NULL, FALSE);
979     g_return_val_if_fail (GDL_IS_DOCK_ITEM (widget), FALSE);
980     g_return_val_if_fail (event != NULL, FALSE);
982     item = GDL_DOCK_ITEM (widget);
984     if (!EVENT_IN_GRIP_EVENT_WINDOW (event, item->_priv->grip))
985         return FALSE;
987     if (GDL_DOCK_ITEM_IN_PREDRAG (item)) {
988         if (gtk_drag_check_threshold (widget,
989                                       item->_priv->start_x,
990                                       item->_priv->start_y,
991                                       event->x,
992                                       event->y)) {
993             GDL_DOCK_ITEM_UNSET_FLAGS (item, GDL_DOCK_IN_PREDRAG);
994             item->dragoff_x = item->_priv->start_x;
995             item->dragoff_y = item->_priv->start_y;
997             gdl_dock_item_drag_start (item);
998         }
999     }
1000     
1001     if (!GDL_DOCK_ITEM_IN_DRAG (item))
1002         return FALSE;
1004     new_x = event->x_root;
1005     new_y = event->y_root;
1006     
1007     g_signal_emit (item, gdl_dock_item_signals [DOCK_DRAG_MOTION], 
1008                    0, new_x, new_y);
1010     return TRUE;
1013 static gboolean
1014 gdl_dock_item_key_press (GtkWidget   *widget,
1015                          GdkEventKey *event)
1017     gboolean event_handled = FALSE;
1018     if (GDL_DOCK_ITEM_IN_DRAG (widget)) {
1019         if (event->keyval == GDK_Escape) {
1020             gdl_dock_item_drag_end (GDL_DOCK_ITEM (widget), TRUE);
1021             event_handled = TRUE;
1022         }
1023     }
1025     if (event_handled)
1026         return TRUE;
1027     else
1028         return GDL_CALL_PARENT_WITH_DEFAULT (GTK_WIDGET_CLASS,
1029                                                key_press_event,
1030                                                (widget, event),
1031                                                FALSE);
1034 static gboolean
1035 gdl_dock_item_dock_request (GdlDockObject  *object,
1036                             gint            x,
1037                             gint            y,
1038                             GdlDockRequest *request)
1040     GtkAllocation *alloc;
1041     gint           rel_x, rel_y;
1043     /* we get (x,y) in our allocation coordinates system */
1044     
1045     /* Get item's allocation. */
1046     alloc = &(GTK_WIDGET (object)->allocation);
1047     
1048     /* Get coordinates relative to our window. */
1049     rel_x = x - alloc->x;
1050     rel_y = y - alloc->y;
1052     /* Location is inside. */
1053     if (rel_x > 0 && rel_x < alloc->width &&
1054         rel_y > 0 && rel_y < alloc->height) {
1055         float rx, ry;
1056         GtkRequisition my, other;
1057         gint divider = -1;
1058         
1059         /* this are for calculating the extra docking parameter */
1060         gdl_dock_item_preferred_size (GDL_DOCK_ITEM (request->applicant), &other);
1061         gdl_dock_item_preferred_size (GDL_DOCK_ITEM (object), &my);
1062         
1063         /* Calculate location in terms of the available space (0-100%). */
1064         rx = (float) rel_x / alloc->width;
1065         ry = (float) rel_y / alloc->height;
1067         /* Determine dock location. */
1068         if (rx < SPLIT_RATIO) {
1069             request->position = GDL_DOCK_LEFT;
1070             divider = other.width;
1071         }
1072         else if (rx > (1 - SPLIT_RATIO)) {
1073             request->position = GDL_DOCK_RIGHT;
1074             rx = 1 - rx;
1075             divider = MAX (0, my.width - other.width);
1076         }
1077         else if (ry < SPLIT_RATIO && ry < rx) {
1078             request->position = GDL_DOCK_TOP;
1079             divider = other.height;
1080         }
1081         else if (ry > (1 - SPLIT_RATIO) && (1 - ry) < rx) {
1082             request->position = GDL_DOCK_BOTTOM;
1083             divider = MAX (0, my.height - other.height);
1084         }
1085         else
1086             request->position = GDL_DOCK_CENTER;
1088         /* Reset rectangle coordinates to entire item. */
1089         request->rect.x = 0;
1090         request->rect.y = 0;
1091         request->rect.width = alloc->width;
1092         request->rect.height = alloc->height;
1094         GdlDockItemBehavior behavior = GDL_DOCK_ITEM(object)->behavior;
1096         /* Calculate docking indicator rectangle size for new locations. Only
1097            do this when we're not over the item's current location. */
1098         if (request->applicant != object) {
1099             switch (request->position) {
1100                 case GDL_DOCK_TOP:
1101                     if (behavior & GDL_DOCK_ITEM_BEH_CANT_DOCK_TOP)
1102                         return FALSE;
1103                     request->rect.height *= SPLIT_RATIO;
1104                     break;
1105                 case GDL_DOCK_BOTTOM:
1106                     if (behavior & GDL_DOCK_ITEM_BEH_CANT_DOCK_BOTTOM)
1107                         return FALSE;
1108                     request->rect.y += request->rect.height * (1 - SPLIT_RATIO);
1109                     request->rect.height *= SPLIT_RATIO;
1110                     break;
1111                 case GDL_DOCK_LEFT:
1112                     if (behavior & GDL_DOCK_ITEM_BEH_CANT_DOCK_LEFT)
1113                         return FALSE;
1114                     request->rect.width *= SPLIT_RATIO;
1115                     break;
1116                 case GDL_DOCK_RIGHT:
1117                     if (behavior & GDL_DOCK_ITEM_BEH_CANT_DOCK_RIGHT)
1118                         return FALSE;
1119                     request->rect.x += request->rect.width * (1 - SPLIT_RATIO);
1120                     request->rect.width *= SPLIT_RATIO;
1121                     break;
1122                 case GDL_DOCK_CENTER:
1123                     if (behavior & GDL_DOCK_ITEM_BEH_CANT_DOCK_CENTER)
1124                         return FALSE;
1125                     request->rect.x = request->rect.width * SPLIT_RATIO/2;
1126                     request->rect.y = request->rect.height * SPLIT_RATIO/2;
1127                     request->rect.width = (request->rect.width *
1128                                            (1 - SPLIT_RATIO/2)) - request->rect.x;
1129                     request->rect.height = (request->rect.height *
1130                                             (1 - SPLIT_RATIO/2)) - request->rect.y;
1131                     break;
1132                 default:
1133                     break;
1134             }
1135         }
1137         /* adjust returned coordinates so they are have the same
1138            origin as our window */
1139         request->rect.x += alloc->x;
1140         request->rect.y += alloc->y;
1141         
1142         /* Set possible target location and return TRUE. */            
1143         request->target = object;
1145         /* fill-in other dock information */
1146         if (request->position != GDL_DOCK_CENTER && divider >= 0) {
1147             if (G_IS_VALUE (&request->extra))
1148                 g_value_unset (&request->extra);
1149             g_value_init (&request->extra, G_TYPE_UINT);
1150             g_value_set_uint (&request->extra, (guint) divider);
1151         }
1152         
1153         return TRUE;         
1154     }
1155     else /* No docking possible at this location. */            
1156         return FALSE;
1159 static void
1160 gdl_dock_item_dock (GdlDockObject    *object,
1161                     GdlDockObject    *requestor,
1162                     GdlDockPlacement  position,
1163                     GValue           *other_data)
1165     GdlDockObject *new_parent, *parent;
1166     gboolean       add_ourselves_first;
1168     guint          available_space=0;
1169     gint           pref_size=-1;
1170     guint          splitpos=0;
1171     GtkRequisition req, object_req, parent_req;
1172     
1173     parent = gdl_dock_object_get_parent_object (object);
1174     gdl_dock_item_preferred_size (GDL_DOCK_ITEM (requestor), &req);
1175     gdl_dock_item_preferred_size (GDL_DOCK_ITEM (object), &object_req);
1176     if (GDL_IS_DOCK_ITEM (parent))
1177         gdl_dock_item_preferred_size (GDL_DOCK_ITEM (parent), &parent_req);
1178     else
1179     {
1180         parent_req.height = GTK_WIDGET (parent)->allocation.height;
1181         parent_req.width = GTK_WIDGET (parent)->allocation.width;
1182     }
1183     
1184     /* If preferred size is not set on the requestor (perhaps a new item),
1185      * then estimate and set it. The default value (either 0 or 1 pixels) is
1186      * not any good.
1187      */
1188     switch (position) {
1189         case GDL_DOCK_TOP:
1190         case GDL_DOCK_BOTTOM:
1191             if (req.width < 2)
1192             {
1193                 req.width = object_req.width;
1194                 g_object_set (requestor, "preferred-width", req.width, NULL);
1195             }
1196             if (req.height < 2)
1197             {
1198                 req.height = NEW_DOCK_ITEM_RATIO * object_req.height;
1199                 g_object_set (requestor, "preferred-height", req.height, NULL);
1200             }
1201             if (req.width > 1)
1202                 g_object_set (object, "preferred-width", req.width, NULL);
1203             if (req.height > 1)
1204                 g_object_set (object, "preferred-height",
1205                               object_req.height - req.height, NULL);
1206             break;
1207         case GDL_DOCK_LEFT:
1208         case GDL_DOCK_RIGHT:
1209             if (req.height < 2)
1210             {
1211                 req.height = object_req.height;
1212                 g_object_set (requestor, "preferred-height", req.height, NULL);
1213             }
1214             if (req.width < 2)
1215             {
1216                 req.width = NEW_DOCK_ITEM_RATIO * object_req.width;
1217                 g_object_set (requestor, "preferred-width", req.width, NULL);
1218             }
1219             if (req.height > 1)
1220                 g_object_set (object, "preferred-height", req.height, NULL);
1221             if (req.width > 1)
1222                 g_object_set (object, "preferred-width",
1223                           object_req.width - req.width, NULL);
1224             break;
1225         case GDL_DOCK_CENTER:
1226             if (req.height < 2)
1227             {
1228                 req.height = object_req.height;
1229                 g_object_set (requestor, "preferred-height", req.height, NULL);
1230             }
1231             if (req.width < 2)
1232             {
1233                 req.width = object_req.width;
1234                 g_object_set (requestor, "preferred-width", req.width, NULL);
1235             }
1236             if (req.height > 1)
1237                 g_object_set (object, "preferred-height", req.height, NULL);
1238             if (req.width > 1)
1239                 g_object_set (object, "preferred-width", req.width, NULL);
1240             break;
1241         default: 
1242         {
1243             GEnumClass *enum_class = G_ENUM_CLASS (g_type_class_ref (GDL_TYPE_DOCK_PLACEMENT));
1244             GEnumValue *enum_value = g_enum_get_value (enum_class, position);
1245             const gchar *name = enum_value ? enum_value->value_name : NULL;
1246             
1247             g_warning (_("Unsupported docking strategy %s in dock object of type %s"),
1248                        name,  G_OBJECT_TYPE_NAME (object));
1249             g_type_class_unref (enum_class);
1250             return;
1251         }
1252     }
1253     switch (position) {
1254         case GDL_DOCK_TOP:
1255         case GDL_DOCK_BOTTOM:
1256             /* get a paned style dock object */
1257             new_parent = g_object_new (gdl_dock_object_type_from_nick ("paned"),
1258                                        "orientation", GTK_ORIENTATION_VERTICAL,
1259                                        "preferred-width", object_req.width,
1260                                        "preferred-height", object_req.height,
1261                                        NULL);
1262             add_ourselves_first = (position == GDL_DOCK_BOTTOM);
1263             if (parent)
1264                 available_space = parent_req.height;
1265             pref_size = req.height;
1266             break;
1267         case GDL_DOCK_LEFT:
1268         case GDL_DOCK_RIGHT:
1269             new_parent = g_object_new (gdl_dock_object_type_from_nick ("paned"),
1270                                        "orientation", GTK_ORIENTATION_HORIZONTAL,
1271                                        "preferred-width", object_req.width,
1272                                        "preferred-height", object_req.height,
1273                                        NULL);
1274             add_ourselves_first = (position == GDL_DOCK_RIGHT);
1275             if(parent)
1276                 available_space = parent_req.width;
1277             pref_size = req.width;
1278             break;
1279         case GDL_DOCK_CENTER:
1280             new_parent = g_object_new (gdl_dock_object_type_from_nick ("notebook"),
1281                                        "preferred-width", object_req.width,
1282                                        "preferred-height", object_req.height,
1283                                        NULL);
1284             add_ourselves_first = TRUE;
1285             break;
1286         default: 
1287         {
1288             GEnumClass *enum_class = G_ENUM_CLASS (g_type_class_ref (GDL_TYPE_DOCK_PLACEMENT));
1289             GEnumValue *enum_value = g_enum_get_value (enum_class, position);
1290             const gchar *name = enum_value ? enum_value->value_name : NULL;
1291             
1292             g_warning (_("Unsupported docking strategy %s in dock object of type %s"),
1293                        name,  G_OBJECT_TYPE_NAME (object));
1294             g_type_class_unref (enum_class);
1295             return;
1296         }
1297     }
1299     /* freeze the parent so it doesn't reduce automatically */
1300     if (parent)
1301         gdl_dock_object_freeze (parent);
1303     /* ref ourselves since we could be destroyed when detached */
1304     g_object_ref (object);
1305     GDL_DOCK_OBJECT_SET_FLAGS (object, GDL_DOCK_IN_REFLOW);
1306     gdl_dock_object_detach (object, FALSE);
1308     /* freeze the new parent, so reduce won't get called before it's
1309        actually added to our parent */
1310     gdl_dock_object_freeze (new_parent);
1311     
1312     /* bind the new parent to our master, so the following adds work */
1313     gdl_dock_object_bind (new_parent, G_OBJECT (GDL_DOCK_OBJECT_GET_MASTER (object)));
1314     
1315     /* add the objects */
1316     if (add_ourselves_first) {
1317         gtk_container_add (GTK_CONTAINER (new_parent), GTK_WIDGET (object));
1318         gtk_container_add (GTK_CONTAINER (new_parent), GTK_WIDGET (requestor));
1319         splitpos = available_space - pref_size;
1320     } else {
1321         gtk_container_add (GTK_CONTAINER (new_parent), GTK_WIDGET (requestor));
1322         gtk_container_add (GTK_CONTAINER (new_parent), GTK_WIDGET (object));
1323         splitpos = pref_size;
1324     }
1326     /* add the new parent to the parent */
1327     if (parent)
1328         gtk_container_add (GTK_CONTAINER (parent), GTK_WIDGET (new_parent));
1330     /* show automatic object */
1331     if (GTK_WIDGET_VISIBLE (object))
1332         gtk_widget_show (GTK_WIDGET (new_parent));
1333     
1334     /* use extra docking parameter */
1335     if (position != GDL_DOCK_CENTER && other_data &&
1336         G_VALUE_HOLDS (other_data, G_TYPE_UINT)) {
1337         
1338         g_object_set (G_OBJECT (new_parent),
1339                       "position", g_value_get_uint (other_data),
1340                       NULL);
1341     } else if (splitpos > 0 && splitpos < available_space) {
1342         g_object_set (G_OBJECT (new_parent), "position", splitpos, NULL);
1343     }
1344     
1345     GDL_DOCK_OBJECT_UNSET_FLAGS (object, GDL_DOCK_IN_REFLOW);
1346     g_object_unref (object);
1348     gdl_dock_object_thaw (new_parent);
1349     if (parent)
1350         gdl_dock_object_thaw (parent);
1353 static void
1354 gdl_dock_item_detach_menu (GtkWidget *widget,
1355                            GtkMenu   *menu)
1357     GdlDockItem *item;
1358    
1359     item = GDL_DOCK_ITEM (widget);
1360     item->_priv->menu = NULL;
1363 static void
1364 gdl_dock_item_popup_menu (GdlDockItem  *item, 
1365                           guint         button,
1366                           guint32       time)
1368     GtkWidget *mitem;
1370     if (!item->_priv->menu) {
1371         /* Create popup menu and attach it to the dock item */
1372         item->_priv->menu = gtk_menu_new ();
1373         gtk_menu_attach_to_widget (GTK_MENU (item->_priv->menu),
1374                                    GTK_WIDGET (item),
1375                                    gdl_dock_item_detach_menu);
1376         
1377         if (item->behavior & GDL_DOCK_ITEM_BEH_LOCKED) {
1378             /* UnLock menuitem */
1379             mitem = gtk_menu_item_new_with_label (_("UnLock"));
1380             gtk_menu_shell_append (GTK_MENU_SHELL (item->_priv->menu), 
1381                                    mitem);
1382             g_signal_connect (mitem, "activate",
1383                               G_CALLBACK (gdl_dock_item_unlock_cb), item);
1384         } else {
1385             /* Hide menuitem. */
1386             mitem = gtk_menu_item_new_with_label (_("Hide"));
1387             gtk_menu_shell_append (GTK_MENU_SHELL (item->_priv->menu), mitem);
1388             g_signal_connect (mitem, "activate", 
1389                               G_CALLBACK (gdl_dock_item_hide_cb), item);
1390             /* Lock menuitem */
1391             mitem = gtk_menu_item_new_with_label (_("Lock"));
1392             gtk_menu_shell_append (GTK_MENU_SHELL (item->_priv->menu), mitem);
1393             g_signal_connect (mitem, "activate",
1394                               G_CALLBACK (gdl_dock_item_lock_cb), item);
1395         }
1396     }
1398     /* Show popup menu. */
1399     gtk_widget_show_all (item->_priv->menu);
1400     gtk_menu_popup (GTK_MENU (item->_priv->menu), NULL, NULL, NULL, NULL, 
1401                     button, time);
1404 static void
1405 gdl_dock_item_drag_start (GdlDockItem *item)
1407     GdkCursor *fleur;
1409     if (!GTK_WIDGET_REALIZED (item))
1410         gtk_widget_realize (GTK_WIDGET (item));
1411     
1412     GDL_DOCK_ITEM_SET_FLAGS (item, GDL_DOCK_IN_DRAG);
1413             
1414     /* grab the pointer so we receive all mouse events */
1415     fleur = gdk_cursor_new (GDK_FLEUR);
1417     /* grab the keyboard & pointer */
1418     gtk_grab_add (GTK_WIDGET (item));
1419     
1420     gdk_cursor_unref (fleur);
1421             
1422     g_signal_emit (item, gdl_dock_item_signals [DOCK_DRAG_BEGIN], 0);
1425 static void
1426 gdl_dock_item_drag_end (GdlDockItem *item,
1427                         gboolean     cancel)
1429     /* Release pointer & keyboard. */
1430     gtk_grab_remove (gtk_grab_get_current ());
1431     
1432     g_signal_emit (item, gdl_dock_item_signals [DOCK_DRAG_END], 0, cancel);
1433     
1434     GDL_DOCK_ITEM_UNSET_FLAGS (item, GDL_DOCK_IN_DRAG);
1437 static void 
1438 gdl_dock_item_tab_button (GtkWidget      *widget,
1439                           GdkEventButton *event,
1440                           gpointer        data)
1442     GdlDockItem *item;
1444     item = GDL_DOCK_ITEM (data);
1446     if (!GDL_DOCK_ITEM_NOT_LOCKED (item))
1447         return;
1449     switch (event->button) {
1450     case 1:
1451         /* set dragoff_{x,y} as we the user clicked on the middle of the 
1452            drag handle */
1453         switch (item->orientation) {
1454         case GTK_ORIENTATION_HORIZONTAL:
1455             /*item->dragoff_x = item->_priv->grip_size / 2;*/
1456             item->dragoff_y = GTK_WIDGET (data)->allocation.height / 2;
1457             break;
1458         case GTK_ORIENTATION_VERTICAL:
1459             /*item->dragoff_x = GTK_WIDGET (data)->allocation.width / 2;*/
1460             item->dragoff_y = item->_priv->grip_size / 2;
1461             break;
1462         };
1463         gdl_dock_item_drag_start (item);
1464         break;
1466     case 3:
1467         gdl_dock_item_popup_menu (item, event->button, event->time);
1468         break;
1470     default:
1471         break;
1472     };
1475 static void
1476 gdl_dock_item_hide_cb (GtkWidget   *widget, 
1477                        GdlDockItem *item)
1479     GdlDockMaster *master;
1480     
1481     g_return_if_fail (item != NULL);
1483     master = GDL_DOCK_OBJECT_GET_MASTER (item);
1484     gdl_dock_item_hide_item (item);
1487 static void
1488 gdl_dock_item_lock_cb (GtkWidget   *widget,
1489                        GdlDockItem *item)
1491     g_return_if_fail (item != NULL);
1493     gdl_dock_item_lock (item);
1496 static void
1497 gdl_dock_item_unlock_cb (GtkWidget   *widget,
1498                        GdlDockItem *item)
1500     g_return_if_fail (item != NULL);
1502     gdl_dock_item_unlock (item);
1505 static void
1506 gdl_dock_item_showhide_grip (GdlDockItem *item)
1508     GdkDisplay *display;
1509     GdkCursor *cursor;
1510     
1511     gdl_dock_item_detach_menu (GTK_WIDGET (item), NULL); 
1512     display = gtk_widget_get_display (GTK_WIDGET (item));
1513     cursor = NULL;
1514     
1515     if (item->_priv->grip) {
1516         if (GDL_DOCK_ITEM_GRIP_SHOWN (item) && 
1517             GDL_DOCK_ITEM_NOT_LOCKED(item))
1518              cursor = gdk_cursor_new_for_display (display, GDK_HAND2);
1519     }
1520     if (item->_priv->grip && GDL_DOCK_ITEM_GRIP (item->_priv->grip)->title_window)
1521         gdk_window_set_cursor (GDL_DOCK_ITEM_GRIP (item->_priv->grip)->title_window, cursor);
1523     if (cursor)
1524         gdk_cursor_unref (cursor);
1525     
1526     gtk_widget_queue_resize (GTK_WIDGET (item));
1529 static void
1530 gdl_dock_item_real_set_orientation (GdlDockItem    *item,
1531                                     GtkOrientation  orientation)
1533     item->orientation = orientation;
1534     
1535     if (GTK_WIDGET_DRAWABLE (item))
1536         gtk_widget_queue_draw (GTK_WIDGET (item));
1537     gtk_widget_queue_resize (GTK_WIDGET (item));
1541 /* ----- Public interface ----- */
1543 GtkWidget *
1544 gdl_dock_item_new (const gchar         *name,
1545                    const gchar         *long_name,
1546                    GdlDockItemBehavior  behavior)
1548     GdlDockItem *item;
1550     item = GDL_DOCK_ITEM (g_object_new (GDL_TYPE_DOCK_ITEM, 
1551                                         "name", name, 
1552                                         "long-name", long_name,
1553                                         "behavior", behavior,
1554                                         NULL));
1555     GDL_DOCK_OBJECT_UNSET_FLAGS (item, GDL_DOCK_AUTOMATIC);
1556     gdl_dock_item_set_tablabel (item, gtk_label_new (long_name));
1557     return GTK_WIDGET (item);
1560 GtkWidget *
1561 gdl_dock_item_new_with_stock (const gchar         *name,
1562                               const gchar         *long_name,
1563                               const gchar         *stock_id,
1564                               GdlDockItemBehavior  behavior)
1566     GdlDockItem *item;
1568     item = GDL_DOCK_ITEM (g_object_new (GDL_TYPE_DOCK_ITEM, 
1569                                         "name", name, 
1570                                         "long-name", long_name,
1571                                         "stock-id", stock_id,
1572                                         "behavior", behavior,
1573                                         NULL));
1574     GDL_DOCK_OBJECT_UNSET_FLAGS (item, GDL_DOCK_AUTOMATIC);
1575     gdl_dock_item_set_tablabel (item, gtk_label_new (long_name));
1577     return GTK_WIDGET (item);
1580 GtkWidget *
1581 gdl_dock_item_new_with_pixbuf_icon (const gchar         *name,
1582                                     const gchar         *long_name,
1583                                     const GdkPixbuf     *pixbuf_icon,
1584                                     GdlDockItemBehavior  behavior)
1586     GdlDockItem *item;
1588     item = GDL_DOCK_ITEM (g_object_new (GDL_TYPE_DOCK_ITEM, 
1589                                         "name", name, 
1590                                         "long-name", long_name,
1591                                         "pixbuf-icon", pixbuf_icon,
1592                                         "behavior", behavior,
1593                                         NULL));
1595     GDL_DOCK_OBJECT_UNSET_FLAGS (item, GDL_DOCK_AUTOMATIC);
1596     gdl_dock_item_set_tablabel (item, gtk_label_new (long_name));
1598     return GTK_WIDGET (item);
1601 /* convenient function (and to preserve source compat) */
1602 void
1603 gdl_dock_item_dock_to (GdlDockItem      *item,
1604                        GdlDockItem      *target,
1605                        GdlDockPlacement  position,
1606                        gint              docking_param)
1608     g_return_if_fail (item != NULL);
1609     g_return_if_fail (item != target);
1610     g_return_if_fail (target != NULL || position == GDL_DOCK_FLOATING);
1611     g_return_if_fail ((item->behavior & GDL_DOCK_ITEM_BEH_NEVER_FLOATING) == 0 || position != GDL_DOCK_FLOATING);
1613     if (position == GDL_DOCK_FLOATING || !target) {
1614         GdlDockObject *controller;
1616         if (!gdl_dock_object_is_bound (GDL_DOCK_OBJECT (item))) {
1617             g_warning (_("Attempt to bind an unbound item %p"), item);
1618             return;
1619         }
1621         controller = gdl_dock_master_get_controller (GDL_DOCK_OBJECT_GET_MASTER (item));
1622         
1623         /* FIXME: save previous docking position for later
1624            re-docking... does this make sense now? */
1626         /* Create new floating dock for widget. */
1627         item->dragoff_x = item->dragoff_y = 0;
1628         gdl_dock_add_floating_item (GDL_DOCK (controller),
1629                                     item, 0, 0, -1, -1);
1631     } else
1632         gdl_dock_object_dock (GDL_DOCK_OBJECT (target),
1633                               GDL_DOCK_OBJECT (item),
1634                               position, NULL);
1637 void
1638 gdl_dock_item_set_orientation (GdlDockItem    *item,
1639                                GtkOrientation  orientation)
1641     GParamSpec *pspec;
1643     g_return_if_fail (item != NULL);
1645     if (item->orientation != orientation) {
1646         /* push the property down the hierarchy if our child supports it */
1647         if (item->child != NULL) {
1648             pspec = g_object_class_find_property (
1649                 G_OBJECT_GET_CLASS (item->child), "orientation");
1650             if (pspec && pspec->value_type == GTK_TYPE_ORIENTATION)
1651                 g_object_set (G_OBJECT (item->child),
1652                               "orientation", orientation,
1653                               NULL);
1654         };
1656         GDL_CALL_VIRTUAL (item, GDL_DOCK_ITEM_GET_CLASS, set_orientation, (item, orientation));
1657         g_object_notify (G_OBJECT (item), "orientation");
1658     }
1661 GtkWidget *
1662 gdl_dock_item_get_tablabel (GdlDockItem *item)
1664     g_return_val_if_fail (item != NULL, NULL);
1665     g_return_val_if_fail (GDL_IS_DOCK_ITEM (item), NULL);
1667     return item->_priv->tab_label;
1670 void
1671 gdl_dock_item_set_tablabel (GdlDockItem *item,
1672                             GtkWidget   *tablabel)
1674     g_return_if_fail (item != NULL);
1676     if (item->_priv->tab_label) {
1677         /* disconnect and unref the previous tablabel */
1678         if (GDL_IS_DOCK_TABLABEL (item->_priv->tab_label)) {
1679             g_signal_handlers_disconnect_matched (item->_priv->tab_label,
1680                                                   G_SIGNAL_MATCH_DATA,
1681                                                   0, 0, NULL,
1682                                                   NULL, item);
1683             g_object_set (item->_priv->tab_label, "item", NULL, NULL);
1684         }
1685         gtk_widget_unref (item->_priv->tab_label);
1686         item->_priv->tab_label = NULL;
1687     }
1688     
1689     if (tablabel) {
1690         gtk_widget_ref (tablabel);
1691         gtk_object_sink (GTK_OBJECT (tablabel));
1692         item->_priv->tab_label = tablabel;
1693         if (GDL_IS_DOCK_TABLABEL (tablabel)) {
1694             g_object_set (tablabel, "item", item, NULL);
1695             /* connect to tablabel signal */
1696             g_signal_connect (tablabel, "button_pressed_handle",
1697                               G_CALLBACK (gdl_dock_item_tab_button), item);
1698         }
1699     }
1702 void 
1703 gdl_dock_item_hide_grip (GdlDockItem *item)
1705     g_return_if_fail (item != NULL);
1706     if (item->_priv->grip_shown) {
1707         item->_priv->grip_shown = FALSE;
1708         gdl_dock_item_showhide_grip (item);
1709     };
1710     g_warning ("Grips always show unless GDL_DOCK_ITEM_BEH_NO_GRIP is set\n" );
1713 void
1714 gdl_dock_item_show_grip (GdlDockItem *item)
1716     g_return_if_fail (item != NULL);
1717     if (!item->_priv->grip_shown) {
1718         item->_priv->grip_shown = TRUE;
1719         gdl_dock_item_showhide_grip (item);
1720     };
1723 /* convenient function (and to preserve source compat) */
1724 void
1725 gdl_dock_item_bind (GdlDockItem *item,
1726                     GtkWidget   *dock)
1728     g_return_if_fail (item != NULL);
1729     g_return_if_fail (dock == NULL || GDL_IS_DOCK (dock));
1730     
1731     gdl_dock_object_bind (GDL_DOCK_OBJECT (item),
1732                           G_OBJECT (GDL_DOCK_OBJECT_GET_MASTER (dock)));
1735 /* convenient function (and to preserve source compat) */
1736 void
1737 gdl_dock_item_unbind (GdlDockItem *item)
1739     g_return_if_fail (item != NULL);
1741     gdl_dock_object_unbind (GDL_DOCK_OBJECT (item));
1744 void
1745 gdl_dock_item_hide_item (GdlDockItem *item)
1747     g_return_if_fail (item != NULL);
1749     if (!GDL_DOCK_OBJECT_ATTACHED (item))
1750         /* already hidden/detached */
1751         return;
1752        
1753     /* if the object is manual, create a new placeholder to be able to
1754        restore the position later */
1755     if (!GDL_DOCK_OBJECT_AUTOMATIC (item)) {
1756         if (item->_priv->ph)
1757             g_object_unref (item->_priv->ph); 
1758         
1759         gboolean isFloating = FALSE;
1760         gint width=0, height=0, x=0, y = 0;
1761         
1762         if (GDL_IS_DOCK (gdl_dock_object_get_parent_object (GDL_DOCK_OBJECT (item))))
1763         {
1764             GdlDock* dock = GDL_DOCK (gdl_dock_object_get_parent_object (GDL_DOCK_OBJECT (item)));
1765             g_object_get (dock,
1766                           "floating", &isFloating, 
1767                           "width", &width,
1768                           "height",&height,
1769                           "floatx",&x,
1770                           "floaty",&y,
1771                           NULL);
1772         } else {
1773             item->_priv->preferred_width=GTK_WIDGET (item)->allocation.width;
1774             item->_priv->preferred_height=GTK_WIDGET (item)->allocation.height;
1775         }
1776         item->_priv->ph = GDL_DOCK_PLACEHOLDER (
1777             g_object_new (GDL_TYPE_DOCK_PLACEHOLDER,
1778                           "sticky", FALSE,
1779                           "host", item,
1780                           "width", width,
1781                           "height", height,
1782                           "floating", isFloating,
1783                           "floatx", x,
1784                           "floaty", y,
1785                           NULL));
1786         g_object_ref (item->_priv->ph);
1787         gtk_object_sink (GTK_OBJECT (item->_priv->ph));
1788     }
1789     
1790     gdl_dock_object_freeze (GDL_DOCK_OBJECT (item));
1791     
1792     /* hide our children first, so they can also set placeholders */
1793     if (gdl_dock_object_is_compound (GDL_DOCK_OBJECT (item))) 
1794         gtk_container_foreach (GTK_CONTAINER (item),
1795                                (GtkCallback) gdl_dock_item_hide_item,
1796                                NULL);
1797     
1798     /* detach the item recursively */
1799     gdl_dock_object_detach (GDL_DOCK_OBJECT (item), TRUE);
1801     gtk_widget_hide (GTK_WIDGET (item));
1803     gdl_dock_object_thaw (GDL_DOCK_OBJECT (item));
1806 void
1807 gdl_dock_item_iconify_item (GdlDockItem *item)
1809     g_return_if_fail (item != NULL);
1810     
1811     GDL_DOCK_OBJECT_SET_FLAGS (item, GDL_DOCK_ICONIFIED);
1812     gdl_dock_item_hide_item (item);
1815 void
1816 gdl_dock_item_show_item (GdlDockItem *item)
1818     g_return_if_fail (item != NULL);
1820     GDL_DOCK_OBJECT_UNSET_FLAGS (item, GDL_DOCK_ICONIFIED);
1821     
1822     if (item->_priv->ph) {
1823         gboolean isFloating=FALSE;
1824         gint width = 0, height = 0, x= 0, y = 0;
1825         g_object_get (G_OBJECT(item->_priv->ph),
1826                       "width", &width,
1827                       "height", &height,
1828                       "floating",&isFloating,
1829                       "floatx", &x,
1830                       "floaty", &y,
1831                       NULL);
1832         if (isFloating) {
1833             GdlDockObject *controller =
1834                 gdl_dock_master_get_controller (GDL_DOCK_OBJECT_GET_MASTER (item));
1835             gdl_dock_add_floating_item (GDL_DOCK (controller),
1836                                         item, x, y, width, height);
1837         } else {
1838             gtk_container_add (GTK_CONTAINER (item->_priv->ph),
1839                                GTK_WIDGET (item));
1840         }
1841         g_object_unref (item->_priv->ph);
1842         item->_priv->ph = NULL;
1843         
1844     } else if (gdl_dock_object_is_bound (GDL_DOCK_OBJECT (item))) {
1845         GdlDockObject *toplevel;
1846         
1847         toplevel = gdl_dock_master_get_controller
1848                         (GDL_DOCK_OBJECT_GET_MASTER (item));
1849         
1850         if (item->behavior & GDL_DOCK_ITEM_BEH_NEVER_FLOATING) {
1851             g_warning("Object %s has no default position and flag GDL_DOCK_ITEM_BEH_NEVER_FLOATING is set.\n",
1852                       GDL_DOCK_OBJECT(item)->name);
1853         } else if (toplevel) {
1854             gdl_dock_object_dock (toplevel, GDL_DOCK_OBJECT (item),
1855                                   GDL_DOCK_FLOATING, NULL);
1856         } else
1857             g_warning("There is no toplevel window. GdlDockItem %s cannot be shown.\n", GDL_DOCK_OBJECT(item)->name);
1858         
1859     } else
1860         g_warning("GdlDockItem %s is not bound. It cannot be shown.\n",
1861                   GDL_DOCK_OBJECT(item)->name);
1862     
1863     gtk_widget_show (GTK_WIDGET (item));
1866 void
1867 gdl_dock_item_lock (GdlDockItem *item)
1869     g_object_set (item, "locked", TRUE, NULL);
1872 void
1873 gdl_dock_item_unlock (GdlDockItem *item)
1875     g_object_set (item, "locked", FALSE, NULL);
1878 void 
1879 gdl_dock_item_set_default_position (GdlDockItem   *item,
1880                                     GdlDockObject *reference)
1882     g_return_if_fail (item != NULL);
1884     if (item->_priv->ph) {
1885         g_object_unref (item->_priv->ph);
1886         item->_priv->ph = NULL;
1887     }
1889     if (reference && GDL_DOCK_OBJECT_ATTACHED (reference)) {
1890         if (GDL_IS_DOCK_PLACEHOLDER (reference)) {
1891             g_object_ref (reference);
1892             gtk_object_sink (GTK_OBJECT (reference));
1893             item->_priv->ph = GDL_DOCK_PLACEHOLDER (reference);
1894         } else {
1895             item->_priv->ph = GDL_DOCK_PLACEHOLDER (
1896                 g_object_new (GDL_TYPE_DOCK_PLACEHOLDER,
1897                               "sticky", TRUE,
1898                               "host", reference,
1899                               NULL));
1900             g_object_ref (item->_priv->ph);
1901             gtk_object_sink (GTK_OBJECT (item->_priv->ph));
1902         }
1903     }
1906 void 
1907 gdl_dock_item_preferred_size (GdlDockItem    *item,
1908                               GtkRequisition *req)
1910     if (!req)
1911         return;
1913     req->width = MAX (item->_priv->preferred_width,
1914                       GTK_WIDGET (item)->allocation.width);
1915     req->height = MAX (item->_priv->preferred_height,
1916                        GTK_WIDGET (item)->allocation.height);
1920 /* ----- gtk orientation type exporter/importer ----- */
1922 static void 
1923 gdl_dock_param_export_gtk_orientation (const GValue *src,
1924                                        GValue       *dst)
1926     dst->data [0].v_pointer =
1927         g_strdup_printf ("%s", (src->data [0].v_int == GTK_ORIENTATION_HORIZONTAL) ?
1928                          "horizontal" : "vertical");
1931 static void 
1932 gdl_dock_param_import_gtk_orientation (const GValue *src,
1933                                        GValue       *dst)
1935     if (!strcmp (src->data [0].v_pointer, "horizontal"))
1936         dst->data [0].v_int = GTK_ORIENTATION_HORIZONTAL;
1937     else
1938         dst->data [0].v_int = GTK_ORIENTATION_VERTICAL;