cinnamon-menus-3.6.0/0000775000175000017500000000000013173357262013404 5ustar fabiofabiocinnamon-menus-3.6.0/xmldocs.make0000664000175000017500000000615413173357262015722 0ustar fabiofabio# # No modifications of this Makefile should be necessary. # # To use this template: # 1) Define: figdir, docname, lang, omffile, and entities in # your Makefile.am file for each document directory, # although figdir, omffile, and entities may be empty # 2) Make sure the Makefile in (1) also includes # "include $(top_srcdir)/xmldocs.make" and # "dist-hook: app-dist-hook". # 3) Optionally define 'entities' to hold xml entities which # you would also like installed # 4) Figures must go under $(figdir)/ and be in PNG format # 5) You should only have one document per directory # 6) Note that the figure directory, $(figdir)/, should not have its # own Makefile since this Makefile installs those figures. # # example Makefile.am: # figdir = figures # docname = scrollkeeper-manual # lang = C # omffile=scrollkeeper-manual-C.omf # entities = fdl.xml # include $(top_srcdir)/xmldocs.make # dist-hook: app-dist-hook # # About this file: # This file was taken from scrollkeeper_example2, a package illustrating # how to install documentation and OMF files for use with ScrollKeeper # 0.3.x and 0.4.x. For more information, see: # http://scrollkeeper.sourceforge.net/ # Version: 0.1.2 (last updated: March 20, 2002) # # ********** Begin of section some packagers may need to modify ********** # This variable (docdir) specifies where the documents should be installed. # This default value should work for most packages. docdir = $(datadir)/gnome/help/$(docname)/$(lang) # ********** You should not have to edit below this line ********** xml_files = $(entities) $(docname).xml EXTRA_DIST = $(xml_files) $(omffile) CLEANFILES = omf_timestamp include $(top_srcdir)/omf.make all: omf $(docname).xml: $(entities) -ourdir=`pwd`; \ cd $(srcdir); \ cp $(entities) $$ourdir app-dist-hook: if test "$(figdir)"; then \ $(mkinstalldirs) $(distdir)/$(figdir); \ for file in $(srcdir)/$(figdir)/*.png; do \ basefile=`echo $$file | sed -e 's,^.*/,,'`; \ $(INSTALL_DATA) $$file $(distdir)/$(figdir)/$$basefile; \ done \ fi install-data-local: omf $(mkinstalldirs) $(DESTDIR)$(docdir) for file in $(xml_files); do \ cp $(srcdir)/$$file $(DESTDIR)$(docdir); \ done if test "$(figdir)"; then \ $(mkinstalldirs) $(DESTDIR)$(docdir)/$(figdir); \ for file in $(srcdir)/$(figdir)/*.png; do \ basefile=`echo $$file | sed -e 's,^.*/,,'`; \ $(INSTALL_DATA) $$file $(DESTDIR)$(docdir)/$(figdir)/$$basefile; \ done \ fi install-data-hook: install-data-hook-omf uninstall-local: uninstall-local-doc uninstall-local-omf uninstall-local-doc: -if test "$(figdir)"; then \ for file in $(srcdir)/$(figdir)/*.png; do \ basefile=`echo $$file | sed -e 's,^.*/,,'`; \ rm -f $(DESTDIR)$(docdir)/$(figdir)/$$basefile; \ done; \ rmdir $(DESTDIR)$(docdir)/$(figdir); \ fi -for file in $(xml_files); do \ rm -f $(DESTDIR)$(docdir)/$$file; \ done -rmdir $(DESTDIR)$(docdir) clean-local: clean-local-doc clean-local-omf # for non-srcdir builds, remove the copied entities. clean-local-doc: if test $(srcdir) != .; then \ rm -f $(entities); \ fi cinnamon-menus-3.6.0/COPYING0000664000175000017500000004325413173357262014447 0ustar fabiofabio GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. cinnamon-menus-3.6.0/debian/0000775000175000017500000000000013173357262014626 5ustar fabiofabiocinnamon-menus-3.6.0/configure.ac0000664000175000017500000000625613173357262015703 0ustar fabiofabioAC_PREREQ(2.62) AC_INIT([cinnamon-menus], [3.6.0]) AC_CONFIG_SRCDIR(libmenu/gmenu-tree.h) m4_ifdef([AX_IS_RELEASE], [AX_IS_RELEASE([always])]) AM_INIT_AUTOMAKE([1.11 foreign no-dist-gzip dist-xz]) m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) AC_CONFIG_MACRO_DIR([m4]) AC_SUBST([ACLOCAL_AMFLAGS], ["-I $ac_macro_dir \${ACLOCAL_FLAGS}"]) AC_CONFIG_HEADERS(config.h) AM_MAINTAINER_MODE # Before making a release, the LT_VERSION string should be modified. # The string is of the form C:R:A. # - If interfaces have been changed or added, but binary compatibility has # been preserved, change to C+1:0:A+1 # - If binary compatibility has been broken (eg removed or changed interfaces) # change to C+1:0:0 # - If the interface is the same as the previous version, change to C:R+1:A LIB_MENU_LT_VERSION=0:1:0 AC_SUBST(LIB_MENU_LT_VERSION) AC_PROG_CC AC_STDC_HEADERS AC_ARG_PROGRAM AC_LIBTOOL_WIN32_DLL AM_PROG_LIBTOOL PKG_CHECK_MODULES(GIO_UNIX, gio-unix-2.0 >= 2.29.15) AC_SUBST(GIO_UNIX_CFLAGS) AC_SUBST(GIO_UNIX_LIBS) m4_ifdef([AX_COMPILER_FLAGS], [AX_COMPILER_FLAGS([WARN_CFLAGS],[WARN_LDFLAGS])]) AC_ARG_ENABLE(deprecation_flags, [AC_HELP_STRING([--enable-deprecation-flags], [use *_DISABLE_DEPRECATED flags @<:@default=no@:>@])],, [enable_deprecation_flags=no]) if test "x$enable_deprecation_flags" = "xyes"; then DISABLE_DEPRECATED_CFLAGS=$DISABLE_DEPRECATED AC_SUBST(DISABLE_DEPRECATED_CFLAGS) fi AC_ARG_ENABLE(debug, [AS_HELP_STRING([--enable-debug=@<:@no/minimum/yes@:>@], [turn on debugging @<:@default=debug_default@:>@])],, [enable_debug=debug_default]) if test "x$enable_debug" = "xyes"; then DEBUG_CFLAGS="-DG_ENABLE_DEBUG" else if test "x$enable_debug" = "xno"; then DEBUG_CFLAGS="-DG_DISABLE_ASSERT -DG_DISABLE_CHECKS -DG_DISABLE_CAST_CHECKS" else DEBUG_CFLAGS="-DG_ENABLE_DEBUG -DG_DISABLE_CAST_CHECKS" fi fi AC_SUBST(DEBUG_CFLAGS) GOBJECT_INTROSPECTION_CHECK([0.9.5]) AC_OUTPUT([ Makefile libmenu/Makefile libmenu/libcinnamon-menu-3.0.pc libmenu/libcinnamon-menu-3.0-uninstalled.pc ]) dnl --------------------------------------------------------------------------- dnl - Show summary dnl --------------------------------------------------------------------------- echo " cinnamon-menus $VERSION `echo cinnamon-menus $VERSION | sed "s/./=/g"` prefix: ${prefix} exec_prefix: ${exec_prefix} libdir: ${libdir} bindir: ${bindir} sbindir: ${sbindir} sysconfdir: ${sysconfdir} localstatedir: ${localstatedir} datadir: ${datadir} source code location: ${srcdir} compiler: ${CC} cflags: ${CFLAGS} Maintainer mode: ${USE_MAINTAINER_MODE} Use *_DISABLE_DEPRECATED: ${enable_deprecation_flags} Turn on debugging: ${enable_debug} Build introspection support: ${found_introspection} " cinnamon-menus-3.6.0/libmenu/0000775000175000017500000000000013173357262015037 5ustar fabiofabiocinnamon-menus-3.6.0/libmenu/menu-util.h0000664000175000017500000000271713173357262017136 0ustar fabiofabio/* Random utility functions for menu code */ /* * Copyright (C) 2003 Red Hat, Inc. * * 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., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef __MENU_UTIL_H__ #define __MENU_UTIL_H__ #include #include "menu-layout.h" G_BEGIN_DECLS #ifdef G_ENABLE_DEBUG void menu_verbose (const char *format, ...) G_GNUC_PRINTF (1, 2); void menu_debug_print_layout (MenuLayoutNode *node, gboolean onelevel); #else /* !defined(G_ENABLE_DEBUG) */ #ifdef G_HAVE_ISO_VARARGS #define menu_verbose(...) #elif defined(G_HAVE_GNUC_VARARGS) #define menu_verbose(format...) #else #error "Cannot disable verbose mode due to lack of varargs macros" #endif #define menu_debug_print_layout(n,o) #endif /* G_ENABLE_DEBUG */ G_END_DECLS #endif /* __MENU_UTIL_H__ */ cinnamon-menus-3.6.0/libmenu/libcinnamon-menu-3.0-uninstalled.pc.in0000664000175000017500000000046113173357262024042 0ustar fabiofabioprefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: libcinnamon-menu Description: Desktop Menu Specification Implementation Requires: gio-unix-2.0 Version: @VERSION@ Libs: ${pc_top_builddir}/${pcfiledir}/libcinnamon-menu-3.la Cflags: -I${pc_top_builddir}/${pcfiledir} cinnamon-menus-3.6.0/libmenu/canonicalize.h0000664000175000017500000000231713173357262017652 0ustar fabiofabio/* Return the canonical absolute name of a given file. Copyright (C) 1996-2001, 2002 Free Software Foundation, Inc. This file is part of the GNU C Library. Copyright (C) 2002 Red Hat, Inc. (trivial port to GLib) The GNU C 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.1 of the License, or (at your option) any later version. The GNU C 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 the GNU C Library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ #ifndef G_CANONICALIZE_H #define G_CANONICALIZE_H #include G_BEGIN_DECLS char* menu_canonicalize_file_name (const char *name, gboolean allow_missing_basename); G_END_DECLS #endif /* G_CANONICALIZE_H */ cinnamon-menus-3.6.0/libmenu/menu-layout.c0000664000175000017500000017213613173357262017474 0ustar fabiofabio/* Menu layout in-memory data structure (a custom "DOM tree") */ /* * Copyright (C) 2002 - 2004 Red Hat, Inc. * * 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., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include #include "menu-layout.h" #include #include #include #include #include #include "canonicalize.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 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); } 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; int limit; limit = strtol (inline_limit, &end, 10); if (*end == '\0') { values->inline_limit = 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, 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, int 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, int 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, " element can only appear below other 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 elements in a 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 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 , 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 or is either empty, * or contain one of type "all", or contain one of type "menus" * and one of type "files". If this is not the case, we try to clean up * things: * + if there is at least one of type "all", then we only keep the * last of type "all" and remove all others * + if there is no with type "all", we keep only the last of * type "menus" and the last of type "files". If there's no * of type "menus" we append one, and then if there's no 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 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, "/ 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, "/ 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, "/ elements missing under \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 into multiple */ 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 = child; 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, " elements are required to contain a 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; } out: parser->stack_top = parser->stack_top->parent; } static gboolean all_whitespace (const char *text, int 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; } 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, 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 element", filename); } menu_parser_free (&parser); return retval; } cinnamon-menus-3.6.0/libmenu/menu-monitor.h0000664000175000017500000000345613173357262017651 0ustar fabiofabio/* * Copyright (C) 2005 Red Hat, Inc. * * 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., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef __MENU_MONITOR_H__ #define __MENU_MONITOR_H__ #include G_BEGIN_DECLS typedef struct MenuMonitor MenuMonitor; typedef enum { MENU_MONITOR_EVENT_INVALID = 0, MENU_MONITOR_EVENT_CREATED = 1, MENU_MONITOR_EVENT_DELETED = 2, MENU_MONITOR_EVENT_CHANGED = 3 } MenuMonitorEvent; typedef void (*MenuMonitorNotifyFunc) (MenuMonitor *monitor, MenuMonitorEvent event, const char *path, gpointer user_data); MenuMonitor *menu_get_file_monitor (const char *path); MenuMonitor *menu_get_directory_monitor (const char *path); MenuMonitor *menu_monitor_ref (MenuMonitor *monitor); void menu_monitor_unref (MenuMonitor *monitor); void menu_monitor_add_notify (MenuMonitor *monitor, MenuMonitorNotifyFunc notify_func, gpointer user_data); void menu_monitor_remove_notify (MenuMonitor *monitor, MenuMonitorNotifyFunc notify_func, gpointer user_data); G_END_DECLS #endif /* __MENU_MONITOR_H__ */ cinnamon-menus-3.6.0/libmenu/menu-layout.h0000664000175000017500000001445013173357262017473 0ustar fabiofabio/* Menu layout in-memory data structure (a custom "DOM tree") */ /* * Copyright (C) 2002 - 2004 Red Hat, Inc. * * 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., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef __MENU_LAYOUT_H__ #define __MENU_LAYOUT_H__ #include #include "entry-directories.h" G_BEGIN_DECLS typedef struct MenuLayoutNode MenuLayoutNode; typedef enum { MENU_LAYOUT_NODE_ROOT, MENU_LAYOUT_NODE_PASSTHROUGH, MENU_LAYOUT_NODE_MENU, MENU_LAYOUT_NODE_APP_DIR, MENU_LAYOUT_NODE_DEFAULT_APP_DIRS, MENU_LAYOUT_NODE_DIRECTORY_DIR, MENU_LAYOUT_NODE_DEFAULT_DIRECTORY_DIRS, MENU_LAYOUT_NODE_DEFAULT_MERGE_DIRS, MENU_LAYOUT_NODE_NAME, MENU_LAYOUT_NODE_DIRECTORY, MENU_LAYOUT_NODE_ONLY_UNALLOCATED, MENU_LAYOUT_NODE_NOT_ONLY_UNALLOCATED, MENU_LAYOUT_NODE_INCLUDE, MENU_LAYOUT_NODE_EXCLUDE, MENU_LAYOUT_NODE_FILENAME, MENU_LAYOUT_NODE_CATEGORY, MENU_LAYOUT_NODE_ALL, MENU_LAYOUT_NODE_AND, MENU_LAYOUT_NODE_OR, MENU_LAYOUT_NODE_NOT, MENU_LAYOUT_NODE_MERGE_FILE, MENU_LAYOUT_NODE_MERGE_DIR, MENU_LAYOUT_NODE_LEGACY_DIR, MENU_LAYOUT_NODE_KDE_LEGACY_DIRS, MENU_LAYOUT_NODE_MOVE, MENU_LAYOUT_NODE_OLD, MENU_LAYOUT_NODE_NEW, MENU_LAYOUT_NODE_DELETED, MENU_LAYOUT_NODE_NOT_DELETED, MENU_LAYOUT_NODE_LAYOUT, MENU_LAYOUT_NODE_DEFAULT_LAYOUT, MENU_LAYOUT_NODE_MENUNAME, MENU_LAYOUT_NODE_SEPARATOR, MENU_LAYOUT_NODE_MERGE } MenuLayoutNodeType; typedef enum { MENU_MERGE_FILE_TYPE_PATH = 0, MENU_MERGE_FILE_TYPE_PARENT } MenuMergeFileType; typedef enum { MENU_LAYOUT_MERGE_NONE, MENU_LAYOUT_MERGE_MENUS, MENU_LAYOUT_MERGE_FILES, MENU_LAYOUT_MERGE_ALL } MenuLayoutMergeType; typedef enum { MENU_LAYOUT_VALUES_NONE = 0, MENU_LAYOUT_VALUES_SHOW_EMPTY = 1 << 0, MENU_LAYOUT_VALUES_INLINE_MENUS = 1 << 1, MENU_LAYOUT_VALUES_INLINE_LIMIT = 1 << 2, MENU_LAYOUT_VALUES_INLINE_HEADER = 1 << 3, MENU_LAYOUT_VALUES_INLINE_ALIAS = 1 << 4 } MenuLayoutValuesMask; typedef struct { MenuLayoutValuesMask mask; guint show_empty : 1; guint inline_menus : 1; guint inline_header : 1; guint inline_alias : 1; guint inline_limit; } MenuLayoutValues; MenuLayoutNode *menu_layout_load (const char *filename, const char *non_prefixed_basename, GError **error); MenuLayoutNode *menu_layout_node_new (MenuLayoutNodeType type); MenuLayoutNode *menu_layout_node_ref (MenuLayoutNode *node); void menu_layout_node_unref (MenuLayoutNode *node); MenuLayoutNodeType menu_layout_node_get_type (MenuLayoutNode *node); MenuLayoutNode *menu_layout_node_get_root (MenuLayoutNode *node); MenuLayoutNode *menu_layout_node_get_parent (MenuLayoutNode *node); MenuLayoutNode *menu_layout_node_get_children (MenuLayoutNode *node); MenuLayoutNode *menu_layout_node_get_next (MenuLayoutNode *node); void menu_layout_node_insert_before (MenuLayoutNode *node, MenuLayoutNode *new_sibling); void menu_layout_node_insert_after (MenuLayoutNode *node, MenuLayoutNode *new_sibling); void menu_layout_node_prepend_child (MenuLayoutNode *parent, MenuLayoutNode *new_child); void menu_layout_node_append_child (MenuLayoutNode *parent, MenuLayoutNode *new_child); void menu_layout_node_unlink (MenuLayoutNode *node); void menu_layout_node_steal (MenuLayoutNode *node); const char *menu_layout_node_get_content (MenuLayoutNode *node); void menu_layout_node_set_content (MenuLayoutNode *node, const char *content); char *menu_layout_node_get_content_as_path (MenuLayoutNode *node); const char *menu_layout_node_root_get_name (MenuLayoutNode *node); const char *menu_layout_node_root_get_basedir (MenuLayoutNode *node); const char *menu_layout_node_menu_get_name (MenuLayoutNode *node); EntryDirectoryList *menu_layout_node_menu_get_app_dirs (MenuLayoutNode *node); EntryDirectoryList *menu_layout_node_menu_get_directory_dirs (MenuLayoutNode *node); const char *menu_layout_node_move_get_old (MenuLayoutNode *node); const char *menu_layout_node_move_get_new (MenuLayoutNode *node); const char *menu_layout_node_legacy_dir_get_prefix (MenuLayoutNode *node); void menu_layout_node_legacy_dir_set_prefix (MenuLayoutNode *node, const char *prefix); MenuMergeFileType menu_layout_node_merge_file_get_type (MenuLayoutNode *node); void menu_layout_node_merge_file_set_type (MenuLayoutNode *node, MenuMergeFileType type); MenuLayoutMergeType menu_layout_node_merge_get_type (MenuLayoutNode *node); void menu_layout_node_default_layout_get_values (MenuLayoutNode *node, MenuLayoutValues *values); void menu_layout_node_menuname_get_values (MenuLayoutNode *node, MenuLayoutValues *values); typedef void (* MenuLayoutNodeEntriesChangedFunc) (MenuLayoutNode *node, gpointer user_data); void menu_layout_node_root_add_entries_monitor (MenuLayoutNode *node, MenuLayoutNodeEntriesChangedFunc callback, gpointer user_data); void menu_layout_node_root_remove_entries_monitor (MenuLayoutNode *node, MenuLayoutNodeEntriesChangedFunc callback, gpointer user_data); G_END_DECLS #endif /* __MENU_LAYOUT_H__ */ cinnamon-menus-3.6.0/libmenu/entry-directories.c0000664000175000017500000007624513173357262020674 0ustar fabiofabio/* * Copyright (C) 2002 - 2004 Red Hat, Inc. * * 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., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include #include "entry-directories.h" #include #include #include #include #include "menu-util.h" #include "menu-monitor.h" #include "canonicalize.h" typedef struct CachedDir CachedDir; typedef struct CachedDirMonitor CachedDirMonitor; struct EntryDirectory { CachedDir *dir; guint entry_type : 2; guint refcount : 24; }; struct EntryDirectoryList { int refcount; int length; GList *dirs; }; struct CachedDir { CachedDir *parent; char *name; GSList *entries; GSList *subdirs; GSList *retry_later_desktop_entries; 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_slist_free_full (dir->retry_later_desktop_entries, g_free); dir->retry_later_desktop_entries = 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; DesktopEntryResultCode code; entry = desktop_entry_new (path, &code); if (entry == NULL) { if (code == DESKTOP_ENTRY_LOAD_FAIL_APPINFO) { menu_verbose ("Adding %s to the retry list (mimeinfo.cache maybe isn't done getting updated yet\n", path); dir->retry_later_desktop_entries = g_slist_prepend (dir->retry_later_desktop_entries, g_strdup (path)); } 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; cached_dir_unref (subdir); dir->subdirs = g_slist_remove (dir->subdirs, 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; gboolean retry_changes = 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_strcmp0 (basename, "mimeinfo.cache") == 0) { /* The observed file notifies when a new desktop file is added * (but fails to load) go something like: * * NOTIFY: foo.desktop * NOTIFY: mimeinfo.cache.tempfile * NOTIFY: mimeinfo.cache.tempfile * NOTIFY: mimeinfo.cache * * Additionally, the failure is not upon trying to read the file, * but attempting to get its GAppInfo (g_desktop_app_info_new_from_filename() * in desktop-entries.c ln 277). If you jigger desktop_entry_load() around * and read the file as a keyfile *first*, it succeeds. If you then try * to run g_desktop_app_info_new_from_keyfile(), *then* it fails. * * The theory here is there is a race condition where app info (which includes * mimetype stuff) is unavailable because mimeinfo.cache is updated immediately * after the app is installed. * * What we do here is, when a desktop fails to load, we add it to a temporary * list. We wait until mimeinfo.cache changes, then retry that desktop file, * which succeeds this second time. * * Note: An alternative fix (presented more as a proof than a suggestion) is to * change line 151 in menu-monitor.c to use g_timeout_add_seconds, and delay * for one second. This also avoids the issue (but it remains a race condition). */ GSList *iter; menu_verbose ("mimeinfo changed, checking for failed entries\n"); for (iter = dir->retry_later_desktop_entries; iter != NULL; iter = iter->next) { const gchar *retry_path = iter->data; menu_verbose ("retrying %s\n", retry_path); char *retry_basename = g_path_get_basename (retry_path); if (cached_dir_update_entry (dir, retry_basename, retry_path)) retry_changes = TRUE; g_free (retry_basename); } g_slist_free_full (dir->retry_later_desktop_entries, g_free); dir->retry_later_desktop_entries = NULL; handled = retry_changes; } else /* 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, unless it's the mimeinfo.cache file changing */ if (retry_changes || (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) { 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 */ EntryDirectory * entry_directory_new (DesktopEntryType entry_type, const char *path) { EntryDirectory *ed; char *canonical; menu_verbose ("Loading entry directory \"%s\"\n", path); canonical = menu_canonicalize_file_name (path, FALSE); 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->entry_type = entry_type; ed->refcount = 1; g_free (canonical); return ed; } 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; 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) { retval = g_strdelimit (g_strdup (relative_path), "/", '-'); } 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; int 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) { 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; } } cinnamon-menus-3.6.0/libmenu/menu-util.c0000664000175000017500000003005413173357262017124 0ustar fabiofabio/* Random utility functions for menu code */ /* * Copyright (C) 2003 Red Hat, Inc. * * 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., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include #include "menu-util.h" #include #include #ifdef G_ENABLE_DEBUG static gboolean verbose = FALSE; static gboolean initted = FALSE; static inline gboolean menu_verbose_enabled (void) { if (!initted) { verbose = g_getenv ("MENU_VERBOSE") != NULL; initted = TRUE; } return verbose; } static int utf8_fputs (const char *str, FILE *f) { char *l; int ret; l = g_locale_from_utf8 (str, -1, NULL, NULL, NULL); if (l == NULL) ret = fputs (str, f); /* just print it anyway, better than nothing */ else ret = fputs (l, f); g_free (l); return ret; } void menu_verbose (const char *format, ...) { va_list args; char *str; if (!menu_verbose_enabled ()) return; va_start (args, format); str = g_strdup_vprintf (format, args); va_end (args); utf8_fputs (str, stderr); fflush (stderr); g_free (str); } static void append_to_string (MenuLayoutNode *node, gboolean onelevel, int depth, GString *str); static void append_spaces (GString *str, int depth) { while (depth > 0) { g_string_append_c (str, ' '); --depth; } } static void append_children (MenuLayoutNode *node, int depth, GString *str) { MenuLayoutNode *iter; iter = menu_layout_node_get_children (node); while (iter != NULL) { append_to_string (iter, FALSE, depth, str); iter = menu_layout_node_get_next (iter); } } static void append_simple_with_attr (MenuLayoutNode *node, int depth, const char *node_name, const char *attr_name, const char *attr_value, GString *str) { const char *content; append_spaces (str, depth); if ((content = menu_layout_node_get_content (node))) { char *escaped; escaped = g_markup_escape_text (content, -1); if (attr_name && attr_value) { char *attr_escaped; attr_escaped = g_markup_escape_text (attr_value, -1); g_string_append_printf (str, "<%s %s=\"%s\">%s\n", node_name, attr_name, attr_escaped, escaped, node_name); g_free (attr_escaped); } else { g_string_append_printf (str, "<%s>%s\n", node_name, escaped, node_name); } g_free (escaped); } else { if (attr_name && attr_value) { char *attr_escaped; attr_escaped = g_markup_escape_text (attr_value, -1); g_string_append_printf (str, "<%s %s=\"%s\"/>\n", node_name, attr_name, attr_escaped); g_free (attr_escaped); } else { g_string_append_printf (str, "<%s/>\n", node_name); } } } static void append_layout (MenuLayoutNode *node, int depth, const char *node_name, MenuLayoutValues *layout_values, GString *str) { const char *content; append_spaces (str, depth); if ((content = menu_layout_node_get_content (node))) { char *escaped; escaped = g_markup_escape_text (content, -1); g_string_append_printf (str, "<%s show_empty=\"%s\" inline=\"%s\" inline_header=\"%s\"" " inline_alias=\"%s\" inline_limit=\"%d\">%s\n", node_name, layout_values->show_empty ? "true" : "false", layout_values->inline_menus ? "true" : "false", layout_values->inline_header ? "true" : "false", layout_values->inline_alias ? "true" : "false", layout_values->inline_limit, escaped, node_name); g_free (escaped); } else { g_string_append_printf (str, "<%s show_empty=\"%s\" inline=\"%s\" inline_header=\"%s\"" " inline_alias=\"%s\" inline_limit=\"%d\"/>\n", node_name, layout_values->show_empty ? "true" : "false", layout_values->inline_menus ? "true" : "false", layout_values->inline_header ? "true" : "false", layout_values->inline_alias ? "true" : "false", layout_values->inline_limit); } } static void append_merge (MenuLayoutNode *node, int depth, const char *node_name, MenuLayoutMergeType merge_type, GString *str) { const char *merge_type_str; merge_type_str = NULL; switch (merge_type) { case MENU_LAYOUT_MERGE_NONE: merge_type_str = "none"; break; case MENU_LAYOUT_MERGE_MENUS: merge_type_str = "menus"; break; case MENU_LAYOUT_MERGE_FILES: merge_type_str = "files"; break; case MENU_LAYOUT_MERGE_ALL: merge_type_str = "all"; break; default: g_assert_not_reached (); break; } append_simple_with_attr (node, depth, node_name, "type", merge_type_str, str); } static void append_simple (MenuLayoutNode *node, int depth, const char *node_name, GString *str) { append_simple_with_attr (node, depth, node_name, NULL, NULL, str); } static void append_start (MenuLayoutNode *node, int depth, const char *node_name, GString *str) { append_spaces (str, depth); g_string_append_printf (str, "<%s>\n", node_name); } static void append_end (MenuLayoutNode *node, int depth, const char *node_name, GString *str) { append_spaces (str, depth); g_string_append_printf (str, "\n", node_name); } static void append_container (MenuLayoutNode *node, gboolean onelevel, int depth, const char *node_name, GString *str) { append_start (node, depth, node_name, str); if (!onelevel) { append_children (node, depth + 2, str); append_end (node, depth, node_name, str); } } static void append_to_string (MenuLayoutNode *node, gboolean onelevel, int depth, GString *str) { MenuLayoutValues layout_values; switch (menu_layout_node_get_type (node)) { case MENU_LAYOUT_NODE_ROOT: if (!onelevel) append_children (node, depth - 1, str); /* -1 to ignore depth of root */ else append_start (node, depth - 1, "Root", str); break; case MENU_LAYOUT_NODE_PASSTHROUGH: g_string_append (str, menu_layout_node_get_content (node)); g_string_append_c (str, '\n'); break; case MENU_LAYOUT_NODE_MENU: append_container (node, onelevel, depth, "Menu", str); break; case MENU_LAYOUT_NODE_APP_DIR: append_simple (node, depth, "AppDir", str); break; case MENU_LAYOUT_NODE_DEFAULT_APP_DIRS: append_simple (node, depth, "DefaultAppDirs", str); break; case MENU_LAYOUT_NODE_DIRECTORY_DIR: append_simple (node, depth, "DirectoryDir", str); break; case MENU_LAYOUT_NODE_DEFAULT_DIRECTORY_DIRS: append_simple (node, depth, "DefaultDirectoryDirs", str); break; case MENU_LAYOUT_NODE_DEFAULT_MERGE_DIRS: append_simple (node, depth, "DefaultMergeDirs", str); break; case MENU_LAYOUT_NODE_NAME: append_simple (node, depth, "Name", str); break; case MENU_LAYOUT_NODE_DIRECTORY: append_simple (node, depth, "Directory", str); break; case MENU_LAYOUT_NODE_ONLY_UNALLOCATED: append_simple (node, depth, "OnlyUnallocated", str); break; case MENU_LAYOUT_NODE_NOT_ONLY_UNALLOCATED: append_simple (node, depth, "NotOnlyUnallocated", str); break; case MENU_LAYOUT_NODE_INCLUDE: append_container (node, onelevel, depth, "Include", str); break; case MENU_LAYOUT_NODE_EXCLUDE: append_container (node, onelevel, depth, "Exclude", str); break; case MENU_LAYOUT_NODE_FILENAME: append_simple (node, depth, "Filename", str); break; case MENU_LAYOUT_NODE_CATEGORY: append_simple (node, depth, "Category", str); break; case MENU_LAYOUT_NODE_ALL: append_simple (node, depth, "All", str); break; case MENU_LAYOUT_NODE_AND: append_container (node, onelevel, depth, "And", str); break; case MENU_LAYOUT_NODE_OR: append_container (node, onelevel, depth, "Or", str); break; case MENU_LAYOUT_NODE_NOT: append_container (node, onelevel, depth, "Not", str); break; case MENU_LAYOUT_NODE_MERGE_FILE: { MenuMergeFileType type; type = menu_layout_node_merge_file_get_type (node); append_simple_with_attr (node, depth, "MergeFile", "type", type == MENU_MERGE_FILE_TYPE_PARENT ? "parent" : "path", str); break; } case MENU_LAYOUT_NODE_MERGE_DIR: append_simple (node, depth, "MergeDir", str); break; case MENU_LAYOUT_NODE_LEGACY_DIR: append_simple_with_attr (node, depth, "LegacyDir", "prefix", menu_layout_node_legacy_dir_get_prefix (node), str); break; case MENU_LAYOUT_NODE_KDE_LEGACY_DIRS: append_simple (node, depth, "KDELegacyDirs", str); break; case MENU_LAYOUT_NODE_MOVE: append_container (node, onelevel, depth, "Move", str); break; case MENU_LAYOUT_NODE_OLD: append_simple (node, depth, "Old", str); break; case MENU_LAYOUT_NODE_NEW: append_simple (node, depth, "New", str); break; case MENU_LAYOUT_NODE_DELETED: append_simple (node, depth, "Deleted", str); break; case MENU_LAYOUT_NODE_NOT_DELETED: append_simple (node, depth, "NotDeleted", str); break; case MENU_LAYOUT_NODE_LAYOUT: append_container (node, onelevel, depth, "Layout", str); break; case MENU_LAYOUT_NODE_DEFAULT_LAYOUT: menu_layout_node_default_layout_get_values (node, &layout_values); append_layout (node, depth, "DefaultLayout", &layout_values, str); break; case MENU_LAYOUT_NODE_MENUNAME: menu_layout_node_menuname_get_values (node, &layout_values); append_layout (node, depth, "MenuName", &layout_values, str); break; case MENU_LAYOUT_NODE_SEPARATOR: append_simple (node, depth, "Name", str); break; case MENU_LAYOUT_NODE_MERGE: append_merge (node, depth, "Merge", menu_layout_node_merge_get_type (node), str); break; default: g_assert_not_reached (); break; } } void menu_debug_print_layout (MenuLayoutNode *node, gboolean onelevel) { if (menu_verbose_enabled ()) { GString *str; str = g_string_new (NULL); append_to_string (node, onelevel, 0, str); utf8_fputs (str->str, stderr); fflush (stderr); g_string_free (str, TRUE); } } #endif /* G_ENABLE_DEBUG */ cinnamon-menus-3.6.0/libmenu/gmenu-tree.h0000664000175000017500000001602313173357262017262 0ustar fabiofabio/* * Copyright (C) 2004, 2011 Red Hat, Inc. * * 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., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef __GMENU_TREE_H__ #define __GMENU_TREE_H__ #ifndef GMENU_I_KNOW_THIS_IS_UNSTABLE #error "libgnome-menu should only be used if you understand that it's subject to frequent change, and is not supported as a fixed API/ABI or as part of the platform" #endif #include G_BEGIN_DECLS #define GMENU_TYPE_TREE (gmenu_tree_get_type ()) #define GMENU_TREE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GMENU_TYPE_TREE, GMenuTree)) #define GMENU_TREE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GMENU_TYPE_TREE, GMenuTreeClass)) #define GMENU_IS_TREE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GMENU_TYPE_TREE)) #define GMENU_IS_TREE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GMENU_TYPE_TREE)) #define GMENU_TREE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_DESKTOP_APP_INFO, GMenuTreeClass)) typedef struct _GMenuTree GMenuTree; typedef struct _GMenuTreeClass GMenuTreeClass; struct _GMenuTreeClass { GObjectClass parent_class; }; GType gmenu_tree_get_type (void) G_GNUC_CONST; typedef struct GMenuTreeIter GMenuTreeIter; typedef struct GMenuTreeDirectory GMenuTreeDirectory; typedef struct GMenuTreeEntry GMenuTreeEntry; typedef struct GMenuTreeSeparator GMenuTreeSeparator; typedef struct GMenuTreeHeader GMenuTreeHeader; typedef struct GMenuTreeAlias GMenuTreeAlias; typedef enum { GMENU_TREE_ITEM_INVALID = 0, GMENU_TREE_ITEM_DIRECTORY, GMENU_TREE_ITEM_ENTRY, GMENU_TREE_ITEM_SEPARATOR, GMENU_TREE_ITEM_HEADER, GMENU_TREE_ITEM_ALIAS } GMenuTreeItemType; GType gmenu_tree_iter_get_type (void); /* Explicitly skip item, it's a "hidden" base class */ GType gmenu_tree_directory_get_type (void); GType gmenu_tree_entry_get_type (void); GType gmenu_tree_separator_get_type (void); GType gmenu_tree_header_get_type (void); GType gmenu_tree_alias_get_type (void); typedef enum { GMENU_TREE_FLAGS_NONE = 0, GMENU_TREE_FLAGS_INCLUDE_EXCLUDED = 1 << 0, GMENU_TREE_FLAGS_INCLUDE_NODISPLAY = 1 << 1, GMENU_TREE_FLAGS_INCLUDE_UNALLOCATED = 1 << 2, /* leave some space for more include flags */ GMENU_TREE_FLAGS_SHOW_EMPTY = 1 << 8, GMENU_TREE_FLAGS_SHOW_ALL_SEPARATORS = 1 << 9, /* leave some space for more show flags */ GMENU_TREE_FLAGS_SORT_DISPLAY_NAME = 1 << 16 } GMenuTreeFlags; GType gmenu_tree_flags_get_type (void); #define GMENU_TYPE_TREE_FLAGS (gmenu_tree_flags_get_type ()) GMenuTree *gmenu_tree_new (const char *menu_basename, GMenuTreeFlags flags); GMenuTree *gmenu_tree_new_for_path (const char *menu_path, GMenuTreeFlags flags); gboolean gmenu_tree_load_sync (GMenuTree *tree, GError **error); const char *gmenu_tree_get_canonical_menu_path (GMenuTree *tree); GMenuTreeDirectory *gmenu_tree_get_root_directory (GMenuTree *tree); GMenuTreeDirectory *gmenu_tree_get_directory_from_path (GMenuTree *tree, const char *path); GMenuTreeEntry *gmenu_tree_get_entry_by_id (GMenuTree *tree, const char *id); gpointer gmenu_tree_item_ref (gpointer item); void gmenu_tree_item_unref (gpointer item); GMenuTreeDirectory *gmenu_tree_directory_get_parent (GMenuTreeDirectory *directory); const char *gmenu_tree_directory_get_name (GMenuTreeDirectory *directory); const char *gmenu_tree_directory_get_generic_name (GMenuTreeDirectory *directory); const char *gmenu_tree_directory_get_comment (GMenuTreeDirectory *directory); GIcon *gmenu_tree_directory_get_icon (GMenuTreeDirectory *directory); const char *gmenu_tree_directory_get_desktop_file_path (GMenuTreeDirectory *directory); const char *gmenu_tree_directory_get_menu_id (GMenuTreeDirectory *directory); GMenuTree *gmenu_tree_directory_get_tree (GMenuTreeDirectory *directory); gboolean gmenu_tree_directory_get_is_nodisplay (GMenuTreeDirectory *directory); GMenuTreeIter *gmenu_tree_directory_iter (GMenuTreeDirectory *directory); GMenuTreeIter *gmenu_tree_iter_ref (GMenuTreeIter *iter); void gmenu_tree_iter_unref (GMenuTreeIter *iter); GMenuTreeItemType gmenu_tree_iter_next (GMenuTreeIter *iter); GMenuTreeDirectory *gmenu_tree_iter_get_directory (GMenuTreeIter *iter); GMenuTreeEntry *gmenu_tree_iter_get_entry (GMenuTreeIter *iter); GMenuTreeHeader *gmenu_tree_iter_get_header (GMenuTreeIter *iter); GMenuTreeAlias *gmenu_tree_iter_get_alias (GMenuTreeIter *iter); GMenuTreeSeparator *gmenu_tree_iter_get_separator (GMenuTreeIter *iter); char *gmenu_tree_directory_make_path (GMenuTreeDirectory *directory, GMenuTreeEntry *entry); GDesktopAppInfo *gmenu_tree_entry_get_app_info (GMenuTreeEntry *entry); GMenuTreeDirectory *gmenu_tree_entry_get_parent (GMenuTreeEntry *entry); GMenuTree *gmenu_tree_entry_get_tree (GMenuTreeEntry *entry); const char *gmenu_tree_entry_get_desktop_file_path (GMenuTreeEntry *entry); const char *gmenu_tree_entry_get_desktop_file_id (GMenuTreeEntry *entry); gboolean gmenu_tree_entry_get_is_nodisplay_recurse (GMenuTreeEntry *entry); gboolean gmenu_tree_entry_get_is_excluded (GMenuTreeEntry *entry); gboolean gmenu_tree_entry_get_is_unallocated (GMenuTreeEntry *entry); GMenuTreeDirectory *gmenu_tree_header_get_directory (GMenuTreeHeader *header); GMenuTree *gmenu_tree_header_get_tree (GMenuTreeHeader *header); GMenuTreeDirectory *gmenu_tree_header_get_parent (GMenuTreeHeader *header); GMenuTreeDirectory *gmenu_tree_alias_get_directory (GMenuTreeAlias *alias); GMenuTreeItemType gmenu_tree_alias_get_aliased_item_type (GMenuTreeAlias *alias); GMenuTreeDirectory *gmenu_tree_alias_get_aliased_directory (GMenuTreeAlias *alias); GMenuTreeEntry *gmenu_tree_alias_get_aliased_entry (GMenuTreeAlias *alias); GMenuTree *gmenu_tree_alias_get_tree (GMenuTreeAlias *alias); GMenuTreeDirectory *gmenu_tree_alias_get_parent (GMenuTreeAlias *alias); GMenuTree *gmenu_tree_separator_get_tree (GMenuTreeSeparator *separator); GMenuTreeDirectory *gmenu_tree_separator_get_parent (GMenuTreeSeparator *separator); G_END_DECLS #endif /* __GMENU_TREE_H__ */ cinnamon-menus-3.6.0/libmenu/gmenu-tree.c0000664000175000017500000036323513173357262017267 0ustar fabiofabio/* -*- mode:c; c-file-style: "gnu"; indent-tabs-mode: nil -*- * Copyright (C) 2003, 2004, 2011 Red Hat, Inc. * * 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., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include #include "gmenu-tree.h" #include #include #include "menu-layout.h" #include "menu-monitor.h" #include "menu-util.h" #include "canonicalize.h" /* private */ typedef struct GMenuTreeItem GMenuTreeItem; #define GMENU_TREE_ITEM(i) ((GMenuTreeItem *)(i)) #define GMENU_TREE_DIRECTORY(i) ((GMenuTreeDirectory *)(i)) #define GMENU_TREE_ENTRY(i) ((GMenuTreeEntry *)(i)) #define GMENU_TREE_SEPARATOR(i) ((GMenuTreeSeparator *)(i)) #define GMENU_TREE_HEADER(i) ((GMenuTreeHeader *)(i)) #define GMENU_TREE_ALIAS(i) ((GMenuTreeAlias *)(i)) enum { PROP_0, PROP_MENU_BASENAME, PROP_MENU_PATH, PROP_FLAGS }; /* Signals */ enum { CHANGED, LAST_SIGNAL }; static guint gmenu_tree_signals [LAST_SIGNAL] = { 0 }; struct _GMenuTree { GObject parent_instance; char *basename; char *non_prefixed_basename; char *path; char *canonical_path; GMenuTreeFlags flags; GSList *menu_file_monitors; MenuLayoutNode *layout; GMenuTreeDirectory *root; GHashTable *entries_by_id; guint canonical : 1; guint loaded : 1; }; G_DEFINE_TYPE (GMenuTree, gmenu_tree, G_TYPE_OBJECT) struct GMenuTreeItem { volatile gint refcount; GMenuTreeItemType type; GMenuTreeDirectory *parent; GMenuTree *tree; }; struct GMenuTreeIter { volatile gint refcount; GMenuTreeItem *item; GSList *contents; GSList *contents_iter; }; struct GMenuTreeDirectory { GMenuTreeItem 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 GMenuTreeEntry { GMenuTreeItem item; DesktopEntry *desktop_entry; char *desktop_file_id; guint is_excluded : 1; guint is_unallocated : 1; }; struct GMenuTreeSeparator { GMenuTreeItem item; }; struct GMenuTreeHeader { GMenuTreeItem item; GMenuTreeDirectory *directory; }; struct GMenuTreeAlias { GMenuTreeItem item; GMenuTreeDirectory *directory; GMenuTreeItem *aliased_item; }; static gboolean gmenu_tree_load_layout (GMenuTree *tree, GError **error); static void gmenu_tree_force_reload (GMenuTree *tree); static gboolean gmenu_tree_build_from_layout (GMenuTree *tree, GError **error); static void gmenu_tree_force_rebuild (GMenuTree *tree); static void gmenu_tree_resolve_files (GMenuTree *tree, GHashTable *loaded_menu_files, MenuLayoutNode *layout); static void gmenu_tree_force_recanonicalize (GMenuTree *tree); static void gmenu_tree_invoke_monitors (GMenuTree *tree); static void gmenu_tree_item_unref_and_unset_parent (gpointer itemp); 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, GMenuTree *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"); gmenu_tree_force_recanonicalize (tree); gmenu_tree_invoke_monitors (tree); } } static void handle_menu_file_changed (MenuMonitor *monitor, MenuMonitorEvent event, const char *path, GMenuTree *tree) { menu_verbose ("\"%s\" %s, marking tree for recanicalization\n", path, event == MENU_MONITOR_EVENT_CREATED ? "created" : event == MENU_MONITOR_EVENT_CHANGED ? "changed" : "deleted"); gmenu_tree_force_recanonicalize (tree); gmenu_tree_invoke_monitors (tree); } static void handle_menu_file_directory_changed (MenuMonitor *monitor, MenuMonitorEvent event, const char *path, GMenuTree *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"); gmenu_tree_force_recanonicalize (tree); gmenu_tree_invoke_monitors (tree); } static void gmenu_tree_add_menu_file_monitor (GMenuTree *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, GMenuTree *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 gmenu_tree_remove_menu_file_monitors (GMenuTree *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 (GMenuTree *tree, const char *path) { tree->canonical_path = menu_canonicalize_file_name (path, FALSE); if (tree->canonical_path) { tree->canonical = TRUE; gmenu_tree_add_menu_file_monitor (tree, tree->canonical_path, MENU_FILE_MONITOR_FILE); } else { gmenu_tree_add_menu_file_monitor (tree, path, MENU_FILE_MONITOR_NONEXISTENT_FILE); } return tree->canonical; } static gboolean canonicalize_basename_with_config_dir (GMenuTree *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 (GMenuTree *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 char * prefix_menu_name (const char *orig_name) { char *prefix; prefix = (char *) g_getenv ("XDG_MENU_PREFIX"); if (prefix == NULL) prefix = "gnome-"; return g_strconcat (prefix, orig_name, NULL); } static gboolean gmenu_tree_canonicalize_path (GMenuTree *tree, GError **error) { const char *menu_file = NULL; if (tree->canonical) return TRUE; g_assert (tree->canonical_path == NULL); gmenu_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) xdg_menu_prefix = "gnome-"; 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, "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 ("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 gmenu_tree_force_recanonicalize (GMenuTree *tree) { gmenu_tree_remove_menu_file_monitors (tree); if (tree->canonical) { gmenu_tree_force_reload (tree); g_free (tree->canonical_path); tree->canonical_path = NULL; tree->canonical = FALSE; } } /** * gmenu_tree_new: * @menu_basename: Basename of menu file * @flags: Flags controlling menu content * * Returns: (transfer full): A new #GMenuTree instance */ GMenuTree * gmenu_tree_new (const char *menu_basename, GMenuTreeFlags flags) { g_return_val_if_fail (menu_basename != NULL, NULL); return g_object_new (GMENU_TYPE_TREE, "menu-basename", menu_basename, "flags", flags, NULL); } /** * gmenu_tree_new_fo_path: * @menu_path: Path of menu file * @flags: Flags controlling menu content * * Returns: (transfer full): A new #GMenuTree instance */ GMenuTree * gmenu_tree_new_for_path (const char *menu_path, GMenuTreeFlags flags) { g_return_val_if_fail (menu_path != NULL, NULL); return g_object_new (GMENU_TYPE_TREE, "menu-path", menu_path, "flags", flags, NULL); } static GObject * gmenu_tree_constructor (GType type, guint n_construct_properties, GObjectConstructParam *construct_properties) { GObject *obj; GMenuTree *self; obj = G_OBJECT_CLASS (gmenu_tree_parent_class)->constructor (type, n_construct_properties, construct_properties); /* If GMenuTree:menu-path is set, then we should make sure that * GMenuTree: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 = GMENU_TREE (obj); if (self->path != NULL) g_object_set (self, "menu-basename", NULL, NULL); return obj; } static void gmenu_tree_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { GMenuTree *self = GMENU_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 gmenu_tree_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { GMenuTree *self = GMENU_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 gmenu_tree_finalize (GObject *object) { GMenuTree *tree = GMENU_TREE (object); gmenu_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; G_OBJECT_CLASS (gmenu_tree_parent_class)->finalize (object); } static void gmenu_tree_init (GMenuTree *self) { self->entries_by_id = g_hash_table_new (g_str_hash, g_str_equal); } static void gmenu_tree_class_init (GMenuTreeClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->constructor = gmenu_tree_constructor; gobject_class->get_property = gmenu_tree_get_property; gobject_class->set_property = gmenu_tree_set_property; gobject_class->finalize = gmenu_tree_finalize; /** * GMenuTree: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)); /** * GMenuTree:menu-path: * * The full path of the menu file. If set, GMenuTree: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)); /** * GMenuTree:flags: * * Flags controlling the content of the menu. */ g_object_class_install_property (gobject_class, PROP_FLAGS, g_param_spec_flags ("flags", "", "", GMENU_TYPE_TREE_FLAGS, GMENU_TREE_FLAGS_NONE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); /** * GMenuTree:changed: * * This signal is emitted when applications are added, removed, or * upgraded. But note the new data will only be visible after * gmenu_tree_load_sync() or a variant thereof is invoked. */ gmenu_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); } /** * gmenu_tree_get_canonical_menu_path: * @tree: a #GMenuTree * * This function is only available if the tree has been loaded via * gmenu_tree_load_sync() or a variant thereof. * * Returns: The absolute and canonicalized path to the loaded menu file */ const char * gmenu_tree_get_canonical_menu_path (GMenuTree *tree) { g_return_val_if_fail (GMENU_IS_TREE (tree), NULL); g_return_val_if_fail (tree->loaded, NULL); return tree->canonical_path; } /** * gmenu_tree_load_sync: * @tree: a #GMenuTree * @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 gmenu_tree_load_sync (GMenuTree *tree, GError **error) { GError *local_error = NULL; if (tree->loaded) return TRUE; if (!gmenu_tree_build_from_layout (tree, &local_error)) { if (local_error) g_propagate_error (error, local_error); return FALSE; } tree->loaded = TRUE; return TRUE; } /** * gmenu_tree_get_root_directory: * @tree: a #GMenuTree * * Get the root directory; you must have loaded the tree first (at * least once) via gmenu_tree_load_sync() or a variant thereof. * * Returns: (transfer full): Root of the tree */ GMenuTreeDirectory * gmenu_tree_get_root_directory (GMenuTree *tree) { g_return_val_if_fail (tree != NULL, NULL); g_return_val_if_fail (tree->loaded, NULL); return gmenu_tree_item_ref (tree->root); } static GMenuTreeDirectory * find_path (GMenuTreeDirectory *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, slash - path); path = slash + 1; } else { name = path; path = NULL; } tmp = directory->contents; while (tmp != NULL) { GMenuTreeItem *item = tmp->data; if (item->type != GMENU_TREE_ITEM_DIRECTORY) { tmp = tmp->next; continue; } if (!strcmp (name, GMENU_TREE_DIRECTORY (item)->name)) { g_free (freeme); if (path) return find_path (GMENU_TREE_DIRECTORY (item), path); else return GMENU_TREE_DIRECTORY (item); } tmp = tmp->next; } g_free (freeme); return NULL; } GMenuTreeDirectory * gmenu_tree_get_directory_from_path (GMenuTree *tree, const char *path) { GMenuTreeDirectory *root; GMenuTreeDirectory *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 = gmenu_tree_get_root_directory (tree))) return NULL; directory = find_path (root, path); gmenu_tree_item_unref (root); return directory ? gmenu_tree_item_ref (directory) : NULL; } /** * gmenu_tree_get_entry_by_id: * @tree: a #GMenuTree * @id: a desktop file ID * * Look up the entry corresponding to the given "desktop file id". * * Returns: (transfer full): A newly referenced #GMenuTreeEntry, or %NULL if none */ GMenuTreeEntry * gmenu_tree_get_entry_by_id (GMenuTree *tree, const char *id) { GMenuTreeEntry *entry; g_return_val_if_fail (tree->loaded, NULL); entry = g_hash_table_lookup (tree->entries_by_id, id); if (entry != NULL) gmenu_tree_item_ref (entry); return entry; } static void gmenu_tree_invoke_monitors (GMenuTree *tree) { g_signal_emit (tree, gmenu_tree_signals[CHANGED], 0); } static GMenuTreeDirectory * get_parent (GMenuTreeItem *item) { g_return_val_if_fail (item != NULL, NULL); return item->parent ? gmenu_tree_item_ref (item->parent) : NULL; } /** * gmenu_tree_directory_get_parent: * @directory: a #GMenuTreeDirectory * * Returns: (transfer full): The parent directory, or %NULL if none */ GMenuTreeDirectory * gmenu_tree_directory_get_parent (GMenuTreeDirectory *directory) { return get_parent ((GMenuTreeItem *)directory); } /** * gmenu_tree_entry_get_parent: * @entry: a #GMenuTreeEntry * * Returns: (transfer full): The parent directory, or %NULL if none */ GMenuTreeDirectory * gmenu_tree_entry_get_parent (GMenuTreeEntry *entry) { return get_parent ((GMenuTreeItem *)entry); } /** * gmenu_tree_alias_get_parent: * @alias: a #GMenuTreeAlias * * Returns: (transfer full): The parent directory, or %NULL if none */ GMenuTreeDirectory * gmenu_tree_alias_get_parent (GMenuTreeAlias *alias) { return get_parent ((GMenuTreeItem *)alias); } /** * gmenu_tree_header_get_parent: * @header: a #GMenuTreeHeader * * Returns: (transfer full): The parent directory, or %NULL if none */ GMenuTreeDirectory * gmenu_tree_header_get_parent (GMenuTreeHeader *header) { return get_parent ((GMenuTreeItem *)header); } /** * gmenu_tree_separator_get_parent: * @separator: a #GMenuTreeSeparator * * Returns: (transfer full): The parent directory, or %NULL if none */ GMenuTreeDirectory * gmenu_tree_separator_get_parent (GMenuTreeSeparator *separator) { return get_parent ((GMenuTreeItem *)separator); } static void gmenu_tree_item_set_parent (GMenuTreeItem *item, GMenuTreeDirectory *parent) { g_return_if_fail (item != NULL); item->parent = parent; } /** * gmenu_tree_iter_ref: (skip) * @iter: iter * * Increment the reference count of @iter */ GMenuTreeIter * gmenu_tree_iter_ref (GMenuTreeIter *iter) { g_atomic_int_inc (&iter->refcount); return iter; } /** * gmenu_tree_iter_unref: (skip) * @iter: iter * * Decrement the reference count of @iter */ void gmenu_tree_iter_unref (GMenuTreeIter *iter) { if (!g_atomic_int_dec_and_test (&iter->refcount)) return; g_slist_foreach (iter->contents, (GFunc)gmenu_tree_item_unref, NULL); g_slist_free (iter->contents); g_slice_free (GMenuTreeIter, iter); } /** * gmenu_tree_directory_iter: * @directory: directory * * Returns: (transfer full): A new iterator over the directory contents */ GMenuTreeIter * gmenu_tree_directory_iter (GMenuTreeDirectory *directory) { GMenuTreeIter *iter; g_return_val_if_fail (directory != NULL, NULL); iter = g_slice_new0 (GMenuTreeIter); iter->refcount = 1; iter->contents = g_slist_copy (directory->contents); iter->contents_iter = iter->contents; g_slist_foreach (iter->contents, (GFunc) gmenu_tree_item_ref, NULL); return iter; } /** * gmenu_tree_iter_next: * @iter: iter * * Change the iterator to the next item, and return its type. If * there are no more items, %GMENU_TREE_ITEM_INVALID is returned. * * Returns: The type of the next item that can be retrived from the iterator */ GMenuTreeItemType gmenu_tree_iter_next (GMenuTreeIter *iter) { g_return_val_if_fail (iter != NULL, GMENU_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 GMENU_TREE_ITEM_INVALID; } /** * gmenu_tree_iter_get_directory: * @iter: iter * * This method may only be called if gmenu_tree_iter_next() * returned GMENU_TREE_ITEM_DIRECTORY. * * Returns: (transfer full): A directory */ GMenuTreeDirectory * gmenu_tree_iter_get_directory (GMenuTreeIter *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 == GMENU_TREE_ITEM_DIRECTORY, NULL); return (GMenuTreeDirectory*)gmenu_tree_item_ref (iter->item); } /** * gmenu_tree_iter_get_entry: * @iter: iter * * This method may only be called if gmenu_tree_iter_next() * returned GMENU_TREE_ITEM_ENTRY. * * Returns: (transfer full): An entry */ GMenuTreeEntry * gmenu_tree_iter_get_entry (GMenuTreeIter *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 == GMENU_TREE_ITEM_ENTRY, NULL); return (GMenuTreeEntry*)gmenu_tree_item_ref (iter->item); } /** * gmenu_tree_iter_get_header: * @iter: iter * * This method may only be called if gmenu_tree_iter_next() * returned GMENU_TREE_ITEM_HEADER. * * Returns: (transfer full): A header */ GMenuTreeHeader * gmenu_tree_iter_get_header (GMenuTreeIter *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 == GMENU_TREE_ITEM_HEADER, NULL); return (GMenuTreeHeader*)gmenu_tree_item_ref (iter->item); } /** * gmenu_tree_iter_get_alias: * @iter: iter * * This method may only be called if gmenu_tree_iter_next() * returned GMENU_TREE_ITEM_ALIAS. * * Returns: (transfer full): An alias */ GMenuTreeAlias * gmenu_tree_iter_get_alias (GMenuTreeIter *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 == GMENU_TREE_ITEM_ALIAS, NULL); return (GMenuTreeAlias*)gmenu_tree_item_ref (iter->item); } /** * gmenu_tree_iter_get_separator: * @iter: iter * * This method may only be called if gmenu_tree_iter_next() * returned #GMENU_TREE_ITEM_SEPARATOR. * * Returns: (transfer full): A separator */ GMenuTreeSeparator * gmenu_tree_iter_get_separator (GMenuTreeIter *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 == GMENU_TREE_ITEM_SEPARATOR, NULL); return (GMenuTreeSeparator*)gmenu_tree_item_ref (iter->item); } const char * gmenu_tree_directory_get_name (GMenuTreeDirectory *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 * gmenu_tree_directory_get_generic_name (GMenuTreeDirectory *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 * gmenu_tree_directory_get_comment (GMenuTreeDirectory *directory) { g_return_val_if_fail (directory != NULL, NULL); if (!directory->directory_entry) return NULL; return desktop_entry_get_comment (directory->directory_entry); } /** * gmenu_tree_directory_get_icon: * @directory: a #GMenuTreeDirectory * * Gets the icon for the directory. * * Returns: (transfer none): The #GIcon for this directory */ GIcon * gmenu_tree_directory_get_icon (GMenuTreeDirectory *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 * gmenu_tree_directory_get_desktop_file_path (GMenuTreeDirectory *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 * gmenu_tree_directory_get_menu_id (GMenuTreeDirectory *directory) { g_return_val_if_fail (directory != NULL, NULL); return directory->name; } gboolean gmenu_tree_directory_get_is_nodisplay (GMenuTreeDirectory *directory) { g_return_val_if_fail (directory != NULL, FALSE); return directory->is_nodisplay; } /** * gmenu_tree_directory_get_tree: * @directory: A #GMenuTreeDirectory * * Grab the tree associated with a #GMenuTreeItem. * * Returns: (transfer full): The #GMenuTree */ GMenuTree * gmenu_tree_directory_get_tree (GMenuTreeDirectory *directory) { g_return_val_if_fail (directory != NULL, NULL); return g_object_ref (directory->item.tree); } static void append_directory_path (GMenuTreeDirectory *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 * gmenu_tree_directory_make_path (GMenuTreeDirectory *directory, GMenuTreeEntry *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); } /** * gmenu_tree_entry_get_app_info: * @entry: a #GMenuTreeEntry * * Returns: (transfer none): The #GDesktopAppInfo for this entry */ GDesktopAppInfo * gmenu_tree_entry_get_app_info (GMenuTreeEntry *entry) { g_return_val_if_fail (entry != NULL, NULL); return desktop_entry_get_app_info (entry->desktop_entry); } const char * gmenu_tree_entry_get_desktop_file_path (GMenuTreeEntry *entry) { g_return_val_if_fail (entry != NULL, NULL); return desktop_entry_get_path (entry->desktop_entry); } const char * gmenu_tree_entry_get_desktop_file_id (GMenuTreeEntry *entry) { g_return_val_if_fail (entry != NULL, NULL); return entry->desktop_file_id; } gboolean gmenu_tree_entry_get_is_nodisplay_recurse (GMenuTreeEntry *entry) { GMenuTreeDirectory *directory; GDesktopAppInfo *app_info; g_return_val_if_fail (entry != NULL, FALSE); app_info = gmenu_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 gmenu_tree_entry_get_is_excluded (GMenuTreeEntry *entry) { g_return_val_if_fail (entry != NULL, FALSE); return entry->is_excluded; } gboolean gmenu_tree_entry_get_is_unallocated (GMenuTreeEntry *entry) { g_return_val_if_fail (entry != NULL, FALSE); return entry->is_unallocated; } /** * gmenu_tree_entry_get_tree: * @entry: A #GMenuTreeEntry * * Grab the tree associated with a #GMenuTreeEntry. * * Returns: (transfer full): The #GMenuTree */ GMenuTree * gmenu_tree_entry_get_tree (GMenuTreeEntry *entry) { g_return_val_if_fail (entry != NULL, NULL); return g_object_ref (entry->item.tree); } GMenuTreeDirectory * gmenu_tree_header_get_directory (GMenuTreeHeader *header) { g_return_val_if_fail (header != NULL, NULL); return gmenu_tree_item_ref (header->directory); } /** * gmenu_tree_header_get_tree: * @header: A #GMenuTreeHeader * * Grab the tree associated with a #GMenuTreeHeader. * * Returns: (transfer full): The #GMenuTree */ GMenuTree * gmenu_tree_header_get_tree (GMenuTreeHeader *header) { g_return_val_if_fail (header != NULL, NULL); return g_object_ref (header->item.tree); } GMenuTreeItemType gmenu_tree_alias_get_aliased_item_type (GMenuTreeAlias *alias) { g_return_val_if_fail (alias != NULL, GMENU_TREE_ITEM_INVALID); g_assert (alias->aliased_item != NULL); return alias->aliased_item->type; } GMenuTreeDirectory * gmenu_tree_alias_get_directory (GMenuTreeAlias *alias) { g_return_val_if_fail (alias != NULL, NULL); return gmenu_tree_item_ref (alias->directory); } /** * gmenu_tree_alias_get_tree: * @alias: A #GMenuTreeAlias * * Grab the tree associated with a #GMenuTreeAlias. * * Returns: (transfer full): The #GMenuTree */ GMenuTree * gmenu_tree_alias_get_tree (GMenuTreeAlias *alias) { g_return_val_if_fail (alias != NULL, NULL); return g_object_ref (alias->item.tree); } /** * gmenu_tree_separator_get_tree: * @separator: A #GMenuTreeSeparator * * Grab the tree associated with a #GMenuTreeSeparator. * * Returns: (transfer full): The #GMenuTree */ GMenuTree * gmenu_tree_separator_get_tree (GMenuTreeSeparator *separator) { g_return_val_if_fail (separator != NULL, NULL); return g_object_ref (separator->item.tree); } /** * gmenu_tree_alias_get_aliased_directory: * @alias: alias * * Returns: (transfer full): The aliased directory entry */ GMenuTreeDirectory * gmenu_tree_alias_get_aliased_directory (GMenuTreeAlias *alias) { g_return_val_if_fail (alias != NULL, NULL); g_return_val_if_fail (alias->aliased_item->type == GMENU_TREE_ITEM_DIRECTORY, NULL); return (GMenuTreeDirectory *) gmenu_tree_item_ref (alias->aliased_item); } /** * gmenu_tree_alias_get_aliased_entry: * @alias: alias * * Returns: (transfer full): The aliased entry */ GMenuTreeEntry * gmenu_tree_alias_get_aliased_entry (GMenuTreeAlias *alias) { g_return_val_if_fail (alias != NULL, NULL); g_return_val_if_fail (alias->aliased_item->type == GMENU_TREE_ITEM_ENTRY, NULL); return (GMenuTreeEntry *) gmenu_tree_item_ref (alias->aliased_item); } static GMenuTreeDirectory * gmenu_tree_directory_new (GMenuTree *tree, GMenuTreeDirectory *parent, const char *name) { GMenuTreeDirectory *retval; retval = g_slice_new0 (GMenuTreeDirectory); retval->item.type = GMENU_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 gmenu_tree_directory_finalize (GMenuTreeDirectory *directory) { g_assert (directory->item.refcount == 0); g_slist_foreach (directory->contents, (GFunc) gmenu_tree_item_unref_and_unset_parent, NULL); g_slist_free (directory->contents); directory->contents = NULL; g_slist_foreach (directory->default_layout_info, (GFunc) menu_layout_node_unref, NULL); g_slist_free (directory->default_layout_info); directory->default_layout_info = NULL; g_slist_foreach (directory->layout_info, (GFunc) menu_layout_node_unref, NULL); g_slist_free (directory->layout_info); directory->layout_info = NULL; g_slist_foreach (directory->subdirs, (GFunc) gmenu_tree_item_unref_and_unset_parent, NULL); g_slist_free (directory->subdirs); directory->subdirs = NULL; g_slist_foreach (directory->entries, (GFunc) gmenu_tree_item_unref_and_unset_parent, NULL); g_slist_free (directory->entries); 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 (GMenuTreeDirectory, directory); } static GMenuTreeSeparator * gmenu_tree_separator_new (GMenuTreeDirectory *parent) { GMenuTreeSeparator *retval; retval = g_slice_new0 (GMenuTreeSeparator); retval->item.type = GMENU_TREE_ITEM_SEPARATOR; retval->item.parent = parent; retval->item.refcount = 1; retval->item.tree = parent->item.tree; return retval; } static void gmenu_tree_separator_finalize (GMenuTreeSeparator *separator) { g_assert (separator->item.refcount == 0); g_slice_free (GMenuTreeSeparator, separator); } static GMenuTreeHeader * gmenu_tree_header_new (GMenuTreeDirectory *parent, GMenuTreeDirectory *directory) { GMenuTreeHeader *retval; retval = g_slice_new0 (GMenuTreeHeader); retval->item.type = GMENU_TREE_ITEM_HEADER; retval->item.parent = parent; retval->item.refcount = 1; retval->item.tree = parent->item.tree; retval->directory = gmenu_tree_item_ref (directory); gmenu_tree_item_set_parent (GMENU_TREE_ITEM (retval->directory), NULL); return retval; } static void gmenu_tree_header_finalize (GMenuTreeHeader *header) { g_assert (header->item.refcount == 0); if (header->directory != NULL) gmenu_tree_item_unref (header->directory); header->directory = NULL; g_slice_free (GMenuTreeHeader, header); } static GMenuTreeAlias * gmenu_tree_alias_new (GMenuTreeDirectory *parent, GMenuTreeDirectory *directory, GMenuTreeItem *item) { GMenuTreeAlias *retval; retval = g_slice_new0 (GMenuTreeAlias); retval->item.type = GMENU_TREE_ITEM_ALIAS; retval->item.parent = parent; retval->item.refcount = 1; retval->item.tree = parent->item.tree; retval->directory = gmenu_tree_item_ref (directory); if (item->type != GMENU_TREE_ITEM_ALIAS) retval->aliased_item = gmenu_tree_item_ref (item); else { GMenuTreeAlias *alias = GMENU_TREE_ALIAS (item); retval->aliased_item = gmenu_tree_item_ref (alias->aliased_item); } gmenu_tree_item_set_parent (GMENU_TREE_ITEM (retval->directory), NULL); gmenu_tree_item_set_parent (retval->aliased_item, NULL); return retval; } static void gmenu_tree_alias_finalize (GMenuTreeAlias *alias) { g_assert (alias->item.refcount == 0); if (alias->directory != NULL) gmenu_tree_item_unref (alias->directory); alias->directory = NULL; if (alias->aliased_item != NULL) gmenu_tree_item_unref (alias->aliased_item); alias->aliased_item = NULL; g_slice_free (GMenuTreeAlias, alias); } static GMenuTreeEntry * gmenu_tree_entry_new (GMenuTreeDirectory *parent, DesktopEntry *desktop_entry, const char *desktop_file_id, gboolean is_excluded, gboolean is_unallocated) { GMenuTreeEntry *retval; retval = g_slice_new0 (GMenuTreeEntry); retval->item.type = GMENU_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 gmenu_tree_entry_finalize (GMenuTreeEntry *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 (GMenuTreeEntry, entry); } static int gmenu_tree_entry_compare_by_id (GMenuTreeItem *a, GMenuTreeItem *b) { if (a->type == GMENU_TREE_ITEM_ALIAS) a = GMENU_TREE_ALIAS (a)->aliased_item; if (b->type == GMENU_TREE_ITEM_ALIAS) b = GMENU_TREE_ALIAS (b)->aliased_item; return strcmp (GMENU_TREE_ENTRY (a)->desktop_file_id, GMENU_TREE_ENTRY (b)->desktop_file_id); } /** * gmenu_tree_item_ref: * @item: a #GMenuTreeItem * * Returns: (transfer full): The same @item, or %NULL if @item is not a valid #GMenuTreeItem */ gpointer gmenu_tree_item_ref (gpointer itemp) { GMenuTreeItem *item; item = (GMenuTreeItem *) 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 gmenu_tree_item_unref (gpointer itemp) { GMenuTreeItem *item; item = (GMenuTreeItem *) 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 GMENU_TREE_ITEM_DIRECTORY: gmenu_tree_directory_finalize (GMENU_TREE_DIRECTORY (item)); break; case GMENU_TREE_ITEM_ENTRY: gmenu_tree_entry_finalize (GMENU_TREE_ENTRY (item)); break; case GMENU_TREE_ITEM_SEPARATOR: gmenu_tree_separator_finalize (GMENU_TREE_SEPARATOR (item)); break; case GMENU_TREE_ITEM_HEADER: gmenu_tree_header_finalize (GMENU_TREE_HEADER (item)); break; case GMENU_TREE_ITEM_ALIAS: gmenu_tree_alias_finalize (GMENU_TREE_ALIAS (item)); break; default: g_assert_not_reached (); break; } } } static void gmenu_tree_item_unref_and_unset_parent (gpointer itemp) { GMenuTreeItem *item; item = (GMenuTreeItem *) itemp; g_return_if_fail (item != NULL); gmenu_tree_item_set_parent (item, NULL); gmenu_tree_item_unref (item); } static inline const char * gmenu_tree_item_compare_get_name_helper (GMenuTreeItem *item, GMenuTreeFlags flags) { const char *name; name = NULL; switch (item->type) { case GMENU_TREE_ITEM_DIRECTORY: if (GMENU_TREE_DIRECTORY (item)->directory_entry) name = desktop_entry_get_name (GMENU_TREE_DIRECTORY (item)->directory_entry); else name = GMENU_TREE_DIRECTORY (item)->name; break; case GMENU_TREE_ITEM_ENTRY: if (flags & GMENU_TREE_FLAGS_SORT_DISPLAY_NAME) name = g_app_info_get_display_name (G_APP_INFO (gmenu_tree_entry_get_app_info (GMENU_TREE_ENTRY (item)))); else name = desktop_entry_get_name (GMENU_TREE_ENTRY (item)->desktop_entry); break; case GMENU_TREE_ITEM_ALIAS: { GMenuTreeItem *dir; dir = GMENU_TREE_ITEM (GMENU_TREE_ALIAS (item)->directory); name = gmenu_tree_item_compare_get_name_helper (dir, flags); } break; case GMENU_TREE_ITEM_SEPARATOR: case GMENU_TREE_ITEM_HEADER: default: g_assert_not_reached (); break; } return name; } static int gmenu_tree_item_compare (GMenuTreeItem *a, GMenuTreeItem *b, gpointer flags_p) { const char *name_a; const char *name_b; GMenuTreeFlags flags; flags = GPOINTER_TO_INT (flags_p); name_a = gmenu_tree_item_compare_get_name_helper (a, flags); name_b = gmenu_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 (GMenuTree *tree, GHashTable *loaded_menu_files, MenuLayoutNode *where, MenuLayoutNode *from) { MenuLayoutNode *insert_after; MenuLayoutNode *menu_child; MenuLayoutNode *from_child; gmenu_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 */ 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 (GMenuTree *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 = menu_canonicalize_file_name (filename, FALSE); if (canonical == NULL) { if (add_monitor) gmenu_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) gmenu_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 (GMenuTree *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 = menu_canonicalize_file_name (dirname, FALSE); 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 (GMenuTree *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 (GMenuTree *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 = menu_canonicalize_file_name (basedir, FALSE); 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, "applications.menu") == 0) { char *prefixed_basename; prefixed_basename = prefix_menu_name (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 (GMenuTree *tree, GHashTable *loaded_menu_files, const char *dirname, MenuLayoutNode *where) { GDir *dir; const char *menu_file; menu_verbose ("Loading merge dir \"%s\"\n", dirname); gmenu_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 (GMenuTree *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 (GMenuTree *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 (GMenuTree *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 (GMenuTree *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 %s in \n", dirname); g_free (dirname); return tmp; } static void resolve_default_app_dirs (GMenuTree *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 (GMenuTree *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 %s in \n", dirname); g_free (dirname); return tmp; } static void resolve_default_directory_dirs (GMenuTree *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) { 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 (GMenuTree *tree, GHashTable *loaded_menu_files, MenuLayoutNode *layout) { MenuLayoutNode *root; const char *menu_name; char *merge_name; const char * const *system_config_dirs; int i; 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 gmenu_tree_resolve_files (GMenuTree *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: menu_verbose ("Ignoring obsolete legacy dir"); break; case MENU_LAYOUT_NODE_KDE_LEGACY_DIRS: menu_verbose ("Ignoring obsolete KDE legacy dirs"); 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); gmenu_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 */ } 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 gmenu_tree_strip_duplicate_children (GMenuTree *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) gmenu_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, 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 gmenu_tree_execute_moves (GMenuTree *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. */ gmenu_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 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) gmenu_tree_strip_duplicate_children (tree, layout); } static gboolean gmenu_tree_load_layout (GMenuTree *tree, GError **error) { GHashTable *loaded_menu_files; if (tree->layout) return TRUE; if (!gmenu_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)); gmenu_tree_resolve_files (tree, loaded_menu_files, tree->layout); g_hash_table_destroy (loaded_menu_files); gmenu_tree_strip_duplicate_children (tree, tree->layout); gmenu_tree_execute_moves (tree, tree->layout, NULL); return TRUE; } static void gmenu_tree_force_reload (GMenuTree *tree) { gmenu_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); } 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); } 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 \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 \n"); } break; case MENU_LAYOUT_NODE_OR: { MenuLayoutNode *child; menu_verbose ("Processing \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 \n"); } break; case MENU_LAYOUT_NODE_NOT: { /* First get the OR of all the rules */ MenuLayoutNode *child; menu_verbose ("Processing \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 \n"); } break; case MENU_LAYOUT_NODE_ALL: menu_verbose ("Processing \n"); set = desktop_entry_set_new (); desktop_entry_set_union (set, entry_pool); menu_verbose ("Processed \n"); break; case MENU_LAYOUT_NODE_FILENAME: { DesktopEntry *entry; menu_verbose ("Processing %s\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 %s\n", menu_layout_node_get_content (layout)); } break; case MENU_LAYOUT_NODE_CATEGORY: menu_verbose ("Processing %s\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 %s\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 %d 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, GMenuTreeDirectory *directory) { directory->entries = g_slist_prepend (directory->entries, gmenu_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, GMenuTreeDirectory *directory) { directory->entries = g_slist_prepend (directory->entries, gmenu_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, GMenuTreeDirectory *directory) { directory->entries = g_slist_prepend (directory->entries, gmenu_tree_entry_new (directory, desktop_entry, desktop_file_id, FALSE, TRUE)); } static void set_default_layout_values (GMenuTreeDirectory *parent, GMenuTreeDirectory *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) { GMenuTreeDirectory *subdir = tmp->data; set_default_layout_values (child, subdir); tmp = tmp->next; } } static GMenuTreeDirectory * process_layout (GMenuTree *tree, GMenuTreeDirectory *parent, MenuLayoutNode *layout, DesktopEntrySet *allocated) { MenuLayoutNode *layout_iter; GMenuTreeDirectory *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 = gmenu_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 & GMENU_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 */ { GMenuTreeDirectory *child_dir; menu_verbose ("Processing \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 \n"); } break; case MENU_LAYOUT_NODE_INCLUDE: { /* The match rule children of the are * independent (logical OR) so we can process each one by * itself */ MenuLayoutNode *rule; menu_verbose ("Processing (%d 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 (%d entries)\n", desktop_entry_set_get_count (entries)); } break; case MENU_LAYOUT_NODE_EXCLUDE: { /* The match rule children of the are * independent (logical OR) so we can process each one by * itself */ MenuLayoutNode *rule; menu_verbose ("Processing (%d 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 (%d entries)\n", desktop_entry_set_get_count (entries)); } break; case MENU_LAYOUT_NODE_DIRECTORY: { DesktopEntry *entry; menu_verbose ("Processing %s\n", menu_layout_node_get_content (layout_iter)); /* * The last 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 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 \n"); deleted = TRUE; break; case MENU_LAYOUT_NODE_NOT_DELETED: menu_verbose ("Processed \n"); deleted = FALSE; break; case MENU_LAYOUT_NODE_ONLY_UNALLOCATED: menu_verbose ("Processed \n"); only_unallocated = TRUE; break; case MENU_LAYOUT_NODE_NOT_ONLY_UNALLOCATED: menu_verbose ("Processed \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 \n"); break; case MENU_LAYOUT_NODE_LAYOUT: collect_layout_info (layout_iter, &directory->layout_info); menu_verbose ("Processed \n"); break; default: break; } layout_iter = menu_layout_node_get_next (layout_iter); } desktop_entry_set_unref (entry_pool); directory->only_unallocated = only_unallocated; 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 & GMENU_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); gmenu_tree_item_unref (directory); return NULL; } 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) { GMenuTreeDirectory *subdir = tmp->data; set_default_layout_values (directory, subdir); tmp = tmp->next; } tmp = directory->entries; while (tmp != NULL) { GMenuTreeEntry *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 & GMENU_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); gmenu_tree_item_unref_and_unset_parent (entry); } tmp = next; } g_assert (directory->name != NULL); return directory; } static void process_only_unallocated (GMenuTree *tree, GMenuTreeDirectory *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) { GMenuTreeEntry *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); gmenu_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) { GMenuTreeDirectory *subdir = tmp->data; process_only_unallocated (tree, subdir, allocated, unallocated_used); tmp = tmp->next; } } typedef struct { GMenuTree *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 & GMENU_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 (GMenuTree *tree, GMenuTreeDirectory *directory); static GSList * get_layout_info (GMenuTreeDirectory *directory, gboolean *is_default_layout) { GMenuTreeDirectory *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 = GMENU_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 (GMenuTreeDirectory *directory) { guint len; GSList *tmp; len = 0; tmp = directory->subdirs; while (tmp != NULL) { GMenuTreeDirectory *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 (GMenuTree *tree, GMenuTreeDirectory *directory, GMenuTreeDirectory *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 & GMENU_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) { GMenuTreeAlias *alias; GMenuTreeItem *item; GSList *list; if (subdir->subdirs != NULL) list = subdir->subdirs; else list = subdir->entries; item = GMENU_TREE_ITEM (list->data); menu_verbose ("Inline aliasing '%s' to '%s'\n", item->type == GMENU_TREE_ITEM_ENTRY ? g_app_info_get_name (G_APP_INFO (gmenu_tree_entry_get_app_info (GMENU_TREE_ENTRY (item)))) : (item->type == GMENU_TREE_ITEM_DIRECTORY ? gmenu_tree_directory_get_name (GMENU_TREE_DIRECTORY (item)) : gmenu_tree_directory_get_name (GMENU_TREE_ALIAS (item)->directory)), subdir->name); alias = gmenu_tree_alias_new (directory, subdir, item); g_slist_foreach (list, (GFunc) gmenu_tree_item_unref_and_unset_parent, NULL); g_slist_free (list); subdir->subdirs = NULL; subdir->entries = NULL; if (item->type == GMENU_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 = layout_values->inline_limit; else subdir->will_inline_header = 0; } else { g_slist_foreach (subdir->subdirs, (GFunc) gmenu_tree_item_set_parent, directory); directory->subdirs = g_slist_concat (directory->subdirs, subdir->subdirs); subdir->subdirs = NULL; g_slist_foreach (subdir->entries, (GFunc) gmenu_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 (GMenuTree *tree, GMenuTreeDirectory *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; GMenuTreeDirectory *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); gmenu_tree_item_unref_and_unset_parent (GMENU_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) { GMenuTreeDirectory *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); gmenu_tree_item_unref_and_unset_parent (GMENU_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) gmenu_tree_entry_compare_by_id); tmp = directory->entries; while (tmp != NULL && tmp->next != NULL) { GMenuTreeItem *a = tmp->data; GMenuTreeItem *b = tmp->next->data; if (a->type == GMENU_TREE_ITEM_ALIAS) a = GMENU_TREE_ALIAS (a)->aliased_item; if (b->type == GMENU_TREE_ITEM_ALIAS) b = GMENU_TREE_ALIAS (b)->aliased_item; if (strcmp (GMENU_TREE_ENTRY (a)->desktop_file_id, GMENU_TREE_ENTRY (b)->desktop_file_id) == 0) { tmp = g_slist_delete_link (tmp, tmp->next); gmenu_tree_item_unref (b); } else tmp = tmp->next; } } directory->preprocessed = TRUE; } static void process_layout_info (GMenuTree *tree, GMenuTreeDirectory *directory); static void check_pending_separator (GMenuTreeDirectory *directory) { if (directory->layout_pending_separator) { menu_verbose ("Adding pending separator in '%s'\n", directory->name); directory->contents = g_slist_append (directory->contents, gmenu_tree_separator_new (directory)); directory->layout_pending_separator = FALSE; } } static void merge_alias (GMenuTree *tree, GMenuTreeDirectory *directory, GMenuTreeAlias *alias) { menu_verbose ("Merging alias '%s' in directory '%s'\n", alias->directory->name, directory->name); if (alias->aliased_item->type == GMENU_TREE_ITEM_DIRECTORY) { process_layout_info (tree, GMENU_TREE_DIRECTORY (alias->aliased_item)); } check_pending_separator (directory); directory->contents = g_slist_append (directory->contents, gmenu_tree_item_ref (alias)); } static void merge_subdir (GMenuTree *tree, GMenuTreeDirectory *directory, GMenuTreeDirectory *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)) { GMenuTreeHeader *header; header = gmenu_tree_header_new (directory, subdir); directory->contents = g_slist_append (directory->contents, header); g_slist_foreach (subdir->contents, (GFunc) gmenu_tree_item_set_parent, directory); directory->contents = g_slist_concat (directory->contents, subdir->contents); subdir->contents = NULL; subdir->will_inline_header = G_MAXUINT16; gmenu_tree_item_set_parent (GMENU_TREE_ITEM (subdir), NULL); } else { directory->contents = g_slist_append (directory->contents, gmenu_tree_item_ref (subdir)); } } static void merge_subdir_by_name (GMenuTree *tree, GMenuTreeDirectory *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) { GMenuTreeDirectory *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 (GMENU_TREE_ITEM (subdir)->type == GMENU_TREE_ITEM_ALIAS) continue; if (!strcmp (subdir->name, subdir_name)) { directory->subdirs = g_slist_delete_link (directory->subdirs, tmp); merge_subdir (tree, directory, subdir); gmenu_tree_item_unref (subdir); } tmp = next; } } static void merge_entry (GMenuTree *tree, GMenuTreeDirectory *directory, GMenuTreeEntry *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, gmenu_tree_item_ref (entry)); } static void merge_entry_by_id (GMenuTree *tree, GMenuTreeDirectory *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) { GMenuTreeEntry *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 (GMENU_TREE_ITEM (entry)->type == GMENU_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); gmenu_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 (GMenuTree *tree, GMenuTreeDirectory *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) gmenu_tree_item_compare, GINT_TO_POINTER (GMENU_TREE_FLAGS_NONE)); tmp = subdirs; while (tmp != NULL) { GMenuTreeDirectory *subdir = tmp->data; if (GMENU_TREE_ITEM (subdir)->type == GMENU_TREE_ITEM_ALIAS) { merge_alias (tree, directory, GMENU_TREE_ALIAS (subdir)); gmenu_tree_item_unref (subdir); } else if (!find_name_in_list (subdir->name, except)) { merge_subdir (tree, directory, subdir); gmenu_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 (GMenuTree *tree, GMenuTreeDirectory *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) gmenu_tree_item_compare, GINT_TO_POINTER (tree->flags)); tmp = entries; while (tmp != NULL) { GMenuTreeEntry *entry = tmp->data; if (GMENU_TREE_ITEM (entry)->type == GMENU_TREE_ITEM_ALIAS) { merge_alias (tree, directory, GMENU_TREE_ALIAS (entry)); gmenu_tree_item_unref (entry); } else if (!find_name_in_list (entry->desktop_file_id, except)) { merge_entry (tree, directory, entry); gmenu_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 (GMenuTree *tree, GMenuTreeDirectory *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) gmenu_tree_item_compare, GINT_TO_POINTER (tree->flags)); tmp = items; while (tmp != NULL) { GMenuTreeItem *item = tmp->data; GMenuTreeItemType type; type = item->type; if (type == GMENU_TREE_ITEM_ALIAS) { merge_alias (tree, directory, GMENU_TREE_ALIAS (item)); gmenu_tree_item_unref (item); } else if (type == GMENU_TREE_ITEM_DIRECTORY) { if (!find_name_in_list (GMENU_TREE_DIRECTORY (item)->name, except_subdirs)) { merge_subdir (tree, directory, GMENU_TREE_DIRECTORY (item)); gmenu_tree_item_unref (item); } else { menu_verbose ("Not merging directory '%s' yet\n", GMENU_TREE_DIRECTORY (item)->name); directory->subdirs = g_slist_append (directory->subdirs, item); } } else if (type == GMENU_TREE_ITEM_ENTRY) { if (!find_name_in_list (GMENU_TREE_ENTRY (item)->desktop_file_id, except_entries)) { merge_entry (tree, directory, GMENU_TREE_ENTRY (item)); gmenu_tree_item_unref (item); } else { menu_verbose ("Not merging entry '%s' yet\n", GMENU_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 (GMenuTree *tree, GMenuTreeDirectory *directory) { GSList *layout_info; menu_verbose ("Processing menu layout hints for %s\n", directory->name); g_slist_foreach (directory->contents, (GFunc) gmenu_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 & GMENU_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_foreach (directory->subdirs, (GFunc) gmenu_tree_item_unref, NULL); g_slist_free (directory->subdirs); directory->subdirs = NULL; g_slist_foreach (directory->entries, (GFunc) gmenu_tree_item_unref, NULL); g_slist_free (directory->entries); directory->entries = NULL; g_slist_foreach (directory->default_layout_info, (GFunc) menu_layout_node_unref, NULL); g_slist_free (directory->default_layout_info); directory->default_layout_info = NULL; g_slist_foreach (directory->layout_info, (GFunc) menu_layout_node_unref, NULL); g_slist_free (directory->layout_info); directory->layout_info = NULL; } static void handle_entries_changed (MenuLayoutNode *layout, GMenuTree *tree) { if (tree->layout == layout) { gmenu_tree_force_rebuild (tree); gmenu_tree_invoke_monitors (tree); } } static void update_entry_index (GMenuTree *tree, GMenuTreeDirectory *dir) { GMenuTreeIter *iter = gmenu_tree_directory_iter (dir); GMenuTreeItemType next_type; while ((next_type = gmenu_tree_iter_next (iter)) != GMENU_TREE_ITEM_INVALID) { gpointer item = NULL; switch (next_type) { case GMENU_TREE_ITEM_ENTRY: { const char *id; item = gmenu_tree_iter_get_entry (iter); id = gmenu_tree_entry_get_desktop_file_id (item); if (id != NULL) g_hash_table_insert (tree->entries_by_id, (char*)id, item); } break; case GMENU_TREE_ITEM_DIRECTORY: { item = gmenu_tree_iter_get_directory (iter); update_entry_index (tree, (GMenuTreeDirectory*)item); } break; default: break; } if (item != NULL) gmenu_tree_item_unref (item); } gmenu_tree_iter_unref (iter); } static gboolean gmenu_tree_build_from_layout (GMenuTree *tree, GError **error) { DesktopEntrySet *allocated; if (tree->root) return TRUE; if (!gmenu_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 & GMENU_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 gmenu_tree_force_rebuild (GMenuTree *tree) { if (tree->root) { g_hash_table_remove_all (tree->entries_by_id); gmenu_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 gmenu_tree_iter_get_type (void) { static GType gtype = G_TYPE_INVALID; if (gtype == G_TYPE_INVALID) { gtype = g_boxed_type_register_static ("GMenuTreeIter", (GBoxedCopyFunc)gmenu_tree_iter_ref, (GBoxedFreeFunc)gmenu_tree_iter_unref); } return gtype; } GType gmenu_tree_directory_get_type (void) { static GType gtype = G_TYPE_INVALID; if (gtype == G_TYPE_INVALID) { gtype = g_boxed_type_register_static ("GMenuTreeDirectory", (GBoxedCopyFunc)gmenu_tree_item_ref, (GBoxedFreeFunc)gmenu_tree_item_unref); } return gtype; } GType gmenu_tree_entry_get_type (void) { static GType gtype = G_TYPE_INVALID; if (gtype == G_TYPE_INVALID) { gtype = g_boxed_type_register_static ("GMenuTreeEntry", (GBoxedCopyFunc)gmenu_tree_item_ref, (GBoxedFreeFunc)gmenu_tree_item_unref); } return gtype; } GType gmenu_tree_separator_get_type (void) { static GType gtype = G_TYPE_INVALID; if (gtype == G_TYPE_INVALID) { gtype = g_boxed_type_register_static ("GMenuTreeSeparator", (GBoxedCopyFunc)gmenu_tree_item_ref, (GBoxedFreeFunc)gmenu_tree_item_unref); } return gtype; } GType gmenu_tree_header_get_type (void) { static GType gtype = G_TYPE_INVALID; if (gtype == G_TYPE_INVALID) { gtype = g_boxed_type_register_static ("GMenuTreeHeader", (GBoxedCopyFunc)gmenu_tree_item_ref, (GBoxedFreeFunc)gmenu_tree_item_unref); } return gtype; } GType gmenu_tree_alias_get_type (void) { static GType gtype = G_TYPE_INVALID; if (gtype == G_TYPE_INVALID) { gtype = g_boxed_type_register_static ("GMenuTreeAlias", (GBoxedCopyFunc)gmenu_tree_item_ref, (GBoxedFreeFunc)gmenu_tree_item_unref); } return gtype; } GType gmenu_tree_flags_get_type (void) { static GType enum_type_id = 0; if (G_UNLIKELY (!enum_type_id)) { static const GFlagsValue values[] = { { GMENU_TREE_FLAGS_NONE, "GMENU_TREE_FLAGS_NONE", "none" }, { GMENU_TREE_FLAGS_INCLUDE_EXCLUDED, "GMENU_TREE_FLAGS_INCLUDE_EXCLUDED", "include-excluded" }, { GMENU_TREE_FLAGS_SHOW_EMPTY, "GMENU_TREE_FLAGS_SHOW_EMPTY", "show-empty" }, { GMENU_TREE_FLAGS_INCLUDE_NODISPLAY, "GMENU_TREE_FLAGS_INCLUDE_NODISPLAY", "include-nodisplay" }, { GMENU_TREE_FLAGS_SHOW_ALL_SEPARATORS, "GMENU_TREE_FLAGS_SHOW_ALL_SEPARATORS", "show-all-separators" }, { GMENU_TREE_FLAGS_SORT_DISPLAY_NAME, "GMENU_TREE_FLAGS_SORT_DISPLAY_NAME", "sort-display-name" }, { GMENU_TREE_FLAGS_INCLUDE_UNALLOCATED, "GMENU_TREE_FLAGS_INCLUDE_UNALLOCATED,", "include-unallocated" }, { 0, NULL, NULL } }; enum_type_id = g_flags_register_static ("GMenuTreeFlags", values); } return enum_type_id; } cinnamon-menus-3.6.0/libmenu/desktop-entries.c0000664000175000017500000005654413173357262020341 0ustar fabiofabio/* * Copyright (C) 2002 - 2004 Red Hat, Inc. * * 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., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include #include "desktop-entries.h" #include #include #include "menu-util.h" #define DESKTOP_ENTRY_GROUP "Desktop Entry" struct DesktopEntry { guint refcount; char *path; const char *basename; guint type : 2; guint reserved : 30; }; typedef struct { DesktopEntry base; GDesktopAppInfo *appinfo; GQuark *categories; guint showin : 1; } DesktopEntryDesktop; typedef struct { DesktopEntry base; char *name; char *generic_name; char *comment; GIcon *icon; guint nodisplay : 1; guint hidden : 1; guint showin : 1; } DesktopEntryDirectory; struct DesktopEntrySet { int refcount; GHashTable *hash; }; /* * Desktop entries */ /** * unix_basename_from_path: * @path: Path string * * Returns: A constant pointer into the basename of @path */ static const char * unix_basename_from_path (const char *path) { const char *basename = g_strrstr (path, "/"); if (basename) return basename + 1; else return path; } static const char * get_current_desktop (void) { static char *current_desktop = NULL; /* Support XDG_CURRENT_DESKTOP environment variable; this can be used * to abuse gnome-menus in non-GNOME desktops. */ if (!current_desktop) { const char *desktop; desktop = g_getenv ("XDG_CURRENT_DESKTOP"); /* Note: if XDG_CURRENT_DESKTOP is set but empty, do as if it * was not set */ if (!desktop || desktop[0] == '\0') current_desktop = g_strdup ("GNOME"); else current_desktop = g_strdup (desktop); } /* Using "*" means skipping desktop-related checks */ if (g_strcmp0 (current_desktop, "*") == 0) return NULL; return current_desktop; } static GIcon * key_file_get_icon (GKeyFile *key_file) { GIcon *icon = NULL; gchar *icon_name; icon_name = g_key_file_get_locale_string (key_file, DESKTOP_ENTRY_GROUP, "Icon", NULL, NULL); if (!icon_name) return NULL; if (g_path_is_absolute (icon_name)) { GFile *file; file = g_file_new_for_path (icon_name); icon = g_file_icon_new (file); g_object_unref (file); } else { char *p; /* Work around a common mistake in desktop files */ if ((p = strrchr (icon_name, '.')) != NULL && (strcmp (p, ".png") == 0 || strcmp (p, ".xpm") == 0 || strcmp (p, ".svg") == 0)) *p = 0; icon = g_themed_icon_new (icon_name); } g_free (icon_name); return icon; } static gboolean key_file_get_show_in (GKeyFile *key_file) { const gchar *current_desktop; gchar **strv; gboolean show_in = TRUE; int i; gchar *exec; current_desktop = get_current_desktop (); if (!current_desktop) return TRUE; exec = g_key_file_get_string (key_file, DESKTOP_ENTRY_GROUP, "Exec", NULL); if (exec) { if (g_str_has_prefix (exec, "gnome-control-center")) { g_free (exec); return FALSE; } g_free (exec); } strv = g_key_file_get_string_list (key_file, DESKTOP_ENTRY_GROUP, "OnlyShowIn", NULL, NULL); if (strv) { show_in = FALSE; for (i = 0; strv[i]; i++) { if (!strcmp (strv[i], "GNOME") || !strcmp (strv[i], "X-Cinnamon")) { show_in = TRUE; break; } } } else { strv = g_key_file_get_string_list (key_file, DESKTOP_ENTRY_GROUP, "NotShowIn", NULL, NULL); if (strv) { show_in = TRUE; for (i = 0; strv[i]; i++) { if (!strcmp (strv[i], current_desktop)) { show_in = FALSE; } } } } g_strfreev (strv); return show_in; } static gboolean desktop_entry_load_directory (DesktopEntry *entry, GKeyFile *key_file, GError **error) { DesktopEntryDirectory *entry_directory = (DesktopEntryDirectory*)entry; char *type_str; type_str = g_key_file_get_string (key_file, DESKTOP_ENTRY_GROUP, "Type", error); if (!type_str) return FALSE; if (strcmp (type_str, "Directory") != 0) { g_set_error (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_INVALID_VALUE, "\"%s\" does not contain the correct \"Type\" value\n", entry->path); g_free (type_str); return FALSE; } g_free (type_str); entry_directory->name = g_key_file_get_locale_string (key_file, DESKTOP_ENTRY_GROUP, "Name", NULL, error); if (entry_directory->name == NULL) return FALSE; entry_directory->generic_name = g_key_file_get_locale_string (key_file, DESKTOP_ENTRY_GROUP, "GenericName", NULL, NULL); entry_directory->comment = g_key_file_get_locale_string (key_file, DESKTOP_ENTRY_GROUP, "Comment", NULL, NULL); entry_directory->icon = key_file_get_icon (key_file); entry_directory->nodisplay = g_key_file_get_boolean (key_file, DESKTOP_ENTRY_GROUP, "NoDisplay", NULL); entry_directory->hidden = g_key_file_get_boolean (key_file, DESKTOP_ENTRY_GROUP, "Hidden", NULL); entry_directory->showin = key_file_get_show_in (key_file); return TRUE; } static DesktopEntryResultCode desktop_entry_load (DesktopEntry *entry) { if (strstr (entry->path, "/menu-xdg/")) return DESKTOP_ENTRY_LOAD_FAIL_OTHER; if (entry->type == DESKTOP_ENTRY_DESKTOP) { GKeyFile *key_file = NULL; DesktopEntryDesktop *entry_desktop = (DesktopEntryDesktop*)entry; const char *categories_str; entry_desktop->appinfo = g_desktop_app_info_new_from_filename (entry->path); if (!entry_desktop->appinfo || !g_app_info_get_name (G_APP_INFO (entry_desktop->appinfo)) || !g_app_info_get_executable (G_APP_INFO (entry_desktop->appinfo))) { menu_verbose ("Failed to load \"%s\"\n", entry->path); return DESKTOP_ENTRY_LOAD_FAIL_APPINFO; } categories_str = g_desktop_app_info_get_categories (entry_desktop->appinfo); if (categories_str) { char **categories; int i; categories = g_strsplit (categories_str, ";", -1); entry_desktop->categories = g_new0 (GQuark, g_strv_length (categories) + 1); for (i = 0; categories[i]; i++) entry_desktop->categories[i] = g_quark_from_string (categories[i]); g_strfreev (categories); } key_file = g_key_file_new (); if (!g_key_file_load_from_file (key_file, entry->path, 0, NULL)) entry_desktop->showin = TRUE; else entry_desktop->showin = key_file_get_show_in (key_file); g_key_file_free (key_file); return DESKTOP_ENTRY_LOAD_SUCCESS; } else if (entry->type == DESKTOP_ENTRY_DIRECTORY) { GKeyFile *key_file = NULL; GError *error = NULL; DesktopEntryResultCode rescode = DESKTOP_ENTRY_LOAD_SUCCESS; key_file = g_key_file_new (); if (!g_key_file_load_from_file (key_file, entry->path, 0, &error)) { rescode = DESKTOP_ENTRY_LOAD_FAIL_OTHER; goto out; } if (!desktop_entry_load_directory (entry, key_file, &error)) { rescode = DESKTOP_ENTRY_LOAD_FAIL_OTHER; goto out; } rescode = DESKTOP_ENTRY_LOAD_SUCCESS; out: g_key_file_free (key_file); if (rescode == DESKTOP_ENTRY_LOAD_FAIL_OTHER) { if (error) { menu_verbose ("Failed to load \"%s\": %s\n", entry->path, error->message); g_error_free (error); } else menu_verbose ("Failed to load \"%s\"\n", entry->path); } return rescode; } else g_assert_not_reached (); return DESKTOP_ENTRY_LOAD_FAIL_OTHER; } DesktopEntry * desktop_entry_new (const char *path, DesktopEntryResultCode *res_code) { DesktopEntryType type; DesktopEntry *retval; DesktopEntryResultCode code; menu_verbose ("Loading desktop entry \"%s\"\n", path); if (g_str_has_suffix (path, ".desktop")) { type = DESKTOP_ENTRY_DESKTOP; retval = (DesktopEntry*)g_new0 (DesktopEntryDesktop, 1); } else if (g_str_has_suffix (path, ".directory")) { type = DESKTOP_ENTRY_DIRECTORY; retval = (DesktopEntry*)g_new0 (DesktopEntryDirectory, 1); } else { menu_verbose ("Unknown desktop entry suffix in \"%s\"\n", path); *res_code = DESKTOP_ENTRY_LOAD_FAIL_OTHER; return NULL; } retval->refcount = 1; retval->type = type; retval->path = g_strdup (path); retval->basename = unix_basename_from_path (retval->path); code = desktop_entry_load (retval); *res_code = code; if (code < DESKTOP_ENTRY_LOAD_SUCCESS) { desktop_entry_unref (retval); return NULL; } return retval; } DesktopEntry * desktop_entry_reload (DesktopEntry *entry) { g_return_val_if_fail (entry != NULL, NULL); menu_verbose ("Re-loading desktop entry \"%s\"\n", entry->path); if (entry->type == DESKTOP_ENTRY_DESKTOP) { DesktopEntryDesktop *entry_desktop = (DesktopEntryDesktop *) entry; g_object_unref (entry_desktop->appinfo); entry_desktop->appinfo = NULL; g_free (entry_desktop->categories); entry_desktop->categories = NULL; } else if (entry->type == DESKTOP_ENTRY_DIRECTORY) { DesktopEntryDirectory *entry_directory = (DesktopEntryDirectory*) entry; g_free (entry_directory->name); entry_directory->name = NULL; g_free (entry_directory->comment); entry_directory->comment = NULL; g_object_unref (entry_directory->icon); entry_directory->icon = NULL; } else g_assert_not_reached (); if (desktop_entry_load (entry) < DESKTOP_ENTRY_LOAD_SUCCESS) { desktop_entry_unref (entry); return NULL; } return entry; } DesktopEntry * desktop_entry_ref (DesktopEntry *entry) { g_return_val_if_fail (entry != NULL, NULL); g_return_val_if_fail (entry->refcount > 0, NULL); entry->refcount += 1; return entry; } DesktopEntry * desktop_entry_copy (DesktopEntry *entry) { DesktopEntry *retval; menu_verbose ("Copying desktop entry \"%s\"\n", entry->basename); if (entry->type == DESKTOP_ENTRY_DESKTOP) retval = (DesktopEntry*)g_new0 (DesktopEntryDesktop, 1); else if (entry->type == DESKTOP_ENTRY_DIRECTORY) retval = (DesktopEntry*)g_new0 (DesktopEntryDirectory, 1); else g_assert_not_reached (); retval->refcount = 1; retval->type = entry->type; retval->path = g_strdup (entry->path); retval->basename = unix_basename_from_path (retval->path); if (retval->type == DESKTOP_ENTRY_DESKTOP) { DesktopEntryDesktop *desktop_entry = (DesktopEntryDesktop*) entry; DesktopEntryDesktop *retval_desktop_entry = (DesktopEntryDesktop*) retval; int i; retval_desktop_entry->appinfo = g_object_ref (desktop_entry->appinfo); if (desktop_entry->categories != NULL) { i = 0; for (; desktop_entry->categories[i]; i++); retval_desktop_entry->categories = g_new0 (GQuark, i + 1); i = 0; for (; desktop_entry->categories[i]; i++) retval_desktop_entry->categories[i] = desktop_entry->categories[i]; } else retval_desktop_entry->categories = NULL; } else if (entry->type == DESKTOP_ENTRY_DIRECTORY) { DesktopEntryDirectory *entry_directory = (DesktopEntryDirectory*)entry; DesktopEntryDirectory *retval_directory = (DesktopEntryDirectory*)retval; retval_directory->name = g_strdup (entry_directory->name); retval_directory->comment = g_strdup (entry_directory->comment); retval_directory->icon = g_object_ref (entry_directory->icon); retval_directory->nodisplay = entry_directory->nodisplay; retval_directory->hidden = entry_directory->hidden; retval_directory->showin = entry_directory->showin; } return retval; } void desktop_entry_unref (DesktopEntry *entry) { g_return_if_fail (entry != NULL); g_return_if_fail (entry->refcount > 0); entry->refcount -= 1; if (entry->refcount != 0) return; g_free (entry->path); entry->path = NULL; if (entry->type == DESKTOP_ENTRY_DESKTOP) { DesktopEntryDesktop *desktop_entry = (DesktopEntryDesktop*) entry; g_free (desktop_entry->categories); if (desktop_entry->appinfo) g_object_unref (desktop_entry->appinfo); } else if (entry->type == DESKTOP_ENTRY_DIRECTORY) { DesktopEntryDirectory *entry_directory = (DesktopEntryDirectory*) entry; g_free (entry_directory->name); entry_directory->name = NULL; g_free (entry_directory->comment); entry_directory->comment = NULL; if (entry_directory->icon != NULL) { g_object_unref (entry_directory->icon); entry_directory->icon = NULL; } } else g_assert_not_reached (); g_free (entry); } DesktopEntryType desktop_entry_get_type (DesktopEntry *entry) { return entry->type; } const char * desktop_entry_get_path (DesktopEntry *entry) { return entry->path; } const char * desktop_entry_get_basename (DesktopEntry *entry) { return entry->basename; } const char * desktop_entry_get_name (DesktopEntry *entry) { if (entry->type == DESKTOP_ENTRY_DESKTOP) return g_app_info_get_name (G_APP_INFO (((DesktopEntryDesktop*)entry)->appinfo)); return ((DesktopEntryDirectory*)entry)->name; } const char * desktop_entry_get_generic_name (DesktopEntry *entry) { if (entry->type == DESKTOP_ENTRY_DESKTOP) return g_desktop_app_info_get_generic_name (((DesktopEntryDesktop*)entry)->appinfo); return ((DesktopEntryDirectory*)entry)->generic_name; } const char * desktop_entry_get_comment (DesktopEntry *entry) { if (entry->type == DESKTOP_ENTRY_DESKTOP) return g_app_info_get_description (G_APP_INFO (((DesktopEntryDesktop*)entry)->appinfo)); return ((DesktopEntryDirectory*)entry)->comment; } GIcon * desktop_entry_get_icon (DesktopEntry *entry) { if (entry->type == DESKTOP_ENTRY_DESKTOP) return g_app_info_get_icon (G_APP_INFO (((DesktopEntryDesktop*)entry)->appinfo)); return ((DesktopEntryDirectory*)entry)->icon; } gboolean desktop_entry_get_no_display (DesktopEntry *entry) { if (entry->type == DESKTOP_ENTRY_DESKTOP) return g_desktop_app_info_get_nodisplay (((DesktopEntryDesktop*)entry)->appinfo); return ((DesktopEntryDirectory*)entry)->nodisplay; } gboolean desktop_entry_get_hidden (DesktopEntry *entry) { if (entry->type == DESKTOP_ENTRY_DESKTOP) return g_desktop_app_info_get_is_hidden (((DesktopEntryDesktop*)entry)->appinfo); return ((DesktopEntryDirectory*)entry)->hidden; } gboolean desktop_entry_get_show_in (DesktopEntry *entry) { if (entry->type == DESKTOP_ENTRY_DESKTOP) { const char *current_desktop = get_current_desktop (); if (current_desktop == NULL) return TRUE; else { return ((DesktopEntryDesktop *)entry)->showin; } } return ((DesktopEntryDirectory*)entry)->showin; } GDesktopAppInfo * desktop_entry_get_app_info (DesktopEntry *entry) { g_return_val_if_fail (entry->type == DESKTOP_ENTRY_DESKTOP, NULL); return ((DesktopEntryDesktop*)entry)->appinfo; } gboolean desktop_entry_has_categories (DesktopEntry *entry) { DesktopEntryDesktop *desktop_entry; if (entry->type != DESKTOP_ENTRY_DESKTOP) return FALSE; desktop_entry = (DesktopEntryDesktop*) entry; return (desktop_entry->categories != NULL && desktop_entry->categories[0] != 0); } gboolean desktop_entry_has_category (DesktopEntry *entry, const char *category) { GQuark quark; int i; DesktopEntryDesktop *desktop_entry; if (entry->type != DESKTOP_ENTRY_DESKTOP) return FALSE; desktop_entry = (DesktopEntryDesktop*) entry; if (desktop_entry->categories == NULL) return FALSE; if (!(quark = g_quark_try_string (category))) return FALSE; for (i = 0; desktop_entry->categories[i]; i++) { if (quark == desktop_entry->categories[i]) return TRUE; } return FALSE; } /* * Entry sets */ DesktopEntrySet * desktop_entry_set_new (void) { DesktopEntrySet *set; set = g_new0 (DesktopEntrySet, 1); set->refcount = 1; menu_verbose (" New entry set %p\n", set); return set; } DesktopEntrySet * desktop_entry_set_ref (DesktopEntrySet *set) { g_return_val_if_fail (set != NULL, NULL); g_return_val_if_fail (set->refcount > 0, NULL); set->refcount += 1; return set; } void desktop_entry_set_unref (DesktopEntrySet *set) { g_return_if_fail (set != NULL); g_return_if_fail (set->refcount > 0); set->refcount -= 1; if (set->refcount == 0) { menu_verbose (" Deleting entry set %p\n", set); if (set->hash) g_hash_table_destroy (set->hash); set->hash = NULL; g_free (set); } } void desktop_entry_set_add_entry (DesktopEntrySet *set, DesktopEntry *entry, const char *file_id) { menu_verbose (" Adding to set %p entry %s\n", set, file_id); if (set->hash == NULL) { set->hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) desktop_entry_unref); } g_hash_table_replace (set->hash, g_strdup (file_id), desktop_entry_ref (entry)); } DesktopEntry * desktop_entry_set_lookup (DesktopEntrySet *set, const char *file_id) { if (set->hash == NULL) return NULL; return g_hash_table_lookup (set->hash, file_id); } typedef struct { DesktopEntrySetForeachFunc func; gpointer user_data; } EntryHashForeachData; static void entry_hash_foreach (const char *file_id, DesktopEntry *entry, EntryHashForeachData *fd) { fd->func (file_id, entry, fd->user_data); } void desktop_entry_set_foreach (DesktopEntrySet *set, DesktopEntrySetForeachFunc func, gpointer user_data) { g_return_if_fail (set != NULL); g_return_if_fail (func != NULL); if (set->hash != NULL) { EntryHashForeachData fd; fd.func = func; fd.user_data = user_data; g_hash_table_foreach (set->hash, (GHFunc) entry_hash_foreach, &fd); } } static void desktop_entry_set_clear (DesktopEntrySet *set) { menu_verbose (" Clearing set %p\n", set); if (set->hash != NULL) { g_hash_table_destroy (set->hash); set->hash = NULL; } } int desktop_entry_set_get_count (DesktopEntrySet *set) { if (set->hash == NULL) return 0; return g_hash_table_size (set->hash); } static void union_foreach (const char *file_id, DesktopEntry *entry, DesktopEntrySet *set) { /* we are iterating over "with" adding anything not * already in "set". We unconditionally overwrite * the stuff in "set" because we can assume * two entries with the same name are equivalent. */ desktop_entry_set_add_entry (set, entry, file_id); } void desktop_entry_set_union (DesktopEntrySet *set, DesktopEntrySet *with) { menu_verbose (" Union of %p and %p\n", set, with); if (desktop_entry_set_get_count (with) == 0) return; /* A fast simple case */ g_hash_table_foreach (with->hash, (GHFunc) union_foreach, set); } typedef struct { DesktopEntrySet *set; DesktopEntrySet *with; } IntersectData; static gboolean intersect_foreach_remove (const char *file_id, DesktopEntry *entry, IntersectData *id) { /* Remove everything in "set" which is not in "with" */ if (g_hash_table_lookup (id->with->hash, file_id) != NULL) return FALSE; menu_verbose (" Removing from %p entry %s\n", id->set, file_id); return TRUE; /* return TRUE to remove */ } void desktop_entry_set_intersection (DesktopEntrySet *set, DesktopEntrySet *with) { IntersectData id; menu_verbose (" Intersection of %p and %p\n", set, with); if (desktop_entry_set_get_count (set) == 0 || desktop_entry_set_get_count (with) == 0) { /* A fast simple case */ desktop_entry_set_clear (set); return; } id.set = set; id.with = with; g_hash_table_foreach_remove (set->hash, (GHRFunc) intersect_foreach_remove, &id); } typedef struct { DesktopEntrySet *set; DesktopEntrySet *other; } SubtractData; static gboolean subtract_foreach_remove (const char *file_id, DesktopEntry *entry, SubtractData *sd) { /* Remove everything in "set" which is not in "other" */ if (g_hash_table_lookup (sd->other->hash, file_id) == NULL) return FALSE; menu_verbose (" Removing from %p entry %s\n", sd->set, file_id); return TRUE; /* return TRUE to remove */ } void desktop_entry_set_subtract (DesktopEntrySet *set, DesktopEntrySet *other) { SubtractData sd; menu_verbose (" Subtract from %p set %p\n", set, other); if (desktop_entry_set_get_count (set) == 0 || desktop_entry_set_get_count (other) == 0) return; /* A fast simple case */ sd.set = set; sd.other = other; g_hash_table_foreach_remove (set->hash, (GHRFunc) subtract_foreach_remove, &sd); } void desktop_entry_set_swap_contents (DesktopEntrySet *a, DesktopEntrySet *b) { GHashTable *tmp; menu_verbose (" Swap contents of %p and %p\n", a, b); tmp = a->hash; a->hash = b->hash; b->hash = tmp; } cinnamon-menus-3.6.0/libmenu/menu-monitor.c0000664000175000017500000002410513173357262017636 0ustar fabiofabio/* * Copyright (C) 2005 Red Hat, Inc. * Copyright (C) 2006 Mark McLoughlin * Copyright (C) 2007 Sebastian Dröge * Copyright (C) 2008 Vincent Untz * * 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., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include #include "menu-monitor.h" #include #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 *menu_monitor_notify_ref (MenuMonitorNotify *notify); static void 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) 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); } 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; 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 ? "" : ""); } 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 menu_monitor_ref (retval); } } MenuMonitor * menu_get_file_monitor (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 * 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) { 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) 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 * 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 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) { MenuMonitorNotify *notify; GSList *tmp; 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 menu_monitor_remove_notify (MenuMonitor *monitor, MenuMonitorNotifyFunc notify_func, gpointer user_data) { GSList *tmp; 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; menu_monitor_notify_unref (notify); monitor->notifies = g_slist_delete_link (monitor->notifies, tmp); } tmp = next; } } cinnamon-menus-3.6.0/libmenu/canonicalize.c0000664000175000017500000001656313173357262017655 0ustar fabiofabio/* Return the canonical absolute name of a given file. Copyright (C) 1996-2001, 2002 Free Software Foundation, Inc. This file is part of the GNU C Library. Copyright (C) 2002 Red Hat, Inc. (trivial port to GLib) The GNU C 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.1 of the License, or (at your option) any later version. The GNU C 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 the GNU C Library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ #include #include "canonicalize.h" #include #include #include #include #include #include #include #include /* Return the canonical absolute name of file NAME. A canonical name does not contain any `.', `..' components nor any repeated path separators ('/') or symlinks. All path components must exist. If RESOLVED is null, the result is malloc'd; otherwise, if the canonical name is PATH_MAX chars or more, returns null with `errno' set to ENAMETOOLONG; if the name fits in fewer than PATH_MAX chars, returns the name in RESOLVED. If the name cannot be resolved and RESOLVED is non-NULL, it contains the path of the first component that cannot be resolved. If the path can be resolved, RESOLVED holds the same value as the value returned. */ static char* menu_realpath (const char *name, char *resolved) { char *rpath, *dest, *extra_buf = NULL; const char *start, *end, *rpath_limit; long int path_max; int num_links = 0; if (name == NULL) { /* As per Single Unix Specification V2 we must return an error if either parameter is a null pointer. We extend this to allow the RESOLVED parameter to be NULL in case the we are expected to allocate the room for the return value. */ errno = EINVAL; return NULL; } if (name[0] == '\0') { /* As per Single Unix Specification V2 we must return an error if the name argument points to an empty string. */ errno = ENOENT; return NULL; } #ifdef PATH_MAX path_max = PATH_MAX; #else path_max = pathconf (name, _PC_PATH_MAX); if (path_max <= 0) path_max = 1024; #endif rpath = resolved ? g_alloca (path_max) : g_malloc (path_max); rpath_limit = rpath + path_max; if (name[0] != G_DIR_SEPARATOR) { if (!getcwd (rpath, path_max)) { rpath[0] = '\0'; goto error; } dest = strchr (rpath, '\0'); } else { rpath[0] = G_DIR_SEPARATOR; dest = rpath + 1; } for (start = end = name; *start; start = end) { struct stat st; int n; /* Skip sequence of multiple path-separators. */ while (*start == G_DIR_SEPARATOR) ++start; /* Find end of path component. */ for (end = start; *end && *end != G_DIR_SEPARATOR; ++end) /* Nothing. */; if (end - start == 0) break; else if (end - start == 1 && start[0] == '.') /* nothing */; else if (end - start == 2 && start[0] == '.' && start[1] == '.') { /* Back up to previous component, ignore if at root already. */ if (dest > rpath + 1) while ((--dest)[-1] != G_DIR_SEPARATOR); } else { size_t new_size; if (dest[-1] != G_DIR_SEPARATOR) *dest++ = G_DIR_SEPARATOR; if (dest + (end - start) >= rpath_limit) { ptrdiff_t dest_offset = dest - rpath; char *new_rpath; if (resolved) { #ifdef ENAMETOOLONG errno = ENAMETOOLONG; #else /* Uh... just pick something */ errno = EINVAL; #endif if (dest > rpath + 1) dest--; *dest = '\0'; goto error; } new_size = rpath_limit - rpath; if (end - start + 1 > path_max) new_size += end - start + 1; else new_size += path_max; new_rpath = (char *) realloc (rpath, new_size); if (new_rpath == NULL) goto error; rpath = new_rpath; rpath_limit = rpath + new_size; dest = rpath + dest_offset; } memcpy (dest, start, end - start); dest = dest + (end - start); *dest = '\0'; if (stat (rpath, &st) < 0) goto error; if (S_ISLNK (st.st_mode)) { char *buf = alloca (path_max); size_t len; if (++num_links > MAXSYMLINKS) { errno = ELOOP; goto error; } n = readlink (rpath, buf, path_max); if (n < 0) goto error; buf[n] = '\0'; if (!extra_buf) extra_buf = g_alloca (path_max); len = strlen (end); if ((long int) (n + len) >= path_max) { #ifdef ENAMETOOLONG errno = ENAMETOOLONG; #else /* Uh... just pick something */ errno = EINVAL; #endif goto error; } /* Careful here, end may be a pointer into extra_buf... */ g_memmove (&extra_buf[n], end, len + 1); name = end = memcpy (extra_buf, buf, n); if (buf[0] == G_DIR_SEPARATOR) dest = rpath + 1; /* It's an absolute symlink */ else /* Back up to previous component, ignore if at root already: */ if (dest > rpath + 1) while ((--dest)[-1] != G_DIR_SEPARATOR); } } } if (dest > rpath + 1 && dest[-1] == G_DIR_SEPARATOR) --dest; *dest = '\0'; return resolved ? memcpy (resolved, rpath, dest - rpath + 1) : rpath; error: if (resolved) strcpy (resolved, rpath); else g_free (rpath); return NULL; } char * menu_canonicalize_file_name (const char *name, gboolean allow_missing_basename) { char *retval; retval = menu_realpath (name, NULL); /* We could avoid some system calls by using the second * argument to realpath() instead of doing realpath * all over again, but who cares really. we'll see if * it's ever in a profile. */ if (allow_missing_basename && retval == NULL) { char *dirname; char *canonical_dirname; dirname = g_path_get_dirname (name); canonical_dirname = menu_realpath (dirname, NULL); g_free (dirname); if (canonical_dirname) { char *basename; basename = g_path_get_basename (name); retval = g_build_filename (canonical_dirname, basename, NULL); g_free (basename); g_free (canonical_dirname); } } return retval; } cinnamon-menus-3.6.0/libmenu/libcinnamon-menu-3.0.pc.in0000664000175000017500000000043213173357262021520 0ustar fabiofabioprefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: libcinnamon-menu Description: Desktop Menu Specification Implementation Requires: gio-unix-2.0 Version: @VERSION@ Libs: -L${libdir} -lcinnamon-menu-3 Cflags: -I${includedir}/cinnamon-menus-3.0 cinnamon-menus-3.6.0/libmenu/desktop-entries.h0000664000175000017500000001003713173357262020331 0ustar fabiofabio/* * Copyright (C) 2002 - 2004 Red Hat, Inc. * * 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., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef __DESKTOP_ENTRIES_H__ #define __DESKTOP_ENTRIES_H__ #include G_BEGIN_DECLS typedef enum { DESKTOP_ENTRY_INVALID = 0, DESKTOP_ENTRY_DESKTOP, DESKTOP_ENTRY_DIRECTORY } DesktopEntryType; typedef enum { DESKTOP_ENTRY_LOAD_FAIL_OTHER = 0, DESKTOP_ENTRY_LOAD_FAIL_APPINFO, DESKTOP_ENTRY_LOAD_SUCCESS } DesktopEntryResultCode; typedef struct DesktopEntry DesktopEntry; DesktopEntry *desktop_entry_new (const char *path, DesktopEntryResultCode *res_code); DesktopEntry *desktop_entry_ref (DesktopEntry *entry); DesktopEntry *desktop_entry_copy (DesktopEntry *entry); DesktopEntry *desktop_entry_reload (DesktopEntry *entry); void desktop_entry_unref (DesktopEntry *entry); DesktopEntryType desktop_entry_get_type (DesktopEntry *entry); const char *desktop_entry_get_path (DesktopEntry *entry); const char *desktop_entry_get_basename (DesktopEntry *entry); const char *desktop_entry_get_name (DesktopEntry *entry); const char *desktop_entry_get_generic_name (DesktopEntry *entry); const char *desktop_entry_get_comment (DesktopEntry *entry); GIcon *desktop_entry_get_icon (DesktopEntry *entry); gboolean desktop_entry_get_hidden (DesktopEntry *entry); gboolean desktop_entry_get_no_display (DesktopEntry *entry); gboolean desktop_entry_get_show_in (DesktopEntry *entry); /* Only valid for DESKTOP_ENTRY_DESKTOP */ GDesktopAppInfo *desktop_entry_get_app_info (DesktopEntry *entry); gboolean desktop_entry_has_categories (DesktopEntry *entry); gboolean desktop_entry_has_category (DesktopEntry *entry, const char *category); typedef struct DesktopEntrySet DesktopEntrySet; DesktopEntrySet *desktop_entry_set_new (void); DesktopEntrySet *desktop_entry_set_ref (DesktopEntrySet *set); void desktop_entry_set_unref (DesktopEntrySet *set); void desktop_entry_set_add_entry (DesktopEntrySet *set, DesktopEntry *entry, const char *file_id); DesktopEntry* desktop_entry_set_lookup (DesktopEntrySet *set, const char *file_id); int desktop_entry_set_get_count (DesktopEntrySet *set); void desktop_entry_set_union (DesktopEntrySet *set, DesktopEntrySet *with); void desktop_entry_set_intersection (DesktopEntrySet *set, DesktopEntrySet *with); void desktop_entry_set_subtract (DesktopEntrySet *set, DesktopEntrySet *other); void desktop_entry_set_swap_contents (DesktopEntrySet *a, DesktopEntrySet *b); typedef void (*DesktopEntrySetForeachFunc) (const char *file_id, DesktopEntry *entry, gpointer user_data); void desktop_entry_set_foreach (DesktopEntrySet *set, DesktopEntrySetForeachFunc func, gpointer user_data); G_END_DECLS #endif /* __DESKTOP_ENTRIES_H__ */ cinnamon-menus-3.6.0/libmenu/entry-directories.h0000664000175000017500000000623213173357262020666 0ustar fabiofabio/* * Copyright (C) 2002 - 2004 Red Hat, Inc. * * 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., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef __ENTRY_DIRECTORIES_H__ #define __ENTRY_DIRECTORIES_H__ #include #include "desktop-entries.h" G_BEGIN_DECLS typedef struct EntryDirectory EntryDirectory; typedef void (*EntryDirectoryChangedFunc) (EntryDirectory *ed, gpointer user_data); EntryDirectory *entry_directory_new (DesktopEntryType entry_type, const char *path); EntryDirectory *entry_directory_ref (EntryDirectory *ed); void entry_directory_unref (EntryDirectory *ed); void entry_directory_get_flat_contents (EntryDirectory *ed, DesktopEntrySet *desktop_entries, DesktopEntrySet *directory_entries, GSList **subdirs); typedef struct EntryDirectoryList EntryDirectoryList; EntryDirectoryList *entry_directory_list_new (void); EntryDirectoryList *entry_directory_list_ref (EntryDirectoryList *list); void entry_directory_list_unref (EntryDirectoryList *list); int entry_directory_list_get_length (EntryDirectoryList *list); gboolean _entry_directory_list_compare (const EntryDirectoryList *a, const EntryDirectoryList *b); void entry_directory_list_prepend (EntryDirectoryList *list, EntryDirectory *ed); void entry_directory_list_append_list (EntryDirectoryList *list, EntryDirectoryList *to_append); void entry_directory_list_add_monitors (EntryDirectoryList *list, EntryDirectoryChangedFunc callback, gpointer user_data); void entry_directory_list_remove_monitors (EntryDirectoryList *list, EntryDirectoryChangedFunc callback, gpointer user_data); DesktopEntry* entry_directory_list_get_directory (EntryDirectoryList *list, const char *relative_path); DesktopEntrySet *_entry_directory_list_get_all_desktops (EntryDirectoryList *list); void _entry_directory_list_empty_desktop_cache (void); G_END_DECLS #endif /* __ENTRY_DIRECTORIES_H__ */ cinnamon-menus-3.6.0/libmenu/Makefile.am0000664000175000017500000000377713173357262017111 0ustar fabiofabiolib_LTLIBRARIES = libcinnamon-menu-3.la AM_CPPFLAGS = \ $(GIO_UNIX_CFLAGS) \ $(WARN_CFLAGS) \ -DGMENU_I_KNOW_THIS_IS_UNSTABLE \ $(DISABLE_DEPRECATED_CFLAGS) \ $(DEBUG_CFLAGS) AM_CFLAGS = $(WARN_CFLAGS) libcinnamon_menu_3_includedir = $(includedir)/cinnamon-menus-3.0 libcinnamon_menu_3_include_HEADERS = \ gmenu-tree.h libcinnamon_menu_3_sources = \ canonicalize.c \ desktop-entries.c \ entry-directories.c \ gmenu-tree.c \ menu-layout.c \ menu-monitor.c \ menu-util.c libcinnamon_menu_3_la_SOURCES = \ $(libcinnamon_menu_3_sources) \ canonicalize.h \ desktop-entries.h \ entry-directories.h \ gmenu-tree.h \ menu-layout.h \ menu-monitor.h \ menu-util.h libcinnamon_menu_3_la_LIBADD = \ $(GIO_UNIX_LIBS) libcinnamon_menu_3_la_LDFLAGS = \ $(WARN_LDFLAGS) \ -version-info $(LIB_MENU_LT_VERSION) \ -no-undefined \ -export-symbols-regex gmenu_tree pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = libcinnamon-menu-3.0.pc EXTRA_DIST = \ libcinnamon-menu-3.0.pc.in \ libcinnamon-menu-3.0-uninstalled.pc.in CLEANFILES = # Introspection -include $(INTROSPECTION_MAKEFILE) INTROSPECTION_GIRS = INTROSPECTION_SCANNER_ARGS = --warn-all --add-include-path=$(srcdir) INTROSPECTION_COMPILER_ARGS = --includedir=$(srcdir) if HAVE_INTROSPECTION introspection_sources = $(libcinnamon_menu_3_include_HEADERS) gmenu-tree.c CMenu-3.0.gir: libcinnamon-menu-3.la CMenu_3_0_gir_INCLUDES = Gio-2.0 CMenu_3_0_gir_CFLAGS = $(AM_CPPFLAGS) CMenu_3_0_gir_LIBS = libcinnamon-menu-3.la CMenu_3_0_gir_SCANNERFLAGS = $(WARN_SCANNERFLAGS) --identifier-prefix=GMenu --symbol-prefix=gmenu --pkg-export=libcinnamon-menu-3.0 --c-include=gmenu-tree.h CMenu_3_0_gir_FILES = $(addprefix $(srcdir)/,$(introspection_sources)) INTROSPECTION_GIRS += CMenu-3.0.gir girdir = $(INTROSPECTION_GIRDIR) gir_DATA = $(INTROSPECTION_GIRS) typelibdir = $(INTROSPECTION_TYPELIBDIR) typelib_DATA = $(INTROSPECTION_GIRS:.gir=.typelib) CLEANFILES += $(gir_DATA) $(typelib_DATA) endif -include $(top_srcdir)/git.mk cinnamon-menus-3.6.0/MAINTAINERS0000664000175000017500000000054313173357262015103 0ustar fabiofabioCurrently active maintainers ---------------------------- Jasper St. Pierre E-mail: jstpierre@mecheye.net Userid: jstpierre Non-active maintainers, who have a good understanding of the code ----------------------------------------------------------------- #Mark McLoughlin #E-mail: mark@skynet.ie #Vincent Untz #E-mail: vuntz@gnome.org #Userid: vuntz cinnamon-menus-3.6.0/ChangeLog0000664000175000017500000036426313173357262015174 0ustar fabiofabio# Generated by Makefile. Do not edit. commit 246a46e6f240915c7ba58c455f8d45ea59cebb2e Author: Matthias Clasen Date: Mon Mar 25 22:31:16 2013 -0400 3.8.0 M NEWS M configure.ac commit 0a37bcb06b0094a677178bd9777758cc16441274 Author: Ani Peter Date: Mon Mar 25 19:19:17 2013 +0530 Completed for Malayalam M po/ml.po commit e28da785eccf196ef0b987b1f4dbe74a5102aff5 Author: Krishnababu Krothapalli Date: Mon Mar 25 15:49:39 2013 +0530 Updated Telugu Translations M po/te.po commit 4343c5db75e0b306b835212a98b3edee8ea66db5 Author: Reşat SABIQ Date: Sun Mar 24 13:26:35 2013 -0500 Minor update for Crimean Tatar/Turkish translation M po/crh.po commit 292ecdb7084cd52c5c8016c2e4b25a78f1debdef Author: Reşat SABIQ Date: Sun Mar 24 13:24:38 2013 -0500 Updated Crimean Tatar (Crimean Turkish) translation M po/crh.po commit 47378bcc11d754c3490c7ce06b6a258b8c8afa8c Author: Jiro Matsuzawa Date: Mon Mar 25 02:30:49 2013 +0900 l10n: Update Japanese translation M po/ja.po commit 5e5b387cfd590cd028df6dc323a547e66c76c23b Author: Wouter Bolsterlee Date: Sat Mar 23 23:39:30 2013 +0100 Updated Dutch translation M po/nl.po commit 5a6cb52314c39f577ff6faaa6e94bb9620d96f00 Author: Daniel Korostil Date: Sat Mar 23 12:42:16 2013 +0200 Uploaded Ukranian M po/uk.po commit 370d50f85550cd5e2398198f7148e52c69e2d38f Author: Shantha kumar Date: Fri Mar 22 15:45:10 2013 +0530 Tamil Translations Updated M po/ta.po commit 8770020280a690de1f047aa55d360968d187f94b Author: Rajesh Ranjan Date: Fri Mar 22 12:40:18 2013 +0530 hindi translation M po/hi.po commit c03b16747e734de180c3dfd678f9657f1f5f163d Author: Sandeep Sheshrao Shedmake Date: Thu Mar 21 18:46:00 2013 +0530 Updated Marathi Translations M po/mr.po commit 5c50a43768e2eff9f68f94b3a1207b0c1d8cd905 Author: ManojKumar Giri Date: Wed Mar 20 18:04:23 2013 +0530 Updated Odia Translation. M po/or.po commit fb40f7702b8d443e5c6a4983da133b1eafad300c Author: Danial Behzadi Date: Wed Mar 20 09:26:27 2013 +0330 l10n: Updated Persian translation M po/fa.po commit 107172dc7b4c8b7936113d74f3c53fe53b24bdeb Author: Victor Ibragimov Date: Tue Mar 19 20:40:30 2013 +0100 [l10n] Added Tadjik translation M po/LINGUAS A po/tg.po commit c573fa0e08616a908e6f1c335641ec6c1b9105f4 Author: Shankar Prasad Date: Mon Mar 18 16:02:23 2013 +0530 Updated kn translations M po/kn.po commit e26a4cd263ffc78db3104a9aca6e85034d48a67b Author: Marek Černocký Date: Sun Mar 17 22:46:15 2013 +0100 Updated Czech translation M po/cs.po commit 3fa134531fa6a7f1ed2ac4b572ea455a55eb1dd4 Author: Carles Ferrando Date: Sun Mar 17 19:51:46 2013 +0100 [l10n] Updated Catalan (Valencian) translation M po/ca@valencia.po commit 94a7ab3216a5d64c38272f616a20715ea6028b5f Author: Ask H. Larsen Date: Sun Mar 17 14:44:39 2013 +0100 Updated Danish translation M po/da.po commit f4adf5966ff0a6ebc86cdad7b22126dd88859804 Author: Ihar Hrachyshka Date: Sun Mar 17 16:24:34 2013 +0300 Updated Belarusian translation. M po/be.po commit b717f6e4f5d9a792f441f626bb769ec150e4bdd9 Author: Changwoo Ryu Date: Sat Mar 16 22:26:34 2013 +0900 Updated Korean translation M po/ko.po commit 8da5f3028860bfa6f5d8b38f6071285b501bb800 Author: Balázs Úr Date: Thu Mar 14 22:14:33 2013 +0100 Updated Hungarian translation M po/hu.po commit ca3a6029128122ee4f4b4b991686529174f25d02 Author: Duarte Loreto Date: Tue Mar 12 13:05:37 2013 +0000 Updated Portuguese translation and converted to New Spelling (Novo AO) M po/pt.po commit 758c22ec25528869dc96a3a77b76f378e4339d1e Author: Gil Forcada Date: Mon Mar 11 23:01:17 2013 +0100 [l10n] Updated Catalan translation M po/ca.po commit ed0dbd6eba3bcff96ce03dc7ba03c1d632e9e14d Author: Mattias Põldaru Date: Mon Mar 11 15:31:45 2013 +0200 [l10n] Updated Estonian translation M po/et.po commit 2be1f73f29a753a243f8dfcdf540916c8c9f20ad Author: Andika Triwidada Date: Mon Mar 11 12:39:01 2013 +0700 Updated Indonesian translation M po/id.po commit 8d0d9190809b44cdd94ae293d47df2abc4b1fe8d Author: Khaled Hosny Date: Sun Mar 10 00:06:18 2013 +0200 Update Arabic translation M po/ar.po commit a6c9fb7eaa9c3df19dbe6d555795f563fcdfa7c8 Author: Baurzhan Muftakhidinov Date: Sat Mar 9 21:28:34 2013 +0600 Updated Kazakh translation. M po/kk.po commit eebcb71254cc92334da975505e07446d1a00bf09 Author: Ville-Pekka Vainio Date: Thu Mar 7 20:57:21 2013 +0200 Finnish translation update by Jiri Grönroos M po/fi.po commit 47aec8856134fb8960679260a92c499578228b26 Author: Rafael Ferreira Date: Tue Mar 5 10:16:37 2013 -0300 Updated Brazilian Portuguese translation M po/pt_BR.po commit e63b9b880787e06c945a525048a7a7d92f8e19c8 Author: Nilamdyuti Goswami Date: Tue Mar 5 15:34:11 2013 +0530 Assamese translation updated for gnome 3.8 M po/as.po commit 192c6566cc281c52a2ca438da965da32c5e752f2 Author: Inaki Larranaga Murgoitio Date: Mon Mar 4 23:08:53 2013 +0100 Updated Basque language M po/eu.po commit 0f929355820faafef8712b17be906e834c3c97cc Author: Jeremy Bicha Date: Sun Mar 3 01:49:02 2013 -0500 sundry: Add Power Statistics & Personal File Sharing These seem to sort of duplicate what we have in Settings. Perhaps the extra graphs and info from Power Statistics should find a new home in System Monitor? https://bugzilla.gnome.org/show_bug.cgi?id=695047 M layout/gnome-applications.menu commit af5d71c573cda0e3527ea935516454dc3cd35e28 Author: William Jon McCann Date: Sun Mar 3 15:33:51 2013 -0500 Revert "Remove the special casing for Fedora desktop vendor renames" This reverts commit 5cc05790a308bbf7a5905254e377d3ca47df603b. We should really wait a bit before pushing this so the changes have time to propagate. M layout/gnome-applications.menu commit a5382598d7f982e49b6267869db69ac7cf562b26 Author: William Jon McCann Date: Sun Mar 3 15:19:45 2013 -0500 layout: put im-chooser into Sundry M layout/gnome-applications.menu commit 28ab24749488986519a21a51ca8060e5d9002256 Author: Benjamin Steinwender Date: Sun Mar 3 19:52:08 2013 +0100 [l10n] Updated German translation M po/de.po commit 6f27ae37ae12ee34b15479bb2a76d8a83ad5be81 Author: Luca Ferretti Date: Sun Mar 3 01:19:56 2013 +0100 l10n: Updated Italian translation M po/it.po commit 5cc05790a308bbf7a5905254e377d3ca47df603b Author: Kalev Lember Date: Fri Feb 22 13:15:18 2013 +0100 Remove the special casing for Fedora desktop vendor renames The desktop file vendor prefixes are gone from eog.desktop, file-roller.desktop, gucharmap.desktop, and yelp.desktop in Fedora 19+, so we no longer need to special case them in gnome-applications.menu. https://bugzilla.gnome.org/show_bug.cgi?id=694444 M layout/gnome-applications.menu commit e85f2419023afef93e25d2058560fc833c22d310 Author: Мирослав Николић Date: Fri Mar 1 09:26:42 2013 +0100 Updated Serbian translation M po/sr.po M po/sr@latin.po commit aab9d5ed860175bb86063a58a1fb82b3083d3431 Author: Chao-Hsiung Liao Date: Wed Feb 27 22:51:08 2013 +0800 Updated Traditional Chinese translation(Hong Kong and Taiwan) M po/zh_HK.po M po/zh_TW.po commit 7ba60014ce6bcc74996b519540ad58940ecd7205 Author: Sweta Kothari Date: Wed Feb 27 13:58:35 2013 +0530 Updated gujarati file M po/gu.po commit 4a4d04d822e226eaf2c13a24a79ec639b620eb8a Author: Pavol Klačanský Date: Mon Feb 25 20:57:24 2013 +0100 Updated slovak translation M po/sk.po commit 3b28046273ca53e7a4f2d78f26fad10ea7c06424 Author: Kjartan Maraas Date: Mon Feb 25 19:18:08 2013 +0100 Updated Norwegian bokmål translation M po/nb.po commit 7c94f1773354a31f9db1f140bd164f9bb5a2f872 Author: Jasper St. Pierre Date: Mon Feb 25 11:13:14 2013 -0500 configure: Remove AM_PATH_PYTHON This was a leftover from removing the simple-editor. M configure.ac commit fe172ebde5ff50e55784611bc6048ecf56580699 Author: Aurimas Černius Date: Sun Feb 24 21:59:22 2013 +0200 Updated Lithuanian translation M po/lt.po commit 4338482231743c5e8bab7dd565e269e3a0cbe2cd Author: Dimitris Spingos Date: Sun Feb 24 10:23:11 2013 +0200 Updated Greek translation M po/el.po commit 3e5fe2ec16b5683ffdf225f1b2721ac4e4f902c6 Author: Matej Urbančič Date: Fri Feb 22 22:28:14 2013 +0100 Updated Slovenian translation M po/sl.po commit 57b724f42917bcde9cf6b52d885bea4662b18f89 Author: A S Alam Date: Fri Feb 22 14:55:42 2013 +0000 Punjabi: Translation updated (aalam) M po/pa.po commit 0cfe9c374d664492a77dbda436da5fe3bed7da4c Author: Gheyret Kenji Date: Fri Feb 22 22:58:01 2013 +0900 Updated Uyghur translation Signed-off-by: Gheyret Kenji M po/ug.po commit e1849964ad1a91d2a017f0bcc743bb936c52fb0d Author: Yaron Shahrabani Date: Thu Feb 21 19:41:32 2013 +0200 Updated Hebrew translation. M po/he.po commit ecc8ace4d8d7603f3f1c182de3ae107c20701302 Author: Daniel Mustieles Date: Thu Feb 21 16:55:32 2013 +0100 Updated Spanish translation M po/es.po commit eb5f8823904a79787d0e6057f36fc9d08d44c332 Author: Yuri Myasoedov Date: Thu Feb 21 15:54:56 2013 +0400 Updated Russian translation M po/ru.po commit 207fa196dd04124ea3919cc7e66b44be75f982f9 Author: Cosimo Cecchi Date: Wed Feb 20 18:48:01 2013 -0500 sundry: add more Java stuff I get these indecipherable desktop files when I install Eclipse. https://bugzilla.gnome.org/show_bug.cgi?id=694324 M layout/gnome-applications.menu commit 31b32c027426007639369832e4d6bb48f4f6c0fc Author: Daniel Martinez Date: Wed Feb 20 23:11:23 2013 +0100 Updated Aragonese translation M po/an.po commit 48307035146fb3b1e6fea66f5b73a4bfe001a064 Author: Alexandre Franke Date: Wed Feb 20 21:15:02 2013 +0100 Update French translation M po/fr.po commit e775ffd58b91ec437c4a17d3fea2f5bc45b2423a Author: Fran Diéguez Date: Wed Feb 20 10:38:36 2013 +0100 Updated Galician translations M po/gl.po commit 96ae298b28c458bfd3f735a6ed8dac5d79b3d0b1 Author: Rūdolfs Mazurs Date: Wed Feb 20 11:33:40 2013 +0200 Updated Latvian translation M po/lv.po commit a2f3bacaba079df016cf53b8331fb4e1c77f2eab Author: Jasper St. Pierre Date: Tue Feb 19 18:55:17 2013 -0500 Post-release version bump M configure.ac commit 005605f79c8fb7f13542c497ccc1f1fe52907902 Author: Jasper St. Pierre Date: Tue Feb 19 18:53:34 2013 -0500 Release 3.7.90 M NEWS M configure.ac commit 64e189fedf5a04956cb96c54560f54736a012f4b Author: Piotr Drąg Date: Tue Feb 19 23:32:28 2013 +0100 Updated Polish translation M po/pl.po commit f3c636fbcf38846698c657d6aeb920cdabb0ea17 Author: Piotr Drąg Date: Tue Feb 19 23:31:14 2013 +0100 Updated POTFILES.in M po/POTFILES.in commit ac21fd7730b0c7f84b06468f862592d9666dfac6 Author: Florian Müllner Date: Tue Feb 19 12:47:18 2013 +0100 Add .directory file for Sundry https://bugzilla.gnome.org/show_bug.cgi?id=694131 M desktop-directories/Makefile.am A desktop-directories/X-GNOME-Sundry.directory.in M layout/gnome-applications.menu commit 95858c40461f6a2c53e2d4427d0851a08e0181c7 Author: Alexandre Franke Date: Tue Feb 19 21:56:15 2013 +0100 Update French translation M po/fr.po commit acb41bfe71a4e4717707eb21aef6c2b19b331e76 Author: Jasper St. Pierre Date: Tue Feb 19 15:14:48 2013 -0500 layout: Add some more items to Sundry M layout/gnome-applications.menu commit 1f4c3b6891637dfd2b8f42bae139166cd0e1fd8e Author: Piotr Drąg Date: Tue Feb 19 20:56:29 2013 +0100 Updated Polish translation M po/pl.po commit b1f831d2894df2501086f7c4bd523fe0e67f3676 Author: Piotr Drąg Date: Tue Feb 19 20:44:51 2013 +0100 Updated POTFILES.in M po/POTFILES.in commit 36d5d699d7d4193a1b3d84777566466326f78b19 Author: Florian Müllner Date: Sun Feb 17 03:40:22 2013 +0100 layout: Add Sundry category Creates a Sundry directory to contain things that either aren't really apps or are scheduled for removal but that are hard to remove at the moment for various reasons. We just need a temporary way to segregate these items so they don't pollute the app view. https://bugzilla.gnome.org/show_bug.cgi?id=694131 M layout/gnome-applications.menu commit 8cb236fbf31dee1a533582f1f8ef72b7b96a33e0 Author: Florian Müllner Date: Sun Feb 17 02:51:00 2013 +0100 layout: Account for gcalctool => gnome-calculator name change https://bugzilla.gnome.org/show_bug.cgi?id=694131 M layout/gnome-applications.menu commit a06c0348c1a129ffd996e00615d5d6d25b17dcdc Author: Florian Müllner Date: Sun Feb 17 02:25:42 2013 +0100 layout: Deal with some vendor renames The menu spec doesn't offer very specific guidance for how to use vendor prefixes. And it isn't even clear about what a vendor is. Or how it helps avoid collisions with a naming scheme that isn't fully qualified in the first place. Longer term we should move to something that uses fully qualified names like the dbus spec. In the meantime we have to deal with "aliases" for app names. https://bugzilla.gnome.org/show_bug.cgi?id=694131 M layout/gnome-applications.menu commit e644a541b5bab530fe6649c3b8f212faca46eb66 Author: Florian Müllner Date: Sun Feb 17 02:18:51 2013 +0100 layout: Add a new X-GNOME-Utilities directory Rather than using a developer-defined category, this directory contains an explicit whitelist of known-good GNOME tools. This more or less follows the definition set in the GNOME jhbuild moduleset. https://bugzilla.gnome.org/show_bug.cgi?id=694131 M desktop-directories/Makefile.am A desktop-directories/X-GNOME-Utilities.directory.in M layout/gnome-applications.menu commit a5e2e6e4912c2dfcf6559828186063ecd6d09e57 Author: Jasper St. Pierre Date: Mon Feb 18 14:36:03 2013 -0500 desktop-directories: Remove system settings desktop entries They're unused by gnome-control-center. D desktop-directories/Hardware.directory.in M desktop-directories/Makefile.am D desktop-directories/Personal.directory.in D desktop-directories/System.directory.in commit b53758987b4d04bf9c869bfd02c0ec1d252638bf Author: Jasper St. Pierre Date: Fri Feb 15 19:23:22 2013 -0500 layout: Install as gnome-applications.menu gnome-session now sets XDG_MENU_PREFIX by default, so we should install it where applications that use gnome-menus look. M layout/Makefile.am R100 layout/applications.menu layout/gnome-applications.menu commit cfaa9d460ef9fb502585d0fcdfcf3bb4e25b19aa Author: Jasper St. Pierre Date: Fri Feb 15 17:33:00 2013 -0500 layout: Put control center apps in the applications menu This allows gnome-shell to do app tracking for them. It should not display them, as the desktop directory itself is marked as NoDisplay=true. M desktop-directories/Makefile.am A desktop-directories/X-GNOME-SystemSettings.directory.in M layout/applications.menu commit a1c6559dcb1f9c9d12d9ebb6bd226a078a02d19a Author: Jasper St. Pierre Date: Fri Feb 15 20:44:48 2013 -0500 gmenu-tree: Fix error reporting No idea why this was here.. M libmenu/gmenu-tree.c commit 1e12d0f9f309c977702dad1363e56cd9989b7375 Author: Jasper St. Pierre Date: Fri Feb 15 20:29:18 2013 -0500 configure: Have better debugging on by default M configure.ac commit 589ba554d79366dacf3592b3670459003e45ace4 Author: William Jon McCann Date: Fri Sep 28 09:53:22 2012 -0400 Remove the simple editor gnome-menus is a dependency component and we don't really want it to be installing applications as a side effect. There are other better and maintained options for menu editing now anyway. https://bugzilla.gnome.org/show_bug.cgi?id=684900 M Makefile.am M configure.ac M po/POTFILES.in D simple-editor/GMenuSimpleEditor/Makefile.am D simple-editor/GMenuSimpleEditor/__init__.py D simple-editor/GMenuSimpleEditor/config.py.in D simple-editor/GMenuSimpleEditor/main.py D simple-editor/GMenuSimpleEditor/maindialog.py D simple-editor/GMenuSimpleEditor/menufilewriter.py D simple-editor/GMenuSimpleEditor/menutreemodel.py D simple-editor/Makefile.am D simple-editor/gmenu-simple-editor.desktop.in D simple-editor/gmenu-simple-editor.in D simple-editor/gmenu-simple-editor.ui commit 7d90221e27fcc72de497f45db4f3ef3c2d623fd6 Author: Jasper St. Pierre Date: Fri Feb 1 21:46:13 2013 -0500 Memory leak fixes Based on a patch by William Jon McCann https://bugzilla.gnome.org/show_bug.cgi?id=349695 M libmenu/desktop-entries.c M libmenu/entry-directories.c M libmenu/gmenu-tree.c commit db75a478f53f2619b1d96cd09d3a4f9abe682fc9 Author: Inaki Larranaga Murgoitio Date: Wed Jan 30 21:26:01 2013 +0100 Updated Basque language M po/eu.po commit 2b4eb5f9db8411e62da6ec8d80ada1895772d05b Author: Nuno Araujo Date: Tue Jan 15 22:36:11 2013 +0100 Fix the build with automake 1.13 In Automake 1.13, the long-deprecated macro AM_CONFIG_HEADER (deprecated since 2002) has been removed in favour of AC_CONFIG_HEADERS. https://bugzilla.gnome.org/show_bug.cgi?id=692108 M configure.ac commit 86241e29cd1101fe638a5665b8c52e8d3c84444d Author: Fabio Tomat Date: Sat Jan 19 02:34:16 2013 +0100 Updated Friulian translation M po/fur.po commit 8d161da704766d9f4a42f1213ca4dc6b8d0e7873 Author: Matthias Clasen Date: Tue Jan 15 07:42:02 2013 -0500 Bump version M configure.ac commit d4a11d28df389f00550848a078c40a2438908df5 Author: Matthias Clasen Date: Tue Jan 15 07:35:47 2013 -0500 3.6.2 M NEWS commit 234940bf89aa82012c3622674cec151dcf4ff128 Author: Gheyret Kenji Date: Sat Jan 12 14:00:36 2013 +0900 Updated Uyghur translation Signed-off-by: Gheyret Kenji M po/ug.po commit 0594875c19b1bf981f1bbde416a1ca440c7caeb5 Author: Vincent Untz Date: Mon Jan 7 16:36:24 2013 +0100 misc: Remove myself from maintainers I'm not really doing anything on gnome-menus anymore, and Jasper is happy being the sole maintainer. M MAINTAINERS M gnome-menus.doap commit 9c1c50146b8d1a92ac556ba0a0ca174426bc6ffa Author: Gheyret Kenji Date: Fri Jan 4 21:38:40 2013 +0900 Uyghur translation Signed-off-by: Gheyret Kenji M po/ug.po commit fd4a321b9eaf6ee11b80d0c377b8ff8873e6c777 Author: Khaled Hosny Date: Mon Dec 24 13:38:26 2012 +0200 Update Arabic translation M po/ar.po commit 9d45c5369f4a486d2ba5c3a8c3262c84172b095a Author: Alexandre Rostovtsev Date: Sat Nov 24 15:24:50 2012 -0500 libmenu: always call menu_layout_load() with non_prefixed_name parameter We must ensure that when loading "${XDG_MENU_PREFIX}applications.menu" or "applications.menu", the root layout node's name is set to "applications", not "${XDG_MENU_PREFIX}applications", because the menu spec states that the default merge directory for "${XDG_MENU_PREFIX}applications.menu" is "applications-merged", not "${XDG_MENU_PREFIX}applications-merged". https://bugzilla.gnome.org/show_bug.cgi?id=688972 M libmenu/gmenu-tree.c commit dfae41dd0f0df6a72c1ef47dfe1e9efb1bfee746 Author: Shankar Prasad Date: Thu Nov 29 14:24:44 2012 +0530 Updated kn translation M po/kn.po commit aad880ce305edc2fe61fb3070ec8df1d9d29f28a Author: Shankar Prasad Date: Thu Nov 29 14:21:37 2012 +0530 Updated kn translation M po/kn.po commit df21eb893653e0b3c0e0f43cd0bc0de00c28d54a Author: Shankar Prasad Date: Thu Nov 29 14:13:13 2012 +0530 Updated kn translation M po/kn.po commit 09ea3c32275547f8de8799bb47ef3f9441abf12f Author: Shankar Prasad Date: Tue Nov 27 16:00:31 2012 +0530 Updated kn translation M po/kn.po commit 6a113f67b993702417d9db189d36e63fd9e4806d Author: Matthias Clasen Date: Tue Nov 13 22:56:06 2012 -0500 Post-release version bump M configure.ac commit 0ebd911ccbd177b856d34f43a6ef680275ab0871 Author: Matthias Clasen Date: Tue Nov 13 22:54:01 2012 -0500 3.6.1 M NEWS commit fff8296aa0be3e257b2147e590da38cf470f4054 Author: Bahodir Mansurov <6ahodir@gmail.com> Date: Sun Oct 14 13:42:00 2012 -0400 updating Uzbek@cyrillic translation M po/uz@cyrillic.po commit d9dce393a242ddc4799fdc776f0af305bf935ff5 Author: Bahodir Mansurov <6ahodir@gmail.com> Date: Sun Oct 14 13:04:49 2012 -0400 updating Uzbek@cyrillic translation M po/uz@cyrillic.po commit 7acb8e2e5bff34c049c150d53190843996aa3f3d Author: Pavol Klačanský Date: Tue Oct 9 10:44:23 2012 +0100 Updated Slovak translation M po/sk.po commit 78b37e6142300931604566e2d8eb4123c70cfeb4 Author: Daniel Martinez Cucalon Date: Sat Sep 29 00:54:41 2012 +0200 Updated Aragonese translation M po/an.po commit 206da7e2fb133923f5898b42fbdc26db7f46d437 Author: Daniel Korostil Date: Wed Sep 26 09:15:57 2012 +0300 Added uk translation M po/uk.po commit 20f8d7169da967c39a92fb9bd97c74f5e1bb798d Author: Carles Ferrando Date: Wed Sep 26 01:31:05 2012 +0200 [l10n] Updated Catalan (Valencian) translation M po/ca@valencia.po commit e8cda485fec0fb2480f8e232b790387c1f41c77f Author: Gil Forcada Date: Wed Sep 26 01:30:42 2012 +0200 [l10n] Updated Catalan translation M po/ca.po commit e2ad6f559992e253ee7b10db7358c709d3a54328 Author: Matthias Clasen Date: Tue Sep 25 08:01:46 2012 -0400 post-release version bump M configure.ac commit ff379b515cdb3728dfd2f49c233797603b9658b8 Author: Matthias Clasen Date: Tue Sep 25 07:54:10 2012 -0400 3.6.0 M NEWS commit 1f596f13f5b793515dcb07cc7dd0834581f9f22e Author: Rūdolfs Mazurs Date: Mon Sep 24 13:22:34 2012 +0300 Updated Latvian translation M po/lv.po commit 3839fb3f2784f3575c454a7128fb7ff4fed12233 Author: Mattias Põldaru Date: Mon Sep 24 11:15:01 2012 +0300 [l10n] Updated Estonian translation M po/et.po commit b2199a2b2972f37d85deab11818a94ce636a2d0a Author: Timur Zhamakeev Date: Mon Sep 24 09:21:40 2012 +0600 Updated Kyrgyz translation M po/ky.po commit d1ea18a03b123039f63e54360b5aac02d58e07fd Author: Runa Bhattacharjee Date: Fri Sep 21 19:23:07 2012 +0530 Updated Bengali India Translation M po/bn_IN.po commit 4883507b4742178707f227922f421e130ae0e612 Author: Rajesh Ranjan Date: Fri Sep 21 14:21:36 2012 +0530 hindi update M po/hi.po commit 319bc9001ec45d837da239a4dbd1cf30546ba4ce Author: Praveen Illa Date: Wed Sep 19 22:08:18 2012 +0530 Updated Telugu Translation M po/te.po commit dd8991b15488f384122120ea31e8c6043e0144ea Author: Ani Peter Date: Tue Sep 18 23:04:10 2012 +0530 Updated Malayalam file M po/ml.po commit e24c669b5331a6deab6730c0bb7f582ad98c138c Author: Noriko Mizumoto Date: Tue Sep 18 12:57:52 2012 +0900 [l10n] Update Japanese translation M po/ja.po commit d529600f891fdd32d111e281a65677c9468d8f5c Author: Djavan Fagundes Date: Mon Sep 17 21:28:13 2012 -0300 Revert "Updated Brazilian Portuguese translation" This reverts commit 0b8278f3dc7f834b5856a98762cda69b0d713707. A po/pt_BR.po commit 0b8278f3dc7f834b5856a98762cda69b0d713707 Author: Djavan Fagundes Date: Mon Sep 17 20:40:04 2012 -0300 Updated Brazilian Portuguese translation D po/pt_BR.po commit 0619ec60ce602e023beb9d14df34d4958d326a31 Author: Og B. Maciel Date: Mon Sep 17 16:51:48 2012 -0400 Updated translation for Brazilian Portuguese. M po/pt_BR.po commit 4ea3f1de69fec378262bc966f5d3e355f82c2b20 Author: Vincent Untz Date: Mon Sep 17 13:10:55 2012 +0200 release: post-release bump to 3.6.0 M configure.ac commit a4e3e70fb8150461af79a5314cdd3b9a8fe5956a Author: Vincent Untz Date: Mon Sep 17 13:10:14 2012 +0200 release: 3.5.92 M NEWS M configure.ac commit c21dc9a2fbc2fbf288de0d688afd519e2f25da4b Author: Ask H. Larsen Date: Sun Sep 16 13:25:58 2012 +0200 Updated Danish translation M po/da.po commit fe09bdbae726dbefa92f22d2e851b7ec5b30dfef Author: Changwoo Ryu Date: Sat Sep 15 16:55:29 2012 +0900 Updated Korean translation M po/ko.po commit aceac14e0b728626c966d3ccfab51bb99a396ba6 Author: Rico Tzschichholz Date: Wed Sep 12 08:40:37 2012 +0200 libmenu: Add proper header reference to GMenu-3.0.gir M libmenu/Makefile.am commit 491c7eccff4e5fedc3f68f2d9d33399d49eed9a8 Author: Marek Černocký Date: Mon Sep 10 00:47:09 2012 +0200 Czech translation M po/cs.po commit 2c2c4f24c60952ef3e014ce0fd24c9db83c7f3fb Author: Milo Casagrande Date: Thu Sep 6 22:51:48 2012 +0200 [l10n] Updated Italian translation. M po/it.po commit f33d2b42226ae7c0d2fe871b55563b544dfcd252 Author: Arash Mousavi Date: Thu Sep 6 21:02:51 2012 +0430 l10n: Updated Persian translation M po/fa.po commit db8230cb5375eb951fc3385fa56e0fbd20222030 Author: Theppitak Karoonboonyanan Date: Thu Sep 6 20:23:58 2012 +0700 Updated Thai translation. M po/th.po commit b2cd372cf3ef3d5e10ca7e724366c72322a44849 Author: Timo Jyrinki Date: Thu Sep 6 08:59:09 2012 +0300 Finnish translation update by Jiri Grönroos M po/fi.po commit 103fb06fe86990bc7ec95e7cd8f61b71b69f9d9a Author: Bruce Cowan Date: Wed Sep 5 19:50:45 2012 +0100 Updated British English translation M po/en_GB.po commit a8f755a4b967738261540d2efd4c3ab45a9b283b Author: Alexandre Franke Date: Tue Sep 4 20:52:48 2012 +0200 Update French translation M po/fr.po commit b4c27cda6d161dbffb3878bfb1fd9164bb292de7 Author: Gabor Kelemen Date: Tue Sep 4 13:49:16 2012 +0200 Updated Hungarian translation M po/hu.po commit c66d0da3965cb3157d08b33278220c2d8d52c5f6 Author: Muhammet Kara Date: Tue Sep 4 06:48:19 2012 +0300 [l10n]Updated Turkish translation M po/tr.po commit 691760dbea68b46aa6b895f0d6c739302c372806 Author: Piotr Drąg Date: Mon Sep 3 19:09:33 2012 +0200 Updated Polish translation M po/pl.po commit a797159ac6b20b5e608290731ff0c9ccc1792848 Author: Dr.T.Vasudevan Date: Sun Sep 2 18:02:37 2012 +0530 updated Tamil translation M po/ta.po commit a44c335288e7c50db6d95bb080329bb1151d40a7 Author: Daniel Nylander Date: Sun Sep 2 11:59:21 2012 +0200 Updated Swedish translation M po/sv.po commit 8a09d2aad657036cb91c6d26fc370fd3a7be3d80 Author: Nguyễn Thái Ngọc Duy Date: Sat Sep 1 22:04:28 2012 +0700 Updated Vietnamese translation M po/vi.po commit c126f269866ed323926841157c72c48df5753a99 Author: Nguyễn Thái Ngọc Duy Date: Sat Sep 1 22:01:50 2012 +0700 po/vi: import from Damned Lies M po/vi.po commit d2b6d0a7e02d36125a455b99f2a43fb2598fc7cc Author: Duarte Loreto Date: Sat Sep 1 01:17:02 2012 +0100 Updated Portuguese translation M po/pt.po commit 6cbb7189d43207cc7abb5709255ddf8a6d5e4562 Author: Aurimas Černius Date: Wed Aug 29 22:57:58 2012 +0300 Updated Lithuanian translation M po/lt.po commit 55149c45571e54ed59599429e5c131fd64789d80 Author: Chrovex Fan Date: Tue Aug 28 22:32:44 2012 +0800 update Simplified Chinese (zh_CN) translation M po/zh_CN.po commit 2bfb0ab9c702b811d26c5adbd276eebdf0e04592 Author: Nilamdyuti Goswami Date: Tue Aug 28 00:03:50 2012 +0530 Implemented FUEL entries to Assamese translation M po/as.po commit 387804f05c1c6a8648f3930b45df939bf1b684f2 Author: A S Alam Date: Mon Aug 27 19:58:58 2012 +0530 update Punjabi Translation M po/pa.po commit 3be4bc499b3df622c149f89780ad8ffc8c67319a Author: Piotr Drąg Date: Thu Aug 23 03:24:55 2012 +0200 Updated Polish translation M po/pl.po commit 1682e39a113f5ea19e1fd8d0da5fb27db6a95274 Author: Dirgita Date: Tue Aug 21 22:20:32 2012 +0700 Updated Indonesian translation M po/id.po commit 0ed913bb8276b06e3f305b5e1907d87cc23d906a Author: Fran Diéguez Date: Fri Aug 17 23:37:20 2012 +0200 Updated Galician translations M po/gl.po commit d0e752350b2a9d8f85845614fda54571e143cef5 Author: Sandeep Sheshrao Shedmake Date: Thu Aug 16 12:19:50 2012 +0530 Updated Marathi Translations M po/mr.po commit 37f8b405844aedc87169ee902a8ed150978ffb1a Author: Jasper St. Pierre Date: Mon Aug 6 18:42:20 2012 -0300 Post-release version bump M configure.ac commit 42ace8382ab15914f55c7dc6dffab418b38e0e50 Author: Jasper St. Pierre Date: Mon Aug 6 18:36:56 2012 -0300 Release 3.5.5 M NEWS commit 685323a7cae56b1ced25c10f26af8eda7c9c893a Author: Мирослав Николић Date: Mon Aug 6 11:30:07 2012 +0200 Updated Serbian translation M po/sr.po M po/sr@latin.po commit b0c0c0afbc6a563f987df95e508c66c881bb5c27 Author: Sweta Kothari Date: Mon Jul 30 13:54:26 2012 +0530 Updated gujarati file M po/gu.po commit 70f681351441c9146e1e4eb1c48154c5db1d2a5b Author: Przemysław Buczkowski Date: Thu Jul 26 19:41:14 2012 +0200 Added Silesian translation M po/LINGUAS A po/szl.po commit 09053a26df6431e32d706eda69c0b7bf9100994b Author: Baurzhan Muftakhidinov Date: Thu Jul 26 09:20:18 2012 +0600 Updated Kazakh translation M po/kk.po commit 73b36491c3a472c67a15f1684893d080c743c601 Author: Tobias Endrigkeit Date: Mon Jul 23 22:43:15 2012 +0200 Updated German translation M po/de.po commit 985728566f914aa8a3a3c571dd203ee4b50827f3 Author: Chao-Hsiung Liao Date: Tue Jul 17 15:01:09 2012 +0800 Updated Traditional Chinese translation(Hong Kong and Taiwan) M po/zh_HK.po M po/zh_TW.po commit 50f09c92d3418043cbead8ccc014235ab3eb9bd3 Author: Jasper St. Pierre Date: Mon Jul 16 20:24:57 2012 -0400 Post-release version bump M configure.ac commit f8e4cb42e10b7c2666f3771da158646a145abd34 Author: Jasper St. Pierre Date: Mon Jul 16 20:24:15 2012 -0400 Release 3.5.4 M NEWS commit 1e62d7d67525ea5628296435bd95ef325954786d Author: chingiz Date: Mon Jul 16 11:36:35 2012 +0400 Updated Kirghiz translation M po/ky.po commit b3f0c47b46b5456d220b7cb8b384a52e71018fa3 Author: Alexander Shopov Date: Wed Jul 4 07:17:08 2012 +0300 Updated Bulgarian translation M po/bg.po commit 2c31301300f13a4ced24a0b116a010f9e20b0756 Author: Jasper St. Pierre Date: Tue Jun 26 13:19:56 2012 -0400 Post-release version bump M configure.ac commit 78ac358fcb3135574377cea00781edd19bad6065 Author: Jasper St. Pierre Date: Tue Jun 26 13:18:33 2012 -0400 Release 3.5.3 M NEWS commit 1fba5a23c30af1a8cd07a6bd2f85e3c805129813 Author: Sasi Bhushan Boddepalli Date: Thu Jun 21 14:27:51 2012 +0530 Updated Telugu Translation M po/te.po commit 4a00f1eb30bc33afc14e647efb6ec1450ca7ff51 Author: Nilamdyuti Goswami Date: Thu Jun 21 14:01:14 2012 +0530 Assamese translation reviewed M po/as.po commit d1beaf075f02281e28bb730f3e43dd15dfea2ef3 Author: Jasper St. Pierre Date: Wed Jun 20 14:57:58 2012 -0400 libmenu: Allow grabbing the parent from any GMenuTreeItem Oh how I wish we had real inheritance. M libmenu/gmenu-tree.c M libmenu/gmenu-tree.h commit ade49e7f07ef6cb0168002f6c12a4be6c1bdbee8 Author: Jasper St. Pierre Date: Mon Jun 18 18:08:26 2012 -0400 libmenu: Add a way to grab NoDisplay on an item and all of its parents Some applications may want a way to show or hide a tree entry based on its visibility in the actual tree. https://bugzilla.gnome.org/show_bug.cgi?id=678419 M libmenu/gmenu-tree.c M libmenu/gmenu-tree.h commit fef1440c7b440d0360992c2178ef636b02ef777a Author: Jasper St. Pierre Date: Fri Jun 1 09:16:01 2012 -0400 libmenu: Allow grabbing the GMenuTree from any GMenuTreeItem https://bugzilla.gnome.org/show_bug.cgi?id=677270 M libmenu/gmenu-tree.c M libmenu/gmenu-tree.h commit eec6221af6a8ee04dddb11af9dbac45819a296db Author: Vincent Untz Date: Wed Jun 20 19:56:20 2012 +0200 misc: Add Jasper as co-maintainer Jasper is completely fixing alacarte, so he's the menu guy now :-) M MAINTAINERS M gnome-menus.doap commit 7a87b12a86d7f8dfebca2a33791c04d5249de692 Author: Tom Tryfonidis Date: Tue Jun 19 19:26:24 2012 +0300 Updated Greek translation M po/el.po commit 8cd6efdf03a7056ed4c7373e00a2eb7657e5c6cc Author: Jasper St. Pierre Date: Sat Jun 2 13:27:24 2012 -0400 libmenu: Add the forgotten gmenu_tree_iter_get_separator https://bugzilla.gnome.org/show_bug.cgi?id=677344 M libmenu/gmenu-tree.c M libmenu/gmenu-tree.h commit 2a83843d381cebf95a775a9ae2c176c5ae816fc8 Author: Colin Walters Date: Fri Jun 8 16:34:12 2012 -0400 gnome-menus: Fix g-ir-scanner warnings M libmenu/gmenu-tree.c commit 4973cf303cb9d5e8b160fb1ac0649ac2f864b8b4 Author: Ihar Hrachyshka Date: Fri Jun 8 18:55:01 2012 +0300 Updated Belarusian translation. M po/be.po commit 175f03aeef932bf9bf0527e11624e9a8e126e769 Author: Matthias Clasen Date: Tue Jun 5 09:21:45 2012 -0400 Post-release version bump M configure.ac commit c43bc6bc305b156be066fba6ae385b46e0063409 Author: Matthias Clasen Date: Tue Jun 5 09:17:34 2012 -0400 3.5.2 M NEWS M configure.ac commit 326c6ca76a4a739bb3d669ed4ce2d36d9b89eabe Author: Kjartan Maraas Date: Wed May 30 14:06:31 2012 +0200 Updated Norwegian bokmål translation M po/nb.po commit efc14ec052a53b706d8d2132ec62426cd46f54ea Author: Aleksej Kabanov Date: Sun May 27 00:56:26 2012 +0400 Updated Russian translation M po/ru.po commit 4693fdd844b623a69e02ec853f90888aee82a728 Author: Yaron Shahrabani Date: Fri May 25 15:29:40 2012 +0300 Updated Hebrew translation. M po/he.po commit ef10032c3c9d20cb46482829f7bbf5e09c311dee Author: Matej Urbančič Date: Wed May 23 20:02:27 2012 +0200 Updated Slovenian translation M po/sl.po commit 64606ea5a3c0f3faa5f406a63c53a51a1eb8823a Author: Fran Diéguez Date: Sun May 20 17:57:35 2012 +0200 Updated Galician translations M po/gl.po commit 068bfbdbcb328c0f7eb29faecaf41c82e5633bb5 Author: Daniel Mustieles Date: Mon May 14 16:49:12 2012 +0200 Updated Spanish translation M po/es.po commit 9452548505add051e3ca099332edad2dc7764181 Author: Giovanni Campagna Date: Tue May 1 00:23:39 2012 +0200 layout: Add a separate category for Web Applications For technical reasons, it is not possible to guess an appropriate category for web applications created with Epiphany, so they would all end up in Others. Instead, make up a category and submenu just for them. https://bugzilla.gnome.org/show_bug.cgi?id=675198 M desktop-directories/Makefile.am A desktop-directories/X-GNOME-WebApplications.directory.in M layout/applications.menu M po/POTFILES.in commit b93cc6818c7ee7be1339f0f7bf8d3ec24790df59 Author: Vincent Untz Date: Thu Feb 2 10:19:02 2012 +0100 util: Add --include-unallocated option to gnome-menu-spec-test We want easy testing for GMENU_TREE_FLAGS_INCLUDE_UNALLOCATED. https://bugzilla.gnome.org/show_bug.cgi?id=668512 M util/test-menu-spec.c commit 159bfe766f024598e967765a418430dfa46d2197 Author: Vincent Untz Date: Thu Feb 2 10:16:40 2012 +0100 libmenu: Add GMENU_TREE_FLAGS_INCLUDE_UNALLOCATED flag Add a new GMENU_TREE_FLAGS_INCLUDE_UNALLOCATED flag to add in the root directory entries that are not allocated anywhere else. This is useful if the user really wants to get absolutely all entries (in addition to using INCLUDE_EXCLUDED, which is a bit different, and INCLUDE_NODISPLAY). Add gmenu_tree_entry_get_is_unallocated() API matching this flag. https://bugzilla.gnome.org/show_bug.cgi?id=668512 M libmenu/gmenu-tree.c M libmenu/gmenu-tree.h commit 6b6956b75de4566772c7144a01b39634671b364f Author: Piotr Drąg Date: Mon Apr 16 19:15:35 2012 +0200 Added Kashubian translation M po/LINGUAS A po/csb.po commit 9d68b854b35573d5567fe79cc58d011ae23ed23e Author: Alexander Shopov Date: Sun Apr 1 18:11:32 2012 +0300 Updated Bulgarian translation M po/bg.po commit da8b0ff0c8733d8f94d7e29f1ba67ff2e9bace98 Author: Vincent Untz Date: Mon Mar 26 10:37:18 2012 +0200 release: post-release bump to 3.4.1 M configure.ac commit e1b3578220871cd5e607a93ab1b251da9e56d4d6 Author: Vincent Untz Date: Mon Mar 26 10:36:57 2012 +0200 release: 3.4.0 M NEWS M configure.ac commit f553195fee927f128963702fc9443ce6d84fb20b Author: Khoem Sokhem Date: Sat Mar 10 13:24:57 2012 +0100 Add initial Khmer translation M po/LINGUAS A po/km.po commit f289d086d2f3162bef06eada7276cd473f73fbfe Author: Bahodir Mansurov <6ahodir@gmail.com> Date: Fri Feb 24 21:24:22 2012 -0500 Updated Uzbek@cyrillic translation M po/uz@cyrillic.po commit 17b1dc3a3c850794bfe3df028ed72483fb21489e Author: Vincent Untz Date: Wed Feb 22 15:10:35 2012 +0100 build: Generate ChangeLog on make dist M Makefile.am commit ac467f5484a8c32ccb1484147003dbea8ee51d99 Author: Vincent Untz Date: Wed Feb 22 15:03:08 2012 +0100 build: Update git.mk and ignore generated tarballs M Makefile.am M git.mk commit 82f575edbd949f682bc9242263e318f7e1d4d57c Author: Vincent Untz Date: Mon Feb 6 12:27:00 2012 +0100 release: post-release bump to 3.3.90 M configure.ac commit 44030054b1752ea353c010ec0588fbc804ffc662 Author: Vincent Untz Date: Mon Feb 6 12:26:54 2012 +0100 release: 3.3.5 M NEWS M configure.ac commit 2adf8dc80b99459450e891161b3b7f254317d9b5 Author: Danishka Navin Date: Wed Feb 1 17:29:09 2012 +0530 fixed few typos in si.po file M po/si.po commit 17d5a7f7942f9045acdb8d5cbe18c967169d054e Author: Danishka Navin Date: Wed Feb 1 16:08:30 2012 +0530 fixed few typos in si.po file M po/si.po commit 7b895320a73ef066508d0d411c9ce1e725188a96 Author: Kjartan Maraas Date: Wed Jan 25 14:47:48 2012 +0100 Updated Norwegian bokmål translation M po/nb.po commit a84c7d360b4cb22a10cac76c95eff4654effd730 Author: Andiswa Mvanyashe Date: Tue Jan 17 08:09:02 2012 +0200 Updated translation for Xhosa (xh) M po/xh.po commit 91a67746970ca4327c03c2f048e48c666a1f3776 Author: Vincent Untz Date: Tue Dec 20 09:45:45 2011 +0100 build: Create xz tarballs M configure.ac commit 581bdf9920d18024413fa7afcacd83e733a26089 Author: Jiro Matsuzawa Date: Fri Nov 11 02:51:39 2011 +0900 Updated Japanese translation M po/ja.po commit 8d46c3a20c4718384b66180990258c0ad6c12f49 Author: Vincent Untz Date: Mon Oct 24 13:58:21 2011 +0200 release: post-release bump to 3.3.2 M configure.ac commit f258c9c144c569772a1ea7ea81179c98110fc58f Author: Vincent Untz Date: Mon Oct 24 13:58:14 2011 +0200 release: 3.3.1 M NEWS M configure.ac commit fbabc41cb6f2c6520f9f117137fa296a7f8340f3 Author: Florian Müllner Date: Fri Oct 21 19:57:55 2011 +0200 libmenu: Ignore invalid desktop entries Both "Name" and "Exec" are mandatory keys according to the desktop entry spec; some .desktop files missing one or the other have been spotted in the while, so ignore them explicitly. https://bugzilla.gnome.org/show_bug.cgi?id=662409 M libmenu/desktop-entries.c commit 2179d84e1d7ce5a5b79f2736bf64a13e7bca1b4d Author: Vincent Untz Date: Fri Oct 7 10:43:02 2011 +0200 layout: Put the Other menu at the end It's really a special menu, which should not be handled with alphabetical order. M layout/applications.menu commit 480edac95baba553b3cfc471817779e50a367871 Author: Vincent Untz Date: Wed Sep 28 09:24:19 2011 +0200 release: post-release bump to 3.2.1 M configure.ac commit 2d5915b925791d2d3bd736704a8706553455bd26 Author: Vincent Untz Date: Wed Sep 28 09:24:11 2011 +0200 release: 3.2.0.1 M NEWS M configure.ac commit f1c76629d33a616089a15bd034708d21e8bd9a87 Author: Vincent Untz Date: Tue Sep 27 12:09:35 2011 +0200 editor: Fix to work with latest pygi https://bugzilla.gnome.org/show_bug.cgi?id=660112 M simple-editor/GMenuSimpleEditor/main.py M simple-editor/GMenuSimpleEditor/menufilewriter.py M simple-editor/GMenuSimpleEditor/menutreemodel.py commit b87a8c15e95fa64c94343b0bfdc6523e8f6c1bbc Author: Vincent Untz Date: Mon Sep 26 11:09:13 2011 +0200 release: post-release bump to 3.2.1 M configure.ac commit 4b0e41e9b45087402e64d90c2760c8d2101cd53e Author: Vincent Untz Date: Mon Sep 26 11:09:08 2011 +0200 release: 3.2.0 M NEWS commit 36f7db2473f1d6a0c7a1b1e05e056210ac87e80b Author: Nilamdyuti Goswami Date: Sat Sep 24 15:27:52 2011 +0530 Updated Assamese Translations:bugzilla#659595 M po/as.po commit 1412cf2222ef8e0f33e9bfa234cf7fdbf6aee035 Author: ipraveen Date: Fri Sep 23 16:32:28 2011 +0530 Updated Telugu Translation M po/te.po commit 3ecf6975346ab77b111a99c45b0f7a79228b202f Author: ipraveen Date: Fri Sep 23 16:26:27 2011 +0530 Updated Telugu Translation M po/te.po commit 26e1dd8864b18c5dee038a929bc23cfcadc0f6e1 Author: Nilamdyuti Goswami Date: Thu Sep 22 20:17:41 2011 +0200 Updated Assamese translation M po/as.po commit 612011be0953e15f349946ba48df5e6e8f036282 Author: Vincent Untz Date: Mon Sep 19 19:34:28 2011 +0200 release: post-release bump to 3.2.0 M configure.ac commit 5d7dd75fbf694cfd31f04a3f81c80786b1b5841d Author: Vincent Untz Date: Mon Sep 19 19:34:21 2011 +0200 release: 3.1.92 M NEWS M configure.ac commit 49406208c5b1bfe449ed46c20d0bf16c7a363518 Author: Ihar Hrachyshka Date: Sun Sep 18 18:22:42 2011 +0300 Updated Belarusian translation (some fixes after manual testing). M po/be.po commit c3cc2e1d2dfa4f219c01c8dfee4d14e8aebd3a1e Author: Jiro Matsuzawa Date: Sat Sep 17 12:08:06 2011 +0900 Updated Japanese translation M po/ja.po commit 8c4aa57ca8ff56f1941899eb3c67babd2b7a3839 Author: dmustieles Date: Wed Sep 14 17:37:05 2011 +0200 Updated Spanish translation M po/es.po commit ecb47adb12c8ec9a3998efa96656dfedfe002525 Author: Vincent Untz Date: Mon Aug 29 08:43:26 2011 +0200 libmenu: Fix build failure with --enable-debug gmenu_tree_entry_get_name() was still being used. https://bugzilla.gnome.org/show_bug.cgi?id=656714 M libmenu/gmenu-tree.c commit 20d76fb2f1664aadf0b33c7c91603b47dfe04ef0 Author: Matthias Clasen Date: Sun Aug 28 19:22:26 2011 -0400 Post-release version bump M configure.ac commit 002662a50ab7fbce7398596a53e7270fc6f168e3 Author: Matthias Clasen Date: Sun Aug 28 19:22:00 2011 -0400 3.1.90 M NEWS commit 739d3f6ef7d533544f1ec2b4d6e237bfaca99e85 Author: Jasper St. Pierre Date: Sun Aug 21 17:27:50 2011 -0400 libmenu: Don't try to unref potentially NULL pointers When a DIRECTORY desktop entry fails to load, we'll try to unref some NULL pointers. This was causing some warnings. https://bugzilla.gnome.org/show_bug.cgi?id=657042 M libmenu/desktop-entries.c commit 5fade40dc3ebff3b4c00cdf37e6d7198e39e9bce Author: Vincent Untz Date: Fri Aug 12 11:50:59 2011 +0200 release: post-release bump to 3.1.90 M configure.ac commit 8e0ecffe8a6d4434dab086525f52d05e00edf0e9 Author: Vincent Untz Date: Fri Aug 12 11:50:52 2011 +0200 release: 3.1.5 M NEWS M configure.ac commit 61c87a8e66bfa1d7a823a588b073e3922edc78c3 Author: Vincent Untz Date: Fri Aug 12 10:44:32 2011 +0200 libmenu: Support XDG_CURRENT_DESKTOP https://bugzilla.gnome.org/show_bug.cgi?id=653440 M libmenu/desktop-entries.c commit f0e41152be32a3e07772b53a6c3a0d4dbb0545d9 Author: Colin Walters Date: Tue Aug 2 23:13:30 2011 -0400 gmenu_tree_get_entry_by_id: New API It's useful for gnome-shell if we have an index by desktop file ID to the tree entry. The cost is very small. https://bugzilla.gnome.org/show_bug.cgi?id=655868 M libmenu/gmenu-tree.c M libmenu/gmenu-tree.h commit 2a17c07927cfc23c9837969ac43f2fbcf9e433d7 Author: Vincent Untz Date: Sat Jul 23 10:06:35 2011 +0200 build: Require gio-unix-2.0 >= 2.29.15 g_desktop_app_info_get_show_in() was pushed, but after 2.29.14. M configure.ac commit 3fae17cc6e86b3e988df481a05a386e005b7637c Author: Vincent Untz Date: Fri Jul 22 13:21:17 2011 +0200 editor: Stop editing settings.menu We dropped that file a while ago. We could replace this with gnomecc.menu, but gnome-control-center loads the file from the system configuration and explicitly ignore the user changes there... M simple-editor/GMenuSimpleEditor/menutreemodel.py commit 1cb6fe8f97cc5a12933bd77d278a16827e763c13 Author: Vincent Untz Date: Fri Jul 22 08:55:19 2011 +0200 libmenu: Unset GMenuTree:menu-basename if GMenuTree:menu-path is set Since GMenuTree:menu-basename has a non-NULL default value, we really want to unset it if the user sets GMenuTree:menu-path, to avoid any potential confusion. We need a constructor to do this magic, as the properties are construct-only, and doing this anywhere else would be too late. M libmenu/gmenu-tree.c commit 02d0f749a1c4f406fa773254c2095e6c4f3d5979 Author: Vincent Untz Date: Thu Jul 21 18:28:56 2011 +0200 libmenu: Group GMenuTreeFlags flags in a pseudo-consistent way Since we're breaking ABI, let's go crazy and change the value of flags. Grouping flags by some sort of meaning (include ones, show ones, misc. ones) makes the header a bit nicer to read. M libmenu/gmenu-tree.h commit 9e2b4abdc8ec63e20f4c2a52b3047f1d767ebd8d Author: Vincent Untz Date: Thu Jul 21 18:19:24 2011 +0200 editor: Port to introspection-based gmenu bindings M simple-editor/GMenuSimpleEditor/maindialog.py M simple-editor/GMenuSimpleEditor/menutreemodel.py commit 6f4cf16ec2633ebc62cb91759dbfd01923cd3c73 Author: Vincent Untz Date: Thu Jul 21 18:09:25 2011 +0200 libmenu, util: Rename gmenu_tree_get_menu_path() Renamed to gmenu_tree_get_canonical_menu_path(), to avoid any potential confusion with the menu-path property. M libmenu/gmenu-tree.c M libmenu/gmenu-tree.h M util/test-menu-spec.c commit 8bc90706c9bfbf0d55c2eaddc859afb4a52a3281 Author: Vincent Untz Date: Thu Jul 21 18:07:54 2011 +0200 libmenu: Add API to load menu file from full path Add gmenu_tree_new_for_path() and GMenuTree:menu-path. M libmenu/gmenu-tree.c M libmenu/gmenu-tree.h commit c653bbe8904acca4340f70574357d843e549035c Author: Vincent Untz Date: Thu Jul 21 17:22:20 2011 +0200 libmenu, util: Rename GMenuTree:name to GMenuTree:menu-basename This is a much clearer name for the property, else people might assume they can use a full path, or something that is not a basename. Note that a relative path will work, but that really should not be of much use, hence the choice of basename. M libmenu/gmenu-tree.c M libmenu/gmenu-tree.h M util/gnome-menus-ls.js commit c45596c3d87fa3e344b9e9a32c51190e12d67d93 Author: Vincent Untz Date: Thu Jul 21 17:13:18 2011 +0200 libmenu: Fix critical warning when trying to load "" menu Yes, this is a weird use of the API, but still. M libmenu/gmenu-tree.c commit 4c85e0ee58a4e8b11f1e5527909eefd02133a72a Author: Vincent Untz Date: Thu Jul 21 17:00:25 2011 +0200 libmenu: Default GMenuTree::name property to "applications.menu" This is useful for introspection bindings, since users will not necessarily think of setting the menu name property at construct time. And it's a saner default than NULL. M libmenu/gmenu-tree.c commit d8be0079a48bb7b486cc2b1355ee0cea2596fecb Author: Vincent Untz Date: Thu Jul 21 16:55:57 2011 +0200 libmenu: Do not keep internal load_error in GMenuTree This is not used, and it actually gets corrupted at some point (since g_propagate_error means we lose the ownership of the GError). M libmenu/gmenu-tree.c commit bb3e154f60f81e97178b0cb8457827b29ee20692 Author: Vincent Untz Date: Thu Jul 21 16:01:46 2011 +0200 build: Bump version to 3.1.4 This is needed so users can know which version to require for the new API. M configure.ac commit 70e9aaacbe0783fe11b7c69310873e0dc638b274 Author: Vincent Untz Date: Thu Jul 21 15:57:18 2011 +0200 libmenu: Drop GMenuTreeDirectoryRoot This was only used internally, but with no reason. M libmenu/gmenu-tree.c commit 17961198a182d5f68e4773427b1c8f735e0a01cd Author: Vincent Untz Date: Thu Jul 21 15:48:35 2011 +0200 libmenu: Make gmenu_tree_directory_get_icon() return a GIcon It's much better to use GIcon than icon name strings. M libmenu/desktop-entries.c M libmenu/desktop-entries.h M libmenu/gmenu-tree.c M libmenu/gmenu-tree.h commit 3c6ad47de8cfc3581fee66107a41ba4b39dd0de0 Author: Vincent Untz Date: Thu Jul 21 15:26:11 2011 +0200 libmenu: Correctly deal with OnlyShowIn/NotShowIn We're using new glib API for this. Also, we do not remove entries because of OnlyShowIn/NotShowIn before processing the layout, as we might want to add a GMenuTreeFlags value in the future, to include .desktop files that are excluded because of OnlyShowIn/NotShowIn. M configure.ac M libmenu/desktop-entries.c M libmenu/desktop-entries.h M libmenu/gmenu-tree.c commit 66557ef586adff091c8ce69a2f472da00dcd89ba Author: Vincent Untz Date: Mon Jul 4 17:23:32 2011 +0200 build: Drop check for -fno-strict-aliasing support This was only needed for our previous python bindings. M configure.ac commit fc5492ab992fde2b8d378ed91f0b4c6c62cbe69b Author: Colin Walters Date: Tue Jun 14 13:52:06 2011 -0400 libmenu: Dispose of source before context to avoid possible double unref M libmenu/menu-layout.c commit 21062503fff272872fdfc5fb468288b1faa85127 Author: Colin Walters Date: Tue Jun 14 13:25:47 2011 -0400 libmenu: Make refcounts volatile as g_atomic_* expect M libmenu/gmenu-tree.c commit b8b2471e8a3bd0f2d469dea41067721919359c22 Author: Vincent Untz Date: Tue Jun 14 09:04:31 2011 +0200 libmenu: Correctly look up at NoDisplay for .desktop files We were using Hidden instead. This requires glib 2.29.9. M configure.ac M libmenu/desktop-entries.c commit 6ba57847943cd74839a306ed6c8926401e370b85 Author: Vincent Untz Date: Sun Jun 12 12:50:05 2011 +0200 libmenu: Simplify some code M libmenu/desktop-entries.c commit 2c47cdbebb15715d91d95aa4babc2de7a993dbd4 Author: Vincent Untz Date: Sun Jun 12 12:38:30 2011 +0200 libmenu: Fix loading of .directory files We were always returning FALSE, leaking some data and not displaying a debug message in case of error (like for .desktop files). M libmenu/desktop-entries.c commit 6aff671fbeccb5ff7ec3290d0fd6345fbbb632c8 Author: Vincent Untz Date: Sun Jun 12 12:34:26 2011 +0200 util: Fix build after gmenu_tree_alias_get_item_type() renaming M util/test-menu-spec.c commit 398fd5c145524b7961c9d1dbc105c1cd49d98103 Author: Vincent Untz Date: Sun Jun 12 12:12:55 2011 +0200 doc: Do not reference non-existing gmenu_tree_iter_get_next_type() This is gmenu_tree_iter_next(). M libmenu/gmenu-tree.c commit e71520d5d2ccc4392b046e5feb84349d71a2c69f Author: Vincent Untz Date: Sun Jun 12 12:11:31 2011 +0200 libmenu: Handle GenericName for .directory files While we do not strictly need them right now, this is allowed by the specification and might be used later. M libmenu/desktop-entries.c M libmenu/desktop-entries.h M libmenu/gmenu-tree.c M libmenu/gmenu-tree.h commit f29590a8c3771af47debf75c929170fe59877dce Author: Vincent Untz Date: Sun Jun 12 11:53:59 2011 +0200 libmenu: Fix desktop_entry_copy() for .desktop file We were not dealing with the appinfo field at all. M libmenu/desktop-entries.c commit 0d5f1909518f86d7283cde404e9470e6f55a42d2 Author: Vincent Untz Date: Sun Jun 12 11:41:20 2011 +0200 libmenu: Rename gmenu_tree_alias_get_item_type Renamed to gmenu_tree_alias_get_aliased_item_type, to avoid any potential confusion about the typoe of what item this is. M libmenu/gmenu-tree.c M libmenu/gmenu-tree.h commit 4d96af04a6d1d2e211c2005cc9d03d2b0e1a85c0 Author: Vincent Untz Date: Sun Jun 12 11:33:16 2011 +0200 libmenu: Drop gmenu_tree_entry_get_is_nodisplay() from header The function got dropped, and having a similar function in GDesktopAppInfo would be the right thing to do. M libmenu/gmenu-tree.h commit f587ca8642070892fd4172ab01735c4a0789dc19 Author: Vincent Untz Date: Sun Jun 12 11:12:42 2011 +0200 libmenu: Remove unneeded cast M libmenu/gmenu-tree.c commit 2167c042bc089ba1e570ac9ba60dcdddb1d1d3c3 Author: Vincent Untz Date: Sun Jun 12 11:05:52 2011 +0200 libmenu: Rename desktop_entry_desktop_get_icon to desktop_entry_get_icon The additional desktop in the name was confusing. M libmenu/desktop-entries.c M libmenu/desktop-entries.h M libmenu/gmenu-tree.c commit 0f7d374006f32559893629fba47fecfaa031d4f8 Author: Vincent Untz Date: Sun Jun 12 10:53:05 2011 +0200 libmenu: Fix getting the flags property This was not handled at all. M libmenu/gmenu-tree.c commit 0eaed2f94d52a8b03263e5976a4d16104546929c Author: Vincent Untz Date: Sun Jun 12 10:39:54 2011 +0200 libmenu: Drop support for "KDE Desktop Entry" group This was deprecated for years, and with the move to GDesktopAppInfo, we don't support this group for .desktop files anyway, so it makes no sense to keep supporting it for .directory files. M libmenu/desktop-entries.c commit 536c6d510f53bbf76ff0c4134e935c8e7cf552da Author: Vincent Untz Date: Sun Jun 12 10:31:37 2011 +0200 libmenu: Use explicit GMENU_TREE_FLAGS_NONE instead of 0 M libmenu/gmenu-tree.c commit d5da147d09ca09e593e4a88c11b782ee7479268f Author: Vincent Untz Date: Sun Jun 12 09:58:20 2011 +0200 Spaces/tabs fixes M libmenu/desktop-entries.c M libmenu/desktop-entries.h M libmenu/gmenu-tree.c M libmenu/gmenu-tree.h M libmenu/menu-layout.c commit 6920649082e3c0be363094054eecc9f1185a8185 Author: Vincent Untz Date: Wed Jun 8 17:55:43 2011 +0200 util: Do not call exit() in test tool A simple return statement is enough. M util/test-menu-spec.c commit f88d8c4dd17055932a6b335c008b496327d536b3 Author: Vincent Untz Date: Wed Jun 8 17:48:58 2011 +0200 build: Fix pkg-config to require gio-unix-2.0 and not glib-2.0 We reference gio/gdesktopappinfo.h in our public header. M libmenu/libgnome-menu-3.0-uninstalled.pc.in M libmenu/libgnome-menu-3.0.pc.in commit cfb20fb779eeed93a0c9571a8340251c8964ef69 Author: Vincent Untz Date: Wed Jun 8 17:44:16 2011 +0200 gi: Add/Fix annotations to remove introspection warnings M libmenu/gmenu-tree.c commit 91a698f7860b3d8d5f29ffcdfcdbadf0cc347bad Author: Vincent Untz Date: Wed Jun 8 17:39:42 2011 +0200 build: Version the library name and the pkg-config file The library is now libgnome-menu-3, the pkg-config file is libgnome-menu-3.0, the header directory is gnome-menus-3.0 and the gettext package is gnome-menus-3.0. This way, it's possible to keep the old libgnome-menu around if some applications still need it. M configure.ac M libmenu/Makefile.am R080 libmenu/libgnome-menu-uninstalled.pc.in libmenu/libgnome-menu-3.0-uninstalled.pc.in R072 libmenu/libgnome-menu.pc.in libmenu/libgnome-menu-3.0.pc.in M util/Makefile.am commit 669f03a403ca83facf83020e9c37b894c67f1b9b Author: Vincent Untz Date: Wed Jun 8 16:19:49 2011 +0200 build: Remove old python-related build stuff M configure.ac D m4/python.m4 commit 1cf4482fa3416b98916653db23b40edb26d8646d Author: Colin Walters Date: Thu Apr 21 13:21:57 2011 -0400 Bump to GMenu-4.0.gir; this matches the package version. M libmenu/Makefile.am commit 5aa2c53ef25f684a848312455dad396f28c96a90 Author: Colin Walters Date: Wed Apr 20 17:48:07 2011 -0400 Replace Python example with JS M util/Makefile.am A util/gnome-menus-ls.js D util/gnome-menus-ls.py commit f0101da1c3df623f5a0023d9c787535e89476291 Author: Colin Walters Date: Wed Apr 20 17:42:39 2011 -0400 Remove GMenuTreeItem from public API gmenu_tree_directory_get_contents() wasn't actually bindable because there's no way to express to introspection the inheritance hierarchy (and to do the runtime type checking necessary via gmenu_tree_item_get_item_type()). Therefore, drop it, and instead add explicit functions where each type is needed. So for gmenu_tree_directory_get_contents(), we instead add an explicit typesafe iterator object. The only other instance was gmenu_tree_alias. Note that gmenu_tree_item_ref() and gmenu_tree_item_unref() remain; they're C only, and already just took "gpointer". M libmenu/gmenu-tree.c M libmenu/gmenu-tree.h M util/test-menu-spec.c commit 76895ad01d450696519425d03beab8845230397b Author: Colin Walters Date: Wed Apr 20 17:16:25 2011 -0400 introspection: scan gmenu-tree.c Now that we're actually adding gtk-doc. M libmenu/Makefile.am commit 0877f894a6c74f0dc7522e9cb93cc223097d1494 Author: Colin Walters Date: Mon Apr 18 16:17:04 2011 -0400 Document a few functions M libmenu/gmenu-tree.c commit bd78be7ad777f70d2390309507162f5dc267ea2f Author: Colin Walters Date: Mon Apr 18 15:36:01 2011 -0400 gmenu_tree_entry_get_parent: New function Earlier we moved this down to GMenuTreeDirectory, but it turns out gnome-shell does expect to be able to get the parent of a GMenuTreeEntry. In the future I want to nuke that code, but for now just readd this functionality. M libmenu/gmenu-tree.c M libmenu/gmenu-tree.h commit 94b2ea48a5221bd6dc82b8b468bb85ea6d420e57 Author: Colin Walters Date: Mon Apr 18 10:10:53 2011 -0400 layout: Use thread-default main context for callbacks Rather than hardcoding g_idle_add(); this gives us future flexibility for thread support. https://bugzilla.gnome.org/show_bug.cgi?id=647968 M libmenu/menu-layout.c commit 52f9f7017779b6691e2b3f46f6c24e37f219abbf Author: Colin Walters Date: Sun Apr 17 11:22:32 2011 -0400 Remove gmenu_tree_directory_get_tree() This causes a circular reference internally, and API consumers can just keep track of it easily enough externally. https://bugzilla.gnome.org/show_bug.cgi?id=647968 M libmenu/gmenu-tree.c M libmenu/gmenu-tree.h commit 5d1e03adc206f52dec82f2b5e8831022321cb1f8 Author: Colin Walters Date: Sun Apr 17 10:17:20 2011 -0400 Switch to gslice for most data This is more efficient for the small items we have here. https://bugzilla.gnome.org/show_bug.cgi?id=647968 M libmenu/gmenu-tree.c commit 4f47a654d91cc3b08d46ef518a42c7f484f01be3 Author: Colin Walters Date: Sun Apr 17 09:57:47 2011 -0400 Rename gmenu_tree_get_menu_file() to gmenu_tree_get_menu_path() Document it and clean it up. https://bugzilla.gnome.org/show_bug.cgi?id=647968 M libmenu/gmenu-tree.c M libmenu/gmenu-tree.h M util/test-menu-spec.c commit 13920c91868f77a964b4498b00072563608f583e Author: Colin Walters Date: Sun Apr 17 09:51:58 2011 -0400 Further propagate GError for gmenu_tree_load_sync() We had some GError use internally; clean things up so we propagate it more consistently to the top of gmenu_tree_load_sync(). https://bugzilla.gnome.org/show_bug.cgi?id=647968 M libmenu/gmenu-tree.c commit 8ecb1aa5d762d25f787002dd001e92e404efabe9 Author: Colin Walters Date: Sun Apr 17 09:31:34 2011 -0400 Add explicit gmenu_tree_load_sync() Rather than having _get_root_directory() be lazy, require users to explicitly load via this function (or in the future, an async variant). https://bugzilla.gnome.org/show_bug.cgi?id=647968 M libmenu/gmenu-tree.c M libmenu/gmenu-tree.h M util/test-menu-spec.c commit a2bda1462b6b46b1c8e47a19977f15c294226ef9 Author: Colin Walters Date: Sun Apr 17 08:58:19 2011 -0400 Lower gmenu_tree_item_get_parent to gmenu_tree_directory_get_parent Introspection doesn't know about the GMenuTreeItem "subclassing", so we would have to duplicate the _get_parent method on all subclasses - but in practice it only seems to be used on directories, so just lower it there. https://bugzilla.gnome.org/show_bug.cgi?id=647968 M libmenu/gmenu-tree.c M libmenu/gmenu-tree.h M util/test-menu-spec.c commit 14c163c8a8d35fe2daa9c0973495fb4462a3ddec Author: Colin Walters Date: Sun Apr 17 08:46:14 2011 -0400 GMenuTreeItem: Register boxed types, drop user data This was a hack for the static Python bindings, no longer necessary after we register proper boxed types for the structures. Convert the refcount to an atomic integer too; the _unref may be called from a garbage collector thread in bindings. https://bugzilla.gnome.org/show_bug.cgi?id=647968 M libmenu/gmenu-tree.c M libmenu/gmenu-tree.h commit 8eaf04da66be9437c518ac21e27cbbb67abcd3f5 Author: Colin Walters Date: Sun Apr 17 08:44:02 2011 -0400 Rename gmenu_tree_item_get_type() to _get_item_type() The _get_type() namespace suffix is reserved for GType. https://bugzilla.gnome.org/show_bug.cgi?id=647968 M libmenu/gmenu-tree.c M libmenu/gmenu-tree.h M util/test-menu-spec.c commit 474b86aac925d7b45d3bbbaba9111719ca0d9b8a Author: Colin Walters Date: Sun Apr 17 08:40:47 2011 -0400 Replace monitor API with a simple "changed" signal So much simpler... https://bugzilla.gnome.org/show_bug.cgi?id=647968 M libmenu/gmenu-tree.c M libmenu/gmenu-tree.h M util/test-menu-spec.c commit 21672a2d0ee94a9588fc15cf0cd4c5ffdfc818c1 Author: Colin Walters Date: Sun Apr 17 07:50:19 2011 -0400 Convert to GObject, drop static Python bindings GMenuTree is now a GObject. Drop the static Python bindings, since introspection gives us coverage of most of the API now. https://bugzilla.gnome.org/show_bug.cgi?id=647968 M Makefile.am M configure.ac M libmenu/gmenu-tree.c M libmenu/gmenu-tree.h M libmenu/menu-monitor.c D python/Makefile.am D python/gmenu.c M util/gnome-menus-ls.py M util/test-menu-spec.c commit 0a7e4736d221ada2167a064c8b4aa9710a2e04bf Author: Colin Walters Date: Sun Apr 17 07:32:07 2011 -0400 Drop GMenuTree caching and support for absolute paths This is work towards converting to GObject; the API to create a tree is now just gmenu_tree_new(). First, the internal caching makes things very complex for little gain - gnome-shell only creates one GMenuTree, and gnome-panel could pretty easily be converted to do so. In a Google Code Search, I couldn't find anyone using absolute paths for menus, and looking through the revision history, I don't see a rationale for it. So, just drop it too. https://bugzilla.gnome.org/show_bug.cgi?id=647968 M libmenu/gmenu-tree.c M libmenu/gmenu-tree.h M python/gmenu.c M util/test-menu-spec.c commit 903c144be9527f048fa1389c39872d0f22740b62 Author: Colin Walters Date: Sun Apr 17 07:17:22 2011 -0400 GMenuTreeFlags: Register with GType https://bugzilla.gnome.org/show_bug.cgi?id=647968 M libmenu/gmenu-tree.c M libmenu/gmenu-tree.h commit d660a95756c99fe053ffd4fbaf523008868f5a67 Author: Colin Walters Date: Sun Apr 17 07:10:01 2011 -0400 Fold sorting into GMenuTreeFlags There's only two sorts right now, and so we can make one the default and select the other with the flags. Drop the ability to set the sort at runtime; this never was compatible with the current GMenuTree caching, and also I'm trying to move GMenuTree towards being immutable. M libmenu/gmenu-tree.c M libmenu/gmenu-tree.h M python/gmenu.c commit 4d00dcf9ce31b248c61dc66430b881ba32e2d611 Author: Colin Walters Date: Sat Apr 16 14:09:57 2011 -0400 Rebase DesktopEntry on GDesktopAppInfo The main motivation for this work is to avoid gnome-shell having to read all .desktop files *twice* - once from gnome-menus, and once from gio (when doing MIME assocation etc.) This patch replaces almost all of the accessors for GMenuTreeEntry with the simple gmenu_tree_entry_get_app_info(). Note this patch depends on patches from (see bug 647967). https://bugzilla.gnome.org/show_bug.cgi?id=647968 M configure.ac M libmenu/Makefile.am M libmenu/desktop-entries.c M libmenu/desktop-entries.h M libmenu/gmenu-tree.c M libmenu/gmenu-tree.h M python/Makefile.am M python/gmenu.c M util/Makefile.am commit 8a01f3c268a1515fb4e9ee8c924dae20a0880bb1 Author: Colin Walters Date: Sat Apr 16 13:31:21 2011 -0400 desktop-entries.c: Split structure explicitly between .desktop and .directory This is code cleaup preparatory work for rebasing DesktopEntry on GDesktopAppInfo. These two cases are different; make this explicit via structure subclassing of a common base structure. https://bugzilla.gnome.org/show_bug.cgi?id=647968 M libmenu/desktop-entries.c commit 5a936c77ebb7ffddbc4e282662bb2d5f62a8a814 Author: Colin Walters Date: Sat Apr 16 11:54:17 2011 -0400 DesktopEntry: Clean up structure, make TryExec evaluation lazy This is preparatory work for rebasing DesktopEntry on top of GDesktopAppInfo. First, it doesn't make sense to represet a random subset of booleans as flags; just flatten these into a bitfield. Move the refcount to be a plain guint at top. Second, previously we were calling g_find_program_in_path when creating a .desktop file, even if the caller wasn't interested in whether the TryExec succeeded. Make this lazy. https://bugzilla.gnome.org/show_bug.cgi?id=647968 M libmenu/desktop-entries.c commit ea008b417c02e8238f58d2d419d01129c2de9226 Author: Colin Walters Date: Sat Apr 16 11:04:26 2011 -0400 DesktopEntry: Make basename const reference Avoids another malloc block. https://bugzilla.gnome.org/show_bug.cgi?id=647968 M libmenu/desktop-entries.c commit 6cf363f9eaf4ba8ea736ca0a9e8d427226f381f6 Author: Friedel Wolff Date: Thu Jul 7 22:30:06 2011 +0200 Add Zulu (zu) to LINGUAS M po/LINGUAS commit 756d19762d1793ebe80df661328a1c7a510b9773 Author: Priscilla Mahlangu Date: Thu Jul 7 22:28:26 2011 +0200 New translation for Zulu (zu) A po/zu.po commit 5c896c6418cbc6111c823cca5401204bd62c16dc Author: Ihar Hrachyshka Date: Sat Jun 25 20:33:13 2011 +0300 Updated Belarusian translation. M po/be.po commit 9841d10ecf88b816ea18f186b64d4f1798f0c21a Author: Carles Ferrando Date: Sun May 29 18:26:56 2011 +0200 [l10n]Updated Catalan (Valencian) translation M po/ca@valencia.po commit 7336104da09a01db012c6d7e4d11abc3bac2f4e8 Author: Vincent Untz Date: Tue Apr 26 09:12:06 2011 +0200 release: post-release bump to 3.0.2 M configure.ac commit 582b78f2d085130fb63537c8e7b1120ff77c9979 Author: Vincent Untz Date: Tue Apr 26 09:11:59 2011 +0200 release: 3.0.1 M NEWS M configure.ac commit 1febd5e57900e931b9aa53774a8c3caaff4f6a2a Author: Theppitak Karoonboonyanan Date: Mon Apr 25 08:41:18 2011 +0700 Updated Thai translation. M po/th.po commit df2f20a03a78d23665148c608b4955bb4702c2d9 Author: Anousak Souphavanh Date: Tue Apr 19 12:47:55 2011 +0300 l10n: Added Lao translation for gnome-menus M po/LINGUAS A po/lo.po commit 2a436ad5101f3a05a029ffaded8d60c412686540 Author: Vincent Untz Date: Mon Apr 4 23:04:28 2011 +0200 release: post-release bump to 3.0.1 M configure.ac commit 8f288a1114e95adf98f8186cb309e5cacad66105 Author: Vincent Untz Date: Mon Apr 4 23:04:21 2011 +0200 release: 3.0.0 M NEWS M configure.ac commit a153239bdf7126154ada0b7e22bf827860cad330 Author: Jamil Ahmed Date: Mon Apr 4 15:25:07 2011 +0600 Updated Bengali translation M po/bn.po commit 381752ab7db377bdabcfda08ab90353ce78770f7 Author: Jordi Serratosa Date: Mon Apr 4 00:14:46 2011 +0200 [l10n]Fixes on Catalan translation M po/ca.po commit 61593b848a8f03694a7963a0cb796aa06b82330c Author: Nguyễn Thái Ngọc Duy Date: Sat Apr 2 10:31:35 2011 +0700 Updated Vietnamese translation M po/vi.po commit 5d9e2b6e441b907ca9724ab3fdc02fc32fa12205 Author: Nguyễn Thái Ngọc Duy Date: Sat Apr 2 10:31:18 2011 +0700 po/vi.po: import from Damned Lies M po/vi.po commit e8d7b8fb9d128ac39e3e0f1dd6a8e96c7ce18e75 Author: krishnababu k Date: Fri Apr 1 13:21:53 2011 +0530 Updated Telugu translations done by praveen M po/te.po commit 4fb0ed4d0e7a4fb40e56af6d462467bd16d4c512 Author: Vincent Untz Date: Wed Mar 30 12:53:39 2011 +0530 layout: Show administration tools and old capplets in Other Right now, administration tools and capplets that used to be in the Control Center are not accessible in any way. With this patch, they appear in Other. While this is not perfect, this will make them accessible until we find the right solution. To do this, we just exclude .desktop files that match panels from the Control Center instead of excludings the Settings category, which is too broad. https://bugzilla.gnome.org/show_bug.cgi?id=645061 M layout/applications.menu commit 7c73245fcbd5feaf66df50ee6ca5e8c9132a11aa Author: Abduxukur Abdurixit Date: Sun Mar 27 19:50:01 2011 +0200 Added UG translation M po/ug.po commit d2bd20e2e09fa4791da9a9881d77d58f31328ec9 Author: Sense Hofstede Date: Fri Mar 18 19:52:08 2011 +0100 Updated Frisian translation M po/fy.po commit de31041b34a03b1438407a5604a0d416ea791e31 Author: Khaled Hosny Date: Mon Mar 7 08:07:25 2011 +0200 Updated Arabic translation M po/ar.po commit 819557fd56cd9a867bc1ed66955b70c823292b71 Author: Vincent Untz Date: Sun Mar 6 23:38:58 2011 +0100 release: post-release bump to 2.91.92 M configure.ac commit 764cf265fac8b7ee574d045b44431fe7cfbd4852 Author: Vincent Untz Date: Sun Mar 6 23:38:51 2011 +0100 release: 2.91.91 M NEWS M configure.ac commit 8e256fa080eadccecea4823f688974da34ed3790 Author: Daniel Korostil Date: Tue Mar 1 17:43:54 2011 +0200 Uploaded Ukranian M po/uk.po commit 715a80efbe9d2be0f329979e13073b0ece9241af Author: Changwoo Ryu Date: Sun Feb 27 17:17:37 2011 +0900 Updated Korean translation M po/ko.po commit 2966b4886b300a0bba438242fa5f44ac9f16f1c0 Author: Vincent Untz Date: Wed Feb 23 02:55:18 2011 +0100 editor: Fix to work with latest pygi https://bugzilla.gnome.org/show_bug.cgi?id=643019 M simple-editor/GMenuSimpleEditor/maindialog.py M simple-editor/GMenuSimpleEditor/menufilewriter.py M simple-editor/GMenuSimpleEditor/menutreemodel.py commit 8c66c8ad72ab512bc194cf16eadb7bf76696d817 Author: Friedel Wolff Date: Mon Feb 7 07:59:47 2011 +0200 Updated translation for Afrikaans (af) M po/af.po commit 9792ac69297dc003f5ba4d9461e15874ac1a6908 Author: Vincent Untz Date: Fri Feb 4 12:01:19 2011 +0100 build: Add ${ACLOCAL_FLAGS} to ACLOCAL_AMFLAGS This is used by gnome-autogen.sh, and we need it when aclocal.m4 is to be rebuilt by make, to avoid losing some aclocal paths. Also, don't set ACLOCAL_AMFLAGS in configure. M Makefile.am M configure.ac commit 6223b261d5a46ac170567ca4d5c0a00a07f64d43 Author: Pavol Klačanský Date: Wed Feb 2 09:39:00 2011 +0100 Updated Slovak translation M po/sk.po commit 668835a8d12d1dd5c88e3bc0fdd44eb6a63272e3 Author: Vincent Untz Date: Wed Feb 2 01:21:58 2011 +0100 release: post-release bump to 2.91.90 M configure.ac commit c24f0b2ada3123141049ca579d080245a923b88a Author: Vincent Untz Date: Wed Feb 2 01:21:07 2011 +0100 release: 2.91.6 M NEWS M configure.ac commit 8c2c9b554fdbbd10769c6028a51e230e362b9b4c Author: Vincent Untz Date: Wed Feb 2 01:19:36 2011 +0100 build: Fix distcheck (missing file in POTFILES.in) M po/POTFILES.in commit b68bcd27f44ce2c494f6e3cd9695890b9c02af04 Author: Vincent Untz Date: Tue Feb 1 15:18:49 2011 +0100 layout: Drop settings.menu It doesn't fit in the GNOME 3 world, where we use gnomecc.menu. D desktop-directories/InternetAndNetwork.directory.in D desktop-directories/LookAndFeel.directory.in M desktop-directories/Makefile.am D desktop-directories/Settings-System.directory.in D desktop-directories/Settings.directory.in D desktop-directories/X-GNOME-Menu-System.directory.in M layout/Makefile.am D layout/settings.menu M po/POTFILES.in commit 9ff2b267f4ae08ec38a2eb49c9de7f6778708d22 Author: Fran Diéguez Date: Thu Jan 27 03:38:30 2011 +0100 QA of Galician translations M po/gl.po commit 81dcfb9938e49083eddf545ec4ad919885a041c8 Author: Gheyret T.Kenji Date: Thu Dec 23 19:02:04 2010 +0100 Added UG translation M po/ug.po commit 340f2aa06c57705e9c3c7dcc62abccfa583ed2c8 Author: Mattias Põldaru Date: Mon Dec 20 14:06:53 2010 +0200 [l10n] Updated Estonian translation M po/et.po commit 31c1309040172956c84a6123460e7254221ac2eb Author: Nguyễn Thái Ngọc Duy Date: Sun Dec 19 12:56:53 2010 +0700 po/vi.po: import some translations from Ubuntu/Maverick M po/vi.po commit faf02643fb23eb2ca0ef08b75cd77e1cae0237ab Author: Gheyret T.Kenji Date: Sat Nov 20 11:41:03 2010 +0100 Added UG translation M po/ug.po commit f921bd15a02ee98d8ad2a5301135f096e0a0621f Author: Mahyar Moghimi Date: Fri Nov 19 16:16:58 2010 +0330 Updating Persian Translation M po/fa.po commit eb15998cee9614522a4a6018249aa449a5b9ead0 Author: Gheyret T.Kenji Date: Sat Nov 13 22:20:28 2010 +0100 Added UG translation M po/ug.po commit 5062b41c9ede06f1dfaa030a72201d7cb24e28d4 Author: Carles Ferrando Date: Fri Oct 29 01:09:57 2010 +0100 Updated Catalan (Valencian) translation M po/ca@valencia.po commit cfaef75741125ab692e4154dcf9ceda8b554c26d Author: Vincent Untz Date: Sat Oct 9 11:48:56 2010 +0200 build: Update git.mk from pango M git.mk commit 9cbd519531eaacb61cc686c13dce94cc5efb2fb2 Author: Vincent Untz Date: Wed Oct 6 16:44:29 2010 +0200 editor: Remove useless import M simple-editor/GMenuSimpleEditor/maindialog.py commit 6a776912dd1c963644b0291dbf8726ef1ec116ee Author: Vincent Untz Date: Wed Oct 6 16:42:06 2010 +0200 introspection: Tell g-ir-scanner what are the prefixes This means we now require gobject-introspection 0.9.5. M configure.ac M libmenu/Makefile.am commit 5092245c4fe1449bee93b8923e370d7db339f40f Author: Vincent Untz Date: Wed Oct 6 15:45:31 2010 +0200 libmenu: Do not send multiple notifications for one file change We emit notifications in the idle loop, which enables us to compress multiple notifications: this way, we can check if there is already a notification for a directory. We do this at the CachedDir level because it enables us to get one notification for a file rename, for example. But we also need it at the MenuLayout level to be sure to compress notifications accross multiple directories. It could be argued that we only need the latter, but I like it this way :-) https://bugzilla.gnome.org/show_bug.cgi?id=172046 M libmenu/entry-directories.c M libmenu/menu-layout.c commit 2e394806107ccbf0a3b822ab299042ea441c721e Author: Vincent Untz Date: Wed Oct 6 12:01:53 2010 +0200 editor: Forgot to remove "import pygtk" :-) M simple-editor/GMenuSimpleEditor/main.py commit adb08fee8c3677e539b9d1ca8fab9be1c4bf14b9 Author: Vincent Untz Date: Wed Oct 6 11:58:31 2010 +0200 editor: Remove has_separator property It's gone in new GTK+. M simple-editor/gmenu-simple-editor.ui commit cd550ce70f391ba3511800a1e100213bc3ff6c45 Author: Vincent Untz Date: Wed Oct 6 11:56:48 2010 +0200 editor: port to pygobject-based introspection bindings https://bugzilla.gnome.org/show_bug.cgi?id=626256 M simple-editor/GMenuSimpleEditor/main.py M simple-editor/GMenuSimpleEditor/maindialog.py M simple-editor/GMenuSimpleEditor/menufilewriter.py M simple-editor/GMenuSimpleEditor/menutreemodel.py commit ce014ee2884f134bcd9764043a58eb32294062ed Author: Kikongo Translation Team Date: Mon Sep 27 13:27:43 2010 +0200 Added Kikongo translation M po/LINGUAS A po/kg.po commit 15056451271dc5faf31ee2161cdb5696773275c5 Author: Vincent Untz Date: Mon Sep 27 12:58:48 2010 +0200 release: post-release bump to 2.30.5 M configure.ac commit 9c269a6850ca8c97edc1e41419d8f7778574c84f Author: Vincent Untz Date: Mon Sep 27 12:58:40 2010 +0200 release: 2.30.4 M NEWS M configure.ac commit 90a58de1f3a7381b26206be39624a3d40d8ffb07 Author: Vincent Untz Date: Mon Sep 27 12:29:45 2010 +0200 misc: Rename --enable-deprecations to --enable-deprecation-flags This is a better name for this configure option, since it's really about enabling the use of the deprecation flags, not allowing the use of deprecated API. M configure.ac commit f1da5abb7c1783134cfa485b323aedb4f5bc2465 Author: Vincent Untz Date: Mon Sep 27 12:04:53 2010 +0200 introspection: Associate .gir with pkg-config file M libmenu/Makefile.am commit fbd3e2c9f8fb8de4c975367f30b84121478da7d2 Author: Vincent Untz Date: Thu Sep 23 19:40:47 2010 +0200 libmenu: Clear cache of desktop entries set when files are added/removed When installing or removing an application, we get a notification of change. However, the menu tree still contained the same content. We were simply not clearing the cache containing the list of desktop entries when a file got added or removed, meaning that we always got the same list of desktop entries. (Note that it didn't affect gnome-panel, it's unclear why) https://bugzilla.gnome.org/show_bug.cgi?id=630410 M libmenu/entry-directories.c commit 54af0ebf8863955c0641670343ac57900092f104 Author: Torstein Winterseth Date: Wed Sep 22 14:32:00 2010 +0200 Updated Norwegian Nynorsk translation. M po/nn.po commit b8874d1d36c9b7d448b4958af5b71f2fa3606984 Author: Vincent Untz Date: Mon Sep 20 17:39:28 2010 +0200 build: Update all Makefile.am to more recent standards M Makefile.am M desktop-directories/Makefile.am M layout/Makefile.am M libmenu/Makefile.am M python/Makefile.am M simple-editor/GMenuSimpleEditor/Makefile.am M simple-editor/Makefile.am M util/Makefile.am commit 7060e3c48a391c0fa96e57879575e333c3a54e67 Author: Vincent Untz Date: Mon Sep 20 17:37:50 2010 +0200 misc: Update instructions for commit messages We're switching to "tag:" instead of "[tag]". M ChangeLog commit 22642577f7324349b3681bc58e32004820236066 Author: Takayuki KUSANO Date: Mon Sep 20 18:06:25 2010 +0900 Updated Japanese translation M po/ja.po commit feeb6b4d64f2d3e5e0f284aa3e1bfd625a627b1f Author: Daniel Martinez Date: Sat Sep 18 20:45:19 2010 +0200 Updated Aragonese translation M po/an.po commit 6010094c40eb65965622141cb09fec3a0c8b31d3 Author: Baurzhan Muftakhidinov Date: Wed Sep 15 12:55:46 2010 +0600 Updated Kazakh translation M po/kk.po commit fe36b5b5371404ea96a46ac6d6b4a003f54d9828 Author: Vincent Untz Date: Tue Sep 14 14:40:07 2010 +0200 release: post-release bump to 2.30.4 M configure.ac commit 721b241e2c5449003238d333d03063fdfe7b3aa1 Author: Vincent Untz Date: Tue Sep 14 14:39:56 2010 +0200 release: 2.30.3 M NEWS M configure.ac commit 328cff2e2951e817a6e999b3a3a8b563f07cd778 Author: Vincent Untz Date: Wed Sep 8 19:23:00 2010 +0200 [misc] Update AUTHORS, HACKING, MAINTAINERS, README Nothing new, but make sure this is up-to-date. Also tweak a bit autogen.sh and Makefile.am to make them look similar in all my modules. M AUTHORS M HACKING M MAINTAINERS M Makefile.am M README M autogen.sh M gnome-menus.doap commit 4cfd7db5d1ac221bc1397854a04dc40a654f1188 Author: Vincent Untz Date: Wed Sep 8 17:48:37 2010 +0200 [misc] Update license files to latest text Note that this doesn't change the license. The license text was updated for the latest FSF address, for example. M COPYING M COPYING.LIB commit bd02ce500383aa04914fda89900af93cdb33809d Author: Vincent Untz Date: Wed Sep 8 17:09:44 2010 +0200 [build] Rename configure.in to configure.ac M autogen.sh R100 configure.in configure.ac commit 8b14b78f279bad26580387b69a3274ba1197f66b Author: Dirgita Date: Wed Aug 4 10:36:10 2010 +0700 Updated Indonesian translation M po/id.po commit d524140a28c92719bdcc81df207c38c3c96052c0 Author: Pablo Castellano Date: Tue Aug 3 19:57:18 2010 +0200 Update git.mk from pango M git.mk commit d0cbd37e93980bae0781704765c56009913b0204 Author: Gheyret Tohti Date: Tue Aug 3 13:54:06 2010 +0200 Updated Uyghur translation M po/ug.po commit 784b664c760d655ae306d2d5d718231a1d09333f Author: Fran Diéguez Date: Tue Jul 20 12:10:56 2010 +0200 Updated Galician translations M po/gl.po commit 1100aed1ec5863ebfa80e51ca7f07e2b964459dd Author: Sense Hofstede Date: Sun Jul 18 00:34:23 2010 +0200 Fix forgotten copyright notices in the Frisian translations. M po/fy.po commit e29c70ada73e7f029423361da318685d517cbd2b Author: Sense Hofstede Date: Sun Jul 18 00:31:14 2010 +0200 Updated Frisian translation and added it to the LINGUAS file M po/LINGUAS A po/fy.po commit ce67a8b1603bb878131c23b625cf48b44ea8c477 Author: Reuben Potts Date: Wed Jul 7 13:51:56 2010 +0200 Added Manx translation M po/LINGUAS A po/gv.po commit 5e30e13fd5923befda0372473f64a81b98b35f92 Author: Baurzhan Muftakhidinov Date: Fri Jul 2 14:40:12 2010 +0300 Updated Kazakh translation for gdm M po/kk.po commit 2054996af842e6df1726e58d81a569f03e30d75f Author: Vincent Untz Date: Wed Jun 30 12:36:26 2010 +0200 [editor] Better fix for XDG_MENU_PREFIX support in editor The previous fix was partly wrong, because the menu file referenced in the created user menu file was not using the prefix. So instead of letting the library handle XDG_MENU_PREFIX, we handle it ourselves everywhere. M simple-editor/GMenuSimpleEditor/menufilewriter.py M simple-editor/GMenuSimpleEditor/menutreemodel.py commit ac425c5ac9e386fd171801496503d591d5c5ade2 Author: Vincent Untz Date: Wed Jun 30 12:31:33 2010 +0200 [editor] Respect XDG_MENU_PREFIX when writing user menu file https://bugzilla.gnome.org/show_bug.cgi?id=623197 M simple-editor/GMenuSimpleEditor/menufilewriter.py commit e8a7198465e090f3e35fc1ac328800909574801f Author: Kristjan Schmidt Date: Wed Jun 23 17:15:51 2010 +0200 Updated Esperanto translation M po/eo.po commit 0e25cc961e1814cb2b6e6e2b9172a649f5542a50 Author: Vincent Untz Date: Tue Jun 22 03:57:14 2010 +0200 [release] post-release bump to 2.30.3 M configure.in commit 3ec065b74712c99d1cdde955e864f203afccb41b Author: Vincent Untz Date: Tue Jun 22 03:57:06 2010 +0200 [release] 2.30.2 M NEWS M README M configure.in commit 320453668ea791f1d7c9f11d84363bfbae63fb25 Author: Yaakov Selkowitz Date: Tue Jun 15 23:00:19 2010 -0500 [build] Do not dist gir_DATA GIR files contain a shared-library attribute which varies per platform, and therefore must not be disted; see bug 621611 for rationale. https://bugzilla.gnome.org/show_bug.cgi?id=621724 M libmenu/Makefile.am commit 612aeb7cd40f57757a733d0805a52f6a6a000152 Author: Fran Diéguez Date: Sun Jun 13 19:32:21 2010 +0200 Updated Galician translations M po/gl.po commit 7e1332e718118f16ab451a622d40ec3afde79c2b Author: Matej Urbančič Date: Fri May 21 22:24:04 2010 +0200 Updated Slovenian translation M po/sl.po commit f93cca66736b876b8739fb900b51c82e22c7aead Author: Thomas Thurman Date: Sun May 16 18:04:30 2010 -0400 Updated Shavian translation M po/en@shaw.po commit 0928101b34ac6bc6e7da5c8bd52d39af43a9f38f Author: Thomas Thurman Date: Wed May 12 18:41:06 2010 -0400 Updated Shavian transliteration M po/en@shaw.po commit a66c03d1771a15ffe054e9e64ff5183bda951bcf Author: Peteris Krisjanis Date: Sat Apr 24 22:25:22 2010 +0300 Updated Latvian translation. M po/lv.po commit 498f225b6f9a4a146a534bacdc8b8b665cf71d95 Author: Daniel Martinez Date: Sat Apr 24 11:21:36 2010 +0200 Added Aragonese translation M po/LINGUAS A po/an.po commit 5b8ae6e132b31a0a0f14021ef6cf2fc2794bc87e Author: Carles Ferrando Date: Wed Apr 21 23:31:15 2010 +0200 Updated Catalan (Valencian) translation M po/ca@valencia.po commit 7408013978ba989af175f2ca17526fec4eea49c6 Author: Shankar Prasad Date: Wed Apr 21 11:50:02 2010 +0530 Updated Kannada translations M po/kn.po commit 20d4732caf167f54c376d34184ca7fdcf7d834b7 Author: Jordi Serratosa Date: Fri Apr 2 17:41:22 2010 +0200 Fixes to Catalan translation M po/ca.po commit a08bbc660cd320245ea4f3ccab4c33261e83dc7b Author: Christian Kirbach Date: Wed Mar 31 20:11:39 2010 +0200 Updated German translation M po/de.po commit ffbddbf28289a4ac72b284e3cc6f4db04cf0b43e Author: Wouter Bolsterlee Date: Tue Mar 30 18:01:20 2010 +0200 Dutch translation updated by Wouter Bolsterlee M po/nl.po commit 8d9f3db359acde4395645a0ea95c48567e5d79f4 Author: Reşat SABIQ Date: Tue Mar 30 00:46:58 2010 -0500 Updated Crimean Tatar (Crimean Turkish) translation M po/crh.po commit fe6b717216cb93b05abc547fd11c550d9d4f4f4e Author: Vincent Untz Date: Tue Mar 30 02:02:15 2010 +0200 [release] post-release bump to 2.30.1 M configure.in commit ada97a6e81d2f282796938269da7280dc5a8c83f Author: Vincent Untz Date: Tue Mar 30 02:02:06 2010 +0200 [release] 2.30.0 M NEWS M README M configure.in commit 2d0068dc1859b3adf21cfcc47a23aa00fc7d4502 Author: Kostas Papadimas Date: Sun Mar 28 10:01:00 2010 +0300 Updated Greek translation M po/el.po commit b7d6ee4f3f252b57a85787043fa3506d1640fa05 Author: Inaki Larranaga Murgoitio Date: Tue Mar 23 15:10:22 2010 +0100 Updated Basque language M po/eu.po commit e74ebd36d267033054e6bf15851da0f88d12eb3a Author: Badral Sanligiin Date: Tue Mar 23 02:57:26 2010 +0100 Updated Mongolian translation M po/mn.po commit 1bec0581f480c3e8b485be1453de389083feff5c Author: David Planella Date: Wed Mar 17 08:24:02 2010 +0100 Updated Catalan translation as per fixes discussed on the translation mailing list M po/ca.po commit 1253c82ed7ba774cbad7794c06d31249bf591c37 Author: Vincent Untz Date: Mon Mar 15 09:55:46 2010 +0100 [libmenu] Never ignore Menuname nodes from DefaultLayout If a Menuname node applies to a subdirectory that appears because of inlining, we generally want to ignore it. However, if this Menuname node comes from a DefaultLayout, then it should really be applied. M libmenu/gmenu-tree.c commit 7dba2d5302b539e24131e669e2db26f3c16e1547 Author: Vincent Untz Date: Mon Mar 15 09:27:44 2010 +0100 [libmenu] Fix layout processing for Menuname nodes Because of the scope of a variable, no submenu were matching Menuname nodes in the layouts, making all the Menuname nodes non-working. https://bugzilla.gnome.org/show_bug.cgi?id=612585 M libmenu/gmenu-tree.c commit c48304179db938f7336223f99ead95dda6044f7a Author: Changwoo Ryu Date: Sat Mar 13 23:55:09 2010 +0900 Updated Korean translation M po/ko.po commit 87f03667154f5c77548506218c57020e37d04c81 Author: Vincent Untz Date: Tue Mar 9 14:06:21 2010 +0100 [build] Add configure summary M configure.in commit 9fbaa3d7f76ffb4076216d231acf39da0331e0bc Author: Vincent Untz Date: Mon Mar 8 14:49:09 2010 +0100 [release] post-release bump to 2.30.0 M configure.in commit 076f8c42ce3e46c3b5cb761a3cd65c6f1e0a55a2 Author: Vincent Untz Date: Mon Mar 8 14:49:01 2010 +0100 [release] 2.29.92 M NEWS M README M configure.in commit d50ff15f43bc2311fbd06bd9664ae9571fd550ea Author: Vincent Untz Date: Mon Mar 8 14:43:18 2010 +0100 [libmenu] Add gobject-introspection support https://bugzilla.gnome.org/show_bug.cgi?id=598406 M Makefile.am M autogen.sh M configure.in M libmenu/Makefile.am commit d3135a15585afcb8d597fae05beadfaf1c743238 Author: Vincent Untz Date: Thu Mar 4 17:55:40 2010 +0100 [misc] Do not call bindtextdomain() and friends in test-menu-spec.c We decided to not make this application translatable in commit 25c10ed7, so we don't need those calls. M util/test-menu-spec.c commit 57e825050cf8eafb970c8ddd2dc9c66885d25ce3 Author: Nikos Charonitakis Date: Thu Feb 25 00:52:48 2010 +0200 Updated Greek translation M po/el.po commit 274a633ddb053a70e26278308a6fb52ef933e608 Author: Pavol Klačanský Date: Tue Feb 23 17:55:32 2010 +0100 Updated Slovak translation M po/sk.po commit 41d1ce2320c26dce1677e0624c36119ec88e6823 Author: Vincent Untz Date: Mon Feb 22 20:18:20 2010 +0100 [release] post-release bump to 2.29.92 M configure.in commit 5eaf95e2c9e7755332a7c586eef34726244290dd Author: Vincent Untz Date: Mon Feb 22 20:18:05 2010 +0100 [release] 2.29.91 M NEWS M README M configure.in commit ca24481c06829c7382d20ac7ee2283e22b908451 Author: Vincent Untz Date: Mon Feb 22 12:11:57 2010 +0100 [misc] Add translator comment for Personal https://bugzilla.gnome.org/show_bug.cgi?id=610661 M desktop-directories/Personal.directory.in commit 25c10ed70c8125516537b3b731dff1c559bcab94 Author: Vincent Untz Date: Mon Feb 22 12:06:48 2010 +0100 [misc] Do not make the string test-menu-spec.c translatable This is only a test program that doesn't get installed, so there's no point in making translators work on the strings there. https://bugzilla.gnome.org/show_bug.cgi?id=609441 M po/POTFILES.in A po/POTFILES.skip M util/test-menu-spec.c commit 8f52b3c312da587fb29590559cb50370787f6116 Author: Fran Diéguez Date: Mon Feb 15 18:44:37 2010 +0100 Updated Galician Translation M po/gl.po commit ef1383eda9480c96ac2e5d958342c98ab1be41cb Author: Torstein Adolf Winterseth Date: Thu Jan 28 12:10:06 2010 +0100 Updated Norwegian Nynorsk translation M po/nn.po commit a0a398d77aa2ea28d267f106880e0df1417d8fab Author: Vincent Untz Date: Wed Jan 27 16:47:53 2010 +0100 [release] post-release bump to 2.29.90 M configure.in commit 2c37a7cbdd9d6f7e9f1ccfb0d2979da897e9668e Author: Vincent Untz Date: Wed Jan 27 16:47:41 2010 +0100 [release] 2.29.6 M NEWS M README M configure.in commit a462be862680ee3051eef3ca238f5bc405640e16 Author: Jamil Ahmed Date: Sun Jan 24 23:15:18 2010 +0600 Updated Bengali translation M po/bn.po commit fdafd8c07a65e65fdf6f2e7d358b7eb2b14d01eb Author: Aron Xu Date: Sun Jan 24 15:34:41 2010 +0800 Update Simplified Chinese translation. M po/zh_CN.po commit 89309a8bb1e76209e147180a3fe2fd15307d6012 Author: Vincent Untz Date: Fri Jan 15 00:02:07 2010 +0100 [libmenu] Do not count non-inlining submenus as inlining with header When computing if a submenu should be inlined, we use the number of items in the subsubmenus. It was always assuming that inlining with a header would be used, because of misuse of the will_inline_header field. M libmenu/gmenu-tree.c commit 4b51a77fcbc1a2c3e94945c4b92a65559f7e5996 Author: Vincent Untz Date: Thu Jan 14 23:30:47 2010 +0100 [libmenu] Support inline alias of an inline alias M libmenu/gmenu-tree.c commit cd1d0a199110ca94266c3e8b928e61d12cd2aac3 Author: Vincent Untz Date: Thu Jan 14 23:26:49 2010 +0100 [libmenu] Add real support for inline aliases during layout processing It looks like the inline aliases were not really tested before... All the processing of the layout info (merge_subdir_by_name, merge_entry_by_id, merge_subdirs, merge_entries, merge_subdirs_and_entries) was completely ignoring the fact that some subdirs or entries might be aliases, and therefore need a different processing. This is now done correctly, and we have a merge_alias() function to do the right thing when an alias is detected. Also, this simplifies quite a bit the sorting of subdirs or entries since we always need to check the types of the items we compare, so we can get rid of gmenu_tree_directory_compare and gmenu_tree_entry_compare. M libmenu/gmenu-tree.c commit cda451cec61345d357cbc26e5a5dfd102f476ea5 Author: Vincent Untz Date: Thu Jan 14 21:29:42 2010 +0100 [libmenu] Fix miscalulation for inlining when inline_header = true When inline_header = true for a subdirectory, the length of the content of the parent directory was miscomputed: instead of adding the length of the subdirectory, it was just replacing the current length with the length of the subdirectory. Also fix a typo in a comment M libmenu/gmenu-tree.c commit b6dc7a2443ef3d8fef1a1175532124882e95d03b Author: krishnababu k Date: Wed Dec 2 14:36:36 2009 +0530 Updated telugu translation M po/te.po commit 696867378e99b410c1803412c7f894c99b386f64 Author: Thomas Thurman Date: Sat Oct 31 22:26:02 2009 -0400 Shavian translation M po/LINGUAS A po/en@shaw.po commit 4c76410de345e06b879c85fa015659286f42c240 Author: Nils-Christoph Fiedler Date: Thu Oct 22 19:47:54 2009 +0200 Updated Low German translation M po/nds.po commit 5f2c53bd3fbd4b39707609955f445c96eb9b5fb3 Author: Reşat SABIQ Date: Wed Oct 21 00:29:46 2009 -0500 Update for Crimean Tatar (Crimean Turkish) translation M po/crh.po commit bde6cabd440da28d25983e762ed6b71849da918f Author: Leonid Kanter Date: Tue Oct 20 12:06:06 2009 +0300 Updated Russian translation M po/ru.po commit 72f4ede1ab9bbcfa29affda98e1fcfecdfcf87ef Author: Gil Forcada Date: Tue Oct 6 17:36:50 2009 +0200 Updated Catalan translation M po/ca.po commit e1c00f83d903581233e7e15655916b4079b3e500 Author: Sveinn í Felli Date: Fri Oct 2 06:17:30 2009 +0000 Updated Icelandic translation M po/is.po commit 5aff8025f08164699721084857a5321ed2a45abe Author: Vincent Untz Date: Thu Oct 1 13:25:58 2009 +0200 [release] post-release bump to 2.28.1 M configure.in commit 1d899c5b7bafb9ea01e0f407df7acb2b0d6432f4 Author: Vincent Untz Date: Thu Oct 1 13:25:50 2009 +0200 [release] 2.28.0.1 M NEWS M README M configure.in commit 9d8e52a9a3e495f1e8e50a61f89510a03a4d31c9 Author: Vincent Untz Date: Thu Oct 1 13:21:45 2009 +0200 [build] Link the python binding to libpython Thanks to Frederic Crozat for spotting this. D acinclude.m4 A m4/python.m4 M python/Makefile.am commit 117273842932a6fb24143926513474c28e3595b4 Author: Frederic Crozat Date: Tue Sep 29 15:35:10 2009 +0200 [libmenu] Make sure to use a value when sorting items during a merge The name variable was not assigned any value... M libmenu/gmenu-tree.c commit 5713f8e66b0fe3791a57555ebd12ddb076176cd9 Author: Vincent Untz Date: Mon Sep 21 18:18:27 2009 +0200 [release] post-release bump to 2.28.1 M configure.in commit ddc11bf8f9d279cc2578c074ccb2c85a837e4ba7 Author: Vincent Untz Date: Mon Sep 21 18:18:18 2009 +0200 [release] 2.28.0 M NEWS M README M configure.in commit 87d43c2dd053ae3cb16e53341db2cb60ae348cf9 Author: Amitakhya Phukan Date: Mon Sep 21 11:30:34 2009 +0530 Updating Assamese translations M po/as.po commit a44cc5ae04856e05300dc189cab1a9e0ea0d7697 Author: Amitakhya Phukan Date: Mon Sep 21 11:29:14 2009 +0530 Updating Assamese translations M po/as.po commit e6a5afde9de461ccbc45556ca7720d0b9e1c63a8 Author: Shankar Prasad Date: Mon Sep 21 10:18:37 2009 +0530 Updated Kannada(kn) translation M po/kn.po commit dc324804a5105c78f5fded62232d7f83976dec2d Author: Petr Kovar Date: Mon Sep 21 04:22:31 2009 +0200 Updated Czech translation M po/cs.po commit c7045e1d8937e7730cde99e87dce31ccb3474d76 Author: Rajesh Ranjan Date: Sun Sep 20 12:35:22 2009 +0530 maithili update, translated by Sangeeta Kumari M po/mai.po commit 3e5f8caa3f659f4a3ea7730ea79af98c639379ac Author: Daniel Nylander Date: Sat Sep 19 10:25:52 2009 +0200 Updated Swedish translation M po/sv.po commit 8af7dc29b7ccf039422d70c38f3927d1e493e726 Author: Luca Ferretti Date: Fri Sep 18 15:08:11 2009 +0200 Added Italian translation M po/it.po commit b46ad5eb16a78883c09ebddcf09634368052ac72 Author: krishnababu k Date: Thu Sep 17 21:43:06 2009 +0530 Updated Telugu Translation M po/te.po commit 9f35303a5dbb43c2f024d001de95dd58402e4519 Author: Maxim V. Dziumanenko Date: Thu Sep 17 13:56:16 2009 +0300 Added Ukrainian translation M po/uk.po commit 9f656630180c4328bfe95e325c4e2afbf61a22d4 Author: Niels-Christoph Fiedler Date: Wed Sep 16 18:33:04 2009 +0200 Updated German translation M po/nds.po commit 3989f3da21c0b72c2161c4476620e992ca533a02 Author: Adi Roiban Date: Wed Sep 16 16:01:54 2009 +0300 Updated Romanian translation M po/ro.po commit a9f05d49e0e31e5cd7ec6ca6e835b3de438f3033 Author: Rajesh Ranjan Date: Wed Sep 16 13:47:43 2009 +0530 hindi update by Rajesh Ranjan M po/hi.po commit 19d5f458acc6001cee1446caa39163f3369af273 Author: Gintautas Miliauskas Date: Tue Sep 15 02:11:17 2009 +0300 Updated Lithuanian translation. M po/lt.po commit 1102f0542b4df782708eb857e638bfd5966aa7dc Author: Ask H. Larsen Date: Sun Sep 13 03:23:56 2009 +0200 Updated Danish translation M po/da.po commit da99034888d5191a27e3029cbad6c73c7e52e10a Author: Philip Withnall Date: Sat Sep 12 23:35:46 2009 +0100 Updated British English translation M po/en_GB.po commit 67c6af706c287319a7d5c73cf86d5209fd89f44f Author: Chao-Hsiung Liao Date: Sun Sep 13 06:32:22 2009 +0800 Updated Traditional Chinese translation(Hong Kong and Taiwan) M po/zh_HK.po M po/zh_TW.po commit ebd7a9aaa45fb4b43ac7e5b35e45412ddd55dc9d Author: Ani Date: Sat Sep 12 18:32:13 2009 +0530 Updaeted Malayalam Translations M po/ml.po commit 37d761d18917ba9167c2be84c8fd5c6948ff3139 Author: Matej Urbančič Date: Sat Sep 12 10:31:29 2009 +0200 Updated Slovenian translation M po/sl.po commit 333ce6b4509b8137c085171b730b92f0d2870c7b Author: Sandeep Shedmake Date: Fri Sep 11 19:35:53 2009 +0530 Updated Marathi Translations M po/mr.po commit 068006c9639123b7c7fe47855f5e1801c78c7dd0 Author: A S Alam Date: Thu Sep 10 06:46:10 2009 +0530 updating for Punjabi M po/pa.po commit 7411701b1e806aa8f283ea9868d165ab150ac2c7 Author: Baris Cicek Date: Thu Sep 10 00:25:54 2009 +0300 Updated Turkish translation. M po/tr.po commit eb43a84b5a1b55f1743e9d0b9aee036a72962b17 Author: Vincent Untz Date: Wed Sep 9 02:10:28 2009 +0200 [build] Generate bzip2 tarballs and use foreign automake option M configure.in commit 4d35bde7e67f6b59c108ebc8615a342cb93cbd17 Author: Vincent Untz Date: Wed Sep 9 02:08:49 2009 +0200 [release] post-release bump to 2.28.0 M configure.in commit 908771796bad88ce49158f951bd7ca40c51b256b Author: Vincent Untz Date: Wed Sep 9 02:08:40 2009 +0200 [release] 2.27.92 M NEWS M README M configure.in commit f1a8145230fc24f47f4818f39839aafe5b911c37 Author: Takayuki KUSANO Date: Tue Sep 8 22:14:00 2009 +0900 Updated Japanese translation M po/ja.po commit 0178e1acc5aeca2e05ddfcf67e8f03e8ee82eff6 Author: Vincent Untz Date: Tue Sep 8 00:20:56 2009 +0200 [misc] Bump version to 2.27.92 M configure.in commit db81a7f1194e435b538ab6710d752276fb1c7135 Author: Vincent Untz Date: Mon Sep 7 23:28:56 2009 +0200 [editor] Use display name instead of name M simple-editor/GMenuSimpleEditor/menutreemodel.py commit d3fdd0ba4133c66e670561bca5b1bf4dd1a8feea Author: Vincent Untz Date: Mon Sep 7 23:13:22 2009 +0200 [libmenu,python] Add gmenu_tree_get_sort_key()/gmenu_tree_set_sort_key() This is needed for users of gnome-menus that display applications by strings obtained via get_display_name(), since the order of entry changes if strings are different. The default is to keep the sort order by name. At the moment, the only other option is to use a sort by display name. Bindings for python are added too. M libmenu/gmenu-tree.c M libmenu/gmenu-tree.h M python/gmenu.c commit 4b007ffddf4cfd58d508a8c24d49ca8724753bb0 Author: Vincent Untz Date: Mon Sep 7 23:19:49 2009 +0200 [python] Fix accidental swap between display_name and generic_name M python/gmenu.c commit 22b78e8b3cbc5d68638a1729728b682a4ce1d10b Author: Vincent Untz Date: Mon Sep 7 19:21:12 2009 +0200 [python] Add bindings for get_generic_name() and get_display_name() M python/gmenu.c commit e539c64e99a000b3a89a53bb5a6233a6b16ed96a Author: Vincent Untz Date: Mon Sep 7 19:19:41 2009 +0200 [libmenu] Add gmenu_tree_entry_get_display_name() API This API returns the content of the X-GNOME-FullName key if available, and fallbacks to Name. M libmenu/desktop-entries.c M libmenu/desktop-entries.h M libmenu/gmenu-tree.c M libmenu/gmenu-tree.h commit 9767b9094ef22034be948f289473726e922e1d28 Author: Gabor Kelemen Date: Mon Sep 7 02:30:34 2009 +0200 Hungarian translation updated M po/hu.po commit c2e0a3cedf6ceded675e6d6b8368d98dc8e64716 Author: Rodrigo L. M. Flores Date: Sun Aug 30 19:56:20 2009 -0300 Updated Brazilian Portuguese mailing list address. M po/pt_BR.po commit e5c016f25d380332029e4b4d78ebc795fcc06e27 Author: Mario Blättermann Date: Sun Aug 30 14:54:33 2009 +0200 Updated German translation M po/de.po commit aadd320b39ccd5a713b2fc9f5fb85d894141c61f Author: Miloš Popović Date: Sat Aug 29 14:43:42 2009 +0200 Updated Serbian translation M po/sr.po M po/sr@latin.po commit 900767c9056455409c0ad674d17959b50b1a0f49 Author: Runa Bhattacharjee Date: Fri Aug 28 12:30:47 2009 +0530 Updated Bengali India Translations M po/bn_IN.po commit 66913548e742cc1a8c138fccce9302b88fe5eae9 Author: Duarte Loreto Date: Thu Aug 27 08:58:37 2009 +0100 Updated Portuguese translation M po/pt.po commit 4dbf30628a5f29f8eb0b85c65ffd5b9b0caa5177 Author: Claude Paroz Date: Wed Aug 26 21:33:09 2009 +0200 Updated French translation M po/fr.po commit bcf479be8e2b92c26becafe004a0f1b1c62af679 Author: Inaki Larranaga Murgoitio Date: Wed Aug 26 18:27:07 2009 +0200 Updated Basque language M po/eu.po commit 5ee416dd4c7b0ddb11c7c38581bf293b3b9352dd Author: Luca Ferretti Date: Wed Aug 26 09:31:11 2009 +0200 Updated Italian translation M po/it.po commit a02a132ed3b18f156bf2c7e965c3075685f72759 Author: Tomasz Dominikowski Date: Tue Aug 25 19:26:18 2009 +0200 Updated Polish translation M po/pl.po commit 6f55d2289a2a30d03db2bf40c31f79474ff952c0 Author: Frédéric Péters Date: Sat Aug 22 13:44:25 2009 +0200 Remove deprecated Encoding key from desktop file M simple-editor/gmenu-simple-editor.desktop.in commit 4771bdb6af1b29f10ba39706cb2b63433d1f0480 Author: Andre Klapper Date: Mon Aug 24 23:01:03 2009 +0200 Change nds_NFE to nds as discussed on irc M po/LINGUAS R100 po/nds_NFE.po po/nds.po commit 539360b4cfa0c26b884c9811acd15d943a7574a0 Author: Sweta Kothari Date: Mon Aug 24 15:37:44 2009 +0530 Updated Gujarati Translations M po/gu.po commit 28501f2afe901298d74760362f47bb7f67107699 Author: Changwoo Ryu Date: Sun Aug 23 07:27:42 2009 +0900 Update Korean translation M po/ko.po commit b2b842dbd5fd8687562d0961ad62391d5f72004f Author: Mario Blättermann Date: Sat Aug 22 15:09:52 2009 +0200 Added Low German to LINGUAS M po/LINGUAS commit 4694660b8ebac75d9c72ef1de5654bd031a9062e Author: Mario Blättermann Date: Sat Aug 22 15:07:10 2009 +0200 Added Low German translation A po/nds_NFE.po commit 9dc36cf2b049dbca649c92911f29644b073f031b Author: Khaled Hosny Date: Thu Aug 20 21:19:07 2009 +0300 Updated Arabic translation M po/ar.po commit 895ba3af6f107e61cc6a4414746d94ba3df38a8f Author: Alexander Shopov Date: Fri Aug 14 07:48:02 2009 +0300 Updated Bulgarian translation M po/bg.po commit 761e44b26fd2315f8823d3b5f2347346f1ef9eb0 Author: Manoj Kumar Giri Date: Thu Aug 13 20:09:24 2009 +0530 Updated Oriya Translation M po/or.po commit c49f4bb691fd51039add261b9a19c5b45bbbd0d5 Author: Antón Méixome Date: Wed Aug 12 13:25:27 2009 +0200 Updated Galician translation M po/gl.po commit 3ed6cedb4154fa399f48b036088fa703291d28d9 Author: Krix Apolinário Date: Wed Aug 12 00:01:59 2009 -0300 Updated Brazilian Portuguese translation. M po/pt_BR.po commit 3f85e31f5fbb7e3e77ed8459af8b18f071ed3767 Author: Kjartan Maraas Date: Tue Aug 4 18:15:34 2009 +0200 Updated Norwegian bokmål translation. M po/nb.po commit ed5f86151fd103c446535bc8d1ed3d80604e2899 Author: Seán de Búrca Date: Wed Jul 29 17:45:34 2009 -0600 Updated Irish translation M po/ga.po commit 6b5cd5918a318712237b5fc511fe122a1229a29b Author: Denis Arnaud Date: Wed Jul 29 12:13:13 2009 +0200 Updated breton translation M po/br.po commit c6f657e60f70bb848b70b9462591a38c973b4010 Author: drtvasudevan Date: Wed Jul 29 12:12:26 2009 +0530 Updated Tamil translation M po/ta.po commit 458924440a7f69c64272ddeb9fbe1999ac70bbcd Author: drtvasudevan Date: Wed Jul 29 12:10:37 2009 +0530 Updated Tamil translation M po/ta.po commit 3d9f5ce3d34839e2342692a849838c9e7503c588 Author: Denis Arnaud Date: Wed Jul 29 07:09:51 2009 +0200 Updated breton translation M po/br.po commit ea7245e45238b380a8ba11ee8dabc67775e49c6f Author: Vincent Untz Date: Wed Jul 29 00:08:38 2009 +0200 [misc] Update commit guidelines for referencing bugs M ChangeLog commit 374cdd09e33122d6a7f06eec9748130b3c85f9fe Author: Vincent Untz Date: Tue Jul 28 18:07:17 2009 +0200 [release] post-release bump to 2.27.90 M configure.in commit e399908c171a0add6ab6febd405b5121b3772fd2 Author: Vincent Untz Date: Tue Jul 28 18:07:07 2009 +0200 [release] 2.27.5 M NEWS M README M configure.in commit 2bb16693175f59d28c8a798d50ffb9b462c916f3 Author: Denis Arnaud Date: Tue Jul 28 14:10:04 2009 +0200 Updated breton translation M po/br.po commit b9cbf41d7c93ad8f18a5b9162dbd85a9e2774efa Author: Aron Xu Date: Sun Jul 26 18:25:28 2009 +0800 Updated Simplified Chinese translation. M po/zh_CN.po commit 0aed3c9a813e79ad57ec335e2aa410beb5d23991 Author: Theppitak Karoonboonyanan Date: Thu Jul 23 15:13:40 2009 +0700 Updated Thai translation. M po/th.po commit 83e66d9b0487b0be48ea935801d500e9857a4ae5 Author: Vincent Untz Date: Tue Jul 21 19:35:18 2009 +0200 [build] Set m4/ as macro dir M Makefile.am M configure.in commit 34c1fcb2306df9ca22634a82e75865eda1031769 Author: Vincent Untz Date: Tue Jul 21 18:53:35 2009 +0200 [build] Use silent-rules instead of shave for quiet build M configure.in D m4/shave.m4 D shave-libtool.in D shave.in M simple-editor/GMenuSimpleEditor/Makefile.am M simple-editor/Makefile.am commit 8ca7e0223b9f0e935889c146bfb5378bfeadf0ea Author: Ilkka Tuohela Date: Sun Jul 19 10:39:57 2009 +0300 Updated Finnish translation M po/fi.po commit b8fa21a37017f3fe5e333a616434c5b489e7f333 Author: Vincent Untz Date: Wed Jul 15 16:52:26 2009 +0200 [release] post-release bump to 2.27.5 M configure.in commit c17916822f13accf78dfc4d1f3ef8bc398e398f5 Author: Vincent Untz Date: Wed Jul 15 16:52:15 2009 +0200 [release] 2.27.4 M NEWS M README M configure.in commit d49ae45dc30c3fbafded146e5fbccbe23215316b Author: Daniel Nylander Date: Tue Jul 7 19:37:51 2009 +0200 Updated Swedish translation M po/sv.po commit 0b7478a5de36192e3bc31576c7e8114a3a6dbed3 Author: Yaron Shahrabani Date: Sat Jul 4 22:39:12 2009 +0300 Updated Hebrew translation M po/he.po commit 8bddaf2076e094043189a20c36bf66b8c3813921 Author: Vincent Untz Date: Sat Jun 27 04:18:02 2009 +0200 [libmenu] Sort inlined items unless inline_header is used We also strip duplicate entries in a menu when we do such a sort. This is actually not a trivial task since it means we have to correctly preprocess all inline data before using the layout -- else, we'd try to sort items while a layout has been used, which is too late (since you can specify that you want mixed entries and directories, eg; or even entries or directories in a specified order). We make sure to evaluate the inline data at the deepest first, so we can correctly propagate as much inline items as possible to the top. http://bugzilla.gnome.org/show_bug.cgi?id=490483 M libmenu/gmenu-tree.c commit 0b1f053e07d1d4c081a58303cfe7ed6b583ed966 Author: Jorge Gonzalez Date: Sat Jun 27 14:58:46 2009 +0200 Updated Spanish translation M po/es.po commit 7a6b5b5a292357357f4458820b6e8f851472c592 Author: Vincent Untz Date: Fri Jun 26 23:42:59 2009 +0200 [libmenu] Do not always inherit parent DefaultLayout attributes We should not use the parent DefaultLayout attributes when there no DefaultLayout children for the child but there are DefaultLayout attributes for the child. Ie, if the child has , then it used to keep inheriting the parent DefaultLayout attributes, and then possibly lose the inline="true". M libmenu/gmenu-tree.c commit 89ab14f5fd34d18ef1591baf1000f5197344cd92 Author: Vincent Untz Date: Fri Jun 26 23:19:47 2009 +0200 [libmenu] Fix DefaultLayout attributes not being inherited The submenus were not inheriting the DefaultLayout attributes (eg, all the inline parameters) of their parent when there was no child to the DefaultLayout of their parent. Concretely, this was ignored for submenus: while this should have behaved like: M libmenu/gmenu-tree.c commit 6f948049c368cad02b713332430b98c5daa11b5e Author: Mattias Põldaru Date: Thu Jun 25 14:29:03 2009 +0300 Updating Estonian translation M po/et.po commit 80f5477a91e11630403d60b701658c7ae242c7d7 Author: Claude Paroz Date: Sun Jun 21 19:35:00 2009 +0200 Add translator comment M simple-editor/GMenuSimpleEditor/main.py commit fc7e3ebf3266a47de4dc96a2fc938e01848eb53d Author: Claude Paroz Date: Sun Jun 21 19:28:21 2009 +0200 Fix POTFILES.in M po/POTFILES.in commit dca731f53b3686436cc94cf2704035419c03b7e8 Author: Robert Staudinger Date: Thu May 7 17:28:08 2009 +0200 [libmenu] Add API to access GenericName http://bugzilla.gnome.org/show_bug.cgi?id=581887 M libmenu/desktop-entries.c M libmenu/desktop-entries.h M libmenu/gmenu-tree.c M libmenu/gmenu-tree.h commit d504fabf22e5303abd631e5a868fb5f45f4c7b05 Author: Pedro Fragoso Date: Sun Jun 21 01:17:26 2009 +0200 [editor] Port to GtkBuilder (with a few tweaks by me to make it work) http://bugzilla.gnome.org/show_bug.cgi?id=580158 M po/POTFILES.in M simple-editor/GMenuSimpleEditor/config.py.in M simple-editor/GMenuSimpleEditor/main.py M simple-editor/GMenuSimpleEditor/maindialog.py M simple-editor/Makefile.am D simple-editor/gmenu-simple-editor.glade A simple-editor/gmenu-simple-editor.ui commit fecc2847cf75048a6fe41c7423ac1e705a32b12b Author: Vincent Untz Date: Sun Jun 21 01:09:40 2009 +0200 [editor] Add --help and --version arguments http://bugzilla.gnome.org/show_bug.cgi?id=552989 M simple-editor/GMenuSimpleEditor/main.py commit db6d999b0ada473a3124a14dd83ed7ccb2375474 Author: Seán de Búrca Date: Sun May 24 21:21:58 2009 +0100 Updated Irish translation M po/ga.po commit 0aa50a649aa76ea95f2cda225a87448e8a16b69c Author: Inash Zubair Date: Sat May 23 13:32:29 2009 +0200 Added Divehi translation M po/LINGUAS A po/dv.po commit 569e4835258906ca51b6b32e062cb3f860e5aeac Author: Michael Meeks Date: Thu May 21 03:55:59 2009 +0200 [libmenu] Add a cache for listing the desktop files When processing a layout, we get the list of desktop files from a set of directories. It turns out that we nearly always use the same set of directories, so adding a one-entry cache makes it possible to avoid computing things again and again. I changed Michael's patch a bit, mainly to empty the cache when the last GMenuTree is unref'ed. Closes: bgo#498749 M libmenu/entry-directories.c M libmenu/entry-directories.h M libmenu/gmenu-tree.c commit 020085ed0a4086509ede57eb4fb480bbdbb5c79a Author: Vincent Untz Date: Sat May 2 02:24:07 2009 +0200 Update commit messages guidelines M ChangeLog commit 4900c5713c5caf69d4d476023fb5638fb9d60a17 Author: Vincent Untz Date: Mon Apr 27 00:20:04 2009 +0200 Use git.mk from pango to autogenerate .gitignore files M Makefile.am M desktop-directories/Makefile.am A git.mk M layout/Makefile.am M libmenu/Makefile.am M python/Makefile.am M simple-editor/GMenuSimpleEditor/Makefile.am M simple-editor/Makefile.am M util/Makefile.am commit c11452a831ab1ecf15ccb516492740980bda7c18 Author: Vincent Untz Date: Mon Apr 27 01:10:53 2009 +0200 Remove .cvsignore files D .cvsignore D desktop-directories/.cvsignore D layout/.cvsignore D libmenu/.cvsignore D po/.cvsignore D python/.cvsignore D simple-editor/.cvsignore D simple-editor/GMenuSimpleEditor/.cvsignore D util/.cvsignore commit 5fa8af519c166de0a0abd4d9cf0e396540270b4f Author: Vincent Untz Date: Mon Apr 27 00:13:56 2009 +0200 Use shave to improve build log readability See http://git.lespiau.name/cgit/shave/tree/README for more details. M configure.in A m4/shave.m4 A shave-libtool.in A shave.in M simple-editor/GMenuSimpleEditor/Makefile.am M simple-editor/Makefile.am commit 22e86da4dfd79d8f2084bce3537607fa2b9bd40e Author: Vincent Untz Date: Sun Apr 26 12:26:11 2009 +0200 Fix doap file to have mail address as URL (mailto:) M gnome-menus.doap commit 1e3078b3ef1f58c7f6e8878ff0836d312c3e57dc Author: Vincent Untz Date: Fri Apr 24 03:25:12 2009 +0200 Make autogen.sh more modern. M autogen.sh commit 2f4ef97ac8153a1460b2a1404c4f622712b3db81 Author: Vincent Untz Date: Wed Apr 22 17:28:55 2009 +0200 Add doap file A gnome-menus.doap commit e5c0da200efb4a82d8478d45c62959b848085d77 Author: Vincent Untz Date: Wed Apr 22 17:27:40 2009 +0200 ename ChangeLog files to ChangeLog.pre-git M ChangeLog C100 ChangeLog ChangeLog.pre-git R100 po/ChangeLog po/ChangeLog.pre-git commit 71593a96a06add451182f6b2bf848daaae3d37a0 Author: Miquel Esplà Date: Tue Apr 21 15:05:10 2009 +0200 Added Valencian-Catalan translation M po/LINGUAS A po/ca@valencia.po commit 44190fe0edccfe82eeed523af572bcda3cb55dab Author: Vincent Untz Date: Mon Apr 13 22:05:39 2009 +0000 post-release bump to 2.26.2 2009-04-14 Vincent Untz * configure.in: post-release bump to 2.26.2 svn path=/trunk/; revision=1017 M ChangeLog M configure.in M po/ChangeLog cinnamon-menus-3.6.0/COPYING.LIB0000664000175000017500000006144713173357262015060 0ustar fabiofabio GNU LIBRARY GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1991 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the library GPL. It is numbered 2 because it goes with version 2 of the ordinary GPL.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Library General Public License, applies to some specially designated Free Software Foundation software, and to any other libraries whose authors decide to use it. You can use it for your libraries, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library, or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link a program with the library, you must provide complete object files to the recipients so that they can relink them with the library, after making changes to the library and recompiling it. And you must show them these terms so they know their rights. Our method of protecting your rights has two steps: (1) copyright the library, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the library. Also, for each distributor's protection, we want to make certain that everyone understands that there is no warranty for this free library. If the library is modified by someone else and passed on, we want its recipients to know that what they have is not the original version, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that companies distributing free software will individually obtain patent licenses, thus in effect transforming the program into proprietary software. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License, which was designed for utility programs. This license, the GNU Library General Public License, applies to certain designated libraries. This license is quite different from the ordinary one; be sure to read it in full, and don't assume that anything in it is the same as in the ordinary license. The reason we have a separate public license for some libraries is that they blur the distinction we usually make between modifying or adding to a program and simply using it. Linking a program with a library, without changing the library, is in some sense simply using the library, and is analogous to running a utility program or application program. However, in a textual and legal sense, the linked executable is a combined work, a derivative of the original library, and the ordinary General Public License treats it as such. Because of this blurred distinction, using the ordinary General Public License for libraries did not effectively promote software sharing, because most developers did not use the libraries. We concluded that weaker conditions might promote sharing better. However, unrestricted linking of non-free programs would deprive the users of those programs of all benefit from the free status of the libraries themselves. This Library General Public License is intended to permit developers of non-free programs to use free libraries, while preserving your freedom as a user of such programs to change the free libraries that are incorporated in them. (We have not seen how to achieve this as regards changes in header files, but we have achieved it as regards changes in the actual functions of the Library.) The hope is that this will lead to faster development of free libraries. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, while the latter only works together with the library. Note that it is possible for a library to be covered by the ordinary General Public License rather than by this special one. GNU LIBRARY GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Library General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also compile or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. c) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. d) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Library General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! cinnamon-menus-3.6.0/.pc/0000775000175000017500000000000013173357262014064 5ustar fabiofabiocinnamon-menus-3.6.0/autogen.sh0000775000175000017500000000202613173357262015405 0ustar fabiofabio#!/bin/sh # Run this to generate all the initial makefiles, etc. test -n "$srcdir" || srcdir=$(dirname "$0") test -n "$srcdir" || srcdir=. olddir=$(pwd) cd $srcdir (test -f configure.ac) || { echo "*** ERROR: Directory '$srcdir' does not look like the top-level project directory ***" exit 1 } # shellcheck disable=SC2016 PKG_NAME=$(autoconf --trace 'AC_INIT:$1' configure.ac) if [ "$#" = 0 -a "x$NOCONFIGURE" = "x" ]; then echo "*** WARNING: I am going to run 'configure' with no arguments." >&2 echo "*** If you wish to pass any to it, please specify them on the" >&2 echo "*** '$0' command line." >&2 echo "" >&2 fi mkdir -p m4 glib-gettextize --force --copy || exit 1 intltoolize --force --copy --automake || exit 1 autoreconf --verbose --force --install || exit 1 cd "$olddir" if [ "$NOCONFIGURE" = "" ]; then $srcdir/configure "$@" || exit 1 if [ "$1" = "--help" ]; then exit 0 else echo "Now type 'make' to compile $PKG_NAME" || exit 1 fi else echo "Skipping configure process." fi cinnamon-menus-3.6.0/HACKING0000664000175000017500000000155413173357262014400 0ustar fabiofabioHacking on gnome-menus ====================== + The development occurs in git: http://git.gnome.org/browse/gnome-menus For information on how to access GNOME git please read: http://live.gnome.org/Git + Please send patches as bug reports in GNOME Bugzilla: https://bugzilla.gnome.org/ (product gnome-menus) Your patch should be in unified diff form (the -u option to GNU diff). See also: http://live.gnome.org/GnomeLove/SubmittingPatches + Please try and send a patch against a recent version of this package. Patches against git master are most preferable. + Don't commit any but the most trivial patches without approval. + Exceptions to this are: - Translators may commit basic i18n related patches to the build setup. - Build sheriff are welcome - in accordance with the relevant build sheriff constraints. cinnamon-menus-3.6.0/AUTHORS0000664000175000017500000000014113173357262014450 0ustar fabiofabioMark McLoughlin Havoc Pennington Vincent Untz cinnamon-menus-3.6.0/README0000664000175000017500000000233513173357262014267 0ustar fabiofabiognome-menus =========== gnome-menus contains the libgnome-menu library, the layout configuration files for the GNOME menu, as well as a simple menu editor. The libgnome-menu library implements the "Desktop Menu Specification" from freedesktop.org: http://freedesktop.org/wiki/Specifications/menu-spec http://specifications.freedesktop.org/menu-spec/menu-spec-latest.html You may download updates to the package from: http://download.gnome.org/sources/gnome-menus/ To discuss gnome-menus, you may use the desktop-devel-list mailing list: http://mail.gnome.org/mailman/listinfo/desktop-devel-list Installation ============ See the file 'INSTALL'. If you are not using a released version of gnome-menus (for example, if you checked out the code from git), you first need to run './autogen.sh'. How to report bugs ================== Bugs should be reported to the GNOME bug tracking system: https://bugzilla.gnome.org/ (product gnome-menus) You will need to create an account for yourself. Please read the following page on how to prepare a useful bug report: https://bugzilla.gnome.org/page.cgi?id=bug-writing.html Please read the HACKING file for information on where to send changes or bugfixes for this package. cinnamon-menus-3.6.0/Makefile.am0000664000175000017500000000126313173357262015442 0ustar fabiofabioSUBDIRS = libmenu ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS} DISTCHECK_CONFIGURE_FLAGS = --enable-introspection EXTRA_DIST = \ HACKING \ MAINTAINERS MAINTAINERCLEANFILES = \ $(srcdir)/INSTALL \ $(srcdir)/aclocal.m4 \ $(srcdir)/config.guess \ $(srcdir)/config.h.in \ $(srcdir)/config.sub \ $(srcdir)/depcomp \ $(srcdir)/install-sh \ $(srcdir)/ltmain.sh \ $(srcdir)/missing \ $(srcdir)/mkinstalldirs \ $(srcdir)/py-compile \ `find "$(srcdir)" -type f -name Makefile.in -print` \ $(srcdir)/configure \ $(srcdir)/m4/intltool.m4 \ $(srcdir)/m4/libtool.m4 \ $(srcdir)/m4/ltoptions.m4 \ $(srcdir)/m4/ltsugar.m4 \ $(srcdir)/m4/ltversion.m4 \ $(srcdir)/m4/lt~obsolete.m4 cinnamon-menus-3.6.0/omf.make0000664000175000017500000000456113173357262015032 0ustar fabiofabio# # No modifications of this Makefile should be necessary. # # This file contains the build instructions for installing OMF files. It is # generally called from the makefiles for particular formats of documentation. # # Note that you must configure your package with --localstatedir=/var # so that the scrollkeeper-update command below will update the database # in the standard scrollkeeper directory. # # If it is impossible to configure with --localstatedir=/var, then # modify the definition of scrollkeeper_localstate_dir so that # it points to the correct location. Note that you must still use # $(localstatedir) in this or when people build RPMs it will update # the real database on their system instead of the one under RPM_BUILD_ROOT. # # Note: This make file is not incorporated into xmldocs.make because, in # general, there will be other documents install besides XML documents # and the makefiles for these formats should also include this file. # # About this file: # This file was derived from scrollkeeper_example2, a package # illustrating how to install documentation and OMF files for use with # ScrollKeeper 0.3.x and 0.4.x. For more information, see: # http://scrollkeeper.sourceforge.net/ # Version: 0.1.3 (last updated: March 20, 2002) # omf_dest_dir=$(datadir)/omf/@PACKAGE@ scrollkeeper_localstate_dir = $(localstatedir)/scrollkeeper # At some point, it may be wise to change to something like this: # scrollkeeper_localstate_dir = @SCROLLKEEPER_STATEDIR@ omf: omf_timestamp omf_timestamp: $(omffile) -for file in $(omffile); do \ absfile=$(srcdir)/$$file; \ test -r $$file && absfile=$$file; \ scrollkeeper-preinstall $(docdir)/$(docname).xml $$absfile $$file.out; \ done; \ touch omf_timestamp install-data-hook-omf: $(mkinstalldirs) $(DESTDIR)$(omf_dest_dir) for file in $(omffile); do \ absfile=$(srcdir)/$$file.out; \ test -r $$file.out && absfile=$$file.out; \ $(INSTALL_DATA) $$absfile $(DESTDIR)$(omf_dest_dir)/$$file; \ done -scrollkeeper-update -p $(DESTDIR)$(scrollkeeper_localstate_dir) -o $(DESTDIR)$(omf_dest_dir) uninstall-local-omf: -for file in $(omffile); do \ basefile=`basename $$file`; \ rm -f $(DESTDIR)$(omf_dest_dir)/$$basefile; \ done -rmdir $(DESTDIR)$(omf_dest_dir) -scrollkeeper-update -p $(DESTDIR)$(scrollkeeper_localstate_dir) clean-local-omf: -for file in $(omffile); do \ rm -f $$file.out; \ done cinnamon-menus-3.6.0/NEWS0000664000175000017500000011023313173357262014103 0ustar fabiofabio============= Version 3.8.0 ============= * Add more java things to sundry * Add im-chooser, power statistics and personal file sharing to sundry * Remove special-casing for Fedora vendor prefixdd * Translation updates ============== Version 3.7.90 ============== Bugs Fixed: * Fix error reporting * Memory leak fixes (William John McCann) * Remove the simple editor (William John McCann) * New menu layout for gnome-shell tweaks (William John McCann, Florian Muellner, Jasper St. Pierre) * Remove old gnome-control-center files (Jasper St. Pierre) Translation updates: * Alexandre Franke * Fabio Tomat * Inaki Larranaga Murgoitio * Piotr Drąg * William Jon McCann ============= Version 3.6.2 ============= * 688972 call menu_layout_load() with non_prefixed_name parameter * Translation updates (Arabic, Kannada, Uyghur) ============= Version 3.6.1 ============= * Translation updates (Catalan, Catalan (Valencian), Ukrainian, Aragonese, Slovak, Uzbek) ============= Version 3.6.0 ============= * Translation updates ============== Version 3.5.92 ============== libmenu * Add proper header reference to GMenu-3.0.gir (Rico Tzschichholz) Translators * Nilamdyuti Goswami (as) * Petr Kovar (cs) * Peter Bach (da) * Bruce Cowan (en_GB) * Arash Mousavi (fa) * Jiri Grönroos (fi) * Claude Paroz (fr) * Leandro Regueiro (gl) * Gabor Kelemen (hu) * Dirgita (id) * Milo Casagrande (it) * Changwoo Ryu (ko) * Aurimas Černius (lt) * Sandeep Shedmake (mr) * A S Alam (pa) * Piotr Drąg (pl) * Duarte Loreto (pt) * Daniel Nylander (sv) * Dr.T.Vasudevan (ta) * Theppitak Karoonboonyanan (th) * Muhammet Kara (tr) * Nguyễn Thái Ngọc Duy (vi) * Chrovex Fan (zh_CN) ============= Version 3.5.5 ============= Translations Updates: * Traditional Chinese (Chao-Hsiung Liao) * German (Tobias Endrigkeit) * Kazakh (Baurzhan Muftakhidinov) * Silesian (Przemysław Buczkowski) * Gujarati (Sweta Kothari) * Serbian (Мирослав Николић) ============= Version 3.5.4 ============= Translations Updates * Belarusian (Alexander Shopov) ============= Version 3.5.3 ============= libmenu * Fix scanner warnings (Colin Walters) * Add gmenu_tree_iter_get_separator (Jasper St. Pierre) * Add a recursive NoDisplay getter (Jasper St. Pierre) * Add a way to get a GMenuTree from a GMenuTreeItem (Jasper St. Pierre) * Add a way to get the parent item for a GMenuTreeItem (Jasper St. Pierre) Translations Updates * Belarusian (Ihar Hrachyshka) * Greek (Tom Tryfonidis) * Assamese (Nilamdyuti Goswami) * Telugu (Sasi Bhushan Boddepalli) ============= Version 3.5.2 ============= Layout * Add a separate category for Web Applications (Giovanni Campagna) libmenu * Add a new GMENU_TREE_FLAGS_INCLUDE_UNALLOCATED flag (Vincent Untz) Translations * Bulgarian * Kashubian * Spanish * Galician * Slovenian * Hebrew * Russian * Norwegian bokmål ============= Version 3.4.0 ============= Translators * Morn Met (km) * Bahodir Mansurov (uz@cyrillic) ============= Version 3.3.5 ============= Translators * Jiro Matsuzawa (ja) * Kjartan Maraas (nb) * Danishka Navin (si) * Andiswa Mvanyashe (xh) ============= Version 3.3.1 ============= libmenu * Ignore desktop entries with no Name or Exec key (Florian Müllner) Layout * Put the Other menu at the end (Vincent) =============== Version 3.2.0.1 =============== Menu Editor * Work with latest pygobject (Vincent) ============= Version 3.2.0 ============= Translators * Nilamdyuti Goswami (as) * Praveen Illa (te) ============== Version 3.1.92 ============== libmenu * Fix build failure with --enable-debug (Vincent) Translators * Ігар Грачышка (be) * Daniel Mustieles (es) * Jiro Matsuzawa (ja) ============== Version 3.1.90 ============== libmenu * Don't try to unref potentially NULL pointers (Jasper St. Pierre) ============= Version 3.1.5 ============= This version of gnome-menus comes with a significant API and ABI break, to modernize the code. As a result, the name of the library and the pkg-config filename have changed, so that this new version is parallel-installable with previous ones. As of now, there is no guide to migrate to the new API, but it is rather straight-forward as changes were mostly done to improve the experience for introspection-based bindings. The examples shipped in util/ are a good basis. libmenu * Rebase internal representation of .desktop files on top of GDesktopAppInfo (Colin Walters) * Make GMenuTree a GObject (Colin Walters) * Use GSlice for various data (Colin Walters) * Use thread-default main context for callbacks for future flexibility for thread support (Colin Walters) * Many API changes, see new headers for changes. The most visible one is that gmenu_tree_load_sync() should explicitly be used to load the menu now. (Colin Walters, Vincent) * Drop support for "KDE Desktop Entry" group (Vincent) * Return GIcon instead of char * for icon-related API (Vincent) * Various fixes and cleanups to merge Colin's branch (Vincent, Colin Walters) * Add gmenu_tree_get_entry_by_id() API (Colin Walters) * Support XDG_CURRENT_DESKTOP (Vincent) Menu Editor * Port to introspection-based bindings (Vincent) * Stop editing settings.menu (Vincent) Python * Drop static python bindings; introspection-based ones should be used now (Colin Walters) Misc * Replace example of python code with javascript code (Colin Walters) * Change library name, header directory, pkg-config filename (Vincent) * Require glib 2.29.15 for new API (Vincent) Translators * Ігар Грачышка (be) * Gil Forcada (ca@valencia) * Priscilla Mahlangu (zu) ============= Version 3.0.1 ============= Translators * Anousak Souphavanh (lo) * Theppitak Karoonboonyanan (th) ============= Version 3.0.0 ============= Layout * Show administration tools and old capplets in Other (Vincent) Translators * Khaled Hosny (ar) * Zenat Rahnuma (bn) * Gil Forcada (ca) * Sense Hofstede (fy) * Praveen Illa (te) * Sahran (ug) * Clytie Siddall (vi) =============== Version 2.91.91 =============== Menu Editor * Fix to work with latest pygi (Vincent) Misc * Build fix (Vincent) Translators * F Wolff (af) * Changwoo Ryu (ko) * Pavol Klačanský (sk) * Korostil Daniel (uk) ============== Version 2.91.6 ============== libmenu * Do not send multiple notifications for one file change (Vincent) Menu Editor * Port to pygobject-based introspection bindings (Vincent) * Make editor GTK+ 3 ready (Vincent) Misc * Improve introspection build (Vincent) * Drop settings.menu (Vincent) Translators * Gil Forcada (ca@valencia) * Mattias Põldaru (et) * Mahyar Moghimi (fa) * Fran Diéguez (gl) * Kikongo Translation Team (kg) * Sahran (ug) * Clytie Siddall (vi) ============== Version 2.30.4 ============== libmenu * Clear cache of desktop entries set when files are added/removed (Vincent) Misc * Associate .gir with pkg-config file (Vincent) * Rename --enable-deprecations configure option to --enable-deprecation-flags (Vincent) Translators * Daniel Martinez (an) * Takayuki KUSANO (ja) * Baurzhan Muftakhidinov (kk) * Torstein Winterseth (nn) ============== Version 2.30.3 ============== Menu Editor * Respect XDG_MENU_PREFIX when writing user menu file (Vincent) Misc * Update information in README and other files (Vincent) Translators * Kristjan SCHMIDT (eo) * Sense Hofstede (fy) * Fran Diéguez (gl) * Reuben Potts (gv) * Dirgita (id) * Baurzhan M. (kk) * Sahran (ug) ============== Version 2.30.2 ============== Misc * Do not ship gir files in the tarball (Yaakov Selkowitz) Translators * Daniel Martinez (an) * Gil Forcada (ca) * Gil Forcada (ca@valencia) * Reşat SABIQ (crh) * Christian Kirbach (de) * Thomas Thurman (en@shaw) * Fran Diéguez (gl) * Shankar Prasad (kn) * Peteris Krisjanis (lv) * Wouter Bolsterlee (nl) * Matej Urbančič (sl) ============== Version 2.30.0 ============== libmenu * Fix layout processing for Menuname nodes (Vincent) * Never ignore Menuname nodes from DefaultLayout (Vincent) Misc * Add configure summary (Vincent) Translators * Gil Forcada (ca) * Kostas Papadimas (el) * Iñaki Larrañaga Murgoitio (eu) * Changwoo Ryu (ko) * Badral (mn) =============== Version 2.29.92 =============== libmenu * Add gobject-introspection support (Vincent) Misc * Build fix (Vincent) Translators * Nikos Charonitakis (el) * Pavol Klačanský (sk) =============== Version 2.29.91 =============== Misc * Make some non-visible strings non-translatable (Vincent) * Add translator comment (Vincent) Translators * Fran Diéguez (gl) * Torstein Adolf Winterseth (nn) ============== Version 2.29.6 ============== libmenu * Fix miscalculation for inlining when inline_header = true (Vincent) * Add real support for inline aliases during layout processing (Vincent) * Support inline alias of an inline alias (Vincent) * Do not count non-inlining submenus as inlining with header (Vincent) Translators * Sadia Afroz (bn) * Gil Forcada (ca) * Reşat SABIQ (crh) * Thomas Thurman (en@shaw) * Sveinn í Felli (is) * Nils-Christoph Fiedler (nds) * Dmitry Yacenko (ru) * Krishna Babu K (te) * 甘露(Gan Lu) (zh_CN) ================ Version 2.28.0.1 ================ libmenu * Fix sorting of menu items during merge to actually work (and not crash) (Frédéric Crozat) Python * Link the python module to libpython (Frédéric Crozat, Vincent) ============== Version 2.28.0 ============== Translators * Amitakhya Phukan (as) * Petr Kovar (cs) * Peter Bach (da) * Philip Withnall (en_GB) * Rajesh Ranjan (hi) * Luca Ferretti (it) * Shankar Prasad (kn) * Gintautas Miliauskas (lt) * Rajesh Ranjan (mai) * Peter Ani * Sandeep Shedmake (mr) * Nils-Christoph Fiedler (nds) * A S Alam (pa) * Adi Roiban (ro) * Matej Urbančič (sl) * Daniel Nylander (sv) * Krishna Babu K (te) * Baris Cicek (tr) * Maxim Dziumanenko (uk) * Chao-Hsiung Liao (zh_HK) * Chao-Hsiung Liao (zh_TW) =============== Version 2.27.92 =============== This releases features new API that applications can use to display the full name contained in .desktop files that is now in the X-GNOME-FullName key. If an application chooses to use this, then it should set the sort key so that .desktop files are correctly sorted. libmenu * Add gmenu_tree_entry_get_display_name() API (Vincent) This will return X-GNOME-FullName if available, and fallback to Name. * Add gmenu_tree_get_sort_key()/gmenu_tree_set_sort_key() (Vincent) The default sort key is still Name. Users of gmenu_tree_entry_get_display_name() should use gmenu_tree_set_sort_key(). Python * Bind new API (Vincent) * Add missing bindings for gmenu_tree_entry_get_generic_name() (Vincent) Menu Editor * Remove deprecated Encoding key from desktop file (Frédéric Péters) * Use display name instead of name (Vincent) Translators * Khaled Hosny (ar) * Alexander Shopov (bg) * Runa Bhattacharjee (bn_IN) * Denis (br) * Mario Blättermann (de) * Iñaki Larrañaga Murgoitio (eu) * Claude Paroz (fr) * Seán de Búrca (ga) * Anton Meixome (gl) * Sweta Kothari (gu) * Gabor Kelemen (hu) * Francesco Marletta (it) * Takayuki KUSANO (ja) * Changwoo Ryu (ko) * Kjartan Maraas (nb) * Mario Blättermann (nds) * Manoj Kumar Giri (or) * Tomasz Dominikowski (pl) * Duarte Loreto (pt) * Krix Apolinário (pt_BR) * Горан Ракић (sr) * Goran Rakić (sr@latin) * Dr.T.Vasudevan (ta) * Theppitak Karoonboonyanan (th) * 甘露 (Lu Gan) (zh_CN) ============== Version 2.27.5 ============== Misc * Use silent-rules with automake 1.11 (Vincent) Translators * Ilkka Tuohela (fi) ============== Version 2.27.4 ============== libmenu * Improve performance by using a cache to not compute the same thing again and again (Michael Meeks, Vincent) * Add API to access GenericName (Robert Staudinger) * Fix DefaultLayout attributes not being inherited (Vincent) * Do not always inherit parent DefaultLayout attributes (Vincent) * Sort inlined items unless inline_header is used (Vincent) Menu Editor * Add --help and --version arguments (Vincent) * Port to GtkBuilder (Pedro Fragoso, Vincent) Misc * Use shave to improve build log readability (Vincent) Translators * Jordi Mallach (ca@valencia) * Huxain (dv) * Jorge González (es) * Mattias Põldaru (et) * Seán de Búrca (ga) * Yaron Shahrabani (he) * Daniel Nylander (sv) ============== Version 2.26.1 ============== Translators * Khaled Hosny (ar) * Daniel Nylander (sv) ============== Version 2.26.0 ============== Translators * Reşat SABIQ (crh) * Suso Baleato (gl) * Rajesh Ranjan (hi) * Francesco Marletta (it) * Manoj Kumar Giri (or) =============== Version 2.25.91 =============== Translators * Changwoo Ryu (ko) * Raivis Dejus (lv) * Sandeep Shedmake (mr) * Горан Ракић (sr) * Daniel Nylander (sv) * Woodman Tuen (zh_HK) * Woodman Tuen (zh_TW) ============== Version 2.25.5 ============== Misc * Use gnome-common macro to define DEPRECATED build variables (Vincent) Translators * Reşat SABIQ (crh) * Saudat Mohammed (ha) * Sylvester Onye (ig) * Fajuyitan, Sunday Ayo (yo) ============== Version 2.25.2 ============== Fixes * Fix a critical warning in the python binding for monitoring a file (Vincent) Misc * Ship a gnome-menus-ls.py script that is an example of python bindings and that can be used as a replacement for gnome-menu-spec-test (Vincent) ============== Version 2.24.2 ============== Translators * Mikel González (ast) ============== Version 2.24.1 ============== Translators * Khaled Hosny (ar) * Rostislav "zbrox" Raykov (bg) * Margulan Moldabekov (kk) ============== Version 2.24.0 ============== Translators * F Wolff (af) * Khaled Hosny (ar) * Roozbeh Pournader (fa) * Rajesh Ranjan (hi) * Gabor Kelemen (hu) * Francesco Marletta (it) * Shankar Prasad (kn) * Leonardo Ferreira Fontenelle (pt_BR) * Mișu Moldovan (ro) =============== Version 2.23.92 =============== Translators * Seán de Búrca (ga) * Krešo Kunjas (hr) * Praveen Arimbrathodiyil (ml) * Leonardo Ferreira Fontenelle (pt_BR) =============== Version 2.23.91 =============== Translators * Khaled Hosny (ar) * Reuven Gonen (he) * Shankar Prasad (kn) ============== Version 2.23.6 ============== Layout * Fix the icon for the accessibility menu (Matthias Clasen) Translators * Khaled Hosny (ar) * Fabrício Godoy (pt_BR) ============== Version 2.23.5 ============== Translators * Yannig Marchegay (Kokoyaya) (oc) * Wadim Dziedzic (pl) * Zabeeh Khan (ps) * Laurent Dhima (sq) ============== Version 2.23.4 ============== Misc * Require intltool 0.40.0 (Vincent) Translators * 甘露(Lu Gan) (zh_CN) ============== Version 2.23.3 ============== Features * Implement handling of $XDG_MENU_PREFIX from the xdg menu specification (Vincent) Fixes * Fix the values of (ie, show_empty, inline, inline_limit, etc.) not being inherited by submenus when the node is after the node in the .menu file (Vincent) * Fix a bug where the fallback on the filename in couldn't be used (Vincent) Translators * Djihed Afifi (ar) * Anna Ármansdóttir (is) * Manoj Kumar Giri (or) ============== Version 2.23.1 ============== Features * Do not show separators at the beginning/end of a menu, or after another separator, but add an option to show them (Vincent) Fixes * Call g_type_init() because GIO needs it and we use GIO for file monitoring (Vincent) * Fix a crash when a file notification is emitted with a non-ascii filename (Vincent) * Remove entries from the excluded set if they are included after they were excluded (eg: somethingsomething) (Vincent) * Implicitly add nodes to and that are missing some (Vincent) * Correctly order the move operations to execute so that moving something and moving it back again works as undo (Vincent) * Simplify some code (William Jon McCann, Vincent) * Plug leak (Vincent) Layout * Do not show accessibility items in the accessories submenu (Josselin Mouette) * Merge menus and files at the end of the layout of settings.menu * Explicitly do not include gnomecc.menu in the preferences menu instead of explicitly excluding it, so that alacarte doesn't know it was excluded (Vincent) * Rename many .directory files so they use fd.o Categories as name (Vincent) * Remove preferences.menu and directly include things in settings.menu (Vincent) * Update a few icons used in .directory files according to the icon naming spec (Vincent) * Remove the accessibility submenu from Preferences, since it's empty now anyway (Vincent) Misc * Remove shebangs from non-executable Python scripts. Translators * Žygimantas Beručka (lt) * Kjartan Maraas (nb) ============== Version 2.22.1 ============== Misc * Remove shebangs from non-executable Python scripts. Translators * David Lodge (en_GB) * Massimo Furlani (fur) * Eskild Hustvedt (nn) ============== Version 2.22.0 ============== Translators * Petr Kovar (cs) * nikosCharonitakis (el) * Changwoo Ryu (ko) * Žygimantas Beručka (lt) * Sandeep Shedmake (mr) * Woodman Tuen (zh_HK) * Woodman Tuen (zh_TW) =============== Version 2.21.92 =============== Translators * Massimo Furlani (fur) * Sangeeta Kumari (mai) * Nabin Gautam (ne) =============== Version 2.21.91 =============== Fixes * Remove the various monitor backends, and unconditionnaly use gio (Vincent) Misc * Do not install gnome-menu-spec-test, it's useless for the user (Vincent) * Add a hard dependency on gio (Vincent) Translators * Woodman Tuen (zh_HK) * Woodman Tuen (zh_TW) =============== Version 2.21.90 =============== Misc * When using gio, require version 2.15.2 (Saleem Abdulrasool) Translators * Djihed Afifi (ar) * Alexander Nyakhaychyk (be) * Michael Terry (io) ============== Version 2.21.5 ============== Fixes * Fix for API changes in gio (Sebastian Bacher, Wouter Bolsterlee, Vincent) Translators * F Wolff (af) * Iñaki Larrañaga Murgoitio (eu) * Erdal Ronahi (ku) * Kjartan Maraas (nn) * Yannig Marchegay (Kokoyaya) (oc) ============== Version 2.21.3 ============== Features * Fix for api change in gio (Kjartan Maraas) * Prevent a major memory leak (Sebastian Droge) Translators * Petr Kovar (cs) * Seán de Búrca (ga) * Danishka Navin (si) ============== Version 2.21.2 ============== Features * Add a new GIO-based monitor backend. It is now the preferred one. The --with-monitor-backend configure flag can be used to select the backend to build. (Sebastian Dröge) Translators * Anas Husseini (ar) * Peter Bach (da) * Jorge González (es) * Seán de Búrca (ga) * Ignacio Casal Quinteiro (gl) * Huda Toriq (id) * Matej Urbančič (sl) ============== Version 2.20.1 ============== Translators * Peter Bach (da) ============== Version 2.20.0 ============== Translators * Anas Husseini (ar) * Amitakhya Phukan (as) * Jordi Mallach (ca) * Peter Bach (da) * Simos Xenitellis (el) * Francesco Marletta (it) * Shankar Prasad (kn) * Žygimantas Beručka (lt) * Alexandru Szasz (ro) * Nickolay V. Shmyrev (ru) * Peter Tuhársky (sk) * Горан Ракић (sr) * Maxim Dziumanenko (uk) =============== Version 2.19.92 =============== Fixes * Fix potential crash (Rob Bradford) Translators * Rostislav "zbrox" Raykov (bg) * Stéphane Raimbault (fr) * Arangel Angov (mk) * Tomasz Dominikowski (pl) * Leonardo Ferreira Fontenelle (pt_BR) * Duarte Loreto (pt) * Funda Wang (zh_CN) =============== Version 2.19.90 =============== Translators * Ani Peter (ml) * Inaki Larranaga Murgoitio (eu) * Ankit Patel (gu) * Ilkka Tuohela (fi) * Yair Hershkovitz (he) * Sean Burke (ga) ============== Version 2.19.6 ============== Translators * Ihar Hrachyshka (be@latin) * Runa Bhattacharjee (bn_IN) * Hendrik Richter (de) * Ilkka Tuohela (fi) * Gabor Kelemen (hu) * Eunju Kim (ko) * Raivis Dejus (lv) * Wouter Bolsterlee (nl) * Bharat Kumar (te) * Nurali Abdurahmonov (uz@cyrillic) ============== Version 2.19.5 ============== Fixes * Use python-config to get python includes (Sebastien Bacher) * Don't show screensavers in the menus (Ray Strode) Translators * Tshewang Norbu (dz) * Takeshi AIHANA (ja) * Tomasz Dominikowski (pl) * Danishka Navin (si) * Daniel Nylander (sv) * Dr.T.Vasudevan (ta) * Bharat Kumar (te) * Nurali Abdurahmonov (uz) * Clytie Siddall (vi) ============== Version 2.19.4 ============== Fixes * Fix crashes in python bindings (Colin Walters) * Fix crash in inotify backend when ~/.config/menus is created (Vincent) Translators * Alexander Nyakhaychyk (be) ============== Version 2.19.3 ============== Fixes * Use G_DIR_SEPARATOR instead of '/' (Vincent) * Fix small leak (William KJon McCann) Translators * David Lodge (en_GB) * Jorge González (es) * Ivar Smolin (et) * Theppitak Karoonboonyanan (th) ============== Version 2.19.2 ============== Menu Layout * Fix "system-wide" typo (Vincent) * Put Preferences before Administration in the System menu (Vincent) * Use icons from the icon naming spec (Luca Ferreti, Matthias Clasen, Vincent) * Use Universal Access instead of Accessibility (Calum Benson, Vincent) * Use System instead of Desktop since the menu got renamed (Sébastien Bacher) * Do not require the Application category in the Other submenu (Vincent) Menu Editor * Fix a crash when unselecting the current menu (Vincent) * Require pygtk at runtime (Vincent) * Use the python executable found by configure (Vincent) Misc * Require automake 1.9 Translators * Ihar Hrachyshka (be@latin) * norbu (dz) * Jorge González (es) * Iñaki Larrañaga Murgoitio (eu) * Ignacio Casal Quinteiro (gl) * Francesco Marletta (it) * Raivis Dejus (lv) * Kjartan Maraas (nb) * Yannig MARCHEGAY (Kokoyaya) (oc) * Matej Urbančič (sl) * Daniel Nylander (sv) ============== Version 2.18.0 ============== Translators * Alaksandar Navicki (be@latin) * Peter Bach (da) * Simos Xenitellis (el) * Ankit Patel (gu) * Žygimantas Beručka (lt) * wadim dziedzic (pl) * Elian Myftiu (sq) * Горан Ракић (sr) =============== Version 2.17.92 =============== Fixes * Show the system menu directories by default (as it was in 2.16) (Denis Washington) Translators * Peter Bach (da) * Takeshi AIHANA (ja) * Duarte Loreto (pt) * Clytie Siddall (vi) * Funda Wang (zh_CN) * Woodman Tuen (zh_HK) * Woodman Tuen (zh_TW) =============== Version 2.17.91 =============== Features * Rework the layout so that it's easy to have the old layout. The control center will ship its own menu file to have the layout for the shell. (Denis Washington) Translators * Khaled Hosny (ar) * Ihar Hrachyshka (be) * Alaksandar Navicki (be@latin) * Rostislav "zbrox" Raykov (bg) * Runa Bhattacharjee (bn_IN) * Mahay Alam Khan (bn) * Jordi Mallach (ca) * Jakub Friedl (cs) * Rhys Jones (cy) * Hendrik Richter (de) * David Lodge (en_GB) * Ivar Smolin (et) * Ilkka Tuohela (fi) * Robert-André Mauchin (fr) * Reuven Gonen (he) * Rajesh Ranjan (hi) * Gabor Kelemen (hu) * Young-Ho Cha (ko) * Žygimantas Beručka (lt) * Jovan Naumovski (mk) * Badral (mn) * Kjartan Maraas (nb) * Wouter Bolsterlee (nl) * Og Maciel (pt_BR) * Leonid Kanter (ru) * Steve Murphy (rw) * Daniel Nylander (sv) * Theppitak Karoonboonyanan (th) * Clytie Siddall (vi) ============== Version 2.17.5 ============== Features * New menu layout for the control center capplets (Denis Washington) Translators * Khaled Hosny (ar) * Ihar Hrachyshka (be) ============== Version 2.17.2 ============== Features * Flesh out inotify support (use --enable-inotify) (Mark) Fixes * Don't load incorrectly encoded .desktop files (Mark) * Fix compile warning (Mark) Translators * Khaled Hosny (ar) * Guillaume Savaton (eo) ============== Version 2.16.1 ============== Translators * David Lodge (en_GB) * Francesco Marletta (it) ============== Version 2.16.0 ============== Translators * Gabor Kelemen (hu) * Jovan Naumovski (mk) * Badral (mn) * Rahul Bhalerao (mr) * Matic Žgur (sl) * Onur Can Çakmak (tr) =============== Version 2.15.91 =============== Translators * Runa Bhattacharjee (bn_IN) * Francisco Javier F. Serrador (es) * Arangel Angov (mk) * Matic Žgur (sl) =============== Version 2.15.90 =============== Translators * Ani Peter (ml) * Subhransu Behera (or) * Theppitak Karoonboonyanan (th) ================ Version 2.15.4.1 ================ Fixes * Correctly update LT_VERSION (Vincent) ============== Version 2.15.4 ============== Features * Add new API to know if an application should be launched in a terminal and to know the path to the desktop file (Travis Watkins) * Complete python bindings for the "No Display" flag (Travis Watkins) Menu Editor * Allow specifying alternate menu files as command line arguments (William Jon McCann) Misc * Use po/LINGUAS (Wouter Bolsterlee) * Require intltool 0.35.0 (Vincent Untz) Translators * Runa Bhattacharjee (bn_IN) * Matheus Grandi (gn) * Swapnil Hajare (mr) ============== Version 2.14.0 ============== Features * Start inotify support (not compiled by default) (Mark McLoughlin) Fixes * Small fix for the python bindings (Mark McLoughlin) * Fix infinite loop (Mark McLoughlin) Translators * Ales Nyakhaychyk (be) * Jérémy Le Floc'h (br) * Petr Tomeš (cs) * Rhys Jones (cy) * Ole Laursen (da) * Hendrik Richter (de) * Pema Geyleg (dz) * Kostas Papadimas (el) * Reuven Gonen (he) * Rajesh Ranjan (hi) * Takeshi AIHANA (ja) * Vladimer Sichinava (ka) * Erdal Ronahi (ku) * Žygimantas Beručka (lt) * Raivis Dejus (lv) * Thierry Randrianiriana (mg) * Wouter Bolsterlee (nl) * Kjartan Maraas (nn) * GNOME PL Team (pl) * Evandro Fernandes Giovanini (pt_BR) * Duarte Loreto (pt) * Sebastian Ivan (ro) * Leonid Kanter (ru) * Elian Myftiu (sq) * Слободан Д. Средојевић (sr) * Maxim Dziumanenko (uk) * Funda Wang (zh_CN) * Woodman Tuen (zh_HK) * Woodman Tuen (zh_TW) ============== Version 2.13.5 ============== Features * Add "include NoDisplay" flag (Mark McLoughlin) Fixes * Fix issue where menu wouldn't fully reload after lots of file change events (Mark McLoughlin, Frederic Crozat) * Remove some unused code (Kjartan Maraas) * Fix incorrect escaping of C format string (The Written Word) Translators * Rostislav Raykov (bg) * Mahay Alam Khan (bn) * Jordi Mallach (ca) * Miloslav Trmac (cs) * Adam Weinberger (en_CA) * Francisco Javier F. Serrador (es) * Priit Laes (et) * Iñaki Larrañaga (eu) * Ilkka Tuohela (fi) * Christophe Merlet (RedFox) (fr) * Ignacio Casal Quinteiro (gl) * Ankit Patel (gu) * Gabor Kelemen (hu) * Francesco Marletta (it) * Erdal Ronahî (ku) * Timur Jamakeev (ky) * Kjartan Maraas (nb, no) * Tino Meinen (nl) * Marcel Telka (sk) * Christian Rose (sv) * Y.Kiran Chandra (te) * Theppitak Karoonboonyanan (th) * Abduxukur Abdurixit (ug) * Clytie Siddall (vi) * Woodman Tuen (zh_HK, zh_TW) ============== Version 2.12.0 ============== Fixes * Fix FAM crasher in gmenu-simple-editor (Ed Catmur) Translators * Rhys Jones (cy) * Vincent Untz (fr) * Ignacio Casal Quinteiro (gl) * Norayr Chilingaryan (hy) * Žygimantas Beručka (lt) * Duarte Loreto (pt) * Leonid Kanter (ru) * Elian Myftiu (sq) * Данило Шеган (sr) * Onur Can Cakmak (tr) * Clytie Siddall (vi) =============== Version 2.11.92 =============== Fixes * Fix memory corruption crasher handling notifies (Mark) * Fix python syntax warning (Mark) * Fix build when FAM isn't found (Elijah Newren) * Fix crasher when a references a subdir of another (Mark) * Fix duplicate entries after updating (Mark) * Fix infinite loop (Frederic Crozat) * Make with prefix work again (Chris Lahey, Mark) Translators * Rostislav "zbrox" Raykov (bg) * Jordi Mallach (ca) * Hendrik Brandt (de) * Nikos Charonitakis (el) * Roozbeh Pournader (fa) * ahmad riza h nst (id) * Takeshi AIHANA (ja) * Young-Ho Cha (ko) * GNOME PL Team (pl) * Sebastian Ivan (ro) * Maxim Dziumanenko (uk) * Clytie Siddall (vi) =============== Version 2.11.91 =============== Fixes * Install .desktop file for editor (Dennis Cranston, Mark) * Fix the window icon in the editor (Jaap A. Haitsma, Mark) * Allow running editor in different prefix from python (Mark) Translators * Miloslav Trmac (cs) * Hendrik Brandt (de) * Adam Weinberger (en_CA) * Francisco Javier F. Serrador (es) * Priit Laes (et) * Ilkka Tuohela (fi) * Ankit Patel (gu) * Reuven Gonen (he) * Gabor Kelemen (hu) * Takeshi AIHANA (ja) * Kjartan Maraas (nb) * Tino Meinen (nl) * Kjartan Maraas (no) * Afonso Celso Medina (pt_BR) * Marcel Telka (sk) * Theppitak Karoonboonyanan (th) * Clytie Siddall (vi) * Funda Wang (zh_CN) * Woodman Tuen (zh_TW) =============== Version 2.11.90 =============== Fixes * Fix issue with handling of filename encodings (Mark) * Only try to include ".directory" for if it exists (Mark) * Re-name the Edutainment sub-menu to Education (Mark) * Fix spec compliance issue with tag handling (Mark) * Remove some unused code (Mark) * Plug some leaks (Mark) Menu Editor * HIGify menu editor (Dennis Cranston) * Make "Desktop" menu appear correctly (Mark) Misc * Allow building against uninstalled library (Brian Cameron) Translators * Ales Nyakhaychyk (be) * Rostislav "zbrox" Raykov (bg) * Miloslav Trmac (cs) * Martin Willemoes Hansen (da) * Hendrik Brandt (de) * Nikos Charonitakis (el) * Adam Weinberger (en_CA) * Francisco Javier F. Serrador (es) * Priit Laes (et) * Iñaki Larrañaga (eu) * Ilkka Tuohela (fi) * Ignacio Casal Quinteiro (gl) * Ankit Patel (gu) * Yuval Tanny (he) * Swapnil Hajare (mr) * Terance Edward Sola (nb) * Ganesh Ghimire (ne) * Tino Meinen (nl) * Terance Edward Sola (no) * Marcel Telka (sk) * Elian Myftiu (sq) * Данило Шеган (sr) * Theppitak Karoonboonyanan (th) * Onur Can Cakmak (tr) * Clytie Siddall (vi) * Funda Wang (zh_CN) * Woodman Tuen (zh_TW) ================ Version 2.11.1.1 ================ Fixes * Fix crasher bug in libgnome-menu triggered by editor (Mark) * Make the editor create $XDG_CONFIG_HOME/menus if it doesn't exist (Mark) ============== Version 2.11.1 ============== Features * Simple menu editor (Mark) * Python bindings (Mark) * Support for and (Mark, Frederic Crozat) * Use FAM directly for monitoring rather than gnome-vfs (Mark) * Add API for retaining empty sub-menus and excluded items in the GMenuTree (Mark, Christian Neumair) * Add gmenu_tree_directory_get_menu_id() API (Mark) * Add gmenu_tree_directory_get_tree() and gmenu_tree_get_menu_file() API (Mark) * Namespace the API - i.e. MenuTree -> GMenuTree (Mark) Fixes * Plug major memory leak when the menu is reloaded (Mark) * Fix "recursive inclusion" crash (Mark) * Fix problem where you could end up with identical items in the same menu (Mark) * Fix issue where you could end up with more than one menu with the same name (Mark) * Update for changes to behaviour in spec (Mark) * Fix off-by-one errors shown up in valgrind (Mark) * Remove s from default menu (Mark) Translators * Vladimir "Kaladan" Petkov (bg) * Miloslav Trmac (cs) * Hendrik Brandt (de) * Adam Weinberger (en_CA) * David Lodge (en_GB) * Francisco Javier F. Serrador (es) * Priit Laes (et) * Iñaki Larrañaga (eu) * Takeshi AIHANA (ja) * Steve Murphy (rw) * Canonical Ltd (xh) ============== Version 2.10.1 ============== Fixes * Add support for new "type" argument to (Mark) * Monitor s for changes (Mark) * Make user desktop entries override system ones (Mark) * Make .directory files in s be pulled in (Mark) * Fix weirdess with [KDE Desktop Entry] files (Mark) * Fix s which don't contain any entries in the toplevel (Mark) * Make sure items in s as allocated (Mark) * Make s with a prefix work correctly (Mark) Translators * Adam Weinberger (en_CA) * Daniel van Eeden (nl) ============== Version 2.10.0 ============== Fixes * Fix 64-bit crasher (Jeremy Katz) Translators * Dafydd Harries (cy) * Farzaneh Sarafraz (fa) * Rajesh Ranjan (hi) * Žygimantas Beručka (lt) * Данило Шеган (sr) * Woodman Tuen (zh_TW) ============== Version 2.9.92 ============== Fixes * Fix issue with file monitoring and subdirs of (Mark) * Fix bug with the directive (Mark) * Make gnome-menu-spec-test work with menu-spec test framework again (Mark) Translators * Arafat Medini (ar) * Jordi Mallach (ca) * Martin Willemoes Hansen (da) * Nikos Charonitakis (el) * David Lodge (en_GB) * Ankit Patel (gu) * Laszlo Dvornik (hu) * ahmad riza h nst (id) * Francesco Marletta (it) * Takeshi AIHANA (ja) * Sang-Gju Kim (ko) * Rajeev Shrestha (ne) * Daniel van Eeden (nl) * GNOME PL Team (pl) * Duarte Loreto (pt) * Dan Damian (ro) * Leonid Kanter (ru) * Elian Myftiu (sq) ============== Version 2.9.90 ============== Fixes * Do not include the Core category in the Other menu (Vincent Untz) Translators * Vladimir "Kaladan" Petkov (bg) * Francisco Javier F. Serrador (es) * Priit Laes (et) * Tommi Vainikainen (fi) * Baptiste Mille-Mathias (fr) * Žygimantas Beručka (lt) * Kjartan Maraas (nb) * Kjartan Maraas (nn) * Raphael Higino (pt_BR) * Marcel Telka (sk) * Christian Rose (sv) * Theppitak Karoonboonyanan (th) * Maxim Dziumanenko (uk) =============== Version 2.9.4.1 =============== Features * Add menu_tree_entry_get_exec() (Richard Hult) Translators * Miloslav Trmac (cs) * Kjartan Maraas (nb) ============= Version 2.9.4 ============= Fixes * New menus layout (Vincent Untz) * Reload menus correctly when they are deleted/updated (Frederic Crozat) * Ref the return value from menu_tree_entry_get_parent() (Mark) Translators * Hendrik Brandt (de) * Adam Weinberger (en_CA) * Baptiste Mille-Mathias (fr) * Arangel Angov (mk) ============= Version 2.9.3 ============= Fixes * Find the right icon path in desktop files (Frederic Crozat) * Handle root path correctly (Mark) * Always remove file monitors (Mark) * Plug leak (Vincent Untz) * Implement behaviour defined in version 0.9 of the spec: entries that match an rule and an rule are marked as "allocated" (Mark) Translators * Vladimir "Kaladan" Petkov (bg) * David Nielsen (da) * Hendrik Brandt (de) * Simos Xenitellis (el) * Iñaki Larrañaga (eu) * Tommi Vainikainen (fi) * Gabor Kelemen (hu) * Žygimantas Beručka (lt) * Duarte Loreto (pt) * Dmitry G. Mastrukov (ru) * Marcel Telka (sk) * Данило Шеган (sr) * Christian Rose (sv) * Theppitak Karoonboonyanan (th) ============= Version 2.9.2 ============= Fixes * Fix a bunch of leaks (Frederic Crozat) * Fix problem where menu entries appear in random places (Mark) * Don't go into an infinite loop if $XDG_CONFIG_DIRS is set wrong (Mark) * Put the user config/data dirs before the system dirs (Mark) * Allow removing monitors from handlers (Mark) Translators * Miloslav Trmac (cs) * Hendrik Brandt (de) * Adam Weinberger (en_CA) * Francisco Javier F. Serrador (es) * Satoru SATOH (ja) * Hasbullah Bin Pit (ms) * Kjartan Maraas (nb) * Daniel van Eeden (nl) * Raphael Higino (pt_BR) * Funda Wang (zh_CN)