int
main (int argc, char **argv)
{
	GtkWidget *main_window;
	GtkTreeModel *main_model;
	GtkTreeIter iter;
	RBEntryView *view;
	RhythmDB *db;
	RhythmDBEntry *entry;

	gtk_init (&argc, &argv);
	gdk_threads_init ();
	rb_thread_helpers_init ();
	rb_file_helpers_init (TRUE);
	rb_stock_icons_init ();
	rb_debug_init (TRUE);

	GDK_THREADS_ENTER ();

	db = rhythmdb_tree_new ("test");

	rhythmdb_write_lock (db);

	entry = create_entry (db, "file:///sin.mp3",
			      "Sin", "Pretty Hate Machine", "Nine Inch Nails", "Rock");
	
	rhythmdb_write_unlock (db);

	rhythmdb_read_lock (db);

	main_model = GTK_TREE_MODEL (rhythmdb_query_model_new_empty (db));
	rhythmdb_do_full_query (db, main_model,
				RHYTHMDB_QUERY_PROP_EQUALS,
				RHYTHMDB_PROP_TYPE, RHYTHMDB_ENTRY_TYPE_IGNORE,
				RHYTHMDB_QUERY_END);

	wait_for_model_completion (RHYTHMDB_QUERY_MODEL (main_model));
	g_assert (gtk_tree_model_get_iter_first (main_model, &iter));

	main_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);

	view = rb_entry_view_new (db, rb_file ("rb-entry-view-library.xml"));

	rb_entry_view_set_query_model (view, RHYTHMDB_QUERY_MODEL (main_model));

	gtk_container_add (GTK_CONTAINER (main_window), GTK_WIDGET (view));

	g_signal_connect (G_OBJECT (main_window), "destroy",
			  G_CALLBACK (gtk_main_quit), NULL);

	gtk_widget_show_all (GTK_WIDGET (main_window));

	gtk_main ();
	
	rhythmdb_shutdown (db);
	g_object_unref (G_OBJECT (db));
	GDK_THREADS_LEAVE ();
	
	exit (0);
}
static void
rb_import_errors_source_constructed (GObject *object)
{
	GObject *shell_player;
	RBImportErrorsSource *source;
	RBShell *shell;
	GPtrArray *query;
	RhythmDBQueryModel *model;
	RhythmDBEntryType *entry_type;
	GtkWidget *box;
	GtkWidget *label;

	RB_CHAIN_GOBJECT_METHOD (rb_import_errors_source_parent_class, constructed, object);

	source = RB_IMPORT_ERRORS_SOURCE (object);

	g_object_get (source,
		      "shell", &shell,
		      "entry-type", &entry_type,
		      NULL);
	g_object_get (shell, "db", &source->priv->db, NULL);
	shell_player = rb_shell_get_player (shell);
	g_object_unref (shell);

	/* construct real query */
	query = rhythmdb_query_parse (source->priv->db,
				      RHYTHMDB_QUERY_PROP_EQUALS,
				      	RHYTHMDB_PROP_TYPE,
					entry_type,
				      RHYTHMDB_QUERY_END);

	model = rhythmdb_query_model_new (source->priv->db, query,
					  (GCompareDataFunc) rhythmdb_query_model_string_sort_func,
					  GUINT_TO_POINTER (RHYTHMDB_PROP_LOCATION), NULL, FALSE);
	rhythmdb_query_free (query);

	/* set up entry view */
	source->priv->view = rb_entry_view_new (source->priv->db, shell_player,
						NULL, FALSE, FALSE);

	rb_entry_view_set_model (source->priv->view, model);

	rb_entry_view_append_column (source->priv->view, RB_ENTRY_VIEW_COL_LOCATION, TRUE);
	rb_entry_view_append_column (source->priv->view, RB_ENTRY_VIEW_COL_ERROR, TRUE);

	g_signal_connect_object (source->priv->view, "show_popup",
				 G_CALLBACK (rb_import_errors_source_songs_show_popup_cb), source, 0);

	g_object_set (source, "query-model", model, NULL);
	g_object_unref (model);

	/* set up query model for tracking missing plugin information */
	query = rhythmdb_query_parse (source->priv->db,
				      RHYTHMDB_QUERY_PROP_EQUALS,
				        RHYTHMDB_PROP_TYPE,
					entry_type,
				      RHYTHMDB_QUERY_PROP_NOT_EQUAL,
				        RHYTHMDB_PROP_COMMENT,
					"",
				      RHYTHMDB_QUERY_END);

	source->priv->missing_plugin_model = rhythmdb_query_model_new_empty (source->priv->db);
	rhythmdb_do_full_query_async_parsed (source->priv->db,
					     RHYTHMDB_QUERY_RESULTS (source->priv->missing_plugin_model),
					     query);
	rhythmdb_query_free (query);

	/* set up info bar for triggering codec installation */
	source->priv->infobar = gtk_info_bar_new_with_buttons (_("Install Plugins"), GTK_RESPONSE_OK, NULL);
	g_signal_connect_object (source->priv->infobar,
				 "response",
				 G_CALLBACK (infobar_response_cb),
				 source, 0);

	label = gtk_label_new (_("Additional GStreamer plugins are required to play some of these files."));
	gtk_container_add (GTK_CONTAINER (gtk_info_bar_get_content_area (GTK_INFO_BAR (source->priv->infobar))),
			   label);

	g_object_unref (entry_type);

	box = gtk_vbox_new (FALSE, 6);
	gtk_box_pack_start (GTK_BOX (box), GTK_WIDGET (source->priv->view), TRUE, TRUE, 0);
	gtk_box_pack_start (GTK_BOX (box), source->priv->infobar, FALSE, FALSE, 0);

	gtk_container_add (GTK_CONTAINER (source), box);
	gtk_widget_show_all (GTK_WIDGET (source));
	gtk_widget_hide (source->priv->infobar);

	/* show the info bar when there are missing plugin entries */
	g_signal_connect_object (source->priv->missing_plugin_model,
				 "row-inserted",
				 G_CALLBACK (missing_plugin_row_inserted_cb),
				 source, 0);
	g_signal_connect_object (source->priv->missing_plugin_model,
				 "row-deleted",
				 G_CALLBACK (missing_plugin_row_deleted_cb),
				 source, 0);
}
Beispiel #3
0
static void
impl_constructed (GObject *object)
{
	RBPodcastAddDialog *dialog;
	GtkBuilder *builder;
	GtkWidget *widget;
	GtkWidget *paned;
	GtkTreeViewColumn *column;
	GtkCellRenderer *renderer;
	RBEntryView *episodes;
	RBShellPlayer *shell_player;
	RhythmDBQuery *query;
	RhythmDBQueryModel *query_model;
	const char *episode_strings[3];

	RB_CHAIN_GOBJECT_METHOD (rb_podcast_add_dialog_parent_class, constructed, object);
	dialog = RB_PODCAST_ADD_DIALOG (object);

	g_object_get (dialog->priv->podcast_mgr, "db", &dialog->priv->db, NULL);

	builder = rb_builder_load ("podcast-add-dialog.ui", NULL);

	dialog->priv->info_bar_message = gtk_label_new ("");
	dialog->priv->info_bar = gtk_info_bar_new ();
	g_object_set (dialog->priv->info_bar, "spacing", 0, NULL);
	gtk_container_add (GTK_CONTAINER (gtk_info_bar_get_content_area (GTK_INFO_BAR (dialog->priv->info_bar))),
			   dialog->priv->info_bar_message);
	gtk_widget_set_no_show_all (dialog->priv->info_bar, TRUE);
	gtk_box_pack_start (GTK_BOX (dialog), dialog->priv->info_bar, FALSE, FALSE, 0);
	gtk_widget_show (dialog->priv->info_bar_message);

	dialog->priv->subscribe_button = GTK_WIDGET (gtk_builder_get_object (builder, "subscribe-button"));
	g_signal_connect_object (dialog->priv->subscribe_button, "clicked", G_CALLBACK (subscribe_clicked_cb), dialog, 0);
	gtk_widget_set_sensitive (dialog->priv->subscribe_button, FALSE);

	dialog->priv->feed_view = GTK_WIDGET (gtk_builder_get_object (builder, "feed-view"));
	g_signal_connect (dialog->priv->feed_view, "row-activated", G_CALLBACK (feed_activated_cb), dialog);
	g_signal_connect (gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->priv->feed_view)),
			  "changed",
			  G_CALLBACK (feed_selection_changed_cb),
			  dialog);


	dialog->priv->search_entry = rb_search_entry_new (FALSE);
	gtk_widget_set_size_request (GTK_WIDGET (dialog->priv->search_entry), 400, -1);
	g_object_set (dialog->priv->search_entry,"explicit-mode", TRUE, NULL);
	g_signal_connect (dialog->priv->search_entry, "search", G_CALLBACK (search_cb), dialog);
	g_signal_connect (dialog->priv->search_entry, "activate", G_CALLBACK (search_cb), dialog);
	gtk_container_add (GTK_CONTAINER (gtk_builder_get_object (builder, "search-entry-box")),
			   GTK_WIDGET (dialog->priv->search_entry));

	g_signal_connect (gtk_builder_get_object (builder, "close-button"),
			  "clicked",
			  G_CALLBACK (close_clicked_cb),
			  dialog);

	dialog->priv->feed_model = gtk_list_store_new (7,
						       G_TYPE_STRING,	/* name */
						       G_TYPE_STRING,	/* author */
						       GDK_TYPE_PIXBUF, /* image */
						       G_TYPE_FILE,	/* image file */
						       G_TYPE_INT,	/* episode count */
						       G_TYPE_POINTER,	/* RBPodcastChannel */
						       G_TYPE_ULONG);	/* date */
	gtk_tree_view_set_model (GTK_TREE_VIEW (dialog->priv->feed_view), GTK_TREE_MODEL (dialog->priv->feed_model));

	column = gtk_tree_view_column_new_with_attributes (_("Title"), gtk_cell_renderer_pixbuf_new (), "pixbuf", FEED_COLUMN_IMAGE, NULL);
	renderer = gtk_cell_renderer_text_new ();
	g_object_set (renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
	gtk_tree_view_column_pack_start (column, renderer, TRUE);
	gtk_tree_view_column_set_attributes (column, renderer, "text", FEED_COLUMN_TITLE, NULL);

	gtk_tree_view_column_set_expand (column, TRUE);
	gtk_tree_view_append_column (GTK_TREE_VIEW (dialog->priv->feed_view), column);

	renderer = gtk_cell_renderer_text_new ();
	g_object_set (renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
	column = gtk_tree_view_column_new_with_attributes (_("Author"), renderer, "text", FEED_COLUMN_AUTHOR, NULL);
	gtk_tree_view_column_set_expand (column, TRUE);
	gtk_tree_view_append_column (GTK_TREE_VIEW (dialog->priv->feed_view), column);

	renderer = gtk_cell_renderer_text_new ();
	column = gtk_tree_view_column_new_with_attributes (_("Episodes"), renderer, NULL);
	gtk_tree_view_column_set_cell_data_func (column, renderer, episode_count_column_cell_data_func, NULL, NULL);
	episode_strings[0] = "0000";
	episode_strings[1] = _("Episodes");
	episode_strings[2] = NULL;
	rb_set_tree_view_column_fixed_width (dialog->priv->feed_view, column, renderer, episode_strings, 6);
	gtk_tree_view_append_column (GTK_TREE_VIEW (dialog->priv->feed_view), column);

	widget = GTK_WIDGET (gtk_builder_get_object (builder, "podcast-add-dialog"));
	gtk_box_pack_start (GTK_BOX (dialog), widget, TRUE, TRUE, 0);

	/* set up episode view */
	g_object_get (dialog->priv->shell, "shell-player", &shell_player, NULL);
	episodes = rb_entry_view_new (dialog->priv->db, G_OBJECT (shell_player), TRUE, FALSE);
	g_object_unref (shell_player);

	g_signal_connect (episodes, "entry-activated", G_CALLBACK (episode_entry_activated_cb), dialog);

	/* date column */
	column = gtk_tree_view_column_new ();
	renderer = gtk_cell_renderer_text_new();

	gtk_tree_view_column_pack_start (column, renderer, TRUE);

	gtk_tree_view_column_set_clickable (column, TRUE);
	gtk_tree_view_column_set_resizable (column, TRUE);
	gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
	{
		const char *sample_strings[3];
		sample_strings[0] = _("Date");
		sample_strings[1] = rb_entry_view_get_time_date_column_sample ();
		sample_strings[2] = NULL;
		rb_entry_view_set_fixed_column_width (episodes, column, renderer, sample_strings);
	}

	gtk_tree_view_column_set_cell_data_func (column, renderer,
						 (GtkTreeCellDataFunc) podcast_post_date_cell_data_func,
						 dialog, NULL);

	rb_entry_view_append_column_custom (episodes, column,
					    _("Date"), "Date",
					    (GCompareDataFunc) podcast_post_date_sort_func,
					    0, NULL);
	rb_entry_view_append_column (episodes, RB_ENTRY_VIEW_COL_TITLE, TRUE);
	rb_entry_view_append_column (episodes, RB_ENTRY_VIEW_COL_DURATION, TRUE);
	rb_entry_view_set_sorting_order (RB_ENTRY_VIEW (episodes), "Date", GTK_SORT_DESCENDING);
	g_signal_connect (episodes,
			  "notify::sort-order",
			  G_CALLBACK (episodes_sort_changed_cb),
			  dialog);

	query = rhythmdb_query_parse (dialog->priv->db,
				      RHYTHMDB_QUERY_PROP_EQUALS,
				      RHYTHMDB_PROP_TYPE,
				      RHYTHMDB_ENTRY_TYPE_PODCAST_SEARCH,
				      RHYTHMDB_QUERY_END);
	query_model = rhythmdb_query_model_new_empty (dialog->priv->db);
	rb_entry_view_set_model (episodes, query_model);

	rhythmdb_do_full_query_async_parsed (dialog->priv->db, RHYTHMDB_QUERY_RESULTS (query_model), query);
	rhythmdb_query_free (query);

	g_object_unref (query_model);

	paned = GTK_WIDGET (gtk_builder_get_object (builder, "paned"));
	g_signal_connect (paned, "size-allocate", G_CALLBACK (paned_size_allocate_cb), dialog);
	gtk_paned_pack2 (GTK_PANED (paned),
			 GTK_WIDGET (episodes),
			 TRUE,
			 FALSE);

	gtk_widget_show_all (GTK_WIDGET (dialog));
	g_object_unref (builder);
}
static void
rb_missing_files_source_constructed (GObject *object)
{
	GObject *shell_player;
	RBMissingFilesSource *source;
	RBShell *shell;
	GPtrArray *query;
	RhythmDBQueryModel *model;
	RhythmDBEntryType *entry_type;

	RB_CHAIN_GOBJECT_METHOD (rb_missing_files_source_parent_class, constructed, object);
	source = RB_MISSING_FILES_SOURCE (object);

	g_object_get (source,
		      "shell", &shell,
		      "entry-type", &entry_type,
		      NULL);
	g_object_get (shell, "db", &source->priv->db, NULL);
	shell_player = rb_shell_get_player (shell);
	g_object_unref (shell);

	/* construct real query */
	query = rhythmdb_query_parse (source->priv->db,
				      RHYTHMDB_QUERY_PROP_EQUALS,
				      	RHYTHMDB_PROP_TYPE,
					entry_type,
				      RHYTHMDB_QUERY_PROP_EQUALS,
				      	RHYTHMDB_PROP_HIDDEN,
					TRUE,
				      RHYTHMDB_QUERY_END);
	g_object_unref (entry_type);

	model = rhythmdb_query_model_new (source->priv->db, query,
					  NULL, NULL, NULL, FALSE);

	rhythmdb_query_free (query);

	g_object_set (model, "show-hidden", TRUE, NULL);

	/* set up entry view */
	source->priv->view = rb_entry_view_new (source->priv->db, shell_player,
						NULL, FALSE, FALSE);

	rb_entry_view_set_model (source->priv->view, model);

	rb_entry_view_append_column (source->priv->view, RB_ENTRY_VIEW_COL_TRACK_NUMBER, FALSE);
	rb_entry_view_append_column (source->priv->view, RB_ENTRY_VIEW_COL_TITLE, TRUE);
/*	rb_entry_view_append_column (source->priv->view, RB_ENTRY_VIEW_COL_GENRE, FALSE); */
	rb_entry_view_append_column (source->priv->view, RB_ENTRY_VIEW_COL_ARTIST, FALSE);
	rb_entry_view_append_column (source->priv->view, RB_ENTRY_VIEW_COL_ALBUM, FALSE);
	rb_entry_view_append_column (source->priv->view, RB_ENTRY_VIEW_COL_LOCATION, TRUE);
	rb_entry_view_append_column (source->priv->view, RB_ENTRY_VIEW_COL_LAST_SEEN, TRUE);

	rb_entry_view_set_columns_clickable (source->priv->view, TRUE);

	gtk_container_add (GTK_CONTAINER (source), GTK_WIDGET (source->priv->view));
	g_signal_connect_object (source->priv->view, "show_popup",
				 G_CALLBACK (rb_missing_files_source_songs_show_popup_cb), source, 0);
	g_signal_connect_object (source->priv->view, "sort-order-changed",
				 G_CALLBACK (rb_missing_files_source_songs_sort_order_changed_cb), source, 0);

	gtk_widget_show_all (GTK_WIDGET (source));

	g_object_set (source, "query-model", model, NULL);
	g_object_unref (model);
}
Beispiel #5
0
static void
impl_constructed (GObject *object)
{
	RBImportDialog *dialog;
	RhythmDBQuery *query;
	GtkBuilder *builder;
	GSettings *settings;
	char **locations;

	RB_CHAIN_GOBJECT_METHOD (rb_import_dialog_parent_class, constructed, object);
	dialog = RB_IMPORT_DIALOG (object);

	g_object_get (dialog->priv->shell,
		      "db", &dialog->priv->db,
		      "shell-player", &dialog->priv->shell_player,
		      NULL);

	/* create entry types */
	dialog->priv->entry_type = g_object_new (rb_import_dialog_entry_type_get_type (),
						 "db", dialog->priv->db,
						 "name", "import-dialog",
						 NULL);
	dialog->priv->ignore_type = g_object_new (rb_import_dialog_ignore_type_get_type (),
						  "db", dialog->priv->db,
						  "name", "import-dialog-ignore",
						  NULL);
	rhythmdb_register_entry_type (dialog->priv->db, dialog->priv->entry_type);
	rhythmdb_register_entry_type (dialog->priv->db, dialog->priv->ignore_type);


	builder = rb_builder_load ("import-dialog.ui", NULL);

	dialog->priv->import_button = GTK_WIDGET (gtk_builder_get_object (builder, "import-button"));
	g_signal_connect_object (dialog->priv->import_button, "clicked", G_CALLBACK (import_clicked_cb), dialog, 0);
	gtk_widget_set_sensitive (dialog->priv->import_button, FALSE);

	dialog->priv->copy_check = GTK_WIDGET (gtk_builder_get_object (builder, "copy-check"));

	g_signal_connect (gtk_builder_get_object (builder, "close-button"),
			  "clicked",
			  G_CALLBACK (close_clicked_cb),
			  dialog);

	dialog->priv->file_chooser = GTK_WIDGET (gtk_builder_get_object (builder, "file-chooser-button"));
	
	/* select the first library location, since the default may be
	 * the user's home dir or / or something that will take forever to scan.
	 */
	settings = g_settings_new ("org.gnome.rhythmbox.rhythmdb");
	locations = g_settings_get_strv (settings, "locations");
	if (locations[0] != NULL) {
		dialog->priv->current_uri = g_strdup (locations[0]);
	} else {
		dialog->priv->current_uri = g_filename_to_uri (rb_music_dir (), NULL, NULL);
	}
	gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (dialog->priv->file_chooser),
						 dialog->priv->current_uri);
	g_strfreev (locations);
	g_object_unref (settings);

	g_signal_connect_object (dialog->priv->file_chooser, "selection-changed", G_CALLBACK (current_folder_changed_cb), dialog, 0);

	/* not sure why we have to set this, it should be the default */
	gtk_widget_set_vexpand (gtk_widget_get_parent (dialog->priv->file_chooser), FALSE);

	dialog->priv->info_bar_container = GTK_WIDGET (gtk_builder_get_object (builder, "info-bar-container"));

	/* set up entry view */
	dialog->priv->entry_view = rb_entry_view_new (dialog->priv->db, G_OBJECT (dialog->priv->shell_player), TRUE, FALSE);

	g_signal_connect (dialog->priv->entry_view, "entry-activated", G_CALLBACK (entry_activated_cb), dialog);
	g_signal_connect (dialog->priv->entry_view, "selection-changed", G_CALLBACK (selection_changed_cb), dialog);

	rb_entry_view_append_column (dialog->priv->entry_view, RB_ENTRY_VIEW_COL_TRACK_NUMBER, FALSE);
	rb_entry_view_append_column (dialog->priv->entry_view, RB_ENTRY_VIEW_COL_TITLE, TRUE);
	rb_entry_view_append_column (dialog->priv->entry_view, RB_ENTRY_VIEW_COL_GENRE, FALSE);
	rb_entry_view_append_column (dialog->priv->entry_view, RB_ENTRY_VIEW_COL_ARTIST, FALSE);
	rb_entry_view_append_column (dialog->priv->entry_view, RB_ENTRY_VIEW_COL_ALBUM, FALSE);
	rb_entry_view_append_column (dialog->priv->entry_view, RB_ENTRY_VIEW_COL_YEAR, FALSE);
	rb_entry_view_append_column (dialog->priv->entry_view, RB_ENTRY_VIEW_COL_DURATION, FALSE);
 	rb_entry_view_append_column (dialog->priv->entry_view, RB_ENTRY_VIEW_COL_QUALITY, FALSE);
	rb_entry_view_append_column (dialog->priv->entry_view, RB_ENTRY_VIEW_COL_PLAY_COUNT, FALSE);
	rb_entry_view_append_column (dialog->priv->entry_view, RB_ENTRY_VIEW_COL_BPM, FALSE);
	rb_entry_view_append_column (dialog->priv->entry_view, RB_ENTRY_VIEW_COL_COMMENT, FALSE);
	rb_entry_view_append_column (dialog->priv->entry_view, RB_ENTRY_VIEW_COL_LOCATION, FALSE);

	settings = g_settings_new ("org.gnome.rhythmbox.sources");
	g_settings_bind (settings, "visible-columns", dialog->priv->entry_view, "visible-columns", G_SETTINGS_BIND_DEFAULT);
	g_object_unref (settings);

	g_signal_connect (dialog->priv->entry_view,
			  "notify::sort-order",
			  G_CALLBACK (sort_changed_cb),
			  dialog);
	rb_entry_view_set_sorting_order (dialog->priv->entry_view, "Album", GTK_SORT_ASCENDING);

	gtk_container_add (GTK_CONTAINER (gtk_builder_get_object (builder, "entry-view-container")),
			   GTK_WIDGET (dialog->priv->entry_view));

	dialog->priv->query_model = rhythmdb_query_model_new_empty (dialog->priv->db);
	rb_entry_view_set_model (dialog->priv->entry_view, dialog->priv->query_model);
	query = rhythmdb_query_parse (dialog->priv->db,
				      RHYTHMDB_QUERY_PROP_EQUALS, RHYTHMDB_PROP_TYPE, dialog->priv->entry_type,
				      RHYTHMDB_QUERY_END);
	rhythmdb_do_full_query_async_parsed (dialog->priv->db, RHYTHMDB_QUERY_RESULTS (dialog->priv->query_model), query);
	rhythmdb_query_free (query);

	g_signal_connect (dialog->priv->query_model, "post-entry-delete", G_CALLBACK (entry_deleted_cb), dialog);
	g_signal_connect (dialog->priv->query_model, "row-inserted", G_CALLBACK (entry_inserted_cb), dialog);

	gtk_container_add (GTK_CONTAINER (dialog), GTK_WIDGET (gtk_builder_get_object (builder, "import-dialog")));

	gtk_widget_show_all (GTK_WIDGET (dialog));
	g_object_unref (builder);
}
static void
rb_grilo_source_constructed (GObject *object)
{
	RBGriloSource *source;
	RBShell *shell;
	RBShellPlayer *shell_player;
	const GList *source_keys;
	GtkTreeViewColumn *column;
	GtkCellRenderer *renderer;
	GtkTreeSelection *selection;
	GtkWidget *scrolled;
	GtkWidget *browserbox;
	GtkWidget *vbox;
	GtkWidget *mainbox;
	GtkAdjustment *adjustment;

	RB_CHAIN_GOBJECT_METHOD (rb_grilo_source_parent_class, constructed, object);
	source = RB_GRILO_SOURCE (object);

	g_object_get (source, "shell", &shell, NULL);
	g_object_get (shell,
		      "db", &source->priv->db,
		      "shell-player", &shell_player,
		      NULL);
	g_object_unref (shell);

	g_object_get (source, "entry-type", &source->priv->entry_type, NULL);

	source->priv->entry_view = rb_entry_view_new (source->priv->db, G_OBJECT (shell_player), TRUE, FALSE);
	g_object_unref (shell_player);
	g_signal_connect (source->priv->entry_view,
			  "notify::sort-order",
			  G_CALLBACK (notify_sort_order_cb),
			  source);

	source_keys = grl_source_supported_keys (source->priv->grilo_source);

	if (g_list_find ((GList *)source_keys, GUINT_TO_POINTER(GRL_METADATA_KEY_TRACK_NUMBER))) {
		rb_entry_view_append_column (source->priv->entry_view, RB_ENTRY_VIEW_COL_TRACK_NUMBER, FALSE);
		source->priv->grilo_keys = g_list_prepend (source->priv->grilo_keys,
							   GUINT_TO_POINTER(GRL_METADATA_KEY_TRACK_NUMBER));
	}

	if (g_list_find ((GList *)source_keys, GUINT_TO_POINTER(GRL_METADATA_KEY_TITLE))) {
		rb_entry_view_append_column (source->priv->entry_view, RB_ENTRY_VIEW_COL_TITLE, TRUE);
		source->priv->grilo_keys = g_list_prepend (source->priv->grilo_keys,
							   GUINT_TO_POINTER(GRL_METADATA_KEY_TITLE));
	}

	if (g_list_find ((GList *)source_keys, GUINT_TO_POINTER(GRL_METADATA_KEY_GENRE))) {
		rb_entry_view_append_column (source->priv->entry_view, RB_ENTRY_VIEW_COL_GENRE, FALSE);
		source->priv->grilo_keys = g_list_prepend (source->priv->grilo_keys,
							   GUINT_TO_POINTER(GRL_METADATA_KEY_GENRE));
	}
	if (g_list_find ((GList *)source_keys, GUINT_TO_POINTER(GRL_METADATA_KEY_ARTIST))) {
		rb_entry_view_append_column (source->priv->entry_view, RB_ENTRY_VIEW_COL_ARTIST, FALSE);
		source->priv->grilo_keys = g_list_prepend (source->priv->grilo_keys,
							   GUINT_TO_POINTER(GRL_METADATA_KEY_ARTIST));
	}
	if (g_list_find ((GList *)source_keys, GUINT_TO_POINTER(GRL_METADATA_KEY_ALBUM))) {
		rb_entry_view_append_column (source->priv->entry_view, RB_ENTRY_VIEW_COL_ALBUM, FALSE);
		source->priv->grilo_keys = g_list_prepend (source->priv->grilo_keys,
							   GUINT_TO_POINTER(GRL_METADATA_KEY_ALBUM));
	}
	/*
	if (g_list_find ((GList *)source_keys, GUINT_TO_POINTER(GRL_METADATA_KEY_DATE))) {
		rb_entry_view_append_column (source->priv->entry_view, RB_ENTRY_VIEW_COL_YEAR, FALSE);
		source->priv->grilo_keys = g_list_prepend (source->priv->grilo_keys,
							   GUINT_TO_POINTER(GRL_METADATA_KEY_DATE));
	}
	*/
	if (g_list_find ((GList *)source_keys, GUINT_TO_POINTER(GRL_METADATA_KEY_DURATION))) {
		rb_entry_view_append_column (source->priv->entry_view, RB_ENTRY_VIEW_COL_DURATION, FALSE);
		source->priv->grilo_keys = g_list_prepend (source->priv->grilo_keys,
							   GUINT_TO_POINTER(GRL_METADATA_KEY_DURATION));
	}

	source->priv->grilo_keys = g_list_prepend (source->priv->grilo_keys, GUINT_TO_POINTER(GRL_METADATA_KEY_CHILDCOUNT));
	source->priv->grilo_keys = g_list_prepend (source->priv->grilo_keys, GUINT_TO_POINTER(GRL_METADATA_KEY_URL));
	source->priv->grilo_keys = g_list_prepend (source->priv->grilo_keys, GUINT_TO_POINTER(GRL_METADATA_KEY_THUMBNAIL));

	/* probably add an image column too? */
	source->priv->browser_model = gtk_tree_store_new (4, GRL_TYPE_MEDIA, G_TYPE_STRING, G_TYPE_INT, G_TYPE_INT);
	source->priv->browser_view = gtk_tree_view_new ();
	gtk_tree_view_set_model (GTK_TREE_VIEW (source->priv->browser_view), GTK_TREE_MODEL (source->priv->browser_model));

	column = gtk_tree_view_column_new ();
	renderer = gtk_cell_renderer_text_new ();
	gtk_tree_view_column_set_title (column, _("Browse"));
	gtk_tree_view_column_pack_start (column, renderer, FALSE);
	gtk_tree_view_column_add_attribute (column, renderer, "text", 1);
	gtk_tree_view_column_set_expand (column, TRUE);
	gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);

	gtk_tree_view_append_column (GTK_TREE_VIEW (source->priv->browser_view), column);
	gtk_tree_view_set_show_expanders (GTK_TREE_VIEW (source->priv->browser_view), TRUE);
	gtk_tree_view_set_expander_column (GTK_TREE_VIEW (source->priv->browser_view), column);
	gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (source->priv->browser_view), TRUE);
	gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (source->priv->browser_view), TRUE);

	g_signal_connect (source->priv->browser_view, "row-expanded", G_CALLBACK (browser_row_expanded_cb), source);
	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (source->priv->browser_view));
	gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);	/* should be multiple eventually */
	g_signal_connect (selection, "changed", G_CALLBACK (browser_selection_changed_cb), source);

	scrolled = gtk_scrolled_window_new (NULL, NULL);
	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled), GTK_SHADOW_IN);
	adjustment = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (scrolled));
	g_signal_connect (adjustment, "changed", G_CALLBACK (scroll_adjust_changed_cb), source);
	g_signal_connect (adjustment, "value-changed", G_CALLBACK (scroll_adjust_value_changed_cb), source);

	browserbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);

	/* search bar (if the source supports searching) */
	if (grl_source_supported_operations (source->priv->grilo_source) & GRL_OP_SEARCH) {
		source->priv->search_entry = rb_search_entry_new (FALSE);
		g_object_set (source->priv->search_entry, "explicit-mode", TRUE, NULL);
		g_signal_connect (source->priv->search_entry, "search", G_CALLBACK (search_cb), source);
		g_signal_connect (source->priv->search_entry, "activate", G_CALLBACK (search_cb), source);
		gtk_box_pack_start (GTK_BOX (browserbox), GTK_WIDGET (source->priv->search_entry), FALSE, FALSE, 6);
	}
	gtk_container_add (GTK_CONTAINER (scrolled), source->priv->browser_view);
	gtk_box_pack_start (GTK_BOX (browserbox), scrolled, TRUE, TRUE, 0);

	mainbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
	gtk_box_pack_start (GTK_BOX (source), mainbox, TRUE, TRUE, 0);

	/* info bar */
	source->priv->info_bar_label = gtk_label_new ("");
	source->priv->info_bar = gtk_info_bar_new ();
	gtk_info_bar_set_message_type (GTK_INFO_BAR (source->priv->info_bar), GTK_MESSAGE_INFO);
	gtk_info_bar_add_button (GTK_INFO_BAR (source->priv->info_bar), _("Fetch more tracks"), GTK_RESPONSE_OK);
	gtk_container_add (GTK_CONTAINER (gtk_info_bar_get_content_area (GTK_INFO_BAR (source->priv->info_bar))),
			   source->priv->info_bar_label);
	gtk_widget_show (GTK_WIDGET (source->priv->info_bar_label));
	gtk_widget_set_no_show_all (GTK_WIDGET (source->priv->info_bar), TRUE);
	g_signal_connect (source->priv->info_bar, "response", G_CALLBACK (fetch_more_cb), source);

	/* don't allow the browser to be hidden? */
	source->priv->paned = gtk_paned_new (GTK_ORIENTATION_HORIZONTAL);
	rb_source_bind_settings (RB_SOURCE (source), GTK_WIDGET (source->priv->entry_view), source->priv->paned, NULL);
	gtk_paned_pack1 (GTK_PANED (source->priv->paned), browserbox, FALSE, FALSE);

	vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
	gtk_box_pack_start (GTK_BOX (vbox), GTK_WIDGET (source->priv->entry_view), TRUE, TRUE, 0);
	gtk_box_pack_start (GTK_BOX (vbox), source->priv->info_bar, FALSE, FALSE, 0);
	gtk_paned_pack2 (GTK_PANED (source->priv->paned), vbox, TRUE, FALSE);

	gtk_box_pack_start (GTK_BOX (mainbox), source->priv->paned, TRUE, TRUE, 0);

	gtk_widget_show_all (GTK_WIDGET (source));
}
static void
rb_playlist_source_constructed (GObject *object)
{
	GObject *shell_player;
	RBPlaylistSource *source;
	RBShell *shell;
	RhythmDB *db;
	RhythmDBQueryModel *query_model;
	GtkBuilder *builder;
	GSettings *settings;

	RB_CHAIN_GOBJECT_METHOD (rb_playlist_source_parent_class, constructed, object);
	source = RB_PLAYLIST_SOURCE (object);

	g_object_get (source, "shell", &shell, NULL);
	g_object_get (shell,
		      "db", &db,
		      "shell-player", &shell_player,
		      NULL);
	rb_playlist_source_set_db (source, db);
	g_object_unref (db);

	g_object_unref (shell);

	/* store playlist settings using the memory backend
	 * this means the settings path doesn't have to be consistent,
	 * it just has to be unique, so the address of the source object works.
	 * for local playlists, we write the settings into the playlist file on disk
	 * to make them persistent.
	 */
	g_object_get (source, "settings", &settings, NULL);
	if (settings == NULL) {
		char *path;
		path = g_strdup_printf ("/org/gnome/rhythmbox/playlist/%p/", source);
		settings = g_settings_new_with_backend_and_path ("org.gnome.rhythmbox.source",
								 playlist_settings_backend,
								 path);
		g_free (path);

		g_object_set (source, "settings", settings, NULL);
	}

	g_signal_connect (settings, "changed", G_CALLBACK (playlist_settings_changed_cb), source);
	g_object_unref (settings);

	builder = rb_builder_load ("playlist-popup.ui", NULL);
	source->priv->popup = G_MENU (gtk_builder_get_object (builder, "playlist-popup"));
	rb_application_link_shared_menus (RB_APPLICATION (g_application_get_default ()), source->priv->popup);
	g_object_ref (source->priv->popup);
	g_object_unref (builder);

	source->priv->entries = g_hash_table_new_full (rb_refstring_hash, rb_refstring_equal,
						       (GDestroyNotify)rb_refstring_unref, NULL);

	source->priv->songs = rb_entry_view_new (source->priv->db,
						 shell_player,
						 TRUE, TRUE);
	g_object_unref (shell_player);

	g_signal_connect_object (source->priv->songs,
				 "notify::sort-order",
				 G_CALLBACK (rb_playlist_source_songs_sort_order_changed_cb),
				 source, 0);

	query_model = rhythmdb_query_model_new_empty (source->priv->db);
	rb_playlist_source_set_query_model (source, query_model);
	g_object_unref (query_model);

	{
		const char *title = "";
		const char *strings[3] = {0};

		GtkTreeViewColumn *column = gtk_tree_view_column_new ();
		GtkCellRenderer *renderer = gtk_cell_renderer_text_new ();

		g_object_set(renderer,
			     "style", PANGO_STYLE_OBLIQUE,
			     "weight", PANGO_WEIGHT_LIGHT,
			     "xalign", 1.0,
			     NULL);

		gtk_tree_view_column_pack_start (column, renderer, TRUE);

		gtk_tree_view_column_set_resizable (column, TRUE);
		gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);

		strings[0] = title;
		strings[1] = "9999";
		rb_entry_view_set_fixed_column_width (source->priv->songs, column, renderer,
						      strings);
		gtk_tree_view_column_set_cell_data_func (column, renderer,
							 (GtkTreeCellDataFunc)
							 rb_playlist_source_track_cell_data_func,
							 source, NULL);
		rb_entry_view_insert_column_custom (source->priv->songs, column, title,
						    "PlaylistTrack", NULL, 0, NULL, 0);
	}

	rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_TRACK_NUMBER, FALSE);
	rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_TITLE, TRUE);
	rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_GENRE, FALSE);
	rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_ARTIST, FALSE);
	rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_COMPOSER, FALSE);
	rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_ALBUM, FALSE);
	rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_YEAR, FALSE);
	rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_DURATION, FALSE);
 	rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_QUALITY, FALSE);
	rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_RATING, FALSE);
	rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_PLAY_COUNT, FALSE);
	rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_COMMENT, FALSE);
	rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_LOCATION, FALSE);
	rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_LAST_PLAYED, FALSE);
	rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_FIRST_SEEN, FALSE);
	rb_entry_view_append_column (source->priv->songs, RB_ENTRY_VIEW_COL_BPM, FALSE);
	rb_entry_view_set_columns_clickable (source->priv->songs, FALSE);

	rb_playlist_source_setup_entry_view (source, source->priv->songs);

	gtk_container_add (GTK_CONTAINER (source), GTK_WIDGET (source->priv->songs));

	gtk_widget_show_all (GTK_WIDGET (source));
}
static void
rb_iradio_source_constructed (GObject *object)
{
	RBIRadioSource *source;
	RBShell *shell;
	GtkAction *action;
	GSettings *settings;
	GtkUIManager *ui_manager;
	GtkWidget *grid;
	GtkWidget *paned;
	RBSourceToolbar *toolbar;
	gint size;
	GdkPixbuf *pixbuf;

	RB_CHAIN_GOBJECT_METHOD (rb_iradio_source_parent_class, constructed, object);
	source = RB_IRADIO_SOURCE (object);

	paned = gtk_paned_new (GTK_ORIENTATION_HORIZONTAL);

	g_object_get (source, "shell", &shell, NULL);
	g_object_get (shell,
		      "db", &source->priv->db,
		      "shell-player", &source->priv->player,
		      "ui-manager", &ui_manager,
		      NULL);
	g_object_unref (shell);

	gtk_icon_size_lookup (RB_SOURCE_ICON_SIZE, &size, NULL);
	pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (),
					   IRADIO_SOURCE_ICON,
					   size,
					   0, NULL);
	g_object_set (source, "pixbuf", pixbuf, NULL);
	if (pixbuf != NULL) {
		g_object_unref (pixbuf);
	}

	settings = g_settings_new ("org.gnome.rhythmbox.plugins.iradio");
	if (g_settings_get_boolean (settings, "initial-stations-loaded") == FALSE) {
		GObject *plugin;
		char *file;

		g_object_get (source, "plugin", &plugin, NULL);
		file = rb_find_plugin_data_file (plugin, "iradio-initial.xspf");
		if (file != NULL) {
			char *uri = g_filename_to_uri (file, NULL, NULL);
			if (uri != NULL) {
				rb_iradio_source_add_from_playlist (source, uri);
				g_free (uri);

				g_settings_set_boolean (settings, "initial-stations-loaded", TRUE);
			}
		}
		g_free (file);
		g_object_unref (plugin);
	}

	source->priv->action_group = _rb_display_page_register_action_group (RB_DISPLAY_PAGE (source),
									     "IRadioActions",
									     rb_iradio_source_actions,
									     G_N_ELEMENTS (rb_iradio_source_actions),
									     source);

	action = gtk_action_group_get_action (source->priv->action_group,
                                              "MusicNewInternetRadioStation");
        /* Translators: this is the toolbar button label for 
           New Internet Radio Station action. */
        g_object_set (action, "short-label", C_("Radio", "Add"), NULL);


	/* set up stations view */
	source->priv->stations = rb_entry_view_new (source->priv->db, G_OBJECT (source->priv->player),
						    FALSE, FALSE);

	rb_entry_view_append_column (source->priv->stations, RB_ENTRY_VIEW_COL_TITLE, TRUE);
	rb_entry_view_append_column (source->priv->stations, RB_ENTRY_VIEW_COL_GENRE, FALSE);
/* 	rb_entry_view_append_column (source->priv->stations, RB_ENTRY_VIEW_COL_QUALITY, FALSE); */
	rb_entry_view_append_column (source->priv->stations, RB_ENTRY_VIEW_COL_RATING, FALSE);
/*	rb_entry_view_append_column (source->priv->stations, RB_ENTRY_VIEW_COL_PLAY_COUNT, FALSE);*/
	rb_entry_view_append_column (source->priv->stations, RB_ENTRY_VIEW_COL_LAST_PLAYED, FALSE);
	g_signal_connect_object (source->priv->stations,
				 "notify::sort-order",
				 G_CALLBACK (rb_iradio_source_songs_view_sort_order_changed_cb),
				 source, 0);

	/* set up drag and drop for the song tree view.
	 * we don't use RBEntryView's DnD support because it does too much.
	 * we just want to be able to drop stations in to add them.
	 */
	g_signal_connect_object (source->priv->stations,
				 "drag_data_received",
				 G_CALLBACK (stations_view_drag_data_received_cb),
				 source, 0);
	gtk_drag_dest_set (GTK_WIDGET (source->priv->stations),
			   GTK_DEST_DEFAULT_ALL,
			   stations_view_drag_types, 2,
			   GDK_ACTION_COPY | GDK_ACTION_MOVE);

	g_signal_connect_object (source->priv->stations, "show_popup",
				 G_CALLBACK (rb_iradio_source_songs_show_popup_cb), source, 0);

	/* set up genre entry view */
	source->priv->genres = rb_property_view_new (source->priv->db,
						     RHYTHMDB_PROP_GENRE,
						     _("Genre"));
	gtk_widget_show_all (GTK_WIDGET (source->priv->genres));
	gtk_widget_set_no_show_all (GTK_WIDGET (source->priv->genres), TRUE);
	g_signal_connect_object (source->priv->genres,
				 "property-selected",
				 G_CALLBACK (genre_selected_cb),
				 source, 0);
	g_signal_connect_object (source->priv->genres,
				 "property-selection-reset",
				 G_CALLBACK (genre_selection_reset_cb),
				 source, 0);

	g_object_set (source->priv->genres, "vscrollbar_policy",
		      GTK_POLICY_AUTOMATIC, NULL);

	gtk_paned_pack1 (GTK_PANED (paned), GTK_WIDGET (source->priv->genres), FALSE, FALSE);
	gtk_paned_pack2 (GTK_PANED (paned), GTK_WIDGET (source->priv->stations), TRUE, FALSE);

	/* set up toolbar */
	toolbar = rb_source_toolbar_new (RB_SOURCE (source), ui_manager);
	rb_source_toolbar_add_search_entry (toolbar, NULL, _("Search your internet radio stations"));

	grid = gtk_grid_new ();
	gtk_grid_set_column_spacing (GTK_GRID (grid), 6);
	gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
	gtk_widget_set_margin_top (GTK_WIDGET (grid), 6);
	gtk_grid_attach (GTK_GRID (grid), GTK_WIDGET (toolbar), 0, 0, 1, 1);
	gtk_grid_attach (GTK_GRID (grid), paned, 0, 1, 1, 1);

	gtk_container_add (GTK_CONTAINER (source), grid);

	rb_source_bind_settings (RB_SOURCE (source),
				 GTK_WIDGET (source->priv->stations),
				 paned,
				 GTK_WIDGET (source->priv->genres));

	gtk_widget_show_all (GTK_WIDGET (source));

	g_signal_connect_object (source->priv->player, "playing-source-changed",
				 G_CALLBACK (playing_source_changed_cb),
				 source, 0);

	source->priv->default_search = rb_iradio_source_search_new ();

	rb_iradio_source_do_query (source);
}