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