File: rb-device-source.c

package info (click to toggle)
rhythmbox 3.4.9-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 27,376 kB
  • sloc: ansic: 114,861; python: 4,941; xml: 730; javascript: 350; perl: 307; sh: 84; makefile: 43
file content (385 lines) | stat: -rw-r--r-- 10,820 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
 *
 *  Copyright (C) 2011  Jonathan Matthew  <jonathan@d14n.org>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  The Rhythmbox authors hereby grant permission for non-GPL compatible
 *  GStreamer plugins to be used and distributed together with GStreamer
 *  and Rhythmbox. This permission is above and beyond the permissions granted
 *  by the GPL license by which Rhythmbox is covered. If you modify this code
 *  you may extend this exception to your version of the code, but you are not
 *  obligated to do so. If you do not wish to do so, delete this exception
 *  statement from your version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, see <http://www.gnu.org/licenses/>.
 *
 */

#include <config.h>

#include <gio/gio.h>
#include <glib/gi18n.h>

#include "rb-device-source.h"
#include "rb-source.h"
#include "rb-debug.h"
#include "rb-dialog.h"

G_DEFINE_INTERFACE (RBDeviceSource, rb_device_source, 0)

/**
 * SECTION:rbdevicesource
 * @short_description: interface for sources based on physical devices
 * @include: rb-device-source.h
 *
 * Sources that represent physical devices should implement this interface.
 * It exposes the ability to eject the device, and also can be used to
 * implement some #RBSource methods by using details from a #GVolume or
 * #GMount accessed via 'volume' and 'mount' properties on the source object.
 * Devices that are not based on a #GVolume or #GMount can still use the
 * interface, but they must provide implementations of the @can_eject and
 * @eject methods.
 */

static gboolean
default_can_eject (RBDeviceSource *source)
{
	gboolean result = FALSE;
	GVolume *volume = NULL;
	GMount *mount = NULL;

	if (g_object_class_find_property (G_OBJECT_GET_CLASS (source), "volume")) {
		g_object_get (source, "volume", &volume, NULL);
	}
	if (g_object_class_find_property (G_OBJECT_GET_CLASS (source), "mount")) {
		g_object_get (source, "mount", &mount, NULL);
	}

	if (volume != NULL) {
		result = g_volume_can_eject (volume);

		g_object_unref (volume);
		if (mount != NULL) {
			g_object_unref (mount);
		}
	} else if (mount != NULL) {
		result = g_mount_can_eject (mount) || g_mount_can_unmount (mount);

		if (mount != NULL) {
			g_object_unref (mount);
		}
	}

	return result;
}

static void
eject_cb (GObject *object, GAsyncResult *result, gpointer nothing)
{
	GError *error = NULL;

	if (G_IS_VOLUME (object)) {
		GVolume *volume = G_VOLUME (object);

		rb_debug ("finishing ejection of volume");
		g_volume_eject_with_operation_finish (volume, result, &error);
	} else if (G_IS_MOUNT (object)) {
		GMount *mount = G_MOUNT (object);

		rb_debug ("finishing ejection of mount");
		g_mount_eject_with_operation_finish (mount, result, &error);
	}

	if (error != NULL) {
		if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_FAILED_HANDLED)) {
			rb_error_dialog (NULL, _("Unable to eject"), "%s", error->message);
		} else {
			rb_debug ("eject failure has already been handled");
		}
		g_error_free (error);
	}
}

static void
unmount_cb (GObject *object, GAsyncResult *result, gpointer nothing)
{
	GMount *mount = G_MOUNT (object);
	GError *error = NULL;

	rb_debug ("finishing unmount of mount");
	g_mount_unmount_with_operation_finish (mount, result, &error);
	if (error != NULL) {
		if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_FAILED_HANDLED)) {
			rb_error_dialog (NULL, _("Unable to unmount"), "%s", error->message);
		} else {
			rb_debug ("unmount failure has already been handled");
		}
		g_error_free (error);
	}
}

/**
 * rb_device_source_default_eject:
 * @source: a #RBDeviceSource
 *
 * Default method for ejecting devices.  Implementations can
 * perform any required work before ejecting, then call this do
 * eject the device.
 */
void
rb_device_source_default_eject (RBDeviceSource *source)
{
	GVolume *volume = NULL;
	GMount *mount = NULL;

	if (g_object_class_find_property (G_OBJECT_GET_CLASS (source), "volume")) {
		g_object_get (source, "volume", &volume, NULL);
	}
	if (g_object_class_find_property (G_OBJECT_GET_CLASS (source), "mount")) {
		g_object_get (source, "mount", &mount, NULL);
	}

	/* try ejecting based on volume first, then based on the mount,
	 * and finally try unmounting.
	 */
	if (volume != NULL) {
		if (g_volume_can_eject (volume)) {
			rb_debug ("ejecting volume");
			g_volume_eject_with_operation (volume,
						       G_MOUNT_UNMOUNT_NONE,
						       NULL,
						       NULL,
						       (GAsyncReadyCallback) eject_cb,
						       NULL);
		} else {
			/* this should never happen; the eject command will be
			 * insensitive if the selected source cannot be ejected.
			 */
			rb_debug ("don't know what to do with this volume");
		}
	} else if (mount != NULL) {
		if (g_mount_can_eject (mount)) {
			rb_debug ("ejecting mount");
			g_mount_eject_with_operation (mount,
						      G_MOUNT_UNMOUNT_NONE,
						      NULL,
						      NULL,
						      (GAsyncReadyCallback) eject_cb,
						      NULL);
		} else if (g_mount_can_unmount (mount)) {
			rb_debug ("unmounting mount");
			g_mount_unmount_with_operation (mount,
							G_MOUNT_UNMOUNT_NONE,
							NULL,
							NULL,
							(GAsyncReadyCallback) unmount_cb,
							NULL);
		} else {
			/* this should never happen; the eject command will be
			 * insensitive if the selected source cannot be ejected.
			 */
			rb_debug ("don't know what to do with this mount");
		}
	}

	if (volume != NULL) {
		g_object_unref (volume);
	}
	if (mount != NULL) {
		g_object_unref (mount);
	}
}

/**
 * rb_device_can_eject:
 * @source: a #RBDeviceSource
 *
 * Checks if the device that the source represents can be ejected.
 *
 * Return value: %TRUE if the device can be ejected
 */
gboolean
rb_device_source_can_eject (RBDeviceSource *source)
{
	RBDeviceSourceInterface *iface = RB_DEVICE_SOURCE_GET_IFACE (source);
	return iface->can_eject (source);
}

/**
 * rb_device_source_eject:
 * @source: a #RBDeviceSource
 *
 * Ejects the device that the source represents.
 */
void
rb_device_source_eject (RBDeviceSource *source)
{
	RBDeviceSourceInterface *iface = RB_DEVICE_SOURCE_GET_IFACE (source);
	iface->eject (source);
}

/**
 * rb_device_source_want_uri:
 * @source: a #RBDeviceSource
 * @uri: a URI to consider
 *
 * Checks whether @uri identifies a path underneath the
 * device's mount point.  Should be used to implement
 * the #RBSource want_uri method.
 *
 * Return value: URI match strength
 */
guint
rb_device_source_want_uri (RBSource *source, const char *uri)
{
	GMount *mount = NULL;
	GVolume *volume = NULL;
	GFile *file;
	char *device_path, *uri_path;
	int retval;
	int len;

	retval = 0;

	file = g_file_new_for_uri (uri);

	/* Deal with the mount root being passed, eg. file:///media/IPODNAME */
	if (g_object_class_find_property (G_OBJECT_GET_CLASS (source), "mount")) {
		g_object_get (source, "mount", &mount, NULL);
	}
	if (mount != NULL) {
		GFile *root;

		root = g_mount_get_root (mount);
		retval = g_file_equal (root, file) ? 100 : 0;
		g_object_unref (root);
		if (retval) {
			g_object_unref (file);
			g_object_unref (mount);
			return retval;
		}
		volume = g_mount_get_volume (mount);
		g_object_unref (mount);
	} else {
		if (g_object_class_find_property (G_OBJECT_GET_CLASS (source), "volume")) {
			g_object_get (source, "volume", &volume, NULL);
		}
	}

	/* ignore anything that isn't a local file or doesn't have a volume */
	if (g_file_has_uri_scheme (file, "file") == FALSE || volume == NULL) {
		g_object_unref (file);
		return 0;
	}

	/* Deal with the path to the device node being passed */
	device_path = g_volume_get_identifier (volume, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
	g_object_unref (volume);
	if (device_path == NULL) {
		g_object_unref (file);
		return 0;
	}

	uri_path = g_file_get_path (file);
	g_object_unref (file);
	if (uri_path == NULL)
		return 0;
	len = strlen (uri_path);
	if (uri_path[len - 1] == '/') {
		if (strncmp (uri_path, device_path, len - 1) == 0) {
			retval = 100;
		}
	} else if (strcmp (uri_path, device_path) == 0) {
		retval = 100;
	}

	g_free (device_path);
	g_free (uri_path);
	return retval;
}

/**
 * rb_device_source_uri_is_source:
 * @source: a #RBDeviceSource
 * @uri: a URI to check
 *
 * Returns %TRUE if @uri matches @source.  This should be
 * used to implement the uri_is_source #RBSource method.
 *
 * Return value: %TRUE if @uri matches @source
 */
gboolean
rb_device_source_uri_is_source (RBSource *source, const char *uri)
{
	return (rb_device_source_want_uri (source, uri) == 100);
}

/**
 * rb_device_source_set_display_details:
 * @source: a #RBDeviceSource
 *
 * Sets the icon and display name for a device-based source.
 * The details come from the mount and/or volume.  This should
 * be called in the source's constructed method.
 */
void
rb_device_source_set_display_details (RBDeviceSource *source)
{
	GMount *mount = NULL;
	GVolume *volume = NULL;
	GIcon *icon = NULL;
	char *display_name;

	if (g_object_class_find_property (G_OBJECT_GET_CLASS (source), "volume")) {
		g_object_get (source, "volume", &volume, NULL);
	}
	if (g_object_class_find_property (G_OBJECT_GET_CLASS (source), "mount")) {
		g_object_get (source, "mount", &mount, NULL);
	}

	/* prefer mount details to volume details, as the nautilus sidebar does */
	if (mount != NULL) {
		mount = g_object_ref (mount);
	} else if (volume != NULL) {
		mount = g_volume_get_mount (volume);
	} else {
		mount = NULL;
	}

	if (mount != NULL) {
		display_name = g_mount_get_name (mount);
		icon = g_mount_get_symbolic_icon (mount);
		rb_debug ("details from mount: display name = %s, icon = %p", display_name, icon);
	} else if (volume != NULL) {
		display_name = g_volume_get_name (volume);
		icon = g_volume_get_symbolic_icon (volume);
		rb_debug ("details from volume: display name = %s, icon = %p", display_name, icon);
	} else {
		display_name = g_strdup ("Unknown Device");
		icon = g_themed_icon_new ("multimedia-player-symbolic");
	}

	g_object_set (source, "name", display_name, "icon", icon, NULL);
	g_free (display_name);

	g_clear_object (&mount);
	g_clear_object (&volume);
	g_clear_object (&icon);
}

static void
rb_device_source_default_init (RBDeviceSourceInterface *interface)
{
	interface->can_eject = default_can_eject;
	interface->eject = rb_device_source_default_eject;
}