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