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 }