summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Davis <paul@linuxaudiosystems.com>2010-11-09 06:03:51 +0000
committerPaul Davis <paul@linuxaudiosystems.com>2010-11-09 06:03:51 +0000
commit5c6ba165f684fbd45be33c83d41083567d4dd88f (patch)
treeb3108d53b3a82fb1ea522038fe4500e0c38349bb
parentd29f14bf33bc807be7b95015e4f011f4ad741cc6 (diff)
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
-rw-r--r--gtk2_ardour/ardour_ui.cc28
-rw-r--r--gtk2_ardour/ardour_ui.h2
-rw-r--r--gtk2_ardour/missing_file_dialog.cc166
-rw-r--r--gtk2_ardour/missing_file_dialog.h39
-rw-r--r--gtk2_ardour/option_editor.h2
-rw-r--r--gtk2_ardour/search_path_option.cc156
-rw-r--r--gtk2_ardour/search_path_option.h50
-rw-r--r--gtk2_ardour/session_option_editor.cc30
-rw-r--r--gtk2_ardour/wscript2
-rw-r--r--libs/ardour/ardour/file_source.h28
-rw-r--r--libs/ardour/ardour/session.h15
-rw-r--r--libs/ardour/ardour/session_configuration_vars.h2
-rw-r--r--libs/ardour/file_source.cc116
-rw-r--r--libs/ardour/session.cc57
-rw-r--r--libs/ardour/session_state.cc45
-rw-r--r--libs/ardour/source_factory.cc1
16 files changed, 701 insertions, 38 deletions
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<WindowProxyBase*> _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\
+<tt>%3</tt>\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<string> 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<string>::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 <string>
+#include <gtkmm/label.h>
+#include <gtkmm/filechooserwidget.h>
+#include <gtkmm/radiobutton.h>
+
+#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<std::string> get, sigc::slot<bool, std::string> 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 ("<i>%1</i>", _("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 ("<b>%1</b>", _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<PathEntry*>::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<string> dirs;
+
+ clear ();
+ path_box.pack_start (session_label);
+
+ split (str, dirs, ':');
+
+ for (vector<string>::iterator d = dirs.begin(); d != dirs.end(); ++d) {
+ add_path (*d);
+ }
+}
+
+void
+SearchPathOption::changed ()
+{
+ string str;
+
+ for (list<PathEntry*>::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 <string>
+
+#include <gtkmm/filechooserbutton.h>
+#include <gtkmm/entry.h>
+#include <gtkmm/button.h>
+#include <gtkmm/box.h>
+
+#include "option_editor.h"
+
+class SearchPathOption : public Option
+{
+ public:
+ SearchPathOption (const std::string& pathname, const std::string& label,
+ sigc::slot<std::string>, sigc::slot<bool, std::string>);
+ ~SearchPathOption ();
+
+ void set_state_from_config ();
+ void add_to_page (OptionEditorPage*);
+ void clear ();
+
+ protected:
+ sigc::slot<std::string> _get; ///< slot to get the configuration variable's value
+ sigc::slot<bool, std::string> _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<PathEntry*> 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<SampleFormat>* sf = new ComboOption<SampleFormat> (
"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<HeaderFormat>* hf = new ComboOption<HeaderFormat> (
"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<LayerModel>* lm = new ComboOption<LayerModel> (
"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<InsertMergePolicy>* li = new ComboOption<InsertMergePolicy> (
"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 <list>
+#include <string>
#include <exception>
#include <time.h>
#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<int,std::string,std::string,std::vector<std::string> > 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<DataType, std::string> 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<int,Session*,std::string,DataType> MissingFile;
+
/** Emitted when the session wants Ardour to quit */
static PBD::Signal0<void> 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<DataType, string> FileSource::search_paths;
+PBD::Signal3<int,std::string,std::string,std::vector<std::string> > 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<string> dirs;
+ vector<string> 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<string>::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;
@@ -398,12 +492,6 @@ FileSource::set_source_name (const string& newname, bool destructive)
}
void
-FileSource::set_search_path (DataType type, const string& p)
-{
- search_paths[type] = p;
-}
-
-void
FileSource::mark_immutable ()
{
/* destructive sources stay writable, and their other flags don't change. */
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<void,std::string> Session::Dialog;
PBD::Signal0<int> Session::AskAboutPendingState;
PBD::Signal2<int,nframes_t,nframes_t> Session::AskAboutSampleRateMismatch;
PBD::Signal0<void> Session::SendFeedback;
+PBD::Signal3<int,Session*,std::string,DataType> Session::MissingFile;
PBD::Signal0<void> Session::TimecodeOffsetChanged;
PBD::Signal1<void, framepos_t> 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<space_and_path>::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<string> 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<string>::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<string>& 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<Source> ret (src);