2006-05-14 James Livingston <doclivingston@gmail.com>
[rhythmbox.git] / shell / rb-shell.c
blob82e25ed94c6cd9d213087e5e1dd3938b27ac0f0f
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 <libgnomeui/gnome-stock-icons.h>
40 #include <libgnome/gnome-init.h>
41 #include <libgnome/gnome-program.h>
42 #include <libgnomeui/gnome-window-icon.h>
43 #include <libgnomeui/gnome-client.h>
45 #include <libgnomevfs/gnome-vfs.h>
46 #include <libgnomevfs/gnome-vfs-mime-utils.h>
48 #include "rb-shell.h"
49 #include "rb-debug.h"
50 #include "rb-dialog.h"
51 #ifdef WITH_RHYTHMDB_TREE
52 #include "rhythmdb-tree.h"
53 #elif defined(WITH_RHYTHMDB_GDA)
54 #include "rhythmdb-gda.h"
55 #else
56 #error "no database specified. configure broken?"
57 #endif
58 #include "rb-stock-icons.h"
59 #include "rb-sourcelist.h"
60 #include "rb-file-helpers.h"
61 #include "rb-source.h"
62 #include "rb-playlist-manager.h"
63 #include "rb-removable-media-manager.h"
64 #include "rb-preferences.h"
65 #include "rb-druid.h"
66 #include "rb-shell-clipboard.h"
67 #include "rb-shell-player.h"
68 #include "rb-source-header.h"
69 #include "rb-tray-icon.h"
70 #include "rb-statusbar.h"
71 #include "rb-shell-preferences.h"
72 #include "rb-library-source.h"
73 #include "rb-podcast-source.h"
74 #include "totem-pl-parser.h"
75 #ifdef WITH_DAAP_SUPPORT
76 #include "rb-daap-source.h"
77 #include "rb-daap-sharing.h"
78 #endif /* WITH_DAAP_SUPPORT */
79 #include "rb-iradio-source.h"
80 #include "rb-shell-preferences.h"
81 #include "rb-playlist-source.h"
82 #include "rb-static-playlist-source.h"
83 #include "rb-play-queue-source.h"
84 #include "eel-gconf-extensions.h"
85 #include "bacon-volume.h"
86 #include "rb-missing-files-source.h"
87 #include "rb-import-errors-source.h"
88 #include "rb-plugins-engine.h"
89 #include "rb-plugin-manager.h"
90 #include "rb-proxy-config.h"
91 #include "rb-util.h"
92 #include "rb-sourcelist-model.h"
94 static void rb_shell_class_init (RBShellClass *klass);
95 static void rb_shell_init (RBShell *shell);
96 static GObject *rb_shell_constructor (GType type,
97 guint n_construct_properties,
98 GObjectConstructParam *construct_properties);
99 static void rb_shell_finalize (GObject *object);
100 static void rb_shell_set_property (GObject *object,
101 guint prop_id,
102 const GValue *value,
103 GParamSpec *pspec);
104 static void rb_shell_get_property (GObject *object,
105 guint prop_id,
106 GValue *value,
107 GParamSpec *pspec);
108 static gboolean rb_shell_get_visibility (RBShell *shell);
109 static gboolean rb_shell_window_state_cb (GtkWidget *widget,
110 GdkEventWindowState *event,
111 RBShell *shell);
112 static gboolean rb_shell_window_configure_cb (GtkWidget *win,
113 GdkEventConfigure*event,
114 RBShell *shell);
115 static gboolean rb_shell_window_delete_cb (GtkWidget *win,
116 GdkEventAny *event,
117 RBShell *shell);
118 static void rb_shell_sync_window_state (RBShell *shell, gboolean dont_maximise);
119 static void rb_shell_sync_paned (RBShell *shell);
120 static void rb_shell_sync_party_mode (RBShell *shell);
121 static void rb_shell_select_source (RBShell *shell, RBSource *source);
122 static void source_selected_cb (RBSourceList *sourcelist,
123 RBSource *source,
124 RBShell *shell);
125 static void rb_shell_playing_source_changed_cb (RBShellPlayer *player,
126 RBSource *source,
127 RBShell *shell);
128 static void rb_shell_playing_entry_changed_cb (RBShellPlayer *player,
129 RhythmDBEntry *entry,
130 RBShell *shell);
131 static void rb_shell_playing_from_queue_cb (RBShellPlayer *player,
132 GParamSpec *arg,
133 RBShell *shell);
134 static void source_activated_cb (RBSourceList *sourcelist,
135 RBSource *source,
136 RBShell *shell);
137 static void rb_shell_db_save_error_cb (RhythmDB *db,
138 const char *uri, const GError *error,
139 RBShell *shell);
140 static void rb_shell_db_entry_added_cb (RhythmDB *db,
141 RhythmDBEntry *entry,
142 RBShell *shell);
143 static void rb_shell_druid_response_cb (GtkDialog *druid,
144 guint response,
145 RBShell *shell);
147 static void rb_shell_playlist_added_cb (RBPlaylistManager *mgr, RBSource *source, RBShell *shell);
148 static void rb_shell_playlist_created_cb (RBPlaylistManager *mgr, RBSource *source, RBShell *shell);
149 static void rb_shell_medium_added_cb (RBRemovableMediaManager *mgr, RBSource *source, RBShell *shell);
150 static void rb_shell_transfer_progress_cb (RBRemovableMediaManager *mgr,
151 gint done,
152 gint total,
153 double fraction,
154 RBShell *shell);
155 static void rb_shell_source_deleted_cb (RBSource *source, RBShell *shell);
156 static void rb_shell_set_window_title (RBShell *shell, const char *window_title);
157 static void rb_shell_set_elapsed (RBShell *shell, guint elapsed);
158 static void rb_shell_player_window_title_changed_cb (RBShellPlayer *player,
159 const char *window_title,
160 RBShell *shell);
161 static void rb_shell_player_elapsed_changed_cb (RBShellPlayer *player,
162 guint elapsed,
163 RBShell *shell);
164 static void rb_shell_player_stream_song_changed_cb (RBShellPlayer *player,
165 GParamSpec *arg,
166 RBShell *shell);
167 static void rb_shell_cmd_about (GtkAction *action,
168 RBShell *shell);
169 static void rb_shell_cmd_contents (GtkAction *action,
170 RBShell *shell);
171 static void rb_shell_cmd_toggle_visibility (GtkAction *action,
172 RBShell *shell);
173 static void rb_shell_cmd_quit (GtkAction *action,
174 RBShell *shell);
175 static void rb_shell_cmd_preferences (GtkAction *action,
176 RBShell *shell);
177 static void rb_shell_cmd_plugins (GtkAction *action,
178 RBShell *shell);
179 static void rb_shell_cmd_add_folder_to_library (GtkAction *action,
180 RBShell *shell);
181 static void rb_shell_cmd_add_file_to_library (GtkAction *action,
182 RBShell *shell);
184 static void rb_shell_cmd_extract_cd (GtkAction *action,
185 RBShell *shell);
186 static void rb_shell_cmd_current_song (GtkAction *action,
187 RBShell *shell);
188 static void rb_shell_jump_to_current (RBShell *shell);
189 static void rb_shell_jump_to_entry_with_source (RBShell *shell, RBSource *source,
190 RhythmDBEntry *entry);
191 static void rb_shell_play_entry (RBShell *shell, RhythmDBEntry *entry);
192 static void rb_shell_quit (RBShell *shell);
193 static void rb_shell_cmd_view_all (GtkAction *action,
194 RBShell *shell);
195 static void rb_shell_view_sourcelist_changed_cb (GtkAction *action,
196 RBShell *shell);
197 static void rb_shell_view_toolbar_changed_cb (GtkAction *action,
198 RBShell *shell);
199 static void rb_shell_view_party_mode_changed_cb (GtkAction *action,
200 RBShell *shell);
201 static void rb_shell_view_smalldisplay_changed_cb (GtkAction *action,
202 RBShell *shell);
203 static void rb_shell_view_statusbar_changed_cb (GtkAction *action,
204 RBShell *shell);
205 static void rb_shell_view_queue_as_sidebar_changed_cb (GtkAction *action,
206 RBShell *shell);
207 static void rb_shell_load_complete_cb (RhythmDB *db, RBShell *shell);
208 static void rb_shell_sync_sourcelist_visibility (RBShell *shell);
209 static void rb_shell_sync_toolbar_state (RBShell *shell);
210 static void rb_shell_sync_smalldisplay (RBShell *shell);
211 static void rb_shell_sync_pane_visibility (RBShell *shell);
212 static void rb_shell_sync_statusbar_visibility (RBShell *shell);
213 static void rb_shell_set_visibility (RBShell *shell,
214 gboolean visible,
215 gboolean force);
216 static void sourcelist_visibility_changed_cb (GConfClient *client,
217 guint cnxn_id,
218 GConfEntry *entry,
219 RBShell *shell);
220 static void toolbar_state_changed_cb (GConfClient *client,
221 guint cnxn_id,
222 GConfEntry *entry,
223 RBShell *shell);
224 static void smalldisplay_changed_cb (GConfClient *client,
225 guint cnxn_id,
226 GConfEntry *entry,
227 RBShell *shell);
228 static void sourcelist_drag_received_cb (RBSourceList *sourcelist,
229 RBSource *source,
230 GtkSelectionData *data,
231 RBShell *shell);
232 static gboolean rb_shell_show_popup_cb (RBSourceList *sourcelist,
233 RBSource *target,
234 RBShell *shell);
235 static gboolean tray_destroy_cb (GtkObject *object, RBShell *shell);
237 static gboolean save_yourself_cb (GnomeClient *client,
238 gint phase,
239 GnomeSaveStyle save_style,
240 gboolean shutdown,
241 GnomeInteractStyle interact_style,
242 gboolean fast,
243 RBShell *shell);
244 static void paned_size_allocate_cb (GtkWidget *widget,
245 GtkAllocation *allocation,
246 RBShell *shell);
247 static void sidebar_paned_size_allocate_cb (GtkWidget *widget,
248 GtkAllocation *allocation,
249 RBShell *shell);
250 static void rb_shell_volume_widget_changed_cb (BaconVolumeButton *vol,
251 RBShell *shell);
252 static void rb_shell_player_volume_changed_cb (RBShellPlayer *player,
253 GParamSpec *arg,
254 RBShell *shell);
256 static void session_die_cb (GnomeClient *client, RBShell *shell);
257 static void rb_shell_session_init (RBShell *shell);
259 enum
261 PROP_NONE,
262 PROP_ARGC,
263 PROP_ARGV,
264 PROP_NO_REGISTRATION,
265 PROP_NO_UPDATE,
266 PROP_DRY_RUN,
267 PROP_RHYTHMDB_FILE,
268 PROP_SELECTED_SOURCE,
269 PROP_DB,
270 PROP_UI_MANAGER,
271 PROP_CLIPBOARD,
272 PROP_PLAYLIST_MANAGER,
273 PROP_REMOVABLE_MEDIA_MANAGER,
274 PROP_SHELL_PLAYER,
275 PROP_WINDOW,
276 PROP_PREFS,
277 PROP_QUEUE_SOURCE,
278 PROP_PROXY_CONFIG,
279 PROP_LIBRARY_SOURCE,
280 PROP_SOURCELIST_MODEL,
281 PROP_VISIBILITY,
284 /* prefs */
285 #define CONF_STATE_WINDOW_WIDTH CONF_PREFIX "/state/window_width"
286 #define CONF_STATE_WINDOW_HEIGHT CONF_PREFIX "/state/window_height"
287 #define CONF_STATE_SMALL_WIDTH CONF_PREFIX "/state/small_width"
288 #define CONF_STATE_WINDOW_MAXIMIZED CONF_PREFIX "/state/window_maximized"
289 #define CONF_STATE_PANED_POSITION CONF_PREFIX "/state/paned_position"
290 #define CONF_STATE_ADD_DIR CONF_PREFIX "/state/add_dir"
291 #define CONF_STATE_WINDOW_X_POSITION CONF_PREFIX "/state/window_x_position"
292 #define CONF_STATE_WINDOW_Y_POSITION CONF_PREFIX "/state/window_y_position"
293 #define CONF_STATE_SOURCELIST_HEIGHT CONF_PREFIX "/state/sourcelist_height"
295 enum
297 VISIBILITY_CHANGED,
298 LAST_SIGNAL
301 static guint rb_shell_signals[LAST_SIGNAL] = { 0 };
303 G_DEFINE_TYPE (RBShell, rb_shell, G_TYPE_OBJECT)
306 struct RBShellPrivate
308 GtkWidget *window;
309 gboolean iconified;
310 guint idle_hide_mainwindow_id;
312 GtkUIManager *ui_manager;
313 GtkActionGroup *actiongroup;
314 guint source_ui_merge_id;
316 GtkWidget *main_vbox;
317 GtkWidget *paned;
318 GtkWidget *sourcelist;
319 GtkWidget *notebook;
320 GtkWidget *queue_paned;
321 GtkWidget *queue_sidebar;
323 GtkBox *sidebar_container;
324 GtkBox *top_container;
325 GtkBox *bottom_container;
327 GList *sources;
328 GHashTable *sources_hash;
330 guint async_state_save_id;
331 guint save_playlist_id;
332 guint save_db_id;
334 gboolean shutting_down;
335 gboolean load_complete;
337 int argc;
338 char **argv;
339 gboolean no_registration;
340 gboolean no_update;
341 gboolean dry_run;
342 char *rhythmdb_file;
344 RhythmDB *db;
345 char *pending_entry;
347 RBShellPlayer *player_shell;
348 RBShellClipboard *clipboard_shell;
349 RBSourceHeader *source_header;
350 RBStatusbar *statusbar;
351 RBPlaylistManager *playlist_manager;
352 RBRemovableMediaManager *removable_media_manager;
354 GList *supported_media_extensions;
356 RBLibrarySource *library_source;
357 RBIRadioSource *iradio_source;
358 RBPodcastSource *podcast_source;
359 RBPlaylistSource *queue_source;
360 RBSource *missing_files_source;
361 RBSource *import_errors_source;
363 RBProxyConfig *proxy_config;
365 RBSource *selected_source;
367 GtkWidget *prefs;
368 GtkWidget *plugins;
370 RBTrayIcon *tray_icon;
371 GtkTooltips *tooltips;
372 GtkWidget *volume_button;
373 gboolean syncing_volume;
375 char *cached_title;
376 char *cached_duration;
377 gboolean cached_playing;
379 guint sourcelist_visibility_notify_id;
380 guint toolbar_visibility_notify_id;
381 guint toolbar_style_notify_id;
382 guint smalldisplay_notify_id;
384 glong last_small_time; /* when we last changed small mode */
386 /* Cached copies of the gconf keys.
388 * To avoid race conditions, the only time the keys are actually read is at startup
390 guint window_width;
391 guint window_height;
392 guint small_width;
393 gboolean window_maximised;
394 gboolean window_small;
395 gboolean queue_as_sidebar;
396 gboolean statusbar_hidden;
397 gboolean party_mode;
398 gint window_x;
399 gint window_y;
400 gint paned_position;
401 gint sourcelist_height;
404 #define RB_SHELL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), RB_TYPE_SHELL, RBShellPrivate))
406 static GtkActionEntry rb_shell_actions [] =
408 { "Music", NULL, N_("_Music") },
409 { "Edit", NULL, N_("_Edit") },
410 { "View", NULL, N_("_View") },
411 { "Control", NULL, N_("_Control") },
412 { "Tools", NULL, N_("_Tools") },
413 { "Help", NULL, N_("_Help") },
415 { "MusicImportFolder", GTK_STOCK_DIRECTORY, N_("_Import Folder..."), "<control>O",
416 N_("Choose folder to be added to the Library"),
417 G_CALLBACK (rb_shell_cmd_add_folder_to_library) },
418 { "MusicImportFile", GTK_STOCK_FILE, N_("Import _File..."), NULL,
419 N_("Choose file to be added to the Library"),
420 G_CALLBACK (rb_shell_cmd_add_file_to_library) },
421 { "MusicImportCD", GTK_STOCK_CDROM, N_("Import _Audio CD..."), "<control>E",
422 N_("Extract and import songs from a CD"),
423 G_CALLBACK (rb_shell_cmd_extract_cd) },
424 { "HelpAbout", GTK_STOCK_ABOUT, N_("_About"), NULL,
425 N_("Show information about the music player"),
426 G_CALLBACK (rb_shell_cmd_about) },
427 { "HelpContents", GTK_STOCK_HELP, N_("_Contents"), "F1",
428 N_("Display music player help"),
429 G_CALLBACK (rb_shell_cmd_contents) },
430 { "MusicClose", GTK_STOCK_CLOSE, N_("_Close"), "<control>W",
431 N_("Hide the music player window"),
432 G_CALLBACK (rb_shell_cmd_toggle_visibility) },
433 { "MusicQuit", GTK_STOCK_QUIT, N_("_Quit"), "<control>Q",
434 N_("Quit the music player"),
435 G_CALLBACK (rb_shell_cmd_quit) },
436 { "EditPreferences", GTK_STOCK_PREFERENCES, N_("Prefere_nces..."), NULL,
437 N_("Edit music player preferences"),
438 G_CALLBACK (rb_shell_cmd_preferences) },
439 { "EditPlugins", NULL, N_("P_lugins..."), NULL,
440 N_("Change and configure plugins"),
441 G_CALLBACK (rb_shell_cmd_plugins) },
442 { "ViewAll", NULL, N_("Show _All"), "<control>Y",
443 N_("Show all items in this music source"),
444 G_CALLBACK (rb_shell_cmd_view_all) },
445 { "ViewJumpToPlaying", GTK_STOCK_JUMP_TO, N_("_Jump to Playing Song"), "<control>J",
446 N_("Scroll the view to the currently playing song"),
447 G_CALLBACK (rb_shell_cmd_current_song) },
449 static guint rb_shell_n_actions = G_N_ELEMENTS (rb_shell_actions);
451 static GtkToggleActionEntry rb_shell_toggle_entries [] =
453 { "ViewSourceList", NULL, N_("Source _List"), "<control>L",
454 N_("Change the visibility of the source list"),
455 G_CALLBACK (rb_shell_view_sourcelist_changed_cb), TRUE },
456 { "ViewToolbar", NULL, N_("_Toolbar"), NULL,
457 N_("Change the visibility of the toolbar"),
458 G_CALLBACK (rb_shell_view_toolbar_changed_cb), TRUE },
459 { "ViewSmallDisplay", NULL, N_("_Small Display"), "<control>D",
460 N_("Make the main window smaller"),
461 G_CALLBACK (rb_shell_view_smalldisplay_changed_cb), },
462 { "ViewPartyMode", NULL, N_("_Party Mode"), NULL,
463 N_("Change the status of the party mode"),
464 G_CALLBACK (rb_shell_view_party_mode_changed_cb), FALSE },
465 { "ViewQueueAsSidebar", NULL, N_("Play _Queue as Side Pane"), "<control>K",
466 N_("Change whether the queue is visible as a source or a sidebar"),
467 G_CALLBACK (rb_shell_view_queue_as_sidebar_changed_cb) },
468 { "ViewStatusbar", NULL, N_("S_tatusbar"), NULL,
469 N_("Change the visibility of the statusbar"),
470 G_CALLBACK (rb_shell_view_statusbar_changed_cb), TRUE }
472 static guint rb_shell_n_toggle_entries = G_N_ELEMENTS (rb_shell_toggle_entries);
474 static void
475 rb_shell_class_init (RBShellClass *klass)
477 GObjectClass *object_class = (GObjectClass *) klass;
479 object_class->set_property = rb_shell_set_property;
480 object_class->get_property = rb_shell_get_property;
481 object_class->finalize = rb_shell_finalize;
482 object_class->constructor = rb_shell_constructor;
484 g_object_class_install_property (object_class,
485 PROP_ARGC,
486 g_param_spec_int ("argc",
487 "argc",
488 "Argument count",
489 0, 128,
490 0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
492 g_object_class_install_property (object_class,
493 PROP_ARGV,
494 g_param_spec_pointer ("argv",
495 "argv",
496 "Arguments",
497 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
499 g_object_class_install_property (object_class,
500 PROP_NO_REGISTRATION,
501 g_param_spec_boolean ("no-registration",
502 "no-registration",
503 "Whether or not to register",
504 FALSE,
505 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
507 g_object_class_install_property (object_class,
508 PROP_NO_UPDATE,
509 g_param_spec_boolean ("no-update",
510 "no-update",
511 "Whether or not to update the library",
512 FALSE,
513 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
515 g_object_class_install_property (object_class,
516 PROP_DRY_RUN,
517 g_param_spec_boolean ("dry-run",
518 "dry-run",
519 "Whether or not this is a dry run",
520 FALSE,
521 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
523 g_object_class_install_property (object_class,
524 PROP_RHYTHMDB_FILE,
525 g_param_spec_string ("rhythmdb-file",
526 "rhythmdb-file",
527 "The RhythmDB file to use",
528 #ifdef WITH_RHYTHMDB_TREE
529 "rhythmdb.xml",
530 #elif defined(WITH_RHYTHMDB_GDA)
531 "rhythmdb.sqlite", /* FIXME: correct extension? */
532 #endif
533 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
535 g_object_class_install_property (object_class,
536 PROP_SELECTED_SOURCE,
537 g_param_spec_object ("selected-source",
538 "selected-source",
539 "Source which is currently selected",
540 RB_TYPE_SOURCE,
541 G_PARAM_READABLE));
544 g_object_class_install_property (object_class,
545 PROP_DB,
546 g_param_spec_object ("db",
547 "RhythmDB",
548 "RhythmDB object",
549 RHYTHMDB_TYPE,
550 G_PARAM_READABLE));
552 g_object_class_install_property (object_class,
553 PROP_UI_MANAGER,
554 g_param_spec_object ("ui-manager",
555 "GtkUIManager",
556 "GtkUIManager object",
557 GTK_TYPE_UI_MANAGER,
558 G_PARAM_READABLE));
560 g_object_class_install_property (object_class,
561 PROP_CLIPBOARD,
562 g_param_spec_object ("clipboard",
563 "RBShellClipboard",
564 "RBShellClipboard object",
565 RB_TYPE_SHELL_CLIPBOARD,
566 G_PARAM_READABLE));
567 g_object_class_install_property (object_class,
568 PROP_PLAYLIST_MANAGER,
569 g_param_spec_object ("playlist-manager",
570 "RBPlaylistManager",
571 "RBPlaylistManager object",
572 RB_TYPE_PLAYLIST_MANAGER,
573 G_PARAM_READABLE));
574 g_object_class_install_property (object_class,
575 PROP_SHELL_PLAYER,
576 g_param_spec_object ("shell-player",
577 "RBShellPlayer",
578 "RBShellPlayer object",
579 RB_TYPE_SHELL_PLAYER,
580 G_PARAM_READABLE));
581 g_object_class_install_property (object_class,
582 PROP_REMOVABLE_MEDIA_MANAGER,
583 g_param_spec_object ("removable-media-manager",
584 "RBRemovableMediaManager",
585 "RBRemovableMediaManager object",
586 RB_TYPE_REMOVABLE_MEDIA_MANAGER,
587 G_PARAM_READABLE));
588 g_object_class_install_property (object_class,
589 PROP_WINDOW,
590 g_param_spec_object ("window",
591 "GtkWindow",
592 "GtkWindow object",
593 GTK_TYPE_WINDOW,
594 G_PARAM_READABLE));
595 g_object_class_install_property (object_class,
596 PROP_PREFS,
597 g_param_spec_object ("prefs",
598 "RBShellPreferences",
599 "RBShellPreferences object",
600 RB_TYPE_SHELL_PREFERENCES,
601 G_PARAM_READABLE));
602 g_object_class_install_property (object_class,
603 PROP_QUEUE_SOURCE,
604 g_param_spec_object ("queue-source",
605 "queue-source",
606 "Queue source",
607 RB_TYPE_PLAY_QUEUE_SOURCE,
608 G_PARAM_READABLE));
609 g_object_class_install_property (object_class,
610 PROP_PROXY_CONFIG,
611 g_param_spec_object ("proxy-config",
612 "proxy-config",
613 "HTTP proxy configuration",
614 RB_TYPE_PROXY_CONFIG,
615 G_PARAM_READABLE));
616 g_object_class_install_property (object_class,
617 PROP_LIBRARY_SOURCE,
618 g_param_spec_object ("library-source",
619 "library-source",
620 "Library source",
621 RB_TYPE_LIBRARY_SOURCE,
622 G_PARAM_READABLE));
623 g_object_class_install_property (object_class,
624 PROP_SOURCELIST_MODEL,
625 g_param_spec_object ("sourcelist-model",
626 "sourcelist-model",
627 "RBSourcelistModel",
628 RB_TYPE_SOURCELIST_MODEL,
629 G_PARAM_READABLE));
631 g_object_class_install_property (object_class,
632 PROP_VISIBILITY,
633 g_param_spec_boolean ("visibility",
634 "visibility",
635 "Current window visibility",
636 TRUE,
637 G_PARAM_READWRITE));
639 rb_shell_signals[VISIBILITY_CHANGED] =
640 g_signal_new ("visibility_changed",
641 G_OBJECT_CLASS_TYPE (object_class),
642 G_SIGNAL_RUN_LAST,
643 G_STRUCT_OFFSET (RBShellClass, visibility_changed),
644 NULL, NULL,
645 g_cclosure_marshal_VOID__BOOLEAN,
646 G_TYPE_NONE,
648 G_TYPE_BOOLEAN);
650 g_type_class_add_private (klass, sizeof (RBShellPrivate));
653 static void
654 rb_shell_init (RBShell *shell)
656 shell->priv = RB_SHELL_GET_PRIVATE (shell);
658 rb_dot_dir ();
660 rb_shell_session_init (shell);
662 eel_gconf_monitor_add (CONF_PREFIX);
665 static void
666 rb_shell_set_property (GObject *object,
667 guint prop_id,
668 const GValue *value,
669 GParamSpec *pspec)
671 RBShell *shell = RB_SHELL (object);
673 switch (prop_id)
675 case PROP_ARGC:
676 shell->priv->argc = g_value_get_int (value);
677 break;
678 case PROP_ARGV:
679 shell->priv->argv = g_value_get_pointer (value);
680 break;
681 case PROP_NO_REGISTRATION:
682 shell->priv->no_registration = g_value_get_boolean (value);
683 break;
684 case PROP_NO_UPDATE:
685 shell->priv->no_update = g_value_get_boolean (value);
686 break;
687 case PROP_DRY_RUN:
688 shell->priv->dry_run = g_value_get_boolean (value);
689 if (shell->priv->dry_run)
690 shell->priv->no_registration = TRUE;
691 break;
692 case PROP_RHYTHMDB_FILE:
693 shell->priv->rhythmdb_file = g_value_dup_string (value);
694 break;
695 case PROP_VISIBILITY:
696 rb_shell_set_visibility (shell, g_value_get_boolean (value), FALSE);
697 break;
698 default:
699 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
700 break;
704 static void
705 rb_shell_get_property (GObject *object,
706 guint prop_id,
707 GValue *value,
708 GParamSpec *pspec)
710 RBShell *shell = RB_SHELL (object);
712 switch (prop_id)
714 case PROP_ARGC:
715 g_value_set_int (value, shell->priv->argc);
716 break;
717 case PROP_ARGV:
718 g_value_set_pointer (value, shell->priv->argv);
719 break;
720 case PROP_NO_REGISTRATION:
721 g_value_set_boolean (value, shell->priv->no_registration);
722 break;
723 case PROP_NO_UPDATE:
724 g_value_set_boolean (value, shell->priv->no_update);
725 break;
726 case PROP_DRY_RUN:
727 g_value_set_boolean (value, shell->priv->dry_run);
728 break;
729 case PROP_RHYTHMDB_FILE:
730 g_value_set_string (value, shell->priv->rhythmdb_file);
731 break;
732 case PROP_DB:
733 g_value_set_object (value, shell->priv->db);
734 break;
735 case PROP_UI_MANAGER:
736 g_value_set_object (value, shell->priv->ui_manager);
737 break;
738 case PROP_CLIPBOARD:
739 g_value_set_object (value, shell->priv->clipboard_shell);
740 break;
741 case PROP_PLAYLIST_MANAGER:
742 g_value_set_object (value, shell->priv->playlist_manager);
743 break;
744 case PROP_SHELL_PLAYER:
745 g_value_set_object (value, shell->priv->player_shell);
746 break;
747 case PROP_REMOVABLE_MEDIA_MANAGER:
748 g_value_set_object (value, shell->priv->removable_media_manager);
749 break;
750 case PROP_SELECTED_SOURCE:
751 g_value_set_object (value, shell->priv->selected_source);
752 break;
753 case PROP_WINDOW:
754 g_value_set_object (value, shell->priv->window);
755 break;
756 case PROP_PREFS:
757 g_value_set_object (value, shell->priv->prefs);
758 break;
759 case PROP_QUEUE_SOURCE:
760 g_value_set_object (value, shell->priv->queue_source);
761 break;
762 case PROP_PROXY_CONFIG:
763 g_value_set_object (value, shell->priv->proxy_config);
764 break;
765 case PROP_LIBRARY_SOURCE:
766 g_value_set_object (value, shell->priv->library_source);
767 break;
768 case PROP_SOURCELIST_MODEL:
770 GtkTreeModel *model = NULL;
772 g_object_get (G_OBJECT (shell->priv->sourcelist), "model", &model, NULL);
773 g_value_set_object (value, model);
775 break;
776 case PROP_VISIBILITY:
777 g_value_set_boolean (value, rb_shell_get_visibility (shell));
778 break;
779 default:
780 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
781 break;
785 static gboolean
786 rb_shell_sync_state (RBShell *shell)
788 if (shell->priv->dry_run) {
789 rb_debug ("in dry-run mode, not syncing state");
790 return FALSE;
793 if (!shell->priv->load_complete) {
794 rb_debug ("load incomplete, not syncing state");
795 return FALSE;
798 rb_debug ("saving playlists");
799 rb_playlist_manager_save_playlists (shell->priv->playlist_manager, TRUE);
801 rb_debug ("saving db");
802 rhythmdb_save (shell->priv->db);
803 return FALSE;
806 static gboolean
807 idle_save_rhythmdb (RBShell *shell)
809 rhythmdb_save (shell->priv->db);
811 shell->priv->save_db_id = 0;
813 return FALSE;
816 static gboolean
817 idle_save_playlist_manager (RBPlaylistManager *mgr)
819 rb_playlist_manager_save_playlists (mgr, FALSE);
821 return TRUE;
824 static void
825 rb_shell_shutdown (RBShell *shell)
827 GdkDisplay *display;
829 if (shell->priv->shutting_down)
830 return;
831 shell->priv->shutting_down = TRUE;
833 /* Hide the main window and tray icon as soon as possible */
834 display = gtk_widget_get_display (shell->priv->window);
835 gtk_widget_hide (shell->priv->window);
836 gtk_widget_hide (GTK_WIDGET (shell->priv->tray_icon));
837 gdk_display_sync (display);
840 static void
841 rb_shell_finalize (GObject *object)
843 RBShell *shell = RB_SHELL (object);
845 rb_debug ("Finalizing shell");
847 rb_shell_player_stop (shell->priv->player_shell);
849 eel_gconf_monitor_remove (CONF_PREFIX);
850 eel_gconf_notification_remove (shell->priv->sourcelist_visibility_notify_id);
851 eel_gconf_notification_remove (shell->priv->toolbar_visibility_notify_id);
852 eel_gconf_notification_remove (shell->priv->toolbar_style_notify_id);
853 eel_gconf_notification_remove (shell->priv->smalldisplay_notify_id);
855 g_list_free (shell->priv->supported_media_extensions);
857 gtk_widget_destroy (GTK_WIDGET (shell->priv->tray_icon));
859 if (shell->priv->save_playlist_id > 0) {
860 g_source_remove (shell->priv->save_playlist_id);
861 shell->priv->save_playlist_id = 0;
864 if (shell->priv->save_db_id > 0) {
865 g_source_remove (shell->priv->save_db_id);
866 shell->priv->save_db_id = 0;
869 rb_debug ("shutting down playlist manager");
870 rb_playlist_manager_shutdown (shell->priv->playlist_manager);
872 rb_debug ("unreffing playlist manager");
873 g_object_unref (G_OBJECT (shell->priv->playlist_manager));
875 rb_debug ("unreffing removable media manager");
876 g_object_unref (G_OBJECT (shell->priv->removable_media_manager));
878 g_object_unref (G_OBJECT (shell->priv->proxy_config));
880 rb_debug ("unreffing clipboard shell");
881 g_object_unref (G_OBJECT (shell->priv->clipboard_shell));
883 rb_debug ("shutting down DB");
884 rhythmdb_shutdown (shell->priv->db);
886 rb_debug ("unreffing DB");
887 g_object_unref (G_OBJECT (shell->priv->db));
889 rb_debug ("destroying prefs");
890 if (shell->priv->prefs != NULL)
891 gtk_widget_destroy (shell->priv->prefs);
893 rb_debug ("destroying tooltips");
894 gtk_object_destroy (GTK_OBJECT (shell->priv->tooltips));
896 g_free (shell->priv->rhythmdb_file);
898 rb_debug ("destroying window");
899 gtk_widget_destroy (shell->priv->window);
901 g_list_free (shell->priv->sources);
902 shell->priv->sources = NULL;
904 g_hash_table_destroy (shell->priv->sources_hash);
906 ((GObjectClass*)rb_shell_parent_class)->finalize (G_OBJECT (shell));
908 rb_debug ("shell shutdown complete");
911 RBShell *
912 rb_shell_new (int argc,
913 char **argv,
914 gboolean no_registration,
915 gboolean no_update,
916 gboolean dry_run,
917 char *rhythmdb)
919 RBShell *s;
921 s = g_object_new (RB_TYPE_SHELL, "argc", argc, "argv", argv,
922 "no-registration", no_registration,
923 "no-update", no_update,
924 "dry-run", dry_run, "rhythmdb-file", rhythmdb, NULL);
926 return s;
929 static void
930 construct_db (RBShell *shell)
932 char *pathname;
934 /* Initialize the database */
935 rb_debug ("creating database object");
936 rb_profile_start ("creating database object");
938 if (shell->priv->rhythmdb_file)
939 pathname = g_strdup (shell->priv->rhythmdb_file);
940 else
941 pathname = g_build_filename (rb_dot_dir (), "rhythmdb.xml", NULL);
943 #ifdef WITH_RHYTHMDB_TREE
944 shell->priv->db = rhythmdb_tree_new (pathname);
945 #elif defined(WITH_RHYTHMDB_GDA)
946 shell->priv->db = rhythmdb_gda_new (pathname);
947 #endif
948 g_free (pathname);
950 if (shell->priv->dry_run)
951 g_object_set (G_OBJECT (shell->priv->db), "dry-run", TRUE, NULL);
952 if (shell->priv->no_update)
953 g_object_set (G_OBJECT (shell->priv->db), "no-update", TRUE, NULL);
955 g_signal_connect_object (G_OBJECT (shell->priv->db), "load-complete",
956 G_CALLBACK (rb_shell_load_complete_cb), shell,
959 rb_profile_end ("creating database object");
962 static void
963 construct_widgets (RBShell *shell)
965 GtkWindow *win;
967 rb_profile_start ("constructing widgets");
969 /* initialize UI */
970 win = GTK_WINDOW (gtk_window_new (GTK_WINDOW_TOPLEVEL));
971 gtk_window_set_title (win, _("Music Player"));
973 shell->priv->window = GTK_WIDGET (win);
974 shell->priv->iconified = FALSE;
975 g_signal_connect_object (G_OBJECT (win), "window-state-event",
976 G_CALLBACK (rb_shell_window_state_cb),
977 shell, 0);
979 g_signal_connect_object (G_OBJECT (win), "configure-event",
980 G_CALLBACK (rb_shell_window_configure_cb),
981 shell, 0);
983 /* connect after, so that things can affect behaviour */
984 g_signal_connect_object (G_OBJECT (win), "delete_event",
985 G_CALLBACK (rb_shell_window_delete_cb),
986 shell, G_CONNECT_AFTER);
988 rb_debug ("shell: initializing shell services");
990 shell->priv->ui_manager = gtk_ui_manager_new ();
991 shell->priv->source_ui_merge_id = gtk_ui_manager_new_merge_id (shell->priv->ui_manager);
993 shell->priv->player_shell = rb_shell_player_new (shell->priv->db,
994 shell->priv->ui_manager,
995 shell->priv->actiongroup);
996 g_signal_connect_object (G_OBJECT (shell->priv->player_shell),
997 "playing-source-changed",
998 G_CALLBACK (rb_shell_playing_source_changed_cb),
999 shell, 0);
1000 g_signal_connect_object (G_OBJECT (shell->priv->player_shell),
1001 "playing-song-changed",
1002 G_CALLBACK (rb_shell_playing_entry_changed_cb),
1003 shell, 0);
1004 g_signal_connect_object (G_OBJECT (shell->priv->player_shell),
1005 "notify::playing-from-queue",
1006 G_CALLBACK (rb_shell_playing_from_queue_cb),
1007 shell, 0);
1008 g_signal_connect_object (G_OBJECT (shell->priv->player_shell),
1009 "window_title_changed",
1010 G_CALLBACK (rb_shell_player_window_title_changed_cb),
1011 shell, 0);
1012 g_signal_connect_object (G_OBJECT (shell->priv->player_shell),
1013 "elapsed_changed",
1014 G_CALLBACK (rb_shell_player_elapsed_changed_cb),
1015 shell, 0);
1016 g_signal_connect_object (G_OBJECT (shell->priv->player_shell),
1017 "notify::stream-song",
1018 G_CALLBACK (rb_shell_player_stream_song_changed_cb),
1019 shell, 0);
1020 shell->priv->clipboard_shell = rb_shell_clipboard_new (shell->priv->actiongroup,
1021 shell->priv->db);
1022 shell->priv->source_header = rb_source_header_new (shell->priv->ui_manager,
1023 shell->priv->actiongroup);
1024 gtk_widget_show_all (GTK_WIDGET (shell->priv->source_header));
1026 shell->priv->paned = gtk_hpaned_new ();
1028 shell->priv->sourcelist = rb_sourcelist_new (shell);
1029 gtk_widget_show_all (shell->priv->sourcelist);
1030 g_signal_connect_object (G_OBJECT (shell->priv->sourcelist), "drop_received",
1031 G_CALLBACK (sourcelist_drag_received_cb), shell, 0);
1032 g_signal_connect_object (G_OBJECT (shell->priv->sourcelist), "source_activated",
1033 G_CALLBACK (source_activated_cb), shell, 0);
1034 g_signal_connect_object (G_OBJECT (shell->priv->sourcelist), "show_popup",
1035 G_CALLBACK (rb_shell_show_popup_cb), shell, 0);
1037 shell->priv->statusbar = rb_statusbar_new (shell->priv->db,
1038 shell->priv->actiongroup);
1039 g_object_set (shell->priv->player_shell, "statusbar", shell->priv->statusbar, NULL);
1040 gtk_widget_show (GTK_WIDGET (shell->priv->statusbar));
1042 g_signal_connect_object (G_OBJECT (shell->priv->sourcelist), "selected",
1043 G_CALLBACK (source_selected_cb), shell, 0);
1045 shell->priv->notebook = gtk_notebook_new ();
1046 gtk_widget_show (shell->priv->notebook);
1047 gtk_notebook_set_show_tabs (GTK_NOTEBOOK (shell->priv->notebook), FALSE);
1048 gtk_notebook_set_show_border (GTK_NOTEBOOK (shell->priv->notebook), FALSE);
1049 g_signal_connect_object (G_OBJECT (shell->priv->sourcelist),
1050 "size-allocate",
1051 G_CALLBACK (paned_size_allocate_cb),
1052 shell, 0);
1054 shell->priv->queue_source = RB_PLAYLIST_SOURCE (rb_play_queue_source_new (shell));
1055 g_object_set (G_OBJECT(shell->priv->player_shell), "queue-source", shell->priv->queue_source, NULL);
1056 g_object_set (G_OBJECT(shell->priv->clipboard_shell), "queue-source", shell->priv->queue_source, NULL);
1057 rb_shell_append_source (shell, RB_SOURCE (shell->priv->queue_source), NULL);
1058 g_object_get (G_OBJECT (shell->priv->queue_source), "sidebar", &shell->priv->queue_sidebar, NULL);
1059 gtk_widget_show_all (shell->priv->queue_sidebar);
1060 gtk_widget_set_no_show_all (shell->priv->queue_sidebar, TRUE);
1062 /* places for plugins to put UI */
1063 shell->priv->top_container = GTK_BOX (gtk_vbox_new (FALSE, 0));
1064 shell->priv->bottom_container = GTK_BOX (gtk_vbox_new (FALSE, 0));
1065 shell->priv->sidebar_container = GTK_BOX (gtk_vbox_new (FALSE, 0));
1067 /* set up sidebars */
1068 shell->priv->paned = gtk_hpaned_new ();
1070 GtkWidget *vbox2 = gtk_vbox_new (FALSE, 0);
1072 shell->priv->queue_paned = gtk_vpaned_new ();
1073 gtk_paned_pack1 (GTK_PANED (shell->priv->queue_paned),
1074 shell->priv->sourcelist,
1075 FALSE, TRUE);
1076 gtk_paned_pack2 (GTK_PANED (shell->priv->queue_paned),
1077 shell->priv->queue_sidebar,
1078 TRUE, TRUE);
1079 gtk_container_child_set (GTK_CONTAINER (shell->priv->queue_paned),
1080 GTK_WIDGET (shell->priv->queue_sidebar),
1081 "resize", FALSE,
1082 NULL);
1084 gtk_box_pack_start (GTK_BOX (vbox2),
1085 GTK_WIDGET (shell->priv->source_header),
1086 FALSE, FALSE, 0);
1087 gtk_box_pack_start (GTK_BOX (vbox2),
1088 shell->priv->notebook,
1089 TRUE, TRUE, 0);
1091 gtk_box_pack_start_defaults (shell->priv->sidebar_container, shell->priv->queue_paned);
1092 gtk_paned_pack1 (GTK_PANED (shell->priv->paned),
1093 GTK_WIDGET (shell->priv->sidebar_container),
1094 FALSE, TRUE);
1095 gtk_paned_pack2 (GTK_PANED (shell->priv->paned),
1096 vbox2,
1097 TRUE, TRUE);
1098 gtk_widget_show (vbox2);
1101 g_signal_connect_object (G_OBJECT (shell->priv->queue_paned),
1102 "size-allocate",
1103 G_CALLBACK (sidebar_paned_size_allocate_cb),
1104 shell, 0);
1105 gtk_widget_show (shell->priv->paned);
1107 shell->priv->main_vbox = gtk_vbox_new (FALSE, 0);
1108 gtk_container_set_border_width (GTK_CONTAINER (shell->priv->main_vbox), 0);
1109 gtk_box_pack_start (GTK_BOX (shell->priv->main_vbox), GTK_WIDGET (shell->priv->player_shell), FALSE, TRUE, 6);
1110 gtk_widget_show (GTK_WIDGET (shell->priv->player_shell));
1112 gtk_box_pack_start (GTK_BOX (shell->priv->main_vbox), GTK_WIDGET (shell->priv->top_container), FALSE, TRUE, 0);
1113 gtk_box_pack_start (GTK_BOX (shell->priv->main_vbox), shell->priv->paned, TRUE, TRUE, 0);
1114 gtk_box_pack_start (GTK_BOX (shell->priv->main_vbox), GTK_WIDGET (shell->priv->bottom_container), FALSE, TRUE, 0);
1115 gtk_box_pack_start (GTK_BOX (shell->priv->main_vbox), GTK_WIDGET (shell->priv->statusbar), FALSE, TRUE, 0);
1116 gtk_widget_show_all (shell->priv->main_vbox);
1118 gtk_container_add (GTK_CONTAINER (win), shell->priv->main_vbox);
1120 shell->priv->proxy_config = rb_proxy_config_new ();
1122 rb_profile_end ("constructing widgets");
1125 static void
1126 construct_sources (RBShell *shell)
1128 rb_profile_start ("constructing sources");
1130 shell->priv->library_source = RB_LIBRARY_SOURCE (rb_library_source_new (shell));
1131 rb_shell_append_source (shell, RB_SOURCE (shell->priv->library_source), NULL);
1132 shell->priv->iradio_source = RB_IRADIO_SOURCE (rb_iradio_source_new (shell));
1133 rb_shell_append_source (shell, RB_SOURCE (shell->priv->iradio_source), NULL);
1134 shell->priv->podcast_source = RB_PODCAST_SOURCE (rb_podcast_source_new (shell));
1135 rb_shell_append_source (shell, RB_SOURCE (shell->priv->podcast_source), NULL);
1137 shell->priv->missing_files_source = rb_missing_files_source_new (shell, shell->priv->library_source);
1138 rb_shell_append_source (shell, shell->priv->missing_files_source, RB_SOURCE (shell->priv->library_source));
1139 shell->priv->import_errors_source = rb_import_errors_source_new (shell, shell->priv->library_source);
1140 rb_shell_append_source (shell, shell->priv->import_errors_source, RB_SOURCE (shell->priv->library_source));
1142 /* Initialize playlist manager */
1143 rb_debug ("shell: creating playlist manager");
1144 shell->priv->playlist_manager = rb_playlist_manager_new (shell,
1145 RB_SOURCELIST (shell->priv->sourcelist));
1147 g_signal_connect_object (G_OBJECT (shell->priv->playlist_manager), "playlist_added",
1148 G_CALLBACK (rb_shell_playlist_added_cb), shell, 0);
1149 g_signal_connect_object (G_OBJECT (shell->priv->playlist_manager), "playlist_created",
1150 G_CALLBACK (rb_shell_playlist_created_cb), shell, 0);
1152 #ifdef WITH_DAAP_SUPPORT
1153 rb_daap_sources_init (shell);
1154 if (!shell->priv->no_registration)
1155 rb_daap_sharing_init (shell);
1156 #endif
1158 /* Initialize removable media manager */
1159 rb_debug ("shell: creating removable media manager");
1160 shell->priv->removable_media_manager = rb_removable_media_manager_new (shell,
1161 RB_SOURCELIST (shell->priv->sourcelist));
1163 g_signal_connect_object (G_OBJECT (shell->priv->removable_media_manager), "medium_added",
1164 G_CALLBACK (rb_shell_medium_added_cb), shell, 0);
1165 g_signal_connect_object (G_OBJECT (shell->priv->removable_media_manager), "transfer-progress",
1166 G_CALLBACK (rb_shell_transfer_progress_cb), shell, 0);
1168 rb_profile_end ("constructing sources");
1171 static void
1172 construct_load_ui (RBShell *shell)
1174 GtkWidget *menubar;
1175 GtkWidget *toolbar;
1176 GtkWidget *hbox;
1177 GtkToolItem *tool_item;
1178 GError *error = NULL;
1180 rb_debug ("shell: loading ui");
1181 rb_profile_start ("loading ui");
1183 gtk_ui_manager_insert_action_group (shell->priv->ui_manager,
1184 shell->priv->actiongroup, 0);
1185 gtk_ui_manager_add_ui_from_file (shell->priv->ui_manager,
1186 rb_file ("rhythmbox-ui.xml"), &error);
1188 gtk_ui_manager_ensure_update (shell->priv->ui_manager);
1189 gtk_window_add_accel_group (GTK_WINDOW (shell->priv->window),
1190 gtk_ui_manager_get_accel_group (shell->priv->ui_manager));
1191 menubar = gtk_ui_manager_get_widget (shell->priv->ui_manager, "/MenuBar");
1193 gtk_box_pack_start (GTK_BOX (shell->priv->main_vbox), menubar, FALSE, FALSE, 0);
1194 gtk_box_reorder_child (GTK_BOX (shell->priv->main_vbox), menubar, 0);
1196 hbox = gtk_hbox_new (FALSE, 0);
1197 gtk_box_pack_start (GTK_BOX (shell->priv->main_vbox), hbox, FALSE, FALSE, 0);
1198 gtk_box_reorder_child (GTK_BOX (shell->priv->main_vbox), hbox, 1);
1200 toolbar = gtk_ui_manager_get_widget (shell->priv->ui_manager, "/ToolBar");
1201 gtk_box_pack_start_defaults (GTK_BOX (hbox), toolbar);
1203 shell->priv->volume_button = bacon_volume_button_new (GTK_ICON_SIZE_LARGE_TOOLBAR,
1204 0.0, 1.0, 0.02);
1205 g_signal_connect (shell->priv->volume_button, "value-changed",
1206 G_CALLBACK (rb_shell_volume_widget_changed_cb),
1207 shell);
1208 g_signal_connect (shell->priv->player_shell, "notify::volume",
1209 G_CALLBACK (rb_shell_player_volume_changed_cb),
1210 shell);
1211 rb_shell_player_volume_changed_cb (shell->priv->player_shell, NULL, shell);
1213 tool_item = gtk_tool_item_new ();
1214 gtk_tool_item_set_expand (tool_item, TRUE);
1215 gtk_widget_show (GTK_WIDGET (tool_item));
1216 gtk_toolbar_insert (GTK_TOOLBAR (toolbar), tool_item, -1);
1218 tool_item = gtk_tool_item_new ();
1219 gtk_container_add (GTK_CONTAINER (tool_item), shell->priv->volume_button);
1220 gtk_widget_show_all (GTK_WIDGET (tool_item));
1221 gtk_toolbar_insert (GTK_TOOLBAR (toolbar), tool_item, -1);
1223 gtk_widget_show (hbox);
1225 shell->priv->tooltips = gtk_tooltips_new ();
1226 gtk_tooltips_enable (shell->priv->tooltips);
1228 gtk_tooltips_set_tip (GTK_TOOLTIPS (shell->priv->tooltips),
1229 GTK_WIDGET (shell->priv->volume_button),
1230 _("Change the music volume"), NULL);
1232 if (error != NULL) {
1233 g_warning ("Couldn't merge %s: %s",
1234 rb_file ("rhythmbox-ui.xml"), error->message);
1235 g_clear_error (&error);
1238 rb_profile_end ("loading ui");
1241 static GObject *
1242 rb_shell_constructor (GType type,
1243 guint n_construct_properties,
1244 GObjectConstructParam *construct_properties)
1246 RBShell *shell;
1248 shell = RB_SHELL (((GObjectClass*)rb_shell_parent_class)
1249 ->constructor (type, n_construct_properties, construct_properties));
1251 rb_debug ("Constructing shell");
1252 rb_profile_start ("constructing shell");
1254 shell->priv->actiongroup = gtk_action_group_new ("MainActions");
1255 gtk_action_group_set_translation_domain (shell->priv->actiongroup,
1256 GETTEXT_PACKAGE);
1257 gtk_action_group_add_actions (shell->priv->actiongroup,
1258 rb_shell_actions,
1259 rb_shell_n_actions, shell);
1260 gtk_action_group_add_toggle_actions (shell->priv->actiongroup,
1261 rb_shell_toggle_entries,
1262 rb_shell_n_toggle_entries,
1263 shell);
1265 construct_db (shell);
1267 /* initialize shell services */
1268 construct_widgets (shell);
1270 rb_debug ("shell: setting up tray icon");
1271 tray_destroy_cb (NULL, shell);
1273 rb_debug ("shell: adding gconf notification");
1274 /* sync state */
1275 shell->priv->sourcelist_visibility_notify_id =
1276 eel_gconf_notification_add (CONF_UI_SOURCELIST_HIDDEN,
1277 (GConfClientNotifyFunc) sourcelist_visibility_changed_cb,
1278 shell);
1279 shell->priv->toolbar_visibility_notify_id =
1280 eel_gconf_notification_add (CONF_UI_TOOLBAR_HIDDEN,
1281 (GConfClientNotifyFunc) toolbar_state_changed_cb,
1282 shell);
1283 shell->priv->toolbar_style_notify_id =
1284 eel_gconf_notification_add (CONF_UI_TOOLBAR_STYLE,
1285 (GConfClientNotifyFunc) toolbar_state_changed_cb,
1286 shell);
1287 shell->priv->smalldisplay_notify_id =
1288 eel_gconf_notification_add (CONF_UI_SMALL_DISPLAY,
1289 (GConfClientNotifyFunc) smalldisplay_changed_cb,
1290 shell);
1292 /* read the cached copies of the gconf keys */
1293 shell->priv->window_width = eel_gconf_get_integer (CONF_STATE_WINDOW_WIDTH);
1294 shell->priv->window_height = eel_gconf_get_integer (CONF_STATE_WINDOW_HEIGHT);
1295 shell->priv->small_width = eel_gconf_get_integer (CONF_STATE_SMALL_WIDTH);
1296 shell->priv->window_maximised = eel_gconf_get_boolean (CONF_STATE_WINDOW_MAXIMIZED);
1297 shell->priv->window_small = eel_gconf_get_boolean (CONF_UI_SMALL_DISPLAY);
1298 shell->priv->queue_as_sidebar = eel_gconf_get_boolean (CONF_UI_QUEUE_AS_SIDEBAR);
1299 shell->priv->window_x = eel_gconf_get_integer (CONF_STATE_WINDOW_X_POSITION);
1300 shell->priv->window_y = eel_gconf_get_integer (CONF_STATE_WINDOW_Y_POSITION);
1301 shell->priv->paned_position = eel_gconf_get_integer (CONF_STATE_PANED_POSITION);
1302 shell->priv->sourcelist_height = eel_gconf_get_integer (CONF_STATE_SOURCELIST_HEIGHT);
1303 shell->priv->statusbar_hidden = eel_gconf_get_boolean (CONF_UI_STATUSBAR_HIDDEN);
1306 rb_debug ("shell: syncing with gconf");
1307 rb_shell_sync_sourcelist_visibility (shell);
1308 rb_shell_sync_pane_visibility (shell);
1310 g_signal_connect_object (G_OBJECT (shell->priv->db), "save-error",
1311 G_CALLBACK (rb_shell_db_save_error_cb), shell, 0);
1312 g_signal_connect_object (G_OBJECT (shell->priv->db), "entry-added",
1313 G_CALLBACK (rb_shell_db_entry_added_cb), shell, 0);
1315 construct_sources (shell);
1317 construct_load_ui (shell);
1319 rb_shell_sync_window_state (shell, FALSE);
1320 rb_shell_sync_smalldisplay (shell);
1321 rb_shell_sync_party_mode (shell);
1322 rb_shell_sync_toolbar_state (shell);
1324 rb_shell_select_source (shell, RB_SOURCE (shell->priv->library_source));
1326 rb_plugins_engine_init (shell);
1328 /* GO GO GO! */
1329 rb_debug ("loading database");
1330 rhythmdb_load (shell->priv->db);
1332 rb_debug ("shell: syncing window state");
1333 rb_shell_sync_paned (shell);
1334 gtk_widget_show_all (GTK_WIDGET (shell->priv->tray_icon));
1336 /* Stop here if this is the first time. */
1337 if (!eel_gconf_get_boolean (CONF_FIRST_TIME)) {
1338 RBDruid *druid;
1339 druid = rb_druid_new (shell->priv->db);
1340 g_signal_connect (G_OBJECT (druid),
1341 "response",
1342 G_CALLBACK (rb_shell_druid_response_cb),
1343 shell);
1345 gtk_widget_show_all (GTK_WIDGET (druid));
1346 } else {
1347 rb_shell_set_visibility (shell, eel_gconf_get_boolean (CONF_STATE_WINDOW_VISIBLE), TRUE);
1348 gdk_notify_startup_complete ();
1351 /* focus play if small, the entry view if not */
1352 if (shell->priv->window_small) {
1353 GtkWidget *play_button;
1355 play_button = gtk_ui_manager_get_widget (shell->priv->ui_manager, "/ToolBar/Play");
1356 gtk_widget_grab_focus (play_button);
1357 } else {
1358 RBEntryView *view;
1360 view = rb_source_get_entry_view (RB_SOURCE (shell->priv->library_source));
1361 gtk_widget_grab_focus (GTK_WIDGET (view));
1364 rb_profile_end ("constructing shell");
1366 return G_OBJECT (shell);
1369 /* Based on a function found in wnck */
1370 static void
1371 set_icon_geometry (GdkWindow *window,
1372 int x,
1373 int y,
1374 int width,
1375 int height)
1377 gulong data[4];
1378 Display *dpy = gdk_x11_drawable_get_xdisplay (window);
1380 data[0] = x;
1381 data[1] = y;
1382 data[2] = width;
1383 data[3] = height;
1385 XChangeProperty (dpy,
1386 GDK_WINDOW_XID (window),
1387 gdk_x11_get_xatom_by_name_for_display (gdk_drawable_get_display (window),
1388 "_NET_WM_ICON_GEOMETRY"),
1389 XA_CARDINAL, 32, PropModeReplace,
1390 (guchar *)&data, 4);
1393 static gboolean
1394 rb_shell_window_state_cb (GtkWidget *widget,
1395 GdkEventWindowState *event,
1396 RBShell *shell)
1398 shell->priv->iconified = ((event->new_window_state & GDK_WINDOW_STATE_ICONIFIED) != 0);
1400 if (event->changed_mask & (GDK_WINDOW_STATE_WITHDRAWN | GDK_WINDOW_STATE_ICONIFIED)) {
1401 g_signal_emit (shell, rb_shell_signals[VISIBILITY_CHANGED], 0,
1402 rb_shell_get_visibility (shell));
1405 /* don't save maximized state when is hidden */
1406 if (!GTK_WIDGET_VISIBLE(shell->priv->window))
1407 return FALSE;
1409 if (event->changed_mask & GDK_WINDOW_STATE_MAXIMIZED) {
1410 gboolean maximised = ((event->new_window_state & GDK_WINDOW_STATE_MAXIMIZED) != 0);
1412 gtk_statusbar_set_has_resize_grip (GTK_STATUSBAR (shell->priv->statusbar),
1413 !maximised);
1414 if (!shell->priv->window_small) {
1415 shell->priv->window_maximised = maximised;
1416 eel_gconf_set_boolean (CONF_STATE_WINDOW_MAXIMIZED,
1417 shell->priv->window_maximised);
1419 rb_shell_sync_window_state (shell, TRUE);
1420 rb_shell_sync_paned (shell);
1423 return FALSE;
1426 static gboolean
1427 rb_shell_get_visibility (RBShell *shell)
1429 GdkWindowState state;
1431 if (!GTK_WIDGET_REALIZED (shell->priv->window))
1432 return FALSE;
1433 if (shell->priv->iconified)
1434 return FALSE;
1436 state = gdk_window_get_state (GTK_WIDGET (shell->priv->window)->window);
1437 if (state & (GDK_WINDOW_STATE_WITHDRAWN | GDK_WINDOW_STATE_ICONIFIED))
1438 return FALSE;
1440 return TRUE;
1443 static gboolean
1444 idle_hide_mainwindow (gpointer data)
1446 RBShell *shell = RB_SHELL (data);
1448 GDK_THREADS_ENTER ();
1450 gtk_widget_hide (GTK_WIDGET (shell->priv->window));
1452 g_signal_emit (shell, rb_shell_signals[VISIBILITY_CHANGED], 0, FALSE);
1454 g_object_unref (shell);
1456 GDK_THREADS_LEAVE ();
1458 return FALSE;
1461 static void
1462 rb_shell_set_visibility (RBShell *shell,
1463 gboolean visible,
1464 gboolean force)
1466 gboolean current_visible;
1468 rb_profile_start ("changing shell visibility");
1470 current_visible = rb_shell_get_visibility (shell);
1471 if (!force && visible == current_visible) {
1472 rb_profile_end ("changing shell visibility");
1473 return;
1476 /* FIXME - see below */
1477 if (shell->priv->idle_hide_mainwindow_id > 0)
1478 g_source_remove (shell->priv->idle_hide_mainwindow_id);
1479 shell->priv->idle_hide_mainwindow_id = 0;
1481 /* don't minimise if we have no tray icon */
1482 if (visible || (!force && !egg_tray_icon_have_manager (EGG_TRAY_ICON (shell->priv->tray_icon)))) {
1483 rb_debug ("showing main window");
1484 rb_shell_sync_window_state (shell, FALSE);
1486 if (egg_tray_icon_have_manager (EGG_TRAY_ICON (shell->priv->tray_icon)))
1487 gtk_window_set_skip_taskbar_hint (GTK_WINDOW (shell->priv->window), FALSE);
1488 gtk_widget_show (GTK_WIDGET (shell->priv->window));
1489 gtk_window_deiconify (GTK_WINDOW (shell->priv->window));
1491 if (GTK_WIDGET_REALIZED (GTK_WIDGET (shell->priv->window)))
1492 rb_shell_present (shell, gtk_get_current_event_time (), NULL);
1493 else
1494 gtk_widget_show_all (GTK_WIDGET (shell->priv->window));
1496 g_signal_emit (shell, rb_shell_signals[VISIBILITY_CHANGED], 0, visible);
1498 eel_gconf_set_boolean (CONF_STATE_WINDOW_VISIBLE, TRUE);
1499 } else {
1500 int x, y, width, height;
1502 rb_debug ("hiding main window");
1503 shell->priv->iconified = TRUE;
1504 rb_tray_icon_get_geom (shell->priv->tray_icon,
1505 &x, &y, &width, &height);
1506 if (GTK_WIDGET_REALIZED (GTK_WIDGET (shell->priv->window)))
1507 set_icon_geometry (GTK_WIDGET (shell->priv->window)->window,
1508 x, y, width, height);
1509 if (egg_tray_icon_have_manager (EGG_TRAY_ICON (shell->priv->tray_icon)))
1510 gtk_window_set_skip_taskbar_hint (GTK_WINDOW (shell->priv->window), TRUE);
1511 gtk_window_iconify (GTK_WINDOW (shell->priv->window));
1513 /* FIMXE - this is horribly evil racy workaround for a
1514 * current bug in the tasklist not noticing our hint
1515 * change
1517 shell->priv->idle_hide_mainwindow_id =
1518 g_timeout_add (250, idle_hide_mainwindow, g_object_ref (shell));
1520 eel_gconf_set_boolean (CONF_STATE_WINDOW_VISIBLE, FALSE);
1523 rb_profile_end ("changing shell visibility");
1526 static gboolean
1527 rb_shell_window_configure_cb (GtkWidget *win,
1528 GdkEventConfigure *event,
1529 RBShell *shell)
1531 if (shell->priv->window_small) {
1532 rb_debug ("storing small window width of %d", event->width);
1533 shell->priv->small_width = event->width;
1534 eel_gconf_set_integer (CONF_STATE_SMALL_WIDTH, event->width);
1535 } else if (!shell->priv->window_maximised && !shell->priv->iconified) {
1536 rb_debug ("storing window size of %d:%d", event->width, event->height);
1537 shell->priv->window_width = event->width;
1538 shell->priv->window_height = event->height;
1539 eel_gconf_set_integer (CONF_STATE_WINDOW_WIDTH, event->width);
1540 eel_gconf_set_integer (CONF_STATE_WINDOW_HEIGHT, event->height);
1543 if (shell->priv->window_small || !shell->priv->window_maximised) {
1544 gtk_window_get_position (GTK_WINDOW(shell->priv->window),
1545 &shell->priv->window_x,
1546 &shell->priv->window_y);
1548 eel_gconf_set_integer (CONF_STATE_WINDOW_X_POSITION, shell->priv->window_x);
1549 eel_gconf_set_integer (CONF_STATE_WINDOW_Y_POSITION, shell->priv->window_y);
1552 return FALSE;
1555 static gboolean
1556 rb_shell_window_delete_cb (GtkWidget *win,
1557 GdkEventAny *event,
1558 RBShell *shell)
1560 if (shell->priv->party_mode) {
1561 return TRUE;
1564 rb_shell_quit (shell);
1566 return TRUE;
1569 static void
1570 rb_shell_sync_window_state (RBShell *shell,
1571 gboolean dont_maximise)
1573 GdkGeometry hints;
1575 rb_profile_start ("syncing window state");
1577 if (shell->priv->window_small) {
1578 hints.min_height = -1;
1579 hints.min_width = -1;
1580 hints.max_height = -1;
1581 hints.max_width = 3000;
1582 gtk_window_set_default_size (GTK_WINDOW (shell->priv->window),
1583 shell->priv->small_width, 0);
1584 gtk_window_resize (GTK_WINDOW (shell->priv->window),
1585 shell->priv->small_width, 1);
1586 gtk_window_set_geometry_hints (GTK_WINDOW (shell->priv->window),
1587 NULL,
1588 &hints,
1589 GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE);
1590 gtk_window_unmaximize (GTK_WINDOW (shell->priv->window));
1591 rb_debug ("syncing small window width to %d", shell->priv->small_width);
1592 } else {
1593 if (!dont_maximise) {
1594 if (shell->priv->window_maximised)
1595 gtk_window_maximize (GTK_WINDOW (shell->priv->window));
1596 else
1597 gtk_window_unmaximize (GTK_WINDOW (shell->priv->window));
1600 gtk_window_set_default_size (GTK_WINDOW (shell->priv->window),
1601 shell->priv->window_width,
1602 shell->priv->window_height);
1603 gtk_window_resize (GTK_WINDOW (shell->priv->window),
1604 shell->priv->window_width,
1605 shell->priv->window_height);
1606 gtk_window_set_geometry_hints (GTK_WINDOW (shell->priv->window),
1607 NULL,
1608 &hints,
1612 gtk_window_move (GTK_WINDOW (shell->priv->window),
1613 shell->priv->window_x,
1614 shell->priv->window_y);
1615 rb_profile_end ("syncing window state");
1618 static void
1619 source_selected_cb (RBSourceList *sourcelist,
1620 RBSource *source,
1621 RBShell *shell)
1623 rb_debug ("source selected");
1624 rb_shell_select_source (shell, source);
1627 static void
1628 source_activated_cb (RBSourceList *sourcelist,
1629 RBSource *source,
1630 RBShell *shell)
1632 rb_debug ("source activated");
1634 /* Stop the playing source, if any */
1635 rb_shell_player_set_playing_source (shell->priv->player_shell, NULL);
1637 /* Select the new one, and start it playing */
1638 rb_shell_select_source (shell, source);
1639 rb_shell_player_set_playing_source (shell->priv->player_shell, source);
1640 /* Ignore error from here */
1641 rb_shell_player_playpause (shell->priv->player_shell, FALSE, NULL);
1644 static void
1645 rb_shell_db_save_error_cb (RhythmDB *db,
1646 const char *uri, const GError *error,
1647 RBShell *shell)
1649 rb_error_dialog (GTK_WINDOW (shell->priv->window),
1650 _("Error while saving song information"),
1651 "%s", error->message);
1654 static void
1655 rb_shell_db_entry_added_cb (RhythmDB *db,
1656 RhythmDBEntry *entry,
1657 RBShell *shell)
1659 if (shell->priv->pending_entry == NULL)
1660 return;
1662 rb_debug ("got entry added for %s", entry->location);
1663 if (strcmp (entry->location, shell->priv->pending_entry) == 0) {
1664 rb_shell_play_entry (shell, entry);
1666 g_free (shell->priv->pending_entry);
1667 shell->priv->pending_entry = NULL;
1671 RBSource *
1672 rb_shell_get_source_by_entry_type (RBShell *shell,
1673 RhythmDBEntryType type)
1675 return g_hash_table_lookup (shell->priv->sources_hash, type);
1678 void
1679 rb_shell_register_entry_type_for_source (RBShell *shell,
1680 RBSource *source,
1681 RhythmDBEntryType type)
1683 if (shell->priv->sources_hash == NULL) {
1684 shell->priv->sources_hash = g_hash_table_new (g_direct_hash,
1685 g_direct_equal);
1687 g_assert (g_hash_table_lookup (shell->priv->sources_hash, type) == NULL);
1688 g_hash_table_insert (shell->priv->sources_hash, type, source);
1693 void
1694 rb_shell_append_source (RBShell *shell,
1695 RBSource *source,
1696 RBSource *parent)
1698 shell->priv->sources
1699 = g_list_append (shell->priv->sources, source);
1701 g_signal_connect_object (G_OBJECT (source), "deleted",
1702 G_CALLBACK (rb_shell_source_deleted_cb), shell, 0);
1704 gtk_notebook_append_page (GTK_NOTEBOOK (shell->priv->notebook),
1705 GTK_WIDGET (source),
1706 gtk_label_new (""));
1707 gtk_widget_show (GTK_WIDGET (source));
1709 rb_sourcelist_append (RB_SOURCELIST (shell->priv->sourcelist),
1710 source, parent);
1713 static void
1714 rb_shell_playlist_added_cb (RBPlaylistManager *mgr,
1715 RBSource *source,
1716 RBShell *shell)
1718 rb_shell_append_source (shell, source, NULL);
1721 static void
1722 rb_shell_playlist_created_cb (RBPlaylistManager *mgr,
1723 RBSource *source,
1724 RBShell *shell)
1726 shell->priv->window_small = FALSE;
1727 eel_gconf_set_boolean (CONF_UI_SMALL_DISPLAY, shell->priv->window_small);
1728 eel_gconf_set_boolean (CONF_UI_SOURCELIST_HIDDEN, shell->priv->window_small);
1730 rb_shell_sync_window_state (shell, FALSE);
1733 static void
1734 rb_shell_medium_added_cb (RBRemovableMediaManager *mgr,
1735 RBSource *source,
1736 RBShell *shell)
1738 rb_shell_append_source (shell, source, NULL);
1741 static void
1742 rb_shell_transfer_progress_cb (RBRemovableMediaManager *mgr,
1743 gint done,
1744 gint total,
1745 double fraction,
1746 RBShell *shell)
1748 rb_debug ("transferred %d tracks out of %d", done, total);
1750 if (total > 0) {
1751 char *s;
1753 if (fraction >= 0)
1754 s = g_strdup_printf (_("Transferring track %d out of %d (%.0f%%)"),
1755 done + 1, total, fraction * 100);
1756 else
1757 s = g_strdup_printf (_("Transferring track %d out of %d"),
1758 done + 1, total);
1760 rb_statusbar_set_progress (shell->priv->statusbar,
1761 (((double)(done) + fraction)/total),
1763 } else {
1764 rb_statusbar_set_progress (shell->priv->statusbar, -1, NULL);
1768 static void
1769 rb_shell_source_deleted_cb (RBSource *source,
1770 RBShell *shell)
1772 rb_debug ("source deleted");
1774 if (source == rb_shell_player_get_playing_source (shell->priv->player_shell)) {
1775 rb_shell_player_set_playing_source (shell->priv->player_shell, NULL);
1777 if (source == shell->priv->selected_source) {
1778 if (source != RB_SOURCE (shell->priv->library_source))
1779 rb_shell_select_source (shell, RB_SOURCE (shell->priv->library_source));
1780 else
1781 rb_shell_select_source (shell, NULL);
1784 shell->priv->sources = g_list_remove (shell->priv->sources, source);
1786 rb_sourcelist_remove (RB_SOURCELIST (shell->priv->sourcelist), source);
1788 gtk_notebook_remove_page (GTK_NOTEBOOK (shell->priv->notebook),
1789 gtk_notebook_page_num (GTK_NOTEBOOK (shell->priv->notebook),
1790 GTK_WIDGET (source)));
1793 static void
1794 rb_shell_playing_source_changed_cb (RBShellPlayer *player,
1795 RBSource *source,
1796 RBShell *shell)
1798 rb_debug ("playing source changed");
1799 if (source != RB_SOURCE (shell->priv->queue_source))
1800 rb_sourcelist_set_playing_source (RB_SOURCELIST (shell->priv->sourcelist),
1801 source);
1804 static void
1805 rb_shell_playing_from_queue_cb (RBShellPlayer *player,
1806 GParamSpec *param,
1807 RBShell *shell)
1809 gboolean from_queue;
1810 g_object_get (G_OBJECT (player), "playing-from-queue", &from_queue, NULL);
1811 if (!shell->priv->queue_as_sidebar) {
1812 rb_sourcelist_set_playing_source (RB_SOURCELIST (shell->priv->sourcelist),
1813 rb_shell_player_get_playing_source (shell->priv->player_shell));
1814 } else {
1815 RBSource *source;
1816 RBEntryView *songs;
1817 RhythmDBEntry *entry;
1818 RhythmDBEntryType entry_type;
1820 /* if playing from the queue, show the playing entry as playing in the
1821 * registered source for its type, so it makes sense when 'jump to current'
1822 * jumps to it there.
1824 entry = rb_shell_player_get_playing_entry (shell->priv->player_shell);
1825 if (entry == NULL)
1826 return;
1828 entry_type = rhythmdb_entry_get_entry_type (entry);
1829 source = rb_shell_get_source_by_entry_type (shell, entry_type);
1830 songs = rb_source_get_entry_view (source);
1832 rb_entry_view_set_state (songs, from_queue ? RB_ENTRY_VIEW_PLAYING : RB_ENTRY_VIEW_NOT_PLAYING);
1833 rb_sourcelist_set_playing_source (RB_SOURCELIST (shell->priv->sourcelist),
1834 rb_shell_player_get_active_source (shell->priv->player_shell));
1838 static void
1839 rb_shell_playing_entry_changed_cb (RBShellPlayer *player,
1840 RhythmDBEntry *entry,
1841 RBShell *shell)
1843 /* RBRemoteSong song; */
1844 char *notifytitle;
1846 /* emit remote song_changed notification */
1848 song.title = g_strdup (rb_refstring_get (entry->title));
1849 song.artist = g_strdup (rb_refstring_get (entry->artist));
1850 song.genre = g_strdup (rb_refstring_get (entry->genre));
1851 song.album = g_strdup (rb_refstring_get (entry->album));
1852 song.uri = g_strdup (entry->location);
1854 song.track_number = entry->tracknum;
1855 song.disc_number = entry->discnum;
1856 song.duration = entry->duration;
1857 song.bitrate = entry->bitrate;
1858 song.filesize = entry->file_size;
1859 song.rating = entry->rating;
1860 song.play_count = entry->play_count;
1861 song.last_played = entry->last_played;
1863 g_signal_emit_by_name (RB_REMOTE_PROXY (shell), "song_changed", &song);
1866 /* Translators: by Artist from Album*/
1867 notifytitle = g_strdup_printf (_("by %s from %s"),
1868 rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ARTIST),
1869 rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ALBUM));
1870 rb_shell_hidden_notify (shell,
1871 4000,
1872 rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_TITLE),
1873 NULL,
1874 notifytitle);
1875 g_free (notifytitle);
1878 static void
1879 merge_source_ui_cb (const char *action,
1880 RBShell *shell)
1882 gtk_ui_manager_add_ui (shell->priv->ui_manager,
1883 shell->priv->source_ui_merge_id,
1884 "/ToolBar",
1885 action,
1886 action,
1887 GTK_UI_MANAGER_AUTO,
1888 FALSE);
1891 static void
1892 rb_shell_select_source (RBShell *shell,
1893 RBSource *source)
1895 GList *actions;
1897 if (shell->priv->selected_source == source)
1898 return;
1900 rb_debug ("selecting source %p", source);
1902 if (shell->priv->selected_source) {
1903 rb_source_deactivate (shell->priv->selected_source);
1904 gtk_ui_manager_remove_ui (shell->priv->ui_manager, shell->priv->source_ui_merge_id);
1907 shell->priv->selected_source = source;
1908 rb_source_activate (shell->priv->selected_source);
1910 /* show source */
1911 gtk_notebook_set_current_page (GTK_NOTEBOOK (shell->priv->notebook),
1912 gtk_notebook_page_num (GTK_NOTEBOOK (shell->priv->notebook), GTK_WIDGET (source)));
1914 g_signal_handlers_block_by_func (G_OBJECT (shell->priv->sourcelist),
1915 G_CALLBACK (source_selected_cb),
1916 shell);
1917 rb_sourcelist_select (RB_SOURCELIST (shell->priv->sourcelist),
1918 source);
1919 g_signal_handlers_unblock_by_func (G_OBJECT (shell->priv->sourcelist),
1920 G_CALLBACK (source_selected_cb),
1921 shell);
1923 /* update services */
1924 rb_shell_clipboard_set_source (shell->priv->clipboard_shell, source);
1925 rb_shell_player_set_selected_source (shell->priv->player_shell, source);
1926 rb_source_header_set_source (shell->priv->source_header, source);
1927 rb_statusbar_set_source (shell->priv->statusbar, source);
1928 g_object_set (G_OBJECT (shell->priv->playlist_manager), "source", source, NULL);
1929 g_object_set (G_OBJECT (shell->priv->removable_media_manager), "source", source, NULL);
1931 /* merge the source-specific UI */
1932 actions = rb_source_get_ui_actions (source);
1933 g_list_foreach (actions, (GFunc)merge_source_ui_cb, shell);
1934 rb_list_deep_free (actions);
1937 static void
1938 rb_shell_player_window_title_changed_cb (RBShellPlayer *player,
1939 const char *window_title,
1940 RBShell *shell)
1942 rb_shell_set_window_title (shell, window_title);
1945 static void
1946 rb_shell_player_elapsed_changed_cb (RBShellPlayer *player,
1947 guint elapsed,
1948 RBShell *shell)
1950 rb_shell_set_elapsed (shell, elapsed);
1953 static void
1954 rb_shell_player_stream_song_changed_cb (RBShellPlayer *player,
1955 GParamSpec *arg,
1956 RBShell *shell)
1958 char *song;
1959 g_object_get (G_OBJECT (player), "stream-song", &song, NULL);
1960 if (song) {
1961 rb_shell_hidden_notify (shell, 4000, song, NULL, NULL);
1962 g_free (song);
1966 static void
1967 rb_shell_set_elapsed (RBShell *shell,
1968 guint elapsed)
1970 gboolean playing;
1971 char *elapsed_string;
1972 char *tooltip;
1974 rb_shell_player_get_playing (shell->priv->player_shell, &playing, NULL);
1975 elapsed_string = rb_shell_player_get_playing_time_string (shell->priv->player_shell);
1977 if (shell->priv->cached_title == NULL)
1978 tooltip = g_strdup (_("Not playing"));
1979 else if (!playing) {
1980 /* Translators: the first %s is substituted by the song name, the second one is the elapsed and total time */
1981 tooltip = g_strdup_printf (_("%s\nPaused, %s"),
1982 shell->priv->cached_title, elapsed_string);
1983 } else {
1984 /* Translators: the first %s is substituted by the song name, the second one is the elapsed and total time */
1985 tooltip = g_strdup_printf (_("%s\n%s"),
1986 shell->priv->cached_title, elapsed_string);
1989 rb_tray_icon_set_tooltip (shell->priv->tray_icon, tooltip);
1990 g_free (elapsed_string);
1991 g_free (tooltip);
1994 static void
1995 rb_shell_set_window_title (RBShell *shell,
1996 const char *window_title)
1998 if (window_title == NULL) {
1999 rb_debug ("clearing title");
2001 g_free (shell->priv->cached_title);
2002 shell->priv->cached_title = NULL;
2004 gtk_window_set_title (GTK_WINDOW (shell->priv->window),
2005 _("Music Player"));
2007 else {
2008 gboolean playing;
2009 char *title;
2011 rb_shell_player_get_playing (shell->priv->player_shell, &playing, NULL);
2013 if (shell->priv->cached_title &&
2014 !strcmp (shell->priv->cached_title, window_title) &&
2015 playing == shell->priv->cached_playing) {
2016 return;
2018 g_free (shell->priv->cached_title);
2019 shell->priv->cached_title = g_strdup (window_title);
2020 shell->priv->cached_playing = playing;
2022 rb_debug ("setting title to \"%s\"", window_title);
2023 if (!playing) {
2024 /* Translators: %s is the song name */
2025 title = g_strdup_printf (_("%s (Paused)"), window_title);
2026 gtk_window_set_title (GTK_WINDOW (shell->priv->window),
2027 title);
2028 g_free (title);
2029 } else {
2030 gtk_window_set_title (GTK_WINDOW (shell->priv->window),
2031 window_title);
2036 static void
2037 rb_shell_view_sourcelist_changed_cb (GtkAction *action,
2038 RBShell *shell)
2040 eel_gconf_set_boolean (CONF_UI_SOURCELIST_HIDDEN,
2041 !gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)));
2044 static void
2045 rb_shell_view_toolbar_changed_cb (GtkAction *action,
2046 RBShell *shell)
2048 eel_gconf_set_boolean (CONF_UI_TOOLBAR_HIDDEN,
2049 !gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)));
2052 static void
2053 rb_shell_view_smalldisplay_changed_cb (GtkAction *action,
2054 RBShell *shell)
2056 GTimeVal time;
2058 /* don't change more than once per second, it causes weirdness */
2059 g_get_current_time (&time);
2060 if (time.tv_sec == shell->priv->last_small_time)
2061 return;
2063 shell->priv->last_small_time = time.tv_sec;
2065 shell->priv->window_small = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
2066 eel_gconf_set_boolean (CONF_UI_SMALL_DISPLAY, shell->priv->window_small);
2069 static void
2070 rb_shell_view_statusbar_changed_cb (GtkAction *action,
2071 RBShell *shell)
2073 shell->priv->statusbar_hidden = !gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
2074 eel_gconf_set_boolean (CONF_UI_STATUSBAR_HIDDEN, shell->priv->statusbar_hidden);
2076 rb_shell_sync_statusbar_visibility (shell);
2079 static void
2080 rb_shell_view_queue_as_sidebar_changed_cb (GtkAction *action,
2081 RBShell *shell)
2083 shell->priv->queue_as_sidebar = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
2084 eel_gconf_set_boolean (CONF_UI_QUEUE_AS_SIDEBAR, shell->priv->queue_as_sidebar);
2086 if (shell->priv->queue_as_sidebar &&
2087 shell->priv->selected_source == RB_SOURCE (shell->priv->queue_source)) {
2088 /* queue no longer exists as a source, so change to the library */
2089 rb_shell_select_source (shell, RB_SOURCE (shell->priv->library_source));
2092 rb_shell_playing_from_queue_cb (shell->priv->player_shell, NULL, shell);
2094 rb_shell_sync_pane_visibility (shell);
2097 static void
2098 rb_shell_view_party_mode_changed_cb (GtkAction *action,
2099 RBShell *shell)
2101 shell->priv->party_mode = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
2102 rb_shell_sync_party_mode (shell);
2105 static void
2106 rb_shell_cmd_about (GtkAction *action,
2107 RBShell *shell)
2109 const char **tem;
2110 GString *comment;
2111 GdkPixbuf *pixbuf = NULL;
2113 const char *authors[] = {
2115 #include "MAINTAINERS.tab"
2117 NULL,
2118 #include "MAINTAINERS.old.tab"
2120 NULL,
2121 #include "AUTHORS.tab"
2122 NULL
2125 const char *documenters[] = {
2126 #include "DOCUMENTERS.tab"
2127 NULL
2130 const char *translator_credits = _("translator_credits");
2132 const char *license[] = {
2133 N_("Rhythmbox is free software; you can redistribute it and/or modify\n"
2134 "it under the terms of the GNU General Public License as published by\n"
2135 "the Free Software Foundation; either version 2 of the License, or\n"
2136 "(at your option) any later version.\n"),
2137 N_("Rhythmbox is distributed in the hope that it will be useful,\n"
2138 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
2139 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
2140 "GNU General Public License for more details.\n"),
2141 N_("You should have received a copy of the GNU General Public License\n"
2142 "along with Rhythmbox; if not, write to the Free Software Foundation, Inc.,\n"
2143 "51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\n")
2146 char *license_trans;
2148 pixbuf = gdk_pixbuf_new_from_file (rb_file ("about-logo.png"), NULL);
2150 authors[0] = _("Maintainers:");
2151 for (tem = authors; *tem != NULL; tem++)
2153 *tem = _("Former Maintainers:");
2154 for (; *tem != NULL; tem++)
2156 *tem = _("Contributors:");
2158 comment = g_string_new (_("Music management and playback software for GNOME."));
2160 license_trans = g_strconcat (_(license[0]), "\n", _(license[1]), "\n",
2161 _(license[2]), "\n", NULL);
2163 gtk_show_about_dialog (GTK_WINDOW (shell->priv->window),
2164 "name", "Rhythmbox",
2165 "version", VERSION,
2166 "copyright", "Copyright \xc2\xa9 2005 The Rhythmbox authors\nCopyright \xc2\xa9 2003 - 2005 Colin Walters\nCopyright \xc2\xa9 2002, 2003 Jorn Baayen",
2167 "license", license_trans,
2168 "website", "http://www.gnome.org/projects/rhythmbox",
2169 "comments", comment->str,
2170 "authors", (const char **) authors,
2171 "documenters", (const char **) documenters,
2172 "translator-credits", strcmp (translator_credits, "translator_credits") != 0 ? translator_credits : NULL,
2173 "logo", pixbuf,
2174 NULL);
2175 if (pixbuf)
2176 g_object_unref (pixbuf);
2177 g_string_free (comment, TRUE);
2178 g_free (license_trans);
2181 void
2182 rb_shell_toggle_visibility (RBShell *shell)
2184 gboolean visible;
2186 visible = rb_shell_get_visibility (shell);
2188 rb_shell_set_visibility (shell, !visible, FALSE);
2191 static void
2192 rb_shell_cmd_toggle_visibility (GtkAction *action,
2193 RBShell *shell)
2195 rb_shell_toggle_visibility (shell);
2198 static void
2199 rb_shell_cmd_quit (GtkAction *action,
2200 RBShell *shell)
2202 rb_shell_quit (shell);
2205 static void
2206 rb_shell_cmd_contents (GtkAction *action,
2207 RBShell *shell)
2209 GError *error = NULL;
2211 gnome_help_display ("rhythmbox.xml", NULL, &error);
2213 if (error != NULL) {
2214 rb_error_dialog (NULL, _("Couldn't display help"),
2215 "%s", error->message);
2216 g_error_free (error);
2220 static void
2221 rb_shell_cmd_preferences (GtkAction *action,
2222 RBShell *shell)
2224 if (shell->priv->prefs == NULL) {
2225 shell->priv->prefs = rb_shell_preferences_new (shell->priv->sources);
2227 gtk_window_set_transient_for (GTK_WINDOW (shell->priv->prefs),
2228 GTK_WINDOW (shell->priv->window));
2229 gtk_widget_show_all (shell->priv->prefs);
2232 gtk_window_present (GTK_WINDOW (shell->priv->prefs));
2235 static gboolean
2236 rb_shell_plugins_window_delete_cb (GtkWidget *window,
2237 GdkEventAny *event,
2238 gpointer data)
2240 gtk_widget_hide (window);
2242 return TRUE;
2245 static void
2246 rb_shell_plugins_response_cb (GtkDialog *dialog,
2247 int response_id,
2248 gpointer data)
2250 if (response_id == GTK_RESPONSE_CLOSE)
2251 gtk_widget_hide (GTK_WIDGET (dialog));
2255 static void
2256 rb_shell_cmd_plugins (GtkAction *action,
2257 RBShell *shell)
2259 if (shell->priv->plugins == NULL) {
2260 GtkWidget *manager;
2262 shell->priv->plugins = gtk_dialog_new_with_buttons (_("Configure Plugins"),
2263 GTK_WINDOW (shell->priv->window),
2264 GTK_DIALOG_DESTROY_WITH_PARENT,
2265 GTK_STOCK_CLOSE,
2266 GTK_RESPONSE_CLOSE,
2267 NULL);
2268 g_signal_connect_object (G_OBJECT (shell->priv->plugins),
2269 "delete_event",
2270 G_CALLBACK (rb_shell_plugins_window_delete_cb),
2271 NULL, 0);
2272 g_signal_connect_object (G_OBJECT (shell->priv->plugins),
2273 "response",
2274 G_CALLBACK (rb_shell_plugins_response_cb),
2275 NULL, 0);
2277 manager = rb_plugin_manager_new ();
2278 gtk_widget_show_all (GTK_WIDGET (manager));
2279 gtk_container_add (GTK_CONTAINER (GTK_DIALOG (shell->priv->plugins)->vbox),
2280 manager);
2283 gtk_window_present (GTK_WINDOW (shell->priv->plugins));
2286 static void
2287 add_to_library_response_cb (GtkDialog *dialog,
2288 int response_id,
2289 RBShell *shell)
2292 char *current_dir = NULL;
2293 GSList *uri_list = NULL, *uris = NULL;
2295 if (response_id != GTK_RESPONSE_ACCEPT) {
2296 gtk_widget_destroy (GTK_WIDGET (dialog));
2297 return;
2300 current_dir = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
2301 eel_gconf_set_string (CONF_STATE_ADD_DIR, current_dir);
2303 uri_list = gtk_file_chooser_get_uris (GTK_FILE_CHOOSER (dialog));
2304 if (uri_list == NULL) {
2305 uri_list = g_slist_prepend (uri_list, g_strdup (current_dir));
2308 for (uris = uri_list; uris; uris = uris->next) {
2309 rb_shell_load_uri (shell, (char *)uris->data, FALSE, NULL);
2310 g_free (uris->data);
2312 g_slist_free (uri_list);
2313 g_free (current_dir);
2314 gtk_widget_destroy (GTK_WIDGET (dialog));
2316 if (shell->priv->save_db_id > 0) {
2317 g_source_remove (shell->priv->save_db_id);
2319 shell->priv->save_db_id = g_timeout_add (10000, (GSourceFunc) idle_save_rhythmdb, shell);
2322 static void
2323 rb_shell_cmd_add_folder_to_library (GtkAction *action,
2324 RBShell *shell)
2326 char * dir = eel_gconf_get_string (CONF_STATE_ADD_DIR);
2327 GtkWidget *dialog;
2329 dialog = rb_file_chooser_new (_("Import Folder into Library"),
2330 GTK_WINDOW (shell->priv->window),
2331 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
2332 FALSE);
2333 if (dir)
2334 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (dialog),
2335 dir);
2338 g_signal_connect_object (G_OBJECT (dialog),
2339 "response",
2340 G_CALLBACK (add_to_library_response_cb),
2341 shell, 0);
2344 static void
2345 rb_shell_cmd_add_file_to_library (GtkAction *action,
2346 RBShell *shell)
2348 char * dir = eel_gconf_get_string (CONF_STATE_ADD_DIR);
2349 GtkWidget *dialog;
2351 dialog = rb_file_chooser_new (_("Import File into Library"),
2352 GTK_WINDOW (shell->priv->window),
2353 GTK_FILE_CHOOSER_ACTION_OPEN,
2354 FALSE);
2355 if (dir)
2356 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (dialog),
2357 dir);
2360 g_signal_connect_object (G_OBJECT (dialog),
2361 "response",
2362 G_CALLBACK (add_to_library_response_cb),
2363 shell, 0);
2366 static void
2367 rb_shell_cmd_extract_cd (GtkAction *action,
2368 RBShell *shell)
2370 GError *error = NULL;
2372 if (g_find_program_in_path ("sound-juicer") == NULL) {
2373 rb_error_dialog (GTK_WINDOW (shell->priv->window),
2374 _("CD Ripper not found"),
2375 _("To extract CDs you must install the Sound Juicer CD Ripper package."));
2376 return;
2379 g_spawn_command_line_async ("sound-juicer", &error);
2381 if (error != NULL)
2382 rb_error_dialog (GTK_WINDOW (shell->priv->window),
2383 _("Couldn't run CD Ripper"),
2384 _("An error occurred while running sound-juicer: %s"),
2385 error->message);
2387 g_clear_error (&error);
2390 static void
2391 rb_shell_quit (RBShell *shell)
2393 rb_debug ("Quitting");
2395 rb_plugins_engine_shutdown ();
2397 rb_podcast_source_shutdown (shell->priv->podcast_source);
2399 #ifdef WITH_DAAP_SUPPORT
2400 rb_daap_sources_shutdown (shell);
2401 rb_daap_sharing_shutdown (shell);
2402 #endif /* WITH_DAAP_SUPPORT */
2403 rb_shell_shutdown (shell);
2404 rb_shell_sync_state (shell);
2405 g_object_unref (G_OBJECT (shell));
2407 g_timeout_add (10000, (GSourceFunc)gtk_main_quit, NULL);
2410 static gboolean
2411 idle_handle_load_complete (RBShell *shell)
2413 GDK_THREADS_ENTER ();
2415 rb_debug ("load complete");
2417 rb_playlist_manager_load_playlists (shell->priv->playlist_manager);
2418 shell->priv->load_complete = TRUE;
2419 shell->priv->save_playlist_id = g_timeout_add (10000, (GSourceFunc) idle_save_playlist_manager, shell->priv->playlist_manager);
2421 rhythmdb_start_action_thread (shell->priv->db);
2423 GDK_THREADS_LEAVE ();
2425 return FALSE;
2428 static void
2429 rb_shell_load_complete_cb (RhythmDB *db,
2430 RBShell *shell)
2432 g_idle_add ((GSourceFunc) idle_handle_load_complete, shell);
2435 static void
2436 rb_shell_sync_sourcelist_visibility (RBShell *shell)
2438 gboolean visible;
2439 GtkAction *action;
2441 visible = !eel_gconf_get_boolean (CONF_UI_SOURCELIST_HIDDEN);
2443 if (visible)
2444 gtk_widget_show (GTK_WIDGET (shell->priv->sourcelist));
2445 else
2446 gtk_widget_hide (GTK_WIDGET (shell->priv->sourcelist));
2448 action = gtk_action_group_get_action (shell->priv->actiongroup,
2449 "ViewSourceList");
2450 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
2451 visible);
2454 static void
2455 rb_shell_sync_pane_visibility (RBShell *shell)
2457 gboolean sourcelist_visible;
2458 GtkAction *action;
2460 g_object_get (G_OBJECT (shell->priv->sourcelist), "visible", &sourcelist_visible, NULL);
2461 if (shell->priv->queue_source)
2462 g_object_set (G_OBJECT (shell->priv->queue_source), "visibility", !shell->priv->queue_as_sidebar, NULL);
2464 if (shell->priv->queue_as_sidebar)
2465 gtk_widget_show (shell->priv->queue_sidebar);
2466 else
2467 gtk_widget_hide (shell->priv->queue_sidebar);
2469 if (sourcelist_visible == FALSE && shell->priv->queue_as_sidebar == FALSE)
2470 gtk_widget_hide (GTK_WIDGET (shell->priv->queue_paned));
2471 else
2472 gtk_widget_show (GTK_WIDGET (shell->priv->queue_paned));
2474 action = gtk_action_group_get_action (shell->priv->actiongroup,
2475 "ViewQueueAsSidebar");
2476 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
2477 shell->priv->queue_as_sidebar);
2481 static void
2482 rb_shell_sync_toolbar_state (RBShell *shell)
2484 GtkWidget *toolbar;
2485 gboolean visible;
2486 GtkAction *action;
2487 guint toolbar_style;
2489 visible = !eel_gconf_get_boolean (CONF_UI_TOOLBAR_HIDDEN);
2491 toolbar = gtk_ui_manager_get_widget (shell->priv->ui_manager, "/ToolBar");
2492 if (visible)
2493 gtk_widget_show (toolbar);
2494 else
2495 gtk_widget_hide (toolbar);
2497 action = gtk_action_group_get_action (shell->priv->actiongroup,
2498 "ViewToolbar");
2499 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
2500 visible);
2502 /* icons-only in small mode */
2503 if (shell->priv->window_small)
2504 toolbar_style = 3;
2505 else
2506 toolbar_style = eel_gconf_get_integer (CONF_UI_TOOLBAR_STYLE);
2508 switch (toolbar_style) {
2509 case 0:
2510 /* default*/
2511 gtk_toolbar_unset_style (GTK_TOOLBAR (toolbar));
2512 break;
2513 case 1:
2514 /* text below icons */
2515 gtk_toolbar_set_style (GTK_TOOLBAR (toolbar), GTK_TOOLBAR_BOTH);
2516 break;
2517 case 2:
2518 /* text beside icons */
2519 gtk_toolbar_set_style (GTK_TOOLBAR (toolbar), GTK_TOOLBAR_BOTH_HORIZ);
2520 break;
2521 case 3:
2522 /* icons only */
2523 gtk_toolbar_set_style (GTK_TOOLBAR (toolbar), GTK_TOOLBAR_ICONS);
2524 break;
2525 case 4:
2526 /* test only */
2527 gtk_toolbar_set_style (GTK_TOOLBAR (toolbar), GTK_TOOLBAR_TEXT);
2528 break;
2529 default:
2530 g_warning ("unknown toolbar style type");
2531 gtk_toolbar_unset_style (GTK_TOOLBAR (toolbar));
2535 static gboolean
2536 window_state_event_cb (GtkWidget *widget,
2537 GdkEventWindowState *event,
2538 RBShell *shell)
2540 if (event->changed_mask & GDK_WINDOW_STATE_ICONIFIED) {
2541 rb_shell_present (shell, gtk_get_current_event_time (), NULL);
2544 return TRUE;
2547 static void
2548 rb_shell_sync_party_mode (RBShell *shell)
2550 GtkAction *action;
2552 /* party mode does not use gconf as a model since it
2553 should not be persistent */
2555 /* disable/enable quit action */
2556 action = gtk_action_group_get_action (shell->priv->actiongroup, "MusicClose");
2557 g_object_set (G_OBJECT (action), "sensitive", !shell->priv->party_mode, NULL);
2558 action = gtk_action_group_get_action (shell->priv->actiongroup, "MusicQuit");
2559 g_object_set (G_OBJECT (action), "sensitive", !shell->priv->party_mode, NULL);
2560 action = gtk_action_group_get_action (shell->priv->actiongroup, "ViewSmallDisplay");
2561 g_object_set (G_OBJECT (action), "sensitive", !shell->priv->party_mode, NULL);
2563 /* show/hide queue as sidebar ? */
2565 g_object_set (shell->priv->player_shell, "queue-only", shell->priv->party_mode, NULL);
2567 /* Set playlist manager source to the current source to update properties */
2568 if (shell->priv->selected_source) {
2569 g_object_set (G_OBJECT (shell->priv->playlist_manager), "source", shell->priv->selected_source, NULL);
2570 rb_shell_clipboard_set_source (shell->priv->clipboard_shell, shell->priv->selected_source);
2573 gtk_window_set_keep_above (GTK_WINDOW (shell->priv->window), shell->priv->party_mode);
2574 if (shell->priv->party_mode) {
2575 gtk_window_fullscreen (GTK_WINDOW (shell->priv->window));
2576 gtk_window_stick (GTK_WINDOW (shell->priv->window));
2577 g_signal_connect (shell->priv->window, "window-state-event", G_CALLBACK (window_state_event_cb), shell);
2578 } else {
2579 gtk_window_unstick (GTK_WINDOW (shell->priv->window));
2580 gtk_window_unfullscreen (GTK_WINDOW (shell->priv->window));
2581 g_signal_handlers_disconnect_by_func (shell->priv->window, window_state_event_cb, shell);
2585 static void
2586 rb_shell_sync_smalldisplay (RBShell *shell)
2588 GtkAction *action;
2589 GtkAction *queue_action;
2590 GtkAction *party_mode_action;
2591 GtkWidget *toolbar;
2593 rb_shell_sync_window_state (shell, FALSE);
2595 action = gtk_action_group_get_action (shell->priv->actiongroup,
2596 "ViewSourceList");
2597 queue_action = gtk_action_group_get_action (shell->priv->actiongroup,
2598 "ViewQueueAsSidebar");
2599 party_mode_action = gtk_action_group_get_action (shell->priv->actiongroup,
2600 "ViewPartyMode");
2602 toolbar = gtk_ui_manager_get_widget (shell->priv->ui_manager, "/ToolBar");
2604 if (shell->priv->window_small) {
2605 g_object_set (G_OBJECT (action), "sensitive", FALSE, NULL);
2606 g_object_set (G_OBJECT (queue_action), "sensitive", FALSE, NULL);
2607 g_object_set (G_OBJECT (party_mode_action), "sensitive", FALSE, NULL);
2609 gtk_widget_hide (GTK_WIDGET (shell->priv->paned));
2610 } else {
2611 g_object_set (G_OBJECT (action), "sensitive", TRUE, NULL);
2612 g_object_set (G_OBJECT (queue_action), "sensitive", TRUE, NULL);
2613 g_object_set (G_OBJECT (party_mode_action), "sensitive", TRUE, NULL);
2615 gtk_widget_show (GTK_WIDGET (shell->priv->paned));
2617 rb_shell_sync_statusbar_visibility (shell);
2618 rb_shell_sync_toolbar_state (shell);
2620 rb_source_header_sync_control_state (shell->priv->source_header);
2622 action = gtk_action_group_get_action (shell->priv->actiongroup,
2623 "ViewSmallDisplay");
2624 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
2625 shell->priv->window_small);
2628 static void
2629 rb_shell_sync_statusbar_visibility (RBShell *shell)
2631 if (shell->priv->statusbar_hidden || shell->priv->window_small)
2632 gtk_widget_hide (GTK_WIDGET (shell->priv->statusbar));
2633 else
2634 gtk_widget_show (GTK_WIDGET (shell->priv->statusbar));
2637 static void
2638 sourcelist_visibility_changed_cb (GConfClient *client,
2639 guint cnxn_id,
2640 GConfEntry *entry,
2641 RBShell *shell)
2643 rb_debug ("sourcelist visibility changed");
2644 rb_shell_sync_sourcelist_visibility (shell);
2645 rb_shell_sync_pane_visibility (shell);
2648 static void
2649 toolbar_state_changed_cb (GConfClient *client,
2650 guint cnxn_id,
2651 GConfEntry *entry,
2652 RBShell *shell)
2654 rb_debug ("toolbar state changed");
2655 rb_shell_sync_toolbar_state (shell);
2658 static void
2659 smalldisplay_changed_cb (GConfClient *client,
2660 guint cnxn_id,
2661 GConfEntry *entry,
2662 RBShell *shell)
2664 rb_debug ("small display mode changed");
2665 shell->priv->window_small = eel_gconf_get_boolean (CONF_UI_SMALL_DISPLAY);
2666 rb_shell_sync_smalldisplay (shell);
2669 static void
2670 rb_shell_sync_paned (RBShell *shell)
2672 gtk_paned_set_position (GTK_PANED (shell->priv->paned),
2673 shell->priv->paned_position);
2674 gtk_paned_set_position (GTK_PANED (shell->priv->queue_paned),
2675 shell->priv->sourcelist_height);
2678 static void
2679 paned_size_allocate_cb (GtkWidget *widget,
2680 GtkAllocation *allocation,
2681 RBShell *shell)
2683 shell->priv->paned_position = gtk_paned_get_position (GTK_PANED (shell->priv->paned));
2684 rb_debug ("paned position %d", shell->priv->paned_position);
2685 eel_gconf_set_integer (CONF_STATE_PANED_POSITION, shell->priv->paned_position);
2688 static void
2689 sidebar_paned_size_allocate_cb (GtkWidget *widget,
2690 GtkAllocation *allocation,
2691 RBShell *shell)
2693 shell->priv->sourcelist_height = gtk_paned_get_position (GTK_PANED (shell->priv->queue_paned));
2694 rb_debug ("sidebar paned position %d", shell->priv->sourcelist_height);
2695 eel_gconf_set_integer (CONF_STATE_SOURCELIST_HEIGHT, shell->priv->sourcelist_height);
2698 static void
2699 sourcelist_drag_received_cb (RBSourceList *sourcelist,
2700 RBSource *source,
2701 GtkSelectionData *data,
2702 RBShell *shell)
2704 if (source == NULL) {
2705 source = rb_playlist_manager_new_playlist_from_selection_data (shell->priv->playlist_manager,
2706 data);
2709 if (source != NULL) {
2710 rb_source_receive_drag (source, data);
2715 static void
2716 rb_shell_cmd_current_song (GtkAction *action,
2717 RBShell *shell)
2719 rb_debug ("current song");
2721 rb_shell_jump_to_current (shell);
2725 static void
2726 rb_shell_cmd_view_all (GtkAction *action,
2727 RBShell *shell)
2729 rb_debug ("view all");
2731 rb_source_reset_filters (shell->priv->selected_source);
2732 rb_source_header_clear_search (shell->priv->source_header);
2733 rb_source_header_focus_search_box (shell->priv->source_header);
2736 static void
2737 rb_shell_jump_to_entry_with_source (RBShell *shell,
2738 RBSource *source,
2739 RhythmDBEntry *entry)
2741 RBEntryView *songs;
2743 g_return_if_fail (entry != NULL);
2745 if ((source == RB_SOURCE (shell->priv->queue_source) &&
2746 shell->priv->queue_as_sidebar) ||
2747 source == NULL) {
2748 RhythmDBEntryType entry_type;
2749 entry_type = rhythmdb_entry_get_entry_type (entry);
2750 source = rb_shell_get_source_by_entry_type (shell, entry_type);
2752 if (source == NULL)
2753 return;
2755 songs = rb_source_get_entry_view (source);
2756 rb_shell_select_source (shell, source);
2758 rb_entry_view_scroll_to_entry (songs, entry);
2759 rb_entry_view_select_entry (songs, entry);
2762 static void
2763 rb_shell_play_entry (RBShell *shell,
2764 RhythmDBEntry *entry)
2766 rb_shell_player_stop (shell->priv->player_shell);
2767 rb_shell_jump_to_entry_with_source (shell, NULL, entry);
2768 rb_shell_player_play_entry (shell->priv->player_shell, entry);
2771 static void
2772 rb_shell_jump_to_current (RBShell *shell)
2774 RBSource *source = rb_shell_player_get_playing_source (shell->priv->player_shell);
2775 RhythmDBEntry *playing;
2777 g_return_if_fail (source != NULL);
2779 playing = rb_shell_player_get_playing_entry (shell->priv->player_shell);
2781 rb_shell_jump_to_entry_with_source (shell, source, playing);
2784 static gboolean
2785 rb_shell_show_popup_cb (RBSourceList *sourcelist,
2786 RBSource *target,
2787 RBShell *shell)
2789 rb_debug ("popup");
2790 return rb_source_show_popup (target);
2793 static void
2794 tray_embedded_cb (GtkPlug *plug,
2795 gpointer data)
2797 /* FIXME - this doens't work */
2798 #if 0
2799 RBShell *shell = RB_SHELL (data);
2801 rb_debug ("got embedded signal");
2803 gdk_window_set_decorations (shell->priv->window->window,
2804 GDK_DECOR_ALL | GDK_DECOR_MINIMIZE | GDK_DECOR_MAXIMIZE);
2805 #endif
2808 static gboolean
2809 tray_destroy_cb (GtkObject *object,
2810 RBShell *shell)
2812 if (shell->priv->tray_icon) {
2813 rb_debug ("caught destroy event for tray icon %p", object);
2814 gtk_object_sink (object);
2815 shell->priv->tray_icon = NULL;
2816 rb_debug ("finished sinking tray");
2819 rb_debug ("creating new tray icon");
2820 shell->priv->tray_icon = rb_tray_icon_new (shell->priv->ui_manager, shell);
2821 g_signal_connect_object (G_OBJECT (shell->priv->tray_icon), "destroy",
2822 G_CALLBACK (tray_destroy_cb), shell, 0);
2823 g_signal_connect_object (G_OBJECT (shell->priv->tray_icon), "embedded",
2824 G_CALLBACK (tray_embedded_cb), shell, 0);
2826 gtk_widget_show_all (GTK_WIDGET (shell->priv->tray_icon));
2828 rb_debug ("done creating new tray icon %p", shell->priv->tray_icon);
2829 return TRUE;
2832 void
2833 rb_shell_hidden_notify (RBShell *shell,
2834 guint timeout,
2835 const char *primary,
2836 GtkWidget *icon,
2837 const char *secondary)
2840 if (rb_shell_get_visibility (shell)) {
2841 rb_debug ("shell is visible, not notifying");
2842 return;
2845 rb_tray_icon_notify (shell->priv->tray_icon,
2846 timeout, primary,
2847 icon, secondary);
2850 static void
2851 session_die_cb (GnomeClient *client,
2852 RBShell *shell)
2854 rb_debug ("session die");
2855 rb_shell_quit (shell);
2858 GQuark
2859 rb_shell_error_quark (void)
2861 static GQuark quark = 0;
2862 if (!quark)
2863 quark = g_quark_from_static_string ("rb_shell_error");
2865 return quark;
2868 static gboolean
2869 save_yourself_cb (GnomeClient *client,
2870 gint phase,
2871 GnomeSaveStyle save_style,
2872 gboolean shutdown,
2873 GnomeInteractStyle interact_style,
2874 gboolean fast,
2875 RBShell *shell)
2877 rb_debug ("session save yourself");
2878 rb_shell_sync_state (shell);
2879 return TRUE;
2882 static void
2883 rb_shell_session_init (RBShell *shell)
2885 GnomeClient *client;
2887 client = gnome_master_client ();
2889 g_signal_connect_object (G_OBJECT (client),
2890 "save_yourself",
2891 G_CALLBACK (save_yourself_cb),
2892 shell, 0);
2894 g_signal_connect_object (G_OBJECT (client),
2895 "die",
2896 G_CALLBACK (session_die_cb),
2897 shell, 0);
2900 RhythmDBEntryType
2901 rb_shell_guess_type_for_uri (RBShell *shell,
2902 const char *uri)
2904 GnomeVFSURI *vfs_uri;
2905 RhythmDBEntryType entry_type = RHYTHMDB_ENTRY_TYPE_SONG;
2906 const char *scheme;
2907 const char *path;
2908 const char *pathend;
2910 if (uri == NULL)
2911 return RHYTHMDB_ENTRY_TYPE_INVALID;
2913 vfs_uri = gnome_vfs_uri_new (uri);
2914 if (vfs_uri == NULL) {
2915 rb_debug ("Invalid uri: %s", uri);
2916 return RHYTHMDB_ENTRY_TYPE_INVALID;
2918 scheme = gnome_vfs_uri_get_scheme (vfs_uri);
2919 path = gnome_vfs_uri_get_scheme (vfs_uri);
2920 pathend = path + strlen (path);
2922 if (strcmp ("file", scheme) == 0) {
2923 entry_type = RHYTHMDB_ENTRY_TYPE_SONG;
2924 } else if (strcmp ("http", scheme) == 0) {
2925 if ((strcasecmp (".xml", pathend-4) == 0)
2926 || strcasecmp (".rss", pathend-4) == 0)
2927 entry_type = RHYTHMDB_ENTRY_TYPE_PODCAST_FEED;
2928 else
2929 entry_type = RHYTHMDB_ENTRY_TYPE_IRADIO_STATION;
2930 } else if ((strcmp ("pnm", scheme) == 0) ||
2931 (strcmp ("rtsp", scheme) == 0) ||
2932 (strcmp ("mms", scheme) == 0) ||
2933 (strcmp ("mmsh", scheme) == 0)) {
2934 entry_type = RHYTHMDB_ENTRY_TYPE_IRADIO_STATION;
2937 gnome_vfs_uri_unref (vfs_uri);
2938 return entry_type;
2941 /* Load a URI representing an element of the given type, with
2942 * optional metadata
2944 gboolean
2945 rb_shell_add_uri (RBShell *shell,
2946 RhythmDBEntryType entrytype,
2947 const char *uri,
2948 const char *title,
2949 const char *genre,
2950 GError **error)
2952 RBSource *source;
2954 /* FIXME should abstract this... */
2955 source = rb_shell_get_source_by_entry_type (shell, entrytype);
2956 if (source == RB_SOURCE (shell->priv->iradio_source)) {
2957 if (rb_uri_is_local (uri)) {
2958 rb_iradio_source_add_from_playlist (shell->priv->iradio_source,
2959 uri);
2960 } else {
2961 rb_iradio_source_add_station (shell->priv->iradio_source,
2962 uri,
2963 title,
2964 genre);
2966 return TRUE;
2967 } else if (source == RB_SOURCE (shell->priv->podcast_source)) {
2968 rb_podcast_source_add_feed (shell->priv->podcast_source, uri);
2969 return TRUE;
2970 } else if (entrytype == RHYTHMDB_ENTRY_TYPE_SONG) {
2971 /* FIXME should be sync... */
2972 rhythmdb_add_uri (shell->priv->db, uri);
2973 return TRUE;
2974 } else {
2975 g_assert_not_reached ();
2976 return FALSE;
2980 typedef struct {
2981 RBShell *shell;
2982 gboolean iradio_playlist;
2983 } PlaylistParseData;
2985 static void
2986 handle_playlist_entry_cb (TotemPlParser *playlist,
2987 const char *uri,
2988 const char *title,
2989 const char *genre,
2990 PlaylistParseData *data)
2992 RhythmDBEntryType entry_type;
2994 g_return_if_fail (uri != NULL);
2996 entry_type = rb_shell_guess_type_for_uri (data->shell, uri);
2998 if (entry_type != RHYTHMDB_ENTRY_TYPE_IRADIO_STATION)
2999 data->iradio_playlist = FALSE;
3002 /* Load a URI representing a single song, a directory, a playlist, or
3003 * an internet radio station, and optionally start playing it.
3005 * For playlists containing only stream URLs, we either add the playlist
3006 * itself (if it's remote) or each URL from it (if it's local). The main
3007 * reason for this is so clicking on stream playlist links in web browsers
3008 * works properly - the playlist file will be downloaded to /tmp/, and
3009 * we can't add that to the database, so we need to add the stream URLs
3010 * instead.
3012 gboolean
3013 rb_shell_load_uri (RBShell *shell,
3014 const char *uri,
3015 gboolean play,
3016 GError **error)
3018 RhythmDBEntry *entry;
3020 entry = rhythmdb_entry_lookup_by_location (shell->priv->db, uri);
3022 if (entry == NULL) {
3023 TotemPlParser *parser;
3024 TotemPlParserResult result;
3025 PlaylistParseData data;
3027 data.shell = shell;
3028 if (rb_uri_is_local (uri)) {
3029 /* TODO: we'd need to grab the first stream URL from the playlist
3030 * to be able to play stations added this way.
3032 play = FALSE;
3035 rb_debug ("adding uri %s", uri);
3036 parser = totem_pl_parser_new ();
3038 g_signal_connect_data (G_OBJECT (parser), "entry",
3039 G_CALLBACK (handle_playlist_entry_cb),
3040 &data, NULL, 0);
3042 totem_pl_parser_add_ignored_mimetype (parser, "x-directory/normal");
3043 if (g_object_class_find_property (G_OBJECT_GET_CLASS (parser), "recurse"))
3044 g_object_set (G_OBJECT (parser), "recurse", FALSE, NULL);
3046 result = totem_pl_parser_parse (parser, uri, FALSE);
3047 g_object_unref (parser);
3049 if (result == TOTEM_PL_PARSER_RESULT_SUCCESS && data.iradio_playlist == FALSE) {
3050 rb_debug ("adding %s as a static playlist", uri);
3051 if (!rb_playlist_manager_parse_file (shell->priv->playlist_manager,
3052 uri, error))
3053 return FALSE;
3054 } else {
3055 RhythmDBEntryType entrytype;
3057 if (result == TOTEM_PL_PARSER_RESULT_SUCCESS) {
3058 rb_debug ("adding %s as an iradio playlist", uri);
3059 entrytype = RHYTHMDB_ENTRY_TYPE_IRADIO_STATION;
3060 } else {
3061 rb_debug ("%s didn't parse as a playlist", uri);
3062 entrytype = rb_shell_guess_type_for_uri (shell, uri);
3063 if (entrytype < 0)
3064 return FALSE;
3067 rb_shell_add_uri (shell, entrytype, uri, NULL, NULL, NULL);
3071 if (play) {
3072 if (entry == NULL)
3073 entry = rhythmdb_entry_lookup_by_location (shell->priv->db, uri);
3075 if (entry) {
3076 rb_shell_play_entry (shell, entry);
3077 } else {
3078 /* wait for the entry to be added, and then play it */
3079 if (shell->priv->pending_entry)
3080 g_free (shell->priv->pending_entry);
3082 shell->priv->pending_entry = g_strdup (uri);
3086 return TRUE;
3089 gboolean
3090 rb_shell_get_party_mode (RBShell *shell)
3092 return shell->priv->party_mode;
3095 GObject *
3096 rb_shell_get_player (RBShell *shell)
3098 return G_OBJECT (shell->priv->player_shell);
3101 const char *
3102 rb_shell_get_player_path (RBShell *shell)
3104 return "/org/gnome/Rhythmbox/Player";
3107 GObject *
3108 rb_shell_get_playlist_manager (RBShell *shell)
3110 return G_OBJECT (shell->priv->playlist_manager);
3113 const char *
3114 rb_shell_get_playlist_manager_path (RBShell *shell)
3116 return "/org/gnome/Rhythmbox/PlaylistManager";
3119 GObject *
3120 rb_shell_get_ui_manager (RBShell *shell)
3122 return G_OBJECT (shell->priv->ui_manager);
3125 gboolean
3126 rb_shell_add_to_queue (RBShell *shell,
3127 const gchar *uri,
3128 GError **error)
3130 rb_static_playlist_source_add_location (RB_STATIC_PLAYLIST_SOURCE (shell->priv->queue_source),
3131 uri, -1);
3132 return TRUE;
3135 gboolean
3136 rb_shell_remove_from_queue (RBShell *shell,
3137 const gchar *uri,
3138 GError **error)
3140 if (rb_playlist_source_location_in_map (RB_PLAYLIST_SOURCE (shell->priv->queue_source), uri))
3141 rb_static_playlist_source_remove_location (RB_STATIC_PLAYLIST_SOURCE (shell->priv->queue_source),
3142 uri);
3143 return TRUE;
3147 gboolean
3148 rb_shell_present (RBShell *shell,
3149 guint32 timestamp,
3150 GError **error)
3152 rb_profile_start ("presenting shell");
3154 rb_debug ("presenting with timestamp %u", timestamp);
3155 gtk_widget_show (GTK_WIDGET (shell->priv->window));
3156 #if GTK_MINOR_VERSION >= 8
3157 gtk_window_present_with_time (GTK_WINDOW (shell->priv->window), timestamp);
3158 #else
3159 gtk_window_present (GTK_WINDOW (shell->priv->window));
3160 gdk_window_focus (GTK_WIDGET (shell->priv->window)->window, timestamp);
3161 #endif
3163 rb_profile_end ("presenting shell");
3165 return TRUE;
3168 static void
3169 unset_and_free_g_value (gpointer valpointer)
3171 GValue *value = valpointer;
3172 g_value_unset (value);
3173 g_free (value);
3176 gboolean
3177 rb_shell_get_song_properties (RBShell *shell,
3178 const char *uri,
3179 GHashTable **properties,
3180 GError **error)
3182 RhythmDBEntry *entry;
3183 GEnumClass *klass;
3184 guint i;
3186 entry = rhythmdb_entry_lookup_by_location (shell->priv->db, uri);
3188 if (entry == NULL) {
3189 g_set_error (error,
3190 RB_SHELL_ERROR,
3191 RB_SHELL_ERROR_NO_SUCH_URI,
3192 _("Unknown song URI: %s"),
3193 uri);
3194 return FALSE;
3197 *properties = g_hash_table_new_full (g_str_hash, g_str_equal,
3198 NULL,
3199 unset_and_free_g_value);
3201 klass = g_type_class_ref (RHYTHMDB_TYPE_PROP_TYPE);
3202 for (i = 0; i < klass->n_values; i++) {
3203 GValue *value;
3204 gint prop;
3205 GType value_type;
3207 prop = klass->values[i].value;
3209 /* only include easily marshallable types in the hash table */
3210 value_type = rhythmdb_get_property_type (shell->priv->db, prop);
3211 switch (value_type) {
3212 case G_TYPE_STRING:
3213 case G_TYPE_BOOLEAN:
3214 case G_TYPE_ULONG:
3215 case G_TYPE_UINT64:
3216 case G_TYPE_DOUBLE:
3217 break;
3218 default:
3219 continue;
3222 value = g_new0 (GValue, 1);
3223 g_value_init (value, value_type);
3224 rhythmdb_entry_get (shell->priv->db, entry, prop, value);
3225 g_hash_table_insert (*properties,
3226 (gpointer) rhythmdb_nice_elt_name_from_propid (shell->priv->db, prop),
3227 value);
3229 g_type_class_unref (klass);
3231 return TRUE;
3234 gboolean
3235 rb_shell_set_song_property (RBShell *shell,
3236 const char *uri,
3237 const char *propname,
3238 const GValue *value,
3239 GError **error)
3241 RhythmDBEntry *entry;
3242 GType proptype;
3243 int propid;
3245 entry = rhythmdb_entry_lookup_by_location (shell->priv->db, uri);
3247 if (entry == NULL) {
3248 g_set_error (error,
3249 RB_SHELL_ERROR,
3250 RB_SHELL_ERROR_NO_SUCH_URI,
3251 _("Unknown song URI: %s"),
3252 uri);
3253 return FALSE;
3256 if ((propid = rhythmdb_propid_from_nice_elt_name (shell->priv->db, (guchar *) propname)) < 0) {
3257 g_set_error (error,
3258 RB_SHELL_ERROR,
3259 RB_SHELL_ERROR_NO_SUCH_PROPERTY,
3260 _("Unknown property %s"),
3261 propname);
3262 return FALSE;
3265 proptype = rhythmdb_get_property_type (shell->priv->db, propid);
3266 if (G_VALUE_TYPE (value) != proptype) {
3267 g_set_error (error,
3268 RB_SHELL_ERROR,
3269 RB_SHELL_ERROR_INVALID_PROPERTY_TYPE,
3270 _("Invalid property type %s for property %s"),
3271 g_type_name (G_VALUE_TYPE (value)),
3272 uri);
3273 return FALSE;
3276 rhythmdb_entry_set (shell->priv->db, entry, propid, value);
3277 return TRUE;
3280 static void
3281 rb_shell_volume_widget_changed_cb (BaconVolumeButton *vol,
3282 RBShell *shell)
3284 if (!shell->priv->syncing_volume)
3285 g_object_set (shell->priv->player_shell,
3286 "volume", bacon_volume_button_get_value (vol),
3287 NULL);
3290 static void
3291 rb_shell_player_volume_changed_cb (RBShellPlayer *player,
3292 GParamSpec *arg,
3293 RBShell *shell)
3295 float volume;
3296 g_object_get (G_OBJECT (player), "volume", &volume, NULL);
3297 shell->priv->syncing_volume = TRUE;
3298 bacon_volume_button_set_value (BACON_VOLUME_BUTTON (shell->priv->volume_button),
3299 volume);
3300 shell->priv->syncing_volume = FALSE;
3304 static void
3305 rb_shell_druid_response_cb (GtkDialog *druid,
3306 guint response,
3307 RBShell *shell)
3309 gtk_widget_show_all (GTK_WIDGET (shell->priv->window));
3310 gtk_widget_destroy (GTK_WIDGET (druid));
3313 static GtkBox*
3314 rb_shell_get_box_for_ui_location (RBShell *shell, RBShellUILocation location)
3316 GtkBox *box = NULL;
3318 switch (location) {
3319 case RB_SHELL_UI_LOCATION_SIDEBAR:
3320 box = shell->priv->sidebar_container;
3321 break;
3322 case RB_SHELL_UI_LOCATION_MAIN_TOP:
3323 box = shell->priv->top_container;
3324 break;
3325 case RB_SHELL_UI_LOCATION_MAIN_BOTTOM:
3326 box = shell->priv->bottom_container;
3327 break;
3330 return box;
3333 void
3334 rb_shell_add_widget (RBShell *shell, GtkWidget *widget, RBShellUILocation location)
3336 GtkBox *box;
3338 box = rb_shell_get_box_for_ui_location (shell, location);
3339 g_return_if_fail (box != NULL);
3341 gtk_box_pack_start (box, widget, FALSE, TRUE, 0);
3344 void
3345 rb_shell_remove_widget (RBShell *shell, GtkWidget *widget, RBShellUILocation location)
3347 GtkBox *box;
3349 box = rb_shell_get_box_for_ui_location (shell, location);
3350 g_return_if_fail (box != NULL);
3352 gtk_container_remove (GTK_CONTAINER (box), widget);
3355 /* This should really be standard. */
3356 #define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
3358 GType
3359 rb_shell_ui_location_get_type (void)
3361 static GType etype = 0;
3363 if (etype == 0) {
3364 static const GEnumValue values[] = {
3365 ENUM_ENTRY (RB_SHELL_UI_LOCATION_SIDEBAR, "Sidebar"),
3366 ENUM_ENTRY (RB_SHELL_UI_LOCATION_MAIN_TOP, "Main Top"),
3367 ENUM_ENTRY (RB_SHELL_UI_LOCATION_MAIN_BOTTOM, "Main Bottom"),
3368 { 0, 0, 0 }
3371 etype = g_enum_register_static ("RBShellUILocation", values);
3374 return etype;