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