2006-12-14 Francisco Javier F. Serrador <serrador@openshine.com>
[rhythmbox.git] / shell / rb-shell.c
blob8f2b8bbc33fdf131c75ff63a3f745f980b1d3d99
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
3 * arch-tag: Implementation of main Rhythmbox shell
5 * Copyright (C) 2002, 2003 Jorn Baayen
6 * Copyright (C) 2003, 2004 Colin Walters <walters@gnome.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, or (at your option)
11 * 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.
24 #include <config.h>
26 #include <string.h>
27 #include <ctype.h>
28 #include <stdio.h>
29 #include <sys/stat.h>
31 #include <glib/gi18n.h>
32 #include <gdk/gdk.h>
33 #include <gdk/gdkx.h>
34 #include <gtk/gtk.h>
36 #include <X11/Xatom.h>
38 #include <libgnome/libgnome.h>
39 #include <libgnome/gnome-init.h>
40 #include <libgnome/gnome-program.h>
41 #include <libgnomeui/gnome-client.h>
43 #include <libgnomevfs/gnome-vfs.h>
44 #include <libgnomevfs/gnome-vfs-mime-utils.h>
46 #include "rb-shell.h"
47 #include "rb-debug.h"
48 #include "rb-dialog.h"
49 #ifdef WITH_RHYTHMDB_TREE
50 #include "rhythmdb-tree.h"
51 #elif defined(WITH_RHYTHMDB_GDA)
52 #include "rhythmdb-gda.h"
53 #else
54 #error "no database specified. configure broken?"
55 #endif
56 #include "rb-stock-icons.h"
57 #include "rb-sourcelist.h"
58 #include "rb-file-helpers.h"
59 #include "rb-source.h"
60 #include "rb-playlist-manager.h"
61 #include "rb-removable-media-manager.h"
62 #include "rb-preferences.h"
63 #include "rb-druid.h"
64 #include "rb-shell-clipboard.h"
65 #include "rb-shell-player.h"
66 #include "rb-source-header.h"
67 #include "rb-tray-icon.h"
68 #include "rb-statusbar.h"
69 #include "rb-shell-preferences.h"
70 #include "rb-library-source.h"
71 #include "rb-podcast-source.h"
72 #include "totem-pl-parser.h"
73 #include "rb-shell-preferences.h"
74 #include "rb-playlist-source.h"
75 #include "rb-static-playlist-source.h"
76 #include "rb-play-queue-source.h"
77 #include "eel-gconf-extensions.h"
78 #include "bacon-volume.h"
79 #include "rb-missing-files-source.h"
80 #include "rb-import-errors-source.h"
81 #include "rb-plugins-engine.h"
82 #include "rb-plugin-manager.h"
83 #include "rb-proxy-config.h"
84 #include "rb-util.h"
85 #include "rb-sourcelist-model.h"
86 #include "rb-song-info.h"
87 #include "rb-marshal.h"
89 static void rb_shell_class_init (RBShellClass *klass);
90 static void rb_shell_init (RBShell *shell);
91 static GObject *rb_shell_constructor (GType type,
92 guint n_construct_properties,
93 GObjectConstructParam *construct_properties);
94 static void rb_shell_finalize (GObject *object);
95 static void rb_shell_set_property (GObject *object,
96 guint prop_id,
97 const GValue *value,
98 GParamSpec *pspec);
99 static void rb_shell_get_property (GObject *object,
100 guint prop_id,
101 GValue *value,
102 GParamSpec *pspec);
103 static gboolean rb_shell_get_visibility (RBShell *shell);
104 static gboolean rb_shell_window_state_cb (GtkWidget *widget,
105 GdkEventWindowState *event,
106 RBShell *shell);
107 static gboolean rb_shell_window_configure_cb (GtkWidget *win,
108 GdkEventConfigure*event,
109 RBShell *shell);
110 static gboolean rb_shell_window_delete_cb (GtkWidget *win,
111 GdkEventAny *event,
112 RBShell *shell);
113 static void rb_shell_sync_window_state (RBShell *shell, gboolean dont_maximise);
114 static void rb_shell_sync_paned (RBShell *shell);
115 static void rb_shell_sync_party_mode (RBShell *shell);
116 static void rb_shell_select_source (RBShell *shell, RBSource *source);
117 static void source_selected_cb (RBSourceList *sourcelist,
118 RBSource *source,
119 RBShell *shell);
120 static void rb_shell_playing_source_changed_cb (RBShellPlayer *player,
121 RBSource *source,
122 RBShell *shell);
123 static void rb_shell_playing_entry_changed_cb (RBShellPlayer *player,
124 RhythmDBEntry *entry,
125 RBShell *shell);
126 static void rb_shell_playing_from_queue_cb (RBShellPlayer *player,
127 GParamSpec *arg,
128 RBShell *shell);
129 static void source_activated_cb (RBSourceList *sourcelist,
130 RBSource *source,
131 RBShell *shell);
132 static void rb_shell_db_save_error_cb (RhythmDB *db,
133 const char *uri, const GError *error,
134 RBShell *shell);
135 static void rb_shell_db_entry_added_cb (RhythmDB *db,
136 RhythmDBEntry *entry,
137 RBShell *shell);
138 static void rb_shell_druid_response_cb (GtkDialog *druid,
139 guint response,
140 RBShell *shell);
142 static void rb_shell_playlist_added_cb (RBPlaylistManager *mgr, RBSource *source, RBShell *shell);
143 static void rb_shell_playlist_created_cb (RBPlaylistManager *mgr, RBSource *source, RBShell *shell);
144 static void rb_shell_medium_added_cb (RBRemovableMediaManager *mgr, RBSource *source, RBShell *shell);
145 static void rb_shell_transfer_progress_cb (RBRemovableMediaManager *mgr,
146 gint done,
147 gint total,
148 double fraction,
149 RBShell *shell);
150 static void rb_shell_source_deleted_cb (RBSource *source, RBShell *shell);
151 static void rb_shell_set_window_title (RBShell *shell, const char *window_title);
152 static void rb_shell_set_elapsed (RBShell *shell, guint elapsed);
153 static void rb_shell_player_window_title_changed_cb (RBShellPlayer *player,
154 const char *window_title,
155 RBShell *shell);
156 static void rb_shell_player_elapsed_changed_cb (RBShellPlayer *player,
157 guint elapsed,
158 RBShell *shell);
159 static void rb_shell_cmd_about (GtkAction *action,
160 RBShell *shell);
161 static void rb_shell_cmd_contents (GtkAction *action,
162 RBShell *shell);
163 static void rb_shell_cmd_toggle_visibility (GtkAction *action,
164 RBShell *shell);
165 static void rb_shell_cmd_quit (GtkAction *action,
166 RBShell *shell);
167 static void rb_shell_cmd_preferences (GtkAction *action,
168 RBShell *shell);
169 static void rb_shell_cmd_plugins (GtkAction *action,
170 RBShell *shell);
171 static void rb_shell_cmd_add_folder_to_library (GtkAction *action,
172 RBShell *shell);
173 static void rb_shell_cmd_add_file_to_library (GtkAction *action,
174 RBShell *shell);
176 static void rb_shell_cmd_current_song (GtkAction *action,
177 RBShell *shell);
178 static void rb_shell_jump_to_current (RBShell *shell);
179 static void rb_shell_jump_to_entry_with_source (RBShell *shell, RBSource *source,
180 RhythmDBEntry *entry);
181 static void rb_shell_play_entry (RBShell *shell, RhythmDBEntry *entry);
182 static void rb_shell_cmd_view_all (GtkAction *action,
183 RBShell *shell);
184 static void rb_shell_view_sidepane_changed_cb (GtkAction *action,
185 RBShell *shell);
186 static void rb_shell_view_toolbar_changed_cb (GtkAction *action,
187 RBShell *shell);
188 static void rb_shell_view_party_mode_changed_cb (GtkAction *action,
189 RBShell *shell);
190 static void rb_shell_view_smalldisplay_changed_cb (GtkAction *action,
191 RBShell *shell);
192 static void rb_shell_view_statusbar_changed_cb (GtkAction *action,
193 RBShell *shell);
194 static void rb_shell_view_queue_as_sidebar_changed_cb (GtkAction *action,
195 RBShell *shell);
196 static void rb_shell_load_complete_cb (RhythmDB *db, RBShell *shell);
197 static void rb_shell_sync_sidepane_visibility (RBShell *shell);
198 static void rb_shell_sync_toolbar_state (RBShell *shell);
199 static void rb_shell_sync_smalldisplay (RBShell *shell);
200 static void rb_shell_sync_pane_visibility (RBShell *shell);
201 static void rb_shell_sync_statusbar_visibility (RBShell *shell);
202 static void rb_shell_set_visibility (RBShell *shell,
203 gboolean visible,
204 gboolean force);
205 static void sidepane_visibility_changed_cb (GConfClient *client,
206 guint cnxn_id,
207 GConfEntry *entry,
208 RBShell *shell);
209 static void toolbar_state_changed_cb (GConfClient *client,
210 guint cnxn_id,
211 GConfEntry *entry,
212 RBShell *shell);
213 static void smalldisplay_changed_cb (GConfClient *client,
214 guint cnxn_id,
215 GConfEntry *entry,
216 RBShell *shell);
217 static void sourcelist_drag_received_cb (RBSourceList *sourcelist,
218 RBSource *source,
219 GtkSelectionData *data,
220 RBShell *shell);
221 static gboolean rb_shell_show_popup_cb (RBSourceList *sourcelist,
222 RBSource *target,
223 RBShell *shell);
224 static gboolean tray_destroy_cb (GtkObject *object, RBShell *shell);
226 static gboolean save_yourself_cb (GnomeClient *client,
227 gint phase,
228 GnomeSaveStyle save_style,
229 gboolean shutdown,
230 GnomeInteractStyle interact_style,
231 gboolean fast,
232 RBShell *shell);
233 static void paned_size_allocate_cb (GtkWidget *widget,
234 GtkAllocation *allocation,
235 RBShell *shell);
236 static void sidebar_paned_size_allocate_cb (GtkWidget *widget,
237 GtkAllocation *allocation,
238 RBShell *shell);
239 static void rb_shell_volume_widget_changed_cb (BaconVolumeButton *vol,
240 RBShell *shell);
241 static void rb_shell_player_volume_changed_cb (RBShellPlayer *player,
242 GParamSpec *arg,
243 RBShell *shell);
245 static void session_die_cb (GnomeClient *client, RBShell *shell);
246 static void rb_shell_session_init (RBShell *shell);
248 enum
250 PROP_NONE,
251 PROP_ARGC,
252 PROP_ARGV,
253 PROP_NO_REGISTRATION,
254 PROP_NO_UPDATE,
255 PROP_DRY_RUN,
256 PROP_RHYTHMDB_FILE,
257 PROP_PLAYLISTS_FILE,
258 PROP_SELECTED_SOURCE,
259 PROP_DB,
260 PROP_UI_MANAGER,
261 PROP_CLIPBOARD,
262 PROP_PLAYLIST_MANAGER,
263 PROP_REMOVABLE_MEDIA_MANAGER,
264 PROP_SHELL_PLAYER,
265 PROP_WINDOW,
266 PROP_PREFS,
267 PROP_QUEUE_SOURCE,
268 PROP_PROXY_CONFIG,
269 PROP_LIBRARY_SOURCE,
270 PROP_SOURCELIST_MODEL,
271 PROP_VISIBILITY,
274 /* prefs */
275 #define CONF_STATE_WINDOW_WIDTH CONF_PREFIX "/state/window_width"
276 #define CONF_STATE_WINDOW_HEIGHT CONF_PREFIX "/state/window_height"
277 #define CONF_STATE_SMALL_WIDTH CONF_PREFIX "/state/small_width"
278 #define CONF_STATE_WINDOW_MAXIMIZED CONF_PREFIX "/state/window_maximized"
279 #define CONF_STATE_PANED_POSITION CONF_PREFIX "/state/paned_position"
280 #define CONF_STATE_ADD_DIR CONF_PREFIX "/state/add_dir"
281 #define CONF_STATE_WINDOW_X_POSITION CONF_PREFIX "/state/window_x_position"
282 #define CONF_STATE_WINDOW_Y_POSITION CONF_PREFIX "/state/window_y_position"
283 #define CONF_STATE_SOURCELIST_HEIGHT CONF_PREFIX "/state/sourcelist_height"
285 enum
287 VISIBILITY_CHANGED,
288 CREATE_SONG_INFO,
289 LAST_SIGNAL
292 static guint rb_shell_signals[LAST_SIGNAL] = { 0 };
294 G_DEFINE_TYPE (RBShell, rb_shell, G_TYPE_OBJECT)
296 struct RBShellPrivate
298 GtkWidget *window;
299 gboolean iconified;
300 guint idle_hide_mainwindow_id;
302 GtkUIManager *ui_manager;
303 GtkActionGroup *actiongroup;
304 guint source_ui_merge_id;
306 GtkWidget *main_vbox;
307 GtkWidget *paned;
308 GtkWidget *sourcelist;
309 GtkWidget *notebook;
310 GtkWidget *queue_paned;
311 GtkWidget *queue_sidebar;
313 GtkBox *sidebar_container;
314 GtkBox *top_container;
315 GtkBox *bottom_container;
317 GList *sources;
318 GHashTable *sources_hash;
320 guint async_state_save_id;
321 guint save_playlist_id;
322 guint save_db_id;
324 gboolean shutting_down;
325 gboolean load_complete;
327 int argc;
328 char **argv;
329 gboolean no_registration;
330 gboolean no_update;
331 gboolean dry_run;
332 char *rhythmdb_file;
333 char *playlists_file;
335 RhythmDB *db;
336 char *pending_entry;
338 RBShellPlayer *player_shell;
339 RBShellClipboard *clipboard_shell;
340 RBSourceHeader *source_header;
341 RBStatusbar *statusbar;
342 RBPlaylistManager *playlist_manager;
343 RBRemovableMediaManager *removable_media_manager;
345 GList *supported_media_extensions;
347 RBLibrarySource *library_source;
348 RBPodcastSource *podcast_source;
349 RBPlaylistSource *queue_source;
350 RBSource *missing_files_source;
351 RBSource *import_errors_source;
353 RBProxyConfig *proxy_config;
355 RBSource *selected_source;
357 GtkWidget *prefs;
358 GtkWidget *plugins;
360 RBTrayIcon *tray_icon;
361 GtkTooltips *tooltips;
362 GtkWidget *volume_button;
363 gboolean syncing_volume;
365 char *cached_title;
366 char *cached_duration;
367 gboolean cached_playing;
369 guint sidepane_visibility_notify_id;
370 guint toolbar_visibility_notify_id;
371 guint toolbar_style_notify_id;
372 guint smalldisplay_notify_id;
374 glong last_small_time; /* when we last changed small mode */
376 /* Cached copies of the gconf keys.
378 * To avoid race conditions, the only time the keys are actually read is at startup
380 guint window_width;
381 guint window_height;
382 guint small_width;
383 gboolean window_maximised;
384 gboolean window_small;
385 gboolean queue_as_sidebar;
386 gboolean statusbar_hidden;
387 gboolean party_mode;
388 gint window_x;
389 gint window_y;
390 gint paned_position;
391 gint sourcelist_height;
394 #define RB_SHELL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), RB_TYPE_SHELL, RBShellPrivate))
396 static GtkActionEntry rb_shell_actions [] =
398 { "Music", NULL, N_("_Music") },
399 { "Edit", NULL, N_("_Edit") },
400 { "View", NULL, N_("_View") },
401 { "Control", NULL, N_("_Control") },
402 { "Tools", NULL, N_("_Tools") },
403 { "Help", NULL, N_("_Help") },
405 { "MusicImportFolder", GTK_STOCK_OPEN, N_("_Import Folder..."), "<control>O",
406 N_("Choose folder to be added to the Library"),
407 G_CALLBACK (rb_shell_cmd_add_folder_to_library) },
408 { "MusicImportFile", GTK_STOCK_FILE, N_("Import _File..."), NULL,
409 N_("Choose file to be added to the Library"),
410 G_CALLBACK (rb_shell_cmd_add_file_to_library) },
411 { "HelpAbout", GTK_STOCK_ABOUT, N_("_About"), NULL,
412 N_("Show information about the music player"),
413 G_CALLBACK (rb_shell_cmd_about) },
414 { "HelpContents", GTK_STOCK_HELP, N_("_Contents"), "F1",
415 N_("Display music player help"),
416 G_CALLBACK (rb_shell_cmd_contents) },
417 { "MusicClose", GTK_STOCK_CLOSE, N_("_Close"), "<control>W",
418 N_("Hide the music player window"),
419 G_CALLBACK (rb_shell_cmd_toggle_visibility) },
420 { "MusicQuit", GTK_STOCK_QUIT, N_("_Quit"), "<control>Q",
421 N_("Quit the music player"),
422 G_CALLBACK (rb_shell_cmd_quit) },
423 { "EditPreferences", GTK_STOCK_PREFERENCES, N_("Prefere_nces..."), NULL,
424 N_("Edit music player preferences"),
425 G_CALLBACK (rb_shell_cmd_preferences) },
426 { "EditPlugins", NULL, N_("Plu_gins..."), NULL,
427 N_("Change and configure plugins"),
428 G_CALLBACK (rb_shell_cmd_plugins) },
429 { "ViewAll", NULL, N_("Show _All"), "<control>Y",
430 N_("Show all items in this music source"),
431 G_CALLBACK (rb_shell_cmd_view_all) },
432 { "ViewJumpToPlaying", GTK_STOCK_JUMP_TO, N_("_Jump to Playing Song"), "<control>J",
433 N_("Scroll the view to the currently playing song"),
434 G_CALLBACK (rb_shell_cmd_current_song) },
436 static guint rb_shell_n_actions = G_N_ELEMENTS (rb_shell_actions);
438 static GtkToggleActionEntry rb_shell_toggle_entries [] =
440 { "ViewSidePane", NULL, N_("Side _Pane"), "<control>S",
441 N_("Change the visibility of the side pane"),
442 G_CALLBACK (rb_shell_view_sidepane_changed_cb), TRUE },
443 { "ViewToolbar", NULL, N_("_Toolbar"), NULL,
444 N_("Change the visibility of the toolbar"),
445 G_CALLBACK (rb_shell_view_toolbar_changed_cb), TRUE },
446 { "ViewSmallDisplay", NULL, N_("_Small Display"), "<control>D",
447 N_("Make the main window smaller"),
448 G_CALLBACK (rb_shell_view_smalldisplay_changed_cb), },
449 { "ViewPartyMode", NULL, N_("_Party Mode"), "F11",
450 N_("Change the status of the party mode"),
451 G_CALLBACK (rb_shell_view_party_mode_changed_cb), FALSE },
452 { "ViewQueueAsSidebar", NULL, N_("Play _Queue as Side Pane"), "<control>K",
453 N_("Change whether the queue is visible as a source or a sidebar"),
454 G_CALLBACK (rb_shell_view_queue_as_sidebar_changed_cb) },
455 { "ViewStatusbar", NULL, N_("S_tatusbar"), NULL,
456 N_("Change the visibility of the statusbar"),
457 G_CALLBACK (rb_shell_view_statusbar_changed_cb), TRUE }
459 static guint rb_shell_n_toggle_entries = G_N_ELEMENTS (rb_shell_toggle_entries);
461 static void
462 rb_shell_class_init (RBShellClass *klass)
464 GObjectClass *object_class = (GObjectClass *) klass;
466 object_class->set_property = rb_shell_set_property;
467 object_class->get_property = rb_shell_get_property;
468 object_class->finalize = rb_shell_finalize;
469 object_class->constructor = rb_shell_constructor;
471 g_object_class_install_property (object_class,
472 PROP_ARGC,
473 g_param_spec_int ("argc",
474 "argc",
475 "Argument count",
476 0, 128,
477 0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
479 g_object_class_install_property (object_class,
480 PROP_ARGV,
481 g_param_spec_pointer ("argv",
482 "argv",
483 "Arguments",
484 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
486 g_object_class_install_property (object_class,
487 PROP_NO_REGISTRATION,
488 g_param_spec_boolean ("no-registration",
489 "no-registration",
490 "Whether or not to register",
491 FALSE,
492 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
494 g_object_class_install_property (object_class,
495 PROP_NO_UPDATE,
496 g_param_spec_boolean ("no-update",
497 "no-update",
498 "Whether or not to update the library",
499 FALSE,
500 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
502 g_object_class_install_property (object_class,
503 PROP_DRY_RUN,
504 g_param_spec_boolean ("dry-run",
505 "dry-run",
506 "Whether or not this is a dry run",
507 FALSE,
508 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
510 g_object_class_install_property (object_class,
511 PROP_RHYTHMDB_FILE,
512 g_param_spec_string ("rhythmdb-file",
513 "rhythmdb-file",
514 "The RhythmDB file to use",
515 #ifdef WITH_RHYTHMDB_TREE
516 "rhythmdb.xml",
517 #elif defined(WITH_RHYTHMDB_GDA)
518 "rhythmdb.sqlite", /* FIXME: correct extension? */
519 #endif
520 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
523 g_object_class_install_property (object_class,
524 PROP_PLAYLISTS_FILE,
525 g_param_spec_string ("playlists-file",
526 "playlists-file",
527 "The playlists file to use",
528 "playlists.xml",
529 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
533 g_object_class_install_property (object_class,
534 PROP_SELECTED_SOURCE,
535 g_param_spec_object ("selected-source",
536 "selected-source",
537 "Source which is currently selected",
538 RB_TYPE_SOURCE,
539 G_PARAM_READABLE));
541 g_object_class_install_property (object_class,
542 PROP_DB,
543 g_param_spec_object ("db",
544 "RhythmDB",
545 "RhythmDB object",
546 RHYTHMDB_TYPE,
547 G_PARAM_READABLE));
549 g_object_class_install_property (object_class,
550 PROP_UI_MANAGER,
551 g_param_spec_object ("ui-manager",
552 "GtkUIManager",
553 "GtkUIManager object",
554 GTK_TYPE_UI_MANAGER,
555 G_PARAM_READABLE));
557 g_object_class_install_property (object_class,
558 PROP_CLIPBOARD,
559 g_param_spec_object ("clipboard",
560 "RBShellClipboard",
561 "RBShellClipboard object",
562 RB_TYPE_SHELL_CLIPBOARD,
563 G_PARAM_READABLE));
564 g_object_class_install_property (object_class,
565 PROP_PLAYLIST_MANAGER,
566 g_param_spec_object ("playlist-manager",
567 "RBPlaylistManager",
568 "RBPlaylistManager object",
569 RB_TYPE_PLAYLIST_MANAGER,
570 G_PARAM_READABLE));
571 g_object_class_install_property (object_class,
572 PROP_SHELL_PLAYER,
573 g_param_spec_object ("shell-player",
574 "RBShellPlayer",
575 "RBShellPlayer object",
576 RB_TYPE_SHELL_PLAYER,
577 G_PARAM_READABLE));
578 g_object_class_install_property (object_class,
579 PROP_REMOVABLE_MEDIA_MANAGER,
580 g_param_spec_object ("removable-media-manager",
581 "RBRemovableMediaManager",
582 "RBRemovableMediaManager object",
583 RB_TYPE_REMOVABLE_MEDIA_MANAGER,
584 G_PARAM_READABLE));
585 g_object_class_install_property (object_class,
586 PROP_WINDOW,
587 g_param_spec_object ("window",
588 "GtkWindow",
589 "GtkWindow object",
590 GTK_TYPE_WINDOW,
591 G_PARAM_READABLE));
592 g_object_class_install_property (object_class,
593 PROP_PREFS,
594 g_param_spec_object ("prefs",
595 "RBShellPreferences",
596 "RBShellPreferences object",
597 RB_TYPE_SHELL_PREFERENCES,
598 G_PARAM_READABLE));
599 g_object_class_install_property (object_class,
600 PROP_QUEUE_SOURCE,
601 g_param_spec_object ("queue-source",
602 "queue-source",
603 "Queue source",
604 RB_TYPE_PLAY_QUEUE_SOURCE,
605 G_PARAM_READABLE));
606 g_object_class_install_property (object_class,
607 PROP_PROXY_CONFIG,
608 g_param_spec_object ("proxy-config",
609 "proxy-config",
610 "HTTP proxy configuration",
611 RB_TYPE_PROXY_CONFIG,
612 G_PARAM_READABLE));
613 g_object_class_install_property (object_class,
614 PROP_LIBRARY_SOURCE,
615 g_param_spec_object ("library-source",
616 "library-source",
617 "Library source",
618 RB_TYPE_LIBRARY_SOURCE,
619 G_PARAM_READABLE));
620 g_object_class_install_property (object_class,
621 PROP_SOURCELIST_MODEL,
622 g_param_spec_object ("sourcelist-model",
623 "sourcelist-model",
624 "RBSourcelistModel",
625 RB_TYPE_SOURCELIST_MODEL,
626 G_PARAM_READABLE));
628 g_object_class_install_property (object_class,
629 PROP_VISIBILITY,
630 g_param_spec_boolean ("visibility",
631 "visibility",
632 "Current window visibility",
633 TRUE,
634 G_PARAM_READWRITE));
636 rb_shell_signals[VISIBILITY_CHANGED] =
637 g_signal_new ("visibility_changed",
638 G_OBJECT_CLASS_TYPE (object_class),
639 G_SIGNAL_RUN_LAST,
640 G_STRUCT_OFFSET (RBShellClass, visibility_changed),
641 NULL, NULL,
642 g_cclosure_marshal_VOID__BOOLEAN,
643 G_TYPE_NONE,
645 G_TYPE_BOOLEAN);
647 rb_shell_signals[CREATE_SONG_INFO] =
648 g_signal_new ("create_song_info",
649 G_OBJECT_CLASS_TYPE (object_class),
650 G_SIGNAL_RUN_LAST,
651 G_STRUCT_OFFSET (RBShellClass, create_song_info),
652 NULL, NULL,
653 rb_marshal_VOID__OBJECT_BOOLEAN,
654 G_TYPE_NONE,
656 RB_TYPE_SONG_INFO, G_TYPE_BOOLEAN);
658 g_type_class_add_private (klass, sizeof (RBShellPrivate));
661 static void
662 rb_shell_init (RBShell *shell)
664 shell->priv = RB_SHELL_GET_PRIVATE (shell);
666 rb_dot_dir ();
668 rb_shell_session_init (shell);
670 eel_gconf_monitor_add (CONF_PREFIX);
673 static void
674 rb_shell_set_property (GObject *object,
675 guint prop_id,
676 const GValue *value,
677 GParamSpec *pspec)
679 RBShell *shell = RB_SHELL (object);
681 switch (prop_id)
683 case PROP_ARGC:
684 shell->priv->argc = g_value_get_int (value);
685 break;
686 case PROP_ARGV:
687 shell->priv->argv = g_value_get_pointer (value);
688 break;
689 case PROP_NO_REGISTRATION:
690 shell->priv->no_registration = g_value_get_boolean (value);
691 break;
692 case PROP_NO_UPDATE:
693 shell->priv->no_update = g_value_get_boolean (value);
694 break;
695 case PROP_DRY_RUN:
696 shell->priv->dry_run = g_value_get_boolean (value);
697 if (shell->priv->dry_run)
698 shell->priv->no_registration = TRUE;
699 break;
700 case PROP_RHYTHMDB_FILE:
701 g_free (shell->priv->rhythmdb_file);
702 shell->priv->rhythmdb_file = g_value_dup_string (value);
703 break;
704 case PROP_PLAYLISTS_FILE:
705 g_free (shell->priv->playlists_file);
706 shell->priv->playlists_file = g_value_dup_string (value);
707 break;
708 case PROP_VISIBILITY:
709 rb_shell_set_visibility (shell, g_value_get_boolean (value), FALSE);
710 break;
711 default:
712 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
713 break;
717 static void
718 rb_shell_get_property (GObject *object,
719 guint prop_id,
720 GValue *value,
721 GParamSpec *pspec)
723 RBShell *shell = RB_SHELL (object);
725 switch (prop_id)
727 case PROP_ARGC:
728 g_value_set_int (value, shell->priv->argc);
729 break;
730 case PROP_ARGV:
731 g_value_set_pointer (value, shell->priv->argv);
732 break;
733 case PROP_NO_REGISTRATION:
734 g_value_set_boolean (value, shell->priv->no_registration);
735 break;
736 case PROP_NO_UPDATE:
737 g_value_set_boolean (value, shell->priv->no_update);
738 break;
739 case PROP_DRY_RUN:
740 g_value_set_boolean (value, shell->priv->dry_run);
741 break;
742 case PROP_RHYTHMDB_FILE:
743 g_value_set_string (value, shell->priv->rhythmdb_file);
744 break;
745 case PROP_PLAYLISTS_FILE:
746 g_value_set_string (value, shell->priv->playlists_file);
747 break;
748 case PROP_DB:
749 g_value_set_object (value, shell->priv->db);
750 break;
751 case PROP_UI_MANAGER:
752 g_value_set_object (value, shell->priv->ui_manager);
753 break;
754 case PROP_CLIPBOARD:
755 g_value_set_object (value, shell->priv->clipboard_shell);
756 break;
757 case PROP_PLAYLIST_MANAGER:
758 g_value_set_object (value, shell->priv->playlist_manager);
759 break;
760 case PROP_SHELL_PLAYER:
761 g_value_set_object (value, shell->priv->player_shell);
762 break;
763 case PROP_REMOVABLE_MEDIA_MANAGER:
764 g_value_set_object (value, shell->priv->removable_media_manager);
765 break;
766 case PROP_SELECTED_SOURCE:
767 g_value_set_object (value, shell->priv->selected_source);
768 break;
769 case PROP_WINDOW:
770 g_value_set_object (value, shell->priv->window);
771 break;
772 case PROP_PREFS:
773 g_value_set_object (value, shell->priv->prefs);
774 break;
775 case PROP_QUEUE_SOURCE:
776 g_value_set_object (value, shell->priv->queue_source);
777 break;
778 case PROP_PROXY_CONFIG:
779 g_value_set_object (value, shell->priv->proxy_config);
780 break;
781 case PROP_LIBRARY_SOURCE:
782 g_value_set_object (value, shell->priv->library_source);
783 break;
784 case PROP_SOURCELIST_MODEL:
786 GtkTreeModel *model = NULL;
788 g_object_get (shell->priv->sourcelist, "model", &model, NULL);
789 g_value_set_object (value, model);
790 g_object_unref (model);
792 break;
793 case PROP_VISIBILITY:
794 g_value_set_boolean (value, rb_shell_get_visibility (shell));
795 break;
796 default:
797 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
798 break;
802 static gboolean
803 rb_shell_sync_state (RBShell *shell)
805 if (shell->priv->dry_run) {
806 rb_debug ("in dry-run mode, not syncing state");
807 return FALSE;
810 if (!shell->priv->load_complete) {
811 rb_debug ("load incomplete, not syncing state");
812 return FALSE;
815 rb_debug ("saving playlists");
816 rb_playlist_manager_save_playlists (shell->priv->playlist_manager,
817 TRUE);
819 rb_debug ("saving db");
820 rhythmdb_save (shell->priv->db);
821 return FALSE;
824 static gboolean
825 idle_save_rhythmdb (RBShell *shell)
827 rhythmdb_save (shell->priv->db);
829 shell->priv->save_db_id = 0;
831 return FALSE;
834 static gboolean
835 idle_save_playlist_manager (RBShell *shell)
837 GDK_THREADS_ENTER ();
838 rb_playlist_manager_save_playlists (shell->priv->playlist_manager,
839 FALSE);
840 GDK_THREADS_LEAVE ();
842 return TRUE;
845 static void
846 rb_shell_shutdown (RBShell *shell)
848 GdkDisplay *display;
850 if (shell->priv->shutting_down)
851 return;
852 shell->priv->shutting_down = TRUE;
854 /* Hide the main window and tray icon as soon as possible */
855 display = gtk_widget_get_display (shell->priv->window);
856 gtk_widget_hide (shell->priv->window);
857 gtk_widget_hide (GTK_WIDGET (shell->priv->tray_icon));
858 gdk_display_sync (display);
861 static void
862 rb_shell_finalize (GObject *object)
864 RBShell *shell = RB_SHELL (object);
866 rb_debug ("Finalizing shell");
868 rb_shell_player_stop (shell->priv->player_shell);
870 eel_gconf_monitor_remove (CONF_PREFIX);
871 eel_gconf_notification_remove (shell->priv->sidepane_visibility_notify_id);
872 eel_gconf_notification_remove (shell->priv->toolbar_visibility_notify_id);
873 eel_gconf_notification_remove (shell->priv->toolbar_style_notify_id);
874 eel_gconf_notification_remove (shell->priv->smalldisplay_notify_id);
876 g_list_free (shell->priv->supported_media_extensions);
878 gtk_widget_destroy (GTK_WIDGET (shell->priv->tray_icon));
880 if (shell->priv->save_playlist_id > 0) {
881 g_source_remove (shell->priv->save_playlist_id);
882 shell->priv->save_playlist_id = 0;
885 if (shell->priv->save_db_id > 0) {
886 g_source_remove (shell->priv->save_db_id);
887 shell->priv->save_db_id = 0;
890 if (shell->priv->queue_sidebar != NULL) {
891 g_object_unref (shell->priv->queue_sidebar);
894 rb_debug ("shutting down playlist manager");
895 rb_playlist_manager_shutdown (shell->priv->playlist_manager);
897 rb_debug ("unreffing playlist manager");
898 g_object_unref (G_OBJECT (shell->priv->playlist_manager));
900 rb_debug ("unreffing removable media manager");
901 g_object_unref (G_OBJECT (shell->priv->removable_media_manager));
903 g_object_unref (G_OBJECT (shell->priv->proxy_config));
905 rb_debug ("unreffing clipboard shell");
906 g_object_unref (G_OBJECT (shell->priv->clipboard_shell));
908 rb_debug ("destroying prefs");
909 if (shell->priv->prefs != NULL)
910 gtk_widget_destroy (shell->priv->prefs);
912 rb_debug ("destroying tooltips");
913 gtk_object_destroy (GTK_OBJECT (shell->priv->tooltips));
915 g_free (shell->priv->rhythmdb_file);
917 g_free (shell->priv->playlists_file);
919 rb_debug ("destroying window");
920 gtk_widget_destroy (shell->priv->window);
922 g_list_free (shell->priv->sources);
923 shell->priv->sources = NULL;
925 g_hash_table_destroy (shell->priv->sources_hash);
927 rb_debug ("shutting down DB");
928 rhythmdb_shutdown (shell->priv->db);
930 rb_debug ("unreffing DB");
931 g_object_unref (G_OBJECT (shell->priv->db));
933 ((GObjectClass*)rb_shell_parent_class)->finalize (G_OBJECT (shell));
935 rb_debug ("shell shutdown complete");
938 RBShell *
939 rb_shell_new (int argc,
940 char **argv,
941 gboolean no_registration,
942 gboolean no_update,
943 gboolean dry_run,
944 char *rhythmdb,
945 char *playlists)
947 RBShell *s;
948 char *pathname;
950 /* set default playlist name, if none supplied */
951 if (playlists)
952 pathname = g_strdup (playlists);
953 else
954 pathname = g_build_filename (rb_dot_dir (), "playlists.xml", NULL);
956 s = g_object_new (RB_TYPE_SHELL, "argc", argc, "argv", argv,
957 "no-registration", no_registration,
958 "no-update", no_update,
959 "dry-run", dry_run, "rhythmdb-file", rhythmdb,
960 "playlists-file", pathname, NULL);
962 g_free (pathname);
963 return s;
966 static void
967 construct_db (RBShell *shell)
969 char *pathname;
971 /* Initialize the database */
972 rb_debug ("creating database object");
973 rb_profile_start ("creating database object");
975 if (shell->priv->rhythmdb_file)
976 pathname = g_strdup (shell->priv->rhythmdb_file);
977 else
978 pathname = g_build_filename (rb_dot_dir (), "rhythmdb.xml", NULL);
980 #ifdef WITH_RHYTHMDB_TREE
981 shell->priv->db = rhythmdb_tree_new (pathname);
982 #elif defined(WITH_RHYTHMDB_GDA)
983 shell->priv->db = rhythmdb_gda_new (pathname);
984 #endif
985 g_free (pathname);
987 if (shell->priv->dry_run)
988 g_object_set (G_OBJECT (shell->priv->db), "dry-run", TRUE, NULL);
989 if (shell->priv->no_update)
990 g_object_set (G_OBJECT (shell->priv->db), "no-update", TRUE, NULL);
992 g_signal_connect_object (G_OBJECT (shell->priv->db), "load-complete",
993 G_CALLBACK (rb_shell_load_complete_cb), shell,
996 rb_profile_end ("creating database object");
999 static void
1000 construct_widgets (RBShell *shell)
1002 GtkWindow *win;
1004 rb_profile_start ("constructing widgets");
1006 /* initialize UI */
1007 win = GTK_WINDOW (gtk_window_new (GTK_WINDOW_TOPLEVEL));
1008 gtk_window_set_title (win, _("Music Player"));
1010 shell->priv->window = GTK_WIDGET (win);
1011 shell->priv->iconified = FALSE;
1012 g_signal_connect_object (G_OBJECT (win), "window-state-event",
1013 G_CALLBACK (rb_shell_window_state_cb),
1014 shell, 0);
1016 g_signal_connect_object (G_OBJECT (win), "configure-event",
1017 G_CALLBACK (rb_shell_window_configure_cb),
1018 shell, 0);
1020 /* connect after, so that things can affect behaviour */
1021 g_signal_connect_object (G_OBJECT (win), "delete_event",
1022 G_CALLBACK (rb_shell_window_delete_cb),
1023 shell, G_CONNECT_AFTER);
1025 rb_debug ("shell: initializing shell services");
1027 shell->priv->ui_manager = gtk_ui_manager_new ();
1028 shell->priv->source_ui_merge_id = gtk_ui_manager_new_merge_id (shell->priv->ui_manager);
1030 shell->priv->player_shell = rb_shell_player_new (shell->priv->db,
1031 shell->priv->ui_manager,
1032 shell->priv->actiongroup);
1033 g_signal_connect_object (G_OBJECT (shell->priv->player_shell),
1034 "playing-source-changed",
1035 G_CALLBACK (rb_shell_playing_source_changed_cb),
1036 shell, 0);
1037 g_signal_connect_object (G_OBJECT (shell->priv->player_shell),
1038 "playing-song-changed",
1039 G_CALLBACK (rb_shell_playing_entry_changed_cb),
1040 shell, 0);
1041 g_signal_connect_object (G_OBJECT (shell->priv->player_shell),
1042 "notify::playing-from-queue",
1043 G_CALLBACK (rb_shell_playing_from_queue_cb),
1044 shell, 0);
1045 g_signal_connect_object (G_OBJECT (shell->priv->player_shell),
1046 "window_title_changed",
1047 G_CALLBACK (rb_shell_player_window_title_changed_cb),
1048 shell, 0);
1049 g_signal_connect_object (G_OBJECT (shell->priv->player_shell),
1050 "elapsed_changed",
1051 G_CALLBACK (rb_shell_player_elapsed_changed_cb),
1052 shell, 0);
1053 shell->priv->clipboard_shell = rb_shell_clipboard_new (shell->priv->actiongroup,
1054 shell->priv->ui_manager,
1055 shell->priv->db);
1056 shell->priv->source_header = rb_source_header_new (shell->priv->ui_manager,
1057 shell->priv->actiongroup);
1058 gtk_widget_show_all (GTK_WIDGET (shell->priv->source_header));
1060 shell->priv->paned = gtk_hpaned_new ();
1062 shell->priv->sourcelist = rb_sourcelist_new (shell);
1063 gtk_widget_show_all (shell->priv->sourcelist);
1064 g_signal_connect_object (G_OBJECT (shell->priv->sourcelist), "drop_received",
1065 G_CALLBACK (sourcelist_drag_received_cb), shell, 0);
1066 g_signal_connect_object (G_OBJECT (shell->priv->sourcelist), "source_activated",
1067 G_CALLBACK (source_activated_cb), shell, 0);
1068 g_signal_connect_object (G_OBJECT (shell->priv->sourcelist), "show_popup",
1069 G_CALLBACK (rb_shell_show_popup_cb), shell, 0);
1071 shell->priv->statusbar = rb_statusbar_new (shell->priv->db,
1072 shell->priv->ui_manager);
1073 g_object_set (shell->priv->player_shell, "statusbar", shell->priv->statusbar, NULL);
1074 gtk_widget_show (GTK_WIDGET (shell->priv->statusbar));
1076 g_signal_connect_object (G_OBJECT (shell->priv->sourcelist), "selected",
1077 G_CALLBACK (source_selected_cb), shell, 0);
1079 shell->priv->notebook = gtk_notebook_new ();
1080 gtk_widget_show (shell->priv->notebook);
1081 gtk_notebook_set_show_tabs (GTK_NOTEBOOK (shell->priv->notebook), FALSE);
1082 gtk_notebook_set_show_border (GTK_NOTEBOOK (shell->priv->notebook), FALSE);
1083 g_signal_connect_object (G_OBJECT (shell->priv->sourcelist),
1084 "size-allocate",
1085 G_CALLBACK (paned_size_allocate_cb),
1086 shell, 0);
1088 shell->priv->queue_source = RB_PLAYLIST_SOURCE (rb_play_queue_source_new (shell));
1089 g_object_set (G_OBJECT(shell->priv->player_shell), "queue-source", shell->priv->queue_source, NULL);
1090 g_object_set (G_OBJECT(shell->priv->clipboard_shell), "queue-source", shell->priv->queue_source, NULL);
1091 rb_shell_append_source (shell, RB_SOURCE (shell->priv->queue_source), NULL);
1092 g_object_get (shell->priv->queue_source, "sidebar", &shell->priv->queue_sidebar, NULL);
1093 gtk_widget_show_all (shell->priv->queue_sidebar);
1094 gtk_widget_set_no_show_all (shell->priv->queue_sidebar, TRUE);
1096 /* places for plugins to put UI */
1097 shell->priv->top_container = GTK_BOX (gtk_vbox_new (FALSE, 0));
1098 shell->priv->bottom_container = GTK_BOX (gtk_vbox_new (FALSE, 0));
1099 shell->priv->sidebar_container = GTK_BOX (gtk_vbox_new (FALSE, 0));
1101 /* set up sidebars */
1102 shell->priv->paned = gtk_hpaned_new ();
1104 GtkWidget *vbox2 = gtk_vbox_new (FALSE, 0);
1106 shell->priv->queue_paned = gtk_vpaned_new ();
1107 gtk_paned_pack1 (GTK_PANED (shell->priv->queue_paned),
1108 shell->priv->sourcelist,
1109 FALSE, TRUE);
1110 gtk_paned_pack2 (GTK_PANED (shell->priv->queue_paned),
1111 shell->priv->queue_sidebar,
1112 TRUE, TRUE);
1113 gtk_container_child_set (GTK_CONTAINER (shell->priv->queue_paned),
1114 GTK_WIDGET (shell->priv->sourcelist),
1115 "resize", FALSE,
1116 NULL);
1118 gtk_box_pack_start (GTK_BOX (vbox2),
1119 GTK_WIDGET (shell->priv->source_header),
1120 FALSE, FALSE, 0);
1121 gtk_box_pack_start (GTK_BOX (vbox2),
1122 shell->priv->notebook,
1123 TRUE, TRUE, 0);
1125 gtk_box_pack_start_defaults (shell->priv->sidebar_container, shell->priv->queue_paned);
1126 gtk_paned_pack1 (GTK_PANED (shell->priv->paned),
1127 GTK_WIDGET (shell->priv->sidebar_container),
1128 FALSE, TRUE);
1129 gtk_paned_pack2 (GTK_PANED (shell->priv->paned),
1130 vbox2,
1131 TRUE, TRUE);
1132 gtk_widget_show (vbox2);
1135 g_signal_connect_object (G_OBJECT (shell->priv->queue_paned),
1136 "size-allocate",
1137 G_CALLBACK (sidebar_paned_size_allocate_cb),
1138 shell, 0);
1139 gtk_widget_show (shell->priv->paned);
1141 shell->priv->main_vbox = gtk_vbox_new (FALSE, 0);
1142 gtk_container_set_border_width (GTK_CONTAINER (shell->priv->main_vbox), 0);
1143 gtk_box_pack_start (GTK_BOX (shell->priv->main_vbox), GTK_WIDGET (shell->priv->player_shell), FALSE, TRUE, 6);
1144 gtk_widget_show (GTK_WIDGET (shell->priv->player_shell));
1146 gtk_box_pack_start (GTK_BOX (shell->priv->main_vbox), GTK_WIDGET (shell->priv->top_container), FALSE, TRUE, 0);
1147 gtk_box_pack_start (GTK_BOX (shell->priv->main_vbox), shell->priv->paned, TRUE, TRUE, 0);
1148 gtk_box_pack_start (GTK_BOX (shell->priv->main_vbox), GTK_WIDGET (shell->priv->bottom_container), FALSE, TRUE, 0);
1149 gtk_box_pack_start (GTK_BOX (shell->priv->main_vbox), GTK_WIDGET (shell->priv->statusbar), FALSE, TRUE, 0);
1150 gtk_widget_show_all (shell->priv->main_vbox);
1152 gtk_container_add (GTK_CONTAINER (win), shell->priv->main_vbox);
1154 shell->priv->proxy_config = rb_proxy_config_new ();
1156 rb_profile_end ("constructing widgets");
1159 static void
1160 construct_sources (RBShell *shell)
1162 rb_profile_start ("constructing sources");
1164 shell->priv->library_source = RB_LIBRARY_SOURCE (rb_library_source_new (shell));
1165 rb_shell_append_source (shell, RB_SOURCE (shell->priv->library_source), NULL);
1166 shell->priv->podcast_source = RB_PODCAST_SOURCE (rb_podcast_source_new (shell));
1167 rb_shell_append_source (shell, RB_SOURCE (shell->priv->podcast_source), NULL);
1168 shell->priv->missing_files_source = rb_missing_files_source_new (shell, shell->priv->library_source);
1169 rb_shell_append_source (shell, shell->priv->missing_files_source, RB_SOURCE (shell->priv->library_source));
1170 shell->priv->import_errors_source = rb_import_errors_source_new (shell, shell->priv->library_source);
1171 rb_shell_append_source (shell, shell->priv->import_errors_source, RB_SOURCE (shell->priv->library_source));
1173 /* Initialize playlist manager */
1174 rb_debug ("shell: creating playlist manager");
1175 shell->priv->playlist_manager = rb_playlist_manager_new (shell,
1176 RB_SOURCELIST (shell->priv->sourcelist), shell->priv->playlists_file);
1178 g_object_set (G_OBJECT(shell->priv->clipboard_shell), "playlist-manager", shell->priv->playlist_manager, NULL);
1180 g_signal_connect_object (G_OBJECT (shell->priv->playlist_manager), "playlist_added",
1181 G_CALLBACK (rb_shell_playlist_added_cb), shell, 0);
1182 g_signal_connect_object (G_OBJECT (shell->priv->playlist_manager), "playlist_created",
1183 G_CALLBACK (rb_shell_playlist_created_cb), shell, 0);
1185 /* Initialize removable media manager */
1186 rb_debug ("shell: creating removable media manager");
1187 shell->priv->removable_media_manager = rb_removable_media_manager_new (shell,
1188 RB_SOURCELIST (shell->priv->sourcelist));
1190 g_signal_connect_object (G_OBJECT (shell->priv->removable_media_manager), "medium_added",
1191 G_CALLBACK (rb_shell_medium_added_cb), shell, 0);
1192 g_signal_connect_object (G_OBJECT (shell->priv->removable_media_manager), "transfer-progress",
1193 G_CALLBACK (rb_shell_transfer_progress_cb), shell, 0);
1195 rb_profile_end ("constructing sources");
1198 static void
1199 construct_load_ui (RBShell *shell)
1201 GtkWidget *menubar;
1202 GtkWidget *toolbar;
1203 GtkWidget *hbox;
1204 GtkToolItem *tool_item;
1205 GError *error = NULL;
1207 rb_debug ("shell: loading ui");
1208 rb_profile_start ("loading ui");
1210 gtk_ui_manager_insert_action_group (shell->priv->ui_manager,
1211 shell->priv->actiongroup, 0);
1212 gtk_ui_manager_add_ui_from_file (shell->priv->ui_manager,
1213 rb_file ("rhythmbox-ui.xml"), &error);
1215 gtk_ui_manager_ensure_update (shell->priv->ui_manager);
1216 gtk_window_add_accel_group (GTK_WINDOW (shell->priv->window),
1217 gtk_ui_manager_get_accel_group (shell->priv->ui_manager));
1218 menubar = gtk_ui_manager_get_widget (shell->priv->ui_manager, "/MenuBar");
1220 gtk_box_pack_start (GTK_BOX (shell->priv->main_vbox), menubar, FALSE, FALSE, 0);
1221 gtk_box_reorder_child (GTK_BOX (shell->priv->main_vbox), menubar, 0);
1223 hbox = gtk_hbox_new (FALSE, 0);
1224 gtk_box_pack_start (GTK_BOX (shell->priv->main_vbox), hbox, FALSE, FALSE, 0);
1225 gtk_box_reorder_child (GTK_BOX (shell->priv->main_vbox), hbox, 1);
1227 toolbar = gtk_ui_manager_get_widget (shell->priv->ui_manager, "/ToolBar");
1228 gtk_box_pack_start_defaults (GTK_BOX (hbox), toolbar);
1230 shell->priv->volume_button = bacon_volume_button_new (GTK_ICON_SIZE_LARGE_TOOLBAR,
1231 0.0, 1.0, 0.02);
1232 g_signal_connect (shell->priv->volume_button, "value-changed",
1233 G_CALLBACK (rb_shell_volume_widget_changed_cb),
1234 shell);
1235 g_signal_connect (shell->priv->player_shell, "notify::volume",
1236 G_CALLBACK (rb_shell_player_volume_changed_cb),
1237 shell);
1238 rb_shell_player_volume_changed_cb (shell->priv->player_shell, NULL, shell);
1240 tool_item = gtk_tool_item_new ();
1241 gtk_tool_item_set_expand (tool_item, TRUE);
1242 gtk_widget_show (GTK_WIDGET (tool_item));
1243 gtk_toolbar_insert (GTK_TOOLBAR (toolbar), tool_item, -1);
1245 tool_item = gtk_tool_item_new ();
1246 gtk_container_add (GTK_CONTAINER (tool_item), shell->priv->volume_button);
1247 gtk_widget_show_all (GTK_WIDGET (tool_item));
1248 gtk_toolbar_insert (GTK_TOOLBAR (toolbar), tool_item, -1);
1250 gtk_widget_show (hbox);
1252 shell->priv->tooltips = gtk_tooltips_new ();
1253 gtk_tooltips_enable (shell->priv->tooltips);
1255 gtk_tooltips_set_tip (GTK_TOOLTIPS (shell->priv->tooltips),
1256 GTK_WIDGET (shell->priv->volume_button),
1257 _("Change the music volume"), NULL);
1259 if (error != NULL) {
1260 g_warning ("Couldn't merge %s: %s",
1261 rb_file ("rhythmbox-ui.xml"), error->message);
1262 g_clear_error (&error);
1265 rb_profile_end ("loading ui");
1268 static gboolean
1269 _scan_idle (RBShell *shell)
1271 GDK_THREADS_ENTER ();
1272 rb_removable_media_manager_scan (shell->priv->removable_media_manager);
1273 GDK_THREADS_LEAVE ();
1274 return FALSE;
1277 static GObject *
1278 rb_shell_constructor (GType type,
1279 guint n_construct_properties,
1280 GObjectConstructParam *construct_properties)
1282 RBShell *shell;
1284 shell = RB_SHELL (((GObjectClass*)rb_shell_parent_class)
1285 ->constructor (type, n_construct_properties, construct_properties));
1287 rb_debug ("Constructing shell");
1288 rb_profile_start ("constructing shell");
1290 shell->priv->actiongroup = gtk_action_group_new ("MainActions");
1291 gtk_action_group_set_translation_domain (shell->priv->actiongroup,
1292 GETTEXT_PACKAGE);
1293 gtk_action_group_add_actions (shell->priv->actiongroup,
1294 rb_shell_actions,
1295 rb_shell_n_actions, shell);
1296 gtk_action_group_add_toggle_actions (shell->priv->actiongroup,
1297 rb_shell_toggle_entries,
1298 rb_shell_n_toggle_entries,
1299 shell);
1301 construct_db (shell);
1303 /* initialize shell services */
1304 construct_widgets (shell);
1306 rb_debug ("shell: setting up tray icon");
1307 tray_destroy_cb (NULL, shell);
1309 rb_debug ("shell: adding gconf notification");
1310 /* sync state */
1311 shell->priv->sidepane_visibility_notify_id =
1312 eel_gconf_notification_add (CONF_UI_SIDEPANE_HIDDEN,
1313 (GConfClientNotifyFunc) sidepane_visibility_changed_cb,
1314 shell);
1315 shell->priv->toolbar_visibility_notify_id =
1316 eel_gconf_notification_add (CONF_UI_TOOLBAR_HIDDEN,
1317 (GConfClientNotifyFunc) toolbar_state_changed_cb,
1318 shell);
1319 shell->priv->toolbar_style_notify_id =
1320 eel_gconf_notification_add (CONF_UI_TOOLBAR_STYLE,
1321 (GConfClientNotifyFunc) toolbar_state_changed_cb,
1322 shell);
1323 shell->priv->smalldisplay_notify_id =
1324 eel_gconf_notification_add (CONF_UI_SMALL_DISPLAY,
1325 (GConfClientNotifyFunc) smalldisplay_changed_cb,
1326 shell);
1328 /* read the cached copies of the gconf keys */
1329 shell->priv->window_width = eel_gconf_get_integer (CONF_STATE_WINDOW_WIDTH);
1330 shell->priv->window_height = eel_gconf_get_integer (CONF_STATE_WINDOW_HEIGHT);
1331 shell->priv->small_width = eel_gconf_get_integer (CONF_STATE_SMALL_WIDTH);
1332 shell->priv->window_maximised = eel_gconf_get_boolean (CONF_STATE_WINDOW_MAXIMIZED);
1333 shell->priv->window_small = eel_gconf_get_boolean (CONF_UI_SMALL_DISPLAY);
1334 shell->priv->queue_as_sidebar = eel_gconf_get_boolean (CONF_UI_QUEUE_AS_SIDEBAR);
1335 shell->priv->window_x = eel_gconf_get_integer (CONF_STATE_WINDOW_X_POSITION);
1336 shell->priv->window_y = eel_gconf_get_integer (CONF_STATE_WINDOW_Y_POSITION);
1337 shell->priv->paned_position = eel_gconf_get_integer (CONF_STATE_PANED_POSITION);
1338 shell->priv->sourcelist_height = eel_gconf_get_integer (CONF_STATE_SOURCELIST_HEIGHT);
1339 shell->priv->statusbar_hidden = eel_gconf_get_boolean (CONF_UI_STATUSBAR_HIDDEN);
1341 rb_debug ("shell: syncing with gconf");
1342 rb_shell_sync_sidepane_visibility (shell);
1343 rb_shell_sync_pane_visibility (shell);
1345 g_signal_connect_object (G_OBJECT (shell->priv->db), "save-error",
1346 G_CALLBACK (rb_shell_db_save_error_cb), shell, 0);
1347 g_signal_connect_object (G_OBJECT (shell->priv->db), "entry-added",
1348 G_CALLBACK (rb_shell_db_entry_added_cb), shell, 0);
1350 construct_sources (shell);
1352 construct_load_ui (shell);
1354 rb_shell_sync_window_state (shell, FALSE);
1355 rb_shell_sync_smalldisplay (shell);
1356 rb_shell_sync_party_mode (shell);
1357 rb_shell_sync_toolbar_state (shell);
1359 rb_shell_select_source (shell, RB_SOURCE (shell->priv->library_source));
1361 rb_plugins_engine_init (shell);
1363 g_idle_add ((GSourceFunc)_scan_idle, shell);
1365 /* GO GO GO! */
1366 rb_debug ("loading database");
1367 rhythmdb_load (shell->priv->db);
1369 rb_debug ("shell: syncing window state");
1370 rb_shell_sync_paned (shell);
1371 gtk_widget_show_all (GTK_WIDGET (shell->priv->tray_icon));
1373 /* Stop here if this is the first time. */
1374 if (!eel_gconf_get_boolean (CONF_FIRST_TIME)) {
1375 RBDruid *druid;
1376 druid = rb_druid_new (shell->priv->db);
1377 g_signal_connect (G_OBJECT (druid),
1378 "response",
1379 G_CALLBACK (rb_shell_druid_response_cb),
1380 shell);
1382 gtk_widget_show_all (GTK_WIDGET (druid));
1383 } else {
1384 rb_shell_set_visibility (shell, eel_gconf_get_boolean (CONF_STATE_WINDOW_VISIBLE), TRUE);
1385 gdk_notify_startup_complete ();
1388 /* focus play if small, the entry view if not */
1389 if (shell->priv->window_small) {
1390 GtkWidget *play_button;
1392 play_button = gtk_ui_manager_get_widget (shell->priv->ui_manager, "/ToolBar/Play");
1393 gtk_widget_grab_focus (play_button);
1394 } else {
1395 RBEntryView *view;
1397 view = rb_source_get_entry_view (RB_SOURCE (shell->priv->library_source));
1398 gtk_widget_grab_focus (GTK_WIDGET (view));
1401 rb_profile_end ("constructing shell");
1403 return G_OBJECT (shell);
1406 /* Based on a function found in wnck */
1407 static void
1408 set_icon_geometry (GdkWindow *window,
1409 int x,
1410 int y,
1411 int width,
1412 int height)
1414 gulong data[4];
1415 Display *dpy = gdk_x11_drawable_get_xdisplay (window);
1417 data[0] = x;
1418 data[1] = y;
1419 data[2] = width;
1420 data[3] = height;
1422 XChangeProperty (dpy,
1423 GDK_WINDOW_XID (window),
1424 gdk_x11_get_xatom_by_name_for_display (gdk_drawable_get_display (window),
1425 "_NET_WM_ICON_GEOMETRY"),
1426 XA_CARDINAL, 32, PropModeReplace,
1427 (guchar *)&data, 4);
1430 static gboolean
1431 rb_shell_window_state_cb (GtkWidget *widget,
1432 GdkEventWindowState *event,
1433 RBShell *shell)
1435 shell->priv->iconified = ((event->new_window_state & GDK_WINDOW_STATE_ICONIFIED) != 0);
1437 if (event->changed_mask & (GDK_WINDOW_STATE_WITHDRAWN | GDK_WINDOW_STATE_ICONIFIED)) {
1438 g_signal_emit (shell, rb_shell_signals[VISIBILITY_CHANGED], 0,
1439 rb_shell_get_visibility (shell));
1442 /* don't save maximized state when is hidden */
1443 if (!GTK_WIDGET_VISIBLE(shell->priv->window))
1444 return FALSE;
1446 if (event->changed_mask & GDK_WINDOW_STATE_MAXIMIZED) {
1447 gboolean maximised = ((event->new_window_state & GDK_WINDOW_STATE_MAXIMIZED) != 0);
1449 gtk_statusbar_set_has_resize_grip (GTK_STATUSBAR (shell->priv->statusbar),
1450 !maximised);
1451 if (!shell->priv->window_small) {
1452 shell->priv->window_maximised = maximised;
1453 eel_gconf_set_boolean (CONF_STATE_WINDOW_MAXIMIZED,
1454 shell->priv->window_maximised);
1456 rb_shell_sync_window_state (shell, TRUE);
1457 rb_shell_sync_paned (shell);
1460 return FALSE;
1463 static gboolean
1464 rb_shell_get_visibility (RBShell *shell)
1466 GdkWindowState state;
1468 if (!GTK_WIDGET_REALIZED (shell->priv->window))
1469 return FALSE;
1470 if (shell->priv->iconified)
1471 return FALSE;
1473 state = gdk_window_get_state (GTK_WIDGET (shell->priv->window)->window);
1474 if (state & (GDK_WINDOW_STATE_WITHDRAWN | GDK_WINDOW_STATE_ICONIFIED))
1475 return FALSE;
1477 return TRUE;
1480 static gboolean
1481 idle_hide_mainwindow (gpointer data)
1483 RBShell *shell = RB_SHELL (data);
1485 GDK_THREADS_ENTER ();
1487 gtk_widget_hide (GTK_WIDGET (shell->priv->window));
1489 g_signal_emit (shell, rb_shell_signals[VISIBILITY_CHANGED], 0, FALSE);
1491 g_object_unref (shell);
1493 GDK_THREADS_LEAVE ();
1495 return FALSE;
1498 static void
1499 rb_shell_set_visibility (RBShell *shell,
1500 gboolean visible,
1501 gboolean force)
1503 gboolean current_visible;
1505 rb_profile_start ("changing shell visibility");
1507 current_visible = rb_shell_get_visibility (shell);
1508 if (!force && visible == current_visible) {
1509 rb_profile_end ("changing shell visibility");
1510 return;
1513 /* FIXME - see below */
1514 if (shell->priv->idle_hide_mainwindow_id > 0)
1515 g_source_remove (shell->priv->idle_hide_mainwindow_id);
1516 shell->priv->idle_hide_mainwindow_id = 0;
1518 /* don't minimise if we have no tray icon */
1519 if (visible || (!force && !egg_tray_icon_have_manager (EGG_TRAY_ICON (shell->priv->tray_icon)))) {
1520 rb_debug ("showing main window");
1521 rb_shell_sync_window_state (shell, FALSE);
1523 if (egg_tray_icon_have_manager (EGG_TRAY_ICON (shell->priv->tray_icon)))
1524 gtk_window_set_skip_taskbar_hint (GTK_WINDOW (shell->priv->window), FALSE);
1525 gtk_widget_show (GTK_WIDGET (shell->priv->window));
1526 gtk_window_deiconify (GTK_WINDOW (shell->priv->window));
1528 if (GTK_WIDGET_REALIZED (GTK_WIDGET (shell->priv->window)))
1529 rb_shell_present (shell, gtk_get_current_event_time (), NULL);
1530 else
1531 gtk_widget_show_all (GTK_WIDGET (shell->priv->window));
1533 g_signal_emit (shell, rb_shell_signals[VISIBILITY_CHANGED], 0, visible);
1535 eel_gconf_set_boolean (CONF_STATE_WINDOW_VISIBLE, TRUE);
1536 } else {
1537 int x, y, width, height;
1539 rb_debug ("hiding main window");
1540 shell->priv->iconified = TRUE;
1541 rb_tray_icon_get_geom (shell->priv->tray_icon,
1542 &x, &y, &width, &height);
1543 if (GTK_WIDGET_REALIZED (GTK_WIDGET (shell->priv->window)))
1544 set_icon_geometry (GTK_WIDGET (shell->priv->window)->window,
1545 x, y, width, height);
1546 if (egg_tray_icon_have_manager (EGG_TRAY_ICON (shell->priv->tray_icon)))
1547 gtk_window_set_skip_taskbar_hint (GTK_WINDOW (shell->priv->window), TRUE);
1548 gtk_window_iconify (GTK_WINDOW (shell->priv->window));
1550 /* FIMXE - this is horribly evil racy workaround for a
1551 * current bug in the tasklist not noticing our hint
1552 * change
1554 shell->priv->idle_hide_mainwindow_id =
1555 g_timeout_add (250, idle_hide_mainwindow, g_object_ref (shell));
1557 eel_gconf_set_boolean (CONF_STATE_WINDOW_VISIBLE, FALSE);
1560 rb_profile_end ("changing shell visibility");
1563 static gboolean
1564 rb_shell_window_configure_cb (GtkWidget *win,
1565 GdkEventConfigure *event,
1566 RBShell *shell)
1568 if (shell->priv->window_small) {
1569 rb_debug ("storing small window width of %d", event->width);
1570 shell->priv->small_width = event->width;
1571 eel_gconf_set_integer (CONF_STATE_SMALL_WIDTH, event->width);
1572 } else if (!shell->priv->window_maximised && !shell->priv->iconified) {
1573 rb_debug ("storing window size of %d:%d", event->width, event->height);
1574 shell->priv->window_width = event->width;
1575 shell->priv->window_height = event->height;
1576 eel_gconf_set_integer (CONF_STATE_WINDOW_WIDTH, event->width);
1577 eel_gconf_set_integer (CONF_STATE_WINDOW_HEIGHT, event->height);
1580 if (shell->priv->window_small || !shell->priv->window_maximised) {
1581 gtk_window_get_position (GTK_WINDOW(shell->priv->window),
1582 &shell->priv->window_x,
1583 &shell->priv->window_y);
1585 eel_gconf_set_integer (CONF_STATE_WINDOW_X_POSITION, shell->priv->window_x);
1586 eel_gconf_set_integer (CONF_STATE_WINDOW_Y_POSITION, shell->priv->window_y);
1589 return FALSE;
1592 static gboolean
1593 rb_shell_window_delete_cb (GtkWidget *win,
1594 GdkEventAny *event,
1595 RBShell *shell)
1597 if (shell->priv->party_mode) {
1598 return TRUE;
1601 rb_shell_quit (shell, NULL);
1603 return TRUE;
1606 static void
1607 rb_shell_sync_window_state (RBShell *shell,
1608 gboolean dont_maximise)
1610 GdkGeometry hints;
1612 rb_profile_start ("syncing window state");
1614 if (shell->priv->window_small) {
1615 hints.min_height = -1;
1616 hints.min_width = -1;
1617 hints.max_height = -1;
1618 hints.max_width = 3000;
1619 gtk_window_set_default_size (GTK_WINDOW (shell->priv->window),
1620 shell->priv->small_width, 0);
1621 gtk_window_resize (GTK_WINDOW (shell->priv->window),
1622 shell->priv->small_width, 1);
1623 gtk_window_set_geometry_hints (GTK_WINDOW (shell->priv->window),
1624 NULL,
1625 &hints,
1626 GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE);
1627 gtk_window_unmaximize (GTK_WINDOW (shell->priv->window));
1628 rb_debug ("syncing small window width to %d", shell->priv->small_width);
1629 } else {
1630 if (!dont_maximise) {
1631 if (shell->priv->window_maximised)
1632 gtk_window_maximize (GTK_WINDOW (shell->priv->window));
1633 else
1634 gtk_window_unmaximize (GTK_WINDOW (shell->priv->window));
1637 gtk_window_set_default_size (GTK_WINDOW (shell->priv->window),
1638 shell->priv->window_width,
1639 shell->priv->window_height);
1640 gtk_window_resize (GTK_WINDOW (shell->priv->window),
1641 shell->priv->window_width,
1642 shell->priv->window_height);
1643 gtk_window_set_geometry_hints (GTK_WINDOW (shell->priv->window),
1644 NULL,
1645 &hints,
1649 gtk_window_move (GTK_WINDOW (shell->priv->window),
1650 shell->priv->window_x,
1651 shell->priv->window_y);
1652 rb_profile_end ("syncing window state");
1655 static void
1656 source_selected_cb (RBSourceList *sourcelist,
1657 RBSource *source,
1658 RBShell *shell)
1660 rb_debug ("source selected");
1661 rb_shell_select_source (shell, source);
1664 static void
1665 source_activated_cb (RBSourceList *sourcelist,
1666 RBSource *source,
1667 RBShell *shell)
1669 rb_debug ("source activated");
1671 /* Stop the playing source, if any */
1672 rb_shell_player_set_playing_source (shell->priv->player_shell, NULL);
1674 /* Select the new one, and start it playing */
1675 rb_shell_select_source (shell, source);
1676 rb_shell_player_set_playing_source (shell->priv->player_shell, source);
1677 /* Ignore error from here */
1678 rb_shell_player_playpause (shell->priv->player_shell, FALSE, NULL);
1681 static void
1682 rb_shell_db_save_error_cb (RhythmDB *db,
1683 const char *uri, const GError *error,
1684 RBShell *shell)
1686 rb_error_dialog (GTK_WINDOW (shell->priv->window),
1687 _("Error while saving song information"),
1688 "%s", error->message);
1691 static void
1692 rb_shell_db_entry_added_cb (RhythmDB *db,
1693 RhythmDBEntry *entry,
1694 RBShell *shell)
1696 const char *loc;
1698 if (shell->priv->pending_entry == NULL)
1699 return;
1701 loc = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION);
1702 rb_debug ("got entry added for %s", loc);
1703 if (strcmp (loc, shell->priv->pending_entry) == 0) {
1704 rb_shell_play_entry (shell, entry);
1706 g_free (shell->priv->pending_entry);
1707 shell->priv->pending_entry = NULL;
1711 RBSource *
1712 rb_shell_get_source_by_entry_type (RBShell *shell,
1713 RhythmDBEntryType type)
1715 return g_hash_table_lookup (shell->priv->sources_hash, type);
1718 void
1719 rb_shell_register_entry_type_for_source (RBShell *shell,
1720 RBSource *source,
1721 RhythmDBEntryType type)
1723 if (shell->priv->sources_hash == NULL) {
1724 shell->priv->sources_hash = g_hash_table_new (g_direct_hash,
1725 g_direct_equal);
1727 g_assert (g_hash_table_lookup (shell->priv->sources_hash, type) == NULL);
1728 g_hash_table_insert (shell->priv->sources_hash, type, source);
1731 void
1732 rb_shell_append_source (RBShell *shell,
1733 RBSource *source,
1734 RBSource *parent)
1736 shell->priv->sources
1737 = g_list_append (shell->priv->sources, source);
1739 g_signal_connect_object (G_OBJECT (source), "deleted",
1740 G_CALLBACK (rb_shell_source_deleted_cb), shell, 0);
1742 gtk_notebook_append_page (GTK_NOTEBOOK (shell->priv->notebook),
1743 GTK_WIDGET (source),
1744 gtk_label_new (""));
1745 gtk_widget_show (GTK_WIDGET (source));
1747 rb_sourcelist_append (RB_SOURCELIST (shell->priv->sourcelist),
1748 source, parent);
1751 static void
1752 rb_shell_playlist_added_cb (RBPlaylistManager *mgr,
1753 RBSource *source,
1754 RBShell *shell)
1756 rb_shell_append_source (shell, source, NULL);
1759 static void
1760 rb_shell_playlist_created_cb (RBPlaylistManager *mgr,
1761 RBSource *source,
1762 RBShell *shell)
1764 shell->priv->window_small = FALSE;
1765 eel_gconf_set_boolean (CONF_UI_SMALL_DISPLAY, shell->priv->window_small);
1766 eel_gconf_set_boolean (CONF_UI_SIDEPANE_HIDDEN, shell->priv->window_small);
1768 rb_shell_sync_window_state (shell, FALSE);
1771 static void
1772 rb_shell_medium_added_cb (RBRemovableMediaManager *mgr,
1773 RBSource *source,
1774 RBShell *shell)
1776 rb_shell_append_source (shell, source, NULL);
1779 static void
1780 rb_shell_transfer_progress_cb (RBRemovableMediaManager *mgr,
1781 gint done,
1782 gint total,
1783 double fraction,
1784 RBShell *shell)
1786 rb_debug ("transferred %d tracks out of %d", done, total);
1788 if (total > 0) {
1789 char *s;
1791 if (fraction >= 0)
1792 s = g_strdup_printf (_("Transferring track %d out of %d (%.0f%%)"),
1793 done + 1, total, fraction * 100);
1794 else
1795 s = g_strdup_printf (_("Transferring track %d out of %d"),
1796 done + 1, total);
1798 rb_statusbar_set_progress (shell->priv->statusbar,
1799 (((double)(done) + fraction)/total),
1801 } else {
1802 rb_statusbar_set_progress (shell->priv->statusbar, -1, NULL);
1806 static void
1807 rb_shell_source_deleted_cb (RBSource *source,
1808 RBShell *shell)
1810 RhythmDBEntryType entry_type;
1812 rb_debug ("source deleted");
1814 /* remove from the map if the source owns the type */
1815 g_object_get (source, "entry-type", &entry_type, NULL);
1816 if (rb_shell_get_source_by_entry_type (shell, entry_type) == source) {
1817 g_hash_table_remove (shell->priv->sources_hash, entry_type);
1819 g_boxed_free (RHYTHMDB_TYPE_ENTRY_TYPE, entry_type);
1822 if (source == rb_shell_player_get_playing_source (shell->priv->player_shell)) {
1823 rb_shell_player_set_playing_source (shell->priv->player_shell, NULL);
1825 if (source == shell->priv->selected_source) {
1826 if (source != RB_SOURCE (shell->priv->library_source))
1827 rb_shell_select_source (shell, RB_SOURCE (shell->priv->library_source));
1828 else
1829 rb_shell_select_source (shell, NULL);
1832 shell->priv->sources = g_list_remove (shell->priv->sources, source);
1834 rb_sourcelist_remove (RB_SOURCELIST (shell->priv->sourcelist), source);
1836 gtk_notebook_remove_page (GTK_NOTEBOOK (shell->priv->notebook),
1837 gtk_notebook_page_num (GTK_NOTEBOOK (shell->priv->notebook),
1838 GTK_WIDGET (source)));
1841 static void
1842 rb_shell_playing_source_changed_cb (RBShellPlayer *player,
1843 RBSource *source,
1844 RBShell *shell)
1846 rb_debug ("playing source changed");
1847 if (source != RB_SOURCE (shell->priv->queue_source))
1848 rb_sourcelist_set_playing_source (RB_SOURCELIST (shell->priv->sourcelist),
1849 source);
1852 static void
1853 rb_shell_playing_from_queue_cb (RBShellPlayer *player,
1854 GParamSpec *param,
1855 RBShell *shell)
1857 gboolean from_queue;
1859 g_object_get (player, "playing-from-queue", &from_queue, NULL);
1860 if (!shell->priv->queue_as_sidebar) {
1861 rb_sourcelist_set_playing_source (RB_SOURCELIST (shell->priv->sourcelist),
1862 rb_shell_player_get_playing_source (shell->priv->player_shell));
1863 } else {
1864 RBSource *source;
1865 RBEntryView *songs;
1866 RhythmDBEntry *entry;
1867 RhythmDBEntryType entry_type;
1869 /* if playing from the queue, show the playing entry as playing in the
1870 * registered source for its type, so it makes sense when 'jump to current'
1871 * jumps to it there.
1873 entry = rb_shell_player_get_playing_entry (shell->priv->player_shell);
1874 if (entry == NULL)
1875 return;
1877 entry_type = rhythmdb_entry_get_entry_type (entry);
1878 source = rb_shell_get_source_by_entry_type (shell, entry_type);
1879 songs = rb_source_get_entry_view (source);
1881 rb_entry_view_set_state (songs, from_queue ? RB_ENTRY_VIEW_PLAYING : RB_ENTRY_VIEW_NOT_PLAYING);
1882 rb_sourcelist_set_playing_source (RB_SOURCELIST (shell->priv->sourcelist),
1883 rb_shell_player_get_active_source (shell->priv->player_shell));
1884 rhythmdb_entry_unref (entry);
1888 static void
1889 rb_shell_playing_entry_changed_cb (RBShellPlayer *player,
1890 RhythmDBEntry *entry,
1891 RBShell *shell)
1893 rb_shell_notify_playing_entry (shell, entry, FALSE);
1896 static void
1897 merge_source_ui_cb (const char *action,
1898 RBShell *shell)
1900 gtk_ui_manager_add_ui (shell->priv->ui_manager,
1901 shell->priv->source_ui_merge_id,
1902 "/ToolBar",
1903 action,
1904 action,
1905 GTK_UI_MANAGER_AUTO,
1906 FALSE);
1909 static void
1910 rb_shell_select_source (RBShell *shell,
1911 RBSource *source)
1913 GList *actions;
1915 if (shell->priv->selected_source == source)
1916 return;
1918 rb_debug ("selecting source %p", source);
1920 if (shell->priv->selected_source) {
1921 rb_source_deactivate (shell->priv->selected_source);
1922 gtk_ui_manager_remove_ui (shell->priv->ui_manager, shell->priv->source_ui_merge_id);
1925 shell->priv->selected_source = source;
1926 rb_source_activate (shell->priv->selected_source);
1928 /* show source */
1929 gtk_notebook_set_current_page (GTK_NOTEBOOK (shell->priv->notebook),
1930 gtk_notebook_page_num (GTK_NOTEBOOK (shell->priv->notebook), GTK_WIDGET (source)));
1932 g_signal_handlers_block_by_func (G_OBJECT (shell->priv->sourcelist),
1933 G_CALLBACK (source_selected_cb),
1934 shell);
1935 rb_sourcelist_select (RB_SOURCELIST (shell->priv->sourcelist),
1936 source);
1937 g_signal_handlers_unblock_by_func (G_OBJECT (shell->priv->sourcelist),
1938 G_CALLBACK (source_selected_cb),
1939 shell);
1941 /* update services */
1942 rb_shell_clipboard_set_source (shell->priv->clipboard_shell, source);
1943 rb_shell_player_set_selected_source (shell->priv->player_shell, source);
1944 rb_source_header_set_source (shell->priv->source_header, source);
1945 rb_statusbar_set_source (shell->priv->statusbar, source);
1946 g_object_set (G_OBJECT (shell->priv->playlist_manager), "source", source, NULL);
1947 g_object_set (G_OBJECT (shell->priv->removable_media_manager), "source", source, NULL);
1949 /* merge the source-specific UI */
1950 actions = rb_source_get_ui_actions (source);
1951 g_list_foreach (actions, (GFunc)merge_source_ui_cb, shell);
1952 rb_list_deep_free (actions);
1954 g_object_notify (G_OBJECT (shell), "selected-source");
1957 static void
1958 rb_shell_player_window_title_changed_cb (RBShellPlayer *player,
1959 const char *window_title,
1960 RBShell *shell)
1962 rb_shell_set_window_title (shell, window_title);
1965 static void
1966 rb_shell_player_elapsed_changed_cb (RBShellPlayer *player,
1967 guint elapsed,
1968 RBShell *shell)
1970 rb_shell_set_elapsed (shell, elapsed);
1973 static void
1974 rb_shell_set_elapsed (RBShell *shell,
1975 guint elapsed)
1977 gboolean playing;
1978 char *elapsed_string;
1979 char *tooltip;
1981 rb_shell_player_get_playing (shell->priv->player_shell, &playing, NULL);
1982 elapsed_string = rb_shell_player_get_playing_time_string (shell->priv->player_shell);
1984 if (shell->priv->cached_title == NULL)
1985 tooltip = g_strdup (_("Not playing"));
1986 else if (!playing) {
1987 /* Translators: the first %s is substituted by the song name, the second one is the elapsed and total time */
1988 tooltip = g_strdup_printf (_("%s\nPaused, %s"),
1989 shell->priv->cached_title, elapsed_string);
1990 } else {
1991 /* Translators: the first %s is substituted by the song name, the second one is the elapsed and total time */
1992 tooltip = g_strdup_printf (_("%s\n%s"),
1993 shell->priv->cached_title, elapsed_string);
1996 rb_tray_icon_set_tooltip (shell->priv->tray_icon, tooltip);
1997 g_free (elapsed_string);
1998 g_free (tooltip);
2001 static void
2002 rb_shell_set_window_title (RBShell *shell,
2003 const char *window_title)
2005 if (window_title == NULL) {
2006 rb_debug ("clearing title");
2008 g_free (shell->priv->cached_title);
2009 shell->priv->cached_title = NULL;
2011 gtk_window_set_title (GTK_WINDOW (shell->priv->window),
2012 _("Music Player"));
2014 else {
2015 gboolean playing;
2016 char *title;
2018 rb_shell_player_get_playing (shell->priv->player_shell, &playing, NULL);
2020 if (shell->priv->cached_title &&
2021 !strcmp (shell->priv->cached_title, window_title) &&
2022 playing == shell->priv->cached_playing) {
2023 return;
2025 g_free (shell->priv->cached_title);
2026 shell->priv->cached_title = g_strdup (window_title);
2027 shell->priv->cached_playing = playing;
2029 rb_debug ("setting title to \"%s\"", window_title);
2030 if (!playing) {
2031 /* Translators: %s is the song name */
2032 title = g_strdup_printf (_("%s (Paused)"), window_title);
2033 gtk_window_set_title (GTK_WINDOW (shell->priv->window),
2034 title);
2035 g_free (title);
2036 } else {
2037 gtk_window_set_title (GTK_WINDOW (shell->priv->window),
2038 window_title);
2043 static void
2044 rb_shell_view_sidepane_changed_cb (GtkAction *action,
2045 RBShell *shell)
2047 eel_gconf_set_boolean (CONF_UI_SIDEPANE_HIDDEN,
2048 !gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)));
2051 static void
2052 rb_shell_view_toolbar_changed_cb (GtkAction *action,
2053 RBShell *shell)
2055 eel_gconf_set_boolean (CONF_UI_TOOLBAR_HIDDEN,
2056 !gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)));
2059 static void
2060 rb_shell_view_smalldisplay_changed_cb (GtkAction *action,
2061 RBShell *shell)
2063 GTimeVal time;
2065 /* don't change more than once per second, it causes weirdness */
2066 g_get_current_time (&time);
2067 if (time.tv_sec == shell->priv->last_small_time)
2068 return;
2070 shell->priv->last_small_time = time.tv_sec;
2072 shell->priv->window_small = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
2073 eel_gconf_set_boolean (CONF_UI_SMALL_DISPLAY, shell->priv->window_small);
2076 static void
2077 rb_shell_view_statusbar_changed_cb (GtkAction *action,
2078 RBShell *shell)
2080 shell->priv->statusbar_hidden = !gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
2081 eel_gconf_set_boolean (CONF_UI_STATUSBAR_HIDDEN, shell->priv->statusbar_hidden);
2083 rb_shell_sync_statusbar_visibility (shell);
2086 static void
2087 rb_shell_view_queue_as_sidebar_changed_cb (GtkAction *action,
2088 RBShell *shell)
2090 shell->priv->queue_as_sidebar = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
2091 eel_gconf_set_boolean (CONF_UI_QUEUE_AS_SIDEBAR, shell->priv->queue_as_sidebar);
2093 if (shell->priv->queue_as_sidebar &&
2094 shell->priv->selected_source == RB_SOURCE (shell->priv->queue_source)) {
2095 /* queue no longer exists as a source, so change to the library */
2096 rb_shell_select_source (shell, RB_SOURCE (shell->priv->library_source));
2099 rb_shell_playing_from_queue_cb (shell->priv->player_shell, NULL, shell);
2101 rb_shell_sync_pane_visibility (shell);
2104 static void
2105 rb_shell_view_party_mode_changed_cb (GtkAction *action,
2106 RBShell *shell)
2108 shell->priv->party_mode = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
2109 rb_shell_sync_party_mode (shell);
2112 static void
2113 rb_shell_cmd_about (GtkAction *action,
2114 RBShell *shell)
2116 const char **tem;
2117 GString *comment;
2118 GdkPixbuf *pixbuf = NULL;
2120 const char *authors[] = {
2122 #include "MAINTAINERS.tab"
2124 NULL,
2125 #include "MAINTAINERS.old.tab"
2127 NULL,
2128 #include "AUTHORS.tab"
2129 NULL
2132 const char *documenters[] = {
2133 #include "DOCUMENTERS.tab"
2134 NULL
2137 const char *translator_credits = _("translator-credits");
2139 const char *license[] = {
2140 N_("Rhythmbox is free software; you can redistribute it and/or modify\n"
2141 "it under the terms of the GNU General Public License as published by\n"
2142 "the Free Software Foundation; either version 2 of the License, or\n"
2143 "(at your option) any later version.\n"),
2144 N_("Rhythmbox is distributed in the hope that it will be useful,\n"
2145 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
2146 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
2147 "GNU General Public License for more details.\n"),
2148 N_("You should have received a copy of the GNU General Public License\n"
2149 "along with Rhythmbox; if not, write to the Free Software Foundation, Inc.,\n"
2150 "51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\n")
2153 char *license_trans;
2155 pixbuf = gdk_pixbuf_new_from_file (rb_file ("about-logo.png"), NULL);
2157 authors[0] = _("Maintainers:");
2158 for (tem = authors; *tem != NULL; tem++)
2160 *tem = _("Former Maintainers:");
2161 for (; *tem != NULL; tem++)
2163 *tem = _("Contributors:");
2165 comment = g_string_new (_("Music management and playback software for GNOME."));
2167 license_trans = g_strconcat (_(license[0]), "\n", _(license[1]), "\n",
2168 _(license[2]), "\n", NULL);
2170 gtk_show_about_dialog (GTK_WINDOW (shell->priv->window),
2171 "name", "Rhythmbox",
2172 "version", VERSION,
2173 "copyright", "Copyright \xc2\xa9 2005 The Rhythmbox authors\nCopyright \xc2\xa9 2003 - 2005 Colin Walters\nCopyright \xc2\xa9 2002, 2003 Jorn Baayen",
2174 "license", license_trans,
2175 "website", "http://www.gnome.org/projects/rhythmbox",
2176 "comments", comment->str,
2177 "authors", (const char **) authors,
2178 "documenters", (const char **) documenters,
2179 "translator-credits", strcmp (translator_credits, "translator-credits") != 0 ? translator_credits : NULL,
2180 "logo", pixbuf,
2181 NULL);
2182 if (pixbuf)
2183 g_object_unref (pixbuf);
2184 g_string_free (comment, TRUE);
2185 g_free (license_trans);
2188 void
2189 rb_shell_toggle_visibility (RBShell *shell)
2191 gboolean visible;
2193 visible = rb_shell_get_visibility (shell);
2195 rb_shell_set_visibility (shell, !visible, FALSE);
2198 static void
2199 rb_shell_cmd_toggle_visibility (GtkAction *action,
2200 RBShell *shell)
2202 rb_shell_toggle_visibility (shell);
2205 static void
2206 rb_shell_cmd_quit (GtkAction *action,
2207 RBShell *shell)
2209 rb_shell_quit (shell, NULL);
2212 static void
2213 rb_shell_cmd_contents (GtkAction *action,
2214 RBShell *shell)
2216 GError *error = NULL;
2218 gnome_help_display ("rhythmbox.xml", NULL, &error);
2220 if (error != NULL) {
2221 rb_error_dialog (NULL, _("Couldn't display help"),
2222 "%s", error->message);
2223 g_error_free (error);
2227 static void
2228 rb_shell_cmd_preferences (GtkAction *action,
2229 RBShell *shell)
2231 if (shell->priv->prefs == NULL) {
2232 shell->priv->prefs = rb_shell_preferences_new (shell->priv->sources);
2234 gtk_window_set_transient_for (GTK_WINDOW (shell->priv->prefs),
2235 GTK_WINDOW (shell->priv->window));
2236 gtk_widget_show_all (shell->priv->prefs);
2239 gtk_window_present (GTK_WINDOW (shell->priv->prefs));
2242 static gboolean
2243 rb_shell_plugins_window_delete_cb (GtkWidget *window,
2244 GdkEventAny *event,
2245 gpointer data)
2247 gtk_widget_hide (window);
2249 return TRUE;
2252 static void
2253 rb_shell_plugins_response_cb (GtkDialog *dialog,
2254 int response_id,
2255 gpointer data)
2257 if (response_id == GTK_RESPONSE_CLOSE)
2258 gtk_widget_hide (GTK_WIDGET (dialog));
2261 static void
2262 rb_shell_cmd_plugins (GtkAction *action,
2263 RBShell *shell)
2265 if (shell->priv->plugins == NULL) {
2266 GtkWidget *manager;
2268 shell->priv->plugins = gtk_dialog_new_with_buttons (_("Configure Plugins"),
2269 GTK_WINDOW (shell->priv->window),
2270 GTK_DIALOG_DESTROY_WITH_PARENT,
2271 GTK_STOCK_CLOSE,
2272 GTK_RESPONSE_CLOSE,
2273 NULL);
2274 gtk_container_set_border_width (GTK_CONTAINER (shell->priv->plugins), 5);
2275 gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (shell->priv->plugins)->vbox), 2);
2276 gtk_dialog_set_has_separator (GTK_DIALOG (shell->priv->plugins), FALSE);
2278 g_signal_connect_object (G_OBJECT (shell->priv->plugins),
2279 "delete_event",
2280 G_CALLBACK (rb_shell_plugins_window_delete_cb),
2281 NULL, 0);
2282 g_signal_connect_object (G_OBJECT (shell->priv->plugins),
2283 "response",
2284 G_CALLBACK (rb_shell_plugins_response_cb),
2285 NULL, 0);
2287 manager = rb_plugin_manager_new ();
2288 gtk_widget_show_all (GTK_WIDGET (manager));
2289 gtk_container_add (GTK_CONTAINER (GTK_DIALOG (shell->priv->plugins)->vbox),
2290 manager);
2293 gtk_window_present (GTK_WINDOW (shell->priv->plugins));
2296 static void
2297 add_to_library_response_cb (GtkDialog *dialog,
2298 int response_id,
2299 RBShell *shell)
2302 char *current_dir = NULL;
2303 GSList *uri_list = NULL, *uris = NULL;
2305 if (response_id != GTK_RESPONSE_ACCEPT) {
2306 gtk_widget_destroy (GTK_WIDGET (dialog));
2307 return;
2310 current_dir = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
2311 eel_gconf_set_string (CONF_STATE_ADD_DIR, current_dir);
2313 uri_list = gtk_file_chooser_get_uris (GTK_FILE_CHOOSER (dialog));
2314 if (uri_list == NULL) {
2315 uri_list = g_slist_prepend (uri_list, g_strdup (current_dir));
2318 for (uris = uri_list; uris; uris = uris->next) {
2319 rb_shell_load_uri (shell, (char *)uris->data, FALSE, NULL);
2320 g_free (uris->data);
2322 g_slist_free (uri_list);
2323 g_free (current_dir);
2324 gtk_widget_destroy (GTK_WIDGET (dialog));
2326 if (shell->priv->save_db_id > 0) {
2327 g_source_remove (shell->priv->save_db_id);
2329 shell->priv->save_db_id = g_timeout_add (10000, (GSourceFunc) idle_save_rhythmdb, shell);
2332 static void
2333 rb_shell_cmd_add_folder_to_library (GtkAction *action,
2334 RBShell *shell)
2336 char * dir = eel_gconf_get_string (CONF_STATE_ADD_DIR);
2337 GtkWidget *dialog;
2339 dialog = rb_file_chooser_new (_("Import Folder into Library"),
2340 GTK_WINDOW (shell->priv->window),
2341 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
2342 FALSE);
2343 gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (dialog), TRUE);
2344 if (dir)
2345 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (dialog),
2346 dir);
2348 g_signal_connect_object (G_OBJECT (dialog),
2349 "response",
2350 G_CALLBACK (add_to_library_response_cb),
2351 shell, 0);
2354 static void
2355 rb_shell_cmd_add_file_to_library (GtkAction *action,
2356 RBShell *shell)
2358 char * dir = eel_gconf_get_string (CONF_STATE_ADD_DIR);
2359 GtkWidget *dialog;
2361 dialog = rb_file_chooser_new (_("Import File into Library"),
2362 GTK_WINDOW (shell->priv->window),
2363 GTK_FILE_CHOOSER_ACTION_OPEN,
2364 FALSE);
2365 gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (dialog), TRUE);
2366 if (dir)
2367 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (dialog),
2368 dir);
2370 g_signal_connect_object (G_OBJECT (dialog),
2371 "response",
2372 G_CALLBACK (add_to_library_response_cb),
2373 shell, 0);
2376 static gboolean
2377 quit_timeout (gpointer dummy)
2379 GDK_THREADS_ENTER ();
2380 gtk_main_quit ();
2381 GDK_THREADS_LEAVE ();
2382 return FALSE;
2385 gboolean
2386 rb_shell_quit (RBShell *shell,
2387 GError **error)
2389 rb_debug ("Quitting");
2391 /* Stop the playing source, if any */
2392 rb_shell_player_set_playing_source (shell->priv->player_shell, NULL);
2394 rb_plugins_engine_shutdown ();
2396 rb_podcast_source_shutdown (shell->priv->podcast_source);
2398 rb_shell_shutdown (shell);
2399 rb_shell_sync_state (shell);
2400 g_object_unref (G_OBJECT (shell));
2402 g_timeout_add (10000, quit_timeout, NULL);
2403 return TRUE;
2406 static gboolean
2407 idle_handle_load_complete (RBShell *shell)
2409 GDK_THREADS_ENTER ();
2411 rb_debug ("load complete");
2413 rb_playlist_manager_load_playlists (shell->priv->playlist_manager);
2414 shell->priv->load_complete = TRUE;
2415 shell->priv->save_playlist_id = g_timeout_add (10000, (GSourceFunc) idle_save_playlist_manager, shell);
2417 rhythmdb_start_action_thread (shell->priv->db);
2419 GDK_THREADS_LEAVE ();
2421 return FALSE;
2424 static void
2425 rb_shell_load_complete_cb (RhythmDB *db,
2426 RBShell *shell)
2428 g_idle_add ((GSourceFunc) idle_handle_load_complete, shell);
2431 static void
2432 rb_shell_sync_sidepane_visibility (RBShell *shell)
2434 gboolean visible;
2435 GtkAction *action;
2437 visible = !eel_gconf_get_boolean (CONF_UI_SIDEPANE_HIDDEN);
2439 if (visible) {
2440 gtk_widget_show (GTK_WIDGET (shell->priv->sidebar_container));
2441 } else {
2442 gtk_widget_hide (GTK_WIDGET (shell->priv->sidebar_container));
2445 action = gtk_action_group_get_action (shell->priv->actiongroup,
2446 "ViewSidePane");
2447 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
2448 visible);
2451 static void
2452 rb_shell_sync_pane_visibility (RBShell *shell)
2454 GtkAction *action;
2456 if (shell->priv->queue_source != NULL) {
2457 g_object_set (G_OBJECT (shell->priv->queue_source), "visibility", !shell->priv->queue_as_sidebar, NULL);
2460 if (shell->priv->queue_as_sidebar) {
2461 gtk_widget_show (shell->priv->queue_sidebar);
2462 } else {
2463 gtk_widget_hide (shell->priv->queue_sidebar);
2466 action = gtk_action_group_get_action (shell->priv->actiongroup,
2467 "ViewQueueAsSidebar");
2468 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
2469 shell->priv->queue_as_sidebar);
2472 static void
2473 rb_shell_sync_toolbar_state (RBShell *shell)
2475 GtkWidget *toolbar;
2476 gboolean visible;
2477 GtkAction *action;
2478 guint toolbar_style;
2480 visible = !eel_gconf_get_boolean (CONF_UI_TOOLBAR_HIDDEN);
2482 toolbar = gtk_ui_manager_get_widget (shell->priv->ui_manager, "/ToolBar");
2483 if (visible)
2484 gtk_widget_show (toolbar);
2485 else
2486 gtk_widget_hide (toolbar);
2488 action = gtk_action_group_get_action (shell->priv->actiongroup,
2489 "ViewToolbar");
2490 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
2491 visible);
2493 /* icons-only in small mode */
2494 if (shell->priv->window_small)
2495 toolbar_style = 3;
2496 else
2497 toolbar_style = eel_gconf_get_integer (CONF_UI_TOOLBAR_STYLE);
2499 switch (toolbar_style) {
2500 case 0:
2501 /* default*/
2502 gtk_toolbar_unset_style (GTK_TOOLBAR (toolbar));
2503 break;
2504 case 1:
2505 /* text below icons */
2506 gtk_toolbar_set_style (GTK_TOOLBAR (toolbar), GTK_TOOLBAR_BOTH);
2507 break;
2508 case 2:
2509 /* text beside icons */
2510 gtk_toolbar_set_style (GTK_TOOLBAR (toolbar), GTK_TOOLBAR_BOTH_HORIZ);
2511 break;
2512 case 3:
2513 /* icons only */
2514 gtk_toolbar_set_style (GTK_TOOLBAR (toolbar), GTK_TOOLBAR_ICONS);
2515 break;
2516 case 4:
2517 /* test only */
2518 gtk_toolbar_set_style (GTK_TOOLBAR (toolbar), GTK_TOOLBAR_TEXT);
2519 break;
2520 default:
2521 g_warning ("unknown toolbar style type");
2522 gtk_toolbar_unset_style (GTK_TOOLBAR (toolbar));
2526 static gboolean
2527 window_state_event_cb (GtkWidget *widget,
2528 GdkEventWindowState *event,
2529 RBShell *shell)
2531 if (event->changed_mask & GDK_WINDOW_STATE_ICONIFIED) {
2532 rb_shell_present (shell, gtk_get_current_event_time (), NULL);
2535 return TRUE;
2538 static void
2539 rb_shell_sync_party_mode (RBShell *shell)
2541 GtkAction *action;
2543 /* party mode does not use gconf as a model since it
2544 should not be persistent */
2546 /* disable/enable quit action */
2547 action = gtk_action_group_get_action (shell->priv->actiongroup, "MusicClose");
2548 g_object_set (G_OBJECT (action), "sensitive", !shell->priv->party_mode, NULL);
2549 action = gtk_action_group_get_action (shell->priv->actiongroup, "MusicQuit");
2550 g_object_set (G_OBJECT (action), "sensitive", !shell->priv->party_mode, NULL);
2551 action = gtk_action_group_get_action (shell->priv->actiongroup, "ViewSmallDisplay");
2552 g_object_set (G_OBJECT (action), "sensitive", !shell->priv->party_mode, NULL);
2554 /* show/hide queue as sidebar ? */
2556 g_object_set (shell->priv->player_shell, "queue-only", shell->priv->party_mode, NULL);
2558 /* Set playlist manager source to the current source to update properties */
2559 if (shell->priv->selected_source) {
2560 g_object_set (G_OBJECT (shell->priv->playlist_manager), "source", shell->priv->selected_source, NULL);
2561 rb_shell_clipboard_set_source (shell->priv->clipboard_shell, shell->priv->selected_source);
2564 gtk_window_set_keep_above (GTK_WINDOW (shell->priv->window), shell->priv->party_mode);
2565 if (shell->priv->party_mode) {
2566 gtk_window_fullscreen (GTK_WINDOW (shell->priv->window));
2567 gtk_window_stick (GTK_WINDOW (shell->priv->window));
2568 g_signal_connect (shell->priv->window, "window-state-event", G_CALLBACK (window_state_event_cb), shell);
2569 } else {
2570 gtk_window_unstick (GTK_WINDOW (shell->priv->window));
2571 gtk_window_unfullscreen (GTK_WINDOW (shell->priv->window));
2572 g_signal_handlers_disconnect_by_func (shell->priv->window, window_state_event_cb, shell);
2576 static void
2577 rb_shell_sync_smalldisplay (RBShell *shell)
2579 GtkAction *action;
2580 GtkAction *queue_action;
2581 GtkAction *party_mode_action;
2582 GtkWidget *toolbar;
2584 rb_shell_sync_window_state (shell, FALSE);
2586 action = gtk_action_group_get_action (shell->priv->actiongroup,
2587 "ViewSidePane");
2588 queue_action = gtk_action_group_get_action (shell->priv->actiongroup,
2589 "ViewQueueAsSidebar");
2590 party_mode_action = gtk_action_group_get_action (shell->priv->actiongroup,
2591 "ViewPartyMode");
2593 toolbar = gtk_ui_manager_get_widget (shell->priv->ui_manager, "/ToolBar");
2595 if (shell->priv->window_small) {
2596 g_object_set (G_OBJECT (action), "sensitive", FALSE, NULL);
2597 g_object_set (G_OBJECT (queue_action), "sensitive", FALSE, NULL);
2598 g_object_set (G_OBJECT (party_mode_action), "sensitive", FALSE, NULL);
2600 gtk_widget_hide (GTK_WIDGET (shell->priv->paned));
2601 } else {
2602 g_object_set (G_OBJECT (action), "sensitive", TRUE, NULL);
2603 g_object_set (G_OBJECT (queue_action), "sensitive", TRUE, NULL);
2604 g_object_set (G_OBJECT (party_mode_action), "sensitive", TRUE, NULL);
2606 gtk_widget_show (GTK_WIDGET (shell->priv->paned));
2608 rb_shell_sync_statusbar_visibility (shell);
2609 rb_shell_sync_toolbar_state (shell);
2611 rb_source_header_sync_control_state (shell->priv->source_header);
2613 action = gtk_action_group_get_action (shell->priv->actiongroup,
2614 "ViewSmallDisplay");
2615 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
2616 shell->priv->window_small);
2619 static void
2620 rb_shell_sync_statusbar_visibility (RBShell *shell)
2622 if (shell->priv->statusbar_hidden || shell->priv->window_small)
2623 gtk_widget_hide (GTK_WIDGET (shell->priv->statusbar));
2624 else
2625 gtk_widget_show (GTK_WIDGET (shell->priv->statusbar));
2628 static void
2629 sidepane_visibility_changed_cb (GConfClient *client,
2630 guint cnxn_id,
2631 GConfEntry *entry,
2632 RBShell *shell)
2634 rb_debug ("sidepane visibility changed");
2635 rb_shell_sync_sidepane_visibility (shell);
2638 static void
2639 toolbar_state_changed_cb (GConfClient *client,
2640 guint cnxn_id,
2641 GConfEntry *entry,
2642 RBShell *shell)
2644 rb_debug ("toolbar state changed");
2645 rb_shell_sync_toolbar_state (shell);
2648 static void
2649 smalldisplay_changed_cb (GConfClient *client,
2650 guint cnxn_id,
2651 GConfEntry *entry,
2652 RBShell *shell)
2654 rb_debug ("small display mode changed");
2655 shell->priv->window_small = eel_gconf_get_boolean (CONF_UI_SMALL_DISPLAY);
2656 rb_shell_sync_smalldisplay (shell);
2659 static void
2660 rb_shell_sync_paned (RBShell *shell)
2662 gtk_paned_set_position (GTK_PANED (shell->priv->paned),
2663 shell->priv->paned_position);
2664 gtk_paned_set_position (GTK_PANED (shell->priv->queue_paned),
2665 shell->priv->sourcelist_height);
2668 static void
2669 paned_size_allocate_cb (GtkWidget *widget,
2670 GtkAllocation *allocation,
2671 RBShell *shell)
2673 shell->priv->paned_position = gtk_paned_get_position (GTK_PANED (shell->priv->paned));
2674 rb_debug ("paned position %d", shell->priv->paned_position);
2675 eel_gconf_set_integer (CONF_STATE_PANED_POSITION, shell->priv->paned_position);
2678 static void
2679 sidebar_paned_size_allocate_cb (GtkWidget *widget,
2680 GtkAllocation *allocation,
2681 RBShell *shell)
2683 shell->priv->sourcelist_height = gtk_paned_get_position (GTK_PANED (shell->priv->queue_paned));
2684 rb_debug ("sidebar paned position %d", shell->priv->sourcelist_height);
2685 eel_gconf_set_integer (CONF_STATE_SOURCELIST_HEIGHT, shell->priv->sourcelist_height);
2688 static void
2689 sourcelist_drag_received_cb (RBSourceList *sourcelist,
2690 RBSource *source,
2691 GtkSelectionData *data,
2692 RBShell *shell)
2694 if (source == NULL) {
2695 source = rb_playlist_manager_new_playlist_from_selection_data (shell->priv->playlist_manager,
2696 data);
2699 if (source != NULL) {
2700 rb_source_receive_drag (source, data);
2705 static void
2706 rb_shell_cmd_current_song (GtkAction *action,
2707 RBShell *shell)
2709 rb_debug ("current song");
2711 rb_shell_jump_to_current (shell);
2714 static void
2715 rb_shell_cmd_view_all (GtkAction *action,
2716 RBShell *shell)
2718 rb_debug ("view all");
2720 rb_source_reset_filters (shell->priv->selected_source);
2721 rb_source_header_clear_search (shell->priv->source_header);
2722 rb_source_header_focus_search_box (shell->priv->source_header);
2725 static void
2726 rb_shell_jump_to_entry_with_source (RBShell *shell,
2727 RBSource *source,
2728 RhythmDBEntry *entry)
2730 RBEntryView *songs;
2732 g_return_if_fail (entry != NULL);
2734 if ((source == RB_SOURCE (shell->priv->queue_source) &&
2735 shell->priv->queue_as_sidebar) ||
2736 source == NULL) {
2737 RhythmDBEntryType entry_type;
2738 entry_type = rhythmdb_entry_get_entry_type (entry);
2739 source = rb_shell_get_source_by_entry_type (shell, entry_type);
2741 if (source == NULL)
2742 return;
2744 songs = rb_source_get_entry_view (source);
2745 rb_shell_select_source (shell, source);
2747 rb_entry_view_scroll_to_entry (songs, entry);
2748 rb_entry_view_select_entry (songs, entry);
2751 static void
2752 rb_shell_play_entry (RBShell *shell,
2753 RhythmDBEntry *entry)
2755 rb_shell_player_stop (shell->priv->player_shell);
2756 rb_shell_jump_to_entry_with_source (shell, NULL, entry);
2757 rb_shell_player_play_entry (shell->priv->player_shell, entry, NULL);
2760 static void
2761 rb_shell_jump_to_current (RBShell *shell)
2763 RBSource *source;
2764 RhythmDBEntry *playing;
2766 source = rb_shell_player_get_playing_source (shell->priv->player_shell);
2768 g_return_if_fail (source != NULL);
2770 playing = rb_shell_player_get_playing_entry (shell->priv->player_shell);
2772 rb_shell_jump_to_entry_with_source (shell, source, playing);
2773 rhythmdb_entry_unref (playing);
2776 static gboolean
2777 rb_shell_show_popup_cb (RBSourceList *sourcelist,
2778 RBSource *target,
2779 RBShell *shell)
2781 rb_debug ("popup");
2782 return rb_source_show_popup (target);
2785 static void
2786 tray_embedded_cb (GtkPlug *plug,
2787 gpointer data)
2789 /* FIXME - this doens't work */
2790 #if 0
2791 RBShell *shell = RB_SHELL (data);
2793 rb_debug ("got embedded signal");
2795 gdk_window_set_decorations (shell->priv->window->window,
2796 GDK_DECOR_ALL | GDK_DECOR_MINIMIZE | GDK_DECOR_MAXIMIZE);
2797 #endif
2800 static gboolean
2801 tray_destroy_cb (GtkObject *object,
2802 RBShell *shell)
2804 if (shell->priv->tray_icon) {
2805 rb_debug ("caught destroy event for tray icon %p", object);
2806 gtk_object_sink (object);
2807 shell->priv->tray_icon = NULL;
2808 rb_debug ("finished sinking tray");
2811 rb_debug ("creating new tray icon");
2812 shell->priv->tray_icon = rb_tray_icon_new (shell->priv->ui_manager, shell);
2813 g_signal_connect_object (G_OBJECT (shell->priv->tray_icon), "destroy",
2814 G_CALLBACK (tray_destroy_cb), shell, 0);
2815 g_signal_connect_object (G_OBJECT (shell->priv->tray_icon), "embedded",
2816 G_CALLBACK (tray_embedded_cb), shell, 0);
2818 gtk_widget_show_all (GTK_WIDGET (shell->priv->tray_icon));
2820 rb_debug ("done creating new tray icon %p", shell->priv->tray_icon);
2821 return TRUE;
2825 void
2826 rb_shell_notify_playing_entry (RBShell *shell,
2827 RhythmDBEntry *entry,
2828 gboolean requested)
2830 GValue *value;
2831 char *stream_title = NULL;
2832 char *artist = NULL;
2833 char *album = NULL;
2834 char *title = NULL;
2835 char *sec;
2836 GString *secondary;
2838 if (entry == NULL)
2839 return;
2841 secondary = g_string_sized_new (100);
2843 /* get artist, preferring streaming song details */
2844 value = rhythmdb_entry_request_extra_metadata (shell->priv->db,
2845 entry,
2846 RHYTHMDB_PROP_STREAM_SONG_ARTIST);
2847 if (value != NULL) {
2848 artist = g_value_dup_string (value);
2849 g_value_unset (value);
2850 g_free (value);
2851 } else {
2852 artist = rhythmdb_entry_dup_string (entry, RHYTHMDB_PROP_ARTIST);
2855 if (artist != NULL && artist[0] != '\0') {
2856 /* Translators: by Artist */
2857 g_string_append_printf (secondary, _("by %s"), artist);
2859 g_free (artist);
2861 /* get album, preferring streaming song details */
2862 value = rhythmdb_entry_request_extra_metadata (shell->priv->db,
2863 entry,
2864 RHYTHMDB_PROP_STREAM_SONG_ALBUM);
2865 if (value != NULL) {
2866 album = g_value_dup_string (value);
2867 g_value_unset (value);
2868 g_free (value);
2869 } else {
2870 album = rhythmdb_entry_dup_string (entry, RHYTHMDB_PROP_ALBUM);
2873 if (album != NULL && album[0] != '\0') {
2874 if (secondary->len != 0)
2875 g_string_append_c (secondary, ' ');
2877 /* Translators: from Album */
2878 g_string_append_printf (secondary, _("from %s"), album);
2880 g_free (album);
2882 /* get title and possibly stream name.
2883 * if we have a streaming song title, the entry's title
2884 * property is the stream name.
2886 value = rhythmdb_entry_request_extra_metadata (shell->priv->db,
2887 entry,
2888 RHYTHMDB_PROP_STREAM_SONG_TITLE);
2889 if (value != NULL) {
2890 title = g_value_dup_string (value);
2891 g_value_unset (value);
2892 g_free (value);
2894 stream_title = rhythmdb_entry_dup_string (entry, RHYTHMDB_PROP_TITLE);
2895 } else {
2896 title = rhythmdb_entry_dup_string (entry, RHYTHMDB_PROP_TITLE);
2899 if (stream_title != NULL && stream_title[0] != '\0') {
2900 if (secondary->len == 0)
2901 g_string_append (secondary, stream_title);
2902 else
2903 g_string_append_printf (secondary, " (%s)", stream_title);
2905 g_free (stream_title);
2907 sec = g_string_free (secondary, FALSE);
2908 rb_shell_hidden_notify (shell,
2909 4000,
2910 title,
2911 NULL,
2912 sec,
2913 requested);
2914 g_free (sec);
2915 g_free (title);
2918 void
2919 rb_shell_hidden_notify (RBShell *shell,
2920 guint timeout,
2921 const char *primary,
2922 GtkWidget *icon,
2923 const char *secondary,
2924 gboolean requested)
2927 if (requested == FALSE && rb_shell_get_visibility (shell)) {
2928 rb_debug ("shell is visible, not notifying");
2929 return;
2932 rb_tray_icon_notify (shell->priv->tray_icon,
2933 timeout,
2934 primary,
2935 icon,
2936 secondary,
2937 requested);
2940 gboolean
2941 rb_shell_do_notify (RBShell *shell, gboolean requested, GError **error)
2943 RhythmDBEntry *entry;
2945 entry = rb_shell_player_get_playing_entry (shell->priv->player_shell);
2946 rb_shell_notify_playing_entry (shell, entry, requested);
2947 rhythmdb_entry_unref (entry);
2949 return TRUE;
2952 static void
2953 session_die_cb (GnomeClient *client,
2954 RBShell *shell)
2956 rb_debug ("session die");
2957 rb_shell_quit (shell, NULL);
2960 GQuark
2961 rb_shell_error_quark (void)
2963 static GQuark quark = 0;
2964 if (!quark)
2965 quark = g_quark_from_static_string ("rb_shell_error");
2967 return quark;
2970 static gboolean
2971 save_yourself_cb (GnomeClient *client,
2972 gint phase,
2973 GnomeSaveStyle save_style,
2974 gboolean shutdown,
2975 GnomeInteractStyle interact_style,
2976 gboolean fast,
2977 RBShell *shell)
2979 rb_debug ("session save yourself");
2980 rb_shell_sync_state (shell);
2981 return TRUE;
2984 static void
2985 rb_shell_session_init (RBShell *shell)
2987 GnomeClient *client;
2989 client = gnome_master_client ();
2991 g_signal_connect_object (G_OBJECT (client),
2992 "save_yourself",
2993 G_CALLBACK (save_yourself_cb),
2994 shell, 0);
2996 g_signal_connect_object (G_OBJECT (client),
2997 "die",
2998 G_CALLBACK (session_die_cb),
2999 shell, 0);
3002 RBSource *
3003 rb_shell_guess_source_for_uri (RBShell *shell,
3004 const char *uri)
3006 GList *t;
3007 RBSource *best = NULL;
3008 guint strength = 0;
3010 for (t = shell->priv->sources; t != NULL; t = t->next) {
3011 guint s;
3012 RBSource *source;
3014 source = (RBSource *)t->data;
3015 s = rb_source_want_uri (source, uri);
3016 if (s > strength) {
3017 gchar *name;
3019 g_object_get (source, "name", &name, NULL);
3020 rb_debug ("source %s returned strength %u for uri %s",
3021 name, s, uri);
3022 g_free (name);
3024 strength = s;
3025 best = source;
3029 return best;
3032 /* Load a URI representing an element of the given type, with
3033 * optional metadata
3035 gboolean
3036 rb_shell_add_uri (RBShell *shell,
3037 const char *uri,
3038 const char *title,
3039 const char *genre,
3040 GError **error)
3042 RBSource *source;
3044 source = rb_shell_guess_source_for_uri (shell, uri);
3045 if (source == NULL) {
3046 g_set_error (error,
3047 RB_SHELL_ERROR,
3048 RB_SHELL_ERROR_NO_SOURCE_FOR_URI,
3049 _("No registered source can handle URI %s"),
3050 uri);
3051 return FALSE;
3054 rb_source_add_uri (source, uri, title, genre);
3055 return TRUE;
3058 typedef struct {
3059 RBShell *shell;
3060 gboolean can_use_playlist;
3061 RBSource *playlist_source;
3062 } PlaylistParseData;
3064 static void
3065 handle_playlist_entry_cb (TotemPlParser *playlist,
3066 const char *uri,
3067 const char *title,
3068 const char *genre,
3069 PlaylistParseData *data)
3071 RBSource *source;
3074 * Track whether the same playlist-handling source
3075 * wants all the URIs from the playlist; if it does,
3076 * then we'll just give the playlist URI to the source.
3078 if (data->can_use_playlist == FALSE)
3079 return;
3081 source = rb_shell_guess_source_for_uri (data->shell, uri);
3082 if (data->playlist_source == NULL) {
3083 if (source != NULL && rb_source_try_playlist (source)) {
3084 data->playlist_source = RB_SOURCE (g_object_ref (source));
3085 } else {
3086 data->can_use_playlist = FALSE;
3088 } else if (data->playlist_source != source) {
3089 g_object_unref (data->playlist_source);
3090 data->playlist_source = NULL;
3091 data->can_use_playlist = FALSE;
3095 /* Load a URI representing a single song, a directory, a playlist, or
3096 * an internet radio station, and optionally start playing it.
3098 * For playlists containing only stream URLs, we either add the playlist
3099 * itself (if it's remote) or each URL from it (if it's local). The main
3100 * reason for this is so clicking on stream playlist links in web browsers
3101 * works properly - the playlist file will be downloaded to /tmp/, and
3102 * we can't add that to the database, so we need to add the stream URLs
3103 * instead.
3105 gboolean
3106 rb_shell_load_uri (RBShell *shell,
3107 const char *uri,
3108 gboolean play,
3109 GError **error)
3111 RhythmDBEntry *entry;
3112 entry = rhythmdb_entry_lookup_by_location (shell->priv->db, uri);
3114 if (entry == NULL) {
3115 TotemPlParser *parser;
3116 TotemPlParserResult result;
3117 PlaylistParseData data;
3119 data.shell = shell;
3120 data.can_use_playlist = TRUE;
3121 data.playlist_source = NULL;
3123 rb_debug ("adding uri %s", uri);
3124 parser = totem_pl_parser_new ();
3126 g_signal_connect_data (G_OBJECT (parser), "entry",
3127 G_CALLBACK (handle_playlist_entry_cb),
3128 &data, NULL, 0);
3130 totem_pl_parser_add_ignored_mimetype (parser, "x-directory/normal");
3131 if (g_object_class_find_property (G_OBJECT_GET_CLASS (parser), "recurse"))
3132 g_object_set (G_OBJECT (parser), "recurse", FALSE, NULL);
3134 result = totem_pl_parser_parse (parser, uri, FALSE);
3135 g_object_unref (parser);
3137 if (result == TOTEM_PL_PARSER_RESULT_SUCCESS) {
3138 if (data.can_use_playlist && data.playlist_source) {
3139 rb_debug ("adding playlist %s to source", uri);
3140 rb_source_add_uri (data.playlist_source, uri, NULL, NULL);
3141 g_object_unref (data.playlist_source);
3143 /* FIXME: We need some way to determine whether the URI as
3144 * given will appear in the db, or whether something else will.
3145 * This hack assumes we'll never add local playlists to the db
3146 * directly.
3148 if (rb_uri_is_local (uri)) {
3149 play = FALSE;
3151 } else {
3152 rb_debug ("adding %s as a static playlist", uri);
3153 if (!rb_playlist_manager_parse_file (shell->priv->playlist_manager,
3154 uri, error))
3155 return FALSE;
3157 } else {
3158 rb_debug ("%s didn't parse as a playlist", uri);
3159 if (!rb_shell_add_uri (shell, uri, NULL, NULL, error))
3160 return FALSE;
3164 if (play) {
3165 if (entry == NULL)
3166 entry = rhythmdb_entry_lookup_by_location (shell->priv->db, uri);
3168 if (entry) {
3169 rb_shell_play_entry (shell, entry);
3170 } else {
3171 /* wait for the entry to be added, and then play it */
3172 if (shell->priv->pending_entry)
3173 g_free (shell->priv->pending_entry);
3175 shell->priv->pending_entry = g_strdup (uri);
3179 return TRUE;
3182 gboolean
3183 rb_shell_get_party_mode (RBShell *shell)
3185 return shell->priv->party_mode;
3188 GObject *
3189 rb_shell_get_player (RBShell *shell)
3191 return G_OBJECT (shell->priv->player_shell);
3194 const char *
3195 rb_shell_get_player_path (RBShell *shell)
3197 return "/org/gnome/Rhythmbox/Player";
3200 GObject *
3201 rb_shell_get_playlist_manager (RBShell *shell)
3203 return G_OBJECT (shell->priv->playlist_manager);
3206 const char *
3207 rb_shell_get_playlist_manager_path (RBShell *shell)
3209 return "/org/gnome/Rhythmbox/PlaylistManager";
3212 GObject *
3213 rb_shell_get_ui_manager (RBShell *shell)
3215 return G_OBJECT (shell->priv->ui_manager);
3218 gboolean
3219 rb_shell_add_to_queue (RBShell *shell,
3220 const gchar *uri,
3221 GError **error)
3223 rb_static_playlist_source_add_location (RB_STATIC_PLAYLIST_SOURCE (shell->priv->queue_source),
3224 uri, -1);
3225 return TRUE;
3228 gboolean
3229 rb_shell_remove_from_queue (RBShell *shell,
3230 const gchar *uri,
3231 GError **error)
3233 if (rb_playlist_source_location_in_map (RB_PLAYLIST_SOURCE (shell->priv->queue_source), uri))
3234 rb_static_playlist_source_remove_location (RB_STATIC_PLAYLIST_SOURCE (shell->priv->queue_source),
3235 uri);
3236 return TRUE;
3239 gboolean
3240 rb_shell_clear_queue (RBShell *shell,
3241 GError **error)
3243 rb_play_queue_source_clear_queue (RB_PLAY_QUEUE_SOURCE (shell->priv->queue_source));
3244 return TRUE;
3247 gboolean
3248 rb_shell_present (RBShell *shell,
3249 guint32 timestamp,
3250 GError **error)
3252 rb_profile_start ("presenting shell");
3254 rb_debug ("presenting with timestamp %u", timestamp);
3255 gtk_widget_show (GTK_WIDGET (shell->priv->window));
3256 #if GTK_MINOR_VERSION >= 8
3257 gtk_window_present_with_time (GTK_WINDOW (shell->priv->window), timestamp);
3258 #else
3259 gtk_window_present (GTK_WINDOW (shell->priv->window));
3260 gdk_window_focus (GTK_WIDGET (shell->priv->window)->window, timestamp);
3261 #endif
3262 gtk_window_set_skip_taskbar_hint (GTK_WINDOW (shell->priv->window), FALSE);
3264 rb_profile_end ("presenting shell");
3266 return TRUE;
3269 gboolean
3270 rb_shell_get_song_properties (RBShell *shell,
3271 const char *uri,
3272 GHashTable **properties,
3273 GError **error)
3275 RhythmDBEntry *entry;
3277 entry = rhythmdb_entry_lookup_by_location (shell->priv->db, uri);
3279 if (entry == NULL) {
3280 g_set_error (error,
3281 RB_SHELL_ERROR,
3282 RB_SHELL_ERROR_NO_SUCH_URI,
3283 _("Unknown song URI: %s"),
3284 uri);
3285 return FALSE;
3288 *properties = rhythmdb_entry_gather_metadata (shell->priv->db, entry);
3289 return (*properties != NULL);
3292 gboolean
3293 rb_shell_set_song_property (RBShell *shell,
3294 const char *uri,
3295 const char *propname,
3296 const GValue *value,
3297 GError **error)
3299 RhythmDBEntry *entry;
3300 GType proptype;
3301 int propid;
3303 entry = rhythmdb_entry_lookup_by_location (shell->priv->db, uri);
3305 if (entry == NULL) {
3306 g_set_error (error,
3307 RB_SHELL_ERROR,
3308 RB_SHELL_ERROR_NO_SUCH_URI,
3309 _("Unknown song URI: %s"),
3310 uri);
3311 return FALSE;
3314 if ((propid = rhythmdb_propid_from_nice_elt_name (shell->priv->db, (guchar *) propname)) < 0) {
3315 g_set_error (error,
3316 RB_SHELL_ERROR,
3317 RB_SHELL_ERROR_NO_SUCH_PROPERTY,
3318 _("Unknown property %s"),
3319 propname);
3320 return FALSE;
3323 proptype = rhythmdb_get_property_type (shell->priv->db, propid);
3324 if (G_VALUE_TYPE (value) != proptype) {
3325 g_set_error (error,
3326 RB_SHELL_ERROR,
3327 RB_SHELL_ERROR_INVALID_PROPERTY_TYPE,
3328 _("Invalid property type %s for property %s"),
3329 g_type_name (G_VALUE_TYPE (value)),
3330 uri);
3331 return FALSE;
3334 rhythmdb_entry_set (shell->priv->db, entry, propid, value);
3335 return TRUE;
3338 static void
3339 rb_shell_volume_widget_changed_cb (BaconVolumeButton *vol,
3340 RBShell *shell)
3342 if (!shell->priv->syncing_volume)
3343 g_object_set (shell->priv->player_shell,
3344 "volume", bacon_volume_button_get_value (vol),
3345 NULL);
3348 static void
3349 rb_shell_player_volume_changed_cb (RBShellPlayer *player,
3350 GParamSpec *arg,
3351 RBShell *shell)
3353 float volume;
3355 g_object_get (player, "volume", &volume, NULL);
3356 shell->priv->syncing_volume = TRUE;
3357 bacon_volume_button_set_value (BACON_VOLUME_BUTTON (shell->priv->volume_button),
3358 volume);
3359 shell->priv->syncing_volume = FALSE;
3363 static void
3364 rb_shell_druid_response_cb (GtkDialog *druid,
3365 guint response,
3366 RBShell *shell)
3368 gtk_widget_show_all (GTK_WIDGET (shell->priv->window));
3369 gtk_widget_destroy (GTK_WIDGET (druid));
3372 static GtkBox*
3373 rb_shell_get_box_for_ui_location (RBShell *shell, RBShellUILocation location)
3375 GtkBox *box = NULL;
3377 switch (location) {
3378 case RB_SHELL_UI_LOCATION_SIDEBAR:
3379 box = shell->priv->sidebar_container;
3380 break;
3381 case RB_SHELL_UI_LOCATION_MAIN_TOP:
3382 box = shell->priv->top_container;
3383 break;
3384 case RB_SHELL_UI_LOCATION_MAIN_BOTTOM:
3385 box = shell->priv->bottom_container;
3386 break;
3389 return box;
3392 void
3393 rb_shell_add_widget (RBShell *shell, GtkWidget *widget, RBShellUILocation location)
3395 GtkBox *box;
3397 box = rb_shell_get_box_for_ui_location (shell, location);
3398 g_return_if_fail (box != NULL);
3400 gtk_box_pack_start (box, widget, FALSE, TRUE, 0);
3403 void
3404 rb_shell_remove_widget (RBShell *shell, GtkWidget *widget, RBShellUILocation location)
3406 GtkBox *box;
3408 box = rb_shell_get_box_for_ui_location (shell, location);
3409 g_return_if_fail (box != NULL);
3411 gtk_container_remove (GTK_CONTAINER (box), widget);
3414 /* This should really be standard. */
3415 #define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
3417 GType
3418 rb_shell_ui_location_get_type (void)
3420 static GType etype = 0;
3422 if (etype == 0) {
3423 static const GEnumValue values[] = {
3424 ENUM_ENTRY (RB_SHELL_UI_LOCATION_SIDEBAR, "Sidebar"),
3425 ENUM_ENTRY (RB_SHELL_UI_LOCATION_MAIN_TOP, "Main Top"),
3426 ENUM_ENTRY (RB_SHELL_UI_LOCATION_MAIN_BOTTOM, "Main Bottom"),
3427 { 0, 0, 0 }
3430 etype = g_enum_register_static ("RBShellUILocation", values);
3433 return etype;