From 5c6ba165f684fbd45be33c83d41083567d4dd88f Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Tue, 9 Nov 2010 06:03:51 +0000 Subject: initial pass at a missing file dialog and "relocatable" source files. lots more to do here git-svn-id: svn://localhost/ardour2/branches/3.0@7983 d708f5d6-7413-0410-9779-e7cbd77b26cf --- gtk2_ardour/ardour_ui.cc | 28 ++++ gtk2_ardour/ardour_ui.h | 2 + gtk2_ardour/missing_file_dialog.cc | 166 ++++++++++++++++++++++++ gtk2_ardour/missing_file_dialog.h | 39 ++++++ gtk2_ardour/option_editor.h | 2 +- gtk2_ardour/search_path_option.cc | 156 ++++++++++++++++++++++ gtk2_ardour/search_path_option.h | 50 +++++++ gtk2_ardour/session_option_editor.cc | 30 ++++- gtk2_ardour/wscript | 2 + libs/ardour/ardour/file_source.h | 28 ++-- libs/ardour/ardour/session.h | 15 +++ libs/ardour/ardour/session_configuration_vars.h | 2 + libs/ardour/file_source.cc | 116 +++++++++++++++-- libs/ardour/session.cc | 57 ++++++++ libs/ardour/session_state.cc | 45 ++++++- libs/ardour/source_factory.cc | 1 - 16 files changed, 701 insertions(+), 38 deletions(-) create mode 100644 gtk2_ardour/missing_file_dialog.cc create mode 100644 gtk2_ardour/missing_file_dialog.h create mode 100644 gtk2_ardour/search_path_option.cc create mode 100644 gtk2_ardour/search_path_option.h diff --git a/gtk2_ardour/ardour_ui.cc b/gtk2_ardour/ardour_ui.cc index 3c0e5b91fa..a14018e014 100644 --- a/gtk2_ardour/ardour_ui.cc +++ b/gtk2_ardour/ardour_ui.cc @@ -102,6 +102,7 @@ typedef uint64_t microseconds_t; #include "window_proxy.h" #include "global_port_matrix.h" #include "location_ui.h" +#include "missing_file_dialog.h" #include "i18n.h" @@ -264,6 +265,10 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[]) ARDOUR::Session::Quit.connect (forever_connections, MISSING_INVALIDATOR, ui_bind (&ARDOUR_UI::finish, this), gui_context ()); + /* handle requests to deal with missing files */ + + ARDOUR::Session::MissingFile.connect_same_thread (forever_connections, boost::bind (&ARDOUR_UI::missing_file, this, _1, _2, _3)); + /* lets get this party started */ try { @@ -3689,3 +3694,26 @@ ARDOUR_UI::remove_window_proxy (WindowProxyBase* p) { _window_proxies.remove (p); } + +int +ARDOUR_UI::missing_file (Session*s, std::string str, DataType type) +{ + MissingFileDialog dialog (s, str, type); + + dialog.show (); + dialog.present (); + + int result = dialog.run (); + dialog.hide (); + + switch (result) { + case RESPONSE_OK: + break; + default: + return 1; // quit entire session load + } + + result = dialog.get_action (); + + return result; +} diff --git a/gtk2_ardour/ardour_ui.h b/gtk2_ardour/ardour_ui.h index 3f947b2af8..3f4ed3eca5 100644 --- a/gtk2_ardour/ardour_ui.h +++ b/gtk2_ardour/ardour_ui.h @@ -716,6 +716,8 @@ class ARDOUR_UI : public Gtkmm2ext::UI, public ARDOUR::SessionHandlePtr void queue_finish (); std::list _window_proxies; + + int missing_file (ARDOUR::Session*s, std::string str, ARDOUR::DataType type); }; #endif /* __ardour_gui_h__ */ diff --git a/gtk2_ardour/missing_file_dialog.cc b/gtk2_ardour/missing_file_dialog.cc new file mode 100644 index 0000000000..3054adf25c --- /dev/null +++ b/gtk2_ardour/missing_file_dialog.cc @@ -0,0 +1,166 @@ +/* + Copyright (C) 2010 Paul Davis + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "pbd/compose.h" +#include "pbd/replace_all.h" +#include "pbd/strsplit.h" + +#include "ardour/session.h" + +#include "missing_file_dialog.h" + +using namespace Gtk; +using namespace std; +using namespace ARDOUR; +using namespace PBD; + +MissingFileDialog::MissingFileDialog (Session* s, const std::string& path, DataType type) + : ArdourDialog (_("Missing File!"), true, false) + , filetype (type) + , chooser (FILE_CHOOSER_ACTION_SELECT_FOLDER) + , use_chosen (_("Add chosen folder to search path, and try again")) + , choice_group (use_chosen.get_group()) + , use_chosen_and_no_more_questions (choice_group, _("Add chosen folder to search path, try again but don't ask me again"), false) + , stop_loading_button (choice_group, _("Stop loading this session"), false) + , all_missing_ok (choice_group, _("This and all other missing files are OK"), false) + , this_missing_ok (choice_group, _("This missing file is OK"), false) +{ + set_session (s); + + add_button (_("Done"), RESPONSE_OK); + + string typestr; + + switch (type) { + case DataType::AUDIO: + typestr = _("An audio"); + break; + case DataType::MIDI: + typestr = _("A MIDI"); + break; + } + + string dirstr; + + dirstr = s->source_search_path (type); + replace_all (dirstr, ":", "\n"); + + msg.set_markup (string_compose (_("%1 file (\"%2\") cannot be found.\n\n\ +Currently, Ardour has searched in the following folders for this file:\n\n\ +%3\n\n\ +The following options are available:"), typestr, path, dirstr)); + + VBox* button_packer_box = manage (new VBox); + + button_packer_box->set_spacing (6); + button_packer_box->set_border_width (12); + + button_packer_box->pack_start (use_chosen, false, false); + button_packer_box->pack_start (use_chosen_and_no_more_questions, false, false); + button_packer_box->pack_start (this_missing_ok, false, false); + button_packer_box->pack_start (all_missing_ok, false, false); + button_packer_box->pack_start (stop_loading_button, false, false); + + button_packer_box->show_all (); + + get_vbox()->set_spacing (6); + get_vbox()->set_border_width (12); + get_vbox()->set_homogeneous (false); + + get_vbox()->pack_start (msg, false, false); + + HBox* hbox = manage (new HBox); + hbox->pack_start (*button_packer_box, false, true); + hbox->show (); + + get_vbox()->pack_start (*hbox, false, false); + get_vbox()->pack_start (chooser, true, true); + + msg.show (); + chooser.set_size_request (-1, 300); + chooser.show (); + chooser.set_current_folder (Glib::get_home_dir()); + chooser.set_create_folders (false); +} + +void +MissingFileDialog::add_chosen () +{ + string str; + string newdir; + vector dirs; + + switch (filetype) { + case DataType::AUDIO: + str = _session->config.get_audio_search_path(); + break; + case DataType::MIDI: + str = _session->config.get_midi_search_path(); + break; + } + + split (str, dirs, ':'); + + newdir = chooser.get_filename (); + + for (vector::iterator d = dirs.begin(); d != dirs.end(); d++) { + if (*d == newdir) { + return; + } + } + + if (!str.empty()) { + str += ':'; + } + + str += newdir; + + switch (filetype) { + case DataType::AUDIO: + _session->config.set_audio_search_path (str); + break; + case DataType::MIDI: + _session->config.set_midi_search_path (str); + break; + } +} + +int +MissingFileDialog::get_action () +{ + + if (use_chosen.get_active ()) { + add_chosen (); + return 0; + } + + if (use_chosen_and_no_more_questions.get_active()) { + add_chosen (); + return 2; + } + + if (this_missing_ok.get_active()) { + return -1; + } + + if (all_missing_ok.get_active ()) { + return 3; + } + + return 1; +} diff --git a/gtk2_ardour/missing_file_dialog.h b/gtk2_ardour/missing_file_dialog.h new file mode 100644 index 0000000000..dce720fcdb --- /dev/null +++ b/gtk2_ardour/missing_file_dialog.h @@ -0,0 +1,39 @@ +#ifndef __gtk_ardour_missing_file_dialog_h__ +#define __gtk_ardour_missing_file_dialog_h__ + +#include +#include +#include +#include + +#include "ardour/types.h" + +#include "ardour_dialog.h" + +namespace ARDOUR { + class Session; +} + +class MissingFileDialog : public ArdourDialog +{ + public: + MissingFileDialog (ARDOUR::Session*, const std::string& path, ARDOUR::DataType type); + + int get_action(); + + private: + ARDOUR::DataType filetype; + + Gtk::FileChooserWidget chooser; + Gtk::RadioButton use_chosen; + Gtk::RadioButton::Group choice_group; + Gtk::RadioButton use_chosen_and_no_more_questions; + Gtk::RadioButton stop_loading_button; + Gtk::RadioButton all_missing_ok; + Gtk::RadioButton this_missing_ok; + Gtk::Label msg; + + void add_chosen (); +}; + +#endif /* __gtk_ardour_missing_file_dialog_h__ */ diff --git a/gtk2_ardour/option_editor.h b/gtk2_ardour/option_editor.h index f76ba2df93..0258f955aa 100644 --- a/gtk2_ardour/option_editor.h +++ b/gtk2_ardour/option_editor.h @@ -136,7 +136,7 @@ public: return _id; } -private: +protected: std::string _id; std::string _name; diff --git a/gtk2_ardour/search_path_option.cc b/gtk2_ardour/search_path_option.cc new file mode 100644 index 0000000000..be01b9b1aa --- /dev/null +++ b/gtk2_ardour/search_path_option.cc @@ -0,0 +1,156 @@ +/* + Copyright (C) 2010 Paul Davis + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "pbd/strsplit.h" +#include "search_path_option.h" + +using namespace std; +using namespace Gtk; + +SearchPathOption::SearchPathOption (const string& pathname, const string& label, + sigc::slot get, sigc::slot set) + : Option (pathname, label) + , _get (get) + , _set (set) + , add_chooser (_("Select folder to search for media"), FILE_CHOOSER_ACTION_SELECT_FOLDER) +{ + add_chooser.signal_file_set().connect (sigc::mem_fun (*this, &SearchPathOption::path_chosen)); + + HBox* hbox = manage (new HBox); + + hbox->set_border_width (12); + hbox->set_spacing (6); + hbox->pack_end (add_chooser, false, false); + hbox->pack_end (*manage (new Label ("Click to add a new location")), false, false); + hbox->show_all (); + + vbox.pack_start (path_box); + vbox.pack_end (*hbox); + + session_label.set_use_markup (true); + session_label.set_markup (string_compose ("%1", _("the session folder"))); + session_label.set_alignment (0.0, 0.5); + session_label.show (); +} + +SearchPathOption::~SearchPathOption() +{ + + +} + +void +SearchPathOption::path_chosen () +{ + string path = add_chooser.get_filename (); + add_path (path); +} + +void +SearchPathOption::add_to_page (OptionEditorPage* p) +{ + int const n = p->table.property_n_rows(); + p->table.resize (n + 2, 3); + + Label* label = manage (new Label); + label->set_alignment (0.0, 0.5); + label->set_markup (string_compose ("%1", _name)); + + p->table.attach (*label, 0, 1, n, n + 1, FILL | EXPAND); + p->table.attach (vbox, 0, 3, n + 1, n + 2, FILL | EXPAND); +} + +void +SearchPathOption::clear () +{ + path_box.remove (session_label); + for (list::iterator p = paths.begin(); p != paths.end(); ++p) { + path_box.remove ((*p)->box); + delete *p; + } + paths.clear (); +} + +void +SearchPathOption::set_state_from_config () +{ + string str = _get (); + vector dirs; + + clear (); + path_box.pack_start (session_label); + + split (str, dirs, ':'); + + for (vector::iterator d = dirs.begin(); d != dirs.end(); ++d) { + add_path (*d); + } +} + +void +SearchPathOption::changed () +{ + string str; + + for (list::iterator p = paths.begin(); p != paths.end(); ++p) { + + if (p == paths.begin()) { + /* skip first entry, its always "the session" + */ + continue; + } + + if (!str.empty()) { + str += ':'; + } + str += (*p)->entry.get_text (); + } + + _set (str); +} + +void +SearchPathOption::add_path (const string& path, bool removable) +{ + PathEntry* pe = new PathEntry (path, removable); + paths.push_back (pe); + path_box.pack_start (pe->box, false, false); +} + +void +SearchPathOption::remove_path (const string& path) +{ +} + +SearchPathOption::PathEntry::PathEntry (const std::string& path, bool removable) + : remove_button (Stock::REMOVE) +{ + entry.set_text (path); + entry.show (); + + box.set_spacing (6); + box.set_homogeneous (false); + box.pack_start (entry, true, true); + + if (removable) { + box.pack_start (remove_button, false, false); + remove_button.show (); + } + + box.show (); +} diff --git a/gtk2_ardour/search_path_option.h b/gtk2_ardour/search_path_option.h new file mode 100644 index 0000000000..7163924b8c --- /dev/null +++ b/gtk2_ardour/search_path_option.h @@ -0,0 +1,50 @@ +#ifndef __gtk_ardour_search_path_option_h__ +#define __gtk_ardour_search_path_option_h__ + +#include + +#include +#include +#include +#include + +#include "option_editor.h" + +class SearchPathOption : public Option +{ + public: + SearchPathOption (const std::string& pathname, const std::string& label, + sigc::slot, sigc::slot); + ~SearchPathOption (); + + void set_state_from_config (); + void add_to_page (OptionEditorPage*); + void clear (); + + protected: + sigc::slot _get; ///< slot to get the configuration variable's value + sigc::slot _set; ///< slot to set the configuration variable's value + + struct PathEntry { + PathEntry (const std::string& path, bool removable=true); + + Gtk::Entry entry; + Gtk::Button remove_button; + Gtk::HBox box; + + std::string path; + }; + + std::list paths; + Gtk::FileChooserButton add_chooser; + Gtk::VBox vbox; + Gtk::VBox path_box; + Gtk::Label session_label; + + void add_path (const std::string& path, bool removable=true); + void remove_path (const std::string& path); + void changed (); + void path_chosen (); +}; + +#endif /* __gtk_ardour_search_path_option_h__ */ diff --git a/gtk2_ardour/session_option_editor.cc b/gtk2_ardour/session_option_editor.cc index 0715c105fe..ec7ee86c8d 100644 --- a/gtk2_ardour/session_option_editor.cc +++ b/gtk2_ardour/session_option_editor.cc @@ -25,6 +25,7 @@ #include "gui_thread.h" #include "session_option_editor.h" +#include "search_path_option.h" #include "i18n.h" using namespace std; @@ -177,9 +178,9 @@ SessionOptionEditor::SessionOptionEditor (Session* s) sigc::mem_fun (*_session_config, &SessionConfiguration::set_show_region_fades) )); - /* MISC */ + /* Media */ - add_option (_("Misc"), new OptionEditorHeading (_("Audio file format"))); + add_option (_("Media"), new OptionEditorHeading (_("Audio file format"))); ComboOption* sf = new ComboOption ( "native-file-data-format", @@ -192,7 +193,7 @@ SessionOptionEditor::SessionOptionEditor (Session* s) sf->add (FormatInt24, _("24-bit integer")); sf->add (FormatInt16, _("16-bit integer")); - add_option (_("Misc"), sf); + add_option (_("Media"), sf); ComboOption* hf = new ComboOption ( "native-file-header-format", @@ -206,13 +207,28 @@ SessionOptionEditor::SessionOptionEditor (Session* s) hf->add (WAVE64, _("WAVE-64")); hf->add (CAF, _("CAF")); - add_option (_("Misc"), hf); + add_option (_("Media"), hf); + + add_option (_("Media"), new OptionEditorHeading (_("Media Locations"))); - add_option (_("Misc"), new OptionEditorHeading (_("Layering"))); + SearchPathOption* spo = new SearchPathOption ("audio-search-path", _("Search for audio files in:"), + sigc::mem_fun (*_session_config, &SessionConfiguration::get_audio_search_path), + sigc::mem_fun (*_session_config, &SessionConfiguration::set_audio_search_path)); + add_option (_("Media"), spo); + + spo = new SearchPathOption ("midi-search-path", _("Search for MIDI files in:"), + sigc::mem_fun (*_session_config, &SessionConfiguration::get_midi_search_path), + sigc::mem_fun (*_session_config, &SessionConfiguration::set_midi_search_path)); + + add_option (_("Media"), spo); + + /* Misc */ + + add_option (_("Misc"), new OptionEditorHeading (_("Layering (in overlaid mode)"))); ComboOption* lm = new ComboOption ( "layer-model", - _("Layering model in overlaid mode"), + _("Layering model"), sigc::mem_fun (*_session_config, &SessionConfiguration::get_layer_model), sigc::mem_fun (*_session_config, &SessionConfiguration::set_layer_model) ); @@ -227,7 +243,7 @@ SessionOptionEditor::SessionOptionEditor (Session* s) ComboOption* li = new ComboOption ( "insert-merge-policy", - _("Policy for handling same note and channel overlaps"), + _("Policy for handling same note\nand channel overlaps"), sigc::mem_fun (*_session_config, &SessionConfiguration::get_insert_merge_policy), sigc::mem_fun (*_session_config, &SessionConfiguration::set_insert_merge_policy) ); diff --git a/gtk2_ardour/wscript b/gtk2_ardour/wscript index e72d022829..7d3f74f024 100644 --- a/gtk2_ardour/wscript +++ b/gtk2_ardour/wscript @@ -141,6 +141,7 @@ gtk2_ardour_sources = [ 'midi_streamview.cc', 'midi_time_axis.cc', 'midi_tracer.cc', + 'missing_file_dialog.cc', 'mixer_group_tabs.cc', 'mixer_strip.cc', 'mixer_ui.cc', @@ -186,6 +187,7 @@ gtk2_ardour_sources = [ 'route_processor_selection.cc', 'route_time_axis.cc', 'route_ui.cc', + 'search_path_option.cc', 'selection.cc', 'send_ui.cc', 'session_import_dialog.cc', diff --git a/libs/ardour/ardour/file_source.h b/libs/ardour/ardour/file_source.h index aa4fe973b1..78210916f0 100644 --- a/libs/ardour/ardour/file_source.h +++ b/libs/ardour/ardour/file_source.h @@ -20,15 +20,25 @@ #ifndef __ardour_filesource_h__ #define __ardour_filesource_h__ +#include +#include #include #include #include "ardour/source.h" namespace ARDOUR { -class MissingSource : public std::exception { -public: +class MissingSource : public std::exception +{ + public: + MissingSource (const std::string& p, DataType t) throw () + : path (p), type (t) {} + ~MissingSource() throw() {} + virtual const char *what() const throw() { return "source file does not exist"; } + + std::string path; + DataType type; }; /** A source associated with a file on disk somewhere */ @@ -54,15 +64,19 @@ public: int set_source_name (const std::string& newname, bool destructive); - static void set_search_path (DataType type, const std::string& path); + static bool find (Session&, DataType type, const std::string& path, + bool must_exist, bool& is_new, uint16_t& chan, + std::string& found_path); - static bool find (DataType type, const std::string& path, - bool must_exist, bool& is_new, uint16_t& chan, - std::string& found_path); + static bool find_2X (Session&, DataType type, const std::string& path, + bool must_exist, bool& is_new, uint16_t& chan, + std::string& found_path); void inc_use_count (); bool removable () const; + static PBD::Signal3 > AmbiguousFileName; + protected: FileSource (Session& session, DataType type, const std::string& path, @@ -81,8 +95,6 @@ protected: bool _file_is_new; uint16_t _channel; bool _within_session; - - static std::map search_paths; }; } // namespace ARDOUR diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h index 7c7ff820e9..d0fe89ee52 100644 --- a/libs/ardour/ardour/session.h +++ b/libs/ardour/ardour/session.h @@ -785,6 +785,19 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi void request_resume_timecode_transmission (); bool timecode_transmission_suspended () const; + std::string source_search_path(DataType) const; + + /* handlers can return an integer value: + 0: config.set_audio_search_path() or config.set_midi_search_path() was used + to modify the search path and we should try to find it again. + 1: quit entire session load + 2: as 0, but don't ask about other missing files + 3: don't ask about other missing files, and just mark this one missing + -1: just mark this one missing + any other value: as -1 + */ + static PBD::Signal3 MissingFile; + /** Emitted when the session wants Ardour to quit */ static PBD::Signal0 Quit; @@ -1310,6 +1323,8 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi uint32_t _total_free_4k_blocks; Glib::Mutex space_lock; + bool no_questions_about_missing_files; + std::string get_best_session_directory_for_new_source (); mutable gint _playback_load; diff --git a/libs/ardour/ardour/session_configuration_vars.h b/libs/ardour/ardour/session_configuration_vars.h index 828b2cbfcf..057cff01fe 100644 --- a/libs/ardour/ardour/session_configuration_vars.h +++ b/libs/ardour/ardour/session_configuration_vars.h @@ -35,6 +35,8 @@ CONFIG_VARIABLE (bool, punch_out, "punch-out", false) CONFIG_VARIABLE (uint32_t, subframes_per_frame, "subframes-per-frame", 100) CONFIG_VARIABLE (TimecodeFormat, timecode_format, "timecode-format", timecode_30) CONFIG_VARIABLE_SPECIAL(std::string, raid_path, "raid-path", "", path_expand) +CONFIG_VARIABLE_SPECIAL(std::string, audio_search_path, "audio-search-path", "", path_expand) +CONFIG_VARIABLE_SPECIAL(std::string, midi_search_path, "midi-search-path", "", path_expand) CONFIG_VARIABLE (std::string, bwf_country_code, "bwf-country-code", "US") CONFIG_VARIABLE (std::string, bwf_organization_code, "bwf-organization-code", "US") CONFIG_VARIABLE (LayerModel, layer_model, "layer-model", MoveAddHigher) diff --git a/libs/ardour/file_source.cc b/libs/ardour/file_source.cc index 5e52d7739a..95ab3e9f3d 100644 --- a/libs/ardour/file_source.cc +++ b/libs/ardour/file_source.cc @@ -52,7 +52,7 @@ using namespace ARDOUR; using namespace PBD; using namespace Glib; -map FileSource::search_paths; +PBD::Signal3 > FileSource::AmbiguousFileName; FileSource::FileSource (Session& session, DataType type, const string& path, Source::Flag flag) : Source(session, type, path, flag) @@ -84,8 +84,6 @@ FileSource::removable () const && ((_flags & RemoveAtDestroy) || ((_flags & RemovableIfEmpty) && empty() == 0))); - cerr << "is " << _path << " removable ? " << r << endl; - return r; } @@ -94,9 +92,15 @@ FileSource::init (const string& pathstr, bool must_exist) { _timeline_position = 0; - if (!find (_type, pathstr, must_exist, _file_is_new, _channel, _path)) { - throw MissingSource (); - } + if (Stateful::loading_state_version < 3000) { + if (!find_2X (_session, _type, pathstr, must_exist, _file_is_new, _channel, _path)) { + throw MissingSource (pathstr, _type); + } + } else { + if (!find (_session, _type, pathstr, must_exist, _file_is_new, _channel, _path)) { + throw MissingSource (pathstr, _type); + } + } set_within_session_from_path (pathstr); @@ -204,10 +208,100 @@ FileSource::move_to_trash (const string& trash_dir_name) * \return true iff the file was found. */ bool -FileSource::find (DataType type, const string& path, bool must_exist, +FileSource::find (Session& s, DataType type, const string& path, bool must_exist, bool& isnew, uint16_t& chan, string& found_path) { - string search_path = search_paths[type]; + string search_path = s.source_search_path (type); + + string pathstr = path; + bool ret = false; + + cerr << "Searching along " << search_path << endl; + + isnew = false; + + vector dirs; + vector hits; + int cnt; + string fullpath; + string keeppath; + + if (search_path.length() == 0) { + error << _("FileSource: search path not set") << endmsg; + goto out; + } + + split (search_path, dirs, ':'); + + cnt = 0; + hits.clear (); + + for (vector::iterator i = dirs.begin(); i != dirs.end(); ++i) { + + cerr << "Searching in " << *i << " for " << pathstr << endl; + + fullpath = Glib::build_filename (*i, pathstr); + + if (Glib::file_test (fullpath, Glib::FILE_TEST_EXISTS|Glib::FILE_TEST_IS_REGULAR)) { + keeppath = fullpath; + hits.push_back (fullpath); + ++cnt; + } + } + + if (cnt > 1) { + + int which = FileSource::AmbiguousFileName (pathstr, search_path, hits).get_value_or (-1); + + if (which < 0) { + goto out; + } else { + keeppath = hits[which]; + } + + } else if (cnt == 0) { + + if (must_exist) { + error << string_compose( + _("Filesource: cannot find required file (%1): while searching %2"), + pathstr, search_path) << endmsg; + goto out; + } else { + isnew = true; + } + } + + /* Current find() is unable to parse relative path names to yet non-existant + sources. QuickFix(tm) + */ + if (keeppath == "") { + if (must_exist) { + error << "FileSource::find(), keeppath = \"\", but the file must exist" << endl; + } else { + keeppath = pathstr; + } + } + + found_path = keeppath; + + ret = true; + + out: + return ret; +} + +/** Find the actual source file based on \a filename. + * + * If the source is within the session tree, \a filename should be a simple filename (no slashes). + * If the source is external, \a filename should be a full path. + * In either case, found_path is set to the complete absolute path of the source file. + * \return true iff the file was found. + */ +bool +FileSource::find_2X (Session& s, DataType type, const string& path, bool must_exist, + bool& isnew, uint16_t& chan, string& found_path) +{ + string search_path = s.source_search_path (type); string pathstr = path; string::size_type pos; @@ -397,12 +491,6 @@ FileSource::set_source_name (const string& newname, bool destructive) return 0; } -void -FileSource::set_search_path (DataType type, const string& p) -{ - search_paths[type] = p; -} - void FileSource::mark_immutable () { diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index 2392d45ca1..f870fd9b11 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -44,6 +44,7 @@ #include "pbd/stacktrace.h" #include "pbd/file_utils.h" #include "pbd/convert.h" +#include "pbd/strsplit.h" #include "ardour/amp.h" #include "ardour/analyser.h" @@ -117,6 +118,7 @@ PBD::Signal1 Session::Dialog; PBD::Signal0 Session::AskAboutPendingState; PBD::Signal2 Session::AskAboutSampleRateMismatch; PBD::Signal0 Session::SendFeedback; +PBD::Signal3 Session::MissingFile; PBD::Signal0 Session::TimecodeOffsetChanged; PBD::Signal1 Session::StartTimeChanged; @@ -4060,3 +4062,58 @@ Session::end_time_changed (framepos_t old) l->set_end (s->end(), true); } } + +string +Session::source_search_path (DataType type) const +{ + string search_path; + + if (session_dirs.size() == 1) { + switch (type) { + case DataType::AUDIO: + search_path = _session_dir->sound_path().to_string(); + break; + case DataType::MIDI: + search_path = _session_dir->midi_path().to_string(); + break; + } + } else { + for (vector::const_iterator i = session_dirs.begin(); i != session_dirs.end(); ++i) { + SessionDirectory sdir (i->path); + if (!search_path.empty()) { + search_path += ':'; + } + switch (type) { + case DataType::AUDIO: + search_path += sdir.sound_path().to_string(); + break; + case DataType::MIDI: + search_path += sdir.midi_path().to_string(); + break; + } + } + } + + /* now add user-specified locations + */ + + vector dirs; + + switch (type) { + case DataType::AUDIO: + split (config.get_audio_search_path (), dirs, ':'); + break; + case DataType::MIDI: + split (config.get_midi_search_path (), dirs, ':'); + break; + } + + for (vector::iterator i = dirs.begin(); i != dirs.end(); ++i) { + search_path += ':'; + search_path += *i; + + } + + return search_path; +} + diff --git a/libs/ardour/session_state.cc b/libs/ardour/session_state.cc index 4ae4065329..e9feed4e1f 100644 --- a/libs/ardour/session_state.cc +++ b/libs/ardour/session_state.cc @@ -217,6 +217,7 @@ Session::first_stage_init (string fullpath, string snapshot_name) post_export_sync = false; midi_control_ui = 0; _step_editors = 0; + no_questions_about_missing_files = false; AudioDiskstream::allocate_working_buffers(); @@ -423,10 +424,6 @@ Session::setup_raid_path (string path) midi_search_path += sdir.midi_path (); } - // set the search path for each data type - FileSource::set_search_path (DataType::AUDIO, sound_search_path.to_string ()); - SMFSource::set_search_path (DataType::MIDI, midi_search_path.to_string ()); - // reset the round-robin soundfile path thingie last_rr_session_dir = session_dirs.begin(); } @@ -1871,13 +1868,47 @@ Session::load_sources (const XMLNode& node) set_dirty(); for (niter = nlist.begin(); niter != nlist.end(); ++niter) { + retry: try { if ((source = XMLSourceFactory (**niter)) == 0) { error << _("Session: cannot create Source from XML description.") << endmsg; } + } catch (MissingSource& err) { - warning << _("A sound file is missing. It will be replaced by silence.") << endmsg; - source = SourceFactory::createSilent (*this, **niter, max_framecnt, _current_frame_rate); + + int user_choice; + + if (!no_questions_about_missing_files) { + user_choice = MissingFile (this, err.path, err.type).get_value_or (-1); + } else { + user_choice = -2; + } + + switch (user_choice) { + case 0: + /* user added a new search location, so try again */ + goto retry; + + + case 1: + /* user asked to quit the entire session load + */ + return -1; + + case 2: + no_questions_about_missing_files = true; + goto retry; + + case 3: + no_questions_about_missing_files = true; + /* fallthru */ + + case -1: + default: + warning << _("A sound file is missing. It will be replaced by silence.") << endmsg; + source = SourceFactory::createSilent (*this, **niter, max_framecnt, _current_frame_rate); + break; + } } } @@ -2421,7 +2452,7 @@ Session::find_all_sources (string path, set& result) bool is_new; uint16_t chan; - if (FileSource::find (type, prop->value(), true, is_new, chan, found_path)) { + if (FileSource::find (*this, type, prop->value(), true, is_new, chan, found_path)) { result.insert (found_path); } } diff --git a/libs/ardour/source_factory.cc b/libs/ardour/source_factory.cc index d09bf05c6e..ca1dc3b354 100644 --- a/libs/ardour/source_factory.cc +++ b/libs/ardour/source_factory.cc @@ -144,7 +144,6 @@ SourceFactory::create (Session& s, const XMLNode& node, bool defer_peaks) if (type == DataType::AUDIO) { try { - Source* src = new SndFileSource (s, node); // boost_debug_shared_ptr_mark_interesting (src, "Source"); boost::shared_ptr ret (src); -- cgit v1.2.3