8a6aba315081026cbd59f4026be62552c68dc2ae
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;
94 GdkGC *root_xor_gc;
95 gboolean rect_drawn;
96 GdlDock *rect_owner;
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;
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)
127 {
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));
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;
172 }
174 static void
175 gdl_dock_master_instance_init (GdlDockMaster *master)
176 {
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;
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);
188 }
190 static void
191 _gdl_dock_master_remove (GdlDockObject *object,
192 GdlDockMaster *master)
193 {
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;
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 }
242 }
244 static void
245 ht_foreach_build_slist (gpointer key,
246 gpointer value,
247 GSList **slist)
248 {
249 *slist = g_slist_prepend (*slist, value);
250 }
252 static void
253 gdl_dock_master_dispose (GObject *g_object)
254 {
255 GdlDockMaster *master;
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 }
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 }
277 g_hash_table_destroy (master->dock_objects);
278 master->dock_objects = NULL;
279 }
281 if (master->_priv) {
282 if (master->_priv->idle_layout_changed_id)
283 g_source_remove (master->_priv->idle_layout_changed_id);
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;
303 g_free (master->_priv);
304 master->_priv = NULL;
305 }
307 GDL_CALL_PARENT (G_OBJECT_CLASS, dispose, (g_object));
308 }
310 static void
311 foreach_lock_unlock (GdlDockItem *item,
312 gboolean locked)
313 {
314 if (!GDL_IS_DOCK_ITEM (item))
315 return;
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));
322 }
324 static void
325 gdl_dock_master_lock_unlock (GdlDockMaster *master,
326 gboolean locked)
327 {
328 GList *l;
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));
340 }
342 static void
343 gdl_dock_master_set_property (GObject *object,
344 guint prop_id,
345 const GValue *value,
346 GParamSpec *pspec)
347 {
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 }
366 }
368 static void
369 gdl_dock_master_get_property (GObject *object,
370 guint prop_id,
371 GValue *value,
372 GParamSpec *pspec)
373 {
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 }
390 }
392 static void
393 gdl_dock_master_drag_begin (GdlDockItem *item,
394 gpointer data)
395 {
396 GdlDockMaster *master;
397 GdlDockRequest *request;
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;
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;
418 }
420 static void
421 gdl_dock_master_drag_end (GdlDockItem *item,
422 gboolean cancelled,
423 gpointer data)
424 {
425 GdlDockMaster *master;
426 GdlDockRequest *request;
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;
434 g_return_if_fail (GDL_DOCK_OBJECT (item) == request->applicant);
436 /* Erase previously drawn rectangle */
437 if (master->_priv->rect_drawn)
438 gdl_dock_master_xor_rect (master);
440 /* cancel conditions */
441 if (cancelled || request->applicant == request->target)
442 return;
444 /* dock object to the requested position */
445 gdl_dock_object_dock (request->target,
446 request->applicant,
447 request->position,
448 &request->extra);
450 g_signal_emit (master, master_signals [LAYOUT_CHANGED], 0);
451 }
453 static void
454 gdl_dock_master_drag_motion (GdlDockItem *item,
455 gint root_x,
456 gint root_y,
457 gpointer data)
458 {
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;
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);
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;
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 }
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)) {
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;
580 /* draw the previous rectangle */
581 if (~master->_priv->rect_drawn)
582 gdl_dock_master_xor_rect (master);
583 }
585 static void
586 _gdl_dock_master_foreach (gpointer key,
587 gpointer value,
588 gpointer user_data)
589 {
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);
597 }
599 static void
600 gdl_dock_master_xor_rect (GdlDockMaster *master)
601 {
602 gint8 dash_list [2];
603 GdkWindow *window;
604 GdkRectangle *rect;
606 if (!master->_priv || !master->_priv->drag_request)
607 return;
609 master->_priv->rect_drawn = ~master->_priv->rect_drawn;
611 if (master->_priv->rect_owner) {
612 gdl_dock_xor_rect (master->_priv->rect_owner,
613 &master->_priv->drag_request->rect);
614 return;
615 }
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);
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);
647 }
649 static void
650 gdl_dock_master_layout_changed (GdlDockMaster *master)
651 {
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 }
664 }
666 static gboolean
667 idle_emit_layout_changed (gpointer user_data)
668 {
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);
676 return FALSE;
677 }
679 static void
680 item_dock_cb (GdlDockObject *object,
681 GdlDockObject *requestor,
682 GdlDockPlacement position,
683 GValue *other_data,
684 gpointer user_data)
685 {
686 GdlDockMaster *master = user_data;
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 }
700 }
702 static void
703 item_detach_cb (GdlDockObject *object,
704 gboolean recursive,
705 gpointer user_data)
706 {
707 GdlDockMaster *master = user_data;
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 }
718 }
720 static void
721 item_notify_cb (GdlDockObject *object,
722 GParamSpec *pspec,
723 gpointer user_data)
724 {
725 GdlDockMaster *master = user_data;
726 gint locked = COMPUTE_LOCKED (master);
727 gboolean item_locked;
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 }
739 if (COMPUTE_LOCKED (master) != locked)
740 g_object_notify (G_OBJECT (master), "locked");
741 }
743 /* ----- Public interface ----- */
745 void
746 gdl_dock_master_add (GdlDockMaster *master,
747 GdlDockObject *object)
748 {
749 g_return_if_fail (master != NULL && object != NULL);
751 if (!GDL_DOCK_OBJECT_AUTOMATIC (object)) {
752 GdlDockObject *found_object;
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++);
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 }
773 if (GDL_IS_DOCK (object)) {
774 gboolean floating;
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;
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 }
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 }
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 }
831 }
833 void
834 gdl_dock_master_remove (GdlDockMaster *master,
835 GdlDockObject *object)
836 {
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 }
850 /* ref the master, since removing the controller could cause master disposal */
851 g_object_ref (master);
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 }
864 /* balance ref count */
865 g_object_unref (master);
866 }
868 void
869 gdl_dock_master_foreach (GdlDockMaster *master,
870 GFunc function,
871 gpointer user_data)
872 {
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);
883 }
885 void
886 gdl_dock_master_foreach_toplevel (GdlDockMaster *master,
887 gboolean include_controller,
888 GFunc function,
889 gpointer user_data)
890 {
891 GList *l;
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 }
901 }
903 GdlDockObject *
904 gdl_dock_master_get_object (GdlDockMaster *master,
905 const gchar *nick_name)
906 {
907 gpointer *found;
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;
917 }
919 GdlDockObject *
920 gdl_dock_master_get_controller (GdlDockMaster *master)
921 {
922 g_return_val_if_fail (master != NULL, NULL);
924 return master->controller;
925 }
927 void
928 gdl_dock_master_set_controller (GdlDockMaster *master,
929 GdlDockObject *new_controller)
930 {
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);
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 }
948 }
950 static void
951 set_switcher_style_foreach (GtkWidget *obj, gpointer user_data)
952 {
953 GdlSwitcherStyle style = GPOINTER_TO_INT (user_data);
955 if (!GDL_IS_DOCK_ITEM (obj))
956 return;
958 if (GDL_IS_DOCK_NOTEBOOK (obj)) {
960 GtkWidget *child = GDL_DOCK_ITEM (obj)->child;
961 if (GDL_IS_SWITCHER (child)) {
963 g_object_set (child, "switcher-style", style, NULL);
964 }
965 } else if (gdl_dock_object_is_compound (GDL_DOCK_OBJECT (obj))) {
967 gtk_container_foreach (GTK_CONTAINER (obj),
968 set_switcher_style_foreach,
969 user_data);
970 }
971 }
973 static void
974 gdl_dock_master_set_switcher_style (GdlDockMaster *master,
975 GdlSwitcherStyle switcher_style)
976 {
977 GList *l;
978 g_return_if_fail (GDL_IS_DOCK_MASTER (master));
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));
991 }