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