From 8d46b0347ce6cfff71a769b7aec8d402b572bb0b Mon Sep 17 00:00:00 2001 From: "raveit65 (via Travis CI)" Date: Thu, 15 Feb 2024 16:29:48 +0000 Subject: Deploy mate-desktop/mate-menus to github.com/mate-desktop/mate-menus.git:gh-pages --- .../0.html | 2695 +++++ .../1.html | 10409 +++++++++++++++++++ .../2.html | 5217 ++++++++++ .../3.html | 1103 ++ .../index.html | 216 + .../stats.html | 173 + .../style.css | 177 + 7 files changed, 19990 insertions(+) create mode 100644 2023-09-02-151726-1231-cppcheck@7aac87a9810a_master/0.html create mode 100644 2023-09-02-151726-1231-cppcheck@7aac87a9810a_master/1.html create mode 100644 2023-09-02-151726-1231-cppcheck@7aac87a9810a_master/2.html create mode 100644 2023-09-02-151726-1231-cppcheck@7aac87a9810a_master/3.html create mode 100644 2023-09-02-151726-1231-cppcheck@7aac87a9810a_master/index.html create mode 100644 2023-09-02-151726-1231-cppcheck@7aac87a9810a_master/stats.html create mode 100644 2023-09-02-151726-1231-cppcheck@7aac87a9810a_master/style.css (limited to '2023-09-02-151726-1231-cppcheck@7aac87a9810a_master') diff --git a/2023-09-02-151726-1231-cppcheck@7aac87a9810a_master/0.html b/2023-09-02-151726-1231-cppcheck@7aac87a9810a_master/0.html new file mode 100644 index 0000000..954d95a --- /dev/null +++ b/2023-09-02-151726-1231-cppcheck@7aac87a9810a_master/0.html @@ -0,0 +1,2695 @@ + + + + + + Cppcheck - HTML report - mate-menus + + + + + +
+ + + +
+
   1
+   2
+   3
+   4
+   5
+   6
+   7
+   8
+   9
+  10
+  11
+  12
+  13
+  14
+  15
+  16
+  17
+  18
+  19
+  20
+  21
+  22
+  23
+  24
+  25
+  26
+  27
+  28
+  29
+  30
+  31
+  32
+  33
+  34
+  35
+  36
+  37
+  38
+  39
+  40
+  41
+  42
+  43
+  44
+  45
+  46
+  47
+  48
+  49
+  50
+  51
+  52
+  53
+  54
+  55
+  56
+  57
+  58
+  59
+  60
+  61
+  62
+  63
+  64
+  65
+  66
+  67
+  68
+  69
+  70
+  71
+  72
+  73
+  74
+  75
+  76
+  77
+  78
+  79
+  80
+  81
+  82
+  83
+  84
+  85
+  86
+  87
+  88
+  89
+  90
+  91
+  92
+  93
+  94
+  95
+  96
+  97
+  98
+  99
+ 100
+ 101
+ 102
+ 103
+ 104
+ 105
+ 106
+ 107
+ 108
+ 109
+ 110
+ 111
+ 112
+ 113
+ 114
+ 115
+ 116
+ 117
+ 118
+ 119
+ 120
+ 121
+ 122
+ 123
+ 124
+ 125
+ 126
+ 127
+ 128
+ 129
+ 130
+ 131
+ 132
+ 133
+ 134
+ 135
+ 136
+ 137
+ 138
+ 139
+ 140
+ 141
+ 142
+ 143
+ 144
+ 145
+ 146
+ 147
+ 148
+ 149
+ 150
+ 151
+ 152
+ 153
+ 154
+ 155
+ 156
+ 157
+ 158
+ 159
+ 160
+ 161
+ 162
+ 163
+ 164
+ 165
+ 166
+ 167
+ 168
+ 169
+ 170
+ 171
+ 172
+ 173
+ 174
+ 175
+ 176
+ 177
+ 178
+ 179
+ 180
+ 181
+ 182
+ 183
+ 184
+ 185
+ 186
+ 187
+ 188
+ 189
+ 190
+ 191
+ 192
+ 193
+ 194
+ 195
+ 196
+ 197
+ 198
+ 199
+ 200
+ 201
+ 202
+ 203
+ 204
+ 205
+ 206
+ 207
+ 208
+ 209
+ 210
+ 211
+ 212
+ 213
+ 214
+ 215
+ 216
+ 217
+ 218
+ 219
+ 220
+ 221
+ 222
+ 223
+ 224
+ 225
+ 226
+ 227
+ 228
+ 229
+ 230
+ 231
+ 232
+ 233
+ 234
+ 235
+ 236
+ 237
+ 238
+ 239
+ 240
+ 241
+ 242
+ 243
+ 244
+ 245
+ 246
+ 247
+ 248
+ 249
+ 250
+ 251
+ 252
+ 253
+ 254
+ 255
+ 256
+ 257
+ 258
+ 259
+ 260
+ 261
+ 262
+ 263
+ 264
+ 265
+ 266
+ 267
+ 268
+ 269
+ 270
+ 271
+ 272
+ 273
+ 274
+ 275
+ 276
+ 277
+ 278
+ 279
+ 280
+ 281
+ 282
+ 283
+ 284
+ 285
+ 286
+ 287
+ 288
+ 289
+ 290
+ 291
+ 292
+ 293
+ 294
+ 295
+ 296
+ 297
+ 298
+ 299
+ 300
+ 301
+ 302
+ 303
+ 304
+ 305
+ 306
+ 307
+ 308
+ 309
+ 310
+ 311
+ 312
+ 313
+ 314
+ 315
+ 316
+ 317
+ 318
+ 319
+ 320
+ 321
+ 322
+ 323
+ 324
+ 325
+ 326
+ 327
+ 328
+ 329
+ 330
+ 331
+ 332
+ 333
+ 334
+ 335
+ 336
+ 337
+ 338
+ 339
+ 340
+ 341
+ 342
+ 343
+ 344
+ 345
+ 346
+ 347
+ 348
+ 349
+ 350
+ 351
+ 352
+ 353
+ 354
+ 355
+ 356
+ 357
+ 358
+ 359
+ 360
+ 361
+ 362
+ 363
+ 364
+ 365
+ 366
+ 367
+ 368
+ 369
+ 370
+ 371
+ 372
+ 373
+ 374
+ 375
+ 376
+ 377
+ 378
+ 379
+ 380
+ 381
+ 382
+ 383
+ 384
+ 385
+ 386
+ 387
+ 388
+ 389
+ 390
+ 391
+ 392
+ 393
+ 394
+ 395
+ 396
+ 397
+ 398
+ 399
+ 400
+ 401
+ 402
+ 403
+ 404
+ 405
+ 406
+ 407
+ 408
+ 409
+ 410
+ 411
+ 412
+ 413
+ 414
+ 415
+ 416
+ 417
+ 418
+ 419
+ 420
+ 421
+ 422
+ 423
+ 424
+ 425
+ 426
+ 427
+ 428
+ 429
+ 430
+ 431
+ 432
+ 433
+ 434
+ 435
+ 436
+ 437
+ 438
+ 439
+ 440
+ 441
+ 442
+ 443
+ 444
+ 445
+ 446
+ 447
+ 448
+ 449
+ 450
+ 451
+ 452
+ 453
+ 454
+ 455
+ 456
+ 457
+ 458
+ 459
+ 460
+ 461
+ 462
+ 463
+ 464
+ 465
+ 466
+ 467
+ 468
+ 469
+ 470
+ 471
+ 472
+ 473
+ 474
+ 475
+ 476
+ 477
+ 478
+ 479
+ 480
+ 481
+ 482
+ 483
+ 484
+ 485
+ 486
+ 487
+ 488
+ 489
+ 490
+ 491
+ 492
+ 493
+ 494
+ 495
+ 496
+ 497
+ 498
+ 499
+ 500
+ 501
+ 502
+ 503
+ 504
+ 505
+ 506
+ 507
+ 508
+ 509
+ 510
+ 511
+ 512
+ 513
+ 514
+ 515
+ 516
+ 517
+ 518
+ 519
+ 520
+ 521
+ 522
+ 523
+ 524
+ 525
+ 526
+ 527
+ 528
+ 529
+ 530
+ 531
+ 532
+ 533
+ 534
+ 535
+ 536
+ 537
+ 538
+ 539
+ 540
+ 541
+ 542
+ 543
+ 544
+ 545
+ 546
+ 547
+ 548
+ 549
+ 550
+ 551
+ 552
+ 553
+ 554
+ 555
+ 556
+ 557
+ 558
+ 559
+ 560
+ 561
+ 562
+ 563
+ 564
+ 565
+ 566
+ 567
+ 568
+ 569
+ 570
+ 571
+ 572
+ 573
+ 574
+ 575
+ 576
+ 577
+ 578
+ 579
+ 580
+ 581
+ 582
+ 583
+ 584
+ 585
+ 586
+ 587
+ 588
+ 589
+ 590
+ 591
+ 592
+ 593
+ 594
+ 595
+ 596
+ 597
+ 598
+ 599
+ 600
+ 601
+ 602
+ 603
+ 604
+ 605
+ 606
+ 607
+ 608
+ 609
+ 610
+ 611
+ 612
+ 613
+ 614
+ 615
+ 616
+ 617
+ 618
+ 619
+ 620
+ 621
+ 622
+ 623
+ 624
+ 625
+ 626
+ 627
+ 628
+ 629
+ 630
+ 631
+ 632
+ 633
+ 634
+ 635
+ 636
+ 637
+ 638
+ 639
+ 640
+ 641
+ 642
+ 643
+ 644
+ 645
+ 646
+ 647
+ 648
+ 649
+ 650
+ 651
+ 652
+ 653
+ 654
+ 655
+ 656
+ 657
+ 658
+ 659
+ 660
+ 661
+ 662
+ 663
+ 664
+ 665
+ 666
+ 667
+ 668
+ 669
+ 670
+ 671
+ 672
+ 673
+ 674
+ 675
+ 676
+ 677
+ 678
+ 679
+ 680
+ 681
+ 682
+ 683
+ 684
+ 685
+ 686
+ 687
+ 688
+ 689
+ 690
+ 691
+ 692
+ 693
+ 694
+ 695
+ 696
+ 697
+ 698
+ 699
+ 700
+ 701
+ 702
+ 703
+ 704
+ 705
+ 706
+ 707
+ 708
+ 709
+ 710
+ 711
+ 712
+ 713
+ 714
+ 715
+ 716
+ 717
+ 718
+ 719
+ 720
+ 721
+ 722
+ 723
+ 724
+ 725
+ 726
+ 727
+ 728
+ 729
+ 730
+ 731
+ 732
+ 733
+ 734
+ 735
+ 736
+ 737
+ 738
+ 739
+ 740
+ 741
+ 742
+ 743
+ 744
+ 745
+ 746
+ 747
+ 748
+ 749
+ 750
+ 751
+ 752
+ 753
+ 754
+ 755
+ 756
+ 757
+ 758
+ 759
+ 760
+ 761
+ 762
+ 763
+ 764
+ 765
+ 766
+ 767
+ 768
+ 769
+ 770
+ 771
+ 772
+ 773
+ 774
+ 775
+ 776
+ 777
+ 778
+ 779
+ 780
+ 781
+ 782
+ 783
+ 784
+ 785
+ 786
+ 787
+ 788
+ 789
+ 790
+ 791
+ 792
+ 793
+ 794
+ 795
+ 796
+ 797
+ 798
+ 799
+ 800
+ 801
+ 802
+ 803
+ 804
+ 805
+ 806
+ 807
+ 808
+ 809
+ 810
+ 811
+ 812
+ 813
+ 814
+ 815
+ 816
+ 817
+ 818
+ 819
+ 820
+ 821
+ 822
+ 823
+ 824
+ 825
+ 826
+ 827
+ 828
+ 829
+ 830
+ 831
+ 832
+ 833
+ 834
+ 835
+ 836
+ 837
+ 838
+ 839
+ 840
+ 841
+ 842
+ 843
+ 844
+ 845
+ 846
+ 847
+ 848
+ 849
+ 850
+ 851
+ 852
+ 853
+ 854
+ 855
+ 856
+ 857
+ 858
+ 859
+ 860
+ 861
+ 862
+ 863
+ 864
+ 865
+ 866
+ 867
+ 868
+ 869
+ 870
+ 871
+ 872
+ 873
+ 874
+ 875
+ 876
+ 877
+ 878
+ 879
+ 880
+ 881
+ 882
+ 883
+ 884
+ 885
+ 886
+ 887
+ 888
+ 889
+ 890
+ 891
+ 892
+ 893
+ 894
+ 895
+ 896
+ 897
+ 898
+ 899
+ 900
+ 901
+ 902
+ 903
+ 904
+ 905
+ 906
+ 907
+ 908
+ 909
+ 910
+ 911
+ 912
+ 913
+ 914
+ 915
+ 916
+ 917
+ 918
+ 919
+ 920
+ 921
+ 922
+ 923
+ 924
+ 925
+ 926
+ 927
+ 928
+ 929
+ 930
+ 931
+ 932
+ 933
+ 934
+ 935
+ 936
+ 937
+ 938
+ 939
+ 940
+ 941
+ 942
+ 943
+ 944
+ 945
+ 946
+ 947
+ 948
+ 949
+ 950
+ 951
+ 952
+ 953
+ 954
+ 955
+ 956
+ 957
+ 958
+ 959
+ 960
+ 961
+ 962
+ 963
+ 964
+ 965
+ 966
+ 967
+ 968
+ 969
+ 970
+ 971
+ 972
+ 973
+ 974
+ 975
+ 976
+ 977
+ 978
+ 979
+ 980
+ 981
+ 982
+ 983
+ 984
+ 985
+ 986
+ 987
+ 988
+ 989
+ 990
+ 991
+ 992
+ 993
+ 994
+ 995
+ 996
+ 997
+ 998
+ 999
+1000
+1001
+1002
+1003
+1004
+1005
+1006
+1007
+1008
+1009
+1010
+1011
+1012
+1013
+1014
+1015
+1016
+1017
+1018
+1019
+1020
+1021
+1022
+1023
+1024
+1025
+1026
+1027
+1028
+1029
+1030
+1031
+1032
+1033
+1034
+1035
+1036
+1037
+1038
+1039
+1040
+1041
+1042
+1043
+1044
+1045
+1046
+1047
+1048
+1049
+1050
+1051
+1052
+1053
+1054
+1055
+1056
+1057
+1058
+1059
+1060
+1061
+1062
+1063
+1064
+1065
+1066
+1067
+1068
+1069
+1070
+1071
+1072
+1073
+1074
+1075
+1076
+1077
+1078
+1079
+1080
+1081
+1082
+1083
+1084
+1085
+1086
+1087
+1088
+1089
+1090
+1091
+1092
+1093
+1094
+1095
+1096
+1097
+1098
+1099
+1100
+1101
+1102
+1103
+1104
+1105
+1106
+1107
+1108
+1109
+1110
+1111
+1112
+1113
+1114
+1115
+1116
+1117
+1118
+1119
+1120
+1121
+1122
+1123
+1124
+1125
+1126
+1127
+1128
+1129
+1130
+1131
+1132
+1133
+1134
+1135
+1136
+1137
+1138
+1139
+1140
+1141
+1142
+1143
+1144
+1145
+1146
+1147
+1148
+1149
+1150
+1151
+1152
+1153
+1154
+1155
+1156
+1157
+1158
+1159
+1160
+1161
+1162
+1163
+1164
+1165
+1166
+1167
+1168
+1169
+1170
+1171
+1172
+1173
+1174
+1175
+1176
+1177
+1178
+1179
+1180
+1181
+1182
+1183
+1184
+1185
+1186
+1187
+1188
+1189
+1190
+1191
+1192
+1193
+1194
+1195
+1196
+1197
+1198
+1199
+1200
+1201
+1202
+1203
+1204
+1205
+1206
+1207
+1208
+1209
+1210
+1211
+1212
+1213
+1214
+1215
+1216
+1217
+1218
+1219
+1220
+1221
+1222
+1223
+1224
+1225
+1226
+1227
+1228
+1229
/*
+ * Copyright (C) 2002 - 2004 Red Hat, Inc.
+ * Copyright (C) 2012-2021 MATE Developers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/* used for realpath() */
+#define _XOPEN_SOURCE 500
+
+#include <config.h>
+
+#include "entry-directories.h"
+
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <stdlib.h>
+
+#include "menu-util.h"
+#include "menu-monitor.h"
+
+typedef struct CachedDir CachedDir;
+typedef struct CachedDirMonitor CachedDirMonitor;
+
+struct EntryDirectory {
+	CachedDir* dir;
+	char* legacy_prefix;
+
+	guint entry_type: 2;
+	guint is_legacy: 1;
+	guint refcount: 24;
+};
+
+struct EntryDirectoryList {
+	int refcount;
+	int length;
+	GList* dirs;
+};
+
+struct CachedDir {
+	CachedDir* parent;
+	char* name;
+
+	GSList* entries;
+	GSList* subdirs;
+
+	MenuMonitor* dir_monitor;
+	GSList* monitors;
+
+	guint have_read_entries: 1;
+	guint deleted: 1;
+
+  guint references;
+
+  GFunc    notify;
+  gpointer notify_data;
+};
+
+struct CachedDirMonitor {
+	EntryDirectory* ed;
+	EntryDirectoryChangedFunc callback;
+	gpointer user_data;
+};
+
+static void     cached_dir_add_reference          (CachedDir *dir);
+static void     cached_dir_remove_reference       (CachedDir *dir);
+static void     cached_dir_free                   (CachedDir  *dir);
+static gboolean cached_dir_load_entries_recursive (CachedDir  *dir,
+                                                   const char *dirname);
+static void     cached_dir_unref                  (CachedDir *dir);
+static void     cached_dir_unref_noparent         (CachedDir *dir);
+static CachedDir * cached_dir_add_subdir          (CachedDir  *dir,
+                                                   const char *basename,
+                                                   const char *path);
+static gboolean cached_dir_remove_subdir          (CachedDir  *dir,
+                                                   const char *basename);
+
+static void handle_cached_dir_changed (MenuMonitor      *monitor,
+				       MenuMonitorEvent  event,
+				       const char       *path,
+				       CachedDir        *dir);
+
+/*
+ * Entry directory cache
+ */
+
+static CachedDir* dir_cache = NULL;
+
+static void
+clear_cache (CachedDir *dir,
+             gpointer  *cache)
+{
+  *cache = NULL;
+}
+
+static CachedDir *
+cached_dir_new (const char *name)
+{
+	CachedDir* dir;
+
+  dir = g_new0 (CachedDir, 1);
+  dir->name = g_strdup (name);
+
+	return dir;
+}
+
+static CachedDir *
+cached_dir_new_full (const char *name,
+                     GFunc       notify,
+                     gpointer    notify_data)
+{
+  CachedDir *dir;
+
+  dir = cached_dir_new (name);
+
+  dir->notify = notify;
+  dir->notify_data = notify_data;
+
+  return dir;
+}
+
+static void
+cached_dir_free (CachedDir *dir)
+{
+  if (dir->dir_monitor)
+    {
+      menu_monitor_remove_notify (dir->dir_monitor,
+				  (MenuMonitorNotifyFunc) handle_cached_dir_changed,
+				  dir);
+      menu_monitor_unref (dir->dir_monitor);
+      dir->dir_monitor = NULL;
+    }
+
+  g_slist_foreach (dir->monitors, (GFunc) g_free, NULL);
+  g_slist_free (dir->monitors);
+  dir->monitors = NULL;
+
+  g_slist_foreach (dir->entries,
+                   (GFunc) desktop_entry_unref,
+                   NULL);
+  g_slist_free (dir->entries);
+  dir->entries = NULL;
+
+  g_slist_foreach (dir->subdirs,
+                   (GFunc) cached_dir_unref_noparent,
+                   NULL);
+  g_slist_free (dir->subdirs);
+  dir->subdirs = NULL;
+
+  g_free (dir->name);
+  g_free (dir);
+}
+
+static CachedDir *
+cached_dir_ref (CachedDir *dir)
+{
+  dir->references++;
+  return dir;
+}
+
+static void
+cached_dir_unref (CachedDir *dir)
+{
+  if (--dir->references == 0)
+    {
+      CachedDir *parent;
+
+      parent = dir->parent;
+
+      if (parent != NULL)
+        cached_dir_remove_subdir (parent, dir->name);
+
+      if (dir->notify)
+        dir->notify (dir, dir->notify_data);
+
+      cached_dir_free (dir);
+    }
+}
+
+static void
+cached_dir_unref_noparent (CachedDir *dir)
+{
+  if (--dir->references == 0)
+    {
+      if (dir->notify)
+        dir->notify (dir, dir->notify_data);
+
+      cached_dir_free (dir);
+    }
+}
+
+static inline CachedDir* find_subdir(CachedDir* dir, const char* subdir)
+{
+  GSList *tmp;
+
+  tmp = dir->subdirs;
+  while (tmp != NULL)
+    {
+      CachedDir *sub = tmp->data;
+
+      if (strcmp (sub->name, subdir) == 0)
+        return sub;
+
+      tmp = tmp->next;
+    }
+
+  return NULL;
+}
+
+static DesktopEntry* find_entry(CachedDir* dir, const char* basename)
+{
+  GSList *tmp;
+
+  tmp = dir->entries;
+  while (tmp != NULL)
+    {
+      if (strcmp (desktop_entry_get_basename (tmp->data), basename) == 0)
+        return tmp->data;
+
+      tmp = tmp->next;
+    }
+
+  return NULL;
+}
+
+static DesktopEntry* cached_dir_find_relative_path(CachedDir* dir, const char* relative_path)
+{
+  DesktopEntry  *retval = NULL;
+  char         **split;
+  int            i;
+
+  split = g_strsplit (relative_path, "/", -1);
+
+  i = 0;
+  while (split[i] != NULL)
+    {
+      if (split[i + 1] != NULL)
+        {
+          if ((dir = find_subdir (dir, split[i])) == NULL)
+            break;
+        }
+      else
+        {
+          retval = find_entry (dir, split[i]);
+          break;
+        }
+
+      ++i;
+    }
+
+  g_strfreev (split);
+
+  return retval;
+}
+
+static CachedDir* cached_dir_lookup(const char* canonical)
+{
+  CachedDir  *dir;
+  char      **split;
+  int         i;
+
+  if (dir_cache == NULL)
+    dir_cache = cached_dir_new_full ("/",
+                                     (GFunc) clear_cache,
+                                     &dir_cache);
+  dir = dir_cache;
+
+  g_assert (canonical != NULL && canonical[0] == G_DIR_SEPARATOR);
+
+  menu_verbose ("Looking up cached dir \"%s\"\n", canonical);
+
+  split = g_strsplit (canonical + 1, "/", -1);
+
+  i = 0;
+  while (split[i] != NULL)
+    {
+      CachedDir *subdir;
+
+      subdir = cached_dir_add_subdir (dir, split[i], NULL);
+
+      dir = subdir;
+
+      ++i;
+    }
+
+  g_strfreev (split);
+
+  g_assert (dir != NULL);
+
+  return dir;
+}
+
+static gboolean cached_dir_add_entry(CachedDir* dir, const char* basename, const char* path)
+{
+  DesktopEntry *entry;
+
+  entry = desktop_entry_new (path);
+  if (entry == NULL)
+    return FALSE;
+
+  dir->entries = g_slist_prepend (dir->entries, entry);
+
+  return TRUE;
+}
+
+static gboolean cached_dir_update_entry(CachedDir* dir, const char* basename, const char* path)
+{
+  GSList *tmp;
+
+  tmp = dir->entries;
+  while (tmp != NULL)
+    {
+      if (strcmp (desktop_entry_get_basename (tmp->data), basename) == 0)
+        {
+          if (!desktop_entry_reload (tmp->data))
+	    {
+	      dir->entries = g_slist_delete_link (dir->entries, tmp);
+	    }
+
+          return TRUE;
+        }
+
+      tmp = tmp->next;
+    }
+
+  return cached_dir_add_entry (dir, basename, path);
+}
+
+static gboolean cached_dir_remove_entry(CachedDir* dir, const char* basename)
+{
+  GSList *tmp;
+
+  tmp = dir->entries;
+  while (tmp != NULL)
+    {
+      if (strcmp (desktop_entry_get_basename (tmp->data), basename) == 0)
+        {
+          desktop_entry_unref (tmp->data);
+          dir->entries = g_slist_delete_link (dir->entries, tmp);
+          return TRUE;
+        }
+
+      tmp = tmp->next;
+    }
+
+  return FALSE;
+}
+
+static CachedDir *
+cached_dir_add_subdir (CachedDir  *dir,
+                       const char *basename,
+                       const char *path)
+{
+  CachedDir *subdir;
+
+  subdir = find_subdir (dir, basename);
+
+  if (subdir != NULL)
+    {
+      subdir->deleted = FALSE;
+      return subdir;
+    }
+
+  subdir = cached_dir_new (basename);
+
+  if (path != NULL && !cached_dir_load_entries_recursive (subdir, path))
+    {
+      cached_dir_free (subdir);
+      return NULL;
+    }
+
+  menu_verbose ("Caching dir \"%s\"\n", basename);
+
+  subdir->parent = dir;
+  dir->subdirs = g_slist_prepend (dir->subdirs, cached_dir_ref (subdir));
+
+  return subdir;
+}
+
+static gboolean cached_dir_remove_subdir(CachedDir* dir, const char* basename)
+{
+  CachedDir *subdir;
+
+  subdir = find_subdir (dir, basename);
+
+  if (subdir != NULL)
+    {
+      subdir->deleted = TRUE;
+
+      dir->subdirs = g_slist_remove (dir->subdirs, subdir);
+      cached_dir_unref (subdir);
+
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+static guint   monitors_idle_handler = 0;
+static GSList *pending_monitors_dirs = NULL;
+
+static void
+cached_dir_invoke_monitors (CachedDir *dir)
+{
+  GSList *tmp;
+
+  tmp = dir->monitors;
+  while (tmp != NULL)
+    {
+      CachedDirMonitor *monitor = tmp->data;
+      GSList           *next    = tmp->next;
+
+      monitor->callback (monitor->ed, monitor->user_data);
+
+      tmp = next;
+    }
+
+  /* we explicitly don't invoke monitors of the parent since an
+   * event has been queued for it too */
+}
+
+static gboolean
+emit_monitors_in_idle (void)
+{
+  GSList *monitors_to_emit;
+  GSList *tmp;
+
+  monitors_to_emit = pending_monitors_dirs;
+
+  pending_monitors_dirs = NULL;
+  monitors_idle_handler = 0;
+
+  tmp = monitors_to_emit;
+  while (tmp != NULL)
+    {
+      CachedDir *dir = tmp->data;
+
+      cached_dir_invoke_monitors (dir);
+      cached_dir_remove_reference (dir);
+
+      tmp = tmp->next;
+    }
+
+  g_slist_free (monitors_to_emit);
+
+  return FALSE;
+}
+
+static void
+cached_dir_queue_monitor_event (CachedDir *dir)
+{
+  GSList *tmp;
+
+  tmp = pending_monitors_dirs;
+  while (tmp != NULL)
+    {
+      CachedDir *d    = tmp->data;
+      GSList    *next = tmp->next;
+
+      if (dir->parent == d->parent &&
+          g_strcmp0 (dir->name, d->name) == 0)
+        break;
+
+      tmp = next;
+    }
+
+  /* not found, so let's queue it */
+  if (tmp == NULL)
+    {
+      cached_dir_add_reference (dir);
+      pending_monitors_dirs = g_slist_append (pending_monitors_dirs, dir);
+    }
+
+  if (dir->parent)
+    {
+      cached_dir_queue_monitor_event (dir->parent);
+    }
+
+  if (monitors_idle_handler == 0)
+    {
+      monitors_idle_handler = g_idle_add ((GSourceFunc) emit_monitors_in_idle, NULL);
+    }
+}
+
+static void handle_cached_dir_changed (MenuMonitor* monitor, MenuMonitorEvent event, const char* path, CachedDir* dir)
+{
+  gboolean  handled = FALSE;
+  char     *basename;
+  char     *dirname;
+
+  dirname  = g_path_get_dirname  (path);
+  basename = g_path_get_basename (path);
+
+  dir = cached_dir_lookup (dirname);
+  cached_dir_add_reference (dir);
+
+  if (g_str_has_suffix (basename, ".desktop") ||
+      g_str_has_suffix (basename, ".directory"))
+    {
+      switch (event)
+        {
+        case MENU_MONITOR_EVENT_CREATED:
+        case MENU_MONITOR_EVENT_CHANGED:
+          handled = cached_dir_update_entry (dir, basename, path);
+          break;
+
+        case MENU_MONITOR_EVENT_DELETED:
+          handled = cached_dir_remove_entry (dir, basename);
+          break;
+
+        default:
+          g_assert_not_reached ();
+          break;
+        }
+    }
+  else if (g_file_test (path, G_FILE_TEST_IS_DIR)) /* Try recursing */
+    {
+      switch (event)
+        {
+        case MENU_MONITOR_EVENT_CREATED:
+          handled = cached_dir_add_subdir (dir, basename, path) != NULL;
+          break;
+
+        case MENU_MONITOR_EVENT_CHANGED:
+          break;
+
+        case MENU_MONITOR_EVENT_DELETED:
+          handled = cached_dir_remove_subdir (dir, basename);
+          break;
+
+        default:
+          g_assert_not_reached ();
+          break;
+        }
+    }
+
+  g_free (basename);
+  g_free (dirname);
+
+  if (handled)
+    {
+      menu_verbose ("'%s' notified of '%s' %s - invalidating cache\n",
+                    dir->name,
+                    path,
+                    event == MENU_MONITOR_EVENT_CREATED ? ("created") :
+                    event == MENU_MONITOR_EVENT_DELETED ? ("deleted") : ("changed"));
+
+      /* CHANGED events don't change the set of desktop entries */
+      if (event == MENU_MONITOR_EVENT_CREATED || event == MENU_MONITOR_EVENT_DELETED)
+        {
+          _entry_directory_list_empty_desktop_cache ();
+        }
+
+      cached_dir_queue_monitor_event (dir);
+    }
+
+  cached_dir_remove_reference (dir);
+}
+
+static void cached_dir_ensure_monitor(CachedDir* dir, const char* dirname)
+{
+  if (dir->dir_monitor == NULL)
+    {
+      dir->dir_monitor = menu_get_directory_monitor (dirname);
+      menu_monitor_add_notify (dir->dir_monitor,
+			       (MenuMonitorNotifyFunc) handle_cached_dir_changed,
+			       dir);
+    }
+}
+
+static gboolean cached_dir_load_entries_recursive(CachedDir* dir, const char* dirname)
+{
+  DIR           *dp;
+  struct dirent *dent;
+  GString       *fullpath;
+  gsize          fullpath_len;
+
+  g_assert (dir != NULL);
+
+  if (dir->have_read_entries)
+    return TRUE;
+
+  menu_verbose ("Attempting to read entries from %s (full path %s)\n",
+                dir->name, dirname);
+
+  dp = opendir (dirname);
+  if (dp == NULL)
+    {
+      menu_verbose ("Unable to list directory \"%s\"\n",
+                    dirname);
+      return FALSE;
+    }
+
+  cached_dir_ensure_monitor (dir, dirname);
+
+  fullpath = g_string_new (dirname);
+  if (fullpath->str[fullpath->len - 1] != G_DIR_SEPARATOR)
+    g_string_append_c (fullpath, G_DIR_SEPARATOR);
+
+  fullpath_len = fullpath->len;
+
+  while ((dent = readdir (dp)) != NULL)
+    {
+      /* ignore . and .. */
+      if (dent->d_name[0] == '.' &&
+          (dent->d_name[1] == '\0' ||
+           (dent->d_name[1] == '.' &&
+            dent->d_name[2] == '\0')))
+        continue;
+
+      g_string_append (fullpath, dent->d_name);
+
+      if (g_str_has_suffix (dent->d_name, ".desktop") ||
+          g_str_has_suffix (dent->d_name, ".directory"))
+        {
+          cached_dir_add_entry (dir, dent->d_name, fullpath->str);
+        }
+      else /* Try recursing */
+        {
+          cached_dir_add_subdir (dir, dent->d_name, fullpath->str);
+        }
+
+      g_string_truncate (fullpath, fullpath_len);
+    }
+
+  closedir (dp);
+
+  g_string_free (fullpath, TRUE);
+
+  dir->have_read_entries = TRUE;
+
+  return TRUE;
+}
+
+static void cached_dir_add_monitor(CachedDir* dir, EntryDirectory* ed, EntryDirectoryChangedFunc callback, gpointer user_data)
+{
+  CachedDirMonitor *monitor;
+  GSList           *tmp;
+
+  tmp = dir->monitors;
+  while (tmp != NULL)
+    {
+      monitor = tmp->data;
+
+      if (monitor->ed == ed &&
+          monitor->callback == callback &&
+          monitor->user_data == user_data)
+        break;
+
+      tmp = tmp->next;
+    }
+
+  if (tmp == NULL)
+    {
+      monitor            = g_new0 (CachedDirMonitor, 1);
+      monitor->ed        = ed;
+      monitor->callback  = callback;
+      monitor->user_data = user_data;
+
+      dir->monitors = g_slist_append (dir->monitors, monitor);
+    }
+}
+
+static void cached_dir_remove_monitor(CachedDir* dir, EntryDirectory* ed, EntryDirectoryChangedFunc callback, gpointer user_data)<--- Parameter 'ed' can be declared as pointer to const<--- Parameter 'callback' can be declared as pointer to const
+{
+  GSList *tmp;
+
+  tmp = dir->monitors;
+  while (tmp != NULL)
+    {
+      CachedDirMonitor *monitor = tmp->data;
+      GSList           *next = tmp->next;
+
+      if (monitor->ed == ed &&
+          monitor->callback == callback &&
+          monitor->user_data == user_data)
+        {
+          dir->monitors = g_slist_delete_link (dir->monitors, tmp);
+          g_free (monitor);
+        }
+
+      tmp = next;
+    }
+}
+
+static void cached_dir_add_reference(CachedDir* dir)
+{
+  cached_dir_ref (dir);
+
+  if (dir->parent != NULL)
+    {
+      cached_dir_add_reference (dir->parent);
+    }
+}
+
+static void cached_dir_remove_reference(CachedDir* dir)
+{
+  CachedDir *parent;
+
+  parent = dir->parent;
+
+  cached_dir_unref (dir);
+
+  if (parent != NULL)
+    {
+      cached_dir_remove_reference (parent);
+    }
+}
+
+/*
+ * Entry directories
+ */
+
+static EntryDirectory* entry_directory_new_full(DesktopEntryType entry_type, const char* path, gboolean is_legacy, const char* legacy_prefix)
+{
+  EntryDirectory *ed;
+  char           *canonical;
+
+  menu_verbose ("Loading entry directory \"%s\" (legacy %s)\n",
+                path,
+                is_legacy ? "<yes>" : "<no>");
+
+  canonical = realpath (path, NULL);
+  if (canonical == NULL)
+    {
+      menu_verbose ("Failed to canonicalize \"%s\": %s\n",
+                    path, g_strerror (errno));
+      return NULL;
+    }
+
+  ed = g_new0 (EntryDirectory, 1);
+
+  ed->dir = cached_dir_lookup (canonical);
+  g_assert (ed->dir != NULL);
+
+  cached_dir_add_reference (ed->dir);
+  cached_dir_load_entries_recursive (ed->dir, canonical);
+
+  ed->legacy_prefix = g_strdup (legacy_prefix);
+  ed->entry_type    = entry_type;
+  ed->is_legacy     = is_legacy != FALSE;
+  ed->refcount      = 1;
+
+  g_free (canonical);
+
+  return ed;
+}
+
+EntryDirectory* entry_directory_new(DesktopEntryType entry_type, const char* path)
+{
+	return entry_directory_new_full (entry_type, path, FALSE, NULL);
+}
+
+EntryDirectory* entry_directory_new_legacy(DesktopEntryType entry_type, const char* path, const char* legacy_prefix)
+{
+	return entry_directory_new_full(entry_type, path, TRUE, legacy_prefix);
+}
+
+EntryDirectory* entry_directory_ref(EntryDirectory* ed)
+{
+	g_return_val_if_fail(ed != NULL, NULL);
+	g_return_val_if_fail(ed->refcount > 0, NULL);
+
+	ed->refcount++;
+
+	return ed;
+}
+
+void entry_directory_unref(EntryDirectory* ed)
+{
+  g_return_if_fail (ed != NULL);
+  g_return_if_fail (ed->refcount > 0);
+
+  if (--ed->refcount == 0)
+    {
+      cached_dir_remove_reference (ed->dir);
+
+      ed->dir        = NULL;
+      ed->entry_type = DESKTOP_ENTRY_INVALID;
+      ed->is_legacy  = FALSE;
+
+      g_free (ed->legacy_prefix);
+      ed->legacy_prefix = NULL;
+
+      g_free (ed);
+    }
+}
+
+static void entry_directory_add_monitor(EntryDirectory* ed, EntryDirectoryChangedFunc callback, gpointer user_data)
+{
+  cached_dir_add_monitor (ed->dir, ed, callback, user_data);
+}
+
+static void entry_directory_remove_monitor(EntryDirectory* ed, EntryDirectoryChangedFunc callback, gpointer user_data)
+{
+  cached_dir_remove_monitor (ed->dir, ed, callback, user_data);
+}
+
+static DesktopEntry* entry_directory_get_directory(EntryDirectory* ed, const char* relative_path)
+{
+  DesktopEntry *entry;
+
+  if (ed->entry_type != DESKTOP_ENTRY_DIRECTORY)
+    return NULL;
+
+  entry = cached_dir_find_relative_path (ed->dir, relative_path);
+  if (entry == NULL || desktop_entry_get_type (entry) != DESKTOP_ENTRY_DIRECTORY)
+    return NULL;
+
+  return desktop_entry_ref (entry);
+}
+
+static char* get_desktop_file_id_from_path(EntryDirectory* ed, DesktopEntryType  entry_type, const char* relative_path)
+{
+  char *retval;
+
+  retval = NULL;
+
+  if (entry_type == DESKTOP_ENTRY_DESKTOP)
+    {
+      if (!ed->is_legacy)
+	{
+	  retval = g_strdelimit (g_strdup (relative_path), "/", '-');
+	}
+      else
+	{
+	  char *basename;
+
+	  basename = g_path_get_basename (relative_path);
+
+	  if (ed->legacy_prefix)
+	    {
+	      retval = g_strjoin ("-", ed->legacy_prefix, basename, NULL);
+	      g_free (basename);
+	    }
+	  else
+	    {
+	      retval = basename;
+	    }
+	}
+    }
+  else
+    {
+      retval = g_strdup (relative_path);
+    }
+
+  return retval;
+}
+
+typedef gboolean (*EntryDirectoryForeachFunc) (EntryDirectory* ed, DesktopEntry* entry, const char* file_id, DesktopEntrySet* set, gpointer user_data);
+
+static gboolean entry_directory_foreach_recursive(EntryDirectory* ed, CachedDir* cd, GString* relative_path, EntryDirectoryForeachFunc func, DesktopEntrySet* set, gpointer user_data)
+{
+  GSList *tmp;
+  gsize   relative_path_len;
+
+  if (cd->deleted)
+    return TRUE;
+
+  relative_path_len = relative_path->len;
+
+  tmp = cd->entries;
+  while (tmp != NULL)
+    {
+      DesktopEntry *entry = tmp->data;
+
+      if (desktop_entry_get_type (entry) == ed->entry_type)
+        {
+          gboolean  ret;
+          char     *file_id;
+
+          g_string_append (relative_path,
+                           desktop_entry_get_basename (entry));
+
+	  file_id = get_desktop_file_id_from_path (ed,
+						   ed->entry_type,
+						   relative_path->str);
+
+          ret = func (ed, entry, file_id, set, user_data);
+
+          g_free (file_id);
+
+          g_string_truncate (relative_path, relative_path_len);
+
+          if (!ret)
+            return FALSE;
+        }
+
+      tmp = tmp->next;
+    }
+
+  tmp = cd->subdirs;
+  while (tmp != NULL)
+    {
+      CachedDir *subdir = tmp->data;
+
+      g_string_append (relative_path, subdir->name);
+      g_string_append_c (relative_path, G_DIR_SEPARATOR);
+
+      if (!entry_directory_foreach_recursive (ed,
+                                              subdir,
+                                              relative_path,
+                                              func,
+                                              set,
+                                              user_data))
+        return FALSE;
+
+      g_string_truncate (relative_path, relative_path_len);
+
+      tmp = tmp->next;
+    }
+
+  return TRUE;
+}
+
+static void entry_directory_foreach(EntryDirectory* ed, EntryDirectoryForeachFunc func, DesktopEntrySet* set, gpointer user_data)
+{
+  GString *path;
+
+  path = g_string_new (NULL);
+
+  entry_directory_foreach_recursive (ed,
+                                     ed->dir,
+                                     path,
+                                     func,
+                                     set,
+                                     user_data);
+
+  g_string_free (path, TRUE);
+}
+
+void entry_directory_get_flat_contents(EntryDirectory* ed, DesktopEntrySet* desktop_entries, DesktopEntrySet* directory_entries, GSList** subdirs)
+{
+  GSList *tmp;
+
+  if (subdirs)
+    *subdirs = NULL;
+
+  tmp = ed->dir->entries;
+  while (tmp != NULL)
+    {
+      DesktopEntry *entry = tmp->data;
+      const char   *basename;
+
+      basename = desktop_entry_get_basename (entry);
+
+      if (desktop_entries &&
+          desktop_entry_get_type (entry) == DESKTOP_ENTRY_DESKTOP)
+        {
+          char *file_id;
+
+          file_id = get_desktop_file_id_from_path (ed,
+						   DESKTOP_ENTRY_DESKTOP,
+						   basename);
+
+          desktop_entry_set_add_entry (desktop_entries,
+                                       entry,
+                                       file_id);
+
+          g_free (file_id);
+        }
+
+      if (directory_entries &&
+          desktop_entry_get_type (entry) == DESKTOP_ENTRY_DIRECTORY)
+        {
+          desktop_entry_set_add_entry (directory_entries,
+				       entry,
+				       basename);
+        }
+
+      tmp = tmp->next;
+    }
+
+  if (subdirs)
+    {
+      tmp = ed->dir->subdirs;
+      while (tmp != NULL)
+        {
+          CachedDir *cd = tmp->data;
+
+	  if (!cd->deleted)
+	    {
+	      *subdirs = g_slist_prepend (*subdirs, g_strdup (cd->name));
+	    }
+
+          tmp = tmp->next;
+        }
+    }
+
+  if (subdirs)
+    *subdirs = g_slist_reverse (*subdirs);
+}
+
+/*
+ * Entry directory lists
+ */
+
+EntryDirectoryList* entry_directory_list_new(void)
+{
+  EntryDirectoryList *list;
+
+  list = g_new0 (EntryDirectoryList, 1);
+
+  list->refcount = 1;
+  list->dirs = NULL;
+  list->length = 0;
+
+  return list;
+}
+
+EntryDirectoryList* entry_directory_list_ref(EntryDirectoryList* list)
+{
+  g_return_val_if_fail (list != NULL, NULL);
+  g_return_val_if_fail (list->refcount > 0, NULL);
+
+  list->refcount += 1;
+
+  return list;
+}
+
+void entry_directory_list_unref(EntryDirectoryList* list)
+{
+  g_return_if_fail (list != NULL);
+  g_return_if_fail (list->refcount > 0);
+
+  list->refcount -= 1;
+  if (list->refcount == 0)
+    {
+      g_list_foreach (list->dirs, (GFunc) entry_directory_unref, NULL);
+      g_list_free (list->dirs);
+      list->dirs = NULL;
+      list->length = 0;
+      g_free (list);
+    }
+}
+
+void entry_directory_list_prepend(EntryDirectoryList* list, EntryDirectory* ed)
+{
+  list->length += 1;
+  list->dirs = g_list_prepend (list->dirs,
+                               entry_directory_ref (ed));
+}
+
+int entry_directory_list_get_length(EntryDirectoryList* list)
+{
+  return list->length;
+}
+
+void entry_directory_list_append_list(EntryDirectoryList* list, EntryDirectoryList* to_append)
+{
+  GList *tmp;
+  GList *new_dirs = NULL;
+
+  if (to_append->length == 0)
+    return;
+
+  tmp = to_append->dirs;
+  while (tmp != NULL)
+    {
+      list->length += 1;
+      new_dirs = g_list_prepend (new_dirs,
+                                 entry_directory_ref (tmp->data));
+
+      tmp = tmp->next;
+    }
+
+  new_dirs   = g_list_reverse (new_dirs);
+  list->dirs = g_list_concat (list->dirs, new_dirs);
+}
+
+DesktopEntry* entry_directory_list_get_directory(EntryDirectoryList *list, const char* relative_path)
+{
+  DesktopEntry *retval = NULL;
+  GList        *tmp;
+
+  tmp = list->dirs;
+  while (tmp != NULL)
+    {
+      if ((retval = entry_directory_get_directory (tmp->data, relative_path)) != NULL)
+        break;
+
+      tmp = tmp->next;
+    }
+
+  return retval;
+}
+
+gboolean _entry_directory_list_compare(const EntryDirectoryList* a, const EntryDirectoryList* b)
+{
+  GList *al, *bl;
+
+  if (a == NULL && b == NULL)
+    return TRUE;
+
+  if ((a == NULL || b == NULL))
+    return FALSE;
+
+  if (a->length != b->length)
+    return FALSE;
+
+  al = a->dirs; bl = b->dirs;
+  while (al && bl && al->data == bl->data)
+    {
+      al = al->next;
+      bl = bl->next;
+    }
+
+  return (al == NULL && bl == NULL);
+}
+
+static gboolean get_all_func(EntryDirectory* ed, DesktopEntry* entry, const char* file_id, DesktopEntrySet* set, gpointer user_data)
+{
+  if (ed->is_legacy && !desktop_entry_has_categories (entry))
+    {
+      entry = desktop_entry_copy (entry);
+      desktop_entry_add_legacy_category (entry);
+    }
+  else
+    {
+      entry = desktop_entry_ref (entry);
+    }
+
+  desktop_entry_set_add_entry (set, entry, file_id);
+  desktop_entry_unref (entry);
+
+  return TRUE;
+}
+
+static DesktopEntrySet* entry_directory_last_set = NULL;
+static EntryDirectoryList* entry_directory_last_list = NULL;
+
+void _entry_directory_list_empty_desktop_cache(void)
+{
+  if (entry_directory_last_set != NULL)
+    desktop_entry_set_unref (entry_directory_last_set);
+  entry_directory_last_set = NULL;
+
+  if (entry_directory_last_list != NULL)
+    entry_directory_list_unref (entry_directory_last_list);
+  entry_directory_last_list = NULL;
+}
+
+DesktopEntrySet* _entry_directory_list_get_all_desktops(EntryDirectoryList* list)
+{
+  GList *tmp;
+  DesktopEntrySet *set;
+
+  /* The only tricky thing here is that desktop files later
+   * in the search list with the same relative path
+   * are "hidden" by desktop files earlier in the path,
+   * so we have to do the earlier files first causing
+   * the later files to replace the earlier files
+   * in the DesktopEntrySet
+   *
+   * We go from the end of the list so we can just
+   * g_hash_table_replace and not have to do two
+   * hash lookups (check for existing entry, then insert new
+   * entry)
+   */
+
+  /* This method is -extremely- slow, so we have a simple
+     one-entry cache here */
+  if (_entry_directory_list_compare (list, entry_directory_last_list))
+    {
+      menu_verbose (" Hit desktop list (%p) cache\n", list);
+      return desktop_entry_set_ref (entry_directory_last_set);
+    }
+
+  if (entry_directory_last_set != NULL)
+    desktop_entry_set_unref (entry_directory_last_set);
+  if (entry_directory_last_list != NULL)
+    entry_directory_list_unref (entry_directory_last_list);
+
+  set = desktop_entry_set_new ();
+  menu_verbose (" Storing all of list %p in set %p\n",
+                list, set);
+
+  tmp = g_list_last (list->dirs);
+  while (tmp != NULL)
+    {
+      entry_directory_foreach (tmp->data, get_all_func, set, NULL);
+
+      tmp = tmp->prev;
+    }
+
+  entry_directory_last_list = entry_directory_list_ref (list);
+  entry_directory_last_set = desktop_entry_set_ref (set);
+
+  return set;
+}
+
+void entry_directory_list_add_monitors(EntryDirectoryList* list, EntryDirectoryChangedFunc callback, gpointer user_data)
+{
+  GList *tmp;
+
+  tmp = list->dirs;
+  while (tmp != NULL)
+    {
+      entry_directory_add_monitor (tmp->data, callback, user_data);
+      tmp = tmp->next;
+    }
+}
+
+void entry_directory_list_remove_monitors(EntryDirectoryList* list, EntryDirectoryChangedFunc callback, gpointer user_data)
+{
+  GList *tmp;
+
+  tmp = list->dirs;
+  while (tmp != NULL)
+    {
+      entry_directory_remove_monitor (tmp->data, callback, user_data);
+      tmp = tmp->next;
+    }
+}
+
+ +
+ +
+ + diff --git a/2023-09-02-151726-1231-cppcheck@7aac87a9810a_master/1.html b/2023-09-02-151726-1231-cppcheck@7aac87a9810a_master/1.html new file mode 100644 index 0000000..6db698f --- /dev/null +++ b/2023-09-02-151726-1231-cppcheck@7aac87a9810a_master/1.html @@ -0,0 +1,10409 @@ + + + + + + Cppcheck - HTML report - mate-menus + + + + + +
+ + + +
+
   1
+   2
+   3
+   4
+   5
+   6
+   7
+   8
+   9
+  10
+  11
+  12
+  13
+  14
+  15
+  16
+  17
+  18
+  19
+  20
+  21
+  22
+  23
+  24
+  25
+  26
+  27
+  28
+  29
+  30
+  31
+  32
+  33
+  34
+  35
+  36
+  37
+  38
+  39
+  40
+  41
+  42
+  43
+  44
+  45
+  46
+  47
+  48
+  49
+  50
+  51
+  52
+  53
+  54
+  55
+  56
+  57
+  58
+  59
+  60
+  61
+  62
+  63
+  64
+  65
+  66
+  67
+  68
+  69
+  70
+  71
+  72
+  73
+  74
+  75
+  76
+  77
+  78
+  79
+  80
+  81
+  82
+  83
+  84
+  85
+  86
+  87
+  88
+  89
+  90
+  91
+  92
+  93
+  94
+  95
+  96
+  97
+  98
+  99
+ 100
+ 101
+ 102
+ 103
+ 104
+ 105
+ 106
+ 107
+ 108
+ 109
+ 110
+ 111
+ 112
+ 113
+ 114
+ 115
+ 116
+ 117
+ 118
+ 119
+ 120
+ 121
+ 122
+ 123
+ 124
+ 125
+ 126
+ 127
+ 128
+ 129
+ 130
+ 131
+ 132
+ 133
+ 134
+ 135
+ 136
+ 137
+ 138
+ 139
+ 140
+ 141
+ 142
+ 143
+ 144
+ 145
+ 146
+ 147
+ 148
+ 149
+ 150
+ 151
+ 152
+ 153
+ 154
+ 155
+ 156
+ 157
+ 158
+ 159
+ 160
+ 161
+ 162
+ 163
+ 164
+ 165
+ 166
+ 167
+ 168
+ 169
+ 170
+ 171
+ 172
+ 173
+ 174
+ 175
+ 176
+ 177
+ 178
+ 179
+ 180
+ 181
+ 182
+ 183
+ 184
+ 185
+ 186
+ 187
+ 188
+ 189
+ 190
+ 191
+ 192
+ 193
+ 194
+ 195
+ 196
+ 197
+ 198
+ 199
+ 200
+ 201
+ 202
+ 203
+ 204
+ 205
+ 206
+ 207
+ 208
+ 209
+ 210
+ 211
+ 212
+ 213
+ 214
+ 215
+ 216
+ 217
+ 218
+ 219
+ 220
+ 221
+ 222
+ 223
+ 224
+ 225
+ 226
+ 227
+ 228
+ 229
+ 230
+ 231
+ 232
+ 233
+ 234
+ 235
+ 236
+ 237
+ 238
+ 239
+ 240
+ 241
+ 242
+ 243
+ 244
+ 245
+ 246
+ 247
+ 248
+ 249
+ 250
+ 251
+ 252
+ 253
+ 254
+ 255
+ 256
+ 257
+ 258
+ 259
+ 260
+ 261
+ 262
+ 263
+ 264
+ 265
+ 266
+ 267
+ 268
+ 269
+ 270
+ 271
+ 272
+ 273
+ 274
+ 275
+ 276
+ 277
+ 278
+ 279
+ 280
+ 281
+ 282
+ 283
+ 284
+ 285
+ 286
+ 287
+ 288
+ 289
+ 290
+ 291
+ 292
+ 293
+ 294
+ 295
+ 296
+ 297
+ 298
+ 299
+ 300
+ 301
+ 302
+ 303
+ 304
+ 305
+ 306
+ 307
+ 308
+ 309
+ 310
+ 311
+ 312
+ 313
+ 314
+ 315
+ 316
+ 317
+ 318
+ 319
+ 320
+ 321
+ 322
+ 323
+ 324
+ 325
+ 326
+ 327
+ 328
+ 329
+ 330
+ 331
+ 332
+ 333
+ 334
+ 335
+ 336
+ 337
+ 338
+ 339
+ 340
+ 341
+ 342
+ 343
+ 344
+ 345
+ 346
+ 347
+ 348
+ 349
+ 350
+ 351
+ 352
+ 353
+ 354
+ 355
+ 356
+ 357
+ 358
+ 359
+ 360
+ 361
+ 362
+ 363
+ 364
+ 365
+ 366
+ 367
+ 368
+ 369
+ 370
+ 371
+ 372
+ 373
+ 374
+ 375
+ 376
+ 377
+ 378
+ 379
+ 380
+ 381
+ 382
+ 383
+ 384
+ 385
+ 386
+ 387
+ 388
+ 389
+ 390
+ 391
+ 392
+ 393
+ 394
+ 395
+ 396
+ 397
+ 398
+ 399
+ 400
+ 401
+ 402
+ 403
+ 404
+ 405
+ 406
+ 407
+ 408
+ 409
+ 410
+ 411
+ 412
+ 413
+ 414
+ 415
+ 416
+ 417
+ 418
+ 419
+ 420
+ 421
+ 422
+ 423
+ 424
+ 425
+ 426
+ 427
+ 428
+ 429
+ 430
+ 431
+ 432
+ 433
+ 434
+ 435
+ 436
+ 437
+ 438
+ 439
+ 440
+ 441
+ 442
+ 443
+ 444
+ 445
+ 446
+ 447
+ 448
+ 449
+ 450
+ 451
+ 452
+ 453
+ 454
+ 455
+ 456
+ 457
+ 458
+ 459
+ 460
+ 461
+ 462
+ 463
+ 464
+ 465
+ 466
+ 467
+ 468
+ 469
+ 470
+ 471
+ 472
+ 473
+ 474
+ 475
+ 476
+ 477
+ 478
+ 479
+ 480
+ 481
+ 482
+ 483
+ 484
+ 485
+ 486
+ 487
+ 488
+ 489
+ 490
+ 491
+ 492
+ 493
+ 494
+ 495
+ 496
+ 497
+ 498
+ 499
+ 500
+ 501
+ 502
+ 503
+ 504
+ 505
+ 506
+ 507
+ 508
+ 509
+ 510
+ 511
+ 512
+ 513
+ 514
+ 515
+ 516
+ 517
+ 518
+ 519
+ 520
+ 521
+ 522
+ 523
+ 524
+ 525
+ 526
+ 527
+ 528
+ 529
+ 530
+ 531
+ 532
+ 533
+ 534
+ 535
+ 536
+ 537
+ 538
+ 539
+ 540
+ 541
+ 542
+ 543
+ 544
+ 545
+ 546
+ 547
+ 548
+ 549
+ 550
+ 551
+ 552
+ 553
+ 554
+ 555
+ 556
+ 557
+ 558
+ 559
+ 560
+ 561
+ 562
+ 563
+ 564
+ 565
+ 566
+ 567
+ 568
+ 569
+ 570
+ 571
+ 572
+ 573
+ 574
+ 575
+ 576
+ 577
+ 578
+ 579
+ 580
+ 581
+ 582
+ 583
+ 584
+ 585
+ 586
+ 587
+ 588
+ 589
+ 590
+ 591
+ 592
+ 593
+ 594
+ 595
+ 596
+ 597
+ 598
+ 599
+ 600
+ 601
+ 602
+ 603
+ 604
+ 605
+ 606
+ 607
+ 608
+ 609
+ 610
+ 611
+ 612
+ 613
+ 614
+ 615
+ 616
+ 617
+ 618
+ 619
+ 620
+ 621
+ 622
+ 623
+ 624
+ 625
+ 626
+ 627
+ 628
+ 629
+ 630
+ 631
+ 632
+ 633
+ 634
+ 635
+ 636
+ 637
+ 638
+ 639
+ 640
+ 641
+ 642
+ 643
+ 644
+ 645
+ 646
+ 647
+ 648
+ 649
+ 650
+ 651
+ 652
+ 653
+ 654
+ 655
+ 656
+ 657
+ 658
+ 659
+ 660
+ 661
+ 662
+ 663
+ 664
+ 665
+ 666
+ 667
+ 668
+ 669
+ 670
+ 671
+ 672
+ 673
+ 674
+ 675
+ 676
+ 677
+ 678
+ 679
+ 680
+ 681
+ 682
+ 683
+ 684
+ 685
+ 686
+ 687
+ 688
+ 689
+ 690
+ 691
+ 692
+ 693
+ 694
+ 695
+ 696
+ 697
+ 698
+ 699
+ 700
+ 701
+ 702
+ 703
+ 704
+ 705
+ 706
+ 707
+ 708
+ 709
+ 710
+ 711
+ 712
+ 713
+ 714
+ 715
+ 716
+ 717
+ 718
+ 719
+ 720
+ 721
+ 722
+ 723
+ 724
+ 725
+ 726
+ 727
+ 728
+ 729
+ 730
+ 731
+ 732
+ 733
+ 734
+ 735
+ 736
+ 737
+ 738
+ 739
+ 740
+ 741
+ 742
+ 743
+ 744
+ 745
+ 746
+ 747
+ 748
+ 749
+ 750
+ 751
+ 752
+ 753
+ 754
+ 755
+ 756
+ 757
+ 758
+ 759
+ 760
+ 761
+ 762
+ 763
+ 764
+ 765
+ 766
+ 767
+ 768
+ 769
+ 770
+ 771
+ 772
+ 773
+ 774
+ 775
+ 776
+ 777
+ 778
+ 779
+ 780
+ 781
+ 782
+ 783
+ 784
+ 785
+ 786
+ 787
+ 788
+ 789
+ 790
+ 791
+ 792
+ 793
+ 794
+ 795
+ 796
+ 797
+ 798
+ 799
+ 800
+ 801
+ 802
+ 803
+ 804
+ 805
+ 806
+ 807
+ 808
+ 809
+ 810
+ 811
+ 812
+ 813
+ 814
+ 815
+ 816
+ 817
+ 818
+ 819
+ 820
+ 821
+ 822
+ 823
+ 824
+ 825
+ 826
+ 827
+ 828
+ 829
+ 830
+ 831
+ 832
+ 833
+ 834
+ 835
+ 836
+ 837
+ 838
+ 839
+ 840
+ 841
+ 842
+ 843
+ 844
+ 845
+ 846
+ 847
+ 848
+ 849
+ 850
+ 851
+ 852
+ 853
+ 854
+ 855
+ 856
+ 857
+ 858
+ 859
+ 860
+ 861
+ 862
+ 863
+ 864
+ 865
+ 866
+ 867
+ 868
+ 869
+ 870
+ 871
+ 872
+ 873
+ 874
+ 875
+ 876
+ 877
+ 878
+ 879
+ 880
+ 881
+ 882
+ 883
+ 884
+ 885
+ 886
+ 887
+ 888
+ 889
+ 890
+ 891
+ 892
+ 893
+ 894
+ 895
+ 896
+ 897
+ 898
+ 899
+ 900
+ 901
+ 902
+ 903
+ 904
+ 905
+ 906
+ 907
+ 908
+ 909
+ 910
+ 911
+ 912
+ 913
+ 914
+ 915
+ 916
+ 917
+ 918
+ 919
+ 920
+ 921
+ 922
+ 923
+ 924
+ 925
+ 926
+ 927
+ 928
+ 929
+ 930
+ 931
+ 932
+ 933
+ 934
+ 935
+ 936
+ 937
+ 938
+ 939
+ 940
+ 941
+ 942
+ 943
+ 944
+ 945
+ 946
+ 947
+ 948
+ 949
+ 950
+ 951
+ 952
+ 953
+ 954
+ 955
+ 956
+ 957
+ 958
+ 959
+ 960
+ 961
+ 962
+ 963
+ 964
+ 965
+ 966
+ 967
+ 968
+ 969
+ 970
+ 971
+ 972
+ 973
+ 974
+ 975
+ 976
+ 977
+ 978
+ 979
+ 980
+ 981
+ 982
+ 983
+ 984
+ 985
+ 986
+ 987
+ 988
+ 989
+ 990
+ 991
+ 992
+ 993
+ 994
+ 995
+ 996
+ 997
+ 998
+ 999
+1000
+1001
+1002
+1003
+1004
+1005
+1006
+1007
+1008
+1009
+1010
+1011
+1012
+1013
+1014
+1015
+1016
+1017
+1018
+1019
+1020
+1021
+1022
+1023
+1024
+1025
+1026
+1027
+1028
+1029
+1030
+1031
+1032
+1033
+1034
+1035
+1036
+1037
+1038
+1039
+1040
+1041
+1042
+1043
+1044
+1045
+1046
+1047
+1048
+1049
+1050
+1051
+1052
+1053
+1054
+1055
+1056
+1057
+1058
+1059
+1060
+1061
+1062
+1063
+1064
+1065
+1066
+1067
+1068
+1069
+1070
+1071
+1072
+1073
+1074
+1075
+1076
+1077
+1078
+1079
+1080
+1081
+1082
+1083
+1084
+1085
+1086
+1087
+1088
+1089
+1090
+1091
+1092
+1093
+1094
+1095
+1096
+1097
+1098
+1099
+1100
+1101
+1102
+1103
+1104
+1105
+1106
+1107
+1108
+1109
+1110
+1111
+1112
+1113
+1114
+1115
+1116
+1117
+1118
+1119
+1120
+1121
+1122
+1123
+1124
+1125
+1126
+1127
+1128
+1129
+1130
+1131
+1132
+1133
+1134
+1135
+1136
+1137
+1138
+1139
+1140
+1141
+1142
+1143
+1144
+1145
+1146
+1147
+1148
+1149
+1150
+1151
+1152
+1153
+1154
+1155
+1156
+1157
+1158
+1159
+1160
+1161
+1162
+1163
+1164
+1165
+1166
+1167
+1168
+1169
+1170
+1171
+1172
+1173
+1174
+1175
+1176
+1177
+1178
+1179
+1180
+1181
+1182
+1183
+1184
+1185
+1186
+1187
+1188
+1189
+1190
+1191
+1192
+1193
+1194
+1195
+1196
+1197
+1198
+1199
+1200
+1201
+1202
+1203
+1204
+1205
+1206
+1207
+1208
+1209
+1210
+1211
+1212
+1213
+1214
+1215
+1216
+1217
+1218
+1219
+1220
+1221
+1222
+1223
+1224
+1225
+1226
+1227
+1228
+1229
+1230
+1231
+1232
+1233
+1234
+1235
+1236
+1237
+1238
+1239
+1240
+1241
+1242
+1243
+1244
+1245
+1246
+1247
+1248
+1249
+1250
+1251
+1252
+1253
+1254
+1255
+1256
+1257
+1258
+1259
+1260
+1261
+1262
+1263
+1264
+1265
+1266
+1267
+1268
+1269
+1270
+1271
+1272
+1273
+1274
+1275
+1276
+1277
+1278
+1279
+1280
+1281
+1282
+1283
+1284
+1285
+1286
+1287
+1288
+1289
+1290
+1291
+1292
+1293
+1294
+1295
+1296
+1297
+1298
+1299
+1300
+1301
+1302
+1303
+1304
+1305
+1306
+1307
+1308
+1309
+1310
+1311
+1312
+1313
+1314
+1315
+1316
+1317
+1318
+1319
+1320
+1321
+1322
+1323
+1324
+1325
+1326
+1327
+1328
+1329
+1330
+1331
+1332
+1333
+1334
+1335
+1336
+1337
+1338
+1339
+1340
+1341
+1342
+1343
+1344
+1345
+1346
+1347
+1348
+1349
+1350
+1351
+1352
+1353
+1354
+1355
+1356
+1357
+1358
+1359
+1360
+1361
+1362
+1363
+1364
+1365
+1366
+1367
+1368
+1369
+1370
+1371
+1372
+1373
+1374
+1375
+1376
+1377
+1378
+1379
+1380
+1381
+1382
+1383
+1384
+1385
+1386
+1387
+1388
+1389
+1390
+1391
+1392
+1393
+1394
+1395
+1396
+1397
+1398
+1399
+1400
+1401
+1402
+1403
+1404
+1405
+1406
+1407
+1408
+1409
+1410
+1411
+1412
+1413
+1414
+1415
+1416
+1417
+1418
+1419
+1420
+1421
+1422
+1423
+1424
+1425
+1426
+1427
+1428
+1429
+1430
+1431
+1432
+1433
+1434
+1435
+1436
+1437
+1438
+1439
+1440
+1441
+1442
+1443
+1444
+1445
+1446
+1447
+1448
+1449
+1450
+1451
+1452
+1453
+1454
+1455
+1456
+1457
+1458
+1459
+1460
+1461
+1462
+1463
+1464
+1465
+1466
+1467
+1468
+1469
+1470
+1471
+1472
+1473
+1474
+1475
+1476
+1477
+1478
+1479
+1480
+1481
+1482
+1483
+1484
+1485
+1486
+1487
+1488
+1489
+1490
+1491
+1492
+1493
+1494
+1495
+1496
+1497
+1498
+1499
+1500
+1501
+1502
+1503
+1504
+1505
+1506
+1507
+1508
+1509
+1510
+1511
+1512
+1513
+1514
+1515
+1516
+1517
+1518
+1519
+1520
+1521
+1522
+1523
+1524
+1525
+1526
+1527
+1528
+1529
+1530
+1531
+1532
+1533
+1534
+1535
+1536
+1537
+1538
+1539
+1540
+1541
+1542
+1543
+1544
+1545
+1546
+1547
+1548
+1549
+1550
+1551
+1552
+1553
+1554
+1555
+1556
+1557
+1558
+1559
+1560
+1561
+1562
+1563
+1564
+1565
+1566
+1567
+1568
+1569
+1570
+1571
+1572
+1573
+1574
+1575
+1576
+1577
+1578
+1579
+1580
+1581
+1582
+1583
+1584
+1585
+1586
+1587
+1588
+1589
+1590
+1591
+1592
+1593
+1594
+1595
+1596
+1597
+1598
+1599
+1600
+1601
+1602
+1603
+1604
+1605
+1606
+1607
+1608
+1609
+1610
+1611
+1612
+1613
+1614
+1615
+1616
+1617
+1618
+1619
+1620
+1621
+1622
+1623
+1624
+1625
+1626
+1627
+1628
+1629
+1630
+1631
+1632
+1633
+1634
+1635
+1636
+1637
+1638
+1639
+1640
+1641
+1642
+1643
+1644
+1645
+1646
+1647
+1648
+1649
+1650
+1651
+1652
+1653
+1654
+1655
+1656
+1657
+1658
+1659
+1660
+1661
+1662
+1663
+1664
+1665
+1666
+1667
+1668
+1669
+1670
+1671
+1672
+1673
+1674
+1675
+1676
+1677
+1678
+1679
+1680
+1681
+1682
+1683
+1684
+1685
+1686
+1687
+1688
+1689
+1690
+1691
+1692
+1693
+1694
+1695
+1696
+1697
+1698
+1699
+1700
+1701
+1702
+1703
+1704
+1705
+1706
+1707
+1708
+1709
+1710
+1711
+1712
+1713
+1714
+1715
+1716
+1717
+1718
+1719
+1720
+1721
+1722
+1723
+1724
+1725
+1726
+1727
+1728
+1729
+1730
+1731
+1732
+1733
+1734
+1735
+1736
+1737
+1738
+1739
+1740
+1741
+1742
+1743
+1744
+1745
+1746
+1747
+1748
+1749
+1750
+1751
+1752
+1753
+1754
+1755
+1756
+1757
+1758
+1759
+1760
+1761
+1762
+1763
+1764
+1765
+1766
+1767
+1768
+1769
+1770
+1771
+1772
+1773
+1774
+1775
+1776
+1777
+1778
+1779
+1780
+1781
+1782
+1783
+1784
+1785
+1786
+1787
+1788
+1789
+1790
+1791
+1792
+1793
+1794
+1795
+1796
+1797
+1798
+1799
+1800
+1801
+1802
+1803
+1804
+1805
+1806
+1807
+1808
+1809
+1810
+1811
+1812
+1813
+1814
+1815
+1816
+1817
+1818
+1819
+1820
+1821
+1822
+1823
+1824
+1825
+1826
+1827
+1828
+1829
+1830
+1831
+1832
+1833
+1834
+1835
+1836
+1837
+1838
+1839
+1840
+1841
+1842
+1843
+1844
+1845
+1846
+1847
+1848
+1849
+1850
+1851
+1852
+1853
+1854
+1855
+1856
+1857
+1858
+1859
+1860
+1861
+1862
+1863
+1864
+1865
+1866
+1867
+1868
+1869
+1870
+1871
+1872
+1873
+1874
+1875
+1876
+1877
+1878
+1879
+1880
+1881
+1882
+1883
+1884
+1885
+1886
+1887
+1888
+1889
+1890
+1891
+1892
+1893
+1894
+1895
+1896
+1897
+1898
+1899
+1900
+1901
+1902
+1903
+1904
+1905
+1906
+1907
+1908
+1909
+1910
+1911
+1912
+1913
+1914
+1915
+1916
+1917
+1918
+1919
+1920
+1921
+1922
+1923
+1924
+1925
+1926
+1927
+1928
+1929
+1930
+1931
+1932
+1933
+1934
+1935
+1936
+1937
+1938
+1939
+1940
+1941
+1942
+1943
+1944
+1945
+1946
+1947
+1948
+1949
+1950
+1951
+1952
+1953
+1954
+1955
+1956
+1957
+1958
+1959
+1960
+1961
+1962
+1963
+1964
+1965
+1966
+1967
+1968
+1969
+1970
+1971
+1972
+1973
+1974
+1975
+1976
+1977
+1978
+1979
+1980
+1981
+1982
+1983
+1984
+1985
+1986
+1987
+1988
+1989
+1990
+1991
+1992
+1993
+1994
+1995
+1996
+1997
+1998
+1999
+2000
+2001
+2002
+2003
+2004
+2005
+2006
+2007
+2008
+2009
+2010
+2011
+2012
+2013
+2014
+2015
+2016
+2017
+2018
+2019
+2020
+2021
+2022
+2023
+2024
+2025
+2026
+2027
+2028
+2029
+2030
+2031
+2032
+2033
+2034
+2035
+2036
+2037
+2038
+2039
+2040
+2041
+2042
+2043
+2044
+2045
+2046
+2047
+2048
+2049
+2050
+2051
+2052
+2053
+2054
+2055
+2056
+2057
+2058
+2059
+2060
+2061
+2062
+2063
+2064
+2065
+2066
+2067
+2068
+2069
+2070
+2071
+2072
+2073
+2074
+2075
+2076
+2077
+2078
+2079
+2080
+2081
+2082
+2083
+2084
+2085
+2086
+2087
+2088
+2089
+2090
+2091
+2092
+2093
+2094
+2095
+2096
+2097
+2098
+2099
+2100
+2101
+2102
+2103
+2104
+2105
+2106
+2107
+2108
+2109
+2110
+2111
+2112
+2113
+2114
+2115
+2116
+2117
+2118
+2119
+2120
+2121
+2122
+2123
+2124
+2125
+2126
+2127
+2128
+2129
+2130
+2131
+2132
+2133
+2134
+2135
+2136
+2137
+2138
+2139
+2140
+2141
+2142
+2143
+2144
+2145
+2146
+2147
+2148
+2149
+2150
+2151
+2152
+2153
+2154
+2155
+2156
+2157
+2158
+2159
+2160
+2161
+2162
+2163
+2164
+2165
+2166
+2167
+2168
+2169
+2170
+2171
+2172
+2173
+2174
+2175
+2176
+2177
+2178
+2179
+2180
+2181
+2182
+2183
+2184
+2185
+2186
+2187
+2188
+2189
+2190
+2191
+2192
+2193
+2194
+2195
+2196
+2197
+2198
+2199
+2200
+2201
+2202
+2203
+2204
+2205
+2206
+2207
+2208
+2209
+2210
+2211
+2212
+2213
+2214
+2215
+2216
+2217
+2218
+2219
+2220
+2221
+2222
+2223
+2224
+2225
+2226
+2227
+2228
+2229
+2230
+2231
+2232
+2233
+2234
+2235
+2236
+2237
+2238
+2239
+2240
+2241
+2242
+2243
+2244
+2245
+2246
+2247
+2248
+2249
+2250
+2251
+2252
+2253
+2254
+2255
+2256
+2257
+2258
+2259
+2260
+2261
+2262
+2263
+2264
+2265
+2266
+2267
+2268
+2269
+2270
+2271
+2272
+2273
+2274
+2275
+2276
+2277
+2278
+2279
+2280
+2281
+2282
+2283
+2284
+2285
+2286
+2287
+2288
+2289
+2290
+2291
+2292
+2293
+2294
+2295
+2296
+2297
+2298
+2299
+2300
+2301
+2302
+2303
+2304
+2305
+2306
+2307
+2308
+2309
+2310
+2311
+2312
+2313
+2314
+2315
+2316
+2317
+2318
+2319
+2320
+2321
+2322
+2323
+2324
+2325
+2326
+2327
+2328
+2329
+2330
+2331
+2332
+2333
+2334
+2335
+2336
+2337
+2338
+2339
+2340
+2341
+2342
+2343
+2344
+2345
+2346
+2347
+2348
+2349
+2350
+2351
+2352
+2353
+2354
+2355
+2356
+2357
+2358
+2359
+2360
+2361
+2362
+2363
+2364
+2365
+2366
+2367
+2368
+2369
+2370
+2371
+2372
+2373
+2374
+2375
+2376
+2377
+2378
+2379
+2380
+2381
+2382
+2383
+2384
+2385
+2386
+2387
+2388
+2389
+2390
+2391
+2392
+2393
+2394
+2395
+2396
+2397
+2398
+2399
+2400
+2401
+2402
+2403
+2404
+2405
+2406
+2407
+2408
+2409
+2410
+2411
+2412
+2413
+2414
+2415
+2416
+2417
+2418
+2419
+2420
+2421
+2422
+2423
+2424
+2425
+2426
+2427
+2428
+2429
+2430
+2431
+2432
+2433
+2434
+2435
+2436
+2437
+2438
+2439
+2440
+2441
+2442
+2443
+2444
+2445
+2446
+2447
+2448
+2449
+2450
+2451
+2452
+2453
+2454
+2455
+2456
+2457
+2458
+2459
+2460
+2461
+2462
+2463
+2464
+2465
+2466
+2467
+2468
+2469
+2470
+2471
+2472
+2473
+2474
+2475
+2476
+2477
+2478
+2479
+2480
+2481
+2482
+2483
+2484
+2485
+2486
+2487
+2488
+2489
+2490
+2491
+2492
+2493
+2494
+2495
+2496
+2497
+2498
+2499
+2500
+2501
+2502
+2503
+2504
+2505
+2506
+2507
+2508
+2509
+2510
+2511
+2512
+2513
+2514
+2515
+2516
+2517
+2518
+2519
+2520
+2521
+2522
+2523
+2524
+2525
+2526
+2527
+2528
+2529
+2530
+2531
+2532
+2533
+2534
+2535
+2536
+2537
+2538
+2539
+2540
+2541
+2542
+2543
+2544
+2545
+2546
+2547
+2548
+2549
+2550
+2551
+2552
+2553
+2554
+2555
+2556
+2557
+2558
+2559
+2560
+2561
+2562
+2563
+2564
+2565
+2566
+2567
+2568
+2569
+2570
+2571
+2572
+2573
+2574
+2575
+2576
+2577
+2578
+2579
+2580
+2581
+2582
+2583
+2584
+2585
+2586
+2587
+2588
+2589
+2590
+2591
+2592
+2593
+2594
+2595
+2596
+2597
+2598
+2599
+2600
+2601
+2602
+2603
+2604
+2605
+2606
+2607
+2608
+2609
+2610
+2611
+2612
+2613
+2614
+2615
+2616
+2617
+2618
+2619
+2620
+2621
+2622
+2623
+2624
+2625
+2626
+2627
+2628
+2629
+2630
+2631
+2632
+2633
+2634
+2635
+2636
+2637
+2638
+2639
+2640
+2641
+2642
+2643
+2644
+2645
+2646
+2647
+2648
+2649
+2650
+2651
+2652
+2653
+2654
+2655
+2656
+2657
+2658
+2659
+2660
+2661
+2662
+2663
+2664
+2665
+2666
+2667
+2668
+2669
+2670
+2671
+2672
+2673
+2674
+2675
+2676
+2677
+2678
+2679
+2680
+2681
+2682
+2683
+2684
+2685
+2686
+2687
+2688
+2689
+2690
+2691
+2692
+2693
+2694
+2695
+2696
+2697
+2698
+2699
+2700
+2701
+2702
+2703
+2704
+2705
+2706
+2707
+2708
+2709
+2710
+2711
+2712
+2713
+2714
+2715
+2716
+2717
+2718
+2719
+2720
+2721
+2722
+2723
+2724
+2725
+2726
+2727
+2728
+2729
+2730
+2731
+2732
+2733
+2734
+2735
+2736
+2737
+2738
+2739
+2740
+2741
+2742
+2743
+2744
+2745
+2746
+2747
+2748
+2749
+2750
+2751
+2752
+2753
+2754
+2755
+2756
+2757
+2758
+2759
+2760
+2761
+2762
+2763
+2764
+2765
+2766
+2767
+2768
+2769
+2770
+2771
+2772
+2773
+2774
+2775
+2776
+2777
+2778
+2779
+2780
+2781
+2782
+2783
+2784
+2785
+2786
+2787
+2788
+2789
+2790
+2791
+2792
+2793
+2794
+2795
+2796
+2797
+2798
+2799
+2800
+2801
+2802
+2803
+2804
+2805
+2806
+2807
+2808
+2809
+2810
+2811
+2812
+2813
+2814
+2815
+2816
+2817
+2818
+2819
+2820
+2821
+2822
+2823
+2824
+2825
+2826
+2827
+2828
+2829
+2830
+2831
+2832
+2833
+2834
+2835
+2836
+2837
+2838
+2839
+2840
+2841
+2842
+2843
+2844
+2845
+2846
+2847
+2848
+2849
+2850
+2851
+2852
+2853
+2854
+2855
+2856
+2857
+2858
+2859
+2860
+2861
+2862
+2863
+2864
+2865
+2866
+2867
+2868
+2869
+2870
+2871
+2872
+2873
+2874
+2875
+2876
+2877
+2878
+2879
+2880
+2881
+2882
+2883
+2884
+2885
+2886
+2887
+2888
+2889
+2890
+2891
+2892
+2893
+2894
+2895
+2896
+2897
+2898
+2899
+2900
+2901
+2902
+2903
+2904
+2905
+2906
+2907
+2908
+2909
+2910
+2911
+2912
+2913
+2914
+2915
+2916
+2917
+2918
+2919
+2920
+2921
+2922
+2923
+2924
+2925
+2926
+2927
+2928
+2929
+2930
+2931
+2932
+2933
+2934
+2935
+2936
+2937
+2938
+2939
+2940
+2941
+2942
+2943
+2944
+2945
+2946
+2947
+2948
+2949
+2950
+2951
+2952
+2953
+2954
+2955
+2956
+2957
+2958
+2959
+2960
+2961
+2962
+2963
+2964
+2965
+2966
+2967
+2968
+2969
+2970
+2971
+2972
+2973
+2974
+2975
+2976
+2977
+2978
+2979
+2980
+2981
+2982
+2983
+2984
+2985
+2986
+2987
+2988
+2989
+2990
+2991
+2992
+2993
+2994
+2995
+2996
+2997
+2998
+2999
+3000
+3001
+3002
+3003
+3004
+3005
+3006
+3007
+3008
+3009
+3010
+3011
+3012
+3013
+3014
+3015
+3016
+3017
+3018
+3019
+3020
+3021
+3022
+3023
+3024
+3025
+3026
+3027
+3028
+3029
+3030
+3031
+3032
+3033
+3034
+3035
+3036
+3037
+3038
+3039
+3040
+3041
+3042
+3043
+3044
+3045
+3046
+3047
+3048
+3049
+3050
+3051
+3052
+3053
+3054
+3055
+3056
+3057
+3058
+3059
+3060
+3061
+3062
+3063
+3064
+3065
+3066
+3067
+3068
+3069
+3070
+3071
+3072
+3073
+3074
+3075
+3076
+3077
+3078
+3079
+3080
+3081
+3082
+3083
+3084
+3085
+3086
+3087
+3088
+3089
+3090
+3091
+3092
+3093
+3094
+3095
+3096
+3097
+3098
+3099
+3100
+3101
+3102
+3103
+3104
+3105
+3106
+3107
+3108
+3109
+3110
+3111
+3112
+3113
+3114
+3115
+3116
+3117
+3118
+3119
+3120
+3121
+3122
+3123
+3124
+3125
+3126
+3127
+3128
+3129
+3130
+3131
+3132
+3133
+3134
+3135
+3136
+3137
+3138
+3139
+3140
+3141
+3142
+3143
+3144
+3145
+3146
+3147
+3148
+3149
+3150
+3151
+3152
+3153
+3154
+3155
+3156
+3157
+3158
+3159
+3160
+3161
+3162
+3163
+3164
+3165
+3166
+3167
+3168
+3169
+3170
+3171
+3172
+3173
+3174
+3175
+3176
+3177
+3178
+3179
+3180
+3181
+3182
+3183
+3184
+3185
+3186
+3187
+3188
+3189
+3190
+3191
+3192
+3193
+3194
+3195
+3196
+3197
+3198
+3199
+3200
+3201
+3202
+3203
+3204
+3205
+3206
+3207
+3208
+3209
+3210
+3211
+3212
+3213
+3214
+3215
+3216
+3217
+3218
+3219
+3220
+3221
+3222
+3223
+3224
+3225
+3226
+3227
+3228
+3229
+3230
+3231
+3232
+3233
+3234
+3235
+3236
+3237
+3238
+3239
+3240
+3241
+3242
+3243
+3244
+3245
+3246
+3247
+3248
+3249
+3250
+3251
+3252
+3253
+3254
+3255
+3256
+3257
+3258
+3259
+3260
+3261
+3262
+3263
+3264
+3265
+3266
+3267
+3268
+3269
+3270
+3271
+3272
+3273
+3274
+3275
+3276
+3277
+3278
+3279
+3280
+3281
+3282
+3283
+3284
+3285
+3286
+3287
+3288
+3289
+3290
+3291
+3292
+3293
+3294
+3295
+3296
+3297
+3298
+3299
+3300
+3301
+3302
+3303
+3304
+3305
+3306
+3307
+3308
+3309
+3310
+3311
+3312
+3313
+3314
+3315
+3316
+3317
+3318
+3319
+3320
+3321
+3322
+3323
+3324
+3325
+3326
+3327
+3328
+3329
+3330
+3331
+3332
+3333
+3334
+3335
+3336
+3337
+3338
+3339
+3340
+3341
+3342
+3343
+3344
+3345
+3346
+3347
+3348
+3349
+3350
+3351
+3352
+3353
+3354
+3355
+3356
+3357
+3358
+3359
+3360
+3361
+3362
+3363
+3364
+3365
+3366
+3367
+3368
+3369
+3370
+3371
+3372
+3373
+3374
+3375
+3376
+3377
+3378
+3379
+3380
+3381
+3382
+3383
+3384
+3385
+3386
+3387
+3388
+3389
+3390
+3391
+3392
+3393
+3394
+3395
+3396
+3397
+3398
+3399
+3400
+3401
+3402
+3403
+3404
+3405
+3406
+3407
+3408
+3409
+3410
+3411
+3412
+3413
+3414
+3415
+3416
+3417
+3418
+3419
+3420
+3421
+3422
+3423
+3424
+3425
+3426
+3427
+3428
+3429
+3430
+3431
+3432
+3433
+3434
+3435
+3436
+3437
+3438
+3439
+3440
+3441
+3442
+3443
+3444
+3445
+3446
+3447
+3448
+3449
+3450
+3451
+3452
+3453
+3454
+3455
+3456
+3457
+3458
+3459
+3460
+3461
+3462
+3463
+3464
+3465
+3466
+3467
+3468
+3469
+3470
+3471
+3472
+3473
+3474
+3475
+3476
+3477
+3478
+3479
+3480
+3481
+3482
+3483
+3484
+3485
+3486
+3487
+3488
+3489
+3490
+3491
+3492
+3493
+3494
+3495
+3496
+3497
+3498
+3499
+3500
+3501
+3502
+3503
+3504
+3505
+3506
+3507
+3508
+3509
+3510
+3511
+3512
+3513
+3514
+3515
+3516
+3517
+3518
+3519
+3520
+3521
+3522
+3523
+3524
+3525
+3526
+3527
+3528
+3529
+3530
+3531
+3532
+3533
+3534
+3535
+3536
+3537
+3538
+3539
+3540
+3541
+3542
+3543
+3544
+3545
+3546
+3547
+3548
+3549
+3550
+3551
+3552
+3553
+3554
+3555
+3556
+3557
+3558
+3559
+3560
+3561
+3562
+3563
+3564
+3565
+3566
+3567
+3568
+3569
+3570
+3571
+3572
+3573
+3574
+3575
+3576
+3577
+3578
+3579
+3580
+3581
+3582
+3583
+3584
+3585
+3586
+3587
+3588
+3589
+3590
+3591
+3592
+3593
+3594
+3595
+3596
+3597
+3598
+3599
+3600
+3601
+3602
+3603
+3604
+3605
+3606
+3607
+3608
+3609
+3610
+3611
+3612
+3613
+3614
+3615
+3616
+3617
+3618
+3619
+3620
+3621
+3622
+3623
+3624
+3625
+3626
+3627
+3628
+3629
+3630
+3631
+3632
+3633
+3634
+3635
+3636
+3637
+3638
+3639
+3640
+3641
+3642
+3643
+3644
+3645
+3646
+3647
+3648
+3649
+3650
+3651
+3652
+3653
+3654
+3655
+3656
+3657
+3658
+3659
+3660
+3661
+3662
+3663
+3664
+3665
+3666
+3667
+3668
+3669
+3670
+3671
+3672
+3673
+3674
+3675
+3676
+3677
+3678
+3679
+3680
+3681
+3682
+3683
+3684
+3685
+3686
+3687
+3688
+3689
+3690
+3691
+3692
+3693
+3694
+3695
+3696
+3697
+3698
+3699
+3700
+3701
+3702
+3703
+3704
+3705
+3706
+3707
+3708
+3709
+3710
+3711
+3712
+3713
+3714
+3715
+3716
+3717
+3718
+3719
+3720
+3721
+3722
+3723
+3724
+3725
+3726
+3727
+3728
+3729
+3730
+3731
+3732
+3733
+3734
+3735
+3736
+3737
+3738
+3739
+3740
+3741
+3742
+3743
+3744
+3745
+3746
+3747
+3748
+3749
+3750
+3751
+3752
+3753
+3754
+3755
+3756
+3757
+3758
+3759
+3760
+3761
+3762
+3763
+3764
+3765
+3766
+3767
+3768
+3769
+3770
+3771
+3772
+3773
+3774
+3775
+3776
+3777
+3778
+3779
+3780
+3781
+3782
+3783
+3784
+3785
+3786
+3787
+3788
+3789
+3790
+3791
+3792
+3793
+3794
+3795
+3796
+3797
+3798
+3799
+3800
+3801
+3802
+3803
+3804
+3805
+3806
+3807
+3808
+3809
+3810
+3811
+3812
+3813
+3814
+3815
+3816
+3817
+3818
+3819
+3820
+3821
+3822
+3823
+3824
+3825
+3826
+3827
+3828
+3829
+3830
+3831
+3832
+3833
+3834
+3835
+3836
+3837
+3838
+3839
+3840
+3841
+3842
+3843
+3844
+3845
+3846
+3847
+3848
+3849
+3850
+3851
+3852
+3853
+3854
+3855
+3856
+3857
+3858
+3859
+3860
+3861
+3862
+3863
+3864
+3865
+3866
+3867
+3868
+3869
+3870
+3871
+3872
+3873
+3874
+3875
+3876
+3877
+3878
+3879
+3880
+3881
+3882
+3883
+3884
+3885
+3886
+3887
+3888
+3889
+3890
+3891
+3892
+3893
+3894
+3895
+3896
+3897
+3898
+3899
+3900
+3901
+3902
+3903
+3904
+3905
+3906
+3907
+3908
+3909
+3910
+3911
+3912
+3913
+3914
+3915
+3916
+3917
+3918
+3919
+3920
+3921
+3922
+3923
+3924
+3925
+3926
+3927
+3928
+3929
+3930
+3931
+3932
+3933
+3934
+3935
+3936
+3937
+3938
+3939
+3940
+3941
+3942
+3943
+3944
+3945
+3946
+3947
+3948
+3949
+3950
+3951
+3952
+3953
+3954
+3955
+3956
+3957
+3958
+3959
+3960
+3961
+3962
+3963
+3964
+3965
+3966
+3967
+3968
+3969
+3970
+3971
+3972
+3973
+3974
+3975
+3976
+3977
+3978
+3979
+3980
+3981
+3982
+3983
+3984
+3985
+3986
+3987
+3988
+3989
+3990
+3991
+3992
+3993
+3994
+3995
+3996
+3997
+3998
+3999
+4000
+4001
+4002
+4003
+4004
+4005
+4006
+4007
+4008
+4009
+4010
+4011
+4012
+4013
+4014
+4015
+4016
+4017
+4018
+4019
+4020
+4021
+4022
+4023
+4024
+4025
+4026
+4027
+4028
+4029
+4030
+4031
+4032
+4033
+4034
+4035
+4036
+4037
+4038
+4039
+4040
+4041
+4042
+4043
+4044
+4045
+4046
+4047
+4048
+4049
+4050
+4051
+4052
+4053
+4054
+4055
+4056
+4057
+4058
+4059
+4060
+4061
+4062
+4063
+4064
+4065
+4066
+4067
+4068
+4069
+4070
+4071
+4072
+4073
+4074
+4075
+4076
+4077
+4078
+4079
+4080
+4081
+4082
+4083
+4084
+4085
+4086
+4087
+4088
+4089
+4090
+4091
+4092
+4093
+4094
+4095
+4096
+4097
+4098
+4099
+4100
+4101
+4102
+4103
+4104
+4105
+4106
+4107
+4108
+4109
+4110
+4111
+4112
+4113
+4114
+4115
+4116
+4117
+4118
+4119
+4120
+4121
+4122
+4123
+4124
+4125
+4126
+4127
+4128
+4129
+4130
+4131
+4132
+4133
+4134
+4135
+4136
+4137
+4138
+4139
+4140
+4141
+4142
+4143
+4144
+4145
+4146
+4147
+4148
+4149
+4150
+4151
+4152
+4153
+4154
+4155
+4156
+4157
+4158
+4159
+4160
+4161
+4162
+4163
+4164
+4165
+4166
+4167
+4168
+4169
+4170
+4171
+4172
+4173
+4174
+4175
+4176
+4177
+4178
+4179
+4180
+4181
+4182
+4183
+4184
+4185
+4186
+4187
+4188
+4189
+4190
+4191
+4192
+4193
+4194
+4195
+4196
+4197
+4198
+4199
+4200
+4201
+4202
+4203
+4204
+4205
+4206
+4207
+4208
+4209
+4210
+4211
+4212
+4213
+4214
+4215
+4216
+4217
+4218
+4219
+4220
+4221
+4222
+4223
+4224
+4225
+4226
+4227
+4228
+4229
+4230
+4231
+4232
+4233
+4234
+4235
+4236
+4237
+4238
+4239
+4240
+4241
+4242
+4243
+4244
+4245
+4246
+4247
+4248
+4249
+4250
+4251
+4252
+4253
+4254
+4255
+4256
+4257
+4258
+4259
+4260
+4261
+4262
+4263
+4264
+4265
+4266
+4267
+4268
+4269
+4270
+4271
+4272
+4273
+4274
+4275
+4276
+4277
+4278
+4279
+4280
+4281
+4282
+4283
+4284
+4285
+4286
+4287
+4288
+4289
+4290
+4291
+4292
+4293
+4294
+4295
+4296
+4297
+4298
+4299
+4300
+4301
+4302
+4303
+4304
+4305
+4306
+4307
+4308
+4309
+4310
+4311
+4312
+4313
+4314
+4315
+4316
+4317
+4318
+4319
+4320
+4321
+4322
+4323
+4324
+4325
+4326
+4327
+4328
+4329
+4330
+4331
+4332
+4333
+4334
+4335
+4336
+4337
+4338
+4339
+4340
+4341
+4342
+4343
+4344
+4345
+4346
+4347
+4348
+4349
+4350
+4351
+4352
+4353
+4354
+4355
+4356
+4357
+4358
+4359
+4360
+4361
+4362
+4363
+4364
+4365
+4366
+4367
+4368
+4369
+4370
+4371
+4372
+4373
+4374
+4375
+4376
+4377
+4378
+4379
+4380
+4381
+4382
+4383
+4384
+4385
+4386
+4387
+4388
+4389
+4390
+4391
+4392
+4393
+4394
+4395
+4396
+4397
+4398
+4399
+4400
+4401
+4402
+4403
+4404
+4405
+4406
+4407
+4408
+4409
+4410
+4411
+4412
+4413
+4414
+4415
+4416
+4417
+4418
+4419
+4420
+4421
+4422
+4423
+4424
+4425
+4426
+4427
+4428
+4429
+4430
+4431
+4432
+4433
+4434
+4435
+4436
+4437
+4438
+4439
+4440
+4441
+4442
+4443
+4444
+4445
+4446
+4447
+4448
+4449
+4450
+4451
+4452
+4453
+4454
+4455
+4456
+4457
+4458
+4459
+4460
+4461
+4462
+4463
+4464
+4465
+4466
+4467
+4468
+4469
+4470
+4471
+4472
+4473
+4474
+4475
+4476
+4477
+4478
+4479
+4480
+4481
+4482
+4483
+4484
+4485
+4486
+4487
+4488
+4489
+4490
+4491
+4492
+4493
+4494
+4495
+4496
+4497
+4498
+4499
+4500
+4501
+4502
+4503
+4504
+4505
+4506
+4507
+4508
+4509
+4510
+4511
+4512
+4513
+4514
+4515
+4516
+4517
+4518
+4519
+4520
+4521
+4522
+4523
+4524
+4525
+4526
+4527
+4528
+4529
+4530
+4531
+4532
+4533
+4534
+4535
+4536
+4537
+4538
+4539
+4540
+4541
+4542
+4543
+4544
+4545
+4546
+4547
+4548
+4549
+4550
+4551
+4552
+4553
+4554
+4555
+4556
+4557
+4558
+4559
+4560
+4561
+4562
+4563
+4564
+4565
+4566
+4567
+4568
+4569
+4570
+4571
+4572
+4573
+4574
+4575
+4576
+4577
+4578
+4579
+4580
+4581
+4582
+4583
+4584
+4585
+4586
+4587
+4588
+4589
+4590
+4591
+4592
+4593
+4594
+4595
+4596
+4597
+4598
+4599
+4600
+4601
+4602
+4603
+4604
+4605
+4606
+4607
+4608
+4609
+4610
+4611
+4612
+4613
+4614
+4615
+4616
+4617
+4618
+4619
+4620
+4621
+4622
+4623
+4624
+4625
+4626
+4627
+4628
+4629
+4630
+4631
+4632
+4633
+4634
+4635
+4636
+4637
+4638
+4639
+4640
+4641
+4642
+4643
+4644
+4645
+4646
+4647
+4648
+4649
+4650
+4651
+4652
+4653
+4654
+4655
+4656
+4657
+4658
+4659
+4660
+4661
+4662
+4663
+4664
+4665
+4666
+4667
+4668
+4669
+4670
+4671
+4672
+4673
+4674
+4675
+4676
+4677
+4678
+4679
+4680
+4681
+4682
+4683
+4684
+4685
+4686
+4687
+4688
+4689
+4690
+4691
+4692
+4693
+4694
+4695
+4696
+4697
+4698
+4699
+4700
+4701
+4702
+4703
+4704
+4705
+4706
+4707
+4708
+4709
+4710
+4711
+4712
+4713
+4714
+4715
+4716
+4717
+4718
+4719
+4720
+4721
+4722
+4723
+4724
+4725
+4726
+4727
+4728
+4729
+4730
+4731
+4732
+4733
+4734
+4735
+4736
+4737
+4738
+4739
+4740
+4741
+4742
+4743
+4744
+4745
+4746
+4747
+4748
+4749
+4750
+4751
+4752
+4753
+4754
+4755
+4756
+4757
+4758
+4759
+4760
+4761
+4762
+4763
+4764
+4765
+4766
+4767
+4768
+4769
+4770
+4771
+4772
+4773
+4774
+4775
+4776
+4777
+4778
+4779
+4780
+4781
+4782
+4783
+4784
+4785
+4786
+4787
+4788
+4789
+4790
+4791
+4792
+4793
+4794
+4795
+4796
+4797
+4798
+4799
+4800
+4801
+4802
+4803
+4804
+4805
+4806
+4807
+4808
+4809
+4810
+4811
+4812
+4813
+4814
+4815
+4816
+4817
+4818
+4819
+4820
+4821
+4822
+4823
+4824
+4825
+4826
+4827
+4828
+4829
+4830
+4831
+4832
+4833
+4834
+4835
+4836
+4837
+4838
+4839
+4840
+4841
+4842
+4843
+4844
+4845
+4846
+4847
+4848
+4849
+4850
+4851
+4852
+4853
+4854
+4855
+4856
+4857
+4858
+4859
+4860
+4861
+4862
+4863
+4864
+4865
+4866
+4867
+4868
+4869
+4870
+4871
+4872
+4873
+4874
+4875
+4876
+4877
+4878
+4879
+4880
+4881
+4882
+4883
+4884
+4885
+4886
+4887
+4888
+4889
+4890
+4891
+4892
+4893
+4894
+4895
+4896
+4897
+4898
+4899
+4900
+4901
+4902
+4903
+4904
+4905
+4906
+4907
+4908
+4909
+4910
+4911
+4912
+4913
+4914
+4915
+4916
+4917
+4918
+4919
+4920
+4921
+4922
+4923
+4924
+4925
+4926
+4927
+4928
+4929
+4930
+4931
+4932
+4933
+4934
+4935
+4936
+4937
+4938
+4939
+4940
+4941
+4942
+4943
+4944
+4945
+4946
+4947
+4948
+4949
+4950
+4951
+4952
+4953
+4954
+4955
+4956
+4957
+4958
+4959
+4960
+4961
+4962
+4963
+4964
+4965
+4966
+4967
+4968
+4969
+4970
+4971
+4972
+4973
+4974
+4975
+4976
+4977
+4978
+4979
+4980
+4981
+4982
+4983
+4984
+4985
+4986
+4987
+4988
+4989
+4990
+4991
+4992
+4993
+4994
+4995
+4996
+4997
+4998
+4999
+5000
+5001
+5002
+5003
+5004
+5005
+5006
+5007
+5008
+5009
+5010
+5011
+5012
+5013
+5014
+5015
+5016
+5017
+5018
+5019
+5020
+5021
+5022
+5023
+5024
+5025
+5026
+5027
+5028
+5029
+5030
+5031
+5032
+5033
+5034
+5035
+5036
+5037
+5038
+5039
+5040
+5041
+5042
+5043
+5044
+5045
+5046
+5047
+5048
+5049
+5050
+5051
+5052
+5053
+5054
+5055
+5056
+5057
+5058
+5059
+5060
+5061
+5062
+5063
+5064
+5065
+5066
+5067
+5068
+5069
+5070
+5071
+5072
+5073
+5074
+5075
+5076
+5077
+5078
+5079
+5080
+5081
+5082
+5083
+5084
+5085
+5086
/*
+ * Copyright (C) 2003, 2004 Red Hat, Inc.
+ * Copyright (C) 2012-2021 MATE Developers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/* used for realpath() */
+#define _XOPEN_SOURCE 500
+
+#include <config.h>
+
+#include "matemenu-tree.h"
+
+#include <gio/gio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+#include "menu-layout.h"
+#include "menu-monitor.h"
+#include "menu-util.h"
+
+/* private */
+typedef struct MateMenuTreeItem MateMenuTreeItem;
+#define MATEMENU_TREE_ITEM(i)      ((MateMenuTreeItem *)(i))
+#define MATEMENU_TREE_DIRECTORY(i) ((MateMenuTreeDirectory *)(i))
+#define MATEMENU_TREE_ENTRY(i)     ((MateMenuTreeEntry *)(i))
+#define MATEMENU_TREE_SEPARATOR(i) ((MateMenuTreeSeparator *)(i))
+#define MATEMENU_TREE_HEADER(i)    ((MateMenuTreeHeader *)(i))
+#define MATEMENU_TREE_ALIAS(i)     ((MateMenuTreeAlias *)(i))
+
+enum {
+  PROP_0,
+
+  PROP_MENU_BASENAME,
+  PROP_MENU_PATH,
+  PROP_FLAGS
+};
+
+#ifdef WITH_COLLECTION
+typedef enum
+{
+  OBJECT_DRAWER,
+  OBJECT_MENU,
+  OBJECT_LAUNCHER,
+  OBJECT_APPLET,
+  OBJECT_ACTION,
+  OBJECT_MENU_BAR,
+  OBJECT_SEPARATOR,
+} ObjectType;
+#endif /* WITH_COLLECTION */
+
+/* Signals */
+enum
+{
+  CHANGED,
+  LAST_SIGNAL
+};
+
+static guint matemenu_tree_signals [LAST_SIGNAL] = { 0 };
+
+struct _MateMenuTree
+{
+  GObject       parent_instance;
+
+  char *basename;
+  char *non_prefixed_basename;
+  char *path;
+  char *canonical_path;
+
+#ifdef WITH_COLLECTION
+  GPtrArray *collection_applet;
+  GSettings *settings;
+#endif /* WITH_COLLECTION */
+
+  MateMenuTreeFlags flags;
+
+  GSList *menu_file_monitors;
+
+  MenuLayoutNode *layout;
+  MateMenuTreeDirectory *root;
+  GHashTable *entries_by_id;
+
+  guint canonical : 1;
+  guint loaded    : 1;
+};
+
+G_DEFINE_TYPE (MateMenuTree, matemenu_tree, G_TYPE_OBJECT)
+
+struct MateMenuTreeItem
+{
+  volatile gint refcount;
+
+  MateMenuTreeItemType type;
+
+  MateMenuTreeDirectory *parent;
+  MateMenuTree *tree;
+};
+
+struct MateMenuTreeIter
+{
+  volatile gint refcount;
+
+  MateMenuTreeItem *item;
+  GSList        *contents;
+  GSList        *contents_iter;
+};
+
+struct MateMenuTreeDirectory
+{
+  MateMenuTreeItem  item;
+
+  DesktopEntry     *directory_entry;
+  char             *name;
+
+  GSList           *entries;
+  GSList           *subdirs;
+
+  MenuLayoutValues  default_layout_values;
+  GSList           *default_layout_info;
+  GSList           *layout_info;
+  GSList           *contents;
+
+  guint             only_unallocated         : 1;
+  guint             is_nodisplay             : 1;
+  guint             layout_pending_separator : 1;
+  guint             preprocessed             : 1;
+
+  /* 16 bits should be more than enough; G_MAXUINT16 means no inline header */
+  guint             will_inline_header       : 16;
+};
+
+struct MateMenuTreeEntry
+{
+  MateMenuTreeItem  item;
+
+  DesktopEntry     *desktop_entry;
+  char             *desktop_file_id;
+
+  guint             is_excluded    : 1;
+  guint             is_unallocated : 1;
+};
+
+struct MateMenuTreeSeparator
+{
+  MateMenuTreeItem item;
+};
+
+struct MateMenuTreeHeader
+{
+  MateMenuTreeItem item;
+
+  MateMenuTreeDirectory *directory;
+};
+
+struct MateMenuTreeAlias
+{
+  MateMenuTreeItem item;
+
+  MateMenuTreeDirectory *directory;
+  MateMenuTreeItem      *aliased_item;
+};
+
+static gboolean  matemenu_tree_load_layout          (MateMenuTree       *tree,
+                                                  GError         **error);
+static void      matemenu_tree_force_reload         (MateMenuTree       *tree);
+static gboolean  matemenu_tree_build_from_layout    (MateMenuTree       *tree,
+                                                  GError         **error);
+static void      matemenu_tree_force_rebuild        (MateMenuTree       *tree);
+static void      matemenu_tree_resolve_files        (MateMenuTree       *tree,
+						  GHashTable      *loaded_menu_files,
+						  MenuLayoutNode  *layout);
+static void      matemenu_tree_force_recanonicalize (MateMenuTree       *tree);
+static void      matemenu_tree_invoke_monitors      (MateMenuTree       *tree);
+
+static void matemenu_tree_item_unref_and_unset_parent (gpointer itemp);
+
+#ifdef WITH_COLLECTION
+static void collection_applet_changed (GSettings    *settings,
+                                       gchar        *key,
+                                       MateMenuTree *self);
+#endif /* WITH_COLLECTION */
+
+typedef enum
+{
+  MENU_FILE_MONITOR_INVALID = 0,
+  MENU_FILE_MONITOR_FILE,
+  MENU_FILE_MONITOR_NONEXISTENT_FILE,
+  MENU_FILE_MONITOR_DIRECTORY
+} MenuFileMonitorType;
+
+typedef struct
+{
+  MenuFileMonitorType  type;
+  MenuMonitor         *monitor;
+} MenuFileMonitor;
+
+static void
+handle_nonexistent_menu_file_changed (MenuMonitor      *monitor,
+				      MenuMonitorEvent  event,
+				      const char       *path,
+				      MateMenuTree        *tree)
+{
+  if (event == MENU_MONITOR_EVENT_CHANGED ||
+      event == MENU_MONITOR_EVENT_CREATED)
+    {
+      menu_verbose ("\"%s\" %s, marking tree for recanonicalization\n",
+                    path,
+                    event == MENU_MONITOR_EVENT_CREATED ? "created" : "changed");
+
+      matemenu_tree_force_recanonicalize (tree);
+      matemenu_tree_invoke_monitors (tree);
+    }
+}
+
+static void
+handle_menu_file_changed (MenuMonitor      *monitor,
+			  MenuMonitorEvent  event,
+			  const char       *path,
+                          MateMenuTree        *tree)
+{
+  menu_verbose ("\"%s\" %s, marking tree for recanicalization\n",
+		path,
+		event == MENU_MONITOR_EVENT_CREATED ? "created" :
+		event == MENU_MONITOR_EVENT_CHANGED ? "changed" : "deleted");
+
+  matemenu_tree_force_recanonicalize (tree);
+  matemenu_tree_invoke_monitors (tree);
+}
+
+static void
+handle_menu_file_directory_changed (MenuMonitor      *monitor,
+				    MenuMonitorEvent  event,
+				    const char       *path,
+				    MateMenuTree        *tree)
+{
+  if (!g_str_has_suffix (path, ".menu"))
+    return;
+
+  menu_verbose ("\"%s\" %s, marking tree for recanicalization\n",
+		path,
+		event == MENU_MONITOR_EVENT_CREATED ? "created" :
+		event == MENU_MONITOR_EVENT_CHANGED ? "changed" : "deleted");
+
+  matemenu_tree_force_recanonicalize (tree);
+  matemenu_tree_invoke_monitors (tree);
+}
+
+static void
+matemenu_tree_add_menu_file_monitor (MateMenuTree           *tree,
+				  const char          *path,
+				  MenuFileMonitorType  type)
+{
+  MenuFileMonitor *monitor;
+
+  monitor = g_slice_new0 (MenuFileMonitor);
+
+  monitor->type = type;
+
+  switch (type)
+    {
+    case MENU_FILE_MONITOR_FILE:
+      menu_verbose ("Adding a menu file monitor for \"%s\"\n", path);
+
+      monitor->monitor = menu_get_file_monitor (path);
+      menu_monitor_add_notify (monitor->monitor,
+			       (MenuMonitorNotifyFunc) handle_menu_file_changed,
+			       tree);
+      break;
+
+    case MENU_FILE_MONITOR_NONEXISTENT_FILE:
+      menu_verbose ("Adding a menu file monitor for non-existent \"%s\"\n", path);
+
+      monitor->monitor = menu_get_file_monitor (path);
+      menu_monitor_add_notify (monitor->monitor,
+			       (MenuMonitorNotifyFunc) handle_nonexistent_menu_file_changed,
+			       tree);
+      break;
+
+    case MENU_FILE_MONITOR_DIRECTORY:
+      menu_verbose ("Adding a menu directory monitor for \"%s\"\n", path);
+
+      monitor->monitor = menu_get_directory_monitor (path);
+      menu_monitor_add_notify (monitor->monitor,
+			       (MenuMonitorNotifyFunc) handle_menu_file_directory_changed,
+			       tree);
+      break;
+
+    default:
+      g_assert_not_reached ();
+      break;
+    }
+
+  tree->menu_file_monitors = g_slist_prepend (tree->menu_file_monitors, monitor);
+}
+
+static void
+remove_menu_file_monitor (MenuFileMonitor *monitor,
+			  MateMenuTree       *tree)
+{
+  switch (monitor->type)
+    {
+    case MENU_FILE_MONITOR_FILE:
+      menu_monitor_remove_notify (monitor->monitor,
+				  (MenuMonitorNotifyFunc) handle_menu_file_changed,
+				  tree);
+      break;
+
+    case MENU_FILE_MONITOR_NONEXISTENT_FILE:
+      menu_monitor_remove_notify (monitor->monitor,
+				  (MenuMonitorNotifyFunc) handle_nonexistent_menu_file_changed,
+				  tree);
+      break;
+
+    case MENU_FILE_MONITOR_DIRECTORY:
+      menu_monitor_remove_notify (monitor->monitor,
+				  (MenuMonitorNotifyFunc) handle_menu_file_directory_changed,
+				  tree);
+      break;
+
+    default:
+      g_assert_not_reached ();
+      break;
+    }
+
+  menu_monitor_unref (monitor->monitor);
+  monitor->monitor = NULL;
+
+  monitor->type = MENU_FILE_MONITOR_INVALID;
+
+  g_slice_free (MenuFileMonitor, monitor);
+}
+
+static void
+matemenu_tree_remove_menu_file_monitors (MateMenuTree *tree)
+{
+  menu_verbose ("Removing all menu file monitors\n");
+
+  g_slist_foreach (tree->menu_file_monitors,
+                   (GFunc) remove_menu_file_monitor,
+                   tree);
+  g_slist_free (tree->menu_file_monitors);
+  tree->menu_file_monitors = NULL;
+}
+
+static gboolean
+canonicalize_path (MateMenuTree  *tree,
+                   const char *path)
+{
+  tree->canonical_path = realpath (path, NULL);
+  if (tree->canonical_path)
+    {
+      tree->canonical = TRUE;
+      matemenu_tree_add_menu_file_monitor (tree,
+					tree->canonical_path,
+					MENU_FILE_MONITOR_FILE);
+    }
+  else
+    {
+      matemenu_tree_add_menu_file_monitor (tree,
+					path,
+					MENU_FILE_MONITOR_NONEXISTENT_FILE);
+    }
+
+  return tree->canonical;
+}
+
+static gboolean
+canonicalize_basename_with_config_dir (MateMenuTree   *tree,
+                                       const char *basename,
+                                       const char *config_dir)
+{
+  gboolean  ret;
+  char     *path;
+
+  path = g_build_filename (config_dir, "menus",  basename,  NULL);
+  ret = canonicalize_path (tree, path);
+  g_free (path);
+
+  return ret;
+}
+
+static void
+canonicalize_basename (MateMenuTree  *tree,
+                       const char *basename)
+{
+  if (!canonicalize_basename_with_config_dir (tree,
+                                              basename,
+                                              g_get_user_config_dir ()))
+    {
+      const char * const *system_config_dirs;
+      int                 i;
+
+      system_config_dirs = g_get_system_config_dirs ();
+
+      i = 0;
+      while (system_config_dirs[i] != NULL)
+        {
+          if (canonicalize_basename_with_config_dir (tree,
+                                                     basename,
+                                                     system_config_dirs[i]))
+            break;
+
+          ++i;
+        }
+    }
+}
+
+static gboolean matemenu_tree_canonicalize_path(MateMenuTree* tree,
+                              GError   **error)
+{
+  const char *menu_file = NULL;
+
+  if (tree->canonical)
+    return TRUE;
+
+	g_assert(tree->canonical_path == NULL);
+
+  matemenu_tree_remove_menu_file_monitors (tree);
+
+  if (tree->path)
+    {
+      menu_file = tree->path;
+      canonicalize_path (tree, tree->path);
+    }
+  else
+    {
+      const gchar *xdg_menu_prefix;
+
+      menu_file = tree->basename;
+      xdg_menu_prefix = g_getenv ("XDG_MENU_PREFIX");
+
+      if (xdg_menu_prefix != NULL)
+        {
+          gchar *prefixed_basename;
+
+          prefixed_basename = g_strdup_printf ("%sapplications.menu",
+                                               xdg_menu_prefix);
+
+          /* Some gnome-menus using applications just use "applications.menu"
+           * as the basename and expect gnome-menus to prefix it. Others (e.g.
+           * Alacarte) explicitly use "${XDG_MENU_PREFIX}applications.menu" as
+           * the basename, because they want to save changes to the right files
+           * in ~. In both cases, we want to use "applications-merged" as the
+           * merge directory (as required by the fd.o menu spec), so we save
+           * the non-prefixed basename and use it later when calling
+           * menu_layout_load().
+           */
+          if (!g_strcmp0 (tree->basename, "mate-applications.menu") ||
+              !g_strcmp0 (tree->basename, prefixed_basename))
+            {
+              canonicalize_basename (tree, prefixed_basename);
+              g_free (tree->non_prefixed_basename);
+              tree->non_prefixed_basename = g_strdup ("mate-applications.menu");
+            }
+          g_free (prefixed_basename);
+        }
+
+      if (!tree->canonical)
+        canonicalize_basename (tree, tree->basename);
+    }
+
+  if (tree->canonical)
+    {
+      menu_verbose ("Successfully looked up menu_file for \"%s\": %s\n",
+                    menu_file, tree->canonical_path);
+      return TRUE;
+    }
+  else
+    {
+      g_set_error (error,
+                   G_IO_ERROR,
+                   G_IO_ERROR_FAILED,
+                   "Failed to look up menu_file for \"%s\"\n",
+                   menu_file);
+      return FALSE;
+    }
+}
+
+static void
+matemenu_tree_force_recanonicalize (MateMenuTree *tree)
+{
+  matemenu_tree_remove_menu_file_monitors (tree);
+
+  if (tree->canonical)
+    {
+      matemenu_tree_force_reload (tree);
+
+      g_free (tree->canonical_path);
+      tree->canonical_path = NULL;
+
+      tree->canonical = FALSE;
+    }
+}
+
+/**
+ * matemenu_tree_new:
+ * @menu_basename: Basename of menu file
+ * @flags: Flags controlling menu content
+ *
+ * Returns: (transfer full): A new #MateMenuTree instance
+ */
+MateMenuTree *
+matemenu_tree_new (const char     *menu_basename,
+                MateMenuTreeFlags  flags)
+{
+  g_return_val_if_fail (menu_basename != NULL, NULL);
+
+  return g_object_new (MATEMENU_TYPE_TREE,
+                       "menu-basename", menu_basename,
+                       "flags", flags,
+                       NULL);
+}
+
+/**
+ * matemenu_tree_new_fo_path:
+ * @menu_path: Path of menu file
+ * @flags: Flags controlling menu content
+ *
+ * Returns: (transfer full): A new #MateMenuTree instance
+ */
+MateMenuTree *
+matemenu_tree_new_for_path (const char     *menu_path,
+                         MateMenuTreeFlags  flags)
+{
+  g_return_val_if_fail (menu_path != NULL, NULL);
+
+  return g_object_new (MATEMENU_TYPE_TREE,
+                       "menu-path", menu_path,
+                       "flags", flags,
+                       NULL);
+}
+
+static GObject *
+matemenu_tree_constructor (GType                  type,
+                        guint                  n_construct_properties,
+                        GObjectConstructParam *construct_properties)
+{
+	GObject   *obj;
+	MateMenuTree *self;
+
+	obj = G_OBJECT_CLASS (matemenu_tree_parent_class)->constructor (type,
+                                                                     n_construct_properties,
+                                                                     construct_properties);
+
+        /* If MateMenuTree:menu-path is set, then we should make sure that
+         * MateMenuTree:menu-basename is unset (especially as it has a default
+         * value). This has to be done here, in the constructor, since the
+         * properties are construct-only. */
+
+	self = MATEMENU_TREE (obj);
+
+        if (self->path != NULL)
+          g_object_set (self, "menu-basename", NULL, NULL);
+
+	return obj;
+}
+
+static void
+matemenu_tree_set_property (GObject         *object,
+                         guint            prop_id,
+                         const GValue    *value,
+                         GParamSpec      *pspec)
+{
+  MateMenuTree *self = MATEMENU_TREE (object);
+
+  switch (prop_id)
+    {
+    case PROP_MENU_BASENAME:
+      self->basename = g_value_dup_string (value);
+      break;
+
+    case PROP_MENU_PATH:
+      self->path = g_value_dup_string (value);
+      break;
+
+    case PROP_FLAGS:
+      self->flags = g_value_get_flags (value);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+matemenu_tree_get_property (GObject         *object,
+                         guint            prop_id,
+                         GValue          *value,
+                         GParamSpec      *pspec)
+{
+  MateMenuTree *self = MATEMENU_TREE (object);
+
+  switch (prop_id)
+    {
+    case PROP_MENU_BASENAME:
+      g_value_set_string (value, self->basename);
+      break;
+    case PROP_MENU_PATH:
+      g_value_set_string (value, self->path);
+      break;
+    case PROP_FLAGS:
+      g_value_set_flags (value, self->flags);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+matemenu_tree_finalize (GObject *object)
+{
+  MateMenuTree *tree = MATEMENU_TREE (object);
+
+  matemenu_tree_force_recanonicalize (tree);
+
+  if (tree->basename != NULL)
+    g_free (tree->basename);
+  tree->basename = NULL;
+
+  g_free (tree->non_prefixed_basename);
+  tree->non_prefixed_basename = NULL;
+
+  if (tree->path != NULL)
+    g_free (tree->path);
+  tree->path = NULL;
+
+  if (tree->canonical_path != NULL)
+    g_free (tree->canonical_path);
+  tree->canonical_path = NULL;
+
+  g_hash_table_destroy (tree->entries_by_id);
+  tree->entries_by_id = NULL;
+
+#ifdef WITH_COLLECTION
+  if (tree->collection_applet != NULL)
+  {
+    g_ptr_array_free (tree->collection_applet, TRUE);
+    tree->collection_applet = NULL;
+  }
+
+  g_signal_handlers_disconnect_by_func (tree->settings,
+                                        G_CALLBACK (collection_applet_changed),
+                                        tree);
+
+  g_object_unref (tree->settings);
+#endif /* WITH_COLLECTION */
+
+  G_OBJECT_CLASS (matemenu_tree_parent_class)->finalize (object);
+}
+
+#ifdef WITH_COLLECTION
+static void
+load_object (char         *id,
+             MateMenuTree *self)
+{
+  ObjectType   object_type;
+  char        *object_path;
+  GSettings   *settings;
+
+  object_path = g_strdup_printf ("/org/mate/panel/objects/%s/", id);
+  settings = g_settings_new_with_path ("org.mate.panel.object", object_path);
+
+  object_type = g_settings_get_enum (settings, "object-type");
+  if (object_type == OBJECT_LAUNCHER)
+  {
+    char *location;
+    char *desktop_name;
+
+    if (self->collection_applet == NULL)
+        self->collection_applet = g_ptr_array_new_with_free_func ((GDestroyNotify) g_free);
+
+    location = g_settings_get_string (settings, "launcher-location");
+    desktop_name = g_path_get_basename (location);
+    if (strstr (desktop_name, "-1.") != NULL )
+    {
+      char **str;
+
+      str = g_strsplit (desktop_name, "-1.", -1);
+      g_free (desktop_name);
+      desktop_name = g_strdup_printf ("%s.%s", str[0], str[1]);
+      g_strfreev (str);
+    }
+    g_ptr_array_add (self->collection_applet, desktop_name);
+    g_free (location);
+  }
+  g_free (object_path);
+  g_object_unref (settings);
+}
+
+static gboolean
+emit_changed_signal (gpointer data)
+{
+  MateMenuTree *self = data;
+  matemenu_tree_force_rebuild (self);
+  matemenu_tree_invoke_monitors (self);
+
+  return FALSE;
+}
+
+static void
+get_panel_collection_applet (MateMenuTree *self)
+{
+  gchar **list;
+  guint   i;
+
+  list = g_settings_get_strv (self->settings, "object-id-list");
+  for (i = 0; list[i]; i++)
+  {
+    load_object (list[i], self);
+  }
+  g_strfreev (list);
+}
+
+static void
+collection_applet_changed (GSettings    *settings,
+                           gchar        *key,
+                           MateMenuTree *self)
+{
+  if (self->collection_applet != NULL)
+  {
+    g_ptr_array_free (self->collection_applet, TRUE);
+    self->collection_applet = NULL;
+  }
+  get_panel_collection_applet (self);
+  g_idle_add (emit_changed_signal, (gpointer)self);
+}
+#endif /* WITH_COLLECTION */
+
+static void
+matemenu_tree_init (MateMenuTree *self)
+{
+  self->entries_by_id = g_hash_table_new (g_str_hash, g_str_equal);
+#ifdef WITH_COLLECTION
+  self->collection_applet = NULL;
+  self->settings = g_settings_new ("org.mate.panel");
+  get_panel_collection_applet (self);
+  g_signal_connect (self->settings, "changed::object-id-list",
+                    G_CALLBACK (collection_applet_changed),
+                    self);
+#endif /* WITH_COLLECTION */
+}
+
+static void
+matemenu_tree_class_init (MateMenuTreeClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  gobject_class->constructor = matemenu_tree_constructor;
+  gobject_class->get_property = matemenu_tree_get_property;
+  gobject_class->set_property = matemenu_tree_set_property;
+  gobject_class->finalize = matemenu_tree_finalize;
+
+  /**
+   * MateMenuTree:menu-basename:
+   *
+   * The name of the menu file; must be a basename or a relative path. The file
+   * will be looked up in $XDG_CONFIG_DIRS/menus/. See the Desktop Menu
+   * specification.
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_MENU_BASENAME,
+                                   g_param_spec_string ("menu-basename", "", "",
+                                                        "applications.menu",
+                                                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+  /**
+   * MateMenuTree:menu-path:
+   *
+   * The full path of the menu file. If set, MateMenuTree:menu-basename will get
+   * ignored.
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_MENU_PATH,
+                                   g_param_spec_string ("menu-path", "", "",
+                                                        NULL,
+                                                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+  /**
+   * MateMenuTree:flags:
+   *
+   * Flags controlling the content of the menu.
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_FLAGS,
+                                   g_param_spec_flags ("flags", "", "",
+                                                       MATEMENU_TYPE_TREE_FLAGS,
+                                                       MATEMENU_TREE_FLAGS_NONE,
+                                                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+  /**
+   * MateMenuTree:changed:
+   *
+   * This signal is emitted when applications are added, removed, or
+   * upgraded.  But note the new data will only be visible after
+   * matemenu_tree_load_sync() or a variant thereof is invoked.
+   */
+  matemenu_tree_signals[CHANGED] =
+      g_signal_new ("changed",
+                    G_TYPE_FROM_CLASS (klass),
+                    G_SIGNAL_RUN_LAST,
+                    0,
+                    NULL, NULL,
+                    g_cclosure_marshal_VOID__VOID,
+                    G_TYPE_NONE, 0);
+}
+
+/**
+ * matemenu_tree_get_canonical_menu_path:
+ * @tree: a #MateMenuTree
+ *
+ * This function is only available if the tree has been loaded via
+ * matemenu_tree_load_sync() or a variant thereof.
+ *
+ * Returns: The absolute and canonicalized path to the loaded menu file
+ */
+const char *
+matemenu_tree_get_canonical_menu_path (MateMenuTree *tree)
+{
+  g_return_val_if_fail (MATEMENU_IS_TREE (tree), NULL);
+  g_return_val_if_fail (tree->loaded, NULL);
+
+  return tree->canonical_path;
+}
+
+/**
+ * matemenu_tree_load_sync:
+ * @tree: a #MateMenuTree
+ * @error: a #GError
+ *
+ * Synchronously load the menu contents.  This function
+ * performs a significant amount of blocking I/O if the
+ * tree has not been loaded yet.
+ *
+ * Returns: %TRUE on success, %FALSE on error
+ */
+gboolean
+matemenu_tree_load_sync (MateMenuTree  *tree,
+                      GError    **error)
+{
+  GError *local_error = NULL;
+
+  if (tree->loaded)
+    return TRUE;
+
+  if (!matemenu_tree_build_from_layout (tree, &local_error))
+    {
+      if (local_error)
+        g_propagate_error (error, local_error);
+      return FALSE;
+    }
+
+  tree->loaded = TRUE;
+
+  return TRUE;
+}
+
+/**
+ * matemenu_tree_get_root_directory:
+ * @tree: a #MateMenuTree
+ *
+ * Get the root directory; you must have loaded the tree first (at
+ * least once) via matemenu_tree_load_sync() or a variant thereof.
+ *
+ * Returns: (transfer full): Root of the tree
+ */
+MateMenuTreeDirectory *
+matemenu_tree_get_root_directory (MateMenuTree *tree)
+{
+  g_return_val_if_fail (tree != NULL, NULL);
+  g_return_val_if_fail (tree->loaded, NULL);
+
+  return matemenu_tree_item_ref (tree->root);
+}
+
+static MateMenuTreeDirectory *
+find_path (MateMenuTreeDirectory *directory,
+	   const char         *path)
+{
+  const char *name;
+  char       *slash;
+  char       *freeme;
+  GSList     *tmp;
+
+  while (path[0] == G_DIR_SEPARATOR) path++;
+
+  if (path[0] == '\0')
+    return directory;
+
+  freeme = NULL;
+  slash = strchr (path, G_DIR_SEPARATOR);
+  if (slash)
+    {
+      name = freeme = g_strndup (path, (gsize)(slash - path));
+      path = slash + 1;
+    }
+  else
+    {
+      name = path;
+      path = NULL;
+    }
+
+  tmp = directory->contents;
+  while (tmp != NULL)
+    {
+      MateMenuTreeItem *item = tmp->data;
+
+      if (item->type != MATEMENU_TREE_ITEM_DIRECTORY)
+        {
+          tmp = tmp->next;
+          continue;
+        }
+
+      if (!strcmp (name, MATEMENU_TREE_DIRECTORY (item)->name))
+	{
+	  g_free (freeme);
+
+	  if (path)
+	    return find_path (MATEMENU_TREE_DIRECTORY (item), path);
+	  else
+	    return MATEMENU_TREE_DIRECTORY (item);
+	}
+
+      tmp = tmp->next;
+    }
+
+  g_free (freeme);
+
+  return NULL;
+}
+
+MateMenuTreeDirectory *
+matemenu_tree_get_directory_from_path (MateMenuTree  *tree,
+				    const char *path)
+{
+  MateMenuTreeDirectory *root;
+  MateMenuTreeDirectory *directory;
+
+  g_return_val_if_fail (tree != NULL, NULL);
+  g_return_val_if_fail (path != NULL, NULL);
+
+  if (path[0] != G_DIR_SEPARATOR)
+    return NULL;
+
+  if (!(root = matemenu_tree_get_root_directory (tree)))
+    return NULL;
+
+  directory = find_path (root, path);
+
+  matemenu_tree_item_unref (root);
+
+  return directory ? matemenu_tree_item_ref (directory) : NULL;
+}
+
+/**
+ * matemenu_tree_get_entry_by_id:
+ * @tree: a #MateMenuTree
+ * @id: a desktop file ID
+ *
+ * Look up the entry corresponding to the given "desktop file id".
+ *
+ * Returns: (transfer full): A newly referenced #MateMenuTreeEntry, or %NULL if none
+ */
+MateMenuTreeEntry     *
+matemenu_tree_get_entry_by_id (MateMenuTree  *tree,
+			    const char *id)
+{
+  MateMenuTreeEntry *entry;
+
+  g_return_val_if_fail (tree->loaded, NULL);
+
+  entry = g_hash_table_lookup (tree->entries_by_id, id);
+  if (entry != NULL)
+    matemenu_tree_item_ref (entry);
+
+  return entry;
+}
+
+static void
+matemenu_tree_invoke_monitors (MateMenuTree *tree)
+{
+  g_signal_emit (tree, matemenu_tree_signals[CHANGED], 0);
+}
+
+static MateMenuTreeDirectory *
+get_parent (MateMenuTreeItem *item)
+{
+  g_return_val_if_fail (item != NULL, NULL);
+  return item->parent ? matemenu_tree_item_ref (item->parent) : NULL;
+}
+
+/**
+ * matemenu_tree_directory_get_parent:
+ * @directory: a #MateMenuTreeDirectory
+ *
+ * Returns: (transfer full): The parent directory, or %NULL if none
+ */
+MateMenuTreeDirectory *
+matemenu_tree_directory_get_parent (MateMenuTreeDirectory *directory)
+{
+  return get_parent ((MateMenuTreeItem *)directory);
+}
+
+/**
+ * matemenu_tree_entry_get_parent:
+ * @entry: a #MateMenuTreeEntry
+ *
+ * Returns: (transfer full): The parent directory, or %NULL if none
+ */
+MateMenuTreeDirectory *
+matemenu_tree_entry_get_parent (MateMenuTreeEntry *entry)
+{
+  return get_parent ((MateMenuTreeItem *)entry);
+}
+
+/**
+ * matemenu_tree_alias_get_parent:
+ * @alias: a #MateMenuTreeAlias
+ *
+ * Returns: (transfer full): The parent directory, or %NULL if none
+ */
+MateMenuTreeDirectory *
+matemenu_tree_alias_get_parent (MateMenuTreeAlias *alias)
+{
+  return get_parent ((MateMenuTreeItem *)alias);
+}
+
+/**
+ * matemenu_tree_header_get_parent:
+ * @header: a #MateMenuTreeHeader
+ *
+ * Returns: (transfer full): The parent directory, or %NULL if none
+ */
+MateMenuTreeDirectory *
+matemenu_tree_header_get_parent (MateMenuTreeHeader *header)
+{
+  return get_parent ((MateMenuTreeItem *)header);
+}
+
+/**
+ * matemenu_tree_separator_get_parent:
+ * @separator: a #MateMenuTreeSeparator
+ *
+ * Returns: (transfer full): The parent directory, or %NULL if none
+ */
+MateMenuTreeDirectory *
+matemenu_tree_separator_get_parent (MateMenuTreeSeparator *separator)
+{
+  return get_parent ((MateMenuTreeItem *)separator);
+}
+
+static void
+matemenu_tree_item_set_parent (MateMenuTreeItem      *item,
+			    MateMenuTreeDirectory *parent)
+{
+  g_return_if_fail (item != NULL);
+
+  item->parent = parent;
+}
+
+/**
+ * matemenu_tree_iter_ref: (skip)
+ * @iter: iter
+ *
+ * Increment the reference count of @iter
+ */
+MateMenuTreeIter *
+matemenu_tree_iter_ref (MateMenuTreeIter *iter)
+{
+  g_atomic_int_inc (&iter->refcount);
+  return iter;
+}
+
+/**
+ * matemenu_tree_iter_unref: (skip)
+ * @iter: iter
+ *
+ * Decrement the reference count of @iter
+ */
+void
+matemenu_tree_iter_unref (MateMenuTreeIter *iter)
+{
+  if (!g_atomic_int_dec_and_test (&iter->refcount))
+    return;
+
+  g_slist_free_full (iter->contents, (GDestroyNotify)matemenu_tree_item_unref);
+
+  g_slice_free (MateMenuTreeIter, iter);
+}
+
+/**
+ * matemenu_tree_directory_iter:
+ * @directory: directory
+ *
+ * Returns: (transfer full): A new iterator over the directory contents
+ */
+MateMenuTreeIter *
+matemenu_tree_directory_iter (MateMenuTreeDirectory *directory)
+{
+  MateMenuTreeIter *iter;
+
+  g_return_val_if_fail (directory != NULL, NULL);
+
+  iter = g_slice_new0 (MateMenuTreeIter);
+  iter->refcount = 1;
+
+  iter->contents = g_slist_copy (directory->contents);
+  iter->contents_iter = iter->contents;
+  g_slist_foreach (iter->contents, (GFunc) matemenu_tree_item_ref, NULL);
+
+  return iter;
+}
+
+/**
+ * matemenu_tree_iter_next:
+ * @iter: iter
+ *
+ * Change the iterator to the next item, and return its type.  If
+ * there are no more items, %MATEMENU_TREE_ITEM_INVALID is returned.
+ *
+ * Returns: The type of the next item that can be retrived from the iterator
+ */
+MateMenuTreeItemType
+matemenu_tree_iter_next (MateMenuTreeIter *iter)
+{
+  g_return_val_if_fail (iter != NULL, MATEMENU_TREE_ITEM_INVALID);
+
+  if (iter->contents_iter)
+    {
+      iter->item = iter->contents_iter->data;
+      iter->contents_iter = iter->contents_iter->next;
+      return iter->item->type;
+    }
+  else
+    return MATEMENU_TREE_ITEM_INVALID;
+}
+
+/**
+ * matemenu_tree_iter_get_directory:
+ * @iter: iter
+ *
+ * This method may only be called if matemenu_tree_iter_next()
+ * returned MATEMENU_TREE_ITEM_DIRECTORY.
+ *
+ * Returns: (transfer full): A directory
+ */
+MateMenuTreeDirectory *
+matemenu_tree_iter_get_directory (MateMenuTreeIter *iter)
+{
+  g_return_val_if_fail (iter != NULL, NULL);
+  g_return_val_if_fail (iter->item != NULL, NULL);
+  g_return_val_if_fail (iter->item->type == MATEMENU_TREE_ITEM_DIRECTORY, NULL);
+
+  return (MateMenuTreeDirectory*)matemenu_tree_item_ref (iter->item);
+}
+
+/**
+ * matemenu_tree_iter_get_entry:
+ * @iter: iter
+ *
+ * This method may only be called if matemenu_tree_iter_next()
+ * returned MATEMENU_TREE_ITEM_ENTRY.
+ *
+ * Returns: (transfer full): An entry
+ */
+MateMenuTreeEntry *
+matemenu_tree_iter_get_entry (MateMenuTreeIter *iter)
+{
+  g_return_val_if_fail (iter != NULL, NULL);
+  g_return_val_if_fail (iter->item != NULL, NULL);
+  g_return_val_if_fail (iter->item->type == MATEMENU_TREE_ITEM_ENTRY, NULL);
+
+  return (MateMenuTreeEntry*)matemenu_tree_item_ref (iter->item);
+}
+
+/**
+ * matemenu_tree_iter_get_header:
+ * @iter: iter
+ *
+ * This method may only be called if matemenu_tree_iter_next()
+ * returned MATEMENU_TREE_ITEM_HEADER.
+ *
+ * Returns: (transfer full): A header
+ */
+MateMenuTreeHeader *
+matemenu_tree_iter_get_header (MateMenuTreeIter *iter)
+{
+  g_return_val_if_fail (iter != NULL, NULL);
+  g_return_val_if_fail (iter->item != NULL, NULL);
+  g_return_val_if_fail (iter->item->type == MATEMENU_TREE_ITEM_HEADER, NULL);
+
+  return (MateMenuTreeHeader*)matemenu_tree_item_ref (iter->item);
+}
+
+/**
+ * matemenu_tree_iter_get_alias:
+ * @iter: iter
+ *
+ * This method may only be called if matemenu_tree_iter_next()
+ * returned MATEMENU_TREE_ITEM_ALIAS.
+ *
+ * Returns: (transfer full): An alias
+ */
+MateMenuTreeAlias *
+matemenu_tree_iter_get_alias (MateMenuTreeIter *iter)
+{
+  g_return_val_if_fail (iter != NULL, NULL);
+  g_return_val_if_fail (iter->item != NULL, NULL);
+  g_return_val_if_fail (iter->item->type == MATEMENU_TREE_ITEM_ALIAS, NULL);
+
+  return (MateMenuTreeAlias*)matemenu_tree_item_ref (iter->item);
+}
+
+/**
+ * matemenu_tree_iter_get_separator:
+ * @iter: iter
+ *
+ * This method may only be called if matemenu_tree_iter_next()
+ * returned #MATEMENU_TREE_ITEM_SEPARATOR.
+ *
+ * Returns: (transfer full): A separator
+ */
+MateMenuTreeSeparator *
+matemenu_tree_iter_get_separator (MateMenuTreeIter *iter)
+{
+  g_return_val_if_fail (iter != NULL, NULL);
+  g_return_val_if_fail (iter->item != NULL, NULL);
+  g_return_val_if_fail (iter->item->type == MATEMENU_TREE_ITEM_SEPARATOR, NULL);
+
+  return (MateMenuTreeSeparator*)matemenu_tree_item_ref (iter->item);
+}
+
+const char *
+matemenu_tree_directory_get_name (MateMenuTreeDirectory *directory)
+{
+  g_return_val_if_fail (directory != NULL, NULL);
+
+  if (!directory->directory_entry)
+    return directory->name;
+
+  return desktop_entry_get_name (directory->directory_entry);
+}
+
+const char *
+matemenu_tree_directory_get_generic_name (MateMenuTreeDirectory *directory)
+{
+  g_return_val_if_fail (directory != NULL, NULL);
+
+  if (!directory->directory_entry)
+    return NULL;
+
+  return desktop_entry_get_generic_name (directory->directory_entry);
+}
+
+const char *
+matemenu_tree_directory_get_comment (MateMenuTreeDirectory *directory)
+{
+  g_return_val_if_fail (directory != NULL, NULL);
+
+  if (!directory->directory_entry)
+    return NULL;
+
+  return desktop_entry_get_comment (directory->directory_entry);
+}
+
+/**
+ * matemenu_tree_directory_get_icon:
+ * @directory: a #MateMenuTreeDirectory
+ *
+ * Gets the icon for the directory.
+ *
+ * Returns: (transfer none): The #GIcon for this directory
+ */
+GIcon *
+matemenu_tree_directory_get_icon (MateMenuTreeDirectory *directory)
+{
+	g_return_val_if_fail(directory != NULL, NULL);
+
+	if (!directory->directory_entry)
+		return NULL;
+
+	return desktop_entry_get_icon(directory->directory_entry);
+}
+
+const char *
+matemenu_tree_directory_get_desktop_file_path (MateMenuTreeDirectory *directory)
+{
+  g_return_val_if_fail (directory != NULL, NULL);
+
+  if (!directory->directory_entry)
+    return NULL;
+
+  return desktop_entry_get_path (directory->directory_entry);
+}
+
+const char *
+matemenu_tree_directory_get_menu_id (MateMenuTreeDirectory *directory)
+{
+  g_return_val_if_fail (directory != NULL, NULL);
+
+  return directory->name;
+}
+
+gboolean
+matemenu_tree_directory_get_is_nodisplay (MateMenuTreeDirectory *directory)
+{
+  g_return_val_if_fail (directory != NULL, FALSE);
+
+  return directory->is_nodisplay;
+}
+
+/**
+ * matemenu_tree_directory_get_tree:
+ * @directory: A #MateMenuTreeDirectory
+ *
+ * Grab the tree associated with a #MateMenuTreeItem.
+ *
+ * Returns: (transfer full): The #MateMenuTree
+ */
+MateMenuTree *
+matemenu_tree_directory_get_tree (MateMenuTreeDirectory *directory)
+{
+  g_return_val_if_fail (directory != NULL, NULL);
+
+  return g_object_ref (directory->item.tree);
+}
+
+static void
+append_directory_path (MateMenuTreeDirectory *directory,
+		       GString            *path)
+{
+
+  if (!directory->item.parent)
+    {
+      g_string_append_c (path, G_DIR_SEPARATOR);
+      return;
+    }
+
+  append_directory_path (directory->item.parent, path);
+
+  g_string_append (path, directory->name);
+  g_string_append_c (path, G_DIR_SEPARATOR);
+}
+
+char *
+matemenu_tree_directory_make_path (MateMenuTreeDirectory *directory,
+				MateMenuTreeEntry     *entry)
+{
+  GString *path;
+
+  g_return_val_if_fail (directory != NULL, NULL);
+
+  path = g_string_new (NULL);
+
+  append_directory_path (directory, path);
+
+  if (entry != NULL)
+    g_string_append (path,
+		     desktop_entry_get_basename (entry->desktop_entry));
+
+  return g_string_free (path, FALSE);
+}
+
+/**
+ * matemenu_tree_entry_get_app_info:
+ * @entry: a #MateMenuTreeEntry
+ *
+ * Returns: (transfer none): The #GDesktopAppInfo for this entry
+ */
+GDesktopAppInfo *
+matemenu_tree_entry_get_app_info (MateMenuTreeEntry *entry)
+{
+  g_return_val_if_fail (entry != NULL, NULL);
+
+  return desktop_entry_get_app_info (entry->desktop_entry);
+}
+
+const char *
+matemenu_tree_entry_get_desktop_file_path (MateMenuTreeEntry *entry)
+{
+  g_return_val_if_fail (entry != NULL, NULL);
+
+  return desktop_entry_get_path (entry->desktop_entry);
+}
+
+const char *
+matemenu_tree_entry_get_desktop_file_id (MateMenuTreeEntry *entry)
+{
+  g_return_val_if_fail (entry != NULL, NULL);
+
+  return entry->desktop_file_id;
+}
+
+gboolean
+matemenu_tree_entry_get_is_nodisplay_recurse (MateMenuTreeEntry *entry)
+{
+  MateMenuTreeDirectory *directory;
+  GDesktopAppInfo *app_info;
+
+  g_return_val_if_fail (entry != NULL, FALSE);
+
+  app_info = matemenu_tree_entry_get_app_info (entry);
+
+  if (g_desktop_app_info_get_nodisplay (app_info))
+    return TRUE;
+
+  directory = entry->item.parent;
+  while (directory != NULL)
+    {
+      if (directory->is_nodisplay)
+        return TRUE;
+
+      directory = directory->item.parent;
+    }
+
+  return FALSE;
+}
+
+gboolean
+matemenu_tree_entry_get_is_excluded (MateMenuTreeEntry *entry)
+{
+  g_return_val_if_fail(entry != NULL, FALSE);
+
+  return entry->is_excluded;
+}
+
+gboolean
+matemenu_tree_entry_get_is_unallocated (MateMenuTreeEntry *entry)
+{
+  g_return_val_if_fail (entry != NULL, FALSE);
+
+  return entry->is_unallocated;
+}
+
+/**
+ * matemenu_tree_entry_get_tree:
+ * @entry: A #MateMenuTreeEntry
+ *
+ * Grab the tree associated with a #MateMenuTreeEntry.
+ *
+ * Returns: (transfer full): The #MateMenuTree
+ */
+MateMenuTree *
+matemenu_tree_entry_get_tree (MateMenuTreeEntry *entry)
+{
+	g_return_val_if_fail(entry != NULL, NULL);
+
+  return g_object_ref (entry->item.tree);
+}
+
+MateMenuTreeDirectory *
+matemenu_tree_header_get_directory (MateMenuTreeHeader *header)
+{
+  g_return_val_if_fail (header != NULL, NULL);
+
+  return matemenu_tree_item_ref (header->directory);
+}
+
+/**
+ * matemenu_tree_header_get_tree:
+ * @header: A #MateMenuTreeHeader
+ *
+ * Grab the tree associated with a #MateMenuTreeHeader.
+ *
+ * Returns: (transfer full): The #MateMenuTree
+ */
+MateMenuTree *
+matemenu_tree_header_get_tree (MateMenuTreeHeader *header)
+{
+  g_return_val_if_fail (header != NULL, NULL);
+
+  return g_object_ref (header->item.tree);
+}
+
+MateMenuTreeItemType
+matemenu_tree_alias_get_aliased_item_type (MateMenuTreeAlias *alias)
+{
+  g_return_val_if_fail (alias != NULL, MATEMENU_TREE_ITEM_INVALID);
+
+  g_assert (alias->aliased_item != NULL);
+  return alias->aliased_item->type;
+}
+
+MateMenuTreeDirectory* matemenu_tree_alias_get_directory(MateMenuTreeAlias* alias)
+{
+	g_return_val_if_fail (alias != NULL, NULL);
+
+	return matemenu_tree_item_ref(alias->directory);
+}
+
+/**
+ * matemenu_tree_alias_get_tree:
+ * @alias: A #MateMenuTreeAlias
+ *
+ * Grab the tree associated with a #MateMenuTreeAlias.
+ *
+ * Returns: (transfer full): The #MateMenuTree
+ */
+MateMenuTree *
+matemenu_tree_alias_get_tree (MateMenuTreeAlias *alias)
+{
+  g_return_val_if_fail (alias != NULL, NULL);
+
+  return g_object_ref (alias->item.tree);
+}
+
+/**
+ * matemenu_tree_separator_get_tree:
+ * @separator: A #MateMenuTreeSeparator
+ *
+ * Grab the tree associated with a #MateMenuTreeSeparator.
+ *
+ * Returns: (transfer full): The #MateMenuTree
+ */
+MateMenuTree *
+matemenu_tree_separator_get_tree (MateMenuTreeSeparator *separator)
+{
+  g_return_val_if_fail (separator != NULL, NULL);
+
+  return g_object_ref (separator->item.tree);
+}
+
+/**
+ * matemenu_tree_alias_get_aliased_directory:
+ * @alias: alias
+ *
+ * Returns: (transfer full): The aliased directory entry
+ */
+MateMenuTreeDirectory *
+matemenu_tree_alias_get_aliased_directory (MateMenuTreeAlias *alias)
+{
+  g_return_val_if_fail (alias != NULL, NULL);
+  g_return_val_if_fail (alias->aliased_item->type == MATEMENU_TREE_ITEM_DIRECTORY, NULL);
+
+  return (MateMenuTreeDirectory *) matemenu_tree_item_ref (alias->aliased_item);
+}
+
+/**
+ * matemenu_tree_alias_get_aliased_entry:
+ * @alias: alias
+ *
+ * Returns: (transfer full): The aliased entry
+ */
+MateMenuTreeEntry *
+matemenu_tree_alias_get_aliased_entry (MateMenuTreeAlias *alias)
+{
+  g_return_val_if_fail (alias != NULL, NULL);
+  g_return_val_if_fail (alias->aliased_item->type == MATEMENU_TREE_ITEM_ENTRY, NULL);
+
+  return (MateMenuTreeEntry *) matemenu_tree_item_ref (alias->aliased_item);
+}
+
+static MateMenuTreeDirectory *
+matemenu_tree_directory_new (MateMenuTree          *tree,
+                          MateMenuTreeDirectory *parent,
+			  const char         *name)
+{
+  MateMenuTreeDirectory *retval;
+
+  retval = g_slice_new0 (MateMenuTreeDirectory);
+
+  retval->item.type     = MATEMENU_TREE_ITEM_DIRECTORY;
+  retval->item.parent   = parent;
+  retval->item.refcount = 1;
+  retval->item.tree     = tree;
+
+  retval->name                = g_strdup (name);
+  retval->directory_entry     = NULL;
+  retval->entries             = NULL;
+  retval->subdirs             = NULL;
+  retval->default_layout_info = NULL;
+  retval->layout_info         = NULL;
+  retval->contents            = NULL;
+  retval->only_unallocated    = FALSE;
+  retval->is_nodisplay        = FALSE;
+  retval->layout_pending_separator = FALSE;
+  retval->preprocessed        = FALSE;
+  retval->will_inline_header  = G_MAXUINT16;
+
+  retval->default_layout_values.mask          = MENU_LAYOUT_VALUES_NONE;
+  retval->default_layout_values.show_empty    = FALSE;
+  retval->default_layout_values.inline_menus  = FALSE;
+  retval->default_layout_values.inline_limit  = 4;
+  retval->default_layout_values.inline_header = FALSE;
+  retval->default_layout_values.inline_alias  = FALSE;
+
+  return retval;
+}
+
+static void
+matemenu_tree_directory_finalize (MateMenuTreeDirectory *directory)
+{
+  g_assert (directory->item.refcount == 0);
+
+  g_slist_free_full (directory->contents,
+                     (GDestroyNotify) matemenu_tree_item_unref_and_unset_parent);
+  directory->contents = NULL;
+
+  g_slist_free_full (directory->default_layout_info,
+                     (GDestroyNotify) menu_layout_node_unref);
+  directory->default_layout_info = NULL;
+
+  g_slist_free_full (directory->layout_info,
+                     (GDestroyNotify) menu_layout_node_unref);
+  directory->layout_info = NULL;
+
+  g_slist_free_full (directory->subdirs,
+                     (GDestroyNotify) matemenu_tree_item_unref_and_unset_parent);
+  directory->subdirs = NULL;
+
+  g_slist_free_full (directory->entries,
+                     (GDestroyNotify) matemenu_tree_item_unref_and_unset_parent);
+  directory->entries = NULL;
+
+  if (directory->directory_entry)
+    desktop_entry_unref (directory->directory_entry);
+  directory->directory_entry = NULL;
+
+  g_free (directory->name);
+  directory->name = NULL;
+
+  g_slice_free (MateMenuTreeDirectory, directory);
+}
+
+static MateMenuTreeSeparator *
+matemenu_tree_separator_new (MateMenuTreeDirectory *parent)
+{
+  MateMenuTreeSeparator *retval;
+
+  retval = g_slice_new0 (MateMenuTreeSeparator);
+
+  retval->item.type     = MATEMENU_TREE_ITEM_SEPARATOR;
+  retval->item.parent   = parent;
+  retval->item.refcount = 1;
+  retval->item.tree     = parent->item.tree;
+
+  return retval;
+}
+
+static void
+matemenu_tree_separator_finalize (MateMenuTreeSeparator *separator)
+{
+  g_assert (separator->item.refcount == 0);
+
+  g_slice_free (MateMenuTreeSeparator, separator);
+}
+
+static MateMenuTreeHeader *
+matemenu_tree_header_new (MateMenuTreeDirectory *parent,
+		       MateMenuTreeDirectory *directory)
+{
+  MateMenuTreeHeader *retval;
+
+  retval = g_slice_new0 (MateMenuTreeHeader);
+
+  retval->item.type     = MATEMENU_TREE_ITEM_HEADER;
+  retval->item.parent   = parent;
+  retval->item.refcount = 1;
+  retval->item.tree     = parent->item.tree;
+
+  retval->directory = matemenu_tree_item_ref (directory);
+
+  matemenu_tree_item_set_parent (MATEMENU_TREE_ITEM (retval->directory), NULL);
+
+  return retval;
+}
+
+static void
+matemenu_tree_header_finalize (MateMenuTreeHeader *header)
+{
+  g_assert (header->item.refcount == 0);
+
+  if (header->directory != NULL)
+    matemenu_tree_item_unref (header->directory);
+  header->directory = NULL;
+
+  g_slice_free (MateMenuTreeHeader, header);
+}
+
+static MateMenuTreeAlias *
+matemenu_tree_alias_new (MateMenuTreeDirectory *parent,
+		      MateMenuTreeDirectory *directory,
+		      MateMenuTreeItem      *item)
+{
+  MateMenuTreeAlias *retval;
+
+  retval = g_slice_new0 (MateMenuTreeAlias);
+
+  retval->item.type     = MATEMENU_TREE_ITEM_ALIAS;
+  retval->item.parent   = parent;
+  retval->item.refcount = 1;
+  retval->item.tree     = parent->item.tree;
+
+  retval->directory    = matemenu_tree_item_ref (directory);
+  if (item->type != MATEMENU_TREE_ITEM_ALIAS)
+    retval->aliased_item = matemenu_tree_item_ref (item);
+  else
+    {
+      MateMenuTreeAlias *alias = MATEMENU_TREE_ALIAS (item);
+      retval->aliased_item = matemenu_tree_item_ref (alias->aliased_item);
+    }
+
+  matemenu_tree_item_set_parent (MATEMENU_TREE_ITEM (retval->directory), NULL);
+  matemenu_tree_item_set_parent (retval->aliased_item, NULL);
+
+  return retval;
+}
+
+static void
+matemenu_tree_alias_finalize (MateMenuTreeAlias *alias)
+{
+  g_assert (alias->item.refcount == 0);
+
+  if (alias->directory != NULL)
+    matemenu_tree_item_unref (alias->directory);
+  alias->directory = NULL;
+
+  if (alias->aliased_item != NULL)
+    matemenu_tree_item_unref (alias->aliased_item);
+  alias->aliased_item = NULL;
+
+  g_slice_free (MateMenuTreeAlias, alias);
+}
+
+static MateMenuTreeEntry *
+matemenu_tree_entry_new (MateMenuTreeDirectory *parent,
+		      DesktopEntry       *desktop_entry,
+		      const char         *desktop_file_id,
+		      gboolean            is_excluded,
+                      gboolean            is_unallocated)
+{
+  MateMenuTreeEntry *retval;
+
+  retval = g_slice_new0 (MateMenuTreeEntry);
+
+  retval->item.type     = MATEMENU_TREE_ITEM_ENTRY;
+  retval->item.parent   = parent;
+  retval->item.refcount = 1;
+  retval->item.tree     = parent->item.tree;
+
+  retval->desktop_entry   = desktop_entry_ref (desktop_entry);
+  retval->desktop_file_id = g_strdup (desktop_file_id);
+  retval->is_excluded     = is_excluded != FALSE;
+  retval->is_unallocated  = is_unallocated != FALSE;
+
+  return retval;
+}
+
+static void
+matemenu_tree_entry_finalize (MateMenuTreeEntry *entry)
+{
+  g_assert (entry->item.refcount == 0);
+
+  g_free (entry->desktop_file_id);
+  entry->desktop_file_id = NULL;
+
+  if (entry->desktop_entry)
+    desktop_entry_unref (entry->desktop_entry);
+  entry->desktop_entry = NULL;
+
+  g_slice_free (MateMenuTreeEntry, entry);
+}
+
+static int
+matemenu_tree_entry_compare_by_id (MateMenuTreeItem *a,
+				MateMenuTreeItem *b)
+{
+  if (a->type == MATEMENU_TREE_ITEM_ALIAS)
+    a = MATEMENU_TREE_ALIAS (a)->aliased_item;
+
+  if (b->type == MATEMENU_TREE_ITEM_ALIAS)
+    b = MATEMENU_TREE_ALIAS (b)->aliased_item;
+
+  return strcmp (MATEMENU_TREE_ENTRY (a)->desktop_file_id,
+                 MATEMENU_TREE_ENTRY (b)->desktop_file_id);
+}
+
+/**
+ * matemenu_tree_item_ref:
+ * @item: a #MateMenuTreeItem
+ *
+ * Returns: (transfer full): The same @item, or %NULL if @item is not a valid #MateMenuTreeItem
+ */
+gpointer
+matemenu_tree_item_ref (gpointer itemp)
+{
+  MateMenuTreeItem* item = (MateMenuTreeItem*) itemp;
+  g_return_val_if_fail(item != NULL, NULL);
+  g_return_val_if_fail(item->refcount > 0, NULL);
+
+  g_atomic_int_inc (&item->refcount);
+
+  return item;
+}
+
+void
+matemenu_tree_item_unref (gpointer itemp)
+{
+  MateMenuTreeItem *item;
+
+  item = (MateMenuTreeItem *) itemp;
+
+  g_return_if_fail (item != NULL);
+  g_return_if_fail (item->refcount > 0);
+
+  if (g_atomic_int_dec_and_test (&(item->refcount)))
+    {
+      switch (item->type)
+	{
+	case MATEMENU_TREE_ITEM_DIRECTORY:
+	  matemenu_tree_directory_finalize (MATEMENU_TREE_DIRECTORY (item));
+	  break;
+
+	case MATEMENU_TREE_ITEM_ENTRY:
+	  matemenu_tree_entry_finalize (MATEMENU_TREE_ENTRY (item));
+	  break;
+
+	case MATEMENU_TREE_ITEM_SEPARATOR:
+	  matemenu_tree_separator_finalize (MATEMENU_TREE_SEPARATOR (item));
+	  break;
+
+	case MATEMENU_TREE_ITEM_HEADER:
+	  matemenu_tree_header_finalize (MATEMENU_TREE_HEADER (item));
+	  break;
+
+	case MATEMENU_TREE_ITEM_ALIAS:
+	  matemenu_tree_alias_finalize (MATEMENU_TREE_ALIAS (item));
+	  break;
+
+	default:
+	  g_assert_not_reached ();
+	  break;
+	}
+    }
+}
+
+static void
+matemenu_tree_item_unref_and_unset_parent (gpointer itemp)
+{
+  MateMenuTreeItem *item;
+
+  item = (MateMenuTreeItem *) itemp;
+
+  g_return_if_fail (item != NULL);
+
+  matemenu_tree_item_set_parent (item, NULL);
+  matemenu_tree_item_unref (item);
+}
+
+static inline const char *
+matemenu_tree_item_compare_get_name_helper (MateMenuTreeItem    *item,
+					 MateMenuTreeFlags    flags)
+{
+  const char *name;
+
+  name = NULL;
+
+  switch (item->type)
+    {
+    case MATEMENU_TREE_ITEM_DIRECTORY:
+      if (MATEMENU_TREE_DIRECTORY (item)->directory_entry)
+	name = desktop_entry_get_name (MATEMENU_TREE_DIRECTORY (item)->directory_entry);
+      else
+	name = MATEMENU_TREE_DIRECTORY (item)->name;
+      break;
+
+    case MATEMENU_TREE_ITEM_ENTRY:
+      if (flags & MATEMENU_TREE_FLAGS_SORT_DISPLAY_NAME)
+        name = g_app_info_get_display_name (G_APP_INFO (matemenu_tree_entry_get_app_info (MATEMENU_TREE_ENTRY (item))));
+      else
+        name = desktop_entry_get_name (MATEMENU_TREE_ENTRY (item)->desktop_entry);
+      break;
+
+    case MATEMENU_TREE_ITEM_ALIAS:
+      {
+        MateMenuTreeItem *dir;
+        dir = MATEMENU_TREE_ITEM (MATEMENU_TREE_ALIAS (item)->directory);
+        name = matemenu_tree_item_compare_get_name_helper (dir, flags);
+      }
+      break;
+
+    case MATEMENU_TREE_ITEM_SEPARATOR:
+    case MATEMENU_TREE_ITEM_HEADER:
+    default:
+      g_assert_not_reached ();
+      break;
+    }
+
+  return name;
+}
+
+static int
+matemenu_tree_item_compare (MateMenuTreeItem  *a,
+                            MateMenuTreeItem  *b,
+                            MateMenuTreeFlags  flags)
+{
+  const char       *name_a;
+  const char       *name_b;
+
+  name_a = matemenu_tree_item_compare_get_name_helper (a, flags);
+  name_b = matemenu_tree_item_compare_get_name_helper (b, flags);
+
+  return g_utf8_collate (name_a, name_b);
+}
+
+static MenuLayoutNode *
+find_menu_child (MenuLayoutNode *layout)
+{
+  MenuLayoutNode *child;
+
+  child = menu_layout_node_get_children (layout);
+  while (child && menu_layout_node_get_type (child) != MENU_LAYOUT_NODE_MENU)
+    child = menu_layout_node_get_next (child);
+
+  return child;
+}
+
+static void
+merge_resolved_children (MateMenuTree      *tree,
+			 GHashTable     *loaded_menu_files,
+                         MenuLayoutNode *where,
+                         MenuLayoutNode *from)
+{
+  MenuLayoutNode *insert_after;
+  MenuLayoutNode *menu_child;
+  MenuLayoutNode *from_child;
+
+  matemenu_tree_resolve_files (tree, loaded_menu_files, from);
+
+  insert_after = where;
+  g_assert (menu_layout_node_get_type (insert_after) != MENU_LAYOUT_NODE_ROOT);
+  g_assert (menu_layout_node_get_parent (insert_after) != NULL);
+
+  /* skip root node */
+  menu_child = find_menu_child (from);
+  g_assert (menu_child != NULL);
+  g_assert (menu_layout_node_get_type (menu_child) == MENU_LAYOUT_NODE_MENU);
+
+  /* merge children of toplevel <Menu> */
+  from_child = menu_layout_node_get_children (menu_child);
+  while (from_child != NULL)
+    {
+      MenuLayoutNode *next;
+
+      next = menu_layout_node_get_next (from_child);
+
+      menu_verbose ("Merging ");
+      menu_debug_print_layout (from_child, FALSE);
+      menu_verbose (" after ");
+      menu_debug_print_layout (insert_after, FALSE);
+
+      switch (menu_layout_node_get_type (from_child))
+        {
+        case MENU_LAYOUT_NODE_NAME:
+          menu_layout_node_unlink (from_child); /* delete this */
+          break;
+
+        default:
+          menu_layout_node_steal (from_child);
+          menu_layout_node_insert_after (insert_after, from_child);
+          menu_layout_node_unref (from_child);
+
+          insert_after = from_child;
+          break;
+        }
+
+      from_child = next;
+    }
+}
+
+static gboolean
+load_merge_file (MateMenuTree      *tree,
+		 GHashTable     *loaded_menu_files,
+                 const char     *filename,
+                 gboolean        is_canonical,
+		 gboolean        add_monitor,
+                 MenuLayoutNode *where)
+{
+  MenuLayoutNode *to_merge;
+  const char     *canonical;
+  char           *freeme;
+  gboolean        retval;
+
+  freeme = NULL;
+  retval = FALSE;
+
+  if (!is_canonical)
+    {
+      canonical = freeme = realpath (filename, NULL);
+      if (canonical == NULL)
+        {
+	  if (add_monitor)
+	    matemenu_tree_add_menu_file_monitor (tree,
+					     filename,
+					     MENU_FILE_MONITOR_NONEXISTENT_FILE);
+
+          menu_verbose ("Failed to canonicalize merge file path \"%s\": %s\n",
+                        filename, g_strerror (errno));
+	  goto out;
+        }
+    }
+  else
+    {
+      canonical = filename;
+    }
+
+  if (g_hash_table_lookup (loaded_menu_files, canonical) != NULL)
+    {
+      g_warning ("Not loading \"%s\": recursive loop detected in .menu files",
+		 canonical);
+      retval = TRUE;
+      goto out;
+    }
+
+  menu_verbose ("Merging file \"%s\"\n", canonical);
+
+  to_merge = menu_layout_load (canonical, tree->non_prefixed_basename, NULL);
+  if (to_merge == NULL)
+    {
+      menu_verbose ("No menu for file \"%s\" found when merging\n",
+                    canonical);
+      goto out;
+    }
+
+  retval = TRUE;
+
+  g_hash_table_insert (loaded_menu_files, (char *) canonical, GUINT_TO_POINTER (TRUE));
+
+  if (add_monitor)
+    matemenu_tree_add_menu_file_monitor (tree,
+				      canonical,
+				      MENU_FILE_MONITOR_FILE);
+
+  merge_resolved_children (tree, loaded_menu_files, where, to_merge);
+
+  g_hash_table_remove (loaded_menu_files, canonical);
+
+  menu_layout_node_unref (to_merge);
+
+ out:
+  if (freeme)
+    g_free (freeme);
+
+  return retval;
+}
+
+static gboolean
+load_merge_file_with_config_dir (MateMenuTree      *tree,
+				 GHashTable     *loaded_menu_files,
+				 const char     *menu_file,
+				 const char     *config_dir,
+				 MenuLayoutNode *where)
+{
+  char     *merge_file;
+  gboolean  loaded;
+
+  loaded = FALSE;
+
+  merge_file = g_build_filename (config_dir, "menus", menu_file, NULL);
+
+  if (load_merge_file (tree, loaded_menu_files, merge_file, FALSE, TRUE, where))
+    loaded = TRUE;
+
+  g_free (merge_file);
+
+  return loaded;
+}
+
+static gboolean
+compare_basedir_to_config_dir (const char *canonical_basedir,
+			       const char *config_dir)
+{
+  char     *dirname;
+  char     *canonical_menus_dir;
+  gboolean  retval;
+
+  menu_verbose ("Checking to see if basedir '%s' is in '%s'\n",
+		canonical_basedir, config_dir);
+
+  dirname = g_build_filename (config_dir, "menus", NULL);
+
+  retval = FALSE;
+
+  canonical_menus_dir = realpath (dirname, NULL);
+  if (canonical_menus_dir != NULL &&
+      strcmp (canonical_basedir, canonical_menus_dir) == 0)
+    {
+      retval = TRUE;
+    }
+
+  g_free (canonical_menus_dir);
+  g_free (dirname);
+
+  return retval;
+}
+
+static gboolean
+load_parent_merge_file_from_basename (MateMenuTree      *tree,
+                                      GHashTable     *loaded_menu_files,
+			              MenuLayoutNode *layout,
+                                      const char     *menu_file,
+                                      const char     *canonical_basedir)
+{
+  gboolean            found_basedir;
+  const char * const *system_config_dirs;
+  int                 i;
+
+  /* We're not interested in menu files that are in directories which are not a
+   * parent of the base directory of this menu file */
+  found_basedir = compare_basedir_to_config_dir (canonical_basedir,
+						 g_get_user_config_dir ());
+
+  system_config_dirs = g_get_system_config_dirs ();
+
+  i = 0;
+  while (system_config_dirs[i] != NULL)
+    {
+      if (!found_basedir)
+	{
+	  found_basedir = compare_basedir_to_config_dir (canonical_basedir,
+							 system_config_dirs[i]);
+	}
+      else
+	{
+	  menu_verbose ("Looking for parent menu file '%s' in '%s'\n",
+			menu_file, system_config_dirs[i]);
+
+	  if (load_merge_file_with_config_dir (tree,
+					       loaded_menu_files,
+					       menu_file,
+					       system_config_dirs[i],
+					       layout))
+	    {
+	      break;
+	    }
+	}
+
+      ++i;
+    }
+
+  return system_config_dirs[i] != NULL;
+}
+
+static gboolean load_parent_merge_file(MateMenuTree* tree, GHashTable* loaded_menu_files, MenuLayoutNode* layout)
+{
+	MenuLayoutNode* root;
+	const char* basedir;
+	const char* menu_name;
+	char* canonical_basedir;
+	char* menu_file;
+	gboolean found;
+
+	root = menu_layout_node_get_root(layout);
+
+	basedir   = menu_layout_node_root_get_basedir(root);
+	menu_name = menu_layout_node_root_get_name(root);
+
+	canonical_basedir = realpath (basedir, NULL);
+
+	if (canonical_basedir == NULL)
+	{
+		menu_verbose("Menu basedir '%s' no longer exists, not merging parent\n", basedir);
+		return FALSE;
+	}
+
+	found = FALSE;
+	menu_file = g_strconcat(menu_name, ".menu", NULL);
+
+	if (strcmp(menu_file, "mate-applications.menu") == 0 && g_getenv("XDG_MENU_PREFIX"))
+	{
+		char* prefixed_basename;
+		prefixed_basename = g_strdup_printf("%s%s", g_getenv("XDG_MENU_PREFIX"), menu_file);
+		found = load_parent_merge_file_from_basename(tree, loaded_menu_files, layout, prefixed_basename, canonical_basedir);
+		g_free(prefixed_basename);
+	}
+
+	if (!found)
+	{
+		found = load_parent_merge_file_from_basename(tree, loaded_menu_files, layout, menu_file, canonical_basedir);
+	}
+
+	g_free(menu_file);
+	g_free(canonical_basedir);
+
+	return found;
+}
+
+static void
+load_merge_dir (MateMenuTree      *tree,
+		GHashTable     *loaded_menu_files,
+                const char     *dirname,
+                MenuLayoutNode *where)
+{
+  GDir       *dir;
+  const char *menu_file;
+
+  menu_verbose ("Loading merge dir \"%s\"\n", dirname);
+
+  matemenu_tree_add_menu_file_monitor (tree,
+				    dirname,
+				    MENU_FILE_MONITOR_DIRECTORY);
+
+  if ((dir = g_dir_open (dirname, 0, NULL)) == NULL)
+    return;
+
+  while ((menu_file = g_dir_read_name (dir)))
+    {
+      if (g_str_has_suffix (menu_file, ".menu"))
+        {
+          char *full_path;
+
+          full_path = g_build_filename (dirname, menu_file, NULL);
+
+          load_merge_file (tree, loaded_menu_files, full_path, TRUE, FALSE, where);
+
+          g_free (full_path);
+        }
+    }
+
+  g_dir_close (dir);
+}
+
+static void
+load_merge_dir_with_config_dir (MateMenuTree      *tree,
+				GHashTable     *loaded_menu_files,
+                                const char     *config_dir,
+                                const char     *dirname,
+                                MenuLayoutNode *where)
+{
+  char *path;
+
+  path = g_build_filename (config_dir, "menus", dirname, NULL);
+
+  load_merge_dir (tree, loaded_menu_files, path, where);
+
+  g_free (path);
+}
+
+static void
+resolve_merge_file (MateMenuTree      *tree,
+		    GHashTable     *loaded_menu_files,
+                    MenuLayoutNode *layout)
+{
+  char *filename;
+
+  if (menu_layout_node_merge_file_get_type (layout) == MENU_MERGE_FILE_TYPE_PARENT)
+    {
+      if (load_parent_merge_file (tree, loaded_menu_files, layout))
+        return;
+    }
+
+  filename = menu_layout_node_get_content_as_path (layout);
+  if (filename == NULL)
+    {
+      menu_verbose ("didn't get node content as a path, not merging file\n");
+    }
+  else
+    {
+      load_merge_file (tree, loaded_menu_files, filename, FALSE, TRUE, layout);
+
+      g_free (filename);
+    }
+
+  /* remove the now-replaced node */
+  menu_layout_node_unlink (layout);
+}
+
+static void
+resolve_merge_dir (MateMenuTree      *tree,
+		   GHashTable     *loaded_menu_files,
+                   MenuLayoutNode *layout)
+{
+  char *path;
+
+  path = menu_layout_node_get_content_as_path (layout);
+  if (path == NULL)
+    {
+      menu_verbose ("didn't get layout node content as a path, not merging dir\n");
+    }
+  else
+    {
+      load_merge_dir (tree, loaded_menu_files, path, layout);
+
+      g_free (path);
+    }
+
+  /* remove the now-replaced node */
+  menu_layout_node_unlink (layout);
+}
+
+static MenuLayoutNode *
+add_app_dir (MateMenuTree      *tree,
+             MenuLayoutNode *before,
+             const char     *data_dir)
+{
+  MenuLayoutNode *tmp;
+  char           *dirname;
+
+  tmp = menu_layout_node_new (MENU_LAYOUT_NODE_APP_DIR);
+  dirname = g_build_filename (data_dir, "applications", NULL);
+  menu_layout_node_set_content (tmp, dirname);
+  menu_layout_node_insert_before (before, tmp);
+  menu_layout_node_unref (before);
+
+  menu_verbose ("Adding <AppDir>%s</AppDir> in <DefaultAppDirs/>\n",
+                dirname);
+
+  g_free (dirname);
+
+  return tmp;
+}
+
+static void
+resolve_default_app_dirs (MateMenuTree      *tree,
+                          MenuLayoutNode *layout)
+{
+  MenuLayoutNode     *before;
+  const char * const *system_data_dirs;
+  int                 i;
+
+  system_data_dirs = g_get_system_data_dirs ();
+
+  before = add_app_dir (tree,
+			menu_layout_node_ref (layout),
+			g_get_user_data_dir ());
+
+  i = 0;
+  while (system_data_dirs[i] != NULL)
+    {
+      before = add_app_dir (tree, before, system_data_dirs[i]);
+
+      ++i;
+    }
+
+  menu_layout_node_unref (before);
+
+  /* remove the now-replaced node */
+  menu_layout_node_unlink (layout);
+}
+
+static MenuLayoutNode* add_directory_dir(MateMenuTree* tree, MenuLayoutNode* before, const char* data_dir)
+{
+	MenuLayoutNode* tmp;
+	char* dirname;
+
+	tmp = menu_layout_node_new(MENU_LAYOUT_NODE_DIRECTORY_DIR);
+	dirname = g_build_filename(data_dir, "desktop-directories", NULL);
+	menu_layout_node_set_content(tmp, dirname);
+	menu_layout_node_insert_before(before, tmp);
+	menu_layout_node_unref(before);
+
+	menu_verbose("Adding <DirectoryDir>%s</DirectoryDir> in <DefaultDirectoryDirs/>\n", dirname);
+
+	g_free(dirname);
+
+	return tmp;
+}
+
+/* According to desktop spec, since our menu file is called 'mate-applications', our
+ * merged menu folders need to be called 'mate-applications-merged'.  We'll setup the folder
+ * 'applications-merged' if it doesn't exist yet, and a symlink pointing to it in the
+ * ~/.config/menus directory
+ */
+static void
+setup_merge_dir_symlink(void)
+{
+    gchar *user_config = (gchar *) g_get_user_config_dir();
+    gchar *merge_path = g_build_filename (user_config, "menus", "applications-merged", NULL);
+    GFile *merge_file = g_file_new_for_path (merge_path);
+    gchar *sym_path;
+    GFile *sym_file;
+
+    g_file_make_directory_with_parents (merge_file, NULL, NULL);
+
+    sym_path = g_build_filename (user_config, "menus", "mate-applications-merged", NULL);
+    sym_file = g_file_new_for_path (sym_path);
+    if (!g_file_query_exists (sym_file, NULL)) {
+        g_file_make_symbolic_link (sym_file, merge_path, NULL, NULL);
+    }
+
+    g_free (merge_path);
+    g_free (sym_path);
+    g_object_unref (merge_file);
+    g_object_unref (sym_file);
+}
+
+static void
+resolve_default_directory_dirs (MateMenuTree      *tree,
+                                MenuLayoutNode *layout)
+{
+  MenuLayoutNode     *before;
+  const char * const *system_data_dirs;
+  int                 i;
+
+  system_data_dirs = g_get_system_data_dirs ();
+
+  before = add_directory_dir (tree,
+			      menu_layout_node_ref (layout),
+			      g_get_user_data_dir ());
+
+  i = 0;
+  while (system_data_dirs[i] != NULL)
+    {
+		/* Parche para tomar las carpetas /mate/ */
+		char* path = g_build_filename(system_data_dirs[i], "mate", NULL);
+		before = add_directory_dir(tree, before, path);
+		g_free(path);
+		/* /fin parche */
+      before = add_directory_dir (tree, before, system_data_dirs[i]);
+
+      ++i;
+    }
+
+  menu_layout_node_unref (before);
+
+  /* remove the now-replaced node */
+  menu_layout_node_unlink (layout);
+}
+
+static void
+resolve_default_merge_dirs (MateMenuTree      *tree,
+			    GHashTable     *loaded_menu_files,
+                            MenuLayoutNode *layout)
+{
+  MenuLayoutNode     *root;
+  const char         *menu_name;
+  char               *merge_name;
+  const char * const *system_config_dirs;
+  int                 i;
+
+  setup_merge_dir_symlink();
+
+  root = menu_layout_node_get_root (layout);
+  menu_name = menu_layout_node_root_get_name (root);
+
+  merge_name = g_strconcat (menu_name, "-merged", NULL);
+
+  system_config_dirs = g_get_system_config_dirs ();
+
+  /* Merge in reverse order */
+  i = 0;
+  while (system_config_dirs[i] != NULL) i++;
+  while (i > 0)
+    {
+      i--;
+      load_merge_dir_with_config_dir (tree,
+				      loaded_menu_files,
+                                      system_config_dirs[i],
+                                      merge_name,
+                                      layout);
+    }
+
+  load_merge_dir_with_config_dir (tree,
+				  loaded_menu_files,
+                                  g_get_user_config_dir (),
+                                  merge_name,
+                                  layout);
+
+  g_free (merge_name);
+
+  /* remove the now-replaced node */
+  menu_layout_node_unlink (layout);
+}
+
+static void
+add_filename_include (const char     *desktop_file_id,
+                      DesktopEntry   *entry,
+                      MenuLayoutNode *include)
+{
+  if (!desktop_entry_has_categories (entry))
+    {
+      MenuLayoutNode *node;
+
+      node = menu_layout_node_new (MENU_LAYOUT_NODE_FILENAME);
+      menu_layout_node_set_content (node, desktop_file_id);
+
+      menu_layout_node_append_child (include, node);
+      menu_layout_node_unref (node);
+    }
+}
+
+static void
+is_dot_directory (const char   *basename,
+		  DesktopEntry *entry,
+		  gboolean     *has_dot_directory)
+{
+  if (!strcmp (basename, ".directory"))
+    *has_dot_directory = TRUE;
+}
+
+static gboolean
+add_menu_for_legacy_dir (MenuLayoutNode *parent,
+                         const char     *legacy_dir,
+                	 const char     *relative_path,
+                         const char     *legacy_prefix,
+                         const char     *menu_name)
+{
+  EntryDirectory  *ed;
+  DesktopEntrySet *desktop_entries;
+  DesktopEntrySet *directory_entries;
+  GSList          *subdirs;
+  gboolean         menu_added;
+  gboolean         has_dot_directory;
+
+  ed = entry_directory_new_legacy (DESKTOP_ENTRY_INVALID, legacy_dir, legacy_prefix);
+  if (!ed)
+    return FALSE;
+
+  subdirs = NULL;
+  desktop_entries   = desktop_entry_set_new ();
+  directory_entries = desktop_entry_set_new ();
+
+  entry_directory_get_flat_contents (ed,
+                                     desktop_entries,
+                                     directory_entries,
+                                     &subdirs);
+  entry_directory_unref (ed);
+
+  has_dot_directory = FALSE;
+  desktop_entry_set_foreach (directory_entries,
+			     (DesktopEntrySetForeachFunc) is_dot_directory,
+			     &has_dot_directory);
+  desktop_entry_set_unref (directory_entries);
+
+  menu_added = FALSE;
+  if (desktop_entry_set_get_count (desktop_entries) > 0 || subdirs)
+    {
+      MenuLayoutNode *menu;
+      MenuLayoutNode *node;
+      GString        *subdir_path;
+      GString        *subdir_relative;
+      GSList         *tmp;
+      size_t          legacy_dir_len;
+      size_t          relative_path_len;
+
+      menu = menu_layout_node_new (MENU_LAYOUT_NODE_MENU);
+      menu_layout_node_append_child (parent, menu);
+
+      menu_added = TRUE;
+
+      g_assert (menu_name != NULL);
+
+      node = menu_layout_node_new (MENU_LAYOUT_NODE_NAME);
+      menu_layout_node_set_content (node, menu_name);
+      menu_layout_node_append_child (menu, node);
+      menu_layout_node_unref (node);
+
+      if (has_dot_directory)
+	{
+	  node = menu_layout_node_new (MENU_LAYOUT_NODE_DIRECTORY);
+	  if (relative_path != NULL)
+	    {
+	      char *directory_entry_path;
+
+	      directory_entry_path = g_strdup_printf ("%s/.directory", relative_path);
+	      menu_layout_node_set_content (node, directory_entry_path);
+	      g_free (directory_entry_path);
+	    }
+	  else
+	    {
+	      menu_layout_node_set_content (node, ".directory");
+	    }
+	  menu_layout_node_append_child (menu, node);
+	  menu_layout_node_unref (node);
+	}
+
+      if (desktop_entry_set_get_count (desktop_entries) > 0)
+	{
+	  MenuLayoutNode *include;
+
+	  include = menu_layout_node_new (MENU_LAYOUT_NODE_INCLUDE);
+	  menu_layout_node_append_child (menu, include);
+
+	  desktop_entry_set_foreach (desktop_entries,
+				     (DesktopEntrySetForeachFunc) add_filename_include,
+				     include);
+
+	  menu_layout_node_unref (include);
+	}
+
+      subdir_path = g_string_new (legacy_dir);
+      legacy_dir_len = strlen (legacy_dir);
+
+      subdir_relative = g_string_new (relative_path);
+      relative_path_len = relative_path ? strlen (relative_path) : 0;
+
+      tmp = subdirs;
+      while (tmp != NULL)
+        {
+          const char *subdir = tmp->data;
+
+          g_string_append_c (subdir_path, G_DIR_SEPARATOR);
+          g_string_append (subdir_path, subdir);
+
+	  if (relative_path_len)
+	    {
+	      g_string_append_c (subdir_relative, G_DIR_SEPARATOR);
+	    }
+          g_string_append (subdir_relative, subdir);
+
+          add_menu_for_legacy_dir (menu,
+                                   subdir_path->str,
+				   subdir_relative->str,
+                                   legacy_prefix,
+                                   subdir);
+
+          g_string_truncate (subdir_relative, relative_path_len);
+          g_string_truncate (subdir_path, legacy_dir_len);
+
+          tmp = tmp->next;
+        }
+
+      g_string_free (subdir_path, TRUE);
+      g_string_free (subdir_relative, TRUE);
+
+      menu_layout_node_unref (menu);
+    }
+
+  desktop_entry_set_unref (desktop_entries);
+
+  g_slist_free_full (subdirs, (GDestroyNotify) g_free);
+
+  return menu_added;
+}
+
+static void
+resolve_legacy_dir (MateMenuTree      *tree,
+		    GHashTable     *loaded_menu_files,
+                    MenuLayoutNode *legacy)
+{
+  MenuLayoutNode *to_merge;
+  MenuLayoutNode *menu;
+
+  to_merge = menu_layout_node_new (MENU_LAYOUT_NODE_ROOT);
+
+  menu = menu_layout_node_get_parent (legacy);
+  g_assert (menu_layout_node_get_type (menu) == MENU_LAYOUT_NODE_MENU);
+
+  if (add_menu_for_legacy_dir (to_merge,
+                               menu_layout_node_get_content (legacy),
+			       NULL,
+                               menu_layout_node_legacy_dir_get_prefix (legacy),
+                               menu_layout_node_menu_get_name (menu)))
+    {
+      merge_resolved_children (tree, loaded_menu_files, legacy, to_merge);
+    }
+
+  menu_layout_node_unref (to_merge);
+}
+
+static MenuLayoutNode *
+add_legacy_dir (MateMenuTree      *tree,
+		GHashTable     *loaded_menu_files,
+                MenuLayoutNode *before,
+                const char     *data_dir)
+{
+  MenuLayoutNode *legacy;
+  char           *dirname;
+
+  dirname = g_build_filename (data_dir, "applnk", NULL);
+
+  legacy = menu_layout_node_new (MENU_LAYOUT_NODE_LEGACY_DIR);
+  menu_layout_node_set_content (legacy, dirname);
+  menu_layout_node_legacy_dir_set_prefix (legacy, "kde");
+  menu_layout_node_insert_before (before, legacy);
+  menu_layout_node_unref (before);
+
+  menu_verbose ("Adding <LegacyDir>%s</LegacyDir> in <KDELegacyDirs/>\n",
+                dirname);
+
+  resolve_legacy_dir (tree, loaded_menu_files, legacy);
+
+  g_free (dirname);
+
+  return legacy;
+}
+
+static void
+resolve_kde_legacy_dirs (MateMenuTree      *tree,
+			 GHashTable     *loaded_menu_files,
+                         MenuLayoutNode *layout)
+{
+  MenuLayoutNode     *before;
+  const char * const *system_data_dirs;
+  int                 i;
+
+  system_data_dirs = g_get_system_data_dirs ();
+
+  before = add_legacy_dir (tree,
+			   loaded_menu_files,
+			   menu_layout_node_ref (layout),
+			   g_get_user_data_dir ());
+
+  i = 0;
+  while (system_data_dirs[i] != NULL)
+    {
+      before = add_legacy_dir (tree, loaded_menu_files, before, system_data_dirs[i]);
+
+      ++i;
+    }
+
+  menu_layout_node_unref (before);
+
+  /* remove the now-replaced node */
+  menu_layout_node_unlink (layout);
+}
+
+static void
+matemenu_tree_resolve_files (MateMenuTree      *tree,
+			  GHashTable     *loaded_menu_files,
+			  MenuLayoutNode *layout)
+{
+  MenuLayoutNode *child;
+
+  menu_verbose ("Resolving files in: ");
+  menu_debug_print_layout (layout, TRUE);
+
+  switch (menu_layout_node_get_type (layout))
+    {
+    case MENU_LAYOUT_NODE_MERGE_FILE:
+      resolve_merge_file (tree, loaded_menu_files, layout);
+      break;
+
+    case MENU_LAYOUT_NODE_MERGE_DIR:
+      resolve_merge_dir (tree, loaded_menu_files, layout);
+      break;
+
+    case MENU_LAYOUT_NODE_DEFAULT_APP_DIRS:
+      resolve_default_app_dirs (tree, layout);
+      break;
+
+    case MENU_LAYOUT_NODE_DEFAULT_DIRECTORY_DIRS:
+      resolve_default_directory_dirs (tree, layout);
+      break;
+
+    case MENU_LAYOUT_NODE_DEFAULT_MERGE_DIRS:
+      resolve_default_merge_dirs (tree, loaded_menu_files, layout);
+      break;
+
+    case MENU_LAYOUT_NODE_LEGACY_DIR:
+      resolve_legacy_dir (tree, loaded_menu_files, layout);
+      break;
+
+    case MENU_LAYOUT_NODE_KDE_LEGACY_DIRS:
+      resolve_kde_legacy_dirs (tree, loaded_menu_files, layout);
+      break;
+
+    case MENU_LAYOUT_NODE_PASSTHROUGH:
+      /* Just get rid of these, we don't need the memory usage */
+      menu_layout_node_unlink (layout);
+      break;
+
+    default:
+      /* Recurse */
+      child = menu_layout_node_get_children (layout);
+      while (child != NULL)
+        {
+          MenuLayoutNode *next = menu_layout_node_get_next (child);
+
+          matemenu_tree_resolve_files (tree, loaded_menu_files, child);
+
+          child = next;
+        }
+      break;
+    }
+}
+
+static void
+move_children (MenuLayoutNode *from,
+               MenuLayoutNode *to)
+{
+  MenuLayoutNode *from_child;
+  MenuLayoutNode *insert_before;
+
+  insert_before = menu_layout_node_get_children (to);
+  from_child    = menu_layout_node_get_children (from);
+
+  while (from_child != NULL)
+    {
+      MenuLayoutNode *next;
+
+      next = menu_layout_node_get_next (from_child);
+
+      menu_layout_node_steal (from_child);
+
+      if (menu_layout_node_get_type (from_child) == MENU_LAYOUT_NODE_NAME)
+        {
+          ; /* just drop the Name in the old <Menu> */
+        }
+      else if (insert_before)
+        {
+          menu_layout_node_insert_before (insert_before, from_child);
+          g_assert (menu_layout_node_get_next (from_child) == insert_before);
+        }
+      else
+        {
+          menu_layout_node_append_child (to, from_child);
+        }
+
+      menu_layout_node_unref (from_child);
+
+      from_child = next;
+    }
+}
+
+static int
+null_safe_strcmp (const char *a,
+                  const char *b)
+{
+  if (a == NULL && b == NULL)
+    return 0;
+  else if (a == NULL)
+    return -1;
+  else if (b == NULL)
+    return 1;
+  else
+    return strcmp (a, b);
+}
+
+static int
+node_compare_func (const void *a,
+                   const void *b)
+{
+  MenuLayoutNode *node_a = (MenuLayoutNode*) a;
+  MenuLayoutNode *node_b = (MenuLayoutNode*) b;
+  MenuLayoutNodeType t_a = menu_layout_node_get_type (node_a);
+  MenuLayoutNodeType t_b = menu_layout_node_get_type (node_b);
+
+  if (t_a < t_b)
+    return -1;
+  else if (t_a > t_b)
+    return 1;
+  else
+    {
+      const char *c_a = menu_layout_node_get_content (node_a);
+      const char *c_b = menu_layout_node_get_content (node_b);
+
+      return null_safe_strcmp (c_a, c_b);
+    }
+}
+
+static int
+node_menu_compare_func (const void *a,
+                        const void *b)
+{
+  MenuLayoutNode *node_a = (MenuLayoutNode*) a;
+  MenuLayoutNode *node_b = (MenuLayoutNode*) b;
+  MenuLayoutNode *parent_a = menu_layout_node_get_parent (node_a);
+  MenuLayoutNode *parent_b = menu_layout_node_get_parent (node_b);
+
+  if (parent_a < parent_b)
+    return -1;
+  else if (parent_a > parent_b)
+    return 1;
+  else
+    return null_safe_strcmp (menu_layout_node_menu_get_name (node_a),
+                             menu_layout_node_menu_get_name (node_b));
+}
+
+static void
+matemenu_tree_strip_duplicate_children (MateMenuTree      *tree,
+				     MenuLayoutNode *layout)
+{
+  MenuLayoutNode *child;
+  GSList         *simple_nodes;
+  GSList         *menu_layout_nodes;
+  GSList         *prev;
+  GSList         *tmp;
+
+  /* to strip dups, we find all the child nodes where
+   * we want to kill dups, sort them,
+   * then nuke the adjacent nodes that are equal
+   */
+
+  simple_nodes = NULL;
+  menu_layout_nodes = NULL;
+
+  child = menu_layout_node_get_children (layout);
+  while (child != NULL)
+    {
+      switch (menu_layout_node_get_type (child))
+        {
+          /* These are dups if their content is the same */
+        case MENU_LAYOUT_NODE_APP_DIR:
+        case MENU_LAYOUT_NODE_DIRECTORY_DIR:
+        case MENU_LAYOUT_NODE_DIRECTORY:
+          simple_nodes = g_slist_prepend (simple_nodes, child);
+          break;
+
+          /* These have to be merged in a more complicated way,
+           * and then recursed
+           */
+        case MENU_LAYOUT_NODE_MENU:
+          menu_layout_nodes = g_slist_prepend (menu_layout_nodes, child);
+          break;
+
+        default:
+          break;
+        }
+
+      child = menu_layout_node_get_next (child);
+    }
+
+  /* Note that the lists are all backward. So we want to keep
+   * the items that are earlier in the list, because they were
+   * later in the file
+   */
+
+  /* stable sort the simple nodes */
+  simple_nodes = g_slist_sort (simple_nodes,
+                               node_compare_func);
+
+  prev = NULL;
+  tmp = simple_nodes;
+  while (tmp != NULL)
+    {
+      GSList *next = tmp->next;
+
+      if (prev)
+        {
+          MenuLayoutNode *p = prev->data;
+          MenuLayoutNode *n = tmp->data;
+
+          if (node_compare_func (p, n) == 0)
+            {
+              /* nuke it! */
+              menu_layout_node_unlink (n);
+	      simple_nodes = g_slist_delete_link (simple_nodes, tmp);
+	      tmp = prev;
+            }
+        }
+
+      prev = tmp;
+      tmp = next;
+    }
+
+  g_slist_free (simple_nodes);
+  simple_nodes = NULL;
+
+  /* stable sort the menu nodes (the sort includes the
+   * parents of the nodes in the comparison). Remember
+   * the list is backward.
+   */
+  menu_layout_nodes = g_slist_sort (menu_layout_nodes,
+				    node_menu_compare_func);
+
+  prev = NULL;
+  tmp = menu_layout_nodes;
+  while (tmp != NULL)
+    {
+      GSList *next = tmp->next;
+
+      if (prev)
+        {
+          MenuLayoutNode *p = prev->data;
+          MenuLayoutNode *n = tmp->data;
+
+          if (node_menu_compare_func (p, n) == 0)
+            {
+              /* Move children of first menu to the start of second
+               * menu and nuke the first menu
+               */
+              move_children (n, p);
+              menu_layout_node_unlink (n);
+	      menu_layout_nodes = g_slist_delete_link (menu_layout_nodes, tmp);
+	      tmp = prev;
+            }
+        }
+
+      prev = tmp;
+      tmp = next;
+    }
+
+  g_slist_free (menu_layout_nodes);
+  menu_layout_nodes = NULL;
+
+  /* Recursively clean up all children */
+  child = menu_layout_node_get_children (layout);
+  while (child != NULL)
+    {
+      if (menu_layout_node_get_type (child) == MENU_LAYOUT_NODE_MENU)
+        matemenu_tree_strip_duplicate_children (tree, child);
+
+      child = menu_layout_node_get_next (child);
+    }
+}
+
+static MenuLayoutNode *
+find_submenu (MenuLayoutNode *layout,
+              const char     *path,
+              gboolean        create_if_not_found)
+{
+  MenuLayoutNode *child;
+  const char     *slash;
+  const char     *next_path;
+  char           *name;
+
+  menu_verbose (" (splitting \"%s\")\n", path);
+
+  if (path[0] == '\0' || path[0] == G_DIR_SEPARATOR)
+    return NULL;
+
+  slash = strchr (path, G_DIR_SEPARATOR);
+  if (slash != NULL)
+    {
+      name = g_strndup (path, (gsize)(slash - path));
+      next_path = slash + 1;
+      if (*next_path == '\0')
+        next_path = NULL;
+    }
+  else
+    {
+      name = g_strdup (path);
+      next_path = NULL;
+    }
+
+  child = menu_layout_node_get_children (layout);
+  while (child != NULL)
+    {
+      switch (menu_layout_node_get_type (child))
+        {
+        case MENU_LAYOUT_NODE_MENU:
+          {
+            if (strcmp (name, menu_layout_node_menu_get_name (child)) == 0)
+              {
+                menu_verbose ("MenuNode %p found for path component \"%s\"\n",
+                              child, name);
+
+                g_free (name);
+
+                if (!next_path)
+                  {
+                    menu_verbose (" Found menu node %p parent is %p\n",
+                                  child, layout);
+                    return child;
+                  }
+
+                return find_submenu (child, next_path, create_if_not_found);
+              }
+          }
+          break;
+
+        default:
+          break;
+        }
+
+      child = menu_layout_node_get_next (child);
+    }
+
+  if (create_if_not_found)
+    {
+      MenuLayoutNode *name_node;
+
+      child = menu_layout_node_new (MENU_LAYOUT_NODE_MENU);
+      menu_layout_node_append_child (layout, child);
+
+      name_node = menu_layout_node_new (MENU_LAYOUT_NODE_NAME);
+      menu_layout_node_set_content (name_node, name);
+      menu_layout_node_append_child (child, name_node);
+      menu_layout_node_unref (name_node);
+
+      menu_verbose (" Created menu node %p parent is %p\n",
+                    child, layout);
+
+      menu_layout_node_unref (child);
+      g_free (name);
+
+      if (!next_path)
+        return child;
+
+      return find_submenu (child, next_path, create_if_not_found);
+    }
+  else
+    {
+      g_free (name);
+      return NULL;
+    }
+}
+
+/* To call this you first have to strip duplicate children once,
+ * otherwise when you move a menu Foo to Bar then you may only
+ * move one of Foo, not all the merged Foo.
+ */
+static void
+matemenu_tree_execute_moves (MateMenuTree      *tree,
+			  MenuLayoutNode *layout,
+			  gboolean       *need_remove_dups_p)
+{
+  MenuLayoutNode *child;
+  gboolean        need_remove_dups;
+  GSList         *move_nodes;
+  GSList         *tmp;
+
+  need_remove_dups = FALSE;
+
+  move_nodes = NULL;
+
+  child = menu_layout_node_get_children (layout);
+  while (child != NULL)
+    {
+      switch (menu_layout_node_get_type (child))
+        {
+        case MENU_LAYOUT_NODE_MENU:
+          /* Recurse - we recurse first and process the current node
+           * second, as the spec dictates.
+           */
+          matemenu_tree_execute_moves (tree, child, &need_remove_dups);
+          break;
+
+        case MENU_LAYOUT_NODE_MOVE:
+          move_nodes = g_slist_prepend (move_nodes, child);
+          break;
+
+        default:
+          break;
+        }
+
+      child = menu_layout_node_get_next (child);
+    }
+
+  /* We need to execute the move operations in the order that they appear */
+  move_nodes = g_slist_reverse (move_nodes);
+
+  tmp = move_nodes;
+  while (tmp != NULL)
+    {
+      MenuLayoutNode *move_node = tmp->data;
+      MenuLayoutNode *old_node;
+      GSList         *next = tmp->next;
+      const char     *old;
+      const char     *new;
+
+      old = menu_layout_node_move_get_old (move_node);
+      new = menu_layout_node_move_get_new (move_node);
+      g_assert (old != NULL && new != NULL);
+
+      menu_verbose ("executing <Move> old = \"%s\" new = \"%s\"\n",
+                    old, new);
+
+      old_node = find_submenu (layout, old, FALSE);
+      if (old_node != NULL)
+        {
+          MenuLayoutNode *new_node;
+
+          /* here we can create duplicates anywhere below the
+           * node
+           */
+          need_remove_dups = TRUE;
+
+          /* look up new node creating it and its parents if
+           * required
+           */
+          new_node = find_submenu (layout, new, TRUE);
+          g_assert (new_node != NULL);
+
+          move_children (old_node, new_node);
+
+          menu_layout_node_unlink (old_node);
+        }
+
+      menu_layout_node_unlink (move_node);
+
+      tmp = next;
+    }
+
+  g_slist_free (move_nodes);
+
+  /* This oddness is to ensure we only remove dups once,
+   * at the root, instead of recursing the tree over
+   * and over.
+   */
+  if (need_remove_dups_p)
+    *need_remove_dups_p = need_remove_dups;
+  else if (need_remove_dups)
+    matemenu_tree_strip_duplicate_children (tree, layout);
+}
+
+static gboolean
+matemenu_tree_load_layout (MateMenuTree  *tree,
+                        GError    **error)
+{
+  GHashTable *loaded_menu_files;
+
+  if (tree->layout)
+    return TRUE;
+
+  if (!matemenu_tree_canonicalize_path (tree, error))
+    return FALSE;
+
+  menu_verbose ("Loading menu layout from \"%s\"\n",
+                tree->canonical_path);
+
+  tree->layout = menu_layout_load (tree->canonical_path,
+                                   tree->non_prefixed_basename,
+                                   error);
+  if (!tree->layout)
+    return FALSE;
+
+  loaded_menu_files = g_hash_table_new (g_str_hash, g_str_equal);
+  g_hash_table_insert (loaded_menu_files, tree->canonical_path, GUINT_TO_POINTER (TRUE));
+  matemenu_tree_resolve_files (tree, loaded_menu_files, tree->layout);
+  g_hash_table_destroy (loaded_menu_files);
+
+  matemenu_tree_strip_duplicate_children (tree, tree->layout);
+  matemenu_tree_execute_moves (tree, tree->layout, NULL);
+
+  return TRUE;
+}
+
+static void
+matemenu_tree_force_reload (MateMenuTree *tree)
+{
+  matemenu_tree_force_rebuild (tree);
+
+  if (tree->layout)
+    menu_layout_node_unref (tree->layout);
+  tree->layout = NULL;
+}
+
+typedef struct
+{
+  DesktopEntrySet *set;
+  const char      *category;
+} GetByCategoryForeachData;
+
+static void
+get_by_category_foreach (const char               *file_id,
+			 DesktopEntry             *entry,
+			 GetByCategoryForeachData *data)
+{
+  if (desktop_entry_has_category (entry, data->category))
+    desktop_entry_set_add_entry (data->set, entry, file_id);
+}
+
+#ifdef WITH_COLLECTION
+static void
+get_by_desktop_foreach (const char               *file_id,
+                        DesktopEntry             *entry,
+                        GetByCategoryForeachData *data)
+{
+  if (g_strcmp0 (file_id, data->category) == 0)
+    desktop_entry_set_add_entry (data->set, entry, file_id);
+}
+#endif /* WITH_COLLECTION */
+
+static void
+get_by_category (DesktopEntrySet *entry_pool,
+		 DesktopEntrySet *set,
+		 const char      *category)
+{
+  GetByCategoryForeachData data;
+
+  data.set      = set;
+  data.category = category;
+
+  desktop_entry_set_foreach (entry_pool,
+                             (DesktopEntrySetForeachFunc) get_by_category_foreach,
+                             &data);
+}
+
+#ifdef WITH_COLLECTION
+static void
+get_by_desktop (DesktopEntrySet *entry_pool,
+                DesktopEntrySet *set,
+                const char      *desktop_name)
+{
+  GetByCategoryForeachData data;
+
+  data.set      = set;
+  data.category = desktop_name;
+
+  desktop_entry_set_foreach (entry_pool,
+                             (DesktopEntrySetForeachFunc) get_by_desktop_foreach,
+                             &data);
+}
+#endif /* WITH_COLLECTION */
+
+static DesktopEntrySet *
+process_include_rules (MenuLayoutNode  *layout,
+		       DesktopEntrySet *entry_pool)
+{
+  DesktopEntrySet *set = NULL;
+
+  switch (menu_layout_node_get_type (layout))
+    {
+    case MENU_LAYOUT_NODE_AND:
+      {
+        MenuLayoutNode *child;
+
+	menu_verbose ("Processing <And>\n");
+
+        child = menu_layout_node_get_children (layout);
+        while (child != NULL)
+          {
+            DesktopEntrySet *child_set;
+
+            child_set = process_include_rules (child, entry_pool);
+
+            if (set == NULL)
+              {
+                set = child_set;
+              }
+            else
+              {
+                desktop_entry_set_intersection (set, child_set);
+                desktop_entry_set_unref (child_set);
+              }
+
+            /* as soon as we get empty results, we can bail,
+             * because it's an AND
+             */
+            if (desktop_entry_set_get_count (set) == 0)
+              break;
+
+            child = menu_layout_node_get_next (child);
+          }
+	menu_verbose ("Processed <And>\n");
+      }
+      break;
+
+    case MENU_LAYOUT_NODE_OR:
+      {
+        MenuLayoutNode *child;
+
+	menu_verbose ("Processing <Or>\n");
+
+        child = menu_layout_node_get_children (layout);
+        while (child != NULL)
+          {
+            DesktopEntrySet *child_set;
+
+            child_set = process_include_rules (child, entry_pool);
+
+            if (set == NULL)
+              {
+                set = child_set;
+              }
+            else
+              {
+                desktop_entry_set_union (set, child_set);
+                desktop_entry_set_unref (child_set);
+              }
+
+            child = menu_layout_node_get_next (child);
+          }
+	menu_verbose ("Processed <Or>\n");
+      }
+      break;
+
+    case MENU_LAYOUT_NODE_NOT:
+      {
+        /* First get the OR of all the rules */
+        MenuLayoutNode *child;
+
+	menu_verbose ("Processing <Not>\n");
+
+        child = menu_layout_node_get_children (layout);
+        while (child != NULL)
+          {
+            DesktopEntrySet *child_set;
+
+            child_set = process_include_rules (child, entry_pool);
+
+            if (set == NULL)
+              {
+                set = child_set;
+              }
+            else
+              {
+                desktop_entry_set_union (set, child_set);
+                desktop_entry_set_unref (child_set);
+              }
+
+            child = menu_layout_node_get_next (child);
+          }
+
+        if (set != NULL)
+          {
+	    DesktopEntrySet *inverted;
+
+	    /* Now invert the result */
+	    inverted = desktop_entry_set_new ();
+	    desktop_entry_set_union (inverted, entry_pool);
+	    desktop_entry_set_subtract (inverted, set);
+	    desktop_entry_set_unref (set);
+	    set = inverted;
+          }
+	menu_verbose ("Processed <Not>\n");
+      }
+      break;
+
+    case MENU_LAYOUT_NODE_ALL:
+      menu_verbose ("Processing <All>\n");
+      set = desktop_entry_set_new ();
+      desktop_entry_set_union (set, entry_pool);
+      menu_verbose ("Processed <All>\n");
+      break;
+
+    case MENU_LAYOUT_NODE_FILENAME:
+      {
+        DesktopEntry *entry;
+
+	menu_verbose ("Processing <Filename>%s</Filename>\n",
+		      menu_layout_node_get_content (layout));
+
+        entry = desktop_entry_set_lookup (entry_pool,
+					  menu_layout_node_get_content (layout));
+        if (entry != NULL)
+          {
+            set = desktop_entry_set_new ();
+            desktop_entry_set_add_entry (set,
+                                         entry,
+                                         menu_layout_node_get_content (layout));
+          }
+	menu_verbose ("Processed <Filename>%s</Filename>\n",
+		      menu_layout_node_get_content (layout));
+      }
+      break;
+
+    case MENU_LAYOUT_NODE_CATEGORY:
+      menu_verbose ("Processing <Category>%s</Category>\n",
+		    menu_layout_node_get_content (layout));
+      set = desktop_entry_set_new ();
+      get_by_category (entry_pool, set, menu_layout_node_get_content (layout));
+      menu_verbose ("Processed <Category>%s</Category>\n",
+		    menu_layout_node_get_content (layout));
+      break;
+
+    default:
+      break;
+    }
+
+  if (set == NULL)
+    set = desktop_entry_set_new (); /* create an empty set */
+
+  menu_verbose ("Matched %u entries\n", desktop_entry_set_get_count (set));
+
+  return set;
+}
+
+static void
+collect_layout_info (MenuLayoutNode  *layout,
+		     GSList         **layout_info)
+{
+  MenuLayoutNode *iter;
+
+  g_slist_foreach (*layout_info,
+		   (GFunc) menu_layout_node_unref,
+		   NULL);
+  g_slist_free (*layout_info);
+  *layout_info = NULL;
+
+  iter = menu_layout_node_get_children (layout);
+  while (iter != NULL)
+    {
+      switch (menu_layout_node_get_type (iter))
+	{
+	case MENU_LAYOUT_NODE_MENUNAME:
+	case MENU_LAYOUT_NODE_FILENAME:
+	case MENU_LAYOUT_NODE_SEPARATOR:
+	case MENU_LAYOUT_NODE_MERGE:
+	  *layout_info = g_slist_prepend (*layout_info,
+					  menu_layout_node_ref (iter));
+	  break;
+
+	default:
+	  break;
+	}
+
+      iter = menu_layout_node_get_next (iter);
+    }
+
+  *layout_info = g_slist_reverse (*layout_info);
+}
+
+static void
+entries_listify_foreach (const char         *desktop_file_id,
+                         DesktopEntry       *desktop_entry,
+                         MateMenuTreeDirectory *directory)
+{
+  directory->entries =
+    g_slist_prepend (directory->entries,
+		     matemenu_tree_entry_new (directory,
+                                           desktop_entry,
+                                           desktop_file_id,
+                                           FALSE,
+                                           FALSE));
+}
+
+static void
+excluded_entries_listify_foreach (const char         *desktop_file_id,
+				  DesktopEntry       *desktop_entry,
+				  MateMenuTreeDirectory *directory)
+{
+  directory->entries =
+    g_slist_prepend (directory->entries,
+		     matemenu_tree_entry_new (directory,
+					   desktop_entry,
+					   desktop_file_id,
+					   TRUE,
+                                           FALSE));
+}
+
+static void
+unallocated_entries_listify_foreach (const char         *desktop_file_id,
+                                     DesktopEntry       *desktop_entry,
+                                     MateMenuTreeDirectory *directory)
+{
+  directory->entries =
+    g_slist_prepend (directory->entries,
+		     matemenu_tree_entry_new (directory,
+                                           desktop_entry,
+                                           desktop_file_id,
+                                           FALSE,
+                                           TRUE));
+}
+
+static void
+set_default_layout_values (MateMenuTreeDirectory *parent,
+                           MateMenuTreeDirectory *child)
+{
+  GSList *tmp;
+
+  /* if the child has a defined default layout, we don't want to override its
+   * values. The parent might have a non-defined layout info (ie, no child of
+   * the DefaultLayout node) but it doesn't meant the default layout values
+   * (ie, DefaultLayout attributes) aren't different from the global defaults.
+   */
+  if (child->default_layout_info != NULL ||
+      child->default_layout_values.mask != MENU_LAYOUT_VALUES_NONE)
+    return;
+
+  child->default_layout_values = parent->default_layout_values;
+
+  tmp = child->subdirs;
+  while (tmp != NULL)
+    {
+      MateMenuTreeDirectory *subdir = tmp->data;
+
+      set_default_layout_values (child, subdir);
+
+      tmp = tmp->next;
+   }
+}
+
+static MateMenuTreeDirectory *
+process_layout (MateMenuTree          *tree,
+                MateMenuTreeDirectory *parent,
+                MenuLayoutNode     *layout,
+                DesktopEntrySet    *allocated)
+{
+  MenuLayoutNode     *layout_iter;
+  MateMenuTreeDirectory *directory;
+  DesktopEntrySet    *entry_pool;
+  DesktopEntrySet    *entries;
+  DesktopEntrySet    *allocated_set;
+  DesktopEntrySet    *excluded_set;
+  gboolean            deleted;
+  gboolean            only_unallocated;
+  GSList             *tmp;
+
+  g_assert (menu_layout_node_get_type (layout) == MENU_LAYOUT_NODE_MENU);
+  g_assert (menu_layout_node_menu_get_name (layout) != NULL);
+
+  directory = matemenu_tree_directory_new (tree, parent,
+					menu_layout_node_menu_get_name (layout));
+
+  menu_verbose ("=== Menu name = %s ===\n", directory->name);
+
+  deleted = FALSE;
+  only_unallocated = FALSE;
+
+  entries = desktop_entry_set_new ();
+  allocated_set = desktop_entry_set_new ();
+
+  if (tree->flags & MATEMENU_TREE_FLAGS_INCLUDE_EXCLUDED)
+    excluded_set = desktop_entry_set_new ();
+  else
+    excluded_set = NULL;
+
+  entry_pool = _entry_directory_list_get_all_desktops (menu_layout_node_menu_get_app_dirs (layout));
+
+  layout_iter = menu_layout_node_get_children (layout);
+  while (layout_iter != NULL)
+    {
+      switch (menu_layout_node_get_type (layout_iter))
+        {
+        case MENU_LAYOUT_NODE_MENU:
+          /* recurse */
+          {
+            MateMenuTreeDirectory *child_dir;
+
+	    menu_verbose ("Processing <Menu>\n");
+
+            child_dir = process_layout (tree,
+                                        directory,
+                                        layout_iter,
+                                        allocated);
+            if (child_dir)
+              directory->subdirs = g_slist_prepend (directory->subdirs,
+                                                    child_dir);
+
+	    menu_verbose ("Processed <Menu>\n");
+          }
+          break;
+
+        case MENU_LAYOUT_NODE_INCLUDE:
+          {
+            /* The match rule children of the <Include> are
+             * independent (logical OR) so we can process each one by
+             * itself
+             */
+            MenuLayoutNode *rule;
+
+	    menu_verbose ("Processing <Include> (%u entries)\n",
+			  desktop_entry_set_get_count (entries));
+
+            rule = menu_layout_node_get_children (layout_iter);
+            while (rule != NULL)
+              {
+                DesktopEntrySet *rule_set;
+
+                rule_set = process_include_rules (rule, entry_pool);
+                if (rule_set != NULL)
+                  {
+                    desktop_entry_set_union (entries, rule_set);
+                    desktop_entry_set_union (allocated_set, rule_set);
+		    if (excluded_set != NULL)
+		      desktop_entry_set_subtract (excluded_set, rule_set);
+                    desktop_entry_set_unref (rule_set);
+                  }
+
+                rule = menu_layout_node_get_next (rule);
+              }
+
+	    menu_verbose ("Processed <Include> (%u entries)\n",
+			  desktop_entry_set_get_count (entries));
+          }
+          break;
+
+        case MENU_LAYOUT_NODE_EXCLUDE:
+          {
+            /* The match rule children of the <Exclude> are
+             * independent (logical OR) so we can process each one by
+             * itself
+             */
+            MenuLayoutNode *rule;
+
+	    menu_verbose ("Processing <Exclude> (%u entries)\n",
+			  desktop_entry_set_get_count (entries));
+
+            rule = menu_layout_node_get_children (layout_iter);
+            while (rule != NULL)
+              {
+                DesktopEntrySet *rule_set;
+
+                rule_set = process_include_rules (rule, entry_pool);
+                if (rule_set != NULL)
+                  {
+		    if (excluded_set != NULL)
+		      desktop_entry_set_union (excluded_set, rule_set);
+		    desktop_entry_set_subtract (entries, rule_set);
+		    desktop_entry_set_unref (rule_set);
+                  }
+
+                rule = menu_layout_node_get_next (rule);
+              }
+
+	    menu_verbose ("Processed <Exclude> (%u entries)\n",
+			  desktop_entry_set_get_count (entries));
+          }
+          break;
+
+        case MENU_LAYOUT_NODE_DIRECTORY:
+          {
+            DesktopEntry *entry;
+
+	    menu_verbose ("Processing <Directory>%s</Directory>\n",
+			  menu_layout_node_get_content (layout_iter));
+
+	    /*
+             * The last <Directory> to exist wins, so we always try overwriting
+             */
+            entry = entry_directory_list_get_directory (menu_layout_node_menu_get_directory_dirs (layout),
+                                                        menu_layout_node_get_content (layout_iter));
+
+            if (entry != NULL)
+              {
+                if (!desktop_entry_get_hidden (entry))
+                  {
+                    if (directory->directory_entry)
+                      desktop_entry_unref (directory->directory_entry);
+                    directory->directory_entry = entry; /* pass ref ownership */
+                  }
+                else
+                  {
+                    desktop_entry_unref (entry);
+                  }
+              }
+
+            menu_verbose ("Processed <Directory> new directory entry = %p (%s)\n",
+                          directory->directory_entry,
+			  directory->directory_entry? desktop_entry_get_path (directory->directory_entry) : "null");
+          }
+          break;
+
+        case MENU_LAYOUT_NODE_DELETED:
+	  menu_verbose ("Processed <Deleted/>\n");
+          deleted = TRUE;
+          break;
+
+        case MENU_LAYOUT_NODE_NOT_DELETED:
+	  menu_verbose ("Processed <NotDeleted/>\n");
+          deleted = FALSE;
+          break;
+
+        case MENU_LAYOUT_NODE_ONLY_UNALLOCATED:
+	  menu_verbose ("Processed <OnlyUnallocated/>\n");
+          only_unallocated = TRUE;
+          break;
+
+        case MENU_LAYOUT_NODE_NOT_ONLY_UNALLOCATED:
+	  menu_verbose ("Processed <NotOnlyUnallocated/>\n");
+          only_unallocated = FALSE;
+          break;
+
+	case MENU_LAYOUT_NODE_DEFAULT_LAYOUT:
+	  menu_layout_node_default_layout_get_values (layout_iter,
+						      &directory->default_layout_values);
+	  collect_layout_info (layout_iter, &directory->default_layout_info);
+	  menu_verbose ("Processed <DefaultLayout/>\n");
+	  break;
+
+	case MENU_LAYOUT_NODE_LAYOUT:
+	  collect_layout_info (layout_iter, &directory->layout_info);
+	  menu_verbose ("Processed <Layout/>\n");
+	  break;
+
+        default:
+          break;
+        }
+
+      layout_iter = menu_layout_node_get_next (layout_iter);
+    }
+
+  desktop_entry_set_unref (entry_pool);
+
+  directory->only_unallocated = only_unallocated != FALSE;
+
+  if (!directory->only_unallocated)
+    desktop_entry_set_union (allocated, allocated_set);
+
+  desktop_entry_set_unref (allocated_set);
+
+  if (directory->directory_entry)
+    {
+      if (desktop_entry_get_no_display (directory->directory_entry))
+        {
+          directory->is_nodisplay = TRUE;
+
+          if (!(tree->flags & MATEMENU_TREE_FLAGS_INCLUDE_NODISPLAY))
+            {
+              menu_verbose ("Not showing menu %s because NoDisplay=true\n",
+                        desktop_entry_get_name (directory->directory_entry));
+              deleted = TRUE;
+            }
+        }
+
+      if (!desktop_entry_get_show_in (directory->directory_entry))
+        {
+          menu_verbose ("Not showing menu %s because OnlyShowIn!=$DESKTOP or NotShowIn=$DESKTOP (with $DESKTOP=${XDG_CURRENT_DESKTOP:-GNOME})\n",
+                        desktop_entry_get_name (directory->directory_entry));
+          deleted = TRUE;
+        }
+    }
+
+  if (deleted)
+    {
+      if (excluded_set != NULL)
+	desktop_entry_set_unref (excluded_set);
+      desktop_entry_set_unref (entries);
+      matemenu_tree_item_unref (directory);
+      return NULL;
+    }
+
+#ifdef WITH_COLLECTION
+  if (tree->collection_applet && !g_strcmp0 (directory->name, "Collection"))
+    {
+      guint i;
+      for (i = 0; i < tree->collection_applet->len; i++)
+      {
+        const char *desktop_name = g_ptr_array_index (tree->collection_applet, i);
+        get_by_desktop (entry_pool, entries, desktop_name);
+      }
+    }
+#endif /* WITH_COLLECTION */
+
+  desktop_entry_set_foreach (entries,
+                             (DesktopEntrySetForeachFunc) entries_listify_foreach,
+                             directory);
+  desktop_entry_set_unref (entries);
+
+  if (excluded_set != NULL)
+    {
+      desktop_entry_set_foreach (excluded_set,
+				 (DesktopEntrySetForeachFunc) excluded_entries_listify_foreach,
+				 directory);
+      desktop_entry_set_unref (excluded_set);
+    }
+
+  tmp = directory->subdirs;
+  while (tmp != NULL)
+    {
+      MateMenuTreeDirectory *subdir = tmp->data;
+
+      set_default_layout_values (directory, subdir);
+
+      tmp = tmp->next;
+    }
+
+  tmp = directory->entries;
+  while (tmp != NULL)
+    {
+      MateMenuTreeEntry *entry = tmp->data;
+      GSList         *next  = tmp->next;
+      gboolean        delete = FALSE;
+
+      /* If adding a new condition to delete here, it has to be added to
+       * get_still_unallocated_foreach() too */
+
+      if (desktop_entry_get_hidden (entry->desktop_entry))
+        {
+          menu_verbose ("Deleting %s because Hidden=true\n",
+                        desktop_entry_get_name (entry->desktop_entry));
+          delete = TRUE;
+        }
+
+      if (!(tree->flags & MATEMENU_TREE_FLAGS_INCLUDE_NODISPLAY) &&
+          desktop_entry_get_no_display (entry->desktop_entry))
+        {
+          menu_verbose ("Deleting %s because NoDisplay=true\n",
+                        desktop_entry_get_name (entry->desktop_entry));
+          delete = TRUE;
+        }
+
+      if (!desktop_entry_get_show_in (entry->desktop_entry))
+        {
+          menu_verbose ("Deleting %s because OnlyShowIn!=$DESKTOP or NotShowIn=$DESKTOP (with $DESKTOP=${XDG_CURRENT_DESKTOP:-GNOME})\n",
+                        desktop_entry_get_name (entry->desktop_entry));
+          delete = TRUE;
+        }
+
+      /* No need to filter out based on TryExec since GDesktopAppInfo cannot
+       * deal with .desktop files with a failed TryExec. */
+
+      if (delete)
+        {
+          directory->entries = g_slist_delete_link (directory->entries,
+                                                   tmp);
+          matemenu_tree_item_unref_and_unset_parent (entry);
+        }
+
+      tmp = next;
+    }
+
+  g_assert (directory->name != NULL);
+
+  return directory;
+}
+
+static void
+process_only_unallocated (MateMenuTree          *tree,
+			  MateMenuTreeDirectory *directory,
+			  DesktopEntrySet    *allocated,
+			  DesktopEntrySet    *unallocated_used)
+{
+  GSList *tmp;
+
+  /* For any directory marked only_unallocated, we have to remove any
+   * entries that were in fact allocated.
+   */
+
+  if (directory->only_unallocated)
+    {
+      tmp = directory->entries;
+      while (tmp != NULL)
+        {
+          MateMenuTreeEntry *entry = tmp->data;
+          GSList         *next  = tmp->next;
+
+          if (desktop_entry_set_lookup (allocated, entry->desktop_file_id))
+            {
+              directory->entries = g_slist_delete_link (directory->entries,
+                                                        tmp);
+              matemenu_tree_item_unref_and_unset_parent (entry);
+            }
+          else
+            {
+              desktop_entry_set_add_entry (unallocated_used, entry->desktop_entry, entry->desktop_file_id);
+            }
+
+          tmp = next;
+        }
+    }
+
+  tmp = directory->subdirs;
+  while (tmp != NULL)
+    {
+      MateMenuTreeDirectory *subdir = tmp->data;
+
+      process_only_unallocated (tree, subdir, allocated, unallocated_used);
+
+      tmp = tmp->next;
+   }
+}
+
+typedef struct
+{
+  MateMenuTree *tree;
+  DesktopEntrySet *allocated;
+  DesktopEntrySet *unallocated_used;
+  DesktopEntrySet *still_unallocated;
+} GetStillUnallocatedForeachData;
+
+static void
+get_still_unallocated_foreach (const char                     *file_id,
+                               DesktopEntry                   *entry,
+                               GetStillUnallocatedForeachData *data)
+{
+  if (desktop_entry_set_lookup (data->allocated, file_id))
+    return;
+
+  if (desktop_entry_set_lookup (data->unallocated_used, file_id))
+    return;
+
+  /* Same rules than at the end of process_layout() */
+  if (desktop_entry_get_hidden (entry))
+    return;
+
+  if (!(data->tree->flags & MATEMENU_TREE_FLAGS_INCLUDE_NODISPLAY) &&
+      desktop_entry_get_no_display (entry))
+    return;
+
+  if (!desktop_entry_get_show_in (entry))
+    return;
+
+  desktop_entry_set_add_entry (data->still_unallocated, entry, file_id);
+}
+
+static void preprocess_layout_info (MateMenuTree          *tree,
+                                    MateMenuTreeDirectory *directory);
+
+static GSList *
+get_layout_info (MateMenuTreeDirectory *directory,
+                 gboolean           *is_default_layout)
+{
+  MateMenuTreeDirectory *iter;
+
+  if (directory->layout_info != NULL)
+    {
+      if (is_default_layout)
+        {
+          *is_default_layout = FALSE;
+        }
+      return directory->layout_info;
+    }
+
+  /* Even if there's no layout information at all, the result will be an
+   * implicit default layout */
+  if (is_default_layout)
+    {
+      *is_default_layout = TRUE;
+    }
+
+  iter = directory;
+  while (iter != NULL)
+    {
+      /* FIXME: this is broken: we might skip real parent in the
+       * XML structure, that are hidden because of inlining. */
+      if (iter->default_layout_info != NULL)
+	{
+	  return iter->default_layout_info;
+	}
+
+      iter = MATEMENU_TREE_ITEM (iter)->parent;
+    }
+
+  return NULL;
+}
+
+static void
+get_values_with_defaults (MenuLayoutNode   *node,
+			  MenuLayoutValues *layout_values,
+			  MenuLayoutValues *default_layout_values)
+{
+  menu_layout_node_menuname_get_values (node, layout_values);
+
+  if (!(layout_values->mask & MENU_LAYOUT_VALUES_SHOW_EMPTY))
+    layout_values->show_empty = default_layout_values->show_empty;
+
+  if (!(layout_values->mask & MENU_LAYOUT_VALUES_INLINE_MENUS))
+    layout_values->inline_menus = default_layout_values->inline_menus;
+
+  if (!(layout_values->mask & MENU_LAYOUT_VALUES_INLINE_LIMIT))
+    layout_values->inline_limit = default_layout_values->inline_limit;
+
+  if (!(layout_values->mask & MENU_LAYOUT_VALUES_INLINE_HEADER))
+    layout_values->inline_header = default_layout_values->inline_header;
+
+  if (!(layout_values->mask & MENU_LAYOUT_VALUES_INLINE_ALIAS))
+    layout_values->inline_alias = default_layout_values->inline_alias;
+}
+
+static guint
+get_real_subdirs_len (MateMenuTreeDirectory *directory)
+{
+  guint   len;
+  GSList *tmp;
+
+  len = 0;
+
+  tmp = directory->subdirs;
+  while (tmp != NULL)
+    {
+      MateMenuTreeDirectory *subdir = tmp->data;
+
+      tmp = tmp->next;
+
+      if (subdir->will_inline_header != G_MAXUINT16)
+        {
+          len += get_real_subdirs_len (subdir) + g_slist_length (subdir->entries) + 1;
+        }
+      else
+        len += 1;
+    }
+
+  return len;
+}
+
+static void
+preprocess_layout_info_subdir_helper (MateMenuTree          *tree,
+                                      MateMenuTreeDirectory *directory,
+                                      MateMenuTreeDirectory *subdir,
+                                      MenuLayoutValues   *layout_values,
+                                      gboolean           *contents_added,
+                                      gboolean           *should_remove)
+{
+  preprocess_layout_info (tree, subdir);
+
+  *should_remove = FALSE;
+  *contents_added = FALSE;
+
+  if (subdir->subdirs == NULL && subdir->entries == NULL)
+    {
+      if (!(tree->flags & MATEMENU_TREE_FLAGS_SHOW_EMPTY) &&
+          !layout_values->show_empty)
+	{
+	  menu_verbose ("Not showing empty menu '%s'\n", subdir->name);
+	  *should_remove = TRUE;
+	}
+    }
+
+  else if (layout_values->inline_menus)
+    {
+      guint real_subdirs_len;
+
+      real_subdirs_len = get_real_subdirs_len (subdir);
+
+      if (layout_values->inline_alias &&
+          real_subdirs_len + g_slist_length (subdir->entries) == 1)
+        {
+          MateMenuTreeAlias *alias;
+          MateMenuTreeItem  *item;
+          GSList         *list;
+
+          if (subdir->subdirs != NULL)
+            list = subdir->subdirs;
+          else
+            list = subdir->entries;
+
+          item = MATEMENU_TREE_ITEM (list->data);
+
+          menu_verbose ("Inline aliasing '%s' to '%s'\n",
+                        item->type == MATEMENU_TREE_ITEM_ENTRY ?
+                          g_app_info_get_name (G_APP_INFO (matemenu_tree_entry_get_app_info (MATEMENU_TREE_ENTRY (item)))) :
+                          (item->type == MATEMENU_TREE_ITEM_DIRECTORY ?
+                             matemenu_tree_directory_get_name (MATEMENU_TREE_DIRECTORY (item)) :
+                             matemenu_tree_directory_get_name (MATEMENU_TREE_ALIAS (item)->directory)),
+                        subdir->name);
+
+          alias = matemenu_tree_alias_new (directory, subdir, item);
+
+          g_slist_free_full (list,
+                             (GDestroyNotify) matemenu_tree_item_unref_and_unset_parent);
+          subdir->subdirs = NULL;
+          subdir->entries = NULL;
+
+          if (item->type == MATEMENU_TREE_ITEM_DIRECTORY)
+            directory->subdirs = g_slist_append (directory->subdirs, alias);
+          else
+            directory->entries = g_slist_append (directory->entries, alias);
+
+          *contents_added = TRUE;
+          *should_remove = TRUE;
+        }
+
+      else if (layout_values->inline_limit == 0 ||
+               layout_values->inline_limit >= real_subdirs_len + g_slist_length (subdir->entries))
+        {
+          if (layout_values->inline_header)
+            {
+              menu_verbose ("Creating inline header with name '%s'\n", subdir->name);
+              /* we're limited to 16-bits to spare some memory; if the limit is
+               * higher than that (would be crazy), we just consider it's
+               * unlimited */
+              if (layout_values->inline_limit < G_MAXUINT16)
+                subdir->will_inline_header = (guint16) layout_values->inline_limit;
+              else
+                subdir->will_inline_header = 0;
+            }
+          else
+            {
+              g_slist_foreach (subdir->subdirs,
+                               (GFunc) matemenu_tree_item_set_parent,
+                               directory);
+              directory->subdirs = g_slist_concat (directory->subdirs,
+                                                   subdir->subdirs);
+              subdir->subdirs = NULL;
+
+              g_slist_foreach (subdir->entries,
+                               (GFunc) matemenu_tree_item_set_parent,
+                               directory);
+              directory->entries = g_slist_concat (directory->entries,
+                                                   subdir->entries);
+              subdir->entries = NULL;
+
+              *contents_added = TRUE;
+              *should_remove = TRUE;
+            }
+
+          menu_verbose ("Inlining directory contents of '%s' to '%s'\n",
+                        subdir->name, directory->name);
+        }
+    }
+}
+
+static void
+preprocess_layout_info (MateMenuTree          *tree,
+                        MateMenuTreeDirectory *directory)
+{
+  GSList   *tmp;
+  GSList   *layout_info;
+  gboolean  using_default_layout;
+  GSList   *last_subdir;
+  gboolean  strip_duplicates;
+  gboolean  contents_added;
+  gboolean  should_remove;
+  GSList   *subdirs_sentinel;
+
+  /* Note: we need to preprocess all menus, even if the layout mask for a menu
+   * is MENU_LAYOUT_VALUES_NONE: in this case, we need to remove empty menus;
+   * and the layout mask can be different for a submenu anyway */
+
+  menu_verbose ("Processing menu layout inline hints for %s\n", directory->name);
+  g_assert (!directory->preprocessed);
+
+  strip_duplicates = FALSE;
+  /* we use last_subdir to track the last non-inlined subdirectory */
+  last_subdir = g_slist_last (directory->subdirs);
+
+  /*
+   * First process subdirectories with explicit layout
+   */
+  layout_info = get_layout_info (directory, &using_default_layout);
+  tmp = layout_info;
+  /* see comment below about Menuname to understand why we leave the loop if
+   * last_subdir is NULL */
+  while (tmp != NULL && last_subdir != NULL)
+    {
+      MenuLayoutNode     *node = tmp->data;
+      MenuLayoutValues    layout_values;
+      const char         *name;
+      MateMenuTreeDirectory *subdir;
+      GSList             *subdir_l;
+
+      tmp = tmp->next;
+
+      /* only Menuname nodes are relevant here */
+      if (menu_layout_node_get_type (node) != MENU_LAYOUT_NODE_MENUNAME)
+        continue;
+
+      get_values_with_defaults (node,
+                                &layout_values,
+                                &directory->default_layout_values);
+
+      /* find the subdirectory that is affected by those attributes */
+      name = menu_layout_node_get_content (node);
+      subdir = NULL;
+      subdir_l = directory->subdirs;
+      while (subdir_l != NULL)
+        {
+          subdir = subdir_l->data;
+
+          if (!strcmp (subdir->name, name))
+            break;
+
+          subdir = NULL;
+          subdir_l = subdir_l->next;
+
+          /* We do not want to use Menuname on a menu that appeared via
+           * inlining: without inlining, the Menuname wouldn't have matched
+           * anything, and we want to keep the same behavior.
+           * Unless the layout is a default layout, in which case the Menuname
+           * does match the subdirectory. */
+          if (!using_default_layout && subdir_l == last_subdir)
+            {
+              subdir_l = NULL;
+              break;
+            }
+        }
+
+      if (subdir == NULL)
+        continue;
+
+      preprocess_layout_info_subdir_helper (tree, directory,
+                                            subdir, &layout_values,
+                                            &contents_added, &should_remove);
+      strip_duplicates = strip_duplicates || contents_added;
+      if (should_remove)
+        {
+          if (last_subdir == subdir_l)
+            {
+              /* we need to recompute last_subdir since we'll remove it from
+               * the list */
+              GSList *buf;
+
+              if (subdir_l == directory->subdirs)
+                last_subdir = NULL;
+              else
+                {
+                  buf = directory->subdirs;
+                  while (buf != NULL && buf->next != subdir_l)
+                    buf = buf->next;
+                  last_subdir = buf;
+                }
+            }
+
+          directory->subdirs = g_slist_remove (directory->subdirs, subdir);
+          matemenu_tree_item_unref_and_unset_parent (MATEMENU_TREE_ITEM (subdir));
+        }
+    }
+
+  /*
+   * Now process the subdirectories with no explicit layout
+   */
+  /* this is bogus data, but we just need the pointer anyway */
+  subdirs_sentinel = g_slist_prepend (directory->subdirs, PACKAGE);
+  directory->subdirs = subdirs_sentinel;
+
+  tmp = directory->subdirs;
+  while (tmp->next != NULL)
+    {
+      MateMenuTreeDirectory *subdir = tmp->next->data;
+
+      if (subdir->preprocessed)
+        {
+          tmp = tmp->next;
+          continue;
+        }
+
+      preprocess_layout_info_subdir_helper (tree, directory,
+                                            subdir, &directory->default_layout_values,
+                                            &contents_added, &should_remove);
+      strip_duplicates = strip_duplicates || contents_added;
+      if (should_remove)
+        {
+          tmp = g_slist_delete_link (tmp, tmp->next);
+          matemenu_tree_item_unref_and_unset_parent (MATEMENU_TREE_ITEM (subdir));
+        }
+      else
+        tmp = tmp->next;
+    }
+
+  /* remove the sentinel */
+  directory->subdirs = g_slist_delete_link (directory->subdirs,
+                                            directory->subdirs);
+
+  /*
+   * Finally, remove duplicates if needed
+   */
+  if (strip_duplicates)
+    {
+      /* strip duplicate entries; there should be no duplicate directories */
+      directory->entries = g_slist_sort (directory->entries,
+                                         (GCompareFunc) matemenu_tree_entry_compare_by_id);
+      tmp = directory->entries;
+      while (tmp != NULL && tmp->next != NULL)
+        {
+          MateMenuTreeItem *a = tmp->data;
+          MateMenuTreeItem *b = tmp->next->data;
+
+          if (a->type == MATEMENU_TREE_ITEM_ALIAS)
+            a = MATEMENU_TREE_ALIAS (a)->aliased_item;
+
+          if (b->type == MATEMENU_TREE_ITEM_ALIAS)
+            b = MATEMENU_TREE_ALIAS (b)->aliased_item;
+
+          if (strcmp (MATEMENU_TREE_ENTRY (a)->desktop_file_id,
+                      MATEMENU_TREE_ENTRY (b)->desktop_file_id) == 0)
+            {
+              tmp = g_slist_delete_link (tmp, tmp->next);
+              matemenu_tree_item_unref (b);
+            }
+          else
+            tmp = tmp->next;
+        }
+    }
+
+  directory->preprocessed = TRUE;
+}
+
+static void process_layout_info (MateMenuTree          *tree,
+				 MateMenuTreeDirectory *directory);
+
+static void
+check_pending_separator (MateMenuTreeDirectory *directory)
+{
+  if (directory->layout_pending_separator)
+    {
+      menu_verbose ("Adding pending separator in '%s'\n", directory->name);
+
+      directory->contents = g_slist_append (directory->contents,
+					    matemenu_tree_separator_new (directory));
+      directory->layout_pending_separator = FALSE;
+    }
+}
+
+static void
+merge_alias (MateMenuTree          *tree,
+	     MateMenuTreeDirectory *directory,
+	     MateMenuTreeAlias     *alias)
+{
+  menu_verbose ("Merging alias '%s' in directory '%s'\n",
+		alias->directory->name, directory->name);
+
+  if (alias->aliased_item->type == MATEMENU_TREE_ITEM_DIRECTORY)
+    {
+      process_layout_info (tree, MATEMENU_TREE_DIRECTORY (alias->aliased_item));
+    }
+
+  check_pending_separator (directory);
+
+  directory->contents = g_slist_append (directory->contents,
+					matemenu_tree_item_ref (alias));
+}
+
+static void
+merge_subdir (MateMenuTree          *tree,
+	      MateMenuTreeDirectory *directory,
+	      MateMenuTreeDirectory *subdir)
+{
+  menu_verbose ("Merging subdir '%s' in directory '%s'\n",
+		subdir->name, directory->name);
+
+  process_layout_info (tree, subdir);
+
+  check_pending_separator (directory);
+
+  if (subdir->will_inline_header == 0 ||
+      (subdir->will_inline_header != G_MAXUINT16 &&
+       g_slist_length (subdir->contents) <= subdir->will_inline_header))
+    {
+      MateMenuTreeHeader *header;
+
+      header = matemenu_tree_header_new (directory, subdir);
+      directory->contents = g_slist_append (directory->contents, header);
+
+      g_slist_foreach (subdir->contents,
+                       (GFunc) matemenu_tree_item_set_parent,
+                       directory);
+      directory->contents = g_slist_concat (directory->contents,
+                                            subdir->contents);
+      subdir->contents = NULL;
+      subdir->will_inline_header = G_MAXUINT16;
+
+      matemenu_tree_item_set_parent (MATEMENU_TREE_ITEM (subdir), NULL);
+    }
+  else
+    {
+      directory->contents = g_slist_append (directory->contents,
+					    matemenu_tree_item_ref (subdir));
+    }
+}
+
+static void
+merge_subdir_by_name (MateMenuTree          *tree,
+		      MateMenuTreeDirectory *directory,
+		      const char         *subdir_name)
+{
+  GSList *tmp;
+
+  menu_verbose ("Attempting to merge subdir '%s' in directory '%s'\n",
+		subdir_name, directory->name);
+
+  tmp = directory->subdirs;
+  while (tmp != NULL)
+    {
+      MateMenuTreeDirectory *subdir = tmp->data;
+      GSList             *next = tmp->next;
+
+      /* if it's an alias, then it cannot be affected by
+       * the Merge nodes in the layout */
+      if (MATEMENU_TREE_ITEM (subdir)->type == MATEMENU_TREE_ITEM_ALIAS)
+        continue;
+
+      if (!strcmp (subdir->name, subdir_name))
+	{
+	  directory->subdirs = g_slist_delete_link (directory->subdirs, tmp);
+	  merge_subdir (tree, directory, subdir);
+	  matemenu_tree_item_unref (subdir);
+	}
+
+      tmp = next;
+    }
+}
+
+static void
+merge_entry (MateMenuTree          *tree,
+	     MateMenuTreeDirectory *directory,
+	     MateMenuTreeEntry     *entry)
+{
+  menu_verbose ("Merging entry '%s' in directory '%s'\n",
+		entry->desktop_file_id, directory->name);
+
+  check_pending_separator (directory);
+  directory->contents = g_slist_append (directory->contents,
+					matemenu_tree_item_ref (entry));
+}
+
+static void
+merge_entry_by_id (MateMenuTree          *tree,
+		   MateMenuTreeDirectory *directory,
+		   const char         *file_id)
+{
+  GSList *tmp;
+
+  menu_verbose ("Attempting to merge entry '%s' in directory '%s'\n",
+		file_id, directory->name);
+
+  tmp = directory->entries;
+  while (tmp != NULL)
+    {
+      MateMenuTreeEntry *entry = tmp->data;
+      GSList         *next = tmp->next;
+
+      /* if it's an alias, then it cannot be affected by
+       * the Merge nodes in the layout */
+      if (MATEMENU_TREE_ITEM (entry)->type == MATEMENU_TREE_ITEM_ALIAS)
+        continue;
+
+      if (!strcmp (entry->desktop_file_id, file_id))
+	{
+	  directory->entries = g_slist_delete_link (directory->entries, tmp);
+	  merge_entry (tree, directory, entry);
+	  matemenu_tree_item_unref (entry);
+	}
+
+      tmp = next;
+    }
+}
+
+static inline gboolean
+find_name_in_list (const char *name,
+		   GSList     *list)
+{
+  while (list != NULL)
+    {
+      if (!strcmp (name, list->data))
+	return TRUE;
+
+      list = list->next;
+    }
+
+  return FALSE;
+}
+
+static void
+merge_subdirs (MateMenuTree          *tree,
+	       MateMenuTreeDirectory *directory,
+	       GSList             *except)
+{
+  GSList *subdirs;
+  GSList *tmp;
+
+  menu_verbose ("Merging subdirs in directory '%s'\n", directory->name);
+
+  subdirs = directory->subdirs;
+  directory->subdirs = NULL;
+
+  subdirs = g_slist_sort_with_data (subdirs,
+                                    (GCompareDataFunc) matemenu_tree_item_compare,
+                                    (void *) MATEMENU_TREE_FLAGS_NONE);
+
+  tmp = subdirs;
+  while (tmp != NULL)
+    {
+      MateMenuTreeDirectory *subdir = tmp->data;
+
+      if (MATEMENU_TREE_ITEM (subdir)->type == MATEMENU_TREE_ITEM_ALIAS)
+        {
+	  merge_alias (tree, directory, MATEMENU_TREE_ALIAS (subdir));
+	  matemenu_tree_item_unref (subdir);
+        }
+      else if (!find_name_in_list (subdir->name, except))
+	{
+	  merge_subdir (tree, directory, subdir);
+	  matemenu_tree_item_unref (subdir);
+	}
+      else
+	{
+	  menu_verbose ("Not merging directory '%s' yet\n", subdir->name);
+	  directory->subdirs = g_slist_append (directory->subdirs, subdir);
+	}
+
+      tmp = tmp->next;
+    }
+
+  g_slist_free (subdirs);
+  g_slist_free (except);
+}
+
+static void
+merge_entries (MateMenuTree          *tree,
+	       MateMenuTreeDirectory *directory,
+	       GSList             *except)
+{
+  GSList *entries;
+  GSList *tmp;
+
+  menu_verbose ("Merging entries in directory '%s'\n", directory->name);
+
+  entries = directory->entries;
+  directory->entries = NULL;
+
+  entries = g_slist_sort_with_data (entries,
+				    (GCompareDataFunc) matemenu_tree_item_compare,
+                                    (void *) tree->flags);
+
+  tmp = entries;
+  while (tmp != NULL)
+    {
+      MateMenuTreeEntry *entry = tmp->data;
+
+      if (MATEMENU_TREE_ITEM (entry)->type == MATEMENU_TREE_ITEM_ALIAS)
+        {
+	  merge_alias (tree, directory, MATEMENU_TREE_ALIAS (entry));
+	  matemenu_tree_item_unref (entry);
+        }
+      else if (!find_name_in_list (entry->desktop_file_id, except))
+	{
+	  merge_entry (tree, directory, entry);
+	  matemenu_tree_item_unref (entry);
+	}
+      else
+	{
+	  menu_verbose ("Not merging entry '%s' yet\n", entry->desktop_file_id);
+	  directory->entries = g_slist_append (directory->entries, entry);
+	}
+
+      tmp = tmp->next;
+    }
+
+  g_slist_free (entries);
+  g_slist_free (except);
+}
+
+static void
+merge_subdirs_and_entries (MateMenuTree          *tree,
+			   MateMenuTreeDirectory *directory,
+			   GSList             *except_subdirs,
+			   GSList             *except_entries)
+{
+  GSList *items;
+  GSList *tmp;
+
+  menu_verbose ("Merging subdirs and entries together in directory %s\n",
+		directory->name);
+
+  items = g_slist_concat (directory->subdirs, directory->entries);
+
+  directory->subdirs = NULL;
+  directory->entries = NULL;
+
+  items = g_slist_sort_with_data (items,
+                                  (GCompareDataFunc) matemenu_tree_item_compare,
+                                  (void *) tree->flags);
+
+  tmp = items;
+  while (tmp != NULL)
+    {
+      MateMenuTreeItem     *item = tmp->data;
+      MateMenuTreeItemType  type;
+
+      type = item->type;
+
+      if (type == MATEMENU_TREE_ITEM_ALIAS)
+        {
+          merge_alias (tree, directory, MATEMENU_TREE_ALIAS (item));
+          matemenu_tree_item_unref (item);
+        }
+      else if (type == MATEMENU_TREE_ITEM_DIRECTORY)
+	{
+	  if (!find_name_in_list (MATEMENU_TREE_DIRECTORY (item)->name, except_subdirs))
+	    {
+	      merge_subdir (tree,
+			    directory,
+			    MATEMENU_TREE_DIRECTORY (item));
+	      matemenu_tree_item_unref (item);
+	    }
+	  else
+	    {
+	      menu_verbose ("Not merging directory '%s' yet\n",
+			    MATEMENU_TREE_DIRECTORY (item)->name);
+	      directory->subdirs = g_slist_append (directory->subdirs, item);
+	    }
+	}
+      else if (type == MATEMENU_TREE_ITEM_ENTRY)
+	{
+	  if (!find_name_in_list (MATEMENU_TREE_ENTRY (item)->desktop_file_id, except_entries))
+	    {
+	      merge_entry (tree, directory, MATEMENU_TREE_ENTRY (item));
+	      matemenu_tree_item_unref (item);
+	    }
+	  else
+	    {
+	      menu_verbose ("Not merging entry '%s' yet\n",
+			    MATEMENU_TREE_ENTRY (item)->desktop_file_id);
+	      directory->entries = g_slist_append (directory->entries, item);
+	    }
+	}
+      else
+        {
+          g_assert_not_reached ();
+        }
+
+      tmp = tmp->next;
+    }
+
+  g_slist_free (items);
+  g_slist_free (except_subdirs);
+  g_slist_free (except_entries);
+}
+
+static GSList *
+get_subdirs_from_layout_info (GSList *layout_info)
+{
+  GSList *subdirs;
+  GSList *tmp;
+
+  subdirs = NULL;
+
+  tmp = layout_info;
+  while (tmp != NULL)
+    {
+      MenuLayoutNode *node = tmp->data;
+
+      if (menu_layout_node_get_type (node) == MENU_LAYOUT_NODE_MENUNAME)
+	{
+	  subdirs = g_slist_append (subdirs,
+				    (char *) menu_layout_node_get_content (node));
+	}
+
+      tmp = tmp->next;
+    }
+
+  return subdirs;
+}
+
+static GSList *
+get_entries_from_layout_info (GSList *layout_info)
+{
+  GSList *entries;
+  GSList *tmp;
+
+  entries = NULL;
+
+  tmp = layout_info;
+  while (tmp != NULL)
+    {
+      MenuLayoutNode *node = tmp->data;
+
+      if (menu_layout_node_get_type (node) == MENU_LAYOUT_NODE_FILENAME)
+	{
+	  entries = g_slist_append (entries,
+				    (char *) menu_layout_node_get_content (node));
+	}
+
+      tmp = tmp->next;
+    }
+
+  return entries;
+}
+
+static void
+process_layout_info (MateMenuTree          *tree,
+		     MateMenuTreeDirectory *directory)
+{
+  GSList *layout_info;
+
+  menu_verbose ("Processing menu layout hints for %s\n", directory->name);
+
+  g_slist_foreach (directory->contents,
+		   (GFunc) matemenu_tree_item_unref_and_unset_parent,
+		   NULL);
+  g_slist_free (directory->contents);
+  directory->contents = NULL;
+  directory->layout_pending_separator = FALSE;
+
+  layout_info = get_layout_info (directory, NULL);
+
+  if (layout_info == NULL)
+    {
+      merge_subdirs (tree, directory, NULL);
+      merge_entries (tree, directory, NULL);
+    }
+  else
+    {
+      GSList *tmp;
+
+      tmp = layout_info;
+      while (tmp != NULL)
+	{
+	  MenuLayoutNode *node = tmp->data;
+
+	  switch (menu_layout_node_get_type (node))
+	    {
+	    case MENU_LAYOUT_NODE_MENUNAME:
+              merge_subdir_by_name (tree,
+                                    directory,
+                                    menu_layout_node_get_content (node));
+	      break;
+
+	    case MENU_LAYOUT_NODE_FILENAME:
+	      merge_entry_by_id (tree,
+				 directory,
+				 menu_layout_node_get_content (node));
+	      break;
+
+	    case MENU_LAYOUT_NODE_SEPARATOR:
+	      /* Unless explicitly told to show all separators, do not show a
+	       * separator at the beginning of a menu. Note that we don't add
+	       * the separators now, and instead make it pending. This way, we
+	       * won't show two consecutive separators nor will we show a
+	       * separator at the end of a menu. */
+              if (tree->flags & MATEMENU_TREE_FLAGS_SHOW_ALL_SEPARATORS)
+		{
+		  directory->layout_pending_separator = TRUE;
+		  check_pending_separator (directory);
+		}
+	      else if (directory->contents)
+		{
+		  menu_verbose ("Adding a potential separator in '%s'\n",
+				directory->name);
+
+		  directory->layout_pending_separator = TRUE;
+		}
+	      else
+		{
+		  menu_verbose ("Skipping separator at the beginning of '%s'\n",
+				directory->name);
+		}
+	      break;
+
+	    case MENU_LAYOUT_NODE_MERGE:
+	      switch (menu_layout_node_merge_get_type (node))
+		{
+		case MENU_LAYOUT_MERGE_NONE:
+		  break;
+
+		case MENU_LAYOUT_MERGE_MENUS:
+		  merge_subdirs (tree,
+				 directory,
+				 get_subdirs_from_layout_info (tmp->next));
+		  break;
+
+		case MENU_LAYOUT_MERGE_FILES:
+		  merge_entries (tree,
+				 directory,
+				 get_entries_from_layout_info (tmp->next));
+		  break;
+
+		case MENU_LAYOUT_MERGE_ALL:
+		  merge_subdirs_and_entries (tree,
+					     directory,
+					     get_subdirs_from_layout_info (tmp->next),
+					     get_entries_from_layout_info (tmp->next));
+		  break;
+
+		default:
+		  g_assert_not_reached ();
+		  break;
+		}
+	      break;
+
+	    default:
+	      g_assert_not_reached ();
+	      break;
+	    }
+
+	  tmp = tmp->next;
+	}
+    }
+
+  g_slist_free_full (directory->subdirs,
+                     matemenu_tree_item_unref);
+  directory->subdirs = NULL;
+
+  g_slist_free_full (directory->entries,
+                     matemenu_tree_item_unref);
+  directory->entries = NULL;
+
+  g_slist_free_full (directory->default_layout_info,
+                     (GDestroyNotify) menu_layout_node_unref);
+  directory->default_layout_info = NULL;
+
+  g_slist_free_full (directory->layout_info,
+                     (GDestroyNotify) menu_layout_node_unref);
+  directory->layout_info = NULL;
+}
+
+static void
+handle_entries_changed (MenuLayoutNode *layout,<--- Parameter 'layout' can be declared as pointer to const
+                        MateMenuTree       *tree)
+{
+  if (tree->layout == layout)
+    {
+      matemenu_tree_force_rebuild (tree);
+      matemenu_tree_invoke_monitors (tree);
+    }
+}
+
+static void
+update_entry_index (MateMenuTree           *tree,
+		    MateMenuTreeDirectory  *dir)
+{
+  MateMenuTreeIter *iter = matemenu_tree_directory_iter (dir);
+  MateMenuTreeItemType next_type;
+
+  while ((next_type = matemenu_tree_iter_next (iter)) != MATEMENU_TREE_ITEM_INVALID)
+    {
+      gpointer item = NULL;
+
+      switch (next_type)
+        {
+        case MATEMENU_TREE_ITEM_ENTRY:
+          {
+	    const char *id;
+
+            item = matemenu_tree_iter_get_entry (iter);
+            id = matemenu_tree_entry_get_desktop_file_id (item);
+            if (id != NULL)
+              g_hash_table_insert (tree->entries_by_id, (char*)id, item);
+          }
+          break;
+        case MATEMENU_TREE_ITEM_DIRECTORY:
+          {
+            item = matemenu_tree_iter_get_directory (iter);
+            update_entry_index (tree, (MateMenuTreeDirectory*)item);
+          }
+          break;
+        default:
+          break;
+        }
+      if (item != NULL)
+        matemenu_tree_item_unref (item);
+    }
+
+  matemenu_tree_iter_unref (iter);
+}
+
+static gboolean
+matemenu_tree_build_from_layout (MateMenuTree  *tree,
+                              GError    **error)
+{
+  DesktopEntrySet *allocated;
+
+  if (tree->root)
+    return TRUE;
+
+  if (!matemenu_tree_load_layout (tree, error))
+    return FALSE;
+
+  menu_verbose ("Building menu tree from layout\n");
+
+  allocated = desktop_entry_set_new ();
+
+  /* create the menu structure */
+  tree->root = process_layout (tree,
+                               NULL,
+                               find_menu_child (tree->layout),
+                               allocated);
+  if (tree->root)
+    {
+      DesktopEntrySet *unallocated_used;
+
+      unallocated_used = desktop_entry_set_new ();
+
+      process_only_unallocated (tree, tree->root, allocated, unallocated_used);
+      if (tree->flags & MATEMENU_TREE_FLAGS_INCLUDE_UNALLOCATED)
+        {
+          DesktopEntrySet *entry_pool;
+          DesktopEntrySet *still_unallocated;
+          GetStillUnallocatedForeachData data;
+
+          entry_pool = _entry_directory_list_get_all_desktops (menu_layout_node_menu_get_app_dirs (find_menu_child (tree->layout)));
+          still_unallocated = desktop_entry_set_new ();
+
+          data.tree = tree;
+          data.allocated = allocated;
+          data.unallocated_used = unallocated_used;
+          data.still_unallocated = still_unallocated;
+
+          desktop_entry_set_foreach (entry_pool,
+                                     (DesktopEntrySetForeachFunc) get_still_unallocated_foreach,
+                                     &data);
+
+          desktop_entry_set_unref (entry_pool);
+
+          desktop_entry_set_foreach (still_unallocated,
+                                     (DesktopEntrySetForeachFunc) unallocated_entries_listify_foreach,
+                                     tree->root);
+
+          desktop_entry_set_unref (still_unallocated);
+        }
+
+      desktop_entry_set_unref (unallocated_used);
+
+      /* process the layout info part that can move/remove items:
+       * inline, show_empty, etc. */
+      preprocess_layout_info (tree, tree->root);
+      /* populate the menu structure that we got with the items, and order it
+       * according to the layout info */
+      process_layout_info (tree, tree->root);
+
+      update_entry_index (tree, tree->root);
+
+      menu_layout_node_root_add_entries_monitor (tree->layout,
+                                                 (MenuLayoutNodeEntriesChangedFunc) handle_entries_changed,
+                                                 tree);
+    }
+
+  desktop_entry_set_unref (allocated);
+
+  return TRUE;
+}
+
+static void
+matemenu_tree_force_rebuild (MateMenuTree *tree)
+{
+  if (tree->root)
+    {
+      g_hash_table_remove_all (tree->entries_by_id);
+      matemenu_tree_item_unref (tree->root);
+      tree->root = NULL;
+      tree->loaded = FALSE;
+
+      g_assert (tree->layout != NULL);
+
+      menu_layout_node_root_remove_entries_monitor (tree->layout,
+                                                    (MenuLayoutNodeEntriesChangedFunc) handle_entries_changed,
+                                                    tree);
+    }
+}
+
+GType
+matemenu_tree_iter_get_type (void)
+{
+  static GType gtype = G_TYPE_INVALID;
+  if (gtype == G_TYPE_INVALID)
+    {
+      gtype = g_boxed_type_register_static ("MateMenuTreeIter",
+          (GBoxedCopyFunc)matemenu_tree_iter_ref,
+          (GBoxedFreeFunc)matemenu_tree_iter_unref);
+    }
+  return gtype;
+}
+
+GType
+matemenu_tree_directory_get_type (void)
+{
+  static GType gtype = G_TYPE_INVALID;
+  if (gtype == G_TYPE_INVALID)
+    {
+      gtype = g_boxed_type_register_static ("MateMenuTreeDirectory",
+          (GBoxedCopyFunc)matemenu_tree_item_ref,
+          (GBoxedFreeFunc)matemenu_tree_item_unref);
+    }
+  return gtype;
+}
+
+GType
+matemenu_tree_entry_get_type (void)
+{
+  static GType gtype = G_TYPE_INVALID;
+  if (gtype == G_TYPE_INVALID)
+    {
+      gtype = g_boxed_type_register_static ("MateMenuTreeEntry",
+          (GBoxedCopyFunc)matemenu_tree_item_ref,
+          (GBoxedFreeFunc)matemenu_tree_item_unref);
+    }
+  return gtype;
+}
+
+GType
+matemenu_tree_separator_get_type (void)
+{
+  static GType gtype = G_TYPE_INVALID;
+  if (gtype == G_TYPE_INVALID)
+    {
+      gtype = g_boxed_type_register_static ("MateMenuTreeSeparator",
+          (GBoxedCopyFunc)matemenu_tree_item_ref,
+          (GBoxedFreeFunc)matemenu_tree_item_unref);
+    }
+  return gtype;
+}
+
+GType
+matemenu_tree_header_get_type (void)
+{
+  static GType gtype = G_TYPE_INVALID;
+  if (gtype == G_TYPE_INVALID)
+    {
+      gtype = g_boxed_type_register_static ("MateMenuTreeHeader",
+          (GBoxedCopyFunc)matemenu_tree_item_ref,
+          (GBoxedFreeFunc)matemenu_tree_item_unref);
+    }
+  return gtype;
+}
+
+GType
+matemenu_tree_alias_get_type (void)
+{
+  static GType gtype = G_TYPE_INVALID;
+  if (gtype == G_TYPE_INVALID)
+    {
+      gtype = g_boxed_type_register_static ("MateMenuTreeAlias",
+          (GBoxedCopyFunc)matemenu_tree_item_ref,
+          (GBoxedFreeFunc)matemenu_tree_item_unref);
+    }
+  return gtype;
+}
+
+GType
+matemenu_tree_flags_get_type (void)
+{
+  static GType enum_type_id = 0;
+  if (G_UNLIKELY (!enum_type_id))
+    {
+      static const GFlagsValue values[] = {
+        { MATEMENU_TREE_FLAGS_NONE, "MATEMENU_TREE_FLAGS_NONE", "none" },
+        { MATEMENU_TREE_FLAGS_INCLUDE_EXCLUDED, "MATEMENU_TREE_FLAGS_INCLUDE_EXCLUDED", "include-excluded" },
+        { MATEMENU_TREE_FLAGS_SHOW_EMPTY, "MATEMENU_TREE_FLAGS_SHOW_EMPTY", "show-empty" },
+        { MATEMENU_TREE_FLAGS_INCLUDE_NODISPLAY, "MATEMENU_TREE_FLAGS_INCLUDE_NODISPLAY", "include-nodisplay" },
+        { MATEMENU_TREE_FLAGS_SHOW_ALL_SEPARATORS, "MATEMENU_TREE_FLAGS_SHOW_ALL_SEPARATORS", "show-all-separators" },
+        { MATEMENU_TREE_FLAGS_SORT_DISPLAY_NAME, "MATEMENU_TREE_FLAGS_SORT_DISPLAY_NAME", "sort-display-name" },
+        { MATEMENU_TREE_FLAGS_INCLUDE_UNALLOCATED, "MATEMENU_TREE_FLAGS_INCLUDE_UNALLOCATED,", "include-unallocated" },
+        { 0, NULL, NULL }
+      };
+      enum_type_id = g_flags_register_static ("MateMenuTreeFlags", values);
+    }
+  return enum_type_id;
+}
+
+ +
+ +
+ + diff --git a/2023-09-02-151726-1231-cppcheck@7aac87a9810a_master/2.html b/2023-09-02-151726-1231-cppcheck@7aac87a9810a_master/2.html new file mode 100644 index 0000000..1caec7b --- /dev/null +++ b/2023-09-02-151726-1231-cppcheck@7aac87a9810a_master/2.html @@ -0,0 +1,5217 @@ + + + + + + Cppcheck - HTML report - mate-menus + + + + + +
+ + + +
+
   1
+   2
+   3
+   4
+   5
+   6
+   7
+   8
+   9
+  10
+  11
+  12
+  13
+  14
+  15
+  16
+  17
+  18
+  19
+  20
+  21
+  22
+  23
+  24
+  25
+  26
+  27
+  28
+  29
+  30
+  31
+  32
+  33
+  34
+  35
+  36
+  37
+  38
+  39
+  40
+  41
+  42
+  43
+  44
+  45
+  46
+  47
+  48
+  49
+  50
+  51
+  52
+  53
+  54
+  55
+  56
+  57
+  58
+  59
+  60
+  61
+  62
+  63
+  64
+  65
+  66
+  67
+  68
+  69
+  70
+  71
+  72
+  73
+  74
+  75
+  76
+  77
+  78
+  79
+  80
+  81
+  82
+  83
+  84
+  85
+  86
+  87
+  88
+  89
+  90
+  91
+  92
+  93
+  94
+  95
+  96
+  97
+  98
+  99
+ 100
+ 101
+ 102
+ 103
+ 104
+ 105
+ 106
+ 107
+ 108
+ 109
+ 110
+ 111
+ 112
+ 113
+ 114
+ 115
+ 116
+ 117
+ 118
+ 119
+ 120
+ 121
+ 122
+ 123
+ 124
+ 125
+ 126
+ 127
+ 128
+ 129
+ 130
+ 131
+ 132
+ 133
+ 134
+ 135
+ 136
+ 137
+ 138
+ 139
+ 140
+ 141
+ 142
+ 143
+ 144
+ 145
+ 146
+ 147
+ 148
+ 149
+ 150
+ 151
+ 152
+ 153
+ 154
+ 155
+ 156
+ 157
+ 158
+ 159
+ 160
+ 161
+ 162
+ 163
+ 164
+ 165
+ 166
+ 167
+ 168
+ 169
+ 170
+ 171
+ 172
+ 173
+ 174
+ 175
+ 176
+ 177
+ 178
+ 179
+ 180
+ 181
+ 182
+ 183
+ 184
+ 185
+ 186
+ 187
+ 188
+ 189
+ 190
+ 191
+ 192
+ 193
+ 194
+ 195
+ 196
+ 197
+ 198
+ 199
+ 200
+ 201
+ 202
+ 203
+ 204
+ 205
+ 206
+ 207
+ 208
+ 209
+ 210
+ 211
+ 212
+ 213
+ 214
+ 215
+ 216
+ 217
+ 218
+ 219
+ 220
+ 221
+ 222
+ 223
+ 224
+ 225
+ 226
+ 227
+ 228
+ 229
+ 230
+ 231
+ 232
+ 233
+ 234
+ 235
+ 236
+ 237
+ 238
+ 239
+ 240
+ 241
+ 242
+ 243
+ 244
+ 245
+ 246
+ 247
+ 248
+ 249
+ 250
+ 251
+ 252
+ 253
+ 254
+ 255
+ 256
+ 257
+ 258
+ 259
+ 260
+ 261
+ 262
+ 263
+ 264
+ 265
+ 266
+ 267
+ 268
+ 269
+ 270
+ 271
+ 272
+ 273
+ 274
+ 275
+ 276
+ 277
+ 278
+ 279
+ 280
+ 281
+ 282
+ 283
+ 284
+ 285
+ 286
+ 287
+ 288
+ 289
+ 290
+ 291
+ 292
+ 293
+ 294
+ 295
+ 296
+ 297
+ 298
+ 299
+ 300
+ 301
+ 302
+ 303
+ 304
+ 305
+ 306
+ 307
+ 308
+ 309
+ 310
+ 311
+ 312
+ 313
+ 314
+ 315
+ 316
+ 317
+ 318
+ 319
+ 320
+ 321
+ 322
+ 323
+ 324
+ 325
+ 326
+ 327
+ 328
+ 329
+ 330
+ 331
+ 332
+ 333
+ 334
+ 335
+ 336
+ 337
+ 338
+ 339
+ 340
+ 341
+ 342
+ 343
+ 344
+ 345
+ 346
+ 347
+ 348
+ 349
+ 350
+ 351
+ 352
+ 353
+ 354
+ 355
+ 356
+ 357
+ 358
+ 359
+ 360
+ 361
+ 362
+ 363
+ 364
+ 365
+ 366
+ 367
+ 368
+ 369
+ 370
+ 371
+ 372
+ 373
+ 374
+ 375
+ 376
+ 377
+ 378
+ 379
+ 380
+ 381
+ 382
+ 383
+ 384
+ 385
+ 386
+ 387
+ 388
+ 389
+ 390
+ 391
+ 392
+ 393
+ 394
+ 395
+ 396
+ 397
+ 398
+ 399
+ 400
+ 401
+ 402
+ 403
+ 404
+ 405
+ 406
+ 407
+ 408
+ 409
+ 410
+ 411
+ 412
+ 413
+ 414
+ 415
+ 416
+ 417
+ 418
+ 419
+ 420
+ 421
+ 422
+ 423
+ 424
+ 425
+ 426
+ 427
+ 428
+ 429
+ 430
+ 431
+ 432
+ 433
+ 434
+ 435
+ 436
+ 437
+ 438
+ 439
+ 440
+ 441
+ 442
+ 443
+ 444
+ 445
+ 446
+ 447
+ 448
+ 449
+ 450
+ 451
+ 452
+ 453
+ 454
+ 455
+ 456
+ 457
+ 458
+ 459
+ 460
+ 461
+ 462
+ 463
+ 464
+ 465
+ 466
+ 467
+ 468
+ 469
+ 470
+ 471
+ 472
+ 473
+ 474
+ 475
+ 476
+ 477
+ 478
+ 479
+ 480
+ 481
+ 482
+ 483
+ 484
+ 485
+ 486
+ 487
+ 488
+ 489
+ 490
+ 491
+ 492
+ 493
+ 494
+ 495
+ 496
+ 497
+ 498
+ 499
+ 500
+ 501
+ 502
+ 503
+ 504
+ 505
+ 506
+ 507
+ 508
+ 509
+ 510
+ 511
+ 512
+ 513
+ 514
+ 515
+ 516
+ 517
+ 518
+ 519
+ 520
+ 521
+ 522
+ 523
+ 524
+ 525
+ 526
+ 527
+ 528
+ 529
+ 530
+ 531
+ 532
+ 533
+ 534
+ 535
+ 536
+ 537
+ 538
+ 539
+ 540
+ 541
+ 542
+ 543
+ 544
+ 545
+ 546
+ 547
+ 548
+ 549
+ 550
+ 551
+ 552
+ 553
+ 554
+ 555
+ 556
+ 557
+ 558
+ 559
+ 560
+ 561
+ 562
+ 563
+ 564
+ 565
+ 566
+ 567
+ 568
+ 569
+ 570
+ 571
+ 572
+ 573
+ 574
+ 575
+ 576
+ 577
+ 578
+ 579
+ 580
+ 581
+ 582
+ 583
+ 584
+ 585
+ 586
+ 587
+ 588
+ 589
+ 590
+ 591
+ 592
+ 593
+ 594
+ 595
+ 596
+ 597
+ 598
+ 599
+ 600
+ 601
+ 602
+ 603
+ 604
+ 605
+ 606
+ 607
+ 608
+ 609
+ 610
+ 611
+ 612
+ 613
+ 614
+ 615
+ 616
+ 617
+ 618
+ 619
+ 620
+ 621
+ 622
+ 623
+ 624
+ 625
+ 626
+ 627
+ 628
+ 629
+ 630
+ 631
+ 632
+ 633
+ 634
+ 635
+ 636
+ 637
+ 638
+ 639
+ 640
+ 641
+ 642
+ 643
+ 644
+ 645
+ 646
+ 647
+ 648
+ 649
+ 650
+ 651
+ 652
+ 653
+ 654
+ 655
+ 656
+ 657
+ 658
+ 659
+ 660
+ 661
+ 662
+ 663
+ 664
+ 665
+ 666
+ 667
+ 668
+ 669
+ 670
+ 671
+ 672
+ 673
+ 674
+ 675
+ 676
+ 677
+ 678
+ 679
+ 680
+ 681
+ 682
+ 683
+ 684
+ 685
+ 686
+ 687
+ 688
+ 689
+ 690
+ 691
+ 692
+ 693
+ 694
+ 695
+ 696
+ 697
+ 698
+ 699
+ 700
+ 701
+ 702
+ 703
+ 704
+ 705
+ 706
+ 707
+ 708
+ 709
+ 710
+ 711
+ 712
+ 713
+ 714
+ 715
+ 716
+ 717
+ 718
+ 719
+ 720
+ 721
+ 722
+ 723
+ 724
+ 725
+ 726
+ 727
+ 728
+ 729
+ 730
+ 731
+ 732
+ 733
+ 734
+ 735
+ 736
+ 737
+ 738
+ 739
+ 740
+ 741
+ 742
+ 743
+ 744
+ 745
+ 746
+ 747
+ 748
+ 749
+ 750
+ 751
+ 752
+ 753
+ 754
+ 755
+ 756
+ 757
+ 758
+ 759
+ 760
+ 761
+ 762
+ 763
+ 764
+ 765
+ 766
+ 767
+ 768
+ 769
+ 770
+ 771
+ 772
+ 773
+ 774
+ 775
+ 776
+ 777
+ 778
+ 779
+ 780
+ 781
+ 782
+ 783
+ 784
+ 785
+ 786
+ 787
+ 788
+ 789
+ 790
+ 791
+ 792
+ 793
+ 794
+ 795
+ 796
+ 797
+ 798
+ 799
+ 800
+ 801
+ 802
+ 803
+ 804
+ 805
+ 806
+ 807
+ 808
+ 809
+ 810
+ 811
+ 812
+ 813
+ 814
+ 815
+ 816
+ 817
+ 818
+ 819
+ 820
+ 821
+ 822
+ 823
+ 824
+ 825
+ 826
+ 827
+ 828
+ 829
+ 830
+ 831
+ 832
+ 833
+ 834
+ 835
+ 836
+ 837
+ 838
+ 839
+ 840
+ 841
+ 842
+ 843
+ 844
+ 845
+ 846
+ 847
+ 848
+ 849
+ 850
+ 851
+ 852
+ 853
+ 854
+ 855
+ 856
+ 857
+ 858
+ 859
+ 860
+ 861
+ 862
+ 863
+ 864
+ 865
+ 866
+ 867
+ 868
+ 869
+ 870
+ 871
+ 872
+ 873
+ 874
+ 875
+ 876
+ 877
+ 878
+ 879
+ 880
+ 881
+ 882
+ 883
+ 884
+ 885
+ 886
+ 887
+ 888
+ 889
+ 890
+ 891
+ 892
+ 893
+ 894
+ 895
+ 896
+ 897
+ 898
+ 899
+ 900
+ 901
+ 902
+ 903
+ 904
+ 905
+ 906
+ 907
+ 908
+ 909
+ 910
+ 911
+ 912
+ 913
+ 914
+ 915
+ 916
+ 917
+ 918
+ 919
+ 920
+ 921
+ 922
+ 923
+ 924
+ 925
+ 926
+ 927
+ 928
+ 929
+ 930
+ 931
+ 932
+ 933
+ 934
+ 935
+ 936
+ 937
+ 938
+ 939
+ 940
+ 941
+ 942
+ 943
+ 944
+ 945
+ 946
+ 947
+ 948
+ 949
+ 950
+ 951
+ 952
+ 953
+ 954
+ 955
+ 956
+ 957
+ 958
+ 959
+ 960
+ 961
+ 962
+ 963
+ 964
+ 965
+ 966
+ 967
+ 968
+ 969
+ 970
+ 971
+ 972
+ 973
+ 974
+ 975
+ 976
+ 977
+ 978
+ 979
+ 980
+ 981
+ 982
+ 983
+ 984
+ 985
+ 986
+ 987
+ 988
+ 989
+ 990
+ 991
+ 992
+ 993
+ 994
+ 995
+ 996
+ 997
+ 998
+ 999
+1000
+1001
+1002
+1003
+1004
+1005
+1006
+1007
+1008
+1009
+1010
+1011
+1012
+1013
+1014
+1015
+1016
+1017
+1018
+1019
+1020
+1021
+1022
+1023
+1024
+1025
+1026
+1027
+1028
+1029
+1030
+1031
+1032
+1033
+1034
+1035
+1036
+1037
+1038
+1039
+1040
+1041
+1042
+1043
+1044
+1045
+1046
+1047
+1048
+1049
+1050
+1051
+1052
+1053
+1054
+1055
+1056
+1057
+1058
+1059
+1060
+1061
+1062
+1063
+1064
+1065
+1066
+1067
+1068
+1069
+1070
+1071
+1072
+1073
+1074
+1075
+1076
+1077
+1078
+1079
+1080
+1081
+1082
+1083
+1084
+1085
+1086
+1087
+1088
+1089
+1090
+1091
+1092
+1093
+1094
+1095
+1096
+1097
+1098
+1099
+1100
+1101
+1102
+1103
+1104
+1105
+1106
+1107
+1108
+1109
+1110
+1111
+1112
+1113
+1114
+1115
+1116
+1117
+1118
+1119
+1120
+1121
+1122
+1123
+1124
+1125
+1126
+1127
+1128
+1129
+1130
+1131
+1132
+1133
+1134
+1135
+1136
+1137
+1138
+1139
+1140
+1141
+1142
+1143
+1144
+1145
+1146
+1147
+1148
+1149
+1150
+1151
+1152
+1153
+1154
+1155
+1156
+1157
+1158
+1159
+1160
+1161
+1162
+1163
+1164
+1165
+1166
+1167
+1168
+1169
+1170
+1171
+1172
+1173
+1174
+1175
+1176
+1177
+1178
+1179
+1180
+1181
+1182
+1183
+1184
+1185
+1186
+1187
+1188
+1189
+1190
+1191
+1192
+1193
+1194
+1195
+1196
+1197
+1198
+1199
+1200
+1201
+1202
+1203
+1204
+1205
+1206
+1207
+1208
+1209
+1210
+1211
+1212
+1213
+1214
+1215
+1216
+1217
+1218
+1219
+1220
+1221
+1222
+1223
+1224
+1225
+1226
+1227
+1228
+1229
+1230
+1231
+1232
+1233
+1234
+1235
+1236
+1237
+1238
+1239
+1240
+1241
+1242
+1243
+1244
+1245
+1246
+1247
+1248
+1249
+1250
+1251
+1252
+1253
+1254
+1255
+1256
+1257
+1258
+1259
+1260
+1261
+1262
+1263
+1264
+1265
+1266
+1267
+1268
+1269
+1270
+1271
+1272
+1273
+1274
+1275
+1276
+1277
+1278
+1279
+1280
+1281
+1282
+1283
+1284
+1285
+1286
+1287
+1288
+1289
+1290
+1291
+1292
+1293
+1294
+1295
+1296
+1297
+1298
+1299
+1300
+1301
+1302
+1303
+1304
+1305
+1306
+1307
+1308
+1309
+1310
+1311
+1312
+1313
+1314
+1315
+1316
+1317
+1318
+1319
+1320
+1321
+1322
+1323
+1324
+1325
+1326
+1327
+1328
+1329
+1330
+1331
+1332
+1333
+1334
+1335
+1336
+1337
+1338
+1339
+1340
+1341
+1342
+1343
+1344
+1345
+1346
+1347
+1348
+1349
+1350
+1351
+1352
+1353
+1354
+1355
+1356
+1357
+1358
+1359
+1360
+1361
+1362
+1363
+1364
+1365
+1366
+1367
+1368
+1369
+1370
+1371
+1372
+1373
+1374
+1375
+1376
+1377
+1378
+1379
+1380
+1381
+1382
+1383
+1384
+1385
+1386
+1387
+1388
+1389
+1390
+1391
+1392
+1393
+1394
+1395
+1396
+1397
+1398
+1399
+1400
+1401
+1402
+1403
+1404
+1405
+1406
+1407
+1408
+1409
+1410
+1411
+1412
+1413
+1414
+1415
+1416
+1417
+1418
+1419
+1420
+1421
+1422
+1423
+1424
+1425
+1426
+1427
+1428
+1429
+1430
+1431
+1432
+1433
+1434
+1435
+1436
+1437
+1438
+1439
+1440
+1441
+1442
+1443
+1444
+1445
+1446
+1447
+1448
+1449
+1450
+1451
+1452
+1453
+1454
+1455
+1456
+1457
+1458
+1459
+1460
+1461
+1462
+1463
+1464
+1465
+1466
+1467
+1468
+1469
+1470
+1471
+1472
+1473
+1474
+1475
+1476
+1477
+1478
+1479
+1480
+1481
+1482
+1483
+1484
+1485
+1486
+1487
+1488
+1489
+1490
+1491
+1492
+1493
+1494
+1495
+1496
+1497
+1498
+1499
+1500
+1501
+1502
+1503
+1504
+1505
+1506
+1507
+1508
+1509
+1510
+1511
+1512
+1513
+1514
+1515
+1516
+1517
+1518
+1519
+1520
+1521
+1522
+1523
+1524
+1525
+1526
+1527
+1528
+1529
+1530
+1531
+1532
+1533
+1534
+1535
+1536
+1537
+1538
+1539
+1540
+1541
+1542
+1543
+1544
+1545
+1546
+1547
+1548
+1549
+1550
+1551
+1552
+1553
+1554
+1555
+1556
+1557
+1558
+1559
+1560
+1561
+1562
+1563
+1564
+1565
+1566
+1567
+1568
+1569
+1570
+1571
+1572
+1573
+1574
+1575
+1576
+1577
+1578
+1579
+1580
+1581
+1582
+1583
+1584
+1585
+1586
+1587
+1588
+1589
+1590
+1591
+1592
+1593
+1594
+1595
+1596
+1597
+1598
+1599
+1600
+1601
+1602
+1603
+1604
+1605
+1606
+1607
+1608
+1609
+1610
+1611
+1612
+1613
+1614
+1615
+1616
+1617
+1618
+1619
+1620
+1621
+1622
+1623
+1624
+1625
+1626
+1627
+1628
+1629
+1630
+1631
+1632
+1633
+1634
+1635
+1636
+1637
+1638
+1639
+1640
+1641
+1642
+1643
+1644
+1645
+1646
+1647
+1648
+1649
+1650
+1651
+1652
+1653
+1654
+1655
+1656
+1657
+1658
+1659
+1660
+1661
+1662
+1663
+1664
+1665
+1666
+1667
+1668
+1669
+1670
+1671
+1672
+1673
+1674
+1675
+1676
+1677
+1678
+1679
+1680
+1681
+1682
+1683
+1684
+1685
+1686
+1687
+1688
+1689
+1690
+1691
+1692
+1693
+1694
+1695
+1696
+1697
+1698
+1699
+1700
+1701
+1702
+1703
+1704
+1705
+1706
+1707
+1708
+1709
+1710
+1711
+1712
+1713
+1714
+1715
+1716
+1717
+1718
+1719
+1720
+1721
+1722
+1723
+1724
+1725
+1726
+1727
+1728
+1729
+1730
+1731
+1732
+1733
+1734
+1735
+1736
+1737
+1738
+1739
+1740
+1741
+1742
+1743
+1744
+1745
+1746
+1747
+1748
+1749
+1750
+1751
+1752
+1753
+1754
+1755
+1756
+1757
+1758
+1759
+1760
+1761
+1762
+1763
+1764
+1765
+1766
+1767
+1768
+1769
+1770
+1771
+1772
+1773
+1774
+1775
+1776
+1777
+1778
+1779
+1780
+1781
+1782
+1783
+1784
+1785
+1786
+1787
+1788
+1789
+1790
+1791
+1792
+1793
+1794
+1795
+1796
+1797
+1798
+1799
+1800
+1801
+1802
+1803
+1804
+1805
+1806
+1807
+1808
+1809
+1810
+1811
+1812
+1813
+1814
+1815
+1816
+1817
+1818
+1819
+1820
+1821
+1822
+1823
+1824
+1825
+1826
+1827
+1828
+1829
+1830
+1831
+1832
+1833
+1834
+1835
+1836
+1837
+1838
+1839
+1840
+1841
+1842
+1843
+1844
+1845
+1846
+1847
+1848
+1849
+1850
+1851
+1852
+1853
+1854
+1855
+1856
+1857
+1858
+1859
+1860
+1861
+1862
+1863
+1864
+1865
+1866
+1867
+1868
+1869
+1870
+1871
+1872
+1873
+1874
+1875
+1876
+1877
+1878
+1879
+1880
+1881
+1882
+1883
+1884
+1885
+1886
+1887
+1888
+1889
+1890
+1891
+1892
+1893
+1894
+1895
+1896
+1897
+1898
+1899
+1900
+1901
+1902
+1903
+1904
+1905
+1906
+1907
+1908
+1909
+1910
+1911
+1912
+1913
+1914
+1915
+1916
+1917
+1918
+1919
+1920
+1921
+1922
+1923
+1924
+1925
+1926
+1927
+1928
+1929
+1930
+1931
+1932
+1933
+1934
+1935
+1936
+1937
+1938
+1939
+1940
+1941
+1942
+1943
+1944
+1945
+1946
+1947
+1948
+1949
+1950
+1951
+1952
+1953
+1954
+1955
+1956
+1957
+1958
+1959
+1960
+1961
+1962
+1963
+1964
+1965
+1966
+1967
+1968
+1969
+1970
+1971
+1972
+1973
+1974
+1975
+1976
+1977
+1978
+1979
+1980
+1981
+1982
+1983
+1984
+1985
+1986
+1987
+1988
+1989
+1990
+1991
+1992
+1993
+1994
+1995
+1996
+1997
+1998
+1999
+2000
+2001
+2002
+2003
+2004
+2005
+2006
+2007
+2008
+2009
+2010
+2011
+2012
+2013
+2014
+2015
+2016
+2017
+2018
+2019
+2020
+2021
+2022
+2023
+2024
+2025
+2026
+2027
+2028
+2029
+2030
+2031
+2032
+2033
+2034
+2035
+2036
+2037
+2038
+2039
+2040
+2041
+2042
+2043
+2044
+2045
+2046
+2047
+2048
+2049
+2050
+2051
+2052
+2053
+2054
+2055
+2056
+2057
+2058
+2059
+2060
+2061
+2062
+2063
+2064
+2065
+2066
+2067
+2068
+2069
+2070
+2071
+2072
+2073
+2074
+2075
+2076
+2077
+2078
+2079
+2080
+2081
+2082
+2083
+2084
+2085
+2086
+2087
+2088
+2089
+2090
+2091
+2092
+2093
+2094
+2095
+2096
+2097
+2098
+2099
+2100
+2101
+2102
+2103
+2104
+2105
+2106
+2107
+2108
+2109
+2110
+2111
+2112
+2113
+2114
+2115
+2116
+2117
+2118
+2119
+2120
+2121
+2122
+2123
+2124
+2125
+2126
+2127
+2128
+2129
+2130
+2131
+2132
+2133
+2134
+2135
+2136
+2137
+2138
+2139
+2140
+2141
+2142
+2143
+2144
+2145
+2146
+2147
+2148
+2149
+2150
+2151
+2152
+2153
+2154
+2155
+2156
+2157
+2158
+2159
+2160
+2161
+2162
+2163
+2164
+2165
+2166
+2167
+2168
+2169
+2170
+2171
+2172
+2173
+2174
+2175
+2176
+2177
+2178
+2179
+2180
+2181
+2182
+2183
+2184
+2185
+2186
+2187
+2188
+2189
+2190
+2191
+2192
+2193
+2194
+2195
+2196
+2197
+2198
+2199
+2200
+2201
+2202
+2203
+2204
+2205
+2206
+2207
+2208
+2209
+2210
+2211
+2212
+2213
+2214
+2215
+2216
+2217
+2218
+2219
+2220
+2221
+2222
+2223
+2224
+2225
+2226
+2227
+2228
+2229
+2230
+2231
+2232
+2233
+2234
+2235
+2236
+2237
+2238
+2239
+2240
+2241
+2242
+2243
+2244
+2245
+2246
+2247
+2248
+2249
+2250
+2251
+2252
+2253
+2254
+2255
+2256
+2257
+2258
+2259
+2260
+2261
+2262
+2263
+2264
+2265
+2266
+2267
+2268
+2269
+2270
+2271
+2272
+2273
+2274
+2275
+2276
+2277
+2278
+2279
+2280
+2281
+2282
+2283
+2284
+2285
+2286
+2287
+2288
+2289
+2290
+2291
+2292
+2293
+2294
+2295
+2296
+2297
+2298
+2299
+2300
+2301
+2302
+2303
+2304
+2305
+2306
+2307
+2308
+2309
+2310
+2311
+2312
+2313
+2314
+2315
+2316
+2317
+2318
+2319
+2320
+2321
+2322
+2323
+2324
+2325
+2326
+2327
+2328
+2329
+2330
+2331
+2332
+2333
+2334
+2335
+2336
+2337
+2338
+2339
+2340
+2341
+2342
+2343
+2344
+2345
+2346
+2347
+2348
+2349
+2350
+2351
+2352
+2353
+2354
+2355
+2356
+2357
+2358
+2359
+2360
+2361
+2362
+2363
+2364
+2365
+2366
+2367
+2368
+2369
+2370
+2371
+2372
+2373
+2374
+2375
+2376
+2377
+2378
+2379
+2380
+2381
+2382
+2383
+2384
+2385
+2386
+2387
+2388
+2389
+2390
+2391
+2392
+2393
+2394
+2395
/* Menu layout in-memory data structure (a custom "DOM tree") */
+
+/*
+ * Copyright (C) 2002 - 2004 Red Hat, Inc.
+ * Copyright (C) 2012-2021 MATE Developers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <config.h>
+
+#include "menu-layout.h"
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "entry-directories.h"
+#include "menu-util.h"
+
+typedef struct MenuLayoutNodeMenu          MenuLayoutNodeMenu;
+typedef struct MenuLayoutNodeRoot          MenuLayoutNodeRoot;
+typedef struct MenuLayoutNodeLegacyDir     MenuLayoutNodeLegacyDir;
+typedef struct MenuLayoutNodeMergeFile     MenuLayoutNodeMergeFile;
+typedef struct MenuLayoutNodeDefaultLayout MenuLayoutNodeDefaultLayout;
+typedef struct MenuLayoutNodeMenuname      MenuLayoutNodeMenuname;
+typedef struct MenuLayoutNodeMerge         MenuLayoutNodeMerge;
+
+struct MenuLayoutNode
+{
+  /* Node lists are circular, for length-one lists
+   * prev/next point back to the node itself.
+   */
+  MenuLayoutNode *prev;
+  MenuLayoutNode *next;
+  MenuLayoutNode *parent;
+  MenuLayoutNode *children;
+
+  char *content;
+
+  guint refcount : 20;
+  guint type : 7;
+};
+
+struct MenuLayoutNodeRoot
+{
+  MenuLayoutNode node;
+
+  char *basedir;
+  char *name;
+
+  GMainContext *main_context;
+
+  GSList *monitors;
+  GSource *monitors_idle_handler;
+};
+
+struct MenuLayoutNodeMenu
+{
+  MenuLayoutNode node;
+
+  MenuLayoutNode *name_node; /* cache of the <Name> node */
+
+  EntryDirectoryList *app_dirs;
+  EntryDirectoryList *dir_dirs;
+};
+
+struct MenuLayoutNodeLegacyDir
+{
+  MenuLayoutNode node;
+
+  char *prefix;
+};
+
+struct MenuLayoutNodeMergeFile
+{
+  MenuLayoutNode node;
+
+  MenuMergeFileType type;
+};
+
+struct MenuLayoutNodeDefaultLayout
+{
+  MenuLayoutNode node;
+
+  MenuLayoutValues layout_values;
+};
+
+struct MenuLayoutNodeMenuname
+{
+  MenuLayoutNode node;
+
+  MenuLayoutValues layout_values;
+};
+
+struct MenuLayoutNodeMerge
+{
+  MenuLayoutNode node;
+
+  MenuLayoutMergeType merge_type;
+};
+
+typedef struct
+{
+  MenuLayoutNodeEntriesChangedFunc callback;
+  gpointer                         user_data;
+} MenuLayoutNodeEntriesMonitor;
+
+static inline MenuLayoutNode *
+node_next (MenuLayoutNode *node)
+{
+  /* root nodes (no parent) never have siblings */
+  if (node->parent == NULL)
+    return NULL;
+
+  /* circular list */
+  if (node->next == node->parent->children)
+    return NULL;
+
+  return node->next;
+}
+
+static gboolean
+menu_layout_invoke_monitors (MenuLayoutNodeRoot *nr)
+{
+  GSList *tmp;
+
+  g_assert (nr->node.type == MENU_LAYOUT_NODE_ROOT);
+
+  nr->monitors_idle_handler = NULL;
+
+  tmp = nr->monitors;
+  while (tmp != NULL)
+    {
+      MenuLayoutNodeEntriesMonitor *monitor = tmp->data;
+      GSList                       *next    = tmp->next;
+
+      monitor->callback ((MenuLayoutNode *) nr, monitor->user_data);
+
+      tmp = next;
+    }
+
+  return FALSE;
+}
+
+static void
+handle_entry_directory_changed (EntryDirectory *dir,
+                                MenuLayoutNode *node)
+{
+  MenuLayoutNodeRoot *nr;
+
+  g_assert (node->type == MENU_LAYOUT_NODE_MENU);
+
+  nr = (MenuLayoutNodeRoot *) menu_layout_node_get_root (node);
+
+  if (nr->monitors_idle_handler == NULL)
+    {
+      nr->monitors_idle_handler = g_idle_source_new ();
+      g_source_set_callback (nr->monitors_idle_handler,
+                             (GSourceFunc) menu_layout_invoke_monitors, nr, NULL);
+      g_source_attach (nr->monitors_idle_handler, nr->main_context);
+      g_source_unref (nr->monitors_idle_handler);
+    }
+}
+
+static void
+remove_entry_directory_list (MenuLayoutNodeMenu  *nm,
+                             EntryDirectoryList **dirs)
+{
+  if (*dirs)
+    {
+      entry_directory_list_remove_monitors (*dirs,
+                                            (EntryDirectoryChangedFunc) handle_entry_directory_changed,
+                                            nm);
+      entry_directory_list_unref (*dirs);
+      *dirs = NULL;
+    }
+}
+
+MenuLayoutNode *
+menu_layout_node_ref (MenuLayoutNode *node)
+{
+  g_return_val_if_fail (node != NULL, NULL);
+
+  node->refcount += 1;
+
+  return node;
+}
+
+void
+menu_layout_node_unref (MenuLayoutNode *node)
+{
+  g_return_if_fail (node != NULL);
+  g_return_if_fail (node->refcount > 0);
+
+  node->refcount -= 1;
+  if (node->refcount == 0)
+    {
+      MenuLayoutNode *iter;
+
+      iter = node->children;
+      while (iter != NULL)
+        {
+          MenuLayoutNode *next = node_next (iter);
+
+          menu_layout_node_unref (iter);
+
+          iter = next;
+        }
+
+      if (node->type == MENU_LAYOUT_NODE_MENU)
+        {
+          MenuLayoutNodeMenu *nm = (MenuLayoutNodeMenu *) node;
+
+          if (nm->name_node)
+            menu_layout_node_unref (nm->name_node);
+
+          remove_entry_directory_list (nm, &nm->app_dirs);
+          remove_entry_directory_list (nm, &nm->dir_dirs);
+        }
+      else if (node->type == MENU_LAYOUT_NODE_LEGACY_DIR)
+        {
+          MenuLayoutNodeLegacyDir *legacy = (MenuLayoutNodeLegacyDir *) node;
+
+          g_free (legacy->prefix);
+        }
+      else if (node->type == MENU_LAYOUT_NODE_ROOT)
+        {
+          MenuLayoutNodeRoot *nr = (MenuLayoutNodeRoot*) node;
+
+          g_slist_foreach (nr->monitors, (GFunc) g_free, NULL);
+          g_slist_free (nr->monitors);
+
+          if (nr->monitors_idle_handler != NULL)
+            g_source_destroy (nr->monitors_idle_handler);
+          nr->monitors_idle_handler = NULL;
+
+          if (nr->main_context != NULL)
+            g_main_context_unref (nr->main_context);
+          nr->main_context = NULL;
+
+          g_free (nr->basedir);
+          g_free (nr->name);
+        }
+
+      g_free (node->content);
+      g_free (node);
+    }
+}
+
+MenuLayoutNode *
+menu_layout_node_new (MenuLayoutNodeType type)
+{
+  MenuLayoutNode *node;
+
+  switch (type)
+    {
+    case MENU_LAYOUT_NODE_MENU:
+      node = (MenuLayoutNode *) g_new0 (MenuLayoutNodeMenu, 1);
+      break;
+
+    case MENU_LAYOUT_NODE_LEGACY_DIR:
+      node = (MenuLayoutNode *) g_new0 (MenuLayoutNodeLegacyDir, 1);
+      break;
+
+    case MENU_LAYOUT_NODE_ROOT:
+      node = (MenuLayoutNode*) g_new0 (MenuLayoutNodeRoot, 1);
+      break;
+
+    case MENU_LAYOUT_NODE_MERGE_FILE:
+      node = (MenuLayoutNode *) g_new0 (MenuLayoutNodeMergeFile, 1);
+      break;
+
+    case MENU_LAYOUT_NODE_DEFAULT_LAYOUT:
+      node = (MenuLayoutNode *) g_new0 (MenuLayoutNodeDefaultLayout, 1);
+      break;
+
+    case MENU_LAYOUT_NODE_MENUNAME:
+      node = (MenuLayoutNode *) g_new0 (MenuLayoutNodeMenuname, 1);
+      break;
+
+    case MENU_LAYOUT_NODE_MERGE:
+      node = (MenuLayoutNode *) g_new0 (MenuLayoutNodeMerge, 1);
+      break;
+
+    default:
+      node = g_new0 (MenuLayoutNode, 1);
+      break;
+    }
+
+  node->type = type;
+
+  node->refcount = 1;
+
+  /* we're in a list of one node */
+  node->next = node;
+  node->prev = node;
+
+  return node;
+}
+
+MenuLayoutNode *
+menu_layout_node_get_next (MenuLayoutNode *node)
+{
+  return node_next (node);
+}
+
+MenuLayoutNode *
+menu_layout_node_get_parent (MenuLayoutNode *node)
+{
+  return node->parent;
+}
+
+MenuLayoutNode *
+menu_layout_node_get_children (MenuLayoutNode *node)
+{
+  return node->children;
+}
+
+MenuLayoutNode *
+menu_layout_node_get_root (MenuLayoutNode *node)
+{
+  MenuLayoutNode *parent;
+
+  parent = node;
+  while (parent->parent != NULL)
+    parent = parent->parent;
+
+  g_assert (parent->type == MENU_LAYOUT_NODE_ROOT);
+
+  return parent;
+}
+
+char *
+menu_layout_node_get_content_as_path (MenuLayoutNode *node)
+{
+  if (node->content == NULL)
+    {
+      menu_verbose ("  (node has no content to get as a path)\n");
+      return NULL;
+    }
+
+  if (g_path_is_absolute (node->content))
+    {
+      return g_strdup (node->content);
+    }
+  else
+    {
+      MenuLayoutNodeRoot *root;
+
+      root = (MenuLayoutNodeRoot *) menu_layout_node_get_root (node);
+
+      if (root->basedir == NULL)
+        {
+          menu_verbose ("No basedir available, using \"%s\" as-is\n",
+                        node->content);
+          return g_strdup (node->content);
+        }
+      else
+        {
+          menu_verbose ("Using basedir \"%s\" filename \"%s\"\n",
+                        root->basedir, node->content);
+          return g_build_filename (root->basedir, node->content, NULL);
+        }
+    }
+}
+
+#define RETURN_IF_NO_PARENT(node) G_STMT_START {                  \
+    if ((node)->parent == NULL)                                   \
+      {                                                           \
+        g_warning ("To add siblings to a menu node, "             \
+                   "it must not be the root node, "               \
+                   "and must be linked in below some root node\n" \
+                   "node parent = %p and type = %d",              \
+                   (node)->parent, (node)->type);                 \
+        return;                                                   \
+      }                                                           \
+  } G_STMT_END
+
+#define RETURN_IF_HAS_ENTRY_DIRS(node) G_STMT_START {             \
+    if ((node)->type == MENU_LAYOUT_NODE_MENU &&                  \
+        (((MenuLayoutNodeMenu*)(node))->app_dirs != NULL ||       \
+         ((MenuLayoutNodeMenu*)(node))->dir_dirs != NULL))        \
+      {                                                           \
+        g_warning ("node acquired ->app_dirs or ->dir_dirs "      \
+                   "while not rooted in a tree\n");               \
+        return;                                                   \
+      }                                                           \
+  } G_STMT_END                                                    \
+
+void
+menu_layout_node_insert_before (MenuLayoutNode *node,
+                                MenuLayoutNode *new_sibling)
+{
+  g_return_if_fail (new_sibling != NULL);
+  g_return_if_fail (new_sibling->parent == NULL);
+
+  RETURN_IF_NO_PARENT (node);
+  RETURN_IF_HAS_ENTRY_DIRS (new_sibling);
+
+  new_sibling->next = node;
+  new_sibling->prev = node->prev;
+
+  node->prev = new_sibling;
+  new_sibling->prev->next = new_sibling;
+
+  new_sibling->parent = node->parent;
+
+  if (node == node->parent->children)
+    node->parent->children = new_sibling;
+
+  menu_layout_node_ref (new_sibling);
+}
+
+void
+menu_layout_node_insert_after (MenuLayoutNode *node,
+                               MenuLayoutNode *new_sibling)
+{
+  g_return_if_fail (new_sibling != NULL);
+  g_return_if_fail (new_sibling->parent == NULL);
+
+  RETURN_IF_NO_PARENT (node);
+  RETURN_IF_HAS_ENTRY_DIRS (new_sibling);
+
+  new_sibling->prev = node;
+  new_sibling->next = node->next;
+
+  node->next = new_sibling;
+  new_sibling->next->prev = new_sibling;
+
+  new_sibling->parent = node->parent;
+
+  menu_layout_node_ref (new_sibling);
+}
+
+void
+menu_layout_node_prepend_child (MenuLayoutNode *parent,
+                                MenuLayoutNode *new_child)
+{
+  RETURN_IF_HAS_ENTRY_DIRS (new_child);
+
+  if (parent->children)
+    {
+      menu_layout_node_insert_before (parent->children, new_child);
+    }
+  else
+    {
+      parent->children = menu_layout_node_ref (new_child);
+      new_child->parent = parent;
+    }
+}
+
+void
+menu_layout_node_append_child (MenuLayoutNode *parent,
+                               MenuLayoutNode *new_child)
+{
+  RETURN_IF_HAS_ENTRY_DIRS (new_child);
+
+  if (parent->children)
+    {
+      menu_layout_node_insert_after (parent->children->prev, new_child);
+    }
+  else
+    {
+      parent->children = menu_layout_node_ref (new_child);
+      new_child->parent = parent;
+    }
+}
+
+void
+menu_layout_node_unlink (MenuLayoutNode *node)
+{
+  g_return_if_fail (node != NULL);
+  g_return_if_fail (node->parent != NULL);
+
+  menu_layout_node_steal (node);
+  menu_layout_node_unref (node);
+}
+
+static void
+recursive_clean_entry_directory_lists (MenuLayoutNode *node,
+                                       gboolean        apps)
+{
+  EntryDirectoryList **dirs;
+  MenuLayoutNodeMenu  *nm;
+  MenuLayoutNode      *iter;
+
+  if (node->type != MENU_LAYOUT_NODE_MENU)
+    return;
+
+  nm = (MenuLayoutNodeMenu *) node;
+
+  dirs = apps ? &nm->app_dirs : &nm->dir_dirs;
+
+  if (*dirs == NULL || entry_directory_list_get_length (*dirs) == 0)
+    return; /* child menus continue to have valid lists */
+
+  remove_entry_directory_list (nm, dirs);
+
+  iter = node->children;
+  while (iter != NULL)
+    {
+      if (iter->type == MENU_LAYOUT_NODE_MENU)
+        recursive_clean_entry_directory_lists (iter, apps);
+
+      iter = node_next (iter);
+    }
+}
+
+void
+menu_layout_node_steal (MenuLayoutNode *node)
+{
+  g_return_if_fail (node != NULL);
+  g_return_if_fail (node->parent != NULL);
+
+  switch (node->type)
+    {
+    case MENU_LAYOUT_NODE_NAME:
+      {
+        MenuLayoutNodeMenu *nm = (MenuLayoutNodeMenu *) node->parent;
+
+        if (nm->name_node == node)
+          {
+            menu_layout_node_unref (nm->name_node);
+            nm->name_node = NULL;
+          }
+      }
+      break;
+
+    case MENU_LAYOUT_NODE_APP_DIR:
+      recursive_clean_entry_directory_lists (node->parent, TRUE);
+      break;
+
+    case MENU_LAYOUT_NODE_DIRECTORY_DIR:
+      recursive_clean_entry_directory_lists (node->parent, FALSE);
+      break;
+
+    default:
+      break;
+    }
+
+  if (node->parent && node->parent->children == node)
+    {
+      if (node->next != node)
+        node->parent->children = node->next;
+      else
+        node->parent->children = NULL;
+    }
+
+  /* these are no-ops for length-one node lists */
+  node->prev->next = node->next;
+  node->next->prev = node->prev;
+
+  node->parent = NULL;
+
+  /* point to ourselves, now we're length one */
+  node->next = node;
+  node->prev = node;
+}
+
+MenuLayoutNodeType
+menu_layout_node_get_type (MenuLayoutNode *node)
+{
+  return node->type;
+}
+
+const char *
+menu_layout_node_get_content (MenuLayoutNode *node)
+{
+  return node->content;
+}
+
+void
+menu_layout_node_set_content (MenuLayoutNode *node,
+                              const char     *content)
+{
+  if (node->content == content)
+    return;
+
+  g_free (node->content);
+  node->content = g_strdup (content);
+}
+
+const char *
+menu_layout_node_root_get_name (MenuLayoutNode *node)
+{
+  MenuLayoutNodeRoot *nr;
+
+  g_return_val_if_fail (node->type == MENU_LAYOUT_NODE_ROOT, NULL);
+
+  nr = (MenuLayoutNodeRoot*) node;
+
+  return nr->name;
+}
+
+const char *
+menu_layout_node_root_get_basedir (MenuLayoutNode *node)
+{
+  MenuLayoutNodeRoot *nr;
+
+  g_return_val_if_fail (node->type == MENU_LAYOUT_NODE_ROOT, NULL);
+
+  nr = (MenuLayoutNodeRoot*) node;
+
+  return nr->basedir;
+}
+
+const char *
+menu_layout_node_menu_get_name (MenuLayoutNode *node)
+{
+  MenuLayoutNodeMenu *nm;
+
+  g_return_val_if_fail (node->type == MENU_LAYOUT_NODE_MENU, NULL);
+
+  nm = (MenuLayoutNodeMenu*) node;
+
+  if (nm->name_node == NULL)
+    {
+      MenuLayoutNode *iter;
+
+      iter = node->children;
+      while (iter != NULL)
+        {
+          if (iter->type == MENU_LAYOUT_NODE_NAME)
+            {
+              nm->name_node = menu_layout_node_ref (iter);
+              break;
+            }
+
+          iter = node_next (iter);
+        }
+    }
+
+  if (nm->name_node == NULL)
+    return NULL;
+
+  return menu_layout_node_get_content (nm->name_node);
+}
+
+static void
+ensure_dir_lists (MenuLayoutNodeMenu *nm)
+{
+  MenuLayoutNode     *node;
+  MenuLayoutNode     *iter;
+  EntryDirectoryList *app_dirs;
+  EntryDirectoryList *dir_dirs;
+
+  node = (MenuLayoutNode *) nm;
+
+  if (nm->app_dirs && nm->dir_dirs)
+    return;
+
+  app_dirs = NULL;
+  dir_dirs = NULL;
+
+  if (nm->app_dirs == NULL)
+    {
+      app_dirs = entry_directory_list_new ();
+
+      if (node->parent && node->parent->type == MENU_LAYOUT_NODE_MENU)
+        {
+          EntryDirectoryList *dirs;
+
+          if ((dirs = menu_layout_node_menu_get_app_dirs (node->parent)))
+            entry_directory_list_append_list (app_dirs, dirs);
+        }
+    }
+
+  if (nm->dir_dirs == NULL)
+    {
+      dir_dirs = entry_directory_list_new ();
+
+      if (node->parent && node->parent->type == MENU_LAYOUT_NODE_MENU)
+        {
+          EntryDirectoryList *dirs;
+
+          if ((dirs = menu_layout_node_menu_get_directory_dirs (node->parent)))
+            entry_directory_list_append_list (dir_dirs, dirs);
+        }
+    }
+
+  iter = node->children;
+  while (iter != NULL)
+    {
+      EntryDirectory *ed;
+
+      if (app_dirs != NULL && iter->type == MENU_LAYOUT_NODE_APP_DIR)
+        {
+          char *path;
+
+          path = menu_layout_node_get_content_as_path (iter);
+
+          ed = entry_directory_new (DESKTOP_ENTRY_DESKTOP, path);
+          if (ed != NULL)
+            {
+              entry_directory_list_prepend (app_dirs, ed);
+              entry_directory_unref (ed);
+            }
+
+          g_free (path);
+        }
+
+      if (dir_dirs != NULL && iter->type == MENU_LAYOUT_NODE_DIRECTORY_DIR)
+        {
+          char *path;
+
+          path = menu_layout_node_get_content_as_path (iter);
+
+          ed = entry_directory_new (DESKTOP_ENTRY_DIRECTORY, path);
+          if (ed != NULL)
+            {
+              entry_directory_list_prepend (dir_dirs, ed);
+              entry_directory_unref (ed);
+            }
+
+          g_free (path);
+        }
+
+      if (iter->type == MENU_LAYOUT_NODE_LEGACY_DIR)
+        {
+          MenuLayoutNodeLegacyDir *legacy = (MenuLayoutNodeLegacyDir *) iter;
+          char                    *path;
+
+          path = menu_layout_node_get_content_as_path (iter);
+
+          if (app_dirs != NULL) /* we're loading app dirs */
+            {
+              ed = entry_directory_new_legacy (DESKTOP_ENTRY_DESKTOP,
+                                               path,
+                                               legacy->prefix);
+              if (ed != NULL)
+                {
+                  entry_directory_list_prepend (app_dirs, ed);
+                  entry_directory_unref (ed);
+                }
+            }
+
+          if (dir_dirs != NULL) /* we're loading dir dirs */
+            {
+              ed = entry_directory_new_legacy (DESKTOP_ENTRY_DIRECTORY,
+                                               path,
+                                               legacy->prefix);
+              if (ed != NULL)
+                {
+                  entry_directory_list_prepend (dir_dirs, ed);
+                  entry_directory_unref (ed);
+                }
+            }
+
+          g_free (path);
+        }
+
+      iter = node_next (iter);
+    }
+
+  if (app_dirs)
+    {
+      g_assert (nm->app_dirs == NULL);
+
+      nm->app_dirs = app_dirs;
+      entry_directory_list_add_monitors (nm->app_dirs,
+                                         (EntryDirectoryChangedFunc) handle_entry_directory_changed,
+                                         nm);
+    }
+
+  if (dir_dirs)
+    {
+      g_assert (nm->dir_dirs == NULL);
+
+      nm->dir_dirs = dir_dirs;
+      entry_directory_list_add_monitors (nm->dir_dirs,
+                                         (EntryDirectoryChangedFunc) handle_entry_directory_changed,
+                                         nm);
+    }
+}
+
+EntryDirectoryList *
+menu_layout_node_menu_get_app_dirs (MenuLayoutNode *node)
+{
+  MenuLayoutNodeMenu *nm;
+
+  g_return_val_if_fail (node->type == MENU_LAYOUT_NODE_MENU, NULL);
+
+  nm = (MenuLayoutNodeMenu *) node;
+
+  ensure_dir_lists (nm);
+
+  return nm->app_dirs;
+}
+
+EntryDirectoryList *
+menu_layout_node_menu_get_directory_dirs (MenuLayoutNode *node)
+{
+  MenuLayoutNodeMenu *nm;
+
+  g_return_val_if_fail (node->type == MENU_LAYOUT_NODE_MENU, NULL);
+
+  nm = (MenuLayoutNodeMenu *) node;
+
+  ensure_dir_lists (nm);
+
+  return nm->dir_dirs;
+}
+
+const char *
+menu_layout_node_move_get_old (MenuLayoutNode *node)
+{
+  MenuLayoutNode *iter;
+
+  iter = node->children;
+  while (iter != NULL)
+    {
+      if (iter->type == MENU_LAYOUT_NODE_OLD)
+        return iter->content;
+
+      iter = node_next (iter);
+    }
+
+  return NULL;
+}
+
+const char *
+menu_layout_node_move_get_new (MenuLayoutNode *node)
+{
+  MenuLayoutNode *iter;
+
+  iter = node->children;
+  while (iter != NULL)
+    {
+      if (iter->type == MENU_LAYOUT_NODE_NEW)
+        return iter->content;
+
+      iter = node_next (iter);
+    }
+
+  return NULL;
+}
+
+const char *
+menu_layout_node_legacy_dir_get_prefix (MenuLayoutNode *node)
+{
+  MenuLayoutNodeLegacyDir *legacy;
+
+  g_return_val_if_fail (node->type == MENU_LAYOUT_NODE_LEGACY_DIR, NULL);
+
+  legacy = (MenuLayoutNodeLegacyDir *) node;
+
+  return legacy->prefix;
+}
+
+void
+menu_layout_node_legacy_dir_set_prefix (MenuLayoutNode *node,
+                                        const char     *prefix)
+{
+  MenuLayoutNodeLegacyDir *legacy;
+
+  g_return_if_fail (node->type == MENU_LAYOUT_NODE_LEGACY_DIR);
+
+  legacy = (MenuLayoutNodeLegacyDir *) node;
+
+  g_free (legacy->prefix);
+  legacy->prefix = g_strdup (prefix);
+}
+
+MenuMergeFileType
+menu_layout_node_merge_file_get_type (MenuLayoutNode *node)
+{
+  MenuLayoutNodeMergeFile *merge_file;
+
+  g_return_val_if_fail (node->type == MENU_LAYOUT_NODE_MERGE_FILE, FALSE);
+
+  merge_file = (MenuLayoutNodeMergeFile *) node;
+
+  return merge_file->type;
+}
+
+void
+menu_layout_node_merge_file_set_type (MenuLayoutNode    *node,
+				      MenuMergeFileType  type)
+{
+  MenuLayoutNodeMergeFile *merge_file;
+
+  g_return_if_fail (node->type == MENU_LAYOUT_NODE_MERGE_FILE);
+
+  merge_file = (MenuLayoutNodeMergeFile *) node;
+
+  merge_file->type = type;
+}
+
+MenuLayoutMergeType
+menu_layout_node_merge_get_type (MenuLayoutNode *node)
+{
+  MenuLayoutNodeMerge *merge;
+
+  g_return_val_if_fail (node->type == MENU_LAYOUT_NODE_MERGE, 0);
+
+  merge = (MenuLayoutNodeMerge *) node;
+
+  return merge->merge_type;
+}
+
+static void
+menu_layout_node_merge_set_type (MenuLayoutNode *node,
+                                 const char     *merge_type)
+{
+  MenuLayoutNodeMerge *merge;
+
+  g_return_if_fail (node->type == MENU_LAYOUT_NODE_MERGE);
+
+  merge = (MenuLayoutNodeMerge *) node;
+
+  merge->merge_type = MENU_LAYOUT_MERGE_NONE;
+
+  if (strcmp (merge_type, "menus") == 0)
+    {
+      merge->merge_type = MENU_LAYOUT_MERGE_MENUS;
+    }
+  else if (strcmp (merge_type, "files") == 0)
+    {
+      merge->merge_type = MENU_LAYOUT_MERGE_FILES;
+    }
+  else if (strcmp (merge_type, "all") == 0)
+    {
+      merge->merge_type = MENU_LAYOUT_MERGE_ALL;
+    }
+}
+
+void
+menu_layout_node_default_layout_get_values (MenuLayoutNode   *node,
+					    MenuLayoutValues *values)
+{
+  MenuLayoutNodeDefaultLayout *default_layout;
+
+  g_return_if_fail (node->type == MENU_LAYOUT_NODE_DEFAULT_LAYOUT);
+  g_return_if_fail (values != NULL);
+
+  default_layout = (MenuLayoutNodeDefaultLayout *) node;
+
+  *values = default_layout->layout_values;
+}
+
+void
+menu_layout_node_menuname_get_values (MenuLayoutNode   *node,
+				      MenuLayoutValues *values)
+{
+  MenuLayoutNodeMenuname *menuname;
+
+  g_return_if_fail (node->type == MENU_LAYOUT_NODE_MENUNAME);
+  g_return_if_fail (values != NULL);
+
+  menuname = (MenuLayoutNodeMenuname *) node;
+
+  *values = menuname->layout_values;
+}
+
+static void
+menu_layout_values_set (MenuLayoutValues *values,
+			const char       *show_empty,
+			const char       *inline_menus,
+			const char       *inline_limit,
+			const char       *inline_header,
+			const char       *inline_alias)
+{
+  values->mask          = MENU_LAYOUT_VALUES_NONE;
+  values->show_empty    = FALSE;
+  values->inline_menus  = FALSE;
+  values->inline_limit  = 4;
+  values->inline_header = FALSE;
+  values->inline_alias  = FALSE;
+
+  if (show_empty != NULL)
+    {
+      values->show_empty = strcmp (show_empty, "true") == 0;
+      values->mask |= MENU_LAYOUT_VALUES_SHOW_EMPTY;
+    }
+
+  if (inline_menus != NULL)
+    {
+      values->inline_menus = strcmp (inline_menus, "true") == 0;
+      values->mask |= MENU_LAYOUT_VALUES_INLINE_MENUS;
+    }
+
+  if (inline_limit != NULL)
+    {
+      char *end;
+      unsigned long limit;
+
+      limit = strtoul (inline_limit, &end, 10);
+      if (*end == '\0')
+	{
+	  values->inline_limit = (guint) limit;
+	  values->mask |= MENU_LAYOUT_VALUES_INLINE_LIMIT;
+	}
+    }
+
+  if (inline_header != NULL)
+    {
+      values->inline_header = strcmp (inline_header, "true") == 0;
+      values->mask |= MENU_LAYOUT_VALUES_INLINE_HEADER;
+    }
+
+  if (inline_alias != NULL)
+    {
+      values->inline_alias = strcmp (inline_alias, "true") == 0;
+      values->mask |= MENU_LAYOUT_VALUES_INLINE_ALIAS;
+    }
+}
+
+static void
+menu_layout_node_default_layout_set_values (MenuLayoutNode *node,
+					    const char     *show_empty,
+					    const char     *inline_menus,
+					    const char     *inline_limit,
+					    const char     *inline_header,
+					    const char     *inline_alias)
+{
+  MenuLayoutNodeDefaultLayout *default_layout;
+
+  g_return_if_fail (node->type == MENU_LAYOUT_NODE_DEFAULT_LAYOUT);
+
+  default_layout = (MenuLayoutNodeDefaultLayout *) node;
+
+  menu_layout_values_set (&default_layout->layout_values,
+			  show_empty,
+			  inline_menus,
+			  inline_limit,
+			  inline_header,
+			  inline_alias);
+}
+
+static void
+menu_layout_node_menuname_set_values (MenuLayoutNode *node,
+				      const char     *show_empty,
+				      const char     *inline_menus,
+				      const char     *inline_limit,
+				      const char     *inline_header,
+				      const char     *inline_alias)
+{
+  MenuLayoutNodeMenuname *menuname;
+
+  g_return_if_fail (node->type == MENU_LAYOUT_NODE_MENUNAME);
+
+  menuname = (MenuLayoutNodeMenuname *) node;
+
+  menu_layout_values_set (&menuname->layout_values,
+			  show_empty,
+			  inline_menus,
+			  inline_limit,
+			  inline_header,
+			  inline_alias);
+}
+
+void
+menu_layout_node_root_add_entries_monitor (MenuLayoutNode                   *node,
+                                           MenuLayoutNodeEntriesChangedFunc  callback,
+                                           gpointer                          user_data)
+{
+  MenuLayoutNodeEntriesMonitor *monitor;
+  MenuLayoutNodeRoot           *nr;
+  GSList                       *tmp;
+
+  g_return_if_fail (node->type == MENU_LAYOUT_NODE_ROOT);
+
+  nr = (MenuLayoutNodeRoot *) node;
+
+  tmp = nr->monitors;
+  while (tmp != NULL)
+    {
+      monitor = tmp->data;
+
+      if (monitor->callback  == callback &&
+          monitor->user_data == user_data)
+        break;
+
+      tmp = tmp->next;
+    }
+
+  if (tmp == NULL)
+    {
+      monitor            = g_new0 (MenuLayoutNodeEntriesMonitor, 1);
+      monitor->callback  = callback;
+      monitor->user_data = user_data;
+
+      nr->monitors = g_slist_append (nr->monitors, monitor);
+    }
+}
+
+void
+menu_layout_node_root_remove_entries_monitor (MenuLayoutNode                   *node,
+                                              MenuLayoutNodeEntriesChangedFunc  callback,<--- Parameter 'callback' can be declared as pointer to const
+                                              gpointer                          user_data)
+{
+  MenuLayoutNodeRoot *nr;
+  GSList             *tmp;
+
+  g_return_if_fail (node->type == MENU_LAYOUT_NODE_ROOT);
+
+  nr = (MenuLayoutNodeRoot *) node;
+
+  tmp = nr->monitors;
+  while (tmp != NULL)
+    {
+      MenuLayoutNodeEntriesMonitor *monitor = tmp->data;
+      GSList                       *next = tmp->next;
+
+      if (monitor->callback == callback &&
+          monitor->user_data == user_data)
+        {
+          nr->monitors = g_slist_delete_link (nr->monitors, tmp);
+          g_free (monitor);
+        }
+
+      tmp = next;
+    }
+}
+
+/*
+ * Menu file parsing
+ */
+
+typedef struct
+{
+  MenuLayoutNode *root;
+  MenuLayoutNode *stack_top;
+} MenuParser;
+
+static void set_error (GError             **err,
+                       GMarkupParseContext *context,
+                       GQuark               error_domain,
+                       int                  error_code,
+                       const char          *format,
+                       ...) G_GNUC_PRINTF (5, 6);
+
+static void add_context_to_error (GError             **err,
+                                  GMarkupParseContext *context);
+
+static void start_element_handler (GMarkupParseContext  *context,
+                                   const char           *element_name,
+                                   const char          **attribute_names,
+                                   const char          **attribute_values,
+                                   gpointer              user_data,
+                                   GError              **error);
+static void end_element_handler   (GMarkupParseContext  *context,
+                                   const char           *element_name,
+                                   gpointer              user_data,
+                                   GError              **error);
+static void text_handler          (GMarkupParseContext  *context,
+                                   const char           *text,
+                                   gsize                 text_len,
+                                   gpointer              user_data,
+                                   GError              **error);
+static void passthrough_handler   (GMarkupParseContext  *context,
+                                   const char           *passthrough_text,
+                                   gsize                 text_len,
+                                   gpointer              user_data,
+                                   GError              **error);
+
+static GMarkupParser menu_funcs = {
+  start_element_handler,
+  end_element_handler,
+  text_handler,
+  passthrough_handler,
+  NULL
+};
+
+static void
+set_error (GError              **err,
+           GMarkupParseContext  *context,
+           GQuark                error_domain,
+           int                   error_code,
+           const char           *format,
+           ...)
+{
+  int      line, ch;
+  va_list  args;
+  char    *str;
+
+  g_markup_parse_context_get_position (context, &line, &ch);
+
+  va_start (args, format);
+  str = g_strdup_vprintf (format, args);
+  va_end (args);
+
+  g_set_error (err, error_domain, error_code,
+               "Line %d character %d: %s",
+               line, ch, str);
+
+  g_free (str);
+}
+
+static void
+add_context_to_error (GError             **err,
+                      GMarkupParseContext *context)
+{
+  int   line, ch;
+  char *str;
+
+  if (err == NULL || *err == NULL)
+    return;
+
+  g_markup_parse_context_get_position (context, &line, &ch);
+
+  str = g_strdup_printf ("Line %d character %d: %s",
+                         line, ch, (*err)->message);
+  g_free ((*err)->message);
+  (*err)->message = str;
+}
+
+#define ELEMENT_IS(name) (strcmp (element_name, (name)) == 0)
+
+typedef struct
+{
+  const char  *name;
+  const char **retloc;
+} LocateAttr;
+
+static gboolean
+locate_attributes (GMarkupParseContext  *context,
+                   const char           *element_name,
+                   const char          **attribute_names,
+                   const char          **attribute_values,
+                   GError              **error,
+                   const char           *first_attribute_name,
+                   const char          **first_attribute_retloc,
+                   ...)
+{
+#define MAX_ATTRS 24
+  LocateAttr   attrs[MAX_ATTRS];
+  int          n_attrs;
+  va_list      args;
+  const char  *name;
+  const char **retloc;
+  gboolean     retval;
+  int          i;
+
+  g_return_val_if_fail (first_attribute_name != NULL, FALSE);
+  g_return_val_if_fail (first_attribute_retloc != NULL, FALSE);
+
+  retval = TRUE;
+
+  n_attrs = 1;
+  attrs[0].name = first_attribute_name;
+  attrs[0].retloc = first_attribute_retloc;
+  *first_attribute_retloc = NULL;
+
+  va_start (args, first_attribute_retloc);
+
+  name = va_arg (args, const char *);
+  retloc = va_arg (args, const char **);
+
+  while (name != NULL)
+    {
+      g_return_val_if_fail (retloc != NULL, FALSE);
+
+      g_assert (n_attrs < MAX_ATTRS);
+
+      attrs[n_attrs].name = name;
+      attrs[n_attrs].retloc = retloc;
+      n_attrs += 1;
+      *retloc = NULL;
+
+      name = va_arg (args, const char *);
+      retloc = va_arg (args, const char **);
+    }
+
+  va_end (args);
+
+  i = 0;
+  while (attribute_names[i])
+    {
+      int j;
+
+      j = 0;
+      while (j < n_attrs)
+        {
+          if (strcmp (attrs[j].name, attribute_names[i]) == 0)
+            {
+              retloc = attrs[j].retloc;
+
+              if (*retloc != NULL)
+                {
+                  set_error (error, context,
+                             G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+                             "Attribute \"%s\" repeated twice on the same <%s> element",
+                             attrs[j].name, element_name);
+                  retval = FALSE;
+                  goto out;
+                }
+
+              *retloc = attribute_values[i];
+              break;
+            }
+
+          ++j;
+        }
+
+      if (j == n_attrs)
+        {
+          set_error (error, context,
+                     G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+                     "Attribute \"%s\" is invalid on <%s> element in this context",
+                     attribute_names[i], element_name);
+          retval = FALSE;
+          goto out;
+        }
+
+      ++i;
+    }
+
+ out:
+  return retval;
+
+#undef MAX_ATTRS
+}
+
+static gboolean
+check_no_attributes (GMarkupParseContext  *context,
+                     const char           *element_name,
+                     const char          **attribute_names,
+                     const char          **attribute_values,
+                     GError              **error)
+{
+  if (attribute_names[0] != NULL)
+    {
+      set_error (error, context,
+                 G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+                 "Attribute \"%s\" is invalid on <%s> element in this context",
+                 attribute_names[0], element_name);
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+static int
+has_child_of_type (MenuLayoutNode     *node,
+                   MenuLayoutNodeType  type)
+{
+  MenuLayoutNode *iter;
+
+  iter = node->children;
+  while (iter)
+    {
+      if (iter->type == type)
+        return TRUE;
+
+      iter = node_next (iter);
+    }
+
+  return FALSE;
+}
+
+static void
+push_node (MenuParser         *parser,
+           MenuLayoutNodeType  type)
+{
+  MenuLayoutNode *node;
+
+  node = menu_layout_node_new (type);
+  menu_layout_node_append_child (parser->stack_top, node);
+  menu_layout_node_unref (node);
+
+  parser->stack_top = node;
+}
+
+static void
+start_menu_element (MenuParser           *parser,
+                    GMarkupParseContext  *context,
+                    const char           *element_name,
+                    const char          **attribute_names,
+                    const char          **attribute_values,
+                    GError              **error)
+{
+  if (!check_no_attributes (context, element_name,
+                            attribute_names, attribute_values,
+                            error))
+    return;
+
+  if (!(parser->stack_top->type == MENU_LAYOUT_NODE_ROOT ||
+        parser->stack_top->type == MENU_LAYOUT_NODE_MENU))
+    {
+      set_error (error, context,
+                 G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+                 "<Menu> element can only appear below other <Menu> elements or at toplevel\n");
+    }
+  else
+    {
+      push_node (parser, MENU_LAYOUT_NODE_MENU);
+    }
+}
+
+static void
+start_menu_child_element (MenuParser           *parser,
+                          GMarkupParseContext  *context,
+                          const char           *element_name,
+                          const char          **attribute_names,
+                          const char          **attribute_values,
+                          GError              **error)
+{
+  if (ELEMENT_IS ("LegacyDir"))
+    {
+      const char *prefix;
+
+      push_node (parser, MENU_LAYOUT_NODE_LEGACY_DIR);
+
+      if (!locate_attributes (context, element_name,
+                              attribute_names, attribute_values,
+                              error,
+                              "prefix", &prefix,
+                              NULL))
+        return;
+
+      menu_layout_node_legacy_dir_set_prefix (parser->stack_top, prefix);
+    }
+  else if (ELEMENT_IS ("MergeFile"))
+    {
+      const char *type;
+
+      push_node (parser, MENU_LAYOUT_NODE_MERGE_FILE);
+
+      if (!locate_attributes (context, element_name,
+                              attribute_names, attribute_values,
+                              error,
+                              "type", &type,
+                              NULL))
+        return;
+
+      if (type != NULL && strcmp (type, "parent") == 0)
+	{
+	  menu_layout_node_merge_file_set_type (parser->stack_top,
+						MENU_MERGE_FILE_TYPE_PARENT);
+	}
+    }
+  else if (ELEMENT_IS ("DefaultLayout"))
+    {
+      const char *show_empty;
+      const char *inline_menus;
+      const char *inline_limit;
+      const char *inline_header;
+      const char *inline_alias;
+
+      push_node (parser, MENU_LAYOUT_NODE_DEFAULT_LAYOUT);
+
+      locate_attributes (context, element_name,
+                         attribute_names, attribute_values,
+                         error,
+                         "show_empty",    &show_empty,
+                         "inline",        &inline_menus,
+                         "inline_limit",  &inline_limit,
+                         "inline_header", &inline_header,
+                         "inline_alias",  &inline_alias,
+                         NULL);
+
+      menu_layout_node_default_layout_set_values (parser->stack_top,
+						  show_empty,
+						  inline_menus,
+						  inline_limit,
+						  inline_header,
+						  inline_alias);
+    }
+  else
+    {
+      if (!check_no_attributes (context, element_name,
+                                attribute_names, attribute_values,
+                                error))
+        return;
+
+      if (ELEMENT_IS ("AppDir"))
+        {
+          push_node (parser, MENU_LAYOUT_NODE_APP_DIR);
+        }
+      else if (ELEMENT_IS ("DefaultAppDirs"))
+        {
+          push_node (parser, MENU_LAYOUT_NODE_DEFAULT_APP_DIRS);
+        }
+      else if (ELEMENT_IS ("DirectoryDir"))
+        {
+          push_node (parser, MENU_LAYOUT_NODE_DIRECTORY_DIR);
+        }
+      else if (ELEMENT_IS ("DefaultDirectoryDirs"))
+        {
+          push_node (parser, MENU_LAYOUT_NODE_DEFAULT_DIRECTORY_DIRS);
+        }
+      else if (ELEMENT_IS ("DefaultMergeDirs"))
+        {
+          push_node (parser, MENU_LAYOUT_NODE_DEFAULT_MERGE_DIRS);
+        }
+      else if (ELEMENT_IS ("Name"))
+        {
+          if (has_child_of_type (parser->stack_top, MENU_LAYOUT_NODE_NAME))
+            {
+              set_error (error, context,
+                         G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+                         "Multiple <Name> elements in a <Menu> element is not allowed\n");
+              return;
+            }
+
+          push_node (parser, MENU_LAYOUT_NODE_NAME);
+        }
+      else if (ELEMENT_IS ("Directory"))
+        {
+          push_node (parser, MENU_LAYOUT_NODE_DIRECTORY);
+        }
+      else if (ELEMENT_IS ("OnlyUnallocated"))
+        {
+          push_node (parser, MENU_LAYOUT_NODE_ONLY_UNALLOCATED);
+        }
+      else if (ELEMENT_IS ("NotOnlyUnallocated"))
+        {
+          push_node (parser, MENU_LAYOUT_NODE_NOT_ONLY_UNALLOCATED);
+        }
+      else if (ELEMENT_IS ("Include"))
+        {
+          push_node (parser, MENU_LAYOUT_NODE_INCLUDE);
+        }
+      else if (ELEMENT_IS ("Exclude"))
+        {
+          push_node (parser, MENU_LAYOUT_NODE_EXCLUDE);
+        }
+      else if (ELEMENT_IS ("MergeDir"))
+        {
+          push_node (parser, MENU_LAYOUT_NODE_MERGE_DIR);
+        }
+      else if (ELEMENT_IS ("KDELegacyDirs"))
+        {
+          push_node (parser, MENU_LAYOUT_NODE_KDE_LEGACY_DIRS);
+        }
+      else if (ELEMENT_IS ("Move"))
+        {
+          push_node (parser, MENU_LAYOUT_NODE_MOVE);
+        }
+      else if (ELEMENT_IS ("Deleted"))
+        {
+          push_node (parser, MENU_LAYOUT_NODE_DELETED);
+
+        }
+      else if (ELEMENT_IS ("NotDeleted"))
+        {
+          push_node (parser, MENU_LAYOUT_NODE_NOT_DELETED);
+        }
+      else if (ELEMENT_IS ("Layout"))
+        {
+          push_node (parser, MENU_LAYOUT_NODE_LAYOUT);
+        }
+      else
+        {
+          set_error (error, context,
+                     G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
+                     "Element <%s> may not appear below <%s>\n",
+                     element_name, "Menu");
+        }
+    }
+}
+
+static void
+start_matching_rule_element (MenuParser           *parser,
+                             GMarkupParseContext  *context,
+                             const char           *element_name,
+                             const char          **attribute_names,
+                             const char          **attribute_values,
+                             GError              **error)
+{
+  if (!check_no_attributes (context, element_name,
+                            attribute_names, attribute_values,
+                            error))
+    return;
+
+  if (ELEMENT_IS ("Filename"))
+    {
+      push_node (parser, MENU_LAYOUT_NODE_FILENAME);
+    }
+  else if (ELEMENT_IS ("Category"))
+    {
+      push_node (parser, MENU_LAYOUT_NODE_CATEGORY);
+    }
+  else if (ELEMENT_IS ("All"))
+    {
+      push_node (parser, MENU_LAYOUT_NODE_ALL);
+    }
+  else if (ELEMENT_IS ("And"))
+    {
+      push_node (parser, MENU_LAYOUT_NODE_AND);
+    }
+  else if (ELEMENT_IS ("Or"))
+    {
+      push_node (parser, MENU_LAYOUT_NODE_OR);
+    }
+  else if (ELEMENT_IS ("Not"))
+    {
+      push_node (parser, MENU_LAYOUT_NODE_NOT);
+    }
+  else
+    {
+      set_error (error, context,
+                 G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
+                 "Element <%s> may not appear in this context\n",
+                 element_name);
+    }
+}
+
+static void
+start_move_child_element (MenuParser           *parser,
+                          GMarkupParseContext  *context,
+                          const char           *element_name,
+                          const char          **attribute_names,
+                          const char          **attribute_values,
+                          GError              **error)
+{
+  if (!check_no_attributes (context, element_name,
+                            attribute_names, attribute_values,
+                            error))
+    return;
+
+  if (ELEMENT_IS ("Old"))
+    {
+      push_node (parser, MENU_LAYOUT_NODE_OLD);
+    }
+  else if (ELEMENT_IS ("New"))
+    {
+      push_node (parser, MENU_LAYOUT_NODE_NEW);
+    }
+  else
+    {
+      set_error (error, context,
+                 G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
+                 "Element <%s> may not appear below <%s>\n",
+                 element_name, "Move");
+    }
+}
+
+static void
+start_layout_child_element (MenuParser           *parser,
+                            GMarkupParseContext  *context,
+                            const char           *element_name,
+                            const char          **attribute_names,
+                            const char          **attribute_values,
+                            GError              **error)
+{
+  if (ELEMENT_IS ("Menuname"))
+    {
+      const char *show_empty;
+      const char *inline_menus;
+      const char *inline_limit;
+      const char *inline_header;
+      const char *inline_alias;
+
+      push_node (parser, MENU_LAYOUT_NODE_MENUNAME);
+
+      locate_attributes (context, element_name,
+                         attribute_names, attribute_values,
+                         error,
+                         "show_empty",    &show_empty,
+                         "inline",        &inline_menus,
+                         "inline_limit",  &inline_limit,
+                         "inline_header", &inline_header,
+                         "inline_alias",  &inline_alias,
+                         NULL);
+
+       menu_layout_node_menuname_set_values (parser->stack_top,
+					     show_empty,
+					     inline_menus,
+					     inline_limit,
+					     inline_header,
+					     inline_alias);
+    }
+  else if (ELEMENT_IS ("Merge"))
+    {
+        const char *type;
+
+        push_node (parser, MENU_LAYOUT_NODE_MERGE);
+
+        locate_attributes (context, element_name,
+                           attribute_names, attribute_values,
+                           error,
+                           "type", &type,
+                           NULL);
+
+	menu_layout_node_merge_set_type (parser->stack_top, type);
+    }
+  else
+    {
+      if (!check_no_attributes (context, element_name,
+                                attribute_names, attribute_values,
+                                error))
+        return;
+
+      if (ELEMENT_IS ("Filename"))
+        {
+          push_node (parser, MENU_LAYOUT_NODE_FILENAME);
+        }
+      else if (ELEMENT_IS ("Separator"))
+        {
+          push_node (parser, MENU_LAYOUT_NODE_SEPARATOR);
+        }
+      else
+        {
+          set_error (error, context,
+                     G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
+                     "Element <%s> may not appear below <%s>\n",
+                     element_name, "Move");
+        }
+    }
+}
+
+static void
+start_element_handler (GMarkupParseContext   *context,
+                       const char            *element_name,
+                       const char           **attribute_names,
+                       const char           **attribute_values,
+                       gpointer               user_data,
+                       GError               **error)
+{
+  MenuParser *parser = user_data;
+
+  if (ELEMENT_IS ("Menu"))
+    {
+      if (parser->stack_top == parser->root &&
+          has_child_of_type (parser->root, MENU_LAYOUT_NODE_MENU))
+        {
+          set_error (error, context,
+                     G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+                     "Multiple root elements in menu file, only one toplevel <Menu> is allowed\n");
+          return;
+        }
+
+      start_menu_element (parser, context, element_name,
+                          attribute_names, attribute_values,
+                          error);
+    }
+  else if (parser->stack_top == parser->root)
+    {
+      set_error (error, context,
+                 G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+                 "Root element in a menu file must be <Menu>, not <%s>\n",
+                 element_name);
+    }
+  else if (parser->stack_top->type == MENU_LAYOUT_NODE_MENU)
+    {
+      start_menu_child_element (parser, context, element_name,
+                                attribute_names, attribute_values,
+                                error);
+    }
+  else if (parser->stack_top->type == MENU_LAYOUT_NODE_INCLUDE ||
+           parser->stack_top->type == MENU_LAYOUT_NODE_EXCLUDE ||
+           parser->stack_top->type == MENU_LAYOUT_NODE_AND     ||
+           parser->stack_top->type == MENU_LAYOUT_NODE_OR      ||
+           parser->stack_top->type == MENU_LAYOUT_NODE_NOT)
+    {
+      start_matching_rule_element (parser, context, element_name,
+                                   attribute_names, attribute_values,
+                                   error);
+    }
+  else if (parser->stack_top->type == MENU_LAYOUT_NODE_MOVE)
+    {
+      start_move_child_element (parser, context, element_name,
+                                attribute_names, attribute_values,
+                                error);
+    }
+  else if (parser->stack_top->type == MENU_LAYOUT_NODE_LAYOUT ||
+           parser->stack_top->type == MENU_LAYOUT_NODE_DEFAULT_LAYOUT)
+    {
+      start_layout_child_element (parser, context, element_name,
+                                  attribute_names, attribute_values,
+                                  error);
+    }
+  else
+    {
+      set_error (error, context,
+                 G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
+                 "Element <%s> may not appear in this context\n",
+                 element_name);
+    }
+
+  add_context_to_error (error, context);
+}
+
+/* we want to make sure that the <Layout> or <DefaultLayout> is either empty,
+ * or contain one <Merge> of type "all", or contain one <Merge> of type "menus"
+ * and one <Merge> of type "files". If this is not the case, we try to clean up
+ * things:
+ *  + if there is at least one <Merge> of type "all", then we only keep the
+ *    last <Merge> of type "all" and remove all others <Merge>
+ *  + if there is no <Merge> with type "all", we keep only the last <Merge> of
+ *    type "menus" and the last <Merge> of type "files". If there's no <Merge>
+ *    of type "menus" we append one, and then if there's no <Merge> of type
+ *    "files", we append one. (So menus are before files)
+ */
+static gboolean
+fixup_layout_node (GMarkupParseContext   *context,
+                   MenuParser            *parser,
+                   MenuLayoutNode        *node,
+                   GError              **error)
+{
+  MenuLayoutNode *child;
+  MenuLayoutNode *last_all;
+  MenuLayoutNode *last_menus;
+  MenuLayoutNode *last_files;
+  int             n_all;
+  int             n_menus;
+  int             n_files;
+
+  if (!node->children)
+    {
+      return TRUE;
+    }
+
+  last_all   = NULL;
+  last_menus = NULL;
+  last_files = NULL;
+  n_all   = 0;
+  n_menus = 0;
+  n_files = 0;
+
+  child = node->children;
+  while (child != NULL)
+    {
+      switch (child->type)
+        {
+        case MENU_LAYOUT_NODE_MERGE:
+	  switch (menu_layout_node_merge_get_type (child))
+	    {
+	    case MENU_LAYOUT_MERGE_NONE:
+	      break;
+
+	    case MENU_LAYOUT_MERGE_MENUS:
+	      last_menus = child;
+	      n_menus++;
+	      break;
+
+	    case MENU_LAYOUT_MERGE_FILES:
+	      last_files = child;
+	      n_files++;
+	      break;
+
+	    case MENU_LAYOUT_MERGE_ALL:
+	      last_all = child;
+	      n_all++;
+	      break;
+
+	    default:
+	      g_assert_not_reached ();
+	      break;
+	    }
+          break;
+
+	  default:
+	    break;
+	}
+
+      child = node_next (child);
+    }
+
+  if ((n_all == 1 && n_menus == 0 && n_files == 0) ||
+      (n_all == 0 && n_menus == 1 && n_files == 1))
+    {
+      return TRUE;
+    }
+  else if (n_all > 1 || n_menus > 1 || n_files > 1 ||
+           (n_all == 1 && (n_menus != 0 || n_files != 0)))
+    {
+      child = node->children;
+      while (child != NULL)
+	{
+	  MenuLayoutNode *next;
+
+	  next = node_next (child);
+
+	  switch (child->type)
+	    {
+	    case MENU_LAYOUT_NODE_MERGE:
+	      switch (menu_layout_node_merge_get_type (child))
+		{
+		case MENU_LAYOUT_MERGE_NONE:
+		  break;
+
+		case MENU_LAYOUT_MERGE_MENUS:
+		  if (n_all || last_menus != child)
+		    {
+		      menu_verbose ("removing duplicated merge menus element\n");
+		      menu_layout_node_unlink (child);
+		    }
+		  break;
+
+		case MENU_LAYOUT_MERGE_FILES:
+		  if (n_all || last_files != child)
+		    {
+		      menu_verbose ("removing duplicated merge files element\n");
+		      menu_layout_node_unlink (child);
+		    }
+		  break;
+
+		case MENU_LAYOUT_MERGE_ALL:
+		  if (last_all != child)
+		    {
+		      menu_verbose ("removing duplicated merge all element\n");
+		      menu_layout_node_unlink (child);
+		    }
+		  break;
+
+		default:
+		  g_assert_not_reached ();
+		  break;
+		}
+	      break;
+
+	      default:
+		break;
+	    }
+
+	  child = next;
+	}
+    }
+
+  if (n_all == 0 && n_menus == 0)
+    {
+      last_menus = menu_layout_node_new (MENU_LAYOUT_NODE_MERGE);
+      menu_layout_node_merge_set_type (last_menus, "menus");
+      menu_verbose ("appending missing merge menus element\n");
+      menu_layout_node_append_child (node, last_menus);
+    }
+
+  if (n_all == 0 && n_files == 0)
+    {
+      last_files = menu_layout_node_new (MENU_LAYOUT_NODE_MERGE);
+      menu_layout_node_merge_set_type (last_files, "files");
+      menu_verbose ("appending missing merge files element\n");
+      menu_layout_node_append_child (node, last_files);
+    }
+
+  return TRUE;
+}
+
+/* we want to a) check that we have old-new pairs and b) canonicalize
+ * such that each <Move> has exactly one old-new pair
+ */
+static gboolean
+fixup_move_node (GMarkupParseContext   *context,
+                 MenuParser            *parser,
+                 MenuLayoutNode        *node,
+                 GError              **error)
+{
+  MenuLayoutNode *child;
+  int             n_old;
+  int             n_new;
+
+  n_old = 0;
+  n_new = 0;
+
+  child = node->children;
+  while (child != NULL)
+    {
+      switch (child->type)
+        {
+        case MENU_LAYOUT_NODE_OLD:
+          if (n_new != n_old)
+            {
+              set_error (error, context,
+                         G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+                         "<Old>/<New> elements not paired properly\n");
+              return FALSE;
+            }
+
+          n_old += 1;
+
+          break;
+
+        case MENU_LAYOUT_NODE_NEW:
+          n_new += 1;
+
+          if (n_new != n_old)
+            {
+              set_error (error, context,
+                         G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+                         "<Old>/<New> elements not paired properly\n");
+              return FALSE;
+            }
+
+          break;
+
+        default:
+          g_assert_not_reached ();
+          break;
+        }
+
+      child = node_next (child);
+    }
+
+  if (n_new == 0 || n_old == 0)
+    {
+      set_error (error, context,
+                 G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+                 "<Old>/<New> elements missing under <Move>\n");
+      return FALSE;
+    }
+
+  g_assert (n_new == n_old);
+  g_assert ((n_new + n_old) % 2 == 0);
+
+  if (n_new > 1)
+    {
+      MenuLayoutNode *prev;
+      MenuLayoutNode *append_after;
+
+      /* Need to split the <Move> into multiple <Move> */
+
+      n_old = 0;
+      n_new = 0;
+      prev = NULL;
+      append_after = node;
+
+      child = node->children;
+      while (child != NULL)
+        {
+          MenuLayoutNode *next;
+
+          next = node_next (child);
+
+          switch (child->type)
+            {
+            case MENU_LAYOUT_NODE_OLD:
+              n_old += 1;
+              break;
+
+            case MENU_LAYOUT_NODE_NEW:
+              n_new += 1;
+              break;
+
+            default:
+              g_assert_not_reached ();
+              break;
+            }
+
+          if (n_old == n_new &&
+              n_old > 1)
+            {
+              /* Move the just-completed pair */
+              MenuLayoutNode *new_move;
+
+              g_assert (prev != NULL);
+
+              new_move = menu_layout_node_new (MENU_LAYOUT_NODE_MOVE);
+              menu_verbose ("inserting new_move after append_after\n");
+              menu_layout_node_insert_after (append_after, new_move);
+              append_after = new_move;
+
+              menu_layout_node_steal (prev);
+              menu_layout_node_steal (child);
+
+              menu_verbose ("appending prev to new_move\n");
+              menu_layout_node_append_child (new_move, prev);
+              menu_verbose ("appending child to new_move\n");
+              menu_layout_node_append_child (new_move, child);
+
+              menu_verbose ("Created new move element old = %s new = %s\n",
+                            menu_layout_node_move_get_old (new_move),
+                            menu_layout_node_move_get_new (new_move));
+
+              menu_layout_node_unref (new_move);
+              menu_layout_node_unref (prev);
+              menu_layout_node_unref (child);
+
+              prev = NULL;
+            }
+          else
+            {
+              prev = child;<--- prev is assigned
+            }
+
+          prev = child;<--- prev is overwritten
+          child = next;
+        }
+    }
+
+  return TRUE;
+}
+
+static void
+end_element_handler (GMarkupParseContext  *context,
+                     const char           *element_name,
+                     gpointer              user_data,
+                     GError              **error)
+{
+  MenuParser *parser = user_data;
+
+  g_assert (parser->stack_top != NULL);
+
+  switch (parser->stack_top->type)
+    {
+    case MENU_LAYOUT_NODE_APP_DIR:
+    case MENU_LAYOUT_NODE_DIRECTORY_DIR:
+    case MENU_LAYOUT_NODE_NAME:
+    case MENU_LAYOUT_NODE_DIRECTORY:
+    case MENU_LAYOUT_NODE_FILENAME:
+    case MENU_LAYOUT_NODE_CATEGORY:
+    case MENU_LAYOUT_NODE_MERGE_DIR:
+    case MENU_LAYOUT_NODE_LEGACY_DIR:
+    case MENU_LAYOUT_NODE_OLD:
+    case MENU_LAYOUT_NODE_NEW:
+    case MENU_LAYOUT_NODE_MENUNAME:
+      if (menu_layout_node_get_content (parser->stack_top) == NULL)
+        {
+          set_error (error, context,
+                     G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+                     "Element <%s> is required to contain text and was empty\n",
+                     element_name);
+          goto out;
+        }
+      break;
+
+    case MENU_LAYOUT_NODE_MENU:
+      if (!has_child_of_type (parser->stack_top, MENU_LAYOUT_NODE_NAME))
+        {
+          set_error (error, context,
+                     G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+                     "<Menu> elements are required to contain a <Name> element\n");
+          goto out;
+        }
+      break;
+
+    case MENU_LAYOUT_NODE_ROOT:
+    case MENU_LAYOUT_NODE_PASSTHROUGH:
+    case MENU_LAYOUT_NODE_DEFAULT_APP_DIRS:
+    case MENU_LAYOUT_NODE_DEFAULT_DIRECTORY_DIRS:
+    case MENU_LAYOUT_NODE_DEFAULT_MERGE_DIRS:
+    case MENU_LAYOUT_NODE_ONLY_UNALLOCATED:
+    case MENU_LAYOUT_NODE_NOT_ONLY_UNALLOCATED:
+    case MENU_LAYOUT_NODE_INCLUDE:
+    case MENU_LAYOUT_NODE_EXCLUDE:
+    case MENU_LAYOUT_NODE_ALL:
+    case MENU_LAYOUT_NODE_AND:
+    case MENU_LAYOUT_NODE_OR:
+    case MENU_LAYOUT_NODE_NOT:
+    case MENU_LAYOUT_NODE_KDE_LEGACY_DIRS:
+    case MENU_LAYOUT_NODE_DELETED:
+    case MENU_LAYOUT_NODE_NOT_DELETED:
+    case MENU_LAYOUT_NODE_SEPARATOR:
+    case MENU_LAYOUT_NODE_MERGE:
+    case MENU_LAYOUT_NODE_MERGE_FILE:
+      break;
+
+    case MENU_LAYOUT_NODE_LAYOUT:
+    case MENU_LAYOUT_NODE_DEFAULT_LAYOUT:
+      if (!fixup_layout_node (context, parser, parser->stack_top, error))
+        goto out;
+      break;
+
+    case MENU_LAYOUT_NODE_MOVE:
+      if (!fixup_move_node (context, parser, parser->stack_top, error))
+        goto out;
+      break;
+    default:
+      g_assert_not_reached();
+    }
+
+ out:
+  parser->stack_top = parser->stack_top->parent;
+}
+
+static gboolean
+all_whitespace (const char *text,
+                gsize       text_len)
+{
+  const char *p;
+  const char *end;
+
+  p = text;
+  end = text + text_len;
+
+  while (p != end)
+    {
+      if (!g_ascii_isspace (*p))
+        return FALSE;
+
+      p = g_utf8_next_char (p);
+    }
+
+  return TRUE;
+}
+
+static void
+text_handler (GMarkupParseContext  *context,
+              const char           *text,
+              gsize                 text_len,
+              gpointer              user_data,
+              GError              **error)
+{
+  MenuParser *parser = user_data;
+
+  switch (parser->stack_top->type)
+    {
+    case MENU_LAYOUT_NODE_APP_DIR:
+    case MENU_LAYOUT_NODE_DIRECTORY_DIR:
+    case MENU_LAYOUT_NODE_NAME:
+    case MENU_LAYOUT_NODE_DIRECTORY:
+    case MENU_LAYOUT_NODE_FILENAME:
+    case MENU_LAYOUT_NODE_CATEGORY:
+    case MENU_LAYOUT_NODE_MERGE_FILE:
+    case MENU_LAYOUT_NODE_MERGE_DIR:
+    case MENU_LAYOUT_NODE_LEGACY_DIR:
+    case MENU_LAYOUT_NODE_OLD:
+    case MENU_LAYOUT_NODE_NEW:
+    case MENU_LAYOUT_NODE_MENUNAME:
+      g_assert (menu_layout_node_get_content (parser->stack_top) == NULL);
+
+      menu_layout_node_set_content (parser->stack_top, text);
+      break;
+
+    case MENU_LAYOUT_NODE_ROOT:
+    case MENU_LAYOUT_NODE_PASSTHROUGH:
+    case MENU_LAYOUT_NODE_MENU:
+    case MENU_LAYOUT_NODE_DEFAULT_APP_DIRS:
+    case MENU_LAYOUT_NODE_DEFAULT_DIRECTORY_DIRS:
+    case MENU_LAYOUT_NODE_DEFAULT_MERGE_DIRS:
+    case MENU_LAYOUT_NODE_ONLY_UNALLOCATED:
+    case MENU_LAYOUT_NODE_NOT_ONLY_UNALLOCATED:
+    case MENU_LAYOUT_NODE_INCLUDE:
+    case MENU_LAYOUT_NODE_EXCLUDE:
+    case MENU_LAYOUT_NODE_ALL:
+    case MENU_LAYOUT_NODE_AND:
+    case MENU_LAYOUT_NODE_OR:
+    case MENU_LAYOUT_NODE_NOT:
+    case MENU_LAYOUT_NODE_KDE_LEGACY_DIRS:
+    case MENU_LAYOUT_NODE_MOVE:
+    case MENU_LAYOUT_NODE_DELETED:
+    case MENU_LAYOUT_NODE_NOT_DELETED:
+    case MENU_LAYOUT_NODE_LAYOUT:
+    case MENU_LAYOUT_NODE_DEFAULT_LAYOUT:
+    case MENU_LAYOUT_NODE_SEPARATOR:
+    case MENU_LAYOUT_NODE_MERGE:
+      if (!all_whitespace (text, text_len))
+        {
+          set_error (error, context,
+                     G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+                     "No text is allowed inside element <%s>",
+                     g_markup_parse_context_get_element (context));
+        }
+      break;
+    default:
+      g_assert_not_reached();
+    }
+
+  add_context_to_error (error, context);
+}
+
+static void
+passthrough_handler (GMarkupParseContext  *context,
+                     const char           *passthrough_text,
+                     gsize                 text_len,
+                     gpointer              user_data,
+                     GError              **error)
+{
+  MenuParser *parser = user_data;
+  MenuLayoutNode *node;
+
+  /* don't push passthrough on the stack, it's not an element */
+
+  node = menu_layout_node_new (MENU_LAYOUT_NODE_PASSTHROUGH);
+  menu_layout_node_set_content (node, passthrough_text);
+
+  menu_layout_node_append_child (parser->stack_top, node);
+  menu_layout_node_unref (node);
+
+  add_context_to_error (error, context);
+}
+
+static void
+menu_parser_init (MenuParser *parser)
+{
+  parser->root = menu_layout_node_new (MENU_LAYOUT_NODE_ROOT);
+  parser->stack_top = parser->root;
+}
+
+static void
+menu_parser_free (MenuParser *parser)
+{
+  if (parser->root)
+    menu_layout_node_unref (parser->root);
+}
+
+MenuLayoutNode *
+menu_layout_load (const char  *filename,
+                  const char  *non_prefixed_basename,
+                  GError     **err)
+{
+  GMainContext        *main_context;
+  GMarkupParseContext *context;
+  MenuLayoutNodeRoot  *root;
+  MenuLayoutNode      *retval;
+  MenuParser           parser;
+  GError              *error;
+  GString             *str;
+  char                *text;
+  char                *s;
+  gsize                length;
+
+  text = NULL;
+  length = 0;
+  retval = NULL;
+  context = NULL;
+
+  main_context = g_main_context_get_thread_default ();
+
+  menu_verbose ("Loading \"%s\" from disk\n", filename);
+
+  if (!g_file_get_contents (filename,
+                            &text,
+                            &length,
+                            err))
+    {
+      menu_verbose ("Failed to load \"%s\"\n",
+                    filename);
+      return NULL;
+    }
+
+  g_assert (text != NULL);
+
+  menu_parser_init (&parser);
+
+  root = (MenuLayoutNodeRoot *) parser.root;
+
+  root->basedir = g_path_get_dirname (filename);
+  menu_verbose ("Set basedir \"%s\"\n", root->basedir);
+
+  if (non_prefixed_basename)
+    s = g_strdup (non_prefixed_basename);
+  else
+    s = g_path_get_basename (filename);
+  str = g_string_new (s);
+  if (g_str_has_suffix (str->str, ".menu"))
+    g_string_truncate (str, str->len - strlen (".menu"));
+
+  root->name = str->str;
+  menu_verbose ("Set menu name \"%s\"\n", root->name);
+
+  g_string_free (str, FALSE);
+  g_free (s);
+
+  context = g_markup_parse_context_new (&menu_funcs, 0, &parser, NULL);
+
+  error = NULL;
+  if (!g_markup_parse_context_parse (context,
+                                     text,
+                                     (gssize) length,
+                                     &error))
+    goto out;
+
+  error = NULL;
+  g_markup_parse_context_end_parse (context, &error);
+
+  root->main_context = main_context ? g_main_context_ref (main_context) : NULL;
+
+ out:
+  if (context)
+    g_markup_parse_context_free (context);
+  g_free (text);
+
+  if (error)
+    {
+      menu_verbose ("Error \"%s\" loading \"%s\"\n",
+                    error->message, filename);
+      g_propagate_error (err, error);
+    }
+  else if (has_child_of_type (parser.root, MENU_LAYOUT_NODE_MENU))
+    {
+      menu_verbose ("File loaded OK\n");
+      retval = parser.root;
+      parser.root = NULL;
+    }
+  else
+    {
+      menu_verbose ("Did not have a root element in file\n");
+      g_set_error (err, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+                   "Menu file %s did not contain a root <Menu> element",
+                   filename);
+    }
+
+  menu_parser_free (&parser);
+
+  return retval;
+}
+
+ +
+ +
+ + diff --git a/2023-09-02-151726-1231-cppcheck@7aac87a9810a_master/3.html b/2023-09-02-151726-1231-cppcheck@7aac87a9810a_master/3.html new file mode 100644 index 0000000..b72a26e --- /dev/null +++ b/2023-09-02-151726-1231-cppcheck@7aac87a9810a_master/3.html @@ -0,0 +1,1103 @@ + + + + + + Cppcheck - HTML report - mate-menus + + + + + +
+ + + +
+
  1
+  2
+  3
+  4
+  5
+  6
+  7
+  8
+  9
+ 10
+ 11
+ 12
+ 13
+ 14
+ 15
+ 16
+ 17
+ 18
+ 19
+ 20
+ 21
+ 22
+ 23
+ 24
+ 25
+ 26
+ 27
+ 28
+ 29
+ 30
+ 31
+ 32
+ 33
+ 34
+ 35
+ 36
+ 37
+ 38
+ 39
+ 40
+ 41
+ 42
+ 43
+ 44
+ 45
+ 46
+ 47
+ 48
+ 49
+ 50
+ 51
+ 52
+ 53
+ 54
+ 55
+ 56
+ 57
+ 58
+ 59
+ 60
+ 61
+ 62
+ 63
+ 64
+ 65
+ 66
+ 67
+ 68
+ 69
+ 70
+ 71
+ 72
+ 73
+ 74
+ 75
+ 76
+ 77
+ 78
+ 79
+ 80
+ 81
+ 82
+ 83
+ 84
+ 85
+ 86
+ 87
+ 88
+ 89
+ 90
+ 91
+ 92
+ 93
+ 94
+ 95
+ 96
+ 97
+ 98
+ 99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
/*
+ * Copyright (C) 2005 Red Hat, Inc.
+ * Copyright (C) 2006 Mark McLoughlin
+ * Copyright (C) 2007 Sebastian Dröge
+ * Copyright (C) 2008 Vincent Untz
+ * Copyright (C) 2012-2021 MATE Developers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <config.h>
+
+#include "menu-monitor.h"
+
+#include <gio/gio.h>
+
+#include "menu-util.h"
+
+struct MenuMonitor {
+	char* path;
+	guint refcount;
+
+	GSList* notifies;
+
+	GFileMonitor* monitor;
+
+	guint is_directory: 1;
+};
+
+typedef struct {
+	MenuMonitor* monitor;
+	MenuMonitorEvent event;
+	char* path;
+} MenuMonitorEventInfo;
+
+typedef struct {
+	MenuMonitorNotifyFunc notify_func;
+	gpointer user_data;
+	guint refcount;
+} MenuMonitorNotify;
+
+static MenuMonitorNotify* mate_menu_monitor_notify_ref(MenuMonitorNotify* notify);
+static void mate_menu_monitor_notify_unref(MenuMonitorNotify* notify);
+
+static GHashTable* monitors_registry = NULL;
+static guint events_idle_handler = 0;
+static GSList* pending_events = NULL;
+
+static void invoke_notifies(MenuMonitor* monitor, MenuMonitorEvent  event, const char* path)
+{
+  GSList *copy;
+  GSList *tmp;
+
+  copy = g_slist_copy (monitor->notifies);
+  g_slist_foreach (copy,
+		   (GFunc) mate_menu_monitor_notify_ref,
+		   NULL);
+
+  tmp = copy;
+  while (tmp != NULL)
+    {
+      MenuMonitorNotify *notify = tmp->data;
+      GSList            *next   = tmp->next;
+
+      if (notify->notify_func)
+	{
+	  notify->notify_func (monitor, event, path, notify->user_data);
+	}
+
+      mate_menu_monitor_notify_unref(notify);
+
+      tmp = next;
+    }
+
+  g_slist_free (copy);
+}
+
+static gboolean emit_events_in_idle(void)
+{
+  GSList *events_to_emit;
+  GSList *tmp;
+
+  events_to_emit = pending_events;
+
+  pending_events = NULL;
+  events_idle_handler = 0;
+
+  tmp = events_to_emit;
+  while (tmp != NULL)
+    {
+      MenuMonitorEventInfo *event_info = tmp->data;
+
+      mate_menu_monitor_ref(event_info->monitor);
+
+      tmp = tmp->next;
+    }
+
+  tmp = events_to_emit;
+  while (tmp != NULL)
+    {
+      MenuMonitorEventInfo *event_info = tmp->data;
+
+      invoke_notifies (event_info->monitor,
+		       event_info->event,
+		       event_info->path);
+
+      menu_monitor_unref (event_info->monitor);
+      event_info->monitor = NULL;
+
+      g_free (event_info->path);
+      event_info->path = NULL;
+
+      event_info->event = MENU_MONITOR_EVENT_INVALID;
+
+      g_free (event_info);
+
+      tmp = tmp->next;
+    }
+
+  g_slist_free (events_to_emit);
+
+  return FALSE;
+}
+
+static void menu_monitor_queue_event(MenuMonitorEventInfo* event_info)
+{
+  pending_events = g_slist_append (pending_events, event_info);
+
+  if (events_idle_handler == 0)
+    {
+      events_idle_handler = g_idle_add ((GSourceFunc) emit_events_in_idle, NULL);
+    }
+}
+
+static inline char* get_registry_key(const char* path, gboolean is_directory)
+{
+  return g_strdup_printf ("%s:%s",
+			  path,
+			  is_directory ? "<dir>" : "<file>");
+}
+
+static gboolean monitor_callback (GFileMonitor* monitor, GFile* child, GFile* other_file, GFileMonitorEvent eflags, gpointer user_data)
+{
+  MenuMonitorEventInfo *event_info;
+  MenuMonitorEvent      event;
+  MenuMonitor          *menu_monitor = (MenuMonitor *) user_data;
+
+  event = MENU_MONITOR_EVENT_INVALID;
+  switch (eflags)
+    {
+    case G_FILE_MONITOR_EVENT_CHANGED:
+      event = MENU_MONITOR_EVENT_CHANGED;
+      break;
+    case G_FILE_MONITOR_EVENT_CREATED:
+      event = MENU_MONITOR_EVENT_CREATED;
+      break;
+    case G_FILE_MONITOR_EVENT_DELETED:
+      event = MENU_MONITOR_EVENT_DELETED;
+      break;
+    default:
+      return TRUE;
+    }
+
+  event_info = g_new0 (MenuMonitorEventInfo, 1);
+
+  event_info->path    = g_file_get_path (child);
+  event_info->event   = event;
+  event_info->monitor = menu_monitor;
+
+  menu_monitor_queue_event (event_info);
+
+  return TRUE;
+}
+
+static MenuMonitor* register_monitor(const char* path, gboolean is_directory)
+{
+  MenuMonitor     *retval;
+  GFile           *file;
+
+  retval = g_new0 (MenuMonitor, 1);
+
+  retval->path         = g_strdup (path);
+  retval->refcount     = 1;
+  retval->is_directory = is_directory != FALSE;
+
+  file = g_file_new_for_path (retval->path);
+
+  if (file == NULL)
+    {
+      menu_verbose ("Not adding monitor on '%s', failed to create GFile\n",
+                    retval->path);
+      return retval;
+    }
+
+  if (retval->is_directory)
+      retval->monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE,
+                                                  NULL, NULL);
+  else
+      retval->monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE,
+                                             NULL, NULL);
+
+  g_object_unref (G_OBJECT (file));
+
+  if (retval->monitor == NULL)
+    {
+      menu_verbose ("Not adding monitor on '%s', failed to create monitor\n",
+                    retval->path);
+      return retval;
+    }
+
+  g_signal_connect (retval->monitor, "changed",
+                    G_CALLBACK (monitor_callback), retval);
+
+  return retval;
+}
+
+static MenuMonitor* lookup_monitor(const char* path, gboolean is_directory)
+{
+  MenuMonitor *retval;
+  char        *registry_key;
+
+  retval = NULL;
+
+  registry_key = get_registry_key (path, is_directory);
+
+  if (monitors_registry == NULL)
+    {
+      monitors_registry = g_hash_table_new_full (g_str_hash,
+						 g_str_equal,
+						 g_free,
+						 NULL);
+    }
+  else
+    {
+      retval = g_hash_table_lookup (monitors_registry, registry_key);
+    }
+
+  if (retval == NULL)
+    {
+      retval = register_monitor (path, is_directory);
+      g_hash_table_insert (monitors_registry, registry_key, retval);
+
+      return retval;
+    }
+  else
+    {
+      g_free (registry_key);
+
+      return mate_menu_monitor_ref(retval);
+    }
+}
+
+MenuMonitor* mate_menu_monitor_file_get(const char* path)
+{
+	g_return_val_if_fail(path != NULL, NULL);
+
+	return lookup_monitor(path, FALSE);
+}
+
+MenuMonitor* menu_get_directory_monitor(const char* path)
+{
+  g_return_val_if_fail (path != NULL, NULL);
+
+  return lookup_monitor (path, TRUE);
+}
+
+MenuMonitor* mate_menu_monitor_ref(MenuMonitor* monitor)
+{
+	g_return_val_if_fail(monitor != NULL, NULL);
+	g_return_val_if_fail(monitor->refcount > 0, NULL);
+
+	monitor->refcount++;
+
+	return monitor;
+}
+
+static void menu_monitor_clear_pending_events(MenuMonitor* monitor)<--- Parameter 'monitor' can be declared as pointer to const
+{
+  GSList *tmp;
+
+  tmp = pending_events;
+  while (tmp != NULL)
+    {
+      MenuMonitorEventInfo *event_info = tmp->data;
+      GSList               *next = tmp->next;
+
+      if (event_info->monitor == monitor)
+	{
+	  pending_events = g_slist_delete_link (pending_events, tmp);
+
+	  g_free (event_info->path);
+	  event_info->path = NULL;
+
+	  event_info->monitor = NULL;
+	  event_info->event   = MENU_MONITOR_EVENT_INVALID;
+
+	  g_free (event_info);
+	}
+
+      tmp = next;
+    }
+}
+
+void menu_monitor_unref(MenuMonitor* monitor)
+{
+  char *registry_key;
+
+  g_return_if_fail (monitor != NULL);
+  g_return_if_fail (monitor->refcount > 0);
+
+  if (--monitor->refcount > 0)
+    return;
+
+  registry_key = get_registry_key (monitor->path, monitor->is_directory);
+  g_hash_table_remove (monitors_registry, registry_key);
+  g_free (registry_key);
+
+  if (g_hash_table_size (monitors_registry) == 0)
+    {
+      g_hash_table_destroy (monitors_registry);
+      monitors_registry = NULL;
+    }
+
+  if (monitor->monitor)
+    {
+      g_file_monitor_cancel (monitor->monitor);
+      g_object_unref (monitor->monitor);
+      monitor->monitor = NULL;
+    }
+
+  g_slist_foreach (monitor->notifies, (GFunc) mate_menu_monitor_notify_unref, NULL);
+  g_slist_free (monitor->notifies);
+  monitor->notifies = NULL;
+
+  menu_monitor_clear_pending_events (monitor);
+
+  g_free (monitor->path);
+  monitor->path = NULL;
+
+  g_free (monitor);
+}
+
+static MenuMonitorNotify* mate_menu_monitor_notify_ref(MenuMonitorNotify* notify)
+{
+	g_return_val_if_fail(notify != NULL, NULL);
+	g_return_val_if_fail(notify->refcount > 0, NULL);
+
+	notify->refcount++;
+
+	return notify;
+}
+
+static void mate_menu_monitor_notify_unref(MenuMonitorNotify* notify)
+{
+	g_return_if_fail(notify != NULL);
+	g_return_if_fail(notify->refcount > 0);
+
+	if (--notify->refcount > 0)
+	{
+		return;
+	}
+
+	g_free(notify);
+}
+
+void menu_monitor_add_notify(MenuMonitor* monitor, MenuMonitorNotifyFunc notify_func, gpointer user_data)
+{
+	GSList* tmp;
+	MenuMonitorNotify* notify;
+
+	g_return_if_fail(monitor != NULL);
+	g_return_if_fail(notify_func != NULL);
+
+	tmp = monitor->notifies;
+
+	while (tmp != NULL)
+	{
+		notify = tmp->data;
+
+		if (notify->notify_func == notify_func && notify->user_data == user_data)
+		{
+			break;
+		}
+
+		tmp = tmp->next;
+	}
+
+	if (tmp == NULL)
+	{
+		notify = g_new0(MenuMonitorNotify, 1);
+		notify->notify_func = notify_func;
+		notify->user_data = user_data;
+		notify->refcount = 1;
+
+		monitor->notifies = g_slist_append(monitor->notifies, notify);
+	}
+}
+
+void mate_menu_monitor_notify_remove(MenuMonitor* monitor, MenuMonitorNotifyFunc notify_func, gpointer user_data)<--- Parameter 'notify_func' can be declared as pointer to const
+{
+	GSList* tmp = monitor->notifies;
+
+	while (tmp != NULL)
+	{
+		MenuMonitorNotify* notify = tmp->data;
+		GSList* next = tmp->next;
+
+		if (notify->notify_func == notify_func && notify->user_data == user_data)
+		{
+			notify->notify_func = NULL;
+			notify->user_data = NULL;
+
+			mate_menu_monitor_notify_unref(notify);
+
+			monitor->notifies = g_slist_delete_link(monitor->notifies, tmp);
+		}
+
+		tmp = next;
+	}
+}
+
+ +
+ +
+ + diff --git a/2023-09-02-151726-1231-cppcheck@7aac87a9810a_master/index.html b/2023-09-02-151726-1231-cppcheck@7aac87a9810a_master/index.html new file mode 100644 index 0000000..6280d6a --- /dev/null +++ b/2023-09-02-151726-1231-cppcheck@7aac87a9810a_master/index.html @@ -0,0 +1,216 @@ + + + + + + Cppcheck - HTML report - mate-menus + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
LineIdCWESeverityMessage
missingIncludeinformationCppcheck cannot find all the include files (use --check-config for details)
libmenu/entry-directories.c
679constParameter398styleParameter 'ed' can be declared as pointer to const
679constParameter398styleParameter 'callback' can be declared as pointer to const
libmenu/matemenu-tree.c
4846constParameter398styleParameter 'layout' can be declared as pointer to const
libmenu/menu-layout.c
1105constParameter398styleParameter 'callback' can be declared as pointer to const
1424varFuncNullUB475portabilityPassing NULL after the last typed argument to a variadic function leads to undefined behaviour.
1439varFuncNullUB475portabilityPassing NULL after the last typed argument to a variadic function leads to undefined behaviour.
1466varFuncNullUB475portabilityPassing NULL after the last typed argument to a variadic function leads to undefined behaviour.
1671varFuncNullUB475portabilityPassing NULL after the last typed argument to a variadic function leads to undefined behaviour.
1690varFuncNullUB475portabilityPassing NULL after the last typed argument to a variadic function leads to undefined behaviour.
2084redundantAssignment563styleVariable 'prev' is reassigned a value before the old one has been used.
libmenu/menu-monitor.c
290constParameter398styleParameter 'monitor' can be declared as pointer to const
412constParameter398styleParameter 'notify_func' can be declared as pointer to const
+
+ +
+ + diff --git a/2023-09-02-151726-1231-cppcheck@7aac87a9810a_master/stats.html b/2023-09-02-151726-1231-cppcheck@7aac87a9810a_master/stats.html new file mode 100644 index 0000000..9a03acd --- /dev/null +++ b/2023-09-02-151726-1231-cppcheck@7aac87a9810a_master/stats.html @@ -0,0 +1,173 @@ + + + + + + Cppcheck - HTML report - mate-menus + + + + + +
+ + + +
+

Top 10 files for portability severity, total findings: 5
+   5  libmenu/menu-layout.c
+

+

Top 10 files for style severity, total findings: 7
+   2  libmenu/menu-monitor.c
+   2  libmenu/menu-layout.c
+   2  libmenu/entry-directories.c
+   1  libmenu/matemenu-tree.c
+

+ +
+ +
+ + diff --git a/2023-09-02-151726-1231-cppcheck@7aac87a9810a_master/style.css b/2023-09-02-151726-1231-cppcheck@7aac87a9810a_master/style.css new file mode 100644 index 0000000..3897bfa --- /dev/null +++ b/2023-09-02-151726-1231-cppcheck@7aac87a9810a_master/style.css @@ -0,0 +1,177 @@ + +body { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif; + font-size: 13px; + line-height: 1.5; + height: 100%; + margin: 0; +} + +#wrapper { + position: fixed; + height: 100vh; + width: 100vw; + display: grid; + grid-template-rows: fit-content(8rem) auto fit-content(8rem); + grid-template-columns: fit-content(25%) 1fr; + grid-template-areas: + "header header" + "menu content" + "footer footer"; +} + +h1 { + margin: 0 0 8px -2px; + font-size: 175%; +} + +.header { + padding: 0 0 5px 15px; + grid-area: header; + border-bottom: thin solid #aaa; +} + +.footer { + grid-area: footer; + border-top: thin solid #aaa; + font-size: 85%; + +} + +.footer > p { + margin: 4px; +} + +#menu, +#menu_index { + grid-area: menu; + text-align: left; + overflow: auto; + padding: 0 23px 15px 15px; + border-right: thin solid #aaa; + min-width: 200px; +} + +#menu > a { + display: block; + margin-left: 10px; + font-size: 12px; +} + +#content, +#content_index { + grid-area: content; + padding: 0px 5px 15px 15px; + overflow: auto; +} + +label { + white-space: nowrap; +} + +label.checkBtn.disabled { + color: #606060; + background: #e0e0e0; + font-style: italic; +} + +label.checkBtn, input[type="text"] { + border: 1px solid grey; + border-radius: 4px; + box-shadow: 1px 1px inset; + padding: 1px 5px; +} + +label.checkBtn { + white-space: nowrap; + background: #ccddff; +} + +label.unchecked { + background: #eff8ff; + box-shadow: 1px 1px 1px; +} + +label.checkBtn:hover, label.unchecked:hover{ + box-shadow: 0 0 2px; +} + +label.disabled:hover { + box-shadow: 1px 1px inset; +} + +label.checkBtn > input { + display:none; +} + +.summaryTable { + width: 100%; +} + +table.summaryTable td { padding: 0 5px 0 5px; } + +.statHeader, .severityHeader { + font-weight: bold; +} + +.warning { + background-color: #ffffa7; +} + +.error { + background-color: #ffb7b7; +} + +.error2 { + background-color: #faa; + display: inline-block; + margin-left: 4px; +} + +.inconclusive { + background-color: #b6b6b4; +} + +.inconclusive2 { + background-color: #b6b6b4; + display: inline-block; + margin-left: 4px; +} + +.verbose { + display: inline-block; + vertical-align: top; + cursor: help; +} + +.verbose .content { + display: none; + position: absolute; + padding: 10px; + margin: 4px; + max-width: 40%; + white-space: pre-wrap; + border: 1px solid #000; + background-color: #ffffcc; + cursor: auto; +} + +.highlight .hll { + padding: 1px; +} + +.highlighttable { + background-color: #fff; + position: relative; + margin: -10px; +} + +.linenos { + border-right: thin solid #aaa; + color: #d3d3d3; + padding-right: 6px; +} + +.id-filtered, .severity-filtered, .file-filtered, .tool-filtered, .text-filtered { + visibility: collapse; +} -- cgit v1.2.1