add fix handling for serializing files during import
[ardour.git] / gtk2_ardour / sfdb_ui.cc
1 /*
2     Copyright (C) 2005-2006 Paul Davis
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #include <map>
21 #include <cerrno>
22 #include <sstream>
23
24 #include <unistd.h>
25 #include <sys/stat.h>
26 #include <sys/param.h>
27
28 #include <gtkmm/box.h>
29 #include <gtkmm/stock.h>
30 #include <glibmm/fileutils.h>
31
32 #include <pbd/convert.h>
33 #include <pbd/tokenizer.h>
34 #include <pbd/enumwriter.h>
35 #include <pbd/pthread_utils.h>
36 #include <pbd/xml++.h>
37
38 #include <gtkmm2ext/utils.h>
39
40 #include <ardour/audio_library.h>
41 #include <ardour/auditioner.h>
42 #include <ardour/audioregion.h>
43 #include <ardour/audiofilesource.h>
44 #include <ardour/region_factory.h>
45 #include <ardour/source_factory.h>
46 #include <ardour/profile.h>
47
48 #include "ardour_ui.h"
49 #include "editing.h"
50 #include "gui_thread.h"
51 #include "prompter.h"
52 #include "sfdb_ui.h"
53 #include "editing.h"
54 #include "utils.h"
55 #include "gain_meter.h"
56
57 #ifdef FREESOUND
58 #include "sfdb_freesound_mootcher.h"
59 #endif
60
61 #include "i18n.h"
62
63 using namespace ARDOUR;
64 using namespace PBD;
65 using namespace std;
66 using namespace Gtk;
67 using namespace Gtkmm2ext;
68 using namespace Editing;
69
70 using Glib::ustring;
71
72 ustring SoundFileBrowser::persistent_folder;
73
74 static ImportMode
75 string2importmode (string str)
76 {
77         if (str == _("as new tracks")) {
78                 return ImportAsTrack;
79         } else if (str == _("to selected tracks")) {
80                 return ImportToTrack;
81         } else if (str == _("to region list")) {
82                 return ImportAsRegion;
83         } else if (str == _("as new tape tracks")) {
84                 return ImportAsTapeTrack;
85         }
86
87         warning << string_compose (_("programming error: unknown import mode string %1"), str) << endmsg;
88         
89         return ImportAsTrack;
90 }
91
92 static string
93 importmode2string (ImportMode mode)
94 {
95         switch (mode) {
96         case ImportAsTrack:
97                 return _("as new tracks");
98         case ImportToTrack:
99                 return _("to selected tracks");
100         case ImportAsRegion:
101                 return _("to region list");
102         case ImportAsTapeTrack:
103                 return _("as new tape tracks");
104         }
105         /*NOTREACHED*/
106         return _("as new tracks");
107 }
108
109 SoundFileBox::SoundFileBox (bool persistent)
110         : _session(0),
111           table (6, 2),
112           length_clock ("sfboxLengthClock", !persistent, "EditCursorClock", false, true, false),
113           timecode_clock ("sfboxTimecodeClock", !persistent, "EditCursorClock", false, false, false),
114           main_box (false, 6),
115           autoplay_btn (_("Auto-play"))
116         
117 {
118         HBox* hbox;
119         VBox* vbox;
120
121         set_name (X_("SoundFileBox"));
122         set_size_request (300, -1);
123
124         preview_label.set_markup (_("<b>Soundfile Info</b>"));
125
126         border_frame.set_label_widget (preview_label);
127         border_frame.add (main_box);
128
129         pack_start (border_frame, true, true);
130         set_border_width (6);
131
132         main_box.set_border_width (6);
133         main_box.set_spacing (12);
134
135         length.set_text (_("Length:"));
136         timecode.set_text (_("Timestamp:"));
137         format.set_text (_("Format:"));
138         channels.set_text (_("Channels:"));
139         samplerate.set_text (_("Sample rate:"));
140
141         table.set_col_spacings (6);
142         table.set_homogeneous (false);
143         table.set_row_spacings (6);
144
145         table.attach (channels, 0, 1, 0, 1, FILL|EXPAND, (AttachOptions) 0);
146         table.attach (samplerate, 0, 1, 1, 2, FILL|EXPAND, (AttachOptions) 0);
147         table.attach (format, 0, 1, 2, 4, FILL|EXPAND, (AttachOptions) 0);
148         table.attach (length, 0, 1, 4, 5, FILL|EXPAND, (AttachOptions) 0);
149         table.attach (timecode, 0, 1, 5, 6, FILL|EXPAND, (AttachOptions) 0);
150
151         table.attach (channels_value, 1, 2, 0, 1, FILL, (AttachOptions) 0);
152         table.attach (samplerate_value, 1, 2, 1, 2, FILL, (AttachOptions) 0);
153         table.attach (format_text, 1, 2, 2, 4, FILL, AttachOptions (0));
154         table.attach (length_clock, 1, 2, 4, 5, FILL, (AttachOptions) 0);
155         table.attach (timecode_clock, 1, 2, 5, 6, FILL, (AttachOptions) 0);
156
157         length_clock.set_mode (ARDOUR_UI::instance()->secondary_clock.mode());
158         timecode_clock.set_mode (AudioClock::SMPTE);
159
160         hbox = manage (new HBox);
161         hbox->pack_start (table, false, false);
162         main_box.pack_start (*hbox, false, false);
163
164         tags_entry.set_editable (true);
165         tags_entry.signal_focus_out_event().connect (mem_fun (*this, &SoundFileBox::tags_entry_left));
166         hbox = manage (new HBox);
167         hbox->pack_start (tags_entry, true, true);
168
169         vbox = manage (new VBox);
170
171         Label* label = manage (new Label (_("Tags:")));
172         label->set_alignment (0.0f, 0.5f);
173         vbox->set_spacing (6);
174         vbox->pack_start(*label, false, false);
175         vbox->pack_start(*hbox, true, true);
176
177         main_box.pack_start(*vbox, true, true);
178         main_box.pack_start(bottom_box, false, false);
179         
180         play_btn.set_image (*(manage (new Image (Stock::MEDIA_PLAY, ICON_SIZE_BUTTON))));
181         play_btn.set_label (_("Play (double click)"));
182
183         stop_btn.set_image (*(manage (new Image (Stock::MEDIA_STOP, ICON_SIZE_BUTTON))));
184         stop_btn.set_label (_("Stop"));
185         
186         bottom_box.set_homogeneous (false);
187         bottom_box.set_spacing (6);
188         bottom_box.pack_start(play_btn, true, true);
189         bottom_box.pack_start(stop_btn, true, true);
190         bottom_box.pack_start(autoplay_btn, false, false);
191
192         play_btn.signal_clicked().connect (mem_fun (*this, &SoundFileBox::audition));
193         stop_btn.signal_clicked().connect (mem_fun (*this, &SoundFileBox::stop_audition));
194
195         length.set_alignment (0.0f, 0.5f);
196         format.set_alignment (0.0f, 0.5f);
197         channels.set_alignment (0.0f, 0.5f);
198         samplerate.set_alignment (0.0f, 0.5f);
199         timecode.set_alignment (0.0f, 0.5f);
200
201         channels_value.set_alignment (0.0f, 0.5f);
202         samplerate_value.set_alignment (0.0f, 0.5f);
203 }
204
205 void
206 SoundFileBox::set_session(Session* s)
207 {
208         _session = s;
209
210         if (!_session) {
211                 play_btn.set_sensitive (false);
212                 stop_btn.set_sensitive (false);
213         } 
214
215
216         length_clock.set_session (s);
217         timecode_clock.set_session (s);
218 }
219
220 bool
221 SoundFileBox::setup_labels (const ustring& filename) 
222 {
223         if (!path.empty()) {
224                 // save existing tags
225                 tags_changed ();
226         }
227
228         path = filename;
229
230         string error_msg;
231
232         if(!AudioFileSource::get_soundfile_info (filename, sf_info, error_msg)) {
233
234                 preview_label.set_markup (_("<b>Soundfile Info</b>"));
235                 format_text.set_text (_("n/a"));
236                 channels_value.set_text (_("n/a"));
237                 samplerate_value.set_text (_("n/a"));
238                 tags_entry.get_buffer()->set_text ("");
239
240                 length_clock.set (0);
241                 timecode_clock.set (0);
242                 
243                 tags_entry.set_sensitive (false);
244                 play_btn.set_sensitive (false);
245                 
246                 return false;
247         }
248
249         preview_label.set_markup (string_compose ("<b>%1</b>", Glib::path_get_basename (filename)));
250         format_text.set_text (sf_info.format_name);
251         channels_value.set_text (to_string (sf_info.channels, std::dec));
252
253         if (_session && sf_info.samplerate != _session->frame_rate()) {
254                 samplerate.set_markup (string_compose ("<b>%1</b>", _("Sample rate:")));
255                 samplerate_value.set_markup (string_compose (X_("<b>%1 Hz</b>"), sf_info.samplerate));
256                 samplerate_value.set_name ("NewSessionSR1Label");
257                 samplerate.set_name ("NewSessionSR1Label");
258         } else {
259                 samplerate.set_text (_("Sample rate:"));
260                 samplerate_value.set_text (string_compose (X_("%1 Hz"), sf_info.samplerate));
261                 samplerate_value.set_name ("NewSessionSR2Label");
262                 samplerate.set_name ("NewSessionSR2Label");
263         }
264
265         double src_coef = (double) _session->nominal_frame_rate() / sf_info.samplerate;
266
267         length_clock.set (sf_info.length * src_coef + 0.5, true);
268         timecode_clock.set (sf_info.timecode * src_coef + 0.5, true);
269
270         // this is a hack that is fixed in trunk, i think (august 26th, 2007)
271
272         vector<string> tags = Library->get_tags (string ("//") + filename);
273         
274         stringstream tag_string;
275         for (vector<string>::iterator i = tags.begin(); i != tags.end(); ++i) {
276                 if (i != tags.begin()) {
277                         tag_string << ", ";
278                 }
279                 tag_string << *i;
280         }
281         tags_entry.get_buffer()->set_text (tag_string.str());
282         
283         tags_entry.set_sensitive (true);
284         if (_session) {
285                 play_btn.set_sensitive (true);
286         }
287         
288         return true;
289 }
290
291 bool
292 SoundFileBox::autoplay() const
293 {
294         return autoplay_btn.get_active();
295 }
296
297 bool
298 SoundFileBox::audition_oneshot()
299 {
300         audition ();
301         return false;
302 }
303
304 void
305 SoundFileBox::audition ()
306 {
307         if (!_session) {
308                 return;
309         }
310         
311         _session->cancel_audition();
312
313         if (!Glib::file_test (path, Glib::FILE_TEST_EXISTS)) {
314                 warning << string_compose(_("Could not read file: %1 (%2)."), path, strerror(errno)) << endmsg;
315                 return;
316         }
317
318         boost::shared_ptr<Region> r;
319         SourceList srclist;
320         boost::shared_ptr<AudioFileSource> afs;
321         bool old_sbp = AudioSource::get_build_peakfiles ();
322
323         /* don't even think of building peakfiles for these files */
324
325         AudioSource::set_build_peakfiles (false);
326
327         for (int n = 0; n < sf_info.channels; ++n) {
328                 try {
329                         afs = boost::dynamic_pointer_cast<AudioFileSource> (SourceFactory::createReadable (*_session, path, n, AudioFileSource::Flag (0), false));
330                         
331                         srclist.push_back(afs);
332                         
333                 } catch (failed_constructor& err) {
334                         error << _("Could not access soundfile: ") << path << endmsg;
335                         AudioSource::set_build_peakfiles (old_sbp);
336                         return;
337                 }
338         }
339
340         AudioSource::set_build_peakfiles (old_sbp);
341                         
342         if (srclist.empty()) {
343                 return;
344         }
345         
346         afs = boost::dynamic_pointer_cast<AudioFileSource> (srclist[0]);
347         string rname = region_name_from_path (afs->path(), false);
348         r = boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (srclist, 0, srclist[0]->length(), rname, 0, Region::DefaultFlags, false));
349
350         _session->audition_region(r);
351 }
352
353 void
354 SoundFileBox::stop_audition ()
355 {
356         if (_session) {
357                 _session->cancel_audition();
358         }
359 }
360
361 bool
362 SoundFileBox::tags_entry_left (GdkEventFocus *ev)
363 {
364         tags_changed ();
365         return false;
366 }
367
368 void
369 SoundFileBox::tags_changed ()
370 {
371         string tag_string = tags_entry.get_buffer()->get_text ();
372
373         if (tag_string.empty()) {
374                 return;
375         }
376
377         vector<string> tags;
378
379         if (!PBD::tokenize (tag_string, string(",\n"), std::back_inserter (tags), true)) {
380                 warning << _("SoundFileBox: Could not tokenize string: ") << tag_string << endmsg;
381                 return;
382         }
383
384         save_tags (tags);
385 }
386
387 void
388 SoundFileBox::save_tags (const vector<string>& tags)
389 {
390         Library->set_tags (string ("//") + path, tags);
391         Library->save_changes ();
392 }
393
394 SoundFileBrowser::SoundFileBrowser (Gtk::Window& parent, string title, ARDOUR::Session* s, bool persistent)
395         : ArdourDialog (parent, title, false, false),
396           found_list (ListStore::create(found_list_columns)),
397           freesound_list (ListStore::create(freesound_list_columns)),
398           chooser (FILE_CHOOSER_ACTION_OPEN),
399           preview (persistent),
400           found_search_btn (_("Search")),
401           found_list_view (found_list),
402           freesound_search_btn (_("Start Downloading")),
403           freesound_list_view (freesound_list)
404 {
405         resetting_ourselves = false;
406         gm = 0;
407
408         if (ARDOUR::Profile->get_sae()) {
409                 chooser.add_shortcut_folder_uri("file:///Library/GarageBand/Apple Loops");
410                 chooser.add_shortcut_folder_uri("file:///Library/Application Support/GarageBand/Instrument Library/Sampler/Sampler Files");
411         }
412         
413 #ifdef GTKOSX
414         chooser.add_shortcut_folder_uri("file:///Volumes");
415 #endif
416
417         //add the file chooser
418         {
419                 chooser.set_border_width (12);
420                 custom_filter.add_custom (FILE_FILTER_FILENAME, mem_fun(*this, &SoundFileBrowser::on_custom));
421                 custom_filter.set_name (_("Audio files"));
422
423                 matchall_filter.add_pattern ("*.*");
424                 matchall_filter.set_name (_("All files"));
425
426                 chooser.add_filter (custom_filter);
427                 chooser.add_filter (matchall_filter);
428                 chooser.set_select_multiple (true);
429                 chooser.signal_update_preview().connect(mem_fun(*this, &SoundFileBrowser::update_preview));
430                 chooser.signal_file_activated().connect (mem_fun (*this, &SoundFileBrowser::chooser_file_activated));
431 #ifdef GTKOSX
432                 /* some broken redraw behaviour - this is a bandaid */
433                 chooser.signal_selection_changed().connect (mem_fun (chooser, &Widget::queue_draw));
434 #endif
435
436                 if (!persistent_folder.empty()) {
437                         chooser.set_current_folder (persistent_folder);
438                 }
439                 notebook.append_page (chooser, _("Browse Files"));
440         }
441         
442         hpacker.set_spacing (6);
443         hpacker.pack_start (notebook, true, true);
444         hpacker.pack_start (preview, false, false);
445         
446         get_vbox()->pack_start (hpacker, true, true);
447
448         //add tag search
449         {
450                 VBox* vbox;
451                 HBox* hbox;
452
453
454                 hbox = manage(new HBox);
455                 hbox->pack_start (found_entry);
456                 hbox->pack_start (found_search_btn);
457                 
458                 Gtk::ScrolledWindow *scroll = manage(new ScrolledWindow);
459                 scroll->add(found_list_view);
460                 scroll->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
461
462                 vbox = manage(new VBox);
463                 vbox->pack_start (*hbox, PACK_SHRINK);
464                 vbox->pack_start (*scroll);
465                 
466                 found_list_view.append_column(_("Paths"), found_list_columns.pathname);
467
468                 found_list_view.get_selection()->signal_changed().connect(mem_fun(*this, &SoundFileBrowser::found_list_view_selected));
469                 
470                 found_list_view.signal_row_activated().connect (mem_fun (*this, &SoundFileBrowser::found_list_view_activated));
471
472                 found_search_btn.signal_clicked().connect(mem_fun(*this, &SoundFileBrowser::found_search_clicked));
473                 found_entry.signal_activate().connect(mem_fun(*this, &SoundFileBrowser::found_search_clicked));
474
475                 notebook.append_page (*vbox, _("Search Tags"));
476         }
477         
478         //add freesound search
479 #ifdef FREESOUND
480         {
481                 VBox* vbox;
482                 HBox* passbox;
483                 Label* label;
484
485                 passbox = manage(new HBox);
486                 passbox->set_border_width (12);
487                 passbox->set_spacing (6);
488
489                 label = manage (new Label);
490                 label->set_text (_("User:"));
491                 passbox->pack_start (*label, false, false);
492                 passbox->pack_start (freesound_name_entry);
493                 label = manage (new Label);
494                 label->set_text (_("Password:"));
495                 passbox->pack_start (*label, false, false);
496                 passbox->pack_start (freesound_pass_entry);
497                 label = manage (new Label);
498                 label->set_text (_("Tags:"));
499                 passbox->pack_start (*label, false, false);
500                 passbox->pack_start (freesound_entry, false, false);
501                 passbox->pack_start (freesound_search_btn, false, false);
502                 
503                 Gtk::ScrolledWindow *scroll = manage(new ScrolledWindow);
504                 scroll->add(freesound_list_view);
505                 scroll->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
506
507                 vbox = manage(new VBox);
508                 vbox->pack_start (*passbox, PACK_SHRINK);
509                 vbox->pack_start(*scroll);
510                 
511                 //vbox->pack_start (freesound_list_view);
512
513                 freesound_list_view.append_column(_("Paths"), freesound_list_columns.pathname);
514
515                 freesound_list_view.get_selection()->signal_changed().connect(mem_fun(*this, &SoundFileBrowser::freesound_list_view_selected));
516                 
517                 //freesound_list_view.get_selection()->set_mode (SELECTION_MULTIPLE);
518                 freesound_list_view.signal_row_activated().connect (mem_fun (*this, &SoundFileBrowser::freesound_list_view_activated));
519
520                 freesound_search_btn.signal_clicked().connect(mem_fun(*this, &SoundFileBrowser::freesound_search_clicked));
521                 freesound_entry.signal_activate().connect(mem_fun(*this, &SoundFileBrowser::freesound_search_clicked));
522
523                 notebook.append_page (*vbox, _("Search Freesound"));
524         }
525 #endif
526         
527
528         notebook.set_size_request (500, -1);
529
530         set_session (s);
531
532         add_button (Stock::CANCEL, RESPONSE_CANCEL);
533         add_button (Stock::APPLY, RESPONSE_APPLY);
534         add_button (Stock::OK, RESPONSE_OK);
535         
536 }
537
538 SoundFileBrowser::~SoundFileBrowser ()
539 {
540         persistent_folder = chooser.get_current_folder();
541 }
542
543
544 void
545 SoundFileBrowser::on_show ()
546 {
547         ArdourDialog::on_show ();
548         start_metering ();
549 }
550
551 void
552 SoundFileBrowser::clear_selection ()
553 {
554         chooser.unselect_all ();
555         found_list_view.get_selection()->unselect_all ();
556 }
557
558 void
559 SoundFileBrowser::chooser_file_activated ()
560 {
561         preview.audition ();
562 }
563
564 void
565 SoundFileBrowser::found_list_view_activated (const TreeModel::Path& path, TreeViewColumn* col)
566 {
567         preview.audition ();
568 }
569
570 void
571 SoundFileBrowser::freesound_list_view_activated (const TreeModel::Path& path, TreeViewColumn* col)
572 {
573         preview.audition ();
574 }
575
576 void
577 SoundFileBrowser::set_session (Session* s)
578 {
579         ArdourDialog::set_session (s);
580         preview.set_session (s);
581         if (s) {
582                 add_gain_meter ();
583         } else {
584                 remove_gain_meter ();
585         }
586 }
587
588 void
589 SoundFileBrowser::add_gain_meter ()
590 {
591         if (gm) {
592                 delete gm;
593         }
594
595         gm = new GainMeter (*session);
596         gm->set_io (session->the_auditioner());
597
598         meter_packer.set_border_width (12);
599         meter_packer.pack_start (*gm, false, true);
600         hpacker.pack_end (meter_packer, false, false);
601         meter_packer.show_all ();
602         start_metering ();
603 }
604
605 void
606 SoundFileBrowser::remove_gain_meter ()
607 {
608         if (gm) {
609                 meter_packer.remove (*gm);
610                 hpacker.remove (meter_packer);
611                 delete gm;
612                 gm = 0;
613         }
614 }
615
616 void
617 SoundFileBrowser::start_metering ()
618 {
619         metering_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (mem_fun(*this, &SoundFileBrowser::meter));
620 }
621
622 void
623 SoundFileBrowser::stop_metering ()
624 {
625         metering_connection.disconnect();
626 }
627
628 void
629 SoundFileBrowser::meter ()
630 {
631         if (is_mapped () && session && gm) {
632                 gm->update_meters ();
633         }
634 }
635
636 bool
637 SoundFileBrowser::on_custom (const FileFilter::Info& filter_info)
638 {
639         return AudioFileSource::safe_file_extension (filter_info.filename);
640 }
641
642 void
643 SoundFileBrowser::update_preview ()
644 {
645         if (preview.setup_labels (chooser.get_filename())) {
646                 if (preview.autoplay()) {
647                         Glib::signal_idle().connect (mem_fun (preview, &SoundFileBox::audition_oneshot));
648                 }
649         }
650 }
651
652 void
653 SoundFileBrowser::found_list_view_selected ()
654 {
655         if (!reset_options ()) {
656                 set_response_sensitive (RESPONSE_OK, false);
657         } else {
658                 ustring file;
659
660                 TreeView::Selection::ListHandle_Path rows = found_list_view.get_selection()->get_selected_rows ();
661                 
662                 if (!rows.empty()) {
663                         TreeIter iter = found_list->get_iter(*rows.begin());
664                         file = (*iter)[found_list_columns.pathname];
665                         chooser.set_filename (file);
666                         set_response_sensitive (RESPONSE_OK, true);
667                 } else {
668                         set_response_sensitive (RESPONSE_OK, false);
669                 }
670                 
671                 preview.setup_labels (file);
672         }
673 }
674
675 void
676 SoundFileBrowser::freesound_list_view_selected ()
677 {
678         if (!reset_options ()) {
679                 set_response_sensitive (RESPONSE_OK, false);
680         } else {
681                 ustring file;
682
683                 TreeView::Selection::ListHandle_Path rows = freesound_list_view.get_selection()->get_selected_rows ();
684                 
685                 if (!rows.empty()) {
686                         TreeIter iter = freesound_list->get_iter(*rows.begin());
687                         file = (*iter)[freesound_list_columns.pathname];
688                         chooser.set_filename (file);
689                         set_response_sensitive (RESPONSE_OK, true);
690                 } else {
691                         set_response_sensitive (RESPONSE_OK, false);
692                 }
693                 
694                 preview.setup_labels (file);
695         }
696 }
697
698 void
699 SoundFileBrowser::found_search_clicked ()
700 {
701         string tag_string = found_entry.get_text ();
702
703         vector<string> tags;
704
705         if (!PBD::tokenize (tag_string, string(","), std::back_inserter (tags), true)) {
706                 warning << _("SoundFileBrowser: Could not tokenize string: ") << tag_string << endmsg;
707                 return;
708         }
709         
710         vector<string> results;
711         Library->search_members_and (results, tags);
712         
713         found_list->clear();
714         for (vector<string>::iterator i = results.begin(); i != results.end(); ++i) {
715                 TreeModel::iterator new_row = found_list->append();
716                 TreeModel::Row row = *new_row;
717                 string path = Glib::filename_from_uri (string ("file:") + *i);
718                 row[found_list_columns.pathname] = path;
719         }
720 }
721
722 void*
723 freesound_search_thread_entry (void* arg)
724 {
725         PBD::notify_gui_about_thread_creation (pthread_self(), X_("Freesound Search"));
726
727         static_cast<SoundFileBrowser*>(arg)->freesound_search_thread ();
728         
729         return 0;
730 }
731
732 bool searching = false;
733 bool canceling = false;
734
735 void
736 SoundFileBrowser::freesound_search_clicked ()
737 {
738         if (canceling)  //already canceling, button does nothing
739                 return;
740         
741         if ( searching ) {
742                 freesound_search_btn.set_label(_("Cancelling.."));
743                 canceling = true;
744         } else {
745                 searching = true;
746                 freesound_search_btn.set_label(_("Cancel"));
747                 pthread_t freesound_thr;
748                 pthread_create_and_store ("freesound_search", &freesound_thr, 0, freesound_search_thread_entry, this);
749         }
750 }
751
752 void
753 SoundFileBrowser::freesound_search_thread()
754 {
755 #ifdef FREESOUND
756         freesound_list->clear();
757
758         string path;
759         path = Glib::get_home_dir();
760         path += "/Freesound/";
761         Mootcher theMootcher(path.c_str());
762         
763         string name_string = freesound_name_entry.get_text ();
764         string pass_string = freesound_pass_entry.get_text ();
765         string search_string = freesound_entry.get_text ();
766
767         if ( theMootcher.doLogin( name_string, pass_string ) ) {
768
769                 string theString = theMootcher.searchText(search_string);
770
771                 XMLTree doc;
772                 doc.read_buffer( theString );
773                 XMLNode *root = doc.root();
774
775                 if (root==NULL) return;
776
777                 if ( strcmp(root->name().c_str(), "freesound") == 0) {
778
779                         XMLNode *node = 0;
780                         XMLNodeList children = root->children();
781                         XMLNodeConstIterator niter;
782                         for (niter = children.begin(); niter != children.end() && !canceling; ++niter) {
783                                 node = *niter;
784                                 if( strcmp( node->name().c_str(), "sample") == 0 ){
785                                         XMLProperty *prop=node->property ("id");
786                                         string filename = theMootcher.getFile( prop->value().c_str() );
787                                         if ( filename != "" ) {
788                                                 TreeModel::iterator new_row = freesound_list->append();
789                                                 TreeModel::Row row = *new_row;
790                                                 string path = Glib::filename_from_uri (string ("file:") + filename);
791                                                 row[freesound_list_columns.pathname] = path;                                            
792                                         }
793                                 }
794                         }
795                 }
796         }
797
798         searching = false;
799         canceling = false;
800         freesound_search_btn.set_label(_("Start Downloading"));
801 #endif
802 }
803
804 vector<ustring>
805 SoundFileBrowser::get_paths ()
806 {
807         vector<ustring> results;
808         
809         int n = notebook.get_current_page ();
810         
811         if (n == 0) {
812                 vector<ustring> filenames = chooser.get_filenames();
813                 vector<ustring>::iterator i;
814
815                 for (i = filenames.begin(); i != filenames.end(); ++i) {
816                         struct stat buf;
817                         if ((!stat((*i).c_str(), &buf)) && S_ISREG(buf.st_mode)) {
818                                 results.push_back (*i);
819                         }
820                 }
821                 
822         } else if (n==1){
823                 
824                 typedef TreeView::Selection::ListHandle_Path ListPath;
825                 
826                 ListPath rows = found_list_view.get_selection()->get_selected_rows ();
827                 for (ListPath::iterator i = rows.begin() ; i != rows.end(); ++i) {
828                         TreeIter iter = found_list->get_iter(*i);
829                         ustring str = (*iter)[found_list_columns.pathname];
830                         
831                         results.push_back (str);
832                 }
833         } else {
834                 
835                 typedef TreeView::Selection::ListHandle_Path ListPath;
836                 
837                 ListPath rows = freesound_list_view.get_selection()->get_selected_rows ();
838                 for (ListPath::iterator i = rows.begin() ; i != rows.end(); ++i) {
839                         TreeIter iter = freesound_list->get_iter(*i);
840                         ustring str = (*iter)[freesound_list_columns.pathname];
841                         
842                         results.push_back (str);
843                 }
844         }
845
846         return results;
847 }
848
849 void
850 SoundFileOmega::reset_options_noret ()
851 {
852         if (!resetting_ourselves) {
853                 (void) reset_options ();
854         }
855 }
856
857 bool
858 SoundFileOmega::reset_options ()
859 {
860         vector<ustring> paths = get_paths ();
861
862         if (paths.empty()) {
863
864                 channel_combo.set_sensitive (false);
865                 action_combo.set_sensitive (false);
866                 where_combo.set_sensitive (false);
867                 copy_files_btn.set_sensitive (false);
868
869                 return false;
870
871         } else {
872
873                 channel_combo.set_sensitive (true);
874                 action_combo.set_sensitive (true);
875                 where_combo.set_sensitive (true);
876
877                 /* if we get through this function successfully, this may be
878                    reset at the end, once we know if we can use hard links
879                    to do embedding
880                 */
881
882                 if (Config->get_only_copy_imported_files()) {
883                         copy_files_btn.set_sensitive (false);
884                 } else {
885                         copy_files_btn.set_sensitive (false);
886                 }
887         }
888
889         bool same_size;
890         bool src_needed;
891         bool selection_includes_multichannel;
892         bool selection_can_be_embedded_with_links = check_link_status (*session, paths);
893         ImportMode mode;
894
895         if (check_info (paths, same_size, src_needed, selection_includes_multichannel)) {
896                 Glib::signal_idle().connect (mem_fun (*this, &SoundFileOmega::bad_file_message));
897                 return false;
898         }
899
900         ustring existing_choice;
901         vector<string> action_strings;
902
903         if (selected_track_cnt > 0) {
904                 if (channel_combo.get_active_text().length()) {
905                         ImportDisposition id = get_channel_disposition();
906                         
907                         switch (id) {
908                         case Editing::ImportDistinctFiles:
909                                 if (selected_track_cnt == paths.size()) {
910                                         action_strings.push_back (importmode2string (ImportToTrack));
911                                 }
912                                 break;
913                                 
914                         case Editing::ImportDistinctChannels:
915                                 /* XXX it would be nice to allow channel-per-selected track
916                                    but its too hard we don't want to deal with all the 
917                                    different per-file + per-track channel configurations.
918                                 */
919                                 break;
920                                 
921                         default:
922                                 action_strings.push_back (importmode2string (ImportToTrack));
923                                 break;
924                         }
925                 } 
926         }
927
928         action_strings.push_back (importmode2string (ImportAsTrack));
929         action_strings.push_back (importmode2string (ImportAsRegion));
930         action_strings.push_back (importmode2string (ImportAsTapeTrack));
931
932         resetting_ourselves = true;
933
934         existing_choice = action_combo.get_active_text();
935
936         set_popdown_strings (action_combo, action_strings);
937
938         /* preserve any existing choice, if possible */
939
940
941         if (existing_choice.length()) {
942                 vector<string>::iterator x;
943                 for (x = action_strings.begin(); x != action_strings.end(); ++x) {
944                         if (*x == existing_choice) {
945                                 action_combo.set_active_text (existing_choice);
946                                 break;
947                         }
948                 }
949                 if (x == action_strings.end()) {
950                         action_combo.set_active_text (action_strings.front());
951                 }
952         } else {
953                 action_combo.set_active_text (action_strings.front());
954         }
955
956         resetting_ourselves = false;
957
958         if ((mode = get_mode()) == ImportAsRegion) {
959                 where_combo.set_sensitive (false);
960         } else {
961                 where_combo.set_sensitive (true);
962         }
963
964         vector<string> channel_strings;
965         
966         if (mode == ImportAsTrack || mode == ImportAsTapeTrack || mode == ImportToTrack) {
967                 channel_strings.push_back (_("one track per file"));
968
969                 if (selection_includes_multichannel) {
970                         channel_strings.push_back (_("one track per channel"));
971                 }
972
973                 if (paths.size() > 1) {
974                         /* tape tracks are a single region per track, so we cannot
975                            sequence multiple files.
976                         */
977                         if (mode != ImportAsTapeTrack) {
978                                 channel_strings.push_back (_("sequence files"));
979                         }
980                         if (same_size) {
981                                 channel_strings.push_back (_("all files in one track"));
982                         }
983                         
984                 }
985
986         } else {
987                 channel_strings.push_back (_("one region per file"));
988
989                 if (selection_includes_multichannel) {
990                         channel_strings.push_back (_("one region per channel"));
991                 }
992
993                 if (paths.size() > 1) {
994                         if (same_size) {
995                                 channel_strings.push_back (_("all files in one region"));
996                         }
997                 }
998         }
999
1000         existing_choice = channel_combo.get_active_text();
1001
1002         set_popdown_strings (channel_combo, channel_strings);
1003
1004         /* preserve any existing choice, if possible */
1005
1006         if (existing_choice.length()) {
1007                 vector<string>::iterator x;
1008                 for (x = channel_strings.begin(); x != channel_strings.end(); ++x) {
1009                         if (*x == existing_choice) {
1010                                 channel_combo.set_active_text (existing_choice);
1011                                 break;
1012                         }
1013                 }
1014                 if (x == channel_strings.end()) {
1015                         channel_combo.set_active_text (channel_strings.front());
1016                 }
1017         } else {
1018                 channel_combo.set_active_text (channel_strings.front());
1019         }
1020
1021         if (src_needed) {
1022                 src_combo.set_sensitive (true);
1023         } else {
1024                 src_combo.set_sensitive (false);
1025         }
1026         
1027         if (Config->get_only_copy_imported_files()) {
1028
1029                 if (selection_can_be_embedded_with_links) {
1030                         copy_files_btn.set_sensitive (true);
1031                 } else {
1032                         copy_files_btn.set_sensitive (false);
1033                 }
1034
1035         }  else {
1036
1037                 copy_files_btn.set_sensitive (true);
1038         }
1039         
1040         return true;
1041 }       
1042
1043
1044 bool
1045 SoundFileOmega::bad_file_message()
1046 {
1047         MessageDialog msg (*this, 
1048                            _("One or more of the selected files\ncannot be used by Ardour"),
1049                            true,
1050                            Gtk::MESSAGE_INFO,
1051                            Gtk::BUTTONS_OK);
1052         msg.run ();
1053         resetting_ourselves = true;
1054         chooser.unselect_uri (chooser.get_preview_uri());
1055         resetting_ourselves = false;
1056
1057         return false;
1058 }
1059
1060 bool
1061 SoundFileOmega::check_info (const vector<ustring>& paths, bool& same_size, bool& src_needed, bool& multichannel)
1062 {
1063         SoundFileInfo info;
1064         nframes64_t sz = 0;
1065         bool err = false;
1066         string errmsg;
1067
1068         same_size = true;
1069         src_needed = false;
1070         multichannel = false;
1071
1072         for (vector<ustring>::const_iterator i = paths.begin(); i != paths.end(); ++i) {
1073
1074                 if (!AudioFileSource::get_soundfile_info (*i, info, errmsg)) {
1075                         err = true;
1076                 }
1077                 
1078                 if (info.channels > 1) {
1079                         multichannel = true;
1080                 }
1081                 
1082                 if (sz == 0) {
1083                         sz = info.length;
1084                 } else {
1085                         if (sz != info.length) {
1086                                 same_size = false;
1087                         }
1088                 }
1089
1090                 if ((nframes_t) info.samplerate != session->frame_rate()) {
1091                         src_needed = true;
1092                 }
1093         }
1094
1095         return err;
1096 }
1097
1098
1099 bool
1100 SoundFileOmega::check_link_status (const Session& s, const vector<ustring>& paths)
1101 {
1102         string tmpdir = s.sound_dir();
1103         bool ret = false;
1104
1105         tmpdir += "/linktest";
1106
1107         if (mkdir (tmpdir.c_str(), 0744)) {
1108                 if (errno != EEXIST) {
1109                         return false;
1110                 }
1111         }
1112         
1113         for (vector<ustring>::const_iterator i = paths.begin(); i != paths.end(); ++i) {
1114
1115                 char tmpc[MAXPATHLEN+1];
1116
1117                 snprintf (tmpc, sizeof(tmpc), "%s/%s", tmpdir.c_str(), Glib::path_get_basename (*i).c_str());
1118
1119                 /* can we link ? */
1120
1121                 if (link ((*i).c_str(), tmpc)) {
1122                         goto out;
1123                 }
1124                 
1125                 unlink (tmpc);
1126         }
1127
1128         ret = true;
1129
1130   out:
1131         rmdir (tmpdir.c_str());
1132         return ret;
1133 }
1134
1135 SoundFileChooser::SoundFileChooser (Gtk::Window& parent, string title, ARDOUR::Session* s)
1136         : SoundFileBrowser (parent, title, s, false)
1137 {
1138         chooser.set_select_multiple (false);
1139         found_list_view.get_selection()->set_mode (SELECTION_SINGLE);
1140         freesound_list_view.get_selection()->set_mode (SELECTION_SINGLE);
1141 }
1142
1143 void
1144 SoundFileChooser::on_hide ()
1145 {
1146         ArdourDialog::on_hide();
1147         stop_metering ();
1148
1149         if (session) {
1150                 session->cancel_audition();
1151         }
1152 }
1153
1154 ustring
1155 SoundFileChooser::get_filename ()
1156 {
1157         vector<ustring> paths;
1158
1159         paths = get_paths ();
1160
1161         if (paths.empty()) {
1162                 return ustring ();
1163         }
1164         
1165         if (!Glib::file_test (paths.front(), Glib::FILE_TEST_EXISTS|Glib::FILE_TEST_IS_REGULAR)) {
1166                 return ustring();
1167         }
1168
1169         return paths.front();
1170 }
1171
1172 SoundFileOmega::SoundFileOmega (Gtk::Window& parent, string title, ARDOUR::Session* s, int selected_tracks, bool persistent,
1173                                 Editing::ImportMode mode_hint)
1174         : SoundFileBrowser (parent, title, s, persistent),
1175           copy_files_btn ( _("Copy files to session")),
1176           selected_track_cnt (selected_tracks)
1177 {
1178         VBox* vbox;
1179         HBox* hbox;
1180         vector<string> str;
1181
1182         set_size_request (-1, 450);
1183         
1184         block_two.set_border_width (12);
1185         block_three.set_border_width (12);
1186         block_four.set_border_width (12);
1187         
1188         options.set_spacing (12);
1189
1190         str.clear ();
1191         str.push_back (_("use file timestamp"));
1192         str.push_back (_("at edit point"));
1193         str.push_back (_("at playhead"));
1194         str.push_back (_("at session start"));
1195         set_popdown_strings (where_combo, str);
1196         where_combo.set_active_text (str.front());
1197
1198         Label* l = manage (new Label);
1199         l->set_text (_("Add files:"));
1200         
1201         hbox = manage (new HBox);
1202         hbox->set_border_width (12);
1203         hbox->set_spacing (6);
1204         hbox->pack_start (*l, false, false);
1205         hbox->pack_start (action_combo, false, false);
1206         vbox = manage (new VBox);
1207         vbox->pack_start (*hbox, false, false);
1208         options.pack_start (*vbox, false, false);
1209
1210         /* dummy entry for action combo so that it doesn't look odd if we 
1211            come up with no tracks selected.
1212         */
1213
1214         str.clear ();
1215         str.push_back (importmode2string (mode_hint));
1216         set_popdown_strings (action_combo, str);
1217         action_combo.set_active_text (str.front());
1218         action_combo.set_sensitive (false);
1219
1220         l = manage (new Label);
1221         l->set_text (_("Insert:"));
1222
1223         hbox = manage (new HBox);
1224         hbox->set_border_width (12);
1225         hbox->set_spacing (6);
1226         hbox->pack_start (*l, false, false);
1227         hbox->pack_start (where_combo, false, false);
1228         vbox = manage (new VBox);
1229         vbox->pack_start (*hbox, false, false);
1230         options.pack_start (*vbox, false, false);
1231
1232
1233         l = manage (new Label);
1234         l->set_text (_("Mapping:"));
1235
1236         hbox = manage (new HBox);
1237         hbox->set_border_width (12);
1238         hbox->set_spacing (6);
1239         hbox->pack_start (*l, false, false);
1240         hbox->pack_start (channel_combo, false, false);
1241         vbox = manage (new VBox);
1242         vbox->pack_start (*hbox, false, false);
1243         options.pack_start (*vbox, false, false);
1244
1245         str.clear ();
1246         str.push_back (_("one track per file"));
1247         set_popdown_strings (channel_combo, str);
1248         channel_combo.set_active_text (str.front());
1249         channel_combo.set_sensitive (false);
1250
1251         l = manage (new Label);
1252         l->set_text (_("Conversion Quality:"));
1253
1254         hbox = manage (new HBox);
1255         hbox->set_border_width (12);
1256         hbox->set_spacing (6);
1257         hbox->pack_start (*l, false, false);
1258         hbox->pack_start (src_combo, false, false);
1259         vbox = manage (new VBox);
1260         vbox->pack_start (*hbox, false, false);
1261         options.pack_start (*vbox, false, false);
1262
1263         str.clear ();
1264         str.push_back (_("Best"));
1265         str.push_back (_("Good"));
1266         str.push_back (_("Quick"));
1267         str.push_back (_("Fast"));
1268         str.push_back (_("Fastest"));
1269
1270         set_popdown_strings (src_combo, str);
1271         src_combo.set_active_text (str.front());
1272         src_combo.set_sensitive (false);
1273
1274         reset_options ();
1275
1276         action_combo.signal_changed().connect (mem_fun (*this, &SoundFileOmega::reset_options_noret));
1277         
1278         copy_files_btn.set_active (true);
1279
1280         block_four.pack_start (copy_files_btn, false, false);
1281
1282         options.pack_start (block_four, false, false);
1283
1284         get_vbox()->pack_start (options, false, false);
1285
1286         /* setup disposition map */
1287
1288         disposition_map.insert (pair<ustring,ImportDisposition>(_("one track per file"), ImportDistinctFiles));
1289         disposition_map.insert (pair<ustring,ImportDisposition>(_("one track per channel"), ImportDistinctChannels));
1290         disposition_map.insert (pair<ustring,ImportDisposition>(_("merge files"), ImportMergeFiles));
1291         disposition_map.insert (pair<ustring,ImportDisposition>(_("sequence files"), ImportSerializeFiles));
1292
1293         disposition_map.insert (pair<ustring,ImportDisposition>(_("one region per file"), ImportDistinctFiles));
1294         disposition_map.insert (pair<ustring,ImportDisposition>(_("one region per channel"), ImportDistinctChannels));
1295         disposition_map.insert (pair<ustring,ImportDisposition>(_("all files in one region"), ImportMergeFiles));
1296         disposition_map.insert (pair<ustring,ImportDisposition>(_("all files in one track"), ImportSerializeFiles));
1297
1298         chooser.signal_selection_changed().connect (mem_fun (*this, &SoundFileOmega::file_selection_changed));
1299
1300         /* set size requests for a couple of combos to allow them to display the longest text
1301            they will ever be asked to display.  This prevents them being resized when the user
1302            selects a file to import, which in turn prevents the size of the dialog from jumping
1303            around. */
1304
1305         vector<string> t;
1306         t.push_back (_("one track per file"));
1307         t.push_back (_("one track per channel"));
1308         t.push_back (_("sequence files"));
1309         t.push_back (_("all files in one region"));
1310         set_size_request_to_display_given_text (channel_combo, t, COMBO_FUDGE + 10, 15);
1311
1312         t.clear ();
1313         t.push_back (importmode2string (ImportAsTrack));
1314         t.push_back (importmode2string (ImportToTrack));
1315         t.push_back (importmode2string (ImportAsRegion));
1316         t.push_back (importmode2string (ImportAsTapeTrack));
1317         set_size_request_to_display_given_text (action_combo, t, COMBO_FUDGE + 10, 15);
1318 }
1319
1320 void
1321 SoundFileOmega::set_mode (ImportMode mode)
1322 {
1323         action_combo.set_active_text (importmode2string (mode));
1324 }
1325
1326 ImportMode
1327 SoundFileOmega::get_mode () const
1328 {
1329         return string2importmode (action_combo.get_active_text());
1330 }
1331
1332 void
1333 SoundFileOmega::on_hide ()
1334 {
1335         ArdourDialog::on_hide();
1336         if (session) {
1337                 session->cancel_audition();
1338         }
1339 }
1340
1341 ImportPosition
1342 SoundFileOmega::get_position() const
1343 {
1344         ustring str = where_combo.get_active_text();
1345
1346         if (str == _("use file timestamp")) {
1347                 return ImportAtTimestamp;
1348         } else if (str == _("at edit point")) {
1349                 return ImportAtEditPoint;
1350         } else if (str == _("at playhead")) {
1351                 return ImportAtPlayhead;
1352         } else {
1353                 return ImportAtStart;
1354         }
1355 }
1356
1357 SrcQuality
1358 SoundFileOmega::get_src_quality() const
1359 {
1360         ustring str = where_combo.get_active_text();
1361
1362         if (str == _("Best")) {
1363                 return SrcBest;
1364         } else if (str == _("Good")) {
1365                 return SrcGood;
1366         } else if (str == _("Quick")) {
1367                 return SrcQuick;
1368         } else if (str == _("Fast")) {
1369                 return SrcFast;
1370         } else {
1371                 return SrcFastest;
1372         }
1373 }
1374
1375 ImportDisposition
1376 SoundFileOmega::get_channel_disposition () const
1377 {
1378         /* we use a map here because the channel combo can contain different strings
1379            depending on the state of the other combos. the map contains all possible strings
1380            and the ImportDisposition enum that corresponds to it.
1381         */
1382
1383         ustring str = channel_combo.get_active_text();
1384         DispositionMap::const_iterator x = disposition_map.find (str);
1385
1386         if (x == disposition_map.end()) {
1387                 fatal << string_compose (_("programming error: %1 (%2)"), "unknown string for import disposition", str) << endmsg;
1388                 /*NOTREACHED*/
1389         }
1390
1391         return x->second;
1392 }
1393
1394 void
1395 SoundFileOmega::reset (int selected_tracks)
1396 {
1397         selected_track_cnt = selected_tracks;
1398         reset_options ();
1399 }       
1400
1401 void
1402 SoundFileOmega::file_selection_changed ()
1403 {
1404         if (resetting_ourselves) {
1405                 return;
1406         }
1407
1408         if (!reset_options ()) {
1409                 set_response_sensitive (RESPONSE_OK, false);
1410         } else {
1411                 if (chooser.get_filenames().size() > 0) {
1412                         set_response_sensitive (RESPONSE_OK, true);
1413                 } else {
1414                         set_response_sensitive (RESPONSE_OK, false);
1415                 }
1416         }
1417 }
1418