Code

Removed libgdl component not used by Inkscape.
[inkscape.git] / src / libgdl / gdl-dock-master.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 
2  *
3  * gdl-dock-master.c - Object which manages a dock ring
4  *
5  * This file is part of the GNOME Devtools Libraries.
6  *
7  * Copyright (C) 2002 Gustavo Giráldez <gustavo.giraldez@gmx.net>
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
22  */
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
28 #include "gdl-i18n.h"
30 #include "gdl-tools.h"
31 #include "gdl-dock-master.h"
32 #include "gdl-dock.h"
33 #include "gdl-dock-item.h"
34 #include "libgdlmarshal.h"
35 #include "libgdltypebuiltins.h"
37 /* ----- Private prototypes ----- */
39 static void     gdl_dock_master_class_init    (GdlDockMasterClass *klass);
40 static void     gdl_dock_master_instance_init (GdlDockMaster      *master);
42 static void     gdl_dock_master_dispose       (GObject            *g_object);
43 static void     gdl_dock_master_set_property  (GObject            *object,
44                                                guint               prop_id,
45                                                const GValue       *value,
46                                                GParamSpec         *pspec);
47 static void     gdl_dock_master_get_property  (GObject            *object,
48                                                guint               prop_id,
49                                                GValue             *value,
50                                                GParamSpec         *pspec);
52 static void     _gdl_dock_master_remove       (GdlDockObject      *object,
53                                                GdlDockMaster      *master);
55 static void     gdl_dock_master_drag_begin    (GdlDockItem        *item, 
56                                                gpointer            data);
57 static void     gdl_dock_master_drag_end      (GdlDockItem        *item,
58                                                gboolean            cancelled,
59                                                gpointer            data);
60 static void     gdl_dock_master_drag_motion   (GdlDockItem        *item, 
61                                                gint                x, 
62                                                gint                y,
63                                                gpointer            data);
65 static void     _gdl_dock_master_foreach      (gpointer            key,
66                                                gpointer            value,
67                                                gpointer            user_data);
69 static void     gdl_dock_master_xor_rect      (GdlDockMaster      *master);
71 static void     gdl_dock_master_layout_changed (GdlDockMaster     *master);
73 static void gdl_dock_master_set_switcher_style (GdlDockMaster *master,
74                                                 GdlSwitcherStyle switcher_style);
76 /* ----- Private data types and variables ----- */
78 enum {
79     PROP_0,
80     PROP_DEFAULT_TITLE,
81     PROP_LOCKED,
82     PROP_SWITCHER_STYLE
83 };
85 enum {
86     LAYOUT_CHANGED,
87     LAST_SIGNAL
88 };
90 struct _GdlDockMasterPrivate {
91     gint            number;             /* for naming nameless manual objects */
92     gchar          *default_title;
93     
94     GdkGC          *root_xor_gc;
95     gboolean        rect_drawn;
96     GdlDock        *rect_owner;
97     
98     GdlDockRequest *drag_request;
100     /* source id for the idle handler to emit a layout_changed signal */
101     guint           idle_layout_changed_id;
103     /* hashes to quickly calculate the overall locked status: i.e.
104      * if size(unlocked_items) == 0 then locked = 1
105      * else if size(locked_items) == 0 then locked = 0
106      * else locked = -1
107      */
108     GHashTable     *locked_items;
109     GHashTable     *unlocked_items;
110     
111     GdlSwitcherStyle switcher_style;
112 };
114 #define COMPUTE_LOCKED(master)                                          \
115     (g_hash_table_size ((master)->_priv->unlocked_items) == 0 ? 1 :     \
116      (g_hash_table_size ((master)->_priv->locked_items) == 0 ? 0 : -1))
118 static guint master_signals [LAST_SIGNAL] = { 0 };
121 /* ----- Private interface ----- */
123 GDL_CLASS_BOILERPLATE (GdlDockMaster, gdl_dock_master, GObject, G_TYPE_OBJECT);
125 static void
126 gdl_dock_master_class_init (GdlDockMasterClass *klass)
128     GObjectClass      *g_object_class;
130     g_object_class = G_OBJECT_CLASS (klass);
132     g_object_class->dispose = gdl_dock_master_dispose;
133     g_object_class->set_property = gdl_dock_master_set_property;
134     g_object_class->get_property = gdl_dock_master_get_property;
136     g_object_class_install_property (
137         g_object_class, PROP_DEFAULT_TITLE,
138         g_param_spec_string ("default-title", _("Default title"),
139                              _("Default title for newly created floating docks"),
140                              NULL,
141                              G_PARAM_READWRITE));
142     
143     g_object_class_install_property (
144         g_object_class, PROP_LOCKED,
145         g_param_spec_int ("locked", _("Locked"),
146                           _("If is set to 1, all the dock items bound to the master "
147                             "are locked; if it's 0, all are unlocked; -1 indicates "
148                             "inconsistency among the items"),
149                           -1, 1, 0,
150                           G_PARAM_READWRITE));
152     g_object_class_install_property (
153         g_object_class, PROP_SWITCHER_STYLE,
154         g_param_spec_enum ("switcher-style", _("Switcher Style"),
155                            _("Switcher buttons style"),
156                            GDL_TYPE_SWITCHER_STYLE,
157                            GDL_SWITCHER_STYLE_BOTH,
158                            G_PARAM_READWRITE));
160     master_signals [LAYOUT_CHANGED] = 
161         g_signal_new ("layout-changed", 
162                       G_TYPE_FROM_CLASS (klass),
163                       G_SIGNAL_RUN_LAST,
164                       G_STRUCT_OFFSET (GdlDockMasterClass, layout_changed),
165                       NULL, /* accumulator */
166                       NULL, /* accu_data */
167                       gdl_marshal_VOID__VOID,
168                       G_TYPE_NONE, /* return type */
169                       0);
171     klass->layout_changed = gdl_dock_master_layout_changed;
174 static void
175 gdl_dock_master_instance_init (GdlDockMaster *master)
177     master->dock_objects = g_hash_table_new_full (g_str_hash, g_str_equal,
178                                                   g_free, NULL);
179     master->toplevel_docks = NULL;
180     master->controller = NULL;
181     master->dock_number = 1;
182     
183     master->_priv = g_new0 (GdlDockMasterPrivate, 1);
184     master->_priv->number = 1;
185     master->_priv->switcher_style = GDL_SWITCHER_STYLE_BOTH;
186     master->_priv->locked_items = g_hash_table_new (g_direct_hash, g_direct_equal);
187     master->_priv->unlocked_items = g_hash_table_new (g_direct_hash, g_direct_equal);
190 static void
191 _gdl_dock_master_remove (GdlDockObject *object,
192                          GdlDockMaster *master)
194     g_return_if_fail (master != NULL && object != NULL);
196     if (GDL_IS_DOCK (object)) {
197         GList *found_link;
199         found_link = g_list_find (master->toplevel_docks, object);
200         if (found_link)
201             master->toplevel_docks = g_list_delete_link (master->toplevel_docks,
202                                                          found_link);
203         if (object == master->controller) {
204             GList *last;
205             GdlDockObject *new_controller = NULL;
206             
207             /* now find some other non-automatic toplevel to use as a
208                new controller.  start from the last dock, since it's
209                probably a non-floating and manual */
210             last = g_list_last (master->toplevel_docks);
211             while (last) {
212                 if (!GDL_DOCK_OBJECT_AUTOMATIC (last->data)) {
213                     new_controller = GDL_DOCK_OBJECT (last->data);
214                     break;
215                 }
216                 last = last->prev;
217             };
219             if (new_controller) {
220                 /* the new controller gets the ref (implicitly of course) */
221                 master->controller = new_controller;
222             } else {
223                 master->controller = NULL;
224                 /* no controller, no master */
225                 g_object_unref (master);
226             }
227         }
228     }
229     /* disconnect dock object signals */
230     g_signal_handlers_disconnect_matched (object, G_SIGNAL_MATCH_DATA, 
231                                           0, 0, NULL, NULL, master);
233     /* unref the object from the hash if it's there */
234     if (object->name) {
235         GdlDockObject *found_object;
236         found_object = g_hash_table_lookup (master->dock_objects, object->name);
237         if (found_object == object) {
238             g_hash_table_remove (master->dock_objects, object->name);
239             g_object_unref (object);
240         }
241     }
244 static void
245 ht_foreach_build_slist (gpointer  key,
246                         gpointer  value,
247                         GSList  **slist)
249     *slist = g_slist_prepend (*slist, value);
252 static void
253 gdl_dock_master_dispose (GObject *g_object)
255     GdlDockMaster *master;
256     
257     g_return_if_fail (GDL_IS_DOCK_MASTER (g_object));
259     master = GDL_DOCK_MASTER (g_object);
261     if (master->toplevel_docks) {
262         g_list_foreach (master->toplevel_docks,
263                         (GFunc) gdl_dock_object_unbind, NULL);
264         g_list_free (master->toplevel_docks);
265         master->toplevel_docks = NULL;
266     }
267     
268     if (master->dock_objects) {
269         GSList *alive_docks = NULL;
270         g_hash_table_foreach (master->dock_objects,
271                               (GHFunc) ht_foreach_build_slist, &alive_docks);
272         while (alive_docks) {
273             gdl_dock_object_unbind (GDL_DOCK_OBJECT (alive_docks->data));
274             alive_docks = g_slist_delete_link (alive_docks, alive_docks);
275         }
276         
277         g_hash_table_destroy (master->dock_objects);
278         master->dock_objects = NULL;
279     }
280     
281     if (master->_priv) {
282         if (master->_priv->idle_layout_changed_id)
283             g_source_remove (master->_priv->idle_layout_changed_id);
284         
285         if (master->_priv->root_xor_gc) {
286             g_object_unref (master->_priv->root_xor_gc);
287             master->_priv->root_xor_gc = NULL;
288         }
289         if (master->_priv->drag_request) {
290             if (G_IS_VALUE (&master->_priv->drag_request->extra))
291                 g_value_unset (&master->_priv->drag_request->extra);
292             g_free (master->_priv->drag_request);
293             master->_priv->drag_request = NULL;
294         }
295         g_free (master->_priv->default_title);
296         master->_priv->default_title = NULL;
298         g_hash_table_destroy (master->_priv->locked_items);
299         master->_priv->locked_items = NULL;
300         g_hash_table_destroy (master->_priv->unlocked_items);
301         master->_priv->unlocked_items = NULL;
302         
303         g_free (master->_priv);
304         master->_priv = NULL;
305     }
307     GDL_CALL_PARENT (G_OBJECT_CLASS, dispose, (g_object));
310 static void 
311 foreach_lock_unlock (GdlDockItem *item,
312                      gboolean     locked)
314     if (!GDL_IS_DOCK_ITEM (item))
315         return;
316     
317     g_object_set (item, "locked", locked, NULL);
318     if (gdl_dock_object_is_compound (GDL_DOCK_OBJECT (item)))
319         gtk_container_foreach (GTK_CONTAINER (item),
320                                (GtkCallback) foreach_lock_unlock,
321                                GINT_TO_POINTER (locked));
324 static void
325 gdl_dock_master_lock_unlock (GdlDockMaster *master,
326                              gboolean       locked)
328     GList *l;
329     
330     for (l = master->toplevel_docks; l; l = l->next) {
331         GdlDock *dock = GDL_DOCK (l->data);
332         if (dock->root)
333             foreach_lock_unlock (GDL_DOCK_ITEM (dock->root), locked);
334     }
336     /* just to be sure hidden items are set too */
337     gdl_dock_master_foreach (master,
338                              (GFunc) foreach_lock_unlock,
339                              GINT_TO_POINTER (locked));
342 static void
343 gdl_dock_master_set_property  (GObject      *object,
344                                guint         prop_id,
345                                const GValue *value,
346                                GParamSpec   *pspec)
348     GdlDockMaster *master = GDL_DOCK_MASTER (object);
350     switch (prop_id) {
351         case PROP_DEFAULT_TITLE:
352             g_free (master->_priv->default_title);
353             master->_priv->default_title = g_value_dup_string (value);
354             break;
355         case PROP_LOCKED:
356             if (g_value_get_int (value) >= 0)
357                 gdl_dock_master_lock_unlock (master, (g_value_get_int (value) > 0));
358             break;
359         case PROP_SWITCHER_STYLE:
360             gdl_dock_master_set_switcher_style (master, g_value_get_enum (value));
361             break;
362         default:
363             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
364             break;
365     }
368 static void
369 gdl_dock_master_get_property  (GObject      *object,
370                                guint         prop_id,
371                                GValue       *value,
372                                GParamSpec   *pspec)
374     GdlDockMaster *master = GDL_DOCK_MASTER (object);
376     switch (prop_id) {
377         case PROP_DEFAULT_TITLE:
378             g_value_set_string (value, master->_priv->default_title);
379             break;
380         case PROP_LOCKED:
381             g_value_set_int (value, COMPUTE_LOCKED (master));
382             break;
383         case PROP_SWITCHER_STYLE:
384             g_value_set_enum (value, master->_priv->switcher_style);
385             break;
386         default:
387             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
388             break;
389     }
392 static void
393 gdl_dock_master_drag_begin (GdlDockItem *item,
394                             gpointer     data)
396     GdlDockMaster  *master;
397     GdlDockRequest *request;
398     
399     g_return_if_fail (data != NULL);
400     g_return_if_fail (item != NULL);
402     master = GDL_DOCK_MASTER (data);
404     if (!master->_priv->drag_request)
405         master->_priv->drag_request = g_new0 (GdlDockRequest, 1);
407     request = master->_priv->drag_request;
408     
409     /* Set the target to itself so it won't go floating with just a click. */
410     request->applicant = GDL_DOCK_OBJECT (item);
411     request->target = GDL_DOCK_OBJECT (item);
412     request->position = GDL_DOCK_FLOATING;
413     if (G_IS_VALUE (&request->extra))
414         g_value_unset (&request->extra);
416     master->_priv->rect_drawn = FALSE;
417     master->_priv->rect_owner = NULL;
420 static void
421 gdl_dock_master_drag_end (GdlDockItem *item, 
422                           gboolean     cancelled,
423                           gpointer     data)
425     GdlDockMaster  *master;
426     GdlDockRequest *request;
427     
428     g_return_if_fail (data != NULL);
429     g_return_if_fail (item != NULL);
431     master = GDL_DOCK_MASTER (data);
432     request = master->_priv->drag_request;
433     
434     g_return_if_fail (GDL_DOCK_OBJECT (item) == request->applicant);
435     
436     /* Erase previously drawn rectangle */
437     if (master->_priv->rect_drawn)
438         gdl_dock_master_xor_rect (master);
439     
440     /* cancel conditions */
441     if (cancelled || request->applicant == request->target)
442         return;
443     
444     /* dock object to the requested position */
445     gdl_dock_object_dock (request->target,
446                           request->applicant,
447                           request->position,
448                           &request->extra);
449     
450     g_signal_emit (master, master_signals [LAYOUT_CHANGED], 0);
453 static void
454 gdl_dock_master_drag_motion (GdlDockItem *item, 
455                              gint         root_x, 
456                              gint         root_y,
457                              gpointer     data)
459     GdlDockMaster  *master;
460     GdlDockRequest  my_request, *request;
461     GdkWindow      *window;
462     gint            win_x, win_y;
463     gint            x, y;
464     GdlDock        *dock = NULL;
465     gboolean        may_dock = FALSE;
466     
467     g_return_if_fail (item != NULL && data != NULL);
469     master = GDL_DOCK_MASTER (data);
470     request = master->_priv->drag_request;
472     g_return_if_fail (GDL_DOCK_OBJECT (item) == request->applicant);
473     
474     my_request = *request;
476     /* first look under the pointer */
477     window = gdk_window_at_pointer (&win_x, &win_y);
478     if (window) {
479         GtkWidget *widget;
480         /* ok, now get the widget who owns that window and see if we can
481            get to a GdlDock by walking up the hierarchy */
482         gdk_window_get_user_data (window, (gpointer) &widget);
483         if (GTK_IS_WIDGET (widget)) {
484             while (widget && (!GDL_IS_DOCK (widget) || 
485                    GDL_DOCK_OBJECT_GET_MASTER (widget) != master))
486                 widget = widget->parent;
487             if (widget) {
488                 gint win_w, win_h;
489                 
490                 /* verify that the pointer is still in that dock
491                    (the user could have moved it) */
492                 gdk_window_get_geometry (widget->window,
493                                          NULL, NULL, &win_w, &win_h, NULL);
494                 gdk_window_get_origin (widget->window, &win_x, &win_y);
495                 if (root_x >= win_x && root_x < win_x + win_w &&
496                     root_y >= win_y && root_y < win_y + win_h)
497                     dock = GDL_DOCK (widget);
498             }
499         }
500     }
502     if (dock) {
503         /* translate root coordinates into dock object coordinates
504            (i.e. widget coordinates) */
505         gdk_window_get_origin (GTK_WIDGET (dock)->window, &win_x, &win_y);
506         x = root_x - win_x;
507         y = root_y - win_y;
508         may_dock = gdl_dock_object_dock_request (GDL_DOCK_OBJECT (dock),
509                                                  x, y, &my_request);
510     }
511     else {
512         GList *l;
514         /* try to dock the item in all the docks in the ring in turn */
515         for (l = master->toplevel_docks; l; l = l->next) {
516             dock = GDL_DOCK (l->data);
517             /* translate root coordinates into dock object coordinates
518                (i.e. widget coordinates) */
519             gdk_window_get_origin (GTK_WIDGET (dock)->window, &win_x, &win_y);
520             x = root_x - win_x;
521             y = root_y - win_y;
522             may_dock = gdl_dock_object_dock_request (GDL_DOCK_OBJECT (dock),
523                                                      x, y, &my_request);
524             if (may_dock)
525                 break;
526         }
527     }
529   
530     if (!may_dock) {
531         GtkRequisition req;
532         /* Special case for GdlDockItems : they must respect the flags */
533         if(GDL_IS_DOCK_ITEM(item)
534         && GDL_DOCK_ITEM(item)->behavior & GDL_DOCK_ITEM_BEH_NEVER_FLOATING)
535             return;
537         dock = NULL;
538         my_request.target = GDL_DOCK_OBJECT (
539             gdl_dock_object_get_toplevel (request->applicant));
540         my_request.position = GDL_DOCK_FLOATING;
542         gdl_dock_item_preferred_size (GDL_DOCK_ITEM (request->applicant), &req);
543         my_request.rect.width = req.width;
544         my_request.rect.height = req.height;
546         my_request.rect.x = root_x - GDL_DOCK_ITEM (request->applicant)->dragoff_x;
547         my_request.rect.y = root_y - GDL_DOCK_ITEM (request->applicant)->dragoff_y;
549         /* setup extra docking information */
550         if (G_IS_VALUE (&my_request.extra))
551             g_value_unset (&my_request.extra);
553         g_value_init (&my_request.extra, GDK_TYPE_RECTANGLE);
554         g_value_set_boxed (&my_request.extra, &my_request.rect);
555     }
556     /* if we want to enforce GDL_DOCK_ITEM_BEH_NEVER_FLOATING           */
557     /* the item must remain attached to the controller, otherwise       */
558     /* it could be inserted in another floating dock                    */
559     /* so check for the flag at this moment                             */
560     else if(GDL_IS_DOCK_ITEM(item)
561         && GDL_DOCK_ITEM(item)->behavior & GDL_DOCK_ITEM_BEH_NEVER_FLOATING
562         && dock != GDL_DOCK(master->controller))
563             return;
565     if (!(my_request.rect.x == request->rect.x &&
566           my_request.rect.y == request->rect.y &&
567           my_request.rect.width == request->rect.width &&
568           my_request.rect.height == request->rect.height &&
569           dock == master->_priv->rect_owner)) {
570         
571         /* erase the previous rectangle */
572         if (master->_priv->rect_drawn)
573             gdl_dock_master_xor_rect (master);
574     }
576     /* set the new values */
577     *request = my_request;
578     master->_priv->rect_owner = dock;
579     
580     /* draw the previous rectangle */
581     if (~master->_priv->rect_drawn)
582         gdl_dock_master_xor_rect (master);
585 static void
586 _gdl_dock_master_foreach (gpointer key,
587                           gpointer value,
588                           gpointer user_data)
590     (void)key;
591     struct {
592         GFunc    function;
593         gpointer user_data;
594     } *data = user_data;
596     (* data->function) (GTK_WIDGET (value), data->user_data);
599 static void
600 gdl_dock_master_xor_rect (GdlDockMaster *master)
602     gint8         dash_list [2];
603     GdkWindow    *window;
604     GdkRectangle *rect;
605     
606     if (!master->_priv || !master->_priv->drag_request)
607         return;
608     
609     master->_priv->rect_drawn = ~master->_priv->rect_drawn;
610     
611     if (master->_priv->rect_owner) {
612         gdl_dock_xor_rect (master->_priv->rect_owner,
613                            &master->_priv->drag_request->rect);
614         return;
615     }
616     
617     rect = &master->_priv->drag_request->rect;
618     window = gdk_get_default_root_window ();
620     if (!master->_priv->root_xor_gc) {
621         GdkGCValues values;
623         values.function = GDK_INVERT;
624         values.subwindow_mode = GDK_INCLUDE_INFERIORS;
625         master->_priv->root_xor_gc = gdk_gc_new_with_values (
626             window, &values, GDK_GC_FUNCTION | GDK_GC_SUBWINDOW);
627     };
629     gdk_gc_set_line_attributes (master->_priv->root_xor_gc, 1,
630                                 GDK_LINE_ON_OFF_DASH,
631                                 GDK_CAP_NOT_LAST,
632                                 GDK_JOIN_BEVEL);
633     
634     dash_list[0] = 1;
635     dash_list[1] = 1;
636     gdk_gc_set_dashes (master->_priv->root_xor_gc, 1, dash_list, 2);
638     gdk_draw_rectangle (window, master->_priv->root_xor_gc, 0, 
639                         rect->x, rect->y,
640                         rect->width, rect->height);
642     gdk_gc_set_dashes (master->_priv->root_xor_gc, 0, dash_list, 2);
644     gdk_draw_rectangle (window, master->_priv->root_xor_gc, 0, 
645                         rect->x + 1, rect->y + 1,
646                         rect->width - 2, rect->height - 2);
649 static void
650 gdl_dock_master_layout_changed (GdlDockMaster *master)
652     g_return_if_fail (GDL_IS_DOCK_MASTER (master));
654     /* emit "layout-changed" on the controller to notify the user who
655      * normally shouldn't have access to us */
656     if (master->controller)
657         g_signal_emit_by_name (master->controller, "layout-changed");
659     /* remove the idle handler if there is one */
660     if (master->_priv->idle_layout_changed_id) {
661         g_source_remove (master->_priv->idle_layout_changed_id);
662         master->_priv->idle_layout_changed_id = 0;
663     }
666 static gboolean
667 idle_emit_layout_changed (gpointer user_data)
669     GdlDockMaster *master = user_data;
671     g_return_val_if_fail (master && GDL_IS_DOCK_MASTER (master), FALSE);
673     master->_priv->idle_layout_changed_id = 0;
674     g_signal_emit (master, master_signals [LAYOUT_CHANGED], 0);
675     
676     return FALSE;
679 static void 
680 item_dock_cb (GdlDockObject    *object,
681               GdlDockObject    *requestor,
682               GdlDockPlacement  position,
683               GValue           *other_data,
684               gpointer          user_data)
686     GdlDockMaster *master = user_data;
687     
688     g_return_if_fail (requestor && GDL_IS_DOCK_OBJECT (requestor));
689     g_return_if_fail (master && GDL_IS_DOCK_MASTER (master));
691     /* here we are in fact interested in the requestor, since it's
692      * assumed that object will not change its visibility... for the
693      * requestor, however, could mean that it's being shown */
694     if (!GDL_DOCK_OBJECT_IN_REFLOW (requestor) &&
695         !GDL_DOCK_OBJECT_AUTOMATIC (requestor)) {
696         if (!master->_priv->idle_layout_changed_id)
697             master->_priv->idle_layout_changed_id =
698                 g_idle_add (idle_emit_layout_changed, master);
699     }
702 static void 
703 item_detach_cb (GdlDockObject *object,
704                 gboolean       recursive,
705                 gpointer       user_data)
707     GdlDockMaster *master = user_data;
708     
709     g_return_if_fail (object && GDL_IS_DOCK_OBJECT (object));
710     g_return_if_fail (master && GDL_IS_DOCK_MASTER (master));
712     if (!GDL_DOCK_OBJECT_IN_REFLOW (object) &&
713         !GDL_DOCK_OBJECT_AUTOMATIC (object)) {
714         if (!master->_priv->idle_layout_changed_id)
715             master->_priv->idle_layout_changed_id =
716                 g_idle_add (idle_emit_layout_changed, master);
717     }
720 static void
721 item_notify_cb (GdlDockObject *object,
722                 GParamSpec    *pspec,
723                 gpointer       user_data)
725     GdlDockMaster *master = user_data;
726     gint locked = COMPUTE_LOCKED (master);
727     gboolean item_locked;
728     
729     g_object_get (object, "locked", &item_locked, NULL);
731     if (item_locked) {
732         g_hash_table_remove (master->_priv->unlocked_items, object);
733         g_hash_table_insert (master->_priv->locked_items, object, NULL);
734     } else {
735         g_hash_table_remove (master->_priv->locked_items, object);
736         g_hash_table_insert (master->_priv->unlocked_items, object, NULL);
737     }
738     
739     if (COMPUTE_LOCKED (master) != locked)
740         g_object_notify (G_OBJECT (master), "locked");
743 /* ----- Public interface ----- */
745 void
746 gdl_dock_master_add (GdlDockMaster *master,
747                      GdlDockObject *object)
749     g_return_if_fail (master != NULL && object != NULL);
751     if (!GDL_DOCK_OBJECT_AUTOMATIC (object)) {
752         GdlDockObject *found_object;
753         
754         /* create a name for the object if it doesn't have one */
755         if (!object->name)
756             /* directly set the name, since it's a construction only
757                property */
758             object->name = g_strdup_printf ("__dock_%u", master->_priv->number++);
759         
760         /* add the object to our hash list */
761         if ((found_object = g_hash_table_lookup (master->dock_objects, object->name))) {
762             g_warning (_("master %p: unable to add object %p[%s] to the hash.  "
763                          "There already is an item with that name (%p)."),
764                        master, object, object->name, found_object);
765         }
766         else {
767             g_object_ref (object);
768             gtk_object_sink (GTK_OBJECT (object));
769             g_hash_table_insert (master->dock_objects, g_strdup (object->name), object);
770         }
771     }
772     
773     if (GDL_IS_DOCK (object)) {
774         gboolean floating;
775         
776         /* if this is the first toplevel we are adding, name it controller */
777         if (!master->toplevel_docks)
778             /* the dock should already have the ref */
779             master->controller = object;
780         
781         /* add dock to the toplevel list */
782         g_object_get (object, "floating", &floating, NULL);
783         if (floating)
784             master->toplevel_docks = g_list_prepend (master->toplevel_docks, object);
785         else
786             master->toplevel_docks = g_list_append (master->toplevel_docks, object);
788         /* we are interested in the dock request this toplevel
789          * receives to update the layout */
790         g_signal_connect (object, "dock",
791                           G_CALLBACK (item_dock_cb), master);
793     }
794     else if (GDL_IS_DOCK_ITEM (object)) {
795         /* we need to connect the item's signals */
796         g_signal_connect (object, "dock_drag_begin",
797                           G_CALLBACK (gdl_dock_master_drag_begin), master);
798         g_signal_connect (object, "dock_drag_motion",
799                           G_CALLBACK (gdl_dock_master_drag_motion), master);
800         g_signal_connect (object, "dock_drag_end",
801                           G_CALLBACK (gdl_dock_master_drag_end), master);
802         g_signal_connect (object, "dock",
803                           G_CALLBACK (item_dock_cb), master);
804         g_signal_connect (object, "detach",
805                           G_CALLBACK (item_detach_cb), master);
807         /* register to "locked" notification if the item has a grip,
808          * and add the item to the corresponding hash */
809         if (GDL_DOCK_ITEM_HAS_GRIP (GDL_DOCK_ITEM (object))) {
810             g_signal_connect (object, "notify::locked",
811                               G_CALLBACK (item_notify_cb), master);
812             item_notify_cb (object, NULL, master);
813         }
814         
815         /* If the item is notebook, set the switcher style */
816         if (GDL_IS_DOCK_NOTEBOOK (object) &&
817             GDL_IS_SWITCHER (GDL_DOCK_ITEM (object)->child))
818         {
819             g_object_set (GDL_DOCK_ITEM (object)->child, "switcher-style",
820                           master->_priv->switcher_style, NULL);
821         }
822         
823         /* post a layout_changed emission if the item is not automatic
824          * (since it should be added to the items model) */
825         if (!GDL_DOCK_OBJECT_AUTOMATIC (object)) {
826             if (!master->_priv->idle_layout_changed_id)
827                 master->_priv->idle_layout_changed_id =
828                     g_idle_add (idle_emit_layout_changed, master);
829         }
830     }
833 void
834 gdl_dock_master_remove (GdlDockMaster *master,
835                         GdlDockObject *object)
837     g_return_if_fail (master != NULL && object != NULL);
839     /* remove from locked/unlocked hashes and property change if
840      * that's the case */
841     if (GDL_IS_DOCK_ITEM (object) && GDL_DOCK_ITEM_HAS_GRIP (GDL_DOCK_ITEM (object))) {
842         gint locked = COMPUTE_LOCKED (master);
843         if (g_hash_table_remove (master->_priv->locked_items, object) ||
844             g_hash_table_remove (master->_priv->unlocked_items, object)) {
845             if (COMPUTE_LOCKED (master) != locked)
846                 g_object_notify (G_OBJECT (master), "locked");
847         }
848     }
849         
850     /* ref the master, since removing the controller could cause master disposal */
851     g_object_ref (master);
852     
853     /* all the interesting stuff happens in _gdl_dock_master_remove */
854     _gdl_dock_master_remove (object, master);
856     /* post a layout_changed emission if the item is not automatic
857      * (since it should be removed from the items model) */
858     if (!GDL_DOCK_OBJECT_AUTOMATIC (object)) {
859         if (!master->_priv->idle_layout_changed_id)
860             master->_priv->idle_layout_changed_id =
861                 g_idle_add (idle_emit_layout_changed, master);
862     }
863     
864     /* balance ref count */
865     g_object_unref (master);
868 void
869 gdl_dock_master_foreach (GdlDockMaster *master,
870                          GFunc          function,
871                          gpointer       user_data)
873     struct {
874         GFunc    function;
875         gpointer user_data;
876     } data;
878     g_return_if_fail (master != NULL && function != NULL);
880     data.function = function;
881     data.user_data = user_data;
882     g_hash_table_foreach (master->dock_objects, _gdl_dock_master_foreach, &data);
885 void
886 gdl_dock_master_foreach_toplevel (GdlDockMaster *master,
887                                   gboolean       include_controller,
888                                   GFunc          function,
889                                   gpointer       user_data)
891     GList *l;
892     
893     g_return_if_fail (master != NULL && function != NULL);
895     for (l = master->toplevel_docks; l; ) {
896         GdlDockObject *object = GDL_DOCK_OBJECT (l->data);
897         l = l->next;
898         if (object != master->controller || include_controller)
899             (* function) (GTK_WIDGET (object), user_data);
900     }
903 GdlDockObject *
904 gdl_dock_master_get_object (GdlDockMaster *master,
905                             const gchar   *nick_name)
907     gpointer *found;
908     
909     g_return_val_if_fail (master != NULL, NULL);
911     if (!nick_name)
912         return NULL;
914     found = g_hash_table_lookup (master->dock_objects, nick_name);
916     return found ? GDL_DOCK_OBJECT (found) : NULL;
919 GdlDockObject *
920 gdl_dock_master_get_controller (GdlDockMaster *master)
922     g_return_val_if_fail (master != NULL, NULL);
924     return master->controller;
927 void
928 gdl_dock_master_set_controller (GdlDockMaster *master,
929                                 GdlDockObject *new_controller)
931     g_return_if_fail (master != NULL);
933     if (new_controller) {
934         if (GDL_DOCK_OBJECT_AUTOMATIC (new_controller))
935             g_warning (_("The new dock controller %p is automatic.  Only manual "
936                          "dock objects should be named controller."), new_controller);
937         
938         /* check that the controller is in the toplevel list */
939         if (!g_list_find (master->toplevel_docks, new_controller))
940             gdl_dock_master_add (master, new_controller);
941         master->controller = new_controller;
943     } else {
944         master->controller = NULL;
945         /* no controller, no master */
946         g_object_unref (master);
947     }
950 static void
951 set_switcher_style_foreach (GtkWidget *obj, gpointer user_data)
953     GdlSwitcherStyle style = GPOINTER_TO_INT (user_data);
954     
955     if (!GDL_IS_DOCK_ITEM (obj))
956         return;
957     
958     if (GDL_IS_DOCK_NOTEBOOK (obj)) {
959         
960         GtkWidget *child = GDL_DOCK_ITEM (obj)->child;
961         if (GDL_IS_SWITCHER (child)) {
962             
963             g_object_set (child, "switcher-style", style, NULL);
964         }
965     } else if (gdl_dock_object_is_compound (GDL_DOCK_OBJECT (obj))) {
966         
967         gtk_container_foreach (GTK_CONTAINER (obj),
968                                set_switcher_style_foreach,
969                                user_data);
970     }
973 static void
974 gdl_dock_master_set_switcher_style (GdlDockMaster *master,
975                                     GdlSwitcherStyle switcher_style)
977     GList *l;
978     g_return_if_fail (GDL_IS_DOCK_MASTER (master));
979     
980     master->_priv->switcher_style = switcher_style;
981     for (l = master->toplevel_docks; l; l = l->next) {
982         GdlDock *dock = GDL_DOCK (l->data);
983         if (dock->root)
984             set_switcher_style_foreach (GTK_WIDGET (dock->root),
985                                         GINT_TO_POINTER (switcher_style));
986     }
988     /* just to be sure hidden items are set too */
989     gdl_dock_master_foreach (master, (GFunc) set_switcher_style_foreach,
990                              GINT_TO_POINTER (switcher_style));