1 /* xfce4-places-plugin
2 *
3 * Model: volumes bookmarks (i.e., removable media)
4 *
5 * Copyright (c) 2007 Diego Ongaro <ongardie@gmail.com>
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 Library 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 */
21
22 #ifdef HAVE_CONFIG_H
23 # include <config.h>
24 #endif
25
26 #include "model.h"
27 #include "model_volumes.h"
28 #include "support.h"
29
30 #include <gio/gio.h>
31 #ifdef HAVE_GIO_UNIX
32 #include <gio/gunixmounts.h>
33 #endif
34 #include <gtk/gtk.h>
35
36 #ifdef HAVE_LIBNOTIFY
37 #include "model_volumes_notify.h"
38 #endif
39
40 #include <libxfce4util/libxfce4util.h>
41
42 #include <string.h>
43
44 #define pbg_priv(pbg) ((PBVolData*) pbg->priv)
45
46 typedef struct
47 {
48 GVolumeMonitor *volume_monitor;
49 gboolean changed;
50 gboolean mount_and_open_by_default;
51 } PBVolData;
52
53
54 /********** Actions Callbacks **********/
55 static void
pbvol_eject_finish(GObject * object,GAsyncResult * result,gpointer user_data)56 pbvol_eject_finish(GObject *object,
57 GAsyncResult *result,
58 gpointer user_data)
59 {
60 GVolume *volume = G_VOLUME(object);
61 GError *error = NULL;
62
63 g_return_if_fail(G_IS_VOLUME(object));
64 g_return_if_fail(G_IS_ASYNC_RESULT(result));
65
66 if (!g_volume_eject_with_operation_finish(volume, result, &error)) {
67 /* ignore GIO errors handled internally */
68 if(error->domain != G_IO_ERROR || error->code != G_IO_ERROR_FAILED_HANDLED) {
69 gchar *volume_name = g_volume_get_name(volume);
70 places_show_error_dialog(error,
71 _("Failed to eject \"%s\""),
72 volume_name);
73 g_free(volume_name);
74 }
75 g_error_free(error);
76 }
77
78 #ifdef HAVE_LIBNOTIFY
79 pbvol_notify_eject_finish(volume);
80 #endif
81 }
82
83 static void
pbvol_eject(PlacesBookmarkAction * action)84 pbvol_eject(PlacesBookmarkAction *action)
85 {
86 GVolume *volume;
87
88 DBG("Eject");
89
90 g_return_if_fail(G_IS_VOLUME(action->priv));
91 volume = G_VOLUME(action->priv);
92
93 if (g_volume_can_eject(volume)) {
94 #ifdef HAVE_LIBNOTIFY
95 pbvol_notify_eject(volume);
96 #endif
97 g_volume_eject_with_operation(volume, G_MOUNT_UNMOUNT_NONE, NULL,
98 NULL,
99 pbvol_eject_finish,
100 g_object_ref(volume));
101 }
102 }
103
104 static void
pbvol_unmount_finish(GObject * object,GAsyncResult * result,gpointer user_data)105 pbvol_unmount_finish(GObject *object,
106 GAsyncResult *result,
107 gpointer user_data)
108 {
109 GMount *mount = G_MOUNT(object);
110 GError *error = NULL;
111
112 g_return_if_fail(G_IS_MOUNT(object));
113 g_return_if_fail(G_IS_ASYNC_RESULT(result));
114
115 if (!g_mount_unmount_with_operation_finish(mount, result, &error)) {
116 /* ignore GIO errors handled internally */
117 if (error->domain != G_IO_ERROR || error->code != G_IO_ERROR_FAILED_HANDLED) {
118 gchar *mount_name = g_mount_get_name(mount);
119 places_show_error_dialog(error,
120 _("Failed to unmount \"%s\""),
121 mount_name);
122 g_free(mount_name);
123 }
124 g_error_free (error);
125 }
126
127 #ifdef HAVE_LIBNOTIFY
128 pbvol_notify_unmount_finish(mount);
129 #endif
130 }
131
132 static void
pbvol_unmount(PlacesBookmarkAction * action)133 pbvol_unmount(PlacesBookmarkAction *action)
134 {
135 GVolume *volume;
136 GMount *mount;
137
138 DBG("Unmount");
139
140 g_return_if_fail(G_IS_VOLUME(action->priv));
141 volume = G_VOLUME(action->priv);
142 mount = g_volume_get_mount(volume);
143
144 if (mount) {
145 #ifdef HAVE_LIBNOTIFY
146 pbvol_notify_unmount(mount);
147 #endif
148 g_mount_unmount_with_operation(mount, G_MOUNT_UNMOUNT_NONE, NULL,
149 NULL,
150 pbvol_unmount_finish,
151 g_object_ref(volume));
152 }
153 }
154
155 static void
pbvol_mount_finish(GObject * object,GAsyncResult * result,gpointer user_data)156 pbvol_mount_finish(GObject *object,
157 GAsyncResult *result,
158 gpointer user_data)
159 {
160 GVolume *volume = G_VOLUME(object);
161 GError *error = NULL;
162
163 DBG("Mount finish");
164
165 if (!g_volume_mount_finish(volume, result, &error)) {
166 /* ignore GIO errors handled internally */
167 if (error->domain != G_IO_ERROR || error->code != G_IO_ERROR_FAILED_HANDLED) {
168 gchar *volume_name = g_volume_get_name(volume);
169 places_show_error_dialog(error,
170 _("Failed to mount \"%s\""),
171 volume_name);
172 g_free(volume_name);
173 }
174 g_error_free (error);
175 }
176 }
177
178 static void
pbvol_mount_finish_and_open(GObject * object,GAsyncResult * result,gpointer user_data)179 pbvol_mount_finish_and_open(GObject *object,
180 GAsyncResult *result,
181 gpointer user_data)
182 {
183 GVolume *volume = G_VOLUME(object);
184 GError *error = NULL;
185
186 DBG("Mount finish and open");
187
188 if (!g_volume_mount_finish(volume, result, &error)) {
189 /* ignore GIO errors handled internally */
190 if (error->domain != G_IO_ERROR || error->code != G_IO_ERROR_FAILED_HANDLED) {
191 gchar *volume_name = g_volume_get_name(volume);
192 places_show_error_dialog(error,
193 _("Failed to mount \"%s\""),
194 volume_name);
195 g_free(volume_name);
196 }
197 g_error_free (error);
198 } else {
199 GMount *mount;
200 gchar *uri;
201 mount = g_volume_get_mount(volume);
202
203 if (mount) {
204 GFile *file = g_mount_get_root(mount);
205 uri = g_file_get_uri(file);
206 places_load_file_browser(uri);
207 g_free(uri);
208 g_object_unref(file);
209 g_object_unref(mount);
210 }
211 }
212 }
213
214 static void
pbvol_mount(PlacesBookmarkAction * action)215 pbvol_mount(PlacesBookmarkAction *action)
216 {
217 GVolume *volume;
218 GMount *mount;
219
220 DBG("Mount");
221
222 g_return_if_fail(G_IS_VOLUME(action->priv));
223 volume = G_VOLUME(action->priv);
224 mount = g_volume_get_mount(volume);
225
226 if (!mount) {
227 GMountOperation *operation = gtk_mount_operation_new(NULL);
228
229 g_volume_mount(volume, G_MOUNT_MOUNT_NONE, operation, NULL,
230 pbvol_mount_finish,
231 g_object_ref(volume));
232
233 g_object_unref(operation);
234 }
235 }
236
237 static void
pbvol_mount_and_open(PlacesBookmarkAction * action)238 pbvol_mount_and_open(PlacesBookmarkAction *action)
239 {
240 GVolume *volume;
241 GMount *mount;
242
243 DBG("Mount and open");
244
245 g_return_if_fail(G_IS_VOLUME(action->priv));
246 volume = G_VOLUME(action->priv);
247 mount = g_volume_get_mount(volume);
248
249 if (!mount) {
250 GMountOperation *operation = gtk_mount_operation_new(NULL);
251
252 g_volume_mount(volume, G_MOUNT_MOUNT_NONE, operation, NULL,
253 pbvol_mount_finish_and_open,
254 g_object_ref(volume));
255
256 g_object_unref(operation);
257 }
258 }
259
260 #ifdef HAVE_GIO_UNIX
261 static gboolean
pbvol_mount_is_internal(GMount * mount)262 pbvol_mount_is_internal (GMount *mount)
263 {
264 const gchar *point_mount_path;
265 gboolean is_internal = FALSE;
266 GFile *root;
267 GList *lp;
268 GList *mount_points;
269 gchar *mount_path;
270
271 g_return_val_if_fail(G_IS_MOUNT(mount), FALSE);
272
273 /* determine the mount path */
274 root = g_mount_get_root(mount);
275 mount_path = g_file_get_path(root);
276 g_object_unref(root);
277
278 /* assume non-internal if we cannot determine the path */
279 if (!mount_path)
280 return FALSE;
281
282 if (g_unix_is_mount_path_system_internal(mount_path)) {
283 /* mark as internal */
284 is_internal = TRUE;
285 } else {
286 /* get a list of all mount points */
287 mount_points = g_unix_mount_points_get(NULL);
288
289 /* search for the mount point associated with the mount entry */
290 for (lp = mount_points; !is_internal && lp != NULL; lp = lp->next) {
291 point_mount_path = g_unix_mount_point_get_mount_path(lp->data);
292
293 /* check if this is the mount point we are looking for */
294 if (g_strcmp0(mount_path, point_mount_path) == 0) {
295 /* mark as internal if the user cannot mount this device */
296 if (!g_unix_mount_point_is_user_mountable(lp->data))
297 is_internal = TRUE;
298 }
299
300 /* free the mount point, we no longer need it */
301 g_unix_mount_point_free(lp->data);
302 }
303
304 /* free the mount point list */
305 g_list_free(mount_points);
306 }
307
308 g_free(mount_path);
309
310
311 return is_internal;
312 }
313 #endif
314
315
316 static gboolean
pbvol_is_removable(GVolume * volume)317 pbvol_is_removable(GVolume *volume)
318 {
319 gboolean can_eject = FALSE;
320 gboolean can_mount = FALSE;
321 gboolean can_unmount = FALSE;
322 gboolean is_removable = FALSE;
323 gboolean is_internal = FALSE;
324 GDrive *drive;
325 GMount *mount;
326
327 g_return_val_if_fail(G_IS_VOLUME(volume), FALSE);
328
329 /* check if the volume can be ejected */
330 can_eject = g_volume_can_eject(volume);
331
332 /* determine the drive for the volume */
333 drive = g_volume_get_drive(volume);
334 if (drive) {
335 /*check if the drive media can be removed */
336 is_removable = g_drive_is_media_removable(drive);
337
338 /* release the drive */
339 g_object_unref(drive);
340 }
341 /* determine the mount for the volume (if it is mounted at all) */
342 mount = g_volume_get_mount(volume);
343 if (mount) {
344 #ifdef HAVE_GIO_UNIX
345 is_internal = pbvol_mount_is_internal (mount);
346 #endif
347
348 /* check if the volume can be unmounted */
349 can_unmount = g_mount_can_unmount(mount);
350
351 /* release the mount */
352 g_object_unref(mount);
353 }
354
355 /* determine whether the device can be mounted */
356 can_mount = g_volume_can_mount(volume);
357
358 return (!is_internal) && (can_eject || can_unmount || is_removable || can_mount);
359 }
360
361 static gboolean
pbvol_is_present(GVolume * volume)362 pbvol_is_present(GVolume *volume)
363 {
364 gboolean has_media = FALSE;
365 gboolean is_shadowed = FALSE;
366 GDrive *drive;
367 GMount *mount;
368
369 g_return_val_if_fail(G_IS_VOLUME(volume), FALSE);
370
371 drive = g_volume_get_drive (volume);
372 if(drive) {
373 has_media = g_drive_has_media(drive);
374 g_object_unref(drive);
375 }
376
377 mount = g_volume_get_mount(volume);
378 if(mount) {
379 is_shadowed = g_mount_is_shadowed(mount);
380 g_object_unref(mount);
381 }
382
383 return has_media && !is_shadowed;
384 }
385
386 static gboolean
pbvol_show_volume(GVolume * volume)387 pbvol_show_volume(GVolume *volume){
388 GMount *mount = g_volume_get_mount(volume);
389 DBG("Volume: %s [mounted=%lu removable=%x present=%x]", g_volume_get_name(volume),
390 (gulong) mount,
391 pbvol_is_removable(volume),
392 pbvol_is_present(volume));
393 if (mount)
394 g_object_unref(mount);
395
396 return pbvol_is_removable(volume) &&
397 pbvol_is_present(volume);
398 }
399
400 static void
pbvol_set_changed(PlacesBookmarkGroup * bookmark_group)401 pbvol_set_changed(PlacesBookmarkGroup *bookmark_group)
402 {
403 DBG("-");
404 pbg_priv(bookmark_group)->changed = TRUE;
405 }
406
407
408 static void
pbvol_volume_added(GVolumeMonitor * monitor,GVolume * volume,PlacesBookmarkGroup * bookmark_group)409 pbvol_volume_added(GVolumeMonitor *monitor, GVolume *volume, PlacesBookmarkGroup *bookmark_group)
410 {
411 DBG("-");
412
413 pbg_priv(bookmark_group)->changed = TRUE;
414 g_signal_connect_swapped(G_VOLUME(volume), "changed",
415 G_CALLBACK(pbvol_set_changed), bookmark_group);
416 }
417
418 static void
pbvol_volume_removed(GVolumeMonitor * monitor,GVolume * volume,PlacesBookmarkGroup * bookmark_group)419 pbvol_volume_removed(GVolumeMonitor *monitor, GVolume *volume, PlacesBookmarkGroup *bookmark_group)
420 {
421 DBG("-");
422
423 pbg_priv(bookmark_group)->changed = TRUE;
424 g_signal_handlers_disconnect_by_func(G_VOLUME(volume),
425 G_CALLBACK(pbvol_set_changed), bookmark_group);
426 }
427
428 static void
pbvol_bookmark_finalize(PlacesBookmark * bookmark)429 pbvol_bookmark_finalize(PlacesBookmark *bookmark)
430 {
431 if(bookmark->uri != NULL){
432 g_free(bookmark->uri);
433 bookmark->uri = NULL;
434 }
435 }
436
437 static void
pbvol_bookmark_action_finalize(PlacesBookmarkAction * action)438 pbvol_bookmark_action_finalize(PlacesBookmarkAction *action){
439 GVolume *volume;
440
441 g_assert(action != NULL && action->priv != NULL);
442
443 volume = G_VOLUME(action->priv);
444 g_object_unref(volume);
445 action->priv = NULL;
446 }
447
448 static GList*
pbvol_get_bookmarks(PlacesBookmarkGroup * bookmark_group)449 pbvol_get_bookmarks(PlacesBookmarkGroup *bookmark_group)
450 {
451 GList *bookmarks = NULL;
452 PlacesBookmark *bookmark;
453 PlacesBookmarkAction *action, *terminal, *open;
454 const GList *volumes;
455 GVolume *volume;
456 GMount *mount;
457
458 volumes = g_volume_monitor_get_volumes(pbg_priv(bookmark_group)->volume_monitor);
459 while (volumes != NULL) {
460 volume = volumes->data;
461 mount = g_volume_get_mount(volume);
462
463 if(pbvol_show_volume(volume)){
464
465 bookmark = places_bookmark_create((gchar*) g_volume_get_name(volume));
466 if (mount) {
467 GFile *file = g_mount_get_root(mount);
468 bookmark->uri = g_file_get_uri(file);
469 g_object_unref(file);
470 } else
471 bookmark->uri = NULL;
472 bookmark->icon = g_volume_get_icon(volume);
473 bookmark->finalize = pbvol_bookmark_finalize;
474
475 if (!mount) {
476
477 g_object_ref(volume);
478 action = places_bookmark_action_create(_("Mount and Open"));
479 action->may_block = TRUE;
480 action->priv = volume;
481 action->action = pbvol_mount_and_open;
482 action->finalize = pbvol_bookmark_action_finalize;
483 bookmark->actions = g_list_append(bookmark->actions, action);
484
485 if(pbg_priv(bookmark_group)->mount_and_open_by_default){
486 bookmark->primary_action = action;
487 bookmark->force_gray = TRUE;
488 }
489
490 g_object_ref(volume);
491 action = places_bookmark_action_create(_("Mount"));
492 action->may_block = TRUE;
493 action->priv = volume;
494 action->action = pbvol_mount;
495 action->finalize = pbvol_bookmark_action_finalize;
496 bookmark->actions = g_list_append(bookmark->actions, action);
497
498 }else{
499
500 terminal = places_create_open_terminal_action(bookmark);
501 bookmark->actions = g_list_prepend(bookmark->actions, terminal);
502 open = places_create_open_action(bookmark);
503 bookmark->actions = g_list_prepend(bookmark->actions, open);
504 bookmark->primary_action = open;
505
506 }
507
508 if (g_volume_can_eject(volume)) {
509
510 g_object_ref(volume);
511 action = places_bookmark_action_create(_("Eject"));
512 action->may_block = TRUE;
513 action->priv = volume;
514 action->action = pbvol_eject;
515 action->finalize = pbvol_bookmark_action_finalize;
516 bookmark->actions = g_list_append(bookmark->actions, action);
517
518 }
519 if (mount) {
520 g_object_ref(volume);
521 action = places_bookmark_action_create(_("Unmount"));
522 action->may_block = TRUE;
523 action->priv = volume;
524 action->action = pbvol_unmount;
525 action->finalize = pbvol_bookmark_action_finalize;
526 bookmark->actions = g_list_append(bookmark->actions, action);
527 }
528
529 bookmarks = g_list_prepend(bookmarks, bookmark);
530 }
531
532 volumes = volumes->next;
533 }
534
535 pbg_priv(bookmark_group)->changed = FALSE;
536
537 return g_list_reverse(bookmarks);
538
539 }
540
541 static gboolean
pbvol_changed(PlacesBookmarkGroup * bookmark_group)542 pbvol_changed(PlacesBookmarkGroup *bookmark_group)
543 {
544 return pbg_priv(bookmark_group)->changed;
545 }
546
547 static void
pbvol_finalize(PlacesBookmarkGroup * bookmark_group)548 pbvol_finalize(PlacesBookmarkGroup *bookmark_group)
549 {
550 const GList *volumes;
551
552 volumes = g_volume_monitor_get_volumes(pbg_priv(bookmark_group)->volume_monitor);
553 while(volumes != NULL){
554 g_signal_handlers_disconnect_by_func(G_VOLUME(volumes->data),
555 G_CALLBACK(pbvol_set_changed), bookmark_group);
556 volumes = volumes->next;
557 }
558
559 g_signal_handlers_disconnect_by_func(pbg_priv(bookmark_group)->volume_monitor,
560 G_CALLBACK(pbvol_volume_added), bookmark_group);
561 g_signal_handlers_disconnect_by_func(pbg_priv(bookmark_group)->volume_monitor,
562 G_CALLBACK(pbvol_volume_removed), bookmark_group);
563
564 g_object_unref(pbg_priv(bookmark_group)->volume_monitor);
565 pbg_priv(bookmark_group)->volume_monitor = NULL;
566
567 g_free(pbg_priv(bookmark_group));
568 bookmark_group->priv = NULL;
569 }
570
571 PlacesBookmarkGroup*
places_bookmarks_volumes_create(gboolean mount_and_open_by_default)572 places_bookmarks_volumes_create(gboolean mount_and_open_by_default)
573 {
574 GList *volumes;
575 PlacesBookmarkGroup *bookmark_group;
576
577 bookmark_group = places_bookmark_group_create();
578 bookmark_group->get_bookmarks = pbvol_get_bookmarks;
579 bookmark_group->changed = pbvol_changed;
580 bookmark_group->finalize = pbvol_finalize;
581 bookmark_group->priv = g_new0(PBVolData, 1);
582
583 pbg_priv(bookmark_group)->volume_monitor = g_volume_monitor_get();
584 pbg_priv(bookmark_group)->changed = TRUE;
585 pbg_priv(bookmark_group)->mount_and_open_by_default = mount_and_open_by_default;
586
587 volumes = g_volume_monitor_get_volumes(pbg_priv(bookmark_group)->volume_monitor);
588 while(volumes != NULL) {
589 g_signal_connect_swapped(G_OBJECT(volumes->data), "changed",
590 G_CALLBACK(pbvol_set_changed), bookmark_group);
591 g_object_unref(volumes->data);
592 volumes = volumes->next;
593 }
594 g_list_free(volumes);
595
596 g_signal_connect(pbg_priv(bookmark_group)->volume_monitor, "volume-added",
597 G_CALLBACK(pbvol_volume_added), bookmark_group);
598
599 g_signal_connect(pbg_priv(bookmark_group)->volume_monitor, "volume-removed",
600 G_CALLBACK(pbvol_volume_removed), bookmark_group);
601
602 return bookmark_group;
603 }
604
605 /* vim: set ai et tabstop=4: */
606