r3082: Bugfix: If no pinboard or panel icons were present, the menu could not be
[rox-filer.git] / ROX-Filer / src / appmenu.c
blob75e6073bd90553498e354bca3b497e4dd1aa3d28
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2003, the ROX-Filer team.
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
10 * any later version.
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * more details.
17 * You should have received a copy of the GNU General Public License along with
18 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
19 * Place, Suite 330, Boston, MA 02111-1307 USA
22 /* appmenu.c - handles application-specific menus read from XMLwrapper.xml */
24 #include "config.h"
26 #include <gtk/gtk.h>
27 #include <errno.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <ctype.h>
32 #include <libxml/parser.h>
34 #include "global.h"
36 #include "fscache.h"
37 #include "gui_support.h"
38 #include "support.h"
39 #include "menu.h"
40 #include "filer.h"
41 #include "appmenu.h"
42 #include "dir.h"
43 #include "type.h"
44 #include "appinfo.h"
45 #include "xml.h"
46 #include "run.h"
47 #include "diritem.h"
48 #include "action.h"
49 #include "options.h"
51 /* Static prototypes */
52 static void apprun_menu(GtkWidget *item, gpointer data);
53 static GtkWidget *create_menu_item(xmlNode *node);
54 static void show_app_help(GtkWidget *item, gpointer data);
55 static void build_mount_menu(const char *mnt_dir, DirItem *app_item);
56 static void build_app_menu(const char *app_dir, DirItem *app_item);
58 /* There can only be one menu open at a time... we store: */
59 static GtkWidget *current_menu = NULL; /* The GtkMenu */
60 static guchar *current_app_path = NULL; /* The path of the application */
61 static GList *current_items = NULL; /* The GtkMenuItems we added directly
62 * to it --- not submenu items.
64 static Option o_mount_free; /* Command to run to show free space */
65 static Option o_mount_format; /* Command to run to format device */
67 /****************************************************************
68 * EXTERNAL INTERFACE *
69 ****************************************************************/
71 void appmenu_init(void)
73 option_add_string(&o_mount_free, "mount_free", "");
74 option_add_string(&o_mount_format, "mount_format", "");
77 /* Removes all appmenu menu items */
78 void appmenu_remove(void)
80 GList *next;
82 if (!current_menu)
83 return;
85 for (next = current_items; next; next = next->next)
86 gtk_widget_destroy((GtkWidget *) next->data);
88 null_g_free(&current_app_path);
89 current_menu = NULL;
91 g_list_free(current_items);
92 current_items = NULL;
95 /* Add AppMenu entries to 'menu', if appropriate.
96 * This function modifies the menu stored in "menu".
97 * 'app_dir' is the pathname of the application directory, and 'item'
98 * is the corresponding DirItem.
99 * Call appmenu_remove() to undo the effect.
101 void appmenu_add(const gchar *app_dir, DirItem *app_item, GtkWidget *menu)
103 GList *next;
104 GtkWidget *sep;
106 g_return_if_fail(menu != NULL);
108 /* Should have called appmenu_remove() already... */
109 g_return_if_fail(current_menu == NULL);
110 g_return_if_fail(current_items == NULL);
112 if (app_item->flags & ITEM_FLAG_MOUNT_POINT)
113 build_mount_menu(app_dir, app_item);
114 else
115 build_app_menu(app_dir, app_item);
117 if (current_items)
119 sep = gtk_menu_item_new();
120 current_items = g_list_prepend(current_items, sep);
121 gtk_widget_show(sep);
124 for (next = current_items; next; next = next->next)
125 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu),
126 GTK_WIDGET(next->data));
128 current_menu = menu;
129 current_app_path = g_strdup(app_dir);
133 /****************************************************************
134 * INTERNAL FUNCTIONS *
135 ****************************************************************/
137 /* Create a new menu and return it */
138 static GtkWidget *appmenu_add_submenu(xmlNode *subm_node)
140 xmlNode *node;
141 GtkWidget *sub_menu;
143 /* Create the new submenu */
144 sub_menu = gtk_menu_new();
146 /* Add the menu entries */
147 for (node = subm_node->xmlChildrenNode; node; node = node->next)
149 GtkWidget *item;
151 item = create_menu_item(node);
152 if (item)
153 gtk_menu_shell_append(GTK_MENU_SHELL(sub_menu), item);
156 return sub_menu;
159 /* Create and return a menu item */
160 static GtkWidget *create_menu_item(xmlNode *node)
162 GtkWidget *item;
163 xmlNode *label_node;
164 guchar *label, *option = NULL;
165 gboolean is_submenu;
167 if (node->type != XML_ELEMENT_NODE)
168 return NULL;
170 if (strcmp(node->name, "Item") == 0)
172 is_submenu = FALSE;
173 option = xmlGetProp(node, "option");
175 else if (strcmp(node->name, "AppMenu") == 0)
176 is_submenu = TRUE;
177 else
178 return NULL;
180 /* Create the item */
181 label_node = get_subnode(node, NULL, "Label");
182 if (label_node)
184 label = xmlNodeListGetString(label_node->doc,
185 label_node->xmlChildrenNode, 1);
187 else
189 label = xmlGetProp(node, "label");
190 if (!label)
191 label = g_strdup(_("<missing label>"));
193 item = gtk_menu_item_new_with_label(label);
195 gtk_widget_set_accel_path(item, NULL, NULL); /* XXX */
197 g_free(label);
199 if (is_submenu)
201 /* Add submenu items */
203 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item),
204 appmenu_add_submenu(node));
206 else
208 /* Set up callback */
210 if (option)
212 g_object_set_data_full(G_OBJECT(item), "option",
213 g_strdup(option),
214 g_free);
215 g_free(option);
218 g_signal_connect(item, "activate", G_CALLBACK(apprun_menu),
219 NULL);
222 gtk_widget_show(item);
224 return item;
227 /* Function called to execute an AppMenu item */
228 static void apprun_menu(GtkWidget *item, gpointer data)
230 guchar *option;
231 gchar *argv[3];
233 g_return_if_fail(current_app_path != NULL);
235 option = g_object_get_data(G_OBJECT(item), "option");
237 argv[0] = g_strconcat(current_app_path, "/AppRun", NULL);
238 argv[1] = option; /* (may be NULL) */
239 argv[2] = NULL;
241 rox_spawn(NULL, (const gchar **) argv);
243 g_free(argv[0]);
246 static void show_app_help(GtkWidget *item, gpointer data)
248 g_return_if_fail(current_app_path != NULL);
250 show_help_files(current_app_path);
253 static void mnt_eject(GtkWidget *item, gpointer data)
255 GList *dirs;
257 g_return_if_fail(current_app_path != NULL);
258 dirs = g_list_prepend(NULL, current_app_path);
259 action_eject(dirs);
260 g_list_free(dirs);
263 static void mnt_free(GtkWidget *item, gpointer data)
265 char *argv[4]={"sh", "-c", NULL, NULL};
266 char *fmt;
267 gchar *tmp=NULL;
269 g_return_if_fail(current_app_path != NULL);
271 if(o_mount_free.value[0]) {
272 fmt=o_mount_free.value;
273 if(fmt[0]!='/') {
274 /* We do our own searching so we can handle AppDir's */
275 const gchar *path=g_getenv("PATH");
276 gchar **dirs=g_strsplit(path, ":", 100);
277 int i;
278 gchar **words=g_strsplit(fmt, " ", 2);
280 for(i=0; dirs[i]; i++) {
281 gchar *target=g_build_filename(dirs[i],
282 words[0], NULL);
284 if(file_exists(target)) {
285 DirItem *di;
286 di=diritem_new(words[0]);
287 diritem_restat(target, di, NULL);
289 if(di->flags & ITEM_FLAG_APPDIR) {
290 tmp=g_strconcat(target,
291 "/AppRun ",
292 words[1],
293 NULL);
294 fmt=tmp;
296 diritem_free(di);
297 g_free(target);
298 break;
301 diritem_free(di);
303 g_free(target);
306 g_strfreev(dirs);
307 g_strfreev(words);
309 } else {
310 fmt="xterm -hold -e df -k %s";
313 argv[2]=g_strdup_printf(fmt, current_app_path);
314 rox_spawn(current_app_path, (const gchar **) argv);
315 g_free(argv[2]);
317 if(tmp)
318 g_free(tmp);
321 static void mnt_format(GtkWidget *item, gpointer data)
323 char *argv[4]={"sh", "-c", NULL, NULL};
325 g_return_if_fail(current_app_path != NULL);
327 if(!o_mount_format.value[0]) {
328 delayed_error(_("No program has been given to format a device"));
329 return;
332 argv[2]=g_strdup_printf(o_mount_format.value, current_app_path);
333 rox_spawn(current_app_path, (const gchar **) argv);
334 g_free(argv[2]);
338 static void build_mount_menu(const char *mnt_dir, DirItem *app_item)
340 GtkWidget *item;
341 gboolean mounted=(app_item->flags & ITEM_FLAG_MOUNTED);
343 item = gtk_menu_item_new_with_label(_("Eject"));
344 gtk_widget_show(item);
345 current_items = g_list_prepend(current_items, item);
346 g_signal_connect(item, "activate", G_CALLBACK(mnt_eject), NULL);
348 item = gtk_menu_item_new_with_label(_("Format"));
349 gtk_widget_show(item);
350 current_items = g_list_prepend(current_items, item);
351 g_signal_connect(item, "activate", G_CALLBACK(mnt_format), NULL);
352 if(mounted)
353 gtk_widget_set_sensitive(item, FALSE);
355 item = gtk_menu_item_new_with_label(_("Free Space"));
356 gtk_widget_show(item);
357 current_items = g_list_prepend(current_items, item);
358 g_signal_connect(item, "activate", G_CALLBACK(mnt_free), NULL);
359 if(!mounted)
360 gtk_widget_set_sensitive(item, FALSE);
364 /* Adds to current_items */
365 static void build_app_menu(const char *app_dir, DirItem *app_item)
367 XMLwrapper *ai = NULL;
368 xmlNode *node;
369 GtkWidget *item;
371 ai = appinfo_get(app_dir, app_item);
372 if (ai)
374 node = xml_get_section(ai, NULL, "AppMenu");
375 if (node)
376 node = node->xmlChildrenNode;
378 else
380 if (app_item->flags & ITEM_FLAG_APPDIR)
381 node = NULL;
382 else
383 return; /* Not an application AND no AppInfo */
386 /* Add the menu entries */
387 for (; node; node = node->next)
389 item = create_menu_item(node);
391 if (item)
392 current_items = g_list_prepend(current_items, item);
395 item = gtk_image_menu_item_new_from_stock(GTK_STOCK_HELP, NULL);
396 gtk_widget_show(item);
397 current_items = g_list_prepend(current_items, item);
398 g_signal_connect(item, "activate", G_CALLBACK(show_app_help), NULL);
399 gtk_label_set_text(GTK_LABEL(GTK_BIN(item)->child), _("Help"));
401 if (ai)
402 g_object_unref(ai);
406 * Persuade [X]Emacs to use the right indenting
407 * Local Variables:
408 * mode:c
409 * c-basic-offset:8
410 * End: