r3768: Updated years.
[rox-filer.git] / ROX-Filer / src / appmenu.c
blob533ce6543a9a4a5bd74dcfa57ebc24faf188983c
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2005, 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 /* XXX: This handles all File menu extensions. Needs renaming! */
26 #include "config.h"
28 #include <gtk/gtk.h>
29 #include <errno.h>
30 #include <stdio.h>
31 #include <string.h>
32 #include <ctype.h>
34 #include <libxml/parser.h>
36 #include "global.h"
38 #include "choices.h"
39 #include "fscache.h"
40 #include "gui_support.h"
41 #include "support.h"
42 #include "menu.h"
43 #include "filer.h"
44 #include "appmenu.h"
45 #include "dir.h"
46 #include "type.h"
47 #include "appinfo.h"
48 #include "xml.h"
49 #include "run.h"
50 #include "diritem.h"
51 #include "action.h"
52 #include "options.h"
54 /* Static prototypes */
55 static void apprun_menu(GtkWidget *item, gpointer data);
56 static GtkWidget *create_menu_item(xmlNode *node);
57 static void show_app_help(GtkWidget *item, gpointer data);
58 static void build_app_menu(const char *app_dir, DirItem *app_item);
59 static void mnt_eject(GtkWidget *item, gpointer data);
61 /* There can only be one menu open at a time... we store: */
62 static GtkWidget *current_menu = NULL; /* The GtkMenu */
63 static guchar *current_app_path = NULL; /* The path of the application */
64 static GList *current_items = NULL; /* The GtkMenuItems we added directly
65 * to it --- not submenu items.
67 /****************************************************************
68 * EXTERNAL INTERFACE *
69 ****************************************************************/
71 /* Removes all appmenu menu items */
72 void appmenu_remove(void)
74 GList *next;
76 if (!current_menu)
77 return;
79 for (next = current_items; next; next = next->next)
80 gtk_widget_destroy((GtkWidget *) next->data);
82 null_g_free(&current_app_path);
83 current_menu = NULL;
85 g_list_free(current_items);
86 current_items = NULL;
89 /* Add AppMenu entries to 'menu', if appropriate.
90 * This function modifies the menu stored in "menu".
91 * 'app_dir' is the pathname of the application directory, and 'item'
92 * is the corresponding DirItem.
93 * Returns number of entries added.
94 * Call appmenu_remove() to undo the effect.
96 int appmenu_add(const gchar *app_dir, DirItem *app_item, GtkWidget *menu)
98 GList *next;
99 GtkWidget *sep;
100 int nadded = 0;
102 g_return_val_if_fail(menu != NULL, 0);
104 /* Should have called appmenu_remove() already... */
105 g_return_val_if_fail(current_menu == NULL, 0);
106 g_return_val_if_fail(current_items == NULL, 0);
108 build_app_menu(app_dir, app_item);
110 if (app_item->flags & ITEM_FLAG_MOUNT_POINT)
112 GtkWidget *item;
113 item = gtk_menu_item_new_with_label(_("Eject"));
114 gtk_widget_show(item);
115 current_items = g_list_prepend(current_items, item);
116 g_signal_connect(item, "activate", G_CALLBACK(mnt_eject), NULL);
119 if (current_items)
121 sep = gtk_menu_item_new();
122 current_items = g_list_prepend(current_items, sep);
123 gtk_widget_show(sep);
126 for (next = current_items; next; next = next->next)
128 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu),
129 GTK_WIDGET(next->data));
130 nadded++;
133 current_menu = menu;
134 current_app_path = g_strdup(app_dir);
136 return nadded;
140 /****************************************************************
141 * INTERNAL FUNCTIONS *
142 ****************************************************************/
144 /* Create a new menu and return it */
145 static GtkWidget *appmenu_add_submenu(xmlNode *subm_node)
147 xmlNode *node;
148 GtkWidget *sub_menu;
150 /* Create the new submenu */
151 sub_menu = gtk_menu_new();
153 /* Add the menu entries */
154 for (node = subm_node->xmlChildrenNode; node; node = node->next)
156 GtkWidget *item;
158 item = create_menu_item(node);
159 if (item)
160 gtk_menu_shell_append(GTK_MENU_SHELL(sub_menu), item);
163 return sub_menu;
166 /* Create and return a menu item */
167 static GtkWidget *create_menu_item(xmlNode *node)
169 GtkWidget *item;
170 xmlNode *label_node;
171 guchar *label, *option = NULL;
172 gboolean is_submenu;
174 if (node->type != XML_ELEMENT_NODE)
175 return NULL;
177 if (strcmp(node->name, "Item") == 0)
179 is_submenu = FALSE;
180 option = xmlGetProp(node, "option");
182 else if (strcmp(node->name, "AppMenu") == 0)
183 is_submenu = TRUE;
184 else
185 return NULL;
187 /* Create the item */
188 label_node = get_subnode(node, NULL, "Label");
189 if (label_node)
191 label = xmlNodeListGetString(label_node->doc,
192 label_node->xmlChildrenNode, 1);
194 else
196 label = xmlGetProp(node, "label");
197 if (!label)
198 label = g_strdup(_("<missing label>"));
200 item = gtk_menu_item_new_with_label(label);
202 gtk_widget_set_accel_path(item, NULL, NULL); /* XXX */
204 g_free(label);
206 if (is_submenu)
208 /* Add submenu items */
210 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item),
211 appmenu_add_submenu(node));
213 else
215 /* Set up callback */
217 if (option)
219 g_object_set_data_full(G_OBJECT(item), "option",
220 g_strdup(option),
221 g_free);
222 g_free(option);
225 g_signal_connect(item, "activate", G_CALLBACK(apprun_menu),
226 NULL);
229 gtk_widget_show(item);
231 return item;
234 /* Send to current_app_path (though not actually an app) */
235 static void send_to(GtkWidget *item, const char *app)
237 GList *file_list;
239 g_return_if_fail(current_app_path != NULL);
241 file_list = g_list_prepend(NULL, current_app_path);
242 run_with_files(app, file_list);
243 g_list_free(file_list);
246 /* Function called to execute an AppMenu item */
247 static void apprun_menu(GtkWidget *item, gpointer data)
249 guchar *option;
250 gchar *argv[3];
252 g_return_if_fail(current_app_path != NULL);
254 option = g_object_get_data(G_OBJECT(item), "option");
256 argv[0] = g_strconcat(current_app_path, "/AppRun", NULL);
257 argv[1] = option; /* (may be NULL) */
258 argv[2] = NULL;
260 rox_spawn(NULL, (const gchar **) argv);
262 g_free(argv[0]);
265 static void show_app_help(GtkWidget *item, gpointer data)
267 g_return_if_fail(current_app_path != NULL);
269 show_help_files(current_app_path);
272 static void mnt_eject(GtkWidget *item, gpointer data)
274 GList *dirs;
276 g_return_if_fail(current_app_path != NULL);
277 dirs = g_list_prepend(NULL, current_app_path);
278 action_eject(dirs);
279 g_list_free(dirs);
282 static void customise_type(GtkWidget *item, MIME_type *type)
284 char *leaf;
285 char *path;
287 leaf = g_strconcat(".", type->media_type, "_", type->subtype, NULL);
288 path = choices_find_path_save(leaf, "SendTo", TRUE);
289 g_free(leaf);
291 mkdir(path, 0755);
292 filer_opendir(path, NULL, NULL);
293 g_free(path);
295 info_message(_("Symlink any programs you want into this directory. "
296 "They will appear in the menu for all items of this "
297 "type (%s/%s)."), type->media_type, type->subtype);
300 static void build_menu_for_type(MIME_type *type)
302 GPtrArray *names;
303 char *path;
304 int i;
305 char *leaf;
306 GtkWidget *item;
307 DirItem *ditem;
309 leaf = g_strconcat(".", type->media_type, "_", type->subtype, NULL);
310 path = choices_find_path_load(leaf, "SendTo");
312 if (!path)
313 goto out;
315 names = list_dir(path);
317 ditem = diritem_new("");
319 for (i = 0; i < names->len; i++)
321 char *leaf = names->pdata[i];
322 char *full_path;
324 full_path = g_build_filename(path, leaf, NULL);
325 diritem_restat(full_path, ditem, NULL);
327 item = make_send_to_item(ditem, leaf, MIS_SMALL);
328 current_items = g_list_prepend(current_items, item);
329 gtk_widget_show(item);
330 g_signal_connect_data(item, "activate", G_CALLBACK(send_to),
331 full_path, (GClosureNotify) g_free, 0);
334 g_ptr_array_free(names, TRUE);
336 g_free(path);
338 out:
339 item = gtk_menu_item_new_with_label(_("Customise Menu..."));
340 current_items = g_list_prepend(current_items, item);
341 g_signal_connect(item, "activate", G_CALLBACK(customise_type), type);
342 gtk_widget_show(item);
344 g_free(leaf);
347 /* Adds to current_items */
348 static void build_app_menu(const char *app_dir, DirItem *app_item)
350 XMLwrapper *ai = NULL;
351 xmlNode *node;
352 GtkWidget *item;
354 ai = appinfo_get(app_dir, app_item);
355 if (ai)
357 node = xml_get_section(ai, NULL, "AppMenu");
358 if (node)
359 node = node->xmlChildrenNode;
361 else
363 if (app_item->flags & ITEM_FLAG_APPDIR)
364 node = NULL;
365 else
367 /* Not an application AND no AppInfo */
368 build_menu_for_type(app_item->mime_type);
369 return;
373 /* Add the menu entries */
374 for (; node; node = node->next)
376 item = create_menu_item(node);
378 if (item)
379 current_items = g_list_prepend(current_items, item);
382 item = gtk_image_menu_item_new_from_stock(GTK_STOCK_HELP, NULL);
383 gtk_widget_show(item);
384 current_items = g_list_prepend(current_items, item);
385 g_signal_connect(item, "activate", G_CALLBACK(show_app_help), NULL);
386 gtk_label_set_text(GTK_LABEL(GTK_BIN(item)->child), _("Help"));
388 if (ai)
389 g_object_unref(ai);