No issues found
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 |
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 }