r4800: Updated Russian translation (Nikita E. Shalaev).
[rox-filer.git] / ROX-Filer / src / appmenu.c
blob53778f03224bb67d7398c1f0632735d5f8a5f506
1 /*
2 * ROX-Filer, filer for the ROX desktop project
3 * Copyright (C) 2006, Thomas Leonard and others (see changelog for details).
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the Free
7 * Software Foundation; either version 2 of the License, or (at your option)
8 * any later version.
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
15 * You should have received a copy of the GNU General Public License along with
16 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
17 * Place, Suite 330, Boston, MA 02111-1307 USA
20 /* appmenu.c - handles application-specific menus read from XMLwrapper.xml */
22 /* XXX: This handles all File menu extensions. Needs renaming! */
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 "choices.h"
37 #include "fscache.h"
38 #include "gui_support.h"
39 #include "support.h"
40 #include "menu.h"
41 #include "filer.h"
42 #include "appmenu.h"
43 #include "dir.h"
44 #include "type.h"
45 #include "appinfo.h"
46 #include "xml.h"
47 #include "run.h"
48 #include "diritem.h"
49 #include "action.h"
50 #include "options.h"
52 /* Static prototypes */
53 static void apprun_menu(GtkWidget *item, gpointer data);
54 static GtkWidget *create_menu_item(xmlNode *node);
55 static void show_app_help(GtkWidget *item, gpointer data);
56 static void build_app_menu(const char *app_dir, DirItem *app_item);
57 static void mnt_eject(GtkWidget *item, gpointer data);
59 /* There can only be one menu open at a time... we store: */
60 static GtkWidget *current_menu = NULL; /* The GtkMenu */
61 static guchar *current_app_path = NULL; /* The path of the application */
62 static GList *current_items = NULL; /* The GtkMenuItems we added directly
63 * to it --- not submenu items.
65 /****************************************************************
66 * EXTERNAL INTERFACE *
67 ****************************************************************/
69 /* Removes all appmenu menu items */
70 void appmenu_remove(void)
72 GList *next;
74 if (!current_menu)
75 return;
77 for (next = current_items; next; next = next->next)
78 gtk_widget_destroy((GtkWidget *) next->data);
80 null_g_free(&current_app_path);
81 current_menu = NULL;
83 g_list_free(current_items);
84 current_items = NULL;
87 /* Add AppMenu entries to 'menu', if appropriate.
88 * This function modifies the menu stored in "menu".
89 * 'app_dir' is the pathname of the application directory, and 'item'
90 * is the corresponding DirItem.
91 * Returns number of entries added.
92 * Call appmenu_remove() to undo the effect.
94 int appmenu_add(const gchar *app_dir, DirItem *app_item, GtkWidget *menu)
96 GList *next;
97 GtkWidget *sep;
98 int nadded = 0;
100 g_return_val_if_fail(menu != NULL, 0);
102 /* Should have called appmenu_remove() already... */
103 g_return_val_if_fail(current_menu == NULL, 0);
104 g_return_val_if_fail(current_items == NULL, 0);
106 build_app_menu(app_dir, app_item);
108 if (app_item->flags & ITEM_FLAG_MOUNT_POINT)
110 GtkWidget *item;
111 item = gtk_menu_item_new_with_label(_("Eject"));
112 gtk_widget_show(item);
113 current_items = g_list_prepend(current_items, item);
114 g_signal_connect(item, "activate", G_CALLBACK(mnt_eject), NULL);
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)
126 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu),
127 GTK_WIDGET(next->data));
128 nadded++;
131 current_menu = menu;
132 current_app_path = g_strdup(app_dir);
134 return nadded;
138 /****************************************************************
139 * INTERNAL FUNCTIONS *
140 ****************************************************************/
142 /* Create a new menu and return it */
143 static GtkWidget *appmenu_add_submenu(xmlNode *subm_node)
145 xmlNode *node;
146 GtkWidget *sub_menu;
148 /* Create the new submenu */
149 sub_menu = gtk_menu_new();
151 /* Add the menu entries */
152 for (node = subm_node->xmlChildrenNode; node; node = node->next)
154 GtkWidget *item;
156 item = create_menu_item(node);
157 if (item)
158 gtk_menu_shell_append(GTK_MENU_SHELL(sub_menu), item);
161 return sub_menu;
164 /* Create and return a menu item */
165 static GtkWidget *create_menu_item(xmlNode *node)
167 GtkWidget *item;
168 xmlNode *label_node;
169 guchar *label, *option = NULL;
170 guchar *icon_name = NULL;
171 gboolean is_submenu;
173 if (node->type != XML_ELEMENT_NODE)
174 return NULL;
176 if (strcmp(node->name, "Item") == 0)
178 is_submenu = FALSE;
179 option = xmlGetProp(node, "option");
181 else if (strcmp(node->name, "AppMenu") == 0)
182 is_submenu = TRUE;
183 else
184 return NULL;
186 /* Create the item */
187 label_node = get_subnode(node, NULL, "Label");
188 if (label_node)
190 label = xmlNodeListGetString(label_node->doc,
191 label_node->xmlChildrenNode, 1);
193 else
195 label = xmlGetProp(node, "label");
196 if (!label)
197 label = g_strdup(_("<missing label>"));
199 item = gtk_image_menu_item_new_with_label(label);
201 icon_name = xmlGetProp(node, "icon");
202 if (icon_name)
204 GtkWidget *icon = NULL;
205 GtkStockItem stock_item;
206 if (gtk_stock_lookup(icon_name, &stock_item))
207 icon = gtk_image_new_from_stock(icon_name, GTK_ICON_SIZE_MENU);
208 else
210 GdkPixbuf* pixbuf;
211 int size;
212 gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, NULL, &size);
213 pixbuf = gtk_icon_theme_load_icon(icon_theme, icon_name,
214 size, 0, NULL);
215 if (pixbuf)
217 icon = gtk_image_new_from_pixbuf(pixbuf);
218 g_object_unref(pixbuf);
221 g_free(icon_name);
222 if (icon)
223 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item),
224 GTK_WIDGET(icon));
227 gtk_widget_set_accel_path(item, NULL, NULL); /* XXX */
229 g_free(label);
231 if (is_submenu)
233 /* Add submenu items */
235 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item),
236 appmenu_add_submenu(node));
238 else
240 /* Set up callback */
242 if (option)
244 g_object_set_data_full(G_OBJECT(item), "option",
245 g_strdup(option),
246 g_free);
247 g_free(option);
250 g_signal_connect(item, "activate", G_CALLBACK(apprun_menu),
251 NULL);
254 gtk_widget_show(item);
256 return item;
259 /* Send to current_app_path (though not actually an app) */
260 static void send_to(GtkWidget *item, const char *app)
262 GList *file_list;
264 g_return_if_fail(current_app_path != NULL);
266 file_list = g_list_prepend(NULL, current_app_path);
267 run_with_files(app, file_list);
268 g_list_free(file_list);
271 /* Function called to execute an AppMenu item */
272 static void apprun_menu(GtkWidget *item, gpointer data)
274 guchar *option;
275 gchar *argv[3];
277 g_return_if_fail(current_app_path != NULL);
279 option = g_object_get_data(G_OBJECT(item), "option");
281 argv[0] = g_strconcat(current_app_path, "/AppRun", NULL);
282 argv[1] = option; /* (may be NULL) */
283 argv[2] = NULL;
285 rox_spawn(NULL, (const gchar **) argv);
287 g_free(argv[0]);
290 static void show_app_help(GtkWidget *item, gpointer data)
292 g_return_if_fail(current_app_path != NULL);
294 show_help_files(current_app_path);
297 static void mnt_eject(GtkWidget *item, gpointer data)
299 GList *dirs;
301 g_return_if_fail(current_app_path != NULL);
302 dirs = g_list_prepend(NULL, current_app_path);
303 action_eject(dirs);
304 g_list_free(dirs);
307 static void customise_type(GtkWidget *item, MIME_type *type)
309 char *leaf;
310 char *path;
312 leaf = g_strconcat(".", type->media_type, "_", type->subtype, NULL);
313 path = choices_find_xdg_path_save(leaf, "SendTo", SITE, TRUE);
314 g_free(leaf);
316 mkdir(path, 0755);
317 filer_opendir(path, NULL, NULL);
318 g_free(path);
320 info_message(_("Symlink any programs you want into this directory. "
321 "They will appear in the menu for all items of this "
322 "type (%s/%s)."), type->media_type, type->subtype);
325 static void build_menu_for_type(MIME_type *type)
327 GPtrArray *names;
328 char *path;
329 int i;
330 char *leaf;
331 GtkWidget *item;
332 DirItem *ditem;
334 leaf = g_strconcat(".", type->media_type, "_", type->subtype, NULL);
335 path = choices_find_xdg_path_load(leaf, "SendTo", SITE);
337 if (!path)
338 goto out;
340 names = list_dir(path);
342 ditem = diritem_new("");
344 for (i = 0; i < names->len; i++)
346 char *leaf = names->pdata[i];
347 char *full_path;
349 full_path = g_build_filename(path, leaf, NULL);
350 diritem_restat(full_path, ditem, NULL);
352 item = make_send_to_item(ditem, leaf, MIS_SMALL);
353 current_items = g_list_prepend(current_items, item);
354 gtk_widget_show(item);
355 g_signal_connect_data(item, "activate", G_CALLBACK(send_to),
356 full_path, (GClosureNotify) g_free, 0);
359 g_ptr_array_free(names, TRUE);
361 g_free(path);
363 out:
364 item = gtk_menu_item_new_with_label(_("Customise Menu..."));
365 current_items = g_list_prepend(current_items, item);
366 g_signal_connect(item, "activate", G_CALLBACK(customise_type), type);
367 gtk_widget_show(item);
369 g_free(leaf);
372 static inline gboolean is_dir(const char *dir)
374 struct stat info;
375 return stat(dir, &info) == 0 && S_ISDIR(info.st_mode);
378 /* Adds to current_items */
379 static void build_app_menu(const char *app_dir, DirItem *app_item)
381 XMLwrapper *ai = NULL;
382 xmlNode *node;
383 GtkWidget *item;
384 char *help_dir;
386 ai = appinfo_get(app_dir, app_item);
387 if (ai)
389 node = xml_get_section(ai, NULL, "AppMenu");
390 if (node)
391 node = node->xmlChildrenNode;
393 else
395 if (app_item->flags & ITEM_FLAG_APPDIR)
396 node = NULL;
397 else
399 /* Not an application AND no AppInfo */
400 build_menu_for_type(app_item->mime_type);
401 return;
405 /* Add the menu entries */
406 for (; node; node = node->next)
408 item = create_menu_item(node);
410 if (item)
411 current_items = g_list_prepend(current_items, item);
414 help_dir = g_build_filename(app_dir, "Help", NULL);
415 if (is_dir(help_dir))
417 item = gtk_image_menu_item_new_from_stock(GTK_STOCK_HELP, NULL);
418 gtk_widget_show(item);
419 current_items = g_list_prepend(current_items, item);
420 g_signal_connect(item, "activate", G_CALLBACK(show_app_help), NULL);
421 gtk_label_set_text(GTK_LABEL(GTK_BIN(item)->child), _("Help"));
423 g_free(help_dir);
425 if (ai)
426 g_object_unref(ai);