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