diff --git a/Makefile b/Makefile index b091dca..4c61c0f 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ # Comment this line if you don't want icons to appear in menu CFLAGS+=-DWITH_ICONS -# Uncomment this line if Openbox can display SVG icons +# Uncomment this line if Openbox can display SVG icons # Check SVG support with '$ ldd /usr/bin/openbox | grep svg', librsvg must appear.. # CFLAGS+=-DWITH_SVG @@ -13,21 +13,25 @@ DESTDIR ?= $(prefix) BINDIR= ${DESTDIR}/bin -SRC= $(shell ls *.c 2> /dev/null) +SRC= $(shell ls src/*.c 2> /dev/null) OBJ= $(SRC:.c=.o) -all: $(OBJ) openbox-menu +TESTS_SRC= $(shell ls tests/*.c 2> /dev/null) +TEST_OBJ= $(TESTS_SRC:.c=.o) + +all: $(OBJ) $(TEST_OBJ) check openbox-menu %.o: %.c $(CC) $(CFLAGS) -c $< -o $@ + openbox-menu: $(OBJ) $(CC) $(OBJ) -o openbox-menu $(LDFLAGS) $(LIBS) -.PHONY: clean install doc changelog check +.PHONY: clean install doc changelog check xmllint clean: - @rm -f *.o openbox-menu + @rm -f $(OBJ) $(TEST_OBJ) openbox-menu check @rm -rf doc install: @@ -37,8 +41,12 @@ doc: robodoc --src . --doc doc/ --multidoc --index --html --cmode -check: openbox-menu - ./openbox-menu > test.xml +check: $(TEST_OBJ) src/utils.o src/context.o + $(CC) src/utils.o src/context.o $(TEST_OBJ) $(LDFLAGS) $(LIBS) -o check + gtester --verbose check + +xmllint: openbox-menu + ./openbox-menu > test.xml xmllint test.xml rm test.xml diff --git a/archlinux/PKGBUILD b/archlinux/PKGBUILD new file mode 100644 index 0000000..21eecc0 --- /dev/null +++ b/archlinux/PKGBUILD @@ -0,0 +1,33 @@ +# Contributors: Calimero +# Maintainer: mimas + +_pkgname=openbox-menu +pkgname=$_pkgname-hg +pkgver=66.35d948d8b998 +pkgrel=1 +pkgdesc="Dynamic XDG menu for openbox" +arch=('i686' 'x86_64') +provides=("openbox-menu") +conflicts=("openbox-menu") +url="http://mimasgpc.free.fr/openbox-menu.html" +license=('GPL3') +depends=('gtk2' 'menu-cache') +makedepends=("mercurial") +optdepends=('lxmenu-data: LXDE menus' 'gnome-menus: GNOME menus') +source=("hg+https://bitbucket.org/fabriceT/openbox-menu") +md5sums=('SKIP') + +pkgver() { + cd $_pkgname + echo $(hg identify -n).$(hg identify -i) +} + +build() { + cd $_pkgname + make +} + +package() { + cd $_pkgname + make install DESTDIR="${pkgdir}/usr/" +} diff --git a/menu.c b/menu.c deleted file mode 100644 index c7be3be..0000000 --- a/menu.c +++ /dev/null @@ -1,302 +0,0 @@ -// openbox-menu - a dynamic menu for openbox -// Copyright (C) 2010-15 Fabrice THIROUX -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; version 3 of the License. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -// MA 02110-1301, USA. - -#include -#include -#include -#ifdef WITH_ICONS - #include -#endif -#include -#include -#include - -#include "openbox-menu.h" - -GMainLoop *loop = NULL; -#ifdef WITH_ICONS - GtkIconTheme *icon_theme; -#endif - -/* from lxsession */ -void sig_term_handler (int sig) -{ - g_warning ("Aborting"); - g_main_loop_quit (loop); -} - -/****f* openbox-menu/get_default_application_menu - * FUNCTION - * Try to determine which menu file to use if none defined by user. - * XDG_MENU_PREFIX variable exists, it is used to prefix menu name. - * - * RETURN VALUE - * a char that need to be freed by caller. - ****/ -gchar * -get_default_application_menu (void) -{ - gchar menu[APPMENU_SIZE]; - - gchar *xdg_prefix = getenv("XDG_MENU_PREFIX"); - if (xdg_prefix) - { - g_snprintf (menu, APPMENU_SIZE, "%sapplications.menu", xdg_prefix); - } - else - g_strlcpy (menu, "applications.menu", APPMENU_SIZE); - - return strdup (menu); -} - -/****f* openbox-menu/check_application_menu - * FUNCTION - * Test if menu file exists. - * - * PARAMETERS - * * menu, a string containing the filename of the menu - * - * RETURN VALUE - * FALSE if menu file is not found. TRUE otherwise. - * - * NOTES - * User custom menu file can be used if XDG_CONFIG_DIRS is set, i.g - * 'export XDG_CONFIG_DIRS="$HOME/.config/:/etc/xdg/" to use - * menu file located in $HOME/menus or /etc/xdg/ directories. - ****/ -gboolean -check_application_menu (gchar *menu) -{ - const gchar * const *dir; - gchar *menu_path; - - for (dir = g_get_system_config_dirs(); *dir ; dir++) - { - menu_path = g_build_filename (*dir, "menus", menu, NULL); - if (g_file_test (menu_path, G_FILE_TEST_EXISTS)) - { - g_free (menu_path); - return TRUE; - } - - g_free (menu_path); - } - - return FALSE; -} - -OB_Menu * -configure (int argc, char **argv) -{ - GError *error = NULL; - gboolean comment = FALSE; - gchar *terminal_cmd = NULL; - gboolean persistent = FALSE; - gboolean show_gnome = FALSE; - gboolean show_kde = FALSE; - gboolean show_xfce = FALSE; - gboolean show_rox = FALSE; - gboolean show_unknown = FALSE; - gboolean no_icons = FALSE; - gchar *template = NULL; - gboolean sn = FALSE; - gchar *output = NULL; - gchar **app_menu = NULL; - - GOptionEntry entries[] = { - { "comment", 'c', 0, G_OPTION_ARG_NONE, &comment, - "Show generic name instead of application name", NULL }, - { "terminal", 't', 0, G_OPTION_ARG_STRING, &terminal_cmd, - "Terminal command (default xterm -e)", "cmd" }, - { "gnome", 'g', 0, G_OPTION_ARG_NONE, &show_gnome, - "Show GNOME entries", NULL }, - { "kde", 'k', 0, G_OPTION_ARG_NONE, &show_kde, - "Show KDE entries", NULL }, - { "xfce", 'x', 0, G_OPTION_ARG_NONE, &show_xfce, - "Show XFCE entries", NULL }, - { "rox", 'r', 0, G_OPTION_ARG_NONE, &show_rox, - "Show ROX entries", NULL }, - { "unknown", 'u', 0, G_OPTION_ARG_NONE, &show_unknown, - "Show Unknown deskstop entries", NULL }, - { "persistent",'p', 0, G_OPTION_ARG_NONE, &persistent, - "stay active", NULL }, - { "sn", 's', 0, G_OPTION_ARG_NONE, &sn, - "Enable startup notification", NULL }, - { "output", 'o', 0, G_OPTION_ARG_STRING, &output, - "file to write data to", NULL }, - { "template", 'T', 0, G_OPTION_ARG_STRING, &template, - "Use filename as template for openbox-menu output", NULL }, - { "noicons", 'i', 0, G_OPTION_ARG_NONE, &no_icons, - "Don't display icons in menu", NULL }, - { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &app_menu, - NULL, "[file.menu]" }, - {NULL} - }; - GOptionContext *help_context = NULL; - - help_context = g_option_context_new (" - Openbox menu generator " VERSION); - g_option_context_set_help_enabled (help_context, TRUE); - g_option_context_add_main_entries (help_context, entries, NULL); - g_option_context_parse (help_context, &argc, &argv, &error); - - if (error) - { - g_warning ("%s\n", error->message); - g_error_free (error); - return NULL; - } - - OB_Menu *context = g_slice_new0 (OB_Menu); - - if (output) - context->output = g_build_filename (g_get_user_cache_dir (), output, NULL); - else - context->output = NULL; - - // We add extra desktop entries to display. - // Our current desktop is set when menu_cache has loaded its own cache. - // (likely in menu_display function). - if (show_gnome) context->show_flag |= SHOW_IN_GNOME; - if (show_kde) context->show_flag |= SHOW_IN_KDE; - if (show_xfce) context->show_flag |= SHOW_IN_XFCE; - if (show_rox) context->show_flag |= SHOW_IN_ROX; - if (show_unknown) context->show_flag |= 1 << N_KNOWN_DESKTOPS; - - if (terminal_cmd) - context->terminal_cmd = terminal_cmd; - else - context->terminal_cmd = TERMINAL_CMD; - - if (comment) - context->comment = TRUE; - - if (sn) - context->sn = TRUE; - - if (no_icons) - context->no_icons = TRUE; - - if (persistent) - context->persistent = TRUE; - - if (!app_menu) - context->menu_file = get_default_application_menu(); - else - context->menu_file = strdup (*app_menu); - - if (template) - context->template = template; - - g_option_context_free (help_context); - - return context; -} - -guint -run (OB_Menu *context) -{ - gpointer reload_notify_id = NULL; - MenuCache *menu_cache = NULL; - - g_unsetenv("XDG_MENU_PREFIX"); // For unknow reason, it doesn't work when it is set. - - if (context->persistent) /* persistent mode */ - { - // No need to get sync lookup. The callback function will be called - // when menu-cache is ready. - menu_cache = menu_cache_lookup (context->menu_file); - if (!menu_cache) - { - g_warning ("Cannot connect to menu-cache :/"); - return MENU_CACHE_ERROR; - } - - // menucache used to reload the cache after a call to menu_cache_lookup* () - // It's not true anymore with version >= 0.4.0. - reload_notify_id = menu_cache_add_reload_notify (menu_cache, - (MenuCacheReloadNotify) menu_display, - context); - - // install signals handler - signal (SIGTERM, sig_term_handler); - signal (SIGINT, sig_term_handler); - - // run main loop - loop = g_main_loop_new (NULL, FALSE); - g_main_loop_run (loop); - g_main_loop_unref (loop); - - menu_cache_remove_reload_notify (menu_cache, reload_notify_id); - } - else - { /* single shot */ - // wait for the menu to get ready - menu_cache = menu_cache_lookup_sync (context->menu_file); - if (!menu_cache ) - { - g_warning ("Cannot connect to menu-cache :/"); - return MENU_CACHE_ERROR; - } - - // display the menu anyway - menu_display (menu_cache, context); - } - - menu_cache_unref (menu_cache); - - // return error code set in callback function. - return context->code; -} - -void -context_free (OB_Menu *context) -{ - if (context->output) - g_free (context->output); - - if (context->menu_file) - g_free (context->menu_file); - - g_slice_free (OB_Menu, context); -} - - -int -main (int argc, char **argv) -{ - OB_Menu *ob_context; - - setlocale (LC_ALL, ""); - -#ifdef WITH_ICONS - gtk_init (&argc, &argv); - icon_theme = gtk_icon_theme_get_default (); -#endif - - if ((ob_context = configure (argc, argv)) == NULL) - return CONFIG_ERROR; - - if (!check_application_menu (ob_context->menu_file)) - { - g_print ("File %s doesn't exist. Can't create menu.\n", ob_context->menu_file); - return LOOKUP_ERROR; - } - - guint ret = run (ob_context); - context_free (ob_context); - return ret; -} diff --git a/ob_display.c b/ob_display.c deleted file mode 100644 index 7d64a3f..0000000 --- a/ob_display.c +++ /dev/null @@ -1,252 +0,0 @@ -// ob_display.c - this file is part of openbox-menu -// Copyright (C) 2010-15 Fabrice THIROUX -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; version 3 of the License. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -// MA 02110-1301, USA. - -#include "openbox-menu.h" - -const gchar *default_template = - "" - "" - "%MENU%\n"; - -/****f* ob_display/menu_directory - * FUNCTION - * create a menu entry for a directory. - * - * NOTES - * this menu entry has to be closed by "". - ****/ -void -menu_directory (MenuCacheApp *dir, OB_Menu *context) -{ - gchar *dir_id = safe_name (menu_cache_item_get_id (MENU_CACHE_ITEM(dir))); - gchar *dir_name = safe_name (menu_cache_item_get_name (MENU_CACHE_ITEM(dir))); - -#ifdef WITH_ICONS - if (!context->no_icons) - { - gchar *dir_icon = item_icon_path (MENU_CACHE_ITEM(dir)); - - g_string_append_printf (context->builder, - "\n", - dir_id, dir_name, dir_icon); - g_free (dir_icon); - } - else -#endif - { - g_string_append_printf (context->builder, - "\n", - dir_id, dir_name); - } - - g_free (dir_id); - g_free (dir_name); -} - -/****f* ob_display/menu_application - * FUNCTION - * create a menu entry for an application. - ****/ -void -menu_application (MenuCacheApp *app, OB_Menu *context) -{ - gchar *exec_name = NULL; - gchar *exec_icon = NULL; - gchar *exec_cmd = NULL; - - /* is comment (description) or name displayed ? */ - if (context->comment && menu_cache_item_get_comment (MENU_CACHE_ITEM(app))) - exec_name = safe_name (menu_cache_item_get_comment (MENU_CACHE_ITEM(app))); - else - exec_name = safe_name (menu_cache_item_get_name (MENU_CACHE_ITEM(app))); - - exec_cmd = clean_exec (app); - -#ifdef WITH_ICONS - if (!context->no_icons) - { - exec_icon = item_icon_path (MENU_CACHE_ITEM(app)); - g_string_append_printf (context->builder, - "", - exec_name, - exec_icon); - } - else -#endif - { - g_string_append_printf (context->builder, - "", - exec_name); - } - - if (context->sn && menu_cache_app_get_use_sn (app)) - g_string_append (context->builder, - "yes"); - - if (menu_cache_app_get_use_terminal (app)) - g_string_append_printf (context->builder, - "\n\n", - context->terminal_cmd, - exec_cmd); - else - g_string_append_printf (context->builder, - "\n\n", - exec_cmd); - - g_free (exec_name); - g_free (exec_icon); - g_free (exec_cmd); -} - -/****f* ob_display/menu_generate - * FUNCTION - * main routine of menu creation. - * - * NOTES - * It calls itself when 'dir' type is MENU_CACHE_TYPE_DIR. - ****/ -void -menu_generate (MenuCacheDir *dir, OB_Menu *context) -{ - GSList *l = NULL; - - for (l = menu_cache_dir_get_children (dir); l; l = l->next) - switch ((guint) menu_cache_item_get_type (MENU_CACHE_ITEM(l->data))) - { - case MENU_CACHE_TYPE_DIR: - menu_directory (l->data, context); - menu_generate (MENU_CACHE_DIR(l->data), context); - g_string_append (context->builder, "\n"); - break; - - case MENU_CACHE_TYPE_SEP: - g_string_append (context->builder, "\n"); - break; - - case MENU_CACHE_TYPE_APP: - if (app_is_visible (MENU_CACHE_APP(l->data), context->show_flag)) - menu_application (l->data, context); - } -} - - -/****f* ob_display/get_header_footer_from_template - * FUNCTION - * Get header and footer string from a template file. If no template - * file provided, the default template will bu used. - * - * INPUTS - * * template - * - * RETURN VALUE - * * a pointer to an array of strings that needs to be freed with g_strfreev. - ****/ -gchar **get_header_footer_from_template (gchar *template) -{ - gchar *content = NULL; - gchar **tokens = NULL; - - if (template && g_file_get_contents (template, &content, NULL, NULL)) - { - tokens = g_strsplit (content, "%MENU%", 2); - g_free (content); - } - else - { - tokens = g_strsplit (default_template, "%MENU%", 2); - } - return tokens; -} - -/****f* ob_display/menu_display - * FUNCTION - * it begins and closes the menu content, write it into a file or - * display it. - * - * INPUTS - * * menu - * * file, the filename where the menu content should be written to. - * If file is 'NULL' then the menu content is displayed. - * - * RETURN VALUE - * Nothing. A MenuCacheReloadNotify callback returns void. - * - * NOTES - * A 16 KiB GString is allocated for the content of the pipemenu. - * This should be enough prevent too many allocations while building - * the XML. - * - * The size of the XML file created is around 8 KB in my computer but - * I don't have a lot of applications installed. - ****/ -void -menu_display (MenuCache *menu, OB_Menu *context) -{ - gchar **template_parts = NULL; - - MenuCacheDir *dir = menu_cache_dup_root_dir (menu); - if (G_UNLIKELY(dir == NULL)) - { - g_warning ("Can't get menu root directory"); - context->code = MENU_DIR_ERROR; - return; - } - - // Desktops are dynamically detected by menu_cache when reloading - // its cache. It is now time to add our desktop to the show_flag in - // the application context. - add_current_desktop_to_context (menu, context); - - GSList *l = menu_cache_dir_get_children (dir); - - if (g_slist_length (l) != 0) { - context->builder = g_string_sized_new (16 * 1024); - - template_parts = get_header_footer_from_template (context->template); - // TODO: check if template_parts array contains 2 strings. - - g_string_append (context->builder, template_parts[0]); // add header - menu_generate (dir, context); - g_string_append (context->builder, template_parts[1]); // add footer - - g_strfreev (template_parts); - - gchar *buff = g_string_free (context->builder, FALSE); - - /* Has menu content to be saved in a file ? */ - if (context->output) - { - if (!g_file_set_contents (context->output, buff, -1, NULL)) - g_warning ("Can't write to %s\n", context->output); - else - g_message ("wrote to %s", context->output); - } - else /* No, so it's displayed on screen */ - g_print ("%s", buff); - - g_free (buff); - } - else - { - g_warning ("Menu seems to be empty. Check openbox-menu parameters."); - context->code = MENU_EMPTY_ERROR; - } - - menu_cache_item_unref (MENU_CACHE_ITEM(dir)); -} diff --git a/openbox-menu.h b/openbox-menu.h deleted file mode 100644 index 11ee66e..0000000 --- a/openbox-menu.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * openbox-menu.h - this file is part of openbox-menu - * Copyright (C) 2010-15 Fabrice THIROUX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published - * by the Free Software Foundation; version 3.0 only. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301, USA. - */ - -#ifndef __OPENBOXMENU_APP__ -#define __OPENBOXMENU_APP__ -#include - -#define VERSION "0.8.0" -#define APPMENU_SIZE 30 -#define TERMINAL_CMD "xterm -e" - -#ifndef __VERSION_MINOR // since menu-cache 0.5.0. -#warning "If you are running a 0.3.x version of libmenu-cache, you need to compile the 3.6.7 version of openbox-menu" -#endif - -typedef enum { - NO_ERROR = 0, - CONFIG_ERROR, - MENU_DIR_ERROR, - MENU_EMPTY_ERROR, - LOOKUP_ERROR, - MENU_CACHE_ERROR, -} OBM_Error; - - -typedef struct { - /* Configuration */ - gchar *output; - guint32 show_flag; - GString *builder; /* */ - gchar *terminal_cmd; /* command to launch program in a terminal */ - gboolean comment; /* display description instead of name */ - gboolean sn; /* startup notification */ - gboolean no_icons; /* icons disabled */ - gboolean persistent; - gchar *menu_file; - gchar *template; - guint code; -} OB_Menu; - -guint app_is_visible (MenuCacheApp *, guint32); -gchar *clean_exec (MenuCacheApp *); -gchar *safe_name (const char *); -gchar *item_icon_path (MenuCacheItem*); -guint32 get_current_desktop_flag (); -void add_current_desktop_to_context (MenuCache *, OB_Menu *); - -void menu_display (MenuCache *, OB_Menu *); - -#endif // __OPENBOXMENU_APP__ diff --git a/src/context.c b/src/context.c new file mode 100644 index 0000000..aab8e9a --- /dev/null +++ b/src/context.c @@ -0,0 +1,104 @@ +#include "openbox-menu.h" + +OB_Menu* +context_new() +{ + OB_Menu* context = g_slice_new0 (OB_Menu); + + return context; +} + +/****** TERMINAL ******/ + +void +context_set_terminal_cmd (OB_Menu* ctx, gchar* cmd) +{ + ctx->terminal_cmd = cmd; +} + + +gchar* +context_get_terminal_cmd (OB_Menu* ctx) +{ + return ctx->terminal_cmd; +} + +/****** DESCRIPTION DISPLAY ******/ + +void context_set_comment(OB_Menu* ctx, gboolean val) +{ + ctx->comment = val; +} + + +gboolean context_get_comment(OB_Menu* ctx) +{ + return ctx->comment; +} + +/****** SHOW FLAG ******/ + +void +context_set_desktop_flag (OB_Menu* ctx, int flag) +{ + ctx->show_flag = flag; +} + + +void +context_add_desktop_flag (OB_Menu* ctx, int flag) +{ + ctx->show_flag |= flag; +} + + +int +context_get_desktop_flag (OB_Menu* ctx) +{ + return ctx->show_flag; +} + + +/****** SYSTEM NOTIFICATION ******/ +void +context_set_sn (OB_Menu* ctx, int flag) +{ + ctx->sn = flag; +} + + +gboolean +context_get_sn (OB_Menu* ctx) +{ + return ctx->sn; +} + + +/****** PERSISTENT MODE ******/ + +void +context_set_persistent (OB_Menu* ctx, gboolean flag) +{ + ctx->persistent = flag; +} + +gboolean +context_get_persistent (OB_Menu* ctx) +{ + return ctx->persistent; +} + + +/****** DESTRUCTOR ******/ + +void +context_free (OB_Menu *context) +{ + if (context->output) + g_free (context->output); + + if (context->menu_file) + g_free (context->menu_file); + + g_slice_free (OB_Menu, context); +} diff --git a/src/menu.c b/src/menu.c new file mode 100644 index 0000000..5b7e8f8 --- /dev/null +++ b/src/menu.c @@ -0,0 +1,258 @@ +// openbox-menu - a dynamic menu for openbox +// Copyright (C) 2010-15 Fabrice THIROUX +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 3 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. + +#include +#include +#include +#include +#include +#include + +#include "openbox-menu.h" + +GMainLoop *loop = NULL; + +/* from lxsession */ +void sig_term_handler (int sig) +{ + g_warning ("Aborting"); + g_main_loop_quit (loop); +} + + + +/****f* openbox-menu/check_application_menu + * FUNCTION + * Test if menu file exists. + * + * PARAMETERS + * * menu, a string containing the filename of the menu + * + * RETURN VALUE + * FALSE if menu file is not found. TRUE otherwise. + * + * NOTES + * User custom menu file can be used if XDG_CONFIG_DIRS is set, i.g + * 'export XDG_CONFIG_DIRS="$HOME/.config/:/etc/xdg/" to use + * menu file located in $HOME/menus or /etc/xdg/ directories. + ****/ +gboolean +check_application_menu (gchar *menu) +{ + const gchar * const *dir; + gchar *menu_path; + + for (dir = g_get_system_config_dirs(); *dir ; dir++) + { + menu_path = g_build_filename (*dir, "menus", menu, NULL); + if (g_file_test (menu_path, G_FILE_TEST_EXISTS)) + { + g_free (menu_path); + return TRUE; + } + + g_free (menu_path); + } + + return FALSE; +} + +OB_Menu * +configure (int argc, char **argv) +{ + GError *error = NULL; + gboolean comment = FALSE; + gchar *terminal_cmd = NULL; + gboolean persistent = FALSE; + gboolean show_gnome = FALSE; + gboolean show_kde = FALSE; + gboolean show_xfce = FALSE; + gboolean show_rox = FALSE; + gboolean show_unknown = FALSE; + gboolean no_icons = FALSE; + gchar *template = NULL; + gboolean sn = FALSE; + gchar *output = NULL; + gchar **app_menu = NULL; + + GOptionEntry entries[] = { + { "comment", 'c', 0, G_OPTION_ARG_NONE, &comment, + "Show generic name instead of application name", NULL }, + { "terminal", 't', 0, G_OPTION_ARG_STRING, &terminal_cmd, + "Terminal command (default xterm -e)", "cmd" }, + { "gnome", 'g', 0, G_OPTION_ARG_NONE, &show_gnome, + "Show GNOME entries", NULL }, + { "kde", 'k', 0, G_OPTION_ARG_NONE, &show_kde, + "Show KDE entries", NULL }, + { "xfce", 'x', 0, G_OPTION_ARG_NONE, &show_xfce, + "Show XFCE entries", NULL }, + { "rox", 'r', 0, G_OPTION_ARG_NONE, &show_rox, + "Show ROX entries", NULL }, + { "unknown", 'u', 0, G_OPTION_ARG_NONE, &show_unknown, + "Show Unknown deskstop entries", NULL }, + { "persistent",'p', 0, G_OPTION_ARG_NONE, &persistent, + "stay active", NULL }, + { "sn", 's', 0, G_OPTION_ARG_NONE, &sn, + "Enable startup notification", NULL }, + { "output", 'o', 0, G_OPTION_ARG_STRING, &output, + "file to write data to", NULL }, + { "template", 'T', 0, G_OPTION_ARG_STRING, &template, + "Use filename as template for openbox-menu output", NULL }, + { "noicons", 'i', 0, G_OPTION_ARG_NONE, &no_icons, + "Don't display icons in menu", NULL }, + { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &app_menu, + NULL, "[file.menu]" }, + {NULL} + }; + GOptionContext *help_context = NULL; + + help_context = g_option_context_new (" - Openbox menu generator " VERSION); + g_option_context_set_help_enabled (help_context, TRUE); + g_option_context_add_main_entries (help_context, entries, NULL); + g_option_context_parse (help_context, &argc, &argv, &error); + + if (error) + { + g_warning ("%s\n", error->message); + g_error_free (error); + return NULL; + } + + OB_Menu *context = context_new(); + + if (output) + context->output = g_build_filename (g_get_user_cache_dir (), output, NULL); + else + context->output = NULL; + + // We add extra desktop entries to display. + // Our current desktop is set when menu_cache has loaded its own cache. + // (likely in menu_display function). + if (show_gnome) context_add_desktop_flag(context, SHOW_IN_GNOME); + if (show_kde) context_add_desktop_flag(context, SHOW_IN_KDE); + if (show_xfce) context_add_desktop_flag(context, SHOW_IN_XFCE); + if (show_rox) context_add_desktop_flag(context, SHOW_IN_GNOME); + if (show_unknown) context_add_desktop_flag(context, 1 << N_KNOWN_DESKTOPS); + + context_set_terminal_cmd (context, (terminal_cmd) ? terminal_cmd : TERMINAL_CMD); + + context_set_comment(context, comment); + + if (sn) + context->sn = TRUE; + + if (no_icons) + context->no_icons = TRUE; + + if (persistent) + context->persistent = TRUE; + + if (!app_menu) + context->menu_file = get_default_application_menu(); + else + context->menu_file = strdup (*app_menu); + + if (template) + context->template = template; + + g_option_context_free (help_context); + + return context; +} + +guint +run (OB_Menu *context) +{ + gpointer reload_notify_id = NULL; + MenuCache *menu_cache = NULL; + + g_unsetenv("XDG_MENU_PREFIX"); // For unknow reason, it doesn't work when it is set. + + if (context->persistent) /* persistent mode */ + { + // No need to get sync lookup. The callback function will be called + // when menu-cache is ready. + menu_cache = menu_cache_lookup (context->menu_file); + if (!menu_cache) + { + g_warning ("Cannot connect to menu-cache :/"); + return MENU_CACHE_ERROR; + } + + // menucache used to reload the cache after a call to menu_cache_lookup* () + // It's not true anymore with version >= 0.4.0. + reload_notify_id = menu_cache_add_reload_notify (menu_cache, + (MenuCacheReloadNotify) menu_display, + context); + + // install signals handler + signal (SIGTERM, sig_term_handler); + signal (SIGINT, sig_term_handler); + + // run main loop + loop = g_main_loop_new (NULL, FALSE); + g_main_loop_run (loop); + g_main_loop_unref (loop); + + menu_cache_remove_reload_notify (menu_cache, reload_notify_id); + } + else + { /* single shot */ + // wait for the menu to get ready + menu_cache = menu_cache_lookup_sync (context->menu_file); + if (!menu_cache ) + { + g_warning ("Cannot connect to menu-cache :/"); + return MENU_CACHE_ERROR; + } + + // display the menu anyway + menu_display (menu_cache, context); + } + + menu_cache_unref (menu_cache); + + // return error code set in callback function. + return context->code; +} + + +int +main (int argc, char **argv) +{ + OB_Menu *ob_context; + + setlocale (LC_ALL, ""); + +#ifdef WITH_ICONS + gtk_init (&argc, &argv); + icon_theme = gtk_icon_theme_get_default (); +#endif + + if ((ob_context = configure (argc, argv)) == NULL) + return CONFIG_ERROR; + + if (!check_application_menu (ob_context->menu_file)) + { + g_print ("File $XDG_CONFIG_DIRS/%s doesn't exist. Can't create menu.\n", ob_context->menu_file); + return LOOKUP_ERROR; + } + + guint ret = run (ob_context); + context_free (ob_context); + return ret; +} diff --git a/src/ob_display.c b/src/ob_display.c new file mode 100644 index 0000000..afb3588 --- /dev/null +++ b/src/ob_display.c @@ -0,0 +1,282 @@ +// ob_display.c - this file is part of openbox-menu +// Copyright (C) 2010-15 Fabrice THIROUX +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 3 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. + +#include "openbox-menu.h" + +const gchar *default_template = + "" + "" + "%MENU%\n"; + +/****f* ob_display/menu_directory + * FUNCTION + * create a menu entry for a directory. + * + * NOTES + * this menu entry has to be closed by "". + ****/ +void +menu_directory (MenuCacheApp *dir, OB_Menu *context) +{ + gchar *dir_id = safe_name (menu_cache_item_get_id (MENU_CACHE_ITEM(dir))); + gchar *dir_name = safe_name (menu_cache_item_get_name (MENU_CACHE_ITEM(dir))); + +#ifdef WITH_ICONS + if (!context->no_icons) + { + gchar *dir_icon = item_icon_path (MENU_CACHE_ITEM(dir)); + + g_string_append_printf (context->builder, + "\n", + dir_id, dir_name, dir_icon); + g_free (dir_icon); + } + else +#endif + { + g_string_append_printf (context->builder, + "\n", + dir_id, dir_name); + } + + g_free (dir_id); + g_free (dir_name); +} + +gchar* get_item_comment (MenuCacheItem*, gboolean); + +gchar* +get_item_name (MenuCacheItem* item, gboolean alternate) +{ + char* s = safe_name (menu_cache_item_get_name(item)); + + if (s == NULL && alternate == TRUE) { + return get_item_comment(item, FALSE); + } + + return s; +} + + +gchar* +get_item_comment (MenuCacheItem* item, gboolean alternate) +{ + char* s = safe_name (menu_cache_item_get_comment(item)); + + if (s == NULL && alternate == TRUE) { + return get_item_name(item, FALSE); + } + + return s; +} + + +/****f* ob_display/menu_application + * FUNCTION + * create a menu entry for an application. + ****/ +void +menu_application (MenuCacheApp *app, OB_Menu *context) +{ + gchar *exec_name = NULL; + gchar *exec_icon = NULL; + gchar *exec_cmd = NULL; + + /* is comment (description) or name displayed ? */ + exec_name = (context->comment == TRUE) ? + get_item_comment (MENU_CACHE_ITEM(app), TRUE) : get_item_name (MENU_CACHE_ITEM(app), TRUE); + + exec_cmd = clean_exec (app); + // make sure we don't process item with no exec value (issue #13). This should never happend. + if (exec_cmd == NULL){ + return; + } + +#ifdef WITH_ICONS + if (!context->no_icons) + { + exec_icon = item_icon_path (MENU_CACHE_ITEM(app)); + g_string_append_printf (context->builder, + "", + exec_name, + exec_icon); + } + else +#endif + { + g_string_append_printf (context->builder, + "", + exec_name); + } + + if (context->sn && menu_cache_app_get_use_sn (app)) + g_string_append (context->builder, + "yes"); + + if (menu_cache_app_get_use_terminal (app)) + g_string_append_printf (context->builder, + "\n\n", + context->terminal_cmd, + exec_cmd); + else + g_string_append_printf (context->builder, + "\n\n", + exec_cmd); + + g_free (exec_name); + g_free (exec_icon); + g_free (exec_cmd); +} + +/****f* ob_display/menu_generate + * FUNCTION + * main routine of menu creation. + * + * NOTES + * It calls itself when 'dir' type is MENU_CACHE_TYPE_DIR. + ****/ +void +menu_generate (MenuCacheDir *dir, OB_Menu *context) +{ + GSList *l = NULL; + + for (l = menu_cache_dir_get_children (dir); l; l = l->next) + switch ((guint) menu_cache_item_get_type (MENU_CACHE_ITEM(l->data))) + { + case MENU_CACHE_TYPE_DIR: + menu_directory (l->data, context); + menu_generate (MENU_CACHE_DIR(l->data), context); + g_string_append (context->builder, "\n"); + break; + + case MENU_CACHE_TYPE_SEP: + g_string_append (context->builder, "\n"); + break; + + case MENU_CACHE_TYPE_APP: + if (app_is_visible (MENU_CACHE_APP(l->data), context->show_flag)) + menu_application (l->data, context); + } +} + + +/****f* ob_display/get_header_footer_from_template + * FUNCTION + * Get header and footer string from a template file. If no template + * file provided, the default template will bu used. + * + * INPUTS + * * template + * + * RETURN VALUE + * * a pointer to an array of strings that needs to be freed with g_strfreev. + ****/ +gchar **get_header_footer_from_template (gchar *template) +{ + gchar *content = NULL; + gchar **tokens = NULL; + + if (template && g_file_get_contents (template, &content, NULL, NULL)) + { + tokens = g_strsplit (content, "%MENU%", 2); + g_free (content); + } + else + { + tokens = g_strsplit (default_template, "%MENU%", 2); + } + return tokens; +} + +/****f* ob_display/menu_display + * FUNCTION + * it begins and closes the menu content, write it into a file or + * display it. + * + * INPUTS + * * menu + * * file, the filename where the menu content should be written to. + * If file is 'NULL' then the menu content is displayed. + * + * RETURN VALUE + * Nothing. A MenuCacheReloadNotify callback returns void. + * + * NOTES + * A 16 KiB GString is allocated for the content of the pipemenu. + * This should be enough prevent too many allocations while building + * the XML. + * + * The size of the XML file created is around 8 KB in my computer but + * I don't have a lot of applications installed. + ****/ +void +menu_display (MenuCache *menu, OB_Menu *context) +{ + gchar **template_parts = NULL; + + MenuCacheDir *dir = menu_cache_dup_root_dir (menu); + if (G_UNLIKELY(dir == NULL)) + { + g_warning ("Can't get menu root directory"); + context->code = MENU_DIR_ERROR; + return; + } + + // Desktops are dynamically detected by menu_cache when reloading + // its cache. It is now time to add our desktop to the show_flag in + // the application context. + add_current_desktop_to_context (menu, context); + + GSList *l = menu_cache_dir_get_children (dir); + + if (g_slist_length (l) != 0) { + context->builder = g_string_sized_new (16 * 1024); + + template_parts = get_header_footer_from_template (context->template); + // TODO: check if template_parts array contains 2 strings. + + g_string_append (context->builder, template_parts[0]); // add header + menu_generate (dir, context); + g_string_append (context->builder, template_parts[1]); // add footer + + g_strfreev (template_parts); + + gchar *buff = g_string_free (context->builder, FALSE); + + /* Has menu content to be saved in a file ? */ + if (context->output) + { + if (!g_file_set_contents (context->output, buff, -1, NULL)) + g_warning ("Can't write to %s\n", context->output); + else + g_message ("wrote to %s", context->output); + } + else /* No, so it's displayed on screen */ + g_print ("%s", buff); + + g_free (buff); + } + else + { + g_warning ("Menu seems to be empty. Check openbox-menu parameters."); + context->code = MENU_EMPTY_ERROR; + } + + menu_cache_item_unref (MENU_CACHE_ITEM(dir)); +} diff --git a/src/openbox-menu.h b/src/openbox-menu.h new file mode 100644 index 0000000..12f83a5 --- /dev/null +++ b/src/openbox-menu.h @@ -0,0 +1,89 @@ +/* + * openbox-menu.h - this file is part of openbox-menu + * Copyright (C) 2010-15 Fabrice THIROUX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifndef __OPENBOXMENU_APP__ +#define __OPENBOXMENU_APP__ +#include + +#ifdef WITH_ICONS + #include +#endif + +#define VERSION "0.8.0" +#define APPMENU_SIZE 30 +#define TERMINAL_CMD "xterm -e" + +#ifndef __VERSION_MINOR // since menu-cache 0.5.0. +#warning "If you are running a 0.3.x version of libmenu-cache, you need to compile the 3.6.7 version of openbox-menu" +#endif + +typedef enum { + NO_ERROR = 0, + CONFIG_ERROR, + MENU_DIR_ERROR, + MENU_EMPTY_ERROR, + LOOKUP_ERROR, + MENU_CACHE_ERROR, +} OBM_Error; + + +typedef struct { + /* Configuration */ + gchar *output; + guint32 show_flag; + GString *builder; /* */ + gchar *terminal_cmd; /* command to launch program in a terminal */ + gboolean comment; /* display description instead of name */ + gboolean sn; /* startup notification */ + gboolean no_icons; /* icons disabled */ + gboolean persistent; + gchar *menu_file; + gchar *template; + guint code; +} OB_Menu; + + +gchar *get_default_application_menu (); +guint app_is_visible (MenuCacheApp *, guint32); +gchar *clean_exec (MenuCacheApp *); +gchar *safe_name (const char *); +gchar *item_icon_path (MenuCacheItem*); +guint32 get_current_desktop_flag (); +void add_current_desktop_to_context (MenuCache *, OB_Menu *); + +void menu_display (MenuCache *, OB_Menu *); + +OB_Menu *context_new(); +void context_set_terminal_cmd (OB_Menu*, gchar*); +gchar *context_get_terminal_cmd (OB_Menu*); +void context_set_desktop_flag (OB_Menu*, int); +void context_add_desktop_flag (OB_Menu*, int); +int context_get_desktop_flag (OB_Menu*); +void context_set_persistent (OB_Menu*, gboolean); +void context_set_persistent (OB_Menu*, gboolean); +void context_set_comment (OB_Menu*, gboolean); +gboolean context_get_persistent (OB_Menu*); + +void context_free(OB_Menu *); + +#ifdef WITH_ICONS + GtkIconTheme *icon_theme; +#endif + +#endif // __OPENBOXMENU_APP__ diff --git a/src/utils.c b/src/utils.c new file mode 100644 index 0000000..987eb5b --- /dev/null +++ b/src/utils.c @@ -0,0 +1,255 @@ +// utils.c - this file is part of openbox-menu +// Copyright (C) 2010-15 Fabrice THIROUX +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 3 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. + +#include +#ifdef WITH_ICONS + #include +#endif +#include +#include + +#include "openbox-menu.h" + +/****f* utils/get_default_application_menu + * FUNCTION + * Try to determine which menu file to use if none defined by user. + * XDG_MENU_PREFIX variable exists, it is used to prefix menu name. + * + * RETURN VALUE + * a char that need to be freed by caller. + ****/ +gchar * +get_default_application_menu (void) +{ + gchar menu[APPMENU_SIZE]; + + gchar *xdg_prefix = getenv("XDG_MENU_PREFIX"); + if (xdg_prefix) + { + g_snprintf (menu, APPMENU_SIZE, "%sapplications.menu", xdg_prefix); + } + else + g_strlcpy (menu, "applications.menu", APPMENU_SIZE); + + return strdup (menu); +} + +/****f* utils/safe_name + * FUNCTION + * Convert &, <, > and " signs to html entities + * + * OUTPUT + * A gchar that needs to be freed. + ****/ +gchar * +safe_name (const char *name) +{ + if (name == NULL) + return NULL; + + GString *cmd = g_string_sized_new (256); + + for (;*name; ++name) + { + switch(*name) + { + case '&': + g_string_append (cmd, "&"); + break; + case '<': + g_string_append (cmd, "<"); + break; + case '>': + g_string_append (cmd, ">"); + break; + case '"': + g_string_append (cmd, """); + break; + default: + g_string_append_c (cmd, *name); + } + } + return g_string_free (cmd, FALSE); +} + + +/****f* utils/clean_exec + * FUNCTION + * Remove %f, %F, %u, %U, %i, %c, %k from exec field. + * None of theses codes are interesting to manage here. + * %i, %c and %k codes are implemented but don't ask why we need them. :) + * + * OUTPUT + * A gchar that needs to be freed. + * + * NOTES + * %d, %D, %n, %N, %v and %m are deprecated and should be removed. + * + * SEE ALSO + * http://standards.freedesktop.org/desktop-entry-spec/latest/ar01s06.html + ****/ +gchar * +clean_exec (MenuCacheApp *app) +{ + gchar *filepath = NULL; + const char *exec = menu_cache_app_get_exec (MENU_CACHE_APP(app)); + + g_return_val_if_fail(exec != NULL, NULL); + + GString *cmd = g_string_sized_new (64); + + for (;*exec; ++exec) + { + if (*exec == '%') + { + ++exec; + switch (*exec) + { + /* useless and commonly used codes */ + case 'u': + case 'U': + case 'f': + case 'F': break; + /* deprecated codes */ + case 'd': + case 'D': + case 'm': + case 'n': + case 'N': + case 'v': break; + /* Other codes, more or less pointless to implement */ + case 'c': + g_string_append (cmd, menu_cache_item_get_name (MENU_CACHE_ITEM(app))); + break; +#if WITH_ICONS + case 'i': + if (item_icon_path (MENU_CACHE_ITEM(app))) + { + g_string_append_printf (cmd, "--icon %s", + item_icon_path (MENU_CACHE_ITEM(app))); + } + break; +#endif + case 'k': + filepath = menu_cache_item_get_file_path (MENU_CACHE_ITEM(app)); + if (filepath) + { + g_string_append (cmd, filepath); + g_free (filepath); + } + break; + /* It was not in the freedesktop specification. */ + default: + g_string_append_c (cmd, '%'); + g_string_append_c (cmd, *exec); + break; + } + } + else + g_string_append_c (cmd, *exec); + } + return g_strchomp (g_string_free (cmd, FALSE)); +} + + +#if WITH_ICONS + +extern GtkIconTheme *icon_theme; + +/****f* utils/item_icon_path + * OUTPUT + * return the path for the themed icon if item. + * If no icon found, it returns the "empty" icon path. + * + * The returned string should be freed when no longer needed + * + * NOTES + * Imlib2, used by OpenBox to display icons, doesn't load SVG graphics. + * We have to use GTK_ICON_LOOKUP_NO_SVG flag to look up icons. + * + * TODO + * The "2nd fallback" is annoying, I have to think about this. + ****/ +gchar * +item_icon_path (MenuCacheItem *item) +{ + GtkIconInfo *icon_info = NULL; + gchar *icon = NULL; + gchar *tmp_name = NULL; + + const gchar *name = menu_cache_item_get_icon (MENU_CACHE_ITEM(item)); + + if (name) + { + if (g_path_is_absolute (name)) + return g_strdup (name); + + /* We remove the file extension as gtk_icon_theme_lookup_icon can't + * lookup a theme icon for, ie, 'geany.png'. It has to be 'geany'. + */ + tmp_name = strndup (name, strrchr (name, '.') - name); + #ifdef WITH_SVG + icon_info = gtk_icon_theme_lookup_icon (icon_theme, tmp_name, 16, GTK_ICON_LOOKUP_GENERIC_FALLBACK); + #else + icon_info = gtk_icon_theme_lookup_icon (icon_theme, tmp_name, 16, GTK_ICON_LOOKUP_NO_SVG | GTK_ICON_LOOKUP_GENERIC_FALLBACK); + #endif + g_free (tmp_name); + } + + if (!icon_info) /* 2nd fallback */ + icon_info = gtk_icon_theme_lookup_icon (icon_theme, "empty", 16, GTK_ICON_LOOKUP_NO_SVG); + + icon = g_strdup (gtk_icon_info_get_filename (icon_info)); + gtk_icon_info_free (icon_info); + + return icon; +} +#endif /* WITH_ICONS */ + + +guint +app_is_visible(MenuCacheApp *app, guint32 de_flag) +{ + gint32 flags = menu_cache_app_get_show_flags (app); + + if (flags < 0) + return !(- flags & de_flag); + else + return menu_cache_app_get_is_visible(MENU_CACHE_APP(app), de_flag); +} + +const char* get_desktop_name() { + const gchar *desktop = g_getenv ("XDG_CURRENT_DESKTOP"); + if (desktop) + return desktop; + + desktop = g_getenv ("DESKTOP_SESSION"); + if (desktop) + return desktop; + + // We return nothing. + return NULL; +} + +void +add_current_desktop_to_context (MenuCache *menu, OB_Menu *context) { + const char* desktop = get_desktop_name (); + if (desktop) { + context->show_flag |= menu_cache_get_desktop_env_flag(menu, desktop); + } +} + diff --git a/template b/template new file mode 100644 index 0000000..49d0097 --- /dev/null +++ b/template @@ -0,0 +1 @@ +%MENU% \ No newline at end of file diff --git a/tests/context_tests.c b/tests/context_tests.c new file mode 100644 index 0000000..3f08963 --- /dev/null +++ b/tests/context_tests.c @@ -0,0 +1,75 @@ +#include +#include + +#include "../src/openbox-menu.h" + +/* +typedef struct { + gchar *output; + guint32 show_flag; + GString *builder; / + gchar *terminal_cmd; + gboolean comment; + gboolean sn; + gboolean no_icons; + gboolean persistent; + gchar *menu_file; + gchar *template; + guint code; +} OB_Menu; +*/ + + +void test_set_desktop_flag () +{ + OB_Menu* ctx = context_new(); + context_set_desktop_flag (ctx, SHOW_IN_XFCE); + + gint flag = context_get_desktop_flag(ctx); + + g_assert_cmpint(flag, ==, SHOW_IN_XFCE); +} + + +void test_add_desktop_flag () +{ + OB_Menu* ctx = context_new(); + context_set_desktop_flag (ctx, SHOW_IN_XFCE); + context_add_desktop_flag (ctx, SHOW_IN_KDE); + + gint flag = context_get_desktop_flag(ctx); + + g_assert_cmpint(flag, ==, SHOW_IN_XFCE | SHOW_IN_KDE); +} + + +void test_set_terminal_cmd() +{ + OB_Menu* ctx = context_new(); + context_set_terminal_cmd (ctx, "xterm"); + + gchar* cmd = context_get_terminal_cmd(ctx); + + g_assert_cmpstr(cmd, ==, "xterm"); +} + + +void test_persistent_true () +{ + OB_Menu* ctx = context_new(); + context_set_persistent(ctx, TRUE); + + gboolean mode = context_get_persistent(ctx); + + g_assert_true(mode); +} + + +void test_persistent_false () +{ + OB_Menu* ctx = context_new(); + + gboolean mode = context_get_persistent(ctx); + + g_assert_false (mode); +} diff --git a/tests/tests.c b/tests/tests.c new file mode 100644 index 0000000..9280f7c --- /dev/null +++ b/tests/tests.c @@ -0,0 +1,32 @@ +#include +#include + +#include + +#include "tests.h" + + +int main (int argc, char **argv) +{ + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/Utils/safename_returns_foobar", test_safename1); + g_test_add_func ("/Utils/safename_returns_html_entities", test_safename2); + g_test_add_func ("/Utils/safename_returns_null", test_safename3); + + g_test_add_func ("/Utils/application_menu_with_prefix", test_application_menu_with_prefix); + g_test_add_func ("/Utils/application_menu_without_prefix", test_application_menu_without_prefix); + + g_test_add_func ("/Context/set_terminal_cmd", test_set_terminal_cmd); + g_test_add_func ("/Context/set_desktop_flag", test_set_desktop_flag); + g_test_add_func ("/Context/add_desktop_flag", test_add_desktop_flag); + + g_test_add_func ("/Context/set_persistent_true", test_persistent_true); + g_test_add_func ("/Context/set_persistent_false", test_persistent_false); + + + + g_test_set_nonfatal_assertions (); + + return g_test_run(); +} diff --git a/tests/tests.h b/tests/tests.h new file mode 100644 index 0000000..3b87d73 --- /dev/null +++ b/tests/tests.h @@ -0,0 +1,15 @@ +void test_safename1(); +void test_safename2(); +void test_safename3(); + +void test_application_menu_with_prefix(); +void test_application_menu_without_prefix(); + +void test_set_terminal_cmd(); +void test_set_desktop_flag(); +void test_add_desktop_flag(); + +void test_persistent_true(); +void test_persistent_false(); + + diff --git a/tests/utils_tests.c b/tests/utils_tests.c new file mode 100644 index 0000000..54b3211 --- /dev/null +++ b/tests/utils_tests.c @@ -0,0 +1,49 @@ +//#include + +#include +//#include + +#include "../src/openbox-menu.h" + + + +void test_safename1() +{ + char* filename = safe_name("foobar"); + + g_assert_cmpstr(filename, ==, "foobar"); +} + +void test_safename2() +{ + char* filename = safe_name("&<>\""); + + g_assert_cmpstr(filename, ==, "&<>""); +} + + +void test_safename3() +{ + char* filename = safe_name(NULL); + + g_assert (filename == NULL); +} + + +void test_application_menu_with_prefix() +{ + g_setenv("XDG_MENU_PREFIX", "mytest-", TRUE); + gchar* result = get_default_application_menu(); + + g_assert_cmpstr(result, ==, "mytest-applications.menu"); +} + +void test_application_menu_without_prefix() +{ + g_unsetenv("XDG_MENU_PREFIX"); + gchar* result = get_default_application_menu(); + + g_assert_cmpstr(result, ==, "applications.menu"); +} + + diff --git a/utils.c b/utils.c deleted file mode 100644 index 065ddfd..0000000 --- a/utils.c +++ /dev/null @@ -1,229 +0,0 @@ -// utils.c - this file is part of openbox-menu -// Copyright (C) 2010-15 Fabrice THIROUX -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; version 3 of the License. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -// MA 02110-1301, USA. - -#include -#ifdef WITH_ICONS - #include -#endif -#include - -#include "openbox-menu.h" - -/****f* utils/safe_name - * FUNCTION - * Convert &, <, > and " signs to html entities - * - * OUTPUT - * A gchar that needs to be freed. - ****/ -gchar * -safe_name (const char *name) -{ - g_return_val_if_fail (name != NULL, NULL); - - GString *cmd = g_string_sized_new (256); - - for (;*name; ++name) - { - switch(*name) - { - case '&': - g_string_append (cmd, "&"); - break; - case '<': - g_string_append (cmd, "<"); - break; - case '>': - g_string_append (cmd, ">"); - break; - case '"': - g_string_append (cmd, """); - break; - default: - g_string_append_c (cmd, *name); - } - } - return g_string_free (cmd, FALSE); -} - - -/****f* utils/clean_exec - * FUNCTION - * Remove %f, %F, %u, %U, %i, %c, %k from exec field. - * None of theses codes are interesting to manage here. - * %i, %c and %k codes are implemented but don't ask why we need them. :) - * - * OUTPUT - * A gchar that needs to be freed. - * - * NOTES - * %d, %D, %n, %N, %v and %m are deprecated and should be removed. - * - * SEE ALSO - * http://standards.freedesktop.org/desktop-entry-spec/latest/ar01s06.html - ****/ -gchar * -clean_exec (MenuCacheApp *app) -{ - gchar *filepath = NULL; - const char *exec = menu_cache_app_get_exec (MENU_CACHE_APP(app)); - - g_return_val_if_fail(exec,""); - - GString *cmd = g_string_sized_new (64); - - for (;*exec; ++exec) - { - if (*exec == '%') - { - ++exec; - switch (*exec) - { - /* useless and commonly used codes */ - case 'u': - case 'U': - case 'f': - case 'F': break; - /* deprecated codes */ - case 'd': - case 'D': - case 'm': - case 'n': - case 'N': - case 'v': break; - /* Other codes, more or less pointless to implement */ - case 'c': - g_string_append (cmd, menu_cache_item_get_name (MENU_CACHE_ITEM(app))); - break; -#if WITH_ICONS - case 'i': - if (item_icon_path (MENU_CACHE_ITEM(app))) - { - g_string_append_printf (cmd, "--icon %s", - item_icon_path (MENU_CACHE_ITEM(app))); - } - break; -#endif - case 'k': - filepath = menu_cache_item_get_file_path (MENU_CACHE_ITEM(app)); - if (filepath) - { - g_string_append (cmd, filepath); - g_free (filepath); - } - break; - /* It was not in the freedesktop specification. */ - default: - g_string_append_c (cmd, '%'); - g_string_append_c (cmd, *exec); - break; - } - } - else - g_string_append_c (cmd, *exec); - } - return g_strchomp (g_string_free (cmd, FALSE)); -} - - -#if WITH_ICONS - -extern GtkIconTheme *icon_theme; - -/****f* utils/item_icon_path - * OUTPUT - * return the path for the themed icon if item. - * If no icon found, it returns the "empty" icon path. - * - * The returned string should be freed when no longer needed - * - * NOTES - * Imlib2, used by OpenBox to display icons, doesn't load SVG graphics. - * We have to use GTK_ICON_LOOKUP_NO_SVG flag to look up icons. - * - * TODO - * The "2nd fallback" is annoying, I have to think about this. - ****/ -gchar * -item_icon_path (MenuCacheItem *item) -{ - GtkIconInfo *icon_info = NULL; - gchar *icon = NULL; - gchar *tmp_name = NULL; - - const gchar *name = menu_cache_item_get_icon (MENU_CACHE_ITEM(item)); - - if (name) - { - if (g_path_is_absolute (name)) - return g_strdup (name); - - /* We remove the file extension as gtk_icon_theme_lookup_icon can't - * lookup a theme icon for, ie, 'geany.png'. It has to be 'geany'. - */ - tmp_name = strndup (name, strrchr (name, '.') - name); - #ifdef WITH_SVG - icon_info = gtk_icon_theme_lookup_icon (icon_theme, tmp_name, 16, GTK_ICON_LOOKUP_GENERIC_FALLBACK); - #else - icon_info = gtk_icon_theme_lookup_icon (icon_theme, tmp_name, 16, GTK_ICON_LOOKUP_NO_SVG | GTK_ICON_LOOKUP_GENERIC_FALLBACK); - #endif - g_free (tmp_name); - } - - if (!icon_info) /* 2nd fallback */ - icon_info = gtk_icon_theme_lookup_icon (icon_theme, "empty", 16, GTK_ICON_LOOKUP_NO_SVG); - - icon = g_strdup (gtk_icon_info_get_filename (icon_info)); - gtk_icon_info_free (icon_info); - - return icon; -} -#endif /* WITH_ICONS */ - - -guint -app_is_visible(MenuCacheApp *app, guint32 de_flag) -{ - gint32 flags = menu_cache_app_get_show_flags (app); - - if (flags < 0) - return !(- flags & de_flag); - else - return menu_cache_app_get_is_visible(MENU_CACHE_APP(app), de_flag); -} - -const char* get_desktop_name() { - const gchar *desktop = g_getenv ("XDG_CURRENT_DESKTOP"); - if (desktop) - return desktop; - - desktop = g_getenv ("DESKTOP_SESSION"); - if (desktop) - return desktop; - - // We return nothing. - return NULL; -} - -void -add_current_desktop_to_context (MenuCache *menu, OB_Menu *context) { - const char* desktop = get_desktop_name (); - if (desktop) { - context->show_flag |= menu_cache_get_desktop_env_flag(menu, desktop); - } -} -