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