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