1 /*
2 * fm-file-menu.c
3 *
4 * Copyright 2009 PCMan <pcman.tw@gmail.com>
5 * Copyright 2018 Andriy Grytsenko (LStranger) <andrej@rep.kiev.ua>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20 * MA 02110-1301, USA.
21 */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include "fm.h"
28 #include "fm-folder-view.h"
29 #include "fm-gtk-utils.h"
30 #include "gtk-compat.h"
31
32 #include "fm-action.h"
33
34 static GQuark _fm_actions_qdata_id = 0;
35 static FmActionCache *_fm_actions_cache = NULL;
36
on_custom_action_file(GtkAction * act,gpointer menu)37 static void on_custom_action_file(GtkAction* act, gpointer menu)
38 {
39 GAppInfo* item = g_object_get_qdata(G_OBJECT(act), fm_qdata_id);
40 GdkAppLaunchContext* ctx = gdk_display_get_app_launch_context(gdk_display_get_default());
41 GList* files = fm_file_info_list_peek_head_link(fm_file_menu_get_file_info_list(menu));
42 GError *err = NULL;
43
44 gdk_app_launch_context_set_screen(ctx, gtk_widget_get_screen(GTK_WIDGET(fm_file_menu_get_menu(menu))));
45 gdk_app_launch_context_set_timestamp(ctx, gtk_get_current_event_time());
46
47 /* g_debug("item: %s is activated, id:%s", fm_file_action_item_get_name(item),
48 fm_file_action_item_get_id(item)); */
49 g_app_info_launch(item, files, G_APP_LAUNCH_CONTEXT(ctx), &err);
50 if (err)
51 {
52 fm_show_error(NULL, "output", err->message);
53 g_error_free(err);
54 }
55 g_object_unref(ctx);
56 }
57
on_custom_action_folder(GtkAction * act,gpointer folder_view)58 static void on_custom_action_folder(GtkAction* act, gpointer folder_view)
59 {
60 GAppInfo* item = g_object_get_qdata(G_OBJECT(act), fm_qdata_id);
61 GdkAppLaunchContext* ctx = gdk_display_get_app_launch_context(gdk_display_get_default());
62 GList* files = g_list_prepend(NULL, fm_folder_view_get_cwd_info(folder_view));
63 GError *err = NULL;
64
65 gdk_app_launch_context_set_screen(ctx, gtk_widget_get_screen(folder_view));
66 gdk_app_launch_context_set_timestamp(ctx, gtk_get_current_event_time());
67
68 /* g_debug("item: %s is activated, id:%s", fm_file_action_item_get_name(item),
69 fm_file_action_item_get_id(item)); */
70 g_app_info_launch(item, files, G_APP_LAUNCH_CONTEXT(ctx), &err);
71 if (err)
72 {
73 fm_show_error(NULL, "output", err->message);
74 g_error_free(err);
75 }
76 g_object_unref(ctx);
77 g_list_free(files);
78 }
79
add_custom_action_item(GString * xml,FmActionMenu * root_menu,GAppInfo * item,GtkActionGroup * act_grp,GCallback cb,gpointer cb_data)80 static void add_custom_action_item(GString* xml, FmActionMenu *root_menu,
81 GAppInfo* item, GtkActionGroup* act_grp,
82 GCallback cb, gpointer cb_data)
83 {
84 GtkAction* act;
85
86 if(!item) /* separator */
87 {
88 g_string_append(xml, "<separator/>");
89 return;
90 }
91
92 act = gtk_action_new(g_app_info_get_id(item),
93 #if GLIB_CHECK_VERSION(2, 24, 0)
94 g_app_info_get_display_name(item),
95 #else
96 g_app_info_get_name(item),
97 #endif
98 g_app_info_get_description(item),
99 NULL);
100
101 if (FM_IS_ACTION(item))
102 g_signal_connect(act, "activate", cb, cb_data);
103
104 gtk_action_set_gicon(act, g_app_info_get_icon(item));
105 gtk_action_group_add_action(act_grp, act);
106 g_object_unref(act);
107 /* hold a reference on the root FmActionMenu object */
108 g_object_set_qdata_full(G_OBJECT(act), _fm_actions_qdata_id,
109 g_object_ref(root_menu), g_object_unref);
110 /* associate the app info object with the action */
111 g_object_set_qdata(G_OBJECT(act), fm_qdata_id, item);
112 if (FM_IS_ACTION_MENU(item))
113 {
114 const GList* subitems = fm_action_menu_get_children(FM_ACTION_MENU(item));
115 const GList* l;
116 g_string_append_printf(xml, "<menu action='%s'>",
117 g_app_info_get_id(item));
118 for(l=subitems; l; l=l->next)
119 {
120 GAppInfo *subitem = l->data;
121 add_custom_action_item(xml, root_menu, subitem, act_grp, cb, cb_data);
122 }
123 g_string_append(xml, "</menu>");
124 }
125 else
126 {
127 g_string_append_printf(xml, "<menuitem action='%s'/>",
128 g_app_info_get_id(item));
129 }
130 }
131
_fm_actions_init(void)132 static void _fm_actions_init(void)
133 {
134 if (!_fm_actions_cache)
135 _fm_actions_cache = fm_action_cache_new();
136 if (!_fm_actions_qdata_id)
137 _fm_actions_qdata_id = g_quark_from_string("_fm_actions_qdata_id");
138 }
139
_fm_actions_finalize(void)140 static void _fm_actions_finalize(void)
141 {
142 if (_fm_actions_cache)
143 g_object_unref(G_OBJECT(_fm_actions_cache));
144 _fm_actions_cache = NULL;
145 }
146
147 static void
_fm_actions_update_file_menu_for_scheme(GtkWindow * window,GtkUIManager * ui,GString * xml,GtkActionGroup * act_grp,FmFileMenu * menu,FmFileInfoList * files,gboolean single_file)148 _fm_actions_update_file_menu_for_scheme(GtkWindow* window, GtkUIManager* ui,
149 GString* xml, GtkActionGroup* act_grp,
150 FmFileMenu* menu, FmFileInfoList* files,
151 gboolean single_file)
152 {
153 FmActionMenu *root_menu;
154 FmPath *cwd = fm_file_menu_get_cwd(menu);
155 FmFolder *folder;
156 FmFileInfo *location = NULL;
157 const GList *items;
158
159 g_return_if_fail(_fm_actions_cache != NULL && cwd != NULL);
160 folder = fm_folder_find_by_path(cwd);
161 if (folder)
162 location = fm_folder_get_info(folder);
163 if (!location)
164 return;
165
166 /* add custom file actions */
167 root_menu = fm_action_get_for_context(_fm_actions_cache, location, files);
168 items = fm_action_menu_get_children(root_menu);
169 if(items)
170 {
171 g_string_append(xml, "<popup><placeholder name='ph3'>");
172 const GList* l;
173 for(l=items; l; l=l->next)
174 {
175 GAppInfo *item = l->data;
176 add_custom_action_item(xml, root_menu, item, act_grp,
177 G_CALLBACK(on_custom_action_file), menu);
178 }
179 g_string_append(xml, "</placeholder></popup>");
180 }
181 g_object_unref(root_menu);
182 }
183
184 static void
_fm_actions_update_folder_menu_for_scheme(FmFolderView * fv,GtkWindow * window,GtkUIManager * ui,GtkActionGroup * act_grp,FmFileInfoList * files)185 _fm_actions_update_folder_menu_for_scheme(FmFolderView* fv, GtkWindow* window,
186 GtkUIManager* ui, GtkActionGroup* act_grp,
187 FmFileInfoList* files)
188 {
189 FmFileInfo *fi = fm_folder_view_get_cwd_info(fv);
190 FmActionMenu *root_menu;
191 const GList *items;
192
193 g_return_if_fail(_fm_actions_cache != NULL);
194
195 if (fi == NULL) /* incremental folder - no info yet - ignore it */
196 return;
197
198 root_menu = fm_action_get_for_location(_fm_actions_cache, fi);
199 items = fm_action_menu_get_children(root_menu);
200 if(items)
201 {
202 GString *xml = g_string_new("<popup><placeholder name='CustomCommonOps'>");
203 const GList* l;
204
205 for(l=items; l; l=l->next)
206 {
207 GAppInfo *item = l->data;
208 add_custom_action_item(xml, root_menu, item, act_grp,
209 G_CALLBACK(on_custom_action_folder), fv);
210 }
211 g_string_append(xml, "</placeholder></popup>");
212 gtk_ui_manager_add_ui_from_string(ui, xml->str, xml->len, NULL);
213 g_string_free(xml, TRUE);
214 }
215 g_object_unref(root_menu);
216 }
217
218 /* we catch all schemes to be available on every one */
219 FM_DEFINE_MODULE(gtk_menu_scheme, *)
220
221 FmContextMenuSchemeAddonInit fm_module_init_gtk_menu_scheme = {
222 .init = _fm_actions_init,
223 .finalize = _fm_actions_finalize,
224 .update_file_menu_for_scheme = _fm_actions_update_file_menu_for_scheme,
225 .update_folder_menu = _fm_actions_update_folder_menu_for_scheme
226 };
227