~ubuntu-branches/debian/experimental/inkscape/experimental

« back to all changes in this revision

Viewing changes to src/libgdl/gdl-dock-master.c

  • Committer: Bazaar Package Importer
  • Author(s): Thomas Viehmann
  • Date: 2008-09-09 23:29:02 UTC
  • mfrom: (1.1.7 upstream)
  • Revision ID: james.westby@ubuntu.com-20080909232902-c50iujhk1w79u8e7
Tags: 0.46-2.1
* Non-maintainer upload.
* Add upstream patch fixing a crash in the open dialog
  in the zh_CN.utf8 locale. Closes: #487623.
  Thanks to Luca Bruno for the patch.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 */
 
23
 
 
24
#ifdef HAVE_CONFIG_H
 
25
#include <config.h>
 
26
#endif
 
27
 
 
28
#include "gdl-i18n.h"
 
29
 
 
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
 
 
37
/* ----- Private prototypes ----- */
 
38
 
 
39
static void     gdl_dock_master_class_init    (GdlDockMasterClass *klass);
 
40
static void     gdl_dock_master_instance_init (GdlDockMaster      *master);
 
41
 
 
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);
 
51
 
 
52
static void     _gdl_dock_master_remove       (GdlDockObject      *object,
 
53
                                               GdlDockMaster      *master);
 
54
 
 
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);
 
64
 
 
65
static void     _gdl_dock_master_foreach      (gpointer            key,
 
66
                                               gpointer            value,
 
67
                                               gpointer            user_data);
 
68
 
 
69
static void     gdl_dock_master_xor_rect      (GdlDockMaster      *master);
 
70
 
 
71
static void     gdl_dock_master_layout_changed (GdlDockMaster     *master);
 
72
 
 
73
static void gdl_dock_master_set_switcher_style (GdlDockMaster *master,
 
74
                                                GdlSwitcherStyle switcher_style);
 
75
 
 
76
/* ----- Private data types and variables ----- */
 
77
 
 
78
enum {
 
79
    PROP_0,
 
80
    PROP_DEFAULT_TITLE,
 
81
    PROP_LOCKED,
 
82
    PROP_SWITCHER_STYLE,
 
83
    PROP_EXPANSION_DIRECTION
 
84
};
 
85
 
 
86
enum {
 
87
    LAYOUT_CHANGED,
 
88
    LAST_SIGNAL
 
89
};
 
90
 
 
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;
 
100
 
 
101
    /* source id for the idle handler to emit a layout_changed signal */
 
102
    guint           idle_layout_changed_id;
 
103
 
 
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;
 
113
 
 
114
    GdlDockExpansionDirection expansion_direction;
 
115
};
 
116
 
 
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))
 
120
 
 
121
static guint master_signals [LAST_SIGNAL] = { 0 };
 
122
 
 
123
 
 
124
/* ----- Private interface ----- */
 
125
 
 
126
GDL_CLASS_BOILERPLATE (GdlDockMaster, gdl_dock_master, GObject, G_TYPE_OBJECT);
 
127
 
 
128
static void
 
129
gdl_dock_master_class_init (GdlDockMasterClass *klass)
 
130
{
 
131
    GObjectClass      *g_object_class;
 
132
 
 
133
    g_object_class = G_OBJECT_CLASS (klass);
 
134
 
 
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;
 
138
 
 
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));
 
154
 
 
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));
 
162
 
 
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));
 
171
 
 
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);
 
182
 
 
183
    klass->layout_changed = gdl_dock_master_layout_changed;
 
184
}
 
185
 
 
186
static void
 
187
gdl_dock_master_instance_init (GdlDockMaster *master)
 
188
{
 
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);
 
201
}
 
202
 
 
203
static void
 
204
_gdl_dock_master_remove (GdlDockObject *object,
 
205
                         GdlDockMaster *master)
 
206
{
 
207
    g_return_if_fail (master != NULL && object != NULL);
 
208
 
 
209
    if (GDL_IS_DOCK (object)) {
 
210
        GList *found_link;
 
211
 
 
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
            };
 
231
 
 
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);
 
245
 
 
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
    }
 
255
}
 
256
 
 
257
static void
 
258
ht_foreach_build_slist (gpointer  key,
 
259
                        gpointer  value,
 
260
                        GSList  **slist)
 
261
{
 
262
    *slist = g_slist_prepend (*slist, value);
 
263
}
 
264
 
 
265
static void
 
266
gdl_dock_master_dispose (GObject *g_object)
 
267
{
 
268
    GdlDockMaster *master;
 
269
    
 
270
    g_return_if_fail (GDL_IS_DOCK_MASTER (g_object));
 
271
 
 
272
    master = GDL_DOCK_MASTER (g_object);
 
273
 
 
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;
 
310
 
 
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
    }
 
319
 
 
320
    GDL_CALL_PARENT (G_OBJECT_CLASS, dispose, (g_object));
 
321
}
 
322
 
 
323
static void 
 
324
foreach_lock_unlock (GdlDockItem *item,
 
325
                     gboolean     locked)
 
326
{
 
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));
 
335
}
 
336
 
 
337
static void
 
338
gdl_dock_master_lock_unlock (GdlDockMaster *master,
 
339
                             gboolean       locked)
 
340
{
 
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
    }
 
348
 
 
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));
 
353
}
 
354
 
 
355
static void
 
356
gdl_dock_master_set_property  (GObject      *object,
 
357
                               guint         prop_id,
 
358
                               const GValue *value,
 
359
                               GParamSpec   *pspec)
 
360
{
 
361
    GdlDockMaster *master = GDL_DOCK_MASTER (object);
 
362
 
 
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
    }
 
382
}
 
383
 
 
384
static void
 
385
gdl_dock_master_get_property  (GObject      *object,
 
386
                               guint         prop_id,
 
387
                               GValue       *value,
 
388
                               GParamSpec   *pspec)
 
389
{
 
390
    GdlDockMaster *master = GDL_DOCK_MASTER (object);
 
391
 
 
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
    }
 
409
}
 
410
 
 
411
static void
 
412
gdl_dock_master_drag_begin (GdlDockItem *item,
 
413
                            gpointer     data)
 
414
{
 
415
    GdlDockMaster  *master;
 
416
    GdlDockRequest *request;
 
417
    
 
418
    g_return_if_fail (data != NULL);
 
419
    g_return_if_fail (item != NULL);
 
420
 
 
421
    master = GDL_DOCK_MASTER (data);
 
422
 
 
423
    if (!master->_priv->drag_request)
 
424
        master->_priv->drag_request = g_new0 (GdlDockRequest, 1);
 
425
 
 
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);
 
434
 
 
435
    master->_priv->rect_drawn = FALSE;
 
436
    master->_priv->rect_owner = NULL;
 
437
}
 
438
 
 
439
static void
 
440
gdl_dock_master_drag_end (GdlDockItem *item, 
 
441
                          gboolean     cancelled,
 
442
                          gpointer     data)
 
443
{
 
444
    GdlDockMaster  *master;
 
445
    GdlDockRequest *request;
 
446
    
 
447
    g_return_if_fail (data != NULL);
 
448
    g_return_if_fail (item != NULL);
 
449
 
 
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);
 
470
}
 
471
 
 
472
static void
 
473
gdl_dock_master_drag_motion (GdlDockItem *item, 
 
474
                             gint         root_x, 
 
475
                             gint         root_y,
 
476
                             gpointer     data)
 
477
{
 
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);
 
487
 
 
488
    master = GDL_DOCK_MASTER (data);
 
489
    request = master->_priv->drag_request;
 
490
 
 
491
    g_return_if_fail (GDL_DOCK_OBJECT (item) == request->applicant);
 
492
    
 
493
    my_request = *request;
 
494
 
 
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
    }
 
520
 
 
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;
 
532
 
 
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
    }
 
547
 
 
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;
 
555
 
 
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;
 
560
 
 
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;
 
564
 
 
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;
 
567
 
 
568
        /* setup extra docking information */
 
569
        if (G_IS_VALUE (&my_request.extra))
 
570
            g_value_unset (&my_request.extra);
 
571
 
 
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;
 
583
 
 
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
    }
 
594
 
 
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);
 
602
}
 
603
 
 
604
static void
 
605
_gdl_dock_master_foreach (gpointer key,
 
606
                          gpointer value,
 
607
                          gpointer user_data)
 
608
{
 
609
    (void)key;
 
610
    struct {
 
611
        GFunc    function;
 
612
        gpointer user_data;
 
613
    } *data = user_data;
 
614
 
 
615
    (* data->function) (GTK_WIDGET (value), data->user_data);
 
616
}
 
617
 
 
618
static void
 
619
gdl_dock_master_xor_rect (GdlDockMaster *master)
 
620
{
 
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 ();
 
638
 
 
639
    if (!master->_priv->root_xor_gc) {
 
640
        GdkGCValues values;
 
641
 
 
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
    };
 
647
 
 
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);
 
656
 
 
657
    gdk_draw_rectangle (window, master->_priv->root_xor_gc, 0, 
 
658
                        rect->x, rect->y,
 
659
                        rect->width, rect->height);
 
660
 
 
661
    gdk_gc_set_dashes (master->_priv->root_xor_gc, 0, dash_list, 2);
 
662
 
 
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);
 
666
}
 
667
 
 
668
static void
 
669
gdl_dock_master_layout_changed (GdlDockMaster *master)
 
670
{
 
671
    g_return_if_fail (GDL_IS_DOCK_MASTER (master));
 
672
 
 
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");
 
677
 
 
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
    }
 
683
}
 
684
 
 
685
static gboolean
 
686
idle_emit_layout_changed (gpointer user_data)
 
687
{
 
688
    GdlDockMaster *master = user_data;
 
689
 
 
690
    g_return_val_if_fail (master && GDL_IS_DOCK_MASTER (master), FALSE);
 
691
 
 
692
    master->_priv->idle_layout_changed_id = 0;
 
693
    g_signal_emit (master, master_signals [LAYOUT_CHANGED], 0);
 
694
    
 
695
    return FALSE;
 
696
}
 
697
 
 
698
static void 
 
699
item_dock_cb (GdlDockObject    *object,
 
700
              GdlDockObject    *requestor,
 
701
              GdlDockPlacement  position,
 
702
              GValue           *other_data,
 
703
              gpointer          user_data)
 
704
{
 
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));
 
709
 
 
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
    }
 
719
}
 
720
 
 
721
static void 
 
722
item_detach_cb (GdlDockObject *object,
 
723
                gboolean       recursive,
 
724
                gpointer       user_data)
 
725
{
 
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));
 
730
 
 
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
    }
 
737
}
 
738
 
 
739
static void
 
740
item_notify_cb (GdlDockObject *object,
 
741
                GParamSpec    *pspec,
 
742
                gpointer       user_data)
 
743
{
 
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);
 
749
 
 
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");
 
760
}
 
761
 
 
762
/* ----- Public interface ----- */
 
763
 
 
764
void
 
765
gdl_dock_master_add (GdlDockMaster *master,
 
766
                     GdlDockObject *object)
 
767
{
 
768
    g_return_if_fail (master != NULL && object != NULL);
 
769
 
 
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);
 
806
 
 
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);
 
811
 
 
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);
 
825
 
 
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
    }
 
850
}
 
851
 
 
852
void
 
853
gdl_dock_master_remove (GdlDockMaster *master,
 
854
                        GdlDockObject *object)
 
855
{
 
856
    g_return_if_fail (master != NULL && object != NULL);
 
857
 
 
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);
 
874
 
 
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);
 
885
}
 
886
 
 
887
void
 
888
gdl_dock_master_foreach (GdlDockMaster *master,
 
889
                         GFunc          function,
 
890
                         gpointer       user_data)
 
891
{
 
892
    struct {
 
893
        GFunc    function;
 
894
        gpointer user_data;
 
895
    } data;
 
896
 
 
897
    g_return_if_fail (master != NULL && function != NULL);
 
898
 
 
899
    data.function = function;
 
900
    data.user_data = user_data;
 
901
    g_hash_table_foreach (master->dock_objects, _gdl_dock_master_foreach, &data);
 
902
}
 
903
 
 
904
void
 
905
gdl_dock_master_foreach_toplevel (GdlDockMaster *master,
 
906
                                  gboolean       include_controller,
 
907
                                  GFunc          function,
 
908
                                  gpointer       user_data)
 
909
{
 
910
    GList *l;
 
911
    
 
912
    g_return_if_fail (master != NULL && function != NULL);
 
913
 
 
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
    }
 
920
}
 
921
 
 
922
GdlDockObject *
 
923
gdl_dock_master_get_object (GdlDockMaster *master,
 
924
                            const gchar   *nick_name)
 
925
{
 
926
    gpointer *found;
 
927
    
 
928
    g_return_val_if_fail (master != NULL, NULL);
 
929
 
 
930
    if (!nick_name)
 
931
        return NULL;
 
932
 
 
933
    found = g_hash_table_lookup (master->dock_objects, nick_name);
 
934
 
 
935
    return found ? GDL_DOCK_OBJECT (found) : NULL;
 
936
}
 
937
 
 
938
GdlDockObject *
 
939
gdl_dock_master_get_controller (GdlDockMaster *master)
 
940
{
 
941
    g_return_val_if_fail (master != NULL, NULL);
 
942
 
 
943
    return master->controller;
 
944
}
 
945
 
 
946
void
 
947
gdl_dock_master_set_controller (GdlDockMaster *master,
 
948
                                GdlDockObject *new_controller)
 
949
{
 
950
    g_return_if_fail (master != NULL);
 
951
 
 
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;
 
961
 
 
962
    } else {
 
963
        master->controller = NULL;
 
964
        /* no controller, no master */
 
965
        g_object_unref (master);
 
966
    }
 
967
}
 
968
 
 
969
static void
 
970
set_switcher_style_foreach (GtkWidget *obj, gpointer user_data)
 
971
{
 
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
    }
 
990
}
 
991
 
 
992
static void
 
993
gdl_dock_master_set_switcher_style (GdlDockMaster *master,
 
994
                                    GdlSwitcherStyle switcher_style)
 
995
{
 
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
    }
 
1006
 
 
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));
 
1010
}