1 /*
2 * This file is part of brisk-menu.
3 *
4 * Copyright © 2017-2020 Brisk Menu Developers
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 */
11
12 #define _GNU_SOURCE
13
14 #include "util.h"
15
16 BRISK_BEGIN_PEDANTIC
17 #include "favourites-backend.h"
18 #include <glib/gi18n.h>
19 #include <glib/gstdio.h>
20 BRISK_END_PEDANTIC
21
22 #include <errno.h>
23 #include <string.h>
24
25 DEF_AUTOFREE(GFile, g_object_unref)
26 DEF_AUTOFREE(gchar, g_free)
27 DEF_AUTOFREE(GError, g_error_free)
28
29 typedef enum {
30 PIN_STATUS_UNPINNABLE = 0,
31 PIN_STATUS_PINNED = 1,
32 PIN_STATUS_UNPINNED = 2,
33 } DesktopPinStatus;
34
35 /**
36 * get_desktop_item_source:
37 *
38 * Get a GFile for the BriskItem's source
39 */
get_desktop_item_source(BriskItem * item)40 static GFile *get_desktop_item_source(BriskItem *item)
41 {
42 autofree(gchar) *uri = NULL;
43
44 if (!item) {
45 return NULL;
46 }
47
48 uri = brisk_item_get_uri(item);
49 if (!uri) {
50 return NULL;
51 }
52 return g_file_new_for_uri(uri);
53 }
54
55 /**
56 * get_item_path will return the path that would be pinned on the desktop
57 */
get_desktop_item_target_path(GFile * file)58 static gchar *get_desktop_item_target_path(GFile *file)
59 {
60 if (!g_file_query_exists(file, NULL)) {
61 return NULL;
62 }
63 autofree(gchar) *basename = g_file_get_basename(file);
64
65 return g_build_path(G_DIR_SEPARATOR_S,
66 g_get_user_special_dir(G_USER_DIRECTORY_DESKTOP),
67 basename,
68 NULL);
69 }
70
71 /**
72 * get_desktop_item_target:
73 *
74 * Get the target GFile on the desktop
75 */
get_desktop_item_target(GFile * file)76 static GFile *get_desktop_item_target(GFile *file)
77 {
78 autofree(gchar) *path = NULL;
79
80 path = get_desktop_item_target_path(file);
81 return g_file_new_for_path(path);
82 }
83
84 /**
85 * brisk_favourites_backend_action_desktop_pin will pin the item to the desktop
86 * by copying the source file to the target
87 */
brisk_favourites_backend_action_desktop_pin(__brisk_unused__ GSimpleAction * action,__brisk_unused__ GVariant * parameter,BriskFavouritesBackend * self)88 static void brisk_favourites_backend_action_desktop_pin(__brisk_unused__ GSimpleAction *action,
89 __brisk_unused__ GVariant *parameter,
90 BriskFavouritesBackend *self)
91 {
92 autofree(GFile) *source = NULL;
93 autofree(GFile) *dest = NULL;
94 autofree(GError) *error = NULL;
95 autofree(gchar) *path = NULL;
96
97 source = get_desktop_item_source(self->active_item);
98 if (!source) {
99 return;
100 }
101 dest = get_desktop_item_target(source);
102 if (!dest) {
103 return;
104 }
105
106 path = g_file_get_path(dest);
107 if (!path) {
108 return;
109 }
110
111 /* Consider making async. */
112 if (!g_file_copy(source,
113 dest,
114 G_FILE_COPY_ALL_METADATA | G_FILE_COPY_OVERWRITE,
115 NULL,
116 NULL,
117 NULL,
118 &error)) {
119 /* Consider using libnotify */
120 g_message("Failed to pin desktop item: %s", error->message);
121 }
122
123 /* MATE will sanitize .desktop files that are chmod +x */
124 int r = g_chmod(path, 00755);
125 if (r != 0) {
126 g_message("Failed to chmod desktop item: %s", strerror(errno));
127 }
128 }
129
130 /**
131 * brisk_favourites_backend_action_desktop_unpin will attempt to unpin the item
132 * from the desktop by removing the .desktop file
133 */
brisk_favourites_backend_action_desktop_unpin(__brisk_unused__ GSimpleAction * action,__brisk_unused__ GVariant * parameter,BriskFavouritesBackend * self)134 static void brisk_favourites_backend_action_desktop_unpin(__brisk_unused__ GSimpleAction *action,
135 __brisk_unused__ GVariant *parameter,
136 BriskFavouritesBackend *self)
137 {
138 autofree(GFile) *source = NULL;
139 autofree(GFile) *dest = NULL;
140 autofree(GError) *error = NULL;
141
142 source = get_desktop_item_source(self->active_item);
143 if (!source) {
144 return;
145 }
146
147 dest = get_desktop_item_target(source);
148 if (!dest || !g_file_query_exists(dest, NULL)) {
149 return;
150 }
151
152 if (!g_file_delete(dest, NULL, &error)) {
153 /* Consider using libnotify */
154 g_message("Unable to unpin desktop item: %s", error->message);
155 }
156 }
157
158 /**
159 * brisk_favourites_backend_is_desktop_pinned:
160 *
161 * Determine if the source file is actually pinned to the desktop or not
162 */
brisk_favourites_backend_get_desktop_pin_status(BriskItem * item)163 static DesktopPinStatus brisk_favourites_backend_get_desktop_pin_status(BriskItem *item)
164 {
165 autofree(GFile) *source = NULL;
166 autofree(GFile) *dest = NULL;
167 autofree(gchar) *base = NULL;
168
169 source = get_desktop_item_source(item);
170 if (!source) {
171 return PIN_STATUS_UNPINNABLE;
172 }
173
174 /* .desktop .. */
175 base = g_file_get_basename(source);
176 if (!g_str_has_suffix(base, ".desktop")) {
177 return PIN_STATUS_UNPINNABLE;
178 }
179
180 dest = get_desktop_item_target(source);
181 if (!dest || !g_file_query_exists(dest, NULL)) {
182 return PIN_STATUS_UNPINNED;
183 }
184
185 return PIN_STATUS_PINNED;
186 }
187
188 /**
189 * brisk_favourites_backend_init_desktop:
190 *
191 * Initialise actions for the favourite backend's .desktop functionality
192 */
brisk_favourites_backend_init_desktop(BriskFavouritesBackend * self)193 void brisk_favourites_backend_init_desktop(BriskFavouritesBackend *self)
194 {
195 self->action_add_desktop = g_simple_action_new("favourites.pin-desktop", NULL);
196 g_signal_connect(self->action_add_desktop,
197 "activate",
198 G_CALLBACK(brisk_favourites_backend_action_desktop_pin),
199 self);
200 self->action_remove_desktop = g_simple_action_new("favourites.unpin-desktop", NULL);
201 g_signal_connect(self->action_remove_desktop,
202 "activate",
203 G_CALLBACK(brisk_favourites_backend_action_desktop_unpin),
204 self);
205 }
206
207 /**
208 * brisk_favourites_backend_menu_desktop:
209 *
210 * Add relevant entries to the context menu pertaining to .desktop handling
211 */
brisk_favourites_backend_menu_desktop(BriskFavouritesBackend * self,GMenu * menu,GActionGroup * group)212 void brisk_favourites_backend_menu_desktop(BriskFavouritesBackend *self, GMenu *menu,
213 GActionGroup *group)
214 {
215 g_action_map_add_action(G_ACTION_MAP(group), G_ACTION(self->action_add_desktop));
216 g_action_map_add_action(G_ACTION_MAP(group), G_ACTION(self->action_remove_desktop));
217 DesktopPinStatus t = brisk_favourites_backend_get_desktop_pin_status(self->active_item);
218
219 switch (t) {
220 case PIN_STATUS_PINNED:
221 g_menu_append(menu,
222 _("Unpin from desktop"),
223 "brisk-context-items.favourites.unpin-desktop");
224 break;
225 case PIN_STATUS_UNPINNED:
226 g_menu_append(menu,
227 _("Pin to desktop"),
228 "brisk-context-items.favourites.pin-desktop");
229 break;
230 case PIN_STATUS_UNPINNABLE:
231 default:
232 return;
233 }
234 }
235
236 /*
237 * Editor modelines - https://www.wireshark.org/tools/modelines.html
238 *
239 * Local variables:
240 * c-basic-offset: 8
241 * tab-width: 8
242 * indent-tabs-mode: nil
243 * End:
244 *
245 * vi: set shiftwidth=8 tabstop=8 expandtab:
246 * :indentSize=8:tabSize=8:noTabs=true:
247 */
248