~canonical-dx-team/ubuntu/maverick/gtk+2.0/menuproxy

« back to all changes in this revision

Viewing changes to gtk/gtkuimanager.c

  • Committer: Bazaar Package Importer
  • Author(s): Sebastien Bacher
  • Date: 2007-05-04 12:24:25 UTC
  • mfrom: (1.1.21 upstream)
  • Revision ID: james.westby@ubuntu.com-20070504122425-0m8midgzrp40y8w2
Tags: 2.10.12-1ubuntu1
* Sync with Debian
* New upstream version:
  Fixed bugs:
  - 379414 file chooser warnings when changing path in the entry
  - 418585 GtkFileChooserDefault sizing code is not DPI independent
  - 419568 Crash in search if start with special letter
  - 435062 build dies with icon cache validation
  - 379399 Segfault to call gtk_print_operation_run twice.
  - 387889 cups backend has problems when there are too many printers
  - 418531 invalid read to gtkicontheme.c gtk_icon_theme_lookup_icon...
  - 423916 crash in color scheme code
  - 424042 Segmentation fault while quickly pressing Alt+arrows
  - 415260 Protect against negative indices when setting values in G...
  - 419171 XGetVisualInfo() may not set nxvisuals
  - 128852 Gdk cursors don't look good on win32
  - 344657 Ctrl-H doesn't toggle "Show Hidden Files" setting
  - 345345 PrintOperation::paginate is not emitted for class handler
  - 347567 GtkPrintOperation::end-print is not emitted if it's cance...
  - 369112 gtk_ui_manager_add_ui should accept unnamed separator
  - 392015 Selected menu item invisible on Windows Vista
  - 399253 MS-Windows Theme Bottom Tab placement rendering glitches
  - 399425 gtk_input_dialog_fill_axes() adds child to gtkscrolledwin...
  - 403251 [patch] little memory leak in GtkPrintJob
  - 403267 [patch] memory leak in GtkPageSetupUnixDialog
  - 403470 MS-Windows Theme tab placement other than on top leaks a ...
  - 404506 Windows system fonts that have multi-byte font names cann...
  - 405089 Incorrect window placement for GtkEventBox private window
  - 405515 Minor leak in gtkfilesystemmodel.c
  - 405539 gdk_pixbuf_save() for PNG saver can return FALSE without ...
  - 415681 gdk_window_clear_area includes an extra line and column o...
  - 418219 GtkRecentChooser should apply filter before sorting and c...
  - 418403 Scroll to printer after selecting it from settings
  - 421985 _gtk_print_operation_platform_backend_launch_preview
  - 421990 gtk_print_job_get_surface
  - 421993 gtk_print_operation_init
  - 423064 Conditional jump or move depends on uninitialised value(s...
  - 423722 Fix printing header in gtk-demo
  - 424168 gtk_print_operation_run on async preview
  - 425655 Don't install gtk+-unix-print-2.0.pc on non-UNIX platforms
  - 425786 GDK segfaults if XineramaQueryScreens fails
  - 428665 Lpr Backend gets stuck in infinite loop during gtk_enumer...
  - 429902 GtkPrintOperation leaks cairo contextes
  - 431997 First delay of GdkPixbufAnimationIter is wrong
  - 433242 Inconsistent scroll arrow position calculations
  - 433972 Placing gtk.Expander inside a gtk.TextView() changes gtk....
  - 434261 _gtk_toolbar_elide_underscores incorrectly handles some s...
  - 383354 ctrl-L should make 'Location' entry disappear
  - 418673 gtk_recent_manager_add_item
  - 429732 gtk_accel_group_finalize accesses invalid memory
  - 435028 WM_CLIENT_LEADER is wrong on the leader_window
  - 431067 Background of the header window is not updated
  - 338843 add recent files support inside the ui manager
  - 148535 add drop shadow to menus, tooltips, etc. under Windows XP
* debian/control.in:
  - Conflicts on ubuntulooks (<= 0.9.11-1)
* debian/patches/15_default-fallback-icon-theme.patch:
  - patch from Debian, fallback on gnome icon theme

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * GTK - The GIMP Toolkit
 
3
 * Copyright (C) 1998, 1999 Red Hat, Inc.
 
4
 * All rights reserved.
 
5
 *
 
6
 * This Library is free software; you can redistribute it and/or
 
7
 * modify it under the terms of the GNU Library General Public License as
 
8
 * published by the Free Software Foundation; either version 2 of the
 
9
 * License, or (at your option) any later version.
 
10
 *
 
11
 * This Library is distributed in the hope that it will be useful,
 
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
14
 * Library General Public License for more details.
 
15
 *
 
16
 * You should have received a copy of the GNU Library General Public
 
17
 * License along with the Gnome Library; see the file COPYING.LIB.  If not,
 
18
 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 
19
 * Boston, MA 02111-1307, USA.
 
20
 */
 
21
 
 
22
/*
 
23
 * Author: James Henstridge <james@daa.com.au>
 
24
 *
 
25
 * Modified by the GTK+ Team and others 2003.  See the AUTHORS
 
26
 * file for a list of people on the GTK+ Team.  See the ChangeLog
 
27
 * files for a list of changes.  These files are distributed with
 
28
 * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
 
29
 */
 
30
 
 
31
#include <config.h>
 
32
 
 
33
#include <string.h>
 
34
#include "gtkintl.h"
 
35
#include "gtkmarshalers.h"
 
36
#include "gtkmenu.h"
 
37
#include "gtkmenubar.h"
 
38
#include "gtkmenushell.h"
 
39
#include "gtkmenutoolbutton.h"
 
40
#include "gtkseparatormenuitem.h"
 
41
#include "gtkseparatortoolitem.h"
 
42
#include "gtktearoffmenuitem.h"
 
43
#include "gtktoolbar.h"
 
44
#include "gtkuimanager.h"
 
45
#include "gtkprivate.h"
 
46
#include "gtkalias.h"
 
47
 
 
48
#undef DEBUG_UI_MANAGER
 
49
 
 
50
typedef enum 
 
51
{
 
52
  NODE_TYPE_UNDECIDED,
 
53
  NODE_TYPE_ROOT,
 
54
  NODE_TYPE_MENUBAR,
 
55
  NODE_TYPE_MENU,
 
56
  NODE_TYPE_TOOLBAR,
 
57
  NODE_TYPE_MENU_PLACEHOLDER,
 
58
  NODE_TYPE_TOOLBAR_PLACEHOLDER,
 
59
  NODE_TYPE_POPUP,
 
60
  NODE_TYPE_MENUITEM,
 
61
  NODE_TYPE_TOOLITEM,
 
62
  NODE_TYPE_SEPARATOR,
 
63
  NODE_TYPE_ACCELERATOR
 
64
} NodeType;
 
65
 
 
66
typedef struct _Node Node;
 
67
 
 
68
struct _Node {
 
69
  NodeType type;
 
70
 
 
71
  gchar *name;
 
72
 
 
73
  GQuark action_name;
 
74
  GtkAction *action;
 
75
  GtkWidget *proxy;
 
76
  GtkWidget *extra; /* second separator for placeholders */
 
77
 
 
78
  GList *uifiles;
 
79
 
 
80
  guint dirty : 1;
 
81
  guint expand : 1;  /* used for separators */
 
82
};
 
83
 
 
84
#define GTK_UI_MANAGER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_UI_MANAGER, GtkUIManagerPrivate))
 
85
 
 
86
struct _GtkUIManagerPrivate 
 
87
{
 
88
  GtkAccelGroup *accel_group;
 
89
 
 
90
  GNode *root_node;
 
91
  GList *action_groups;
 
92
 
 
93
  guint last_merge_id;
 
94
 
 
95
  guint update_tag;  
 
96
 
 
97
  gboolean add_tearoffs;
 
98
};
 
99
 
 
100
#define NODE_INFO(node) ((Node *)node->data)
 
101
 
 
102
typedef struct _NodeUIReference NodeUIReference;
 
103
 
 
104
struct _NodeUIReference 
 
105
{
 
106
  guint merge_id;
 
107
  GQuark action_quark;
 
108
};
 
109
 
 
110
static void        gtk_ui_manager_finalize        (GObject           *object);
 
111
static void        gtk_ui_manager_set_property    (GObject           *object,
 
112
                                                   guint              prop_id,
 
113
                                                   const GValue      *value,
 
114
                                                   GParamSpec        *pspec);
 
115
static void        gtk_ui_manager_get_property    (GObject           *object,
 
116
                                                   guint              prop_id,
 
117
                                                   GValue            *value,
 
118
                                                   GParamSpec        *pspec);
 
119
static GtkWidget * gtk_ui_manager_real_get_widget (GtkUIManager      *manager,
 
120
                                                   const gchar       *path);
 
121
static GtkAction * gtk_ui_manager_real_get_action (GtkUIManager      *manager,
 
122
                                                   const gchar       *path);
 
123
static void        queue_update                   (GtkUIManager      *self);
 
124
static void        dirty_all_nodes                (GtkUIManager      *self);
 
125
static void        mark_node_dirty                (GNode             *node);
 
126
static GNode     * get_child_node                 (GtkUIManager      *self,
 
127
                                                   GNode             *parent,
 
128
                                                   GNode             *sibling,
 
129
                                                   const gchar       *childname,
 
130
                                                   gint               childname_length,
 
131
                                                   NodeType           node_type,
 
132
                                                   gboolean           create,
 
133
                                                   gboolean           top);
 
134
static GNode     * get_node                       (GtkUIManager      *self,
 
135
                                                   const gchar       *path,
 
136
                                                   NodeType           node_type,
 
137
                                                   gboolean           create);
 
138
static gboolean    free_node                      (GNode             *node);
 
139
static void        node_prepend_ui_reference      (GNode             *node,
 
140
                                                   guint              merge_id,
 
141
                                                   GQuark             action_quark);
 
142
static void        node_remove_ui_reference       (GNode             *node,
 
143
                                                   guint              merge_id);
 
144
 
 
145
 
 
146
enum 
 
147
{
 
148
  ADD_WIDGET,
 
149
  ACTIONS_CHANGED,
 
150
  CONNECT_PROXY,
 
151
  DISCONNECT_PROXY,
 
152
  PRE_ACTIVATE,
 
153
  POST_ACTIVATE,
 
154
  LAST_SIGNAL
 
155
};
 
156
 
 
157
enum
 
158
{
 
159
  PROP_0,
 
160
  PROP_ADD_TEAROFFS,
 
161
  PROP_UI
 
162
};
 
163
 
 
164
static guint ui_manager_signals[LAST_SIGNAL] = { 0 };
 
165
 
 
166
G_DEFINE_TYPE (GtkUIManager, gtk_ui_manager, G_TYPE_OBJECT)
 
167
 
 
168
static void
 
169
gtk_ui_manager_class_init (GtkUIManagerClass *klass)
 
170
{
 
171
  GObjectClass *gobject_class;
 
172
 
 
173
  gobject_class = G_OBJECT_CLASS (klass);
 
174
 
 
175
  gobject_class->finalize = gtk_ui_manager_finalize;
 
176
  gobject_class->set_property = gtk_ui_manager_set_property;
 
177
  gobject_class->get_property = gtk_ui_manager_get_property;
 
178
  klass->get_widget = gtk_ui_manager_real_get_widget;
 
179
  klass->get_action = gtk_ui_manager_real_get_action;
 
180
 
 
181
  /**
 
182
   * GtkUIManager:add-tearoffs:
 
183
   *
 
184
   * The "add-tearoffs" property controls whether generated menus 
 
185
   * have tearoff menu items. 
 
186
   *
 
187
   * Note that this only affects regular menus. Generated popup 
 
188
   * menus never have tearoff menu items.   
 
189
   *
 
190
   * Since: 2.4
 
191
   */
 
192
  g_object_class_install_property (gobject_class,
 
193
                                   PROP_ADD_TEAROFFS,
 
194
                                   g_param_spec_boolean ("add-tearoffs",
 
195
                                                         P_("Add tearoffs to menus"),
 
196
                                                         P_("Whether tearoff menu items should be added to menus"),
 
197
                                                         FALSE,
 
198
                                                         GTK_PARAM_READWRITE));
 
199
 
 
200
  g_object_class_install_property (gobject_class,
 
201
                                   PROP_UI,
 
202
                                   g_param_spec_string ("ui",
 
203
                                                        P_("Merged UI definition"),
 
204
                                                        P_("An XML string describing the merged UI"),
 
205
                                                        "<ui>\n</ui>\n",
 
206
                                                        GTK_PARAM_READABLE));
 
207
 
 
208
 
 
209
  /**
 
210
   * GtkUIManager::add-widget:
 
211
   * @merge: a #GtkUIManager
 
212
   * @widget: the added widget
 
213
   *
 
214
   * The add_widget signal is emitted for each generated menubar and toolbar.
 
215
   * It is not emitted for generated popup menus, which can be obtained by 
 
216
   * gtk_ui_manager_get_widget().
 
217
   *
 
218
   * Since: 2.4
 
219
   */
 
220
  ui_manager_signals[ADD_WIDGET] =
 
221
    g_signal_new (I_("add_widget"),
 
222
                  G_OBJECT_CLASS_TYPE (klass),
 
223
                  G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE,
 
224
                  G_STRUCT_OFFSET (GtkUIManagerClass, add_widget),
 
225
                  NULL, NULL,
 
226
                  g_cclosure_marshal_VOID__OBJECT,
 
227
                  G_TYPE_NONE, 1, 
 
228
                  GTK_TYPE_WIDGET);
 
229
 
 
230
  /**
 
231
   * GtkUIManager::actions-changed:
 
232
   * @merge: a #GtkUIManager
 
233
   *
 
234
   * The "actions-changed" signal is emitted whenever the set of actions
 
235
   * changes.
 
236
   *
 
237
   * Since: 2.4
 
238
   */
 
239
  ui_manager_signals[ACTIONS_CHANGED] =
 
240
    g_signal_new (I_("actions_changed"),
 
241
                  G_OBJECT_CLASS_TYPE (klass),
 
242
                  G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE,
 
243
                  G_STRUCT_OFFSET (GtkUIManagerClass, actions_changed),  
 
244
                  NULL, NULL,
 
245
                  g_cclosure_marshal_VOID__VOID,
 
246
                  G_TYPE_NONE, 0);
 
247
  
 
248
  /**
 
249
   * GtkUIManager::connect-proxy:
 
250
   * @uimanager: the ui manager
 
251
   * @action: the action
 
252
   * @proxy: the proxy
 
253
   *
 
254
   * The connect_proxy signal is emitted after connecting a proxy to 
 
255
   * an action in the group. 
 
256
   *
 
257
   * This is intended for simple customizations for which a custom action
 
258
   * class would be too clumsy, e.g. showing tooltips for menuitems in the
 
259
   * statusbar.
 
260
   *
 
261
   * Since: 2.4
 
262
   */
 
263
  ui_manager_signals[CONNECT_PROXY] =
 
264
    g_signal_new (I_("connect_proxy"),
 
265
                  G_OBJECT_CLASS_TYPE (klass),
 
266
                  G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE,
 
267
                  G_STRUCT_OFFSET (GtkUIManagerClass, connect_proxy),
 
268
                  NULL, NULL,
 
269
                  _gtk_marshal_VOID__OBJECT_OBJECT,
 
270
                  G_TYPE_NONE, 2, 
 
271
                  GTK_TYPE_ACTION,
 
272
                  GTK_TYPE_WIDGET);
 
273
 
 
274
  /**
 
275
   * GtkUIManager::disconnect-proxy:
 
276
   * @uimanager: the ui manager
 
277
   * @action: the action
 
278
   * @proxy: the proxy
 
279
   *
 
280
   * The disconnect_proxy signal is emitted after disconnecting a proxy 
 
281
   * from an action in the group. 
 
282
   *
 
283
   * Since: 2.4
 
284
   */
 
285
  ui_manager_signals[DISCONNECT_PROXY] =
 
286
    g_signal_new (I_("disconnect_proxy"),
 
287
                  G_OBJECT_CLASS_TYPE (klass),
 
288
                  G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE,
 
289
                  G_STRUCT_OFFSET (GtkUIManagerClass, disconnect_proxy),
 
290
                  NULL, NULL,
 
291
                  _gtk_marshal_VOID__OBJECT_OBJECT,
 
292
                  G_TYPE_NONE, 2,
 
293
                  GTK_TYPE_ACTION,
 
294
                  GTK_TYPE_WIDGET);
 
295
 
 
296
  /**
 
297
   * GtkUIManager::pre-activate:
 
298
   * @uimanager: the ui manager
 
299
   * @action: the action
 
300
   *
 
301
   * The pre_activate signal is emitted just before the @action
 
302
   * is activated.
 
303
   *
 
304
   * This is intended for applications to get notification
 
305
   * just before any action is activated.
 
306
   *
 
307
   * Since: 2.4
 
308
   */
 
309
  ui_manager_signals[PRE_ACTIVATE] =
 
310
    g_signal_new (I_("pre_activate"),
 
311
                  G_OBJECT_CLASS_TYPE (klass),
 
312
                  G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE,
 
313
                  G_STRUCT_OFFSET (GtkUIManagerClass, pre_activate),
 
314
                  NULL, NULL,
 
315
                  _gtk_marshal_VOID__OBJECT,
 
316
                  G_TYPE_NONE, 1,
 
317
                  GTK_TYPE_ACTION);
 
318
 
 
319
  /**
 
320
   * GtkUIManager::post-activate:
 
321
   * @uimanager: the ui manager
 
322
   * @action: the action
 
323
   *
 
324
   * The post_activate signal is emitted just after the @action
 
325
   * is activated.
 
326
   *
 
327
   * This is intended for applications to get notification
 
328
   * just after any action is activated.
 
329
   *
 
330
   * Since: 2.4
 
331
   */
 
332
  ui_manager_signals[POST_ACTIVATE] =
 
333
    g_signal_new (I_("post_activate"),
 
334
                  G_OBJECT_CLASS_TYPE (klass),
 
335
                  G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE,
 
336
                  G_STRUCT_OFFSET (GtkUIManagerClass, post_activate),
 
337
                  NULL, NULL,
 
338
                  _gtk_marshal_VOID__OBJECT,
 
339
                  G_TYPE_NONE, 1,
 
340
                  GTK_TYPE_ACTION);
 
341
 
 
342
  klass->add_widget = NULL;
 
343
  klass->actions_changed = NULL;
 
344
  klass->connect_proxy = NULL;
 
345
  klass->disconnect_proxy = NULL;
 
346
  klass->pre_activate = NULL;
 
347
  klass->post_activate = NULL;
 
348
 
 
349
  g_type_class_add_private (gobject_class, sizeof (GtkUIManagerPrivate));
 
350
}
 
351
 
 
352
 
 
353
static void
 
354
gtk_ui_manager_init (GtkUIManager *self)
 
355
{
 
356
  guint merge_id;
 
357
  GNode *node;
 
358
 
 
359
  self->private_data = GTK_UI_MANAGER_GET_PRIVATE (self);
 
360
 
 
361
  self->private_data->accel_group = gtk_accel_group_new ();
 
362
 
 
363
  self->private_data->root_node = NULL;
 
364
  self->private_data->action_groups = NULL;
 
365
 
 
366
  self->private_data->last_merge_id = 0;
 
367
  self->private_data->add_tearoffs = FALSE;
 
368
 
 
369
  merge_id = gtk_ui_manager_new_merge_id (self);
 
370
  node = get_child_node (self, NULL, NULL, "ui", 2,
 
371
                         NODE_TYPE_ROOT, TRUE, FALSE);
 
372
  node_prepend_ui_reference (node, merge_id, 0);
 
373
}
 
374
 
 
375
static void
 
376
gtk_ui_manager_finalize (GObject *object)
 
377
{
 
378
  GtkUIManager *self = GTK_UI_MANAGER (object);
 
379
  
 
380
  if (self->private_data->update_tag != 0)
 
381
    {
 
382
      g_source_remove (self->private_data->update_tag);
 
383
      self->private_data->update_tag = 0;
 
384
    }
 
385
  
 
386
  g_node_traverse (self->private_data->root_node, 
 
387
                   G_POST_ORDER, G_TRAVERSE_ALL, -1,
 
388
                   (GNodeTraverseFunc)free_node, NULL);
 
389
  g_node_destroy (self->private_data->root_node);
 
390
  self->private_data->root_node = NULL;
 
391
  
 
392
  g_list_foreach (self->private_data->action_groups,
 
393
                  (GFunc) g_object_unref, NULL);
 
394
  g_list_free (self->private_data->action_groups);
 
395
  self->private_data->action_groups = NULL;
 
396
 
 
397
  g_object_unref (self->private_data->accel_group);
 
398
  self->private_data->accel_group = NULL;
 
399
 
 
400
  G_OBJECT_CLASS (gtk_ui_manager_parent_class)->finalize (object);
 
401
}
 
402
 
 
403
static void
 
404
gtk_ui_manager_set_property (GObject         *object,
 
405
                             guint            prop_id,
 
406
                             const GValue    *value,
 
407
                             GParamSpec      *pspec)
 
408
{
 
409
  GtkUIManager *self = GTK_UI_MANAGER (object);
 
410
 
 
411
  switch (prop_id)
 
412
    {
 
413
    case PROP_ADD_TEAROFFS:
 
414
      gtk_ui_manager_set_add_tearoffs (self, g_value_get_boolean (value));
 
415
      break;
 
416
    default:
 
417
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
418
      break;
 
419
    }
 
420
}
 
421
 
 
422
static void
 
423
gtk_ui_manager_get_property (GObject         *object,
 
424
                             guint            prop_id,
 
425
                             GValue          *value,
 
426
                             GParamSpec      *pspec)
 
427
{
 
428
  GtkUIManager *self = GTK_UI_MANAGER (object);
 
429
 
 
430
  switch (prop_id)
 
431
    {
 
432
    case PROP_ADD_TEAROFFS:
 
433
      g_value_set_boolean (value, self->private_data->add_tearoffs);
 
434
      break;
 
435
    case PROP_UI:
 
436
      g_value_take_string (value, gtk_ui_manager_get_ui (self));
 
437
      break;
 
438
    default:
 
439
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
440
      break;
 
441
    }
 
442
}
 
443
 
 
444
static GtkWidget *
 
445
gtk_ui_manager_real_get_widget (GtkUIManager *self,
 
446
                                const gchar  *path)
 
447
{
 
448
  GNode *node;
 
449
 
 
450
  /* ensure that there are no pending updates before we get the
 
451
   * widget */
 
452
  gtk_ui_manager_ensure_update (self);
 
453
 
 
454
  node = get_node (self, path, NODE_TYPE_UNDECIDED, FALSE);
 
455
 
 
456
  if (node == NULL)
 
457
    return NULL;
 
458
 
 
459
  return NODE_INFO (node)->proxy;
 
460
}
 
461
 
 
462
static GtkAction *
 
463
gtk_ui_manager_real_get_action (GtkUIManager *self,
 
464
                                const gchar  *path)
 
465
{
 
466
  GNode *node;
 
467
 
 
468
  /* ensure that there are no pending updates before we get
 
469
   * the action */
 
470
  gtk_ui_manager_ensure_update (self);
 
471
 
 
472
  node = get_node (self, path, NODE_TYPE_UNDECIDED, FALSE);
 
473
 
 
474
  if (node == NULL)
 
475
    return NULL;
 
476
 
 
477
  return NODE_INFO (node)->action;
 
478
}
 
479
 
 
480
 
 
481
/**
 
482
 * gtk_ui_manager_new:
 
483
 * 
 
484
 * Creates a new ui manager object.
 
485
 * 
 
486
 * Return value: a new ui manager object.
 
487
 *
 
488
 * Since: 2.4
 
489
 **/
 
490
GtkUIManager*
 
491
gtk_ui_manager_new (void)
 
492
{
 
493
  return g_object_new (GTK_TYPE_UI_MANAGER, NULL);
 
494
}
 
495
 
 
496
 
 
497
/**
 
498
 * gtk_ui_manager_get_add_tearoffs:
 
499
 * @self: a #GtkUIManager
 
500
 * 
 
501
 * Returns whether menus generated by this #GtkUIManager
 
502
 * will have tearoff menu items. 
 
503
 * 
 
504
 * Return value: whether tearoff menu items are added
 
505
 *
 
506
 * Since: 2.4
 
507
 **/
 
508
gboolean 
 
509
gtk_ui_manager_get_add_tearoffs (GtkUIManager *self)
 
510
{
 
511
  g_return_val_if_fail (GTK_IS_UI_MANAGER (self), FALSE);
 
512
  
 
513
  return self->private_data->add_tearoffs;
 
514
}
 
515
 
 
516
 
 
517
/**
 
518
 * gtk_ui_manager_set_add_tearoffs:
 
519
 * @self: a #GtkUIManager
 
520
 * @add_tearoffs: whether tearoff menu items are added
 
521
 * 
 
522
 * Sets the "add_tearoffs" property, which controls whether menus 
 
523
 * generated by this #GtkUIManager will have tearoff menu items. 
 
524
 *
 
525
 * Note that this only affects regular menus. Generated popup 
 
526
 * menus never have tearoff menu items.
 
527
 *
 
528
 * Since: 2.4
 
529
 **/
 
530
void 
 
531
gtk_ui_manager_set_add_tearoffs (GtkUIManager *self,
 
532
                                 gboolean      add_tearoffs)
 
533
{
 
534
  g_return_if_fail (GTK_IS_UI_MANAGER (self));
 
535
 
 
536
  add_tearoffs = add_tearoffs != FALSE;
 
537
 
 
538
  if (add_tearoffs != self->private_data->add_tearoffs)
 
539
    {
 
540
      self->private_data->add_tearoffs = add_tearoffs;
 
541
      
 
542
      dirty_all_nodes (self);
 
543
 
 
544
      g_object_notify (G_OBJECT (self), "add-tearoffs");
 
545
    }
 
546
}
 
547
 
 
548
static void
 
549
cb_proxy_connect_proxy (GtkActionGroup *group, 
 
550
                        GtkAction      *action,
 
551
                        GtkWidget      *proxy, 
 
552
                        GtkUIManager *self)
 
553
{
 
554
  g_signal_emit (self, ui_manager_signals[CONNECT_PROXY], 0, action, proxy);
 
555
}
 
556
 
 
557
static void
 
558
cb_proxy_disconnect_proxy (GtkActionGroup *group, 
 
559
                           GtkAction      *action,
 
560
                           GtkWidget      *proxy, 
 
561
                           GtkUIManager *self)
 
562
{
 
563
  g_signal_emit (self, ui_manager_signals[DISCONNECT_PROXY], 0, action, proxy);
 
564
}
 
565
 
 
566
static void
 
567
cb_proxy_pre_activate (GtkActionGroup *group, 
 
568
                       GtkAction      *action,
 
569
                       GtkUIManager   *self)
 
570
{
 
571
  g_signal_emit (self, ui_manager_signals[PRE_ACTIVATE], 0, action);
 
572
}
 
573
 
 
574
static void
 
575
cb_proxy_post_activate (GtkActionGroup *group, 
 
576
                        GtkAction      *action,
 
577
                        GtkUIManager   *self)
 
578
{
 
579
  g_signal_emit (self, ui_manager_signals[POST_ACTIVATE], 0, action);
 
580
}
 
581
 
 
582
/**
 
583
 * gtk_ui_manager_insert_action_group:
 
584
 * @self: a #GtkUIManager object
 
585
 * @action_group: the action group to be inserted
 
586
 * @pos: the position at which the group will be inserted.
 
587
 * 
 
588
 * Inserts an action group into the list of action groups associated 
 
589
 * with @self. Actions in earlier groups hide actions with the same 
 
590
 * name in later groups. 
 
591
 *
 
592
 * Since: 2.4
 
593
 **/
 
594
void
 
595
gtk_ui_manager_insert_action_group (GtkUIManager   *self,
 
596
                                    GtkActionGroup *action_group, 
 
597
                                    gint            pos)
 
598
{
 
599
  g_return_if_fail (GTK_IS_UI_MANAGER (self));
 
600
  g_return_if_fail (GTK_IS_ACTION_GROUP (action_group));
 
601
  g_return_if_fail (g_list_find (self->private_data->action_groups, 
 
602
                                 action_group) == NULL);
 
603
 
 
604
  g_object_ref (action_group);
 
605
  self->private_data->action_groups = 
 
606
    g_list_insert (self->private_data->action_groups, action_group, pos);
 
607
  g_object_connect (action_group,
 
608
                    "object_signal::connect_proxy", G_CALLBACK (cb_proxy_connect_proxy), self,
 
609
                    "object_signal::disconnect_proxy", G_CALLBACK (cb_proxy_disconnect_proxy), self,
 
610
                    "object_signal::pre_activate", G_CALLBACK (cb_proxy_pre_activate), self,
 
611
                    "object_signal::post_activate", G_CALLBACK (cb_proxy_post_activate), self,
 
612
                    NULL);
 
613
 
 
614
  /* dirty all nodes, as action bindings may change */
 
615
  dirty_all_nodes (self);
 
616
 
 
617
  g_signal_emit (self, ui_manager_signals[ACTIONS_CHANGED], 0);
 
618
}
 
619
 
 
620
/**
 
621
 * gtk_ui_manager_remove_action_group:
 
622
 * @self: a #GtkUIManager object
 
623
 * @action_group: the action group to be removed
 
624
 * 
 
625
 * Removes an action group from the list of action groups associated 
 
626
 * with @self.
 
627
 *
 
628
 * Since: 2.4
 
629
 **/
 
630
void
 
631
gtk_ui_manager_remove_action_group (GtkUIManager   *self,
 
632
                                    GtkActionGroup *action_group)
 
633
{
 
634
  g_return_if_fail (GTK_IS_UI_MANAGER (self));
 
635
  g_return_if_fail (GTK_IS_ACTION_GROUP (action_group));
 
636
  g_return_if_fail (g_list_find (self->private_data->action_groups, 
 
637
                                 action_group) != NULL);
 
638
 
 
639
  self->private_data->action_groups =
 
640
    g_list_remove (self->private_data->action_groups, action_group);
 
641
 
 
642
  g_object_disconnect (action_group,
 
643
                       "any_signal::connect_proxy", G_CALLBACK (cb_proxy_connect_proxy), self,
 
644
                       "any_signal::disconnect_proxy", G_CALLBACK (cb_proxy_disconnect_proxy), self,
 
645
                       "any_signal::pre_activate", G_CALLBACK (cb_proxy_pre_activate), self,
 
646
                       "any_signal::post_activate", G_CALLBACK (cb_proxy_post_activate), self, 
 
647
                       NULL);
 
648
  g_object_unref (action_group);
 
649
 
 
650
  /* dirty all nodes, as action bindings may change */
 
651
  dirty_all_nodes (self);
 
652
 
 
653
  g_signal_emit (self, ui_manager_signals[ACTIONS_CHANGED], 0);
 
654
}
 
655
 
 
656
/**
 
657
 * gtk_ui_manager_get_action_groups:
 
658
 * @self: a #GtkUIManager object
 
659
 * 
 
660
 * Returns the list of action groups associated with @self.
 
661
 *
 
662
 * Return value: a #GList of action groups. The list is owned by GTK+ 
 
663
 *   and should not be modified.
 
664
 *
 
665
 * Since: 2.4
 
666
 **/
 
667
GList *
 
668
gtk_ui_manager_get_action_groups (GtkUIManager *self)
 
669
{
 
670
  g_return_val_if_fail (GTK_IS_UI_MANAGER (self), NULL);
 
671
 
 
672
  return self->private_data->action_groups;
 
673
}
 
674
 
 
675
/**
 
676
 * gtk_ui_manager_get_accel_group:
 
677
 * @self: a #GtkUIManager object
 
678
 * 
 
679
 * Returns the #GtkAccelGroup associated with @self.
 
680
 *
 
681
 * Return value: the #GtkAccelGroup.
 
682
 *
 
683
 * Since: 2.4
 
684
 **/
 
685
GtkAccelGroup *
 
686
gtk_ui_manager_get_accel_group (GtkUIManager *self)
 
687
{
 
688
  g_return_val_if_fail (GTK_IS_UI_MANAGER (self), NULL);
 
689
 
 
690
  return self->private_data->accel_group;
 
691
}
 
692
 
 
693
/**
 
694
 * gtk_ui_manager_get_widget:
 
695
 * @self: a #GtkUIManager
 
696
 * @path: a path
 
697
 * 
 
698
 * Looks up a widget by following a path. 
 
699
 * The path consists of the names specified in the XML description of the UI. 
 
700
 * separated by '/'. Elements which don't have a name or action attribute in 
 
701
 * the XML (e.g. &lt;popup&gt;) can be addressed by their XML element name 
 
702
 * (e.g. "popup"). The root element ("/ui") can be omitted in the path.
 
703
 *
 
704
 * Note that the widget found by following a path that ends in a &lt;menu&gt;
 
705
 * element is the menuitem to which the menu is attached, not the menu itself.
 
706
 *
 
707
 * Also note that the widgets constructed by a ui manager are not tied to 
 
708
 * the lifecycle of the ui manager. If you add the widgets returned by this 
 
709
 * function to some container or explicitly ref them, they will survive the
 
710
 * destruction of the ui manager.
 
711
 * 
 
712
 * Return value: the widget found by following the path, or %NULL if no widget
 
713
 *   was found.
 
714
 *
 
715
 * Since: 2.4
 
716
 **/
 
717
GtkWidget *
 
718
gtk_ui_manager_get_widget (GtkUIManager *self,
 
719
                           const gchar  *path)
 
720
{
 
721
  g_return_val_if_fail (GTK_IS_UI_MANAGER (self), NULL);
 
722
  g_return_val_if_fail (path != NULL, NULL);
 
723
 
 
724
  return GTK_UI_MANAGER_GET_CLASS (self)->get_widget (self, path);
 
725
}
 
726
 
 
727
typedef struct {
 
728
  GtkUIManagerItemType types;
 
729
  GSList *list;
 
730
} ToplevelData;
 
731
 
 
732
static void
 
733
collect_toplevels (GNode   *node, 
 
734
                   gpointer user_data)
 
735
{
 
736
  ToplevelData *data = user_data;
 
737
 
 
738
  if (NODE_INFO (node)->proxy)
 
739
    {
 
740
      switch (NODE_INFO (node)->type) 
 
741
        {
 
742
        case NODE_TYPE_MENUBAR:
 
743
          if (data->types & GTK_UI_MANAGER_MENUBAR)
 
744
        data->list = g_slist_prepend (data->list, NODE_INFO (node)->proxy);
 
745
          break;
 
746
        case NODE_TYPE_TOOLBAR:
 
747
      if (data->types & GTK_UI_MANAGER_TOOLBAR)
 
748
        data->list = g_slist_prepend (data->list, NODE_INFO (node)->proxy);
 
749
      break;
 
750
        case NODE_TYPE_POPUP:
 
751
          if (data->types & GTK_UI_MANAGER_POPUP)
 
752
            data->list = g_slist_prepend (data->list, NODE_INFO (node)->proxy);
 
753
          break;
 
754
        default: ;
 
755
        }
 
756
    }
 
757
}
 
758
 
 
759
/**
 
760
 * gtk_ui_manager_get_toplevels:
 
761
 * @self: a #GtkUIManager
 
762
 * @types: specifies the types of toplevel widgets to include. Allowed
 
763
 *   types are #GTK_UI_MANAGER_MENUBAR, #GTK_UI_MANAGER_TOOLBAR and
 
764
 *   #GTK_UI_MANAGER_POPUP.
 
765
 * 
 
766
 * Obtains a list of all toplevel widgets of the requested types.
 
767
 * 
 
768
 * Return value: a newly-allocated of all toplevel widgets of the requested 
 
769
 * types. 
 
770
 *
 
771
 * Since: 2.4
 
772
 **/
 
773
GSList *
 
774
gtk_ui_manager_get_toplevels (GtkUIManager         *self,
 
775
                              GtkUIManagerItemType  types)
 
776
{
 
777
  ToplevelData data;
 
778
 
 
779
  g_return_val_if_fail (GTK_IS_UI_MANAGER (self), NULL);
 
780
  g_return_val_if_fail ((~(GTK_UI_MANAGER_MENUBAR | 
 
781
                           GTK_UI_MANAGER_TOOLBAR |
 
782
                           GTK_UI_MANAGER_POPUP) & types) == 0, NULL);
 
783
  
 
784
      
 
785
  data.types = types;
 
786
  data.list = NULL;
 
787
 
 
788
  g_node_children_foreach (self->private_data->root_node, 
 
789
                           G_TRAVERSE_ALL, 
 
790
                           collect_toplevels, &data);
 
791
 
 
792
  return data.list;
 
793
}
 
794
 
 
795
 
 
796
/**
 
797
 * gtk_ui_manager_get_action:
 
798
 * @self: a #GtkUIManager
 
799
 * @path: a path
 
800
 * 
 
801
 * Looks up an action by following a path. See gtk_ui_manager_get_widget()
 
802
 * for more information about paths.
 
803
 * 
 
804
 * Return value: the action whose proxy widget is found by following the path, 
 
805
 *     or %NULL if no widget was found.
 
806
 *
 
807
 * Since: 2.4
 
808
 **/
 
809
GtkAction *
 
810
gtk_ui_manager_get_action (GtkUIManager *self,
 
811
                           const gchar  *path)
 
812
{
 
813
  g_return_val_if_fail (GTK_IS_UI_MANAGER (self), NULL);
 
814
  g_return_val_if_fail (path != NULL, NULL);
 
815
 
 
816
  return GTK_UI_MANAGER_GET_CLASS (self)->get_action (self, path);
 
817
}
 
818
 
 
819
static GNode *
 
820
get_child_node (GtkUIManager *self, 
 
821
                GNode        *parent,
 
822
                GNode        *sibling,
 
823
                const gchar  *childname, 
 
824
                gint          childname_length,
 
825
                NodeType      node_type,
 
826
                gboolean      create, 
 
827
                gboolean      top)
 
828
{
 
829
  GNode *child = NULL;
 
830
 
 
831
  if (parent)
 
832
    {
 
833
      if (childname)
 
834
        {
 
835
          for (child = parent->children; child != NULL; child = child->next)
 
836
            {
 
837
              if (NODE_INFO (child)->name &&
 
838
                  strlen (NODE_INFO (child)->name) == childname_length &&
 
839
                  !strncmp (NODE_INFO (child)->name, childname, childname_length))
 
840
                {
 
841
                  /* if undecided about node type, set it */
 
842
                  if (NODE_INFO (child)->type == NODE_TYPE_UNDECIDED)
 
843
                    NODE_INFO (child)->type = node_type;
 
844
                  
 
845
                  /* warn about type mismatch */
 
846
                  if (NODE_INFO (child)->type != NODE_TYPE_UNDECIDED &&
 
847
                      node_type != NODE_TYPE_UNDECIDED &&
 
848
                      NODE_INFO (child)->type != node_type)
 
849
                    g_warning ("node type doesn't match %d (%s is type %d)",
 
850
                               node_type, 
 
851
                               NODE_INFO (child)->name,
 
852
                               NODE_INFO (child)->type);
 
853
                  
 
854
                  return child;
 
855
                }
 
856
            }
 
857
        }
 
858
      if (!child && create)
 
859
        {
 
860
          Node *mnode;
 
861
          
 
862
          mnode = g_slice_new0 (Node);
 
863
          mnode->type = node_type;
 
864
          mnode->name = g_strndup (childname, childname_length);
 
865
 
 
866
          if (sibling)
 
867
            {
 
868
              if (top)
 
869
                child = g_node_insert_before (parent, sibling, 
 
870
                                              g_node_new (mnode));
 
871
              else
 
872
                child = g_node_insert_after (parent, sibling, 
 
873
                                             g_node_new (mnode));
 
874
            }
 
875
          else
 
876
            {
 
877
              if (top)
 
878
                child = g_node_prepend_data (parent, mnode);
 
879
              else
 
880
                child = g_node_append_data (parent, mnode);
 
881
            }
 
882
 
 
883
          mark_node_dirty (child);
 
884
        }
 
885
    }
 
886
  else
 
887
    {
 
888
      /* handle root node */
 
889
      if (self->private_data->root_node)
 
890
        {
 
891
          child = self->private_data->root_node;
 
892
          if (strncmp (NODE_INFO (child)->name, childname, childname_length) != 0)
 
893
            g_warning ("root node name '%s' doesn't match '%s'",
 
894
                       childname, NODE_INFO (child)->name);
 
895
          if (NODE_INFO (child)->type != NODE_TYPE_ROOT)
 
896
            g_warning ("base element must be of type ROOT");
 
897
        }
 
898
      else if (create)
 
899
        {
 
900
          Node *mnode;
 
901
 
 
902
          mnode = g_slice_new0 (Node);
 
903
          mnode->type = node_type;
 
904
          mnode->name = g_strndup (childname, childname_length);
 
905
          mnode->dirty = TRUE;
 
906
          
 
907
          child = self->private_data->root_node = g_node_new (mnode);
 
908
        }
 
909
    }
 
910
 
 
911
  return child;
 
912
}
 
913
 
 
914
static GNode *
 
915
get_node (GtkUIManager *self, 
 
916
          const gchar  *path,
 
917
          NodeType      node_type, 
 
918
          gboolean      create)
 
919
{
 
920
  const gchar *pos, *end;
 
921
  GNode *parent, *node;
 
922
  
 
923
  if (strncmp ("/ui", path, 3) == 0)
 
924
    path += 3;
 
925
  
 
926
  end = path + strlen (path);
 
927
  pos = path;
 
928
  parent = node = NULL;
 
929
  while (pos < end)
 
930
    {
 
931
      const gchar *slash;
 
932
      gsize length;
 
933
 
 
934
      slash = strchr (pos, '/');
 
935
      if (slash)
 
936
        length = slash - pos;
 
937
      else
 
938
        length = strlen (pos);
 
939
 
 
940
      node = get_child_node (self, parent, NULL, pos, length, NODE_TYPE_UNDECIDED,
 
941
                             create, FALSE);
 
942
      if (!node)
 
943
        return NULL;
 
944
 
 
945
      pos += length + 1; /* move past the node name and the slash too */
 
946
      parent = node;
 
947
    }
 
948
 
 
949
  if (node != NULL && NODE_INFO (node)->type == NODE_TYPE_UNDECIDED)
 
950
    NODE_INFO (node)->type = node_type;
 
951
 
 
952
  return node;
 
953
}
 
954
 
 
955
static void
 
956
node_ui_reference_free (gpointer data)
 
957
{
 
958
  g_slice_free (NodeUIReference, data);
 
959
}
 
960
 
 
961
static gboolean
 
962
free_node (GNode *node)
 
963
{
 
964
  Node *info = NODE_INFO (node);
 
965
  
 
966
  g_list_foreach (info->uifiles, (GFunc) node_ui_reference_free, NULL);
 
967
  g_list_free (info->uifiles);
 
968
 
 
969
  if (info->action)
 
970
    g_object_unref (info->action);
 
971
  if (info->proxy)
 
972
    g_object_unref (info->proxy);
 
973
  if (info->extra)
 
974
    g_object_unref (info->extra);
 
975
  g_free (info->name);
 
976
  g_slice_free (Node, info);
 
977
 
 
978
  return FALSE;
 
979
}
 
980
 
 
981
/**
 
982
 * gtk_ui_manager_new_merge_id:
 
983
 * @self: a #GtkUIManager
 
984
 * 
 
985
 * Returns an unused merge id, suitable for use with 
 
986
 * gtk_ui_manager_add_ui().
 
987
 * 
 
988
 * Return value: an unused merge id.
 
989
 *
 
990
 * Since: 2.4
 
991
 **/
 
992
guint
 
993
gtk_ui_manager_new_merge_id (GtkUIManager *self)
 
994
{
 
995
  self->private_data->last_merge_id++;
 
996
 
 
997
  return self->private_data->last_merge_id;
 
998
}
 
999
 
 
1000
static void
 
1001
node_prepend_ui_reference (GNode  *gnode,
 
1002
                           guint   merge_id, 
 
1003
                           GQuark  action_quark)
 
1004
{
 
1005
  Node *node = NODE_INFO (gnode);
 
1006
  NodeUIReference *reference = NULL;
 
1007
 
 
1008
  if (node->uifiles && 
 
1009
      ((NodeUIReference *)node->uifiles->data)->merge_id == merge_id)
 
1010
    reference = node->uifiles->data;
 
1011
  else
 
1012
    {
 
1013
      reference = g_slice_new (NodeUIReference);
 
1014
      node->uifiles = g_list_prepend (node->uifiles, reference);
 
1015
    }
 
1016
 
 
1017
  reference->merge_id = merge_id;
 
1018
  reference->action_quark = action_quark;
 
1019
 
 
1020
  mark_node_dirty (gnode);
 
1021
}
 
1022
 
 
1023
static void
 
1024
node_remove_ui_reference (GNode  *gnode,
 
1025
                          guint  merge_id)
 
1026
{
 
1027
  Node *node = NODE_INFO (gnode);
 
1028
  GList *p;
 
1029
  
 
1030
  for (p = node->uifiles; p != NULL; p = p->next)
 
1031
    {
 
1032
      NodeUIReference *reference = p->data;
 
1033
      
 
1034
      if (reference->merge_id == merge_id)
 
1035
        {
 
1036
          if (p == node->uifiles)
 
1037
            mark_node_dirty (gnode);
 
1038
          node->uifiles = g_list_delete_link (node->uifiles, p);
 
1039
          g_slice_free (NodeUIReference, reference);
 
1040
 
 
1041
          break;
 
1042
        }
 
1043
    }
 
1044
}
 
1045
 
 
1046
/* -------------------- The UI file parser -------------------- */
 
1047
 
 
1048
typedef enum
 
1049
{
 
1050
  STATE_START,
 
1051
  STATE_ROOT,
 
1052
  STATE_MENU,
 
1053
  STATE_TOOLBAR,
 
1054
  STATE_MENUITEM,
 
1055
  STATE_TOOLITEM,
 
1056
  STATE_ACCELERATOR,
 
1057
  STATE_END
 
1058
} ParseState;
 
1059
 
 
1060
typedef struct _ParseContext ParseContext;
 
1061
struct _ParseContext
 
1062
{
 
1063
  ParseState state;
 
1064
  ParseState prev_state;
 
1065
 
 
1066
  GtkUIManager *self;
 
1067
 
 
1068
  GNode *current;
 
1069
 
 
1070
  guint merge_id;
 
1071
};
 
1072
 
 
1073
static void
 
1074
start_element_handler (GMarkupParseContext *context,
 
1075
                       const gchar         *element_name,
 
1076
                       const gchar        **attribute_names,
 
1077
                       const gchar        **attribute_values,
 
1078
                       gpointer             user_data,
 
1079
                       GError             **error)
 
1080
{
 
1081
  ParseContext *ctx = user_data;
 
1082
  GtkUIManager *self = ctx->self;
 
1083
 
 
1084
  gint i;
 
1085
  const gchar *node_name;
 
1086
  const gchar *action;
 
1087
  GQuark action_quark;
 
1088
  gboolean top;
 
1089
  gboolean expand = FALSE;
 
1090
  
 
1091
  gboolean raise_error = TRUE;
 
1092
 
 
1093
  node_name = NULL;
 
1094
  action = NULL;
 
1095
  action_quark = 0;
 
1096
  top = FALSE;
 
1097
 
 
1098
  for (i = 0; attribute_names[i] != NULL; i++)
 
1099
    {
 
1100
      if (!strcmp (attribute_names[i], "name"))
 
1101
        {
 
1102
          node_name = attribute_values[i];
 
1103
        }
 
1104
      else if (!strcmp (attribute_names[i], "action"))
 
1105
        {
 
1106
          action = attribute_values[i];
 
1107
          action_quark = g_quark_from_string (attribute_values[i]);
 
1108
        }
 
1109
      else if (!strcmp (attribute_names[i], "position"))
 
1110
        {
 
1111
          top = !strcmp (attribute_values[i], "top");
 
1112
        }
 
1113
      else if (!strcmp (attribute_names[i], "expand"))
 
1114
        {
 
1115
          expand = !strcmp (attribute_values[i], "true");
 
1116
        }
 
1117
      else
 
1118
        {
 
1119
          gint line_number, char_number;
 
1120
          
 
1121
          g_markup_parse_context_get_position (context,
 
1122
                                               &line_number, &char_number);
 
1123
          g_set_error (error,
 
1124
                       G_MARKUP_ERROR,
 
1125
                       G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE,
 
1126
                       _("Unknown attribute '%s' on line %d char %d"),
 
1127
                       attribute_names[i],
 
1128
                       line_number, char_number);
 
1129
          return;
 
1130
        }
 
1131
    }
 
1132
 
 
1133
  /* Work out a name for this node.  Either the name attribute, or
 
1134
   * the action, or the element name */
 
1135
  if (node_name == NULL) 
 
1136
    {
 
1137
      if (action != NULL)
 
1138
        node_name = action;
 
1139
      else 
 
1140
        node_name = element_name;
 
1141
    }
 
1142
 
 
1143
  switch (element_name[0])
 
1144
    {
 
1145
    case 'a':
 
1146
      if (ctx->state == STATE_ROOT && !strcmp (element_name, "accelerator"))
 
1147
        {
 
1148
          ctx->state = STATE_ACCELERATOR;
 
1149
          ctx->current = get_child_node (self, ctx->current, NULL,
 
1150
                                         node_name, strlen (node_name),
 
1151
                                         NODE_TYPE_ACCELERATOR,
 
1152
                                         TRUE, FALSE);
 
1153
          if (NODE_INFO (ctx->current)->action_name == 0)
 
1154
            NODE_INFO (ctx->current)->action_name = action_quark;
 
1155
 
 
1156
          node_prepend_ui_reference (ctx->current, ctx->merge_id, action_quark);
 
1157
 
 
1158
          raise_error = FALSE;
 
1159
        }
 
1160
      break;
 
1161
    case 'u':
 
1162
      if (ctx->state == STATE_START && !strcmp (element_name, "ui"))
 
1163
        {
 
1164
          ctx->state = STATE_ROOT;
 
1165
          ctx->current = self->private_data->root_node;
 
1166
          raise_error = FALSE;
 
1167
 
 
1168
          node_prepend_ui_reference (ctx->current, ctx->merge_id, action_quark);
 
1169
        }
 
1170
      break;
 
1171
    case 'm':
 
1172
      if (ctx->state == STATE_ROOT && !strcmp (element_name, "menubar"))
 
1173
        {
 
1174
          ctx->state = STATE_MENU;
 
1175
          ctx->current = get_child_node (self, ctx->current, NULL,
 
1176
                                         node_name, strlen (node_name),
 
1177
                                         NODE_TYPE_MENUBAR,
 
1178
                                         TRUE, FALSE);
 
1179
          if (NODE_INFO (ctx->current)->action_name == 0)
 
1180
            NODE_INFO (ctx->current)->action_name = action_quark;
 
1181
 
 
1182
          node_prepend_ui_reference (ctx->current, ctx->merge_id, action_quark);
 
1183
          mark_node_dirty (ctx->current);
 
1184
 
 
1185
          raise_error = FALSE;
 
1186
        }
 
1187
      else if (ctx->state == STATE_MENU && !strcmp (element_name, "menu"))
 
1188
        {
 
1189
          ctx->current = get_child_node (self, ctx->current, NULL,
 
1190
                                         node_name, strlen (node_name),
 
1191
                                         NODE_TYPE_MENU,
 
1192
                                         TRUE, top);
 
1193
          if (NODE_INFO (ctx->current)->action_name == 0)
 
1194
            NODE_INFO (ctx->current)->action_name = action_quark;
 
1195
 
 
1196
          node_prepend_ui_reference (ctx->current, ctx->merge_id, action_quark);
 
1197
          
 
1198
          raise_error = FALSE;
 
1199
        }
 
1200
      else if (ctx->state == STATE_TOOLITEM && !strcmp (element_name, "menu"))
 
1201
        {
 
1202
          ctx->state = STATE_MENU;
 
1203
          
 
1204
          ctx->current = get_child_node (self, g_node_last_child (ctx->current), NULL,
 
1205
                                         node_name, strlen (node_name),
 
1206
                                         NODE_TYPE_MENU,
 
1207
                                         TRUE, top);
 
1208
          if (NODE_INFO (ctx->current)->action_name == 0)
 
1209
            NODE_INFO (ctx->current)->action_name = action_quark;
 
1210
 
 
1211
          node_prepend_ui_reference (ctx->current, ctx->merge_id, action_quark);
 
1212
          
 
1213
          raise_error = FALSE;
 
1214
        }
 
1215
      else if (ctx->state == STATE_MENU && !strcmp (element_name, "menuitem"))
 
1216
        {
 
1217
          GNode *node;
 
1218
 
 
1219
          ctx->state = STATE_MENUITEM;
 
1220
          node = get_child_node (self, ctx->current, NULL,
 
1221
                                 node_name, strlen (node_name),
 
1222
                                 NODE_TYPE_MENUITEM,
 
1223
                                 TRUE, top);
 
1224
          if (NODE_INFO (node)->action_name == 0)
 
1225
            NODE_INFO (node)->action_name = action_quark;
 
1226
          
 
1227
          node_prepend_ui_reference (node, ctx->merge_id, action_quark);
 
1228
          
 
1229
          raise_error = FALSE;
 
1230
        }
 
1231
      break;
 
1232
    case 'p':
 
1233
      if (ctx->state == STATE_ROOT && !strcmp (element_name, "popup"))
 
1234
        {
 
1235
          ctx->state = STATE_MENU;
 
1236
          ctx->current = get_child_node (self, ctx->current, NULL,
 
1237
                                         node_name, strlen (node_name),
 
1238
                                         NODE_TYPE_POPUP,
 
1239
                                         TRUE, FALSE);
 
1240
          if (NODE_INFO (ctx->current)->action_name == 0)
 
1241
            NODE_INFO (ctx->current)->action_name = action_quark;
 
1242
          
 
1243
          node_prepend_ui_reference (ctx->current, ctx->merge_id, action_quark);
 
1244
          
 
1245
          raise_error = FALSE;
 
1246
        }
 
1247
      else if ((ctx->state == STATE_MENU || ctx->state == STATE_TOOLBAR) &&
 
1248
               !strcmp (element_name, "placeholder"))
 
1249
        {
 
1250
          if (ctx->state == STATE_TOOLBAR)
 
1251
            ctx->current = get_child_node (self, ctx->current, NULL,
 
1252
                                           node_name, strlen (node_name),
 
1253
                                           NODE_TYPE_TOOLBAR_PLACEHOLDER,
 
1254
                                           TRUE, top);
 
1255
          else
 
1256
            ctx->current = get_child_node (self, ctx->current, NULL,
 
1257
                                           node_name, strlen (node_name),
 
1258
                                           NODE_TYPE_MENU_PLACEHOLDER,
 
1259
                                           TRUE, top);
 
1260
          
 
1261
          node_prepend_ui_reference (ctx->current, ctx->merge_id, action_quark);
 
1262
          
 
1263
          raise_error = FALSE;
 
1264
        }
 
1265
      break;
 
1266
    case 's':
 
1267
      if ((ctx->state == STATE_MENU || ctx->state == STATE_TOOLBAR) &&
 
1268
          !strcmp (element_name, "separator"))
 
1269
        {
 
1270
          GNode *node;
 
1271
          gint length;
 
1272
 
 
1273
          if (ctx->state == STATE_TOOLBAR)
 
1274
            ctx->state = STATE_TOOLITEM;
 
1275
          else
 
1276
            ctx->state = STATE_MENUITEM;
 
1277
          if (!strcmp (node_name, "separator"))
 
1278
            {
 
1279
              node_name = NULL;
 
1280
              length = 0;
 
1281
            }
 
1282
          else
 
1283
            length = strlen (node_name);
 
1284
          node = get_child_node (self, ctx->current, NULL,
 
1285
                                 node_name, length,
 
1286
                                 NODE_TYPE_SEPARATOR,
 
1287
                                 TRUE, top);
 
1288
 
 
1289
          NODE_INFO (node)->expand = expand;
 
1290
 
 
1291
          if (NODE_INFO (node)->action_name == 0)
 
1292
            NODE_INFO (node)->action_name = action_quark;
 
1293
 
 
1294
          node_prepend_ui_reference (node, ctx->merge_id, action_quark);
 
1295
          
 
1296
          raise_error = FALSE;
 
1297
        }
 
1298
      break;
 
1299
    case 't':
 
1300
      if (ctx->state == STATE_ROOT && !strcmp (element_name, "toolbar"))
 
1301
        {
 
1302
          ctx->state = STATE_TOOLBAR;
 
1303
          ctx->current = get_child_node (self, ctx->current, NULL,
 
1304
                                         node_name, strlen (node_name),
 
1305
                                         NODE_TYPE_TOOLBAR,
 
1306
                                         TRUE, FALSE);
 
1307
          if (NODE_INFO (ctx->current)->action_name == 0)
 
1308
            NODE_INFO (ctx->current)->action_name = action_quark;
 
1309
          
 
1310
          node_prepend_ui_reference (ctx->current, ctx->merge_id, action_quark);
 
1311
          
 
1312
          raise_error = FALSE;
 
1313
        }
 
1314
      else if (ctx->state == STATE_TOOLBAR && !strcmp (element_name, "toolitem"))
 
1315
        {
 
1316
          GNode *node;
 
1317
 
 
1318
          ctx->state = STATE_TOOLITEM;
 
1319
          node = get_child_node (self, ctx->current, NULL,
 
1320
                                node_name, strlen (node_name),
 
1321
                                 NODE_TYPE_TOOLITEM,
 
1322
                                 TRUE, top);
 
1323
          if (NODE_INFO (node)->action_name == 0)
 
1324
            NODE_INFO (node)->action_name = action_quark;
 
1325
          
 
1326
          node_prepend_ui_reference (node, ctx->merge_id, action_quark);
 
1327
 
 
1328
          raise_error = FALSE;
 
1329
        }
 
1330
      break;
 
1331
    default:
 
1332
      break;
 
1333
    }
 
1334
  if (raise_error)
 
1335
    {
 
1336
      gint line_number, char_number;
 
1337
 
 
1338
      g_markup_parse_context_get_position (context,
 
1339
                                           &line_number, &char_number);
 
1340
      g_set_error (error,
 
1341
                   G_MARKUP_ERROR,
 
1342
                   G_MARKUP_ERROR_UNKNOWN_ELEMENT,
 
1343
                   _("Unexpected start tag '%s' on line %d char %d"),
 
1344
                   element_name,
 
1345
                   line_number, char_number);
 
1346
    }
 
1347
}
 
1348
 
 
1349
static void
 
1350
end_element_handler (GMarkupParseContext *context,
 
1351
                     const gchar         *element_name,
 
1352
                     gpointer             user_data,
 
1353
                     GError             **error)
 
1354
{
 
1355
  ParseContext *ctx = user_data;
 
1356
 
 
1357
  switch (ctx->state)
 
1358
    {
 
1359
    case STATE_START:
 
1360
    case STATE_END:
 
1361
      /* no need to GError here, GMarkup already catches this */
 
1362
      break;
 
1363
    case STATE_ROOT:
 
1364
      ctx->current = NULL;
 
1365
      ctx->state = STATE_END;
 
1366
      break;
 
1367
    case STATE_MENU:
 
1368
    case STATE_TOOLBAR:
 
1369
    case STATE_ACCELERATOR:
 
1370
      ctx->current = ctx->current->parent;
 
1371
      if (NODE_INFO (ctx->current)->type == NODE_TYPE_ROOT) 
 
1372
        ctx->state = STATE_ROOT;
 
1373
      else if (NODE_INFO (ctx->current)->type == NODE_TYPE_TOOLITEM)
 
1374
        {
 
1375
          ctx->current = ctx->current->parent;
 
1376
          ctx->state = STATE_TOOLITEM;
 
1377
        }
 
1378
      /* else, stay in same state */
 
1379
      break;
 
1380
    case STATE_MENUITEM:
 
1381
      ctx->state = STATE_MENU;
 
1382
      break;
 
1383
    case STATE_TOOLITEM:
 
1384
      ctx->state = STATE_TOOLBAR;
 
1385
      break;
 
1386
    }
 
1387
}
 
1388
 
 
1389
static void
 
1390
cleanup (GMarkupParseContext *context,
 
1391
         GError              *error,
 
1392
         gpointer             user_data)
 
1393
{
 
1394
  ParseContext *ctx = user_data;
 
1395
 
 
1396
  ctx->current = NULL;
 
1397
  /* should also walk through the tree and get rid of nodes related to
 
1398
   * this UI file's tag */
 
1399
 
 
1400
  gtk_ui_manager_remove_ui (ctx->self, ctx->merge_id);
 
1401
}
 
1402
 
 
1403
static gboolean
 
1404
xml_isspace (char c)
 
1405
{
 
1406
  return c == ' ' || c == '\t' || c == '\n' || c == '\r';
 
1407
}
 
1408
 
 
1409
static void 
 
1410
text_handler (GMarkupParseContext *context,
 
1411
              const gchar         *text,
 
1412
              gsize                text_len,  
 
1413
              gpointer             user_data,
 
1414
              GError             **error)
 
1415
{
 
1416
  const gchar *p;
 
1417
  const gchar *end;
 
1418
 
 
1419
  p = text;
 
1420
  end = text + text_len;
 
1421
  while (p != end && xml_isspace (*p))
 
1422
    ++p;
 
1423
  
 
1424
  if (p != end)
 
1425
    {
 
1426
      gint line_number, char_number;
 
1427
      
 
1428
      g_markup_parse_context_get_position (context,
 
1429
                                           &line_number, &char_number);
 
1430
      g_set_error (error,
 
1431
                   G_MARKUP_ERROR,
 
1432
                   G_MARKUP_ERROR_INVALID_CONTENT,
 
1433
                   _("Unexpected character data on line %d char %d"),
 
1434
                   line_number, char_number);
 
1435
    }
 
1436
}
 
1437
 
 
1438
 
 
1439
static const GMarkupParser ui_parser = {
 
1440
  start_element_handler,
 
1441
  end_element_handler,
 
1442
  text_handler,
 
1443
  NULL,
 
1444
  cleanup
 
1445
};
 
1446
 
 
1447
static guint
 
1448
add_ui_from_string (GtkUIManager *self,
 
1449
                    const gchar  *buffer, 
 
1450
                    gssize        length,
 
1451
                    gboolean      needs_root,
 
1452
                    GError      **error)
 
1453
{
 
1454
  ParseContext ctx = { 0 };
 
1455
  GMarkupParseContext *context;
 
1456
 
 
1457
  ctx.state = STATE_START;
 
1458
  ctx.self = self;
 
1459
  ctx.current = NULL;
 
1460
  ctx.merge_id = gtk_ui_manager_new_merge_id (self);
 
1461
 
 
1462
  context = g_markup_parse_context_new (&ui_parser, 0, &ctx, NULL);
 
1463
 
 
1464
  if (needs_root)
 
1465
    if (!g_markup_parse_context_parse (context, "<ui>", -1, error))
 
1466
      goto out;
 
1467
 
 
1468
  if (!g_markup_parse_context_parse (context, buffer, length, error))
 
1469
    goto out;
 
1470
 
 
1471
  if (needs_root)
 
1472
    if (!g_markup_parse_context_parse (context, "</ui>", -1, error))
 
1473
      goto out;
 
1474
 
 
1475
  if (!g_markup_parse_context_end_parse (context, error))
 
1476
    goto out;
 
1477
 
 
1478
  g_markup_parse_context_free (context);
 
1479
 
 
1480
  queue_update (self);
 
1481
 
 
1482
  g_object_notify (G_OBJECT (self), "ui");      
 
1483
 
 
1484
  return ctx.merge_id;
 
1485
 
 
1486
 out:
 
1487
 
 
1488
  g_markup_parse_context_free (context);
 
1489
 
 
1490
  return 0;
 
1491
}
 
1492
 
 
1493
/**
 
1494
 * gtk_ui_manager_add_ui_from_string:
 
1495
 * @self: a #GtkUIManager object
 
1496
 * @buffer: the string to parse
 
1497
 * @length: the length of @buffer (may be -1 if @buffer is nul-terminated)
 
1498
 * @error: return location for an error
 
1499
 * 
 
1500
 * Parses a string containing a <link linkend="XML-UI">UI definition</link> and 
 
1501
 * merges it with the current contents of @self. An enclosing &lt;ui&gt; 
 
1502
 * element is added if it is missing.
 
1503
 * 
 
1504
 * Return value: The merge id for the merged UI. The merge id can be used
 
1505
 *   to unmerge the UI with gtk_ui_manager_remove_ui(). If an error occurred,
 
1506
 *   the return value is 0.
 
1507
 *
 
1508
 * Since: 2.4
 
1509
 **/
 
1510
guint
 
1511
gtk_ui_manager_add_ui_from_string (GtkUIManager *self,
 
1512
                                   const gchar  *buffer, 
 
1513
                                   gssize        length,
 
1514
                                   GError      **error)
 
1515
{
 
1516
  gboolean needs_root = TRUE;
 
1517
  const gchar *p;
 
1518
  const gchar *end;
 
1519
 
 
1520
  g_return_val_if_fail (GTK_IS_UI_MANAGER (self), 0);
 
1521
  g_return_val_if_fail (buffer != NULL, 0);
 
1522
 
 
1523
  if (length < 0)
 
1524
    length = strlen (buffer);
 
1525
 
 
1526
  p = buffer;
 
1527
  end = buffer + length;
 
1528
  while (p != end && xml_isspace (*p))
 
1529
    ++p;
 
1530
 
 
1531
  if (end - p >= 4 && strncmp (p, "<ui>", 4) == 0)
 
1532
    needs_root = FALSE;
 
1533
  
 
1534
  return add_ui_from_string (self, buffer, length, needs_root, error);
 
1535
}
 
1536
 
 
1537
/**
 
1538
 * gtk_ui_manager_add_ui_from_file:
 
1539
 * @self: a #GtkUIManager object
 
1540
 * @filename: the name of the file to parse 
 
1541
 * @error: return location for an error
 
1542
 * 
 
1543
 * Parses a file containing a <link linkend="XML-UI">UI definition</link> and 
 
1544
 * merges it with the current contents of @self. 
 
1545
 * 
 
1546
 * Return value: The merge id for the merged UI. The merge id can be used
 
1547
 *   to unmerge the UI with gtk_ui_manager_remove_ui(). If an error occurred,
 
1548
 *   the return value is 0.
 
1549
 *
 
1550
 * Since: 2.4
 
1551
 **/
 
1552
guint
 
1553
gtk_ui_manager_add_ui_from_file (GtkUIManager *self,
 
1554
                                 const gchar  *filename,
 
1555
                                 GError      **error)
 
1556
{
 
1557
  gchar *buffer;
 
1558
  gsize length;
 
1559
  guint res;
 
1560
 
 
1561
  g_return_val_if_fail (GTK_IS_UI_MANAGER (self), 0);
 
1562
 
 
1563
  if (!g_file_get_contents (filename, &buffer, &length, error))
 
1564
    return 0;
 
1565
 
 
1566
  res = add_ui_from_string (self, buffer, length, FALSE, error);
 
1567
  g_free (buffer);
 
1568
 
 
1569
  return res;
 
1570
}
 
1571
 
 
1572
/**
 
1573
 * gtk_ui_manager_add_ui:
 
1574
 * @self: a #GtkUIManager
 
1575
 * @merge_id: the merge id for the merged UI, see gtk_ui_manager_new_merge_id()
 
1576
 * @path: a path
 
1577
 * @name: the name for the added UI element
 
1578
 * @action: the name of the action to be proxied, or %NULL to add a separator
 
1579
 * @type: the type of UI element to add.
 
1580
 * @top: if %TRUE, the UI element is added before its siblings, otherwise it 
 
1581
 *   is added after its siblings.
 
1582
 * 
 
1583
 * Adds a UI element to the current contents of @self. 
 
1584
 *
 
1585
 * If @type is %GTK_UI_MANAGER_AUTO, GTK+ inserts a menuitem, toolitem or 
 
1586
 * separator if such an element can be inserted at the place determined by 
 
1587
 * @path. Otherwise @type must indicate an element that can be inserted at 
 
1588
 * the place determined by @path.
 
1589
 *
 
1590
 * If @path points to a menuitem or toolitem, the new element will be inserted
 
1591
 * before or after this item, depending on @top.
 
1592
 * 
 
1593
 * Since: 2.4
 
1594
 **/
 
1595
void
 
1596
gtk_ui_manager_add_ui (GtkUIManager        *self,
 
1597
                       guint                merge_id,
 
1598
                       const gchar         *path,
 
1599
                       const gchar         *name,
 
1600
                       const gchar         *action,
 
1601
                       GtkUIManagerItemType type,
 
1602
                       gboolean             top)
 
1603
{
 
1604
  GNode *node;
 
1605
  GNode *sibling;
 
1606
  GNode *child;
 
1607
  NodeType node_type;
 
1608
  GQuark action_quark = 0;
 
1609
 
 
1610
  g_return_if_fail (GTK_IS_UI_MANAGER (self));  
 
1611
  g_return_if_fail (merge_id > 0);
 
1612
  g_return_if_fail (name != NULL || type == GTK_UI_MANAGER_SEPARATOR);
 
1613
 
 
1614
  node = get_node (self, path, NODE_TYPE_UNDECIDED, FALSE);
 
1615
  sibling = NULL;
 
1616
 
 
1617
  if (node == NULL)
 
1618
    return;
 
1619
 
 
1620
  node_type = NODE_TYPE_UNDECIDED;
 
1621
 
 
1622
 reswitch:
 
1623
  switch (NODE_INFO (node)->type) 
 
1624
    {
 
1625
    case NODE_TYPE_SEPARATOR:
 
1626
    case NODE_TYPE_MENUITEM:
 
1627
    case NODE_TYPE_TOOLITEM:
 
1628
      sibling = node;
 
1629
      node = node->parent;
 
1630
      goto reswitch;
 
1631
    case NODE_TYPE_MENUBAR:
 
1632
    case NODE_TYPE_MENU:
 
1633
    case NODE_TYPE_POPUP:
 
1634
    case NODE_TYPE_MENU_PLACEHOLDER:
 
1635
      switch (type) 
 
1636
        {
 
1637
        case GTK_UI_MANAGER_AUTO:
 
1638
          if (action != NULL)
 
1639
              node_type = NODE_TYPE_MENUITEM;
 
1640
          else
 
1641
              node_type = NODE_TYPE_SEPARATOR;
 
1642
          break;
 
1643
        case GTK_UI_MANAGER_MENU:
 
1644
          node_type = NODE_TYPE_MENU;
 
1645
          break;
 
1646
        case GTK_UI_MANAGER_MENUITEM:
 
1647
          node_type = NODE_TYPE_MENUITEM;
 
1648
          break;
 
1649
        case GTK_UI_MANAGER_SEPARATOR:
 
1650
          node_type = NODE_TYPE_SEPARATOR;
 
1651
          break;
 
1652
        case GTK_UI_MANAGER_PLACEHOLDER:
 
1653
          node_type = NODE_TYPE_MENU_PLACEHOLDER;
 
1654
          break;
 
1655
        default: ;
 
1656
          /* do nothing */
 
1657
        }
 
1658
      break;
 
1659
    case NODE_TYPE_TOOLBAR:
 
1660
    case NODE_TYPE_TOOLBAR_PLACEHOLDER:
 
1661
      switch (type) 
 
1662
        {
 
1663
        case GTK_UI_MANAGER_AUTO:
 
1664
          if (action != NULL)
 
1665
              node_type = NODE_TYPE_TOOLITEM;
 
1666
          else
 
1667
              node_type = NODE_TYPE_SEPARATOR;
 
1668
          break;
 
1669
        case GTK_UI_MANAGER_TOOLITEM:
 
1670
          node_type = NODE_TYPE_TOOLITEM;
 
1671
          break;
 
1672
        case GTK_UI_MANAGER_SEPARATOR:
 
1673
          node_type = NODE_TYPE_SEPARATOR;
 
1674
          break;
 
1675
        case GTK_UI_MANAGER_PLACEHOLDER:
 
1676
          node_type = NODE_TYPE_TOOLBAR_PLACEHOLDER;
 
1677
          break;
 
1678
        default: ;
 
1679
          /* do nothing */
 
1680
        }
 
1681
      break;
 
1682
    case NODE_TYPE_ROOT:
 
1683
      switch (type) 
 
1684
        {
 
1685
        case GTK_UI_MANAGER_MENUBAR:
 
1686
          node_type = NODE_TYPE_MENUBAR;
 
1687
          break;
 
1688
        case GTK_UI_MANAGER_TOOLBAR:
 
1689
          node_type = NODE_TYPE_TOOLBAR;
 
1690
          break;
 
1691
        case GTK_UI_MANAGER_POPUP:
 
1692
          node_type = NODE_TYPE_POPUP;
 
1693
          break;
 
1694
        case GTK_UI_MANAGER_ACCELERATOR:
 
1695
          node_type = NODE_TYPE_ACCELERATOR;
 
1696
          break;
 
1697
        default: ;
 
1698
          /* do nothing */
 
1699
        }
 
1700
      break;
 
1701
    default: ;
 
1702
      /* do nothing */
 
1703
    }
 
1704
 
 
1705
  if (node_type == NODE_TYPE_UNDECIDED)
 
1706
    {
 
1707
      g_warning ("item type %d not suitable for adding at '%s'", 
 
1708
                 type, path);
 
1709
      return;
 
1710
    }
 
1711
   
 
1712
  child = get_child_node (self, node, sibling,
 
1713
                          name, name ? strlen (name) : 0,
 
1714
                          node_type, TRUE, top);
 
1715
 
 
1716
  if (action != NULL)
 
1717
    action_quark = g_quark_from_string (action);
 
1718
 
 
1719
  node_prepend_ui_reference (child, merge_id, action_quark);
 
1720
 
 
1721
  if (NODE_INFO (child)->action_name == 0)
 
1722
    NODE_INFO (child)->action_name = action_quark;
 
1723
 
 
1724
  queue_update (self);
 
1725
 
 
1726
  g_object_notify (G_OBJECT (self), "ui");      
 
1727
}
 
1728
 
 
1729
static gboolean
 
1730
remove_ui (GNode   *node, 
 
1731
           gpointer user_data)
 
1732
{
 
1733
  guint merge_id = GPOINTER_TO_UINT (user_data);
 
1734
 
 
1735
  node_remove_ui_reference (node, merge_id);
 
1736
 
 
1737
  return FALSE; /* continue */
 
1738
}
 
1739
 
 
1740
/**
 
1741
 * gtk_ui_manager_remove_ui:
 
1742
 * @self: a #GtkUIManager object
 
1743
 * @merge_id: a merge id as returned by gtk_ui_manager_add_ui_from_string()
 
1744
 * 
 
1745
 * Unmerges the part of @self<!-- -->s content identified by @merge_id.
 
1746
 *
 
1747
 * Since: 2.4
 
1748
 **/
 
1749
void
 
1750
gtk_ui_manager_remove_ui (GtkUIManager *self, 
 
1751
                          guint         merge_id)
 
1752
{
 
1753
  g_node_traverse (self->private_data->root_node, 
 
1754
                   G_POST_ORDER, G_TRAVERSE_ALL, -1,
 
1755
                   remove_ui, GUINT_TO_POINTER (merge_id));
 
1756
 
 
1757
  queue_update (self);
 
1758
 
 
1759
  g_object_notify (G_OBJECT (self), "ui");      
 
1760
}
 
1761
 
 
1762
/* -------------------- Updates -------------------- */
 
1763
 
 
1764
 
 
1765
static GtkAction *
 
1766
get_action_by_name (GtkUIManager *merge, 
 
1767
                    const gchar  *action_name)
 
1768
{
 
1769
  GList *tmp;
 
1770
 
 
1771
  if (!action_name)
 
1772
    return NULL;
 
1773
  
 
1774
  /* lookup name */
 
1775
  for (tmp = merge->private_data->action_groups; tmp != NULL; tmp = tmp->next)
 
1776
    {
 
1777
      GtkActionGroup *action_group = tmp->data;
 
1778
      GtkAction *action;
 
1779
      
 
1780
      action = gtk_action_group_get_action (action_group, action_name);
 
1781
 
 
1782
      if (action)
 
1783
        return action;
 
1784
    }
 
1785
 
 
1786
  return NULL;
 
1787
}
 
1788
 
 
1789
static gboolean
 
1790
find_menu_position (GNode      *node, 
 
1791
                    GtkWidget **menushell_p, 
 
1792
                    gint       *pos_p)
 
1793
{
 
1794
  GtkWidget *menushell;
 
1795
  gint pos = 0;
 
1796
 
 
1797
  g_return_val_if_fail (node != NULL, FALSE);
 
1798
  g_return_val_if_fail (NODE_INFO (node)->type == NODE_TYPE_MENU ||
 
1799
                        NODE_INFO (node)->type == NODE_TYPE_POPUP ||
 
1800
                        NODE_INFO (node)->type == NODE_TYPE_MENU_PLACEHOLDER ||
 
1801
                        NODE_INFO (node)->type == NODE_TYPE_MENUITEM ||
 
1802
                        NODE_INFO (node)->type == NODE_TYPE_SEPARATOR,
 
1803
                        FALSE);
 
1804
 
 
1805
  /* first sibling -- look at parent */
 
1806
  if (node->prev == NULL)
 
1807
    {
 
1808
      GNode *parent;
 
1809
      GList *siblings;
 
1810
 
 
1811
      parent = node->parent;
 
1812
      switch (NODE_INFO (parent)->type)
 
1813
        {
 
1814
        case NODE_TYPE_MENUBAR:
 
1815
        case NODE_TYPE_POPUP:
 
1816
          menushell = NODE_INFO (parent)->proxy;
 
1817
          pos = 0;
 
1818
          break;
 
1819
        case NODE_TYPE_MENU:
 
1820
          menushell = NODE_INFO (parent)->proxy;
 
1821
          if (GTK_IS_MENU_ITEM (menushell))
 
1822
            menushell = gtk_menu_item_get_submenu (GTK_MENU_ITEM (menushell));
 
1823
          siblings = gtk_container_get_children (GTK_CONTAINER (menushell));
 
1824
          if (siblings != NULL && GTK_IS_TEAROFF_MENU_ITEM (siblings->data))
 
1825
            pos = 1;
 
1826
          else
 
1827
            pos = 0;
 
1828
          g_list_free (siblings);
 
1829
          break;
 
1830
        case NODE_TYPE_MENU_PLACEHOLDER:
 
1831
          menushell = gtk_widget_get_parent (NODE_INFO (parent)->proxy);
 
1832
          g_return_val_if_fail (GTK_IS_MENU_SHELL (menushell), FALSE);
 
1833
          pos = g_list_index (GTK_MENU_SHELL (menushell)->children,
 
1834
                              NODE_INFO (parent)->proxy) + 1;
 
1835
          break;
 
1836
        default:
 
1837
          g_warning ("%s: bad parent node type %d", G_STRLOC,
 
1838
                     NODE_INFO (parent)->type);
 
1839
          return FALSE;
 
1840
        }
 
1841
    }
 
1842
  else
 
1843
    {
 
1844
      GtkWidget *prev_child;
 
1845
      GNode *sibling;
 
1846
 
 
1847
      sibling = node->prev;
 
1848
      if (NODE_INFO (sibling)->type == NODE_TYPE_MENU_PLACEHOLDER)
 
1849
        prev_child = NODE_INFO (sibling)->extra; /* second Separator */
 
1850
      else
 
1851
        prev_child = NODE_INFO (sibling)->proxy;
 
1852
 
 
1853
      if (!GTK_IS_WIDGET (prev_child))
 
1854
        return FALSE;
 
1855
 
 
1856
      menushell = gtk_widget_get_parent (prev_child);
 
1857
      if (!GTK_IS_MENU_SHELL (menushell))
 
1858
        return FALSE;
 
1859
 
 
1860
      pos = g_list_index (GTK_MENU_SHELL (menushell)->children, prev_child) + 1;
 
1861
    }
 
1862
 
 
1863
  if (menushell_p)
 
1864
    *menushell_p = menushell;
 
1865
  if (pos_p)
 
1866
    *pos_p = pos;
 
1867
 
 
1868
  return TRUE;
 
1869
}
 
1870
 
 
1871
static gboolean
 
1872
find_toolbar_position (GNode      *node, 
 
1873
                       GtkWidget **toolbar_p, 
 
1874
                       gint       *pos_p)
 
1875
{
 
1876
  GtkWidget *toolbar;
 
1877
  gint pos;
 
1878
 
 
1879
  g_return_val_if_fail (node != NULL, FALSE);
 
1880
  g_return_val_if_fail (NODE_INFO (node)->type == NODE_TYPE_TOOLBAR ||
 
1881
                        NODE_INFO (node)->type == NODE_TYPE_TOOLBAR_PLACEHOLDER ||
 
1882
                        NODE_INFO (node)->type == NODE_TYPE_TOOLITEM ||
 
1883
                        NODE_INFO (node)->type == NODE_TYPE_SEPARATOR,
 
1884
                        FALSE);
 
1885
  
 
1886
  /* first sibling -- look at parent */
 
1887
  if (node->prev == NULL)
 
1888
    {
 
1889
      GNode *parent;
 
1890
 
 
1891
      parent = node->parent;
 
1892
      switch (NODE_INFO (parent)->type)
 
1893
        {
 
1894
        case NODE_TYPE_TOOLBAR:
 
1895
          toolbar = NODE_INFO (parent)->proxy;
 
1896
          pos = 0;
 
1897
          break;
 
1898
        case NODE_TYPE_TOOLBAR_PLACEHOLDER:
 
1899
          toolbar = gtk_widget_get_parent (NODE_INFO (parent)->proxy);
 
1900
          g_return_val_if_fail (GTK_IS_TOOLBAR (toolbar), FALSE);
 
1901
          pos = gtk_toolbar_get_item_index (GTK_TOOLBAR (toolbar),
 
1902
                                            GTK_TOOL_ITEM (NODE_INFO (parent)->proxy)) + 1;
 
1903
          break;
 
1904
        default:
 
1905
          g_warning ("%s: bad parent node type %d", G_STRLOC,
 
1906
                     NODE_INFO (parent)->type);
 
1907
          return FALSE;
 
1908
        }
 
1909
    }
 
1910
  else
 
1911
    {
 
1912
      GtkWidget *prev_child;
 
1913
      GNode *sibling;
 
1914
 
 
1915
      sibling = node->prev;
 
1916
      if (NODE_INFO (sibling)->type == NODE_TYPE_TOOLBAR_PLACEHOLDER)
 
1917
        prev_child = NODE_INFO (sibling)->extra; /* second Separator */
 
1918
      else
 
1919
        prev_child = NODE_INFO (sibling)->proxy;
 
1920
 
 
1921
      if (!GTK_IS_WIDGET (prev_child))
 
1922
        return FALSE;
 
1923
 
 
1924
      toolbar = gtk_widget_get_parent (prev_child);
 
1925
      if (!GTK_IS_TOOLBAR (toolbar))
 
1926
        return FALSE;
 
1927
 
 
1928
      pos = gtk_toolbar_get_item_index (GTK_TOOLBAR (toolbar),
 
1929
                                        GTK_TOOL_ITEM (prev_child)) + 1;
 
1930
    }
 
1931
  
 
1932
  if (toolbar_p)
 
1933
    *toolbar_p = toolbar;
 
1934
  if (pos_p)
 
1935
    *pos_p = pos;
 
1936
 
 
1937
  return TRUE;
 
1938
}
 
1939
 
 
1940
/**
 
1941
 * _gtk_menu_is_empty:
 
1942
 * @menu: a #GtkMenu or %NULL
 
1943
 * 
 
1944
 * Determines whether @menu is empty. A menu is considered empty if it
 
1945
 * the only visible children are tearoff menu items or "filler" menu 
 
1946
 * items which were inserted to mark the menu as empty.
 
1947
 * 
 
1948
 * This function is used by #GtkAction.
 
1949
 *
 
1950
 * Return value: whether @menu is empty.
 
1951
 **/
 
1952
gboolean
 
1953
_gtk_menu_is_empty (GtkWidget *menu)
 
1954
{
 
1955
  GList *children, *cur;
 
1956
  gboolean result = TRUE;
 
1957
 
 
1958
  g_return_val_if_fail (menu == NULL || GTK_IS_MENU (menu), TRUE);
 
1959
 
 
1960
  if (!menu)
 
1961
    return FALSE;
 
1962
 
 
1963
  children = gtk_container_get_children (GTK_CONTAINER (menu));
 
1964
 
 
1965
  cur = children;
 
1966
  while (cur) 
 
1967
    {
 
1968
      if (GTK_WIDGET_VISIBLE (cur->data))
 
1969
        {
 
1970
          if (!GTK_IS_TEAROFF_MENU_ITEM (cur->data) &&
 
1971
              !g_object_get_data (cur->data, "gtk-empty-menu-item"))
 
1972
            {
 
1973
              result = FALSE;
 
1974
              break;
 
1975
            }
 
1976
        }
 
1977
      cur = cur->next;
 
1978
    }
 
1979
  g_list_free (children);
 
1980
 
 
1981
  return result;
 
1982
}
 
1983
 
 
1984
enum {
 
1985
  SEPARATOR_MODE_SMART,
 
1986
  SEPARATOR_MODE_VISIBLE,
 
1987
  SEPARATOR_MODE_HIDDEN
 
1988
};
 
1989
 
 
1990
static void
 
1991
update_smart_separators (GtkWidget *proxy)
 
1992
{
 
1993
  GtkWidget *parent = NULL;
 
1994
  
 
1995
  if (GTK_IS_MENU (proxy) || GTK_IS_TOOLBAR (proxy))
 
1996
    parent = proxy;
 
1997
  else if (GTK_IS_MENU_ITEM (proxy) || GTK_IS_TOOL_ITEM (proxy))
 
1998
    parent = gtk_widget_get_parent (proxy);
 
1999
 
 
2000
  if (parent) 
 
2001
    {
 
2002
      gboolean visible;
 
2003
      gboolean empty;
 
2004
      GList *children, *cur, *last;
 
2005
      GtkWidget *filler;
 
2006
 
 
2007
      children = gtk_container_get_children (GTK_CONTAINER (parent));
 
2008
      
 
2009
      visible = FALSE;
 
2010
      last = NULL;
 
2011
      empty = TRUE;
 
2012
      filler = NULL;
 
2013
 
 
2014
      cur = children;
 
2015
      while (cur) 
 
2016
        {
 
2017
          if (g_object_get_data (cur->data, "gtk-empty-menu-item"))
 
2018
            {
 
2019
              filler = cur->data;
 
2020
            }
 
2021
          else if (GTK_IS_SEPARATOR_MENU_ITEM (cur->data) ||
 
2022
                   GTK_IS_SEPARATOR_TOOL_ITEM (cur->data))
 
2023
            {
 
2024
              gint mode = 
 
2025
                GPOINTER_TO_INT (g_object_get_data (G_OBJECT (cur->data), 
 
2026
                                                    "gtk-separator-mode"));
 
2027
              switch (mode) 
 
2028
                {
 
2029
                case SEPARATOR_MODE_VISIBLE:
 
2030
                  gtk_widget_show (GTK_WIDGET (cur->data));
 
2031
                  last = NULL;
 
2032
                  visible = FALSE;
 
2033
                  break;
 
2034
                case SEPARATOR_MODE_HIDDEN:
 
2035
                  gtk_widget_hide (GTK_WIDGET (cur->data));
 
2036
                  break;
 
2037
                case SEPARATOR_MODE_SMART:
 
2038
                  if (visible)
 
2039
                    {
 
2040
                      gtk_widget_show (GTK_WIDGET (cur->data));
 
2041
                      last = cur;
 
2042
                      visible = FALSE;
 
2043
                    }
 
2044
                  else 
 
2045
                    gtk_widget_hide (GTK_WIDGET (cur->data));
 
2046
                  break;
 
2047
                }
 
2048
            }
 
2049
          else if (GTK_WIDGET_VISIBLE (cur->data))
 
2050
            {
 
2051
              last = NULL;
 
2052
              if (GTK_IS_TEAROFF_MENU_ITEM (cur->data) || cur->data == filler)
 
2053
                visible = FALSE;
 
2054
              else 
 
2055
                {
 
2056
                  visible = TRUE;
 
2057
                  empty = FALSE;
 
2058
                }
 
2059
            }
 
2060
          
 
2061
          cur = cur->next;
 
2062
        }
 
2063
 
 
2064
      if (last)
 
2065
        gtk_widget_hide (GTK_WIDGET (last->data));
 
2066
 
 
2067
      if (GTK_IS_MENU (parent)) 
 
2068
        {
 
2069
          GtkWidget *item;
 
2070
 
 
2071
          item = gtk_menu_get_attach_widget (GTK_MENU (parent));
 
2072
          if (GTK_IS_MENU_ITEM (item))
 
2073
            _gtk_action_sync_menu_visible (NULL, item, empty);
 
2074
          if (GTK_IS_WIDGET (filler))
 
2075
            {
 
2076
              if (empty)
 
2077
                gtk_widget_show (filler);
 
2078
              else
 
2079
                gtk_widget_hide (filler);
 
2080
            }
 
2081
        }
 
2082
 
 
2083
      g_list_free (children);
 
2084
    }
 
2085
}
 
2086
 
 
2087
static void
 
2088
update_node (GtkUIManager *self, 
 
2089
             GNode        *node,
 
2090
             gboolean      in_popup)
 
2091
{
 
2092
  Node *info;
 
2093
  GNode *child;
 
2094
  GtkAction *action;
 
2095
  const gchar *action_name;
 
2096
  NodeUIReference *ref;
 
2097
  
 
2098
#ifdef DEBUG_UI_MANAGER
 
2099
  GList *tmp;
 
2100
#endif
 
2101
 
 
2102
  g_return_if_fail (node != NULL);
 
2103
  g_return_if_fail (NODE_INFO (node) != NULL);
 
2104
 
 
2105
  info = NODE_INFO (node);
 
2106
  
 
2107
  if (!info->dirty)
 
2108
    return;
 
2109
 
 
2110
  in_popup = in_popup || (info->type == NODE_TYPE_POPUP);
 
2111
 
 
2112
#ifdef DEBUG_UI_MANAGER
 
2113
  g_print ("update_node name=%s dirty=%d popup %d (", 
 
2114
           info->name, info->dirty, in_popup);
 
2115
  for (tmp = info->uifiles; tmp != NULL; tmp = tmp->next)
 
2116
    {
 
2117
      NodeUIReference *ref = tmp->data;
 
2118
      g_print("%s:%u", g_quark_to_string (ref->action_quark), ref->merge_id);
 
2119
      if (tmp->next)
 
2120
        g_print (", ");
 
2121
    }
 
2122
  g_print (")\n");
 
2123
#endif
 
2124
 
 
2125
  if (info->uifiles == NULL) {
 
2126
    /* We may need to remove this node.
 
2127
     * This must be done in post order
 
2128
     */
 
2129
    goto recurse_children;
 
2130
  }
 
2131
  
 
2132
  ref = info->uifiles->data;
 
2133
  action_name = g_quark_to_string (ref->action_quark);
 
2134
  action = get_action_by_name (self, action_name);
 
2135
  
 
2136
  info->dirty = FALSE;
 
2137
  
 
2138
  /* Check if the node doesn't have an action and must have an action */
 
2139
  if (action == NULL &&
 
2140
      info->type != NODE_TYPE_ROOT &&
 
2141
      info->type != NODE_TYPE_MENUBAR &&
 
2142
      info->type != NODE_TYPE_TOOLBAR &&
 
2143
      info->type != NODE_TYPE_POPUP &&
 
2144
      info->type != NODE_TYPE_SEPARATOR &&
 
2145
      info->type != NODE_TYPE_MENU_PLACEHOLDER &&
 
2146
      info->type != NODE_TYPE_TOOLBAR_PLACEHOLDER)
 
2147
    {
 
2148
      g_warning ("%s: missing action %s", info->name, action_name);
 
2149
      
 
2150
      return;
 
2151
    }
 
2152
  
 
2153
  if (action)
 
2154
    gtk_action_set_accel_group (action, self->private_data->accel_group);
 
2155
  
 
2156
  /* If the widget already has a proxy and the action hasn't changed, then
 
2157
   * we only have to update the tearoff menu items.
 
2158
   */
 
2159
  if (info->proxy != NULL && action == info->action)
 
2160
    {
 
2161
      if (info->type == NODE_TYPE_MENU) 
 
2162
        {
 
2163
          GtkWidget *menu;
 
2164
          GList *siblings;
 
2165
          
 
2166
          if (GTK_IS_MENU (info->proxy))
 
2167
            menu = info->proxy;
 
2168
          else
 
2169
            menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (info->proxy));
 
2170
          siblings = gtk_container_get_children (GTK_CONTAINER (menu));
 
2171
          if (siblings != NULL && GTK_IS_TEAROFF_MENU_ITEM (siblings->data))
 
2172
            {
 
2173
              if (self->private_data->add_tearoffs && !in_popup)
 
2174
                gtk_widget_show (GTK_WIDGET (siblings->data));
 
2175
              else
 
2176
                gtk_widget_hide (GTK_WIDGET (siblings->data));
 
2177
            }
 
2178
          g_list_free (siblings);
 
2179
        }
 
2180
      
 
2181
      goto recurse_children;
 
2182
    }
 
2183
  
 
2184
  switch (info->type)
 
2185
    {
 
2186
    case NODE_TYPE_MENUBAR:
 
2187
      if (info->proxy == NULL)
 
2188
        {
 
2189
          info->proxy = gtk_menu_bar_new ();
 
2190
          g_object_ref_sink (info->proxy);
 
2191
          gtk_widget_set_name (info->proxy, info->name);
 
2192
          gtk_widget_show (info->proxy);
 
2193
          g_signal_emit (self, ui_manager_signals[ADD_WIDGET], 0, info->proxy);
 
2194
        }
 
2195
      break;
 
2196
    case NODE_TYPE_POPUP:
 
2197
      if (info->proxy == NULL) 
 
2198
        {
 
2199
          info->proxy = gtk_menu_new ();
 
2200
          g_object_ref_sink (info->proxy);
 
2201
        }
 
2202
      gtk_widget_set_name (info->proxy, info->name);
 
2203
      break;
 
2204
    case NODE_TYPE_MENU:
 
2205
      {
 
2206
        GtkWidget *prev_submenu = NULL;
 
2207
        GtkWidget *menu;
 
2208
        GList *siblings;
 
2209
        /* remove the proxy if it is of the wrong type ... */
 
2210
        if (info->proxy &&  
 
2211
            G_OBJECT_TYPE (info->proxy) != GTK_ACTION_GET_CLASS (action)->menu_item_type)
 
2212
          {
 
2213
            if (GTK_IS_MENU_ITEM (info->proxy))
 
2214
              {
 
2215
                prev_submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (info->proxy));
 
2216
                if (prev_submenu)
 
2217
                  {
 
2218
                    g_object_ref (prev_submenu);
 
2219
                    gtk_menu_item_set_submenu (GTK_MENU_ITEM (info->proxy), NULL);
 
2220
                }
 
2221
              }
 
2222
 
 
2223
            gtk_action_disconnect_proxy (info->action, info->proxy);
 
2224
            gtk_container_remove (GTK_CONTAINER (info->proxy->parent),
 
2225
                                  info->proxy);
 
2226
            g_object_unref (info->proxy);
 
2227
            info->proxy = NULL;
 
2228
          }
 
2229
        /* create proxy if needed ... */
 
2230
        if (info->proxy == NULL)
 
2231
          {
 
2232
            GtkWidget *tearoff;
 
2233
            GtkWidget *filler;
 
2234
            
 
2235
            menu = gtk_menu_new ();
 
2236
            gtk_widget_set_name (menu, info->name);
 
2237
            tearoff = gtk_tearoff_menu_item_new ();
 
2238
            gtk_widget_set_no_show_all (tearoff, TRUE);
 
2239
            gtk_menu_shell_append (GTK_MENU_SHELL (menu), tearoff);
 
2240
            filler = gtk_menu_item_new_with_label (_("Empty"));
 
2241
            g_object_set_data (G_OBJECT (filler),
 
2242
                               I_("gtk-empty-menu-item"),
 
2243
                               GINT_TO_POINTER (TRUE));
 
2244
            gtk_widget_set_sensitive (filler, FALSE);
 
2245
            gtk_widget_set_no_show_all (filler, TRUE);
 
2246
            gtk_menu_shell_append (GTK_MENU_SHELL (menu), filler);
 
2247
            
 
2248
            if (NODE_INFO (node->parent)->type == NODE_TYPE_TOOLITEM)
 
2249
              {
 
2250
                info->proxy = menu;
 
2251
                g_object_ref_sink (info->proxy);
 
2252
                gtk_menu_tool_button_set_menu (GTK_MENU_TOOL_BUTTON (NODE_INFO (node->parent)->proxy),
 
2253
                                               menu);
 
2254
              }
 
2255
            else
 
2256
              {
 
2257
                GtkWidget *menushell;
 
2258
                gint pos;
 
2259
                
 
2260
                if (find_menu_position (node, &menushell, &pos))
 
2261
                  {
 
2262
                     info->proxy = gtk_action_create_menu_item (action);
 
2263
                     g_object_ref_sink (info->proxy);
 
2264
                     g_signal_connect (info->proxy, "notify::visible",
 
2265
                                       G_CALLBACK (update_smart_separators), NULL);
 
2266
                     gtk_widget_set_name (info->proxy, info->name);
 
2267
                
 
2268
                     gtk_menu_item_set_submenu (GTK_MENU_ITEM (info->proxy), menu);
 
2269
                     gtk_menu_shell_insert (GTK_MENU_SHELL (menushell), info->proxy, pos);
 
2270
                 }
 
2271
              }
 
2272
          }
 
2273
        else
 
2274
          gtk_action_connect_proxy (action, info->proxy);
 
2275
        
 
2276
        if (prev_submenu)
 
2277
          {
 
2278
            gtk_menu_item_set_submenu (GTK_MENU_ITEM (info->proxy),
 
2279
                                       prev_submenu);
 
2280
            g_object_unref (prev_submenu);
 
2281
          }
 
2282
        
 
2283
        if (GTK_IS_MENU (info->proxy))
 
2284
          menu = info->proxy;
 
2285
        else
 
2286
          menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (info->proxy));
 
2287
        siblings = gtk_container_get_children (GTK_CONTAINER (menu));
 
2288
        if (siblings != NULL && GTK_IS_TEAROFF_MENU_ITEM (siblings->data))
 
2289
          {
 
2290
            if (self->private_data->add_tearoffs && !in_popup)
 
2291
              gtk_widget_show (GTK_WIDGET (siblings->data));
 
2292
            else
 
2293
              gtk_widget_hide (GTK_WIDGET (siblings->data));
 
2294
          }
 
2295
        g_list_free (siblings);
 
2296
      }
 
2297
      break;
 
2298
    case NODE_TYPE_UNDECIDED:
 
2299
      g_warning ("found undecided node!");
 
2300
      break;
 
2301
    case NODE_TYPE_ROOT:
 
2302
      break;
 
2303
    case NODE_TYPE_TOOLBAR:
 
2304
      if (info->proxy == NULL)
 
2305
        {
 
2306
          info->proxy = gtk_toolbar_new ();
 
2307
          g_object_ref_sink (info->proxy);
 
2308
          gtk_widget_set_name (info->proxy, info->name);
 
2309
          gtk_widget_show (info->proxy);
 
2310
          g_signal_emit (self, ui_manager_signals[ADD_WIDGET], 0, info->proxy);
 
2311
        }
 
2312
      break;
 
2313
    case NODE_TYPE_MENU_PLACEHOLDER:
 
2314
      /* create menu items for placeholders if necessary ... */
 
2315
      if (!GTK_IS_SEPARATOR_MENU_ITEM (info->proxy) ||
 
2316
          !GTK_IS_SEPARATOR_MENU_ITEM (info->extra))
 
2317
        {
 
2318
          if (info->proxy)
 
2319
            {
 
2320
              gtk_container_remove (GTK_CONTAINER (info->proxy->parent),
 
2321
                                    info->proxy);
 
2322
              g_object_unref (info->proxy);
 
2323
              info->proxy = NULL;
 
2324
            }
 
2325
          if (info->extra)
 
2326
            {
 
2327
              gtk_container_remove (GTK_CONTAINER (info->extra->parent),
 
2328
                                    info->extra);
 
2329
              g_object_unref (info->extra);
 
2330
              info->extra = NULL;
 
2331
            }
 
2332
        }
 
2333
      if (info->proxy == NULL)
 
2334
        {
 
2335
          GtkWidget *menushell;
 
2336
          gint pos;
 
2337
          
 
2338
          if (find_menu_position (node, &menushell, &pos))
 
2339
            {
 
2340
              info->proxy = gtk_separator_menu_item_new ();
 
2341
              g_object_ref_sink (info->proxy);
 
2342
              g_object_set_data (G_OBJECT (info->proxy),
 
2343
                                 I_("gtk-separator-mode"),
 
2344
                                 GINT_TO_POINTER (SEPARATOR_MODE_HIDDEN));
 
2345
              gtk_widget_set_no_show_all (info->proxy, TRUE);
 
2346
              gtk_menu_shell_insert (GTK_MENU_SHELL (menushell),
 
2347
                                     NODE_INFO (node)->proxy, pos);
 
2348
          
 
2349
              info->extra = gtk_separator_menu_item_new ();
 
2350
              g_object_ref_sink (info->extra);
 
2351
              g_object_set_data (G_OBJECT (info->extra),
 
2352
                                 I_("gtk-separator-mode"),
 
2353
                                 GINT_TO_POINTER (SEPARATOR_MODE_HIDDEN));
 
2354
              gtk_widget_set_no_show_all (info->extra, TRUE);
 
2355
              gtk_menu_shell_insert (GTK_MENU_SHELL (menushell),
 
2356
                                     NODE_INFO (node)->extra, pos + 1);
 
2357
            }
 
2358
        }
 
2359
      break;
 
2360
    case NODE_TYPE_TOOLBAR_PLACEHOLDER:
 
2361
      /* create toolbar items for placeholders if necessary ... */
 
2362
      if (!GTK_IS_SEPARATOR_TOOL_ITEM (info->proxy) ||
 
2363
          !GTK_IS_SEPARATOR_TOOL_ITEM (info->extra))
 
2364
        {
 
2365
          if (info->proxy)
 
2366
            {
 
2367
              gtk_container_remove (GTK_CONTAINER (info->proxy->parent),
 
2368
                                    info->proxy);
 
2369
              g_object_unref (info->proxy);
 
2370
              info->proxy = NULL;
 
2371
            }
 
2372
          if (info->extra)
 
2373
            {
 
2374
              gtk_container_remove (GTK_CONTAINER (info->extra->parent),
 
2375
                                    info->extra);
 
2376
              g_object_unref (info->extra);
 
2377
              info->extra = NULL;
 
2378
            }
 
2379
        }
 
2380
      if (info->proxy == NULL)
 
2381
        {
 
2382
          GtkWidget *toolbar;
 
2383
          gint pos;
 
2384
          GtkToolItem *item;    
 
2385
          
 
2386
          if (find_toolbar_position (node, &toolbar, &pos))
 
2387
            {
 
2388
              item = gtk_separator_tool_item_new ();
 
2389
              gtk_toolbar_insert (GTK_TOOLBAR (toolbar), item, pos);
 
2390
              info->proxy = GTK_WIDGET (item);
 
2391
              g_object_ref_sink (info->proxy);
 
2392
              g_object_set_data (G_OBJECT (info->proxy),
 
2393
                                 I_("gtk-separator-mode"),
 
2394
                                 GINT_TO_POINTER (SEPARATOR_MODE_HIDDEN));
 
2395
              gtk_widget_set_no_show_all (info->proxy, TRUE);
 
2396
          
 
2397
              item = gtk_separator_tool_item_new ();
 
2398
              gtk_toolbar_insert (GTK_TOOLBAR (toolbar), item, pos+1);
 
2399
              info->extra = GTK_WIDGET (item);
 
2400
              g_object_ref_sink (info->extra);
 
2401
              g_object_set_data (G_OBJECT (info->extra),
 
2402
                                 I_("gtk-separator-mode"),
 
2403
                                 GINT_TO_POINTER (SEPARATOR_MODE_HIDDEN));
 
2404
              gtk_widget_set_no_show_all (info->extra, TRUE);
 
2405
            }
 
2406
        }
 
2407
      break;
 
2408
    case NODE_TYPE_MENUITEM:
 
2409
      /* remove the proxy if it is of the wrong type ... */
 
2410
      if (info->proxy &&  
 
2411
          G_OBJECT_TYPE (info->proxy) != GTK_ACTION_GET_CLASS (action)->menu_item_type)
 
2412
        {
 
2413
          g_signal_handlers_disconnect_by_func (info->proxy,
 
2414
                                                G_CALLBACK (update_smart_separators),
 
2415
                                                NULL);  
 
2416
          gtk_action_disconnect_proxy (info->action, info->proxy);
 
2417
          gtk_container_remove (GTK_CONTAINER (info->proxy->parent),
 
2418
                                info->proxy);
 
2419
          g_object_unref (info->proxy);
 
2420
          info->proxy = NULL;
 
2421
        }
 
2422
      /* create proxy if needed ... */
 
2423
      if (info->proxy == NULL)
 
2424
        {
 
2425
          GtkWidget *menushell;
 
2426
          gint pos;
 
2427
          
 
2428
          if (find_menu_position (node, &menushell, &pos))
 
2429
            {
 
2430
              info->proxy = gtk_action_create_menu_item (action);
 
2431
              g_object_ref_sink (info->proxy);
 
2432
              gtk_widget_set_name (info->proxy, info->name);
 
2433
          
 
2434
              gtk_menu_shell_insert (GTK_MENU_SHELL (menushell),
 
2435
                                     info->proxy, pos);
 
2436
           }
 
2437
        }
 
2438
      else
 
2439
        {
 
2440
          g_signal_handlers_disconnect_by_func (info->proxy,
 
2441
                                                G_CALLBACK (update_smart_separators),
 
2442
                                                NULL);
 
2443
          gtk_menu_item_set_submenu (GTK_MENU_ITEM (info->proxy), NULL);
 
2444
          gtk_action_connect_proxy (action, info->proxy);
 
2445
        }
 
2446
 
 
2447
      if (info->proxy)
 
2448
        {
 
2449
          g_signal_connect (info->proxy, "notify::visible",
 
2450
                            G_CALLBACK (update_smart_separators), NULL);
 
2451
          if (in_popup) 
 
2452
            {
 
2453
              /* don't show accels in popups */
 
2454
              GtkWidget *label = GTK_BIN (info->proxy)->child;
 
2455
              g_object_set (label, "accel-closure", NULL, NULL);
 
2456
            }
 
2457
        }
 
2458
      
 
2459
      break;
 
2460
    case NODE_TYPE_TOOLITEM:
 
2461
      /* remove the proxy if it is of the wrong type ... */
 
2462
      if (info->proxy && 
 
2463
          G_OBJECT_TYPE (info->proxy) != GTK_ACTION_GET_CLASS (action)->toolbar_item_type)
 
2464
        {
 
2465
          g_signal_handlers_disconnect_by_func (info->proxy,
 
2466
                                                G_CALLBACK (update_smart_separators),
 
2467
                                                NULL);
 
2468
          gtk_action_disconnect_proxy (info->action, info->proxy);
 
2469
          gtk_container_remove (GTK_CONTAINER (info->proxy->parent),
 
2470
                                info->proxy);
 
2471
          g_object_unref (info->proxy);
 
2472
          info->proxy = NULL;
 
2473
        }
 
2474
      /* create proxy if needed ... */
 
2475
      if (info->proxy == NULL)
 
2476
        {
 
2477
          GtkWidget *toolbar;
 
2478
          gint pos;
 
2479
          
 
2480
          if (find_toolbar_position (node, &toolbar, &pos))
 
2481
            {
 
2482
              info->proxy = gtk_action_create_tool_item (action);
 
2483
              g_object_ref_sink (info->proxy);
 
2484
              gtk_widget_set_name (info->proxy, info->name);
 
2485
              
 
2486
              gtk_toolbar_insert (GTK_TOOLBAR (toolbar),
 
2487
                                  GTK_TOOL_ITEM (info->proxy), pos);
 
2488
            }
 
2489
        }
 
2490
      else
 
2491
        {
 
2492
          g_signal_handlers_disconnect_by_func (info->proxy,
 
2493
                                                G_CALLBACK (update_smart_separators),
 
2494
                                                NULL);
 
2495
          gtk_action_connect_proxy (action, info->proxy);
 
2496
        }
 
2497
 
 
2498
      if (info->proxy)
 
2499
        {
 
2500
          /* FIXME: we must re-set the tooltip, since tooltips on 
 
2501
           * toolitems can't be set before the toolitem is added 
 
2502
           * to the toolbar.
 
2503
           */
 
2504
          gchar *tooltip;
 
2505
 
 
2506
          g_object_get (G_OBJECT (action), "tooltip", &tooltip, NULL);
 
2507
          g_object_set (G_OBJECT (action), "tooltip", tooltip, NULL);
 
2508
          g_free (tooltip);
 
2509
     
 
2510
          g_signal_connect (info->proxy, "notify::visible",
 
2511
                            G_CALLBACK (update_smart_separators), NULL);
 
2512
        }
 
2513
      break;
 
2514
    case NODE_TYPE_SEPARATOR:
 
2515
      if (NODE_INFO (node->parent)->type == NODE_TYPE_TOOLBAR ||
 
2516
          NODE_INFO (node->parent)->type == NODE_TYPE_TOOLBAR_PLACEHOLDER)
 
2517
        {
 
2518
          GtkWidget *toolbar;
 
2519
          gint pos;
 
2520
          gint separator_mode;
 
2521
          GtkToolItem *item;
 
2522
 
 
2523
          if (GTK_IS_SEPARATOR_TOOL_ITEM (info->proxy))
 
2524
            {
 
2525
              gtk_container_remove (GTK_CONTAINER (info->proxy->parent),
 
2526
                                    info->proxy);
 
2527
              g_object_unref (info->proxy);
 
2528
              info->proxy = NULL;
 
2529
            }
 
2530
          
 
2531
          if (find_toolbar_position (node, &toolbar, &pos))
 
2532
            {
 
2533
              item  = gtk_separator_tool_item_new ();
 
2534
              gtk_toolbar_insert (GTK_TOOLBAR (toolbar), item, pos);
 
2535
              info->proxy = GTK_WIDGET (item);
 
2536
              g_object_ref_sink (info->proxy);
 
2537
              gtk_widget_set_no_show_all (info->proxy, TRUE);
 
2538
              if (info->expand)
 
2539
                {
 
2540
                  gtk_tool_item_set_expand (GTK_TOOL_ITEM (item), TRUE);
 
2541
                  gtk_separator_tool_item_set_draw (GTK_SEPARATOR_TOOL_ITEM (item), FALSE);
 
2542
                  separator_mode = SEPARATOR_MODE_VISIBLE;
 
2543
                }
 
2544
              else
 
2545
                separator_mode = SEPARATOR_MODE_SMART;
 
2546
          
 
2547
              g_object_set_data (G_OBJECT (info->proxy),
 
2548
                                 I_("gtk-separator-mode"),
 
2549
                                 GINT_TO_POINTER (separator_mode));
 
2550
              gtk_widget_show (info->proxy);
 
2551
            }
 
2552
        }
 
2553
      else
 
2554
        {
 
2555
          GtkWidget *menushell;
 
2556
          gint pos;
 
2557
          
 
2558
          if (GTK_IS_SEPARATOR_MENU_ITEM (info->proxy))
 
2559
            {
 
2560
              gtk_container_remove (GTK_CONTAINER (info->proxy->parent),
 
2561
                                    info->proxy);
 
2562
              g_object_unref (info->proxy);
 
2563
              info->proxy = NULL;
 
2564
            }
 
2565
          
 
2566
          if (find_menu_position (node, &menushell, &pos))
 
2567
            {
 
2568
              info->proxy = gtk_separator_menu_item_new ();
 
2569
              g_object_ref_sink (info->proxy);
 
2570
              gtk_widget_set_no_show_all (info->proxy, TRUE);
 
2571
              g_object_set_data (G_OBJECT (info->proxy),
 
2572
                                 I_("gtk-separator-mode"),
 
2573
                                 GINT_TO_POINTER (SEPARATOR_MODE_SMART));
 
2574
              gtk_menu_shell_insert (GTK_MENU_SHELL (menushell),
 
2575
                                     info->proxy, pos);
 
2576
              gtk_widget_show (info->proxy);
 
2577
            }
 
2578
        }
 
2579
      break;
 
2580
    case NODE_TYPE_ACCELERATOR:
 
2581
      gtk_action_connect_accelerator (action);
 
2582
      break;
 
2583
    }
 
2584
  
 
2585
  if (action)
 
2586
    g_object_ref (action);
 
2587
  if (info->action)
 
2588
    g_object_unref (info->action);
 
2589
  info->action = action;
 
2590
 
 
2591
 recurse_children:
 
2592
  child = node->children;
 
2593
  while (child)
 
2594
    {
 
2595
      GNode *current;
 
2596
      
 
2597
      current = child;
 
2598
      child = current->next;
 
2599
      update_node (self, current, in_popup);
 
2600
    }
 
2601
  
 
2602
  if (info->proxy) 
 
2603
    {
 
2604
      if (info->type == NODE_TYPE_MENU && GTK_IS_MENU_ITEM (info->proxy)) 
 
2605
        update_smart_separators (gtk_menu_item_get_submenu (GTK_MENU_ITEM (info->proxy)));
 
2606
      else if (info->type == NODE_TYPE_MENU || 
 
2607
               info->type == NODE_TYPE_TOOLBAR || 
 
2608
               info->type == NODE_TYPE_POPUP) 
 
2609
        update_smart_separators (info->proxy);
 
2610
    }
 
2611
  
 
2612
  /* handle cleanup of dead nodes */
 
2613
  if (node->children == NULL && info->uifiles == NULL)
 
2614
    {
 
2615
      if (info->proxy)
 
2616
        gtk_widget_destroy (info->proxy);
 
2617
      if (info->extra)
 
2618
        gtk_widget_destroy (info->extra);
 
2619
      if (info->type == NODE_TYPE_ACCELERATOR)
 
2620
        gtk_action_disconnect_accelerator (info->action);
 
2621
      free_node (node);
 
2622
      g_node_destroy (node);
 
2623
    }
 
2624
}
 
2625
 
 
2626
static gboolean
 
2627
do_updates (GtkUIManager *self)
 
2628
{
 
2629
  /* this function needs to check through the tree for dirty nodes.
 
2630
   * For such nodes, it needs to do the following:
 
2631
   *
 
2632
   * 1) check if they are referenced by any loaded UI files anymore.
 
2633
   *    In which case, the proxy widget should be destroyed, unless
 
2634
   *    there are any subnodes.
 
2635
   *
 
2636
   * 2) lookup the action for this node again.  If it is different to
 
2637
   *    the current one (or if no previous action has been looked up),
 
2638
   *    the proxy is reconnected to the new action (or a new proxy widget
 
2639
   *    is created and added to the parent container).
 
2640
   */
 
2641
  update_node (self, self->private_data->root_node, FALSE);
 
2642
 
 
2643
  self->private_data->update_tag = 0;
 
2644
 
 
2645
  return FALSE;
 
2646
}
 
2647
 
 
2648
static gboolean
 
2649
do_updates_idle (GtkUIManager *self)
 
2650
{
 
2651
  GDK_THREADS_ENTER ();
 
2652
  do_updates (self);
 
2653
  GDK_THREADS_LEAVE ();
 
2654
 
 
2655
  return FALSE;
 
2656
}
 
2657
 
 
2658
static void
 
2659
queue_update (GtkUIManager *self)
 
2660
{
 
2661
  if (self->private_data->update_tag != 0)
 
2662
    return;
 
2663
 
 
2664
  self->private_data->update_tag = g_idle_add ((GSourceFunc)do_updates_idle, 
 
2665
                                               self);
 
2666
}
 
2667
 
 
2668
 
 
2669
/**
 
2670
 * gtk_ui_manager_ensure_update:
 
2671
 * @self: a #GtkUIManager
 
2672
 * 
 
2673
 * Makes sure that all pending updates to the UI have been completed.
 
2674
 *
 
2675
 * This may occasionally be necessary, since #GtkUIManager updates the 
 
2676
 * UI in an idle function. A typical example where this function is
 
2677
 * useful is to enforce that the menubar and toolbar have been added to 
 
2678
 * the main window before showing it:
 
2679
 * <informalexample>
 
2680
 * <programlisting>
 
2681
 * gtk_container_add (GTK_CONTAINER (window), vbox); 
 
2682
 * g_signal_connect (merge, "add_widget", 
 
2683
 *                   G_CALLBACK (add_widget), vbox);
 
2684
 * gtk_ui_manager_add_ui_from_file (merge, "my-menus");
 
2685
 * gtk_ui_manager_add_ui_from_file (merge, "my-toolbars");
 
2686
 * gtk_ui_manager_ensure_update (merge);  
 
2687
 * gtk_widget_show (window);
 
2688
 * </programlisting>
 
2689
 * </informalexample>
 
2690
 *
 
2691
 * Since: 2.4
 
2692
 **/
 
2693
void
 
2694
gtk_ui_manager_ensure_update (GtkUIManager *self)
 
2695
{
 
2696
  if (self->private_data->update_tag != 0)
 
2697
    {
 
2698
      g_source_remove (self->private_data->update_tag);
 
2699
      do_updates (self);
 
2700
    }
 
2701
}
 
2702
 
 
2703
static gboolean
 
2704
dirty_traverse_func (GNode   *node,
 
2705
                     gpointer data)
 
2706
{
 
2707
  NODE_INFO (node)->dirty = TRUE;
 
2708
  return FALSE;
 
2709
}
 
2710
 
 
2711
static void
 
2712
dirty_all_nodes (GtkUIManager *self)
 
2713
{
 
2714
  g_node_traverse (self->private_data->root_node,
 
2715
                   G_PRE_ORDER, G_TRAVERSE_ALL, -1,
 
2716
                   dirty_traverse_func, NULL);
 
2717
  queue_update (self);
 
2718
}
 
2719
 
 
2720
static void
 
2721
mark_node_dirty (GNode *node)
 
2722
{
 
2723
  GNode *p;
 
2724
 
 
2725
  /* FIXME could optimize this */
 
2726
  for (p = node; p; p = p->parent)
 
2727
    NODE_INFO (p)->dirty = TRUE;  
 
2728
}
 
2729
 
 
2730
static const gchar *
 
2731
open_tag_format (NodeType type)
 
2732
{
 
2733
  switch (type)
 
2734
    {
 
2735
    case NODE_TYPE_UNDECIDED: return "%*s<UNDECIDED"; 
 
2736
    case NODE_TYPE_ROOT: return "%*s<ui"; 
 
2737
    case NODE_TYPE_MENUBAR: return "%*s<menubar";
 
2738
    case NODE_TYPE_MENU: return "%*s<menu";
 
2739
    case NODE_TYPE_TOOLBAR: return "%*s<toolbar";
 
2740
    case NODE_TYPE_MENU_PLACEHOLDER:
 
2741
    case NODE_TYPE_TOOLBAR_PLACEHOLDER: return "%*s<placeholder";
 
2742
    case NODE_TYPE_POPUP: return "%*s<popup";
 
2743
    case NODE_TYPE_MENUITEM: return "%*s<menuitem";
 
2744
    case NODE_TYPE_TOOLITEM: return "%*s<toolitem";
 
2745
    case NODE_TYPE_SEPARATOR: return "%*s<separator";
 
2746
    case NODE_TYPE_ACCELERATOR: return "%*s<accelerator";
 
2747
    default: return NULL;
 
2748
    }
 
2749
}
 
2750
 
 
2751
static const gchar *
 
2752
close_tag_format (NodeType type)
 
2753
{
 
2754
  switch (type)
 
2755
    {
 
2756
    case NODE_TYPE_UNDECIDED: return "%*s</UNDECIDED>\n";
 
2757
    case NODE_TYPE_ROOT: return "%*s</ui>\n";
 
2758
    case NODE_TYPE_MENUBAR: return "%*s</menubar>\n";
 
2759
    case NODE_TYPE_MENU: return "%*s</menu>\n";
 
2760
    case NODE_TYPE_TOOLBAR: return "%*s</toolbar>\n";
 
2761
    case NODE_TYPE_MENU_PLACEHOLDER:
 
2762
    case NODE_TYPE_TOOLBAR_PLACEHOLDER: return "%*s</placeholder>\n";
 
2763
    case NODE_TYPE_POPUP: return "%*s</popup>\n";
 
2764
    default: return NULL;
 
2765
    }
 
2766
}
 
2767
 
 
2768
static void
 
2769
print_node (GtkUIManager *self,
 
2770
            GNode        *node,
 
2771
            gint          indent_level,
 
2772
            GString      *buffer)
 
2773
{
 
2774
  Node  *mnode;
 
2775
  GNode *child;
 
2776
  const gchar *open_fmt;
 
2777
  const gchar *close_fmt;
 
2778
 
 
2779
  mnode = node->data;
 
2780
 
 
2781
  open_fmt = open_tag_format (mnode->type);
 
2782
  close_fmt = close_tag_format (mnode->type);
 
2783
 
 
2784
  g_string_append_printf (buffer, open_fmt, indent_level, "");
 
2785
 
 
2786
  if (mnode->type != NODE_TYPE_ROOT)
 
2787
    {
 
2788
      if (mnode->name)
 
2789
        g_string_append_printf (buffer, " name=\"%s\"", mnode->name);
 
2790
      
 
2791
      if (mnode->action_name)
 
2792
        g_string_append_printf (buffer, " action=\"%s\"",
 
2793
                                g_quark_to_string (mnode->action_name));
 
2794
    }
 
2795
 
 
2796
  g_string_append (buffer, close_fmt ? ">\n" : "/>\n");
 
2797
 
 
2798
  for (child = node->children; child != NULL; child = child->next)
 
2799
    print_node (self, child, indent_level + 2, buffer);
 
2800
 
 
2801
  if (close_fmt)
 
2802
    g_string_append_printf (buffer, close_fmt, indent_level, "");
 
2803
}
 
2804
 
 
2805
/**
 
2806
 * gtk_ui_manager_get_ui:
 
2807
 * @self: a #GtkUIManager
 
2808
 * 
 
2809
 * Creates a <link linkend="XML-UI">UI definition</link> of the merged UI.
 
2810
 * 
 
2811
 * Return value: A newly allocated string containing an XML representation of 
 
2812
 * the merged UI.
 
2813
 *
 
2814
 * Since: 2.4
 
2815
 **/
 
2816
gchar *
 
2817
gtk_ui_manager_get_ui (GtkUIManager *self)
 
2818
{
 
2819
  GString *buffer;
 
2820
 
 
2821
  buffer = g_string_new (NULL);
 
2822
 
 
2823
  gtk_ui_manager_ensure_update (self); 
 
2824
 
 
2825
  print_node (self, self->private_data->root_node, 0, buffer);  
 
2826
 
 
2827
  return g_string_free (buffer, FALSE);
 
2828
}
 
2829
 
 
2830
#ifdef G_OS_WIN32
 
2831
 
 
2832
#undef gtk_ui_manager_add_ui_from_file
 
2833
 
 
2834
guint
 
2835
gtk_ui_manager_add_ui_from_file (GtkUIManager *self,
 
2836
                                 const gchar  *filename,
 
2837
                                 GError      **error)
 
2838
{
 
2839
  gchar *utf8_filename = g_locale_to_utf8 (filename, -1, NULL, NULL, error);
 
2840
  guint retval;
 
2841
 
 
2842
  if (utf8_filename == NULL)
 
2843
    return 0;
 
2844
  
 
2845
  retval = gtk_ui_manager_add_ui_from_file_utf8 (self, utf8_filename, error);
 
2846
 
 
2847
  g_free (utf8_filename);
 
2848
 
 
2849
  return retval;
 
2850
}
 
2851
 
 
2852
#endif
 
2853
 
 
2854
#define __GTK_UI_MANAGER_C__
 
2855
#include "gtkaliasdef.c"