2006-12-03 James Livingston <doclivingston@gmail.com>
[rhythmbox.git] / sources / rb-play-queue-source.c
blob8b9df990f8caa151afc39ce7a7fd10733baa62d6
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * Copyright (C) 2005 Jonathan Matthew <jonathan@kaolin.hn.org>
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.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include "config.h"
23 #include <libxml/tree.h>
24 #include <glib/gi18n.h>
25 #include <gtk/gtk.h>
26 #include <libgnomevfs/gnome-vfs-uri.h>
28 #include "rb-play-queue-source.h"
29 #include "rb-playlist-xml.h"
30 #include "rb-song-info.h"
31 #include "rb-util.h"
32 #include "rb-debug.h"
34 static GObject *rb_play_queue_source_constructor (GType type, guint n_construct_properties,
35 GObjectConstructParam *construct_properties);
36 static void rb_play_queue_source_get_property (GObject *object,
37 guint prop_id,
38 GValue *value,
39 GParamSpec *pspec);
40 static void rb_play_queue_source_track_info_cell_data_func (GtkTreeViewColumn *column,
41 GtkCellRenderer *renderer,
42 GtkTreeModel *tree_model,
43 GtkTreeIter *iter,
44 RBPlaylistSource *source);
45 static void rb_play_queue_source_row_inserted_cb (GtkTreeModel *model,
46 GtkTreePath *path,
47 GtkTreeIter *iter,
48 RBPlayQueueSource *source);
49 static void rb_play_queue_source_row_deleted_cb (GtkTreeModel *model,
50 GtkTreePath *path,
51 RBPlayQueueSource *source);
52 static void rb_play_queue_source_update_count (RBPlayQueueSource *source,
53 GtkTreeModel *model,
54 gint offset);
55 static void impl_show_entry_view_popup (RBPlaylistSource *source,
56 RBEntryView *view,
57 gboolean over_entry);
58 static void impl_save_contents_to_xml (RBPlaylistSource *source,
59 xmlNodePtr node);
60 static void rb_play_queue_source_cmd_clear (GtkAction *action,
61 RBPlayQueueSource *source);
62 static GList *impl_get_ui_actions (RBSource *source);
63 static gboolean impl_show_popup (RBSource *asource);
65 #define PLAY_QUEUE_SOURCE_SONGS_POPUP_PATH "/QueuePlaylistViewPopup"
66 #define PLAY_QUEUE_SOURCE_SIDEBAR_POPUP_PATH "/QueueSidebarViewPopup"
67 #define PLAY_QUEUE_SOURCE_POPUP_PATH "/QueueSourcePopup"
69 typedef struct _RBPlayQueueSourcePrivate RBPlayQueueSourcePrivate;
71 struct _RBPlayQueueSourcePrivate
73 RBEntryView *sidebar;
74 GtkTreeViewColumn *sidebar_column;
75 GtkActionGroup *action_group;
78 enum
80 PROP_0,
81 PROP_SIDEBAR
84 G_DEFINE_TYPE (RBPlayQueueSource, rb_play_queue_source, RB_TYPE_STATIC_PLAYLIST_SOURCE)
85 #define RB_PLAY_QUEUE_SOURCE_GET_PRIVATE(object) (G_TYPE_INSTANCE_GET_PRIVATE ((object), RB_TYPE_PLAY_QUEUE_SOURCE, RBPlayQueueSourcePrivate))
87 static GtkActionEntry rb_play_queue_source_actions [] =
89 { "ClearQueue", GTK_STOCK_CLEAR, N_("Clear _Queue"), NULL,
90 N_("Remove all songs from the play queue"),
91 G_CALLBACK (rb_play_queue_source_cmd_clear) }
94 static void
95 rb_play_queue_sync_playing_state (GObject *entry_view,
96 GParamSpec *pspec,
97 RBPlayQueueSource *source)
99 int state;
100 RBPlayQueueSourcePrivate *priv = RB_PLAY_QUEUE_SOURCE_GET_PRIVATE (source);
101 g_object_get (entry_view, "playing-state", &state, NULL);
102 rb_entry_view_set_state (priv->sidebar, state);
105 static void
106 rb_play_queue_source_finalize (GObject *object)
108 RBPlayQueueSourcePrivate *priv = RB_PLAY_QUEUE_SOURCE_GET_PRIVATE (object);
110 if (priv->action_group != NULL) {
111 g_object_unref (priv->action_group);
114 G_OBJECT_CLASS (rb_play_queue_source_parent_class)->finalize (object);
117 static void
118 rb_play_queue_source_class_init (RBPlayQueueSourceClass *klass)
120 GObjectClass *object_class = G_OBJECT_CLASS (klass);
121 RBSourceClass *source_class = RB_SOURCE_CLASS (klass);
122 RBPlaylistSourceClass *playlist_class = RB_PLAYLIST_SOURCE_CLASS (klass);
124 object_class->constructor = rb_play_queue_source_constructor;
125 object_class->get_property = rb_play_queue_source_get_property;
126 object_class->finalize = rb_play_queue_source_finalize;
128 source_class->impl_can_add_to_queue = (RBSourceFeatureFunc) rb_false_function;
129 source_class->impl_can_rename = (RBSourceFeatureFunc) rb_false_function;
130 source_class->impl_can_search = (RBSourceFeatureFunc) rb_false_function;
131 source_class->impl_can_browse = (RBSourceFeatureFunc) rb_false_function;
132 source_class->impl_get_ui_actions = impl_get_ui_actions;
133 source_class->impl_show_popup = impl_show_popup;
135 playlist_class->impl_show_entry_view_popup = impl_show_entry_view_popup;
136 playlist_class->impl_save_contents_to_xml = impl_save_contents_to_xml;
138 g_object_class_install_property (object_class,
139 PROP_SIDEBAR,
140 g_param_spec_object ("sidebar",
141 "sidebar",
142 "queue sidebar entry view",
143 RB_TYPE_ENTRY_VIEW,
144 G_PARAM_READABLE));
146 g_type_class_add_private (klass, sizeof (RBPlayQueueSourcePrivate));
149 static void
150 rb_play_queue_source_init (RBPlayQueueSource *source)
154 static GObject *
155 rb_play_queue_source_constructor (GType type,
156 guint n_construct_properties,
157 GObjectConstructParam *construct_properties)
159 GObjectClass *parent_class = G_OBJECT_CLASS (rb_play_queue_source_parent_class);
160 RBPlayQueueSource *source = RB_PLAY_QUEUE_SOURCE (
161 parent_class->constructor (type, n_construct_properties, construct_properties));
162 RBPlayQueueSourcePrivate *priv = RB_PLAY_QUEUE_SOURCE_GET_PRIVATE (source);
163 GObject *shell_player;
164 RBShell *shell;
165 RhythmDB *db = rb_playlist_source_get_db (RB_PLAYLIST_SOURCE (source));
166 GtkCellRenderer *renderer;
167 RhythmDBQueryModel *model;
169 g_object_get (source, "shell", &shell, NULL);
170 shell_player = rb_shell_get_player (shell);
171 g_object_unref (shell);
173 priv->action_group = _rb_source_register_action_group (RB_SOURCE (source),
174 "PlayQueueActions",
175 rb_play_queue_source_actions,
176 G_N_ELEMENTS (rb_play_queue_source_actions),
177 source);
179 priv->sidebar = rb_entry_view_new (db, shell_player, NULL, TRUE, TRUE);
181 g_object_set (G_OBJECT (priv->sidebar), "vscrollbar-policy", GTK_POLICY_AUTOMATIC, NULL);
183 priv->sidebar_column = gtk_tree_view_column_new ();
184 renderer = gtk_cell_renderer_text_new ();
185 gtk_tree_view_column_pack_start (priv->sidebar_column, renderer, TRUE);
186 gtk_tree_view_column_set_sizing (priv->sidebar_column, GTK_TREE_VIEW_COLUMN_FIXED);
187 gtk_tree_view_column_set_expand (priv->sidebar_column, TRUE);
188 gtk_tree_view_column_set_clickable (priv->sidebar_column, FALSE);
189 gtk_tree_view_column_set_cell_data_func (priv->sidebar_column, renderer,
190 (GtkTreeCellDataFunc)
191 rb_play_queue_source_track_info_cell_data_func,
192 source, NULL);
193 rb_entry_view_append_column_custom (priv->sidebar, priv->sidebar_column,
194 _("Play Queue"), "Title", NULL, 0);
195 rb_entry_view_set_columns_clickable (priv->sidebar, FALSE);
196 rb_playlist_source_setup_entry_view (RB_PLAYLIST_SOURCE (source), priv->sidebar);
198 model = rb_playlist_source_get_query_model (RB_PLAYLIST_SOURCE (source));
199 rb_entry_view_set_model (priv->sidebar, model);
201 /* sync the state of the main entry view and the sidebar */
202 g_signal_connect_object (G_OBJECT (rb_source_get_entry_view (RB_SOURCE (source))),
203 "notify::playing-state",
204 G_CALLBACK (rb_play_queue_sync_playing_state),
205 source, 0);
207 /* update the queued song count when the query model changes */
208 g_signal_connect_object (G_OBJECT (model), "row-inserted",
209 G_CALLBACK (rb_play_queue_source_row_inserted_cb),
210 source, 0);
211 g_signal_connect_object (G_OBJECT (model), "row-deleted",
212 G_CALLBACK (rb_play_queue_source_row_deleted_cb),
213 source, 0);
215 rb_play_queue_source_update_count (source, GTK_TREE_MODEL (model), 0);
217 return G_OBJECT (source);
220 static void
221 rb_play_queue_source_get_property (GObject *object,
222 guint prop_id,
223 GValue *value,
224 GParamSpec *pspec)
226 RBPlayQueueSourcePrivate *priv = RB_PLAY_QUEUE_SOURCE_GET_PRIVATE (object);
228 switch (prop_id)
230 case PROP_SIDEBAR:
231 g_value_set_object (value, priv->sidebar);
232 break;
233 default:
234 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
235 break;
239 RBSource *
240 rb_play_queue_source_new (RBShell *shell)
242 return RB_SOURCE (g_object_new (RB_TYPE_PLAY_QUEUE_SOURCE,
243 "name", _("Play Queue"),
244 "shell", shell,
245 "is-local", TRUE,
246 "entry-type", RHYTHMDB_ENTRY_TYPE_INVALID,
247 NULL));
250 void
251 rb_play_queue_source_sidebar_song_info (RBPlayQueueSource *source)
253 RBPlayQueueSourcePrivate *priv = RB_PLAY_QUEUE_SOURCE_GET_PRIVATE (source);
254 GtkWidget *song_info = NULL;
256 g_return_if_fail (priv->sidebar != NULL);
258 song_info = rb_song_info_new (RB_SOURCE (source), priv->sidebar);
259 if (song_info)
260 gtk_widget_show_all (song_info);
261 else
262 rb_debug ("failed to create dialog, or no selection!");
265 void
266 rb_play_queue_source_sidebar_delete (RBPlayQueueSource *source)
268 RBPlayQueueSourcePrivate *priv = RB_PLAY_QUEUE_SOURCE_GET_PRIVATE (source);
269 RBEntryView *sidebar = priv->sidebar;
270 GList *sel, *tem;
272 sel = rb_entry_view_get_selected_entries (sidebar);
273 for (tem = sel; tem != NULL; tem = tem->next)
274 rb_static_playlist_source_remove_entry (RB_STATIC_PLAYLIST_SOURCE (source),
275 (RhythmDBEntry *) tem->data);
276 g_list_free (sel);
279 void
280 rb_play_queue_source_clear_queue (RBPlayQueueSource *source)
282 GtkTreeIter iter;
283 RhythmDBEntry *entry;
284 RhythmDBQueryModel *model;
286 model = rb_playlist_source_get_query_model (RB_PLAYLIST_SOURCE (source));
287 while (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (model), &iter)) {
288 entry = rhythmdb_query_model_iter_to_entry (model, &iter);
290 if (entry != NULL) {
291 rhythmdb_query_model_remove_entry (model, entry);
292 rhythmdb_entry_unref (entry);
297 static void
298 impl_show_entry_view_popup (RBPlaylistSource *source,
299 RBEntryView *view,
300 gboolean over_entry)
302 RBPlayQueueSourcePrivate *priv = RB_PLAY_QUEUE_SOURCE_GET_PRIVATE (source);
303 const char *popup = PLAY_QUEUE_SOURCE_SONGS_POPUP_PATH;
304 if (view == priv->sidebar)
305 popup = PLAY_QUEUE_SOURCE_SIDEBAR_POPUP_PATH;
306 else if (!over_entry)
307 return;
308 _rb_source_show_popup (RB_SOURCE (source), popup);
311 static void
312 rb_play_queue_source_track_info_cell_data_func (GtkTreeViewColumn *column,
313 GtkCellRenderer *renderer,
314 GtkTreeModel *tree_model,
315 GtkTreeIter *iter,
316 RBPlaylistSource *source)
318 RhythmDBEntry *entry;
319 const char *title;
320 const char *artist;
321 const char *album;
322 char *markup;
324 gtk_tree_model_get (tree_model, iter, 0, &entry, -1);
326 title = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_TITLE);
327 artist = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ARTIST);
328 album = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ALBUM);
330 /* Translators: format is "<title> from <album> by <artist>" */
331 markup = g_markup_printf_escaped ("%s\n<span size=\"smaller\">%s <i>%s</i>\n%s <i>%s</i></span>",
332 title, _("from"), album, _("by"), artist);
334 g_object_set (G_OBJECT (renderer), "markup", markup, NULL);
336 g_free (markup);
337 rhythmdb_entry_unref (entry);
340 static void
341 rb_play_queue_source_row_inserted_cb (GtkTreeModel *model,
342 GtkTreePath *path,
343 GtkTreeIter *iter,
344 RBPlayQueueSource *source)
346 rb_play_queue_source_update_count (source, model, 0);
349 static void
350 rb_play_queue_source_row_deleted_cb (GtkTreeModel *model,
351 GtkTreePath *path,
352 RBPlayQueueSource *source)
354 rb_play_queue_source_update_count (source, model, -1);
357 static void
358 rb_play_queue_source_update_count (RBPlayQueueSource *source,
359 GtkTreeModel *model,
360 gint offset)
362 gint count = gtk_tree_model_iter_n_children (model, NULL) + offset;
363 RBPlayQueueSourcePrivate *priv = RB_PLAY_QUEUE_SOURCE_GET_PRIVATE (source);
364 char *name = _("Play Queue");
365 GtkAction *action;
367 /* update source name */
368 if (count > 0)
369 name = g_strdup_printf ("%s (%d)", name, count);
371 g_object_set (G_OBJECT (source), "name", name, NULL);
372 gtk_tree_view_column_set_title (priv->sidebar_column, name);
374 if (count > 0)
375 g_free (name);
377 /* make 'clear queue' action sensitive when there are entries in the queue */
378 action = gtk_action_group_get_action (priv->action_group,
379 "ClearQueue");
380 g_object_set (G_OBJECT (action), "sensitive", (count > 0), NULL);
383 static void
384 impl_save_contents_to_xml (RBPlaylistSource *source,
385 xmlNodePtr node)
387 ((RBPlaylistSourceClass*)rb_play_queue_source_parent_class)->impl_save_contents_to_xml (source, node);
388 xmlSetProp (node, RB_PLAYLIST_TYPE, RB_PLAYLIST_QUEUE);
391 static void
392 rb_play_queue_source_cmd_clear (GtkAction *action,
393 RBPlayQueueSource *source)
395 rb_play_queue_source_clear_queue (source);
398 static gboolean
399 impl_show_popup (RBSource *asource)
401 _rb_source_show_popup (asource, PLAY_QUEUE_SOURCE_POPUP_PATH);
402 return TRUE;
405 static GList *
406 impl_get_ui_actions (RBSource *source)
408 GList *actions = NULL;
410 actions = g_list_prepend (actions, g_strdup ("ClearQueue"));
411 return actions;