Code

Remove makefiles that shouldn't be in repos
[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     PROP_EXPANSION_DIRECTION
84 };
86 enum {
87     LAYOUT_CHANGED,
88     LAST_SIGNAL
89 };
91 struct _GdlDockMasterPrivate {
92     gint            number;             /* for naming nameless manual objects */
93     gchar          *default_title;
94     
95     GdkGC          *root_xor_gc;
96     gboolean        rect_drawn;
97     GdlDock        *rect_owner;
98     
99     GdlDockRequest *drag_request;
101     /* source id for the idle handler to emit a layout_changed signal */
102     guint           idle_layout_changed_id;
104     /* hashes to quickly calculate the overall locked status: i.e.
105      * if size(unlocked_items) == 0 then locked = 1
106      * else if size(locked_items) == 0 then locked = 0
107      * else locked = -1
108      */
109     GHashTable     *locked_items;
110     GHashTable     *unlocked_items;
111     
112     GdlSwitcherStyle switcher_style;
114     GdlDockExpansionDirection expansion_direction;
115 };
117 #define COMPUTE_LOCKED(master)                                          \
118     (g_hash_table_size ((master)->_priv->unlocked_items) == 0 ? 1 :     \
119      (g_hash_table_size ((master)->_priv->locked_items) == 0 ? 0 : -1))
121 static guint master_signals [LAST_SIGNAL] = { 0 };
124 /* ----- Private interface ----- */
126 GDL_CLASS_BOILERPLATE (GdlDockMaster, gdl_dock_master, GObject, G_TYPE_OBJECT);
128 static void
129 gdl_dock_master_class_init (GdlDockMasterClass *klass)
131     GObjectClass      *g_object_class;
133     g_object_class = G_OBJECT_CLASS (klass);
135     g_object_class->dispose = gdl_dock_master_dispose;
136     g_object_class->set_property = gdl_dock_master_set_property;
137     g_object_class->get_property = gdl_dock_master_get_property;
139     g_object_class_install_property (
140         g_object_class, PROP_DEFAULT_TITLE,
141         g_param_spec_string ("default-title", _("Default title"),
142                              _("Default title for newly created floating docks"),
143                              NULL,
144                              G_PARAM_READWRITE));
145     
146     g_object_class_install_property (
147         g_object_class, PROP_LOCKED,
148         g_param_spec_int ("locked", _("Locked"),
149                           _("If is set to 1, all the dock items bound to the master "
150                             "are locked; if it's 0, all are unlocked; -1 indicates "
151                             "inconsistency among the items"),
152                           -1, 1, 0,
153                           G_PARAM_READWRITE));
155     g_object_class_install_property (
156         g_object_class, PROP_SWITCHER_STYLE,
157         g_param_spec_enum ("switcher-style", _("Switcher Style"),
158                            _("Switcher buttons style"),
159                            GDL_TYPE_SWITCHER_STYLE,
160                            GDL_SWITCHER_STYLE_BOTH,
161                            G_PARAM_READWRITE));
163     g_object_class_install_property (
164         g_object_class, PROP_EXPANSION_DIRECTION,
165         g_param_spec_enum ("expand-direction", _("Expand direction"),
166                            _("Allow the master's dock items to expand their container "
167                              "dock objects in the given direction"),
168                            GDL_TYPE_EXPANSION_DIRECTION,
169                            GDL_DOCK_EXPANSION_DIRECTION_NONE,
170                            G_PARAM_READWRITE));
172     master_signals [LAYOUT_CHANGED] = 
173         g_signal_new ("layout-changed", 
174                       G_TYPE_FROM_CLASS (klass),
175                       G_SIGNAL_RUN_LAST,
176                       G_STRUCT_OFFSET (GdlDockMasterClass, layout_changed),
177                       NULL, /* accumulator */
178                       NULL, /* accu_data */
179                       gdl_marshal_VOID__VOID,
180                       G_TYPE_NONE, /* return type */
181                       0);
183     klass->layout_changed = gdl_dock_master_layout_changed;
186 static void
187 gdl_dock_master_instance_init (GdlDockMaster *master)
189     master->dock_objects = g_hash_table_new_full (g_str_hash, g_str_equal,
190                                                   g_free, NULL);
191     master->toplevel_docks = NULL;
192     master->controller = NULL;
193     master->dock_number = 1;
194     
195     master->_priv = g_new0 (GdlDockMasterPrivate, 1);
196     master->_priv->number = 1;
197     master->_priv->switcher_style = GDL_SWITCHER_STYLE_BOTH;
198     master->_priv->expansion_direction = GDL_DOCK_EXPANSION_DIRECTION_NONE;
199     master->_priv->locked_items = g_hash_table_new (g_direct_hash, g_direct_equal);
200     master->_priv->unlocked_items = g_hash_table_new (g_direct_hash, g_direct_equal);
203 static void
204 _gdl_dock_master_remove (GdlDockObject *object,
205                          GdlDockMaster *master)
207     g_return_if_fail (master != NULL && object != NULL);
209     if (GDL_IS_DOCK (object)) {
210         GList *found_link;
212         found_link = g_list_find (master->toplevel_docks, object);
213         if (found_link)
214             master->toplevel_docks = g_list_delete_link (master->toplevel_docks,
215                                                          found_link);
216         if (object == master->controller) {
217             GList *last;
218             GdlDockObject *new_controller = NULL;
219             
220             /* now find some other non-automatic toplevel to use as a
221                new controller.  start from the last dock, since it's
222                probably a non-floating and manual */
223             last = g_list_last (master->toplevel_docks);
224             while (last) {
225                 if (!GDL_DOCK_OBJECT_AUTOMATIC (last->data)) {
226                     new_controller = GDL_DOCK_OBJECT (last->data);
227                     break;
228                 }
229                 last = last->prev;
230             };
232             if (new_controller) {
233                 /* the new controller gets the ref (implicitly of course) */
234                 master->controller = new_controller;
235             } else {
236                 master->controller = NULL;
237                 /* no controller, no master */
238                 g_object_unref (master);
239             }
240         }
241     }
242     /* disconnect dock object signals */
243     g_signal_handlers_disconnect_matched (object, G_SIGNAL_MATCH_DATA, 
244                                           0, 0, NULL, NULL, master);
246     /* unref the object from the hash if it's there */
247     if (object->name) {
248         GdlDockObject *found_object;
249         found_object = g_hash_table_lookup (master->dock_objects, object->name);
250         if (found_object == object) {
251             g_hash_table_remove (master->dock_objects, object->name);
252             g_object_unref (object);
253         }
254     }
257 static void
258 ht_foreach_build_slist (gpointer  key,
259                         gpointer  value,
260                         GSList  **slist)
262     *slist = g_slist_prepend (*slist, value);
265 static void
266 gdl_dock_master_dispose (GObject *g_object)
268     GdlDockMaster *master;
269     
270     g_return_if_fail (GDL_IS_DOCK_MASTER (g_object));
272     master = GDL_DOCK_MASTER (g_object);
274     if (master->toplevel_docks) {
275         g_list_foreach (master->toplevel_docks,
276                         (GFunc) gdl_dock_object_unbind, NULL);
277         g_list_free (master->toplevel_docks);
278         master->toplevel_docks = NULL;
279     }
280     
281     if (master->dock_objects) {
282         GSList *alive_docks = NULL;
283         g_hash_table_foreach (master->dock_objects,
284                               (GHFunc) ht_foreach_build_slist, &alive_docks);
285         while (alive_docks) {
286             gdl_dock_object_unbind (GDL_DOCK_OBJECT (alive_docks->data));
287             alive_docks = g_slist_delete_link (alive_docks, alive_docks);
288         }
289         
290         g_hash_table_destroy (master->dock_objects);
291         master->dock_objects = NULL;
292     }
293     
294     if (master->_priv) {
295         if (master->_priv->idle_layout_changed_id)
296             g_source_remove (master->_priv->idle_layout_changed_id);
297         
298         if (master->_priv->root_xor_gc) {
299             g_object_unref (master->_priv->root_xor_gc);
300             master->_priv->root_xor_gc = NULL;
301         }
302         if (master->_priv->drag_request) {
303             if (G_IS_VALUE (&master->_priv->drag_request->extra))
304                 g_value_unset (&master->_priv->drag_request->extra);
305             g_free (master->_priv->drag_request);
306             master->_priv->drag_request = NULL;
307         }
308         g_free (master->_priv->default_title);
309         master->_priv->default_title = NULL;
311         g_hash_table_destroy (master->_priv->locked_items);
312         master->_priv->locked_items = NULL;
313         g_hash_table_destroy (master->_priv->unlocked_items);
314         master->_priv->unlocked_items = NULL;
315         
316         g_free (master->_priv);
317         master->_priv = NULL;
318     }
320     GDL_CALL_PARENT (G_OBJECT_CLASS, dispose, (g_object));
323 static void 
324 foreach_lock_unlock (GdlDockItem *item,
325                      gboolean     locked)
327     if (!GDL_IS_DOCK_ITEM (item))
328         return;
329     
330     g_object_set (item, "locked", locked, NULL);
331     if (gdl_dock_object_is_compound (GDL_DOCK_OBJECT (item)))
332         gtk_container_foreach (GTK_CONTAINER (item),
333                                (GtkCallback) foreach_lock_unlock,
334                                GINT_TO_POINTER (locked));
337 static void
338 gdl_dock_master_lock_unlock (GdlDockMaster *master,
339                              gboolean       locked)
341     GList *l;
342     
343     for (l = master->toplevel_docks; l; l = l->next) {
344         GdlDock *dock = GDL_DOCK (l->data);
345         if (dock->root)
346             foreach_lock_unlock (GDL_DOCK_ITEM (dock->root), locked);
347     }
349     /* just to be sure hidden items are set too */
350     gdl_dock_master_foreach (master,
351                              (GFunc) foreach_lock_unlock,
352                              GINT_TO_POINTER (locked));
355 static void
356 gdl_dock_master_set_property  (GObject      *object,
357                                guint         prop_id,
358                                const GValue *value,
359                                GParamSpec   *pspec)
361     GdlDockMaster *master = GDL_DOCK_MASTER (object);
363     switch (prop_id) {
364         case PROP_DEFAULT_TITLE:
365             g_free (master->_priv->default_title);
366             master->_priv->default_title = g_value_dup_string (value);
367             break;
368         case PROP_LOCKED:
369             if (g_value_get_int (value) >= 0)
370                 gdl_dock_master_lock_unlock (master, (g_value_get_int (value) > 0));
371             break;
372         case PROP_SWITCHER_STYLE:
373             gdl_dock_master_set_switcher_style (master, g_value_get_enum (value));
374             break;
375         case PROP_EXPANSION_DIRECTION:
376             master->_priv->expansion_direction = g_value_get_enum (value);
377             break;
378         default:
379             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
380             break;
381     }
384 static void
385 gdl_dock_master_get_property  (GObject      *object,
386                                guint         prop_id,
387                                GValue       *value,
388                                GParamSpec   *pspec)
390     GdlDockMaster *master = GDL_DOCK_MASTER (object);
392     switch (prop_id) {
393         case PROP_DEFAULT_TITLE:
394             g_value_set_string (value, master->_priv->default_title);
395             break;
396         case PROP_LOCKED:
397             g_value_set_int (value, COMPUTE_LOCKED (master));
398             break;
399         case PROP_SWITCHER_STYLE:
400             g_value_set_enum (value, master->_priv->switcher_style);
401             break;
402         case PROP_EXPANSION_DIRECTION:
403             g_value_set_enum (value, master->_priv->expansion_direction);
404             break;
405         default:
406             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
407             break;
408     }
411 static void
412 gdl_dock_master_drag_begin (GdlDockItem *item,
413                             gpointer     data)
415     GdlDockMaster  *master;
416     GdlDockRequest *request;
417     
418     g_return_if_fail (data != NULL);
419     g_return_if_fail (item != NULL);
421     master = GDL_DOCK_MASTER (data);
423     if (!master->_priv->drag_request)
424         master->_priv->drag_request = g_new0 (GdlDockRequest, 1);
426     request = master->_priv->drag_request;
427     
428     /* Set the target to itself so it won't go floating with just a click. */
429     request->applicant = GDL_DOCK_OBJECT (item);
430     request->target = GDL_DOCK_OBJECT (item);
431     request->position = GDL_DOCK_FLOATING;
432     if (G_IS_VALUE (&request->extra))
433         g_value_unset (&request->extra);
435     master->_priv->rect_drawn = FALSE;
436     master->_priv->rect_owner = NULL;
439 static void
440 gdl_dock_master_drag_end (GdlDockItem *item, 
441                           gboolean     cancelled,
442                           gpointer     data)
444     GdlDockMaster  *master;
445     GdlDockRequest *request;
446     
447     g_return_if_fail (data != NULL);
448     g_return_if_fail (item != NULL);
450     master = GDL_DOCK_MASTER (data);
451     request = master->_priv->drag_request;
452     
453     g_return_if_fail (GDL_DOCK_OBJECT (item) == request->applicant);
454     
455     /* Erase previously drawn rectangle */
456     if (master->_priv->rect_drawn)
457         gdl_dock_master_xor_rect (master);
458     
459     /* cancel conditions */
460     if (cancelled || request->applicant == request->target)
461         return;
462     
463     /* dock object to the requested position */
464     gdl_dock_object_dock (request->target,
465                           request->applicant,
466                           request->position,
467                           &request->extra);
468     
469     g_signal_emit (master, master_signals [LAYOUT_CHANGED], 0);
472 static void
473 gdl_dock_master_drag_motion (GdlDockItem *item, 
474                              gint         root_x, 
475                              gint         root_y,
476                              gpointer     data)
478     GdlDockMaster  *master;
479     GdlDockRequest  my_request, *request;
480     GdkWindow      *window;
481     gint            win_x, win_y;
482     gint            x, y;
483     GdlDock        *dock = NULL;
484     gboolean        may_dock = FALSE;
485     
486     g_return_if_fail (item != NULL && data != NULL);
488     master = GDL_DOCK_MASTER (data);
489     request = master->_priv->drag_request;
491     g_return_if_fail (GDL_DOCK_OBJECT (item) == request->applicant);
492     
493     my_request = *request;
495     /* first look under the pointer */
496     window = gdk_window_at_pointer (&win_x, &win_y);
497     if (window) {
498         GtkWidget *widget;
499         /* ok, now get the widget who owns that window and see if we can
500            get to a GdlDock by walking up the hierarchy */
501         gdk_window_get_user_data (window, (gpointer) &widget);
502         if (GTK_IS_WIDGET (widget)) {
503             while (widget && (!GDL_IS_DOCK (widget) || 
504                    GDL_DOCK_OBJECT_GET_MASTER (widget) != master))
505                 widget = widget->parent;
506             if (widget) {
507                 gint win_w, win_h;
508                 
509                 /* verify that the pointer is still in that dock
510                    (the user could have moved it) */
511                 gdk_window_get_geometry (widget->window,
512                                          NULL, NULL, &win_w, &win_h, NULL);
513                 gdk_window_get_origin (widget->window, &win_x, &win_y);
514                 if (root_x >= win_x && root_x < win_x + win_w &&
515                     root_y >= win_y && root_y < win_y + win_h)
516                     dock = GDL_DOCK (widget);
517             }
518         }
519     }
521     if (dock) {
522         /* translate root coordinates into dock object coordinates
523            (i.e. widget coordinates) */
524         gdk_window_get_origin (GTK_WIDGET (dock)->window, &win_x, &win_y);
525         x = root_x - win_x;
526         y = root_y - win_y;
527         may_dock = gdl_dock_object_dock_request (GDL_DOCK_OBJECT (dock),
528                                                  x, y, &my_request);
529     }
530     else {
531         GList *l;
533         /* try to dock the item in all the docks in the ring in turn */
534         for (l = master->toplevel_docks; l; l = l->next) {
535             dock = GDL_DOCK (l->data);
536             /* translate root coordinates into dock object coordinates
537                (i.e. widget coordinates) */
538             gdk_window_get_origin (GTK_WIDGET (dock)->window, &win_x, &win_y);
539             x = root_x - win_x;
540             y = root_y - win_y;
541             may_dock = gdl_dock_object_dock_request (GDL_DOCK_OBJECT (dock),
542                                                      x, y, &my_request);
543             if (may_dock)
544                 break;
545         }
546     }
548   
549     if (!may_dock) {
550         GtkRequisition req;
551         /* Special case for GdlDockItems : they must respect the flags */
552         if(GDL_IS_DOCK_ITEM(item)
553         && GDL_DOCK_ITEM(item)->behavior & GDL_DOCK_ITEM_BEH_NEVER_FLOATING)
554             return;
556         dock = NULL;
557         my_request.target = GDL_DOCK_OBJECT (
558             gdl_dock_object_get_toplevel (request->applicant));
559         my_request.position = GDL_DOCK_FLOATING;
561         gdl_dock_item_preferred_size (GDL_DOCK_ITEM (request->applicant), &req);
562         my_request.rect.width = req.width;
563         my_request.rect.height = req.height;
565         my_request.rect.x = root_x - GDL_DOCK_ITEM (request->applicant)->dragoff_x;
566         my_request.rect.y = root_y - GDL_DOCK_ITEM (request->applicant)->dragoff_y;
568         /* setup extra docking information */
569         if (G_IS_VALUE (&my_request.extra))
570             g_value_unset (&my_request.extra);
572         g_value_init (&my_request.extra, GDK_TYPE_RECTANGLE);
573         g_value_set_boxed (&my_request.extra, &my_request.rect);
574     }
575     /* if we want to enforce GDL_DOCK_ITEM_BEH_NEVER_FLOATING           */
576     /* the item must remain attached to the controller, otherwise       */
577     /* it could be inserted in another floating dock                    */
578     /* so check for the flag at this moment                             */
579     else if(GDL_IS_DOCK_ITEM(item)
580         && GDL_DOCK_ITEM(item)->behavior & GDL_DOCK_ITEM_BEH_NEVER_FLOATING
581         && dock != GDL_DOCK(master->controller))
582             return;
584     if (!(my_request.rect.x == request->rect.x &&
585           my_request.rect.y == request->rect.y &&
586           my_request.rect.width == request->rect.width &&
587           my_request.rect.height == request->rect.height &&
588           dock == master->_priv->rect_owner)) {
589         
590         /* erase the previous rectangle */
591         if (master->_priv->rect_drawn)
592             gdl_dock_master_xor_rect (master);
593     }
595     /* set the new values */
596     *request = my_request;
597     master->_priv->rect_owner = dock;
598     
599     /* draw the previous rectangle */
600     if (~master->_priv->rect_drawn)
601         gdl_dock_master_xor_rect (master);
604 static void
605 _gdl_dock_master_foreach (gpointer key,
606                           gpointer value,
607                           gpointer user_data)
609     (void)key;
610     struct {
611         GFunc    function;
612         gpointer user_data;
613     } *data = user_data;
615     (* data->function) (GTK_WIDGET (value), data->user_data);
618 static void
619 gdl_dock_master_xor_rect (GdlDockMaster *master)
621     gint8         dash_list [2];
622     GdkWindow    *window;
623     GdkRectangle *rect;
624     
625     if (!master->_priv || !master->_priv->drag_request)
626         return;
627     
628     master->_priv->rect_drawn = ~master->_priv->rect_drawn;
629     
630     if (master->_priv->rect_owner) {
631         gdl_dock_xor_rect (master->_priv->rect_owner,
632                            &master->_priv->drag_request->rect);
633         return;
634     }
635     
636     rect = &master->_priv->drag_request->rect;
637     window = gdk_get_default_root_window ();
639     if (!master->_priv->root_xor_gc) {
640         GdkGCValues values;
642         values.function = GDK_INVERT;
643         values.subwindow_mode = GDK_INCLUDE_INFERIORS;
644         master->_priv->root_xor_gc = gdk_gc_new_with_values (
645             window, &values, GDK_GC_FUNCTION | GDK_GC_SUBWINDOW);
646     };
648     gdk_gc_set_line_attributes (master->_priv->root_xor_gc, 1,
649                                 GDK_LINE_ON_OFF_DASH,
650                                 GDK_CAP_NOT_LAST,
651                                 GDK_JOIN_BEVEL);
652     
653     dash_list[0] = 1;
654     dash_list[1] = 1;
655     gdk_gc_set_dashes (master->_priv->root_xor_gc, 1, dash_list, 2);
657     gdk_draw_rectangle (window, master->_priv->root_xor_gc, 0, 
658                         rect->x, rect->y,
659                         rect->width, rect->height);
661     gdk_gc_set_dashes (master->_priv->root_xor_gc, 0, dash_list, 2);
663     gdk_draw_rectangle (window, master->_priv->root_xor_gc, 0, 
664                         rect->x + 1, rect->y + 1,
665                         rect->width - 2, rect->height - 2);
668 static void
669 gdl_dock_master_layout_changed (GdlDockMaster *master)
671     g_return_if_fail (GDL_IS_DOCK_MASTER (master));
673     /* emit "layout-changed" on the controller to notify the user who
674      * normally shouldn't have access to us */
675     if (master->controller)
676         g_signal_emit_by_name (master->controller, "layout-changed");
678     /* remove the idle handler if there is one */
679     if (master->_priv->idle_layout_changed_id) {
680         g_source_remove (master->_priv->idle_layout_changed_id);
681         master->_priv->idle_layout_changed_id = 0;
682     }
685 static gboolean
686 idle_emit_layout_changed (gpointer user_data)
688     GdlDockMaster *master = user_data;
690     g_return_val_if_fail (master && GDL_IS_DOCK_MASTER (master), FALSE);
692     master->_priv->idle_layout_changed_id = 0;
693     g_signal_emit (master, master_signals [LAYOUT_CHANGED], 0);
694     
695     return FALSE;
698 static void 
699 item_dock_cb (GdlDockObject    *object,
700               GdlDockObject    *requestor,
701               GdlDockPlacement  position,
702               GValue           *other_data,
703               gpointer          user_data)
705     GdlDockMaster *master = user_data;
706     
707     g_return_if_fail (requestor && GDL_IS_DOCK_OBJECT (requestor));
708     g_return_if_fail (master && GDL_IS_DOCK_MASTER (master));
710     /* here we are in fact interested in the requestor, since it's
711      * assumed that object will not change its visibility... for the
712      * requestor, however, could mean that it's being shown */
713     if (!GDL_DOCK_OBJECT_IN_REFLOW (requestor) &&
714         !GDL_DOCK_OBJECT_AUTOMATIC (requestor)) {
715         if (!master->_priv->idle_layout_changed_id)
716             master->_priv->idle_layout_changed_id =
717                 g_idle_add (idle_emit_layout_changed, master);
718     }
721 static void 
722 item_detach_cb (GdlDockObject *object,
723                 gboolean       recursive,
724                 gpointer       user_data)
726     GdlDockMaster *master = user_data;
727     
728     g_return_if_fail (object && GDL_IS_DOCK_OBJECT (object));
729     g_return_if_fail (master && GDL_IS_DOCK_MASTER (master));
731     if (!GDL_DOCK_OBJECT_IN_REFLOW (object) &&
732         !GDL_DOCK_OBJECT_AUTOMATIC (object)) {
733         if (!master->_priv->idle_layout_changed_id)
734             master->_priv->idle_layout_changed_id =
735                 g_idle_add (idle_emit_layout_changed, master);
736     }
739 static void
740 item_notify_cb (GdlDockObject *object,
741                 GParamSpec    *pspec,
742                 gpointer       user_data)
744     GdlDockMaster *master = user_data;
745     gint locked = COMPUTE_LOCKED (master);
746     gboolean item_locked;
747     
748     g_object_get (object, "locked", &item_locked, NULL);
750     if (item_locked) {
751         g_hash_table_remove (master->_priv->unlocked_items, object);
752         g_hash_table_insert (master->_priv->locked_items, object, NULL);
753     } else {
754         g_hash_table_remove (master->_priv->locked_items, object);
755         g_hash_table_insert (master->_priv->unlocked_items, object, NULL);
756     }
757     
758     if (COMPUTE_LOCKED (master) != locked)
759         g_object_notify (G_OBJECT (master), "locked");
762 /* ----- Public interface ----- */
764 void
765 gdl_dock_master_add (GdlDockMaster *master,
766                      GdlDockObject *object)
768     g_return_if_fail (master != NULL && object != NULL);
770     if (!GDL_DOCK_OBJECT_AUTOMATIC (object)) {
771         GdlDockObject *found_object;
772         
773         /* create a name for the object if it doesn't have one */
774         if (!object->name)
775             /* directly set the name, since it's a construction only
776                property */
777             object->name = g_strdup_printf ("__dock_%u", master->_priv->number++);
778         
779         /* add the object to our hash list */
780         if ((found_object = g_hash_table_lookup (master->dock_objects, object->name))) {
781             g_warning (_("master %p: unable to add object %p[%s] to the hash.  "
782                          "There already is an item with that name (%p)."),
783                        master, object, object->name, found_object);
784         }
785         else {
786             g_object_ref (object);
787             gtk_object_sink (GTK_OBJECT (object));
788             g_hash_table_insert (master->dock_objects, g_strdup (object->name), object);
789         }
790     }
791     
792     if (GDL_IS_DOCK (object)) {
793         gboolean floating;
794         
795         /* if this is the first toplevel we are adding, name it controller */
796         if (!master->toplevel_docks)
797             /* the dock should already have the ref */
798             master->controller = object;
799         
800         /* add dock to the toplevel list */
801         g_object_get (object, "floating", &floating, NULL);
802         if (floating)
803             master->toplevel_docks = g_list_prepend (master->toplevel_docks, object);
804         else
805             master->toplevel_docks = g_list_append (master->toplevel_docks, object);
807         /* we are interested in the dock request this toplevel
808          * receives to update the layout */
809         g_signal_connect (object, "dock",
810                           G_CALLBACK (item_dock_cb), master);
812     }
813     else if (GDL_IS_DOCK_ITEM (object)) {
814         /* we need to connect the item's signals */
815         g_signal_connect (object, "dock_drag_begin",
816                           G_CALLBACK (gdl_dock_master_drag_begin), master);
817         g_signal_connect (object, "dock_drag_motion",
818                           G_CALLBACK (gdl_dock_master_drag_motion), master);
819         g_signal_connect (object, "dock_drag_end",
820                           G_CALLBACK (gdl_dock_master_drag_end), master);
821         g_signal_connect (object, "dock",
822                           G_CALLBACK (item_dock_cb), master);
823         g_signal_connect (object, "detach",
824                           G_CALLBACK (item_detach_cb), master);
826         /* register to "locked" notification if the item has a grip,
827          * and add the item to the corresponding hash */
828         if (GDL_DOCK_ITEM_HAS_GRIP (GDL_DOCK_ITEM (object))) {
829             g_signal_connect (object, "notify::locked",
830                               G_CALLBACK (item_notify_cb), master);
831             item_notify_cb (object, NULL, master);
832         }
833         
834         /* If the item is notebook, set the switcher style */
835         if (GDL_IS_DOCK_NOTEBOOK (object) &&
836             GDL_IS_SWITCHER (GDL_DOCK_ITEM (object)->child))
837         {
838             g_object_set (GDL_DOCK_ITEM (object)->child, "switcher-style",
839                           master->_priv->switcher_style, NULL);
840         }
841         
842         /* post a layout_changed emission if the item is not automatic
843          * (since it should be added to the items model) */
844         if (!GDL_DOCK_OBJECT_AUTOMATIC (object)) {
845             if (!master->_priv->idle_layout_changed_id)
846                 master->_priv->idle_layout_changed_id =
847                     g_idle_add (idle_emit_layout_changed, master);
848         }
849     }
852 void
853 gdl_dock_master_remove (GdlDockMaster *master,
854                         GdlDockObject *object)
856     g_return_if_fail (master != NULL && object != NULL);
858     /* remove from locked/unlocked hashes and property change if
859      * that's the case */
860     if (GDL_IS_DOCK_ITEM (object) && GDL_DOCK_ITEM_HAS_GRIP (GDL_DOCK_ITEM (object))) {
861         gint locked = COMPUTE_LOCKED (master);
862         if (g_hash_table_remove (master->_priv->locked_items, object) ||
863             g_hash_table_remove (master->_priv->unlocked_items, object)) {
864             if (COMPUTE_LOCKED (master) != locked)
865                 g_object_notify (G_OBJECT (master), "locked");
866         }
867     }
868         
869     /* ref the master, since removing the controller could cause master disposal */
870     g_object_ref (master);
871     
872     /* all the interesting stuff happens in _gdl_dock_master_remove */
873     _gdl_dock_master_remove (object, master);
875     /* post a layout_changed emission if the item is not automatic
876      * (since it should be removed from the items model) */
877     if (!GDL_DOCK_OBJECT_AUTOMATIC (object)) {
878         if (!master->_priv->idle_layout_changed_id)
879             master->_priv->idle_layout_changed_id =
880                 g_idle_add (idle_emit_layout_changed, master);
881     }
882     
883     /* balance ref count */
884     g_object_unref (master);
887 void
888 gdl_dock_master_foreach (GdlDockMaster *master,
889                          GFunc          function,
890                          gpointer       user_data)
892     struct {
893         GFunc    function;
894         gpointer user_data;
895     } data;
897     g_return_if_fail (master != NULL && function != NULL);
899     data.function = function;
900     data.user_data = user_data;
901     g_hash_table_foreach (master->dock_objects, _gdl_dock_master_foreach, &data);
904 void
905 gdl_dock_master_foreach_toplevel (GdlDockMaster *master,
906                                   gboolean       include_controller,
907                                   GFunc          function,
908                                   gpointer       user_data)
910     GList *l;
911     
912     g_return_if_fail (master != NULL && function != NULL);
914     for (l = master->toplevel_docks; l; ) {
915         GdlDockObject *object = GDL_DOCK_OBJECT (l->data);
916         l = l->next;
917         if (object != master->controller || include_controller)
918             (* function) (GTK_WIDGET (object), user_data);
919     }
922 GdlDockObject *
923 gdl_dock_master_get_object (GdlDockMaster *master,
924                             const gchar   *nick_name)
926     gpointer *found;
927     
928     g_return_val_if_fail (master != NULL, NULL);
930     if (!nick_name)
931         return NULL;
933     found = g_hash_table_lookup (master->dock_objects, nick_name);
935     return found ? GDL_DOCK_OBJECT (found) : NULL;
938 GdlDockObject *
939 gdl_dock_master_get_controller (GdlDockMaster *master)
941     g_return_val_if_fail (master != NULL, NULL);
943     return master->controller;
946 void
947 gdl_dock_master_set_controller (GdlDockMaster *master,
948                                 GdlDockObject *new_controller)
950     g_return_if_fail (master != NULL);
952     if (new_controller) {
953         if (GDL_DOCK_OBJECT_AUTOMATIC (new_controller))
954             g_warning (_("The new dock controller %p is automatic.  Only manual "
955                          "dock objects should be named controller."), new_controller);
956         
957         /* check that the controller is in the toplevel list */
958         if (!g_list_find (master->toplevel_docks, new_controller))
959             gdl_dock_master_add (master, new_controller);
960         master->controller = new_controller;
962     } else {
963         master->controller = NULL;
964         /* no controller, no master */
965         g_object_unref (master);
966     }
969 static void
970 set_switcher_style_foreach (GtkWidget *obj, gpointer user_data)
972     GdlSwitcherStyle style = GPOINTER_TO_INT (user_data);
973     
974     if (!GDL_IS_DOCK_ITEM (obj))
975         return;
976     
977     if (GDL_IS_DOCK_NOTEBOOK (obj)) {
978         
979         GtkWidget *child = GDL_DOCK_ITEM (obj)->child;
980         if (GDL_IS_SWITCHER (child)) {
981             
982             g_object_set (child, "switcher-style", style, NULL);
983         }
984     } else if (gdl_dock_object_is_compound (GDL_DOCK_OBJECT (obj))) {
985         
986         gtk_container_foreach (GTK_CONTAINER (obj),
987                                set_switcher_style_foreach,
988                                user_data);
989     }
992 static void
993 gdl_dock_master_set_switcher_style (GdlDockMaster *master,
994                                     GdlSwitcherStyle switcher_style)
996     GList *l;
997     g_return_if_fail (GDL_IS_DOCK_MASTER (master));
998     
999     master->_priv->switcher_style = switcher_style;
1000     for (l = master->toplevel_docks; l; l = l->next) {
1001         GdlDock *dock = GDL_DOCK (l->data);
1002         if (dock->root)
1003             set_switcher_style_foreach (GTK_WIDGET (dock->root),
1004                                         GINT_TO_POINTER (switcher_style));
1005     }
1007     /* just to be sure hidden items are set too */
1008     gdl_dock_master_foreach (master, (GFunc) set_switcher_style_foreach,
1009                              GINT_TO_POINTER (switcher_style));