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