~ubuntu-branches/ubuntu/maverick/libdbusmenu/maverick

« back to all changes in this revision

Viewing changes to libdbusmenu-glib/client.c

  • Committer: Bazaar Package Importer
  • Author(s): Ted Gould
  • Date: 2010-08-02 20:28:58 UTC
  • mfrom: (1.1.26 upstream)
  • Revision ID: james.westby@ubuntu.com-20100802202858-n5pxge6w7padkqoe
Tags: 0.3.9-0ubuntu1
* New upstream release.
  * Making it so that properties are requested bredth first
  * Globbing layout signaling from the server
  * Globbing property requests into group requests (LP: #604670)

Show diffs side-by-side

added added

removed removed

Lines of Context:
78
78
        DBusGProxy * dbusproxy;
79
79
 
80
80
        GHashTable * type_handlers;
 
81
 
 
82
        GArray * delayed_property_list;
 
83
        GArray * delayed_property_listeners;
 
84
        gint delayed_idle;
81
85
};
82
86
 
83
87
typedef struct _newItemPropData newItemPropData;
88
92
        DbusmenuMenuitem * parent;
89
93
};
90
94
 
 
95
typedef struct _properties_listener_t properties_listener_t;
 
96
struct _properties_listener_t {
 
97
        gint id;
 
98
        org_ayatana_dbusmenu_get_properties_reply callback;
 
99
        gpointer user_data;
 
100
        gboolean replied;
 
101
};
 
102
 
91
103
#define DBUSMENU_CLIENT_GET_PRIVATE(o) \
92
104
(G_TYPE_INSTANCE_GET_PRIVATE ((o), DBUSMENU_TYPE_CLIENT, DbusmenuClientPrivate))
93
105
 
109
121
static void update_layout_cb (DBusGProxy * proxy, guint rev, gchar * xml, GError * in_error, void * data);
110
122
static void update_layout (DbusmenuClient * client);
111
123
static void menuitem_get_properties_cb (DBusGProxy * proxy, GHashTable * properties, GError * error, gpointer data);
 
124
static void get_properties_globber (DbusmenuClient * client, gint id, const gchar ** properties, org_ayatana_dbusmenu_get_properties_reply callback, gpointer user_data);
 
125
static GQuark error_domain (void);
112
126
 
113
127
/* Build a type */
114
128
G_DEFINE_TYPE (DbusmenuClient, dbusmenu_client, G_TYPE_OBJECT);
211
225
        priv->type_handlers = g_hash_table_new_full(g_str_hash, g_str_equal,
212
226
                                                    g_free, NULL);
213
227
 
 
228
        priv->delayed_idle = 0;
 
229
        priv->delayed_property_list = g_array_new(TRUE, FALSE, sizeof(gchar *));
 
230
        priv->delayed_property_listeners = g_array_new(FALSE, FALSE, sizeof(properties_listener_t));
 
231
 
214
232
        return;
215
233
}
216
234
 
219
237
{
220
238
        DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(object);
221
239
 
 
240
        if (priv->delayed_idle != 0) {
 
241
                g_source_remove(priv->delayed_idle);
 
242
                priv->delayed_idle = 0;
 
243
        }
 
244
 
 
245
        /* Only used for queueing up a new command, so we can
 
246
           just drop this array. */
 
247
        if (priv->delayed_property_list == NULL) {
 
248
                gchar ** dataregion = (gchar **)g_array_free(priv->delayed_property_list, FALSE);
 
249
                if (dataregion != NULL) {
 
250
                        g_strfreev(dataregion);
 
251
                }
 
252
                priv->delayed_property_list = NULL;
 
253
        }
 
254
 
 
255
        if (priv->delayed_property_listeners == NULL) {
 
256
                gint i;
 
257
                GError * localerror = NULL;
 
258
 
 
259
                /* Making sure all the callbacks get called so that if they had
 
260
                   memory in their user_data that needs to be free'd that happens. */
 
261
                for (i = 0; i < priv->delayed_property_listeners->len; i++) {
 
262
                        properties_listener_t * listener = &g_array_index(priv->delayed_property_listeners, properties_listener_t, i);
 
263
                        if (!listener->replied) {
 
264
                                if (localerror == NULL) {
 
265
                                        g_set_error_literal(&localerror, error_domain(), 0, "DbusmenuClient Shutdown");
 
266
                                }
 
267
                                listener->callback(priv->menuproxy, NULL, localerror, listener->user_data);
 
268
                        }
 
269
                }
 
270
                if (localerror != NULL) {
 
271
                        g_error_free(localerror);
 
272
                }
 
273
 
 
274
                g_array_free(priv->delayed_property_listeners, TRUE);
 
275
                priv->delayed_property_listeners = NULL;
 
276
        }
 
277
 
222
278
        if (priv->layoutcall != NULL) {
223
279
                dbus_g_proxy_cancel_call(priv->menuproxy, priv->layoutcall);
224
280
                priv->layoutcall = NULL;
310
366
 
311
367
/* Internal funcs */
312
368
 
 
369
static GQuark
 
370
error_domain (void)
 
371
{
 
372
        static GQuark error = 0;
 
373
        if (error == 0) {
 
374
                error = g_quark_from_static_string(G_LOG_DOMAIN "-CLIENT");
 
375
        }
 
376
        return error;
 
377
}
 
378
 
 
379
/* Quick little function to search through the listeners and find
 
380
   one that matches an ID */
 
381
static properties_listener_t *
 
382
find_listener (GArray * listeners, guint index, gint id)
 
383
{
 
384
        if (index >= listeners->len) {
 
385
                return NULL;
 
386
        }
 
387
 
 
388
        properties_listener_t * retval = &g_array_index(listeners, properties_listener_t, index);
 
389
        if (retval->id == id) {
 
390
                return retval;
 
391
        }
 
392
 
 
393
        return find_listener(listeners, index + 1, id);
 
394
}
 
395
 
 
396
/* Call back from getting the group properties, now we need
 
397
   to unwind and call the various functions. */
 
398
static void 
 
399
get_properties_callback (DBusGProxy *proxy, GPtrArray *OUT_properties, GError *error, gpointer userdata)
 
400
{
 
401
        GArray * listeners = (GArray *)userdata;
 
402
        int i;
 
403
 
 
404
        #ifdef MASSIVEDEBUGGING
 
405
        g_debug("Get properties callback: %d", OUT_properties->len);
 
406
        #endif
 
407
 
 
408
        if (error != NULL) {
 
409
                /* If we get an error, all our callbacks need to hear about it. */
 
410
                g_warning("Group Properties error: %s", error->message);
 
411
                for (i = 0; i < listeners->len; i++) {
 
412
                        properties_listener_t * listener = &g_array_index(listeners, properties_listener_t, i);
 
413
                        listener->callback(proxy, NULL, error, listener->user_data);
 
414
                }
 
415
                g_array_free(listeners, TRUE);
 
416
                return;
 
417
        }
 
418
 
 
419
        /* Callback all the folks we can find */
 
420
        for (i = 0; i < OUT_properties->len; i++) {
 
421
                GValueArray * varray = (GValueArray *)g_ptr_array_index(OUT_properties, i);
 
422
 
 
423
                if (varray->n_values != 2) {
 
424
                        g_warning("Value Array is %d entries long but we expected 2.", varray->n_values);
 
425
                        continue;
 
426
                }
 
427
 
 
428
                GValue * vid = g_value_array_get_nth(varray, 0);
 
429
                GValue * vproperties = g_value_array_get_nth(varray, 1);
 
430
 
 
431
                if (G_VALUE_TYPE(vid) != G_TYPE_INT) {
 
432
                        g_warning("ID Entry not holding an int: %s", G_VALUE_TYPE_NAME(vid));
 
433
                }
 
434
                if (G_VALUE_TYPE(vproperties) != dbus_g_type_get_map("GHashTable", G_TYPE_STRING, G_TYPE_VALUE)) {
 
435
                        g_warning("Properties Entry not holding an a{sv}: %s", G_VALUE_TYPE_NAME(vproperties));
 
436
                }
 
437
 
 
438
                gint id = g_value_get_int(vid);
 
439
                GHashTable * properties = g_value_get_boxed(vproperties);
 
440
 
 
441
                properties_listener_t * listener = find_listener(listeners, 0, id);
 
442
                if (listener == NULL) {
 
443
                        g_warning("Unable to find listener for ID %d", id);
 
444
                        continue;
 
445
                }
 
446
 
 
447
                if (!listener->replied) {
 
448
                        listener->callback(proxy, properties, NULL, listener->user_data);
 
449
                        listener->replied = TRUE;
 
450
                } else {
 
451
                        g_warning("Odd, we've already replied to the listener on ID %d", id);
 
452
                }
 
453
        }
 
454
 
 
455
        /* Provide errors for those who we can't */
 
456
        GError * localerror = NULL;
 
457
        for (i = 0; i < listeners->len; i++) {
 
458
                properties_listener_t * listener = &g_array_index(listeners, properties_listener_t, i);
 
459
                if (!listener->replied) {
 
460
                        if (localerror == NULL) {
 
461
                                g_set_error_literal(&localerror, error_domain(), 0, "Error getting properties for ID");
 
462
                        }
 
463
                        listener->callback(proxy, NULL, localerror, listener->user_data);
 
464
                }
 
465
        }
 
466
        if (localerror != NULL) {
 
467
                g_error_free(localerror);
 
468
        }
 
469
 
 
470
        /* Clean up */
 
471
        g_array_free(listeners, TRUE);
 
472
 
 
473
        return;
 
474
}
 
475
 
 
476
/* Idle handler to send out all of our property requests as one big
 
477
   lovely property request. */
 
478
static gboolean
 
479
get_properties_idle (gpointer user_data)
 
480
{
 
481
        DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(user_data);
 
482
        //org_ayatana_dbusmenu_get_properties_async(priv->menuproxy, id, properties, callback, user_data);
 
483
 
 
484
        if (priv->delayed_property_listeners->len == 0) {
 
485
                g_warning("Odd, idle func got no listeners.");
 
486
                return FALSE;
 
487
        }
 
488
 
 
489
        /* Build up an ID list to pass */
 
490
        GArray * idlist = g_array_new(FALSE, FALSE, sizeof(gint));
 
491
        gint i;
 
492
        for (i = 0; i < priv->delayed_property_listeners->len; i++) {
 
493
                g_array_append_val(idlist, g_array_index(priv->delayed_property_listeners, properties_listener_t, i).id);
 
494
        }
 
495
 
 
496
        org_ayatana_dbusmenu_get_group_properties_async(priv->menuproxy, idlist, (const gchar **)priv->delayed_property_list->data, get_properties_callback, priv->delayed_property_listeners);
 
497
 
 
498
        /* Free ID List */
 
499
        g_array_free(idlist, TRUE);
 
500
 
 
501
        /* Free properties */
 
502
        gchar ** dataregion = (gchar **)g_array_free(priv->delayed_property_list, FALSE);
 
503
        if (dataregion != NULL) {
 
504
                g_strfreev(dataregion);
 
505
        }
 
506
        priv->delayed_property_list = g_array_new(TRUE, FALSE, sizeof(gchar *));
 
507
 
 
508
        /* Rebuild the listeners */
 
509
        priv->delayed_property_listeners = g_array_new(FALSE, FALSE, sizeof(properties_listener_t));
 
510
 
 
511
        /* Make sure we set for a new idle */
 
512
        priv->delayed_idle = 0;
 
513
 
 
514
        return FALSE;
 
515
}
 
516
 
 
517
/* Forces a call out to start getting properties with the menu items
 
518
   that we have queued up already. */
 
519
static void
 
520
get_properties_flush (DbusmenuClient * client)
 
521
{
 
522
        DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client);
 
523
 
 
524
        if (priv->delayed_idle == 0) {
 
525
                return;
 
526
        }
 
527
 
 
528
        g_source_remove(priv->delayed_idle);
 
529
        priv->delayed_idle = 0;
 
530
 
 
531
        get_properties_idle(client);
 
532
 
 
533
        dbus_g_connection_flush(priv->session_bus);
 
534
 
 
535
        return;
 
536
}
 
537
 
 
538
/* A function to group all the get_properties commands to make them
 
539
   more efficient over dbus. */
 
540
static void
 
541
get_properties_globber (DbusmenuClient * client, gint id, const gchar ** properties, org_ayatana_dbusmenu_get_properties_reply callback, gpointer user_data)
 
542
{
 
543
        DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(client);
 
544
        if (find_listener(priv->delayed_property_listeners, 0, id) != NULL) {
 
545
                g_warning("Asking for properties from same ID twice: %d", id);
 
546
                GError * localerror = NULL;
 
547
                g_set_error_literal(&localerror, error_domain(), 0, "ID already queued");
 
548
                callback(priv->menuproxy, NULL, localerror, user_data);
 
549
                g_error_free(localerror);
 
550
                return;
 
551
        }
 
552
 
 
553
        if (properties == NULL || properties[0] == NULL) {
 
554
                /* get all case */
 
555
                if (priv->delayed_property_list->len != 0) {
 
556
                        /* If there are entries in the list, then we'll need to
 
557
                           remove them all, and start over */
 
558
                        gchar ** dataregion = (gchar **)g_array_free(priv->delayed_property_list, FALSE);
 
559
                        if (dataregion != NULL) {
 
560
                                g_strfreev(dataregion);
 
561
                        }
 
562
                        priv->delayed_property_list = g_array_new(TRUE, FALSE, sizeof(gchar *));
 
563
                }
 
564
        } else {
 
565
                /* there could be a list we care about */
 
566
                /* TODO: No one uses this today */
 
567
                /* TODO: Copy them into the list */
 
568
        }
 
569
 
 
570
        properties_listener_t listener = {0};
 
571
        listener.id = id;
 
572
        listener.callback = callback;
 
573
        listener.user_data = user_data;
 
574
        listener.replied = FALSE;
 
575
 
 
576
        g_array_append_val(priv->delayed_property_listeners, listener);
 
577
 
 
578
        if (priv->delayed_idle == 0) {
 
579
                priv->delayed_idle = g_idle_add(get_properties_idle, client);
 
580
        }
 
581
 
 
582
        return;
 
583
}
 
584
 
313
585
/* Annoying little wrapper to make the right function update */
314
586
static void
315
587
layout_update (DBusGProxy * proxy, guint revision, gint parent, DbusmenuClient * client)
367
639
        DbusmenuMenuitem * menuitem = dbusmenu_menuitem_find_id(priv->root, id);
368
640
        g_return_if_fail(menuitem != NULL);
369
641
 
370
 
        gchar * properties[1] = {NULL}; /* This gets them all */
371
642
        g_debug("Getting properties");
372
643
        g_object_ref(menuitem);
373
 
        org_ayatana_dbusmenu_get_properties_async(proxy, id, (const gchar **)properties, menuitem_get_properties_cb, menuitem);
 
644
        get_properties_globber(client, id, NULL, menuitem_get_properties_cb, menuitem);
374
645
        return;
375
646
}
376
647
 
655
926
static void
656
927
menuitem_get_properties_new_cb (DBusGProxy * proxy, GHashTable * properties, GError * error, gpointer data)
657
928
{
658
 
        if (error != NULL) {
659
 
                g_warning("Error getting properties on a new menuitem: %s", error->message);
660
 
                g_object_unref(data);
661
 
                return;
662
 
        }
663
929
        g_return_if_fail(data != NULL);
664
 
 
665
930
        newItemPropData * propdata = (newItemPropData *)data;
 
931
 
 
932
        if (error != NULL) {
 
933
                g_warning("Error getting properties on a new menuitem: %s", error->message);
 
934
                g_object_unref(propdata->item);
 
935
                g_free(data);
 
936
                return;
 
937
        }
 
938
 
666
939
        DbusmenuClientPrivate * priv = DBUSMENU_CLIENT_GET_PRIVATE(propdata->client);
667
940
 
 
941
        /* Extra ref as get_properties will unref once itself */
668
942
        g_object_ref(propdata->item);
669
943
        menuitem_get_properties_cb (proxy, properties, error, propdata->item);
670
944
 
784
1058
        return;
785
1059
}
786
1060
 
 
1061
/* Builds a new child with property requests and everything
 
1062
   else to clean up the code a bit */
 
1063
static DbusmenuMenuitem *
 
1064
parse_layout_new_child (gint id, DbusmenuClient * client, DbusmenuMenuitem * parent)
 
1065
{
 
1066
        DbusmenuMenuitem * item = NULL;
 
1067
 
 
1068
        /* Build a new item */
 
1069
        item = DBUSMENU_MENUITEM(dbusmenu_client_menuitem_new(id, client));
 
1070
        if (parent == NULL) {
 
1071
                dbusmenu_menuitem_set_root(item, TRUE);
 
1072
        }
 
1073
 
 
1074
        /* Get the properties queued up for this item */
 
1075
        /* Not happy allocating about this, but I need these :( */
 
1076
        newItemPropData * propdata = g_new0(newItemPropData, 1);
 
1077
        if (propdata != NULL) {
 
1078
                propdata->client  = client;
 
1079
                propdata->item    = item;
 
1080
                propdata->parent  = parent;
 
1081
 
 
1082
                g_object_ref(item);
 
1083
                get_properties_globber(client, id, NULL, menuitem_get_properties_new_cb, propdata);
 
1084
        } else {
 
1085
                g_warning("Unable to allocate memory to get properties for menuitem.  This menuitem will never be realized.");
 
1086
        }
 
1087
 
 
1088
        return item;
 
1089
}
 
1090
 
 
1091
/* Refresh the properties on this item */
 
1092
static void
 
1093
parse_layout_update (DbusmenuMenuitem * item, DbusmenuClient * client)
 
1094
{
 
1095
        g_object_ref(item);
 
1096
        get_properties_globber(client, dbusmenu_menuitem_get_id(item), NULL, menuitem_get_properties_replace_cb, item);
 
1097
        return;
 
1098
}
 
1099
 
787
1100
/* Parse recursively through the XML and make it into
788
1101
   objects as need be */
789
1102
static DbusmenuMenuitem *
790
1103
parse_layout_xml(DbusmenuClient * client, xmlNodePtr node, DbusmenuMenuitem * item, DbusmenuMenuitem * parent, DBusGProxy * proxy)
791
1104
{
 
1105
        /* First verify and figure out what we've got */
792
1106
        gint id = parse_node_get_id(node);
793
1107
        if (id < 0) {
794
1108
                return NULL;
796
1110
        #ifdef MASSIVEDEBUGGING
797
1111
        g_debug("Client looking at node with id: %d", id);
798
1112
        #endif
799
 
        /* If we don't have any item, or the IDs don't match */
800
 
        if (item == NULL || dbusmenu_menuitem_get_id(item) != id) {
801
 
                if (item != NULL) {
802
 
                        if (parent != NULL) {
803
 
                                dbusmenu_menuitem_child_delete(parent, item);
804
 
                        }
805
 
                        item = NULL;
806
 
                }
807
 
 
808
 
                /* Build a new item */
809
 
                item = DBUSMENU_MENUITEM(dbusmenu_client_menuitem_new(id, client));
810
 
                if (parent == NULL) {
811
 
                        dbusmenu_menuitem_set_root(item, TRUE);
812
 
                }
813
 
 
814
 
                /* Get the properties queued up for this item */
815
 
                /* Not happy about this, but I need these :( */
816
 
                newItemPropData * propdata = g_new0(newItemPropData, 1);
817
 
                if (propdata != NULL) {
818
 
                        propdata->client  = client;
819
 
                        propdata->item    = item;
820
 
                        propdata->parent  = parent;
821
 
 
822
 
                        gchar * properties[1] = {NULL}; /* This gets them all */
823
 
                        g_object_ref(item);
824
 
                        org_ayatana_dbusmenu_get_properties_async(proxy, id, (const gchar **)properties, menuitem_get_properties_new_cb, propdata);
825
 
                } else {
826
 
                        g_warning("Unable to allocate memory to get properties for menuitem.  This menuitem will never be realized.");
827
 
                }
828
 
        } else {
829
 
                /* Refresh the properties */
830
 
                /* XXX: We shouldn't need to get the properties everytime we reuse an entry */
831
 
                gchar * properties[1] = {NULL}; /* This gets them all */
832
 
                g_object_ref(item);
833
 
                org_ayatana_dbusmenu_get_properties_async(proxy, id, (const gchar **)properties, menuitem_get_properties_replace_cb, item);
834
 
        }
835
 
 
 
1113
 
 
1114
        g_return_val_if_fail(item != NULL, NULL);
 
1115
        g_return_val_if_fail(id == dbusmenu_menuitem_get_id(item), NULL);
 
1116
 
 
1117
        /* Some variables */
836
1118
        xmlNodePtr children;
837
1119
        guint position;
838
1120
        GList * oldchildren = g_list_copy(dbusmenu_menuitem_get_children(item));
839
1121
        /* g_debug("Starting old children: %d", g_list_length(oldchildren)); */
840
1122
 
 
1123
        /* Go through all the XML Nodes and make sure that we have menuitems
 
1124
           to cover those XML nodes. */
841
1125
        for (children = node->children, position = 0; children != NULL; children = children->next, position++) {
842
1126
                /* g_debug("Looking at child: %d", position); */
843
1127
                gint childid = parse_node_get_id(children);
846
1130
                }
847
1131
                DbusmenuMenuitem * childmi = NULL;
848
1132
 
 
1133
                /* First see if we can recycle a node that we've already built
 
1134
                   on this menu item */
849
1135
                GList * childsearch = NULL;
850
1136
                for (childsearch = oldchildren; childsearch != NULL; childsearch = g_list_next(childsearch)) {
851
1137
                        DbusmenuMenuitem * cs_mi = DBUSMENU_MENUITEM(childsearch->data);
856
1142
                        }
857
1143
                }
858
1144
 
859
 
                DbusmenuMenuitem * newchildmi = parse_layout_xml(client, children, childmi, item, proxy);
860
 
 
861
 
                if (newchildmi != childmi) {
862
 
                        if (childmi != NULL) {
863
 
                                dbusmenu_menuitem_child_delete(item, childmi);
864
 
                        }
865
 
                        dbusmenu_menuitem_child_add_position(item, newchildmi, position);
866
 
                        g_object_unref(newchildmi);
 
1145
                if (childmi == NULL) {
 
1146
                        /* If we can't recycle, then we build a new one */
 
1147
                        childmi = parse_layout_new_child(childid, client, item);
 
1148
                        dbusmenu_menuitem_child_add_position(item, childmi, position);
 
1149
                        g_object_unref(childmi);
867
1150
                } else {
 
1151
                        /* If we can recycle, make sure it's in the right place */
868
1152
                        dbusmenu_menuitem_child_reorder(item, childmi, position);
 
1153
                        parse_layout_update(childmi, client);
869
1154
                }
870
1155
        }
871
1156
 
872
 
        /* g_debug("Stopping old children: %d", g_list_length(oldchildren)); */
 
1157
        /* Remove any children that are no longer used by this version of
 
1158
           the layout. */
873
1159
        GList * oldchildleft = NULL;
874
1160
        for (oldchildleft = oldchildren; oldchildleft != NULL; oldchildleft = g_list_next(oldchildleft)) {
875
1161
                DbusmenuMenuitem * oldmi = DBUSMENU_MENUITEM(oldchildleft->data);
880
1166
        }
881
1167
        g_list_free(oldchildren);
882
1168
 
 
1169
        /* We've got everything built up at this node and reconcilled */
 
1170
 
 
1171
        /* Flush the properties requests */
 
1172
        get_properties_flush(client);
 
1173
 
 
1174
        /* now it's time to recurse down the tree. */
 
1175
        children = node->children;
 
1176
        GList * childmis = dbusmenu_menuitem_get_children(item);
 
1177
        while (children != NULL && childmis != NULL) {
 
1178
                parse_layout_xml(client, children, DBUSMENU_MENUITEM(childmis->data), item, proxy);
 
1179
 
 
1180
                children = children->next;
 
1181
                childmis = g_list_next(childmis);
 
1182
        }
 
1183
        if (children != NULL) {
 
1184
                g_warning("Sync failed, now we've got extra XML nodes.");
 
1185
        }
 
1186
        if (childmis != NULL) {
 
1187
                g_warning("Sync failed, now we've got extra menu items.");
 
1188
        }
 
1189
 
883
1190
        return item;
884
1191
}
885
1192
 
906
1213
 
907
1214
        DbusmenuMenuitem * oldroot = priv->root;
908
1215
 
 
1216
        if (priv->root == NULL) {
 
1217
                priv->root = parse_layout_new_child(0, client, NULL);
 
1218
        } else {
 
1219
                parse_layout_update(priv->root, client);
 
1220
        }
 
1221
 
909
1222
        priv->root = parse_layout_xml(client, root, priv->root, NULL, priv->menuproxy);
910
1223
        xmlFreeDoc(xmldoc);
911
1224