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)
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
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! */
32 #include <libxml/parser.h>
38 #include "gui_support.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)
77 for (next
= current_items
; next
; next
= next
->next
)
78 gtk_widget_destroy((GtkWidget
*) next
->data
);
80 null_g_free(¤t_app_path
);
83 g_list_free(current_items
);
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
)
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
)
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
);
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
));
132 current_app_path
= g_strdup(app_dir
);
138 /****************************************************************
139 * INTERNAL FUNCTIONS *
140 ****************************************************************/
142 /* Create a new menu and return it */
143 static GtkWidget
*appmenu_add_submenu(xmlNode
*subm_node
)
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
)
156 item
= create_menu_item(node
);
158 gtk_menu_shell_append(GTK_MENU_SHELL(sub_menu
), item
);
164 /* Create and return a menu item */
165 static GtkWidget
*create_menu_item(xmlNode
*node
)
169 guchar
*label
, *option
= NULL
;
170 guchar
*icon_name
= NULL
;
173 if (node
->type
!= XML_ELEMENT_NODE
)
176 if (strcmp(node
->name
, "Item") == 0)
179 option
= xmlGetProp(node
, "option");
181 else if (strcmp(node
->name
, "AppMenu") == 0)
186 /* Create the item */
187 label_node
= get_subnode(node
, NULL
, "Label");
190 label
= xmlNodeListGetString(label_node
->doc
,
191 label_node
->xmlChildrenNode
, 1);
195 label
= xmlGetProp(node
, "label");
197 label
= g_strdup(_("<missing label>"));
199 item
= gtk_image_menu_item_new_with_label(label
);
201 icon_name
= xmlGetProp(node
, "icon");
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
);
212 gtk_icon_size_lookup(GTK_ICON_SIZE_MENU
, NULL
, &size
);
213 pixbuf
= theme_load_icon(icon_name
, size
, 0, NULL
);
216 icon
= gtk_image_new_from_pixbuf(pixbuf
);
217 g_object_unref(pixbuf
);
222 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item
),
226 gtk_widget_set_accel_path(item
, NULL
, NULL
); /* XXX */
232 /* Add submenu items */
234 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item
),
235 appmenu_add_submenu(node
));
239 /* Set up callback */
243 g_object_set_data_full(G_OBJECT(item
), "option",
249 g_signal_connect(item
, "activate", G_CALLBACK(apprun_menu
),
253 gtk_widget_show(item
);
258 /* Send to current_app_path (though not actually an app) */
259 static void send_to(GtkWidget
*item
, const char *app
)
263 g_return_if_fail(current_app_path
!= NULL
);
265 file_list
= g_list_prepend(NULL
, current_app_path
);
266 run_with_files(app
, file_list
);
267 g_list_free(file_list
);
270 /* Function called to execute an AppMenu item */
271 static void apprun_menu(GtkWidget
*item
, gpointer data
)
276 g_return_if_fail(current_app_path
!= NULL
);
278 option
= g_object_get_data(G_OBJECT(item
), "option");
280 argv
[0] = g_strconcat(current_app_path
, "/AppRun", NULL
);
281 argv
[1] = option
; /* (may be NULL) */
284 rox_spawn(NULL
, (const gchar
**) argv
);
289 static void show_app_help(GtkWidget
*item
, gpointer data
)
291 g_return_if_fail(current_app_path
!= NULL
);
293 show_help_files(current_app_path
);
296 static void mnt_eject(GtkWidget
*item
, gpointer data
)
300 g_return_if_fail(current_app_path
!= NULL
);
301 dirs
= g_list_prepend(NULL
, current_app_path
);
306 static void customise_type(GtkWidget
*item
, MIME_type
*type
)
311 leaf
= g_strconcat(".", type
->media_type
, "_", type
->subtype
, NULL
);
312 path
= choices_find_xdg_path_save(leaf
, "SendTo", SITE
, TRUE
);
316 filer_opendir(path
, NULL
, NULL
);
319 info_message(_("Symlink any programs you want into this directory. "
320 "They will appear in the menu for all items of this "
321 "type (%s/%s)."), type
->media_type
, type
->subtype
);
324 static void build_menu_for_type(MIME_type
*type
)
333 leaf
= g_strconcat(".", type
->media_type
, "_", type
->subtype
, NULL
);
334 path
= choices_find_xdg_path_load(leaf
, "SendTo", SITE
);
339 names
= list_dir(path
);
341 ditem
= diritem_new("");
343 for (i
= 0; i
< names
->len
; i
++)
345 char *leaf
= names
->pdata
[i
];
348 full_path
= g_build_filename(path
, leaf
, NULL
);
349 diritem_restat(full_path
, ditem
, NULL
);
351 item
= make_send_to_item(ditem
, leaf
, MIS_SMALL
);
352 current_items
= g_list_prepend(current_items
, item
);
353 gtk_widget_show(item
);
354 g_signal_connect_data(item
, "activate", G_CALLBACK(send_to
),
355 full_path
, (GClosureNotify
) g_free
, 0);
358 g_ptr_array_free(names
, TRUE
);
363 item
= gtk_menu_item_new_with_label(_("Customise Menu..."));
364 current_items
= g_list_prepend(current_items
, item
);
365 g_signal_connect(item
, "activate", G_CALLBACK(customise_type
), type
);
366 gtk_widget_show(item
);
371 static inline gboolean
is_dir(const char *dir
)
374 return stat(dir
, &info
) == 0 && S_ISDIR(info
.st_mode
);
377 /* Adds to current_items */
378 static void build_app_menu(const char *app_dir
, DirItem
*app_item
)
380 XMLwrapper
*ai
= NULL
;
385 ai
= appinfo_get(app_dir
, app_item
);
388 node
= xml_get_section(ai
, NULL
, "AppMenu");
390 node
= node
->xmlChildrenNode
;
394 if (app_item
->flags
& ITEM_FLAG_APPDIR
)
398 /* Not an application AND no AppInfo */
399 build_menu_for_type(app_item
->mime_type
);
404 /* Add the menu entries */
405 for (; node
; node
= node
->next
)
407 item
= create_menu_item(node
);
410 current_items
= g_list_prepend(current_items
, item
);
413 help_dir
= g_build_filename(app_dir
, "Help", NULL
);
414 if (is_dir(help_dir
))
416 item
= gtk_image_menu_item_new_from_stock(GTK_STOCK_HELP
, NULL
);
417 gtk_widget_show(item
);
418 current_items
= g_list_prepend(current_items
, item
);
419 g_signal_connect(item
, "activate", G_CALLBACK(show_app_help
), NULL
);
420 gtk_label_set_text(GTK_LABEL(GTK_BIN(item
)->child
), _("Help"));