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 }