2
* Copyright (c) 2012 Nick Schermer <nick@xfce.org>
4
* This program is free software; you can redistribute it and/or modify it
5
* under the terms of the GNU General Public License as published by the Free
6
* Software Foundation; either version 2 of the License, or (at your option)
9
* This program is distributed in the hope that it will be useful, but WITHOUT
10
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14
* You should have received a copy of the GNU General Public License along with
15
* this program; if not, write to the Free Software Foundation, Inc., 59 Temple
16
* Place, Suite 330, Boston, MA 02111-1307 USA
23
#include <thunar/thunar-device.h>
24
#include <thunar/thunar-device-monitor.h>
25
#include <thunar/thunar-private.h>
26
#include <thunar/thunar-file.h>
27
#include <thunar/thunar-notify.h>
31
typedef gboolean (*AsyncCallbackFinish) (GObject *object,
47
static void thunar_device_finalize (GObject *object);
48
static void thunar_device_get_property (GObject *object,
52
static void thunar_device_set_property (GObject *object,
59
struct _ThunarDeviceClass
61
GObjectClass __parent__;
68
/* a GVolume/GMount/GDrive */
71
ThunarDeviceKind kind;
73
/* if the device is the list of hidden names */
76
/* added time for sorting */
82
/* the device the operation is working on */
85
/* finish function for the async callback */
86
AsyncCallbackFinish callback_finish;
88
/* callback for the user */
89
ThunarDeviceCallback callback;
92
ThunarDeviceOperation;
96
G_DEFINE_TYPE (ThunarDevice, thunar_device, G_TYPE_OBJECT)
101
thunar_device_class_init (ThunarDeviceClass *klass)
103
GObjectClass *gobject_class;
105
gobject_class = G_OBJECT_CLASS (klass);
106
gobject_class->finalize = thunar_device_finalize;
107
gobject_class->get_property = thunar_device_get_property;
108
gobject_class->set_property = thunar_device_set_property;
110
g_object_class_install_property (gobject_class,
112
g_param_spec_object ("device",
117
| G_PARAM_CONSTRUCT_ONLY));
119
g_object_class_install_property (gobject_class,
121
g_param_spec_boolean ("hidden",
125
EXO_PARAM_READWRITE));
127
g_object_class_install_property (gobject_class,
129
g_param_spec_uint ("kind",
132
THUNAR_DEVICE_KIND_VOLUME,
133
THUNAR_DEVICE_KIND_MOUNT_REMOTE,
134
THUNAR_DEVICE_KIND_VOLUME,
136
| G_PARAM_CONSTRUCT_ONLY));
142
thunar_device_init (ThunarDevice *device)
144
device->kind = THUNAR_DEVICE_KIND_VOLUME;
145
device->stamp = g_get_real_time ();
151
thunar_device_finalize (GObject *object)
153
ThunarDevice *device = THUNAR_DEVICE (object);
155
if (device->device != NULL)
156
g_object_unref (G_OBJECT (device->device));
158
(*G_OBJECT_CLASS (thunar_device_parent_class)->finalize) (object);
164
thunar_device_get_property (GObject *object,
169
ThunarDevice *device = THUNAR_DEVICE (object);
174
g_value_set_object (value, device->device);
178
g_value_set_boolean (value, device->hidden);
182
g_value_set_uint (value, device->kind);
186
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
194
thunar_device_set_property (GObject *object,
199
ThunarDevice *device = THUNAR_DEVICE (object);
204
device->device = g_value_dup_object (value);
205
_thunar_assert (G_IS_VOLUME (device->device) || G_IS_MOUNT (device->device));
209
device->hidden = g_value_get_boolean (value);
213
device->kind = g_value_get_uint (value);
217
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
224
static ThunarDeviceOperation *
225
thunar_device_operation_new (ThunarDevice *device,
226
ThunarDeviceCallback callback,
228
gpointer callback_finish)
230
ThunarDeviceOperation *op;
232
op = g_slice_new0 (ThunarDeviceOperation);
233
op->device = g_object_ref (device);
234
op->callback = callback;
235
op->callback_finish = callback_finish;
236
op->user_data = user_data;
244
thunar_device_operation_finish (GObject *object,
245
GAsyncResult *result,
248
ThunarDeviceOperation *op = user_data;
249
GError *error = NULL;
251
_thunar_return_if_fail (G_IS_OBJECT (object));
252
_thunar_return_if_fail (G_IS_ASYNC_RESULT (result));
254
/* remove notification */
255
thunar_notify_finish (op->device);
257
/* finish the operation */
258
if (!(op->callback_finish) (object, result, &error))
260
/* unset the error if a helper program has already interacted with the user */
261
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_FAILED_HANDLED))
262
g_clear_error (&error);
264
if (op->callback_finish == (AsyncCallbackFinish) g_volume_mount_finish)
266
/* special handling for mount operation */
267
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_ALREADY_MOUNTED)
268
|| g_error_matches (error, G_IO_ERROR, G_IO_ERROR_PENDING))
269
g_clear_error (&error);
274
(op->callback) (op->device, error, op->user_data);
277
g_clear_error (&error);
278
g_object_unref (G_OBJECT (op->device));
279
g_slice_free (ThunarDeviceOperation, op);
285
thunar_device_emit_pre_unmount (ThunarDevice *device,
286
gboolean all_volumes)
288
ThunarDeviceMonitor *monitor;
291
root_file = thunar_device_get_root (device);
292
if (root_file != NULL)
294
/* make sure the pre-unmount event is emitted, this is important
295
* for the interface */
296
monitor = thunar_device_monitor_get ();
297
g_signal_emit_by_name (monitor, "device-pre-unmount", device, root_file);
298
g_object_unref (monitor);
299
g_object_unref (root_file);
306
thunar_device_get_name (const ThunarDevice *device)
309
gchar *display_name = NULL;
311
_thunar_return_val_if_fail (THUNAR_IS_DEVICE (device), NULL);
313
if (G_IS_VOLUME (device->device))
315
return g_volume_get_name (device->device);
317
else if (G_IS_MOUNT (device->device))
319
/* probably we can make a nicer name for this mount */
320
mount_point = thunar_device_get_root (device);
321
if (mount_point != NULL)
323
display_name = thunar_g_file_get_display_name_remote (mount_point);
324
g_object_unref (mount_point);
327
if (display_name == NULL)
328
display_name = g_mount_get_name (device->device);
333
_thunar_assert_not_reached ();
341
thunar_device_get_icon (const ThunarDevice *device)
343
_thunar_return_val_if_fail (THUNAR_IS_DEVICE (device), NULL);
345
if (G_IS_VOLUME (device->device))
346
return g_volume_get_icon (device->device);
347
else if (G_IS_MOUNT (device->device))
348
return g_mount_get_icon (device->device);
350
_thunar_assert_not_reached ();
358
thunar_device_get_kind (const ThunarDevice *device)
360
_thunar_return_val_if_fail (THUNAR_IS_DEVICE (device), THUNAR_DEVICE_KIND_VOLUME);
367
thunar_device_get_identifier (const ThunarDevice *device)
371
_thunar_return_val_if_fail (THUNAR_IS_DEVICE (device), NULL);
373
if (G_IS_VOLUME (device->device))
375
ident = g_volume_get_uuid (device->device);
377
ident = g_volume_get_identifier (device->device, G_VOLUME_IDENTIFIER_KIND_UUID);
379
ident = g_volume_get_name (device->device);
381
else if (G_IS_MOUNT (device->device))
383
ident = g_mount_get_uuid (device->device);
385
ident = g_mount_get_name (device->device);
388
_thunar_assert_not_reached ();
396
thunar_device_get_hidden (const ThunarDevice *device)
398
_thunar_return_val_if_fail (THUNAR_IS_DEVICE (device), FALSE);
399
return device->hidden;
405
* thunar_device_can_eject:
407
* If the user should see the option to eject this device.
410
thunar_device_can_eject (const ThunarDevice *device)
412
gboolean can_eject = FALSE;
413
GMount *volume_mount;
416
_thunar_return_val_if_fail (THUNAR_IS_DEVICE (device), FALSE);
418
if (G_IS_VOLUME (device->device))
420
drive = g_volume_get_drive (device->device);
423
can_eject = g_drive_can_eject (drive) || g_drive_can_stop (drive);
424
g_object_unref (drive);
429
/* check if the volume can eject */
430
can_eject = g_volume_can_eject (device->device);
433
/* check if the mount can eject/unmount */
434
volume_mount = g_volume_get_mount (device->device);
435
if (volume_mount != NULL)
437
can_eject = g_mount_can_eject (volume_mount) || g_mount_can_unmount (volume_mount);
438
g_object_unref (volume_mount);
443
else if (G_IS_MOUNT (device->device))
445
/* eject or unmount because thats for the user the same
446
* because we prefer volumes over mounts as devices */
447
can_eject = g_mount_can_eject (device->device) || g_mount_can_unmount (device->device);
450
_thunar_assert_not_reached ();
458
* thunar_device_can_mount:
460
* If the user should see the option to mount this device.
463
thunar_device_can_mount (const ThunarDevice *device)
465
gboolean can_mount = FALSE;
466
GMount *volume_mount;
468
_thunar_return_val_if_fail (THUNAR_IS_DEVICE (device), FALSE);
470
if (G_IS_VOLUME (device->device))
472
/* only volumes without a mountpoint and mount capability */
473
volume_mount = g_volume_get_mount (device->device);
474
if (volume_mount == NULL)
475
can_mount = g_volume_can_mount (device->device);
477
g_object_unref (volume_mount);
479
else if (G_IS_MOUNT (device->device))
481
/* a mount is already mounted... */
485
_thunar_assert_not_reached ();
493
* thunar_device_can_unmount:
495
* If the user should see the option to unmount this device.
498
thunar_device_can_unmount (const ThunarDevice *device)
500
gboolean can_unmount = FALSE;
501
GMount *volume_mount;
503
_thunar_return_val_if_fail (THUNAR_IS_DEVICE (device), FALSE);
505
if (G_IS_VOLUME (device->device))
507
/* only volumes with a mountpoint and unmount capability */
508
volume_mount = g_volume_get_mount (device->device);
509
if (volume_mount != NULL)
511
can_unmount = g_mount_can_unmount (volume_mount);
512
g_object_unref (volume_mount);
515
else if (G_IS_MOUNT (device->device))
517
/* check if the mount can unmount */
518
can_unmount = g_mount_can_unmount (device->device);
521
_thunar_assert_not_reached ();
529
thunar_device_is_mounted (const ThunarDevice *device)
531
gboolean is_mounted = FALSE;
532
GMount *volume_mount;
534
_thunar_return_val_if_fail (THUNAR_IS_DEVICE (device), FALSE);
536
if (G_IS_VOLUME (device->device))
538
/* a volume with a mount point is mounted */
539
volume_mount = g_volume_get_mount (device->device);
540
if (volume_mount != NULL)
543
g_object_unref (volume_mount);
546
else if (G_IS_MOUNT (device->device))
548
/* mounter are always mounted... */
552
_thunar_assert_not_reached ();
560
thunar_device_get_root (const ThunarDevice *device)
563
GMount *volume_mount;
565
_thunar_return_val_if_fail (THUNAR_IS_DEVICE (device), NULL);
567
if (G_IS_VOLUME (device->device))
569
volume_mount = g_volume_get_mount (device->device);
570
if (volume_mount != NULL)
572
root = g_mount_get_root (volume_mount);
573
g_object_unref (volume_mount);
577
root = g_volume_get_activation_root (device->device);
579
else if (G_IS_MOUNT (device->device))
581
root = g_mount_get_root (device->device);
584
_thunar_assert_not_reached ();
592
thunar_device_sort (const ThunarDevice *device1,
593
const ThunarDevice *device2)
595
_thunar_return_val_if_fail (THUNAR_IS_DEVICE (device1), 0);
596
_thunar_return_val_if_fail (THUNAR_IS_DEVICE (device2), 0);
598
/* sort volumes above mounts */
599
if (G_OBJECT_TYPE (device1->device) != G_OBJECT_TYPE (device2->device))
600
return G_IS_VOLUME (device1->device) ? 1 : -1;
602
/* sort by detect stamp */
603
return device1->stamp > device2->stamp ? 1 : -1;
609
thunar_device_mount (ThunarDevice *device,
610
GMountOperation *mount_operation,
611
GCancellable *cancellable,
612
ThunarDeviceCallback callback,
615
ThunarDeviceOperation *op;
617
_thunar_return_if_fail (THUNAR_IS_DEVICE (device));
618
_thunar_return_if_fail (G_IS_MOUNT_OPERATION (mount_operation));
619
_thunar_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
620
_thunar_return_if_fail (callback != NULL);
622
if (G_IS_VOLUME (device->device))
624
op = thunar_device_operation_new (device, callback, user_data,
625
g_volume_mount_finish);
626
g_volume_mount (G_VOLUME (device->device),
630
thunar_device_operation_finish,
638
* thunar_device_unmount:
640
* Unmount a #ThunarDevice. Don't try to eject.
643
thunar_device_unmount (ThunarDevice *device,
644
GMountOperation *mount_operation,
645
GCancellable *cancellable,
646
ThunarDeviceCallback callback,
649
ThunarDeviceOperation *op;
652
_thunar_return_if_fail (THUNAR_IS_DEVICE (device));
653
_thunar_return_if_fail (G_IS_MOUNT_OPERATION (mount_operation));
654
_thunar_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
655
_thunar_return_if_fail (callback != NULL);
657
/* get the mount from the volume or use existing mount */
658
if (G_IS_VOLUME (device->device))
659
mount = g_volume_get_mount (device->device);
660
else if (G_IS_MOUNT (device->device))
661
mount = g_object_ref (device->device);
665
if (G_LIKELY (mount != NULL))
667
/* only handle mounts that can be unmounted here */
668
if (g_mount_can_unmount (mount))
671
thunar_notify_unmount (device);
673
/* try unmounting the mount */
674
thunar_device_emit_pre_unmount (device, FALSE);
675
op = thunar_device_operation_new (device, callback, user_data,
676
g_mount_unmount_with_operation_finish);
677
g_mount_unmount_with_operation (mount,
678
G_MOUNT_UNMOUNT_NONE,
681
thunar_device_operation_finish,
685
g_object_unref (G_OBJECT (mount));
692
* thunar_device_unmount:
694
* Try to eject a #ThunarDevice, fall-back to unmounting
697
thunar_device_eject (ThunarDevice *device,
698
GMountOperation *mount_operation,
699
GCancellable *cancellable,
700
ThunarDeviceCallback callback,
703
ThunarDeviceOperation *op;
704
GMount *mount = NULL;
708
_thunar_return_if_fail (THUNAR_IS_DEVICE (device));
709
_thunar_return_if_fail (G_IS_MOUNT_OPERATION (mount_operation));
710
_thunar_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
711
_thunar_return_if_fail (callback != NULL);
713
if (G_IS_VOLUME (device->device))
715
volume = device->device;
716
drive = g_volume_get_drive (volume);
720
if (g_drive_can_stop (drive))
723
thunar_notify_eject (device);
725
/* try to stop the drive */
726
thunar_device_emit_pre_unmount (device, TRUE);
727
op = thunar_device_operation_new (device, callback, user_data,
728
g_drive_stop_finish);
730
G_MOUNT_UNMOUNT_NONE,
733
thunar_device_operation_finish,
736
g_object_unref (drive);
741
else if (g_drive_can_eject (drive))
744
thunar_notify_eject (device);
746
/* try to stop the drive */
747
thunar_device_emit_pre_unmount (device, TRUE);
748
op = thunar_device_operation_new (device, callback, user_data,
749
g_drive_eject_with_operation_finish);
750
g_drive_eject_with_operation (drive,
751
G_MOUNT_UNMOUNT_NONE,
754
thunar_device_operation_finish,
757
g_object_unref (drive);
763
g_object_unref (drive);
766
if (g_volume_can_eject (volume))
769
thunar_notify_eject (device);
771
/* try ejecting the volume */
772
thunar_device_emit_pre_unmount (device, TRUE);
773
op = thunar_device_operation_new (device, callback, user_data,
774
g_volume_eject_with_operation_finish);
775
g_volume_eject_with_operation (volume,
776
G_MOUNT_UNMOUNT_NONE,
779
thunar_device_operation_finish,
787
/* get the mount and fall-through */
788
mount = g_volume_get_mount (volume);
791
else if (G_IS_MOUNT (device->device))
793
/* set the mount and fall-through */
794
mount = g_object_ref (device->device);
800
/* distinguish between ejectable and unmountable mounts */
801
if (g_mount_can_eject (mount))
804
thunar_notify_eject (device);
806
/* try ejecting the mount */
807
thunar_device_emit_pre_unmount (device, FALSE);
808
op = thunar_device_operation_new (device, callback, user_data,
809
g_mount_eject_with_operation_finish);
810
g_mount_eject_with_operation (mount,
811
G_MOUNT_UNMOUNT_NONE,
814
thunar_device_operation_finish,
817
else if (g_mount_can_unmount (mount))
820
thunar_notify_unmount (device);
822
/* try unmounting the mount */
823
thunar_device_emit_pre_unmount (device, FALSE);
824
op = thunar_device_operation_new (device, callback, user_data,
825
g_mount_unmount_with_operation_finish);
826
g_mount_unmount_with_operation (mount,
827
G_MOUNT_UNMOUNT_NONE,
830
thunar_device_operation_finish,
834
g_object_unref (G_OBJECT (mount));