hythmbox-2.98/plugins/brasero-disc-recorder/rb-disc-recorder-plugin.c

No issues found

  1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
  2  *
  3  *  Copyright (C) 2006 William Jon McCann <mccann@jhu.edu>
  4  *  Copyright (C) 2008 Rouquier Philippe <bonfire-app@wanadoo.fr>
  5  *
  6  * This program is free software; you can redistribute it and/or modify
  7  * it under the terms of the GNU General Public License as published by
  8  * the Free Software Foundation; either version 2, or (at your option)
  9  * any later version.
 10  *
 11  * The Rhythmbox authors hereby grant permission for non-GPL compatible
 12  * GStreamer plugins to be used and distributed together with GStreamer
 13  * and Rhythmbox. This permission is above and beyond the permissions granted
 14  * by the GPL license by which Rhythmbox is covered. If you modify this code
 15  * you may extend this exception to your version of the code, but you are not
 16  * obligated to do so. If you do not wish to do so, delete this exception
 17  * statement from your version.
 18  *
 19  * This program is distributed in the hope that it will be useful,
 20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 22  * GNU General Public License for more details.
 23  *
 24  * You should have received a copy of the GNU General Public License
 25  * along with this program; if not, write to the Free Software
 26  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA.
 27  */
 28 
 29 #define __EXTENSIONS__
 30 
 31 #include "config.h"
 32 
 33 #include <errno.h>
 34 #include <string.h> /* For strlen */
 35 #include <unistd.h>
 36 #include <glib/gi18n-lib.h>
 37 #include <gmodule.h>
 38 #include <gtk/gtk.h>
 39 #include <glib.h>
 40 #include <glib-object.h>
 41 #include <glib/gstdio.h>
 42 #include <gdk/gdkx.h>
 43 #include <brasero-media.h>
 44 #include <brasero-medium-monitor.h>
 45 
 46 #include <libxml/xmlerror.h>
 47 #include <libxml/xmlwriter.h>
 48 #include <libxml/parser.h>
 49 #include <libxml/xmlstring.h>
 50 #include <libxml/uri.h>
 51 #include <libxml/xmlsave.h>
 52 
 53 #include "rb-plugin-macros.h"
 54 #include "rb-debug.h"
 55 #include "rb-shell.h"
 56 #include "rb-source.h"
 57 #include "rb-playlist-source.h"
 58 #include "rb-dialog.h"
 59 #include "rb-file-helpers.h"
 60 
 61 #define RB_TYPE_DISC_RECORDER_PLUGIN		(rb_disc_recorder_plugin_get_type ())
 62 #define RB_DISC_RECORDER_PLUGIN(o)		(G_TYPE_CHECK_INSTANCE_CAST ((o), RB_TYPE_DISC_RECORDER_PLUGIN, RBDiscRecorderPlugin))
 63 #define RB_DISC_RECORDER_PLUGIN_CLASS(k)	(G_TYPE_CHECK_CLASS_CAST((k), RB_TYPE_DISC_RECORDER_PLUGIN, RBDiscRecorderPluginClass))
 64 #define RB_IS_DISC_RECORDER_PLUGIN(o)		(G_TYPE_CHECK_INSTANCE_TYPE ((o), RB_TYPE_DISC_RECORDER_PLUGIN))
 65 #define RB_IS_DISC_RECORDER_PLUGIN_CLASS(k)	(G_TYPE_CHECK_CLASS_TYPE ((k), RB_TYPE_DISC_RECORDER_PLUGIN))
 66 #define RB_DISC_RECORDER_PLUGIN_GET_CLASS(o)	(G_TYPE_INSTANCE_GET_CLASS ((o), RB_TYPE_DISC_RECORDER_PLUGIN, RBDiscRecorderPluginClass))
 67 
 68 typedef struct
 69 {
 70 	PeasExtensionBase parent;
 71 
 72 	GtkActionGroup *action_group;
 73 	guint           ui_merge_id;
 74 
 75 	RBDisplayPage  *selected_page;
 76 	guint           enabled : 1;
 77 } RBDiscRecorderPlugin;
 78 
 79 typedef struct
 80 {
 81 	PeasExtensionBaseClass parent_class;
 82 } RBDiscRecorderPluginClass;
 83 
 84 G_MODULE_EXPORT void peas_register_types (PeasObjectModule *module);
 85 
 86 RB_DEFINE_PLUGIN(RB_TYPE_DISC_RECORDER_PLUGIN, RBDiscRecorderPlugin, rb_disc_recorder_plugin,)
 87 
 88 static void rb_disc_recorder_plugin_init (RBDiscRecorderPlugin *plugin);
 89 static void cmd_burn_source (GtkAction          *action,
 90 			     RBDiscRecorderPlugin *pi);
 91 static void cmd_duplicate_cd (GtkAction          *action,
 92 			      RBDiscRecorderPlugin *pi);
 93 
 94 static GtkActionEntry rb_disc_recorder_plugin_actions [] = {
 95 	{ "MusicPlaylistBurnToDiscPlaylist", "media-optical-audio-new", N_("_Create Audio CD..."), NULL,
 96 	  N_("Create an audio CD from playlist"),
 97 	  G_CALLBACK (cmd_burn_source) },
 98 	{ "MusicAudioCDDuplicate", "media-optical-copy", N_("Duplicate Audio CD..."), NULL,
 99 	  N_("Create a copy of this audio CD"),
100 	  G_CALLBACK (cmd_duplicate_cd) },
101 };
102 
103 #define RB_RECORDER_ERROR rb_disc_recorder_error_quark ()
104 
105 GQuark rb_disc_recorder_error_quark (void);
106 
107 GQuark
108 rb_disc_recorder_error_quark (void)
109 {
110         static GQuark quark = 0;
111         if (! quark) {
112                 quark = g_quark_from_static_string ("rb_disc_recorder_error");
113         }
114 
115         return quark;
116 }
117 
118 typedef enum
119 {
120         RB_RECORDER_ERROR_NONE			= 0,
121 	RB_RECORDER_ERROR_GENERAL
122 } RBRecorderError;
123 
124 static void
125 rb_disc_recorder_plugin_init (RBDiscRecorderPlugin *pi)
126 {
127 	rb_debug ("RBDiscRecorderPlugin initialized");
128 }
129 
130 static gboolean
131 rb_disc_recorder_plugin_start_burning (RBDiscRecorderPlugin *pi,
132 					  const char *path,
133 					  gboolean copy)
134 {
135 	GtkWidget *main_window;
136 	GdkWindow *window;
137 	GPtrArray *array;
138 	char **args, *xid_str;
139 	GError *error = NULL;
140 	gboolean ret;
141 	RBShell *shell;
142 	
143 	array = g_ptr_array_new ();
144 	g_ptr_array_add (array, "brasero");
145 	if (copy != FALSE)
146 		g_ptr_array_add (array, "-c");
147 	else
148 		g_ptr_array_add (array, "-r");
149 	g_ptr_array_add (array, (gpointer) path);
150 
151 	g_object_get (pi, "object", &shell, NULL);
152 	g_object_get (shell, "window", &main_window, NULL);
153 	g_object_unref (shell);
154 
155 	window = gtk_widget_get_window (main_window);
156 	if (window) {
157 		int xid;
158 		xid = gdk_x11_window_get_xid (window);
159 		xid_str = g_strdup_printf ("%d", xid);
160 		g_ptr_array_add (array, "-x");
161 		g_ptr_array_add (array, xid_str);
162 	} else {
163 		xid_str = NULL;
164 	}
165 	
166 	g_ptr_array_add (array, NULL);
167 	args = (char **) g_ptr_array_free (array, FALSE);
168 
169 	ret = TRUE;
170 	if (!g_spawn_async (NULL, args, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, &error)) {
171 		if (copy != FALSE) {
172 			rb_error_dialog (GTK_WINDOW (main_window),
173 					 _("Rhythmbox could not duplicate the disc"),
174 					 "%s", error->message);
175 
176 		} else {
177 			rb_error_dialog (GTK_WINDOW (main_window),
178 					 _("Rhythmbox could not record the audio disc"),
179 					 "%s", error->message);
180 		}
181 		ret = FALSE;
182 		g_error_free (error);
183 	}
184 	
185 	g_free (xid_str);
186 	g_free (args);
187 	
188 	return ret;
189 }
190 
191 static gchar*
192 rb_disc_recorder_plugin_write_audio_project (const gchar 	*name,
193 					     GtkTreeModel       *model,
194 					     GError		**error)
195 {
196         GtkTreeIter iter;
197 	xmlTextWriter *project;
198 	xmlDocPtr doc = NULL;
199 	xmlSaveCtxt *save;
200 	gint success;
201     	gchar *path;
202 	int fd;
203 	int use_errno = 0;
204 
205         if (! gtk_tree_model_get_iter_first (model, &iter)) {
206                 g_set_error (error,
207                              RB_RECORDER_ERROR,
208                              RB_RECORDER_ERROR_GENERAL,
209                              _("Unable to build an audio track list"));
210                 return NULL;
211         }
212 
213 	/* get a temporary path */
214 	path = g_build_filename (g_get_tmp_dir (), "brasero-tmp-project-XXXXXX",  NULL);
215 	fd = g_mkstemp (path);
216 	if (fd == -1) {
217 		g_set_error (error,
218                  	     RB_RECORDER_ERROR,
219                      	     RB_RECORDER_ERROR_GENERAL,
220                     	     _("Unable to write audio project file %s: %s"),
221 			     path,
222 			     g_strerror (errno));
223 		rb_debug ("g_mkstemp failed");
224 
225 		g_free (path);
226 		return NULL;
227 	}
228 
229 	project = xmlNewTextWriterDoc (&doc, 0);
230 	if (!project) {
231 		g_remove (path);
232 		g_free (path);
233 		close (fd);
234 
235 		g_set_error (error,
236                  	     RB_RECORDER_ERROR,
237                      	     RB_RECORDER_ERROR_GENERAL,
238                     	     _("Unable to write audio project"));
239 
240 		return NULL;
241 	}
242 
243 	xmlTextWriterSetIndent (project, 1);
244 	xmlTextWriterSetIndentString (project, (xmlChar *) "\t");
245 
246 	success = xmlTextWriterStartDocument (project,
247 					      NULL,
248 					      "UTF8",
249 					      NULL);
250 	if (success < 0)
251 		goto error;
252 
253 	success = xmlTextWriterStartElement (project, (xmlChar *) "braseroproject");
254 	if (success < 0)
255 		goto error;
256 
257 	/* write the name of the version */
258 	success = xmlTextWriterWriteElement (project,
259 					     (xmlChar *) "version",
260 					     (xmlChar *) "0.2");
261 	if (success < 0)
262 		goto error;
263 
264 	if (name) {
265 		success = xmlTextWriterWriteElement (project,
266 						     (xmlChar *) "label",
267 						     (xmlChar *) name);
268 		if (success < 0)
269 			goto error;
270 	}
271 
272 	success = xmlTextWriterStartElement (project, (xmlChar *) "track");
273 	if (success < 0)
274 		goto error;
275 
276         do {
277 		RhythmDBEntry  *entry;
278 		const char *str;
279 		xmlChar *escaped;
280 
281 		success = xmlTextWriterStartElement (project, (xmlChar *) "audio");
282 		if (success < 0)
283 			goto error;
284 
285 		gtk_tree_model_get (model, &iter, 0, &entry, -1);
286 
287 		str = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION);
288 		escaped = (xmlChar *) g_uri_escape_string (str, NULL, FALSE);
289 		success = xmlTextWriterWriteElement (project,
290 						    (xmlChar *) "uri",
291 						     escaped);
292 		g_free (escaped);
293 
294 		if (success == -1)
295 			goto error;
296 
297 		/* start of the song always 0 */
298 		success = xmlTextWriterWriteElement (project,
299 						     (xmlChar *) "start",
300 						     (xmlChar *) "0");
301 		if (success == -1)
302 			goto error;
303 
304 		/* end of the song = duration (in seconds while brasero likes it
305 		 * in nanoseconds =( ) */
306 		/* Disable this for the moment and let brasero check the size
307 		 * itself. In case the user chooses on the fly burning we need
308 		 * a more precise duration or we'd end up burning the track
309 		 * incompletely or with a big padding */
310 		/*
311 		end = g_strdup_printf ("%"G_GINT64_FORMAT, (gint64) (song->duration * 1000000000LL));
312 		success = xmlTextWriterWriteElement (project,
313 						     (xmlChar *) "end",
314 						     (xmlChar *) end);
315 
316 		g_free (end);
317 		if (success == -1)
318 			goto error;
319 		*/
320 
321 		str = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_TITLE);
322 		if (str) {
323 			escaped = (xmlChar *) g_uri_escape_string (str, NULL, FALSE);
324 			success = xmlTextWriterWriteElement (project,
325 							    (xmlChar *) "title",
326 							     escaped);
327 			g_free (escaped);
328 
329 			if (success == -1)
330 				goto error;
331 		}
332 
333 		str = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ARTIST);
334 		if (str) {
335 			escaped = (xmlChar *) g_uri_escape_string (str, NULL, FALSE);
336 			success = xmlTextWriterWriteElement (project,
337 							    (xmlChar *) "artist",
338 							     escaped);
339 			g_free (escaped);
340 
341 			if (success == -1)
342 				goto error;
343 		}
344 
345 		/*
346 		if (song->composer) {
347 			escaped = (unsigned char *) g_uri_escape_string (song->composer, NULL, FALSE);
348 			success = xmlTextWriterWriteElement (project,
349 							    (xmlChar *) "composer",
350 							     escaped);
351 			g_free (escaped);
352 
353 			if (success == -1)
354 				goto error;
355 		}
356 		*/
357 
358 		success = xmlTextWriterEndElement (project); /* audio */
359 		if (success < 0)
360 			goto error;
361         } while (gtk_tree_model_iter_next (model, &iter));
362 
363 	success = xmlTextWriterEndElement (project); /* track */
364 	if (success < 0)
365 		goto error;
366 
367 	success = xmlTextWriterEndElement (project); /* braseroproject */
368 	if (success < 0)
369 		goto error;
370 
371 	success = xmlTextWriterEndDocument (project);
372 	if (success < 0)
373 		goto end_error;
374 
375 	xmlFreeTextWriter (project);
376 
377 	save = xmlSaveToFd (fd, "UTF8", XML_SAVE_FORMAT);
378 	if (save == NULL)
379 		goto save_error;
380 
381 	if (xmlSaveDoc (save, doc) == -1)
382 		goto save_error;
383 
384 	if (xmlSaveClose (save) == -1) {
385 		use_errno = errno;
386 		rb_debug ("xmlSaveClose failed");
387 		goto save_error;
388 	}
389 
390 	xmlFreeDoc (doc);
391 
392 	if (close (fd) == -1) {
393 		use_errno = errno;
394 		rb_debug ("close() failed");
395 		goto save_error;
396 	}
397 
398 	return path;
399 
400 error:
401 	/* cleanup */
402 	xmlTextWriterEndDocument (project);
403 
404 end_error:
405 	xmlFreeTextWriter (project);
406 
407 save_error:
408 	if (use_errno != 0) {
409 		g_set_error (error,
410 			     RB_RECORDER_ERROR,
411 			     RB_RECORDER_ERROR_GENERAL,
412 			     _("Unable to write audio project file %s: %s"),
413 			     path,
414 			     g_strerror (use_errno));
415 	} else {
416 		g_set_error (error,
417 			     RB_RECORDER_ERROR,
418 			     RB_RECORDER_ERROR_GENERAL,
419 			     _("Unable to write audio project"));
420 	}
421 
422 	g_remove (path);
423 	g_free (path);
424 	close (fd);
425 
426 	return NULL;
427 }
428 
429 static void
430 source_burn (RBDiscRecorderPlugin *pi,
431 	     RBSource *source)
432 {
433 	GtkWidget    *parent;
434 	char         *name;
435 	char         *path;
436 	GError       *error = NULL;
437 	GtkTreeModel *model;
438 
439 	g_object_get (source, "query-model", &model, NULL);
440 
441 	/* don't burn if the source is empty */
442 	if (gtk_tree_model_iter_n_children (model, NULL) == 0) {
443 		g_object_unref (model);
444 		return;
445 	}
446 
447 	name = NULL;
448 	g_object_get (source, "name", &name, NULL);
449 	rb_debug ("Burning playlist %s", name);
450 
451 	parent = gtk_widget_get_toplevel (GTK_WIDGET (source));
452 
453 	rb_debug ("Creating audio project");
454 	path = rb_disc_recorder_plugin_write_audio_project (name, model, &error);
455 	g_free (name);
456 
457 	if (! path) {
458 		rb_error_dialog (GTK_WINDOW (parent),
459 				 _("Unable to create audio CD project"),
460 				 "%s", error->message);
461 		g_error_free (error);
462 		return;
463 	}
464 
465 	rb_debug ("Starting brasero");
466 	rb_disc_recorder_plugin_start_burning (pi, path, FALSE);
467 	g_free (path);
468 }
469 
470 static void
471 cmd_burn_source (GtkAction            *action,
472 		 RBDiscRecorderPlugin *pi)
473 {
474 	if (pi->selected_page != NULL && RB_IS_SOURCE (pi->selected_page)) {
475 		source_burn (pi, RB_SOURCE (pi->selected_page));
476 	}
477 }
478 
479 static void
480 cmd_duplicate_cd (GtkAction         	*action,
481 		  RBDiscRecorderPlugin	*pi)
482 {
483 	gchar *device;
484 	GVolume *volume;
485 
486 	if (pi->selected_page == NULL || RB_IS_SOURCE (pi->selected_page) == FALSE)
487 		return;
488 
489 	g_object_get (pi->selected_page, "volume", &volume, NULL);
490 	if (G_IS_VOLUME (volume))
491 		device = g_volume_get_identifier (volume, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
492 	else
493 		device = NULL;
494 
495 	g_object_unref (volume);
496 
497 	rb_disc_recorder_plugin_start_burning (pi, device, TRUE);
498 	g_free (device);
499 }
500 
501 static void
502 playlist_entries_changed (GtkTreeModel         *model,
503 			  RhythmDBEntry        *entry,
504 			  RBDiscRecorderPlugin *pi)
505 {
506 	int        num_tracks;
507 	GtkAction *action;
508 
509 	num_tracks = gtk_tree_model_iter_n_children (model, NULL);
510 
511 	action = gtk_action_group_get_action (pi->action_group, "MusicPlaylistBurnToDiscPlaylist");
512 	gtk_action_set_sensitive (action, (num_tracks > 0));
513 }
514 
515 static void
516 playlist_row_inserted_cb (GtkTreeModel       *model,
517 			  GtkTreePath        *path,
518 			  GtkTreeIter        *iter,
519 			  RBDiscRecorderPlugin *pi)
520 {
521 	RhythmDBEntry *entry = rhythmdb_query_model_iter_to_entry (RHYTHMDB_QUERY_MODEL (model), iter);
522 
523 	playlist_entries_changed (model, entry, pi);
524 
525 	rhythmdb_entry_unref (entry);
526 }
527 
528 static gboolean
529 rb_disc_recorder_has_burner (RBDiscRecorderPlugin *pi)
530 {
531 	BraseroMediumMonitor *monitor;
532 	GSList		     *drives;
533 
534 	/* Find all drives and check capabilities */
535 	monitor = brasero_medium_monitor_get_default ();
536 	drives = brasero_medium_monitor_get_drives (monitor, BRASERO_DRIVE_TYPE_WRITER);
537 	g_object_unref (monitor);
538 
539 	g_slist_foreach (drives, (GFunc) g_object_unref, NULL);
540 	g_slist_free (drives);
541 
542 	if (drives != NULL)
543 		return TRUE;
544 
545 	return FALSE;
546 }
547 
548 
549 static gboolean
550 is_copy_available (RBDiscRecorderPlugin *pi)
551 {
552 	char *cmd;
553 
554 	if (!rb_disc_recorder_has_burner (pi))
555 		return FALSE;
556 
557 	cmd = g_find_program_in_path ("brasero");
558 	if (cmd == NULL)
559 		return FALSE;
560 
561 	g_free (cmd);
562 	return TRUE;
563 }
564 
565 static void
566 update_source (RBDiscRecorderPlugin *pi,
567 	       RBShell            *shell)
568 {
569 	GtkAction *burn_action, *copy_action;
570 	gboolean   playlist_active, is_audiocd_active;
571 	RBDisplayPage *selected_page;
572 	const char *page_type;
573 
574 	if (pi->selected_page != NULL && RB_IS_SOURCE (pi->selected_page)) {
575 		RhythmDBQueryModel *model;
576 
577 		g_object_get (pi->selected_page, "query-model", &model, NULL);
578 		g_signal_handlers_disconnect_by_func (model, playlist_row_inserted_cb, pi);
579 		g_signal_handlers_disconnect_by_func (model, playlist_entries_changed, pi);
580 		g_object_unref (model);
581 	}
582 
583 	g_object_get (shell, "selected-page", &selected_page, NULL);
584 
585 	/* for now restrict to playlist sources */
586 	playlist_active = RB_IS_PLAYLIST_SOURCE (selected_page);
587 
588 	if (selected_page != NULL) {
589 		page_type = G_OBJECT_TYPE_NAME (selected_page);
590 		is_audiocd_active = g_str_equal (page_type, "RBAudioCdSource");
591 	} else {
592 		is_audiocd_active = FALSE;
593 	}
594 
595 	burn_action = gtk_action_group_get_action (pi->action_group,
596 						   "MusicPlaylistBurnToDiscPlaylist");
597 	copy_action = gtk_action_group_get_action (pi->action_group,
598 						   "MusicAudioCDDuplicate");
599 
600 	if (pi->enabled && playlist_active && rb_disc_recorder_has_burner (pi)) {
601 		RhythmDBQueryModel *model;
602 
603 		g_object_get (selected_page, "query-model", &model, NULL);
604 		/* monitor for changes, to enable/disable the burn menu item */
605 		g_signal_connect_object (G_OBJECT (model),
606 					 "row_inserted",
607 					 G_CALLBACK (playlist_row_inserted_cb),
608 					 pi, 0);
609 		g_signal_connect_object (G_OBJECT (model),
610 					 "post-entry-delete",
611 					 G_CALLBACK (playlist_entries_changed),
612 					 pi, 0);
613 
614 		playlist_entries_changed (GTK_TREE_MODEL (model), NULL, pi);
615 		g_object_unref (model);
616 		gtk_action_set_visible (burn_action, TRUE);
617 	} else {
618 		gtk_action_set_visible (burn_action, FALSE);
619 	}
620 
621 	if (pi->enabled && is_audiocd_active && is_copy_available (pi)) {
622 		gtk_action_set_visible (copy_action, TRUE);
623 	} else {
624 		gtk_action_set_visible (copy_action, FALSE);
625 	}
626 
627 	if (pi->selected_page != NULL) {
628 		g_object_unref (pi->selected_page);
629 	}
630 	pi->selected_page = selected_page;
631 }
632 
633 static void
634 shell_selected_page_notify_cb (RBShell            *shell,
635 			       GParamSpec         *param,
636 			       RBDiscRecorderPlugin *pi)
637 {
638 	rb_debug ("RBDiscRecorderPlugin selected page changed");
639 	update_source (pi, shell);
640 }
641 
642 static struct ui_paths {
643 	const char *path;
644 	gboolean for_burn;
645 	gboolean for_copy;
646 } ui_paths[] = {
647 	{ "/QueueSourceToolBar/PluginPlaceholder", TRUE, FALSE },
648 	{ "/QueueSourcePopup/PluginPlaceholder", TRUE, FALSE },
649 	{ "/PlaylistSourcePopup/PluginPlaceholder", TRUE, FALSE },
650 	{ "/AutoPlaylistSourcePopup/PluginPlaceholder", TRUE, FALSE },
651 	{ "/AutoPlaylistSourceToolBar/PluginPlaceholder", TRUE, FALSE },
652 	{ "/StaticPlaylistSourceToolBar/PluginPlaceholder", TRUE, FALSE },
653 	{ "/AudioCdSourcePopup/PluginPlaceholder", FALSE, TRUE },
654 	{ "/AudioCdSourceToolBar/PluginPlaceholder", FALSE, TRUE },
655 };
656 
657 static void
658 impl_activate (PeasActivatable *plugin)
659 {
660 	RBDiscRecorderPlugin *pi = RB_DISC_RECORDER_PLUGIN (plugin);
661 	GtkUIManager         *uimanager = NULL;
662 	GtkAction            *action;
663 	RBShell              *shell;
664 	int                   i;
665 
666 	g_object_get (pi, "object", &shell, NULL);
667 
668 	pi->enabled = TRUE;
669 
670 	rb_debug ("RBDiscRecorderPlugin activating");
671 
672 	brasero_media_library_start ();
673 
674 	g_object_get (shell,
675 		      "ui-manager", &uimanager,
676 		      NULL);
677 
678 	g_signal_connect_object (G_OBJECT (shell),
679 				 "notify::selected-page",
680 				 G_CALLBACK (shell_selected_page_notify_cb),
681 				 pi, 0);
682 
683 	/* add UI */
684 	pi->action_group = gtk_action_group_new ("DiscRecorderActions");
685 	gtk_action_group_set_translation_domain (pi->action_group,
686 						 GETTEXT_PACKAGE);
687 	gtk_action_group_add_actions (pi->action_group,
688 				      rb_disc_recorder_plugin_actions, G_N_ELEMENTS (rb_disc_recorder_plugin_actions),
689 				      pi);
690 	gtk_ui_manager_insert_action_group (uimanager, pi->action_group, 0);
691 	pi->ui_merge_id = gtk_ui_manager_new_merge_id (uimanager);
692 	for (i = 0; i < G_N_ELEMENTS (ui_paths); i++) {
693 		if (ui_paths[i].for_burn)
694 			gtk_ui_manager_add_ui (uimanager,
695 					       pi->ui_merge_id,
696 					       ui_paths[i].path,
697 					       "MusicPlaylistBurnToDiscPlaylistMenu",
698 					       "MusicPlaylistBurnToDiscPlaylist",
699 					       GTK_UI_MANAGER_AUTO,
700 					       FALSE);
701 		if (ui_paths[i].for_copy)
702 			gtk_ui_manager_add_ui (uimanager,
703 					       pi->ui_merge_id,
704 					       ui_paths[i].path,
705 					       "MusicAudioCDDuplicateMenu",
706 					       "MusicAudioCDDuplicate",
707 					       GTK_UI_MANAGER_AUTO,
708 					       FALSE);
709 	}
710 	g_object_unref (uimanager);
711 
712         action = gtk_action_group_get_action (pi->action_group, "MusicPlaylistBurnToDiscPlaylist");
713 	/* Translators: this is the toolbar button label for */
714 	/* Create Audio CD action                            */
715 	g_object_set (action, "short-label", _("Burn"), NULL);
716 
717         action = gtk_action_group_get_action (pi->action_group,
718 					      "MusicAudioCDDuplicate");
719 	/* Translators: this is the toolbar button label for */
720 	/* Duplicate Audio CD action                         */
721 	g_object_set (action, "short-label", _("Copy CD"), NULL);
722 
723 	update_source (pi, shell);
724 
725 	g_object_unref (shell);
726 }
727 
728 static void
729 impl_deactivate	(PeasActivatable *plugin)
730 {
731 	RBDiscRecorderPlugin *pi = RB_DISC_RECORDER_PLUGIN (plugin);
732 	GtkUIManager         *uimanager = NULL;
733 	RBShell              *shell;
734 
735 	g_object_get (pi, "object", &shell, NULL);
736 
737 	pi->enabled = FALSE;
738 
739 	rb_debug ("RBDiscRecorderPlugin deactivating");
740 
741 	update_source (pi, shell);
742 
743 	if (pi->selected_page) {
744 		g_object_unref (pi->selected_page);
745 		pi->selected_page = NULL;
746 	}
747 
748 	g_signal_handlers_disconnect_by_func (shell, shell_selected_page_notify_cb, pi);
749 
750 	g_object_get (shell,
751 		      "ui-manager", &uimanager,
752 		      NULL);
753 
754 	gtk_ui_manager_remove_ui (uimanager, pi->ui_merge_id);
755 	gtk_ui_manager_remove_action_group (uimanager, pi->action_group);
756 
757 	g_object_unref (uimanager);
758 
759 	/* NOTE: don't deactivate libbrasero-media as it could be in use somewhere else */
760 
761 	g_object_unref (shell);
762 }
763 
764 G_MODULE_EXPORT void
765 peas_register_types (PeasObjectModule *module)
766 {
767 	rb_disc_recorder_plugin_register_type (G_TYPE_MODULE (module));
768 	peas_object_module_register_extension_type (module,
769 						    PEAS_TYPE_ACTIVATABLE,
770 						    RB_TYPE_DISC_RECORDER_PLUGIN);
771 }