hythmbox-2.98/shell/rb-removable-media-manager.c

No issues found

Incomplete coverage

Tool Failure ID Location Function Message Data
clang-analyzer no-output-found rb-removable-media-manager.c Message(text='Unable to locate XML output from invoke-clang-analyzer') None
Failure running clang-analyzer ('no-output-found')
Message
Unable to locate XML output from invoke-clang-analyzer
  1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
  2 /*
  3  *  Copyright (C) 2005 James Livingston  <doclivingston@gmail.com>
  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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA.
 26  *
 27  */
 28 
 29 /**
 30  * SECTION:rb-removable-media-manager
 31  * @short_description: handling of removable media such as audio CDs and DAP devices
 32  * 
 33  * The removable media manager maintains the mapping between GIO GVolume and GMount
 34  * objects and rhythmbox sources.
 35  */
 36 
 37 #include "config.h"
 38 
 39 #include <string.h>
 40 #include <glib/gi18n.h>
 41 #include <gtk/gtk.h>
 42 #include <gio/gio.h>
 43 
 44 #if defined(HAVE_GUDEV)
 45 #define G_UDEV_API_IS_SUBJECT_TO_CHANGE
 46 #include <gudev/gudev.h>
 47 #endif
 48 
 49 #include "rb-removable-media-manager.h"
 50 #include "rb-library-source.h"
 51 #include "rb-device-source.h"
 52 
 53 #include "rb-shell.h"
 54 #include "rb-shell-player.h"
 55 #include "rb-debug.h"
 56 #include "rb-dialog.h"
 57 #include "rb-stock-icons.h"
 58 #include "rhythmdb.h"
 59 #include "rb-marshal.h"
 60 #include "rb-util.h"
 61 
 62 static void rb_removable_media_manager_class_init (RBRemovableMediaManagerClass *klass);
 63 static void rb_removable_media_manager_init (RBRemovableMediaManager *mgr);
 64 static void rb_removable_media_manager_dispose (GObject *object);
 65 static void rb_removable_media_manager_finalize (GObject *object);
 66 static void rb_removable_media_manager_set_property (GObject *object,
 67 					      guint prop_id,
 68 					      const GValue *value,
 69 					      GParamSpec *pspec);
 70 static void rb_removable_media_manager_get_property (GObject *object,
 71 					      guint prop_id,
 72 					      GValue *value,
 73 					      GParamSpec *pspec);
 74 
 75 static void rb_removable_media_manager_cmd_check_devices (GtkAction *action,
 76 							  RBRemovableMediaManager *manager);
 77 static void rb_removable_media_manager_cmd_eject_medium (GtkAction *action,
 78 					       RBRemovableMediaManager *mgr);
 79 static gboolean rb_removable_media_manager_source_can_eject (RBRemovableMediaManager *mgr);
 80 static void rb_removable_media_manager_set_uimanager (RBRemovableMediaManager *mgr,
 81 					     GtkUIManager *uimanager);
 82 
 83 static void rb_removable_media_manager_append_media_source (RBRemovableMediaManager *mgr, RBSource *source);
 84 
 85 static void rb_removable_media_manager_add_volume (RBRemovableMediaManager *mgr, GVolume *volume);
 86 static void rb_removable_media_manager_remove_volume (RBRemovableMediaManager *mgr, GVolume *volume);
 87 static void rb_removable_media_manager_add_mount (RBRemovableMediaManager *mgr, GMount *mount);
 88 static void rb_removable_media_manager_remove_mount (RBRemovableMediaManager *mgr, GMount *mount);
 89 
 90 static void volume_added_cb (GVolumeMonitor *monitor, GVolume *volume, RBRemovableMediaManager *manager);
 91 static void volume_removed_cb (GVolumeMonitor *monitor, GVolume *volume, RBRemovableMediaManager *manager);
 92 static void mount_added_cb (GVolumeMonitor *monitor, GMount *mount, RBRemovableMediaManager *manager);
 93 static void mount_removed_cb (GVolumeMonitor *monitor, GMount *mount, RBRemovableMediaManager *manager);
 94 #if defined(HAVE_GUDEV)
 95 static void uevent_cb (GUdevClient *client, const char *action, GUdevDevice *device, RBRemovableMediaManager *manager);
 96 #endif
 97 
 98 typedef struct
 99 {
100 	RBShell *shell;
101 
102 	RBSource *selected_source;
103 
104 	GtkActionGroup *actiongroup;
105 	GtkUIManager *uimanager;
106 
107 	GList *sources;
108 	GHashTable *volume_mapping;
109 	GHashTable *mount_mapping;
110 	GHashTable *device_mapping;
111 	gboolean scanned;
112 
113 	GVolumeMonitor *volume_monitor;
114 	guint mount_added_id;
115 	guint mount_pre_unmount_id;
116 	guint mount_removed_id;
117 	guint volume_added_id;
118 	guint volume_removed_id;
119 
120 #if defined(HAVE_GUDEV)
121 	GUdevClient *gudev_client;
122 	guint uevent_id;
123 #endif
124 } RBRemovableMediaManagerPrivate;
125 
126 G_DEFINE_TYPE (RBRemovableMediaManager, rb_removable_media_manager, G_TYPE_OBJECT)
127 #define GET_PRIVATE(o)   (G_TYPE_INSTANCE_GET_PRIVATE ((o), RB_TYPE_REMOVABLE_MEDIA_MANAGER, RBRemovableMediaManagerPrivate))
128 
129 enum
130 {
131 	PROP_0,
132 	PROP_SHELL,
133 	PROP_SOURCE,
134 	PROP_SCANNED
135 };
136 
137 enum
138 {
139 	MEDIUM_ADDED,
140 	CREATE_SOURCE_DEVICE,
141 	CREATE_SOURCE_VOLUME,
142 	CREATE_SOURCE_MOUNT,
143 	LAST_SIGNAL
144 };
145 
146 static guint rb_removable_media_manager_signals[LAST_SIGNAL] = { 0 };
147 
148 static GtkActionEntry rb_removable_media_manager_actions [] =
149 {
150 	{ "RemovableSourceEject", GNOME_MEDIA_EJECT, N_("_Eject"), NULL,
151 	  N_("Eject this medium"),
152 	  G_CALLBACK (rb_removable_media_manager_cmd_eject_medium) },
153 	{ "MusicCheckDevices", NULL, N_("_Check for New Devices"), NULL,
154 	  N_("Check for new media storage devices that have not been automatically detected"),
155 	  G_CALLBACK (rb_removable_media_manager_cmd_check_devices) },
156 };
157 static guint rb_removable_media_manager_n_actions = G_N_ELEMENTS (rb_removable_media_manager_actions);
158 
159 static void
160 rb_removable_media_manager_class_init (RBRemovableMediaManagerClass *klass)
161 {
162 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
163 
164 	object_class->dispose = rb_removable_media_manager_dispose;
165 	object_class->finalize = rb_removable_media_manager_finalize;
166 	object_class->set_property = rb_removable_media_manager_set_property;
167 	object_class->get_property = rb_removable_media_manager_get_property;
168 
169 	/**
170 	 * RBRemovableMediaManager:source:
171 	 *
172 	 * The current selected source.
173 	 */
174 	g_object_class_install_property (object_class,
175 					 PROP_SOURCE,
176 					 g_param_spec_object ("source",
177 							      "RBSource",
178 							      "RBSource object",
179 							      RB_TYPE_SOURCE,
180 							      G_PARAM_READWRITE));
181 	/**
182 	 * RBRemovableMediaManager:shell:
183 	 *
184 	 * The #RBShell instance.
185 	 */
186 	g_object_class_install_property (object_class,
187 					 PROP_SHELL,
188 					 g_param_spec_object ("shell",
189 							      "RBShell",
190 							      "RBShell object",
191 							      RB_TYPE_SHELL,
192 							      G_PARAM_READWRITE));
193 
194 	/**
195 	 * RBRemovableMediaManager:scanned:
196 	 *
197 	 * This is set to TRUE when the removable media manager has scanned
198 	 * all existing volumes and mounts.  When a plugin that handles removable
199 	 * media is activated, it should request a new scan if this property is
200 	 * already set to TRUE.
201 	 */
202 	g_object_class_install_property (object_class,
203 					 PROP_SCANNED,
204 					 g_param_spec_boolean ("scanned",
205 						 	       "scanned",
206 							       "Whether a scan has been performed",
207 							       FALSE,
208 							       G_PARAM_READABLE));
209 
210 	/**
211 	 * RBRemovableMediaManager::medium-added:
212 	 * @mgr: the #RBRemovableMediaManager
213 	 * @source: the newly added #RBSource
214 	 *
215 	 * Emitted when a new source is added for a removable medium.
216 	 */
217 	rb_removable_media_manager_signals[MEDIUM_ADDED] =
218 		g_signal_new ("medium_added",
219 			      RB_TYPE_REMOVABLE_MEDIA_MANAGER,
220 			      G_SIGNAL_RUN_LAST,
221 			      G_STRUCT_OFFSET (RBRemovableMediaManagerClass, medium_added),
222 			      NULL, NULL,
223 			      g_cclosure_marshal_VOID__OBJECT,
224 			      G_TYPE_NONE,
225 			      1, G_TYPE_OBJECT);
226 
227 
228 	/**
229 	 * RBRemovableMediaManager::create-source-device:
230 	 * @mgr: the #RBRemovableMediaManager
231 	 * @device: the device (actually a #GUdevDevice)
232 	 *
233 	 * Emitted when a new device is detected to allow plugins to create a
234 	 * corresponding #RBSource.  The first signal handler that returns a
235 	 * source wins.  Plugins should only use this signal if there will be
236 	 * no #GVolume or #GMount created for the device.
237 	 *
238 	 * Return value: (transfer full): a source for the device, or NULL
239 	 */
240 	rb_removable_media_manager_signals[CREATE_SOURCE_DEVICE] =
241 		g_signal_new ("create-source-device",
242 			      RB_TYPE_REMOVABLE_MEDIA_MANAGER,
243 			      G_SIGNAL_RUN_LAST,
244 			      G_STRUCT_OFFSET (RBRemovableMediaManagerClass, create_source_device),
245 			      rb_signal_accumulator_object_handled, NULL,
246 			      rb_marshal_OBJECT__OBJECT,
247 			      RB_TYPE_SOURCE,
248 			      1, G_TYPE_OBJECT);
249 	/**
250 	 * RBRemovableMediaManager::create-source-volume:
251 	 * @mgr: the #RBRemovableMediaManager
252 	 * @volume: the #GVolume 
253 	 *
254 	 * Emitted when a new volume is added to allow plugins to create a
255 	 * corresponding #RBSource.  The first signal handler that returns
256 	 * a source wins.  A plugin should only use this signal if it
257 	 * doesn't require the volume to be mounted.  If the volume must be
258 	 * mounted to be useful, use the create-source-mount signal instead.
259 	 *
260 	 * Return value: (transfer full): a source for the volume, or NULL
261 	 */
262 	rb_removable_media_manager_signals[CREATE_SOURCE_VOLUME] =
263 		g_signal_new ("create-source-volume",
264 			      RB_TYPE_REMOVABLE_MEDIA_MANAGER,
265 			      G_SIGNAL_RUN_LAST,
266 			      G_STRUCT_OFFSET (RBRemovableMediaManagerClass, create_source_volume),
267 			      rb_signal_accumulator_object_handled, NULL,
268 			      rb_marshal_OBJECT__OBJECT,
269 			      RB_TYPE_SOURCE,
270 			      1, G_TYPE_VOLUME);
271 
272 	/**
273 	 * RBRemovableMediaManager::create-source-mount:
274 	 * @mgr: the #RBRemovableMediaManager
275 	 * @device_info: a #MPIDDevice containing information on the device
276 	 * @mount: the #GMount
277 	 *
278 	 * Emitted when a new mount is added to allow plugins to create a
279 	 * corresponding #RBSource.  The first signal handler that returns
280 	 * a source wins.  If a source was created for the #GVolume
281 	 * for a mount, then this signal will not be emitted.
282 	 *
283 	 * Return value: (transfer full): a source for the mount, or NULL
284 	 */
285 	rb_removable_media_manager_signals[CREATE_SOURCE_MOUNT] =
286 		g_signal_new ("create-source-mount",
287 			      RB_TYPE_REMOVABLE_MEDIA_MANAGER,
288 			      G_SIGNAL_RUN_LAST,
289 			      G_STRUCT_OFFSET (RBRemovableMediaManagerClass, create_source_mount),
290 			      rb_signal_accumulator_object_handled, NULL,
291 			      rb_marshal_OBJECT__OBJECT_OBJECT,
292 			      RB_TYPE_SOURCE,
293 			      2, G_TYPE_MOUNT, MPID_TYPE_DEVICE);
294 
295 	g_type_class_add_private (klass, sizeof (RBRemovableMediaManagerPrivate));
296 }
297 
298 static guint
299 uint64_hash (gconstpointer v)
300 {
301 	return (guint) *(const guint64*)v;
302 }
303 
304 static gboolean
305 uint64_equal (gconstpointer a, gconstpointer b)
306 {
307 	return *((const guint64*)a) == *((const guint64*) b);
308 }
309 
310 static void
311 rb_removable_media_manager_init (RBRemovableMediaManager *mgr)
312 {
313 	RBRemovableMediaManagerPrivate *priv = GET_PRIVATE (mgr);
314 
315 	priv->volume_mapping = g_hash_table_new (NULL, NULL);
316 	priv->mount_mapping = g_hash_table_new (NULL, NULL);
317 	priv->device_mapping = g_hash_table_new_full (uint64_hash, uint64_equal, g_free, NULL);
318 
319 	/*
320 	 * Monitor new (un)mounted file systems to look for new media;
321 	 * we watch for both volumes and mounts because for some devices,
322 	 * we don't require the volume to actually be mounted.
323 	 *
324 	 * both pre-unmount and unmounted callbacks are registered because it is
325 	 * better to do it before the unmount, but sometimes we don't get those
326 	 * (e.g. someone pressing the eject button on a cd drive). If we get the
327 	 * pre-unmount signal, the corresponding unmounted signal is ignored
328 	 */
329 	priv->volume_monitor = g_object_ref (g_volume_monitor_get ());
330 
331 	priv->volume_added_id = g_signal_connect_object (priv->volume_monitor,
332 							 "volume-added",
333 							 G_CALLBACK (volume_added_cb),
334 							 mgr, 0);
335 	priv->volume_removed_id = g_signal_connect_object (priv->volume_monitor,
336 							   "volume-removed",
337 							   G_CALLBACK (volume_removed_cb),
338 							   mgr, 0);
339 	priv->mount_added_id = g_signal_connect_object (priv->volume_monitor,
340 							"mount-added",
341 							G_CALLBACK (mount_added_cb),
342 							mgr, 0);
343 	priv->mount_pre_unmount_id = g_signal_connect_object (priv->volume_monitor,
344 							      "mount-pre-unmount",
345 							      G_CALLBACK (mount_removed_cb),
346 							      mgr, 0);
347 	priv->mount_removed_id = g_signal_connect_object (G_OBJECT (priv->volume_monitor),
348 							  "mount-removed",
349 							  G_CALLBACK (mount_removed_cb),
350 							  mgr, 0);
351 
352 #if defined(HAVE_GUDEV)
353 	/*
354 	 * Monitor udev device events - we're only really interested in events
355 	 * for USB devices.
356 	 */
357 	{
358 		const char * const subsystems[] = { "usb", NULL };
359 		priv->gudev_client = g_udev_client_new (subsystems);
360 	}
361 
362 	priv->uevent_id = g_signal_connect_object (priv->gudev_client,
363 						   "uevent",
364 						   G_CALLBACK (uevent_cb),
365 						   mgr, 0);
366 #endif
367 
368 	/* enable debugging of media player device lookups if requested */
369 	if (rb_debug_matches ("mpid", "")) {
370 		mpid_enable_debug (TRUE);
371 	}
372 }
373 
374 static void
375 rb_removable_media_manager_dispose (GObject *object)
376 {
377 	RBRemovableMediaManager *mgr = RB_REMOVABLE_MEDIA_MANAGER (object);
378 	RBRemovableMediaManagerPrivate *priv = GET_PRIVATE (mgr);
379 
380 	if (priv->volume_monitor != NULL) {
381 		g_signal_handler_disconnect (priv->volume_monitor,
382 					     priv->mount_added_id);
383 		g_signal_handler_disconnect (priv->volume_monitor,
384 					     priv->mount_pre_unmount_id);
385 		g_signal_handler_disconnect (priv->volume_monitor,
386 					     priv->mount_removed_id);
387 		g_signal_handler_disconnect (priv->volume_monitor,
388 					     priv->volume_added_id);
389 		g_signal_handler_disconnect (priv->volume_monitor,
390 					     priv->volume_removed_id);
391 
392 		priv->mount_added_id = 0;
393 		priv->mount_pre_unmount_id = 0;
394 		priv->mount_removed_id = 0;
395 		priv->volume_added_id = 0;
396 		priv->volume_removed_id = 0;
397 
398 		g_object_unref (priv->volume_monitor);
399 		priv->volume_monitor = NULL;
400 	}
401 
402 #if defined(HAVE_GUDEV)
403 	if (priv->gudev_client != NULL) {
404 		g_signal_handler_disconnect (priv->gudev_client,
405 					     priv->uevent_id);
406 		priv->uevent_id = 0;
407 
408 		g_object_unref (priv->gudev_client);
409 		priv->gudev_client = NULL;
410 	}
411 #endif
412 
413 	if (priv->sources) {
414 		g_list_free (priv->sources);
415 		priv->sources = NULL;
416 	}
417 
418 	G_OBJECT_CLASS (rb_removable_media_manager_parent_class)->dispose (object);
419 }
420 
421 static void
422 rb_removable_media_manager_finalize (GObject *object)
423 {
424 	RBRemovableMediaManagerPrivate *priv = GET_PRIVATE (object);
425 
426 	g_hash_table_destroy (priv->device_mapping);
427 	g_hash_table_destroy (priv->volume_mapping);
428 	g_hash_table_destroy (priv->mount_mapping);
429 
430 	G_OBJECT_CLASS (rb_removable_media_manager_parent_class)->finalize (object);
431 }
432 
433 static void
434 rb_removable_media_manager_set_property (GObject *object,
435 				  guint prop_id,
436 				  const GValue *value,
437 				  GParamSpec *pspec)
438 {
439 	RBRemovableMediaManagerPrivate *priv = GET_PRIVATE (object);
440 
441 	switch (prop_id)
442 	{
443 	case PROP_SOURCE:
444 	{
445 		GtkAction *action;
446 		gboolean can_eject;
447 
448 		priv->selected_source = g_value_get_object (value);
449 		/* make 'eject' command sensitive if the source can be ejected. */
450 		action = gtk_action_group_get_action (priv->actiongroup, "RemovableSourceEject");
451 		can_eject = rb_removable_media_manager_source_can_eject (RB_REMOVABLE_MEDIA_MANAGER (object));
452 		gtk_action_set_sensitive (action, can_eject);
453 		break;
454 	}
455 	case PROP_SHELL:
456 	{
457 		GtkUIManager *uimanager;
458 
459 		priv->shell = g_value_get_object (value);
460 		g_object_get (priv->shell,
461 			      "ui-manager", &uimanager,
462 			      NULL);
463 		rb_removable_media_manager_set_uimanager (RB_REMOVABLE_MEDIA_MANAGER (object), uimanager);
464 		g_object_unref (uimanager);
465 		break;
466 	}
467 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
468 		break;
469 	}
470 }
471 
472 static void
473 rb_removable_media_manager_get_property (GObject *object,
474 			      guint prop_id,
475 			      GValue *value,
476 			      GParamSpec *pspec)
477 {
478 	RBRemovableMediaManagerPrivate *priv = GET_PRIVATE (object);
479 
480 	switch (prop_id)
481 	{
482 	case PROP_SOURCE:
483 		g_value_set_object (value, priv->selected_source);
484 		break;
485 	case PROP_SHELL:
486 		g_value_set_object (value, priv->shell);
487 		break;
488 	case PROP_SCANNED:
489 		g_value_set_boolean (value, priv->scanned);
490 		break;
491 	default:
492 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
493 		break;
494 	}
495 }
496 
497 /**
498  * rb_removable_media_manager_new:
499  * @shell: the #RBShell
500  *
501  * Creates the #RBRemovableMediaManager instance.
502  *
503  * Return value: the #RBRemovableMediaManager
504  */
505 RBRemovableMediaManager *
506 rb_removable_media_manager_new (RBShell *shell)
507 {
508 	return g_object_new (RB_TYPE_REMOVABLE_MEDIA_MANAGER,
509 			     "shell", shell,
510 			     NULL);
511 }
512 
513 static void
514 volume_added_cb (GVolumeMonitor *monitor,
515 		 GVolume *volume,
516 		 RBRemovableMediaManager *mgr)
517 {
518 	GDK_THREADS_ENTER ();
519 	rb_removable_media_manager_add_volume (mgr, volume);
520 	GDK_THREADS_LEAVE ();
521 }
522 
523 static void
524 volume_removed_cb (GVolumeMonitor *monitor,
525 		   GVolume *volume,
526 		   RBRemovableMediaManager *mgr)
527 {
528 	GDK_THREADS_ENTER ();
529 	rb_removable_media_manager_remove_volume (mgr, volume);
530 	GDK_THREADS_LEAVE ();
531 }
532 
533 static void
534 mount_added_cb (GVolumeMonitor *monitor,
535 		GMount *mount,
536 		RBRemovableMediaManager *mgr)
537 {
538 	GDK_THREADS_ENTER ();
539 	rb_removable_media_manager_add_mount (mgr, mount);
540 	GDK_THREADS_LEAVE ();
541 }
542 
543 static void
544 mount_removed_cb (GVolumeMonitor *monitor,
545 		  GMount *mount,
546 		  RBRemovableMediaManager *mgr)
547 {
548 	rb_removable_media_manager_remove_mount (mgr, mount);
549 }
550 
551 #if defined(HAVE_GUDEV)
552 
553 static void
554 uevent_cb (GUdevClient *client, const char *action, GUdevDevice *device, RBRemovableMediaManager *mgr)
555 {
556 	RBRemovableMediaManagerPrivate *priv = GET_PRIVATE (mgr);
557 	guint64 devnum;
558 
559 	GDK_THREADS_ENTER ();
560 
561 	devnum = (guint64) g_udev_device_get_device_number (device);
562 	rb_debug ("%s event for %s (%"G_GINT64_MODIFIER"x)", action,
563                   g_udev_device_get_sysfs_path (device), devnum);
564 
565 	if (g_str_equal (action, "add")) {
566 		RBSource *source = NULL;
567 
568 		/* probably need to filter out devices related to things we've already seen.. */
569 		if (g_hash_table_lookup (priv->device_mapping, &devnum) != NULL) {
570 			rb_debug ("already have a source for this device");
571 		} else {
572 			g_signal_emit (mgr, rb_removable_media_manager_signals[CREATE_SOURCE_DEVICE], 0, device, &source);
573 			if (source != NULL) {
574 				guint64 *key = g_new0 (guint64, 1);
575 				rb_debug ("created a source for this device");
576 				key[0] = devnum;
577 				g_hash_table_insert (priv->device_mapping, key, source);
578 				rb_removable_media_manager_append_media_source (mgr, source);
579 			}
580 		}
581 	} else if (g_str_equal (action, "remove")) {
582 		RBSource *source;
583 
584 		source = g_hash_table_lookup (priv->device_mapping, &devnum);
585 		if (source) {
586 			rb_debug ("removing the source created for this device");
587 			rb_display_page_delete_thyself (RB_DISPLAY_PAGE (source));
588 		}
589 	}
590 
591 	GDK_THREADS_LEAVE ();
592 }
593 #endif
594 
595 static gboolean
596 remove_by_source (gpointer thing, RBSource *source, RBSource *ref_source)
597 {
598 	return (ref_source == source);
599 }
600 
601 static void
602 rb_removable_media_manager_source_deleted_cb (RBSource *source, RBRemovableMediaManager *mgr)
603 {
604 	RBRemovableMediaManagerPrivate *priv = GET_PRIVATE (mgr);
605 
606 	rb_debug ("removing source %p", source);
607 	g_hash_table_foreach_remove (priv->volume_mapping,
608 				     (GHRFunc)remove_by_source,
609 				     source);
610 	g_hash_table_foreach_remove (priv->mount_mapping,
611 				     (GHRFunc)remove_by_source,
612 				     source);
613 	g_hash_table_foreach_remove (priv->device_mapping,
614 				     (GHRFunc)remove_by_source,
615 				     source);
616 	priv->sources = g_list_remove (priv->sources, source);
617 }
618 
619 static void
620 dump_volume_identifiers (GVolume *volume)
621 {
622 	char **identifiers;
623 	int i;
624 
625 	if (volume == NULL) {
626 		rb_debug ("mount has no volume");
627 		return;
628 	}
629 
630 	/* dump all volume identifiers in debug output */
631 	identifiers = g_volume_enumerate_identifiers (volume);
632 	if (identifiers != NULL) {
633 		for (i = 0; identifiers[i] != NULL; i++) {
634 			char *ident;
635 
636 			ident = g_volume_get_identifier (volume, identifiers[i]);
637 			rb_debug ("%s = %s", identifiers[i], ident);
638 		}
639 		g_strfreev (identifiers);
640 	}
641 }
642 
643 static void
644 rb_removable_media_manager_add_volume (RBRemovableMediaManager *mgr, GVolume *volume)
645 {
646 	RBRemovableMediaManagerPrivate *priv = GET_PRIVATE (mgr);
647 	RBSource *source = NULL;
648 	GMount *mount;
649 
650 	g_assert (volume != NULL);
651 
652 	if (g_hash_table_lookup (priv->volume_mapping, volume) != NULL) {
653 		return;
654 	}
655 
656 	mount = g_volume_get_mount (volume);
657 	if (mount != NULL) {
658 		if (g_mount_is_shadowed (mount) != FALSE) {
659 			rb_debug ("mount is shadowed, so ignoring the volume");
660 			g_object_unref (mount);
661 			return;
662 		}
663 		if (g_hash_table_lookup (priv->mount_mapping, mount) != NULL) {
664 			/* this can probably never happen, but it's OK */
665 			rb_debug ("already created a source for the mount, so ignoring the volume");
666 			g_object_unref (mount);
667 			return;
668 		}
669 		g_object_unref (mount);
670 	}
671 
672 	dump_volume_identifiers (volume);
673 
674 	g_signal_emit (G_OBJECT (mgr), rb_removable_media_manager_signals[CREATE_SOURCE_VOLUME], 0, volume, &source);
675 
676 	if (source) {
677 		g_hash_table_insert (priv->volume_mapping, volume, source);
678 		rb_removable_media_manager_append_media_source (mgr, source);
679 	} else {
680 		rb_debug ("Unhandled media");
681 	}
682 }
683 
684 static void
685 rb_removable_media_manager_remove_volume (RBRemovableMediaManager *mgr, GVolume *volume)
686 {
687 	RBRemovableMediaManagerPrivate *priv = GET_PRIVATE (mgr);
688 	RBSource *source;
689 
690 	g_assert (volume != NULL);
691 
692 	rb_debug ("volume removed");
693 	source = g_hash_table_lookup (priv->volume_mapping, volume);
694 	if (source) {
695 		rb_display_page_delete_thyself (RB_DISPLAY_PAGE (source));
696 	}
697 }
698 
699 static void
700 rb_removable_media_manager_add_mount (RBRemovableMediaManager *mgr, GMount *mount)
701 {
702 	RBRemovableMediaManagerPrivate *priv = GET_PRIVATE (mgr);
703 	RBSource *source = NULL;
704 	GVolume *volume;
705 	GFile *mount_root;
706 	char *mountpoint;
707 	MPIDDevice *device_info;
708 
709 	g_assert (mount != NULL);
710 
711 	if (g_hash_table_lookup (priv->mount_mapping, mount) != NULL) {
712 		return;
713 	}
714 	if (g_mount_is_shadowed (mount) != FALSE) {
715 		return;
716 	}
717 	volume = g_mount_get_volume (mount);
718 	if (volume == NULL) {
719 		rb_debug ("Unhandled media, no volume for mount");
720 		return;
721 	}
722 
723 	/* if we've already created a source for the volume,
724 	 * don't do anything with the mount.
725 	 */
726 	if (g_hash_table_lookup (priv->volume_mapping, volume) != NULL) {
727 		rb_debug ("already created a source for the volume, so ignoring the mount");
728 		g_object_unref (volume);
729 		return;
730 	}
731 
732 	dump_volume_identifiers (volume);
733 	g_object_unref (volume);
734 
735 	/* look the device up in the device info database */
736 	mount_root = g_mount_get_root (mount);
737 	if (mount_root == NULL) {
738 		rb_debug ("unable to get mount root, can't create a source for this mount");
739 		return;
740 	}
741 	mountpoint = g_file_get_path (mount_root);
742 	g_object_unref (mount_root);
743 
744 	device_info = mpid_device_new (mountpoint);
745 	g_free (mountpoint);
746 
747 	g_signal_emit (G_OBJECT (mgr), rb_removable_media_manager_signals[CREATE_SOURCE_MOUNT], 0, mount, device_info, &source);
748 
749 	if (source) {
750 		g_hash_table_insert (priv->mount_mapping, mount, source);
751 		rb_removable_media_manager_append_media_source (mgr, source);
752 	} else {
753 		rb_debug ("Unhandled media");
754 	}
755 
756 	g_object_unref (device_info);
757 }
758 
759 static void
760 rb_removable_media_manager_remove_mount (RBRemovableMediaManager *mgr, GMount *mount)
761 {
762 	RBRemovableMediaManagerPrivate *priv = GET_PRIVATE (mgr);
763 	RBSource *source;
764 
765 	g_assert (mount != NULL);
766 
767 	rb_debug ("mount removed");
768 	source = g_hash_table_lookup (priv->mount_mapping, mount);
769 	if (source) {
770 		rb_display_page_delete_thyself (RB_DISPLAY_PAGE (source));
771 	}
772 }
773 
774 static void
775 rb_removable_media_manager_append_media_source (RBRemovableMediaManager *mgr, RBSource *source)
776 {
777 	RBRemovableMediaManagerPrivate *priv = GET_PRIVATE (mgr);
778 
779 	priv->sources = g_list_prepend (priv->sources, source);
780 	g_signal_connect_object (G_OBJECT (source), "deleted",
781 				 G_CALLBACK (rb_removable_media_manager_source_deleted_cb), mgr, 0);
782 
783 	g_signal_emit (G_OBJECT (mgr), rb_removable_media_manager_signals[MEDIUM_ADDED], 0,
784 		       source);
785 }
786 
787 static void
788 rb_removable_media_manager_set_uimanager (RBRemovableMediaManager *mgr,
789 					  GtkUIManager *uimanager)
790 {
791 	RBRemovableMediaManagerPrivate *priv = GET_PRIVATE (mgr);
792 
793 	if (priv->uimanager != NULL) {
794 		if (priv->actiongroup != NULL) {
795 			gtk_ui_manager_remove_action_group (priv->uimanager,
796 							    priv->actiongroup);
797 		}
798 		g_object_unref (G_OBJECT (priv->uimanager));
799 		priv->uimanager = NULL;
800 	}
801 
802 	priv->uimanager = uimanager;
803 
804 	if (priv->uimanager != NULL) {
805 		g_object_ref (priv->uimanager);
806 	}
807 
808 	if (priv->actiongroup == NULL) {
809 		priv->actiongroup = gtk_action_group_new ("RemovableMediaActions");
810 		gtk_action_group_set_translation_domain (priv->actiongroup,
811 							 GETTEXT_PACKAGE);
812 		gtk_action_group_add_actions (priv->actiongroup,
813 					      rb_removable_media_manager_actions,
814 					      rb_removable_media_manager_n_actions,
815 					      mgr);
816 	}
817 
818 	gtk_ui_manager_insert_action_group (priv->uimanager,
819 					    priv->actiongroup,
820 					    0);
821 }
822 
823 static gboolean
824 rb_removable_media_manager_source_can_eject (RBRemovableMediaManager *mgr)
825 {
826 	RBRemovableMediaManagerPrivate *priv = GET_PRIVATE (mgr);
827 
828 	if (RB_IS_DEVICE_SOURCE (priv->selected_source) == FALSE) {
829 		return FALSE;
830 	}
831 	return rb_device_source_can_eject (RB_DEVICE_SOURCE (priv->selected_source));
832 }
833 
834 static void
835 rb_removable_media_manager_cmd_eject_medium (GtkAction *action, RBRemovableMediaManager *mgr)
836 {
837 	RBRemovableMediaManagerPrivate *priv = GET_PRIVATE (mgr);
838 	if (RB_IS_DEVICE_SOURCE (priv->selected_source)) {
839 		rb_device_source_eject (RB_DEVICE_SOURCE (priv->selected_source));
840 	}
841 }
842 
843 static void
844 rb_removable_media_manager_cmd_check_devices (GtkAction *action, RBRemovableMediaManager *manager)
845 {
846 	rb_removable_media_manager_scan (manager);
847 }
848 
849 /**
850  * rb_removable_media_manager_scan:
851  * @manager: the #RBRemovableMediaManager
852  *
853  * Initiates a new scan of all attached media.  Newly activated plugins that use
854  * the create-source-volume or create-source-mount signals should call this if
855  * the 'scanned' property is %TRUE.  Otherwise, the first scan will catch any
856  * existing volumes or mounts that the plugin is interested in.
857  */
858 void
859 rb_removable_media_manager_scan (RBRemovableMediaManager *manager)
860 {
861 	RBRemovableMediaManagerPrivate *priv = GET_PRIVATE (manager);
862 	GHashTableIter iter;
863 	GList *list, *it;
864 	gpointer hkey, hvalue;
865 
866 	priv->scanned = TRUE;
867 
868 	/* check volumes first */
869 	list = g_volume_monitor_get_volumes (priv->volume_monitor);
870 
871 	/* - check for volumes that have disappeared */
872 	g_hash_table_iter_init (&iter, priv->volume_mapping);
873 	while (g_hash_table_iter_next (&iter, &hkey, &hvalue)) {
874 		GVolume *volume = G_VOLUME (hkey);
875 
876 		if (g_list_index (list, volume) == -1) {
877 			/* volume has vanished */
878 			rb_removable_media_manager_remove_volume (manager, volume);
879 		}
880 	}
881 
882 	/* - check for newly added volumes */
883 	for (it = list; it != NULL; it = g_list_next (it)) {
884 		GVolume *volume = G_VOLUME (it->data);
885 		rb_removable_media_manager_add_volume (manager, volume);
886 		g_object_unref (volume);
887 	}
888 	g_list_free (list);
889 
890 	/* check mounts */
891 	list = g_volume_monitor_get_mounts (priv->volume_monitor);
892 
893 	/* - check for mounts that have disappeared */
894 	g_hash_table_iter_init (&iter, priv->mount_mapping);
895 	while (g_hash_table_iter_next (&iter, &hkey, &hvalue)) {
896 		GMount *mount = G_MOUNT (hkey);
897 
898 		if (g_list_index (list, mount) == -1) {
899 			rb_removable_media_manager_remove_mount (manager, mount);
900 		}
901 	}
902 
903 	/* - check for newly added mounts */
904 	for (it = list; it != NULL; it = g_list_next (it)) {
905 		GMount *mount = G_MOUNT (it->data);
906 		rb_removable_media_manager_add_mount (manager, mount);
907 		g_object_unref (mount);
908 	}
909 	g_list_free (list);
910 
911 	/* - check devices */
912 #if defined(HAVE_GUDEV)
913 	list = g_udev_client_query_by_subsystem (priv->gudev_client, "usb");
914 	for (it = list; it != NULL; it = g_list_next (it)) {
915 		/* pretend the device was just added */
916 		uevent_cb (priv->gudev_client, "add", G_UDEV_DEVICE (it->data), manager);
917 	}
918 	g_list_free (list);
919 #endif
920 }