hythmbox-2.98/sources/rb-device-source.c

No issues found

  1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
  2  *
  3  *  Copyright (C) 2011  Jonathan Matthew  <jonathan@d14n.org>
  4  *
  5  *  This program is free software; you can redistribute it and/or modify
  6  *  it under the terms of the GNU General Public License as published by
  7  *  the Free Software Foundation; either version 2 of the License, or
  8  *  (at your option) any later version.
  9  *
 10  *  The Rhythmbox authors hereby grant permission for non-GPL compatible
 11  *  GStreamer plugins to be used and distributed together with GStreamer
 12  *  and Rhythmbox. This permission is above and beyond the permissions granted
 13  *  by the GPL license by which Rhythmbox is covered. If you modify this code
 14  *  you may extend this exception to your version of the code, but you are not
 15  *  obligated to do so. If you do not wish to do so, delete this exception
 16  *  statement from your version.
 17  *
 18  *  This program is distributed in the hope that it will be useful,
 19  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 20  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 21  *  GNU General Public License for more details.
 22  *
 23  *  You should have received a copy of the GNU General Public License
 24  *  along with this program; if not, write to the Free Software
 25  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 26  *
 27  */
 28 
 29 #include <config.h>
 30 
 31 #include <gio/gio.h>
 32 #include <glib/gi18n.h>
 33 
 34 #include "rb-device-source.h"
 35 #include "rb-source.h"
 36 #include "rb-debug.h"
 37 #include "rb-dialog.h"
 38 
 39 G_DEFINE_INTERFACE (RBDeviceSource, rb_device_source, 0)
 40 
 41 /**
 42  * SECTION:rb-device-source
 43  * @short_description: interface for sources based on physical devices
 44  * @include: rb-device-source.h
 45  *
 46  * Sources that represent physical devices should implement this interface.
 47  * It exposes the ability to eject the device, and also can be used to
 48  * implement some #RBSource methods by using details from a #GVolume or
 49  * #GMount accessed via 'volume' and 'mount' properties on the source object.
 50  * Devices that are not based on a #GVolume or #GMount can still use the
 51  * interface, but they must provide implementations of the @can_eject and
 52  * @eject methods.
 53  */
 54 
 55 static gboolean
 56 default_can_eject (RBDeviceSource *source)
 57 {
 58 	gboolean result = FALSE;
 59 	GVolume *volume = NULL;
 60 	GMount *mount = NULL;
 61 
 62 	if (g_object_class_find_property (G_OBJECT_GET_CLASS (source), "volume")) {
 63 		g_object_get (source, "volume", &volume, NULL);
 64 	}
 65 	if (g_object_class_find_property (G_OBJECT_GET_CLASS (source), "mount")) {
 66 		g_object_get (source, "mount", &mount, NULL);
 67 	}
 68 
 69 	if (volume != NULL) {
 70 		result = g_volume_can_eject (volume);
 71 
 72 		g_object_unref (volume);
 73 		if (mount != NULL) {
 74 			g_object_unref (mount);
 75 		}
 76 	} else if (mount != NULL) {
 77 		result = g_mount_can_eject (mount) || g_mount_can_unmount (mount);
 78 
 79 		if (mount != NULL) {
 80 			g_object_unref (mount);
 81 		}
 82 	}
 83 
 84 	return result;
 85 }
 86 
 87 static void
 88 eject_cb (GObject *object, GAsyncResult *result, gpointer nothing)
 89 {
 90 	GError *error = NULL;
 91 
 92 	if (G_IS_VOLUME (object)) {
 93 		GVolume *volume = G_VOLUME (object);
 94 
 95 		rb_debug ("finishing ejection of volume");
 96 		g_volume_eject_with_operation_finish (volume, result, &error);
 97 	} else if (G_IS_MOUNT (object)) {
 98 		GMount *mount = G_MOUNT (object);
 99 
100 		rb_debug ("finishing ejection of mount");
101 		g_mount_eject_with_operation_finish (mount, result, &error);
102 	}
103 
104 	if (error != NULL) {
105 		if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_FAILED_HANDLED)) {
106 			rb_error_dialog (NULL, _("Unable to eject"), "%s", error->message);
107 		} else {
108 			rb_debug ("eject failure has already been handled");
109 		}
110 		g_error_free (error);
111 	}
112 }
113 
114 static void
115 unmount_cb (GObject *object, GAsyncResult *result, gpointer nothing)
116 {
117 	GMount *mount = G_MOUNT (object);
118 	GError *error = NULL;
119 
120 	rb_debug ("finishing unmount of mount");
121 	g_mount_unmount_with_operation_finish (mount, result, &error);
122 	if (error != NULL) {
123 		if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_FAILED_HANDLED)) {
124 			rb_error_dialog (NULL, _("Unable to unmount"), "%s", error->message);
125 		} else {
126 			rb_debug ("unmount failure has already been handled");
127 		}
128 		g_error_free (error);
129 	}
130 }
131 
132 static void
133 default_eject (RBDeviceSource *source)
134 {
135 	GVolume *volume = NULL;
136 	GMount *mount = NULL;
137 
138 	if (g_object_class_find_property (G_OBJECT_GET_CLASS (source), "volume")) {
139 		g_object_get (source, "volume", &volume, NULL);
140 	}
141 	if (g_object_class_find_property (G_OBJECT_GET_CLASS (source), "mount")) {
142 		g_object_get (source, "mount", &mount, NULL);
143 	}
144 
145 	/* try ejecting based on volume first, then based on the mount,
146 	 * and finally try unmounting.
147 	 */
148 	if (volume != NULL) {
149 		if (g_volume_can_eject (volume)) {
150 			rb_debug ("ejecting volume");
151 			g_volume_eject_with_operation (volume,
152 						       G_MOUNT_UNMOUNT_NONE,
153 						       NULL,
154 						       NULL,
155 						       (GAsyncReadyCallback) eject_cb,
156 						       NULL);
157 		} else {
158 			/* this should never happen; the eject command will be
159 			 * insensitive if the selected source cannot be ejected.
160 			 */
161 			rb_debug ("don't know what to do with this volume");
162 		}
163 	} else if (mount != NULL) {
164 		if (g_mount_can_eject (mount)) {
165 			rb_debug ("ejecting mount");
166 			g_mount_eject_with_operation (mount,
167 						      G_MOUNT_UNMOUNT_NONE,
168 						      NULL,
169 						      NULL,
170 						      (GAsyncReadyCallback) eject_cb,
171 						      NULL);
172 		} else if (g_mount_can_unmount (mount)) {
173 			rb_debug ("unmounting mount");
174 			g_mount_unmount_with_operation (mount,
175 							G_MOUNT_UNMOUNT_NONE,
176 							NULL,
177 							NULL,
178 							(GAsyncReadyCallback) unmount_cb,
179 							NULL);
180 		} else {
181 			/* this should never happen; the eject command will be
182 			 * insensitive if the selected source cannot be ejected.
183 			 */
184 			rb_debug ("don't know what to do with this mount");
185 		}
186 	}
187 
188 	if (volume != NULL) {
189 		g_object_unref (volume);
190 	}
191 	if (mount != NULL) {
192 		g_object_unref (mount);
193 	}
194 }
195 
196 /**
197  * rb_device_can_eject:
198  * @source: a #RBDeviceSource
199  *
200  * Checks if the device that the source represents can be ejected.
201  *
202  * Return value: %TRUE if the device can be ejected
203  */
204 gboolean
205 rb_device_source_can_eject (RBDeviceSource *source)
206 {
207 	RBDeviceSourceInterface *iface = RB_DEVICE_SOURCE_GET_IFACE (source);
208 	return iface->can_eject (source);
209 }
210 
211 /**
212  * rb_device_source_eject:
213  * @source: a #RBDeviceSource
214  *
215  * Ejects the device that the source represents.
216  */
217 void
218 rb_device_source_eject (RBDeviceSource *source)
219 {
220 	RBDeviceSourceInterface *iface = RB_DEVICE_SOURCE_GET_IFACE (source);
221 	iface->eject (source);
222 }
223 
224 /**
225  * rb_device_source_want_uri:
226  * @source: a #RBDeviceSource
227  * @uri: a URI to consider
228  *
229  * Checks whether @uri identifies a path underneath the
230  * device's mount point.  Should be used to implement
231  * the #RBSource impl_want_uri method.
232  *
233  * Return value: URI match strength
234  */
235 guint
236 rb_device_source_want_uri (RBSource *source, const char *uri)
237 {
238 	GMount *mount = NULL;
239 	GVolume *volume = NULL;
240 	GFile *file;
241 	char *device_path, *uri_path;
242 	int retval;
243 	int len;
244 
245 	retval = 0;
246 
247 	file = g_file_new_for_uri (uri);
248 
249 	/* Deal with the mount root being passed, eg. file:///media/IPODNAME */
250 	if (g_object_class_find_property (G_OBJECT_GET_CLASS (source), "mount")) {
251 		g_object_get (source, "mount", &mount, NULL);
252 	}
253 	if (mount != NULL) {
254 		GFile *root;
255 
256 		root = g_mount_get_root (mount);
257 		retval = g_file_equal (root, file) ? 100 : 0;
258 		g_object_unref (root);
259 		if (retval) {
260 			g_object_unref (file);
261 			g_object_unref (mount);
262 			return retval;
263 		}
264 		volume = g_mount_get_volume (mount);
265 		g_object_unref (mount);
266 	} else {
267 		if (g_object_class_find_property (G_OBJECT_GET_CLASS (source), "volume")) {
268 			g_object_get (source, "volume", &volume, NULL);
269 		}
270 	}
271 
272 	/* ignore anything that isn't a local file or doesn't have a volume */
273 	if (g_file_has_uri_scheme (file, "file") == FALSE || volume == NULL) {
274 		g_object_unref (file);
275 		return 0;
276 	}
277 
278 	/* Deal with the path to the device node being passed */
279 	device_path = g_volume_get_identifier (volume, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
280 	g_object_unref (volume);
281 	if (device_path == NULL) {
282 		g_object_unref (file);
283 		return 0;
284 	}
285 
286 	uri_path = g_file_get_path (file);
287 	g_object_unref (file);
288 	if (uri_path == NULL)
289 		return 0;
290 	len = strlen (uri_path);
291 	if (uri_path[len - 1] == '/') {
292 		if (strncmp (uri_path, device_path, len - 1) == 0) {
293 			retval = 100;
294 		}
295 	} else if (strcmp (uri_path, device_path) == 0) {
296 		retval = 100;
297 	}
298 
299 	g_free (device_path);
300 	g_free (uri_path);
301 	return retval;
302 }
303 
304 /**
305  * rb_device_source_uri_is_source:
306  * @source: a #RBDeviceSource
307  * @uri: a URI to check
308  *
309  * Returns %TRUE if @uri matches @source.  This should be
310  * used to implement the impl_uri_is_source #RBSource method.
311  *
312  * Return value: %TRUE if @uri matches @source
313  */
314 gboolean
315 rb_device_source_uri_is_source (RBSource *source, const char *uri)
316 {
317 	return (rb_device_source_want_uri (source, uri) == 100);
318 }
319 
320 /**
321  * rb_device_source_set_display_details:
322  * @source: a #RBDeviceSource
323  *
324  * Sets the icon and display name for a device-based source.
325  * The details come from the mount and/or volume.  This should
326  * be called in the source's constructed method.
327  */
328 void
329 rb_device_source_set_display_details (RBDeviceSource *source)
330 {
331 	GMount *mount = NULL;
332 	GVolume *volume = NULL;
333 	GIcon *icon = NULL;
334 	char *display_name;
335 	GdkPixbuf *pixbuf = NULL;
336 
337 	if (g_object_class_find_property (G_OBJECT_GET_CLASS (source), "volume")) {
338 		g_object_get (source, "volume", &volume, NULL);
339 	}
340 	if (g_object_class_find_property (G_OBJECT_GET_CLASS (source), "mount")) {
341 		g_object_get (source, "mount", &mount, NULL);
342 	}
343 
344 	/* prefer mount details to volume details, as the nautilus sidebar does */
345 	if (mount != NULL) {
346 		mount = g_object_ref (mount);
347 	} else if (volume != NULL) {
348 		mount = g_volume_get_mount (volume);
349 	} else {
350 		mount = NULL;
351 	}
352 
353 	if (mount != NULL) {
354 		display_name = g_mount_get_name (mount);
355 		icon = g_mount_get_icon (mount);
356 		rb_debug ("details from mount: display name = %s, icon = %p", display_name, icon);
357 	} else if (volume != NULL) {
358 		display_name = g_volume_get_name (volume);
359 		icon = g_volume_get_icon (volume);
360 		rb_debug ("details from volume: display name = %s, icon = %p", display_name, icon);
361 	} else {
362 		display_name = g_strdup ("Unknown Device");
363 		icon = g_themed_icon_new ("multimedia-player");
364 	}
365 
366 	g_object_set (source, "name", display_name, NULL);
367 	g_free (display_name);
368 
369 	if (icon == NULL) {
370 		rb_debug ("no icon set");
371 		pixbuf = NULL;
372 	} else if (G_IS_THEMED_ICON (icon)) {
373 		GtkIconTheme *theme;
374 		const char * const *names;
375 		gint size;
376 		int i;
377 
378 		theme = gtk_icon_theme_get_default ();
379 		gtk_icon_size_lookup (RB_SOURCE_ICON_SIZE, &size, NULL);
380 
381 		i = 0;
382 		names = g_themed_icon_get_names (G_THEMED_ICON (icon));
383 		while (names[i] != NULL && pixbuf == NULL) {
384 			rb_debug ("looking up themed icon: %s", names[i]);
385 			pixbuf = gtk_icon_theme_load_icon (theme, names[i], size, 0, NULL);
386 			i++;
387 		}
388 
389 	} else if (G_IS_LOADABLE_ICON (icon)) {
390 		rb_debug ("loading of GLoadableIcons is not implemented yet");
391 		pixbuf = NULL;
392 	}
393 
394 	if (pixbuf != NULL) {
395 		g_object_set (source, "pixbuf", pixbuf, NULL);
396 		g_object_unref (pixbuf);
397 	}
398 	if (mount != NULL) {
399 		g_object_unref (mount);
400 	}
401 	if (volume != NULL) {
402 		g_object_unref (volume);
403 	}
404 	if (icon != NULL) {
405 		g_object_unref (icon);
406 	}
407 }
408 
409 static void
410 rb_device_source_default_init (RBDeviceSourceInterface *interface)
411 {
412 	interface->can_eject = default_can_eject;
413 	interface->eject = default_eject;
414 }