Codebase list openbox-menu / upstream/0.8.0+hg20161009
New upstream version 0.8.0+hg20161009 Mateusz Łukasik 6 years ago
16 changed file(s) with 1208 addition(s) and 856 deletion(s). Raw diff Collapse all Expand all
44
55 # Comment this line if you don't want icons to appear in menu
66 CFLAGS+=-DWITH_ICONS
7 # Uncomment this line if Openbox can display SVG icons
7 # Uncomment this line if Openbox can display SVG icons
88 # Check SVG support with '$ ldd /usr/bin/openbox | grep svg', librsvg must appear..
99 # CFLAGS+=-DWITH_SVG
1010
1212 DESTDIR ?= $(prefix)
1313 BINDIR= ${DESTDIR}/bin
1414
15 SRC= $(shell ls *.c 2> /dev/null)
15 SRC= $(shell ls src/*.c 2> /dev/null)
1616 OBJ= $(SRC:.c=.o)
1717
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
1922
2023 %.o: %.c
2124 $(CC) $(CFLAGS) -c $< -o $@
2225
26
2327 openbox-menu: $(OBJ)
2428 $(CC) $(OBJ) -o openbox-menu $(LDFLAGS) $(LIBS)
2529
26 .PHONY: clean install doc changelog check
30 .PHONY: clean install doc changelog check xmllint
2731
2832 clean:
29 @rm -f *.o openbox-menu
33 @rm -f $(OBJ) $(TEST_OBJ) openbox-menu check
3034 @rm -rf doc
3135
3236 install:
3640 doc:
3741 robodoc --src . --doc doc/ --multidoc --index --html --cmode
3842
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
4149 xmllint test.xml
4250 rm test.xml
4351
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
-302
menu.c less more
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
-252
ob_display.c less more
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
-66
openbox-menu.h less more
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, "&amp;");
71 break;
72 case '<':
73 g_string_append (cmd, "&lt;");
74 break;
75 case '>':
76 g_string_append (cmd, "&gt;");
77 break;
78 case '"':
79 g_string_append (cmd, "&quot;");
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 <openbox_menu>%MENU%</openbox_menu>⏎
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, ==, "&amp;&lt;&gt;&quot;");
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
-229
utils.c less more
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, "&amp;");
45 break;
46 case '<':
47 g_string_append (cmd, "&lt;");
48 break;
49 case '>':
50 g_string_append (cmd, "&gt;");
51 break;
52 case '"':
53 g_string_append (cmd, "&quot;");
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