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