1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
3 * arch-tag: Implementation of song cut/paste handler object
5 * Copyright (C) 2002 Jorn Baayen <jorn@nl.linux.org>
6 * Copyright (C) 2003,2004 Colin Walters <walters@verbum.org>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
26 #include <glib/gi18n.h>
28 #include <gtk/gtkstock.h>
30 #include "rb-shell-clipboard.h"
31 #include "rb-playlist-manager.h"
32 #include "rb-play-queue-source.h"
33 #include "rb-sourcelist-model.h"
37 static void rb_shell_clipboard_class_init (RBShellClipboardClass
*klass
);
38 static void rb_shell_clipboard_init (RBShellClipboard
*shell_clipboard
);
39 static GObject
*rb_shell_clipboard_constructor (GType type
, guint n_construct_properties
,
40 GObjectConstructParam
*construct_properties
);
41 static void rb_shell_clipboard_finalize (GObject
*object
);
42 static void rb_shell_clipboard_set_property (GObject
*object
,
46 static void rb_shell_clipboard_get_property (GObject
*object
,
50 static void rb_shell_clipboard_sync (RBShellClipboard
*clipboard
);
51 static void rb_shell_clipboard_cmd_select_all (GtkAction
*action
,
52 RBShellClipboard
*clipboard
);
53 static void rb_shell_clipboard_cmd_select_none (GtkAction
*action
,
54 RBShellClipboard
*clipboard
);
55 static void rb_shell_clipboard_cmd_cut (GtkAction
*action
,
56 RBShellClipboard
*clipboard
);
57 static void rb_shell_clipboard_cmd_copy (GtkAction
*action
,
58 RBShellClipboard
*clipboard
);
59 static void rb_shell_clipboard_cmd_paste (GtkAction
*action
,
60 RBShellClipboard
*clipboard
);
61 static void rb_shell_clipboard_cmd_delete (GtkAction
*action
,
62 RBShellClipboard
*clipboard
);
63 static void rb_shell_clipboard_cmd_queue_delete (GtkAction
*action
,
64 RBShellClipboard
*clipboard
);
65 static void rb_shell_clipboard_cmd_move_to_trash (GtkAction
*action
,
66 RBShellClipboard
*clipboard
);
67 static void rb_shell_clipboard_set (RBShellClipboard
*clipboard
,
69 static gboolean
rb_shell_clipboard_idle_poll_deletions (RBShellClipboard
*clipboard
);
70 static void rb_shell_clipboard_playlist_added_cb (RBPlaylistManager
*mgr
,
71 RBPlaylistSource
*source
,
72 RBShellClipboard
*clipboard
);
73 static void rb_shell_clipboard_entry_deleted_cb (RhythmDB
*db
,
75 RBShellClipboard
*clipboard
);
76 static void rb_shell_clipboard_entryview_changed_cb (RBEntryView
*view
,
77 RBShellClipboard
*clipboard
);
78 static void rb_shell_clipboard_entries_changed_cb (RBEntryView
*view
,
80 RBShellClipboard
*clipboard
);
81 static void rb_shell_clipboard_cmd_add_to_playlist_new (GtkAction
*action
,
82 RBShellClipboard
*clipboard
);
83 static void rb_shell_clipboard_cmd_add_song_to_queue (GtkAction
*action
,
84 RBShellClipboard
*clipboard
);
85 static void rb_shell_clipboard_cmd_song_info (GtkAction
*action
,
86 RBShellClipboard
*clipboard
);
87 static void rb_shell_clipboard_cmd_queue_song_info (GtkAction
*action
,
88 RBShellClipboard
*clipboard
);
89 static void rebuild_playlist_menu (RBShellClipboard
*clipboard
);
90 static gboolean
rebuild_playlist_menu_idle (RBShellClipboard
*clipboard
);
92 struct RBShellClipboardPrivate
96 RBStaticPlaylistSource
*queue_source
;
97 RBPlaylistManager
*playlist_manager
;
100 GtkActionGroup
*actiongroup
;
101 guint playlist_menu_ui_id
;
103 GHashTable
*signal_hash
;
105 GAsyncQueue
*deleted_queue
;
107 guint idle_deletion_id
, idle_sync_id
, idle_playlist_id
;
112 #define RB_SHELL_CLIPBOARD_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), RB_TYPE_SHELL_CLIPBOARD, RBShellClipboardPrivate))
121 PROP_PLAYLIST_MANAGER
,
125 static GtkActionEntry rb_shell_clipboard_actions
[] =
127 { "EditSelectAll", NULL
, N_("Select _All"), "<control>A",
128 N_("Select all songs"),
129 G_CALLBACK (rb_shell_clipboard_cmd_select_all
) },
130 { "EditSelectNone", NULL
, N_("D_eselect All"), "<shift><control>A",
131 N_("Deselect all songs"),
132 G_CALLBACK (rb_shell_clipboard_cmd_select_none
) },
133 { "EditCut", GTK_STOCK_CUT
, N_("Cu_t"), "<control>X",
135 G_CALLBACK (rb_shell_clipboard_cmd_cut
) },
136 { "EditCopy", GTK_STOCK_COPY
, N_("_Copy"), "<control>C",
137 N_("Copy selection"),
138 G_CALLBACK (rb_shell_clipboard_cmd_copy
) },
139 { "EditPaste", GTK_STOCK_PASTE
, N_("_Paste"), "<control>V",
140 N_("Paste selection"),
141 G_CALLBACK (rb_shell_clipboard_cmd_paste
) },
142 { "EditDelete", GTK_STOCK_REMOVE
, N_("_Remove"), NULL
,
143 N_("Remove selection"),
144 G_CALLBACK (rb_shell_clipboard_cmd_delete
) },
145 { "EditMovetoTrash", GTK_STOCK_DELETE
, N_("_Move to Trash"), NULL
,
146 N_("Move selection to the trash"),
147 G_CALLBACK (rb_shell_clipboard_cmd_move_to_trash
) },
149 { "EditPlaylistAdd", NULL
, N_("Add to P_laylist") },
150 { "EditPlaylistAddNew", GTK_STOCK_ADD
, N_("_New Playlist..."), NULL
,
151 N_("Add the selected songs to a new playlist"),
152 G_CALLBACK (rb_shell_clipboard_cmd_add_to_playlist_new
) },
153 { "AddToQueue", GTK_STOCK_ADD
, N_("Add _to Play Queue"), NULL
,
154 N_("Add the selected songs to the play queue"),
155 G_CALLBACK (rb_shell_clipboard_cmd_add_song_to_queue
) },
156 { "QueueDelete", GTK_STOCK_REMOVE
, N_("Remove"), NULL
,
157 N_("Remove selection"),
158 G_CALLBACK (rb_shell_clipboard_cmd_queue_delete
) },
160 { "MusicProperties", GTK_STOCK_PROPERTIES
, N_("_Properties"), "<Alt>Return",
161 N_("Show information on the selected song"),
162 G_CALLBACK (rb_shell_clipboard_cmd_song_info
) },
163 { "QueueMusicProperties", GTK_STOCK_PROPERTIES
, N_("_Properties"), NULL
,
164 N_("Show information on the selected song"),
165 G_CALLBACK (rb_shell_clipboard_cmd_queue_song_info
) },
167 static guint rb_shell_clipboard_n_actions
= G_N_ELEMENTS (rb_shell_clipboard_actions
);
169 static const char *playlist_menu_paths
[] = {
170 "/MenuBar/EditMenu/EditPlaylistAddMenu/EditPlaylistAddPlaceholder",
171 "/BrowserSourceViewPopup/BrowserSourcePopupPlaylistAdd/BrowserSourcePopupPlaylistAddPlaceholder",
172 "/PlaylistViewPopup/PlaylistPopupPlaylistAdd/PlaylistPopupPlaylistAddPlaceholder",
174 static guint num_playlist_menu_paths
= G_N_ELEMENTS (playlist_menu_paths
);
176 G_DEFINE_TYPE (RBShellClipboard
, rb_shell_clipboard
, G_TYPE_OBJECT
)
179 rb_shell_clipboard_class_init (RBShellClipboardClass
*klass
)
181 GObjectClass
*object_class
= G_OBJECT_CLASS (klass
);
183 object_class
->finalize
= rb_shell_clipboard_finalize
;
184 object_class
->constructor
= rb_shell_clipboard_constructor
;
186 object_class
->set_property
= rb_shell_clipboard_set_property
;
187 object_class
->get_property
= rb_shell_clipboard_get_property
;
189 g_object_class_install_property (object_class
,
191 g_param_spec_object ("source",
196 g_object_class_install_property (object_class
,
198 g_param_spec_object ("action-group",
200 "GtkActionGroup object",
201 GTK_TYPE_ACTION_GROUP
,
202 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
));
203 g_object_class_install_property (object_class
,
205 g_param_spec_object ("db",
209 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
));
210 g_object_class_install_property (object_class
,
212 g_param_spec_object ("queue-source",
214 "RBPlaylistSource object",
215 RB_TYPE_PLAYLIST_SOURCE
,
217 g_object_class_install_property (object_class
,
218 PROP_PLAYLIST_MANAGER
,
219 g_param_spec_object ("playlist-manager",
221 "RBPlaylistManager object",
222 RB_TYPE_PLAYLIST_MANAGER
,
224 g_object_class_install_property (object_class
,
226 g_param_spec_object ("ui-manager",
228 "GtkUIManager object",
230 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
));
232 g_type_class_add_private (klass
, sizeof (RBShellClipboardPrivate
));
236 rb_shell_clipboard_init (RBShellClipboard
*shell_clipboard
)
238 shell_clipboard
->priv
= RB_SHELL_CLIPBOARD_GET_PRIVATE (shell_clipboard
);
240 shell_clipboard
->priv
->signal_hash
= g_hash_table_new_full (g_direct_hash
, g_direct_equal
,
243 shell_clipboard
->priv
->deleted_queue
= g_async_queue_new ();
245 shell_clipboard
->priv
->idle_deletion_id
= g_idle_add ((GSourceFunc
) rb_shell_clipboard_idle_poll_deletions
, shell_clipboard
);
249 unset_source_internal (RBShellClipboard
*clipboard
)
251 if (clipboard
->priv
->source
!= NULL
) {
252 RBEntryView
*songs
= rb_source_get_entry_view (clipboard
->priv
->source
);
255 g_signal_handlers_disconnect_by_func (G_OBJECT (songs
),
256 G_CALLBACK (rb_shell_clipboard_entryview_changed_cb
),
258 g_signal_handlers_disconnect_by_func (G_OBJECT (songs
),
259 G_CALLBACK (rb_shell_clipboard_entries_changed_cb
),
263 clipboard
->priv
->source
= NULL
;
267 rb_shell_clipboard_finalize (GObject
*object
)
269 RBShellClipboard
*shell_clipboard
;
271 g_return_if_fail (object
!= NULL
);
272 g_return_if_fail (RB_IS_SHELL_CLIPBOARD (object
));
274 shell_clipboard
= RB_SHELL_CLIPBOARD (object
);
276 g_return_if_fail (shell_clipboard
->priv
!= NULL
);
278 /* release references to the source */
279 unset_source_internal (shell_clipboard
);
281 g_hash_table_destroy (shell_clipboard
->priv
->signal_hash
);
283 g_list_foreach (shell_clipboard
->priv
->entries
, (GFunc
)rhythmdb_entry_unref
, NULL
);
284 g_list_free (shell_clipboard
->priv
->entries
);
286 g_async_queue_unref (shell_clipboard
->priv
->deleted_queue
);
288 if (shell_clipboard
->priv
->idle_sync_id
)
289 g_source_remove (shell_clipboard
->priv
->idle_sync_id
);
290 if (shell_clipboard
->priv
->idle_deletion_id
)
291 g_source_remove (shell_clipboard
->priv
->idle_deletion_id
);
292 if (shell_clipboard
->priv
->idle_playlist_id
)
293 g_source_remove (shell_clipboard
->priv
->idle_playlist_id
);
295 G_OBJECT_CLASS (rb_shell_clipboard_parent_class
)->finalize (object
);
299 rb_shell_clipboard_constructor (GType type
, guint n_construct_properties
,
300 GObjectConstructParam
*construct_properties
)
302 RBShellClipboard
*clip
;
303 RBShellClipboardClass
*klass
;
304 GObjectClass
*parent_class
;
306 klass
= RB_SHELL_CLIPBOARD_CLASS (g_type_class_peek (RB_TYPE_SHELL_CLIPBOARD
));
308 parent_class
= G_OBJECT_CLASS (rb_shell_clipboard_parent_class
);
309 clip
= RB_SHELL_CLIPBOARD (parent_class
->constructor (type
,
310 n_construct_properties
,
311 construct_properties
));
313 g_signal_connect_object (G_OBJECT (clip
->priv
->db
),
315 G_CALLBACK (rb_shell_clipboard_entry_deleted_cb
),
318 return G_OBJECT (clip
);
322 rb_shell_clipboard_set_source_internal (RBShellClipboard
*clipboard
,
325 unset_source_internal (clipboard
);
327 clipboard
->priv
->source
= source
;
328 rb_debug ("selected source %p", source
);
330 rb_shell_clipboard_sync (clipboard
);
332 if (clipboard
->priv
->source
!= NULL
) {
333 RBEntryView
*songs
= rb_source_get_entry_view (clipboard
->priv
->source
);
336 g_signal_connect_object (G_OBJECT (songs
),
338 G_CALLBACK (rb_shell_clipboard_entryview_changed_cb
),
340 g_signal_connect_object (G_OBJECT (songs
),
342 G_CALLBACK (rb_shell_clipboard_entries_changed_cb
),
344 g_signal_connect_object (G_OBJECT (songs
),
346 G_CALLBACK (rb_shell_clipboard_entries_changed_cb
),
348 g_signal_connect_object (G_OBJECT (songs
),
350 G_CALLBACK (rb_shell_clipboard_entryview_changed_cb
),
355 rebuild_playlist_menu (clipboard
);
359 rb_shell_clipboard_set_property (GObject
*object
,
364 RBShellClipboard
*clipboard
= RB_SHELL_CLIPBOARD (object
);
369 rb_shell_clipboard_set_source_internal (clipboard
, g_value_get_object (value
));
371 case PROP_ACTION_GROUP
:
372 clipboard
->priv
->actiongroup
= g_value_get_object (value
);
373 gtk_action_group_add_actions (clipboard
->priv
->actiongroup
,
374 rb_shell_clipboard_actions
,
375 rb_shell_clipboard_n_actions
,
379 clipboard
->priv
->db
= g_value_get_object (value
);
381 case PROP_UI_MANAGER
:
382 clipboard
->priv
->ui_mgr
= g_value_get_object (value
);
384 case PROP_PLAYLIST_MANAGER
:
385 if (clipboard
->priv
->playlist_manager
!= NULL
) {
386 g_signal_handlers_disconnect_by_func (clipboard
->priv
->playlist_manager
,
387 G_CALLBACK (rb_shell_clipboard_playlist_added_cb
),
391 clipboard
->priv
->playlist_manager
= g_value_get_object (value
);
392 if (clipboard
->priv
->playlist_manager
!= NULL
) {
393 g_signal_connect_object (G_OBJECT (clipboard
->priv
->playlist_manager
),
394 "playlist-added", G_CALLBACK (rb_shell_clipboard_playlist_added_cb
),
397 rebuild_playlist_menu (clipboard
);
401 case PROP_QUEUE_SOURCE
:
402 if (clipboard
->priv
->queue_source
!= NULL
) {
403 RBEntryView
*sidebar
;
404 g_object_get (clipboard
->priv
->queue_source
, "sidebar", &sidebar
, NULL
);
405 g_signal_handlers_disconnect_by_func (sidebar
,
406 G_CALLBACK (rb_shell_clipboard_entryview_changed_cb
),
408 g_object_unref (sidebar
);
411 clipboard
->priv
->queue_source
= g_value_get_object (value
);
412 if (clipboard
->priv
->queue_source
!= NULL
) {
413 RBEntryView
*sidebar
;
414 g_object_get (clipboard
->priv
->queue_source
, "sidebar", &sidebar
, NULL
);
415 g_signal_connect_object (G_OBJECT (sidebar
), "selection-changed",
416 G_CALLBACK (rb_shell_clipboard_entryview_changed_cb
),
418 g_object_unref (sidebar
);
422 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
428 rb_shell_clipboard_get_property (GObject
*object
,
433 RBShellClipboard
*clipboard
= RB_SHELL_CLIPBOARD (object
);
438 g_value_set_object (value
, clipboard
->priv
->source
);
440 case PROP_ACTION_GROUP
:
441 g_value_set_object (value
, clipboard
->priv
->actiongroup
);
444 g_value_set_object (value
, clipboard
->priv
->db
);
446 case PROP_UI_MANAGER
:
447 g_value_set_object (value
, clipboard
->priv
->ui_mgr
);
449 case PROP_PLAYLIST_MANAGER
:
450 g_value_set_object (value
, clipboard
->priv
->playlist_manager
);
452 case PROP_QUEUE_SOURCE
:
453 g_value_set_object (value
, clipboard
->priv
->queue_source
);
456 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
462 rb_shell_clipboard_set_source (RBShellClipboard
*clipboard
,
465 g_return_if_fail (RB_IS_SHELL_CLIPBOARD (clipboard
));
467 g_return_if_fail (RB_IS_SOURCE (source
));
469 g_object_set (G_OBJECT (clipboard
), "source", source
, NULL
);
473 rb_shell_clipboard_new (GtkActionGroup
*actiongroup
,
474 GtkUIManager
*ui_mgr
,
477 return g_object_new (RB_TYPE_SHELL_CLIPBOARD
,
478 "action-group", actiongroup
,
479 "ui-manager", ui_mgr
,
485 rb_shell_clipboard_sync_idle (RBShellClipboard
*clipboard
)
487 GDK_THREADS_ENTER ();
488 rb_shell_clipboard_sync (clipboard
);
489 clipboard
->priv
->idle_sync_id
= 0;
490 GDK_THREADS_LEAVE ();
496 rb_shell_clipboard_sync (RBShellClipboard
*clipboard
)
499 gboolean have_selection
= FALSE
;
500 gboolean have_sidebar_selection
= FALSE
;
501 gboolean can_cut
= FALSE
;
502 gboolean can_paste
= FALSE
;
503 gboolean can_delete
= FALSE
;
504 gboolean can_copy
= FALSE
;
505 gboolean can_add_to_queue
= FALSE
;
506 gboolean can_move_to_trash
= FALSE
;
507 gboolean can_select_all
= FALSE
;
508 gboolean can_show_properties
= FALSE
;
510 RhythmDBEntryType entry_type
;
512 if (!clipboard
->priv
->source
)
515 view
= rb_source_get_entry_view (clipboard
->priv
->source
);
517 have_selection
= rb_entry_view_have_selection (view
);
518 can_select_all
= !rb_entry_view_have_complete_selection (view
);
521 if (clipboard
->priv
->queue_source
) {
522 RBEntryView
*sidebar
;
523 g_object_get (clipboard
->priv
->queue_source
, "sidebar", &sidebar
, NULL
);
524 have_sidebar_selection
= rb_entry_view_have_selection (sidebar
);
525 g_object_unref (sidebar
);
528 rb_debug ("syncing clipboard");
530 if (g_list_length (clipboard
->priv
->entries
) > 0)
531 can_paste
= rb_source_can_paste (clipboard
->priv
->source
);
533 if (have_selection
) {
534 can_cut
= rb_source_can_cut (clipboard
->priv
->source
);
535 can_delete
= rb_source_can_delete (clipboard
->priv
->source
);
536 can_copy
= rb_source_can_copy (clipboard
->priv
->source
);
537 can_move_to_trash
= rb_source_can_move_to_trash (clipboard
->priv
->source
);
538 can_show_properties
= rb_source_can_show_properties (clipboard
->priv
->source
);
540 if (clipboard
->priv
->queue_source
)
541 can_add_to_queue
= rb_source_can_add_to_queue (clipboard
->priv
->source
);
544 action
= gtk_action_group_get_action (clipboard
->priv
->actiongroup
, "EditCut");
545 g_object_set (G_OBJECT (action
), "sensitive", can_cut
, NULL
);
547 action
= gtk_action_group_get_action (clipboard
->priv
->actiongroup
, "EditDelete");
548 g_object_set (G_OBJECT (action
), "sensitive", can_delete
, NULL
);
550 action
= gtk_action_group_get_action (clipboard
->priv
->actiongroup
, "EditMovetoTrash");
551 g_object_set (G_OBJECT (action
), "sensitive", can_move_to_trash
, NULL
);
553 action
= gtk_action_group_get_action (clipboard
->priv
->actiongroup
, "EditCopy");
554 g_object_set (G_OBJECT (action
), "sensitive", can_copy
, NULL
);
556 action
= gtk_action_group_get_action (clipboard
->priv
->actiongroup
,"EditPaste");
557 g_object_set (G_OBJECT (action
), "sensitive", can_paste
, NULL
);
559 action
= gtk_action_group_get_action (clipboard
->priv
->actiongroup
, "EditPlaylistAdd");
560 g_object_set (G_OBJECT (action
), "sensitive", can_copy
, NULL
);
562 action
= gtk_action_group_get_action (clipboard
->priv
->actiongroup
, "AddToQueue");
563 g_object_set (G_OBJECT (action
), "sensitive", can_add_to_queue
, NULL
);
565 action
= gtk_action_group_get_action (clipboard
->priv
->actiongroup
, "MusicProperties");
566 g_object_set (G_OBJECT (action
), "sensitive", can_show_properties
, NULL
);
568 action
= gtk_action_group_get_action (clipboard
->priv
->actiongroup
, "QueueMusicProperties");
569 g_object_set (G_OBJECT (action
), "sensitive", have_sidebar_selection
, NULL
);
571 action
= gtk_action_group_get_action (clipboard
->priv
->actiongroup
, "QueueDelete");
572 g_object_set (G_OBJECT (action
), "sensitive", have_sidebar_selection
, NULL
);
574 action
= gtk_action_group_get_action (clipboard
->priv
->actiongroup
, "EditSelectAll");
575 g_object_set (G_OBJECT (action
), "sensitive", can_select_all
, NULL
);
577 action
= gtk_action_group_get_action (clipboard
->priv
->actiongroup
, "EditSelectNone");
578 g_object_set (G_OBJECT (action
), "sensitive", have_selection
, NULL
);
580 /* disable the whole add-to-playlist menu if we can't add to a playlist
581 * FIXME: change this when we support non-library playilst adding
583 action
= gtk_action_group_get_action (clipboard
->priv
->actiongroup
, "EditPlaylistAdd");
584 g_object_get (clipboard
->priv
->source
, "entry-type", &entry_type
, NULL
);
585 gtk_action_set_sensitive (action
, (entry_type
== RHYTHMDB_ENTRY_TYPE_SONG
));
586 g_boxed_free (RHYTHMDB_TYPE_ENTRY_TYPE
, entry_type
);
590 get_focussed_widget (RBShellClipboard
*clipboard
)
595 /* FIXME: this should be better */
596 window
= gtk_widget_get_toplevel (GTK_WIDGET (clipboard
->priv
->source
));
597 widget
= gtk_window_get_focus (GTK_WINDOW (window
));
603 rb_shell_clipboard_cmd_select_all (GtkAction
*action
,
604 RBShellClipboard
*clipboard
)
606 RBEntryView
*entryview
;
609 rb_debug ("select all");
610 widget
= get_focussed_widget (clipboard
);
611 if (GTK_IS_EDITABLE (widget
)) {
612 gtk_editable_select_region (GTK_EDITABLE (widget
), 0, -1);
614 /* select all tracks in the entry view */
615 entryview
= rb_source_get_entry_view (clipboard
->priv
->source
);
616 rb_entry_view_select_all (entryview
);
621 rb_shell_clipboard_cmd_select_none (GtkAction
*action
,
622 RBShellClipboard
*clipboard
)
624 RBEntryView
*entryview
;
627 rb_debug ("select none");
628 widget
= get_focussed_widget (clipboard
);
629 if (GTK_IS_EDITABLE (widget
)) {
630 gtk_editable_select_region (GTK_EDITABLE (widget
), -1, -1);
632 entryview
= rb_source_get_entry_view (clipboard
->priv
->source
);
633 rb_entry_view_select_none (entryview
);
638 rb_shell_clipboard_cmd_cut (GtkAction
*action
,
639 RBShellClipboard
*clipboard
)
642 rb_shell_clipboard_set (clipboard
,
643 rb_source_cut (clipboard
->priv
->source
));
647 rb_shell_clipboard_cmd_copy (GtkAction
*action
,
648 RBShellClipboard
*clipboard
)
651 rb_shell_clipboard_set (clipboard
,
652 rb_source_copy (clipboard
->priv
->source
));
656 rb_shell_clipboard_cmd_paste (GtkAction
*action
,
657 RBShellClipboard
*clipboard
)
660 rb_source_paste (clipboard
->priv
->source
, clipboard
->priv
->entries
);
664 rb_shell_clipboard_cmd_delete (GtkAction
*action
,
665 RBShellClipboard
*clipboard
)
668 rb_source_delete (clipboard
->priv
->source
);
672 rb_shell_clipboard_cmd_queue_delete (GtkAction
*action
,
673 RBShellClipboard
*clipboard
)
676 rb_play_queue_source_sidebar_delete (RB_PLAY_QUEUE_SOURCE (clipboard
->priv
->queue_source
));
680 rb_shell_clipboard_cmd_move_to_trash (GtkAction
*action
,
681 RBShellClipboard
*clipboard
)
683 rb_debug ("movetotrash");
684 rb_source_move_to_trash (clipboard
->priv
->source
);
688 rb_shell_clipboard_set (RBShellClipboard
*clipboard
,
691 if (clipboard
->priv
->entries
!= NULL
) {
692 g_list_foreach (clipboard
->priv
->entries
, (GFunc
)rhythmdb_entry_unref
, NULL
);
693 g_list_free (clipboard
->priv
->entries
);
696 clipboard
->priv
->entries
= entries
;
700 rb_shell_clipboard_process_deletions (RBShellClipboard
*clipboard
)
702 RhythmDBEntry
*entry
;
704 if (clipboard
->priv
->entries
) {
705 GList
*tem
, *finished
= NULL
;
706 gboolean processed
= FALSE
;
708 while ((entry
= g_async_queue_try_pop (clipboard
->priv
->deleted_queue
)) != NULL
) {
709 clipboard
->priv
->entries
= g_list_remove (clipboard
->priv
->entries
, entry
);
710 finished
= g_list_prepend (finished
, entry
);
715 rb_shell_clipboard_sync (clipboard
);
717 for (tem
= finished
; tem
; tem
= tem
->next
)
718 rhythmdb_entry_unref (tem
->data
);
719 g_list_free (finished
);
723 /* Fast path for when there's nothing in the clipboard */
724 while ((entry
= g_async_queue_try_pop (clipboard
->priv
->deleted_queue
)) != NULL
)
725 rhythmdb_entry_unref (entry
);
731 rb_shell_clipboard_idle_poll_deletions (RBShellClipboard
*clipboard
)
735 GDK_THREADS_ENTER ();
737 did_sync
= rb_shell_clipboard_process_deletions (clipboard
);
740 clipboard
->priv
->idle_deletion_id
=
741 g_idle_add_full (G_PRIORITY_LOW
,
742 (GSourceFunc
) rb_shell_clipboard_idle_poll_deletions
,
745 clipboard
->priv
->idle_deletion_id
=
747 (GSourceFunc
) rb_shell_clipboard_idle_poll_deletions
,
750 GDK_THREADS_LEAVE ();
756 rb_shell_clipboard_entry_deleted_cb (RhythmDB
*db
,
757 RhythmDBEntry
*entry
,
758 RBShellClipboard
*clipboard
)
760 rhythmdb_entry_ref (entry
);
761 g_async_queue_push (clipboard
->priv
->deleted_queue
, entry
);
765 rb_shell_clipboard_entryview_changed_cb (RBEntryView
*view
,
766 RBShellClipboard
*clipboard
)
768 if (clipboard
->priv
->idle_sync_id
== 0)
769 clipboard
->priv
->idle_sync_id
= g_idle_add ((GSourceFunc
) rb_shell_clipboard_sync_idle
,
771 rb_debug ("entryview changed");
775 rb_shell_clipboard_entries_changed_cb (RBEntryView
*view
,
777 RBShellClipboard
*clipboard
)
779 rb_debug ("entryview changed");
780 if (clipboard
->priv
->idle_sync_id
== 0)
781 clipboard
->priv
->idle_sync_id
= g_idle_add ((GSourceFunc
) rb_shell_clipboard_sync_idle
,
786 rb_shell_clipboard_cmd_add_to_playlist_new (GtkAction
*action
,
787 RBShellClipboard
*clipboard
)
790 RBSource
*playlist_source
;
792 rb_debug ("add to new playlist");
794 entries
= rb_source_copy (clipboard
->priv
->source
);
795 playlist_source
= rb_playlist_manager_new_playlist (clipboard
->priv
->playlist_manager
,
797 rb_source_paste (playlist_source
, entries
);
799 g_list_foreach (entries
, (GFunc
)rhythmdb_entry_unref
, NULL
);
800 g_list_free (entries
);
804 rb_shell_clipboard_cmd_add_song_to_queue (GtkAction
*action
,
805 RBShellClipboard
*clipboard
)
807 rb_debug ("add to queue");
808 rb_source_add_to_queue (clipboard
->priv
->source
,
809 RB_SOURCE (clipboard
->priv
->queue_source
));
813 rb_shell_clipboard_cmd_song_info (GtkAction
*action
,
814 RBShellClipboard
*clipboard
)
816 rb_debug ("song info");
818 rb_source_song_properties (clipboard
->priv
->source
);
822 rb_shell_clipboard_cmd_queue_song_info (GtkAction
*action
,
823 RBShellClipboard
*clipboard
)
825 rb_debug ("song info");
826 rb_play_queue_source_sidebar_song_info (RB_PLAY_QUEUE_SOURCE (clipboard
->priv
->queue_source
));
830 rb_shell_clipboard_playlist_add_cb (GtkAction
*action
,
831 RBShellClipboard
*clipboard
)
833 RBSource
*playlist_source
;
836 rb_debug ("add to exisintg playlist");
837 playlist_source
= g_object_get_data (G_OBJECT (action
), "playlist-source");
839 entries
= rb_source_copy (clipboard
->priv
->source
);
840 rb_source_paste (playlist_source
, entries
);
842 g_list_foreach (entries
, (GFunc
)rhythmdb_entry_unref
, NULL
);
843 g_list_free (entries
);
847 generate_action_name (RBStaticPlaylistSource
*source
,
848 RBShellClipboard
*clipboard
)
850 return g_strdup_printf ("AddToPlaylistClipboardAction%p", source
);
854 rb_shell_clipboard_playlist_deleted_cb (RBStaticPlaylistSource
*source
,
855 RBShellClipboard
*clipboard
)
860 /* first rebuild the menu */
861 rebuild_playlist_menu (clipboard
);
863 /* then remove the 'add to playlist' action for the deleted playlist */
864 action_name
= generate_action_name (source
, clipboard
);
865 action
= gtk_action_group_get_action (clipboard
->priv
->actiongroup
, action_name
);
867 gtk_action_group_remove_action (clipboard
->priv
->actiongroup
, action
);
868 g_free (action_name
);
872 rb_shell_clipboard_playlist_renamed_cb (RBStaticPlaylistSource
*source
,
874 RBShellClipboard
*clipboard
)
876 char *name
, *action_name
;
879 g_object_get (source
, "name", &name
, NULL
);
881 action_name
= generate_action_name (source
, clipboard
);
882 action
= gtk_action_group_get_action (clipboard
->priv
->actiongroup
, action_name
);
884 g_free (action_name
);
886 g_object_set (action
, "label", name
, NULL
);
888 g_object_unref (action
);
892 rb_shell_clipboard_playlist_visible_cb (RBStaticPlaylistSource
*source
,
894 RBShellClipboard
*clipboard
)
896 gboolean visible
= FALSE
;
900 g_object_get (source
, "visibility", &visible
, NULL
);
902 action_name
= generate_action_name (source
, clipboard
);
903 action
= gtk_action_group_get_action (clipboard
->priv
->actiongroup
, action_name
);
905 g_free (action_name
);
907 gtk_action_set_visible (action
, visible
);
908 g_object_unref (G_OBJECT (action
));
912 add_playlist_to_menu (GtkTreeModel
*model
,
915 RBShellClipboard
*clipboard
)
917 RhythmDBEntryType entry_type
;
918 RhythmDBEntryType source_entry_type
;
919 RBSource
*source
= NULL
;
924 gtk_tree_model_get (GTK_TREE_MODEL (model
), iter
,
925 RB_SOURCELIST_MODEL_COLUMN_SOURCE
, &source
, -1);
927 if (source
== NULL
) {
931 if (!RB_IS_STATIC_PLAYLIST_SOURCE (source
)) {
932 g_object_unref (source
);
936 /* FIXME: allow add-to-playlist for iPods and the like,
937 * based on the currently selected source
939 entry_type
= RHYTHMDB_ENTRY_TYPE_SONG
;
940 g_object_get (source
, "entry-type", &source_entry_type
, NULL
);
941 if (source_entry_type
!= entry_type
) {
942 g_object_unref (source
);
943 g_boxed_free (RHYTHMDB_TYPE_ENTRY_TYPE
, source_entry_type
);
947 action_name
= generate_action_name (RB_STATIC_PLAYLIST_SOURCE (source
), clipboard
);
948 action
= gtk_action_group_get_action (clipboard
->priv
->actiongroup
, action_name
);
949 if (action
== NULL
) {
952 g_object_get (source
, "name", &name
, NULL
);
953 action
= gtk_action_new (action_name
, name
, NULL
, NULL
);
954 gtk_action_group_add_action (clipboard
->priv
->actiongroup
, action
);
957 g_object_set_data (G_OBJECT (action
), "playlist-source", source
);
958 g_signal_connect_object (G_OBJECT (action
),
959 "activate", G_CALLBACK (rb_shell_clipboard_playlist_add_cb
),
962 g_signal_connect_object (source
,
963 "deleted", G_CALLBACK (rb_shell_clipboard_playlist_deleted_cb
),
965 g_signal_connect_object (source
,
966 "notify::name", G_CALLBACK (rb_shell_clipboard_playlist_renamed_cb
),
968 g_signal_connect_object (source
,
969 "notify::visibility", G_CALLBACK (rb_shell_clipboard_playlist_visible_cb
),
973 for (i
= 0; i
< num_playlist_menu_paths
; i
++) {
974 gtk_ui_manager_add_ui (clipboard
->priv
->ui_mgr
, clipboard
->priv
->playlist_menu_ui_id
,
975 playlist_menu_paths
[i
],
976 action_name
, action_name
,
977 GTK_UI_MANAGER_AUTO
, FALSE
);
980 g_boxed_free (RHYTHMDB_TYPE_ENTRY_TYPE
, source_entry_type
);
981 g_free (action_name
);
982 g_object_unref (source
);
988 rebuild_playlist_menu (RBShellClipboard
*clipboard
)
990 GtkTreeModel
*model
= NULL
;
991 GObject
*sourcelist
= NULL
;
993 rb_debug ("rebuilding add-to-playlist menu");
995 if (clipboard
->priv
->playlist_menu_ui_id
!= 0) {
996 gtk_ui_manager_remove_ui (clipboard
->priv
->ui_mgr
,
997 clipboard
->priv
->playlist_menu_ui_id
);
999 clipboard
->priv
->playlist_menu_ui_id
=
1000 gtk_ui_manager_new_merge_id (clipboard
->priv
->ui_mgr
);
1003 if (clipboard
->priv
->playlist_manager
!= NULL
) {
1004 g_object_get (clipboard
->priv
->playlist_manager
, "sourcelist", &sourcelist
, NULL
);
1007 if (sourcelist
!= NULL
) {
1008 g_object_get (sourcelist
, "model", &model
, NULL
);
1009 g_object_unref (sourcelist
);
1012 if (model
!= NULL
) {
1013 gtk_tree_model_foreach (model
, (GtkTreeModelForeachFunc
)add_playlist_to_menu
, clipboard
);
1014 g_object_unref (model
);
1019 rebuild_playlist_menu_idle (RBShellClipboard
*clipboard
)
1021 GDK_THREADS_ENTER ();
1022 rebuild_playlist_menu (clipboard
);
1023 clipboard
->priv
->idle_playlist_id
= 0;
1024 GDK_THREADS_LEAVE ();
1029 rb_shell_clipboard_playlist_added_cb (RBPlaylistManager
*mgr
,
1030 RBPlaylistSource
*source
,
1031 RBShellClipboard
*clipboard
)
1033 if (!RB_IS_STATIC_PLAYLIST_SOURCE (source
))
1036 if (clipboard
->priv
->idle_playlist_id
== 0) {
1037 clipboard
->priv
->idle_playlist_id
=
1038 g_idle_add ((GSourceFunc
)rebuild_playlist_menu_idle
, clipboard
);