hythmbox-2.98/widgets/rb-import-dialog.c

No issues found

  1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
  2  *
  3  *  Copyright (C) 2012 Jonathan Matthew <jonathan@d14n.org>
  4  *
  5  *  This program is free software; you can redistribute it and/or modify
  6  *  it under the terms of the GNU General Public License as published by
  7  *  the Free Software Foundation; either version 2 of the License, or
  8  *  (at your option) any later version.
  9  *
 10  *  The Rhythmbox authors hereby grant permission for non-GPL compatible
 11  *  GStreamer plugins to be used and distributed together with GStreamer
 12  *  and Rhythmbox. This permission is above and beyond the permissions granted
 13  *  by the GPL license by which Rhythmbox is covered. If you modify this code
 14  *  you may extend this exception to your version of the code, but you are not
 15  *  obligated to do so. If you do not wish to do so, delete this exception
 16  *  statement from your version.
 17  *
 18  *  This program is distributed in the hope that it will be useful,
 19  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 20  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 21  *  GNU General Public License for more details.
 22  *
 23  *  You should have received a copy of the GNU General Public License
 24  *  along with this program; if not, write to the Free Software
 25  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA.
 26  *
 27  */
 28 
 29 #include "config.h"
 30 
 31 #include <glib/gi18n.h>
 32 #include <gtk/gtk.h>
 33 
 34 #include "rb-import-dialog.h"
 35 #include "rb-shell-player.h"
 36 #include "rb-builder-helpers.h"
 37 #include "rb-debug.h"
 38 #include "rb-util.h"
 39 #include "rb-cut-and-paste-code.h"
 40 #include "rb-search-entry.h"
 41 #include "rhythmdb-entry-type.h"
 42 #include "rb-device-source.h"
 43 #include "rb-file-helpers.h"
 44 
 45 /* normal entries */
 46 typedef struct _RhythmDBEntryType RBImportDialogEntryType;
 47 typedef struct _RhythmDBEntryTypeClass RBImportDialogEntryTypeClass;
 48 
 49 static void rb_import_dialog_entry_type_class_init (RBImportDialogEntryTypeClass *klass);
 50 static void rb_import_dialog_entry_type_init (RBImportDialogEntryType *etype);
 51 GType rb_import_dialog_entry_type_get_type (void);
 52 
 53 G_DEFINE_TYPE (RBImportDialogEntryType, rb_import_dialog_entry_type, RHYTHMDB_TYPE_ENTRY_TYPE);
 54 
 55 /* ignored files */
 56 typedef struct _RhythmDBEntryType RBImportDialogIgnoreType;
 57 typedef struct _RhythmDBEntryTypeClass RBImportDialogIgnoreTypeClass;
 58 
 59 static void rb_import_dialog_ignore_type_class_init (RBImportDialogIgnoreTypeClass *klass);
 60 static void rb_import_dialog_ignore_type_init (RBImportDialogIgnoreType *etype);
 61 GType rb_import_dialog_ignore_type_get_type (void);
 62 
 63 G_DEFINE_TYPE (RBImportDialogIgnoreType, rb_import_dialog_ignore_type, RHYTHMDB_TYPE_ENTRY_TYPE);
 64 
 65 
 66 static void rb_import_dialog_class_init (RBImportDialogClass *klass);
 67 static void rb_import_dialog_init (RBImportDialog *dialog);
 68 
 69 enum {
 70 	PROP_0,
 71 	PROP_SHELL,
 72 	PROP_STATUS
 73 };
 74 
 75 enum {
 76 	CLOSE,
 77 	CLOSED,
 78 	LAST_SIGNAL
 79 };
 80 
 81 struct RBImportDialogPrivate
 82 {
 83 	RhythmDB *db;
 84 	RBShell *shell;
 85 	RBShellPlayer *shell_player;
 86 
 87 	RhythmDBQueryModel *query_model;
 88 	RBEntryView *entry_view;
 89 
 90 	GtkWidget *info_bar;
 91 	GtkWidget *info_bar_container;
 92 	GtkWidget *import_progress;
 93 	GtkWidget *file_chooser;
 94 	GtkWidget *add_button;
 95 	GtkWidget *copy_button;
 96 	GtkWidget *remove_button;
 97 
 98 	RhythmDBEntryType *entry_type;
 99 	RhythmDBEntryType *ignore_type;
100 	RhythmDBImportJob *import_job;
101 	int entry_count;
102 	gboolean can_copy;
103 	gboolean can_add;
104 
105 	GList *add_entry_list;
106 	guint add_entries_id;
107 	guint added_entries_id;
108 
109 	char *current_uri;
110 	guint pulse_id;
111 };
112 
113 static guint signals[LAST_SIGNAL] = {0,};
114 
115 G_DEFINE_TYPE (RBImportDialog, rb_import_dialog, GTK_TYPE_GRID);
116 
117 static void
118 rb_import_dialog_entry_type_class_init (RBImportDialogEntryTypeClass *klass)
119 {
120 }
121 
122 static void
123 rb_import_dialog_entry_type_init (RBImportDialogEntryType *etype)
124 {
125 }
126 
127 static void
128 rb_import_dialog_ignore_type_class_init (RBImportDialogIgnoreTypeClass *klass)
129 {
130 }
131 
132 static void
133 rb_import_dialog_ignore_type_init (RBImportDialogIgnoreType *etype)
134 {
135 }
136 
137 
138 static void
139 sort_changed_cb (GObject *object, GParamSpec *pspec, RBImportDialog *dialog)
140 {
141 	rb_entry_view_resort_model (RB_ENTRY_VIEW (object));
142 }
143 
144 static void
145 impl_close (RBImportDialog *dialog)
146 {
147 	g_signal_emit (dialog, signals[CLOSED], 0);
148 }
149 
150 static void
151 entry_activated_cb (RBEntryView *entry_view, RhythmDBEntry *entry, RBImportDialog *dialog)
152 {
153 	rb_debug ("import dialog entry %s activated", rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION));
154 	rb_shell_load_uri (dialog->priv->shell,
155 			   rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION),
156 			   TRUE,
157 			   NULL);
158 }
159 
160 static void
161 clear_info_bar (RBImportDialog *dialog)
162 {
163 	if (dialog->priv->info_bar != NULL) {
164 		gtk_container_remove (GTK_CONTAINER (dialog->priv->info_bar_container), dialog->priv->info_bar);
165 		dialog->priv->info_bar = NULL;
166 	}
167 }
168 
169 static gboolean
170 collect_entries (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, GList **list)
171 {
172 	RhythmDBEntry *entry;
173 
174 	entry = rhythmdb_query_model_iter_to_entry (RHYTHMDB_QUERY_MODEL (model), iter);
175 	*list = g_list_prepend (*list, rhythmdb_entry_ref (entry));
176 	return FALSE;
177 }
178 
179 static GList *
180 get_entries (RBImportDialog *dialog)
181 {
182 	/* selection if non-empty, all entries otherwise */
183 	if (rb_entry_view_have_selection (dialog->priv->entry_view)) {
184 		return rb_entry_view_get_selected_entries (dialog->priv->entry_view);
185 	} else {
186 		GList *l = NULL;
187 
188 		gtk_tree_model_foreach (GTK_TREE_MODEL (dialog->priv->query_model),
189 					(GtkTreeModelForeachFunc) collect_entries,
190 					&l);
191 		return g_list_reverse (l);
192 	}
193 }
194 
195 static gboolean
196 add_entries_done (RBImportDialog *dialog)
197 {
198 	/* if we added all the tracks, close the dialog */
199 	if (dialog->priv->entry_count == 0) {
200 		g_signal_emit (dialog, signals[CLOSED], 0);
201 	}
202 
203 	dialog->priv->added_entries_id = 0;
204 	return FALSE;
205 }
206 
207 static gboolean
208 add_entries (RBImportDialog *dialog)
209 {
210 	int i;
211 	GValue new_type = {0,};
212 
213 	g_value_init (&new_type, G_TYPE_OBJECT);
214 	g_value_set_object (&new_type, RHYTHMDB_ENTRY_TYPE_SONG);
215 
216 	for (i = 0; i < 1000; i++) {
217 		RhythmDBEntry *entry;
218 
219 		entry = dialog->priv->add_entry_list->data;
220 		dialog->priv->add_entry_list = g_list_delete_link (dialog->priv->add_entry_list, dialog->priv->add_entry_list);
221 
222 		rhythmdb_entry_set (dialog->priv->db, entry, RHYTHMDB_PROP_TYPE, &new_type);
223 		rhythmdb_entry_unref (entry);
224 
225 		if (dialog->priv->add_entry_list == NULL)
226 			break;
227 	}
228 	
229 	rhythmdb_commit (dialog->priv->db);
230 
231 	if (dialog->priv->add_entry_list == NULL) {
232 		dialog->priv->add_entries_id = 0;
233 
234 		dialog->priv->added_entries_id = g_idle_add ((GSourceFunc) add_entries_done, dialog);
235 
236 		return FALSE;
237 	} else {
238 		return TRUE;
239 	}
240 }
241 
242 static void
243 add_clicked_cb (GtkButton *button, RBImportDialog *dialog)
244 {
245 	GList *entries;
246 
247 	entries = get_entries (dialog);
248 	dialog->priv->add_entry_list = g_list_concat (dialog->priv->add_entry_list, entries);
249 
250 	if (dialog->priv->add_entries_id == 0) {
251 		dialog->priv->add_entries_id = g_idle_add ((GSourceFunc) add_entries, dialog);
252 	}
253 }
254 
255 static void
256 copy_track_done_cb (RBTrackTransferBatch *batch,
257 		    RhythmDBEntry *entry,
258 		    const char *dest,
259 		    guint64 dest_size,
260 		    const char *mediatype,
261 		    GError *error,
262 		    RBImportDialog *dialog)
263 {
264 	int total;
265 	int done;
266 
267 	g_object_get (batch, "total-entries", &total, "done-entries", &done, NULL);
268 	gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (dialog->priv->import_progress),
269 				       (float)done / total);
270 
271 	rhythmdb_entry_delete (dialog->priv->db, entry);
272 	rhythmdb_commit (dialog->priv->db);
273 }
274 
275 static void
276 copy_complete_cb (RBTrackTransferBatch *batch, RBImportDialog *dialog)
277 {
278 	clear_info_bar (dialog);
279 
280 	/* if we copied everything, close the dialog */
281 	if (dialog->priv->entry_count == 0) {
282 		g_signal_emit (dialog, signals[CLOSED], 0);
283 	}
284 }
285 
286 static void
287 copy_clicked_cb (GtkButton *button, RBImportDialog *dialog)
288 {
289 	RBSource *library_source;
290 	RBTrackTransferBatch *batch;
291 	GList *entries;
292 	GtkWidget *content;
293 	GtkWidget *label;
294        
295 	g_object_get (dialog->priv->shell, "library-source", &library_source, NULL);
296 
297 	entries = get_entries (dialog);
298 	batch = rb_source_paste (library_source, entries);
299 	g_list_free_full (entries, (GDestroyNotify) rhythmdb_entry_unref);
300 	g_object_unref (library_source);
301 
302 	/* delete source entries as they finish being copied */
303 	g_signal_connect (batch, "track-done", G_CALLBACK (copy_track_done_cb), dialog);
304 	g_signal_connect (batch, "complete", G_CALLBACK (copy_complete_cb), dialog);
305 
306 	/* set up info bar for copy progress */
307 	clear_info_bar (dialog);
308 
309 	dialog->priv->info_bar = gtk_info_bar_new ();
310 	g_object_set (dialog->priv->info_bar, "hexpand", TRUE, NULL);
311 	gtk_info_bar_set_message_type (GTK_INFO_BAR (dialog->priv->info_bar), GTK_MESSAGE_INFO);
312 	content = gtk_info_bar_get_content_area (GTK_INFO_BAR (dialog->priv->info_bar));
313 
314 	label = gtk_label_new (_("Copying..."));
315 	gtk_container_add (GTK_CONTAINER (content), label);
316 
317 	dialog->priv->import_progress = gtk_progress_bar_new ();
318 	content = gtk_info_bar_get_action_area (GTK_INFO_BAR (dialog->priv->info_bar));
319 	gtk_container_add (GTK_CONTAINER (content), dialog->priv->import_progress);
320 
321 	gtk_container_add (GTK_CONTAINER (dialog->priv->info_bar_container), dialog->priv->info_bar);
322 	gtk_widget_show_all (dialog->priv->info_bar);
323 }
324 
325 static void
326 remove_clicked_cb (GtkButton *button, RBImportDialog *dialog)
327 {
328 	GList *entries;
329 	GList *l;
330 
331 	entries = rb_entry_view_get_selected_entries (dialog->priv->entry_view);
332 	for (l = entries; l != NULL; l = l->next) {
333 		rhythmdb_entry_delete (dialog->priv->db, l->data);
334 	}
335 	rhythmdb_commit (dialog->priv->db);
336 	g_list_free_full (entries, (GDestroyNotify) rhythmdb_entry_unref);
337 }
338 
339 static void
340 close_clicked_cb (GtkButton *button, RBImportDialog *dialog)
341 {
342 	g_signal_emit (dialog, signals[CLOSED], 0);
343 }
344 
345 static void
346 import_status_changed_cb (RhythmDBImportJob *job, int total, int imported, RBImportDialog *dialog)
347 {
348 	if (dialog->priv->pulse_id == 0) {
349 		gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (dialog->priv->import_progress),
350 					       (float)imported / total);
351 	}
352 }
353 
354 static void
355 import_scan_complete_cb (RhythmDBImportJob *job, int total, RBImportDialog *dialog)
356 {
357 	if (dialog->priv->pulse_id != 0) {
358 		g_source_remove (dialog->priv->pulse_id);
359 		dialog->priv->pulse_id = 0;
360 	}
361 
362 	gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (dialog->priv->import_progress), 0.0);
363 }
364 
365 static void
366 import_complete_cb (RhythmDBImportJob *job, int total, RBImportDialog *dialog)
367 {
368 	g_object_unref (job);
369 	dialog->priv->import_job = NULL;
370 
371 	clear_info_bar (dialog);
372 }
373 
374 static gboolean
375 pulse_cb (RBImportDialog *dialog)
376 {
377 	gtk_progress_bar_pulse (GTK_PROGRESS_BAR (dialog->priv->import_progress));
378 	return TRUE;
379 }
380 
381 static void
382 start_scanning (RBImportDialog *dialog)
383 {
384 	rb_debug ("starting %s\n", dialog->priv->current_uri);
385 	dialog->priv->import_job = rhythmdb_import_job_new (dialog->priv->db,
386 							    dialog->priv->entry_type,
387 							    dialog->priv->ignore_type,
388 							    dialog->priv->ignore_type);
389 	g_signal_connect (dialog->priv->import_job, "status-changed", G_CALLBACK (import_status_changed_cb), dialog);
390 	g_signal_connect (dialog->priv->import_job, "scan-complete", G_CALLBACK (import_scan_complete_cb), dialog);
391 	g_signal_connect (dialog->priv->import_job, "complete", G_CALLBACK (import_complete_cb), dialog);
392 	rhythmdb_import_job_add_uri (dialog->priv->import_job, dialog->priv->current_uri);
393 	rhythmdb_import_job_start (dialog->priv->import_job);
394 
395 	dialog->priv->pulse_id = g_timeout_add (100, (GSourceFunc) pulse_cb, dialog);
396 }
397 
398 static void
399 start_deferred_scan (RhythmDBImportJob *job, int total, RBImportDialog *dialog)
400 {
401 	rb_debug ("previous import job finished");
402 	start_scanning (dialog);
403 }
404 
405 static void
406 info_bar_response_cb (GtkInfoBar *bar, gint response, RBImportDialog *dialog)
407 {
408 	RBSource *source;
409 	const char *uri;
410 
411 	g_signal_emit (dialog, signals[CLOSED], 0);
412 	uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog->priv->file_chooser));
413 	source = rb_shell_guess_source_for_uri (dialog->priv->shell, uri);
414 	rb_shell_activate_source (dialog->priv->shell, source, FALSE, NULL);
415 }
416 
417 
418 static void
419 current_folder_changed_cb (GtkFileChooser *chooser, RBImportDialog *dialog)
420 {
421 	GSettings *settings;
422 	RBSource *source;
423 	GtkWidget *label;
424 	GtkWidget *content;
425 	const char *uri;
426 	char **locations;
427 	int i;
428 	
429 	uri = gtk_file_chooser_get_uri (chooser);
430 	if (g_strcmp0 (uri, dialog->priv->current_uri) == 0)
431 		return;
432 	g_free (dialog->priv->current_uri);
433 	dialog->priv->current_uri = g_strdup (uri);
434 
435 	if (dialog->priv->import_job != NULL) {
436 		g_signal_handlers_disconnect_by_func (dialog->priv->import_job, G_CALLBACK (import_status_changed_cb), dialog);
437 		g_signal_handlers_disconnect_by_func (dialog->priv->import_job, G_CALLBACK (import_scan_complete_cb), dialog);
438 		g_signal_handlers_disconnect_by_func (dialog->priv->import_job, G_CALLBACK (import_complete_cb), dialog);
439 		rhythmdb_import_job_cancel (dialog->priv->import_job);
440 	}
441 	if (dialog->priv->pulse_id != 0) {
442 		g_source_remove (dialog->priv->pulse_id);
443 		dialog->priv->pulse_id = 0;
444 	}
445 
446 	rhythmdb_entry_delete_by_type (dialog->priv->db, dialog->priv->entry_type);
447 	rhythmdb_entry_delete_by_type (dialog->priv->db, dialog->priv->ignore_type);
448 	rhythmdb_commit (dialog->priv->db);
449 
450 	clear_info_bar (dialog);
451 
452 	/* disable copy if the selected location is already inside the library */
453 	settings = g_settings_new ("org.gnome.rhythmbox.rhythmdb");
454 	locations = g_settings_get_strv (settings, "locations");
455 	dialog->priv->can_copy = TRUE;
456 	for (i = 0; locations[i] != NULL; i++) {
457 		if (g_str_has_prefix (uri, locations[i])) {
458 			dialog->priv->can_copy = FALSE;
459 			break;
460 		}
461 	}
462 	g_strfreev (locations);
463 	g_object_unref (settings);
464 
465 	dialog->priv->can_add = TRUE;
466 
467 	source = rb_shell_guess_source_for_uri (dialog->priv->shell, uri);
468 	if (source != NULL) {
469 		if (RB_IS_DEVICE_SOURCE (source)) {
470 			char *msg;
471 			char *name;
472 
473 			dialog->priv->info_bar = gtk_info_bar_new ();
474 			g_object_set (dialog->priv->info_bar, "hexpand", TRUE, NULL);
475 
476 			g_object_get (source, "name", &name, NULL);
477 
478 			/* this isn't a terribly helpful message. */
479 			msg = g_strdup_printf (_("The location you have selected is on the device %s."), name);
480 			label = gtk_label_new (msg);
481 			g_free (msg);
482 			content = gtk_info_bar_get_content_area (GTK_INFO_BAR (dialog->priv->info_bar));
483 			gtk_container_add (GTK_CONTAINER (content), label);
484 
485 			msg = g_strdup_printf (_("Show %s"), name);
486 			gtk_info_bar_add_button (GTK_INFO_BAR (dialog->priv->info_bar), msg, GTK_RESPONSE_ACCEPT);
487 			g_free (msg);
488 
489 			g_signal_connect (dialog->priv->info_bar, "response", G_CALLBACK (info_bar_response_cb), dialog);
490 
491 			gtk_widget_show_all (dialog->priv->info_bar);
492 			gtk_container_add (GTK_CONTAINER (dialog->priv->info_bar_container), dialog->priv->info_bar);
493 			return;
494 		}
495 	}
496 
497 	dialog->priv->info_bar = gtk_info_bar_new ();
498 	g_object_set (dialog->priv->info_bar, "hexpand", TRUE, NULL);
499 	gtk_info_bar_set_message_type (GTK_INFO_BAR (dialog->priv->info_bar), GTK_MESSAGE_INFO);
500 	content = gtk_info_bar_get_content_area (GTK_INFO_BAR (dialog->priv->info_bar));
501 
502 	label = gtk_label_new (_("Scanning..."));
503 	gtk_container_add (GTK_CONTAINER (content), label);
504 
505 	dialog->priv->import_progress = gtk_progress_bar_new ();
506 	content = gtk_info_bar_get_action_area (GTK_INFO_BAR (dialog->priv->info_bar));
507 	gtk_container_add (GTK_CONTAINER (content), dialog->priv->import_progress);
508 
509 	gtk_container_add (GTK_CONTAINER (dialog->priv->info_bar_container), dialog->priv->info_bar);
510 	gtk_widget_show_all (dialog->priv->info_bar);
511 
512 	if (dialog->priv->import_job != NULL) {
513 		/* wait for the previous job to finish up */
514 		rb_debug ("need to wait for previous import job to finish");
515 		g_signal_connect (dialog->priv->import_job, "complete", G_CALLBACK (start_deferred_scan), dialog);
516 	} else {
517 		start_scanning (dialog);
518 	}
519 }
520 
521 static void
522 entry_inserted_cb (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, RBImportDialog *dialog)
523 {
524 	if (dialog->priv->entry_count == 0) {
525 		gtk_widget_set_sensitive (dialog->priv->add_button, dialog->priv->can_add);
526 		gtk_widget_set_sensitive (dialog->priv->copy_button, dialog->priv->can_copy);
527 	}
528 
529 	dialog->priv->entry_count++;
530 	g_object_notify (G_OBJECT (dialog), "status");
531 }
532 
533 static void
534 entry_deleted_cb (GtkTreeModel *model, RhythmDBEntry *entry, RBImportDialog *dialog)
535 {
536 	dialog->priv->entry_count--;
537 	if (dialog->priv->entry_count == 0) {
538 		gtk_widget_set_sensitive (dialog->priv->add_button, FALSE);
539 		gtk_widget_set_sensitive (dialog->priv->copy_button, FALSE);
540 	}
541 
542 	g_object_notify (G_OBJECT (dialog), "status");
543 }
544 
545 static void
546 have_selection_changed_cb (RBEntryView *view, gboolean have_selection, RBImportDialog *dialog)
547 {
548 	gtk_widget_set_sensitive (dialog->priv->remove_button, have_selection);
549 }
550 
551 static void
552 impl_constructed (GObject *object)
553 {
554 	RBImportDialog *dialog;
555 	RhythmDBQuery *query;
556 	GtkBuilder *builder;
557 	GSettings *settings;
558 	char **locations;
559 
560 	RB_CHAIN_GOBJECT_METHOD (rb_import_dialog_parent_class, constructed, object);
561 	dialog = RB_IMPORT_DIALOG (object);
562 
563 	g_object_get (dialog->priv->shell,
564 		      "db", &dialog->priv->db,
565 		      "shell-player", &dialog->priv->shell_player,
566 		      NULL);
567 
568 	/* create entry types */
569 	dialog->priv->entry_type = g_object_new (rb_import_dialog_entry_type_get_type (),
570 						 "db", dialog->priv->db,
571 						 "name", "import-dialog",
572 						 NULL);
573 	dialog->priv->ignore_type = g_object_new (rb_import_dialog_ignore_type_get_type (),
574 						  "db", dialog->priv->db,
575 						  "name", "import-dialog-ignore",
576 						  NULL);
577 	rhythmdb_register_entry_type (dialog->priv->db, dialog->priv->entry_type);
578 	rhythmdb_register_entry_type (dialog->priv->db, dialog->priv->ignore_type);
579 
580 
581 	builder = rb_builder_load ("import-dialog.ui", NULL);
582 
583 	dialog->priv->add_button = GTK_WIDGET (gtk_builder_get_object (builder, "add-button"));
584 	g_signal_connect_object (dialog->priv->add_button, "clicked", G_CALLBACK (add_clicked_cb), dialog, 0);
585 	gtk_widget_set_sensitive (dialog->priv->add_button, FALSE);
586 	
587 	dialog->priv->copy_button = GTK_WIDGET (gtk_builder_get_object (builder, "copy-button"));
588 	g_signal_connect_object (dialog->priv->copy_button, "clicked", G_CALLBACK (copy_clicked_cb), dialog, 0);
589 	gtk_widget_set_sensitive (dialog->priv->copy_button, FALSE);
590 
591 	dialog->priv->remove_button = GTK_WIDGET (gtk_builder_get_object (builder, "remove-button"));
592 	g_signal_connect_object (dialog->priv->remove_button, "clicked", G_CALLBACK (remove_clicked_cb), dialog, 0);
593 	gtk_widget_set_sensitive (dialog->priv->remove_button, FALSE);
594 
595 	g_signal_connect (gtk_builder_get_object (builder, "close-button"),
596 			  "clicked",
597 			  G_CALLBACK (close_clicked_cb),
598 			  dialog);
599 
600 	dialog->priv->file_chooser = GTK_WIDGET (gtk_builder_get_object (builder, "file-chooser-button"));
601 	/* select the first library location, since the default may be
602 	 * the user's home dir or / or something that will take forever to scan.
603 	 */
604 	settings = g_settings_new ("org.gnome.rhythmbox.rhythmdb");
605 	locations = g_settings_get_strv (settings, "locations");
606 	if (locations[0] != NULL) {
607 		dialog->priv->current_uri = g_strdup (locations[0]);
608 	} else {
609 		dialog->priv->current_uri = g_filename_to_uri (rb_music_dir (), NULL, NULL);
610 	}
611 	gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (dialog->priv->file_chooser),
612 						 dialog->priv->current_uri);
613 	g_strfreev (locations);
614 	g_object_unref (settings);
615 
616 	g_signal_connect_object (dialog->priv->file_chooser, "current-folder-changed", G_CALLBACK (current_folder_changed_cb), dialog, 0);
617 
618 	/* not sure why we have to set this, it should be the default */
619 	gtk_widget_set_vexpand (gtk_widget_get_parent (dialog->priv->file_chooser), FALSE);
620 
621 	dialog->priv->info_bar_container = GTK_WIDGET (gtk_builder_get_object (builder, "info-bar-container"));
622 
623 	/* set up entry view */
624 	dialog->priv->entry_view = rb_entry_view_new (dialog->priv->db, G_OBJECT (dialog->priv->shell_player), TRUE, FALSE);
625 
626 	g_signal_connect (dialog->priv->entry_view, "entry-activated", G_CALLBACK (entry_activated_cb), dialog);
627 	g_signal_connect (dialog->priv->entry_view, "have-selection-changed", G_CALLBACK (have_selection_changed_cb), dialog);
628 
629 	rb_entry_view_append_column (dialog->priv->entry_view, RB_ENTRY_VIEW_COL_TRACK_NUMBER, FALSE);
630 	rb_entry_view_append_column (dialog->priv->entry_view, RB_ENTRY_VIEW_COL_TITLE, TRUE);
631 	rb_entry_view_append_column (dialog->priv->entry_view, RB_ENTRY_VIEW_COL_GENRE, FALSE);
632 	rb_entry_view_append_column (dialog->priv->entry_view, RB_ENTRY_VIEW_COL_ARTIST, FALSE);
633 	rb_entry_view_append_column (dialog->priv->entry_view, RB_ENTRY_VIEW_COL_ALBUM, FALSE);
634 	rb_entry_view_append_column (dialog->priv->entry_view, RB_ENTRY_VIEW_COL_YEAR, FALSE);
635 	rb_entry_view_append_column (dialog->priv->entry_view, RB_ENTRY_VIEW_COL_DURATION, FALSE);
636  	rb_entry_view_append_column (dialog->priv->entry_view, RB_ENTRY_VIEW_COL_QUALITY, FALSE);
637 	rb_entry_view_append_column (dialog->priv->entry_view, RB_ENTRY_VIEW_COL_PLAY_COUNT, FALSE);
638 	rb_entry_view_append_column (dialog->priv->entry_view, RB_ENTRY_VIEW_COL_BPM, FALSE);
639 	rb_entry_view_append_column (dialog->priv->entry_view, RB_ENTRY_VIEW_COL_COMMENT, FALSE);
640 	rb_entry_view_append_column (dialog->priv->entry_view, RB_ENTRY_VIEW_COL_LOCATION, FALSE);
641 
642 	settings = g_settings_new ("org.gnome.rhythmbox.sources");
643 	g_settings_bind (settings, "visible-columns", dialog->priv->entry_view, "visible-columns", G_SETTINGS_BIND_DEFAULT);
644 	g_object_unref (settings);
645 
646 	g_signal_connect (dialog->priv->entry_view,
647 			  "notify::sort-order",
648 			  G_CALLBACK (sort_changed_cb),
649 			  dialog);
650 	rb_entry_view_set_sorting_order (dialog->priv->entry_view, "Album", GTK_SORT_ASCENDING);
651 
652 	gtk_container_add (GTK_CONTAINER (gtk_builder_get_object (builder, "entry-view-container")),
653 			   GTK_WIDGET (dialog->priv->entry_view));
654 
655 	dialog->priv->query_model = rhythmdb_query_model_new_empty (dialog->priv->db);
656 	rb_entry_view_set_model (dialog->priv->entry_view, dialog->priv->query_model);
657 	query = rhythmdb_query_parse (dialog->priv->db,
658 				      RHYTHMDB_QUERY_PROP_EQUALS, RHYTHMDB_PROP_TYPE, dialog->priv->entry_type,
659 				      RHYTHMDB_QUERY_END);
660 	rhythmdb_do_full_query_async_parsed (dialog->priv->db, RHYTHMDB_QUERY_RESULTS (dialog->priv->query_model), query);
661 	rhythmdb_query_free (query);
662 
663 	g_signal_connect (dialog->priv->query_model, "post-entry-delete", G_CALLBACK (entry_deleted_cb), dialog);
664 	g_signal_connect (dialog->priv->query_model, "row-inserted", G_CALLBACK (entry_inserted_cb), dialog);
665 
666 	gtk_container_add (GTK_CONTAINER (dialog), GTK_WIDGET (gtk_builder_get_object (builder, "import-dialog")));
667 
668 	gtk_widget_show_all (GTK_WIDGET (dialog));
669 	g_object_unref (builder);
670 }
671 
672 static void
673 impl_dispose (GObject *object)
674 {
675 	RBImportDialog *dialog = RB_IMPORT_DIALOG (object);
676 
677 	if (dialog->priv->pulse_id) {
678 		g_source_remove (dialog->priv->pulse_id);
679 		dialog->priv->pulse_id = 0;
680 	}
681 	if (dialog->priv->add_entries_id) {
682 		g_source_remove (dialog->priv->add_entries_id);
683 		dialog->priv->add_entries_id = 0;
684 	}
685 	if (dialog->priv->added_entries_id) {
686 		g_source_remove (dialog->priv->added_entries_id);
687 		dialog->priv->added_entries_id = 0;
688 	}
689 
690 	if (dialog->priv->query_model != NULL) {
691 		g_object_unref (dialog->priv->query_model);
692 		dialog->priv->query_model = NULL;
693 	}
694 	if (dialog->priv->shell != NULL) {
695 		g_object_unref (dialog->priv->shell);
696 		dialog->priv->shell = NULL;
697 	}
698 	if (dialog->priv->shell_player != NULL) {
699 		g_object_unref (dialog->priv->shell_player);
700 		dialog->priv->shell_player = NULL;
701 	}
702 	if (dialog->priv->db != NULL) {
703 		g_object_unref (dialog->priv->db);
704 		dialog->priv->db = NULL;
705 	}
706 
707 	G_OBJECT_CLASS (rb_import_dialog_parent_class)->dispose (object);
708 }
709 
710 static void
711 impl_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
712 {
713 	RBImportDialog *dialog = RB_IMPORT_DIALOG (object);
714 
715 	switch (prop_id) {
716 	case PROP_SHELL:
717 		dialog->priv->shell = g_value_dup_object (value);
718 		break;
719 	default:
720 		g_assert_not_reached ();
721 		break;
722 	}
723 }
724 
725 static char *
726 get_status (RBImportDialog *dialog)
727 {
728 	if (dialog->priv->query_model == NULL)
729 		return NULL;
730 
731 	return rhythmdb_query_model_compute_status_normal (dialog->priv->query_model, "%d song", "%d songs");
732 }
733 
734 static void
735 impl_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
736 {
737 	RBImportDialog *dialog = RB_IMPORT_DIALOG (object);
738 
739 	switch (prop_id) {
740 	case PROP_SHELL:
741 		g_value_set_object (value, dialog->priv->shell);
742 		break;
743 	case PROP_STATUS:
744 		g_value_take_string (value, get_status (dialog));
745 		break;
746 	default:
747 		g_assert_not_reached ();
748 		break;
749 	}
750 }
751 
752 static void
753 rb_import_dialog_init (RBImportDialog *dialog)
754 {
755 	dialog->priv = G_TYPE_INSTANCE_GET_PRIVATE (dialog,
756 						    RB_TYPE_IMPORT_DIALOG,
757 						    RBImportDialogPrivate);
758 }
759 
760 static void
761 rb_import_dialog_class_init (RBImportDialogClass *klass)
762 {
763 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
764 
765 	object_class->constructed = impl_constructed;
766 	object_class->dispose = impl_dispose;
767 	object_class->set_property = impl_set_property;
768 	object_class->get_property = impl_get_property;
769 
770 	klass->close = impl_close;
771 
772 	g_object_class_install_property (object_class,
773 					 PROP_SHELL,
774 					 g_param_spec_object ("shell",
775 							      "shell",
776 							      "RBShell instance",
777 							      RB_TYPE_SHELL,
778 							      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
779 	g_object_class_install_property (object_class,
780 					 PROP_STATUS,
781 					 g_param_spec_string ("status",
782 							      "status",
783 							      "status text",
784 							      NULL,
785 							      G_PARAM_READABLE));
786 
787 	signals[CLOSE] = g_signal_new ("close",
788 				       RB_TYPE_IMPORT_DIALOG,
789 				       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
790 				       G_STRUCT_OFFSET (RBImportDialogClass, close),
791 				       NULL, NULL,
792 				       g_cclosure_marshal_VOID__VOID,
793 				       G_TYPE_NONE,
794 				       0);
795 	signals[CLOSED] = g_signal_new ("closed",
796 					RB_TYPE_IMPORT_DIALOG,
797 					G_SIGNAL_RUN_LAST,
798 					G_STRUCT_OFFSET (RBImportDialogClass, closed),
799 					NULL, NULL,
800 					g_cclosure_marshal_VOID__VOID,
801 					G_TYPE_NONE,
802 					0);
803 
804 	g_type_class_add_private (object_class, sizeof (RBImportDialogPrivate));
805 
806 	gtk_binding_entry_add_signal (gtk_binding_set_by_class (klass),
807 				      GDK_KEY_Escape,
808 				      0,
809 				      "close",
810 				      0);
811 }
812 
813 void
814 rb_import_dialog_reset (RBImportDialog *dialog)
815 {
816 	g_free (dialog->priv->current_uri);
817 	dialog->priv->current_uri = NULL;
818 
819 	current_folder_changed_cb (GTK_FILE_CHOOSER (dialog->priv->file_chooser), dialog);
820 }
821 
822 GtkWidget *
823 rb_import_dialog_new (RBShell *shell)
824 {
825 	return GTK_WIDGET (g_object_new (RB_TYPE_IMPORT_DIALOG,
826 					 "shell", shell,
827 					 "orientation", GTK_ORIENTATION_VERTICAL,
828 					 NULL));
829 }