Code

Extensions. XAML export improvements.
[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"
36 #ifdef WIN32
37 #include "gdl-win32.h"
38 #endif
40 /* ----- Private prototypes ----- */
42 static void     gdl_dock_master_class_init    (GdlDockMasterClass *klass);
43 static void     gdl_dock_master_instance_init (GdlDockMaster      *master);
45 static void     gdl_dock_master_dispose       (GObject            *g_object);
46 static void     gdl_dock_master_set_property  (GObject            *object,
47                                                guint               prop_id,
48                                                const GValue       *value,
49                                                GParamSpec         *pspec);
50 static void     gdl_dock_master_get_property  (GObject            *object,
51                                                guint               prop_id,
52                                                GValue             *value,
53                                                GParamSpec         *pspec);
55 static void     _gdl_dock_master_remove       (GdlDockObject      *object,
56                                                GdlDockMaster      *master);
58 static void     gdl_dock_master_drag_begin    (GdlDockItem        *item, 
59                                                gpointer            data);
60 static void     gdl_dock_master_drag_end      (GdlDockItem        *item,
61                                                gboolean            cancelled,
62                                                gpointer            data);
63 static void     gdl_dock_master_drag_motion   (GdlDockItem        *item, 
64                                                gint                x, 
65                                                gint                y,
66                                                gpointer            data);
68 static void     _gdl_dock_master_foreach      (gpointer            key,
69                                                gpointer            value,
70                                                gpointer            user_data);
72 static void     gdl_dock_master_xor_rect      (GdlDockMaster      *master);
74 static void     gdl_dock_master_layout_changed (GdlDockMaster     *master);
76 static void gdl_dock_master_set_switcher_style (GdlDockMaster *master,
77                                                 GdlSwitcherStyle switcher_style);
79 /* ----- Private data types and variables ----- */
81 enum {
82     PROP_0,
83     PROP_DEFAULT_TITLE,
84     PROP_LOCKED,
85     PROP_SWITCHER_STYLE,
86     PROP_EXPANSION_DIRECTION
87 };
89 enum {
90     LAYOUT_CHANGED,
91     LAST_SIGNAL
92 };
94 struct _GdlDockMasterPrivate {
95     gint            number;             /* for naming nameless manual objects */
96     gchar          *default_title;
97     
98     GdkGC          *root_xor_gc;
99     gboolean        rect_drawn;
100     GdlDock        *rect_owner;
101     
102     GdlDockRequest *drag_request;
104     /* source id for the idle handler to emit a layout_changed signal */
105     guint           idle_layout_changed_id;
107     /* hashes to quickly calculate the overall locked status: i.e.
108      * if size(unlocked_items) == 0 then locked = 1
109      * else if size(locked_items) == 0 then locked = 0
110      * else locked = -1
111      */
112     GHashTable     *locked_items;
113     GHashTable     *unlocked_items;
114     
115     GdlSwitcherStyle switcher_style;
117     GdlDockExpansionDirection expansion_direction;
118 };
120 #define COMPUTE_LOCKED(master)                                          \
121     (g_hash_table_size ((master)->_priv->unlocked_items) == 0 ? 1 :     \
122      (g_hash_table_size ((master)->_priv->locked_items) == 0 ? 0 : -1))
124 static guint master_signals [LAST_SIGNAL] = { 0 };
127 /* ----- Private interface ----- */
129 GDL_CLASS_BOILERPLATE (GdlDockMaster, gdl_dock_master, GObject, G_TYPE_OBJECT);
131 static void
132 gdl_dock_master_class_init (GdlDockMasterClass *klass)
134     GObjectClass      *g_object_class;
136     g_object_class = G_OBJECT_CLASS (klass);
138     g_object_class->dispose = gdl_dock_master_dispose;
139     g_object_class->set_property = gdl_dock_master_set_property;
140     g_object_class->get_property = gdl_dock_master_get_property;
142     g_object_class_install_property (
143         g_object_class, PROP_DEFAULT_TITLE,
144         g_param_spec_string ("default-title", _("Default title"),
145                              _("Default title for newly created floating docks"),
146                              NULL,
147                              G_PARAM_READWRITE));
148     
149     g_object_class_install_property (
150         g_object_class, PROP_LOCKED,
151         g_param_spec_int ("locked", _("Locked"),
152                           _("If is set to 1, all the dock items bound to the master "
153                             "are locked; if it's 0, all are unlocked; -1 indicates "
154                             "inconsistency among the items"),
155                           -1, 1, 0,
156                           G_PARAM_READWRITE));
158     g_object_class_install_property (
159         g_object_class, PROP_SWITCHER_STYLE,
160         g_param_spec_enum ("switcher-style", _("Switcher Style"),
161                            _("Switcher buttons style"),
162                            GDL_TYPE_SWITCHER_STYLE,
163                            GDL_SWITCHER_STYLE_BOTH,
164                            G_PARAM_READWRITE));
166     g_object_class_install_property (
167         g_object_class, PROP_EXPANSION_DIRECTION,
168         g_param_spec_enum ("expand-direction", _("Expand direction"),
169                            _("Allow the master's dock items to expand their container "
170                              "dock objects in the given direction"),
171                            GDL_TYPE_EXPANSION_DIRECTION,
172                            GDL_DOCK_EXPANSION_DIRECTION_NONE,
173                            G_PARAM_READWRITE));
175     master_signals [LAYOUT_CHANGED] = 
176         g_signal_new ("layout-changed", 
177                       G_TYPE_FROM_CLASS (klass),
178                       G_SIGNAL_RUN_LAST,
179                       G_STRUCT_OFFSET (GdlDockMasterClass, layout_changed),
180                       NULL, /* accumulator */
181                       NULL, /* accu_data */
182                       gdl_marshal_VOID__VOID,
183                       G_TYPE_NONE, /* return type */
184                       0);
186     klass->layout_changed = gdl_dock_master_layout_changed;
189 static void
190 gdl_dock_master_instance_init (GdlDockMaster *master)
192     master->dock_objects = g_hash_table_new_full (g_str_hash, g_str_equal,
193                                                   g_free, NULL);
194     master->toplevel_docks = NULL;
195     master->controller = NULL;
196     master->dock_number = 1;
197     
198     master->_priv = g_new0 (GdlDockMasterPrivate, 1);
199     master->_priv->number = 1;
200     master->_priv->switcher_style = GDL_SWITCHER_STYLE_BOTH;
201     master->_priv->expansion_direction = GDL_DOCK_EXPANSION_DIRECTION_NONE;
202     master->_priv->locked_items = g_hash_table_new (g_direct_hash, g_direct_equal);
203     master->_priv->unlocked_items = g_hash_table_new (g_direct_hash, g_direct_equal);
206 static void
207 _gdl_dock_master_remove (GdlDockObject *object,
208                          GdlDockMaster *master)
210     g_return_if_fail (master != NULL && object != NULL);
212     if (GDL_IS_DOCK (object)) {
213         GList *found_link;
215         found_link = g_list_find (master->toplevel_docks, object);
216         if (found_link)
217             master->toplevel_docks = g_list_delete_link (master->toplevel_docks,
218                                                          found_link);
219         if (object == master->controller) {
220             GList *last;
221             GdlDockObject *new_controller = NULL;
222             
223             /* now find some other non-automatic toplevel to use as a
224                new controller.  start from the last dock, since it's
225                probably a non-floating and manual */
226             last = g_list_last (master->toplevel_docks);
227             while (last) {
228                 if (!GDL_DOCK_OBJECT_AUTOMATIC (last->data)) {
229                     new_controller = GDL_DOCK_OBJECT (last->data);
230                     break;
231                 }
232                 last = last->prev;
233             };
235             if (new_controller) {
236                 /* the new controller gets the ref (implicitly of course) */
237                 master->controller = new_controller;
238             } else {
239                 master->controller = NULL;
240                 /* no controller, no master */
241                 g_object_unref (master);
242             }
243         }
244     }
245     /* disconnect dock object signals */
246     g_signal_handlers_disconnect_matched (object, G_SIGNAL_MATCH_DATA, 
247                                           0, 0, NULL, NULL, master);
249     /* unref the object from the hash if it's there */
250     if (object->name) {
251         GdlDockObject *found_object;
252         found_object = g_hash_table_lookup (master->dock_objects, object->name);
253         if (found_object == object) {
254             g_hash_table_remove (master->dock_objects, object->name);
255             g_object_unref (object);
256         }
257     }
260 static void
261 ht_foreach_build_slist (gpointer  key,
262                         gpointer  value,
263                         GSList  **slist)
265     *slist = g_slist_prepend (*slist, value);
268 static void
269 gdl_dock_master_dispose (GObject *g_object)
271     GdlDockMaster *master;
272     
273     g_return_if_fail (GDL_IS_DOCK_MASTER (g_object));
275     master = GDL_DOCK_MASTER (g_object);
277     if (master->toplevel_docks) {
278         g_list_foreach (master->toplevel_docks,
279                         (GFunc) gdl_dock_object_unbind, NULL);
280         g_list_free (master->toplevel_docks);
281         master->toplevel_docks = NULL;
282     }
283     
284     if (master->dock_objects) {
285         GSList *alive_docks = NULL;
286         g_hash_table_foreach (master->dock_objects,
287                               (GHFunc) ht_foreach_build_slist, &alive_docks);
288         while (alive_docks) {
289             gdl_dock_object_unbind (GDL_DOCK_OBJECT (alive_docks->data));
290             alive_docks = g_slist_delete_link (alive_docks, alive_docks);
291         }
292         
293         g_hash_table_destroy (master->dock_objects);
294         master->dock_objects = NULL;
295     }
296     
297     if (master->_priv) {
298         if (master->_priv->idle_layout_changed_id)
299             g_source_remove (master->_priv->idle_layout_changed_id);
300         
301         if (master->_priv->root_xor_gc) {
302             g_object_unref (master->_priv->root_xor_gc);
303             master->_priv->root_xor_gc = NULL;
304         }
305         if (master->_priv->drag_request) {
306             if (G_IS_VALUE (&master->_priv->drag_request->extra))
307                 g_value_unset (&master->_priv->drag_request->extra);
308             g_free (master->_priv->drag_request);
309             master->_priv->drag_request = NULL;
310         }
311         g_free (master->_priv->default_title);
312         master->_priv->default_title = NULL;
314         g_hash_table_destroy (master->_priv->locked_items);
315         master->_priv->locked_items = NULL;
316         g_hash_table_destroy (master->_priv->unlocked_items);
317         master->_priv->unlocked_items = NULL;
318         
319         g_free (master->_priv);
320         master->_priv = NULL;
321     }
323     GDL_CALL_PARENT (G_OBJECT_CLASS, dispose, (g_object));
326 static void 
327 foreach_lock_unlock (GdlDockItem *item,
328                      gboolean     locked)
330     if (!GDL_IS_DOCK_ITEM (item))
331         return;
332     
333     g_object_set (item, "locked", locked, NULL);
334     if (gdl_dock_object_is_compound (GDL_DOCK_OBJECT (item)))
335         gtk_container_foreach (GTK_CONTAINER (item),
336                                (GtkCallback) foreach_lock_unlock,
337                                GINT_TO_POINTER (locked));
340 static void
341 gdl_dock_master_lock_unlock (GdlDockMaster *master,
342                              gboolean       locked)
344     GList *l;
345     
346     for (l = master->toplevel_docks; l; l = l->next) {
347         GdlDock *dock = GDL_DOCK (l->data);
348         if (dock->root)
349             foreach_lock_unlock (GDL_DOCK_ITEM (dock->root), locked);
350     }
352     /* just to be sure hidden items are set too */
353     gdl_dock_master_foreach (master,
354                              (GFunc) foreach_lock_unlock,
355                              GINT_TO_POINTER (locked));
358 static void
359 gdl_dock_master_set_property  (GObject      *object,
360                                guint         prop_id,
361                                const GValue *value,
362                                GParamSpec   *pspec)
364     GdlDockMaster *master = GDL_DOCK_MASTER (object);
366     switch (prop_id) {
367         case PROP_DEFAULT_TITLE:
368             g_free (master->_priv->default_title);
369             master->_priv->default_title = g_value_dup_string (value);
370             break;
371         case PROP_LOCKED:
372             if (g_value_get_int (value) >= 0)
373                 gdl_dock_master_lock_unlock (master, (g_value_get_int (value) > 0));
374             break;
375         case PROP_SWITCHER_STYLE:
376             gdl_dock_master_set_switcher_style (master, g_value_get_enum (value));
377             break;
378         case PROP_EXPANSION_DIRECTION:
379             master->_priv->expansion_direction = g_value_get_enum (value);
380             break;
381         default:
382             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
383             break;
384     }
387 static void
388 gdl_dock_master_get_property  (GObject      *object,
389                                guint         prop_id,
390                                GValue       *value,
391                                GParamSpec   *pspec)
393     GdlDockMaster *master = GDL_DOCK_MASTER (object);
395     switch (prop_id) {
396         case PROP_DEFAULT_TITLE:
397             g_value_set_string (value, master->_priv->default_title);
398             break;
399         case PROP_LOCKED:
400             g_value_set_int (value, COMPUTE_LOCKED (master));
401             break;
402         case PROP_SWITCHER_STYLE:
403             g_value_set_enum (value, master->_priv->switcher_style);
404             break;
405         case PROP_EXPANSION_DIRECTION:
406             g_value_set_enum (value, master->_priv->expansion_direction);
407             break;
408         default:
409             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
410             break;
411     }
414 static void
415 gdl_dock_master_drag_begin (GdlDockItem *item,
416                             gpointer     data)
418     GdlDockMaster  *master;
419     GdlDockRequest *request;
420     
421     g_return_if_fail (data != NULL);
422     g_return_if_fail (item != NULL);
424     master = GDL_DOCK_MASTER (data);
426     if (!master->_priv->drag_request)
427         master->_priv->drag_request = g_new0 (GdlDockRequest, 1);
429     request = master->_priv->drag_request;
430     
431     /* Set the target to itself so it won't go floating with just a click. */
432     request->applicant = GDL_DOCK_OBJECT (item);
433     request->target = GDL_DOCK_OBJECT (item);
434     request->position = GDL_DOCK_FLOATING;
435     if (G_IS_VALUE (&request->extra))
436         g_value_unset (&request->extra);
438     master->_priv->rect_drawn = FALSE;
439     master->_priv->rect_owner = NULL;
442 static void
443 gdl_dock_master_drag_end (GdlDockItem *item, 
444                           gboolean     cancelled,
445                           gpointer     data)
447     GdlDockMaster  *master;
448     GdlDockRequest *request;
449     
450     g_return_if_fail (data != NULL);
451     g_return_if_fail (item != NULL);
453     master = GDL_DOCK_MASTER (data);
454     request = master->_priv->drag_request;
455     
456     g_return_if_fail (GDL_DOCK_OBJECT (item) == request->applicant);
457     
458     /* Erase previously drawn rectangle */
459     if (master->_priv->rect_drawn)
460         gdl_dock_master_xor_rect (master);
461     
462     /* cancel conditions */
463     if (cancelled || request->applicant == request->target)
464         return;
465     
466     /* dock object to the requested position */
467     gdl_dock_object_dock (request->target,
468                           request->applicant,
469                           request->position,
470                           &request->extra);
471     
472     g_signal_emit (master, master_signals [LAYOUT_CHANGED], 0);
475 static void
476 gdl_dock_master_drag_motion (GdlDockItem *item, 
477                              gint         root_x, 
478                              gint         root_y,
479                              gpointer     data)
481     GdlDockMaster  *master;
482     GdlDockRequest  my_request, *request;
483     GdkWindow      *window;
484     gint            win_x, win_y;
485     gint            x, y;
486     GdlDock        *dock = NULL;
487     gboolean        may_dock = FALSE;
488     
489     g_return_if_fail (item != NULL && data != NULL);
491     master = GDL_DOCK_MASTER (data);
492     request = master->_priv->drag_request;
494     g_return_if_fail (GDL_DOCK_OBJECT (item) == request->applicant);
495     
496     my_request = *request;
498     /* first look under the pointer */
499     window = gdk_window_at_pointer (&win_x, &win_y);
500     if (window) {
501         GtkWidget *widget;
502         /* ok, now get the widget who owns that window and see if we can
503            get to a GdlDock by walking up the hierarchy */
504         gdk_window_get_user_data (window, (gpointer) &widget);
505         if (GTK_IS_WIDGET (widget)) {
506             while (widget && (!GDL_IS_DOCK (widget) || 
507                    GDL_DOCK_OBJECT_GET_MASTER (widget) != master))
508                 widget = widget->parent;
509             if (widget) {
510                 gint win_w, win_h;
511                 
512                 /* verify that the pointer is still in that dock
513                    (the user could have moved it) */
514                 gdk_window_get_geometry (widget->window,
515                                          NULL, NULL, &win_w, &win_h, NULL);
516                 gdk_window_get_origin (widget->window, &win_x, &win_y);
517                 if (root_x >= win_x && root_x < win_x + win_w &&
518                     root_y >= win_y && root_y < win_y + win_h)
519                     dock = GDL_DOCK (widget);
520             }
521         }
522     }
524     if (dock) {
525         /* translate root coordinates into dock object coordinates
526            (i.e. widget coordinates) */
527         gdk_window_get_origin (GTK_WIDGET (dock)->window, &win_x, &win_y);
528         x = root_x - win_x;
529         y = root_y - win_y;
530         may_dock = gdl_dock_object_dock_request (GDL_DOCK_OBJECT (dock),
531                                                  x, y, &my_request);
532     }
533     else {
534         GList *l;
536         /* try to dock the item in all the docks in the ring in turn */
537         for (l = master->toplevel_docks; l; l = l->next) {
538             dock = GDL_DOCK (l->data);
539             /* translate root coordinates into dock object coordinates
540                (i.e. widget coordinates) */
541             gdk_window_get_origin (GTK_WIDGET (dock)->window, &win_x, &win_y);
542             x = root_x - win_x;
543             y = root_y - win_y;
544             may_dock = gdl_dock_object_dock_request (GDL_DOCK_OBJECT (dock),
545                                                      x, y, &my_request);
546             if (may_dock)
547                 break;
548         }
549     }
551   
552     if (!may_dock) {
553         GtkRequisition req;
554         /* Special case for GdlDockItems : they must respect the flags */
555         if(GDL_IS_DOCK_ITEM(item)
556         && GDL_DOCK_ITEM(item)->behavior & GDL_DOCK_ITEM_BEH_NEVER_FLOATING)
557             return;
559         dock = NULL;
560         my_request.target = GDL_DOCK_OBJECT (
561             gdl_dock_object_get_toplevel (request->applicant));
562         my_request.position = GDL_DOCK_FLOATING;
564         gdl_dock_item_preferred_size (GDL_DOCK_ITEM (request->applicant), &req);
565         my_request.rect.width = req.width;
566         my_request.rect.height = req.height;
568         my_request.rect.x = root_x - GDL_DOCK_ITEM (request->applicant)->dragoff_x;
569         my_request.rect.y = root_y - GDL_DOCK_ITEM (request->applicant)->dragoff_y;
571         /* setup extra docking information */
572         if (G_IS_VALUE (&my_request.extra))
573             g_value_unset (&my_request.extra);
575         g_value_init (&my_request.extra, GDK_TYPE_RECTANGLE);
576         g_value_set_boxed (&my_request.extra, &my_request.rect);
577     }
578     /* if we want to enforce GDL_DOCK_ITEM_BEH_NEVER_FLOATING           */
579     /* the item must remain attached to the controller, otherwise       */
580     /* it could be inserted in another floating dock                    */
581     /* so check for the flag at this moment                             */
582     else if(GDL_IS_DOCK_ITEM(item)
583         && GDL_DOCK_ITEM(item)->behavior & GDL_DOCK_ITEM_BEH_NEVER_FLOATING
584         && dock != GDL_DOCK(master->controller))
585             return;
587     if (!(my_request.rect.x == request->rect.x &&
588           my_request.rect.y == request->rect.y &&
589           my_request.rect.width == request->rect.width &&
590           my_request.rect.height == request->rect.height &&
591           dock == master->_priv->rect_owner)) {
592         
593         /* erase the previous rectangle */
594         if (master->_priv->rect_drawn)
595             gdl_dock_master_xor_rect (master);
596     }
598     /* set the new values */
599     *request = my_request;
600     master->_priv->rect_owner = dock;
601     
602     /* draw the previous rectangle */
603     if (~master->_priv->rect_drawn)
604         gdl_dock_master_xor_rect (master);
607 static void
608 _gdl_dock_master_foreach (gpointer key,
609                           gpointer value,
610                           gpointer user_data)
612     (void)key;
613     struct {
614         GFunc    function;
615         gpointer user_data;
616     } *data = user_data;
618     (* data->function) (GTK_WIDGET (value), data->user_data);
621 static void
622 gdl_dock_master_xor_rect (GdlDockMaster *master)
624     gint8         dash_list [2];
625     GdkWindow    *window;
626     GdkRectangle *rect;
627     
628     if (!master->_priv || !master->_priv->drag_request)
629         return;
630     
631     master->_priv->rect_drawn = ~master->_priv->rect_drawn;
632     
633     if (master->_priv->rect_owner) {
634         gdl_dock_xor_rect (master->_priv->rect_owner,
635                            &master->_priv->drag_request->rect);
636         return;
637     }
638     
639     rect = &master->_priv->drag_request->rect;
640     window = gdk_get_default_root_window ();
642     if (!master->_priv->root_xor_gc) {
643         GdkGCValues values;
645         values.function = GDK_INVERT;
646         values.subwindow_mode = GDK_INCLUDE_INFERIORS;
647         master->_priv->root_xor_gc = gdk_gc_new_with_values (
648             window, &values, GDK_GC_FUNCTION | GDK_GC_SUBWINDOW);
649     };
651 #ifdef WIN32    
652     GdkLineStyle lineStyle = GDK_LINE_ON_OFF_DASH;
653     if (is_os_vista())
654     {
655         // On Vista the dash-line is increadibly slow to draw, it takes several minutes to draw the tracking lines
656         // With GDK_LINE_SOLID it is parts of a second
657         // No performance issue on WinXP
658         lineStyle = GDK_LINE_SOLID;
659     }
660 #else
661     GdkLineStyle lineStyle = GDK_LINE_ON_OFF_DASH;
662 #endif
663     gdk_gc_set_line_attributes (master->_priv->root_xor_gc, 1,
664                                 lineStyle,
665                                 GDK_CAP_NOT_LAST,
666                                 GDK_JOIN_BEVEL);
667     
668     dash_list[0] = 1;
669     dash_list[1] = 1;
670     gdk_gc_set_dashes (master->_priv->root_xor_gc, 1, dash_list, 2);
672     gdk_draw_rectangle (window, master->_priv->root_xor_gc, 0, 
673                         rect->x, rect->y,
674                         rect->width, rect->height);
676     gdk_gc_set_dashes (master->_priv->root_xor_gc, 0, dash_list, 2);
678     gdk_draw_rectangle (window, master->_priv->root_xor_gc, 0, 
679                         rect->x + 1, rect->y + 1,
680                         rect->width - 2, rect->height - 2);
683 static void
684 gdl_dock_master_layout_changed (GdlDockMaster *master)
686     g_return_if_fail (GDL_IS_DOCK_MASTER (master));
688     /* emit "layout-changed" on the controller to notify the user who
689      * normally shouldn't have access to us */
690     if (master->controller)
691         g_signal_emit_by_name (master->controller, "layout-changed");
693     /* remove the idle handler if there is one */
694     if (master->_priv->idle_layout_changed_id) {
695         g_source_remove (master->_priv->idle_layout_changed_id);
696         master->_priv->idle_layout_changed_id = 0;
697     }
700 static gboolean
701 idle_emit_layout_changed (gpointer user_data)
703     GdlDockMaster *master = user_data;
705     g_return_val_if_fail (master && GDL_IS_DOCK_MASTER (master), FALSE);
707     master->_priv->idle_layout_changed_id = 0;
708     g_signal_emit (master, master_signals [LAYOUT_CHANGED], 0);
709     
710     return FALSE;
713 static void 
714 item_dock_cb (GdlDockObject    *object,
715               GdlDockObject    *requestor,
716               GdlDockPlacement  position,
717               GValue           *other_data,
718               gpointer          user_data)
720     GdlDockMaster *master = user_data;
721     
722     g_return_if_fail (requestor && GDL_IS_DOCK_OBJECT (requestor));
723     g_return_if_fail (master && GDL_IS_DOCK_MASTER (master));
725     /* here we are in fact interested in the requestor, since it's
726      * assumed that object will not change its visibility... for the
727      * requestor, however, could mean that it's being shown */
728     if (!GDL_DOCK_OBJECT_IN_REFLOW (requestor) &&
729         !GDL_DOCK_OBJECT_AUTOMATIC (requestor)) {
730         if (!master->_priv->idle_layout_changed_id)
731             master->_priv->idle_layout_changed_id =
732                 g_idle_add (idle_emit_layout_changed, master);
733     }
736 static void 
737 item_detach_cb (GdlDockObject *object,
738                 gboolean       recursive,
739                 gpointer       user_data)
741     GdlDockMaster *master = user_data;
742     
743     g_return_if_fail (object && GDL_IS_DOCK_OBJECT (object));
744     g_return_if_fail (master && GDL_IS_DOCK_MASTER (master));
746     if (!GDL_DOCK_OBJECT_IN_REFLOW (object) &&
747         !GDL_DOCK_OBJECT_AUTOMATIC (object)) {
748         if (!master->_priv->idle_layout_changed_id)
749             master->_priv->idle_layout_changed_id =
750                 g_idle_add (idle_emit_layout_changed, master);
751     }
754 static void
755 item_notify_cb (GdlDockObject *object,
756                 GParamSpec    *pspec,
757                 gpointer       user_data)
759     GdlDockMaster *master = user_data;
760     gint locked = COMPUTE_LOCKED (master);
761     gboolean item_locked;
762     
763     g_object_get (object, "locked", &item_locked, NULL);
765     if (item_locked) {
766         g_hash_table_remove (master->_priv->unlocked_items, object);
767         g_hash_table_insert (master->_priv->locked_items, object, NULL);
768     } else {
769         g_hash_table_remove (master->_priv->locked_items, object);
770         g_hash_table_insert (master->_priv->unlocked_items, object, NULL);
771     }
772     
773     if (COMPUTE_LOCKED (master) != locked)
774         g_object_notify (G_OBJECT (master), "locked");
777 /* ----- Public interface ----- */
779 void
780 gdl_dock_master_add (GdlDockMaster *master,
781                      GdlDockObject *object)
783     g_return_if_fail (master != NULL && object != NULL);
785     if (!GDL_DOCK_OBJECT_AUTOMATIC (object)) {
786         GdlDockObject *found_object;
787         
788         /* create a name for the object if it doesn't have one */
789         if (!object->name)
790             /* directly set the name, since it's a construction only
791                property */
792             object->name = g_strdup_printf ("__dock_%u", master->_priv->number++);
793         
794         /* add the object to our hash list */
795         if ((found_object = g_hash_table_lookup (master->dock_objects, object->name))) {
796             g_warning (_("master %p: unable to add object %p[%s] to the hash.  "
797                          "There already is an item with that name (%p)."),
798                        master, object, object->name, found_object);
799         }
800         else {
801             g_object_ref (object);
802             gtk_object_sink (GTK_OBJECT (object));
803             g_hash_table_insert (master->dock_objects, g_strdup (object->name), object);
804         }
805     }
806     
807     if (GDL_IS_DOCK (object)) {
808         gboolean floating;
809         
810         /* if this is the first toplevel we are adding, name it controller */
811         if (!master->toplevel_docks)
812             /* the dock should already have the ref */
813             master->controller = object;
814         
815         /* add dock to the toplevel list */
816         g_object_get (object, "floating", &floating, NULL);
817         if (floating)
818             master->toplevel_docks = g_list_prepend (master->toplevel_docks, object);
819         else
820             master->toplevel_docks = g_list_append (master->toplevel_docks, object);
822         /* we are interested in the dock request this toplevel
823          * receives to update the layout */
824         g_signal_connect (object, "dock",
825                           G_CALLBACK (item_dock_cb), master);
827     }
828     else if (GDL_IS_DOCK_ITEM (object)) {
829         /* we need to connect the item's signals */
830         g_signal_connect (object, "dock_drag_begin",
831                           G_CALLBACK (gdl_dock_master_drag_begin), master);
832         g_signal_connect (object, "dock_drag_motion",
833                           G_CALLBACK (gdl_dock_master_drag_motion), master);
834         g_signal_connect (object, "dock_drag_end",
835                           G_CALLBACK (gdl_dock_master_drag_end), master);
836         g_signal_connect (object, "dock",
837                           G_CALLBACK (item_dock_cb), master);
838         g_signal_connect (object, "detach",
839                           G_CALLBACK (item_detach_cb), master);
841         /* register to "locked" notification if the item has a grip,
842          * and add the item to the corresponding hash */
843         if (GDL_DOCK_ITEM_HAS_GRIP (GDL_DOCK_ITEM (object))) {
844             g_signal_connect (object, "notify::locked",
845                               G_CALLBACK (item_notify_cb), master);
846             item_notify_cb (object, NULL, master);
847         }
848         
849         /* If the item is notebook, set the switcher style */
850         if (GDL_IS_DOCK_NOTEBOOK (object) &&
851             GDL_IS_SWITCHER (GDL_DOCK_ITEM (object)->child))
852         {
853             g_object_set (GDL_DOCK_ITEM (object)->child, "switcher-style",
854                           master->_priv->switcher_style, NULL);
855         }
856         
857         /* post a layout_changed emission if the item is not automatic
858          * (since it should be added to the items model) */
859         if (!GDL_DOCK_OBJECT_AUTOMATIC (object)) {
860             if (!master->_priv->idle_layout_changed_id)
861                 master->_priv->idle_layout_changed_id =
862                     g_idle_add (idle_emit_layout_changed, master);
863         }
864     }
867 void
868 gdl_dock_master_remove (GdlDockMaster *master,
869                         GdlDockObject *object)
871     g_return_if_fail (master != NULL && object != NULL);
873     /* remove from locked/unlocked hashes and property change if
874      * that's the case */
875     if (GDL_IS_DOCK_ITEM (object) && GDL_DOCK_ITEM_HAS_GRIP (GDL_DOCK_ITEM (object))) {
876         gint locked = COMPUTE_LOCKED (master);
877         if (g_hash_table_remove (master->_priv->locked_items, object) ||
878             g_hash_table_remove (master->_priv->unlocked_items, object)) {
879             if (COMPUTE_LOCKED (master) != locked)
880                 g_object_notify (G_OBJECT (master), "locked");
881         }
882     }
883         
884     /* ref the master, since removing the controller could cause master disposal */
885     g_object_ref (master);
886     
887     /* all the interesting stuff happens in _gdl_dock_master_remove */
888     _gdl_dock_master_remove (object, master);
890     /* post a layout_changed emission if the item is not automatic
891      * (since it should be removed from the items model) */
892     if (!GDL_DOCK_OBJECT_AUTOMATIC (object)) {
893         if (!master->_priv->idle_layout_changed_id)
894             master->_priv->idle_layout_changed_id =
895                 g_idle_add (idle_emit_layout_changed, master);
896     }
897     
898     /* balance ref count */
899     g_object_unref (master);
902 void
903 gdl_dock_master_foreach (GdlDockMaster *master,
904                          GFunc          function,
905                          gpointer       user_data)
907     struct {
908         GFunc    function;
909         gpointer user_data;
910     } data;
912     g_return_if_fail (master != NULL && function != NULL);
914     data.function = function;
915     data.user_data = user_data;
916     g_hash_table_foreach (master->dock_objects, _gdl_dock_master_foreach, &data);
919 void
920 gdl_dock_master_foreach_toplevel (GdlDockMaster *master,
921                                   gboolean       include_controller,
922                                   GFunc          function,
923                                   gpointer       user_data)
925     GList *l;
926     
927     g_return_if_fail (master != NULL && function != NULL);
929     for (l = master->toplevel_docks; l; ) {
930         GdlDockObject *object = GDL_DOCK_OBJECT (l->data);
931         l = l->next;
932         if (object != master->controller || include_controller)
933             (* function) (GTK_WIDGET (object), user_data);
934     }
937 GdlDockObject *
938 gdl_dock_master_get_object (GdlDockMaster *master,
939                             const gchar   *nick_name)
941     gpointer *found;
942     
943     g_return_val_if_fail (master != NULL, NULL);
945     if (!nick_name)
946         return NULL;
948     found = g_hash_table_lookup (master->dock_objects, nick_name);
950     return found ? GDL_DOCK_OBJECT (found) : NULL;
953 GdlDockObject *
954 gdl_dock_master_get_controller (GdlDockMaster *master)
956     g_return_val_if_fail (master != NULL, NULL);
958     return master->controller;
961 void
962 gdl_dock_master_set_controller (GdlDockMaster *master,
963                                 GdlDockObject *new_controller)
965     g_return_if_fail (master != NULL);
967     if (new_controller) {
968         if (GDL_DOCK_OBJECT_AUTOMATIC (new_controller))
969             g_warning (_("The new dock controller %p is automatic.  Only manual "
970                          "dock objects should be named controller."), new_controller);
971         
972         /* check that the controller is in the toplevel list */
973         if (!g_list_find (master->toplevel_docks, new_controller))
974             gdl_dock_master_add (master, new_controller);
975         master->controller = new_controller;
977     } else {
978         master->controller = NULL;
979         /* no controller, no master */
980         g_object_unref (master);
981     }
984 static void
985 set_switcher_style_foreach (GtkWidget *obj, gpointer user_data)
987     GdlSwitcherStyle style = GPOINTER_TO_INT (user_data);
988     
989     if (!GDL_IS_DOCK_ITEM (obj))
990         return;
991     
992     if (GDL_IS_DOCK_NOTEBOOK (obj)) {
993         
994         GtkWidget *child = GDL_DOCK_ITEM (obj)->child;
995         if (GDL_IS_SWITCHER (child)) {
996             
997             g_object_set (child, "switcher-style", style, NULL);
998         }
999     } else if (gdl_dock_object_is_compound (GDL_DOCK_OBJECT (obj))) {
1000         
1001         gtk_container_foreach (GTK_CONTAINER (obj),
1002                                set_switcher_style_foreach,
1003                                user_data);
1004     }
1007 static void
1008 gdl_dock_master_set_switcher_style (GdlDockMaster *master,
1009                                     GdlSwitcherStyle switcher_style)
1011     GList *l;
1012     g_return_if_fail (GDL_IS_DOCK_MASTER (master));
1013     
1014     master->_priv->switcher_style = switcher_style;
1015     for (l = master->toplevel_docks; l; l = l->next) {
1016         GdlDock *dock = GDL_DOCK (l->data);
1017         if (dock->root)
1018             set_switcher_style_foreach (GTK_WIDGET (dock->root),
1019                                         GINT_TO_POINTER (switcher_style));
1020     }
1022     /* just to be sure hidden items are set too */
1023     gdl_dock_master_foreach (master, (GFunc) set_switcher_style_foreach,
1024                              GINT_TO_POINTER (switcher_style));