├── debian ├── source │ └── format ├── libcinnamon-menu-3-0.install ├── gir1.2-cmenu-3.0.install ├── libcinnamon-menu-3-dev.install ├── rules ├── control ├── copyright └── libcinnamon-menu-3-0.symbols ├── docs └── reference │ ├── version.xml.in │ ├── meson.build │ └── cmenu-docs.xml ├── AUTHORS ├── .gitignore ├── meson_options.txt ├── .github └── workflows │ └── build.yml ├── README ├── meson.build ├── libmenu ├── menu-util.h ├── menu-monitor.h ├── meson.build ├── entry-directories.h ├── desktop-entries.h ├── gmenu-desktopappinfo.h ├── menu-layout.h ├── gmenu-tree.h ├── menu-monitor.c ├── menu-util.c ├── desktop-entries.c ├── gmenu-desktopappinfo.c └── entry-directories.c ├── COPYING └── COPYING.LIB /debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (native) 2 | -------------------------------------------------------------------------------- /docs/reference/version.xml.in: -------------------------------------------------------------------------------- 1 | @VERSION@ 2 | -------------------------------------------------------------------------------- /debian/libcinnamon-menu-3-0.install: -------------------------------------------------------------------------------- 1 | usr/lib/*/*.so.* 2 | -------------------------------------------------------------------------------- /debian/gir1.2-cmenu-3.0.install: -------------------------------------------------------------------------------- 1 | usr/lib/*/girepository-1.0 2 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Mark McLoughlin 2 | Havoc Pennington 3 | Vincent Untz 4 | -------------------------------------------------------------------------------- /debian/libcinnamon-menu-3-dev.install: -------------------------------------------------------------------------------- 1 | usr/include 2 | usr/lib/*/*.so 3 | usr/lib/*/pkgconfig 4 | usr/share/gir-1.0 5 | usr/share/gtk-doc 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.debhelper.log 2 | debian/*.debhelper 3 | debian/*.substvars 4 | debian/debhelper-build-stamp 5 | debian/files 6 | debian/gir1.2-cmenu-3.0/ 7 | debian/libcinnamon-menu-3-0-dbg/ 8 | debian/libcinnamon-menu-3-0/ 9 | debian/libcinnamon-menu-3-dev/ 10 | debian/tmp/ 11 | -------------------------------------------------------------------------------- /meson_options.txt: -------------------------------------------------------------------------------- 1 | option('deprecated_warnings', 2 | type: 'boolean', 3 | value: true, 4 | description: 'Show build warnings for deprecations' 5 | ) 6 | option('enable_debug', 7 | type: 'boolean', 8 | value: true, 9 | description: 'Enable debugging' 10 | ) 11 | option('enable_docs', 12 | type: 'boolean', 13 | value: false, 14 | description: 'Build the API references (requires gtk-doc)' 15 | ) 16 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | 3 | export DPKG_GENSYMBOLS_CHECK_LEVEL = 2 4 | export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed 5 | DEB_HOST_MULTIARCH ?= $(shell dpkg-architecture -qDEB_HOST_MULTIARCH) 6 | 7 | %: 8 | dh $@ --with gir 9 | 10 | override_dh_strip: 11 | dh_strip --dbg-package=libcinnamon-menu-3-0-dbg 12 | 13 | override_dh_auto_configure: 14 | dh_auto_configure -- \ 15 | -D deprecated_warnings=false \ 16 | -D enable_docs=true 17 | -------------------------------------------------------------------------------- /docs/reference/meson.build: -------------------------------------------------------------------------------- 1 | version_conf = configuration_data() 2 | version_conf.set('VERSION', version) 3 | 4 | configure_file( 5 | input: 'version.xml.in', 6 | output: 'version.xml', 7 | configuration: version_conf, 8 | ) 9 | 10 | gnome.gtkdoc( 11 | 'cmenu', 12 | src_dir: join_paths(meson.project_source_root(), 'libmenu'), 13 | main_xml: 'cmenu-docs.xml', 14 | scan_args: ['--rebuild-types'], 15 | ignore_headers: libmenu_private_headers, 16 | dependencies: cmenu_dep, 17 | install: true, 18 | ) 19 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | workflow_dispatch: 11 | inputs: 12 | debug_enabled: 13 | type: boolean 14 | description: 'Start an SSH server on failure.' 15 | required: false 16 | default: false 17 | 18 | jobs: 19 | build: 20 | uses: linuxmint/github-actions/.github/workflows/do-builds.yml@master 21 | with: 22 | commit_id: master 23 | ############################## Comma separated list - like 'linuxmint/xapp, linuxmint/cinnamon-desktop' 24 | dependencies: 25 | ############################## 26 | 27 | -------------------------------------------------------------------------------- /docs/reference/cmenu-docs.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | ]> 6 | 7 | 8 | 9 | Cinnamon Menus Reference Manual 10 | For Cinnamon Menus &version; 11 | 12 | 13 | 14 | 15 | API Reference 16 | 17 | Classes 18 | 19 | 20 | 21 | 22 | 23 | API Index 24 | 25 | 26 | 27 | 28 | 29 | 30 | Index 31 | 32 | 33 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | cinnamon-menus 2 | =========== 3 | 4 | cinnamon-menus contains the libcinnamon-menu library, the layout configuration 5 | files for the Cinnamon menu, as well as a simple menu editor. 6 | 7 | The libcinnamon-menu library implements the "Desktop Menu Specification" 8 | from freedesktop.org: 9 | 10 | http://freedesktop.org/wiki/Specifications/menu-spec 11 | http://specifications.freedesktop.org/menu-spec/menu-spec-latest.html 12 | 13 | You may download updates to the package from: 14 | 15 | https://github.com/linuxmint/cinnamon-menus/releases 16 | 17 | 18 | Installation 19 | ============ 20 | 21 | 1) Run meson with options you like. The following configuration installs 22 | all binaries, libs, and shared files into /usr/local, and enables all 23 | available options: 24 | 25 | meson debian/build \ 26 | --prefix=/usr/local \ 27 | --buildtype=plain \ 28 | -D deprecated_warnings=false 29 | 30 | 2) Compile and install (sudo is needed for install) 31 | 32 | ninja -C debian/build 33 | ninja -C debian/build install 34 | 35 | 3) You can uninstall the installed files with 36 | 37 | ninja -C debian/build uninstall 38 | 39 | 40 | How to report bugs 41 | ================== 42 | 43 | Bugs should be reported to the Cinnamon bug tracking system: 44 | 45 | https://github.com/linuxmint/cinnamon-menus/issues 46 | 47 | You will need to create an account for yourself. 48 | 49 | -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | project('cinnamon-menus', 'c', version : '6.6.0', meson_version : '>=0.56.0') 2 | 3 | gnome = import('gnome') 4 | 5 | version = meson.project_version() 6 | 7 | binary_version = '0.0.1' 8 | binary_major_version = binary_version.split('.')[0] 9 | 10 | cmenu_conf = configuration_data() 11 | cmenu_conf.set_quoted('PACKAGE', meson.project_name()) 12 | 13 | # directories 14 | prefix = get_option('prefix') 15 | datadir = get_option('datadir') 16 | libdir = get_option('libdir') 17 | includedir = get_option('includedir') 18 | 19 | # generate config.h 20 | config_h_file = configure_file( 21 | output : 'config.h', 22 | configuration : cmenu_conf 23 | ) 24 | 25 | config_h = declare_dependency( 26 | sources: config_h_file 27 | ) 28 | 29 | include_root = include_directories('.') 30 | 31 | c_args = [ 32 | '-DGMENU_I_KNOW_THIS_IS_UNSTABLE', 33 | ] 34 | 35 | if get_option('enable_debug') 36 | c_args += '-DG_ENABLE_DEBUG' 37 | else 38 | c_args += '-DG_DISABLE_ASSERT' 39 | c_args += '-DG_DISABLE_CHECKS' 40 | c_args += '-DG_DISABLE_CAST_CHECKS' 41 | endif 42 | 43 | if not get_option('deprecated_warnings') 44 | c_args += '-Wno-deprecated-declarations' 45 | c_args += '-Wno-deprecated' 46 | c_args += '-Wno-declaration-after-statement' 47 | endif 48 | 49 | add_global_arguments(c_args, language: 'c') 50 | 51 | gio = dependency('gio-unix-2.0', version: '>= 2.29.15') 52 | 53 | subdir('libmenu') 54 | if get_option('enable_docs') 55 | subdir('docs/reference') 56 | endif 57 | -------------------------------------------------------------------------------- /libmenu/menu-util.h: -------------------------------------------------------------------------------- 1 | /* Random utility functions for menu code */ 2 | 3 | /* 4 | * Copyright (C) 2003 Red Hat, Inc. 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 2 of the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, write to the 18 | * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 19 | * Boston, MA 02111-1307, USA. 20 | */ 21 | 22 | #ifndef __MENU_UTIL_H__ 23 | #define __MENU_UTIL_H__ 24 | 25 | #include 26 | 27 | #include "menu-layout.h" 28 | 29 | G_BEGIN_DECLS 30 | 31 | #ifdef G_ENABLE_DEBUG 32 | 33 | void menu_verbose (const char *format, ...) G_GNUC_PRINTF (1, 2); 34 | 35 | void menu_debug_print_layout (MenuLayoutNode *node, 36 | gboolean onelevel); 37 | 38 | #else /* !defined(G_ENABLE_DEBUG) */ 39 | 40 | #ifdef G_HAVE_ISO_VARARGS 41 | #define menu_verbose(...) 42 | #elif defined(G_HAVE_GNUC_VARARGS) 43 | #define menu_verbose(format...) 44 | #else 45 | #error "Cannot disable verbose mode due to lack of varargs macros" 46 | #endif 47 | 48 | #define menu_debug_print_layout(n,o) 49 | 50 | #endif /* G_ENABLE_DEBUG */ 51 | 52 | G_END_DECLS 53 | 54 | #endif /* __MENU_UTIL_H__ */ 55 | -------------------------------------------------------------------------------- /libmenu/menu-monitor.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2005 Red Hat, Inc. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, write to the 16 | * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 17 | * Boston, MA 02111-1307, USA. 18 | */ 19 | 20 | #ifndef __MENU_MONITOR_H__ 21 | #define __MENU_MONITOR_H__ 22 | 23 | #include 24 | 25 | G_BEGIN_DECLS 26 | 27 | typedef struct MenuMonitor MenuMonitor; 28 | 29 | typedef enum 30 | { 31 | MENU_MONITOR_EVENT_INVALID = 0, 32 | MENU_MONITOR_EVENT_CREATED = 1, 33 | MENU_MONITOR_EVENT_DELETED = 2, 34 | MENU_MONITOR_EVENT_CHANGED = 3 35 | } MenuMonitorEvent; 36 | 37 | typedef void (*MenuMonitorNotifyFunc) (MenuMonitor *monitor, 38 | MenuMonitorEvent event, 39 | const char *path, 40 | gpointer user_data); 41 | 42 | 43 | MenuMonitor *menu_get_file_monitor (const char *path); 44 | MenuMonitor *menu_get_directory_monitor (const char *path); 45 | 46 | MenuMonitor *menu_monitor_ref (MenuMonitor *monitor); 47 | void menu_monitor_unref (MenuMonitor *monitor); 48 | 49 | void menu_monitor_add_notify (MenuMonitor *monitor, 50 | MenuMonitorNotifyFunc notify_func, 51 | gpointer user_data); 52 | void menu_monitor_remove_notify (MenuMonitor *monitor, 53 | MenuMonitorNotifyFunc notify_func, 54 | gpointer user_data); 55 | 56 | G_END_DECLS 57 | 58 | #endif /* __MENU_MONITOR_H__ */ 59 | -------------------------------------------------------------------------------- /libmenu/meson.build: -------------------------------------------------------------------------------- 1 | public_headers = [ 2 | 'gmenu-tree.h', 3 | 'gmenu-desktopappinfo.h' 4 | ] 5 | 6 | public_sources = [ 7 | 'gmenu-tree.c', 8 | 'gmenu-desktopappinfo.c', 9 | public_headers, 10 | ] 11 | 12 | libmenu_private_headers = [ 13 | 'desktop-entries.h', 14 | 'entry-directories.h', 15 | 'menu-layout.h', 16 | 'menu-monitor.h', 17 | 'menu-util.h', 18 | ] 19 | 20 | libmenu_sources = [ 21 | 'desktop-entries.c', 22 | 'entry-directories.c', 23 | 'menu-layout.c', 24 | 'menu-monitor.c', 25 | 'menu-util.c', 26 | public_sources, 27 | libmenu_private_headers, 28 | ] 29 | 30 | libmenu_deps = [ 31 | gio, 32 | config_h, 33 | ] 34 | 35 | libcinnamon_menus = library( 36 | 'cinnamon-menu-3', 37 | libmenu_sources, 38 | soversion: binary_major_version, 39 | version: binary_version, 40 | include_directories: include_root, 41 | dependencies: libmenu_deps, 42 | install: true, 43 | build_by_default: false, 44 | ) 45 | 46 | cmenu_dep = declare_dependency( 47 | include_directories: include_directories('.'), 48 | link_with: libcinnamon_menus, 49 | dependencies: libmenu_deps, 50 | link_args: ['-Wl,-Bsymbolic', '-Wl,-z,relro', '-Wl,-z,now'], 51 | ) 52 | 53 | install_headers( 54 | public_headers, 55 | subdir: 'cinnamon-menus-3.0' 56 | ) 57 | 58 | pkgconfig = import('pkgconfig') 59 | 60 | # meson 0.46.0 can drop the version keyword and move libraries to a 61 | # positional argument 62 | pkgconfig.generate( 63 | name: 'libcinnamon-menu-3.0', 64 | description: 'Desktop Menu Specification Implementation', 65 | version: version, 66 | libraries: libcinnamon_menus, 67 | subdirs: 'cinnamon-menus-3.0' 68 | ) 69 | 70 | gnome.generate_gir( 71 | libcinnamon_menus, 72 | namespace: 'CMenu', 73 | nsversion: '3.0', 74 | sources: public_sources, 75 | identifier_prefix: 'GMenu', 76 | symbol_prefix: 'gmenu', 77 | includes: 'Gio-2.0', 78 | install: true, 79 | install_dir_gir: join_paths(datadir, 'gir-1.0'), 80 | export_packages: 'libcinnamon-menu-3.0', 81 | ) 82 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: cinnamon-menus 2 | Section: x11 3 | Priority: optional 4 | Maintainer: Clement Lefebvre 5 | Build-Depends: debhelper-compat (= 12), 6 | dh-sequence-gir, 7 | gobject-introspection (>= 1.58.3-2), 8 | gtk-doc-tools (>= 1.4), 9 | libgirepository1.0-dev (>= 1.58.3-2), 10 | libglib2.0-dev (>= 2.30.0), 11 | meson 12 | Standards-Version: 4.5.0 13 | 14 | Package: libcinnamon-menu-3-0 15 | Architecture: any 16 | Section: libs 17 | Priority: optional 18 | Depends: ${misc:Depends}, ${shlibs:Depends} 19 | Description: Cinnamon implementation of the freedesktop menu specification 20 | The package contains an implementation of the draft 21 | "Desktop Menu Specification" from freedesktop.org: 22 | . 23 | http://www.freedesktop.org/Standards/menu-spec 24 | . 25 | This package contains the shared library. 26 | 27 | Package: libcinnamon-menu-3-0-dbg 28 | Section: debug 29 | Priority: optional 30 | Architecture: any 31 | Depends: libcinnamon-menu-3-0 (= ${binary:Version}), ${misc:Depends} 32 | Description: Cinnamon implementation of the freedesktop menu specification 33 | The package contains an implementation of the draft 34 | "Desktop Menu Specification" from freedesktop.org: 35 | . 36 | http://www.freedesktop.org/Standards/menu-spec 37 | . 38 | This package contains debugging symbols. 39 | 40 | Package: libcinnamon-menu-3-dev 41 | Architecture: any 42 | Section: libdevel 43 | Priority: optional 44 | Depends: gir1.2-cmenu-3.0 (= ${binary:Version}), 45 | libcinnamon-menu-3-0 (= ${binary:Version}), 46 | libglib2.0-dev (>= 2.30.0), 47 | ${misc:Depends} 48 | Description: Cinnamon implementation of the freedesktop menu specification 49 | The package contains an implementation of the draft 50 | "Desktop Menu Specification" from freedesktop.org: 51 | . 52 | http://www.freedesktop.org/Standards/menu-spec 53 | . 54 | This package contains the development headers. 55 | 56 | Package: gir1.2-cmenu-3.0 57 | Section: introspection 58 | Priority: optional 59 | Architecture: any 60 | Depends: ${gir:Depends}, ${misc:Depends} 61 | Description: GObject introspection data for the Cinnamon menu library 62 | This package contains introspection data for Cinnamon menu, an 63 | implementation of the desktop menu specification from freedesktop.org. 64 | . 65 | It can be used by languages supporting dynamic bindings with 66 | the GIRepository format. 67 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: cinnamon-menus 3 | Upstream-Contact: Linux Mint Project 4 | Source: https://github.com/linuxmint/cinnamon-menus.git 5 | 6 | Files: * 7 | Copyright: 1996-2011, Free Software Foundation, Inc. 8 | License: GPL-2+ 9 | This program is free software; you can redistribute it and/or modify 10 | it under the terms of the GNU General Public License as published by 11 | the Free Software Foundation; either version 2 of the License, or 12 | (at your option) any later version. 13 | . 14 | This program is distributed in the hope that it will be useful, 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | GNU General Public License for more details. 18 | . 19 | You should have received a copy of the GNU General Public License along 20 | with this program; if not, write to the Free Software Foundation, Inc., 21 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 22 | . 23 | On Debian systems, the complete text of the GNU General Public License 24 | version 2 can be found in `/usr/share/common-licenses/GPL-2'. 25 | 26 | Files: libmenu/desktop-entries.c 27 | libmenu/desktop-entries.h 28 | libmenu/entry-directories.c 29 | libmenu/entry-directories.h 30 | libmenu/gmenu-tree.c 31 | libmenu/gmenu-tree.h 32 | libmenu/menu-layout.c 33 | libmenu/menu-layout.h 34 | libmenu/menu-monitor.c 35 | libmenu/menu-monitor.h 36 | libmenu/menu-util.c 37 | libmenu/menu-util.h 38 | Copyright: 2006, Mark McLoughlin 39 | 2002-2011, Red Hat, Inc 40 | 2007, Sebastian Dröge 41 | 2008, Vincent Untz 42 | License: LGPL-2+ 43 | 44 | Files: debian/* 45 | Copyright: 2014, Clement Lefebvre 46 | 2014, Maximiliano Curia 47 | License: LGPL-2+ 48 | 49 | License: LGPL-2+ 50 | This library is free software; you can redistribute it and/or modify it under 51 | the terms of the GNU Lesser General Public License as published by the Free 52 | Software Foundation; either version 2 of the License, or (at your option) any 53 | later version. 54 | . 55 | This library is distributed in the hope that it will be useful, but WITHOUT 56 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 57 | FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 58 | details. 59 | . 60 | You should have received a copy of the GNU Lesser General Public License 61 | along with this library; if not, write to the Free Software Foundation, Inc., 62 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 63 | . 64 | On Debian systems, the complete text of the GNU Lesser General Public License 65 | version 2 can be found in `/usr/share/common-licenses/LGPL-2'. 66 | -------------------------------------------------------------------------------- /libmenu/entry-directories.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2002 - 2004 Red Hat, Inc. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, write to the 16 | * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 17 | * Boston, MA 02111-1307, USA. 18 | */ 19 | 20 | #ifndef __ENTRY_DIRECTORIES_H__ 21 | #define __ENTRY_DIRECTORIES_H__ 22 | 23 | #include 24 | #include "desktop-entries.h" 25 | 26 | G_BEGIN_DECLS 27 | 28 | typedef struct EntryDirectory EntryDirectory; 29 | 30 | typedef void (*EntryDirectoryChangedFunc) (EntryDirectory *ed, 31 | gpointer user_data); 32 | 33 | EntryDirectory *entry_directory_new (DesktopEntryType entry_type, 34 | const char *path); 35 | 36 | EntryDirectory *entry_directory_ref (EntryDirectory *ed); 37 | void entry_directory_unref (EntryDirectory *ed); 38 | 39 | void entry_directory_get_flat_contents (EntryDirectory *ed, 40 | DesktopEntrySet *desktop_entries, 41 | DesktopEntrySet *directory_entries, 42 | GSList **subdirs); 43 | 44 | 45 | typedef struct EntryDirectoryList EntryDirectoryList; 46 | 47 | EntryDirectoryList *entry_directory_list_new (void); 48 | EntryDirectoryList *entry_directory_list_ref (EntryDirectoryList *list); 49 | void entry_directory_list_unref (EntryDirectoryList *list); 50 | 51 | int entry_directory_list_get_length (EntryDirectoryList *list); 52 | gboolean _entry_directory_list_compare (const EntryDirectoryList *a, 53 | const EntryDirectoryList *b); 54 | 55 | void entry_directory_list_prepend (EntryDirectoryList *list, 56 | EntryDirectory *ed); 57 | void entry_directory_list_append_list (EntryDirectoryList *list, 58 | EntryDirectoryList *to_append); 59 | 60 | void entry_directory_list_add_monitors (EntryDirectoryList *list, 61 | EntryDirectoryChangedFunc callback, 62 | gpointer user_data); 63 | void entry_directory_list_remove_monitors (EntryDirectoryList *list, 64 | EntryDirectoryChangedFunc callback, 65 | gpointer user_data); 66 | 67 | DesktopEntry* entry_directory_list_get_directory (EntryDirectoryList *list, 68 | const char *relative_path); 69 | 70 | DesktopEntrySet *_entry_directory_list_get_all_desktops (EntryDirectoryList *list); 71 | void _entry_directory_list_empty_desktop_cache (void); 72 | 73 | G_END_DECLS 74 | 75 | #endif /* __ENTRY_DIRECTORIES_H__ */ 76 | -------------------------------------------------------------------------------- /libmenu/desktop-entries.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2002 - 2004 Red Hat, Inc. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, write to the 16 | * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 17 | * Boston, MA 02111-1307, USA. 18 | */ 19 | 20 | #ifndef __DESKTOP_ENTRIES_H__ 21 | #define __DESKTOP_ENTRIES_H__ 22 | 23 | #include "gmenu-desktopappinfo.h" 24 | 25 | G_BEGIN_DECLS 26 | 27 | typedef enum 28 | { 29 | DESKTOP_ENTRY_INVALID = 0, 30 | DESKTOP_ENTRY_DESKTOP, 31 | DESKTOP_ENTRY_DIRECTORY 32 | } DesktopEntryType; 33 | 34 | typedef enum 35 | { 36 | DESKTOP_ENTRY_LOAD_FAIL_OTHER = 0, 37 | DESKTOP_ENTRY_LOAD_FAIL_APPINFO, 38 | DESKTOP_ENTRY_LOAD_SUCCESS 39 | } DesktopEntryResultCode; 40 | 41 | typedef struct DesktopEntry DesktopEntry; 42 | 43 | DesktopEntry *desktop_entry_new (const char *path, 44 | DesktopEntryResultCode *res_code); 45 | 46 | DesktopEntry *desktop_entry_ref (DesktopEntry *entry); 47 | DesktopEntry *desktop_entry_copy (DesktopEntry *entry); 48 | DesktopEntry *desktop_entry_reload (DesktopEntry *entry); 49 | void desktop_entry_unref (DesktopEntry *entry); 50 | 51 | DesktopEntryType desktop_entry_get_type (DesktopEntry *entry); 52 | const char *desktop_entry_get_path (DesktopEntry *entry); 53 | const char *desktop_entry_get_basename (DesktopEntry *entry); 54 | const char *desktop_entry_get_name (DesktopEntry *entry); 55 | const char *desktop_entry_get_generic_name (DesktopEntry *entry); 56 | const char *desktop_entry_get_comment (DesktopEntry *entry); 57 | GIcon *desktop_entry_get_icon (DesktopEntry *entry); 58 | gboolean desktop_entry_get_hidden (DesktopEntry *entry); 59 | gboolean desktop_entry_get_no_display (DesktopEntry *entry); 60 | gboolean desktop_entry_get_show_in (DesktopEntry *entry); 61 | 62 | /* Only valid for DESKTOP_ENTRY_DESKTOP */ 63 | GMenuDesktopAppInfo *desktop_entry_get_app_info (DesktopEntry *entry); 64 | gboolean desktop_entry_has_categories (DesktopEntry *entry); 65 | gboolean desktop_entry_has_category (DesktopEntry *entry, 66 | const char *category); 67 | 68 | 69 | typedef struct DesktopEntrySet DesktopEntrySet; 70 | 71 | DesktopEntrySet *desktop_entry_set_new (void); 72 | DesktopEntrySet *desktop_entry_set_ref (DesktopEntrySet *set); 73 | void desktop_entry_set_unref (DesktopEntrySet *set); 74 | 75 | void desktop_entry_set_add_entry (DesktopEntrySet *set, 76 | DesktopEntry *entry, 77 | const char *file_id); 78 | DesktopEntry* desktop_entry_set_lookup (DesktopEntrySet *set, 79 | const char *file_id); 80 | int desktop_entry_set_get_count (DesktopEntrySet *set); 81 | 82 | void desktop_entry_set_union (DesktopEntrySet *set, 83 | DesktopEntrySet *with); 84 | void desktop_entry_set_intersection (DesktopEntrySet *set, 85 | DesktopEntrySet *with); 86 | void desktop_entry_set_subtract (DesktopEntrySet *set, 87 | DesktopEntrySet *other); 88 | void desktop_entry_set_swap_contents (DesktopEntrySet *a, 89 | DesktopEntrySet *b); 90 | const char * desktop_entry_get_id (DesktopEntry *entry); 91 | 92 | typedef void (*DesktopEntrySetForeachFunc) (const char *file_id, 93 | DesktopEntry *entry, 94 | gpointer user_data); 95 | 96 | void desktop_entry_set_foreach (DesktopEntrySet *set, 97 | DesktopEntrySetForeachFunc func, 98 | gpointer user_data); 99 | 100 | G_END_DECLS 101 | 102 | #endif /* __DESKTOP_ENTRIES_H__ */ 103 | -------------------------------------------------------------------------------- /libmenu/gmenu-desktopappinfo.h: -------------------------------------------------------------------------------- 1 | /* 2 | * gmenu-desktopappinfo.h 3 | * Copyright (C) 2020 Lars Mueller 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library; if not, write to the 17 | * Free Software Foundation, 51 Franklin Street, Fifth Floor, 18 | * Boston, MA 02110, USA. 19 | */ 20 | 21 | 22 | #ifndef _GMENU_DESKTOPAPPINFO_H_ 23 | #define _GMENU_DESKTOPAPPINFO_H_ 24 | 25 | #ifndef GMENU_I_KNOW_THIS_IS_UNSTABLE 26 | #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" 27 | #endif 28 | 29 | #include 30 | 31 | G_BEGIN_DECLS 32 | 33 | #define GMENU_TYPE_DESKTOPAPPINFO (gmenu_desktopappinfo_get_type ()) 34 | #define GMENU_DESKTOPAPPINFO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GMENU_TYPE_DESKTOPAPPINFO, GMenuDesktopAppInfo)) 35 | #define GMENU_DESKTOPAPPINFO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GMENU_TYPE_DESKTOPAPPINFO, GMenuDesktopAppInfoClass)) 36 | #define GMENU_IS_DESKTOPAPPINFO(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GMENU_TYPE_DESKTOPAPPINFO)) 37 | #define GMENU_IS_DESKTOPAPPINFO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GMENU_TYPE_DESKTOPAPPINFO)) 38 | #define GMENU_DESKTOPAPPINFO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GMENU_TYPE_DESKTOPAPPINFO, GMenuDesktopAppInfoClass)) 39 | 40 | typedef struct _GMenuDesktopAppInfoClass GMenuDesktopAppInfoClass; 41 | typedef struct _GMenuDesktopAppInfo GMenuDesktopAppInfo; 42 | 43 | struct _GMenuDesktopAppInfoClass 44 | { 45 | GObjectClass parent_class; 46 | }; 47 | 48 | #define GMENU_DESKTOPAPPINFO_FLATPAK_SUFFIX ":flatpak" 49 | 50 | GType gmenu_desktopappinfo_get_type (void) G_GNUC_CONST; 51 | 52 | GMenuDesktopAppInfo* gmenu_desktopappinfo_new_from_filename (gchar *filename); 53 | GMenuDesktopAppInfo *gmenu_desktopappinfo_new_from_keyfile (GKeyFile *key_file); 54 | 55 | const char * gmenu_desktopappinfo_get_filename (GMenuDesktopAppInfo *appinfo); 56 | const char * gmenu_desktopappinfo_get_generic_name (GMenuDesktopAppInfo *appinfo); 57 | const char * gmenu_desktopappinfo_get_categories (GMenuDesktopAppInfo *appinfo); 58 | const char * const *gmenu_desktopappinfo_get_keywords (GMenuDesktopAppInfo *appinfo); 59 | gboolean gmenu_desktopappinfo_get_nodisplay (GMenuDesktopAppInfo *appinfo); 60 | gboolean gmenu_desktopappinfo_get_show_in (GMenuDesktopAppInfo *appinfo, const gchar *desktop_env); 61 | const char * gmenu_desktopappinfo_get_startup_wm_class (GMenuDesktopAppInfo *appinfo); 62 | GMenuDesktopAppInfo *gmenu_desktopappinfo_new (const char *desktop_id); 63 | gboolean gmenu_desktopappinfo_get_is_hidden (GMenuDesktopAppInfo *appinfo); 64 | gboolean gmenu_desktopappinfo_has_key (GMenuDesktopAppInfo *appinfo, const char *key); 65 | char * gmenu_desktopappinfo_get_string (GMenuDesktopAppInfo *appinfo, const char *key); 66 | char * gmenu_desktopappinfo_get_locale_string (GMenuDesktopAppInfo *appinfo, const char *key); 67 | gboolean gmenu_desktopappinfo_get_boolean (GMenuDesktopAppInfo *appinfo, const char *key); 68 | const gchar * const * gmenu_desktopappinfo_list_actions (GMenuDesktopAppInfo *appinfo); 69 | void gmenu_desktopappinfo_launch_action (GMenuDesktopAppInfo *appinfo, const gchar *action_name, GAppLaunchContext *launch_context); 70 | gchar * gmenu_desktopappinfo_get_action_name (GMenuDesktopAppInfo *appinfo, const gchar *action_name); 71 | gboolean gmenu_desktopappinfo_launch_uris_as_manager (GMenuDesktopAppInfo *appinfo, 72 | GList *uris, 73 | GAppLaunchContext *launch_context, 74 | GSpawnFlags spawn_flags, 75 | GSpawnChildSetupFunc user_setup, 76 | gpointer user_setup_data, 77 | GDesktopAppLaunchCallback pid_callback, 78 | gpointer pid_callback_data, 79 | GError **error); 80 | 81 | gboolean gmenu_desktopappinfo_get_is_flatpak (GMenuDesktopAppInfo *appinfo); 82 | const char * gmenu_desktopappinfo_get_flatpak_app_id (GMenuDesktopAppInfo *appinfo); 83 | 84 | G_END_DECLS 85 | 86 | #endif /* _GMENU_DESKTOPAPPINFO_H_ */ 87 | 88 | -------------------------------------------------------------------------------- /libmenu/menu-layout.h: -------------------------------------------------------------------------------- 1 | /* Menu layout in-memory data structure (a custom "DOM tree") */ 2 | 3 | /* 4 | * Copyright (C) 2002 - 2004 Red Hat, Inc. 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 2 of the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, write to the 18 | * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 19 | * Boston, MA 02111-1307, USA. 20 | */ 21 | 22 | #ifndef __MENU_LAYOUT_H__ 23 | #define __MENU_LAYOUT_H__ 24 | 25 | #include 26 | 27 | #include "entry-directories.h" 28 | 29 | G_BEGIN_DECLS 30 | 31 | typedef struct MenuLayoutNode MenuLayoutNode; 32 | 33 | typedef enum 34 | { 35 | MENU_LAYOUT_NODE_ROOT, 36 | MENU_LAYOUT_NODE_PASSTHROUGH, 37 | MENU_LAYOUT_NODE_MENU, 38 | MENU_LAYOUT_NODE_APP_DIR, 39 | MENU_LAYOUT_NODE_DEFAULT_APP_DIRS, 40 | MENU_LAYOUT_NODE_DIRECTORY_DIR, 41 | MENU_LAYOUT_NODE_DEFAULT_DIRECTORY_DIRS, 42 | MENU_LAYOUT_NODE_DEFAULT_MERGE_DIRS, 43 | MENU_LAYOUT_NODE_NAME, 44 | MENU_LAYOUT_NODE_DIRECTORY, 45 | MENU_LAYOUT_NODE_ONLY_UNALLOCATED, 46 | MENU_LAYOUT_NODE_NOT_ONLY_UNALLOCATED, 47 | MENU_LAYOUT_NODE_INCLUDE, 48 | MENU_LAYOUT_NODE_EXCLUDE, 49 | MENU_LAYOUT_NODE_FILENAME, 50 | MENU_LAYOUT_NODE_CATEGORY, 51 | MENU_LAYOUT_NODE_ALL, 52 | MENU_LAYOUT_NODE_AND, 53 | MENU_LAYOUT_NODE_OR, 54 | MENU_LAYOUT_NODE_NOT, 55 | MENU_LAYOUT_NODE_MERGE_FILE, 56 | MENU_LAYOUT_NODE_MERGE_DIR, 57 | MENU_LAYOUT_NODE_LEGACY_DIR, 58 | MENU_LAYOUT_NODE_KDE_LEGACY_DIRS, 59 | MENU_LAYOUT_NODE_MOVE, 60 | MENU_LAYOUT_NODE_OLD, 61 | MENU_LAYOUT_NODE_NEW, 62 | MENU_LAYOUT_NODE_DELETED, 63 | MENU_LAYOUT_NODE_NOT_DELETED, 64 | MENU_LAYOUT_NODE_LAYOUT, 65 | MENU_LAYOUT_NODE_DEFAULT_LAYOUT, 66 | MENU_LAYOUT_NODE_MENUNAME, 67 | MENU_LAYOUT_NODE_SEPARATOR, 68 | MENU_LAYOUT_NODE_MERGE 69 | } MenuLayoutNodeType; 70 | 71 | typedef enum 72 | { 73 | MENU_MERGE_FILE_TYPE_PATH = 0, 74 | MENU_MERGE_FILE_TYPE_PARENT 75 | } MenuMergeFileType; 76 | 77 | typedef enum 78 | { 79 | MENU_LAYOUT_MERGE_NONE, 80 | MENU_LAYOUT_MERGE_MENUS, 81 | MENU_LAYOUT_MERGE_FILES, 82 | MENU_LAYOUT_MERGE_ALL 83 | } MenuLayoutMergeType; 84 | 85 | typedef enum 86 | { 87 | MENU_LAYOUT_VALUES_NONE = 0, 88 | MENU_LAYOUT_VALUES_SHOW_EMPTY = 1 << 0, 89 | MENU_LAYOUT_VALUES_INLINE_MENUS = 1 << 1, 90 | MENU_LAYOUT_VALUES_INLINE_LIMIT = 1 << 2, 91 | MENU_LAYOUT_VALUES_INLINE_HEADER = 1 << 3, 92 | MENU_LAYOUT_VALUES_INLINE_ALIAS = 1 << 4 93 | } MenuLayoutValuesMask; 94 | 95 | typedef struct 96 | { 97 | MenuLayoutValuesMask mask; 98 | 99 | guint show_empty : 1; 100 | guint inline_menus : 1; 101 | guint inline_header : 1; 102 | guint inline_alias : 1; 103 | 104 | guint inline_limit; 105 | } MenuLayoutValues; 106 | 107 | 108 | MenuLayoutNode *menu_layout_load (const char *filename, 109 | const char *non_prefixed_basename, 110 | GError **error); 111 | 112 | MenuLayoutNode *menu_layout_node_new (MenuLayoutNodeType type); 113 | MenuLayoutNode *menu_layout_node_ref (MenuLayoutNode *node); 114 | void menu_layout_node_unref (MenuLayoutNode *node); 115 | 116 | MenuLayoutNodeType menu_layout_node_get_type (MenuLayoutNode *node); 117 | 118 | MenuLayoutNode *menu_layout_node_get_root (MenuLayoutNode *node); 119 | MenuLayoutNode *menu_layout_node_get_parent (MenuLayoutNode *node); 120 | MenuLayoutNode *menu_layout_node_get_children (MenuLayoutNode *node); 121 | MenuLayoutNode *menu_layout_node_get_next (MenuLayoutNode *node); 122 | 123 | void menu_layout_node_insert_before (MenuLayoutNode *node, 124 | MenuLayoutNode *new_sibling); 125 | void menu_layout_node_insert_after (MenuLayoutNode *node, 126 | MenuLayoutNode *new_sibling); 127 | void menu_layout_node_prepend_child (MenuLayoutNode *parent, 128 | MenuLayoutNode *new_child); 129 | void menu_layout_node_append_child (MenuLayoutNode *parent, 130 | MenuLayoutNode *new_child); 131 | 132 | void menu_layout_node_unlink (MenuLayoutNode *node); 133 | void menu_layout_node_steal (MenuLayoutNode *node); 134 | 135 | const char *menu_layout_node_get_content (MenuLayoutNode *node); 136 | void menu_layout_node_set_content (MenuLayoutNode *node, 137 | const char *content); 138 | 139 | char *menu_layout_node_get_content_as_path (MenuLayoutNode *node); 140 | 141 | const char *menu_layout_node_root_get_name (MenuLayoutNode *node); 142 | const char *menu_layout_node_root_get_basedir (MenuLayoutNode *node); 143 | 144 | const char *menu_layout_node_menu_get_name (MenuLayoutNode *node); 145 | EntryDirectoryList *menu_layout_node_menu_get_app_dirs (MenuLayoutNode *node); 146 | EntryDirectoryList *menu_layout_node_menu_get_directory_dirs (MenuLayoutNode *node); 147 | 148 | const char *menu_layout_node_move_get_old (MenuLayoutNode *node); 149 | const char *menu_layout_node_move_get_new (MenuLayoutNode *node); 150 | 151 | const char *menu_layout_node_legacy_dir_get_prefix (MenuLayoutNode *node); 152 | void menu_layout_node_legacy_dir_set_prefix (MenuLayoutNode *node, 153 | const char *prefix); 154 | 155 | MenuMergeFileType menu_layout_node_merge_file_get_type (MenuLayoutNode *node); 156 | void menu_layout_node_merge_file_set_type (MenuLayoutNode *node, 157 | MenuMergeFileType type); 158 | 159 | MenuLayoutMergeType menu_layout_node_merge_get_type (MenuLayoutNode *node); 160 | 161 | void menu_layout_node_default_layout_get_values (MenuLayoutNode *node, 162 | MenuLayoutValues *values); 163 | void menu_layout_node_menuname_get_values (MenuLayoutNode *node, 164 | MenuLayoutValues *values); 165 | 166 | typedef void (* MenuLayoutNodeEntriesChangedFunc) (MenuLayoutNode *node, 167 | gpointer user_data); 168 | 169 | void menu_layout_node_root_add_entries_monitor (MenuLayoutNode *node, 170 | MenuLayoutNodeEntriesChangedFunc callback, 171 | gpointer user_data); 172 | void menu_layout_node_root_remove_entries_monitor (MenuLayoutNode *node, 173 | MenuLayoutNodeEntriesChangedFunc callback, 174 | gpointer user_data); 175 | 176 | G_END_DECLS 177 | 178 | #endif /* __MENU_LAYOUT_H__ */ 179 | -------------------------------------------------------------------------------- /debian/libcinnamon-menu-3-0.symbols: -------------------------------------------------------------------------------- 1 | libcinnamon-menu-3.so.0 libcinnamon-menu-3-0 #MINVER# 2 | _entry_directory_list_compare@Base 4.8.3 3 | _entry_directory_list_empty_desktop_cache@Base 4.8.3 4 | _entry_directory_list_get_all_desktops@Base 4.8.3 5 | desktop_entry_copy@Base 4.8.3 6 | desktop_entry_get_app_info@Base 4.8.3 7 | desktop_entry_get_basename@Base 4.8.3 8 | desktop_entry_get_comment@Base 4.8.3 9 | desktop_entry_get_generic_name@Base 4.8.3 10 | desktop_entry_get_hidden@Base 4.8.3 11 | desktop_entry_get_icon@Base 4.8.3 12 | desktop_entry_get_id@Base 4.8.3 13 | desktop_entry_get_name@Base 4.8.3 14 | desktop_entry_get_no_display@Base 4.8.3 15 | desktop_entry_get_path@Base 4.8.3 16 | desktop_entry_get_show_in@Base 4.8.3 17 | desktop_entry_get_type@Base 4.8.3 18 | desktop_entry_has_categories@Base 4.8.3 19 | desktop_entry_has_category@Base 4.8.3 20 | desktop_entry_new@Base 4.8.3 21 | desktop_entry_ref@Base 4.8.3 22 | desktop_entry_reload@Base 4.8.3 23 | desktop_entry_set_add_entry@Base 4.8.3 24 | desktop_entry_set_foreach@Base 4.8.3 25 | desktop_entry_set_get_count@Base 4.8.3 26 | desktop_entry_set_intersection@Base 4.8.3 27 | desktop_entry_set_lookup@Base 4.8.3 28 | desktop_entry_set_new@Base 4.8.3 29 | desktop_entry_set_ref@Base 4.8.3 30 | desktop_entry_set_subtract@Base 4.8.3 31 | desktop_entry_set_swap_contents@Base 4.8.3 32 | desktop_entry_set_union@Base 4.8.3 33 | desktop_entry_set_unref@Base 4.8.3 34 | desktop_entry_unref@Base 4.8.3 35 | entry_directory_get_flat_contents@Base 4.8.3 36 | entry_directory_list_add_monitors@Base 4.8.3 37 | entry_directory_list_append_list@Base 4.8.3 38 | entry_directory_list_get_directory@Base 4.8.3 39 | entry_directory_list_get_length@Base 4.8.3 40 | entry_directory_list_new@Base 4.8.3 41 | entry_directory_list_prepend@Base 4.8.3 42 | entry_directory_list_ref@Base 4.8.3 43 | entry_directory_list_remove_monitors@Base 4.8.3 44 | entry_directory_list_unref@Base 4.8.3 45 | entry_directory_new@Base 4.8.3 46 | entry_directory_ref@Base 4.8.3 47 | entry_directory_unref@Base 4.8.3 48 | gmenu_desktopappinfo_get_action_name@Base 4.8.3 49 | gmenu_desktopappinfo_get_boolean@Base 4.8.3 50 | gmenu_desktopappinfo_get_categories@Base 4.8.3 51 | gmenu_desktopappinfo_get_filename@Base 4.8.3 52 | gmenu_desktopappinfo_get_flatpak_app_id@Base 4.8.3 53 | gmenu_desktopappinfo_get_generic_name@Base 4.8.3 54 | gmenu_desktopappinfo_get_is_flatpak@Base 4.8.3 55 | gmenu_desktopappinfo_get_is_hidden@Base 4.8.3 56 | gmenu_desktopappinfo_get_keywords@Base 4.8.3 57 | gmenu_desktopappinfo_get_locale_string@Base 4.8.3 58 | gmenu_desktopappinfo_get_nodisplay@Base 4.8.3 59 | gmenu_desktopappinfo_get_show_in@Base 4.8.3 60 | gmenu_desktopappinfo_get_startup_wm_class@Base 4.8.3 61 | gmenu_desktopappinfo_get_string@Base 4.8.3 62 | gmenu_desktopappinfo_get_type@Base 4.8.3 63 | gmenu_desktopappinfo_has_key@Base 4.8.3 64 | gmenu_desktopappinfo_launch_action@Base 4.8.3 65 | gmenu_desktopappinfo_launch_uris_as_manager@Base 4.8.3 66 | gmenu_desktopappinfo_list_actions@Base 4.8.3 67 | gmenu_desktopappinfo_new@Base 4.8.3 68 | gmenu_desktopappinfo_new_from_filename@Base 4.8.3 69 | gmenu_desktopappinfo_new_from_keyfile@Base 4.8.3 70 | gmenu_tree_alias_get_aliased_directory@Base 2.2.0 71 | gmenu_tree_alias_get_aliased_entry@Base 2.2.0 72 | gmenu_tree_alias_get_aliased_item_type@Base 2.2.0 73 | gmenu_tree_alias_get_directory@Base 2.2.0 74 | gmenu_tree_alias_get_parent@Base 2.2.0 75 | gmenu_tree_alias_get_tree@Base 2.2.0 76 | gmenu_tree_alias_get_type@Base 2.2.0 77 | gmenu_tree_directory_get_comment@Base 2.2.0 78 | gmenu_tree_directory_get_desktop_file_path@Base 2.2.0 79 | gmenu_tree_directory_get_generic_name@Base 2.2.0 80 | gmenu_tree_directory_get_icon@Base 2.2.0 81 | gmenu_tree_directory_get_is_nodisplay@Base 2.2.0 82 | gmenu_tree_directory_get_menu_id@Base 2.2.0 83 | gmenu_tree_directory_get_name@Base 2.2.0 84 | gmenu_tree_directory_get_parent@Base 2.2.0 85 | gmenu_tree_directory_get_tree@Base 2.2.0 86 | gmenu_tree_directory_get_type@Base 2.2.0 87 | gmenu_tree_directory_iter@Base 2.2.0 88 | gmenu_tree_directory_make_path@Base 2.2.0 89 | gmenu_tree_entry_get_app_info@Base 2.2.0 90 | gmenu_tree_entry_get_desktop_file_id@Base 2.2.0 91 | gmenu_tree_entry_get_desktop_file_path@Base 2.2.0 92 | gmenu_tree_entry_get_is_excluded@Base 2.2.0 93 | gmenu_tree_entry_get_is_flatpak@Base 4.8.3 94 | gmenu_tree_entry_get_is_nodisplay_recurse@Base 2.2.0 95 | gmenu_tree_entry_get_is_unallocated@Base 2.2.0 96 | gmenu_tree_entry_get_parent@Base 2.2.0 97 | gmenu_tree_entry_get_tree@Base 2.2.0 98 | gmenu_tree_entry_get_type@Base 2.2.0 99 | gmenu_tree_flags_get_type@Base 2.2.0 100 | gmenu_tree_get_canonical_menu_path@Base 2.2.0 101 | gmenu_tree_get_directory_from_path@Base 2.2.0 102 | gmenu_tree_get_entry_by_id@Base 2.2.0 103 | gmenu_tree_get_root_directory@Base 2.2.0 104 | gmenu_tree_get_type@Base 2.2.0 105 | gmenu_tree_header_get_directory@Base 2.2.0 106 | gmenu_tree_header_get_parent@Base 2.2.0 107 | gmenu_tree_header_get_tree@Base 2.2.0 108 | gmenu_tree_header_get_type@Base 2.2.0 109 | gmenu_tree_item_ref@Base 2.2.0 110 | gmenu_tree_item_unref@Base 2.2.0 111 | gmenu_tree_iter_get_alias@Base 2.2.0 112 | gmenu_tree_iter_get_directory@Base 2.2.0 113 | gmenu_tree_iter_get_entry@Base 2.2.0 114 | gmenu_tree_iter_get_header@Base 2.2.0 115 | gmenu_tree_iter_get_separator@Base 2.2.0 116 | gmenu_tree_iter_get_type@Base 2.2.0 117 | gmenu_tree_iter_next@Base 2.2.0 118 | gmenu_tree_iter_ref@Base 2.2.0 119 | gmenu_tree_iter_unref@Base 2.2.0 120 | gmenu_tree_load_sync@Base 2.2.0 121 | gmenu_tree_new@Base 2.2.0 122 | gmenu_tree_new_for_path@Base 2.2.0 123 | gmenu_tree_separator_get_parent@Base 2.2.0 124 | gmenu_tree_separator_get_tree@Base 2.2.0 125 | gmenu_tree_separator_get_type@Base 2.2.0 126 | menu_debug_print_layout@Base 4.8.3 127 | menu_get_directory_monitor@Base 4.8.3 128 | menu_get_file_monitor@Base 4.8.3 129 | menu_layout_load@Base 4.8.3 130 | menu_layout_node_append_child@Base 4.8.3 131 | menu_layout_node_default_layout_get_values@Base 4.8.3 132 | menu_layout_node_get_children@Base 4.8.3 133 | menu_layout_node_get_content@Base 4.8.3 134 | menu_layout_node_get_content_as_path@Base 4.8.3 135 | menu_layout_node_get_next@Base 4.8.3 136 | menu_layout_node_get_parent@Base 4.8.3 137 | menu_layout_node_get_root@Base 4.8.3 138 | menu_layout_node_get_type@Base 4.8.3 139 | menu_layout_node_insert_after@Base 4.8.3 140 | menu_layout_node_insert_before@Base 4.8.3 141 | menu_layout_node_legacy_dir_get_prefix@Base 4.8.3 142 | menu_layout_node_legacy_dir_set_prefix@Base 4.8.3 143 | menu_layout_node_menu_get_app_dirs@Base 4.8.3 144 | menu_layout_node_menu_get_directory_dirs@Base 4.8.3 145 | menu_layout_node_menu_get_name@Base 4.8.3 146 | menu_layout_node_menuname_get_values@Base 4.8.3 147 | menu_layout_node_merge_file_get_type@Base 4.8.3 148 | menu_layout_node_merge_file_set_type@Base 4.8.3 149 | menu_layout_node_merge_get_type@Base 4.8.3 150 | menu_layout_node_move_get_new@Base 4.8.3 151 | menu_layout_node_move_get_old@Base 4.8.3 152 | menu_layout_node_new@Base 4.8.3 153 | menu_layout_node_prepend_child@Base 4.8.3 154 | menu_layout_node_ref@Base 4.8.3 155 | menu_layout_node_root_add_entries_monitor@Base 4.8.3 156 | menu_layout_node_root_get_basedir@Base 4.8.3 157 | menu_layout_node_root_get_name@Base 4.8.3 158 | menu_layout_node_root_remove_entries_monitor@Base 4.8.3 159 | menu_layout_node_set_content@Base 4.8.3 160 | menu_layout_node_steal@Base 4.8.3 161 | menu_layout_node_unlink@Base 4.8.3 162 | menu_layout_node_unref@Base 4.8.3 163 | menu_monitor_add_notify@Base 4.8.3 164 | menu_monitor_ref@Base 4.8.3 165 | menu_monitor_remove_notify@Base 4.8.3 166 | menu_monitor_unref@Base 4.8.3 167 | menu_verbose@Base 4.8.3 168 | -------------------------------------------------------------------------------- /libmenu/gmenu-tree.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2004, 2011 Red Hat, Inc. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, write to the 16 | * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 17 | * Boston, MA 02111-1307, USA. 18 | */ 19 | 20 | #ifndef __GMENU_TREE_H__ 21 | #define __GMENU_TREE_H__ 22 | 23 | #ifndef GMENU_I_KNOW_THIS_IS_UNSTABLE 24 | #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" 25 | #endif 26 | 27 | #include "gmenu-desktopappinfo.h" 28 | 29 | G_BEGIN_DECLS 30 | 31 | #define GMENU_TYPE_TREE (gmenu_tree_get_type ()) 32 | #define GMENU_TREE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GMENU_TYPE_TREE, GMenuTree)) 33 | #define GMENU_TREE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GMENU_TYPE_TREE, GMenuTreeClass)) 34 | #define GMENU_IS_TREE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GMENU_TYPE_TREE)) 35 | #define GMENU_IS_TREE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GMENU_TYPE_TREE)) 36 | #define GMENU_TREE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_DESKTOP_APP_INFO, GMenuTreeClass)) 37 | 38 | typedef struct _GMenuTree GMenuTree; 39 | typedef struct _GMenuTreeClass GMenuTreeClass; 40 | 41 | struct _GMenuTreeClass 42 | { 43 | GObjectClass parent_class; 44 | }; 45 | 46 | GType gmenu_tree_get_type (void) G_GNUC_CONST; 47 | 48 | typedef struct GMenuTreeIter GMenuTreeIter; 49 | typedef struct GMenuTreeDirectory GMenuTreeDirectory; 50 | typedef struct GMenuTreeEntry GMenuTreeEntry; 51 | typedef struct GMenuTreeSeparator GMenuTreeSeparator; 52 | typedef struct GMenuTreeHeader GMenuTreeHeader; 53 | typedef struct GMenuTreeAlias GMenuTreeAlias; 54 | 55 | typedef enum 56 | { 57 | GMENU_TREE_ITEM_INVALID = 0, 58 | GMENU_TREE_ITEM_DIRECTORY, 59 | GMENU_TREE_ITEM_ENTRY, 60 | GMENU_TREE_ITEM_SEPARATOR, 61 | GMENU_TREE_ITEM_HEADER, 62 | GMENU_TREE_ITEM_ALIAS 63 | } GMenuTreeItemType; 64 | 65 | GType gmenu_tree_iter_get_type (void); 66 | 67 | /* Explicitly skip item, it's a "hidden" base class */ 68 | GType gmenu_tree_directory_get_type (void); 69 | GType gmenu_tree_entry_get_type (void); 70 | GType gmenu_tree_separator_get_type (void); 71 | GType gmenu_tree_header_get_type (void); 72 | GType gmenu_tree_alias_get_type (void); 73 | 74 | typedef enum 75 | { 76 | GMENU_TREE_FLAGS_NONE = 0, 77 | GMENU_TREE_FLAGS_INCLUDE_EXCLUDED = 1 << 0, 78 | GMENU_TREE_FLAGS_INCLUDE_NODISPLAY = 1 << 1, 79 | GMENU_TREE_FLAGS_INCLUDE_UNALLOCATED = 1 << 2, 80 | /* leave some space for more include flags */ 81 | GMENU_TREE_FLAGS_SHOW_EMPTY = 1 << 8, 82 | GMENU_TREE_FLAGS_SHOW_ALL_SEPARATORS = 1 << 9, 83 | /* leave some space for more show flags */ 84 | GMENU_TREE_FLAGS_SORT_DISPLAY_NAME = 1 << 16 85 | } GMenuTreeFlags; 86 | GType gmenu_tree_flags_get_type (void); 87 | #define GMENU_TYPE_TREE_FLAGS (gmenu_tree_flags_get_type ()) 88 | 89 | GMenuTree *gmenu_tree_new (const char *menu_basename, 90 | GMenuTreeFlags flags); 91 | 92 | GMenuTree *gmenu_tree_new_for_path (const char *menu_path, 93 | GMenuTreeFlags flags); 94 | 95 | gboolean gmenu_tree_load_sync (GMenuTree *tree, 96 | GError **error); 97 | 98 | const char *gmenu_tree_get_canonical_menu_path (GMenuTree *tree); 99 | GMenuTreeDirectory *gmenu_tree_get_root_directory (GMenuTree *tree); 100 | GMenuTreeDirectory *gmenu_tree_get_directory_from_path (GMenuTree *tree, 101 | const char *path); 102 | GMenuTreeEntry *gmenu_tree_get_entry_by_id (GMenuTree *tree, 103 | const char *id); 104 | 105 | gpointer gmenu_tree_item_ref (gpointer item); 106 | void gmenu_tree_item_unref (gpointer item); 107 | 108 | GMenuTreeDirectory *gmenu_tree_directory_get_parent (GMenuTreeDirectory *directory); 109 | const char *gmenu_tree_directory_get_name (GMenuTreeDirectory *directory); 110 | const char *gmenu_tree_directory_get_generic_name (GMenuTreeDirectory *directory); 111 | const char *gmenu_tree_directory_get_comment (GMenuTreeDirectory *directory); 112 | GIcon *gmenu_tree_directory_get_icon (GMenuTreeDirectory *directory); 113 | const char *gmenu_tree_directory_get_desktop_file_path (GMenuTreeDirectory *directory); 114 | const char *gmenu_tree_directory_get_menu_id (GMenuTreeDirectory *directory); 115 | GMenuTree *gmenu_tree_directory_get_tree (GMenuTreeDirectory *directory); 116 | 117 | gboolean gmenu_tree_directory_get_is_nodisplay (GMenuTreeDirectory *directory); 118 | 119 | GMenuTreeIter *gmenu_tree_directory_iter (GMenuTreeDirectory *directory); 120 | 121 | GMenuTreeIter *gmenu_tree_iter_ref (GMenuTreeIter *iter); 122 | void gmenu_tree_iter_unref (GMenuTreeIter *iter); 123 | 124 | GMenuTreeItemType gmenu_tree_iter_next (GMenuTreeIter *iter); 125 | GMenuTreeDirectory *gmenu_tree_iter_get_directory (GMenuTreeIter *iter); 126 | GMenuTreeEntry *gmenu_tree_iter_get_entry (GMenuTreeIter *iter); 127 | GMenuTreeHeader *gmenu_tree_iter_get_header (GMenuTreeIter *iter); 128 | GMenuTreeAlias *gmenu_tree_iter_get_alias (GMenuTreeIter *iter); 129 | GMenuTreeSeparator *gmenu_tree_iter_get_separator (GMenuTreeIter *iter); 130 | 131 | char *gmenu_tree_directory_make_path (GMenuTreeDirectory *directory, 132 | GMenuTreeEntry *entry); 133 | 134 | 135 | GMenuDesktopAppInfo *gmenu_tree_entry_get_app_info (GMenuTreeEntry *entry); 136 | GMenuTreeDirectory *gmenu_tree_entry_get_parent (GMenuTreeEntry *entry); 137 | GMenuTree *gmenu_tree_entry_get_tree (GMenuTreeEntry *entry); 138 | 139 | const char *gmenu_tree_entry_get_desktop_file_path (GMenuTreeEntry *entry); 140 | const char *gmenu_tree_entry_get_desktop_file_id (GMenuTreeEntry *entry); 141 | 142 | gboolean gmenu_tree_entry_get_is_nodisplay_recurse (GMenuTreeEntry *entry); 143 | gboolean gmenu_tree_entry_get_is_excluded (GMenuTreeEntry *entry); 144 | gboolean gmenu_tree_entry_get_is_unallocated (GMenuTreeEntry *entry); 145 | gboolean gmenu_tree_entry_get_is_flatpak (GMenuTreeEntry *entry); 146 | 147 | GMenuTreeDirectory *gmenu_tree_header_get_directory (GMenuTreeHeader *header); 148 | GMenuTree *gmenu_tree_header_get_tree (GMenuTreeHeader *header); 149 | GMenuTreeDirectory *gmenu_tree_header_get_parent (GMenuTreeHeader *header); 150 | 151 | GMenuTreeDirectory *gmenu_tree_alias_get_directory (GMenuTreeAlias *alias); 152 | GMenuTreeItemType gmenu_tree_alias_get_aliased_item_type (GMenuTreeAlias *alias); 153 | GMenuTreeDirectory *gmenu_tree_alias_get_aliased_directory (GMenuTreeAlias *alias); 154 | GMenuTreeEntry *gmenu_tree_alias_get_aliased_entry (GMenuTreeAlias *alias); 155 | GMenuTree *gmenu_tree_alias_get_tree (GMenuTreeAlias *alias); 156 | GMenuTreeDirectory *gmenu_tree_alias_get_parent (GMenuTreeAlias *alias); 157 | 158 | GMenuTree *gmenu_tree_separator_get_tree (GMenuTreeSeparator *separator); 159 | GMenuTreeDirectory *gmenu_tree_separator_get_parent (GMenuTreeSeparator *separator); 160 | 161 | G_END_DECLS 162 | 163 | #endif /* __GMENU_TREE_H__ */ 164 | -------------------------------------------------------------------------------- /libmenu/menu-monitor.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2005 Red Hat, Inc. 3 | * Copyright (C) 2006 Mark McLoughlin 4 | * Copyright (C) 2007 Sebastian Dröge 5 | * Copyright (C) 2008 Vincent Untz 6 | * 7 | * This library is free software; you can redistribute it and/or 8 | * modify it under the terms of the GNU Lesser General Public 9 | * License as published by the Free Software Foundation; either 10 | * version 2 of the License, or (at your option) any later version. 11 | * 12 | * This library is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * Lesser General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Lesser General Public 18 | * License along with this library; if not, write to the 19 | * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 20 | * Boston, MA 02111-1307, USA. 21 | */ 22 | 23 | #include "menu-monitor.h" 24 | 25 | #include 26 | 27 | #include "menu-util.h" 28 | 29 | struct MenuMonitor 30 | { 31 | char *path; 32 | guint refcount; 33 | 34 | GSList *notifies; 35 | 36 | GFileMonitor *monitor; 37 | 38 | guint is_directory : 1; 39 | }; 40 | 41 | typedef struct 42 | { 43 | MenuMonitor *monitor; 44 | MenuMonitorEvent event; 45 | char *path; 46 | } MenuMonitorEventInfo; 47 | 48 | typedef struct 49 | { 50 | MenuMonitorNotifyFunc notify_func; 51 | gpointer user_data; 52 | guint refcount; 53 | } MenuMonitorNotify; 54 | 55 | static MenuMonitorNotify *menu_monitor_notify_ref (MenuMonitorNotify *notify); 56 | static void menu_monitor_notify_unref (MenuMonitorNotify *notify); 57 | 58 | static GHashTable *monitors_registry = NULL; 59 | static guint events_idle_handler = 0; 60 | static GSList *pending_events = NULL; 61 | 62 | static void 63 | invoke_notifies (MenuMonitor *monitor, 64 | MenuMonitorEvent event, 65 | const char *path) 66 | { 67 | GSList *copy; 68 | GSList *tmp; 69 | 70 | copy = g_slist_copy (monitor->notifies); 71 | g_slist_foreach (copy, 72 | (GFunc) menu_monitor_notify_ref, 73 | NULL); 74 | 75 | tmp = copy; 76 | while (tmp != NULL) 77 | { 78 | MenuMonitorNotify *notify = tmp->data; 79 | GSList *next = tmp->next; 80 | 81 | if (notify->notify_func) 82 | { 83 | notify->notify_func (monitor, event, path, notify->user_data); 84 | } 85 | 86 | menu_monitor_notify_unref (notify); 87 | 88 | tmp = next; 89 | } 90 | 91 | g_slist_free (copy); 92 | } 93 | 94 | static gboolean 95 | emit_events_in_idle (void) 96 | { 97 | GSList *events_to_emit; 98 | GSList *tmp; 99 | 100 | events_to_emit = pending_events; 101 | 102 | pending_events = NULL; 103 | events_idle_handler = 0; 104 | 105 | tmp = events_to_emit; 106 | while (tmp != NULL) 107 | { 108 | MenuMonitorEventInfo *event_info = tmp->data; 109 | 110 | menu_monitor_ref (event_info->monitor); 111 | 112 | tmp = tmp->next; 113 | } 114 | 115 | tmp = events_to_emit; 116 | while (tmp != NULL) 117 | { 118 | MenuMonitorEventInfo *event_info = tmp->data; 119 | 120 | invoke_notifies (event_info->monitor, 121 | event_info->event, 122 | event_info->path); 123 | 124 | menu_monitor_unref (event_info->monitor); 125 | event_info->monitor = NULL; 126 | 127 | g_free (event_info->path); 128 | event_info->path = NULL; 129 | 130 | event_info->event = MENU_MONITOR_EVENT_INVALID; 131 | 132 | g_free (event_info); 133 | 134 | tmp = tmp->next; 135 | } 136 | 137 | g_slist_free (events_to_emit); 138 | 139 | return FALSE; 140 | } 141 | 142 | static void 143 | menu_monitor_queue_event (MenuMonitorEventInfo *event_info) 144 | { 145 | pending_events = g_slist_append (pending_events, event_info); 146 | 147 | if (events_idle_handler > 0) 148 | { 149 | g_source_remove (events_idle_handler); 150 | } 151 | 152 | events_idle_handler = g_timeout_add (100, (GSourceFunc) emit_events_in_idle, NULL); 153 | } 154 | 155 | static inline char * 156 | get_registry_key (const char *path, 157 | gboolean is_directory) 158 | { 159 | return g_strdup_printf ("%s:%s", 160 | path, 161 | is_directory ? "" : ""); 162 | } 163 | 164 | static gboolean 165 | monitor_callback (GFileMonitor *monitor, 166 | GFile *child, 167 | GFile *other_file, 168 | GFileMonitorEvent eflags, 169 | gpointer user_data) 170 | { 171 | MenuMonitorEventInfo *event_info; 172 | MenuMonitorEvent event; 173 | MenuMonitor *menu_monitor = (MenuMonitor *) user_data; 174 | 175 | event = MENU_MONITOR_EVENT_INVALID; 176 | switch (eflags) 177 | { 178 | case G_FILE_MONITOR_EVENT_CHANGED: 179 | event = MENU_MONITOR_EVENT_CHANGED; 180 | break; 181 | case G_FILE_MONITOR_EVENT_CREATED: 182 | event = MENU_MONITOR_EVENT_CREATED; 183 | break; 184 | case G_FILE_MONITOR_EVENT_DELETED: 185 | event = MENU_MONITOR_EVENT_DELETED; 186 | break; 187 | default: 188 | return TRUE; 189 | } 190 | 191 | event_info = g_new0 (MenuMonitorEventInfo, 1); 192 | 193 | event_info->path = g_file_get_path (child); 194 | event_info->event = event; 195 | event_info->monitor = menu_monitor; 196 | 197 | menu_monitor_queue_event (event_info); 198 | 199 | return TRUE; 200 | } 201 | 202 | static MenuMonitor * 203 | register_monitor (const char *path, 204 | gboolean is_directory) 205 | { 206 | MenuMonitor *retval; 207 | GFile *file; 208 | 209 | retval = g_new0 (MenuMonitor, 1); 210 | 211 | retval->path = g_strdup (path); 212 | retval->refcount = 1; 213 | retval->is_directory = is_directory != FALSE; 214 | 215 | file = g_file_new_for_path (retval->path); 216 | 217 | if (file == NULL) 218 | { 219 | menu_verbose ("Not adding monitor on '%s', failed to create GFile\n", 220 | retval->path); 221 | return retval; 222 | } 223 | 224 | if (retval->is_directory) 225 | retval->monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE, 226 | NULL, NULL); 227 | else 228 | retval->monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE, 229 | NULL, NULL); 230 | 231 | g_object_unref (G_OBJECT (file)); 232 | 233 | if (retval->monitor == NULL) 234 | { 235 | menu_verbose ("Not adding monitor on '%s', failed to create monitor\n", 236 | retval->path); 237 | return retval; 238 | } 239 | 240 | g_signal_connect (retval->monitor, "changed", 241 | G_CALLBACK (monitor_callback), retval); 242 | 243 | return retval; 244 | } 245 | 246 | static MenuMonitor * 247 | lookup_monitor (const char *path, 248 | gboolean is_directory) 249 | { 250 | MenuMonitor *retval; 251 | char *registry_key; 252 | 253 | retval = NULL; 254 | 255 | registry_key = get_registry_key (path, is_directory); 256 | 257 | if (monitors_registry == NULL) 258 | { 259 | monitors_registry = g_hash_table_new_full (g_str_hash, 260 | g_str_equal, 261 | g_free, 262 | NULL); 263 | } 264 | else 265 | { 266 | retval = g_hash_table_lookup (monitors_registry, registry_key); 267 | } 268 | 269 | if (retval == NULL) 270 | { 271 | retval = register_monitor (path, is_directory); 272 | g_hash_table_insert (monitors_registry, registry_key, retval); 273 | 274 | return retval; 275 | } 276 | else 277 | { 278 | g_free (registry_key); 279 | 280 | return menu_monitor_ref (retval); 281 | } 282 | } 283 | 284 | MenuMonitor * 285 | menu_get_file_monitor (const char *path) 286 | { 287 | g_return_val_if_fail (path != NULL, NULL); 288 | 289 | return lookup_monitor (path, FALSE); 290 | } 291 | 292 | MenuMonitor * 293 | menu_get_directory_monitor (const char *path) 294 | { 295 | g_return_val_if_fail (path != NULL, NULL); 296 | 297 | return lookup_monitor (path, TRUE); 298 | } 299 | 300 | MenuMonitor * 301 | menu_monitor_ref (MenuMonitor *monitor) 302 | { 303 | g_return_val_if_fail (monitor != NULL, NULL); 304 | g_return_val_if_fail (monitor->refcount > 0, NULL); 305 | 306 | monitor->refcount++; 307 | 308 | return monitor; 309 | } 310 | 311 | static void 312 | menu_monitor_clear_pending_events (MenuMonitor *monitor) 313 | { 314 | GSList *tmp; 315 | 316 | tmp = pending_events; 317 | while (tmp != NULL) 318 | { 319 | MenuMonitorEventInfo *event_info = tmp->data; 320 | GSList *next = tmp->next; 321 | 322 | if (event_info->monitor == monitor) 323 | { 324 | pending_events = g_slist_delete_link (pending_events, tmp); 325 | 326 | g_free (event_info->path); 327 | event_info->path = NULL; 328 | 329 | event_info->monitor = NULL; 330 | event_info->event = MENU_MONITOR_EVENT_INVALID; 331 | 332 | g_free (event_info); 333 | } 334 | 335 | tmp = next; 336 | } 337 | } 338 | 339 | void 340 | menu_monitor_unref (MenuMonitor *monitor) 341 | { 342 | char *registry_key; 343 | 344 | g_return_if_fail (monitor != NULL); 345 | g_return_if_fail (monitor->refcount > 0); 346 | 347 | if (--monitor->refcount > 0) 348 | return; 349 | 350 | registry_key = get_registry_key (monitor->path, monitor->is_directory); 351 | g_hash_table_remove (monitors_registry, registry_key); 352 | g_free (registry_key); 353 | 354 | if (g_hash_table_size (monitors_registry) == 0) 355 | { 356 | g_hash_table_destroy (monitors_registry); 357 | monitors_registry = NULL; 358 | } 359 | 360 | if (monitor->monitor) 361 | { 362 | g_file_monitor_cancel (monitor->monitor); 363 | g_object_unref (monitor->monitor); 364 | monitor->monitor = NULL; 365 | } 366 | 367 | g_slist_foreach (monitor->notifies, (GFunc) menu_monitor_notify_unref, NULL); 368 | g_slist_free (monitor->notifies); 369 | monitor->notifies = NULL; 370 | 371 | menu_monitor_clear_pending_events (monitor); 372 | 373 | g_free (monitor->path); 374 | monitor->path = NULL; 375 | 376 | g_free (monitor); 377 | } 378 | 379 | static MenuMonitorNotify * 380 | menu_monitor_notify_ref (MenuMonitorNotify *notify) 381 | { 382 | g_return_val_if_fail (notify != NULL, NULL); 383 | g_return_val_if_fail (notify->refcount > 0, NULL); 384 | 385 | notify->refcount++; 386 | 387 | return notify; 388 | } 389 | 390 | static void 391 | menu_monitor_notify_unref (MenuMonitorNotify *notify) 392 | { 393 | g_return_if_fail (notify != NULL); 394 | g_return_if_fail (notify->refcount > 0); 395 | 396 | if (--notify->refcount > 0) 397 | return; 398 | 399 | g_free (notify); 400 | } 401 | 402 | void 403 | menu_monitor_add_notify (MenuMonitor *monitor, 404 | MenuMonitorNotifyFunc notify_func, 405 | gpointer user_data) 406 | { 407 | MenuMonitorNotify *notify; 408 | GSList *tmp; 409 | 410 | g_return_if_fail (monitor != NULL); 411 | g_return_if_fail (notify_func != NULL); 412 | 413 | tmp = monitor->notifies; 414 | while (tmp != NULL) 415 | { 416 | notify = tmp->data; 417 | 418 | if (notify->notify_func == notify_func && 419 | notify->user_data == user_data) 420 | break; 421 | 422 | tmp = tmp->next; 423 | } 424 | 425 | if (tmp == NULL) 426 | { 427 | notify = g_new0 (MenuMonitorNotify, 1); 428 | notify->notify_func = notify_func; 429 | notify->user_data = user_data; 430 | notify->refcount = 1; 431 | 432 | monitor->notifies = g_slist_append (monitor->notifies, notify); 433 | } 434 | } 435 | 436 | void 437 | menu_monitor_remove_notify (MenuMonitor *monitor, 438 | MenuMonitorNotifyFunc notify_func, 439 | gpointer user_data) 440 | { 441 | GSList *tmp; 442 | 443 | tmp = monitor->notifies; 444 | while (tmp != NULL) 445 | { 446 | MenuMonitorNotify *notify = tmp->data; 447 | GSList *next = tmp->next; 448 | 449 | if (notify->notify_func == notify_func && 450 | notify->user_data == user_data) 451 | { 452 | notify->notify_func = NULL; 453 | notify->user_data = NULL; 454 | menu_monitor_notify_unref (notify); 455 | 456 | monitor->notifies = g_slist_delete_link (monitor->notifies, tmp); 457 | } 458 | 459 | tmp = next; 460 | } 461 | } 462 | -------------------------------------------------------------------------------- /libmenu/menu-util.c: -------------------------------------------------------------------------------- 1 | /* Random utility functions for menu code */ 2 | 3 | /* 4 | * Copyright (C) 2003 Red Hat, Inc. 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 2 of the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, write to the 18 | * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 19 | * Boston, MA 02111-1307, USA. 20 | */ 21 | 22 | #include "menu-util.h" 23 | 24 | #include 25 | #include 26 | 27 | 28 | #ifdef G_ENABLE_DEBUG 29 | 30 | static gboolean verbose = FALSE; 31 | static gboolean initted = FALSE; 32 | 33 | static inline gboolean 34 | menu_verbose_enabled (void) 35 | { 36 | if (!initted) 37 | { 38 | verbose = g_getenv ("MENU_VERBOSE") != NULL; 39 | initted = TRUE; 40 | } 41 | 42 | return verbose; 43 | } 44 | 45 | static int 46 | utf8_fputs (const char *str, 47 | FILE *f) 48 | { 49 | char *l; 50 | int ret; 51 | 52 | l = g_locale_from_utf8 (str, -1, NULL, NULL, NULL); 53 | 54 | if (l == NULL) 55 | ret = fputs (str, f); /* just print it anyway, better than nothing */ 56 | else 57 | ret = fputs (l, f); 58 | 59 | g_free (l); 60 | 61 | return ret; 62 | } 63 | 64 | void 65 | menu_verbose (const char *format, 66 | ...) 67 | { 68 | va_list args; 69 | char *str; 70 | 71 | if (!menu_verbose_enabled ()) 72 | return; 73 | 74 | va_start (args, format); 75 | str = g_strdup_vprintf (format, args); 76 | va_end (args); 77 | 78 | utf8_fputs (str, stderr); 79 | fflush (stderr); 80 | 81 | g_free (str); 82 | } 83 | 84 | static void append_to_string (MenuLayoutNode *node, 85 | gboolean onelevel, 86 | int depth, 87 | GString *str); 88 | 89 | static void 90 | append_spaces (GString *str, 91 | int depth) 92 | { 93 | while (depth > 0) 94 | { 95 | g_string_append_c (str, ' '); 96 | --depth; 97 | } 98 | } 99 | 100 | static void 101 | append_children (MenuLayoutNode *node, 102 | int depth, 103 | GString *str) 104 | { 105 | MenuLayoutNode *iter; 106 | 107 | iter = menu_layout_node_get_children (node); 108 | while (iter != NULL) 109 | { 110 | append_to_string (iter, FALSE, depth, str); 111 | 112 | iter = menu_layout_node_get_next (iter); 113 | } 114 | } 115 | 116 | static void 117 | append_simple_with_attr (MenuLayoutNode *node, 118 | int depth, 119 | const char *node_name, 120 | const char *attr_name, 121 | const char *attr_value, 122 | GString *str) 123 | { 124 | const char *content; 125 | 126 | append_spaces (str, depth); 127 | 128 | if ((content = menu_layout_node_get_content (node))) 129 | { 130 | char *escaped; 131 | 132 | escaped = g_markup_escape_text (content, -1); 133 | 134 | if (attr_name && attr_value) 135 | { 136 | char *attr_escaped; 137 | 138 | attr_escaped = g_markup_escape_text (attr_value, -1); 139 | 140 | g_string_append_printf (str, "<%s %s=\"%s\">%s\n", 141 | node_name, attr_name, 142 | attr_escaped, escaped, node_name); 143 | 144 | g_free (attr_escaped); 145 | } 146 | else 147 | { 148 | g_string_append_printf (str, "<%s>%s\n", 149 | node_name, escaped, node_name); 150 | } 151 | 152 | g_free (escaped); 153 | } 154 | else 155 | { 156 | if (attr_name && attr_value) 157 | { 158 | char *attr_escaped; 159 | 160 | attr_escaped = g_markup_escape_text (attr_value, -1); 161 | 162 | g_string_append_printf (str, "<%s %s=\"%s\"/>\n", 163 | node_name, attr_name, 164 | attr_escaped); 165 | 166 | g_free (attr_escaped); 167 | } 168 | else 169 | { 170 | g_string_append_printf (str, "<%s/>\n", node_name); 171 | } 172 | } 173 | } 174 | 175 | static void 176 | append_layout (MenuLayoutNode *node, 177 | int depth, 178 | const char *node_name, 179 | MenuLayoutValues *layout_values, 180 | GString *str) 181 | { 182 | const char *content; 183 | 184 | append_spaces (str, depth); 185 | 186 | if ((content = menu_layout_node_get_content (node))) 187 | { 188 | char *escaped; 189 | 190 | escaped = g_markup_escape_text (content, -1); 191 | 192 | g_string_append_printf (str, 193 | "<%s show_empty=\"%s\" inline=\"%s\" inline_header=\"%s\"" 194 | " inline_alias=\"%s\" inline_limit=\"%d\">%s\n", 195 | node_name, 196 | layout_values->show_empty ? "true" : "false", 197 | layout_values->inline_menus ? "true" : "false", 198 | layout_values->inline_header ? "true" : "false", 199 | layout_values->inline_alias ? "true" : "false", 200 | layout_values->inline_limit, 201 | escaped, 202 | node_name); 203 | 204 | g_free (escaped); 205 | } 206 | else 207 | { 208 | g_string_append_printf (str, 209 | "<%s show_empty=\"%s\" inline=\"%s\" inline_header=\"%s\"" 210 | " inline_alias=\"%s\" inline_limit=\"%d\"/>\n", 211 | node_name, 212 | layout_values->show_empty ? "true" : "false", 213 | layout_values->inline_menus ? "true" : "false", 214 | layout_values->inline_header ? "true" : "false", 215 | layout_values->inline_alias ? "true" : "false", 216 | layout_values->inline_limit); 217 | } 218 | } 219 | 220 | static void 221 | append_merge (MenuLayoutNode *node, 222 | int depth, 223 | const char *node_name, 224 | MenuLayoutMergeType merge_type, 225 | GString *str) 226 | { 227 | const char *merge_type_str; 228 | 229 | merge_type_str = NULL; 230 | 231 | switch (merge_type) 232 | { 233 | case MENU_LAYOUT_MERGE_NONE: 234 | merge_type_str = "none"; 235 | break; 236 | 237 | case MENU_LAYOUT_MERGE_MENUS: 238 | merge_type_str = "menus"; 239 | break; 240 | 241 | case MENU_LAYOUT_MERGE_FILES: 242 | merge_type_str = "files"; 243 | break; 244 | 245 | case MENU_LAYOUT_MERGE_ALL: 246 | merge_type_str = "all"; 247 | break; 248 | 249 | default: 250 | g_assert_not_reached (); 251 | break; 252 | } 253 | 254 | append_simple_with_attr (node, depth, node_name, "type", merge_type_str, str); 255 | } 256 | 257 | static void 258 | append_simple (MenuLayoutNode *node, 259 | int depth, 260 | const char *node_name, 261 | GString *str) 262 | { 263 | append_simple_with_attr (node, depth, node_name, NULL, NULL, str); 264 | } 265 | 266 | static void 267 | append_start (MenuLayoutNode *node, 268 | int depth, 269 | const char *node_name, 270 | GString *str) 271 | { 272 | append_spaces (str, depth); 273 | 274 | g_string_append_printf (str, "<%s>\n", node_name); 275 | } 276 | 277 | static void 278 | append_end (MenuLayoutNode *node, 279 | int depth, 280 | const char *node_name, 281 | GString *str) 282 | { 283 | append_spaces (str, depth); 284 | 285 | g_string_append_printf (str, "\n", node_name); 286 | } 287 | 288 | static void 289 | append_container (MenuLayoutNode *node, 290 | gboolean onelevel, 291 | int depth, 292 | const char *node_name, 293 | GString *str) 294 | { 295 | append_start (node, depth, node_name, str); 296 | if (!onelevel) 297 | { 298 | append_children (node, depth + 2, str); 299 | append_end (node, depth, node_name, str); 300 | } 301 | } 302 | 303 | static void 304 | append_to_string (MenuLayoutNode *node, 305 | gboolean onelevel, 306 | int depth, 307 | GString *str) 308 | { 309 | MenuLayoutValues layout_values; 310 | 311 | switch (menu_layout_node_get_type (node)) 312 | { 313 | case MENU_LAYOUT_NODE_ROOT: 314 | if (!onelevel) 315 | append_children (node, depth - 1, str); /* -1 to ignore depth of root */ 316 | else 317 | append_start (node, depth - 1, "Root", str); 318 | break; 319 | 320 | case MENU_LAYOUT_NODE_PASSTHROUGH: 321 | g_string_append (str, 322 | menu_layout_node_get_content (node)); 323 | g_string_append_c (str, '\n'); 324 | break; 325 | 326 | case MENU_LAYOUT_NODE_MENU: 327 | append_container (node, onelevel, depth, "Menu", str); 328 | break; 329 | 330 | case MENU_LAYOUT_NODE_APP_DIR: 331 | append_simple (node, depth, "AppDir", str); 332 | break; 333 | 334 | case MENU_LAYOUT_NODE_DEFAULT_APP_DIRS: 335 | append_simple (node, depth, "DefaultAppDirs", str); 336 | break; 337 | 338 | case MENU_LAYOUT_NODE_DIRECTORY_DIR: 339 | append_simple (node, depth, "DirectoryDir", str); 340 | break; 341 | 342 | case MENU_LAYOUT_NODE_DEFAULT_DIRECTORY_DIRS: 343 | append_simple (node, depth, "DefaultDirectoryDirs", str); 344 | break; 345 | 346 | case MENU_LAYOUT_NODE_DEFAULT_MERGE_DIRS: 347 | append_simple (node, depth, "DefaultMergeDirs", str); 348 | break; 349 | 350 | case MENU_LAYOUT_NODE_NAME: 351 | append_simple (node, depth, "Name", str); 352 | break; 353 | 354 | case MENU_LAYOUT_NODE_DIRECTORY: 355 | append_simple (node, depth, "Directory", str); 356 | break; 357 | 358 | case MENU_LAYOUT_NODE_ONLY_UNALLOCATED: 359 | append_simple (node, depth, "OnlyUnallocated", str); 360 | break; 361 | 362 | case MENU_LAYOUT_NODE_NOT_ONLY_UNALLOCATED: 363 | append_simple (node, depth, "NotOnlyUnallocated", str); 364 | break; 365 | 366 | case MENU_LAYOUT_NODE_INCLUDE: 367 | append_container (node, onelevel, depth, "Include", str); 368 | break; 369 | 370 | case MENU_LAYOUT_NODE_EXCLUDE: 371 | append_container (node, onelevel, depth, "Exclude", str); 372 | break; 373 | 374 | case MENU_LAYOUT_NODE_FILENAME: 375 | append_simple (node, depth, "Filename", str); 376 | break; 377 | 378 | case MENU_LAYOUT_NODE_CATEGORY: 379 | append_simple (node, depth, "Category", str); 380 | break; 381 | 382 | case MENU_LAYOUT_NODE_ALL: 383 | append_simple (node, depth, "All", str); 384 | break; 385 | 386 | case MENU_LAYOUT_NODE_AND: 387 | append_container (node, onelevel, depth, "And", str); 388 | break; 389 | 390 | case MENU_LAYOUT_NODE_OR: 391 | append_container (node, onelevel, depth, "Or", str); 392 | break; 393 | 394 | case MENU_LAYOUT_NODE_NOT: 395 | append_container (node, onelevel, depth, "Not", str); 396 | break; 397 | 398 | case MENU_LAYOUT_NODE_MERGE_FILE: 399 | { 400 | MenuMergeFileType type; 401 | 402 | type = menu_layout_node_merge_file_get_type (node); 403 | 404 | append_simple_with_attr (node, depth, "MergeFile", 405 | "type", type == MENU_MERGE_FILE_TYPE_PARENT ? "parent" : "path", 406 | str); 407 | break; 408 | } 409 | 410 | case MENU_LAYOUT_NODE_MERGE_DIR: 411 | append_simple (node, depth, "MergeDir", str); 412 | break; 413 | 414 | case MENU_LAYOUT_NODE_LEGACY_DIR: 415 | append_simple_with_attr (node, depth, "LegacyDir", 416 | "prefix", menu_layout_node_legacy_dir_get_prefix (node), 417 | str); 418 | break; 419 | 420 | case MENU_LAYOUT_NODE_KDE_LEGACY_DIRS: 421 | append_simple (node, depth, "KDELegacyDirs", str); 422 | break; 423 | 424 | case MENU_LAYOUT_NODE_MOVE: 425 | append_container (node, onelevel, depth, "Move", str); 426 | break; 427 | 428 | case MENU_LAYOUT_NODE_OLD: 429 | append_simple (node, depth, "Old", str); 430 | break; 431 | 432 | case MENU_LAYOUT_NODE_NEW: 433 | append_simple (node, depth, "New", str); 434 | break; 435 | 436 | case MENU_LAYOUT_NODE_DELETED: 437 | append_simple (node, depth, "Deleted", str); 438 | break; 439 | 440 | case MENU_LAYOUT_NODE_NOT_DELETED: 441 | append_simple (node, depth, "NotDeleted", str); 442 | break; 443 | 444 | case MENU_LAYOUT_NODE_LAYOUT: 445 | append_container (node, onelevel, depth, "Layout", str); 446 | break; 447 | 448 | case MENU_LAYOUT_NODE_DEFAULT_LAYOUT: 449 | menu_layout_node_default_layout_get_values (node, &layout_values); 450 | append_layout (node, depth, "DefaultLayout", &layout_values, str); 451 | break; 452 | 453 | case MENU_LAYOUT_NODE_MENUNAME: 454 | menu_layout_node_menuname_get_values (node, &layout_values); 455 | append_layout (node, depth, "MenuName", &layout_values, str); 456 | break; 457 | 458 | case MENU_LAYOUT_NODE_SEPARATOR: 459 | append_simple (node, depth, "Name", str); 460 | break; 461 | 462 | case MENU_LAYOUT_NODE_MERGE: 463 | append_merge (node, 464 | depth, 465 | "Merge", 466 | menu_layout_node_merge_get_type (node), 467 | str); 468 | break; 469 | 470 | default: 471 | g_assert_not_reached (); 472 | break; 473 | } 474 | } 475 | 476 | void 477 | menu_debug_print_layout (MenuLayoutNode *node, 478 | gboolean onelevel) 479 | { 480 | if (menu_verbose_enabled ()) 481 | { 482 | GString *str; 483 | 484 | str = g_string_new (NULL); 485 | append_to_string (node, onelevel, 0, str); 486 | 487 | utf8_fputs (str->str, stderr); 488 | fflush (stderr); 489 | 490 | g_string_free (str, TRUE); 491 | } 492 | } 493 | 494 | #endif /* G_ENABLE_DEBUG */ 495 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /COPYING.LIB: -------------------------------------------------------------------------------- 1 | GNU LIBRARY GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1991 Free Software Foundation, Inc. 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | [This is the first released version of the library GPL. It is 10 | numbered 2 because it goes with version 2 of the ordinary GPL.] 11 | 12 | Preamble 13 | 14 | The licenses for most software are designed to take away your 15 | freedom to share and change it. By contrast, the GNU General Public 16 | Licenses are intended to guarantee your freedom to share and change 17 | free software--to make sure the software is free for all its users. 18 | 19 | This license, the Library General Public License, applies to some 20 | specially designated Free Software Foundation software, and to any 21 | other libraries whose authors decide to use it. You can use it for 22 | your libraries, too. 23 | 24 | When we speak of free software, we are referring to freedom, not 25 | price. Our General Public Licenses are designed to make sure that you 26 | have the freedom to distribute copies of free software (and charge for 27 | this service if you wish), that you receive source code or can get it 28 | if you want it, that you can change the software or use pieces of it 29 | in new free programs; and that you know you can do these things. 30 | 31 | To protect your rights, we need to make restrictions that forbid 32 | anyone to deny you these rights or to ask you to surrender the rights. 33 | These restrictions translate to certain responsibilities for you if 34 | you distribute copies of the library, or if you modify it. 35 | 36 | For example, if you distribute copies of the library, whether gratis 37 | or for a fee, you must give the recipients all the rights that we gave 38 | you. You must make sure that they, too, receive or can get the source 39 | code. If you link a program with the library, you must provide 40 | complete object files to the recipients so that they can relink them 41 | with the library, after making changes to the library and recompiling 42 | it. And you must show them these terms so they know their rights. 43 | 44 | Our method of protecting your rights has two steps: (1) copyright 45 | the library, and (2) offer you this license which gives you legal 46 | permission to copy, distribute and/or modify the library. 47 | 48 | Also, for each distributor's protection, we want to make certain 49 | that everyone understands that there is no warranty for this free 50 | library. If the library is modified by someone else and passed on, we 51 | want its recipients to know that what they have is not the original 52 | version, so that any problems introduced by others will not reflect on 53 | the original authors' reputations. 54 | 55 | Finally, any free program is threatened constantly by software 56 | patents. We wish to avoid the danger that companies distributing free 57 | software will individually obtain patent licenses, thus in effect 58 | transforming the program into proprietary software. To prevent this, 59 | we have made it clear that any patent must be licensed for everyone's 60 | free use or not licensed at all. 61 | 62 | Most GNU software, including some libraries, is covered by the ordinary 63 | GNU General Public License, which was designed for utility programs. This 64 | license, the GNU Library General Public License, applies to certain 65 | designated libraries. This license is quite different from the ordinary 66 | one; be sure to read it in full, and don't assume that anything in it is 67 | the same as in the ordinary license. 68 | 69 | The reason we have a separate public license for some libraries is that 70 | they blur the distinction we usually make between modifying or adding to a 71 | program and simply using it. Linking a program with a library, without 72 | changing the library, is in some sense simply using the library, and is 73 | analogous to running a utility program or application program. However, in 74 | a textual and legal sense, the linked executable is a combined work, a 75 | derivative of the original library, and the ordinary General Public License 76 | treats it as such. 77 | 78 | Because of this blurred distinction, using the ordinary General 79 | Public License for libraries did not effectively promote software 80 | sharing, because most developers did not use the libraries. We 81 | concluded that weaker conditions might promote sharing better. 82 | 83 | However, unrestricted linking of non-free programs would deprive the 84 | users of those programs of all benefit from the free status of the 85 | libraries themselves. This Library General Public License is intended to 86 | permit developers of non-free programs to use free libraries, while 87 | preserving your freedom as a user of such programs to change the free 88 | libraries that are incorporated in them. (We have not seen how to achieve 89 | this as regards changes in header files, but we have achieved it as regards 90 | changes in the actual functions of the Library.) The hope is that this 91 | will lead to faster development of free libraries. 92 | 93 | The precise terms and conditions for copying, distribution and 94 | modification follow. Pay close attention to the difference between a 95 | "work based on the library" and a "work that uses the library". The 96 | former contains code derived from the library, while the latter only 97 | works together with the library. 98 | 99 | Note that it is possible for a library to be covered by the ordinary 100 | General Public License rather than by this special one. 101 | 102 | GNU LIBRARY GENERAL PUBLIC LICENSE 103 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 104 | 105 | 0. This License Agreement applies to any software library which 106 | contains a notice placed by the copyright holder or other authorized 107 | party saying it may be distributed under the terms of this Library 108 | General Public License (also called "this License"). Each licensee is 109 | addressed as "you". 110 | 111 | A "library" means a collection of software functions and/or data 112 | prepared so as to be conveniently linked with application programs 113 | (which use some of those functions and data) to form executables. 114 | 115 | The "Library", below, refers to any such software library or work 116 | which has been distributed under these terms. A "work based on the 117 | Library" means either the Library or any derivative work under 118 | copyright law: that is to say, a work containing the Library or a 119 | portion of it, either verbatim or with modifications and/or translated 120 | straightforwardly into another language. (Hereinafter, translation is 121 | included without limitation in the term "modification".) 122 | 123 | "Source code" for a work means the preferred form of the work for 124 | making modifications to it. For a library, complete source code means 125 | all the source code for all modules it contains, plus any associated 126 | interface definition files, plus the scripts used to control compilation 127 | and installation of the library. 128 | 129 | Activities other than copying, distribution and modification are not 130 | covered by this License; they are outside its scope. The act of 131 | running a program using the Library is not restricted, and output from 132 | such a program is covered only if its contents constitute a work based 133 | on the Library (independent of the use of the Library in a tool for 134 | writing it). Whether that is true depends on what the Library does 135 | and what the program that uses the Library does. 136 | 137 | 1. You may copy and distribute verbatim copies of the Library's 138 | complete source code as you receive it, in any medium, provided that 139 | you conspicuously and appropriately publish on each copy an 140 | appropriate copyright notice and disclaimer of warranty; keep intact 141 | all the notices that refer to this License and to the absence of any 142 | warranty; and distribute a copy of this License along with the 143 | Library. 144 | 145 | You may charge a fee for the physical act of transferring a copy, 146 | and you may at your option offer warranty protection in exchange for a 147 | fee. 148 | 149 | 2. You may modify your copy or copies of the Library or any portion 150 | of it, thus forming a work based on the Library, and copy and 151 | distribute such modifications or work under the terms of Section 1 152 | above, provided that you also meet all of these conditions: 153 | 154 | a) The modified work must itself be a software library. 155 | 156 | b) You must cause the files modified to carry prominent notices 157 | stating that you changed the files and the date of any change. 158 | 159 | c) You must cause the whole of the work to be licensed at no 160 | charge to all third parties under the terms of this License. 161 | 162 | d) If a facility in the modified Library refers to a function or a 163 | table of data to be supplied by an application program that uses 164 | the facility, other than as an argument passed when the facility 165 | is invoked, then you must make a good faith effort to ensure that, 166 | in the event an application does not supply such function or 167 | table, the facility still operates, and performs whatever part of 168 | its purpose remains meaningful. 169 | 170 | (For example, a function in a library to compute square roots has 171 | a purpose that is entirely well-defined independent of the 172 | application. Therefore, Subsection 2d requires that any 173 | application-supplied function or table used by this function must 174 | be optional: if the application does not supply it, the square 175 | root function must still compute square roots.) 176 | 177 | These requirements apply to the modified work as a whole. If 178 | identifiable sections of that work are not derived from the Library, 179 | and can be reasonably considered independent and separate works in 180 | themselves, then this License, and its terms, do not apply to those 181 | sections when you distribute them as separate works. But when you 182 | distribute the same sections as part of a whole which is a work based 183 | on the Library, the distribution of the whole must be on the terms of 184 | this License, whose permissions for other licensees extend to the 185 | entire whole, and thus to each and every part regardless of who wrote 186 | it. 187 | 188 | Thus, it is not the intent of this section to claim rights or contest 189 | your rights to work written entirely by you; rather, the intent is to 190 | exercise the right to control the distribution of derivative or 191 | collective works based on the Library. 192 | 193 | In addition, mere aggregation of another work not based on the Library 194 | with the Library (or with a work based on the Library) on a volume of 195 | a storage or distribution medium does not bring the other work under 196 | the scope of this License. 197 | 198 | 3. You may opt to apply the terms of the ordinary GNU General Public 199 | License instead of this License to a given copy of the Library. To do 200 | this, you must alter all the notices that refer to this License, so 201 | that they refer to the ordinary GNU General Public License, version 2, 202 | instead of to this License. (If a newer version than version 2 of the 203 | ordinary GNU General Public License has appeared, then you can specify 204 | that version instead if you wish.) Do not make any other change in 205 | these notices. 206 | 207 | Once this change is made in a given copy, it is irreversible for 208 | that copy, so the ordinary GNU General Public License applies to all 209 | subsequent copies and derivative works made from that copy. 210 | 211 | This option is useful when you wish to copy part of the code of 212 | the Library into a program that is not a library. 213 | 214 | 4. You may copy and distribute the Library (or a portion or 215 | derivative of it, under Section 2) in object code or executable form 216 | under the terms of Sections 1 and 2 above provided that you accompany 217 | it with the complete corresponding machine-readable source code, which 218 | must be distributed under the terms of Sections 1 and 2 above on a 219 | medium customarily used for software interchange. 220 | 221 | If distribution of object code is made by offering access to copy 222 | from a designated place, then offering equivalent access to copy the 223 | source code from the same place satisfies the requirement to 224 | distribute the source code, even though third parties are not 225 | compelled to copy the source along with the object code. 226 | 227 | 5. A program that contains no derivative of any portion of the 228 | Library, but is designed to work with the Library by being compiled or 229 | linked with it, is called a "work that uses the Library". Such a 230 | work, in isolation, is not a derivative work of the Library, and 231 | therefore falls outside the scope of this License. 232 | 233 | However, linking a "work that uses the Library" with the Library 234 | creates an executable that is a derivative of the Library (because it 235 | contains portions of the Library), rather than a "work that uses the 236 | library". The executable is therefore covered by this License. 237 | Section 6 states terms for distribution of such executables. 238 | 239 | When a "work that uses the Library" uses material from a header file 240 | that is part of the Library, the object code for the work may be a 241 | derivative work of the Library even though the source code is not. 242 | Whether this is true is especially significant if the work can be 243 | linked without the Library, or if the work is itself a library. The 244 | threshold for this to be true is not precisely defined by law. 245 | 246 | If such an object file uses only numerical parameters, data 247 | structure layouts and accessors, and small macros and small inline 248 | functions (ten lines or less in length), then the use of the object 249 | file is unrestricted, regardless of whether it is legally a derivative 250 | work. (Executables containing this object code plus portions of the 251 | Library will still fall under Section 6.) 252 | 253 | Otherwise, if the work is a derivative of the Library, you may 254 | distribute the object code for the work under the terms of Section 6. 255 | Any executables containing that work also fall under Section 6, 256 | whether or not they are linked directly with the Library itself. 257 | 258 | 6. As an exception to the Sections above, you may also compile or 259 | link a "work that uses the Library" with the Library to produce a 260 | work containing portions of the Library, and distribute that work 261 | under terms of your choice, provided that the terms permit 262 | modification of the work for the customer's own use and reverse 263 | engineering for debugging such modifications. 264 | 265 | You must give prominent notice with each copy of the work that the 266 | Library is used in it and that the Library and its use are covered by 267 | this License. You must supply a copy of this License. If the work 268 | during execution displays copyright notices, you must include the 269 | copyright notice for the Library among them, as well as a reference 270 | directing the user to the copy of this License. Also, you must do one 271 | of these things: 272 | 273 | a) Accompany the work with the complete corresponding 274 | machine-readable source code for the Library including whatever 275 | changes were used in the work (which must be distributed under 276 | Sections 1 and 2 above); and, if the work is an executable linked 277 | with the Library, with the complete machine-readable "work that 278 | uses the Library", as object code and/or source code, so that the 279 | user can modify the Library and then relink to produce a modified 280 | executable containing the modified Library. (It is understood 281 | that the user who changes the contents of definitions files in the 282 | Library will not necessarily be able to recompile the application 283 | to use the modified definitions.) 284 | 285 | b) Accompany the work with a written offer, valid for at 286 | least three years, to give the same user the materials 287 | specified in Subsection 6a, above, for a charge no more 288 | than the cost of performing this distribution. 289 | 290 | c) If distribution of the work is made by offering access to copy 291 | from a designated place, offer equivalent access to copy the above 292 | specified materials from the same place. 293 | 294 | d) Verify that the user has already received a copy of these 295 | materials or that you have already sent this user a copy. 296 | 297 | For an executable, the required form of the "work that uses the 298 | Library" must include any data and utility programs needed for 299 | reproducing the executable from it. However, as a special exception, 300 | the source code distributed need not include anything that is normally 301 | distributed (in either source or binary form) with the major 302 | components (compiler, kernel, and so on) of the operating system on 303 | which the executable runs, unless that component itself accompanies 304 | the executable. 305 | 306 | It may happen that this requirement contradicts the license 307 | restrictions of other proprietary libraries that do not normally 308 | accompany the operating system. Such a contradiction means you cannot 309 | use both them and the Library together in an executable that you 310 | distribute. 311 | 312 | 7. You may place library facilities that are a work based on the 313 | Library side-by-side in a single library together with other library 314 | facilities not covered by this License, and distribute such a combined 315 | library, provided that the separate distribution of the work based on 316 | the Library and of the other library facilities is otherwise 317 | permitted, and provided that you do these two things: 318 | 319 | a) Accompany the combined library with a copy of the same work 320 | based on the Library, uncombined with any other library 321 | facilities. This must be distributed under the terms of the 322 | Sections above. 323 | 324 | b) Give prominent notice with the combined library of the fact 325 | that part of it is a work based on the Library, and explaining 326 | where to find the accompanying uncombined form of the same work. 327 | 328 | 8. You may not copy, modify, sublicense, link with, or distribute 329 | the Library except as expressly provided under this License. Any 330 | attempt otherwise to copy, modify, sublicense, link with, or 331 | distribute the Library is void, and will automatically terminate your 332 | rights under this License. However, parties who have received copies, 333 | or rights, from you under this License will not have their licenses 334 | terminated so long as such parties remain in full compliance. 335 | 336 | 9. You are not required to accept this License, since you have not 337 | signed it. However, nothing else grants you permission to modify or 338 | distribute the Library or its derivative works. These actions are 339 | prohibited by law if you do not accept this License. Therefore, by 340 | modifying or distributing the Library (or any work based on the 341 | Library), you indicate your acceptance of this License to do so, and 342 | all its terms and conditions for copying, distributing or modifying 343 | the Library or works based on it. 344 | 345 | 10. Each time you redistribute the Library (or any work based on the 346 | Library), the recipient automatically receives a license from the 347 | original licensor to copy, distribute, link with or modify the Library 348 | subject to these terms and conditions. You may not impose any further 349 | restrictions on the recipients' exercise of the rights granted herein. 350 | You are not responsible for enforcing compliance by third parties to 351 | this License. 352 | 353 | 11. If, as a consequence of a court judgment or allegation of patent 354 | infringement or for any other reason (not limited to patent issues), 355 | conditions are imposed on you (whether by court order, agreement or 356 | otherwise) that contradict the conditions of this License, they do not 357 | excuse you from the conditions of this License. If you cannot 358 | distribute so as to satisfy simultaneously your obligations under this 359 | License and any other pertinent obligations, then as a consequence you 360 | may not distribute the Library at all. For example, if a patent 361 | license would not permit royalty-free redistribution of the Library by 362 | all those who receive copies directly or indirectly through you, then 363 | the only way you could satisfy both it and this License would be to 364 | refrain entirely from distribution of the Library. 365 | 366 | If any portion of this section is held invalid or unenforceable under any 367 | particular circumstance, the balance of the section is intended to apply, 368 | and the section as a whole is intended to apply in other circumstances. 369 | 370 | It is not the purpose of this section to induce you to infringe any 371 | patents or other property right claims or to contest validity of any 372 | such claims; this section has the sole purpose of protecting the 373 | integrity of the free software distribution system which is 374 | implemented by public license practices. Many people have made 375 | generous contributions to the wide range of software distributed 376 | through that system in reliance on consistent application of that 377 | system; it is up to the author/donor to decide if he or she is willing 378 | to distribute software through any other system and a licensee cannot 379 | impose that choice. 380 | 381 | This section is intended to make thoroughly clear what is believed to 382 | be a consequence of the rest of this License. 383 | 384 | 12. If the distribution and/or use of the Library is restricted in 385 | certain countries either by patents or by copyrighted interfaces, the 386 | original copyright holder who places the Library under this License may add 387 | an explicit geographical distribution limitation excluding those countries, 388 | so that distribution is permitted only in or among countries not thus 389 | excluded. In such case, this License incorporates the limitation as if 390 | written in the body of this License. 391 | 392 | 13. The Free Software Foundation may publish revised and/or new 393 | versions of the Library General Public License from time to time. 394 | Such new versions will be similar in spirit to the present version, 395 | but may differ in detail to address new problems or concerns. 396 | 397 | Each version is given a distinguishing version number. If the Library 398 | specifies a version number of this License which applies to it and 399 | "any later version", you have the option of following the terms and 400 | conditions either of that version or of any later version published by 401 | the Free Software Foundation. If the Library does not specify a 402 | license version number, you may choose any version ever published by 403 | the Free Software Foundation. 404 | 405 | 14. If you wish to incorporate parts of the Library into other free 406 | programs whose distribution conditions are incompatible with these, 407 | write to the author to ask for permission. For software which is 408 | copyrighted by the Free Software Foundation, write to the Free 409 | Software Foundation; we sometimes make exceptions for this. Our 410 | decision will be guided by the two goals of preserving the free status 411 | of all derivatives of our free software and of promoting the sharing 412 | and reuse of software generally. 413 | 414 | NO WARRANTY 415 | 416 | 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO 417 | WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. 418 | EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR 419 | OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY 420 | KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE 421 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 422 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE 423 | LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME 424 | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 425 | 426 | 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN 427 | WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY 428 | AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU 429 | FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR 430 | CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE 431 | LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING 432 | RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A 433 | FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF 434 | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 435 | DAMAGES. 436 | 437 | END OF TERMS AND CONDITIONS 438 | 439 | How to Apply These Terms to Your New Libraries 440 | 441 | If you develop a new library, and you want it to be of the greatest 442 | possible use to the public, we recommend making it free software that 443 | everyone can redistribute and change. You can do so by permitting 444 | redistribution under these terms (or, alternatively, under the terms of the 445 | ordinary General Public License). 446 | 447 | To apply these terms, attach the following notices to the library. It is 448 | safest to attach them to the start of each source file to most effectively 449 | convey the exclusion of warranty; and each file should have at least the 450 | "copyright" line and a pointer to where the full notice is found. 451 | 452 | 453 | Copyright (C) 454 | 455 | This library is free software; you can redistribute it and/or 456 | modify it under the terms of the GNU Library General Public 457 | License as published by the Free Software Foundation; either 458 | version 2 of the License, or (at your option) any later version. 459 | 460 | This library is distributed in the hope that it will be useful, 461 | but WITHOUT ANY WARRANTY; without even the implied warranty of 462 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 463 | Library General Public License for more details. 464 | 465 | You should have received a copy of the GNU Library General Public 466 | License along with this library; if not, write to the Free Software 467 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 468 | 469 | Also add information on how to contact you by electronic and paper mail. 470 | 471 | You should also get your employer (if you work as a programmer) or your 472 | school, if any, to sign a "copyright disclaimer" for the library, if 473 | necessary. Here is a sample; alter the names: 474 | 475 | Yoyodyne, Inc., hereby disclaims all copyright interest in the 476 | library `Frob' (a library for tweaking knobs) written by James Random Hacker. 477 | 478 | , 1 April 1990 479 | Ty Coon, President of Vice 480 | 481 | That's all there is to it! 482 | -------------------------------------------------------------------------------- /libmenu/desktop-entries.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2002 - 2004 Red Hat, Inc. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, write to the 16 | * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 17 | * Boston, MA 02111-1307, USA. 18 | */ 19 | 20 | #include "desktop-entries.h" 21 | #include "gmenu-desktopappinfo.h" 22 | 23 | #include 24 | 25 | #include "menu-util.h" 26 | 27 | #define DESKTOP_ENTRY_GROUP "Desktop Entry" 28 | 29 | struct DesktopEntry 30 | { 31 | guint refcount; 32 | 33 | char *path; 34 | const char *basename; 35 | 36 | guint type : 2; 37 | guint reserved : 30; 38 | }; 39 | 40 | typedef struct 41 | { 42 | DesktopEntry base; 43 | 44 | GMenuDesktopAppInfo *appinfo; 45 | GQuark *categories; 46 | guint showin : 1; 47 | } DesktopEntryDesktop; 48 | 49 | typedef struct 50 | { 51 | DesktopEntry base; 52 | 53 | char *name; 54 | char *generic_name; 55 | char *comment; 56 | GIcon *icon; 57 | 58 | guint nodisplay : 1; 59 | guint hidden : 1; 60 | guint showin : 1; 61 | } DesktopEntryDirectory; 62 | 63 | struct DesktopEntrySet 64 | { 65 | int refcount; 66 | GHashTable *hash; 67 | }; 68 | 69 | /* 70 | * Desktop entries 71 | */ 72 | 73 | /** 74 | * unix_basename_from_path: 75 | * @path: Path string 76 | * 77 | * Returns: A constant pointer into the basename of @path 78 | */ 79 | static const char * 80 | unix_basename_from_path (const char *path) 81 | { 82 | const char *basename = g_strrstr (path, "/"); 83 | if (basename) 84 | return basename + 1; 85 | else 86 | return path; 87 | } 88 | 89 | static const char * 90 | get_current_desktop (void) 91 | { 92 | static char *current_desktop = NULL; 93 | 94 | /* Support XDG_CURRENT_DESKTOP environment variable; this can be used 95 | * to abuse gnome-menus in non-GNOME desktops. */ 96 | if (!current_desktop) 97 | { 98 | const char *desktop; 99 | 100 | desktop = g_getenv ("XDG_CURRENT_DESKTOP"); 101 | 102 | /* Note: if XDG_CURRENT_DESKTOP is set but empty, do as if it 103 | * was not set */ 104 | if (!desktop || desktop[0] == '\0') 105 | current_desktop = g_strdup ("GNOME"); 106 | else 107 | current_desktop = g_strdup (desktop); 108 | } 109 | 110 | /* Using "*" means skipping desktop-related checks */ 111 | if (g_strcmp0 (current_desktop, "*") == 0) 112 | return NULL; 113 | 114 | return current_desktop; 115 | } 116 | 117 | static GIcon * 118 | key_file_get_icon (GKeyFile *key_file) 119 | { 120 | GIcon *icon = NULL; 121 | gchar *icon_name; 122 | 123 | icon_name = g_key_file_get_locale_string (key_file, DESKTOP_ENTRY_GROUP, 124 | "Icon", NULL, NULL); 125 | if (!icon_name) 126 | return NULL; 127 | 128 | if (g_path_is_absolute (icon_name)) { 129 | GFile *file; 130 | 131 | file = g_file_new_for_path (icon_name); 132 | icon = g_file_icon_new (file); 133 | g_object_unref (file); 134 | } else { 135 | char *p; 136 | 137 | /* Work around a common mistake in desktop files */ 138 | if ((p = strrchr (icon_name, '.')) != NULL && 139 | (strcmp (p, ".png") == 0 || 140 | strcmp (p, ".xpm") == 0 || 141 | strcmp (p, ".svg") == 0)) 142 | *p = 0; 143 | 144 | icon = g_themed_icon_new (icon_name); 145 | } 146 | 147 | g_free (icon_name); 148 | 149 | return icon; 150 | } 151 | 152 | static gboolean 153 | key_file_get_show_in (GKeyFile *key_file) 154 | { 155 | const gchar *current_desktop; 156 | gchar **strv; 157 | gboolean show_in = TRUE; 158 | int i; 159 | gchar *exec; 160 | 161 | current_desktop = get_current_desktop (); 162 | if (!current_desktop) 163 | return TRUE; 164 | 165 | exec = g_key_file_get_string (key_file, 166 | DESKTOP_ENTRY_GROUP, 167 | "Exec", 168 | NULL); 169 | 170 | if (exec) { 171 | if (g_str_has_prefix (exec, "gnome-control-center")) { 172 | g_free (exec); 173 | return FALSE; 174 | } 175 | g_free (exec); 176 | } 177 | 178 | strv = g_key_file_get_string_list (key_file, 179 | DESKTOP_ENTRY_GROUP, 180 | "OnlyShowIn", 181 | NULL, 182 | NULL); 183 | 184 | if (strv) 185 | { 186 | show_in = FALSE; 187 | for (i = 0; strv[i]; i++) 188 | { 189 | if (!strcmp (strv[i], "GNOME") || !strcmp (strv[i], "X-Cinnamon")) 190 | { 191 | show_in = TRUE; 192 | break; 193 | } 194 | } 195 | } 196 | else 197 | { 198 | strv = g_key_file_get_string_list (key_file, 199 | DESKTOP_ENTRY_GROUP, 200 | "NotShowIn", 201 | NULL, 202 | NULL); 203 | if (strv) 204 | { 205 | show_in = TRUE; 206 | for (i = 0; strv[i]; i++) 207 | { 208 | if (!strcmp (strv[i], current_desktop)) 209 | { 210 | show_in = FALSE; 211 | } 212 | } 213 | } 214 | } 215 | g_strfreev (strv); 216 | 217 | return show_in; 218 | } 219 | 220 | static gboolean 221 | desktop_entry_load_directory (DesktopEntry *entry, 222 | GKeyFile *key_file, 223 | GError **error) 224 | { 225 | DesktopEntryDirectory *entry_directory = (DesktopEntryDirectory*)entry; 226 | char *type_str; 227 | 228 | type_str = g_key_file_get_string (key_file, DESKTOP_ENTRY_GROUP, "Type", error); 229 | if (!type_str) 230 | return FALSE; 231 | 232 | if (strcmp (type_str, "Directory") != 0) 233 | { 234 | g_set_error (error, 235 | G_KEY_FILE_ERROR, 236 | G_KEY_FILE_ERROR_INVALID_VALUE, 237 | "\"%s\" does not contain the correct \"Type\" value\n", entry->path); 238 | g_free (type_str); 239 | return FALSE; 240 | } 241 | 242 | g_free (type_str); 243 | 244 | entry_directory->name = g_key_file_get_locale_string (key_file, DESKTOP_ENTRY_GROUP, "Name", NULL, error); 245 | if (entry_directory->name == NULL) 246 | return FALSE; 247 | 248 | entry_directory->generic_name = g_key_file_get_locale_string (key_file, DESKTOP_ENTRY_GROUP, "GenericName", NULL, NULL); 249 | entry_directory->comment = g_key_file_get_locale_string (key_file, DESKTOP_ENTRY_GROUP, "Comment", NULL, NULL); 250 | entry_directory->icon = key_file_get_icon (key_file); 251 | entry_directory->nodisplay = g_key_file_get_boolean (key_file, 252 | DESKTOP_ENTRY_GROUP, 253 | "NoDisplay", 254 | NULL); 255 | entry_directory->hidden = g_key_file_get_boolean (key_file, 256 | DESKTOP_ENTRY_GROUP, 257 | "Hidden", 258 | NULL); 259 | entry_directory->showin = key_file_get_show_in (key_file); 260 | 261 | return TRUE; 262 | } 263 | 264 | static DesktopEntryResultCode 265 | desktop_entry_load (DesktopEntry *entry) 266 | { 267 | if (strstr (entry->path, "/menu-xdg/")) 268 | return DESKTOP_ENTRY_LOAD_FAIL_OTHER; 269 | if (entry->type == DESKTOP_ENTRY_DESKTOP) 270 | { 271 | GKeyFile *key_file = NULL; 272 | DesktopEntryDesktop *entry_desktop = (DesktopEntryDesktop*)entry; 273 | const char *categories_str; 274 | 275 | entry_desktop->appinfo = gmenu_desktopappinfo_new_from_filename (entry->path); 276 | if (!GMENU_IS_DESKTOPAPPINFO (((DesktopEntryDesktop*)entry)->appinfo)) 277 | { 278 | menu_verbose ("Failed to load \"%s\"\n", entry->path); 279 | return DESKTOP_ENTRY_LOAD_FAIL_APPINFO; 280 | } 281 | 282 | categories_str = gmenu_desktopappinfo_get_categories (entry_desktop->appinfo); 283 | if (categories_str) 284 | { 285 | char **categories; 286 | int i; 287 | 288 | categories = g_strsplit (categories_str, ";", -1); 289 | entry_desktop->categories = g_new0 (GQuark, g_strv_length (categories) + 1); 290 | 291 | for (i = 0; categories[i]; i++) 292 | entry_desktop->categories[i] = g_quark_from_string (categories[i]); 293 | 294 | g_strfreev (categories); 295 | } 296 | 297 | key_file = g_key_file_new (); 298 | 299 | if (!g_key_file_load_from_file (key_file, entry->path, 0, NULL)) 300 | entry_desktop->showin = TRUE; 301 | else 302 | entry_desktop->showin = key_file_get_show_in (key_file); 303 | 304 | g_key_file_free (key_file); 305 | 306 | return DESKTOP_ENTRY_LOAD_SUCCESS; 307 | } 308 | else if (entry->type == DESKTOP_ENTRY_DIRECTORY) 309 | { 310 | GKeyFile *key_file = NULL; 311 | GError *error = NULL; 312 | DesktopEntryResultCode rescode = DESKTOP_ENTRY_LOAD_SUCCESS; 313 | 314 | key_file = g_key_file_new (); 315 | 316 | if (!g_key_file_load_from_file (key_file, entry->path, 0, &error)) 317 | { 318 | rescode = DESKTOP_ENTRY_LOAD_FAIL_OTHER; 319 | goto out; 320 | } 321 | 322 | if (!desktop_entry_load_directory (entry, key_file, &error)) 323 | { 324 | rescode = DESKTOP_ENTRY_LOAD_FAIL_OTHER; 325 | goto out; 326 | } 327 | 328 | rescode = DESKTOP_ENTRY_LOAD_SUCCESS; 329 | 330 | out: 331 | g_key_file_free (key_file); 332 | 333 | if (rescode == DESKTOP_ENTRY_LOAD_FAIL_OTHER) 334 | { 335 | if (error) 336 | { 337 | menu_verbose ("Failed to load \"%s\": %s\n", entry->path, error->message); 338 | g_error_free (error); 339 | } 340 | else 341 | menu_verbose ("Failed to load \"%s\"\n", entry->path); 342 | } 343 | 344 | return rescode; 345 | } 346 | else 347 | g_assert_not_reached (); 348 | 349 | return DESKTOP_ENTRY_LOAD_FAIL_OTHER; 350 | } 351 | 352 | DesktopEntry * 353 | desktop_entry_new (const char *path, 354 | DesktopEntryResultCode *res_code) 355 | { 356 | DesktopEntryType type; 357 | DesktopEntry *retval; 358 | DesktopEntryResultCode code; 359 | 360 | menu_verbose ("Loading desktop entry \"%s\"\n", path); 361 | 362 | if (g_str_has_suffix (path, ".desktop")) 363 | { 364 | type = DESKTOP_ENTRY_DESKTOP; 365 | retval = (DesktopEntry*)g_new0 (DesktopEntryDesktop, 1); 366 | } 367 | else if (g_str_has_suffix (path, ".directory")) 368 | { 369 | type = DESKTOP_ENTRY_DIRECTORY; 370 | retval = (DesktopEntry*)g_new0 (DesktopEntryDirectory, 1); 371 | } 372 | else 373 | { 374 | menu_verbose ("Unknown desktop entry suffix in \"%s\"\n", 375 | path); 376 | *res_code = DESKTOP_ENTRY_LOAD_FAIL_OTHER; 377 | return NULL; 378 | } 379 | 380 | retval->refcount = 1; 381 | retval->type = type; 382 | retval->path = g_strdup (path); 383 | retval->basename = unix_basename_from_path (retval->path); 384 | 385 | code = desktop_entry_load (retval); 386 | *res_code = code; 387 | 388 | if (code < DESKTOP_ENTRY_LOAD_SUCCESS) 389 | { 390 | desktop_entry_unref (retval); 391 | return NULL; 392 | } 393 | 394 | return retval; 395 | } 396 | 397 | DesktopEntry * 398 | desktop_entry_reload (DesktopEntry *entry) 399 | { 400 | g_return_val_if_fail (entry != NULL, NULL); 401 | 402 | menu_verbose ("Re-loading desktop entry \"%s\"\n", entry->path); 403 | 404 | if (entry->type == DESKTOP_ENTRY_DESKTOP) 405 | { 406 | DesktopEntryDesktop *entry_desktop = (DesktopEntryDesktop *) entry; 407 | 408 | g_object_unref (entry_desktop->appinfo); 409 | entry_desktop->appinfo = NULL; 410 | 411 | g_free (entry_desktop->categories); 412 | entry_desktop->categories = NULL; 413 | } 414 | else if (entry->type == DESKTOP_ENTRY_DIRECTORY) 415 | { 416 | DesktopEntryDirectory *entry_directory = (DesktopEntryDirectory*) entry; 417 | 418 | g_free (entry_directory->name); 419 | entry_directory->name = NULL; 420 | 421 | g_free (entry_directory->comment); 422 | entry_directory->comment = NULL; 423 | 424 | g_object_unref (entry_directory->icon); 425 | entry_directory->icon = NULL; 426 | } 427 | else 428 | g_assert_not_reached (); 429 | 430 | if (desktop_entry_load (entry) < DESKTOP_ENTRY_LOAD_SUCCESS) 431 | { 432 | desktop_entry_unref (entry); 433 | return NULL; 434 | } 435 | 436 | return entry; 437 | } 438 | 439 | DesktopEntry * 440 | desktop_entry_ref (DesktopEntry *entry) 441 | { 442 | g_return_val_if_fail (entry != NULL, NULL); 443 | g_return_val_if_fail (entry->refcount > 0, NULL); 444 | 445 | entry->refcount += 1; 446 | 447 | return entry; 448 | } 449 | 450 | DesktopEntry * 451 | desktop_entry_copy (DesktopEntry *entry) 452 | { 453 | DesktopEntry *retval; 454 | 455 | menu_verbose ("Copying desktop entry \"%s\"\n", 456 | entry->basename); 457 | 458 | if (entry->type == DESKTOP_ENTRY_DESKTOP) 459 | retval = (DesktopEntry*)g_new0 (DesktopEntryDesktop, 1); 460 | else if (entry->type == DESKTOP_ENTRY_DIRECTORY) 461 | retval = (DesktopEntry*)g_new0 (DesktopEntryDirectory, 1); 462 | else 463 | g_assert_not_reached (); 464 | 465 | retval->refcount = 1; 466 | retval->type = entry->type; 467 | retval->path = g_strdup (entry->path); 468 | retval->basename = unix_basename_from_path (retval->path); 469 | 470 | if (retval->type == DESKTOP_ENTRY_DESKTOP) 471 | { 472 | DesktopEntryDesktop *desktop_entry = (DesktopEntryDesktop*) entry; 473 | DesktopEntryDesktop *retval_desktop_entry = (DesktopEntryDesktop*) retval; 474 | int i; 475 | 476 | retval_desktop_entry->appinfo = g_object_ref (desktop_entry->appinfo); 477 | 478 | if (desktop_entry->categories != NULL) 479 | { 480 | i = 0; 481 | for (; desktop_entry->categories[i]; i++); 482 | 483 | retval_desktop_entry->categories = g_new0 (GQuark, i + 1); 484 | 485 | i = 0; 486 | for (; desktop_entry->categories[i]; i++) 487 | retval_desktop_entry->categories[i] = desktop_entry->categories[i]; 488 | } 489 | else 490 | retval_desktop_entry->categories = NULL; 491 | } 492 | else if (entry->type == DESKTOP_ENTRY_DIRECTORY) 493 | { 494 | DesktopEntryDirectory *entry_directory = (DesktopEntryDirectory*)entry; 495 | DesktopEntryDirectory *retval_directory = (DesktopEntryDirectory*)retval; 496 | 497 | retval_directory->name = g_strdup (entry_directory->name); 498 | retval_directory->comment = g_strdup (entry_directory->comment); 499 | retval_directory->icon = g_object_ref (entry_directory->icon); 500 | retval_directory->nodisplay = entry_directory->nodisplay; 501 | retval_directory->hidden = entry_directory->hidden; 502 | retval_directory->showin = entry_directory->showin; 503 | } 504 | 505 | return retval; 506 | } 507 | 508 | void 509 | desktop_entry_unref (DesktopEntry *entry) 510 | { 511 | g_return_if_fail (entry != NULL); 512 | g_return_if_fail (entry->refcount > 0); 513 | 514 | entry->refcount -= 1; 515 | if (entry->refcount != 0) 516 | return; 517 | 518 | g_free (entry->path); 519 | entry->path = NULL; 520 | 521 | if (entry->type == DESKTOP_ENTRY_DESKTOP) 522 | { 523 | DesktopEntryDesktop *desktop_entry = (DesktopEntryDesktop*) entry; 524 | g_free (desktop_entry->categories); 525 | if (desktop_entry->appinfo) 526 | { 527 | g_object_unref (desktop_entry->appinfo); 528 | desktop_entry->appinfo = NULL; 529 | } 530 | } 531 | else if (entry->type == DESKTOP_ENTRY_DIRECTORY) 532 | { 533 | DesktopEntryDirectory *entry_directory = (DesktopEntryDirectory*) entry; 534 | 535 | g_free (entry_directory->name); 536 | entry_directory->name = NULL; 537 | 538 | g_free (entry_directory->comment); 539 | entry_directory->comment = NULL; 540 | 541 | if (entry_directory->icon != NULL) 542 | { 543 | g_object_unref (entry_directory->icon); 544 | entry_directory->icon = NULL; 545 | } 546 | } 547 | else 548 | g_assert_not_reached (); 549 | 550 | g_free (entry); 551 | } 552 | 553 | DesktopEntryType 554 | desktop_entry_get_type (DesktopEntry *entry) 555 | { 556 | return entry->type; 557 | } 558 | 559 | const char * 560 | desktop_entry_get_path (DesktopEntry *entry) 561 | { 562 | return entry->path; 563 | } 564 | 565 | const char * 566 | desktop_entry_get_basename (DesktopEntry *entry) 567 | { 568 | return entry->basename; 569 | } 570 | 571 | const char * 572 | desktop_entry_get_name (DesktopEntry *entry) 573 | { 574 | if (entry->type == DESKTOP_ENTRY_DESKTOP) 575 | { 576 | g_return_val_if_fail (GMENU_IS_DESKTOPAPPINFO (((DesktopEntryDesktop*)entry)->appinfo), NULL); 577 | return g_app_info_get_name (G_APP_INFO (((DesktopEntryDesktop*)entry)->appinfo)); 578 | } 579 | 580 | return ((DesktopEntryDirectory*)entry)->name; 581 | } 582 | 583 | const char * 584 | desktop_entry_get_generic_name (DesktopEntry *entry) 585 | { 586 | if (entry->type == DESKTOP_ENTRY_DESKTOP) 587 | { 588 | g_return_val_if_fail (GMENU_IS_DESKTOPAPPINFO (((DesktopEntryDesktop*)entry)->appinfo), NULL); 589 | return gmenu_desktopappinfo_get_generic_name (((DesktopEntryDesktop*)entry)->appinfo); 590 | } 591 | 592 | return ((DesktopEntryDirectory*)entry)->generic_name; 593 | } 594 | 595 | const char * 596 | desktop_entry_get_comment (DesktopEntry *entry) 597 | { 598 | if (entry->type == DESKTOP_ENTRY_DESKTOP) 599 | { 600 | g_return_val_if_fail (GMENU_IS_DESKTOPAPPINFO (((DesktopEntryDesktop*)entry)->appinfo), NULL); 601 | return g_app_info_get_description (G_APP_INFO (((DesktopEntryDesktop*)entry)->appinfo)); 602 | } 603 | 604 | return ((DesktopEntryDirectory*)entry)->comment; 605 | } 606 | 607 | GIcon * 608 | desktop_entry_get_icon (DesktopEntry *entry) 609 | { 610 | if (entry->type == DESKTOP_ENTRY_DESKTOP) 611 | { 612 | g_return_val_if_fail (GMENU_IS_DESKTOPAPPINFO (((DesktopEntryDesktop*)entry)->appinfo), NULL); 613 | return g_app_info_get_icon (G_APP_INFO (((DesktopEntryDesktop*)entry)->appinfo)); 614 | } 615 | 616 | return ((DesktopEntryDirectory*)entry)->icon; 617 | } 618 | 619 | gboolean 620 | desktop_entry_get_no_display (DesktopEntry *entry) 621 | { 622 | if (entry->type == DESKTOP_ENTRY_DESKTOP) 623 | { 624 | g_return_val_if_fail (GMENU_IS_DESKTOPAPPINFO (((DesktopEntryDesktop*)entry)->appinfo), FALSE); 625 | return gmenu_desktopappinfo_get_nodisplay (((DesktopEntryDesktop*)entry)->appinfo); 626 | } 627 | 628 | return ((DesktopEntryDirectory*)entry)->nodisplay; 629 | } 630 | 631 | gboolean 632 | desktop_entry_get_hidden (DesktopEntry *entry) 633 | { 634 | if (entry->type == DESKTOP_ENTRY_DESKTOP) 635 | { 636 | g_return_val_if_fail (GMENU_IS_DESKTOPAPPINFO (((DesktopEntryDesktop*)entry)->appinfo), FALSE); 637 | return gmenu_desktopappinfo_get_is_hidden (((DesktopEntryDesktop*)entry)->appinfo); 638 | } 639 | 640 | return ((DesktopEntryDirectory*)entry)->hidden; 641 | } 642 | 643 | gboolean 644 | desktop_entry_get_show_in (DesktopEntry *entry) 645 | { 646 | if (entry->type == DESKTOP_ENTRY_DESKTOP) 647 | { 648 | const char *current_desktop = get_current_desktop (); 649 | 650 | if (current_desktop == NULL) 651 | return TRUE; 652 | else { 653 | return ((DesktopEntryDesktop *)entry)->showin; 654 | } 655 | } 656 | return ((DesktopEntryDirectory*)entry)->showin; 657 | } 658 | 659 | const char * 660 | desktop_entry_get_id (DesktopEntry *entry) 661 | { 662 | if (entry->type == DESKTOP_ENTRY_DESKTOP) 663 | { 664 | g_return_val_if_fail (GMENU_IS_DESKTOPAPPINFO (((DesktopEntryDesktop*)entry)->appinfo), NULL); 665 | return g_app_info_get_id (G_APP_INFO (((DesktopEntryDesktop*)entry)->appinfo)); 666 | } 667 | 668 | // this only applies to non-desktop entries 669 | return entry->basename; 670 | } 671 | 672 | GMenuDesktopAppInfo * 673 | desktop_entry_get_app_info (DesktopEntry *entry) 674 | { 675 | g_return_val_if_fail (entry->type == DESKTOP_ENTRY_DESKTOP, NULL); 676 | return ((DesktopEntryDesktop*)entry)->appinfo; 677 | } 678 | 679 | gboolean 680 | desktop_entry_has_categories (DesktopEntry *entry) 681 | { 682 | DesktopEntryDesktop *desktop_entry; 683 | if (entry->type != DESKTOP_ENTRY_DESKTOP) 684 | return FALSE; 685 | 686 | desktop_entry = (DesktopEntryDesktop*) entry; 687 | return (desktop_entry->categories != NULL && desktop_entry->categories[0] != 0); 688 | } 689 | 690 | gboolean 691 | desktop_entry_has_category (DesktopEntry *entry, 692 | const char *category) 693 | { 694 | GQuark quark; 695 | int i; 696 | DesktopEntryDesktop *desktop_entry; 697 | 698 | if (entry->type != DESKTOP_ENTRY_DESKTOP) 699 | return FALSE; 700 | 701 | desktop_entry = (DesktopEntryDesktop*) entry; 702 | 703 | if (desktop_entry->categories == NULL) 704 | return FALSE; 705 | 706 | if (!(quark = g_quark_try_string (category))) 707 | return FALSE; 708 | 709 | for (i = 0; desktop_entry->categories[i]; i++) 710 | { 711 | if (quark == desktop_entry->categories[i]) 712 | return TRUE; 713 | } 714 | 715 | return FALSE; 716 | } 717 | 718 | /* 719 | * Entry sets 720 | */ 721 | 722 | DesktopEntrySet * 723 | desktop_entry_set_new (void) 724 | { 725 | DesktopEntrySet *set; 726 | 727 | set = g_new0 (DesktopEntrySet, 1); 728 | set->refcount = 1; 729 | 730 | menu_verbose (" New entry set %p\n", set); 731 | 732 | return set; 733 | } 734 | 735 | DesktopEntrySet * 736 | desktop_entry_set_ref (DesktopEntrySet *set) 737 | { 738 | g_return_val_if_fail (set != NULL, NULL); 739 | g_return_val_if_fail (set->refcount > 0, NULL); 740 | 741 | set->refcount += 1; 742 | 743 | return set; 744 | } 745 | 746 | void 747 | desktop_entry_set_unref (DesktopEntrySet *set) 748 | { 749 | g_return_if_fail (set != NULL); 750 | g_return_if_fail (set->refcount > 0); 751 | 752 | set->refcount -= 1; 753 | if (set->refcount == 0) 754 | { 755 | menu_verbose (" Deleting entry set %p\n", set); 756 | 757 | if (set->hash) 758 | g_hash_table_destroy (set->hash); 759 | set->hash = NULL; 760 | 761 | g_free (set); 762 | } 763 | } 764 | 765 | void 766 | desktop_entry_set_add_entry (DesktopEntrySet *set, 767 | DesktopEntry *entry, 768 | const char *file_id) 769 | { 770 | menu_verbose (" Adding to set %p entry %s\n", set, file_id); 771 | 772 | if (set->hash == NULL) 773 | { 774 | set->hash = g_hash_table_new_full (g_str_hash, 775 | g_str_equal, 776 | g_free, 777 | (GDestroyNotify) desktop_entry_unref); 778 | } 779 | 780 | g_hash_table_replace (set->hash, 781 | g_strdup (file_id), 782 | desktop_entry_ref (entry)); 783 | } 784 | 785 | DesktopEntry * 786 | desktop_entry_set_lookup (DesktopEntrySet *set, 787 | const char *file_id) 788 | { 789 | if (set->hash == NULL) 790 | return NULL; 791 | 792 | return g_hash_table_lookup (set->hash, file_id); 793 | } 794 | 795 | typedef struct 796 | { 797 | DesktopEntrySetForeachFunc func; 798 | gpointer user_data; 799 | } EntryHashForeachData; 800 | 801 | static void 802 | entry_hash_foreach (const char *file_id, 803 | DesktopEntry *entry, 804 | EntryHashForeachData *fd) 805 | { 806 | fd->func (file_id, entry, fd->user_data); 807 | } 808 | 809 | void 810 | desktop_entry_set_foreach (DesktopEntrySet *set, 811 | DesktopEntrySetForeachFunc func, 812 | gpointer user_data) 813 | { 814 | g_return_if_fail (set != NULL); 815 | g_return_if_fail (func != NULL); 816 | 817 | if (set->hash != NULL) 818 | { 819 | EntryHashForeachData fd; 820 | 821 | fd.func = func; 822 | fd.user_data = user_data; 823 | 824 | g_hash_table_foreach (set->hash, 825 | (GHFunc) entry_hash_foreach, 826 | &fd); 827 | } 828 | } 829 | 830 | static void 831 | desktop_entry_set_clear (DesktopEntrySet *set) 832 | { 833 | menu_verbose (" Clearing set %p\n", set); 834 | 835 | if (set->hash != NULL) 836 | { 837 | g_hash_table_destroy (set->hash); 838 | set->hash = NULL; 839 | } 840 | } 841 | 842 | int 843 | desktop_entry_set_get_count (DesktopEntrySet *set) 844 | { 845 | if (set->hash == NULL) 846 | return 0; 847 | 848 | return g_hash_table_size (set->hash); 849 | } 850 | 851 | static void 852 | union_foreach (const char *file_id, 853 | DesktopEntry *entry, 854 | DesktopEntrySet *set) 855 | { 856 | /* we are iterating over "with" adding anything not 857 | * already in "set". We unconditionally overwrite 858 | * the stuff in "set" because we can assume 859 | * two entries with the same name are equivalent. 860 | */ 861 | desktop_entry_set_add_entry (set, entry, file_id); 862 | } 863 | 864 | void 865 | desktop_entry_set_union (DesktopEntrySet *set, 866 | DesktopEntrySet *with) 867 | { 868 | menu_verbose (" Union of %p and %p\n", set, with); 869 | 870 | if (desktop_entry_set_get_count (with) == 0) 871 | return; /* A fast simple case */ 872 | 873 | g_hash_table_foreach (with->hash, 874 | (GHFunc) union_foreach, 875 | set); 876 | } 877 | 878 | typedef struct 879 | { 880 | DesktopEntrySet *set; 881 | DesktopEntrySet *with; 882 | } IntersectData; 883 | 884 | static gboolean 885 | intersect_foreach_remove (const char *file_id, 886 | DesktopEntry *entry, 887 | IntersectData *id) 888 | { 889 | /* Remove everything in "set" which is not in "with" */ 890 | 891 | if (g_hash_table_lookup (id->with->hash, file_id) != NULL) 892 | return FALSE; 893 | 894 | menu_verbose (" Removing from %p entry %s\n", id->set, file_id); 895 | 896 | return TRUE; /* return TRUE to remove */ 897 | } 898 | 899 | void 900 | desktop_entry_set_intersection (DesktopEntrySet *set, 901 | DesktopEntrySet *with) 902 | { 903 | IntersectData id; 904 | 905 | menu_verbose (" Intersection of %p and %p\n", set, with); 906 | 907 | if (desktop_entry_set_get_count (set) == 0 || 908 | desktop_entry_set_get_count (with) == 0) 909 | { 910 | /* A fast simple case */ 911 | desktop_entry_set_clear (set); 912 | return; 913 | } 914 | 915 | id.set = set; 916 | id.with = with; 917 | 918 | g_hash_table_foreach_remove (set->hash, 919 | (GHRFunc) intersect_foreach_remove, 920 | &id); 921 | } 922 | 923 | typedef struct 924 | { 925 | DesktopEntrySet *set; 926 | DesktopEntrySet *other; 927 | } SubtractData; 928 | 929 | static gboolean 930 | subtract_foreach_remove (const char *file_id, 931 | DesktopEntry *entry, 932 | SubtractData *sd) 933 | { 934 | /* Remove everything in "set" which is not in "other" */ 935 | 936 | if (g_hash_table_lookup (sd->other->hash, file_id) == NULL) 937 | return FALSE; 938 | 939 | menu_verbose (" Removing from %p entry %s\n", sd->set, file_id); 940 | 941 | return TRUE; /* return TRUE to remove */ 942 | } 943 | 944 | void 945 | desktop_entry_set_subtract (DesktopEntrySet *set, 946 | DesktopEntrySet *other) 947 | { 948 | SubtractData sd; 949 | 950 | menu_verbose (" Subtract from %p set %p\n", set, other); 951 | 952 | if (desktop_entry_set_get_count (set) == 0 || 953 | desktop_entry_set_get_count (other) == 0) 954 | return; /* A fast simple case */ 955 | 956 | sd.set = set; 957 | sd.other = other; 958 | 959 | g_hash_table_foreach_remove (set->hash, 960 | (GHRFunc) subtract_foreach_remove, 961 | &sd); 962 | } 963 | 964 | void 965 | desktop_entry_set_swap_contents (DesktopEntrySet *a, 966 | DesktopEntrySet *b) 967 | { 968 | GHashTable *tmp; 969 | 970 | menu_verbose (" Swap contents of %p and %p\n", a, b); 971 | 972 | tmp = a->hash; 973 | a->hash = b->hash; 974 | b->hash = tmp; 975 | } 976 | -------------------------------------------------------------------------------- /libmenu/gmenu-desktopappinfo.c: -------------------------------------------------------------------------------- 1 | /* 2 | * gmenu-desktopappinfo.c 3 | * Copyright (C) 2020 Lars Mueller 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library; if not, write to the 17 | * Free Software Foundation, 51 Franklin Street, Fifth Floor, 18 | * Boston, MA 02110, USA. 19 | */ 20 | 21 | #include "gmenu-desktopappinfo.h" 22 | #include 23 | 24 | struct _GMenuDesktopAppInfo 25 | { 26 | GObject parent_instance; 27 | 28 | GDesktopAppInfo *super_appinfo; 29 | gchar *desktop_id; 30 | gboolean is_flatpak; 31 | gchar *startup_wm_class; 32 | gchar *flatpak_app_id; 33 | }; 34 | 35 | // This function sets desktop id and startup wm class and adds ":flatpak" for flatpak apps 36 | static void 37 | update_app_data (GMenuDesktopAppInfo *info) 38 | { 39 | gchar *exec = NULL; 40 | gchar *id = NULL; 41 | gchar *startup_wm_class = NULL; 42 | 43 | g_free (info->desktop_id); 44 | info->desktop_id = NULL; 45 | 46 | g_free (info->startup_wm_class); 47 | info->startup_wm_class = NULL; 48 | 49 | g_free (info->flatpak_app_id); 50 | info->flatpak_app_id = NULL; 51 | 52 | if (info->super_appinfo != NULL) 53 | { 54 | exec = (gchar *) g_app_info_get_executable (G_APP_INFO (info->super_appinfo)); 55 | id = (gchar *) g_app_info_get_id (G_APP_INFO (info->super_appinfo)); 56 | startup_wm_class = (gchar *) g_desktop_app_info_get_startup_wm_class (info->super_appinfo); 57 | 58 | if (exec && (strstr (exec, "flatpak") || strstr (exec, "bwrap"))) 59 | { 60 | info->desktop_id = g_strconcat (id, GMENU_DESKTOPAPPINFO_FLATPAK_SUFFIX, NULL); 61 | if (startup_wm_class) 62 | { 63 | info->startup_wm_class = g_strconcat (startup_wm_class, GMENU_DESKTOPAPPINFO_FLATPAK_SUFFIX, NULL); 64 | } 65 | 66 | // if (g_desktop_app_info_has_key (info->super_appinfo, "X-Flatpak")) 67 | // { 68 | info->flatpak_app_id = g_desktop_app_info_get_string (info->super_appinfo, "X-Flatpak"); 69 | // } 70 | 71 | info->is_flatpak = TRUE; 72 | } else { 73 | info->desktop_id = g_strdup (id); 74 | info->is_flatpak = FALSE; 75 | info->startup_wm_class = g_strdup (startup_wm_class); 76 | } 77 | } 78 | } 79 | 80 | static GAppInfo * 81 | gmenu_desktopappinfo_dup (GAppInfo *appinfo) 82 | { 83 | g_return_val_if_fail (GMENU_IS_DESKTOPAPPINFO (appinfo), NULL); 84 | 85 | GMenuDesktopAppInfo *info = GMENU_DESKTOPAPPINFO (appinfo); 86 | GMenuDesktopAppInfo *new_info; 87 | 88 | new_info = g_object_new (GMENU_TYPE_DESKTOPAPPINFO, NULL); 89 | 90 | new_info->super_appinfo = G_DESKTOP_APP_INFO (g_app_info_dup(G_APP_INFO(info->super_appinfo))); 91 | 92 | update_app_data (new_info); 93 | 94 | return G_APP_INFO (new_info); 95 | } 96 | 97 | static gboolean 98 | gmenu_desktopappinfo_equal (GAppInfo *appinfo1, 99 | GAppInfo *appinfo2) 100 | { 101 | GMenuDesktopAppInfo *info1 = GMENU_DESKTOPAPPINFO (appinfo1); 102 | GMenuDesktopAppInfo *info2 = GMENU_DESKTOPAPPINFO (appinfo2); 103 | 104 | if (info1->desktop_id == NULL || 105 | info2->desktop_id == NULL) 106 | return info1 == info2; 107 | 108 | return strcmp (info1->desktop_id, info2->desktop_id) == 0; 109 | } 110 | 111 | static const char * 112 | gmenu_desktopappinfo_get_id (GAppInfo *appinfo) 113 | { 114 | g_return_val_if_fail (GMENU_IS_DESKTOPAPPINFO (appinfo), NULL); 115 | 116 | GMenuDesktopAppInfo *info = GMENU_DESKTOPAPPINFO (appinfo); 117 | 118 | return info->desktop_id; 119 | } 120 | 121 | static const char * 122 | gmenu_desktopappinfo_get_name (GAppInfo *appinfo) 123 | { 124 | g_return_val_if_fail (GMENU_IS_DESKTOPAPPINFO (appinfo), NULL); 125 | 126 | GMenuDesktopAppInfo *info = GMENU_DESKTOPAPPINFO (appinfo); 127 | return g_app_info_get_name (G_APP_INFO(info->super_appinfo)); 128 | } 129 | 130 | static const char * 131 | gmenu_desktopappinfo_get_description (GAppInfo *appinfo) 132 | { 133 | g_return_val_if_fail (GMENU_IS_DESKTOPAPPINFO (appinfo), NULL); 134 | 135 | GMenuDesktopAppInfo *info = GMENU_DESKTOPAPPINFO (appinfo); 136 | return g_app_info_get_description (G_APP_INFO(info->super_appinfo)); 137 | } 138 | 139 | static const char * 140 | gmenu_desktopappinfo_get_executable (GAppInfo *appinfo) 141 | { 142 | g_return_val_if_fail (GMENU_IS_DESKTOPAPPINFO (appinfo), NULL); 143 | 144 | GMenuDesktopAppInfo *info = GMENU_DESKTOPAPPINFO (appinfo); 145 | return g_app_info_get_executable (G_APP_INFO(info->super_appinfo)); 146 | } 147 | 148 | static GIcon * 149 | gmenu_desktopappinfo_get_icon (GAppInfo *appinfo) 150 | { 151 | g_return_val_if_fail (GMENU_IS_DESKTOPAPPINFO (appinfo), NULL); 152 | 153 | GMenuDesktopAppInfo *info = GMENU_DESKTOPAPPINFO (appinfo); 154 | return g_app_info_get_icon (G_APP_INFO(info->super_appinfo)); 155 | } 156 | 157 | static gboolean 158 | gmenu_desktopappinfo_launch (GAppInfo *appinfo, GList *files, GAppLaunchContext *launch_context, GError **error) 159 | { 160 | g_return_val_if_fail (GMENU_IS_DESKTOPAPPINFO (appinfo), FALSE); 161 | 162 | GMenuDesktopAppInfo *info = GMENU_DESKTOPAPPINFO (appinfo); 163 | return g_app_info_launch (G_APP_INFO(info->super_appinfo), files, launch_context, error); 164 | } 165 | 166 | static gboolean 167 | gmenu_desktopappinfo_supports_uris (GAppInfo *appinfo) 168 | { 169 | g_return_val_if_fail (GMENU_IS_DESKTOPAPPINFO (appinfo), FALSE); 170 | 171 | GMenuDesktopAppInfo *info = GMENU_DESKTOPAPPINFO (appinfo); 172 | return g_app_info_supports_uris (G_APP_INFO(info->super_appinfo)); 173 | } 174 | 175 | static gboolean 176 | gmenu_desktopappinfo_supports_files (GAppInfo *appinfo) 177 | { 178 | g_return_val_if_fail (GMENU_IS_DESKTOPAPPINFO (appinfo), FALSE); 179 | 180 | GMenuDesktopAppInfo *info = GMENU_DESKTOPAPPINFO (appinfo); 181 | return g_app_info_supports_files (G_APP_INFO(info->super_appinfo)); 182 | } 183 | 184 | static gboolean 185 | gmenu_desktopappinfo_launch_uris (GAppInfo *appinfo, 186 | GList *uris, 187 | GAppLaunchContext *launch_context, 188 | GError **error) 189 | { 190 | g_return_val_if_fail (GMENU_IS_DESKTOPAPPINFO (appinfo), FALSE); 191 | 192 | GMenuDesktopAppInfo *info = GMENU_DESKTOPAPPINFO (appinfo); 193 | return g_app_info_launch_uris (G_APP_INFO(info->super_appinfo), uris, launch_context, error); 194 | } 195 | 196 | static gboolean 197 | gmenu_desktopappinfo_should_show (GAppInfo *appinfo) 198 | { 199 | g_return_val_if_fail (GMENU_IS_DESKTOPAPPINFO (appinfo), FALSE); 200 | 201 | GMenuDesktopAppInfo *info = GMENU_DESKTOPAPPINFO (appinfo); 202 | return g_app_info_should_show (G_APP_INFO(info->super_appinfo)); 203 | } 204 | 205 | static gboolean 206 | gmenu_desktopappinfo_set_as_default_for_type (GAppInfo *appinfo, 207 | const char *content_type, 208 | GError **error) 209 | { 210 | g_return_val_if_fail (GMENU_IS_DESKTOPAPPINFO (appinfo), FALSE); 211 | 212 | GMenuDesktopAppInfo *info = GMENU_DESKTOPAPPINFO (appinfo); 213 | return g_app_info_set_as_default_for_type (G_APP_INFO(info->super_appinfo), content_type, error); 214 | } 215 | 216 | static gboolean 217 | gmenu_desktopappinfo_set_as_default_for_extension (GAppInfo *appinfo, 218 | const char *extension, 219 | GError **error) 220 | { 221 | g_return_val_if_fail (GMENU_IS_DESKTOPAPPINFO (appinfo), FALSE); 222 | 223 | GMenuDesktopAppInfo *info = GMENU_DESKTOPAPPINFO (appinfo); 224 | return g_app_info_set_as_default_for_extension (G_APP_INFO(info->super_appinfo), extension, error); 225 | } 226 | 227 | static gboolean 228 | gmenu_desktopappinfo_add_supports_type (GAppInfo *appinfo, 229 | const char *content_type, 230 | GError **error) 231 | { 232 | g_return_val_if_fail (GMENU_IS_DESKTOPAPPINFO (appinfo), FALSE); 233 | 234 | GMenuDesktopAppInfo *info = GMENU_DESKTOPAPPINFO (appinfo); 235 | return g_app_info_add_supports_type (G_APP_INFO(info->super_appinfo), content_type, error); 236 | } 237 | 238 | static gboolean 239 | gmenu_desktopappinfo_can_remove_supports_type (GAppInfo *appinfo) 240 | { 241 | g_return_val_if_fail (GMENU_IS_DESKTOPAPPINFO (appinfo), FALSE); 242 | 243 | GMenuDesktopAppInfo *info = GMENU_DESKTOPAPPINFO (appinfo); 244 | return g_app_info_can_remove_supports_type (G_APP_INFO(info->super_appinfo)); 245 | } 246 | 247 | static gboolean 248 | gmenu_desktopappinfo_remove_supports_type (GAppInfo *appinfo, 249 | const char *content_type, 250 | GError **error) 251 | { 252 | g_return_val_if_fail (GMENU_IS_DESKTOPAPPINFO (appinfo), FALSE); 253 | 254 | GMenuDesktopAppInfo *info = GMENU_DESKTOPAPPINFO (appinfo); 255 | return g_app_info_remove_supports_type (G_APP_INFO(info->super_appinfo), content_type, error); 256 | } 257 | 258 | static gboolean 259 | gmenu_desktopappinfo_can_delete (GAppInfo *appinfo) 260 | { 261 | g_return_val_if_fail (GMENU_IS_DESKTOPAPPINFO (appinfo), FALSE); 262 | 263 | GMenuDesktopAppInfo *info = GMENU_DESKTOPAPPINFO (appinfo); 264 | return g_app_info_can_delete (G_APP_INFO(info->super_appinfo)); 265 | } 266 | 267 | static gboolean 268 | gmenu_desktopappinfo_delete (GAppInfo *appinfo) 269 | { 270 | g_return_val_if_fail (GMENU_IS_DESKTOPAPPINFO (appinfo), FALSE); 271 | 272 | GMenuDesktopAppInfo *info = GMENU_DESKTOPAPPINFO (appinfo); 273 | return g_app_info_delete (G_APP_INFO(info->super_appinfo)); 274 | } 275 | 276 | static const char * 277 | gmenu_desktopappinfo_get_commandline (GAppInfo *appinfo) 278 | { 279 | g_return_val_if_fail (GMENU_IS_DESKTOPAPPINFO (appinfo), NULL); 280 | 281 | GMenuDesktopAppInfo *info = GMENU_DESKTOPAPPINFO (appinfo); 282 | return g_app_info_get_commandline (G_APP_INFO(info->super_appinfo)); 283 | } 284 | 285 | static const char * 286 | gmenu_desktopappinfo_get_display_name (GAppInfo *appinfo) 287 | { 288 | g_return_val_if_fail (GMENU_IS_DESKTOPAPPINFO (appinfo), NULL); 289 | 290 | GMenuDesktopAppInfo *info = GMENU_DESKTOPAPPINFO (appinfo); 291 | return g_app_info_get_display_name (G_APP_INFO(info->super_appinfo)); 292 | } 293 | 294 | static gboolean 295 | gmenu_desktopappinfo_set_as_last_used_for_type (GAppInfo *appinfo, 296 | const char *content_type, 297 | GError **error) 298 | { 299 | g_return_val_if_fail (GMENU_IS_DESKTOPAPPINFO (appinfo), FALSE); 300 | 301 | GMenuDesktopAppInfo *info = GMENU_DESKTOPAPPINFO (appinfo); 302 | return g_app_info_set_as_last_used_for_type (G_APP_INFO(info->super_appinfo), content_type, error); 303 | } 304 | 305 | static const char ** 306 | gmenu_desktopappinfo_get_supported_types (GAppInfo *appinfo) 307 | { 308 | g_return_val_if_fail (GMENU_IS_DESKTOPAPPINFO (appinfo), NULL); 309 | 310 | GMenuDesktopAppInfo *info = GMENU_DESKTOPAPPINFO (appinfo); 311 | return g_app_info_get_supported_types (G_APP_INFO(info->super_appinfo)); 312 | } 313 | 314 | static void 315 | gmenu_desktopappinfo_interface_init (GAppInfoIface *iface) 316 | { 317 | iface->dup = gmenu_desktopappinfo_dup; 318 | iface->equal = gmenu_desktopappinfo_equal; 319 | iface->get_id = gmenu_desktopappinfo_get_id; 320 | iface->get_name = gmenu_desktopappinfo_get_name; 321 | iface->get_description = gmenu_desktopappinfo_get_description; 322 | iface->get_executable = gmenu_desktopappinfo_get_executable; 323 | iface->get_icon = gmenu_desktopappinfo_get_icon; 324 | iface->launch = gmenu_desktopappinfo_launch; 325 | iface->supports_uris = gmenu_desktopappinfo_supports_uris; 326 | iface->supports_files = gmenu_desktopappinfo_supports_files; 327 | iface->launch_uris = gmenu_desktopappinfo_launch_uris; 328 | iface->should_show = gmenu_desktopappinfo_should_show; 329 | iface->set_as_default_for_type = gmenu_desktopappinfo_set_as_default_for_type; 330 | iface->set_as_default_for_extension = gmenu_desktopappinfo_set_as_default_for_extension; 331 | iface->add_supports_type = gmenu_desktopappinfo_add_supports_type; 332 | iface->can_remove_supports_type = gmenu_desktopappinfo_can_remove_supports_type; 333 | iface->remove_supports_type = gmenu_desktopappinfo_remove_supports_type; 334 | iface->can_delete = gmenu_desktopappinfo_can_delete; 335 | iface->do_delete = gmenu_desktopappinfo_delete; 336 | iface->get_commandline = gmenu_desktopappinfo_get_commandline; 337 | iface->get_display_name = gmenu_desktopappinfo_get_display_name; 338 | iface->set_as_last_used_for_type = gmenu_desktopappinfo_set_as_last_used_for_type; 339 | iface->get_supported_types = gmenu_desktopappinfo_get_supported_types; 340 | } 341 | 342 | G_DEFINE_TYPE_WITH_CODE (GMenuDesktopAppInfo, gmenu_desktopappinfo, G_TYPE_OBJECT, 343 | G_IMPLEMENT_INTERFACE (G_TYPE_APP_INFO, gmenu_desktopappinfo_interface_init)) 344 | 345 | static void 346 | gmenu_desktopappinfo_init (GMenuDesktopAppInfo *info) 347 | { 348 | info->super_appinfo = NULL; 349 | info->desktop_id = NULL; 350 | info->flatpak_app_id = NULL; 351 | info->startup_wm_class = NULL; 352 | } 353 | 354 | static void 355 | gmenu_desktopappinfo_finalize (GObject *object) 356 | { 357 | /* TODO: Add deinitalization code here */ 358 | 359 | GMenuDesktopAppInfo *info = GMENU_DESKTOPAPPINFO (object); 360 | 361 | g_free (info->desktop_id); 362 | g_free (info->startup_wm_class); 363 | g_free (info->flatpak_app_id); 364 | 365 | if (info->super_appinfo) 366 | g_object_unref (info->super_appinfo); 367 | } 368 | 369 | static void 370 | gmenu_desktopappinfo_class_init (GMenuDesktopAppInfoClass *klass) 371 | { 372 | GObjectClass* object_class = G_OBJECT_CLASS (klass); 373 | 374 | object_class->finalize = gmenu_desktopappinfo_finalize; 375 | } 376 | 377 | /** 378 | * gmenu_desktopappinfo_new: 379 | * @desktop_id: the desktop file id 380 | * 381 | * This is currently unused in Cinnamon and does not make sense here 382 | * because the desktop id as used here is not necessarily unique 383 | * 384 | * Returns: (nullable): %NULL 385 | */ 386 | GMenuDesktopAppInfo* 387 | gmenu_desktopappinfo_new (const char *desktop_id) 388 | { 389 | return NULL; 390 | } 391 | 392 | /** 393 | * gmenu_desktopappinfo_new_from_filename: 394 | * @filename: (type filename): the path of a desktop file, in the GLib 395 | * filename encoding 396 | * 397 | * Creates a new #GMenuDesktopAppInfo. 398 | * 399 | * Returns: (nullable): a new #GMenuDesktopAppInfo or %NULL on error. 400 | **/ 401 | GMenuDesktopAppInfo* 402 | gmenu_desktopappinfo_new_from_filename (gchar *filename) 403 | { 404 | GMenuDesktopAppInfo *info = NULL; 405 | 406 | info = g_object_new (GMENU_TYPE_DESKTOPAPPINFO, NULL); 407 | info->super_appinfo = g_desktop_app_info_new_from_filename (filename); 408 | 409 | if (info->super_appinfo) 410 | { 411 | update_app_data (info); 412 | return info; 413 | } else { 414 | g_object_unref (info); 415 | return NULL; 416 | } 417 | } 418 | 419 | /** 420 | * gmenu_desktopappinfo_new_from_keyfile: 421 | * @key_file: an opened #GKeyFile 422 | * 423 | * Creates a new #GMenuDesktopAppInfo. 424 | * 425 | * Returns: (nullable): a new #GMenuDesktopAppInfo or %NULL on error. 426 | **/ 427 | GMenuDesktopAppInfo* 428 | gmenu_desktopappinfo_new_from_keyfile (GKeyFile *keyfile) 429 | { 430 | GMenuDesktopAppInfo *info = NULL; 431 | 432 | info = g_object_new (GMENU_TYPE_DESKTOPAPPINFO, NULL); 433 | info->super_appinfo = g_desktop_app_info_new_from_keyfile (keyfile); 434 | 435 | if (info->super_appinfo) 436 | { 437 | update_app_data (info); 438 | return info; 439 | } else { 440 | g_object_unref (info); 441 | return NULL; 442 | } 443 | } 444 | 445 | /** 446 | * gmenu_desktopappinfo_get_filename: 447 | * @appinfo: a #MenuGDesktopAppInfo 448 | * 449 | * When @info was created from a known filename, return it. In some 450 | * situations such as the #GMenuDesktopAppInfo returned from 451 | * gmenu_desktopappinfo_new_from_keyfile(), this function will return %NULL. 452 | * 453 | * Returns: (type filename): The full path to the file for @info, 454 | * or %NULL if not known. 455 | * Since: 2.24 456 | */ 457 | const char * gmenu_desktopappinfo_get_filename (GMenuDesktopAppInfo *appinfo) 458 | { 459 | g_return_val_if_fail (GMENU_IS_DESKTOPAPPINFO (appinfo), NULL); 460 | return g_desktop_app_info_get_filename (appinfo->super_appinfo); 461 | } 462 | 463 | /** 464 | * gmenu_desktopappinfo_get_generic_name: 465 | * @appinfo: a #MenuGDesktopAppInfo 466 | * 467 | * Gets the generic name from the destkop file. 468 | * 469 | * Returns: The value of the GenericName key 470 | */ 471 | const char * gmenu_desktopappinfo_get_generic_name (GMenuDesktopAppInfo *appinfo) 472 | { 473 | g_return_val_if_fail (GMENU_IS_DESKTOPAPPINFO (appinfo), NULL); 474 | return g_desktop_app_info_get_generic_name (appinfo->super_appinfo); 475 | } 476 | 477 | /** 478 | * gmenu_desktopappinfo_get_categories: 479 | * @appinfo: a #GMenuDesktopAppInfo 480 | * 481 | * Gets the categories from the desktop file. 482 | * 483 | * Returns: The unparsed Categories key from the desktop file; 484 | * i.e. no attempt is made to split it by ';' or validate it. 485 | */ 486 | const char * gmenu_desktopappinfo_get_categories (GMenuDesktopAppInfo *appinfo) 487 | { 488 | g_return_val_if_fail (GMENU_IS_DESKTOPAPPINFO (appinfo), NULL); 489 | return g_desktop_app_info_get_categories (appinfo->super_appinfo); 490 | } 491 | 492 | /** 493 | * gmenu_desktopappinfo_get_keywords: 494 | * @appinfo: a #GMenuDesktopAppInfo 495 | * 496 | * Gets the keywords from the desktop file. 497 | * 498 | * Returns: (transfer none): The value of the Keywords key 499 | */ 500 | const char * const *gmenu_desktopappinfo_get_keywords (GMenuDesktopAppInfo *appinfo) 501 | { 502 | g_return_val_if_fail (GMENU_IS_DESKTOPAPPINFO (appinfo), NULL); 503 | return g_desktop_app_info_get_keywords (appinfo->super_appinfo); 504 | } 505 | 506 | /** 507 | * gmenu_desktopappinfo_get_nodisplay: 508 | * @appinfo: a #GMenuDesktopAppInfo 509 | * 510 | * Gets the value of the NoDisplay key, which helps determine if the 511 | * application info should be shown in menus. See 512 | * #G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY and g_app_info_should_show(). 513 | * 514 | * Returns: The value of the NoDisplay key 515 | */ 516 | gboolean gmenu_desktopappinfo_get_nodisplay (GMenuDesktopAppInfo *appinfo) 517 | { 518 | g_return_val_if_fail (GMENU_IS_DESKTOPAPPINFO (appinfo), FALSE); 519 | return g_desktop_app_info_get_nodisplay (appinfo->super_appinfo); 520 | } 521 | 522 | /** 523 | * gmenu_desktopappinfo_get_show_in: 524 | * @appinfo: a #GMenuDesktopAppInfo 525 | * @desktop_env: (nullable): a string specifying a desktop name 526 | * 527 | * Checks if the application info should be shown in menus that list available 528 | * applications for a specific name of the desktop, based on the 529 | * `OnlyShowIn` and `NotShowIn` keys. 530 | * 531 | * @desktop_env should typically be given as %NULL, in which case the 532 | * `XDG_CURRENT_DESKTOP` environment variable is consulted. If you want 533 | * to override the default mechanism then you may specify @desktop_env, 534 | * but this is not recommended. 535 | * 536 | * Note that g_app_info_should_show() for @info will include this check (with 537 | * %NULL for @desktop_env) as well as additional checks. 538 | * 539 | * Returns: %TRUE if the @info should be shown in @desktop_env according to the 540 | * `OnlyShowIn` and `NotShowIn` keys, %FALSE 541 | * otherwise. 542 | */ 543 | gboolean gmenu_desktopappinfo_get_show_in (GMenuDesktopAppInfo *appinfo, const gchar *desktop_env) 544 | { 545 | g_return_val_if_fail (GMENU_IS_DESKTOPAPPINFO (appinfo), FALSE); 546 | return g_desktop_app_info_get_show_in (appinfo->super_appinfo, desktop_env); 547 | } 548 | 549 | /** 550 | * gmenu_desktopappinfo_get_startup_wm_class: 551 | * @appinfo: a #GMenuDesktopAppInfo that supports startup notify 552 | * 553 | * Retrieves the StartupWMClass field from @info. This represents the 554 | * WM_CLASS property of the main window of the application, if launched 555 | * through @info. 556 | * 557 | * Note: The returned value contain the suffix ":flatpak" if @info specifies a flatpak app 558 | * and if the desktop file has a StartupWMClass 559 | * 560 | * Returns: (transfer none): the startup WM class, or %NULL if none is set 561 | * in the desktop file. 562 | */ 563 | const char * gmenu_desktopappinfo_get_startup_wm_class (GMenuDesktopAppInfo *appinfo) 564 | { 565 | g_return_val_if_fail (GMENU_IS_DESKTOPAPPINFO (appinfo), NULL); 566 | 567 | return appinfo->startup_wm_class; 568 | } 569 | 570 | /** 571 | * gmenu_desktopappinfo_get_is_hidden: 572 | * @appinfo: a #GMenuDesktopAppInfo. 573 | * 574 | * A desktop file is hidden if the Hidden key in it is 575 | * set to True. 576 | * 577 | * Returns: %TRUE if hidden, %FALSE otherwise. 578 | **/ 579 | gboolean gmenu_desktopappinfo_get_is_hidden (GMenuDesktopAppInfo *appinfo) 580 | { 581 | g_return_val_if_fail (GMENU_IS_DESKTOPAPPINFO (appinfo), FALSE); 582 | return g_desktop_app_info_get_is_hidden (appinfo->super_appinfo); 583 | } 584 | 585 | /** 586 | * gmenu_desktopappinfo_has_key: 587 | * @appinfo: a #GMenuDesktopAppInfo 588 | * @key: the key to look up 589 | * 590 | * Returns whether @key exists in the "Desktop Entry" group 591 | * of the keyfile backing @info. 592 | * 593 | * Returns: %TRUE if the @key exists 594 | */ 595 | gboolean gmenu_desktopappinfo_has_key (GMenuDesktopAppInfo *appinfo, const char *key) 596 | { 597 | g_return_val_if_fail (GMENU_IS_DESKTOPAPPINFO (appinfo), FALSE); 598 | return g_desktop_app_info_has_key (appinfo->super_appinfo, key); 599 | } 600 | 601 | /** 602 | * gmenu_desktopappinfo_get_string: 603 | * @appinfo: a #GMenuDesktopAppInfo 604 | * @key: the key to look up 605 | * 606 | * Looks up a string value in the keyfile backing @info. 607 | * 608 | * The @key is looked up in the "Desktop Entry" group. 609 | * 610 | * Returns: a newly allocated string, or %NULL if the key 611 | * is not found 612 | */ 613 | char * gmenu_desktopappinfo_get_string (GMenuDesktopAppInfo *appinfo, const char *key) 614 | { 615 | g_return_val_if_fail (GMENU_IS_DESKTOPAPPINFO (appinfo), NULL); 616 | return g_desktop_app_info_get_string (appinfo->super_appinfo, key); 617 | } 618 | 619 | /** 620 | * gmenu_desktopappinfo_get_locale_string: 621 | * @appinfo: a #GMenuDesktopAppInfo 622 | * @key: the key to look up 623 | * 624 | * Looks up a localized string value in the keyfile backing @info 625 | * translated to the current locale. 626 | * 627 | * The @key is looked up in the "Desktop Entry" group. 628 | * 629 | * Returns: (nullable): a newly allocated string, or %NULL if the key 630 | * is not found 631 | */ 632 | char * gmenu_desktopappinfo_get_locale_string (GMenuDesktopAppInfo *appinfo, const char *key) 633 | { 634 | g_return_val_if_fail (GMENU_IS_DESKTOPAPPINFO (appinfo), NULL); 635 | return g_desktop_app_info_get_locale_string (appinfo->super_appinfo, key); 636 | } 637 | 638 | /** 639 | * gmenu_desktopappinfo_get_boolean: 640 | * @appinfo: a #GMenuDesktopAppInfo 641 | * @key: the key to look up 642 | * 643 | * Looks up a boolean value in the keyfile backing @info. 644 | * 645 | * The @key is looked up in the "Desktop Entry" group. 646 | * 647 | * Returns: the boolean value, or %FALSE if the key 648 | * is not found 649 | */ 650 | gboolean gmenu_desktopappinfo_get_boolean (GMenuDesktopAppInfo *appinfo, const char *key) 651 | { 652 | g_return_val_if_fail (GMENU_IS_DESKTOPAPPINFO (appinfo), FALSE); 653 | return g_desktop_app_info_get_boolean (appinfo->super_appinfo, key); 654 | } 655 | 656 | /** 657 | * gmenu_desktopappinfo_list_actions: 658 | * @appinfo: a #GMenuDesktopAppInfo 659 | * 660 | * Returns the list of "additional application actions" supported on the 661 | * desktop file, as per the desktop file specification. 662 | * 663 | * As per the specification, this is the list of actions that are 664 | * explicitly listed in the "Actions" key of the [Desktop Entry] group. 665 | * 666 | * Returns: (array zero-terminated=1) (element-type utf8) (transfer none): a list of strings, always non-%NULL 667 | **/ 668 | const gchar * const * gmenu_desktopappinfo_list_actions (GMenuDesktopAppInfo *appinfo) 669 | { 670 | g_return_val_if_fail (GMENU_IS_DESKTOPAPPINFO (appinfo), NULL); 671 | return g_desktop_app_info_list_actions (appinfo->super_appinfo); 672 | } 673 | 674 | /** 675 | * gmenu_desktopappinfo_launch_action: 676 | * @appinfo: a #GMenuDesktopAppInfo 677 | * @action_name: the name of the action as from 678 | * g_desktop_app_info_list_actions() 679 | * @launch_context: (nullable): a #GAppLaunchContext 680 | * 681 | * Activates the named application action. 682 | * 683 | * You may only call this function on action names that were 684 | * returned from g_desktop_app_info_list_actions(). 685 | * 686 | * Note that if the main entry of the desktop file indicates that the 687 | * application supports startup notification, and @launch_context is 688 | * non-%NULL, then startup notification will be used when activating the 689 | * action (and as such, invocation of the action on the receiving side 690 | * must signal the end of startup notification when it is completed). 691 | * This is the expected behaviour of applications declaring additional 692 | * actions, as per the desktop file specification. 693 | * 694 | * As with g_app_info_launch() there is no way to detect failures that 695 | * occur while using this function. 696 | */ 697 | void gmenu_desktopappinfo_launch_action (GMenuDesktopAppInfo *appinfo, const gchar *action_name, GAppLaunchContext *launch_context) 698 | { 699 | g_return_if_fail (GMENU_IS_DESKTOPAPPINFO (appinfo)); 700 | g_desktop_app_info_launch_action (appinfo->super_appinfo, action_name, launch_context); 701 | } 702 | 703 | /** 704 | * gmenu_desktopappinfo_get_action_name: 705 | * @appinfo: a #GMenuDesktopAppInfo 706 | * @action_name: the name of the action as from 707 | * gmenu_desktopappinfo_list_actions() 708 | * 709 | * Gets the user-visible display name of the "additional application 710 | * action" specified by @action_name. 711 | * 712 | * This corresponds to the "Name" key within the keyfile group for the 713 | * action. 714 | * 715 | * Returns: (transfer full): the locale-specific action name 716 | * 717 | * Since: 2.38 718 | */ 719 | gchar * gmenu_desktopappinfo_get_action_name (GMenuDesktopAppInfo *appinfo, const gchar *action_name) 720 | { 721 | g_return_val_if_fail (GMENU_IS_DESKTOPAPPINFO (appinfo), NULL); 722 | return g_desktop_app_info_get_action_name (appinfo->super_appinfo, action_name); 723 | } 724 | 725 | /** 726 | * gmenu_desktopappinfo_launch_uris_as_manager: 727 | * @appinfo: a #GMenuDesktopAppInfo 728 | * @uris: (element-type utf8): List of URIs 729 | * @launch_context: (nullable): a #GAppLaunchContext 730 | * @spawn_flags: #GSpawnFlags, used for each process 731 | * @user_setup: (scope async) (nullable): a #GSpawnChildSetupFunc, used once 732 | * for each process. 733 | * @user_setup_data: (closure user_setup) (nullable): User data for @user_setup 734 | * @pid_callback: (scope call) (nullable): Callback for child processes 735 | * @pid_callback_data: (closure pid_callback) (nullable): User data for @callback 736 | * @error: return location for a #GError, or %NULL 737 | * 738 | * This function performs the equivalent of g_app_info_launch_uris(), 739 | * but is intended primarily for operating system components that 740 | * launch applications. Ordinary applications should use 741 | * g_app_info_launch_uris(). 742 | * 743 | * If the application is launched via GSpawn, then @spawn_flags, @user_setup 744 | * and @user_setup_data are used for the call to g_spawn_async(). 745 | * Additionally, @pid_callback (with @pid_callback_data) will be called to 746 | * inform about the PID of the created process. See g_spawn_async_with_pipes() 747 | * for information on certain parameter conditions that can enable an 748 | * optimized posix_spawn() codepath to be used. 749 | * 750 | * If application launching occurs via some other mechanism (eg: D-Bus 751 | * activation) then @spawn_flags, @user_setup, @user_setup_data, 752 | * @pid_callback and @pid_callback_data are ignored. 753 | * 754 | * Returns: %TRUE on successful launch, %FALSE otherwise. 755 | */ 756 | gboolean gmenu_desktopappinfo_launch_uris_as_manager (GMenuDesktopAppInfo *appinfo, 757 | GList *uris, 758 | GAppLaunchContext *launch_context, 759 | GSpawnFlags spawn_flags, 760 | GSpawnChildSetupFunc user_setup, 761 | gpointer user_setup_data, 762 | GDesktopAppLaunchCallback pid_callback, 763 | gpointer pid_callback_data, 764 | GError **error) 765 | { 766 | g_return_val_if_fail (GMENU_IS_DESKTOPAPPINFO (appinfo), FALSE); 767 | return g_desktop_app_info_launch_uris_as_manager (appinfo->super_appinfo, 768 | uris, 769 | launch_context, 770 | spawn_flags, 771 | user_setup, 772 | user_setup_data, 773 | pid_callback, 774 | pid_callback_data, 775 | error); 776 | } 777 | 778 | /** 779 | * gmenu_desktopappinfo_get_is_flatpak: 780 | * @appinfo: a #GMenuMenuDesktopAppInfo 781 | * 782 | * Returns: %TRUE if @info specifies a flatpak app, %FALSE otherwise 783 | */ 784 | gboolean gmenu_desktopappinfo_get_is_flatpak (GMenuDesktopAppInfo *appinfo) 785 | { 786 | return appinfo->is_flatpak; 787 | } 788 | 789 | /** 790 | * gmenu_desktopappinfo_get_flatpak_app_id: 791 | * @appinfo: a #GMenuMenuDesktopAppInfo 792 | * 793 | * This function looks up the "X-Flatpak" key of the [Desktop Entry] group, 794 | * which contains the Flatpak App ID 795 | * 796 | * Returns: (nullable): the flatpak app id or %NULL 797 | */ 798 | const char * 799 | gmenu_desktopappinfo_get_flatpak_app_id (GMenuDesktopAppInfo *appinfo) 800 | { 801 | return appinfo->flatpak_app_id; 802 | } 803 | -------------------------------------------------------------------------------- /libmenu/entry-directories.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2002 - 2004 Red Hat, Inc. 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, write to the 16 | * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 17 | * Boston, MA 02111-1307, USA. 18 | */ 19 | 20 | #include "entry-directories.h" 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "menu-util.h" 29 | #include "menu-monitor.h" 30 | 31 | typedef struct CachedDir CachedDir; 32 | typedef struct CachedDirMonitor CachedDirMonitor; 33 | 34 | struct EntryDirectory 35 | { 36 | CachedDir *dir; 37 | 38 | guint entry_type : 2; 39 | guint refcount : 24; 40 | }; 41 | 42 | struct EntryDirectoryList 43 | { 44 | int refcount; 45 | int length; 46 | GList *dirs; 47 | }; 48 | 49 | struct CachedDir 50 | { 51 | CachedDir *parent; 52 | char *name; 53 | 54 | GSList *entries; 55 | GSList *subdirs; 56 | GSList *retry_later_desktop_entries; 57 | 58 | MenuMonitor *dir_monitor; 59 | GSList *monitors; 60 | 61 | guint have_read_entries : 1; 62 | guint deleted : 1; 63 | 64 | guint references; 65 | 66 | GFunc notify; 67 | gpointer notify_data; 68 | }; 69 | 70 | struct CachedDirMonitor 71 | { 72 | EntryDirectory *ed; 73 | EntryDirectoryChangedFunc callback; 74 | gpointer user_data; 75 | }; 76 | 77 | static void cached_dir_add_reference (CachedDir *dir); 78 | static void cached_dir_remove_reference (CachedDir *dir); 79 | static void cached_dir_free (CachedDir *dir); 80 | static gboolean cached_dir_load_entries_recursive (CachedDir *dir, 81 | const char *dirname); 82 | static void cached_dir_unref (CachedDir *dir); 83 | static void cached_dir_unref_noparent (CachedDir *dir); 84 | static CachedDir * cached_dir_add_subdir (CachedDir *dir, 85 | const char *basename, 86 | const char *path); 87 | static gboolean cached_dir_remove_subdir (CachedDir *dir, 88 | const char *basename); 89 | 90 | static void handle_cached_dir_changed (MenuMonitor *monitor, 91 | MenuMonitorEvent event, 92 | const char *path, 93 | CachedDir *dir); 94 | 95 | /* 96 | * Entry directory cache 97 | */ 98 | 99 | static CachedDir *dir_cache = NULL; 100 | 101 | static void 102 | clear_cache (CachedDir *dir, 103 | gpointer *cache) 104 | { 105 | *cache = NULL; 106 | } 107 | 108 | static CachedDir * 109 | cached_dir_new (const char *name) 110 | { 111 | CachedDir *dir; 112 | 113 | dir = g_new0 (CachedDir, 1); 114 | dir->name = g_strdup (name); 115 | 116 | return dir; 117 | } 118 | 119 | static CachedDir * 120 | cached_dir_new_full (const char *name, 121 | GFunc notify, 122 | gpointer notify_data) 123 | { 124 | CachedDir *dir; 125 | 126 | dir = cached_dir_new (name); 127 | 128 | dir->notify = notify; 129 | dir->notify_data = notify_data; 130 | 131 | return dir; 132 | } 133 | 134 | static void 135 | cached_dir_free (CachedDir *dir) 136 | { 137 | if (dir->dir_monitor) 138 | { 139 | menu_monitor_remove_notify (dir->dir_monitor, 140 | (MenuMonitorNotifyFunc) handle_cached_dir_changed, 141 | dir); 142 | menu_monitor_unref (dir->dir_monitor); 143 | dir->dir_monitor = NULL; 144 | } 145 | 146 | g_slist_foreach (dir->monitors, (GFunc) g_free, NULL); 147 | g_slist_free (dir->monitors); 148 | dir->monitors = NULL; 149 | 150 | g_slist_foreach (dir->entries, 151 | (GFunc) desktop_entry_unref, 152 | NULL); 153 | g_slist_free (dir->entries); 154 | dir->entries = NULL; 155 | 156 | g_slist_foreach (dir->subdirs, 157 | (GFunc) cached_dir_unref_noparent, 158 | NULL); 159 | g_slist_free (dir->subdirs); 160 | dir->subdirs = NULL; 161 | 162 | g_slist_free_full (dir->retry_later_desktop_entries, g_free); 163 | dir->retry_later_desktop_entries = NULL; 164 | 165 | g_free (dir->name); 166 | g_free (dir); 167 | } 168 | 169 | static CachedDir * 170 | cached_dir_ref (CachedDir *dir) 171 | { 172 | dir->references++; 173 | return dir; 174 | } 175 | 176 | static void 177 | cached_dir_unref (CachedDir *dir) 178 | { 179 | if (--dir->references == 0) 180 | { 181 | CachedDir *parent; 182 | 183 | parent = dir->parent; 184 | 185 | if (parent != NULL) 186 | cached_dir_remove_subdir (parent, dir->name); 187 | 188 | if (dir->notify) 189 | dir->notify (dir, dir->notify_data); 190 | 191 | cached_dir_free (dir); 192 | } 193 | } 194 | 195 | static void 196 | cached_dir_unref_noparent (CachedDir *dir) 197 | { 198 | if (--dir->references == 0) 199 | { 200 | if (dir->notify) 201 | dir->notify (dir, dir->notify_data); 202 | 203 | cached_dir_free (dir); 204 | } 205 | } 206 | 207 | static inline CachedDir * 208 | find_subdir (CachedDir *dir, 209 | const char *subdir) 210 | { 211 | GSList *tmp; 212 | 213 | tmp = dir->subdirs; 214 | while (tmp != NULL) 215 | { 216 | CachedDir *sub = tmp->data; 217 | 218 | if (strcmp (sub->name, subdir) == 0) 219 | return sub; 220 | 221 | tmp = tmp->next; 222 | } 223 | 224 | return NULL; 225 | } 226 | 227 | static DesktopEntry * 228 | find_entry (CachedDir *dir, 229 | const char *basename) 230 | { 231 | GSList *tmp; 232 | 233 | tmp = dir->entries; 234 | while (tmp != NULL) 235 | { 236 | if (strcmp (desktop_entry_get_basename (tmp->data), basename) == 0) 237 | return tmp->data; 238 | 239 | tmp = tmp->next; 240 | } 241 | 242 | return NULL; 243 | } 244 | 245 | static DesktopEntry * 246 | cached_dir_find_relative_path (CachedDir *dir, 247 | const char *relative_path) 248 | { 249 | DesktopEntry *retval = NULL; 250 | char **split; 251 | int i; 252 | 253 | split = g_strsplit (relative_path, "/", -1); 254 | 255 | i = 0; 256 | while (split[i] != NULL) 257 | { 258 | if (split[i + 1] != NULL) 259 | { 260 | if ((dir = find_subdir (dir, split[i])) == NULL) 261 | break; 262 | } 263 | else 264 | { 265 | retval = find_entry (dir, split[i]); 266 | break; 267 | } 268 | 269 | ++i; 270 | } 271 | 272 | g_strfreev (split); 273 | 274 | return retval; 275 | } 276 | 277 | static CachedDir * 278 | cached_dir_lookup (const char *canonical) 279 | { 280 | CachedDir *dir; 281 | char **split; 282 | int i; 283 | 284 | if (dir_cache == NULL) 285 | dir_cache = cached_dir_new_full ("/", 286 | (GFunc) clear_cache, 287 | &dir_cache); 288 | dir = dir_cache; 289 | 290 | g_assert (canonical != NULL && canonical[0] == G_DIR_SEPARATOR); 291 | 292 | menu_verbose ("Looking up cached dir \"%s\"\n", canonical); 293 | 294 | split = g_strsplit (canonical + 1, "/", -1); 295 | 296 | i = 0; 297 | while (split[i] != NULL) 298 | { 299 | CachedDir *subdir; 300 | 301 | subdir = cached_dir_add_subdir (dir, split[i], NULL); 302 | 303 | dir = subdir; 304 | 305 | ++i; 306 | } 307 | 308 | g_strfreev (split); 309 | 310 | g_assert (dir != NULL); 311 | 312 | return dir; 313 | } 314 | 315 | static gboolean 316 | cached_dir_add_entry (CachedDir *dir, 317 | const char *basename, 318 | const char *path) 319 | { 320 | DesktopEntry *entry; 321 | DesktopEntryResultCode code; 322 | 323 | entry = desktop_entry_new (path, &code); 324 | if (entry == NULL) 325 | { 326 | if (code == DESKTOP_ENTRY_LOAD_FAIL_APPINFO) 327 | { 328 | menu_verbose ("Adding %s to the retry list (mimeinfo.cache maybe isn't done getting updated yet\n", path); 329 | 330 | dir->retry_later_desktop_entries = g_slist_prepend (dir->retry_later_desktop_entries, g_strdup (path)); 331 | } 332 | 333 | return FALSE; 334 | } 335 | 336 | dir->entries = g_slist_prepend (dir->entries, entry); 337 | 338 | return TRUE; 339 | } 340 | 341 | static gboolean 342 | cached_dir_update_entry (CachedDir *dir, 343 | const char *basename, 344 | const char *path) 345 | { 346 | GSList *tmp; 347 | 348 | tmp = dir->entries; 349 | while (tmp != NULL) 350 | { 351 | if (strcmp (desktop_entry_get_basename (tmp->data), basename) == 0) 352 | { 353 | if (!desktop_entry_reload (tmp->data)) 354 | { 355 | dir->entries = g_slist_delete_link (dir->entries, tmp); 356 | } 357 | 358 | return TRUE; 359 | } 360 | 361 | tmp = tmp->next; 362 | } 363 | 364 | return cached_dir_add_entry (dir, basename, path); 365 | } 366 | 367 | static gboolean 368 | cached_dir_remove_entry (CachedDir *dir, 369 | const char *basename) 370 | { 371 | GSList *tmp; 372 | 373 | tmp = dir->entries; 374 | while (tmp != NULL) 375 | { 376 | if (strcmp (desktop_entry_get_basename (tmp->data), basename) == 0) 377 | { 378 | desktop_entry_unref (tmp->data); 379 | dir->entries = g_slist_delete_link (dir->entries, tmp); 380 | return TRUE; 381 | } 382 | 383 | tmp = tmp->next; 384 | } 385 | 386 | return FALSE; 387 | } 388 | 389 | static CachedDir * 390 | cached_dir_add_subdir (CachedDir *dir, 391 | const char *basename, 392 | const char *path) 393 | { 394 | CachedDir *subdir; 395 | 396 | subdir = find_subdir (dir, basename); 397 | 398 | if (subdir != NULL) 399 | { 400 | subdir->deleted = FALSE; 401 | return subdir; 402 | } 403 | 404 | subdir = cached_dir_new (basename); 405 | 406 | if (path != NULL && !cached_dir_load_entries_recursive (subdir, path)) 407 | { 408 | cached_dir_free (subdir); 409 | return NULL; 410 | } 411 | 412 | menu_verbose ("Caching dir \"%s\"\n", basename); 413 | 414 | subdir->parent = dir; 415 | dir->subdirs = g_slist_prepend (dir->subdirs, cached_dir_ref (subdir)); 416 | 417 | return subdir; 418 | } 419 | 420 | static gboolean 421 | cached_dir_remove_subdir (CachedDir *dir, 422 | const char *basename) 423 | { 424 | CachedDir *subdir; 425 | 426 | subdir = find_subdir (dir, basename); 427 | 428 | if (subdir != NULL) 429 | { 430 | subdir->deleted = TRUE; 431 | 432 | cached_dir_unref (subdir); 433 | dir->subdirs = g_slist_remove (dir->subdirs, subdir); 434 | 435 | return TRUE; 436 | } 437 | 438 | return FALSE; 439 | } 440 | 441 | static guint monitors_idle_handler = 0; 442 | static GSList *pending_monitors_dirs = NULL; 443 | 444 | static void 445 | cached_dir_invoke_monitors (CachedDir *dir) 446 | { 447 | GSList *tmp; 448 | 449 | tmp = dir->monitors; 450 | while (tmp != NULL) 451 | { 452 | CachedDirMonitor *monitor = tmp->data; 453 | GSList *next = tmp->next; 454 | 455 | monitor->callback (monitor->ed, monitor->user_data); 456 | 457 | tmp = next; 458 | } 459 | 460 | /* we explicitly don't invoke monitors of the parent since an 461 | * event has been queued for it too */ 462 | } 463 | 464 | static gboolean 465 | emit_monitors_in_idle (void) 466 | { 467 | GSList *monitors_to_emit; 468 | GSList *tmp; 469 | 470 | monitors_to_emit = pending_monitors_dirs; 471 | 472 | pending_monitors_dirs = NULL; 473 | monitors_idle_handler = 0; 474 | 475 | tmp = monitors_to_emit; 476 | while (tmp != NULL) 477 | { 478 | CachedDir *dir = tmp->data; 479 | 480 | cached_dir_invoke_monitors (dir); 481 | cached_dir_remove_reference (dir); 482 | 483 | tmp = tmp->next; 484 | } 485 | 486 | g_slist_free (monitors_to_emit); 487 | 488 | return FALSE; 489 | } 490 | 491 | static void 492 | cached_dir_queue_monitor_event (CachedDir *dir) 493 | { 494 | GSList *tmp; 495 | 496 | tmp = pending_monitors_dirs; 497 | while (tmp != NULL) 498 | { 499 | CachedDir *d = tmp->data; 500 | GSList *next = tmp->next; 501 | 502 | if (dir->parent == d->parent && 503 | g_strcmp0 (dir->name, d->name) == 0) 504 | break; 505 | 506 | tmp = next; 507 | } 508 | 509 | /* not found, so let's queue it */ 510 | if (tmp == NULL) 511 | { 512 | cached_dir_add_reference (dir); 513 | pending_monitors_dirs = g_slist_append (pending_monitors_dirs, dir); 514 | } 515 | 516 | if (dir->parent) 517 | { 518 | cached_dir_queue_monitor_event (dir->parent); 519 | } 520 | 521 | if (monitors_idle_handler > 0) 522 | { 523 | g_source_remove (monitors_idle_handler); 524 | } 525 | 526 | monitors_idle_handler = g_timeout_add (100, (GSourceFunc) emit_monitors_in_idle, NULL); 527 | } 528 | 529 | static void 530 | handle_cached_dir_changed (MenuMonitor *monitor, 531 | MenuMonitorEvent event, 532 | const char *path, 533 | CachedDir *dir) 534 | { 535 | gboolean handled = FALSE; 536 | gboolean retry_changes = FALSE; 537 | 538 | char *basename; 539 | char *dirname; 540 | 541 | dirname = g_path_get_dirname (path); 542 | basename = g_path_get_basename (path); 543 | 544 | dir = cached_dir_lookup (dirname); 545 | cached_dir_add_reference (dir); 546 | 547 | if (g_str_has_suffix (basename, ".desktop") || 548 | g_str_has_suffix (basename, ".directory")) 549 | { 550 | switch (event) 551 | { 552 | case MENU_MONITOR_EVENT_CREATED: 553 | case MENU_MONITOR_EVENT_CHANGED: 554 | handled = cached_dir_update_entry (dir, basename, path); 555 | break; 556 | 557 | case MENU_MONITOR_EVENT_DELETED: 558 | handled = cached_dir_remove_entry (dir, basename); 559 | break; 560 | 561 | default: 562 | g_assert_not_reached (); 563 | break; 564 | } 565 | } 566 | else if (g_strcmp0 (basename, "mimeinfo.cache") == 0) 567 | { 568 | /* The observed file notifies when a new desktop file is added 569 | * (but fails to load) go something like: 570 | * 571 | * NOTIFY: foo.desktop 572 | * NOTIFY: mimeinfo.cache.tempfile 573 | * NOTIFY: mimeinfo.cache.tempfile 574 | * NOTIFY: mimeinfo.cache 575 | * 576 | * Additionally, the failure is not upon trying to read the file, 577 | * but attempting to get its GAppInfo (gmenu_desktopappinfo_new_from_filename() 578 | * in desktop-entries.c ln 277). If you jigger desktop_entry_load() around 579 | * and read the file as a keyfile *first*, it succeeds. If you then try 580 | * to run gmenu_desktopappinfo_new_from_keyfile(), *then* it fails. 581 | * 582 | * The theory here is there is a race condition where app info (which includes 583 | * mimetype stuff) is unavailable because mimeinfo.cache is updated immediately 584 | * after the app is installed. 585 | * 586 | * What we do here is, when a desktop fails to load, we add it to a temporary 587 | * list. We wait until mimeinfo.cache changes, then retry that desktop file, 588 | * which succeeds this second time. 589 | * 590 | * Note: An alternative fix (presented more as a proof than a suggestion) is to 591 | * change line 151 in menu-monitor.c to use g_timeout_add_seconds, and delay 592 | * for one second. This also avoids the issue (but it remains a race condition). 593 | */ 594 | 595 | GSList *iter; 596 | 597 | menu_verbose ("mimeinfo changed, checking for failed entries\n"); 598 | 599 | for (iter = dir->retry_later_desktop_entries; iter != NULL; iter = iter->next) 600 | { 601 | const gchar *retry_path = iter->data; 602 | 603 | menu_verbose ("retrying %s\n", retry_path); 604 | 605 | char *retry_basename = g_path_get_basename (retry_path); 606 | 607 | if (cached_dir_update_entry (dir, retry_basename, retry_path)) 608 | retry_changes = TRUE; 609 | 610 | g_free (retry_basename); 611 | } 612 | 613 | g_slist_free_full (dir->retry_later_desktop_entries, g_free); 614 | dir->retry_later_desktop_entries = NULL; 615 | 616 | handled = retry_changes; 617 | } 618 | else /* Try recursing */ 619 | { 620 | switch (event) 621 | { 622 | case MENU_MONITOR_EVENT_CREATED: 623 | handled = cached_dir_add_subdir (dir, basename, path) != NULL; 624 | break; 625 | 626 | case MENU_MONITOR_EVENT_CHANGED: 627 | break; 628 | 629 | case MENU_MONITOR_EVENT_DELETED: 630 | handled = cached_dir_remove_subdir (dir, basename); 631 | break; 632 | 633 | default: 634 | g_assert_not_reached (); 635 | break; 636 | } 637 | } 638 | 639 | g_free (basename); 640 | g_free (dirname); 641 | 642 | if (handled) 643 | { 644 | menu_verbose ("'%s' notified of '%s' %s - invalidating cache\n", 645 | dir->name, 646 | path, 647 | event == MENU_MONITOR_EVENT_CREATED ? ("created") : 648 | event == MENU_MONITOR_EVENT_DELETED ? ("deleted") : ("changed")); 649 | 650 | /* CHANGED events don't change the set of desktop entries, unless it's the mimeinfo.cache file changing */ 651 | if (retry_changes || (event == MENU_MONITOR_EVENT_CREATED || event == MENU_MONITOR_EVENT_DELETED)) 652 | { 653 | _entry_directory_list_empty_desktop_cache (); 654 | } 655 | 656 | cached_dir_queue_monitor_event (dir); 657 | } 658 | 659 | cached_dir_remove_reference (dir); 660 | } 661 | 662 | static void 663 | cached_dir_ensure_monitor (CachedDir *dir, 664 | const char *dirname) 665 | { 666 | if (dir->dir_monitor == NULL) 667 | { 668 | dir->dir_monitor = menu_get_directory_monitor (dirname); 669 | menu_monitor_add_notify (dir->dir_monitor, 670 | (MenuMonitorNotifyFunc) handle_cached_dir_changed, 671 | dir); 672 | } 673 | } 674 | 675 | static gboolean 676 | cached_dir_load_entries_recursive (CachedDir *dir, 677 | const char *dirname) 678 | { 679 | DIR *dp; 680 | struct dirent *dent; 681 | GString *fullpath; 682 | gsize fullpath_len; 683 | 684 | g_assert (dir != NULL); 685 | 686 | if (dir->have_read_entries) 687 | return TRUE; 688 | 689 | menu_verbose ("Attempting to read entries from %s (full path %s)\n", 690 | dir->name, dirname); 691 | 692 | dp = opendir (dirname); 693 | if (dp == NULL) 694 | { 695 | menu_verbose ("Unable to list directory \"%s\"\n", 696 | dirname); 697 | return FALSE; 698 | } 699 | 700 | cached_dir_ensure_monitor (dir, dirname); 701 | 702 | fullpath = g_string_new (dirname); 703 | if (fullpath->str[fullpath->len - 1] != G_DIR_SEPARATOR) 704 | g_string_append_c (fullpath, G_DIR_SEPARATOR); 705 | 706 | fullpath_len = fullpath->len; 707 | 708 | while ((dent = readdir (dp)) != NULL) 709 | { 710 | /* ignore . and .. */ 711 | if (dent->d_name[0] == '.' && 712 | (dent->d_name[1] == '\0' || 713 | (dent->d_name[1] == '.' && 714 | dent->d_name[2] == '\0'))) 715 | continue; 716 | 717 | g_string_append (fullpath, dent->d_name); 718 | 719 | if (g_str_has_suffix (dent->d_name, ".desktop") || 720 | g_str_has_suffix (dent->d_name, ".directory")) 721 | { 722 | cached_dir_add_entry (dir, dent->d_name, fullpath->str); 723 | } 724 | else /* Try recursing */ 725 | { 726 | cached_dir_add_subdir (dir, dent->d_name, fullpath->str); 727 | } 728 | 729 | g_string_truncate (fullpath, fullpath_len); 730 | } 731 | 732 | closedir (dp); 733 | 734 | g_string_free (fullpath, TRUE); 735 | 736 | dir->have_read_entries = TRUE; 737 | 738 | return TRUE; 739 | } 740 | 741 | static void 742 | cached_dir_add_monitor (CachedDir *dir, 743 | EntryDirectory *ed, 744 | EntryDirectoryChangedFunc callback, 745 | gpointer user_data) 746 | { 747 | CachedDirMonitor *monitor; 748 | GSList *tmp; 749 | 750 | tmp = dir->monitors; 751 | while (tmp != NULL) 752 | { 753 | monitor = tmp->data; 754 | 755 | if (monitor->ed == ed && 756 | monitor->callback == callback && 757 | monitor->user_data == user_data) 758 | break; 759 | 760 | tmp = tmp->next; 761 | } 762 | 763 | if (tmp == NULL) 764 | { 765 | monitor = g_new0 (CachedDirMonitor, 1); 766 | monitor->ed = ed; 767 | monitor->callback = callback; 768 | monitor->user_data = user_data; 769 | 770 | dir->monitors = g_slist_append (dir->monitors, monitor); 771 | } 772 | } 773 | 774 | static void 775 | cached_dir_remove_monitor (CachedDir *dir, 776 | EntryDirectory *ed, 777 | EntryDirectoryChangedFunc callback, 778 | gpointer user_data) 779 | { 780 | GSList *tmp; 781 | 782 | tmp = dir->monitors; 783 | while (tmp != NULL) 784 | { 785 | CachedDirMonitor *monitor = tmp->data; 786 | GSList *next = tmp->next; 787 | 788 | if (monitor->ed == ed && 789 | monitor->callback == callback && 790 | monitor->user_data == user_data) 791 | { 792 | dir->monitors = g_slist_delete_link (dir->monitors, tmp); 793 | g_free (monitor); 794 | } 795 | 796 | tmp = next; 797 | } 798 | } 799 | 800 | static void 801 | cached_dir_add_reference (CachedDir *dir) 802 | { 803 | cached_dir_ref (dir); 804 | 805 | if (dir->parent != NULL) 806 | { 807 | cached_dir_add_reference (dir->parent); 808 | } 809 | } 810 | 811 | static void 812 | cached_dir_remove_reference (CachedDir *dir) 813 | { 814 | CachedDir *parent; 815 | 816 | parent = dir->parent; 817 | 818 | cached_dir_unref (dir); 819 | 820 | if (parent != NULL) 821 | { 822 | cached_dir_remove_reference (parent); 823 | } 824 | } 825 | 826 | /* 827 | * Entry directories 828 | */ 829 | 830 | EntryDirectory * 831 | entry_directory_new (DesktopEntryType entry_type, 832 | const char *path) 833 | { 834 | EntryDirectory *ed; 835 | char *canonical; 836 | 837 | menu_verbose ("Loading entry directory \"%s\"\n", path); 838 | 839 | canonical = realpath (path, NULL); 840 | if (canonical == NULL) 841 | { 842 | menu_verbose ("Failed to canonicalize \"%s\": %s\n", 843 | path, g_strerror (errno)); 844 | return NULL; 845 | } 846 | 847 | ed = g_new0 (EntryDirectory, 1); 848 | 849 | ed->dir = cached_dir_lookup (canonical); 850 | g_assert (ed->dir != NULL); 851 | 852 | cached_dir_add_reference (ed->dir); 853 | cached_dir_load_entries_recursive (ed->dir, canonical); 854 | 855 | ed->entry_type = entry_type; 856 | ed->refcount = 1; 857 | 858 | g_free (canonical); 859 | 860 | return ed; 861 | } 862 | 863 | EntryDirectory * 864 | entry_directory_ref (EntryDirectory *ed) 865 | { 866 | g_return_val_if_fail (ed != NULL, NULL); 867 | g_return_val_if_fail (ed->refcount > 0, NULL); 868 | 869 | ed->refcount++; 870 | 871 | return ed; 872 | } 873 | 874 | void 875 | entry_directory_unref (EntryDirectory *ed) 876 | { 877 | g_return_if_fail (ed != NULL); 878 | g_return_if_fail (ed->refcount > 0); 879 | 880 | if (--ed->refcount == 0) 881 | { 882 | cached_dir_remove_reference (ed->dir); 883 | 884 | ed->dir = NULL; 885 | ed->entry_type = DESKTOP_ENTRY_INVALID; 886 | 887 | g_free (ed); 888 | } 889 | } 890 | 891 | static void 892 | entry_directory_add_monitor (EntryDirectory *ed, 893 | EntryDirectoryChangedFunc callback, 894 | gpointer user_data) 895 | { 896 | cached_dir_add_monitor (ed->dir, ed, callback, user_data); 897 | } 898 | 899 | static void 900 | entry_directory_remove_monitor (EntryDirectory *ed, 901 | EntryDirectoryChangedFunc callback, 902 | gpointer user_data) 903 | { 904 | cached_dir_remove_monitor (ed->dir, ed, callback, user_data); 905 | } 906 | 907 | static DesktopEntry * 908 | entry_directory_get_directory (EntryDirectory *ed, 909 | const char *relative_path) 910 | { 911 | DesktopEntry *entry; 912 | 913 | if (ed->entry_type != DESKTOP_ENTRY_DIRECTORY) 914 | return NULL; 915 | 916 | entry = cached_dir_find_relative_path (ed->dir, relative_path); 917 | if (entry == NULL || desktop_entry_get_type (entry) != DESKTOP_ENTRY_DIRECTORY) 918 | return NULL; 919 | 920 | return desktop_entry_ref (entry); 921 | } 922 | 923 | static char * 924 | get_desktop_file_id_from_path (EntryDirectory *ed, 925 | DesktopEntryType entry_type, 926 | const char *relative_path, 927 | DesktopEntry *entry) 928 | { 929 | char *retval; 930 | 931 | retval = NULL; 932 | 933 | if (entry_type == DESKTOP_ENTRY_DESKTOP) 934 | { 935 | GMenuDesktopAppInfo *appinfo; 936 | appinfo = desktop_entry_get_app_info (entry); 937 | retval = g_strdelimit (g_strdup (relative_path), "/", '-'); 938 | if (gmenu_desktopappinfo_get_is_flatpak (appinfo)) 939 | { 940 | char* tmp; 941 | tmp = retval; 942 | retval = g_strconcat (retval, GMENU_DESKTOPAPPINFO_FLATPAK_SUFFIX, NULL); 943 | g_free (tmp); 944 | } 945 | } 946 | else 947 | { 948 | retval = g_strdup (relative_path); 949 | } 950 | 951 | return retval; 952 | } 953 | 954 | typedef gboolean (* EntryDirectoryForeachFunc) (EntryDirectory *ed, 955 | DesktopEntry *entry, 956 | const char *file_id, 957 | DesktopEntrySet *set, 958 | gpointer user_data); 959 | 960 | static gboolean 961 | entry_directory_foreach_recursive (EntryDirectory *ed, 962 | CachedDir *cd, 963 | GString *relative_path, 964 | EntryDirectoryForeachFunc func, 965 | DesktopEntrySet *set, 966 | gpointer user_data) 967 | { 968 | GSList *tmp; 969 | int relative_path_len; 970 | 971 | if (cd->deleted) 972 | return TRUE; 973 | 974 | relative_path_len = relative_path->len; 975 | 976 | tmp = cd->entries; 977 | while (tmp != NULL) 978 | { 979 | DesktopEntry *entry = tmp->data; 980 | 981 | if (desktop_entry_get_type (entry) == ed->entry_type) 982 | { 983 | gboolean ret; 984 | char *file_id; 985 | 986 | g_string_append (relative_path, 987 | desktop_entry_get_basename (entry)); 988 | 989 | file_id = get_desktop_file_id_from_path (ed, 990 | ed->entry_type, 991 | relative_path->str, 992 | entry); 993 | 994 | ret = func (ed, entry, file_id, set, user_data); 995 | 996 | g_free (file_id); 997 | 998 | g_string_truncate (relative_path, relative_path_len); 999 | 1000 | if (!ret) 1001 | return FALSE; 1002 | } 1003 | 1004 | tmp = tmp->next; 1005 | } 1006 | 1007 | tmp = cd->subdirs; 1008 | while (tmp != NULL) 1009 | { 1010 | CachedDir *subdir = tmp->data; 1011 | 1012 | g_string_append (relative_path, subdir->name); 1013 | g_string_append_c (relative_path, G_DIR_SEPARATOR); 1014 | 1015 | if (!entry_directory_foreach_recursive (ed, 1016 | subdir, 1017 | relative_path, 1018 | func, 1019 | set, 1020 | user_data)) 1021 | return FALSE; 1022 | 1023 | g_string_truncate (relative_path, relative_path_len); 1024 | 1025 | tmp = tmp->next; 1026 | } 1027 | 1028 | return TRUE; 1029 | } 1030 | 1031 | static void 1032 | entry_directory_foreach (EntryDirectory *ed, 1033 | EntryDirectoryForeachFunc func, 1034 | DesktopEntrySet *set, 1035 | gpointer user_data) 1036 | { 1037 | GString *path; 1038 | 1039 | path = g_string_new (NULL); 1040 | 1041 | entry_directory_foreach_recursive (ed, 1042 | ed->dir, 1043 | path, 1044 | func, 1045 | set, 1046 | user_data); 1047 | 1048 | g_string_free (path, TRUE); 1049 | } 1050 | 1051 | void 1052 | entry_directory_get_flat_contents (EntryDirectory *ed, 1053 | DesktopEntrySet *desktop_entries, 1054 | DesktopEntrySet *directory_entries, 1055 | GSList **subdirs) 1056 | { 1057 | GSList *tmp; 1058 | 1059 | if (subdirs) 1060 | *subdirs = NULL; 1061 | 1062 | tmp = ed->dir->entries; 1063 | while (tmp != NULL) 1064 | { 1065 | DesktopEntry *entry = tmp->data; 1066 | const char *basename; 1067 | 1068 | basename = desktop_entry_get_basename (entry); 1069 | 1070 | if (desktop_entries && 1071 | desktop_entry_get_type (entry) == DESKTOP_ENTRY_DESKTOP) 1072 | { 1073 | desktop_entry_set_add_entry (desktop_entries, 1074 | entry, 1075 | NULL); 1076 | } 1077 | 1078 | if (directory_entries && 1079 | desktop_entry_get_type (entry) == DESKTOP_ENTRY_DIRECTORY) 1080 | { 1081 | desktop_entry_set_add_entry (directory_entries, 1082 | entry, 1083 | basename); 1084 | } 1085 | 1086 | tmp = tmp->next; 1087 | } 1088 | 1089 | if (subdirs) 1090 | { 1091 | tmp = ed->dir->subdirs; 1092 | while (tmp != NULL) 1093 | { 1094 | CachedDir *cd = tmp->data; 1095 | 1096 | if (!cd->deleted) 1097 | { 1098 | *subdirs = g_slist_prepend (*subdirs, g_strdup (cd->name)); 1099 | } 1100 | 1101 | tmp = tmp->next; 1102 | } 1103 | } 1104 | 1105 | if (subdirs) 1106 | *subdirs = g_slist_reverse (*subdirs); 1107 | } 1108 | 1109 | /* 1110 | * Entry directory lists 1111 | */ 1112 | 1113 | EntryDirectoryList * 1114 | entry_directory_list_new (void) 1115 | { 1116 | EntryDirectoryList *list; 1117 | 1118 | list = g_new0 (EntryDirectoryList, 1); 1119 | 1120 | list->refcount = 1; 1121 | list->dirs = NULL; 1122 | list->length = 0; 1123 | 1124 | return list; 1125 | } 1126 | 1127 | EntryDirectoryList * 1128 | entry_directory_list_ref (EntryDirectoryList *list) 1129 | { 1130 | g_return_val_if_fail (list != NULL, NULL); 1131 | g_return_val_if_fail (list->refcount > 0, NULL); 1132 | 1133 | list->refcount += 1; 1134 | 1135 | return list; 1136 | } 1137 | 1138 | void 1139 | entry_directory_list_unref (EntryDirectoryList *list) 1140 | { 1141 | g_return_if_fail (list != NULL); 1142 | g_return_if_fail (list->refcount > 0); 1143 | 1144 | list->refcount -= 1; 1145 | if (list->refcount == 0) 1146 | { 1147 | g_list_foreach (list->dirs, (GFunc) entry_directory_unref, NULL); 1148 | g_list_free (list->dirs); 1149 | list->dirs = NULL; 1150 | list->length = 0; 1151 | g_free (list); 1152 | } 1153 | } 1154 | 1155 | void 1156 | entry_directory_list_prepend (EntryDirectoryList *list, 1157 | EntryDirectory *ed) 1158 | { 1159 | list->length += 1; 1160 | list->dirs = g_list_prepend (list->dirs, 1161 | entry_directory_ref (ed)); 1162 | } 1163 | 1164 | int 1165 | entry_directory_list_get_length (EntryDirectoryList *list) 1166 | { 1167 | return list->length; 1168 | } 1169 | 1170 | void 1171 | entry_directory_list_append_list (EntryDirectoryList *list, 1172 | EntryDirectoryList *to_append) 1173 | { 1174 | GList *tmp; 1175 | GList *new_dirs = NULL; 1176 | 1177 | if (to_append->length == 0) 1178 | return; 1179 | 1180 | tmp = to_append->dirs; 1181 | while (tmp != NULL) 1182 | { 1183 | list->length += 1; 1184 | new_dirs = g_list_prepend (new_dirs, 1185 | entry_directory_ref (tmp->data)); 1186 | 1187 | tmp = tmp->next; 1188 | } 1189 | 1190 | new_dirs = g_list_reverse (new_dirs); 1191 | list->dirs = g_list_concat (list->dirs, new_dirs); 1192 | } 1193 | 1194 | DesktopEntry * 1195 | entry_directory_list_get_directory (EntryDirectoryList *list, 1196 | const char *relative_path) 1197 | { 1198 | DesktopEntry *retval = NULL; 1199 | GList *tmp; 1200 | 1201 | tmp = list->dirs; 1202 | while (tmp != NULL) 1203 | { 1204 | if ((retval = entry_directory_get_directory (tmp->data, relative_path)) != NULL) 1205 | break; 1206 | 1207 | tmp = tmp->next; 1208 | } 1209 | 1210 | return retval; 1211 | } 1212 | 1213 | gboolean 1214 | _entry_directory_list_compare (const EntryDirectoryList *a, 1215 | const EntryDirectoryList *b) 1216 | { 1217 | GList *al, *bl; 1218 | 1219 | if (a == NULL && b == NULL) 1220 | return TRUE; 1221 | 1222 | if ((a == NULL || b == NULL)) 1223 | return FALSE; 1224 | 1225 | if (a->length != b->length) 1226 | return FALSE; 1227 | 1228 | al = a->dirs; bl = b->dirs; 1229 | while (al && bl && al->data == bl->data) 1230 | { 1231 | al = al->next; 1232 | bl = bl->next; 1233 | } 1234 | 1235 | return (al == NULL && bl == NULL); 1236 | } 1237 | 1238 | static gboolean 1239 | get_all_func (EntryDirectory *ed, 1240 | DesktopEntry *entry, 1241 | const char *file_id, 1242 | DesktopEntrySet *set, 1243 | gpointer user_data) 1244 | { 1245 | entry = desktop_entry_ref (entry); 1246 | 1247 | desktop_entry_set_add_entry (set, entry, file_id); 1248 | desktop_entry_unref (entry); 1249 | 1250 | return TRUE; 1251 | } 1252 | 1253 | static DesktopEntrySet *entry_directory_last_set = NULL; 1254 | static EntryDirectoryList *entry_directory_last_list = NULL; 1255 | 1256 | void 1257 | _entry_directory_list_empty_desktop_cache (void) 1258 | { 1259 | if (entry_directory_last_set != NULL) 1260 | desktop_entry_set_unref (entry_directory_last_set); 1261 | entry_directory_last_set = NULL; 1262 | 1263 | if (entry_directory_last_list != NULL) 1264 | entry_directory_list_unref (entry_directory_last_list); 1265 | entry_directory_last_list = NULL; 1266 | } 1267 | 1268 | DesktopEntrySet * 1269 | _entry_directory_list_get_all_desktops (EntryDirectoryList *list) 1270 | { 1271 | GList *tmp; 1272 | DesktopEntrySet *set; 1273 | 1274 | /* The only tricky thing here is that desktop files later 1275 | * in the search list with the same relative path 1276 | * are "hidden" by desktop files earlier in the path, 1277 | * so we have to do the earlier files first causing 1278 | * the later files to replace the earlier files 1279 | * in the DesktopEntrySet 1280 | * 1281 | * We go from the end of the list so we can just 1282 | * g_hash_table_replace and not have to do two 1283 | * hash lookups (check for existing entry, then insert new 1284 | * entry) 1285 | */ 1286 | 1287 | /* This method is -extremely- slow, so we have a simple 1288 | one-entry cache here */ 1289 | if (_entry_directory_list_compare (list, entry_directory_last_list)) 1290 | { 1291 | menu_verbose (" Hit desktop list (%p) cache\n", list); 1292 | return desktop_entry_set_ref (entry_directory_last_set); 1293 | } 1294 | 1295 | if (entry_directory_last_set != NULL) 1296 | desktop_entry_set_unref (entry_directory_last_set); 1297 | if (entry_directory_last_list != NULL) 1298 | entry_directory_list_unref (entry_directory_last_list); 1299 | 1300 | set = desktop_entry_set_new (); 1301 | menu_verbose (" Storing all of list %p in set %p\n", 1302 | list, set); 1303 | 1304 | tmp = g_list_last (list->dirs); 1305 | while (tmp != NULL) 1306 | { 1307 | entry_directory_foreach (tmp->data, get_all_func, set, NULL); 1308 | 1309 | tmp = tmp->prev; 1310 | } 1311 | 1312 | entry_directory_last_list = entry_directory_list_ref (list); 1313 | entry_directory_last_set = desktop_entry_set_ref (set); 1314 | 1315 | return set; 1316 | } 1317 | 1318 | void 1319 | entry_directory_list_add_monitors (EntryDirectoryList *list, 1320 | EntryDirectoryChangedFunc callback, 1321 | gpointer user_data) 1322 | { 1323 | GList *tmp; 1324 | 1325 | tmp = list->dirs; 1326 | while (tmp != NULL) 1327 | { 1328 | entry_directory_add_monitor (tmp->data, callback, user_data); 1329 | tmp = tmp->next; 1330 | } 1331 | } 1332 | 1333 | void 1334 | entry_directory_list_remove_monitors (EntryDirectoryList *list, 1335 | EntryDirectoryChangedFunc callback, 1336 | gpointer user_data) 1337 | { 1338 | GList *tmp; 1339 | 1340 | tmp = list->dirs; 1341 | while (tmp != NULL) 1342 | { 1343 | entry_directory_remove_monitor (tmp->data, callback, user_data); 1344 | tmp = tmp->next; 1345 | } 1346 | } 1347 | --------------------------------------------------------------------------------