fix mis-handling of button press events on rec-enable that ought to forward to Bindab...
[ardour2.git] / gtk2_ardour / sfdb_ui.cc
blob88b233e3300f9d200f58bbe58be9715d5af47053
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 #include <map>
21 #include <cerrno>
22 #include <sstream>
24 #include <unistd.h>
25 #include <sys/stat.h>
26 #include <sys/param.h>
28 #include <gtkmm/box.h>
29 #include <gtkmm/stock.h>
30 #include <glibmm/fileutils.h>
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>
38 #include <gtkmm2ext/utils.h>
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>
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"
57 #ifdef FREESOUND
58 #include "sfdb_freesound_mootcher.h"
59 #endif
61 #include "i18n.h"
63 using namespace ARDOUR;
64 using namespace PBD;
65 using namespace std;
66 using namespace Gtk;
67 using namespace Gtkmm2ext;
68 using namespace Editing;
70 using Glib::ustring;
72 ustring SoundFileBrowser::persistent_folder;
74 static ImportMode
75 string2importmode (string str)
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;
87 warning << string_compose (_("programming error: unknown import mode string %1"), str) << endmsg;
89 return ImportAsTrack;
92 static string
93 importmode2string (ImportMode mode)
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");
105 /*NOTREACHED*/
106 return _("as new tracks");
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"))
118 HBox* hbox;
119 VBox* vbox;
121 set_name (X_("SoundFileBox"));
122 set_size_request (300, -1);
124 preview_label.set_markup (_("<b>Soundfile Info</b>"));
126 border_frame.set_label_widget (preview_label);
127 border_frame.add (main_box);
129 pack_start (border_frame, true, true);
130 set_border_width (6);
132 main_box.set_border_width (6);
133 main_box.set_spacing (12);
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:"));
141 table.set_col_spacings (6);
142 table.set_homogeneous (false);
143 table.set_row_spacings (6);
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);
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);
157 length_clock.set_mode (ARDOUR_UI::instance()->secondary_clock.mode());
158 timecode_clock.set_mode (AudioClock::SMPTE);
160 hbox = manage (new HBox);
161 hbox->pack_start (table, false, false);
162 main_box.pack_start (*hbox, false, false);
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);
169 vbox = manage (new VBox);
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);
177 main_box.pack_start(*vbox, true, true);
178 main_box.pack_start(bottom_box, false, false);
180 play_btn.set_image (*(manage (new Image (Stock::MEDIA_PLAY, ICON_SIZE_BUTTON))));
181 play_btn.set_label (_("Play (double click)"));
183 stop_btn.set_image (*(manage (new Image (Stock::MEDIA_STOP, ICON_SIZE_BUTTON))));
184 stop_btn.set_label (_("Stop"));
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);
192 play_btn.signal_clicked().connect (mem_fun (*this, &SoundFileBox::audition));
193 stop_btn.signal_clicked().connect (mem_fun (*this, &SoundFileBox::stop_audition));
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);
201 channels_value.set_alignment (0.0f, 0.5f);
202 samplerate_value.set_alignment (0.0f, 0.5f);
205 void
206 SoundFileBox::set_session(Session* s)
208 _session = s;
210 if (!_session) {
211 play_btn.set_sensitive (false);
212 stop_btn.set_sensitive (false);
216 length_clock.set_session (s);
217 timecode_clock.set_session (s);
220 bool
221 SoundFileBox::setup_labels (const ustring& filename)
223 if (!path.empty()) {
224 // save existing tags
225 tags_changed ();
228 path = filename;
230 string error_msg;
232 if(!AudioFileSource::get_soundfile_info (filename, sf_info, error_msg)) {
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 ("");
240 length_clock.set (0);
241 timecode_clock.set (0);
243 tags_entry.set_sensitive (false);
244 play_btn.set_sensitive (false);
246 return false;
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));
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");
265 double src_coef = (double) _session->nominal_frame_rate() / sf_info.samplerate;
267 length_clock.set (sf_info.length * src_coef + 0.5, true);
268 timecode_clock.set (sf_info.timecode * src_coef + 0.5, true);
270 // this is a hack that is fixed in trunk, i think (august 26th, 2007)
272 vector<string> tags = Library->get_tags (string ("//") + filename);
274 stringstream tag_string;
275 for (vector<string>::iterator i = tags.begin(); i != tags.end(); ++i) {
276 if (i != tags.begin()) {
277 tag_string << ", ";
279 tag_string << *i;
281 tags_entry.get_buffer()->set_text (tag_string.str());
283 tags_entry.set_sensitive (true);
284 if (_session) {
285 play_btn.set_sensitive (true);
288 return true;
291 bool
292 SoundFileBox::autoplay() const
294 return autoplay_btn.get_active();
297 bool
298 SoundFileBox::audition_oneshot()
300 audition ();
301 return false;
304 void
305 SoundFileBox::audition ()
307 if (!_session) {
308 return;
311 _session->cancel_audition();
313 if (!Glib::file_test (path, Glib::FILE_TEST_EXISTS)) {
314 warning << string_compose(_("Could not read file: %1 (%2)."), path, strerror(errno)) << endmsg;
315 return;
318 boost::shared_ptr<Region> r;
319 SourceList srclist;
320 boost::shared_ptr<AudioFileSource> afs;
321 bool old_sbp = AudioSource::get_build_peakfiles ();
323 /* don't even think of building peakfiles for these files */
325 AudioSource::set_build_peakfiles (false);
327 for (int n = 0; n < sf_info.channels; ++n) {
328 try {
329 afs = boost::dynamic_pointer_cast<AudioFileSource> (SourceFactory::createReadable (*_session, path, n, AudioFileSource::Flag (0), false));
331 srclist.push_back(afs);
333 } catch (failed_constructor& err) {
334 error << _("Could not access soundfile: ") << path << endmsg;
335 AudioSource::set_build_peakfiles (old_sbp);
336 return;
340 AudioSource::set_build_peakfiles (old_sbp);
342 if (srclist.empty()) {
343 return;
346 afs = boost::dynamic_pointer_cast<AudioFileSource> (srclist[0]);
347 string rname = region_name_from_path (afs->path(), false);
348 r = boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (srclist, 0, srclist[0]->length(), rname, 0, Region::DefaultFlags, false));
350 _session->audition_region(r);
353 void
354 SoundFileBox::stop_audition ()
356 if (_session) {
357 _session->cancel_audition();
361 bool
362 SoundFileBox::tags_entry_left (GdkEventFocus *ev)
364 tags_changed ();
365 return false;
368 void
369 SoundFileBox::tags_changed ()
371 string tag_string = tags_entry.get_buffer()->get_text ();
373 if (tag_string.empty()) {
374 return;
377 vector<string> tags;
379 if (!PBD::tokenize (tag_string, string(",\n"), std::back_inserter (tags), true)) {
380 warning << _("SoundFileBox: Could not tokenize string: ") << tag_string << endmsg;
381 return;
384 save_tags (tags);
387 void
388 SoundFileBox::save_tags (const vector<string>& tags)
390 Library->set_tags (string ("//") + path, tags);
391 Library->save_changes ();
394 SoundFileBrowser::SoundFileBrowser (Gtk::Window& parent, string title, ARDOUR::Session* s, bool persistent)
395 : ArdourDialog (parent, title, false, false),
396 found_list (ListStore::create(found_list_columns)),
397 freesound_list (ListStore::create(freesound_list_columns)),
398 chooser (FILE_CHOOSER_ACTION_OPEN),
399 preview (persistent),
400 found_search_btn (_("Search")),
401 found_list_view (found_list),
402 freesound_search_btn (_("Start Downloading")),
403 freesound_list_view (freesound_list)
405 resetting_ourselves = false;
406 gm = 0;
408 if (ARDOUR::Profile->get_sae()) {
409 chooser.add_shortcut_folder_uri("file:///Library/GarageBand/Apple Loops");
410 chooser.add_shortcut_folder_uri("file:///Library/Application Support/GarageBand/Instrument Library/Sampler/Sampler Files");
413 //add the file chooser
415 chooser.set_border_width (12);
416 custom_filter.add_custom (FILE_FILTER_FILENAME, mem_fun(*this, &SoundFileBrowser::on_custom));
417 custom_filter.set_name (_("Audio files"));
419 matchall_filter.add_pattern ("*.*");
420 matchall_filter.set_name (_("All files"));
422 chooser.add_filter (custom_filter);
423 chooser.add_filter (matchall_filter);
424 chooser.set_select_multiple (true);
425 chooser.signal_update_preview().connect(mem_fun(*this, &SoundFileBrowser::update_preview));
426 chooser.signal_file_activated().connect (mem_fun (*this, &SoundFileBrowser::chooser_file_activated));
427 #ifdef GTKOSX
428 /* some broken redraw behaviour - this is a bandaid */
429 chooser.signal_selection_changed().connect (mem_fun (chooser, &Widget::queue_draw));
430 #endif
432 if (!persistent_folder.empty()) {
433 chooser.set_current_folder (persistent_folder);
435 notebook.append_page (chooser, _("Browse Files"));
438 hpacker.set_spacing (6);
439 hpacker.pack_start (notebook, true, true);
440 hpacker.pack_start (preview, false, false);
442 get_vbox()->pack_start (hpacker, true, true);
444 //add tag search
446 VBox* vbox;
447 HBox* hbox;
450 hbox = manage(new HBox);
451 hbox->pack_start (found_entry);
452 hbox->pack_start (found_search_btn);
454 Gtk::ScrolledWindow *scroll = manage(new ScrolledWindow);
455 scroll->add(found_list_view);
456 scroll->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
458 vbox = manage(new VBox);
459 vbox->pack_start (*hbox, PACK_SHRINK);
460 vbox->pack_start (*scroll);
462 found_list_view.append_column(_("Paths"), found_list_columns.pathname);
464 found_list_view.get_selection()->signal_changed().connect(mem_fun(*this, &SoundFileBrowser::found_list_view_selected));
466 found_list_view.signal_row_activated().connect (mem_fun (*this, &SoundFileBrowser::found_list_view_activated));
468 found_search_btn.signal_clicked().connect(mem_fun(*this, &SoundFileBrowser::found_search_clicked));
469 found_entry.signal_activate().connect(mem_fun(*this, &SoundFileBrowser::found_search_clicked));
471 notebook.append_page (*vbox, _("Search Tags"));
474 //add freesound search
475 #ifdef FREESOUND
477 VBox* vbox;
478 HBox* passbox;
479 Label* label;
481 passbox = manage(new HBox);
482 passbox->set_border_width (12);
483 passbox->set_spacing (6);
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);
499 Gtk::ScrolledWindow *scroll = manage(new ScrolledWindow);
500 scroll->add(freesound_list_view);
501 scroll->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
503 vbox = manage(new VBox);
504 vbox->pack_start (*passbox, PACK_SHRINK);
505 vbox->pack_start(*scroll);
507 //vbox->pack_start (freesound_list_view);
509 freesound_list_view.append_column(_("Paths"), freesound_list_columns.pathname);
511 freesound_list_view.get_selection()->signal_changed().connect(mem_fun(*this, &SoundFileBrowser::freesound_list_view_selected));
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));
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));
519 notebook.append_page (*vbox, _("Search Freesound"));
521 #endif
524 notebook.set_size_request (500, -1);
526 set_session (s);
528 add_button (Stock::CANCEL, RESPONSE_CANCEL);
529 add_button (Stock::APPLY, RESPONSE_APPLY);
530 add_button (Stock::OK, RESPONSE_OK);
534 SoundFileBrowser::~SoundFileBrowser ()
536 persistent_folder = chooser.get_current_folder();
540 void
541 SoundFileBrowser::on_show ()
543 ArdourDialog::on_show ();
544 start_metering ();
547 void
548 SoundFileBrowser::clear_selection ()
550 chooser.unselect_all ();
551 found_list_view.get_selection()->unselect_all ();
554 void
555 SoundFileBrowser::chooser_file_activated ()
557 preview.audition ();
560 void
561 SoundFileBrowser::found_list_view_activated (const TreeModel::Path& path, TreeViewColumn* col)
563 preview.audition ();
566 void
567 SoundFileBrowser::freesound_list_view_activated (const TreeModel::Path& path, TreeViewColumn* col)
569 preview.audition ();
572 void
573 SoundFileBrowser::set_session (Session* s)
575 ArdourDialog::set_session (s);
576 preview.set_session (s);
577 if (s) {
578 add_gain_meter ();
579 } else {
580 remove_gain_meter ();
584 void
585 SoundFileBrowser::add_gain_meter ()
587 if (gm) {
588 delete gm;
591 gm = new GainMeter (*session);
592 gm->set_io (session->the_auditioner());
594 meter_packer.set_border_width (12);
595 meter_packer.pack_start (*gm, false, true);
596 hpacker.pack_end (meter_packer, false, false);
597 meter_packer.show_all ();
598 start_metering ();
601 void
602 SoundFileBrowser::remove_gain_meter ()
604 if (gm) {
605 meter_packer.remove (*gm);
606 hpacker.remove (meter_packer);
607 delete gm;
608 gm = 0;
612 void
613 SoundFileBrowser::start_metering ()
615 metering_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (mem_fun(*this, &SoundFileBrowser::meter));
618 void
619 SoundFileBrowser::stop_metering ()
621 metering_connection.disconnect();
624 void
625 SoundFileBrowser::meter ()
627 if (is_mapped () && session && gm) {
628 gm->update_meters ();
632 bool
633 SoundFileBrowser::on_custom (const FileFilter::Info& filter_info)
635 return AudioFileSource::safe_file_extension (filter_info.filename);
638 void
639 SoundFileBrowser::update_preview ()
641 if (preview.setup_labels (chooser.get_filename())) {
642 if (preview.autoplay()) {
643 Glib::signal_idle().connect (mem_fun (preview, &SoundFileBox::audition_oneshot));
648 void
649 SoundFileBrowser::found_list_view_selected ()
651 if (!reset_options ()) {
652 set_response_sensitive (RESPONSE_OK, false);
653 } else {
654 ustring file;
656 TreeView::Selection::ListHandle_Path rows = found_list_view.get_selection()->get_selected_rows ();
658 if (!rows.empty()) {
659 TreeIter iter = found_list->get_iter(*rows.begin());
660 file = (*iter)[found_list_columns.pathname];
661 chooser.set_filename (file);
662 set_response_sensitive (RESPONSE_OK, true);
663 } else {
664 set_response_sensitive (RESPONSE_OK, false);
667 preview.setup_labels (file);
671 void
672 SoundFileBrowser::freesound_list_view_selected ()
674 if (!reset_options ()) {
675 set_response_sensitive (RESPONSE_OK, false);
676 } else {
677 ustring file;
679 TreeView::Selection::ListHandle_Path rows = freesound_list_view.get_selection()->get_selected_rows ();
681 if (!rows.empty()) {
682 TreeIter iter = freesound_list->get_iter(*rows.begin());
683 file = (*iter)[freesound_list_columns.pathname];
684 chooser.set_filename (file);
685 set_response_sensitive (RESPONSE_OK, true);
686 } else {
687 set_response_sensitive (RESPONSE_OK, false);
690 preview.setup_labels (file);
694 void
695 SoundFileBrowser::found_search_clicked ()
697 string tag_string = found_entry.get_text ();
699 vector<string> tags;
701 if (!PBD::tokenize (tag_string, string(","), std::back_inserter (tags), true)) {
702 warning << _("SoundFileBrowser: Could not tokenize string: ") << tag_string << endmsg;
703 return;
706 vector<string> results;
707 Library->search_members_and (results, tags);
709 found_list->clear();
710 for (vector<string>::iterator i = results.begin(); i != results.end(); ++i) {
711 TreeModel::iterator new_row = found_list->append();
712 TreeModel::Row row = *new_row;
713 string path = Glib::filename_from_uri (string ("file:") + *i);
714 row[found_list_columns.pathname] = path;
718 void*
719 freesound_search_thread_entry (void* arg)
721 PBD::notify_gui_about_thread_creation (pthread_self(), X_("Freesound Search"));
723 static_cast<SoundFileBrowser*>(arg)->freesound_search_thread ();
725 return 0;
728 bool searching = false;
729 bool canceling = false;
731 void
732 SoundFileBrowser::freesound_search_clicked ()
734 if (canceling) //already canceling, button does nothing
735 return;
737 if ( searching ) {
738 freesound_search_btn.set_label(_("Cancelling.."));
739 canceling = true;
740 } else {
741 searching = true;
742 freesound_search_btn.set_label(_("Cancel"));
743 pthread_t freesound_thr;
744 pthread_create_and_store ("freesound_search", &freesound_thr, 0, freesound_search_thread_entry, this);
748 void
749 SoundFileBrowser::freesound_search_thread()
751 #ifdef FREESOUND
752 freesound_list->clear();
754 string path;
755 path = Glib::get_home_dir();
756 path += "/Freesound/";
757 Mootcher theMootcher(path.c_str());
759 string name_string = freesound_name_entry.get_text ();
760 string pass_string = freesound_pass_entry.get_text ();
761 string search_string = freesound_entry.get_text ();
763 if ( theMootcher.doLogin( name_string, pass_string ) ) {
765 string theString = theMootcher.searchText(search_string);
767 XMLTree doc;
768 doc.read_buffer( theString );
769 XMLNode *root = doc.root();
771 if (root==NULL) return;
773 if ( strcmp(root->name().c_str(), "freesound") == 0) {
775 XMLNode *node = 0;
776 XMLNodeList children = root->children();
777 XMLNodeConstIterator niter;
778 for (niter = children.begin(); niter != children.end() && !canceling; ++niter) {
779 node = *niter;
780 if( strcmp( node->name().c_str(), "sample") == 0 ){
781 XMLProperty *prop=node->property ("id");
782 string filename = theMootcher.getFile( prop->value().c_str() );
783 if ( filename != "" ) {
784 TreeModel::iterator new_row = freesound_list->append();
785 TreeModel::Row row = *new_row;
786 string path = Glib::filename_from_uri (string ("file:") + filename);
787 row[freesound_list_columns.pathname] = path;
794 searching = false;
795 canceling = false;
796 freesound_search_btn.set_label(_("Start Downloading"));
797 #endif
800 vector<ustring>
801 SoundFileBrowser::get_paths ()
803 vector<ustring> results;
805 int n = notebook.get_current_page ();
807 if (n == 0) {
808 vector<ustring> filenames = chooser.get_filenames();
809 vector<ustring>::iterator i;
811 for (i = filenames.begin(); i != filenames.end(); ++i) {
812 struct stat buf;
813 if ((!stat((*i).c_str(), &buf)) && S_ISREG(buf.st_mode)) {
814 results.push_back (*i);
818 } else if (n==1){
820 typedef TreeView::Selection::ListHandle_Path ListPath;
822 ListPath rows = found_list_view.get_selection()->get_selected_rows ();
823 for (ListPath::iterator i = rows.begin() ; i != rows.end(); ++i) {
824 TreeIter iter = found_list->get_iter(*i);
825 ustring str = (*iter)[found_list_columns.pathname];
827 results.push_back (str);
829 } else {
831 typedef TreeView::Selection::ListHandle_Path ListPath;
833 ListPath rows = freesound_list_view.get_selection()->get_selected_rows ();
834 for (ListPath::iterator i = rows.begin() ; i != rows.end(); ++i) {
835 TreeIter iter = freesound_list->get_iter(*i);
836 ustring str = (*iter)[freesound_list_columns.pathname];
838 results.push_back (str);
842 return results;
845 void
846 SoundFileOmega::reset_options_noret ()
848 if (!resetting_ourselves) {
849 (void) reset_options ();
853 bool
854 SoundFileOmega::reset_options ()
856 vector<ustring> paths = get_paths ();
858 if (paths.empty()) {
860 channel_combo.set_sensitive (false);
861 action_combo.set_sensitive (false);
862 where_combo.set_sensitive (false);
863 copy_files_btn.set_sensitive (false);
865 return false;
867 } else {
869 channel_combo.set_sensitive (true);
870 action_combo.set_sensitive (true);
871 where_combo.set_sensitive (true);
873 /* if we get through this function successfully, this may be
874 reset at the end, once we know if we can use hard links
875 to do embedding
878 if (Config->get_only_copy_imported_files()) {
879 copy_files_btn.set_sensitive (false);
880 } else {
881 copy_files_btn.set_sensitive (false);
885 bool same_size;
886 bool src_needed;
887 bool selection_includes_multichannel;
888 bool selection_can_be_embedded_with_links = check_link_status (*session, paths);
889 ImportMode mode;
891 if (check_info (paths, same_size, src_needed, selection_includes_multichannel)) {
892 Glib::signal_idle().connect (mem_fun (*this, &SoundFileOmega::bad_file_message));
893 return false;
896 ustring existing_choice;
897 vector<string> action_strings;
899 if (selected_track_cnt > 0) {
900 if (channel_combo.get_active_text().length()) {
901 ImportDisposition id = get_channel_disposition();
903 switch (id) {
904 case Editing::ImportDistinctFiles:
905 if (selected_track_cnt == paths.size()) {
906 action_strings.push_back (importmode2string (ImportToTrack));
908 break;
910 case Editing::ImportDistinctChannels:
911 /* XXX it would be nice to allow channel-per-selected track
912 but its too hard we don't want to deal with all the
913 different per-file + per-track channel configurations.
915 break;
917 default:
918 action_strings.push_back (importmode2string (ImportToTrack));
919 break;
924 action_strings.push_back (importmode2string (ImportAsTrack));
925 action_strings.push_back (importmode2string (ImportAsRegion));
926 action_strings.push_back (importmode2string (ImportAsTapeTrack));
928 resetting_ourselves = true;
930 existing_choice = action_combo.get_active_text();
932 set_popdown_strings (action_combo, action_strings);
934 /* preserve any existing choice, if possible */
937 if (existing_choice.length()) {
938 vector<string>::iterator x;
939 for (x = action_strings.begin(); x != action_strings.end(); ++x) {
940 if (*x == existing_choice) {
941 action_combo.set_active_text (existing_choice);
942 break;
945 if (x == action_strings.end()) {
946 action_combo.set_active_text (action_strings.front());
948 } else {
949 action_combo.set_active_text (action_strings.front());
952 resetting_ourselves = false;
954 if ((mode = get_mode()) == ImportAsRegion) {
955 where_combo.set_sensitive (false);
956 } else {
957 where_combo.set_sensitive (true);
960 vector<string> channel_strings;
962 if (mode == ImportAsTrack || mode == ImportAsTapeTrack || mode == ImportToTrack) {
963 channel_strings.push_back (_("one track per file"));
965 if (selection_includes_multichannel) {
966 channel_strings.push_back (_("one track per channel"));
969 if (paths.size() > 1) {
970 /* tape tracks are a single region per track, so we cannot
971 sequence multiple files.
973 if (mode != ImportAsTapeTrack) {
974 channel_strings.push_back (_("sequence files"));
976 if (same_size) {
977 channel_strings.push_back (_("all files in one track"));
982 } else {
983 channel_strings.push_back (_("one region per file"));
985 if (selection_includes_multichannel) {
986 channel_strings.push_back (_("one region per channel"));
989 if (paths.size() > 1) {
990 if (same_size) {
991 channel_strings.push_back (_("all files in one region"));
996 existing_choice = channel_combo.get_active_text();
998 set_popdown_strings (channel_combo, channel_strings);
1000 /* preserve any existing choice, if possible */
1002 if (existing_choice.length()) {
1003 vector<string>::iterator x;
1004 for (x = channel_strings.begin(); x != channel_strings.end(); ++x) {
1005 if (*x == existing_choice) {
1006 channel_combo.set_active_text (existing_choice);
1007 break;
1010 if (x == channel_strings.end()) {
1011 channel_combo.set_active_text (channel_strings.front());
1013 } else {
1014 channel_combo.set_active_text (channel_strings.front());
1017 if (src_needed) {
1018 src_combo.set_sensitive (true);
1019 } else {
1020 src_combo.set_sensitive (false);
1023 if (Config->get_only_copy_imported_files()) {
1025 if (selection_can_be_embedded_with_links) {
1026 copy_files_btn.set_sensitive (true);
1027 } else {
1028 copy_files_btn.set_sensitive (false);
1031 } else {
1033 copy_files_btn.set_sensitive (true);
1036 return true;
1040 bool
1041 SoundFileOmega::bad_file_message()
1043 MessageDialog msg (*this,
1044 _("One or more of the selected files\ncannot be used by Ardour"),
1045 true,
1046 Gtk::MESSAGE_INFO,
1047 Gtk::BUTTONS_OK);
1048 msg.run ();
1049 resetting_ourselves = true;
1050 chooser.unselect_uri (chooser.get_preview_uri());
1051 resetting_ourselves = false;
1053 return false;
1056 bool
1057 SoundFileOmega::check_info (const vector<ustring>& paths, bool& same_size, bool& src_needed, bool& multichannel)
1059 SoundFileInfo info;
1060 nframes64_t sz = 0;
1061 bool err = false;
1062 string errmsg;
1064 same_size = true;
1065 src_needed = false;
1066 multichannel = false;
1068 for (vector<ustring>::const_iterator i = paths.begin(); i != paths.end(); ++i) {
1070 if (!AudioFileSource::get_soundfile_info (*i, info, errmsg)) {
1071 err = true;
1074 if (info.channels > 1) {
1075 multichannel = true;
1078 if (sz == 0) {
1079 sz = info.length;
1080 } else {
1081 if (sz != info.length) {
1082 same_size = false;
1086 if ((nframes_t) info.samplerate != session->frame_rate()) {
1087 src_needed = true;
1091 return err;
1095 bool
1096 SoundFileOmega::check_link_status (const Session& s, const vector<ustring>& paths)
1098 string tmpdir = s.sound_dir();
1099 bool ret = false;
1101 tmpdir += "/linktest";
1103 if (mkdir (tmpdir.c_str(), 0744)) {
1104 if (errno != EEXIST) {
1105 return false;
1109 for (vector<ustring>::const_iterator i = paths.begin(); i != paths.end(); ++i) {
1111 char tmpc[MAXPATHLEN+1];
1113 snprintf (tmpc, sizeof(tmpc), "%s/%s", tmpdir.c_str(), Glib::path_get_basename (*i).c_str());
1115 /* can we link ? */
1117 if (link ((*i).c_str(), tmpc)) {
1118 goto out;
1121 unlink (tmpc);
1124 ret = true;
1126 out:
1127 rmdir (tmpdir.c_str());
1128 return ret;
1131 SoundFileChooser::SoundFileChooser (Gtk::Window& parent, string title, ARDOUR::Session* s)
1132 : SoundFileBrowser (parent, title, s, false)
1134 chooser.set_select_multiple (false);
1135 found_list_view.get_selection()->set_mode (SELECTION_SINGLE);
1136 freesound_list_view.get_selection()->set_mode (SELECTION_SINGLE);
1139 void
1140 SoundFileChooser::on_hide ()
1142 ArdourDialog::on_hide();
1143 stop_metering ();
1145 if (session) {
1146 session->cancel_audition();
1150 ustring
1151 SoundFileChooser::get_filename ()
1153 vector<ustring> paths;
1155 paths = get_paths ();
1157 if (paths.empty()) {
1158 return ustring ();
1161 if (!Glib::file_test (paths.front(), Glib::FILE_TEST_EXISTS|Glib::FILE_TEST_IS_REGULAR)) {
1162 return ustring();
1165 return paths.front();
1168 SoundFileOmega::SoundFileOmega (Gtk::Window& parent, string title, ARDOUR::Session* s, int selected_tracks, bool persistent,
1169 Editing::ImportMode mode_hint)
1170 : SoundFileBrowser (parent, title, s, persistent),
1171 copy_files_btn ( _("Copy files to session")),
1172 selected_track_cnt (selected_tracks)
1174 VBox* vbox;
1175 HBox* hbox;
1176 vector<string> str;
1178 set_size_request (-1, 450);
1180 block_two.set_border_width (12);
1181 block_three.set_border_width (12);
1182 block_four.set_border_width (12);
1184 options.set_spacing (12);
1186 str.clear ();
1187 str.push_back (_("use file timestamp"));
1188 str.push_back (_("at edit point"));
1189 str.push_back (_("at playhead"));
1190 str.push_back (_("at session start"));
1191 set_popdown_strings (where_combo, str);
1192 where_combo.set_active_text (str.front());
1194 Label* l = manage (new Label);
1195 l->set_text (_("Add files:"));
1197 hbox = manage (new HBox);
1198 hbox->set_border_width (12);
1199 hbox->set_spacing (6);
1200 hbox->pack_start (*l, false, false);
1201 hbox->pack_start (action_combo, false, false);
1202 vbox = manage (new VBox);
1203 vbox->pack_start (*hbox, false, false);
1204 options.pack_start (*vbox, false, false);
1206 /* dummy entry for action combo so that it doesn't look odd if we
1207 come up with no tracks selected.
1210 str.clear ();
1211 str.push_back (importmode2string (mode_hint));
1212 set_popdown_strings (action_combo, str);
1213 action_combo.set_active_text (str.front());
1214 action_combo.set_sensitive (false);
1216 l = manage (new Label);
1217 l->set_text (_("Insert:"));
1219 hbox = manage (new HBox);
1220 hbox->set_border_width (12);
1221 hbox->set_spacing (6);
1222 hbox->pack_start (*l, false, false);
1223 hbox->pack_start (where_combo, false, false);
1224 vbox = manage (new VBox);
1225 vbox->pack_start (*hbox, false, false);
1226 options.pack_start (*vbox, false, false);
1229 l = manage (new Label);
1230 l->set_text (_("Mapping:"));
1232 hbox = manage (new HBox);
1233 hbox->set_border_width (12);
1234 hbox->set_spacing (6);
1235 hbox->pack_start (*l, false, false);
1236 hbox->pack_start (channel_combo, false, false);
1237 vbox = manage (new VBox);
1238 vbox->pack_start (*hbox, false, false);
1239 options.pack_start (*vbox, false, false);
1241 str.clear ();
1242 str.push_back (_("one track per file"));
1243 set_popdown_strings (channel_combo, str);
1244 channel_combo.set_active_text (str.front());
1245 channel_combo.set_sensitive (false);
1247 l = manage (new Label);
1248 l->set_text (_("Conversion Quality:"));
1250 hbox = manage (new HBox);
1251 hbox->set_border_width (12);
1252 hbox->set_spacing (6);
1253 hbox->pack_start (*l, false, false);
1254 hbox->pack_start (src_combo, false, false);
1255 vbox = manage (new VBox);
1256 vbox->pack_start (*hbox, false, false);
1257 options.pack_start (*vbox, false, false);
1259 str.clear ();
1260 str.push_back (_("Best"));
1261 str.push_back (_("Good"));
1262 str.push_back (_("Quick"));
1263 str.push_back (_("Fast"));
1264 str.push_back (_("Fastest"));
1266 set_popdown_strings (src_combo, str);
1267 src_combo.set_active_text (str.front());
1268 src_combo.set_sensitive (false);
1270 reset_options ();
1272 action_combo.signal_changed().connect (mem_fun (*this, &SoundFileOmega::reset_options_noret));
1274 copy_files_btn.set_active (true);
1276 block_four.pack_start (copy_files_btn, false, false);
1278 options.pack_start (block_four, false, false);
1280 get_vbox()->pack_start (options, false, false);
1282 /* setup disposition map */
1284 disposition_map.insert (pair<ustring,ImportDisposition>(_("one track per file"), ImportDistinctFiles));
1285 disposition_map.insert (pair<ustring,ImportDisposition>(_("one track per channel"), ImportDistinctChannels));
1286 disposition_map.insert (pair<ustring,ImportDisposition>(_("merge files"), ImportMergeFiles));
1287 disposition_map.insert (pair<ustring,ImportDisposition>(_("sequence files"), ImportSerializeFiles));
1289 disposition_map.insert (pair<ustring,ImportDisposition>(_("one region per file"), ImportDistinctFiles));
1290 disposition_map.insert (pair<ustring,ImportDisposition>(_("one region per channel"), ImportDistinctChannels));
1291 disposition_map.insert (pair<ustring,ImportDisposition>(_("all files in one region"), ImportMergeFiles));
1293 chooser.signal_selection_changed().connect (mem_fun (*this, &SoundFileOmega::file_selection_changed));
1295 /* set size requests for a couple of combos to allow them to display the longest text
1296 they will ever be asked to display. This prevents them being resized when the user
1297 selects a file to import, which in turn prevents the size of the dialog from jumping
1298 around. */
1300 vector<string> t;
1301 t.push_back (_("one track per file"));
1302 t.push_back (_("one track per channel"));
1303 t.push_back (_("sequence files"));
1304 t.push_back (_("all files in one region"));
1305 set_size_request_to_display_given_text (channel_combo, t, COMBO_FUDGE + 10, 15);
1307 t.clear ();
1308 t.push_back (importmode2string (ImportAsTrack));
1309 t.push_back (importmode2string (ImportToTrack));
1310 t.push_back (importmode2string (ImportAsRegion));
1311 t.push_back (importmode2string (ImportAsTapeTrack));
1312 set_size_request_to_display_given_text (action_combo, t, COMBO_FUDGE + 10, 15);
1315 void
1316 SoundFileOmega::set_mode (ImportMode mode)
1318 action_combo.set_active_text (importmode2string (mode));
1321 ImportMode
1322 SoundFileOmega::get_mode () const
1324 return string2importmode (action_combo.get_active_text());
1327 void
1328 SoundFileOmega::on_hide ()
1330 ArdourDialog::on_hide();
1331 if (session) {
1332 session->cancel_audition();
1336 ImportPosition
1337 SoundFileOmega::get_position() const
1339 ustring str = where_combo.get_active_text();
1341 if (str == _("use file timestamp")) {
1342 return ImportAtTimestamp;
1343 } else if (str == _("at edit point")) {
1344 return ImportAtEditPoint;
1345 } else if (str == _("at playhead")) {
1346 return ImportAtPlayhead;
1347 } else {
1348 return ImportAtStart;
1352 SrcQuality
1353 SoundFileOmega::get_src_quality() const
1355 ustring str = where_combo.get_active_text();
1357 if (str == _("Best")) {
1358 return SrcBest;
1359 } else if (str == _("Good")) {
1360 return SrcGood;
1361 } else if (str == _("Quick")) {
1362 return SrcQuick;
1363 } else if (str == _("Fast")) {
1364 return SrcFast;
1365 } else {
1366 return SrcFastest;
1370 ImportDisposition
1371 SoundFileOmega::get_channel_disposition () const
1373 /* we use a map here because the channel combo can contain different strings
1374 depending on the state of the other combos. the map contains all possible strings
1375 and the ImportDisposition enum that corresponds to it.
1378 ustring str = channel_combo.get_active_text();
1379 DispositionMap::const_iterator x = disposition_map.find (str);
1381 if (x == disposition_map.end()) {
1382 fatal << string_compose (_("programming error: %1 (%2)"), "unknown string for import disposition", str) << endmsg;
1383 /*NOTREACHED*/
1386 return x->second;
1389 void
1390 SoundFileOmega::reset (int selected_tracks)
1392 selected_track_cnt = selected_tracks;
1393 reset_options ();
1396 void
1397 SoundFileOmega::file_selection_changed ()
1399 if (resetting_ourselves) {
1400 return;
1403 if (!reset_options ()) {
1404 set_response_sensitive (RESPONSE_OK, false);
1405 } else {
1406 if (chooser.get_filenames().size() > 0) {
1407 set_response_sensitive (RESPONSE_OK, true);
1408 } else {
1409 set_response_sensitive (RESPONSE_OK, false);