various fixes to MidiRegionView selection handling, key handling, drawing of ghost...
[ardour2.git] / gtk2_ardour / sfdb_ui.cc
blob2b98fd5dcc0435e7b9af175875f3eff5fc86973e
1 /*
2 Copyright (C) 2005-2006 Paul Davis
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.
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.
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.
20 #ifdef WAF_BUILD
21 #include "gtk2ardour-config.h"
22 #endif
24 #include <map>
25 #include <cerrno>
26 #include <sstream>
28 #include <unistd.h>
29 #include <sys/stat.h>
30 #include <sys/param.h>
32 #include <gtkmm/box.h>
33 #include <gtkmm/stock.h>
34 #include <glibmm/fileutils.h>
36 #include "pbd/convert.h"
37 #include "pbd/tokenizer.h"
38 #include "pbd/enumwriter.h"
39 #include "pbd/pthread_utils.h"
40 #include "pbd/xml++.h"
42 #include <gtkmm2ext/utils.h>
44 #include "evoral/SMF.hpp"
46 #include "ardour/amp.h"
47 #include "ardour/audio_library.h"
48 #include "ardour/auditioner.h"
49 #include "ardour/audioregion.h"
50 #include "ardour/audiofilesource.h"
51 #include "ardour/smf_source.h"
52 #include "ardour/region_factory.h"
53 #include "ardour/source_factory.h"
54 #include "ardour/session.h"
55 #include "ardour/session_directory.h"
56 #include "ardour/profile.h"
58 #include "ardour_ui.h"
59 #include "editing.h"
60 #include "gui_thread.h"
61 #include "prompter.h"
62 #include "sfdb_ui.h"
63 #include "editing.h"
64 #include "utils.h"
65 #include "gain_meter.h"
67 #ifdef FREESOUND
68 #include "sfdb_freesound_mootcher.h"
69 #endif
71 #include "i18n.h"
73 using namespace ARDOUR;
74 using namespace PBD;
75 using namespace std;
76 using namespace Gtk;
77 using namespace Gtkmm2ext;
78 using namespace Editing;
80 using std::string;
82 string SoundFileBrowser::persistent_folder;
84 static ImportMode
85 string2importmode (string str)
87 if (str == _("as new tracks")) {
88 return ImportAsTrack;
89 } else if (str == _("to selected tracks")) {
90 return ImportToTrack;
91 } else if (str == _("to region list")) {
92 return ImportAsRegion;
93 } else if (str == _("as new tape tracks")) {
94 return ImportAsTapeTrack;
97 warning << string_compose (_("programming error: unknown import mode string %1"), str) << endmsg;
99 return ImportAsTrack;
102 static string
103 importmode2string (ImportMode mode)
105 switch (mode) {
106 case ImportAsTrack:
107 return _("as new tracks");
108 case ImportToTrack:
109 return _("to selected tracks");
110 case ImportAsRegion:
111 return _("to region list");
112 case ImportAsTapeTrack:
113 return _("as new tape tracks");
115 /*NOTREACHED*/
116 return _("as new tracks");
119 SoundFileBox::SoundFileBox (bool persistent)
120 : table (6, 2),
121 length_clock ("sfboxLengthClock", !persistent, "EditCursorClock", false, false, true, false),
122 timecode_clock ("sfboxTimecodeClock", !persistent, "EditCursorClock", false, false, false, false),
123 main_box (false, 6),
124 autoplay_btn (_("Auto-play"))
127 set_name (X_("SoundFileBox"));
128 set_size_request (300, -1);
130 preview_label.set_markup (_("<b>Sound File Information</b>"));
132 border_frame.set_label_widget (preview_label);
133 border_frame.add (main_box);
135 pack_start (border_frame, true, true);
136 set_border_width (6);
138 main_box.set_border_width (6);
140 length.set_text (_("Length:"));
141 length.set_alignment (1, 0.5);
142 timecode.set_text (_("Timestamp:"));
143 timecode.set_alignment (1, 0.5);
144 format.set_text (_("Format:"));
145 format.set_alignment (1, 0.5);
146 channels.set_text (_("Channels:"));
147 channels.set_alignment (1, 0.5);
148 samplerate.set_text (_("Sample rate:"));
149 samplerate.set_alignment (1, 0.5);
151 preview_label.set_max_width_chars (50);
152 preview_label.set_ellipsize (Pango::ELLIPSIZE_END);
154 format_text.set_max_width_chars (20);
155 format_text.set_ellipsize (Pango::ELLIPSIZE_END);
156 format_text.set_alignment (0, 1);
158 table.set_col_spacings (6);
159 table.set_homogeneous (false);
160 table.set_row_spacings (6);
162 table.attach (channels, 0, 1, 0, 1, FILL, FILL);
163 table.attach (samplerate, 0, 1, 1, 2, FILL, FILL);
164 table.attach (format, 0, 1, 2, 4, FILL, FILL);
165 table.attach (length, 0, 1, 4, 5, FILL, FILL);
166 table.attach (timecode, 0, 1, 5, 6, FILL, FILL);
168 table.attach (channels_value, 1, 2, 0, 1, FILL, FILL);
169 table.attach (samplerate_value, 1, 2, 1, 2, FILL, FILL);
170 table.attach (format_text, 1, 2, 2, 4, FILL, FILL);
171 table.attach (length_clock, 1, 2, 4, 5, FILL, FILL);
172 table.attach (timecode_clock, 1, 2, 5, 6, FILL, FILL);
174 length_clock.set_mode (ARDOUR_UI::instance()->secondary_clock->mode());
175 timecode_clock.set_mode (AudioClock::Timecode);
177 main_box.pack_start (table, false, false);
179 tags_entry.set_editable (true);
180 tags_entry.signal_focus_out_event().connect (sigc::mem_fun (*this, &SoundFileBox::tags_entry_left));
182 Label* label = manage (new Label (_("Tags:")));
183 label->set_alignment (0.0f, 0.5f);
184 main_box.pack_start (*label, false, false);
185 main_box.pack_start (tags_entry, true, true);
187 main_box.pack_start (bottom_box, false, false);
189 play_btn.set_image (*(manage (new Image (Stock::MEDIA_PLAY, ICON_SIZE_BUTTON))));
190 play_btn.set_label (_("Play"));
192 stop_btn.set_image (*(manage (new Image (Stock::MEDIA_STOP, ICON_SIZE_BUTTON))));
193 stop_btn.set_label (_("Stop"));
195 bottom_box.set_homogeneous (false);
196 bottom_box.set_spacing (6);
197 bottom_box.pack_start(play_btn, true, true);
198 bottom_box.pack_start(stop_btn, true, true);
199 bottom_box.pack_start(autoplay_btn, false, false);
201 play_btn.signal_clicked().connect (sigc::mem_fun (*this, &SoundFileBox::audition));
202 stop_btn.signal_clicked().connect (sigc::mem_fun (*this, &SoundFileBox::stop_audition));
204 channels_value.set_alignment (0.0f, 0.5f);
205 samplerate_value.set_alignment (0.0f, 0.5f);
208 void
209 SoundFileBox::set_session(Session* s)
211 SessionHandlePtr::set_session (s);
213 length_clock.set_session (s);
214 timecode_clock.set_session (s);
216 if (!_session) {
217 play_btn.set_sensitive (false);
218 stop_btn.set_sensitive (false);
222 bool
223 SoundFileBox::setup_labels (const string& filename)
225 if (!path.empty()) {
226 // save existing tags
227 tags_changed ();
230 path = filename;
232 string error_msg;
234 if(!AudioFileSource::get_soundfile_info (filename, sf_info, error_msg)) {
236 preview_label.set_markup (_("<b>Sound File Information</b>"));
237 format_text.set_text ("");
238 channels_value.set_text ("");
239 samplerate_value.set_text ("");
240 tags_entry.get_buffer()->set_text ("");
242 length_clock.set (0);
243 timecode_clock.set (0);
245 tags_entry.set_sensitive (false);
246 play_btn.set_sensitive (false);
248 return false;
251 preview_label.set_markup (string_compose ("<b>%1</b>", Glib::path_get_basename (filename)));
252 std::string n = sf_info.format_name;
253 if (n.substr (0, 8) == X_("Format: ")) {
254 n = n.substr (8);
256 format_text.set_text (n);
257 channels_value.set_text (to_string (sf_info.channels, std::dec));
259 if (_session && sf_info.samplerate != _session->frame_rate()) {
260 samplerate.set_markup (string_compose ("<b>%1</b>", _("Sample rate:")));
261 samplerate_value.set_markup (string_compose (X_("<b>%1 Hz</b>"), sf_info.samplerate));
262 samplerate_value.set_name ("NewSessionSR1Label");
263 samplerate.set_name ("NewSessionSR1Label");
264 } else {
265 samplerate.set_text (_("Sample rate:"));
266 samplerate_value.set_text (string_compose (X_("%1 Hz"), sf_info.samplerate));
267 samplerate_value.set_name ("NewSessionSR2Label");
268 samplerate.set_name ("NewSessionSR2Label");
271 framecnt_t const nfr = _session ? _session->nominal_frame_rate() : 25;
272 double src_coef = (double) nfr / sf_info.samplerate;
274 length_clock.set (sf_info.length * src_coef + 0.5, true);
275 timecode_clock.set (sf_info.timecode * src_coef + 0.5, true);
277 // this is a hack that is fixed in trunk, i think (august 26th, 2007)
279 vector<string> tags = Library->get_tags (string ("//") + filename);
281 stringstream tag_string;
282 for (vector<string>::iterator i = tags.begin(); i != tags.end(); ++i) {
283 if (i != tags.begin()) {
284 tag_string << ", ";
286 tag_string << *i;
288 tags_entry.get_buffer()->set_text (tag_string.str());
290 tags_entry.set_sensitive (true);
291 if (_session) {
292 play_btn.set_sensitive (true);
295 return true;
298 bool
299 SoundFileBox::autoplay() const
301 return autoplay_btn.get_active();
304 bool
305 SoundFileBox::audition_oneshot()
307 audition ();
308 return false;
311 void
312 SoundFileBox::audition ()
314 if (!_session) {
315 return;
318 if (SMFSource::safe_midi_file_extension (path)) {
319 error << _("Auditioning of MIDI files is not yet supported") << endmsg;
320 return;
323 _session->cancel_audition();
325 if (!Glib::file_test (path, Glib::FILE_TEST_EXISTS)) {
326 warning << string_compose(_("Could not read file: %1 (%2)."), path, strerror(errno)) << endmsg;
327 return;
330 boost::shared_ptr<Region> r;
331 SourceList srclist;
332 boost::shared_ptr<AudioFileSource> afs;
333 bool old_sbp = AudioSource::get_build_peakfiles ();
335 /* don't even think of building peakfiles for these files */
337 AudioSource::set_build_peakfiles (false);
339 for (int n = 0; n < sf_info.channels; ++n) {
340 try {
341 afs = boost::dynamic_pointer_cast<AudioFileSource> (
342 SourceFactory::createReadable (DataType::AUDIO, *_session,
343 path, n, Source::Flag (0), false));
345 srclist.push_back(afs);
347 } catch (failed_constructor& err) {
348 error << _("Could not access soundfile: ") << path << endmsg;
349 AudioSource::set_build_peakfiles (old_sbp);
350 return;
354 AudioSource::set_build_peakfiles (old_sbp);
356 if (srclist.empty()) {
357 return;
360 afs = boost::dynamic_pointer_cast<AudioFileSource> (srclist[0]);
361 string rname = region_name_from_path (afs->path(), false);
363 PropertyList plist;
365 plist.add (ARDOUR::Properties::start, 0);
366 plist.add (ARDOUR::Properties::length, srclist[0]->length(srclist[0]->timeline_position()));
367 plist.add (ARDOUR::Properties::name, rname);
368 plist.add (ARDOUR::Properties::layer, 0);
370 r = boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (srclist, plist, false));
372 _session->audition_region(r);
375 void
376 SoundFileBox::stop_audition ()
378 if (_session) {
379 _session->cancel_audition();
383 bool
384 SoundFileBox::tags_entry_left (GdkEventFocus *)
386 tags_changed ();
387 return false;
390 void
391 SoundFileBox::tags_changed ()
393 string tag_string = tags_entry.get_buffer()->get_text ();
395 if (tag_string.empty()) {
396 return;
399 vector<string> tags;
401 if (!PBD::tokenize (tag_string, string(",\n"), std::back_inserter (tags), true)) {
402 warning << _("SoundFileBox: Could not tokenize string: ") << tag_string << endmsg;
403 return;
406 save_tags (tags);
409 void
410 SoundFileBox::save_tags (const vector<string>& tags)
412 Library->set_tags (string ("//") + path, tags);
413 Library->save_changes ();
416 SoundFileBrowser::SoundFileBrowser (Gtk::Window& parent, string title, ARDOUR::Session* s, bool persistent)
417 : ArdourDialog (parent, title, false, false),
418 found_list (ListStore::create(found_list_columns)),
419 freesound_list (ListStore::create(freesound_list_columns)),
420 chooser (FILE_CHOOSER_ACTION_OPEN),
421 preview (persistent),
422 found_search_btn (_("Search")),
423 found_list_view (found_list),
424 freesound_search_btn (_("Start Downloading")),
425 freesound_list_view (freesound_list)
427 resetting_ourselves = false;
428 gm = 0;
430 resetting_ourselves = false;
431 gm = 0;
433 #ifdef GTKOSX
434 chooser.add_shortcut_folder_uri("file:///Library/GarageBand/Apple Loops");
435 chooser.add_shortcut_folder_uri("file:///Library/Audio/Apple Loops");
436 chooser.add_shortcut_folder_uri("file:///Library/Application Support/GarageBand/Instrument Library/Sampler/Sampler Files");
438 chooser.add_shortcut_folder_uri("file:///Volumes");
439 #endif
441 //add the file chooser
443 chooser.set_border_width (12);
445 audio_filter.add_custom (FILE_FILTER_FILENAME, sigc::mem_fun(*this, &SoundFileBrowser::on_audio_filter));
446 audio_filter.set_name (_("Audio files"));
448 midi_filter.add_custom (FILE_FILTER_FILENAME, sigc::mem_fun(*this, &SoundFileBrowser::on_midi_filter));
449 midi_filter.set_name (_("MIDI files"));
451 matchall_filter.add_pattern ("*.*");
452 matchall_filter.set_name (_("All files"));
454 chooser.add_filter (audio_filter);
455 chooser.add_filter (midi_filter);
456 chooser.add_filter (matchall_filter);
457 chooser.set_select_multiple (true);
458 chooser.signal_update_preview().connect(sigc::mem_fun(*this, &SoundFileBrowser::update_preview));
459 chooser.signal_file_activated().connect (sigc::mem_fun (*this, &SoundFileBrowser::chooser_file_activated));
460 #ifdef GTKOSX
461 /* some broken redraw behaviour - this is a bandaid */
462 chooser.signal_selection_changed().connect (mem_fun (chooser, &Widget::queue_draw));
463 #endif
465 if (!persistent_folder.empty()) {
466 chooser.set_current_folder (persistent_folder);
468 notebook.append_page (chooser, _("Browse Files"));
471 hpacker.set_spacing (6);
472 hpacker.pack_start (notebook, true, true);
473 hpacker.pack_start (preview, false, false);
475 get_vbox()->pack_start (hpacker, true, true);
477 //add tag search
479 VBox* vbox;
480 HBox* hbox;
483 hbox = manage(new HBox);
484 hbox->pack_start (found_entry);
485 hbox->pack_start (found_search_btn);
487 Gtk::ScrolledWindow *scroll = manage(new ScrolledWindow);
488 scroll->add(found_list_view);
489 scroll->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
491 vbox = manage(new VBox);
492 vbox->pack_start (*hbox, PACK_SHRINK);
493 vbox->pack_start (*scroll);
495 found_list_view.append_column(_("Paths"), found_list_columns.pathname);
497 found_list_view.get_selection()->signal_changed().connect(sigc::mem_fun(*this, &SoundFileBrowser::found_list_view_selected));
499 found_list_view.signal_row_activated().connect (sigc::mem_fun (*this, &SoundFileBrowser::found_list_view_activated));
501 found_search_btn.signal_clicked().connect(sigc::mem_fun(*this, &SoundFileBrowser::found_search_clicked));
502 found_entry.signal_activate().connect(sigc::mem_fun(*this, &SoundFileBrowser::found_search_clicked));
504 notebook.append_page (*vbox, _("Search Tags"));
507 //add freesound search
508 #ifdef FREESOUND
510 VBox* vbox;
511 HBox* passbox;
512 Label* label;
514 passbox = manage(new HBox);
515 passbox->set_border_width (12);
516 passbox->set_spacing (6);
518 label = manage (new Label);
519 label->set_text (_("User:"));
520 passbox->pack_start (*label, false, false);
521 passbox->pack_start (freesound_name_entry);
522 label = manage (new Label);
523 label->set_text (_("Password:"));
524 passbox->pack_start (*label, false, false);
525 passbox->pack_start (freesound_pass_entry);
526 label = manage (new Label);
527 label->set_text (_("Tags:"));
528 passbox->pack_start (*label, false, false);
529 passbox->pack_start (freesound_entry, false, false);
530 passbox->pack_start (freesound_search_btn, false, false);
532 Gtk::ScrolledWindow *scroll = manage(new ScrolledWindow);
533 scroll->add(freesound_list_view);
534 scroll->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
536 vbox = manage(new VBox);
537 vbox->pack_start (*passbox, PACK_SHRINK);
538 vbox->pack_start(*scroll);
540 //vbox->pack_start (freesound_list_view);
542 freesound_list_view.append_column(_("Paths"), freesound_list_columns.pathname);
543 freesound_list_view.get_selection()->signal_changed().connect(sigc::mem_fun(*this, &SoundFileBrowser::freesound_list_view_selected));
545 //freesound_list_view.get_selection()->set_mode (SELECTION_MULTIPLE);
546 freesound_list_view.signal_row_activated().connect (sigc::mem_fun (*this, &SoundFileBrowser::freesound_list_view_activated));
547 freesound_search_btn.signal_clicked().connect(sigc::mem_fun(*this, &SoundFileBrowser::freesound_search_clicked));
548 freesound_entry.signal_activate().connect(sigc::mem_fun(*this, &SoundFileBrowser::freesound_search_clicked));
549 notebook.append_page (*vbox, _("Search Freesound"));
551 #endif
554 notebook.set_size_request (500, -1);
556 set_session (s);
558 add_button (Stock::CANCEL, RESPONSE_CANCEL);
559 add_button (Stock::APPLY, RESPONSE_APPLY);
560 add_button (Stock::OK, RESPONSE_OK);
564 SoundFileBrowser::~SoundFileBrowser ()
566 persistent_folder = chooser.get_current_folder();
570 void
571 SoundFileBrowser::on_show ()
573 ArdourDialog::on_show ();
574 start_metering ();
577 void
578 SoundFileBrowser::clear_selection ()
580 chooser.unselect_all ();
581 found_list_view.get_selection()->unselect_all ();
584 void
585 SoundFileBrowser::chooser_file_activated ()
587 preview.audition ();
590 void
591 SoundFileBrowser::found_list_view_activated (const TreeModel::Path&, TreeViewColumn*)
593 preview.audition ();
596 void
597 SoundFileBrowser::freesound_list_view_activated (const TreeModel::Path&, TreeViewColumn*)
599 preview.audition ();
602 void
603 SoundFileBrowser::set_session (Session* s)
605 ArdourDialog::set_session (s);
606 preview.set_session (s);
608 if (_session) {
609 add_gain_meter ();
610 } else {
611 remove_gain_meter ();
615 void
616 SoundFileBrowser::add_gain_meter ()
618 delete gm;
620 gm = new GainMeter (_session, 250);
622 boost::shared_ptr<Route> r = _session->the_auditioner ();
624 gm->set_controls (r, r->shared_peak_meter(), r->amp());
626 meter_packer.set_border_width (12);
627 meter_packer.pack_start (*gm, false, true);
628 hpacker.pack_end (meter_packer, false, false);
629 meter_packer.show_all ();
630 start_metering ();
633 void
634 SoundFileBrowser::remove_gain_meter ()
636 if (gm) {
637 meter_packer.remove (*gm);
638 hpacker.remove (meter_packer);
639 delete gm;
640 gm = 0;
644 void
645 SoundFileBrowser::start_metering ()
647 metering_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (sigc::mem_fun(*this, &SoundFileBrowser::meter));
650 void
651 SoundFileBrowser::stop_metering ()
653 metering_connection.disconnect();
656 void
657 SoundFileBrowser::meter ()
659 if (is_mapped () && _session && gm) {
660 gm->update_meters ();
664 bool
665 SoundFileBrowser::on_audio_filter (const FileFilter::Info& filter_info)
667 return AudioFileSource::safe_audio_file_extension (filter_info.filename);
670 bool
671 SoundFileBrowser::on_midi_filter (const FileFilter::Info& filter_info)
673 return SMFSource::safe_midi_file_extension (filter_info.filename);
676 void
677 SoundFileBrowser::update_preview ()
679 if (preview.setup_labels (chooser.get_filename())) {
680 if (preview.autoplay()) {
681 Glib::signal_idle().connect (sigc::mem_fun (preview, &SoundFileBox::audition_oneshot));
686 void
687 SoundFileBrowser::found_list_view_selected ()
689 if (!reset_options ()) {
690 set_response_sensitive (RESPONSE_OK, false);
691 } else {
692 string file;
694 TreeView::Selection::ListHandle_Path rows = found_list_view.get_selection()->get_selected_rows ();
696 if (!rows.empty()) {
697 TreeIter iter = found_list->get_iter(*rows.begin());
698 file = (*iter)[found_list_columns.pathname];
699 chooser.set_filename (file);
700 set_response_sensitive (RESPONSE_OK, true);
701 } else {
702 set_response_sensitive (RESPONSE_OK, false);
705 preview.setup_labels (file);
709 void
710 SoundFileBrowser::freesound_list_view_selected ()
712 if (!reset_options ()) {
713 set_response_sensitive (RESPONSE_OK, false);
714 } else {
715 string file;
717 TreeView::Selection::ListHandle_Path rows = freesound_list_view.get_selection()->get_selected_rows ();
719 if (!rows.empty()) {
720 TreeIter iter = freesound_list->get_iter(*rows.begin());
721 file = (*iter)[freesound_list_columns.pathname];
722 chooser.set_filename (file);
723 set_response_sensitive (RESPONSE_OK, true);
724 } else {
725 set_response_sensitive (RESPONSE_OK, false);
728 preview.setup_labels (file);
732 void
733 SoundFileBrowser::found_search_clicked ()
735 string tag_string = found_entry.get_text ();
737 vector<string> tags;
739 if (!PBD::tokenize (tag_string, string(","), std::back_inserter (tags), true)) {
740 warning << _("SoundFileBrowser: Could not tokenize string: ") << tag_string << endmsg;
741 return;
744 vector<string> results;
745 Library->search_members_and (results, tags);
747 found_list->clear();
748 for (vector<string>::iterator i = results.begin(); i != results.end(); ++i) {
749 TreeModel::iterator new_row = found_list->append();
750 TreeModel::Row row = *new_row;
751 string path = Glib::filename_from_uri (string ("file:") + *i);
752 row[found_list_columns.pathname] = path;
756 void*
757 freesound_search_thread_entry (void* arg)
759 SessionEvent::create_per_thread_pool ("freesound events", 64);
761 static_cast<SoundFileBrowser*>(arg)->freesound_search_thread ();
763 return 0;
766 bool searching = false;
767 bool canceling = false;
769 void
770 SoundFileBrowser::freesound_search_clicked ()
772 if (canceling) //already canceling, button does nothing
773 return;
775 if ( searching ) {
776 freesound_search_btn.set_label(_("Cancelling.."));
777 canceling = true;
778 } else {
779 searching = true;
780 freesound_search_btn.set_label(_("Cancel"));
781 pthread_t freesound_thr;
782 pthread_create_and_store ("freesound_search", &freesound_thr, freesound_search_thread_entry, this);
786 void
787 SoundFileBrowser::freesound_search_thread()
789 #if 0
791 THIS IS ALL TOTALLY THREAD-ILLEGAL ... YOU CANNOT DO GTK STUFF IN THIS THREAD
793 #ifdef FREESOUND
794 freesound_list->clear();
796 string path;
797 path = Glib::get_home_dir();
798 path += "/Freesound/";
799 Mootcher theMootcher(path.c_str());
801 string name_string = freesound_name_entry.get_text ();
802 string pass_string = freesound_pass_entry.get_text ();
803 string search_string = freesound_entry.get_text ();
805 if ( theMootcher.doLogin( name_string, pass_string ) ) {
807 string theString = theMootcher.searchText(search_string);
809 XMLTree doc;
810 doc.read_buffer( theString );
811 XMLNode *root = doc.root();
813 if (root==NULL) return;
815 if ( strcmp(root->name().c_str(), "freesound") == 0) {
817 XMLNode *node = 0;
818 XMLNodeList children = root->children();
819 XMLNodeConstIterator niter;
820 for (niter = children.begin(); niter != children.end() && !canceling; ++niter) {
821 node = *niter;
822 if( strcmp( node->name().c_str(), "sample") == 0 ){
823 XMLProperty *prop=node->property ("id");
824 string filename = theMootcher.getFile( prop->value().c_str() );
825 if ( filename != "" ) {
826 TreeModel::iterator new_row = freesound_list->append();
827 TreeModel::Row row = *new_row;
828 string path = Glib::filename_from_uri (string ("file:") + filename);
829 row[freesound_list_columns.pathname] = path;
836 searching = false;
837 canceling = false;
838 freesound_search_btn.set_label(_("Start Downloading"));
839 #endif
840 #endif
844 vector<string>
845 SoundFileBrowser::get_paths ()
847 vector<string> results;
849 int n = notebook.get_current_page ();
851 if (n == 0) {
852 vector<string> filenames = chooser.get_filenames();
853 vector<string>::iterator i;
855 for (i = filenames.begin(); i != filenames.end(); ++i) {
856 struct stat buf;
857 if ((!stat((*i).c_str(), &buf)) && S_ISREG(buf.st_mode)) {
858 results.push_back (*i);
862 } else if (n==1){
864 typedef TreeView::Selection::ListHandle_Path ListPath;
866 ListPath rows = found_list_view.get_selection()->get_selected_rows ();
867 for (ListPath::iterator i = rows.begin() ; i != rows.end(); ++i) {
868 TreeIter iter = found_list->get_iter(*i);
869 string str = (*iter)[found_list_columns.pathname];
871 results.push_back (str);
873 } else {
875 typedef TreeView::Selection::ListHandle_Path ListPath;
877 ListPath rows = freesound_list_view.get_selection()->get_selected_rows ();
878 for (ListPath::iterator i = rows.begin() ; i != rows.end(); ++i) {
879 TreeIter iter = freesound_list->get_iter(*i);
880 string str = (*iter)[freesound_list_columns.pathname];
882 results.push_back (str);
886 return results;
889 void
890 SoundFileOmega::reset_options_noret ()
892 if (!resetting_ourselves) {
893 (void) reset_options ();
897 bool
898 SoundFileOmega::reset_options ()
900 vector<string> paths = get_paths ();
902 if (paths.empty()) {
904 channel_combo.set_sensitive (false);
905 action_combo.set_sensitive (false);
906 where_combo.set_sensitive (false);
907 copy_files_btn.set_sensitive (false);
909 return false;
911 } else {
913 channel_combo.set_sensitive (true);
914 action_combo.set_sensitive (true);
915 where_combo.set_sensitive (true);
917 /* if we get through this function successfully, this may be
918 reset at the end, once we know if we can use hard links
919 to do embedding
922 if (Config->get_only_copy_imported_files()) {
923 copy_files_btn.set_sensitive (false);
924 } else {
925 copy_files_btn.set_sensitive (false);
929 bool same_size;
930 bool src_needed;
931 bool selection_includes_multichannel;
932 bool selection_can_be_embedded_with_links = check_link_status (_session, paths);
933 ImportMode mode;
935 if (check_info (paths, same_size, src_needed, selection_includes_multichannel)) {
936 Glib::signal_idle().connect (sigc::mem_fun (*this, &SoundFileOmega::bad_file_message));
937 return false;
940 string existing_choice;
941 vector<string> action_strings;
943 if (chooser.get_filter() == &audio_filter) {
945 /* AUDIO */
947 if (selected_audio_track_cnt > 0) {
948 if (channel_combo.get_active_text().length()) {
949 ImportDisposition id = get_channel_disposition();
951 switch (id) {
952 case Editing::ImportDistinctFiles:
953 if (selected_audio_track_cnt == paths.size()) {
954 action_strings.push_back (importmode2string (ImportToTrack));
956 break;
958 case Editing::ImportDistinctChannels:
959 /* XXX it would be nice to allow channel-per-selected track
960 but its too hard we don't want to deal with all the
961 different per-file + per-track channel configurations.
963 break;
965 default:
966 action_strings.push_back (importmode2string (ImportToTrack));
967 break;
972 } else {
974 /* MIDI */
976 if (selected_midi_track_cnt > 0) {
977 action_strings.push_back (importmode2string (ImportToTrack));
981 action_strings.push_back (importmode2string (ImportAsTrack));
982 action_strings.push_back (importmode2string (ImportAsRegion));
983 action_strings.push_back (importmode2string (ImportAsTapeTrack));
985 resetting_ourselves = true;
987 existing_choice = action_combo.get_active_text();
989 set_popdown_strings (action_combo, action_strings);
991 /* preserve any existing choice, if possible */
994 if (existing_choice.length()) {
995 vector<string>::iterator x;
996 for (x = action_strings.begin(); x != action_strings.end(); ++x) {
997 if (*x == existing_choice) {
998 action_combo.set_active_text (existing_choice);
999 break;
1002 if (x == action_strings.end()) {
1003 action_combo.set_active_text (action_strings.front());
1005 } else {
1006 action_combo.set_active_text (action_strings.front());
1009 resetting_ourselves = false;
1011 if ((mode = get_mode()) == ImportAsRegion) {
1012 where_combo.set_sensitive (false);
1013 } else {
1014 where_combo.set_sensitive (true);
1017 vector<string> channel_strings;
1019 if (mode == ImportAsTrack || mode == ImportAsTapeTrack || mode == ImportToTrack) {
1020 channel_strings.push_back (_("one track per file"));
1022 if (selection_includes_multichannel) {
1023 channel_strings.push_back (_("one track per channel"));
1026 if (paths.size() > 1) {
1027 /* tape tracks are a single region per track, so we cannot
1028 sequence multiple files.
1030 if (mode != ImportAsTapeTrack) {
1031 channel_strings.push_back (_("sequence files"));
1033 if (same_size) {
1034 channel_strings.push_back (_("all files in one track"));
1035 channel_strings.push_back (_("merge files"));
1040 } else {
1041 channel_strings.push_back (_("one region per file"));
1043 if (selection_includes_multichannel) {
1044 channel_strings.push_back (_("one region per channel"));
1047 if (paths.size() > 1) {
1048 if (same_size) {
1049 channel_strings.push_back (_("all files in one region"));
1054 resetting_ourselves = true;
1056 existing_choice = channel_combo.get_active_text();
1058 set_popdown_strings (channel_combo, channel_strings);
1060 /* preserve any existing choice, if possible */
1062 if (existing_choice.length()) {
1063 vector<string>::iterator x;
1064 for (x = channel_strings.begin(); x != channel_strings.end(); ++x) {
1065 if (*x == existing_choice) {
1066 channel_combo.set_active_text (existing_choice);
1067 break;
1070 if (x == channel_strings.end()) {
1071 channel_combo.set_active_text (channel_strings.front());
1073 } else {
1074 channel_combo.set_active_text (channel_strings.front());
1077 resetting_ourselves = false;
1079 if (src_needed) {
1080 src_combo.set_sensitive (true);
1081 } else {
1082 src_combo.set_sensitive (false);
1085 if (Config->get_only_copy_imported_files()) {
1087 if (selection_can_be_embedded_with_links) {
1088 copy_files_btn.set_sensitive (true);
1089 } else {
1090 copy_files_btn.set_sensitive (false);
1093 } else {
1095 copy_files_btn.set_sensitive (true);
1098 return true;
1102 bool
1103 SoundFileOmega::bad_file_message()
1105 MessageDialog msg (*this,
1106 string_compose (_("One or more of the selected files\ncannot be used by %1"), PROGRAM_NAME),
1107 true,
1108 Gtk::MESSAGE_INFO,
1109 Gtk::BUTTONS_OK);
1110 msg.run ();
1111 resetting_ourselves = true;
1112 chooser.unselect_uri (chooser.get_preview_uri());
1113 resetting_ourselves = false;
1115 return false;
1118 bool
1119 SoundFileOmega::check_info (const vector<string>& paths, bool& same_size, bool& src_needed, bool& multichannel)
1121 SoundFileInfo info;
1122 framepos_t sz = 0;
1123 bool err = false;
1124 string errmsg;
1126 same_size = true;
1127 src_needed = false;
1128 multichannel = false;
1130 for (vector<string>::const_iterator i = paths.begin(); i != paths.end(); ++i) {
1132 if (AudioFileSource::get_soundfile_info (*i, info, errmsg)) {
1133 if (info.channels > 1) {
1134 multichannel = true;
1136 if (sz == 0) {
1137 sz = info.length;
1138 } else {
1139 if (sz != info.length) {
1140 same_size = false;
1144 if (info.samplerate != _session->frame_rate()) {
1145 src_needed = true;
1148 } else if (SMFSource::safe_midi_file_extension (*i)) {
1150 Evoral::SMF reader;
1151 reader.open(*i);
1152 if (reader.num_tracks() > 1) {
1153 multichannel = true; // "channel" == track here...
1156 /* XXX we need err = true handling here in case
1157 we can't check the file
1160 } else {
1161 err = true;
1165 return err;
1169 bool
1170 SoundFileOmega::check_link_status (const Session* s, const vector<string>& paths)
1172 sys::path path = s->session_directory().sound_path() / "linktest";
1173 string tmpdir = path.to_string();
1174 bool ret = false;
1176 if (mkdir (tmpdir.c_str(), 0744)) {
1177 if (errno != EEXIST) {
1178 return false;
1182 for (vector<string>::const_iterator i = paths.begin(); i != paths.end(); ++i) {
1184 char tmpc[MAXPATHLEN+1];
1186 snprintf (tmpc, sizeof(tmpc), "%s/%s", tmpdir.c_str(), Glib::path_get_basename (*i).c_str());
1188 /* can we link ? */
1190 if (link ((*i).c_str(), tmpc)) {
1191 goto out;
1194 unlink (tmpc);
1197 ret = true;
1199 out:
1200 rmdir (tmpdir.c_str());
1201 return ret;
1204 SoundFileChooser::SoundFileChooser (Gtk::Window& parent, string title, ARDOUR::Session* s)
1205 : SoundFileBrowser (parent, title, s, false)
1207 chooser.set_select_multiple (false);
1208 found_list_view.get_selection()->set_mode (SELECTION_SINGLE);
1209 freesound_list_view.get_selection()->set_mode (SELECTION_SINGLE);
1212 void
1213 SoundFileChooser::on_hide ()
1215 ArdourDialog::on_hide();
1216 stop_metering ();
1218 if (_session) {
1219 _session->cancel_audition();
1223 string
1224 SoundFileChooser::get_filename ()
1226 vector<string> paths;
1228 paths = get_paths ();
1230 if (paths.empty()) {
1231 return string ();
1234 if (!Glib::file_test (paths.front(), Glib::FILE_TEST_EXISTS|Glib::FILE_TEST_IS_REGULAR)) {
1235 return string();
1238 return paths.front();
1241 SoundFileOmega::SoundFileOmega (Gtk::Window& parent, string title, ARDOUR::Session* s,
1242 uint32_t selected_audio_tracks,
1243 uint32_t selected_midi_tracks,
1244 bool persistent,
1245 Editing::ImportMode mode_hint)
1246 : SoundFileBrowser (parent, title, s, persistent)
1247 , copy_files_btn ( _("Copy files to session"))
1248 , selected_audio_track_cnt (selected_audio_tracks)
1249 , selected_midi_track_cnt (selected_midi_tracks)
1251 VBox* vbox;
1252 HBox* hbox;
1253 vector<string> str;
1255 set_size_request (-1, 450);
1257 block_two.set_border_width (12);
1258 block_three.set_border_width (12);
1259 block_four.set_border_width (12);
1261 options.set_spacing (12);
1263 str.clear ();
1264 str.push_back (_("file timestamp"));
1265 str.push_back (_("edit point"));
1266 str.push_back (_("playhead"));
1267 str.push_back (_("session start"));
1268 set_popdown_strings (where_combo, str);
1269 where_combo.set_active_text (str.front());
1271 Label* l = manage (new Label);
1272 l->set_text (_("Add files:"));
1274 hbox = manage (new HBox);
1275 hbox->set_border_width (12);
1276 hbox->set_spacing (6);
1277 hbox->pack_start (*l, false, false);
1278 hbox->pack_start (action_combo, false, false);
1279 vbox = manage (new VBox);
1280 vbox->pack_start (*hbox, false, false);
1281 options.pack_start (*vbox, false, false);
1283 /* dummy entry for action combo so that it doesn't look odd if we
1284 come up with no tracks selected.
1287 str.clear ();
1288 str.push_back (importmode2string (mode_hint));
1289 set_popdown_strings (action_combo, str);
1290 action_combo.set_active_text (str.front());
1291 action_combo.set_sensitive (false);
1293 l = manage (new Label);
1294 l->set_text (_("Insert at:"));
1296 hbox = manage (new HBox);
1297 hbox->set_border_width (12);
1298 hbox->set_spacing (6);
1299 hbox->pack_start (*l, false, false);
1300 hbox->pack_start (where_combo, false, false);
1301 vbox = manage (new VBox);
1302 vbox->pack_start (*hbox, false, false);
1303 options.pack_start (*vbox, false, false);
1306 l = manage (new Label);
1307 l->set_text (_("Mapping:"));
1309 hbox = manage (new HBox);
1310 hbox->set_border_width (12);
1311 hbox->set_spacing (6);
1312 hbox->pack_start (*l, false, false);
1313 hbox->pack_start (channel_combo, false, false);
1314 vbox = manage (new VBox);
1315 vbox->pack_start (*hbox, false, false);
1316 options.pack_start (*vbox, false, false);
1318 str.clear ();
1319 str.push_back (_("one track per file"));
1320 set_popdown_strings (channel_combo, str);
1321 channel_combo.set_active_text (str.front());
1322 channel_combo.set_sensitive (false);
1324 l = manage (new Label);
1325 l->set_text (_("Conversion quality:"));
1327 hbox = manage (new HBox);
1328 hbox->set_border_width (12);
1329 hbox->set_spacing (6);
1330 hbox->pack_start (*l, false, false);
1331 hbox->pack_start (src_combo, false, false);
1332 vbox = manage (new VBox);
1333 vbox->pack_start (*hbox, false, false);
1334 options.pack_start (*vbox, false, false);
1336 str.clear ();
1337 str.push_back (_("Best"));
1338 str.push_back (_("Good"));
1339 str.push_back (_("Quick"));
1340 str.push_back (_("Fast"));
1341 str.push_back (_("Fastest"));
1343 set_popdown_strings (src_combo, str);
1344 src_combo.set_active_text (str.front());
1345 src_combo.set_sensitive (false);
1347 reset_options ();
1349 action_combo.signal_changed().connect (sigc::mem_fun (*this, &SoundFileOmega::reset_options_noret));
1350 channel_combo.signal_changed().connect (sigc::mem_fun (*this, &SoundFileOmega::reset_options_noret));
1352 copy_files_btn.set_active (true);
1354 block_four.pack_start (copy_files_btn, false, false);
1356 options.pack_start (block_four, false, false);
1358 get_vbox()->pack_start (options, false, false);
1360 /* setup disposition map */
1362 disposition_map.insert (pair<string,ImportDisposition>(_("one track per file"), ImportDistinctFiles));
1363 disposition_map.insert (pair<string,ImportDisposition>(_("one track per channel"), ImportDistinctChannels));
1364 disposition_map.insert (pair<string,ImportDisposition>(_("merge files"), ImportMergeFiles));
1365 disposition_map.insert (pair<string,ImportDisposition>(_("sequence files"), ImportSerializeFiles));
1367 disposition_map.insert (pair<string,ImportDisposition>(_("one region per file"), ImportDistinctFiles));
1368 disposition_map.insert (pair<string,ImportDisposition>(_("one region per channel"), ImportDistinctChannels));
1369 disposition_map.insert (pair<string,ImportDisposition>(_("all files in one region"), ImportMergeFiles));
1370 disposition_map.insert (pair<string,ImportDisposition>(_("all files in one track"), ImportMergeFiles));
1372 chooser.signal_selection_changed().connect (sigc::mem_fun (*this, &SoundFileOmega::file_selection_changed));
1374 /* set size requests for a couple of combos to allow them to display the longest text
1375 they will ever be asked to display. This prevents them being resized when the user
1376 selects a file to import, which in turn prevents the size of the dialog from jumping
1377 around. */
1379 vector<string> t;
1380 t.push_back (_("one track per file"));
1381 t.push_back (_("one track per channel"));
1382 t.push_back (_("sequence files"));
1383 t.push_back (_("all files in one region"));
1384 set_size_request_to_display_given_text (channel_combo, t, COMBO_FUDGE + 10, 15);
1386 t.clear ();
1387 t.push_back (importmode2string (ImportAsTrack));
1388 t.push_back (importmode2string (ImportToTrack));
1389 t.push_back (importmode2string (ImportAsRegion));
1390 t.push_back (importmode2string (ImportAsTapeTrack));
1391 set_size_request_to_display_given_text (action_combo, t, COMBO_FUDGE + 10, 15);
1394 void
1395 SoundFileOmega::set_mode (ImportMode mode)
1397 action_combo.set_active_text (importmode2string (mode));
1400 ImportMode
1401 SoundFileOmega::get_mode () const
1403 return string2importmode (action_combo.get_active_text());
1406 void
1407 SoundFileOmega::on_hide ()
1409 ArdourDialog::on_hide();
1410 if (_session) {
1411 _session->cancel_audition();
1415 ImportPosition
1416 SoundFileOmega::get_position() const
1418 string str = where_combo.get_active_text();
1420 if (str == _("file timestamp")) {
1421 return ImportAtTimestamp;
1422 } else if (str == _("edit point")) {
1423 return ImportAtEditPoint;
1424 } else if (str == _("playhead")) {
1425 return ImportAtPlayhead;
1426 } else {
1427 return ImportAtStart;
1431 SrcQuality
1432 SoundFileOmega::get_src_quality() const
1434 string str = where_combo.get_active_text();
1436 if (str == _("Best")) {
1437 return SrcBest;
1438 } else if (str == _("Good")) {
1439 return SrcGood;
1440 } else if (str == _("Quick")) {
1441 return SrcQuick;
1442 } else if (str == _("Fast")) {
1443 return SrcFast;
1444 } else {
1445 return SrcFastest;
1449 ImportDisposition
1450 SoundFileOmega::get_channel_disposition () const
1452 /* we use a map here because the channel combo can contain different strings
1453 depending on the state of the other combos. the map contains all possible strings
1454 and the ImportDisposition enum that corresponds to it.
1457 string str = channel_combo.get_active_text();
1458 DispositionMap::const_iterator x = disposition_map.find (str);
1460 if (x == disposition_map.end()) {
1461 fatal << string_compose (_("programming error: %1 (%2)"), "unknown string for import disposition", str) << endmsg;
1462 /*NOTREACHED*/
1465 return x->second;
1468 void
1469 SoundFileOmega::reset (uint32_t selected_audio_tracks, uint32_t selected_midi_tracks)
1471 selected_audio_track_cnt = selected_audio_tracks;
1472 selected_midi_track_cnt = selected_midi_tracks;
1473 reset_options ();
1476 void
1477 SoundFileOmega::file_selection_changed ()
1479 if (resetting_ourselves) {
1480 return;
1483 if (!reset_options ()) {
1484 set_response_sensitive (RESPONSE_OK, false);
1485 } else {
1486 if (chooser.get_filenames().size() > 0) {
1487 set_response_sensitive (RESPONSE_OK, true);
1488 } else {
1489 set_response_sensitive (RESPONSE_OK, false);