New upstream version 0.8.0+hg20161009
Mateusz Ĺukasik
6 years ago
4 | 4 | |
5 | 5 | # Comment this line if you don't want icons to appear in menu |
6 | 6 | CFLAGS+=-DWITH_ICONS |
7 | # Uncomment this line if Openbox can display SVG icons | |
7 | # Uncomment this line if Openbox can display SVG icons | |
8 | 8 | # Check SVG support with '$ ldd /usr/bin/openbox | grep svg', librsvg must appear.. |
9 | 9 | # CFLAGS+=-DWITH_SVG |
10 | 10 | |
12 | 12 | DESTDIR ?= $(prefix) |
13 | 13 | BINDIR= ${DESTDIR}/bin |
14 | 14 | |
15 | SRC= $(shell ls *.c 2> /dev/null) | |
15 | SRC= $(shell ls src/*.c 2> /dev/null) | |
16 | 16 | OBJ= $(SRC:.c=.o) |
17 | 17 | |
18 | all: $(OBJ) openbox-menu | |
18 | TESTS_SRC= $(shell ls tests/*.c 2> /dev/null) | |
19 | TEST_OBJ= $(TESTS_SRC:.c=.o) | |
20 | ||
21 | all: $(OBJ) $(TEST_OBJ) check openbox-menu | |
19 | 22 | |
20 | 23 | %.o: %.c |
21 | 24 | $(CC) $(CFLAGS) -c $< -o $@ |
22 | 25 | |
26 | ||
23 | 27 | openbox-menu: $(OBJ) |
24 | 28 | $(CC) $(OBJ) -o openbox-menu $(LDFLAGS) $(LIBS) |
25 | 29 | |
26 | .PHONY: clean install doc changelog check | |
30 | .PHONY: clean install doc changelog check xmllint | |
27 | 31 | |
28 | 32 | clean: |
29 | @rm -f *.o openbox-menu | |
33 | @rm -f $(OBJ) $(TEST_OBJ) openbox-menu check | |
30 | 34 | @rm -rf doc |
31 | 35 | |
32 | 36 | install: |
36 | 40 | doc: |
37 | 41 | robodoc --src . --doc doc/ --multidoc --index --html --cmode |
38 | 42 | |
39 | check: openbox-menu | |
40 | ./openbox-menu > test.xml | |
43 | check: $(TEST_OBJ) src/utils.o src/context.o | |
44 | $(CC) src/utils.o src/context.o $(TEST_OBJ) $(LDFLAGS) $(LIBS) -o check | |
45 | gtester --verbose check | |
46 | ||
47 | xmllint: openbox-menu | |
48 | ./openbox-menu > test.xml | |
41 | 49 | xmllint test.xml |
42 | 50 | rm test.xml |
43 | 51 |
0 | # Contributors: Calimero <calimeroteknik@free.fr> | |
1 | # Maintainer: mimas <mimasgpc@free.fr> | |
2 | ||
3 | _pkgname=openbox-menu | |
4 | pkgname=$_pkgname-hg | |
5 | pkgver=66.35d948d8b998 | |
6 | pkgrel=1 | |
7 | pkgdesc="Dynamic XDG menu for openbox" | |
8 | arch=('i686' 'x86_64') | |
9 | provides=("openbox-menu") | |
10 | conflicts=("openbox-menu") | |
11 | url="http://mimasgpc.free.fr/openbox-menu.html" | |
12 | license=('GPL3') | |
13 | depends=('gtk2' 'menu-cache') | |
14 | makedepends=("mercurial") | |
15 | optdepends=('lxmenu-data: LXDE menus' 'gnome-menus: GNOME menus') | |
16 | source=("hg+https://bitbucket.org/fabriceT/openbox-menu") | |
17 | md5sums=('SKIP') | |
18 | ||
19 | pkgver() { | |
20 | cd $_pkgname | |
21 | echo $(hg identify -n).$(hg identify -i) | |
22 | } | |
23 | ||
24 | build() { | |
25 | cd $_pkgname | |
26 | make | |
27 | } | |
28 | ||
29 | package() { | |
30 | cd $_pkgname | |
31 | make install DESTDIR="${pkgdir}/usr/" | |
32 | } |
0 | // openbox-menu - a dynamic menu for openbox | |
1 | // Copyright (C) 2010-15 Fabrice THIROUX <fabrice.thiroux@free.fr> | |
2 | // | |
3 | // This program is free software; you can redistribute it and/or modify | |
4 | // it under the terms of the GNU General Public License as published by | |
5 | // the Free Software Foundation; version 3 of the License. | |
6 | // | |
7 | // This program is distributed in the hope that it will be useful, | |
8 | // but WITHOUT ANY WARRANTY; without even the implied warranty of | |
9 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
10 | // GNU General Public License for more details. | |
11 | // | |
12 | // You should have received a copy of the GNU General Public License | |
13 | // along with this program; if not, write to the Free Software | |
14 | // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | |
15 | // MA 02110-1301, USA. | |
16 | ||
17 | #include <stdio.h> | |
18 | #include <glib.h> | |
19 | #include <glib/gi18n.h> | |
20 | #ifdef WITH_ICONS | |
21 | #include <gtk/gtk.h> | |
22 | #endif | |
23 | #include <signal.h> | |
24 | #include <locale.h> | |
25 | #include <stdlib.h> | |
26 | ||
27 | #include "openbox-menu.h" | |
28 | ||
29 | GMainLoop *loop = NULL; | |
30 | #ifdef WITH_ICONS | |
31 | GtkIconTheme *icon_theme; | |
32 | #endif | |
33 | ||
34 | /* from lxsession */ | |
35 | void sig_term_handler (int sig) | |
36 | { | |
37 | g_warning ("Aborting"); | |
38 | g_main_loop_quit (loop); | |
39 | } | |
40 | ||
41 | /****f* openbox-menu/get_default_application_menu | |
42 | * FUNCTION | |
43 | * Try to determine which menu file to use if none defined by user. | |
44 | * XDG_MENU_PREFIX variable exists, it is used to prefix menu name. | |
45 | * | |
46 | * RETURN VALUE | |
47 | * a char that need to be freed by caller. | |
48 | ****/ | |
49 | gchar * | |
50 | get_default_application_menu (void) | |
51 | { | |
52 | gchar menu[APPMENU_SIZE]; | |
53 | ||
54 | gchar *xdg_prefix = getenv("XDG_MENU_PREFIX"); | |
55 | if (xdg_prefix) | |
56 | { | |
57 | g_snprintf (menu, APPMENU_SIZE, "%sapplications.menu", xdg_prefix); | |
58 | } | |
59 | else | |
60 | g_strlcpy (menu, "applications.menu", APPMENU_SIZE); | |
61 | ||
62 | return strdup (menu); | |
63 | } | |
64 | ||
65 | /****f* openbox-menu/check_application_menu | |
66 | * FUNCTION | |
67 | * Test if menu file exists. | |
68 | * | |
69 | * PARAMETERS | |
70 | * * menu, a string containing the filename of the menu | |
71 | * | |
72 | * RETURN VALUE | |
73 | * FALSE if menu file is not found. TRUE otherwise. | |
74 | * | |
75 | * NOTES | |
76 | * User custom menu file can be used if XDG_CONFIG_DIRS is set, i.g | |
77 | * 'export XDG_CONFIG_DIRS="$HOME/.config/:/etc/xdg/" to use | |
78 | * menu file located in $HOME/menus or /etc/xdg/ directories. | |
79 | ****/ | |
80 | gboolean | |
81 | check_application_menu (gchar *menu) | |
82 | { | |
83 | const gchar * const *dir; | |
84 | gchar *menu_path; | |
85 | ||
86 | for (dir = g_get_system_config_dirs(); *dir ; dir++) | |
87 | { | |
88 | menu_path = g_build_filename (*dir, "menus", menu, NULL); | |
89 | if (g_file_test (menu_path, G_FILE_TEST_EXISTS)) | |
90 | { | |
91 | g_free (menu_path); | |
92 | return TRUE; | |
93 | } | |
94 | ||
95 | g_free (menu_path); | |
96 | } | |
97 | ||
98 | return FALSE; | |
99 | } | |
100 | ||
101 | OB_Menu * | |
102 | configure (int argc, char **argv) | |
103 | { | |
104 | GError *error = NULL; | |
105 | gboolean comment = FALSE; | |
106 | gchar *terminal_cmd = NULL; | |
107 | gboolean persistent = FALSE; | |
108 | gboolean show_gnome = FALSE; | |
109 | gboolean show_kde = FALSE; | |
110 | gboolean show_xfce = FALSE; | |
111 | gboolean show_rox = FALSE; | |
112 | gboolean show_unknown = FALSE; | |
113 | gboolean no_icons = FALSE; | |
114 | gchar *template = NULL; | |
115 | gboolean sn = FALSE; | |
116 | gchar *output = NULL; | |
117 | gchar **app_menu = NULL; | |
118 | ||
119 | GOptionEntry entries[] = { | |
120 | { "comment", 'c', 0, G_OPTION_ARG_NONE, &comment, | |
121 | "Show generic name instead of application name", NULL }, | |
122 | { "terminal", 't', 0, G_OPTION_ARG_STRING, &terminal_cmd, | |
123 | "Terminal command (default xterm -e)", "cmd" }, | |
124 | { "gnome", 'g', 0, G_OPTION_ARG_NONE, &show_gnome, | |
125 | "Show GNOME entries", NULL }, | |
126 | { "kde", 'k', 0, G_OPTION_ARG_NONE, &show_kde, | |
127 | "Show KDE entries", NULL }, | |
128 | { "xfce", 'x', 0, G_OPTION_ARG_NONE, &show_xfce, | |
129 | "Show XFCE entries", NULL }, | |
130 | { "rox", 'r', 0, G_OPTION_ARG_NONE, &show_rox, | |
131 | "Show ROX entries", NULL }, | |
132 | { "unknown", 'u', 0, G_OPTION_ARG_NONE, &show_unknown, | |
133 | "Show Unknown deskstop entries", NULL }, | |
134 | { "persistent",'p', 0, G_OPTION_ARG_NONE, &persistent, | |
135 | "stay active", NULL }, | |
136 | { "sn", 's', 0, G_OPTION_ARG_NONE, &sn, | |
137 | "Enable startup notification", NULL }, | |
138 | { "output", 'o', 0, G_OPTION_ARG_STRING, &output, | |
139 | "file to write data to", NULL }, | |
140 | { "template", 'T', 0, G_OPTION_ARG_STRING, &template, | |
141 | "Use filename as template for openbox-menu output", NULL }, | |
142 | { "noicons", 'i', 0, G_OPTION_ARG_NONE, &no_icons, | |
143 | "Don't display icons in menu", NULL }, | |
144 | { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &app_menu, | |
145 | NULL, "[file.menu]" }, | |
146 | {NULL} | |
147 | }; | |
148 | GOptionContext *help_context = NULL; | |
149 | ||
150 | help_context = g_option_context_new (" - Openbox menu generator " VERSION); | |
151 | g_option_context_set_help_enabled (help_context, TRUE); | |
152 | g_option_context_add_main_entries (help_context, entries, NULL); | |
153 | g_option_context_parse (help_context, &argc, &argv, &error); | |
154 | ||
155 | if (error) | |
156 | { | |
157 | g_warning ("%s\n", error->message); | |
158 | g_error_free (error); | |
159 | return NULL; | |
160 | } | |
161 | ||
162 | OB_Menu *context = g_slice_new0 (OB_Menu); | |
163 | ||
164 | if (output) | |
165 | context->output = g_build_filename (g_get_user_cache_dir (), output, NULL); | |
166 | else | |
167 | context->output = NULL; | |
168 | ||
169 | // We add extra desktop entries to display. | |
170 | // Our current desktop is set when menu_cache has loaded its own cache. | |
171 | // (likely in menu_display function). | |
172 | if (show_gnome) context->show_flag |= SHOW_IN_GNOME; | |
173 | if (show_kde) context->show_flag |= SHOW_IN_KDE; | |
174 | if (show_xfce) context->show_flag |= SHOW_IN_XFCE; | |
175 | if (show_rox) context->show_flag |= SHOW_IN_ROX; | |
176 | if (show_unknown) context->show_flag |= 1 << N_KNOWN_DESKTOPS; | |
177 | ||
178 | if (terminal_cmd) | |
179 | context->terminal_cmd = terminal_cmd; | |
180 | else | |
181 | context->terminal_cmd = TERMINAL_CMD; | |
182 | ||
183 | if (comment) | |
184 | context->comment = TRUE; | |
185 | ||
186 | if (sn) | |
187 | context->sn = TRUE; | |
188 | ||
189 | if (no_icons) | |
190 | context->no_icons = TRUE; | |
191 | ||
192 | if (persistent) | |
193 | context->persistent = TRUE; | |
194 | ||
195 | if (!app_menu) | |
196 | context->menu_file = get_default_application_menu(); | |
197 | else | |
198 | context->menu_file = strdup (*app_menu); | |
199 | ||
200 | if (template) | |
201 | context->template = template; | |
202 | ||
203 | g_option_context_free (help_context); | |
204 | ||
205 | return context; | |
206 | } | |
207 | ||
208 | guint | |
209 | run (OB_Menu *context) | |
210 | { | |
211 | gpointer reload_notify_id = NULL; | |
212 | MenuCache *menu_cache = NULL; | |
213 | ||
214 | g_unsetenv("XDG_MENU_PREFIX"); // For unknow reason, it doesn't work when it is set. | |
215 | ||
216 | if (context->persistent) /* persistent mode */ | |
217 | { | |
218 | // No need to get sync lookup. The callback function will be called | |
219 | // when menu-cache is ready. | |
220 | menu_cache = menu_cache_lookup (context->menu_file); | |
221 | if (!menu_cache) | |
222 | { | |
223 | g_warning ("Cannot connect to menu-cache :/"); | |
224 | return MENU_CACHE_ERROR; | |
225 | } | |
226 | ||
227 | // menucache used to reload the cache after a call to menu_cache_lookup* () | |
228 | // It's not true anymore with version >= 0.4.0. | |
229 | reload_notify_id = menu_cache_add_reload_notify (menu_cache, | |
230 | (MenuCacheReloadNotify) menu_display, | |
231 | context); | |
232 | ||
233 | // install signals handler | |
234 | signal (SIGTERM, sig_term_handler); | |
235 | signal (SIGINT, sig_term_handler); | |
236 | ||
237 | // run main loop | |
238 | loop = g_main_loop_new (NULL, FALSE); | |
239 | g_main_loop_run (loop); | |
240 | g_main_loop_unref (loop); | |
241 | ||
242 | menu_cache_remove_reload_notify (menu_cache, reload_notify_id); | |
243 | } | |
244 | else | |
245 | { /* single shot */ | |
246 | // wait for the menu to get ready | |
247 | menu_cache = menu_cache_lookup_sync (context->menu_file); | |
248 | if (!menu_cache ) | |
249 | { | |
250 | g_warning ("Cannot connect to menu-cache :/"); | |
251 | return MENU_CACHE_ERROR; | |
252 | } | |
253 | ||
254 | // display the menu anyway | |
255 | menu_display (menu_cache, context); | |
256 | } | |
257 | ||
258 | menu_cache_unref (menu_cache); | |
259 | ||
260 | // return error code set in callback function. | |
261 | return context->code; | |
262 | } | |
263 | ||
264 | void | |
265 | context_free (OB_Menu *context) | |
266 | { | |
267 | if (context->output) | |
268 | g_free (context->output); | |
269 | ||
270 | if (context->menu_file) | |
271 | g_free (context->menu_file); | |
272 | ||
273 | g_slice_free (OB_Menu, context); | |
274 | } | |
275 | ||
276 | ||
277 | int | |
278 | main (int argc, char **argv) | |
279 | { | |
280 | OB_Menu *ob_context; | |
281 | ||
282 | setlocale (LC_ALL, ""); | |
283 | ||
284 | #ifdef WITH_ICONS | |
285 | gtk_init (&argc, &argv); | |
286 | icon_theme = gtk_icon_theme_get_default (); | |
287 | #endif | |
288 | ||
289 | if ((ob_context = configure (argc, argv)) == NULL) | |
290 | return CONFIG_ERROR; | |
291 | ||
292 | if (!check_application_menu (ob_context->menu_file)) | |
293 | { | |
294 | g_print ("File %s doesn't exist. Can't create menu.\n", ob_context->menu_file); | |
295 | return LOOKUP_ERROR; | |
296 | } | |
297 | ||
298 | guint ret = run (ob_context); | |
299 | context_free (ob_context); | |
300 | return ret; | |
301 | } |
0 | // ob_display.c - this file is part of openbox-menu | |
1 | // Copyright (C) 2010-15 Fabrice THIROUX <fabrice.thiroux@free.fr> | |
2 | // | |
3 | // This program is free software; you can redistribute it and/or modify | |
4 | // it under the terms of the GNU General Public License as published by | |
5 | // the Free Software Foundation; version 3 of the License. | |
6 | // | |
7 | // This program is distributed in the hope that it will be useful, | |
8 | // but WITHOUT ANY WARRANTY; without even the implied warranty of | |
9 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
10 | // GNU General Public License for more details. | |
11 | // | |
12 | // You should have received a copy of the GNU General Public License | |
13 | // along with this program; if not, write to the Free Software | |
14 | // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | |
15 | // MA 02110-1301, USA. | |
16 | ||
17 | #include "openbox-menu.h" | |
18 | ||
19 | const gchar *default_template = | |
20 | "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>" | |
21 | "<openbox_pipe_menu xmlns=\"http://openbox.org/\"" | |
22 | " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"" | |
23 | " xsi:schemaLocation=\"http://openbox.org/\" >" | |
24 | "%MENU%</openbox_pipe_menu>\n"; | |
25 | ||
26 | /****f* ob_display/menu_directory | |
27 | * FUNCTION | |
28 | * create a menu entry for a directory. | |
29 | * | |
30 | * NOTES | |
31 | * this menu entry has to be closed by "</menu>". | |
32 | ****/ | |
33 | void | |
34 | menu_directory (MenuCacheApp *dir, OB_Menu *context) | |
35 | { | |
36 | gchar *dir_id = safe_name (menu_cache_item_get_id (MENU_CACHE_ITEM(dir))); | |
37 | gchar *dir_name = safe_name (menu_cache_item_get_name (MENU_CACHE_ITEM(dir))); | |
38 | ||
39 | #ifdef WITH_ICONS | |
40 | if (!context->no_icons) | |
41 | { | |
42 | gchar *dir_icon = item_icon_path (MENU_CACHE_ITEM(dir)); | |
43 | ||
44 | g_string_append_printf (context->builder, | |
45 | "<menu id=\"openbox-%s\" label=\"%s\" icon=\"%s\">\n", | |
46 | dir_id, dir_name, dir_icon); | |
47 | g_free (dir_icon); | |
48 | } | |
49 | else | |
50 | #endif | |
51 | { | |
52 | g_string_append_printf (context->builder, | |
53 | "<menu id=\"openbox-%s\" label=\"%s\">\n", | |
54 | dir_id, dir_name); | |
55 | } | |
56 | ||
57 | g_free (dir_id); | |
58 | g_free (dir_name); | |
59 | } | |
60 | ||
61 | /****f* ob_display/menu_application | |
62 | * FUNCTION | |
63 | * create a menu entry for an application. | |
64 | ****/ | |
65 | void | |
66 | menu_application (MenuCacheApp *app, OB_Menu *context) | |
67 | { | |
68 | gchar *exec_name = NULL; | |
69 | gchar *exec_icon = NULL; | |
70 | gchar *exec_cmd = NULL; | |
71 | ||
72 | /* is comment (description) or name displayed ? */ | |
73 | if (context->comment && menu_cache_item_get_comment (MENU_CACHE_ITEM(app))) | |
74 | exec_name = safe_name (menu_cache_item_get_comment (MENU_CACHE_ITEM(app))); | |
75 | else | |
76 | exec_name = safe_name (menu_cache_item_get_name (MENU_CACHE_ITEM(app))); | |
77 | ||
78 | exec_cmd = clean_exec (app); | |
79 | ||
80 | #ifdef WITH_ICONS | |
81 | if (!context->no_icons) | |
82 | { | |
83 | exec_icon = item_icon_path (MENU_CACHE_ITEM(app)); | |
84 | g_string_append_printf (context->builder, | |
85 | "<item label=\"%s\" icon=\"%s\"><action name=\"Execute\">", | |
86 | exec_name, | |
87 | exec_icon); | |
88 | } | |
89 | else | |
90 | #endif | |
91 | { | |
92 | g_string_append_printf (context->builder, | |
93 | "<item label=\"%s\"><action name=\"Execute\">", | |
94 | exec_name); | |
95 | } | |
96 | ||
97 | if (context->sn && menu_cache_app_get_use_sn (app)) | |
98 | g_string_append (context->builder, | |
99 | "<startupnotify><enabled>yes</enabled></startupnotify>"); | |
100 | ||
101 | if (menu_cache_app_get_use_terminal (app)) | |
102 | g_string_append_printf (context->builder, | |
103 | "<command><![CDATA[%s %s]]></command>\n</action></item>\n", | |
104 | context->terminal_cmd, | |
105 | exec_cmd); | |
106 | else | |
107 | g_string_append_printf (context->builder, | |
108 | "<command><![CDATA[%s]]></command>\n</action></item>\n", | |
109 | exec_cmd); | |
110 | ||
111 | g_free (exec_name); | |
112 | g_free (exec_icon); | |
113 | g_free (exec_cmd); | |
114 | } | |
115 | ||
116 | /****f* ob_display/menu_generate | |
117 | * FUNCTION | |
118 | * main routine of menu creation. | |
119 | * | |
120 | * NOTES | |
121 | * It calls itself when 'dir' type is MENU_CACHE_TYPE_DIR. | |
122 | ****/ | |
123 | void | |
124 | menu_generate (MenuCacheDir *dir, OB_Menu *context) | |
125 | { | |
126 | GSList *l = NULL; | |
127 | ||
128 | for (l = menu_cache_dir_get_children (dir); l; l = l->next) | |
129 | switch ((guint) menu_cache_item_get_type (MENU_CACHE_ITEM(l->data))) | |
130 | { | |
131 | case MENU_CACHE_TYPE_DIR: | |
132 | menu_directory (l->data, context); | |
133 | menu_generate (MENU_CACHE_DIR(l->data), context); | |
134 | g_string_append (context->builder, "</menu>\n"); | |
135 | break; | |
136 | ||
137 | case MENU_CACHE_TYPE_SEP: | |
138 | g_string_append (context->builder, "<separator />\n"); | |
139 | break; | |
140 | ||
141 | case MENU_CACHE_TYPE_APP: | |
142 | if (app_is_visible (MENU_CACHE_APP(l->data), context->show_flag)) | |
143 | menu_application (l->data, context); | |
144 | } | |
145 | } | |
146 | ||
147 | ||
148 | /****f* ob_display/get_header_footer_from_template | |
149 | * FUNCTION | |
150 | * Get header and footer string from a template file. If no template | |
151 | * file provided, the default template will bu used. | |
152 | * | |
153 | * INPUTS | |
154 | * * template | |
155 | * | |
156 | * RETURN VALUE | |
157 | * * a pointer to an array of strings that needs to be freed with g_strfreev. | |
158 | ****/ | |
159 | gchar **get_header_footer_from_template (gchar *template) | |
160 | { | |
161 | gchar *content = NULL; | |
162 | gchar **tokens = NULL; | |
163 | ||
164 | if (template && g_file_get_contents (template, &content, NULL, NULL)) | |
165 | { | |
166 | tokens = g_strsplit (content, "%MENU%", 2); | |
167 | g_free (content); | |
168 | } | |
169 | else | |
170 | { | |
171 | tokens = g_strsplit (default_template, "%MENU%", 2); | |
172 | } | |
173 | return tokens; | |
174 | } | |
175 | ||
176 | /****f* ob_display/menu_display | |
177 | * FUNCTION | |
178 | * it begins and closes the menu content, write it into a file or | |
179 | * display it. | |
180 | * | |
181 | * INPUTS | |
182 | * * menu | |
183 | * * file, the filename where the menu content should be written to. | |
184 | * If file is 'NULL' then the menu content is displayed. | |
185 | * | |
186 | * RETURN VALUE | |
187 | * Nothing. A MenuCacheReloadNotify callback returns void. | |
188 | * | |
189 | * NOTES | |
190 | * A 16 KiB GString is allocated for the content of the pipemenu. | |
191 | * This should be enough prevent too many allocations while building | |
192 | * the XML. | |
193 | * | |
194 | * The size of the XML file created is around 8 KB in my computer but | |
195 | * I don't have a lot of applications installed. | |
196 | ****/ | |
197 | void | |
198 | menu_display (MenuCache *menu, OB_Menu *context) | |
199 | { | |
200 | gchar **template_parts = NULL; | |
201 | ||
202 | MenuCacheDir *dir = menu_cache_dup_root_dir (menu); | |
203 | if (G_UNLIKELY(dir == NULL)) | |
204 | { | |
205 | g_warning ("Can't get menu root directory"); | |
206 | context->code = MENU_DIR_ERROR; | |
207 | return; | |
208 | } | |
209 | ||
210 | // Desktops are dynamically detected by menu_cache when reloading | |
211 | // its cache. It is now time to add our desktop to the show_flag in | |
212 | // the application context. | |
213 | add_current_desktop_to_context (menu, context); | |
214 | ||
215 | GSList *l = menu_cache_dir_get_children (dir); | |
216 | ||
217 | if (g_slist_length (l) != 0) { | |
218 | context->builder = g_string_sized_new (16 * 1024); | |
219 | ||
220 | template_parts = get_header_footer_from_template (context->template); | |
221 | // TODO: check if template_parts array contains 2 strings. | |
222 | ||
223 | g_string_append (context->builder, template_parts[0]); // add header | |
224 | menu_generate (dir, context); | |
225 | g_string_append (context->builder, template_parts[1]); // add footer | |
226 | ||
227 | g_strfreev (template_parts); | |
228 | ||
229 | gchar *buff = g_string_free (context->builder, FALSE); | |
230 | ||
231 | /* Has menu content to be saved in a file ? */ | |
232 | if (context->output) | |
233 | { | |
234 | if (!g_file_set_contents (context->output, buff, -1, NULL)) | |
235 | g_warning ("Can't write to %s\n", context->output); | |
236 | else | |
237 | g_message ("wrote to %s", context->output); | |
238 | } | |
239 | else /* No, so it's displayed on screen */ | |
240 | g_print ("%s", buff); | |
241 | ||
242 | g_free (buff); | |
243 | } | |
244 | else | |
245 | { | |
246 | g_warning ("Menu seems to be empty. Check openbox-menu parameters."); | |
247 | context->code = MENU_EMPTY_ERROR; | |
248 | } | |
249 | ||
250 | menu_cache_item_unref (MENU_CACHE_ITEM(dir)); | |
251 | } |
0 | /* | |
1 | * openbox-menu.h - this file is part of openbox-menu | |
2 | * Copyright (C) 2010-15 Fabrice THIROUX <fabrice.thiroux@free.fr> | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify | |
5 | * it under the terms of the GNU Lesser General Public License as published | |
6 | * by the Free Software Foundation; version 3.0 only. | |
7 | * | |
8 | * This program is distributed in the hope that it will be useful, | |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
11 | * GNU General Public License for more details. | |
12 | * | |
13 | * You should have received a copy of the GNU General Public License | |
14 | * along with this program; if not, write to the Free Software | |
15 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | |
16 | * MA 02110-1301, USA. | |
17 | */ | |
18 | ||
19 | #ifndef __OPENBOXMENU_APP__ | |
20 | #define __OPENBOXMENU_APP__ | |
21 | #include <menu-cache.h> | |
22 | ||
23 | #define VERSION "0.8.0" | |
24 | #define APPMENU_SIZE 30 | |
25 | #define TERMINAL_CMD "xterm -e" | |
26 | ||
27 | #ifndef __VERSION_MINOR // since menu-cache 0.5.0. | |
28 | #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" | |
29 | #endif | |
30 | ||
31 | typedef enum { | |
32 | NO_ERROR = 0, | |
33 | CONFIG_ERROR, | |
34 | MENU_DIR_ERROR, | |
35 | MENU_EMPTY_ERROR, | |
36 | LOOKUP_ERROR, | |
37 | MENU_CACHE_ERROR, | |
38 | } OBM_Error; | |
39 | ||
40 | ||
41 | typedef struct { | |
42 | /* Configuration */ | |
43 | gchar *output; | |
44 | guint32 show_flag; | |
45 | GString *builder; /* */ | |
46 | gchar *terminal_cmd; /* command to launch program in a terminal */ | |
47 | gboolean comment; /* display description instead of name */ | |
48 | gboolean sn; /* startup notification */ | |
49 | gboolean no_icons; /* icons disabled */ | |
50 | gboolean persistent; | |
51 | gchar *menu_file; | |
52 | gchar *template; | |
53 | guint code; | |
54 | } OB_Menu; | |
55 | ||
56 | guint app_is_visible (MenuCacheApp *, guint32); | |
57 | gchar *clean_exec (MenuCacheApp *); | |
58 | gchar *safe_name (const char *); | |
59 | gchar *item_icon_path (MenuCacheItem*); | |
60 | guint32 get_current_desktop_flag (); | |
61 | void add_current_desktop_to_context (MenuCache *, OB_Menu *); | |
62 | ||
63 | void menu_display (MenuCache *, OB_Menu *); | |
64 | ||
65 | #endif // __OPENBOXMENU_APP__ |
0 | #include "openbox-menu.h" | |
1 | ||
2 | OB_Menu* | |
3 | context_new() | |
4 | { | |
5 | OB_Menu* context = g_slice_new0 (OB_Menu); | |
6 | ||
7 | return context; | |
8 | } | |
9 | ||
10 | /****** TERMINAL ******/ | |
11 | ||
12 | void | |
13 | context_set_terminal_cmd (OB_Menu* ctx, gchar* cmd) | |
14 | { | |
15 | ctx->terminal_cmd = cmd; | |
16 | } | |
17 | ||
18 | ||
19 | gchar* | |
20 | context_get_terminal_cmd (OB_Menu* ctx) | |
21 | { | |
22 | return ctx->terminal_cmd; | |
23 | } | |
24 | ||
25 | /****** DESCRIPTION DISPLAY ******/ | |
26 | ||
27 | void context_set_comment(OB_Menu* ctx, gboolean val) | |
28 | { | |
29 | ctx->comment = val; | |
30 | } | |
31 | ||
32 | ||
33 | gboolean context_get_comment(OB_Menu* ctx) | |
34 | { | |
35 | return ctx->comment; | |
36 | } | |
37 | ||
38 | /****** SHOW FLAG ******/ | |
39 | ||
40 | void | |
41 | context_set_desktop_flag (OB_Menu* ctx, int flag) | |
42 | { | |
43 | ctx->show_flag = flag; | |
44 | } | |
45 | ||
46 | ||
47 | void | |
48 | context_add_desktop_flag (OB_Menu* ctx, int flag) | |
49 | { | |
50 | ctx->show_flag |= flag; | |
51 | } | |
52 | ||
53 | ||
54 | int | |
55 | context_get_desktop_flag (OB_Menu* ctx) | |
56 | { | |
57 | return ctx->show_flag; | |
58 | } | |
59 | ||
60 | ||
61 | /****** SYSTEM NOTIFICATION ******/ | |
62 | void | |
63 | context_set_sn (OB_Menu* ctx, int flag) | |
64 | { | |
65 | ctx->sn = flag; | |
66 | } | |
67 | ||
68 | ||
69 | gboolean | |
70 | context_get_sn (OB_Menu* ctx) | |
71 | { | |
72 | return ctx->sn; | |
73 | } | |
74 | ||
75 | ||
76 | /****** PERSISTENT MODE ******/ | |
77 | ||
78 | void | |
79 | context_set_persistent (OB_Menu* ctx, gboolean flag) | |
80 | { | |
81 | ctx->persistent = flag; | |
82 | } | |
83 | ||
84 | gboolean | |
85 | context_get_persistent (OB_Menu* ctx) | |
86 | { | |
87 | return ctx->persistent; | |
88 | } | |
89 | ||
90 | ||
91 | /****** DESTRUCTOR ******/ | |
92 | ||
93 | void | |
94 | context_free (OB_Menu *context) | |
95 | { | |
96 | if (context->output) | |
97 | g_free (context->output); | |
98 | ||
99 | if (context->menu_file) | |
100 | g_free (context->menu_file); | |
101 | ||
102 | g_slice_free (OB_Menu, context); | |
103 | } |
0 | // openbox-menu - a dynamic menu for openbox | |
1 | // Copyright (C) 2010-15 Fabrice THIROUX <fabrice.thiroux@free.fr> | |
2 | // | |
3 | // This program is free software; you can redistribute it and/or modify | |
4 | // it under the terms of the GNU General Public License as published by | |
5 | // the Free Software Foundation; version 3 of the License. | |
6 | // | |
7 | // This program is distributed in the hope that it will be useful, | |
8 | // but WITHOUT ANY WARRANTY; without even the implied warranty of | |
9 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
10 | // GNU General Public License for more details. | |
11 | // | |
12 | // You should have received a copy of the GNU General Public License | |
13 | // along with this program; if not, write to the Free Software | |
14 | // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | |
15 | // MA 02110-1301, USA. | |
16 | ||
17 | #include <stdio.h> | |
18 | #include <glib.h> | |
19 | #include <glib/gi18n.h> | |
20 | #include <signal.h> | |
21 | #include <locale.h> | |
22 | #include <stdlib.h> | |
23 | ||
24 | #include "openbox-menu.h" | |
25 | ||
26 | GMainLoop *loop = NULL; | |
27 | ||
28 | /* from lxsession */ | |
29 | void sig_term_handler (int sig) | |
30 | { | |
31 | g_warning ("Aborting"); | |
32 | g_main_loop_quit (loop); | |
33 | } | |
34 | ||
35 | ||
36 | ||
37 | /****f* openbox-menu/check_application_menu | |
38 | * FUNCTION | |
39 | * Test if menu file exists. | |
40 | * | |
41 | * PARAMETERS | |
42 | * * menu, a string containing the filename of the menu | |
43 | * | |
44 | * RETURN VALUE | |
45 | * FALSE if menu file is not found. TRUE otherwise. | |
46 | * | |
47 | * NOTES | |
48 | * User custom menu file can be used if XDG_CONFIG_DIRS is set, i.g | |
49 | * 'export XDG_CONFIG_DIRS="$HOME/.config/:/etc/xdg/" to use | |
50 | * menu file located in $HOME/menus or /etc/xdg/ directories. | |
51 | ****/ | |
52 | gboolean | |
53 | check_application_menu (gchar *menu) | |
54 | { | |
55 | const gchar * const *dir; | |
56 | gchar *menu_path; | |
57 | ||
58 | for (dir = g_get_system_config_dirs(); *dir ; dir++) | |
59 | { | |
60 | menu_path = g_build_filename (*dir, "menus", menu, NULL); | |
61 | if (g_file_test (menu_path, G_FILE_TEST_EXISTS)) | |
62 | { | |
63 | g_free (menu_path); | |
64 | return TRUE; | |
65 | } | |
66 | ||
67 | g_free (menu_path); | |
68 | } | |
69 | ||
70 | return FALSE; | |
71 | } | |
72 | ||
73 | OB_Menu * | |
74 | configure (int argc, char **argv) | |
75 | { | |
76 | GError *error = NULL; | |
77 | gboolean comment = FALSE; | |
78 | gchar *terminal_cmd = NULL; | |
79 | gboolean persistent = FALSE; | |
80 | gboolean show_gnome = FALSE; | |
81 | gboolean show_kde = FALSE; | |
82 | gboolean show_xfce = FALSE; | |
83 | gboolean show_rox = FALSE; | |
84 | gboolean show_unknown = FALSE; | |
85 | gboolean no_icons = FALSE; | |
86 | gchar *template = NULL; | |
87 | gboolean sn = FALSE; | |
88 | gchar *output = NULL; | |
89 | gchar **app_menu = NULL; | |
90 | ||
91 | GOptionEntry entries[] = { | |
92 | { "comment", 'c', 0, G_OPTION_ARG_NONE, &comment, | |
93 | "Show generic name instead of application name", NULL }, | |
94 | { "terminal", 't', 0, G_OPTION_ARG_STRING, &terminal_cmd, | |
95 | "Terminal command (default xterm -e)", "cmd" }, | |
96 | { "gnome", 'g', 0, G_OPTION_ARG_NONE, &show_gnome, | |
97 | "Show GNOME entries", NULL }, | |
98 | { "kde", 'k', 0, G_OPTION_ARG_NONE, &show_kde, | |
99 | "Show KDE entries", NULL }, | |
100 | { "xfce", 'x', 0, G_OPTION_ARG_NONE, &show_xfce, | |
101 | "Show XFCE entries", NULL }, | |
102 | { "rox", 'r', 0, G_OPTION_ARG_NONE, &show_rox, | |
103 | "Show ROX entries", NULL }, | |
104 | { "unknown", 'u', 0, G_OPTION_ARG_NONE, &show_unknown, | |
105 | "Show Unknown deskstop entries", NULL }, | |
106 | { "persistent",'p', 0, G_OPTION_ARG_NONE, &persistent, | |
107 | "stay active", NULL }, | |
108 | { "sn", 's', 0, G_OPTION_ARG_NONE, &sn, | |
109 | "Enable startup notification", NULL }, | |
110 | { "output", 'o', 0, G_OPTION_ARG_STRING, &output, | |
111 | "file to write data to", NULL }, | |
112 | { "template", 'T', 0, G_OPTION_ARG_STRING, &template, | |
113 | "Use filename as template for openbox-menu output", NULL }, | |
114 | { "noicons", 'i', 0, G_OPTION_ARG_NONE, &no_icons, | |
115 | "Don't display icons in menu", NULL }, | |
116 | { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &app_menu, | |
117 | NULL, "[file.menu]" }, | |
118 | {NULL} | |
119 | }; | |
120 | GOptionContext *help_context = NULL; | |
121 | ||
122 | help_context = g_option_context_new (" - Openbox menu generator " VERSION); | |
123 | g_option_context_set_help_enabled (help_context, TRUE); | |
124 | g_option_context_add_main_entries (help_context, entries, NULL); | |
125 | g_option_context_parse (help_context, &argc, &argv, &error); | |
126 | ||
127 | if (error) | |
128 | { | |
129 | g_warning ("%s\n", error->message); | |
130 | g_error_free (error); | |
131 | return NULL; | |
132 | } | |
133 | ||
134 | OB_Menu *context = context_new(); | |
135 | ||
136 | if (output) | |
137 | context->output = g_build_filename (g_get_user_cache_dir (), output, NULL); | |
138 | else | |
139 | context->output = NULL; | |
140 | ||
141 | // We add extra desktop entries to display. | |
142 | // Our current desktop is set when menu_cache has loaded its own cache. | |
143 | // (likely in menu_display function). | |
144 | if (show_gnome) context_add_desktop_flag(context, SHOW_IN_GNOME); | |
145 | if (show_kde) context_add_desktop_flag(context, SHOW_IN_KDE); | |
146 | if (show_xfce) context_add_desktop_flag(context, SHOW_IN_XFCE); | |
147 | if (show_rox) context_add_desktop_flag(context, SHOW_IN_GNOME); | |
148 | if (show_unknown) context_add_desktop_flag(context, 1 << N_KNOWN_DESKTOPS); | |
149 | ||
150 | context_set_terminal_cmd (context, (terminal_cmd) ? terminal_cmd : TERMINAL_CMD); | |
151 | ||
152 | context_set_comment(context, comment); | |
153 | ||
154 | if (sn) | |
155 | context->sn = TRUE; | |
156 | ||
157 | if (no_icons) | |
158 | context->no_icons = TRUE; | |
159 | ||
160 | if (persistent) | |
161 | context->persistent = TRUE; | |
162 | ||
163 | if (!app_menu) | |
164 | context->menu_file = get_default_application_menu(); | |
165 | else | |
166 | context->menu_file = strdup (*app_menu); | |
167 | ||
168 | if (template) | |
169 | context->template = template; | |
170 | ||
171 | g_option_context_free (help_context); | |
172 | ||
173 | return context; | |
174 | } | |
175 | ||
176 | guint | |
177 | run (OB_Menu *context) | |
178 | { | |
179 | gpointer reload_notify_id = NULL; | |
180 | MenuCache *menu_cache = NULL; | |
181 | ||
182 | g_unsetenv("XDG_MENU_PREFIX"); // For unknow reason, it doesn't work when it is set. | |
183 | ||
184 | if (context->persistent) /* persistent mode */ | |
185 | { | |
186 | // No need to get sync lookup. The callback function will be called | |
187 | // when menu-cache is ready. | |
188 | menu_cache = menu_cache_lookup (context->menu_file); | |
189 | if (!menu_cache) | |
190 | { | |
191 | g_warning ("Cannot connect to menu-cache :/"); | |
192 | return MENU_CACHE_ERROR; | |
193 | } | |
194 | ||
195 | // menucache used to reload the cache after a call to menu_cache_lookup* () | |
196 | // It's not true anymore with version >= 0.4.0. | |
197 | reload_notify_id = menu_cache_add_reload_notify (menu_cache, | |
198 | (MenuCacheReloadNotify) menu_display, | |
199 | context); | |
200 | ||
201 | // install signals handler | |
202 | signal (SIGTERM, sig_term_handler); | |
203 | signal (SIGINT, sig_term_handler); | |
204 | ||
205 | // run main loop | |
206 | loop = g_main_loop_new (NULL, FALSE); | |
207 | g_main_loop_run (loop); | |
208 | g_main_loop_unref (loop); | |
209 | ||
210 | menu_cache_remove_reload_notify (menu_cache, reload_notify_id); | |
211 | } | |
212 | else | |
213 | { /* single shot */ | |
214 | // wait for the menu to get ready | |
215 | menu_cache = menu_cache_lookup_sync (context->menu_file); | |
216 | if (!menu_cache ) | |
217 | { | |
218 | g_warning ("Cannot connect to menu-cache :/"); | |
219 | return MENU_CACHE_ERROR; | |
220 | } | |
221 | ||
222 | // display the menu anyway | |
223 | menu_display (menu_cache, context); | |
224 | } | |
225 | ||
226 | menu_cache_unref (menu_cache); | |
227 | ||
228 | // return error code set in callback function. | |
229 | return context->code; | |
230 | } | |
231 | ||
232 | ||
233 | int | |
234 | main (int argc, char **argv) | |
235 | { | |
236 | OB_Menu *ob_context; | |
237 | ||
238 | setlocale (LC_ALL, ""); | |
239 | ||
240 | #ifdef WITH_ICONS | |
241 | gtk_init (&argc, &argv); | |
242 | icon_theme = gtk_icon_theme_get_default (); | |
243 | #endif | |
244 | ||
245 | if ((ob_context = configure (argc, argv)) == NULL) | |
246 | return CONFIG_ERROR; | |
247 | ||
248 | if (!check_application_menu (ob_context->menu_file)) | |
249 | { | |
250 | g_print ("File $XDG_CONFIG_DIRS/%s doesn't exist. Can't create menu.\n", ob_context->menu_file); | |
251 | return LOOKUP_ERROR; | |
252 | } | |
253 | ||
254 | guint ret = run (ob_context); | |
255 | context_free (ob_context); | |
256 | return ret; | |
257 | } |
0 | // ob_display.c - this file is part of openbox-menu | |
1 | // Copyright (C) 2010-15 Fabrice THIROUX <fabrice.thiroux@free.fr> | |
2 | // | |
3 | // This program is free software; you can redistribute it and/or modify | |
4 | // it under the terms of the GNU General Public License as published by | |
5 | // the Free Software Foundation; version 3 of the License. | |
6 | // | |
7 | // This program is distributed in the hope that it will be useful, | |
8 | // but WITHOUT ANY WARRANTY; without even the implied warranty of | |
9 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
10 | // GNU General Public License for more details. | |
11 | // | |
12 | // You should have received a copy of the GNU General Public License | |
13 | // along with this program; if not, write to the Free Software | |
14 | // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | |
15 | // MA 02110-1301, USA. | |
16 | ||
17 | #include "openbox-menu.h" | |
18 | ||
19 | const gchar *default_template = | |
20 | "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>" | |
21 | "<openbox_pipe_menu xmlns=\"http://openbox.org/\"" | |
22 | " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"" | |
23 | " xsi:schemaLocation=\"http://openbox.org/\" >" | |
24 | "%MENU%</openbox_pipe_menu>\n"; | |
25 | ||
26 | /****f* ob_display/menu_directory | |
27 | * FUNCTION | |
28 | * create a menu entry for a directory. | |
29 | * | |
30 | * NOTES | |
31 | * this menu entry has to be closed by "</menu>". | |
32 | ****/ | |
33 | void | |
34 | menu_directory (MenuCacheApp *dir, OB_Menu *context) | |
35 | { | |
36 | gchar *dir_id = safe_name (menu_cache_item_get_id (MENU_CACHE_ITEM(dir))); | |
37 | gchar *dir_name = safe_name (menu_cache_item_get_name (MENU_CACHE_ITEM(dir))); | |
38 | ||
39 | #ifdef WITH_ICONS | |
40 | if (!context->no_icons) | |
41 | { | |
42 | gchar *dir_icon = item_icon_path (MENU_CACHE_ITEM(dir)); | |
43 | ||
44 | g_string_append_printf (context->builder, | |
45 | "<menu id=\"openbox-%s\" label=\"%s\" icon=\"%s\">\n", | |
46 | dir_id, dir_name, dir_icon); | |
47 | g_free (dir_icon); | |
48 | } | |
49 | else | |
50 | #endif | |
51 | { | |
52 | g_string_append_printf (context->builder, | |
53 | "<menu id=\"openbox-%s\" label=\"%s\">\n", | |
54 | dir_id, dir_name); | |
55 | } | |
56 | ||
57 | g_free (dir_id); | |
58 | g_free (dir_name); | |
59 | } | |
60 | ||
61 | gchar* get_item_comment (MenuCacheItem*, gboolean); | |
62 | ||
63 | gchar* | |
64 | get_item_name (MenuCacheItem* item, gboolean alternate) | |
65 | { | |
66 | char* s = safe_name (menu_cache_item_get_name(item)); | |
67 | ||
68 | if (s == NULL && alternate == TRUE) { | |
69 | return get_item_comment(item, FALSE); | |
70 | } | |
71 | ||
72 | return s; | |
73 | } | |
74 | ||
75 | ||
76 | gchar* | |
77 | get_item_comment (MenuCacheItem* item, gboolean alternate) | |
78 | { | |
79 | char* s = safe_name (menu_cache_item_get_comment(item)); | |
80 | ||
81 | if (s == NULL && alternate == TRUE) { | |
82 | return get_item_name(item, FALSE); | |
83 | } | |
84 | ||
85 | return s; | |
86 | } | |
87 | ||
88 | ||
89 | /****f* ob_display/menu_application | |
90 | * FUNCTION | |
91 | * create a menu entry for an application. | |
92 | ****/ | |
93 | void | |
94 | menu_application (MenuCacheApp *app, OB_Menu *context) | |
95 | { | |
96 | gchar *exec_name = NULL; | |
97 | gchar *exec_icon = NULL; | |
98 | gchar *exec_cmd = NULL; | |
99 | ||
100 | /* is comment (description) or name displayed ? */ | |
101 | exec_name = (context->comment == TRUE) ? | |
102 | get_item_comment (MENU_CACHE_ITEM(app), TRUE) : get_item_name (MENU_CACHE_ITEM(app), TRUE); | |
103 | ||
104 | exec_cmd = clean_exec (app); | |
105 | // make sure we don't process item with no exec value (issue #13). This should never happend. | |
106 | if (exec_cmd == NULL){ | |
107 | return; | |
108 | } | |
109 | ||
110 | #ifdef WITH_ICONS | |
111 | if (!context->no_icons) | |
112 | { | |
113 | exec_icon = item_icon_path (MENU_CACHE_ITEM(app)); | |
114 | g_string_append_printf (context->builder, | |
115 | "<item label=\"%s\" icon=\"%s\"><action name=\"Execute\">", | |
116 | exec_name, | |
117 | exec_icon); | |
118 | } | |
119 | else | |
120 | #endif | |
121 | { | |
122 | g_string_append_printf (context->builder, | |
123 | "<item label=\"%s\"><action name=\"Execute\">", | |
124 | exec_name); | |
125 | } | |
126 | ||
127 | if (context->sn && menu_cache_app_get_use_sn (app)) | |
128 | g_string_append (context->builder, | |
129 | "<startupnotify><enabled>yes</enabled></startupnotify>"); | |
130 | ||
131 | if (menu_cache_app_get_use_terminal (app)) | |
132 | g_string_append_printf (context->builder, | |
133 | "<command><![CDATA[%s %s]]></command>\n</action></item>\n", | |
134 | context->terminal_cmd, | |
135 | exec_cmd); | |
136 | else | |
137 | g_string_append_printf (context->builder, | |
138 | "<command><![CDATA[%s]]></command>\n</action></item>\n", | |
139 | exec_cmd); | |
140 | ||
141 | g_free (exec_name); | |
142 | g_free (exec_icon); | |
143 | g_free (exec_cmd); | |
144 | } | |
145 | ||
146 | /****f* ob_display/menu_generate | |
147 | * FUNCTION | |
148 | * main routine of menu creation. | |
149 | * | |
150 | * NOTES | |
151 | * It calls itself when 'dir' type is MENU_CACHE_TYPE_DIR. | |
152 | ****/ | |
153 | void | |
154 | menu_generate (MenuCacheDir *dir, OB_Menu *context) | |
155 | { | |
156 | GSList *l = NULL; | |
157 | ||
158 | for (l = menu_cache_dir_get_children (dir); l; l = l->next) | |
159 | switch ((guint) menu_cache_item_get_type (MENU_CACHE_ITEM(l->data))) | |
160 | { | |
161 | case MENU_CACHE_TYPE_DIR: | |
162 | menu_directory (l->data, context); | |
163 | menu_generate (MENU_CACHE_DIR(l->data), context); | |
164 | g_string_append (context->builder, "</menu>\n"); | |
165 | break; | |
166 | ||
167 | case MENU_CACHE_TYPE_SEP: | |
168 | g_string_append (context->builder, "<separator />\n"); | |
169 | break; | |
170 | ||
171 | case MENU_CACHE_TYPE_APP: | |
172 | if (app_is_visible (MENU_CACHE_APP(l->data), context->show_flag)) | |
173 | menu_application (l->data, context); | |
174 | } | |
175 | } | |
176 | ||
177 | ||
178 | /****f* ob_display/get_header_footer_from_template | |
179 | * FUNCTION | |
180 | * Get header and footer string from a template file. If no template | |
181 | * file provided, the default template will bu used. | |
182 | * | |
183 | * INPUTS | |
184 | * * template | |
185 | * | |
186 | * RETURN VALUE | |
187 | * * a pointer to an array of strings that needs to be freed with g_strfreev. | |
188 | ****/ | |
189 | gchar **get_header_footer_from_template (gchar *template) | |
190 | { | |
191 | gchar *content = NULL; | |
192 | gchar **tokens = NULL; | |
193 | ||
194 | if (template && g_file_get_contents (template, &content, NULL, NULL)) | |
195 | { | |
196 | tokens = g_strsplit (content, "%MENU%", 2); | |
197 | g_free (content); | |
198 | } | |
199 | else | |
200 | { | |
201 | tokens = g_strsplit (default_template, "%MENU%", 2); | |
202 | } | |
203 | return tokens; | |
204 | } | |
205 | ||
206 | /****f* ob_display/menu_display | |
207 | * FUNCTION | |
208 | * it begins and closes the menu content, write it into a file or | |
209 | * display it. | |
210 | * | |
211 | * INPUTS | |
212 | * * menu | |
213 | * * file, the filename where the menu content should be written to. | |
214 | * If file is 'NULL' then the menu content is displayed. | |
215 | * | |
216 | * RETURN VALUE | |
217 | * Nothing. A MenuCacheReloadNotify callback returns void. | |
218 | * | |
219 | * NOTES | |
220 | * A 16 KiB GString is allocated for the content of the pipemenu. | |
221 | * This should be enough prevent too many allocations while building | |
222 | * the XML. | |
223 | * | |
224 | * The size of the XML file created is around 8 KB in my computer but | |
225 | * I don't have a lot of applications installed. | |
226 | ****/ | |
227 | void | |
228 | menu_display (MenuCache *menu, OB_Menu *context) | |
229 | { | |
230 | gchar **template_parts = NULL; | |
231 | ||
232 | MenuCacheDir *dir = menu_cache_dup_root_dir (menu); | |
233 | if (G_UNLIKELY(dir == NULL)) | |
234 | { | |
235 | g_warning ("Can't get menu root directory"); | |
236 | context->code = MENU_DIR_ERROR; | |
237 | return; | |
238 | } | |
239 | ||
240 | // Desktops are dynamically detected by menu_cache when reloading | |
241 | // its cache. It is now time to add our desktop to the show_flag in | |
242 | // the application context. | |
243 | add_current_desktop_to_context (menu, context); | |
244 | ||
245 | GSList *l = menu_cache_dir_get_children (dir); | |
246 | ||
247 | if (g_slist_length (l) != 0) { | |
248 | context->builder = g_string_sized_new (16 * 1024); | |
249 | ||
250 | template_parts = get_header_footer_from_template (context->template); | |
251 | // TODO: check if template_parts array contains 2 strings. | |
252 | ||
253 | g_string_append (context->builder, template_parts[0]); // add header | |
254 | menu_generate (dir, context); | |
255 | g_string_append (context->builder, template_parts[1]); // add footer | |
256 | ||
257 | g_strfreev (template_parts); | |
258 | ||
259 | gchar *buff = g_string_free (context->builder, FALSE); | |
260 | ||
261 | /* Has menu content to be saved in a file ? */ | |
262 | if (context->output) | |
263 | { | |
264 | if (!g_file_set_contents (context->output, buff, -1, NULL)) | |
265 | g_warning ("Can't write to %s\n", context->output); | |
266 | else | |
267 | g_message ("wrote to %s", context->output); | |
268 | } | |
269 | else /* No, so it's displayed on screen */ | |
270 | g_print ("%s", buff); | |
271 | ||
272 | g_free (buff); | |
273 | } | |
274 | else | |
275 | { | |
276 | g_warning ("Menu seems to be empty. Check openbox-menu parameters."); | |
277 | context->code = MENU_EMPTY_ERROR; | |
278 | } | |
279 | ||
280 | menu_cache_item_unref (MENU_CACHE_ITEM(dir)); | |
281 | } |
0 | /* | |
1 | * openbox-menu.h - this file is part of openbox-menu | |
2 | * Copyright (C) 2010-15 Fabrice THIROUX <fabrice.thiroux@free.fr> | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify | |
5 | * it under the terms of the GNU Lesser General Public License as published | |
6 | * by the Free Software Foundation; version 3.0 only. | |
7 | * | |
8 | * This program is distributed in the hope that it will be useful, | |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
11 | * GNU General Public License for more details. | |
12 | * | |
13 | * You should have received a copy of the GNU General Public License | |
14 | * along with this program; if not, write to the Free Software | |
15 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | |
16 | * MA 02110-1301, USA. | |
17 | */ | |
18 | ||
19 | #ifndef __OPENBOXMENU_APP__ | |
20 | #define __OPENBOXMENU_APP__ | |
21 | #include <menu-cache.h> | |
22 | ||
23 | #ifdef WITH_ICONS | |
24 | #include <gtk/gtk.h> | |
25 | #endif | |
26 | ||
27 | #define VERSION "0.8.0" | |
28 | #define APPMENU_SIZE 30 | |
29 | #define TERMINAL_CMD "xterm -e" | |
30 | ||
31 | #ifndef __VERSION_MINOR // since menu-cache 0.5.0. | |
32 | #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" | |
33 | #endif | |
34 | ||
35 | typedef enum { | |
36 | NO_ERROR = 0, | |
37 | CONFIG_ERROR, | |
38 | MENU_DIR_ERROR, | |
39 | MENU_EMPTY_ERROR, | |
40 | LOOKUP_ERROR, | |
41 | MENU_CACHE_ERROR, | |
42 | } OBM_Error; | |
43 | ||
44 | ||
45 | typedef struct { | |
46 | /* Configuration */ | |
47 | gchar *output; | |
48 | guint32 show_flag; | |
49 | GString *builder; /* */ | |
50 | gchar *terminal_cmd; /* command to launch program in a terminal */ | |
51 | gboolean comment; /* display description instead of name */ | |
52 | gboolean sn; /* startup notification */ | |
53 | gboolean no_icons; /* icons disabled */ | |
54 | gboolean persistent; | |
55 | gchar *menu_file; | |
56 | gchar *template; | |
57 | guint code; | |
58 | } OB_Menu; | |
59 | ||
60 | ||
61 | gchar *get_default_application_menu (); | |
62 | guint app_is_visible (MenuCacheApp *, guint32); | |
63 | gchar *clean_exec (MenuCacheApp *); | |
64 | gchar *safe_name (const char *); | |
65 | gchar *item_icon_path (MenuCacheItem*); | |
66 | guint32 get_current_desktop_flag (); | |
67 | void add_current_desktop_to_context (MenuCache *, OB_Menu *); | |
68 | ||
69 | void menu_display (MenuCache *, OB_Menu *); | |
70 | ||
71 | OB_Menu *context_new(); | |
72 | void context_set_terminal_cmd (OB_Menu*, gchar*); | |
73 | gchar *context_get_terminal_cmd (OB_Menu*); | |
74 | void context_set_desktop_flag (OB_Menu*, int); | |
75 | void context_add_desktop_flag (OB_Menu*, int); | |
76 | int context_get_desktop_flag (OB_Menu*); | |
77 | void context_set_persistent (OB_Menu*, gboolean); | |
78 | void context_set_persistent (OB_Menu*, gboolean); | |
79 | void context_set_comment (OB_Menu*, gboolean); | |
80 | gboolean context_get_persistent (OB_Menu*); | |
81 | ||
82 | void context_free(OB_Menu *); | |
83 | ||
84 | #ifdef WITH_ICONS | |
85 | GtkIconTheme *icon_theme; | |
86 | #endif | |
87 | ||
88 | #endif // __OPENBOXMENU_APP__ |
0 | // utils.c - this file is part of openbox-menu | |
1 | // Copyright (C) 2010-15 Fabrice THIROUX <fabrice.thiroux@free.fr> | |
2 | // | |
3 | // This program is free software; you can redistribute it and/or modify | |
4 | // it under the terms of the GNU General Public License as published by | |
5 | // the Free Software Foundation; version 3 of the License. | |
6 | // | |
7 | // This program is distributed in the hope that it will be useful, | |
8 | // but WITHOUT ANY WARRANTY; without even the implied warranty of | |
9 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
10 | // GNU General Public License for more details. | |
11 | // | |
12 | // You should have received a copy of the GNU General Public License | |
13 | // along with this program; if not, write to the Free Software | |
14 | // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | |
15 | // MA 02110-1301, USA. | |
16 | ||
17 | #include <glib.h> | |
18 | #ifdef WITH_ICONS | |
19 | #include <gtk/gtk.h> | |
20 | #endif | |
21 | #include <string.h> | |
22 | #include <stdlib.h> | |
23 | ||
24 | #include "openbox-menu.h" | |
25 | ||
26 | /****f* utils/get_default_application_menu | |
27 | * FUNCTION | |
28 | * Try to determine which menu file to use if none defined by user. | |
29 | * XDG_MENU_PREFIX variable exists, it is used to prefix menu name. | |
30 | * | |
31 | * RETURN VALUE | |
32 | * a char that need to be freed by caller. | |
33 | ****/ | |
34 | gchar * | |
35 | get_default_application_menu (void) | |
36 | { | |
37 | gchar menu[APPMENU_SIZE]; | |
38 | ||
39 | gchar *xdg_prefix = getenv("XDG_MENU_PREFIX"); | |
40 | if (xdg_prefix) | |
41 | { | |
42 | g_snprintf (menu, APPMENU_SIZE, "%sapplications.menu", xdg_prefix); | |
43 | } | |
44 | else | |
45 | g_strlcpy (menu, "applications.menu", APPMENU_SIZE); | |
46 | ||
47 | return strdup (menu); | |
48 | } | |
49 | ||
50 | /****f* utils/safe_name | |
51 | * FUNCTION | |
52 | * Convert &, <, > and " signs to html entities | |
53 | * | |
54 | * OUTPUT | |
55 | * A gchar that needs to be freed. | |
56 | ****/ | |
57 | gchar * | |
58 | safe_name (const char *name) | |
59 | { | |
60 | if (name == NULL) | |
61 | return NULL; | |
62 | ||
63 | GString *cmd = g_string_sized_new (256); | |
64 | ||
65 | for (;*name; ++name) | |
66 | { | |
67 | switch(*name) | |
68 | { | |
69 | case '&': | |
70 | g_string_append (cmd, "&"); | |
71 | break; | |
72 | case '<': | |
73 | g_string_append (cmd, "<"); | |
74 | break; | |
75 | case '>': | |
76 | g_string_append (cmd, ">"); | |
77 | break; | |
78 | case '"': | |
79 | g_string_append (cmd, """); | |
80 | break; | |
81 | default: | |
82 | g_string_append_c (cmd, *name); | |
83 | } | |
84 | } | |
85 | return g_string_free (cmd, FALSE); | |
86 | } | |
87 | ||
88 | ||
89 | /****f* utils/clean_exec | |
90 | * FUNCTION | |
91 | * Remove %f, %F, %u, %U, %i, %c, %k from exec field. | |
92 | * None of theses codes are interesting to manage here. | |
93 | * %i, %c and %k codes are implemented but don't ask why we need them. :) | |
94 | * | |
95 | * OUTPUT | |
96 | * A gchar that needs to be freed. | |
97 | * | |
98 | * NOTES | |
99 | * %d, %D, %n, %N, %v and %m are deprecated and should be removed. | |
100 | * | |
101 | * SEE ALSO | |
102 | * http://standards.freedesktop.org/desktop-entry-spec/latest/ar01s06.html | |
103 | ****/ | |
104 | gchar * | |
105 | clean_exec (MenuCacheApp *app) | |
106 | { | |
107 | gchar *filepath = NULL; | |
108 | const char *exec = menu_cache_app_get_exec (MENU_CACHE_APP(app)); | |
109 | ||
110 | g_return_val_if_fail(exec != NULL, NULL); | |
111 | ||
112 | GString *cmd = g_string_sized_new (64); | |
113 | ||
114 | for (;*exec; ++exec) | |
115 | { | |
116 | if (*exec == '%') | |
117 | { | |
118 | ++exec; | |
119 | switch (*exec) | |
120 | { | |
121 | /* useless and commonly used codes */ | |
122 | case 'u': | |
123 | case 'U': | |
124 | case 'f': | |
125 | case 'F': break; | |
126 | /* deprecated codes */ | |
127 | case 'd': | |
128 | case 'D': | |
129 | case 'm': | |
130 | case 'n': | |
131 | case 'N': | |
132 | case 'v': break; | |
133 | /* Other codes, more or less pointless to implement */ | |
134 | case 'c': | |
135 | g_string_append (cmd, menu_cache_item_get_name (MENU_CACHE_ITEM(app))); | |
136 | break; | |
137 | #if WITH_ICONS | |
138 | case 'i': | |
139 | if (item_icon_path (MENU_CACHE_ITEM(app))) | |
140 | { | |
141 | g_string_append_printf (cmd, "--icon %s", | |
142 | item_icon_path (MENU_CACHE_ITEM(app))); | |
143 | } | |
144 | break; | |
145 | #endif | |
146 | case 'k': | |
147 | filepath = menu_cache_item_get_file_path (MENU_CACHE_ITEM(app)); | |
148 | if (filepath) | |
149 | { | |
150 | g_string_append (cmd, filepath); | |
151 | g_free (filepath); | |
152 | } | |
153 | break; | |
154 | /* It was not in the freedesktop specification. */ | |
155 | default: | |
156 | g_string_append_c (cmd, '%'); | |
157 | g_string_append_c (cmd, *exec); | |
158 | break; | |
159 | } | |
160 | } | |
161 | else | |
162 | g_string_append_c (cmd, *exec); | |
163 | } | |
164 | return g_strchomp (g_string_free (cmd, FALSE)); | |
165 | } | |
166 | ||
167 | ||
168 | #if WITH_ICONS | |
169 | ||
170 | extern GtkIconTheme *icon_theme; | |
171 | ||
172 | /****f* utils/item_icon_path | |
173 | * OUTPUT | |
174 | * return the path for the themed icon if item. | |
175 | * If no icon found, it returns the "empty" icon path. | |
176 | * | |
177 | * The returned string should be freed when no longer needed | |
178 | * | |
179 | * NOTES | |
180 | * Imlib2, used by OpenBox to display icons, doesn't load SVG graphics. | |
181 | * We have to use GTK_ICON_LOOKUP_NO_SVG flag to look up icons. | |
182 | * | |
183 | * TODO | |
184 | * The "2nd fallback" is annoying, I have to think about this. | |
185 | ****/ | |
186 | gchar * | |
187 | item_icon_path (MenuCacheItem *item) | |
188 | { | |
189 | GtkIconInfo *icon_info = NULL; | |
190 | gchar *icon = NULL; | |
191 | gchar *tmp_name = NULL; | |
192 | ||
193 | const gchar *name = menu_cache_item_get_icon (MENU_CACHE_ITEM(item)); | |
194 | ||
195 | if (name) | |
196 | { | |
197 | if (g_path_is_absolute (name)) | |
198 | return g_strdup (name); | |
199 | ||
200 | /* We remove the file extension as gtk_icon_theme_lookup_icon can't | |
201 | * lookup a theme icon for, ie, 'geany.png'. It has to be 'geany'. | |
202 | */ | |
203 | tmp_name = strndup (name, strrchr (name, '.') - name); | |
204 | #ifdef WITH_SVG | |
205 | icon_info = gtk_icon_theme_lookup_icon (icon_theme, tmp_name, 16, GTK_ICON_LOOKUP_GENERIC_FALLBACK); | |
206 | #else | |
207 | icon_info = gtk_icon_theme_lookup_icon (icon_theme, tmp_name, 16, GTK_ICON_LOOKUP_NO_SVG | GTK_ICON_LOOKUP_GENERIC_FALLBACK); | |
208 | #endif | |
209 | g_free (tmp_name); | |
210 | } | |
211 | ||
212 | if (!icon_info) /* 2nd fallback */ | |
213 | icon_info = gtk_icon_theme_lookup_icon (icon_theme, "empty", 16, GTK_ICON_LOOKUP_NO_SVG); | |
214 | ||
215 | icon = g_strdup (gtk_icon_info_get_filename (icon_info)); | |
216 | gtk_icon_info_free (icon_info); | |
217 | ||
218 | return icon; | |
219 | } | |
220 | #endif /* WITH_ICONS */ | |
221 | ||
222 | ||
223 | guint | |
224 | app_is_visible(MenuCacheApp *app, guint32 de_flag) | |
225 | { | |
226 | gint32 flags = menu_cache_app_get_show_flags (app); | |
227 | ||
228 | if (flags < 0) | |
229 | return !(- flags & de_flag); | |
230 | else | |
231 | return menu_cache_app_get_is_visible(MENU_CACHE_APP(app), de_flag); | |
232 | } | |
233 | ||
234 | const char* get_desktop_name() { | |
235 | const gchar *desktop = g_getenv ("XDG_CURRENT_DESKTOP"); | |
236 | if (desktop) | |
237 | return desktop; | |
238 | ||
239 | desktop = g_getenv ("DESKTOP_SESSION"); | |
240 | if (desktop) | |
241 | return desktop; | |
242 | ||
243 | // We return nothing. | |
244 | return NULL; | |
245 | } | |
246 | ||
247 | void | |
248 | add_current_desktop_to_context (MenuCache *menu, OB_Menu *context) { | |
249 | const char* desktop = get_desktop_name (); | |
250 | if (desktop) { | |
251 | context->show_flag |= menu_cache_get_desktop_env_flag(menu, desktop); | |
252 | } | |
253 | } | |
254 |
0 | #include <glib.h> | |
1 | #include <string.h> | |
2 | ||
3 | #include "../src/openbox-menu.h" | |
4 | ||
5 | /* | |
6 | typedef struct { | |
7 | gchar *output; | |
8 | guint32 show_flag; | |
9 | GString *builder; / | |
10 | gchar *terminal_cmd; | |
11 | gboolean comment; | |
12 | gboolean sn; | |
13 | gboolean no_icons; | |
14 | gboolean persistent; | |
15 | gchar *menu_file; | |
16 | gchar *template; | |
17 | guint code; | |
18 | } OB_Menu; | |
19 | */ | |
20 | ||
21 | ||
22 | void test_set_desktop_flag () | |
23 | { | |
24 | OB_Menu* ctx = context_new(); | |
25 | context_set_desktop_flag (ctx, SHOW_IN_XFCE); | |
26 | ||
27 | gint flag = context_get_desktop_flag(ctx); | |
28 | ||
29 | g_assert_cmpint(flag, ==, SHOW_IN_XFCE); | |
30 | } | |
31 | ||
32 | ||
33 | void test_add_desktop_flag () | |
34 | { | |
35 | OB_Menu* ctx = context_new(); | |
36 | context_set_desktop_flag (ctx, SHOW_IN_XFCE); | |
37 | context_add_desktop_flag (ctx, SHOW_IN_KDE); | |
38 | ||
39 | gint flag = context_get_desktop_flag(ctx); | |
40 | ||
41 | g_assert_cmpint(flag, ==, SHOW_IN_XFCE | SHOW_IN_KDE); | |
42 | } | |
43 | ||
44 | ||
45 | void test_set_terminal_cmd() | |
46 | { | |
47 | OB_Menu* ctx = context_new(); | |
48 | context_set_terminal_cmd (ctx, "xterm"); | |
49 | ||
50 | gchar* cmd = context_get_terminal_cmd(ctx); | |
51 | ||
52 | g_assert_cmpstr(cmd, ==, "xterm"); | |
53 | } | |
54 | ||
55 | ||
56 | void test_persistent_true () | |
57 | { | |
58 | OB_Menu* ctx = context_new(); | |
59 | context_set_persistent(ctx, TRUE); | |
60 | ||
61 | gboolean mode = context_get_persistent(ctx); | |
62 | ||
63 | g_assert_true(mode); | |
64 | } | |
65 | ||
66 | ||
67 | void test_persistent_false () | |
68 | { | |
69 | OB_Menu* ctx = context_new(); | |
70 | ||
71 | gboolean mode = context_get_persistent(ctx); | |
72 | ||
73 | g_assert_false (mode); | |
74 | } |
0 | #include <math.h> | |
1 | #include <stdio.h> | |
2 | ||
3 | #include <glib.h> | |
4 | ||
5 | #include "tests.h" | |
6 | ||
7 | ||
8 | int main (int argc, char **argv) | |
9 | { | |
10 | g_test_init (&argc, &argv, NULL); | |
11 | ||
12 | g_test_add_func ("/Utils/safename_returns_foobar", test_safename1); | |
13 | g_test_add_func ("/Utils/safename_returns_html_entities", test_safename2); | |
14 | g_test_add_func ("/Utils/safename_returns_null", test_safename3); | |
15 | ||
16 | g_test_add_func ("/Utils/application_menu_with_prefix", test_application_menu_with_prefix); | |
17 | g_test_add_func ("/Utils/application_menu_without_prefix", test_application_menu_without_prefix); | |
18 | ||
19 | g_test_add_func ("/Context/set_terminal_cmd", test_set_terminal_cmd); | |
20 | g_test_add_func ("/Context/set_desktop_flag", test_set_desktop_flag); | |
21 | g_test_add_func ("/Context/add_desktop_flag", test_add_desktop_flag); | |
22 | ||
23 | g_test_add_func ("/Context/set_persistent_true", test_persistent_true); | |
24 | g_test_add_func ("/Context/set_persistent_false", test_persistent_false); | |
25 | ||
26 | ||
27 | ||
28 | g_test_set_nonfatal_assertions (); | |
29 | ||
30 | return g_test_run(); | |
31 | } |
0 | void test_safename1(); | |
1 | void test_safename2(); | |
2 | void test_safename3(); | |
3 | ||
4 | void test_application_menu_with_prefix(); | |
5 | void test_application_menu_without_prefix(); | |
6 | ||
7 | void test_set_terminal_cmd(); | |
8 | void test_set_desktop_flag(); | |
9 | void test_add_desktop_flag(); | |
10 | ||
11 | void test_persistent_true(); | |
12 | void test_persistent_false(); | |
13 | ||
14 |
0 | //#include <stdio.h> | |
1 | ||
2 | #include <glib.h> | |
3 | //#include <string.h> | |
4 | ||
5 | #include "../src/openbox-menu.h" | |
6 | ||
7 | ||
8 | ||
9 | void test_safename1() | |
10 | { | |
11 | char* filename = safe_name("foobar"); | |
12 | ||
13 | g_assert_cmpstr(filename, ==, "foobar"); | |
14 | } | |
15 | ||
16 | void test_safename2() | |
17 | { | |
18 | char* filename = safe_name("&<>\""); | |
19 | ||
20 | g_assert_cmpstr(filename, ==, "&<>""); | |
21 | } | |
22 | ||
23 | ||
24 | void test_safename3() | |
25 | { | |
26 | char* filename = safe_name(NULL); | |
27 | ||
28 | g_assert (filename == NULL); | |
29 | } | |
30 | ||
31 | ||
32 | void test_application_menu_with_prefix() | |
33 | { | |
34 | g_setenv("XDG_MENU_PREFIX", "mytest-", TRUE); | |
35 | gchar* result = get_default_application_menu(); | |
36 | ||
37 | g_assert_cmpstr(result, ==, "mytest-applications.menu"); | |
38 | } | |
39 | ||
40 | void test_application_menu_without_prefix() | |
41 | { | |
42 | g_unsetenv("XDG_MENU_PREFIX"); | |
43 | gchar* result = get_default_application_menu(); | |
44 | ||
45 | g_assert_cmpstr(result, ==, "applications.menu"); | |
46 | } | |
47 | ||
48 |
0 | // utils.c - this file is part of openbox-menu | |
1 | // Copyright (C) 2010-15 Fabrice THIROUX <fabrice.thiroux@free.fr> | |
2 | // | |
3 | // This program is free software; you can redistribute it and/or modify | |
4 | // it under the terms of the GNU General Public License as published by | |
5 | // the Free Software Foundation; version 3 of the License. | |
6 | // | |
7 | // This program is distributed in the hope that it will be useful, | |
8 | // but WITHOUT ANY WARRANTY; without even the implied warranty of | |
9 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
10 | // GNU General Public License for more details. | |
11 | // | |
12 | // You should have received a copy of the GNU General Public License | |
13 | // along with this program; if not, write to the Free Software | |
14 | // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | |
15 | // MA 02110-1301, USA. | |
16 | ||
17 | #include <glib.h> | |
18 | #ifdef WITH_ICONS | |
19 | #include <gtk/gtk.h> | |
20 | #endif | |
21 | #include <string.h> | |
22 | ||
23 | #include "openbox-menu.h" | |
24 | ||
25 | /****f* utils/safe_name | |
26 | * FUNCTION | |
27 | * Convert &, <, > and " signs to html entities | |
28 | * | |
29 | * OUTPUT | |
30 | * A gchar that needs to be freed. | |
31 | ****/ | |
32 | gchar * | |
33 | safe_name (const char *name) | |
34 | { | |
35 | g_return_val_if_fail (name != NULL, NULL); | |
36 | ||
37 | GString *cmd = g_string_sized_new (256); | |
38 | ||
39 | for (;*name; ++name) | |
40 | { | |
41 | switch(*name) | |
42 | { | |
43 | case '&': | |
44 | g_string_append (cmd, "&"); | |
45 | break; | |
46 | case '<': | |
47 | g_string_append (cmd, "<"); | |
48 | break; | |
49 | case '>': | |
50 | g_string_append (cmd, ">"); | |
51 | break; | |
52 | case '"': | |
53 | g_string_append (cmd, """); | |
54 | break; | |
55 | default: | |
56 | g_string_append_c (cmd, *name); | |
57 | } | |
58 | } | |
59 | return g_string_free (cmd, FALSE); | |
60 | } | |
61 | ||
62 | ||
63 | /****f* utils/clean_exec | |
64 | * FUNCTION | |
65 | * Remove %f, %F, %u, %U, %i, %c, %k from exec field. | |
66 | * None of theses codes are interesting to manage here. | |
67 | * %i, %c and %k codes are implemented but don't ask why we need them. :) | |
68 | * | |
69 | * OUTPUT | |
70 | * A gchar that needs to be freed. | |
71 | * | |
72 | * NOTES | |
73 | * %d, %D, %n, %N, %v and %m are deprecated and should be removed. | |
74 | * | |
75 | * SEE ALSO | |
76 | * http://standards.freedesktop.org/desktop-entry-spec/latest/ar01s06.html | |
77 | ****/ | |
78 | gchar * | |
79 | clean_exec (MenuCacheApp *app) | |
80 | { | |
81 | gchar *filepath = NULL; | |
82 | const char *exec = menu_cache_app_get_exec (MENU_CACHE_APP(app)); | |
83 | ||
84 | g_return_val_if_fail(exec,""); | |
85 | ||
86 | GString *cmd = g_string_sized_new (64); | |
87 | ||
88 | for (;*exec; ++exec) | |
89 | { | |
90 | if (*exec == '%') | |
91 | { | |
92 | ++exec; | |
93 | switch (*exec) | |
94 | { | |
95 | /* useless and commonly used codes */ | |
96 | case 'u': | |
97 | case 'U': | |
98 | case 'f': | |
99 | case 'F': break; | |
100 | /* deprecated codes */ | |
101 | case 'd': | |
102 | case 'D': | |
103 | case 'm': | |
104 | case 'n': | |
105 | case 'N': | |
106 | case 'v': break; | |
107 | /* Other codes, more or less pointless to implement */ | |
108 | case 'c': | |
109 | g_string_append (cmd, menu_cache_item_get_name (MENU_CACHE_ITEM(app))); | |
110 | break; | |
111 | #if WITH_ICONS | |
112 | case 'i': | |
113 | if (item_icon_path (MENU_CACHE_ITEM(app))) | |
114 | { | |
115 | g_string_append_printf (cmd, "--icon %s", | |
116 | item_icon_path (MENU_CACHE_ITEM(app))); | |
117 | } | |
118 | break; | |
119 | #endif | |
120 | case 'k': | |
121 | filepath = menu_cache_item_get_file_path (MENU_CACHE_ITEM(app)); | |
122 | if (filepath) | |
123 | { | |
124 | g_string_append (cmd, filepath); | |
125 | g_free (filepath); | |
126 | } | |
127 | break; | |
128 | /* It was not in the freedesktop specification. */ | |
129 | default: | |
130 | g_string_append_c (cmd, '%'); | |
131 | g_string_append_c (cmd, *exec); | |
132 | break; | |
133 | } | |
134 | } | |
135 | else | |
136 | g_string_append_c (cmd, *exec); | |
137 | } | |
138 | return g_strchomp (g_string_free (cmd, FALSE)); | |
139 | } | |
140 | ||
141 | ||
142 | #if WITH_ICONS | |
143 | ||
144 | extern GtkIconTheme *icon_theme; | |
145 | ||
146 | /****f* utils/item_icon_path | |
147 | * OUTPUT | |
148 | * return the path for the themed icon if item. | |
149 | * If no icon found, it returns the "empty" icon path. | |
150 | * | |
151 | * The returned string should be freed when no longer needed | |
152 | * | |
153 | * NOTES | |
154 | * Imlib2, used by OpenBox to display icons, doesn't load SVG graphics. | |
155 | * We have to use GTK_ICON_LOOKUP_NO_SVG flag to look up icons. | |
156 | * | |
157 | * TODO | |
158 | * The "2nd fallback" is annoying, I have to think about this. | |
159 | ****/ | |
160 | gchar * | |
161 | item_icon_path (MenuCacheItem *item) | |
162 | { | |
163 | GtkIconInfo *icon_info = NULL; | |
164 | gchar *icon = NULL; | |
165 | gchar *tmp_name = NULL; | |
166 | ||
167 | const gchar *name = menu_cache_item_get_icon (MENU_CACHE_ITEM(item)); | |
168 | ||
169 | if (name) | |
170 | { | |
171 | if (g_path_is_absolute (name)) | |
172 | return g_strdup (name); | |
173 | ||
174 | /* We remove the file extension as gtk_icon_theme_lookup_icon can't | |
175 | * lookup a theme icon for, ie, 'geany.png'. It has to be 'geany'. | |
176 | */ | |
177 | tmp_name = strndup (name, strrchr (name, '.') - name); | |
178 | #ifdef WITH_SVG | |
179 | icon_info = gtk_icon_theme_lookup_icon (icon_theme, tmp_name, 16, GTK_ICON_LOOKUP_GENERIC_FALLBACK); | |
180 | #else | |
181 | icon_info = gtk_icon_theme_lookup_icon (icon_theme, tmp_name, 16, GTK_ICON_LOOKUP_NO_SVG | GTK_ICON_LOOKUP_GENERIC_FALLBACK); | |
182 | #endif | |
183 | g_free (tmp_name); | |
184 | } | |
185 | ||
186 | if (!icon_info) /* 2nd fallback */ | |
187 | icon_info = gtk_icon_theme_lookup_icon (icon_theme, "empty", 16, GTK_ICON_LOOKUP_NO_SVG); | |
188 | ||
189 | icon = g_strdup (gtk_icon_info_get_filename (icon_info)); | |
190 | gtk_icon_info_free (icon_info); | |
191 | ||
192 | return icon; | |
193 | } | |
194 | #endif /* WITH_ICONS */ | |
195 | ||
196 | ||
197 | guint | |
198 | app_is_visible(MenuCacheApp *app, guint32 de_flag) | |
199 | { | |
200 | gint32 flags = menu_cache_app_get_show_flags (app); | |
201 | ||
202 | if (flags < 0) | |
203 | return !(- flags & de_flag); | |
204 | else | |
205 | return menu_cache_app_get_is_visible(MENU_CACHE_APP(app), de_flag); | |
206 | } | |
207 | ||
208 | const char* get_desktop_name() { | |
209 | const gchar *desktop = g_getenv ("XDG_CURRENT_DESKTOP"); | |
210 | if (desktop) | |
211 | return desktop; | |
212 | ||
213 | desktop = g_getenv ("DESKTOP_SESSION"); | |
214 | if (desktop) | |
215 | return desktop; | |
216 | ||
217 | // We return nothing. | |
218 | return NULL; | |
219 | } | |
220 | ||
221 | void | |
222 | add_current_desktop_to_context (MenuCache *menu, OB_Menu *context) { | |
223 | const char* desktop = get_desktop_name (); | |
224 | if (desktop) { | |
225 | context->show_flag |= menu_cache_get_desktop_env_flag(menu, desktop); | |
226 | } | |
227 | } | |
228 |