1 /* vi:set et ai sw=2 sts=2 ts=2: */
2 /*-
3 * Copyright (c) 2006 Benedikt Meurer <benny@xfce.org>
4 * Copyright (c) 2009 Jannis Pohlmann <jannis@xfce.org>
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the Free
8 * Software Foundation; either version 2 of the License, or (at your option)
9 * any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * more details.
15 *
16 * You should have received a copy of the GNU General Public License along with
17 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
18 * Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24
25 #ifdef HAVE_MEMORY_H
26 #include <memory.h>
27 #endif
28 #ifdef HAVE_STRING_H
29 #include <string.h>
30 #endif
31
32 #ifdef HAVE_GIO_UNIX
33 #include <gio/gdesktopappinfo.h>
34 #endif
35
36 #include <thunar/thunar-private.h>
37 #include <thunar/thunar-sendto-model.h>
38
39
40
41 static void thunar_sendto_model_finalize (GObject *object);
42 static void thunar_sendto_model_load (ThunarSendtoModel *sendto_model);
43 static void thunar_sendto_model_event (GFileMonitor *monitor,
44 GFile *file,
45 GFile *other_file,
46 GFileMonitorEvent event_type,
47 gpointer user_data);
48
49
50
51 struct _ThunarSendtoModelClass
52 {
53 GObjectClass __parent__;
54 };
55
56 struct _ThunarSendtoModel
57 {
58 GObject __parent__;
59 GList *monitors;
60 GList *handlers;
61 guint loaded : 1;
62 };
63
64
65
G_DEFINE_TYPE(ThunarSendtoModel,thunar_sendto_model,G_TYPE_OBJECT)66 G_DEFINE_TYPE (ThunarSendtoModel, thunar_sendto_model, G_TYPE_OBJECT)
67
68
69
70 static void
71 thunar_sendto_model_class_init (ThunarSendtoModelClass *klass)
72 {
73 GObjectClass *gobject_class;
74
75 gobject_class = G_OBJECT_CLASS (klass);
76 gobject_class->finalize = thunar_sendto_model_finalize;
77 }
78
79
80
81 static void
thunar_sendto_model_init(ThunarSendtoModel * sendto_model)82 thunar_sendto_model_init (ThunarSendtoModel *sendto_model)
83 {
84 sendto_model->monitors = NULL;
85 }
86
87
88
89 static void
thunar_sendto_model_finalize(GObject * object)90 thunar_sendto_model_finalize (GObject *object)
91 {
92 ThunarSendtoModel *sendto_model = THUNAR_SENDTO_MODEL (object);
93 GList *lp;
94
95 /* release the handlers */
96 g_list_free_full (sendto_model->handlers, g_object_unref);
97
98 /* disconnect all monitors */
99 for (lp = sendto_model->monitors; lp != NULL; lp = lp->next)
100 {
101 g_file_monitor_cancel (lp->data);
102 g_object_unref (lp->data);
103 }
104 g_list_free (sendto_model->monitors);
105
106 (*G_OBJECT_CLASS (thunar_sendto_model_parent_class)->finalize) (object);
107 }
108
109
110
111 static gint
g_app_info_compare(gpointer a,gpointer b)112 g_app_info_compare (gpointer a,
113 gpointer b)
114 {
115 return g_utf8_collate (g_app_info_get_name (b),
116 g_app_info_get_name (a));
117 }
118
119
120
121 static void
thunar_sendto_model_load(ThunarSendtoModel * sendto_model)122 thunar_sendto_model_load (ThunarSendtoModel *sendto_model)
123 {
124 #ifdef HAVE_GIO_UNIX
125 GDesktopAppInfo *app_info = NULL;
126 #endif
127 gchar **specs;
128 gchar *path;
129 guint n;
130 GKeyFile *key_file;
131 gchar **mime_types;
132
133 /* lookup all sendto .desktop files */
134 specs = xfce_resource_match (XFCE_RESOURCE_DATA, "Thunar/sendto/*.desktop", TRUE);
135 for (n = 0; specs[n] != NULL; ++n)
136 {
137 /* lookup the absolute path to the .desktop file */
138 path = xfce_resource_lookup (XFCE_RESOURCE_DATA, specs[n]);
139 if (G_LIKELY (path != NULL))
140 {
141 /* try to load the .desktop file */
142 key_file = g_key_file_new ();
143 if (!g_key_file_load_from_file (key_file, path, G_KEY_FILE_NONE, NULL))
144 {
145 g_key_file_free (key_file);
146 continue;
147 }
148
149 #ifdef HAVE_GIO_UNIX
150 app_info = g_desktop_app_info_new_from_keyfile (key_file);
151
152 if (G_LIKELY (app_info != NULL))
153 {
154 /* add to our handler list, sorted by their desktop-ids (reverse order) */
155 sendto_model->handlers = g_list_insert_sorted (sendto_model->handlers,
156 G_APP_INFO (app_info),
157 (GCompareFunc) (void (*)(void)) g_app_info_compare);
158
159 /* attach the mime-types to the object */
160 mime_types = g_key_file_get_string_list (key_file,
161 G_KEY_FILE_DESKTOP_GROUP,
162 G_KEY_FILE_DESKTOP_KEY_MIME_TYPE,
163 NULL, NULL);
164 if (mime_types != NULL)
165 g_object_set_data_full (G_OBJECT (app_info), "mime-types", mime_types, (GDestroyNotify) g_strfreev);
166 }
167 #else
168 /* FIXME try to create the app info ourselves in a platform independent way */
169 #endif
170
171 g_key_file_free (key_file);
172 }
173
174 /* cleanup */
175 g_free (specs[n]);
176 g_free (path);
177 }
178 g_free (specs);
179 }
180
181
182
183 static void
thunar_sendto_model_event(GFileMonitor * monitor,GFile * file,GFile * other_file,GFileMonitorEvent event_type,gpointer user_data)184 thunar_sendto_model_event (GFileMonitor *monitor,
185 GFile *file,
186 GFile *other_file,
187 GFileMonitorEvent event_type,
188 gpointer user_data)
189 {
190 ThunarSendtoModel *sendto_model = THUNAR_SENDTO_MODEL (user_data);
191
192 /* release the previously loaded handlers */
193 if (G_LIKELY (sendto_model->handlers != NULL))
194 {
195 g_list_free_full (sendto_model->handlers, g_object_unref);
196 sendto_model->handlers = NULL;
197 }
198
199 /* reload the handlers for the model */
200 thunar_sendto_model_load (sendto_model);
201 }
202
203
204
205 /**
206 * thunar_sendto_model_get_default:
207 *
208 * Returns a reference to the default #ThunarSendtoModel
209 * instance. The caller is responsible to free the returned
210 * reference using g_object_unref() when no longer needed.
211 *
212 * Return value: reference to the default sendto model.
213 **/
214 ThunarSendtoModel*
thunar_sendto_model_get_default(void)215 thunar_sendto_model_get_default (void)
216 {
217 static ThunarSendtoModel *sendto_model = NULL;
218
219 if (G_UNLIKELY (sendto_model == NULL))
220 {
221 sendto_model = g_object_new (THUNAR_TYPE_SENDTO_MODEL, NULL);
222 g_object_add_weak_pointer (G_OBJECT (sendto_model), (gpointer) &sendto_model);
223 }
224 else
225 {
226 g_object_ref (G_OBJECT (sendto_model));
227 }
228
229 return sendto_model;
230 }
231
232
233
234 /**
235 * thunar_sendto_model_get_matching:
236 * @sendto_model : a #ThunarSendtoModel.
237 * @files : a #GList of #ThunarFile<!---->s.
238 *
239 * Returns the list of #GAppInfo<!---->s for the "Send To" targets that
240 * support the specified @files.
241 *
242 * The returned list is owned by the caller and must be freed when no
243 * longer needed, using:
244 * <informalexample><programlisting>
245 * g_list_free_full (list, g_object_unref);
246 * </programlisting></informalexample>
247 *
248 * Return value: a #GList of supported #GAppInfo<!---->s as
249 * "Send To" targets for the specified @files.
250 **/
251 GList*
thunar_sendto_model_get_matching(ThunarSendtoModel * sendto_model,GList * files)252 thunar_sendto_model_get_matching (ThunarSendtoModel *sendto_model,
253 GList *files)
254 {
255 GFileMonitor *monitor;
256 GFile *file;
257 gchar **datadirs;
258 gchar *dir;
259 GList *handlers = NULL;
260 GList *hp;
261 GList *fp;
262 guint n;
263 const gchar **mime_types;
264 const gchar *content_type;
265
266 _thunar_return_val_if_fail (THUNAR_IS_SENDTO_MODEL (sendto_model), NULL);
267
268 /* no files, no sendto actions */
269 if (G_UNLIKELY (files == NULL))
270 return NULL;
271
272 /* connect to the monitor on-demand */
273 if (G_UNLIKELY (sendto_model->monitors == NULL))
274 {
275 /* watch all possible sendto directories */
276 datadirs = xfce_resource_dirs (XFCE_RESOURCE_DATA);
277 for (n = 0; datadirs[n] != NULL; ++n)
278 {
279 /* determine the path to the sendto directory */
280 dir = g_build_filename (datadirs[n], "Thunar", "sendto", NULL);
281 file = g_file_new_for_path (dir);
282
283 /* watch the directory for changes */
284 monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE, NULL, NULL);
285 if (G_LIKELY (monitor != NULL))
286 {
287 g_signal_connect (monitor, "changed", G_CALLBACK (thunar_sendto_model_event), sendto_model);
288 sendto_model->monitors = g_list_prepend (sendto_model->monitors, monitor);
289 }
290
291 g_object_unref (file);
292 g_free (dir);
293 }
294 g_strfreev (datadirs);
295
296 /* load the model */
297 thunar_sendto_model_load (sendto_model);
298 }
299
300 /* test all handlers */
301 for (hp = sendto_model->handlers; hp != NULL; hp = hp->next)
302 {
303 /* FIXME Ignore GAppInfos which don't support multiple file arguments */
304
305 /* ignore the handler if it doesn't support URIs, but we don't have a local file */
306 if (!g_app_info_supports_uris (hp->data))
307 {
308 /* check if we have any non-local files */
309 for (fp = files; fp != NULL; fp = fp->next)
310 if (!thunar_file_is_local (fp->data))
311 break;
312
313 /* check if the test failed */
314 if (G_UNLIKELY (fp != NULL))
315 continue;
316 }
317
318 /* check if we need to test mime types for this handler */
319 mime_types = g_object_get_data (G_OBJECT (hp->data), "mime-types");
320 if (mime_types != NULL)
321 {
322 /* each file must match atleast one of the specified mime types */
323 for (fp = files; fp != NULL; fp = fp->next)
324 {
325 /* each file must be supported by one of the mime types */
326 for (n = 0; mime_types[n] != NULL; ++n)
327 {
328 content_type = thunar_file_get_content_type (fp->data);
329 if (g_content_type_equals (content_type, mime_types[n]))
330 break;
331 }
332
333 /* check if all mime types failed */
334 if (mime_types[n] == NULL)
335 break;
336 }
337
338 /* check if the test failed */
339 if (G_UNLIKELY (fp != NULL))
340 continue;
341 }
342
343 /* the handler is supported */
344 handlers = g_list_prepend (handlers, g_object_ref (G_OBJECT (hp->data)));
345 }
346
347 return handlers;
348 }
349
350
351