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