add initial version of StartupFSM along with its owners/users
This commit is contained in:
parent
dd29e9b0e9
commit
18b4a4213f
|
@ -301,6 +301,7 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir)
|
||||||
, _mixer_on_top (false)
|
, _mixer_on_top (false)
|
||||||
, _initial_verbose_plugin_scan (false)
|
, _initial_verbose_plugin_scan (false)
|
||||||
, _shared_popup_menu (0)
|
, _shared_popup_menu (0)
|
||||||
|
, startup_fsm (0)
|
||||||
, secondary_clock_spacer (0)
|
, secondary_clock_spacer (0)
|
||||||
, auto_input_button (ArdourButton::led_default_elements)
|
, auto_input_button (ArdourButton::led_default_elements)
|
||||||
, latency_disable_button (ArdourButton::led_default_elements)
|
, latency_disable_button (ArdourButton::led_default_elements)
|
||||||
|
|
|
@ -91,6 +91,7 @@
|
||||||
#include "enums.h"
|
#include "enums.h"
|
||||||
#include "mini_timeline.h"
|
#include "mini_timeline.h"
|
||||||
#include "shuttle_control.h"
|
#include "shuttle_control.h"
|
||||||
|
#include "startup_fsm.h"
|
||||||
#include "transport_control.h"
|
#include "transport_control.h"
|
||||||
#include "transport_control_ui.h"
|
#include "transport_control_ui.h"
|
||||||
#include "visibility_group.h"
|
#include "visibility_group.h"
|
||||||
|
@ -223,6 +224,7 @@ public:
|
||||||
int get_session_parameters (bool quit_on_cancel, bool should_be_new = false, std::string load_template = "");
|
int get_session_parameters (bool quit_on_cancel, bool should_be_new = false, std::string load_template = "");
|
||||||
int build_session_from_dialog (SessionDialog&, const std::string& session_name, const std::string& session_path);
|
int build_session_from_dialog (SessionDialog&, const std::string& session_name, const std::string& session_path);
|
||||||
bool ask_about_loading_existing_session (const std::string& session_path);
|
bool ask_about_loading_existing_session (const std::string& session_path);
|
||||||
|
void load_session_from_startup_fsm ();
|
||||||
|
|
||||||
/// @return true if session was successfully unloaded.
|
/// @return true if session was successfully unloaded.
|
||||||
int unload_session (bool hide_stuff = false);
|
int unload_session (bool hide_stuff = false);
|
||||||
|
@ -438,8 +440,12 @@ private:
|
||||||
static ARDOUR_UI *theArdourUI;
|
static ARDOUR_UI *theArdourUI;
|
||||||
SessionDialog *_session_dialog;
|
SessionDialog *_session_dialog;
|
||||||
|
|
||||||
|
StartupFSM* startup_fsm;
|
||||||
|
|
||||||
int starting ();
|
int starting ();
|
||||||
int nsm_init ();
|
int nsm_init ();
|
||||||
|
void startup_done ();
|
||||||
|
void sfsm_response (StartupFSM::Result);
|
||||||
|
|
||||||
int ask_about_saving_session (const std::vector<std::string>& actions);
|
int ask_about_saving_session (const std::vector<std::string>& actions);
|
||||||
|
|
||||||
|
|
|
@ -57,7 +57,6 @@
|
||||||
#include "ardour/filename_extensions.h"
|
#include "ardour/filename_extensions.h"
|
||||||
#include "ardour/filesystem_paths.h"
|
#include "ardour/filesystem_paths.h"
|
||||||
#include "ardour/profile.h"
|
#include "ardour/profile.h"
|
||||||
#include "ardour/recent_sessions.h"
|
|
||||||
|
|
||||||
#include "gtkmm2ext/application.h"
|
#include "gtkmm2ext/application.h"
|
||||||
|
|
||||||
|
@ -480,27 +479,37 @@ ARDOUR_UI::nsm_init ()
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ARDOUR_UI::sfsm_response (StartupFSM::Result r)
|
||||||
|
{
|
||||||
|
switch (r) {
|
||||||
|
case StartupFSM::ExitProgram:
|
||||||
|
queue_finish ();
|
||||||
|
break;
|
||||||
|
case StartupFSM::LoadSession:
|
||||||
|
_initial_verbose_plugin_scan = false;
|
||||||
|
load_session_from_startup_fsm ();
|
||||||
|
break;
|
||||||
|
case StartupFSM::DoNothing:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
ARDOUR_UI::starting ()
|
ARDOUR_UI::starting ()
|
||||||
{
|
{
|
||||||
Application* app = Application::instance ();
|
|
||||||
bool brand_new_user = ArdourStartup::required ();
|
|
||||||
|
|
||||||
app->ShouldQuit.connect (sigc::mem_fun (*this, &ARDOUR_UI::queue_finish));
|
|
||||||
app->ShouldLoad.connect (sigc::mem_fun (*this, &ARDOUR_UI::load_from_application_api));
|
|
||||||
|
|
||||||
if (ARDOUR_COMMAND_LINE::check_announcements) {
|
if (ARDOUR_COMMAND_LINE::check_announcements) {
|
||||||
check_announcements ();
|
check_announcements ();
|
||||||
}
|
}
|
||||||
|
|
||||||
app->ready ();
|
|
||||||
|
|
||||||
/* we need to create this early because it may need to set the
|
/* we need to create this early because it may need to set the
|
||||||
* audio backend end up.
|
* audio backend end up.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
EngineControl* amd;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
audio_midi_setup.get (true);
|
amd = dynamic_cast<EngineControl*> (audio_midi_setup.get (true));
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
std::cerr << "audio-midi engine setup failed."<< std::endl;
|
std::cerr << "audio-midi engine setup failed."<< std::endl;
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -510,68 +519,53 @@ ARDOUR_UI::starting ()
|
||||||
return -1;
|
return -1;
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
if (brand_new_user) {
|
|
||||||
|
startup_fsm = new StartupFSM (*amd);
|
||||||
|
startup_fsm->start ();
|
||||||
|
startup_fsm->signal_response().connect (sigc::mem_fun (*this, &ARDOUR_UI::sfsm_response));
|
||||||
|
|
||||||
|
if (startup_fsm->brand_new_user()) {
|
||||||
_initial_verbose_plugin_scan = true;
|
_initial_verbose_plugin_scan = true;
|
||||||
ArdourStartup s;
|
|
||||||
s.present ();
|
|
||||||
main().run();
|
|
||||||
s.hide ();
|
|
||||||
_initial_verbose_plugin_scan = false;
|
|
||||||
switch (s.response ()) {
|
|
||||||
case Gtk::RESPONSE_OK:
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: maybe IFF brand_new_user
|
|
||||||
if (ARDOUR::Profile->get_mixbus () && Config->get_copy_demo_sessions ()) {
|
|
||||||
std::string dspd (Config->get_default_session_parent_dir());
|
|
||||||
Searchpath ds (ARDOUR::ardour_data_search_path());
|
|
||||||
ds.add_subdirectory_to_paths ("sessions");
|
|
||||||
vector<string> demos;
|
|
||||||
find_files_matching_pattern (demos, ds, ARDOUR::session_archive_suffix);
|
|
||||||
|
|
||||||
ARDOUR::RecentSessions rs;
|
|
||||||
ARDOUR::read_recent_sessions (rs);
|
|
||||||
|
|
||||||
for (vector<string>::iterator i = demos.begin(); i != demos.end (); ++i) {
|
|
||||||
/* "demo-session" must be inside "demo-session.<session_archive_suffix>" */
|
|
||||||
std::string name = basename_nosuffix (basename_nosuffix (*i));
|
|
||||||
std::string path = Glib::build_filename (dspd, name);
|
|
||||||
/* skip if session-dir already exists */
|
|
||||||
if (Glib::file_test(path.c_str(), Glib::FILE_TEST_IS_DIR)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
/* skip sessions that are already in 'recent'.
|
|
||||||
* eg. a new user changed <session-default-dir> shorly after installation
|
|
||||||
*/
|
|
||||||
for (ARDOUR::RecentSessions::iterator r = rs.begin(); r != rs.end(); ++r) {
|
|
||||||
if ((*r).first == name) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
PBD::FileArchive ar (*i);
|
|
||||||
if (0 == ar.inflate (dspd)) {
|
|
||||||
store_recent_sessions (name, path);
|
|
||||||
info << string_compose (_("Copied Demo Session %1."), name) << endmsg;
|
|
||||||
}
|
|
||||||
} catch (...) {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* go get a session */
|
|
||||||
|
|
||||||
const bool new_session_required = (ARDOUR_COMMAND_LINE::new_session || (!ARDOUR::Profile->get_mixbus() && brand_new_user));
|
|
||||||
|
|
||||||
if (get_session_parameters (false, new_session_required, ARDOUR_COMMAND_LINE::load_template)) {
|
|
||||||
std::cerr << "Cannot get session parameters."<< std::endl;
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ARDOUR_UI::load_session_from_startup_fsm ()
|
||||||
|
{
|
||||||
|
string session_path = startup_fsm->session_path;
|
||||||
|
string session_name = startup_fsm->session_name;
|
||||||
|
string session_template = startup_fsm->session_template;
|
||||||
|
bool session_is_new = startup_fsm->session_is_new;
|
||||||
|
BusProfile bus_profile = startup_fsm->bus_profile;
|
||||||
|
|
||||||
|
std::cerr << " loading from " << session_path << " as " << session_name << " templ " << session_template << " is_new " << session_is_new << " bp " << bus_profile.master_out_channels << std::endl;
|
||||||
|
|
||||||
|
if (session_is_new) {
|
||||||
|
|
||||||
|
if (build_session (session_path, session_name, &bus_profile)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!session_template.empty() && session_template.substr (0, 11) == "urn:ardour:") {
|
||||||
|
meta_session_setup (session_template.substr (11));
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
int ret = load_session (session_path, session_name, session_template);
|
||||||
|
|
||||||
|
if (ret == -2) {
|
||||||
|
/* not connected to the AudioEngine, so quit to avoid an infinite loop */
|
||||||
|
exit (EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ARDOUR_UI::startup_done ()
|
||||||
|
{
|
||||||
use_config ();
|
use_config ();
|
||||||
|
|
||||||
WM::Manager::instance().show_visible ();
|
WM::Manager::instance().show_visible ();
|
||||||
|
@ -582,10 +576,6 @@ ARDOUR_UI::starting ()
|
||||||
_status_bar_visibility.update ();
|
_status_bar_visibility.update ();
|
||||||
|
|
||||||
BootMessage (string_compose (_("%1 is ready for use"), PROGRAM_NAME));
|
BootMessage (string_compose (_("%1 is ready for use"), PROGRAM_NAME));
|
||||||
|
|
||||||
/* all other dialogs are created conditionally */
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
662
gtk2_ardour/startup_fsm.cc
Normal file
662
gtk2_ardour/startup_fsm.cc
Normal file
|
@ -0,0 +1,662 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2019 Paul Davis <paul@linuxaudiosystems.com>
|
||||||
|
*
|
||||||
|
* 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.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <gtkmm/dialog.h>
|
||||||
|
#include <gtkmm/liststore.h>
|
||||||
|
#include <gtkmm/messagedialog.h>
|
||||||
|
|
||||||
|
#include "pbd/basename.h"
|
||||||
|
#include "pbd/file_archive.h"
|
||||||
|
#include "pbd/file_utils.h"
|
||||||
|
#include "pbd/i18n.h"
|
||||||
|
|
||||||
|
#include "ardour/audioengine.h"
|
||||||
|
#include "ardour/filename_extensions.h"
|
||||||
|
#include "ardour/filesystem_paths.h"
|
||||||
|
#include "ardour/profile.h"
|
||||||
|
#include "ardour/recent_sessions.h"
|
||||||
|
#include "ardour/rc_configuration.h"
|
||||||
|
#include "ardour/search_paths.h"
|
||||||
|
#include "ardour/session.h"
|
||||||
|
#include "ardour/session_utils.h"
|
||||||
|
#include "ardour/template_utils.h"
|
||||||
|
|
||||||
|
#include "gtkmm2ext/application.h"
|
||||||
|
#include <gtkmm2ext/doi.h>
|
||||||
|
|
||||||
|
#include "engine_dialog.h"
|
||||||
|
#include "new_user_wizard.h"
|
||||||
|
#include "opts.h"
|
||||||
|
#include "session_dialog.h"
|
||||||
|
#include "startup_fsm.h"
|
||||||
|
|
||||||
|
using namespace ARDOUR;
|
||||||
|
using namespace Gtk;
|
||||||
|
using namespace Gtkmm2ext;
|
||||||
|
using namespace PBD;
|
||||||
|
|
||||||
|
using std::string;
|
||||||
|
using std::vector;
|
||||||
|
|
||||||
|
StartupFSM::StartupFSM (EngineControl& amd)
|
||||||
|
: session_existing_sample_rate (0)
|
||||||
|
, session_is_new (false)
|
||||||
|
, new_user (true /*NewUserWizard::required()*/)
|
||||||
|
, new_session (true)
|
||||||
|
, _state (NeedWizard)
|
||||||
|
, new_user_wizard (0)
|
||||||
|
, audiomidi_dialog (amd)
|
||||||
|
, session_dialog (0)
|
||||||
|
{
|
||||||
|
Application* app = Application::instance ();
|
||||||
|
|
||||||
|
app->ShouldQuit.connect (sigc::mem_fun (*this, &StartupFSM::queue_finish));
|
||||||
|
app->ShouldLoad.connect (sigc::mem_fun (*this, &StartupFSM::load_from_application_api));
|
||||||
|
|
||||||
|
/* this may cause the delivery of ShouldLoad etc if we were invoked in
|
||||||
|
* particular ways. It will happen when the event loop runs again.
|
||||||
|
*/
|
||||||
|
|
||||||
|
app->ready ();
|
||||||
|
}
|
||||||
|
|
||||||
|
StartupFSM::~StartupFSM ()
|
||||||
|
{
|
||||||
|
delete session_dialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
StartupFSM::queue_finish ()
|
||||||
|
{
|
||||||
|
_signal_response (ExitProgram);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
StartupFSM::start ()
|
||||||
|
{
|
||||||
|
if (new_user) {
|
||||||
|
/* show new user wizard */
|
||||||
|
_state = NeedSessionPath;
|
||||||
|
show_new_user_wizard ();
|
||||||
|
} else {
|
||||||
|
/* pretend we just showed the new user wizard and we're done
|
||||||
|
with it
|
||||||
|
*/
|
||||||
|
dialog_response_handler (RESPONSE_OK, NewUserDialog);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
StartupFSM::dialog_response_handler (int response, StartupFSM::DialogID dialog_id)
|
||||||
|
{
|
||||||
|
const bool new_session_required = (ARDOUR_COMMAND_LINE::new_session || (!ARDOUR::Profile->get_mixbus() && new_user));
|
||||||
|
int csp;
|
||||||
|
|
||||||
|
std::cerr << "SFSM state = " << _state << " r = " << response << " did " << dialog_id << std::endl;
|
||||||
|
|
||||||
|
switch (_state) {
|
||||||
|
case NeedSessionPath:
|
||||||
|
switch (dialog_id) {
|
||||||
|
case NewUserDialog:
|
||||||
|
|
||||||
|
current_dialog_connection.disconnect ();
|
||||||
|
delete_when_idle (new_user_wizard);
|
||||||
|
|
||||||
|
switch (response) {
|
||||||
|
case RESPONSE_OK:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
exit (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* new user wizard done, now lets get session params
|
||||||
|
* either from the command line (if given) or a dialog
|
||||||
|
* (if nothing given on the command line or if the
|
||||||
|
* command line arguments don't work for some reason
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (ARDOUR_COMMAND_LINE::session_name.empty()) {
|
||||||
|
|
||||||
|
/* nothing given on the command line ... show new session dialog */
|
||||||
|
|
||||||
|
session_path = string();
|
||||||
|
session_name = string();
|
||||||
|
session_template = string();
|
||||||
|
|
||||||
|
_state = NeedSessionPath;
|
||||||
|
session_dialog = new SessionDialog (new_session_required, string(), string(), string(), false);
|
||||||
|
show_session_dialog ();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if (get_session_parameters_from_command_line (new_session_required)) {
|
||||||
|
|
||||||
|
/* command line arguments all OK. Get engine parameters */
|
||||||
|
|
||||||
|
_state = NeedEngineParams;
|
||||||
|
|
||||||
|
if (!new_session_required && session_existing_sample_rate > 0) {
|
||||||
|
audiomidi_dialog.set_desired_sample_rate (session_existing_sample_rate);
|
||||||
|
}
|
||||||
|
|
||||||
|
show_audiomidi_dialog ();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
/* command line arguments not good. Use
|
||||||
|
* dialog, but prime the dialog with
|
||||||
|
* the information we set up in
|
||||||
|
* get_session_parameters_from_command_line()
|
||||||
|
*/
|
||||||
|
|
||||||
|
_state = NeedSessionPath;
|
||||||
|
session_dialog = new SessionDialog (new_session_required, session_name, session_path, session_template, false);
|
||||||
|
show_session_dialog ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NewSessionDialog:
|
||||||
|
switch (response) {
|
||||||
|
case RESPONSE_OK:
|
||||||
|
case RESPONSE_ACCEPT:
|
||||||
|
csp = check_session_parameters (new_session_required);
|
||||||
|
std::cerr << "csp = " << csp << std::endl;
|
||||||
|
switch (csp) {
|
||||||
|
case -1:
|
||||||
|
/* Unrecoverable error */
|
||||||
|
_signal_response (ExitProgram);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
/* do nothing - keep dialog up for a
|
||||||
|
* retry. Errors were addressed by
|
||||||
|
* ::check_session_parameters()
|
||||||
|
*/
|
||||||
|
break;
|
||||||
|
case 0:
|
||||||
|
_state = NeedEngineParams;
|
||||||
|
session_dialog->hide ();
|
||||||
|
delete session_dialog;
|
||||||
|
session_dialog = 0;
|
||||||
|
current_dialog_connection.disconnect();
|
||||||
|
if (!session_is_new && session_existing_sample_rate > 0) {
|
||||||
|
audiomidi_dialog.set_desired_sample_rate (session_existing_sample_rate);
|
||||||
|
}
|
||||||
|
show_audiomidi_dialog ();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
_signal_response (ExitProgram);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
/* ERROR */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NeedEngineParams:
|
||||||
|
switch (dialog_id) {
|
||||||
|
case AudioMIDISetup:
|
||||||
|
std::cerr << "AMS done, r = " << response << std::endl;
|
||||||
|
switch (response) {
|
||||||
|
case RESPONSE_OK:
|
||||||
|
case RESPONSE_ACCEPT:
|
||||||
|
audiomidi_dialog.hide ();
|
||||||
|
current_dialog_connection.disconnect();
|
||||||
|
/* fallthru */
|
||||||
|
case RESPONSE_DELETE_EVENT:
|
||||||
|
if (AudioEngine::instance()->running()) {
|
||||||
|
_signal_response (LoadSession);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
_signal_response (ExitProgram);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* ERROR */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case NeedWizard:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NeedSessionSR:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NeedEngine:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
StartupFSM::show_new_user_wizard ()
|
||||||
|
{
|
||||||
|
new_user_wizard = new NewUserWizard;
|
||||||
|
current_dialog_connection = new_user_wizard->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &StartupFSM::dialog_response_handler), NewUserDialog));
|
||||||
|
new_user_wizard->present ();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
StartupFSM::show_session_dialog ()
|
||||||
|
{
|
||||||
|
current_dialog_connection = session_dialog->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &StartupFSM::dialog_response_handler), NewSessionDialog));
|
||||||
|
session_dialog->present ();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
StartupFSM::show_audiomidi_dialog ()
|
||||||
|
{
|
||||||
|
current_dialog_connection = audiomidi_dialog.signal_response().connect (sigc::bind (sigc::mem_fun (*this, &StartupFSM::dialog_response_handler), AudioMIDISetup));
|
||||||
|
audiomidi_dialog.present ();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
StartupFSM::get_session_parameters_from_command_line (bool new_session_required)
|
||||||
|
{
|
||||||
|
return get_session_parameters_from_path (ARDOUR_COMMAND_LINE::session_name, ARDOUR_COMMAND_LINE::load_template, new_session_required);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
StartupFSM::get_session_parameters_from_path (string const & path, string const & template_name, bool new_session_required)
|
||||||
|
{
|
||||||
|
if (path.empty()) {
|
||||||
|
/* use GUI to ask the user */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Glib::file_test (path.c_str(), Glib::FILE_TEST_EXISTS)) {
|
||||||
|
|
||||||
|
if (new_session_required) {
|
||||||
|
/* wait! it already exists */
|
||||||
|
|
||||||
|
if (!ask_about_loading_existing_session (path)) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
/* load it anyway */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
session_name = basename_nosuffix (path);
|
||||||
|
|
||||||
|
if (Glib::file_test (path.c_str(), Glib::FILE_TEST_IS_REGULAR)) {
|
||||||
|
/* session/snapshot file, change path to be dir */
|
||||||
|
session_path = Glib::path_get_dirname (path);
|
||||||
|
}
|
||||||
|
|
||||||
|
float sr;
|
||||||
|
SampleFormat fmt;
|
||||||
|
string program_version;
|
||||||
|
|
||||||
|
if (Session::get_info_from_path (session_path, sr, fmt, program_version)) {
|
||||||
|
/* exists but we can't read it */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
session_existing_sample_rate = sr;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Everything after this involves a new session
|
||||||
|
*
|
||||||
|
* ... did the user give us a path or just a name?
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (path.find (G_DIR_SEPARATOR) == string::npos) {
|
||||||
|
/* user gave session name with no path info, use
|
||||||
|
default session folder.
|
||||||
|
*/
|
||||||
|
session_name = ARDOUR_COMMAND_LINE::session_name;
|
||||||
|
session_path = Glib::build_filename (Config->get_default_session_parent_dir (), session_name);
|
||||||
|
} else {
|
||||||
|
session_name = basename_nosuffix (path);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (!template_name.empty()) {
|
||||||
|
|
||||||
|
/* Allow the user to specify a template via path or name on the
|
||||||
|
* command line
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool have_resolved_template_name = false;
|
||||||
|
|
||||||
|
/* compare by name (path may or may not be UTF-8) */
|
||||||
|
|
||||||
|
vector<TemplateInfo> templates;
|
||||||
|
find_session_templates (templates, false);
|
||||||
|
for (vector<TemplateInfo>::iterator x = templates.begin(); x != templates.end(); ++x) {
|
||||||
|
if ((*x).name == template_name) {
|
||||||
|
session_template = (*x).path;
|
||||||
|
have_resolved_template_name = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* look up script by name */
|
||||||
|
LuaScriptList scripts (LuaScripting::instance ().scripts (LuaScriptInfo::SessionInit));
|
||||||
|
LuaScriptList& as (LuaScripting::instance ().scripts (LuaScriptInfo::EditorAction));
|
||||||
|
for (LuaScriptList::const_iterator s = as.begin(); s != as.end(); ++s) {
|
||||||
|
if ((*s)->subtype & LuaScriptInfo::SessionSetup) {
|
||||||
|
scripts.push_back (*s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::sort (scripts.begin(), scripts.end(), LuaScripting::Sorter());
|
||||||
|
for (LuaScriptList::const_iterator s = scripts.begin(); s != scripts.end(); ++s) {
|
||||||
|
if ((*s)->name == template_name) {
|
||||||
|
session_template = "urn:ardour:" + (*s)->path;
|
||||||
|
have_resolved_template_name = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!have_resolved_template_name) {
|
||||||
|
/* this will produce a more or less meaninful error later:
|
||||||
|
* "ERROR: Could not open session template [abs-path to user-config dir]"
|
||||||
|
*/
|
||||||
|
session_template = Glib::build_filename (ARDOUR::user_template_directory (), template_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We don't know what this is, because the session is new and the
|
||||||
|
* command line doesn't let us specify it. The user will get to decide
|
||||||
|
* in the audio/MIDI dialog.
|
||||||
|
*/
|
||||||
|
|
||||||
|
session_existing_sample_rate = 0;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** return values:
|
||||||
|
* -1: failure
|
||||||
|
* 1: failure but user can retry
|
||||||
|
* 0: success, seesion parameters ready for use
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
StartupFSM::check_session_parameters (bool must_be_new)
|
||||||
|
{
|
||||||
|
bool requested_new = false;
|
||||||
|
|
||||||
|
session_name = session_dialog->session_name (requested_new);
|
||||||
|
session_path = session_dialog->session_folder ();
|
||||||
|
|
||||||
|
if (must_be_new) {
|
||||||
|
assert (requested_new);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!must_be_new) {
|
||||||
|
|
||||||
|
/* See if the specified session is a session archive */
|
||||||
|
|
||||||
|
int rv = ARDOUR::inflate_session (session_name, Config->get_default_session_parent_dir(), session_path, session_name);
|
||||||
|
if (rv < 0) {
|
||||||
|
MessageDialog msg (*session_dialog, string_compose (_("Extracting session-archive failed: %1"), inflate_error (rv)));
|
||||||
|
msg.run ();
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
} else if (rv == 0) {
|
||||||
|
/* names are good (and session is unarchived/inflated */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check for ".ardour" in statefile name, because we don't want
|
||||||
|
* it
|
||||||
|
*
|
||||||
|
* XXX Note this wierd conflation of a
|
||||||
|
* file-name-without-a-suffix and the session name. It's not
|
||||||
|
* really a session name at all, but rather the suffix-free
|
||||||
|
* name of a statefile (snapshot).
|
||||||
|
*/
|
||||||
|
|
||||||
|
const string::size_type suffix_at = session_name.find (statefile_suffix);
|
||||||
|
|
||||||
|
if (suffix_at != string::npos) {
|
||||||
|
session_name = session_name.substr (0, suffix_at);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* this shouldn't happen, but we catch it just in case it does */
|
||||||
|
|
||||||
|
if (session_name.empty()) {
|
||||||
|
return 1; /* keep running dialog */
|
||||||
|
}
|
||||||
|
|
||||||
|
if (session_dialog->use_session_template()) {
|
||||||
|
session_template = session_dialog->session_template_name();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (session_name[0] == G_DIR_SEPARATOR ||
|
||||||
|
#ifdef PLATFORM_WINDOWS
|
||||||
|
// Windows file system .. detect absolute path
|
||||||
|
// C:/*
|
||||||
|
(session_name.length() > 3 && session_name[1] == ':' && session_name[2] == G_DIR_SEPARATOR)
|
||||||
|
#else
|
||||||
|
// Sensible file systems
|
||||||
|
// /* or ./* or ../*
|
||||||
|
(session_name.length() > 2 && session_name[0] == '.' && session_name[1] == G_DIR_SEPARATOR) ||
|
||||||
|
(session_name.length() > 3 && session_name[0] == '.' && session_name[1] == '.' && session_name[2] == G_DIR_SEPARATOR)
|
||||||
|
#endif
|
||||||
|
)
|
||||||
|
{
|
||||||
|
|
||||||
|
/* user typed absolute path or cwd-relative path
|
||||||
|
specified into session name field. So ... infer
|
||||||
|
session path and name from what was given.
|
||||||
|
*/
|
||||||
|
|
||||||
|
session_path = Glib::path_get_dirname (session_name);
|
||||||
|
session_name = Glib::path_get_basename (session_name);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
/* session name is just a name */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check if name is legal */
|
||||||
|
|
||||||
|
const char illegal = Session::session_name_is_legal (session_name);
|
||||||
|
|
||||||
|
if (illegal) {
|
||||||
|
MessageDialog msg (*session_dialog,
|
||||||
|
string_compose (_("To ensure compatibility with various systems\n"
|
||||||
|
"session names may not contain a '%1' character"),
|
||||||
|
illegal));
|
||||||
|
msg.run ();
|
||||||
|
ARDOUR_COMMAND_LINE::session_name = ""; // cancel that
|
||||||
|
return 1; /* keep running dialog */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* check if the currently-exists status matches whether or not
|
||||||
|
* it should be new
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (Glib::file_test (session_path, Glib::FileTest (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))) {
|
||||||
|
|
||||||
|
if (requested_new /*&& !nsm*/) {
|
||||||
|
|
||||||
|
std::string existing = Glib::build_filename (session_path, session_name);
|
||||||
|
|
||||||
|
if (!ask_about_loading_existing_session (existing)) {
|
||||||
|
session_dialog->clear_name ();
|
||||||
|
return 1; /* try again */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
session_is_new = false;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
/* does not exist at present */
|
||||||
|
|
||||||
|
if (!requested_new) {
|
||||||
|
// pop_back_splash (session_dialog);
|
||||||
|
MessageDialog msg (string_compose (_("There is no existing session at \"%1\""), session_path));
|
||||||
|
msg.run ();
|
||||||
|
session_dialog->clear_name();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
session_is_new = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
float sr;
|
||||||
|
SampleFormat fmt;
|
||||||
|
string program_version;
|
||||||
|
|
||||||
|
if (!session_is_new && Session::get_info_from_path (session_path, sr, fmt, program_version)) {
|
||||||
|
/* exists but we can't read it */
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
session_existing_sample_rate = sr;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
StartupFSM::copy_demo_sessions ()
|
||||||
|
{
|
||||||
|
// TODO: maybe IFF brand_new_user
|
||||||
|
if (ARDOUR::Profile->get_mixbus () && Config->get_copy_demo_sessions ()) {
|
||||||
|
std::string dspd (Config->get_default_session_parent_dir());
|
||||||
|
Searchpath ds (ARDOUR::ardour_data_search_path());
|
||||||
|
ds.add_subdirectory_to_paths ("sessions");
|
||||||
|
vector<string> demos;
|
||||||
|
find_files_matching_pattern (demos, ds, ARDOUR::session_archive_suffix);
|
||||||
|
|
||||||
|
ARDOUR::RecentSessions rs;
|
||||||
|
ARDOUR::read_recent_sessions (rs);
|
||||||
|
|
||||||
|
for (vector<string>::iterator i = demos.begin(); i != demos.end (); ++i) {
|
||||||
|
/* "demo-session" must be inside "demo-session.<session_archive_suffix>" */
|
||||||
|
std::string name = basename_nosuffix (basename_nosuffix (*i));
|
||||||
|
std::string path = Glib::build_filename (dspd, name);
|
||||||
|
/* skip if session-dir already exists */
|
||||||
|
if (Glib::file_test(path.c_str(), Glib::FILE_TEST_IS_DIR)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/* skip sessions that are already in 'recent'.
|
||||||
|
* eg. a new user changed <session-default-dir> shorly after installation
|
||||||
|
*/
|
||||||
|
for (ARDOUR::RecentSessions::iterator r = rs.begin(); r != rs.end(); ++r) {
|
||||||
|
if ((*r).first == name) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
PBD::FileArchive ar (*i);
|
||||||
|
if (0 == ar.inflate (dspd)) {
|
||||||
|
store_recent_sessions (name, path);
|
||||||
|
info << string_compose (_("Copied Demo Session %1."), name) << endmsg;
|
||||||
|
}
|
||||||
|
} catch (...) {
|
||||||
|
/* relax ? */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
StartupFSM::load_from_application_api (const std::string& path)
|
||||||
|
{
|
||||||
|
/* macOS El Capitan (and probably later) now somehow passes the command
|
||||||
|
line arguments to an app via the openFile delegate protocol. Ardour
|
||||||
|
already does its own command line processing, and having both
|
||||||
|
pathways active causes crashes. So, if the command line was already
|
||||||
|
set, do nothing here.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!ARDOUR_COMMAND_LINE::session_name.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Cancel SessionDialog if it's visible to make macOS delegates work.
|
||||||
|
*
|
||||||
|
* there's a race condition here: we connect to app->ShouldLoad
|
||||||
|
* and then at some point (might) show a session dialog. The race is
|
||||||
|
* caused by the non-deterministic interaction between the macOS event
|
||||||
|
* loop(s) and the GDK one(s).
|
||||||
|
*
|
||||||
|
* - ShouldLoad does not arrive before we show the session dialog
|
||||||
|
* -> here we should hide the session dialog, then use the
|
||||||
|
* supplied path as if it was provided on the command line
|
||||||
|
* - ShouldLoad signal arrives before we show a session dialog
|
||||||
|
* -> don't bother showing the session dialog, just use the
|
||||||
|
* supplied path as if it was provided on the command line
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (session_dialog) {
|
||||||
|
session_dialog->hide ();
|
||||||
|
delete_when_idle (session_dialog);
|
||||||
|
session_dialog = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* no command line argument given ... must just be via
|
||||||
|
* desktop/finder/window manager API (e.g. double click on "foo.ardour"
|
||||||
|
* icon)
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (get_session_parameters_from_path (path, string(), false)) {
|
||||||
|
_signal_response (LoadSession);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* given parameters failed for some reason. This is probably true
|
||||||
|
* anyway, but force it to be true and then carry on with whatever the
|
||||||
|
* main event loop is doing.
|
||||||
|
*/
|
||||||
|
|
||||||
|
_state = NeedSessionPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
StartupFSM::ask_about_loading_existing_session (const std::string& session_path)
|
||||||
|
{
|
||||||
|
std::string str = string_compose (_("This session\n%1\nalready exists. Do you want to open it?"), session_path);
|
||||||
|
|
||||||
|
MessageDialog msg (str,
|
||||||
|
false,
|
||||||
|
Gtk::MESSAGE_WARNING,
|
||||||
|
Gtk::BUTTONS_YES_NO,
|
||||||
|
true);
|
||||||
|
|
||||||
|
|
||||||
|
msg.set_name (X_("OpenExistingDialog"));
|
||||||
|
msg.set_title (_("Open Existing Session"));
|
||||||
|
msg.set_wmclass (X_("existing_session"), PROGRAM_NAME);
|
||||||
|
msg.set_position (Gtk::WIN_POS_CENTER);
|
||||||
|
// pop_back_splash (msg);
|
||||||
|
|
||||||
|
switch (msg.run()) {
|
||||||
|
case RESPONSE_YES:
|
||||||
|
return true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
103
gtk2_ardour/startup_fsm.h
Normal file
103
gtk2_ardour/startup_fsm.h
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2019 Paul Davis <paul@linuxaudiosystems.com>
|
||||||
|
*
|
||||||
|
* 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.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __gtk2_ardour_startup_fsm_h__
|
||||||
|
#define __gtk2_ardour_startup_fsm_h__
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <sigc++/trackable.h>
|
||||||
|
|
||||||
|
#include "ardour/types.h"
|
||||||
|
|
||||||
|
class NewUserWizard;
|
||||||
|
class EngineControl;
|
||||||
|
class SessionDialog;
|
||||||
|
|
||||||
|
class StartupFSM : public sigc::trackable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum DialogID {
|
||||||
|
NewUserDialog,
|
||||||
|
NewSessionDialog,
|
||||||
|
AudioMIDISetup
|
||||||
|
};
|
||||||
|
|
||||||
|
enum Result {
|
||||||
|
LoadSession,
|
||||||
|
ExitProgram,
|
||||||
|
DoNothing, /* seriously? how can this be an option */
|
||||||
|
};
|
||||||
|
|
||||||
|
StartupFSM (EngineControl&);
|
||||||
|
~StartupFSM ();
|
||||||
|
|
||||||
|
void start ();
|
||||||
|
|
||||||
|
std::string session_path;
|
||||||
|
std::string session_name;
|
||||||
|
std::string session_template;
|
||||||
|
int session_existing_sample_rate;
|
||||||
|
bool session_is_new;
|
||||||
|
ARDOUR::BusProfile bus_profile;
|
||||||
|
|
||||||
|
/* It's not a dialog but we provide this to make it behave like a (non-modal)
|
||||||
|
* dialog
|
||||||
|
*/
|
||||||
|
|
||||||
|
sigc::signal1<void,Result>& signal_response() { return _signal_response; }
|
||||||
|
|
||||||
|
bool brand_new_user() const { return new_user; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum MainState {
|
||||||
|
NeedWizard,
|
||||||
|
NeedSessionPath,
|
||||||
|
NeedSessionSR,
|
||||||
|
NeedEngineParams,
|
||||||
|
NeedEngine
|
||||||
|
};
|
||||||
|
|
||||||
|
bool new_user;
|
||||||
|
bool new_session;
|
||||||
|
|
||||||
|
MainState _state;
|
||||||
|
|
||||||
|
void dialog_response_handler (int response, DialogID);
|
||||||
|
|
||||||
|
void show_new_user_wizard ();
|
||||||
|
void show_session_dialog ();
|
||||||
|
void show_audiomidi_dialog ();
|
||||||
|
void copy_demo_sessions ();
|
||||||
|
void load_from_application_api (std::string const &);
|
||||||
|
bool get_session_parameters_from_command_line (bool new_session_required);
|
||||||
|
bool get_session_parameters_from_path (std::string const & path, std::string const & template_name, bool new_session_required);
|
||||||
|
void queue_finish ();
|
||||||
|
bool ask_about_loading_existing_session (const std::string& session_path);
|
||||||
|
int check_session_parameters (bool must_be_new);
|
||||||
|
|
||||||
|
NewUserWizard* new_user_wizard;
|
||||||
|
EngineControl& audiomidi_dialog;
|
||||||
|
SessionDialog* session_dialog;
|
||||||
|
|
||||||
|
sigc::connection current_dialog_connection;
|
||||||
|
|
||||||
|
sigc::signal1<void,Result> _signal_response;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* __gtk2_ardour_startup_fsm_h__ */
|
|
@ -175,6 +175,7 @@ gtk2_ardour_sources = [
|
||||||
'mouse_cursors.cc',
|
'mouse_cursors.cc',
|
||||||
'nag.cc',
|
'nag.cc',
|
||||||
'new_plugin_preset_dialog.cc',
|
'new_plugin_preset_dialog.cc',
|
||||||
|
'new_user_wizard.cc',
|
||||||
'normalize_dialog.cc',
|
'normalize_dialog.cc',
|
||||||
'note.cc',
|
'note.cc',
|
||||||
'note_base.cc',
|
'note_base.cc',
|
||||||
|
@ -251,7 +252,7 @@ gtk2_ardour_sources = [
|
||||||
'soundcloud_export_selector.cc',
|
'soundcloud_export_selector.cc',
|
||||||
'splash.cc',
|
'splash.cc',
|
||||||
'speaker_dialog.cc',
|
'speaker_dialog.cc',
|
||||||
'startup.cc',
|
'startup_fsm.cc',
|
||||||
'step_editor.cc',
|
'step_editor.cc',
|
||||||
'step_entry.cc',
|
'step_entry.cc',
|
||||||
'stereo_panner.cc',
|
'stereo_panner.cc',
|
||||||
|
|
Loading…
Reference in New Issue
Block a user