diff --git a/gtk2_ardour/ardour_ui.cc b/gtk2_ardour/ardour_ui.cc index eb6529f453..c7ebbe847e 100644 --- a/gtk2_ardour/ardour_ui.cc +++ b/gtk2_ardour/ardour_ui.cc @@ -301,6 +301,7 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir) , _mixer_on_top (false) , _initial_verbose_plugin_scan (false) , _shared_popup_menu (0) + , startup_fsm (0) , secondary_clock_spacer (0) , auto_input_button (ArdourButton::led_default_elements) , latency_disable_button (ArdourButton::led_default_elements) diff --git a/gtk2_ardour/ardour_ui.h b/gtk2_ardour/ardour_ui.h index 890572c2c7..8d35fa737d 100644 --- a/gtk2_ardour/ardour_ui.h +++ b/gtk2_ardour/ardour_ui.h @@ -91,6 +91,7 @@ #include "enums.h" #include "mini_timeline.h" #include "shuttle_control.h" +#include "startup_fsm.h" #include "transport_control.h" #include "transport_control_ui.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 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); + void load_session_from_startup_fsm (); /// @return true if session was successfully unloaded. int unload_session (bool hide_stuff = false); @@ -438,8 +440,12 @@ private: static ARDOUR_UI *theArdourUI; SessionDialog *_session_dialog; + StartupFSM* startup_fsm; + int starting (); int nsm_init (); + void startup_done (); + void sfsm_response (StartupFSM::Result); int ask_about_saving_session (const std::vector& actions); diff --git a/gtk2_ardour/ardour_ui_startup.cc b/gtk2_ardour/ardour_ui_startup.cc index 732252c242..e89e5a2f11 100644 --- a/gtk2_ardour/ardour_ui_startup.cc +++ b/gtk2_ardour/ardour_ui_startup.cc @@ -57,7 +57,6 @@ #include "ardour/filename_extensions.h" #include "ardour/filesystem_paths.h" #include "ardour/profile.h" -#include "ardour/recent_sessions.h" #include "gtkmm2ext/application.h" @@ -480,27 +479,37 @@ ARDOUR_UI::nsm_init () 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 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) { check_announcements (); } - app->ready (); - /* we need to create this early because it may need to set the * audio backend end up. */ + EngineControl* amd; + try { - audio_midi_setup.get (true); + amd = dynamic_cast (audio_midi_setup.get (true)); } catch (...) { std::cerr << "audio-midi engine setup failed."<< std::endl; return -1; @@ -510,68 +519,53 @@ ARDOUR_UI::starting () return -1; } 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; - 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 demos; - find_files_matching_pattern (demos, ds, ARDOUR::session_archive_suffix); - - ARDOUR::RecentSessions rs; - ARDOUR::read_recent_sessions (rs); - - for (vector::iterator i = demos.begin(); i != demos.end (); ++i) { - /* "demo-session" must be inside "demo-session." */ - 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 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 (); WM::Manager::instance().show_visible (); @@ -582,10 +576,6 @@ ARDOUR_UI::starting () _status_bar_visibility.update (); BootMessage (string_compose (_("%1 is ready for use"), PROGRAM_NAME)); - - /* all other dialogs are created conditionally */ - - return 0; } void diff --git a/gtk2_ardour/startup_fsm.cc b/gtk2_ardour/startup_fsm.cc new file mode 100644 index 0000000000..53610b6984 --- /dev/null +++ b/gtk2_ardour/startup_fsm.cc @@ -0,0 +1,662 @@ +/* + * Copyright (C) 2019 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include + +#include +#include +#include + +#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 + +#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 templates; + find_session_templates (templates, false); + for (vector::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 demos; + find_files_matching_pattern (demos, ds, ARDOUR::session_archive_suffix); + + ARDOUR::RecentSessions rs; + ARDOUR::read_recent_sessions (rs); + + for (vector::iterator i = demos.begin(); i != demos.end (); ++i) { + /* "demo-session" must be inside "demo-session." */ + 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 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; +} diff --git a/gtk2_ardour/startup_fsm.h b/gtk2_ardour/startup_fsm.h new file mode 100644 index 0000000000..a1d52eb11a --- /dev/null +++ b/gtk2_ardour/startup_fsm.h @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2019 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef __gtk2_ardour_startup_fsm_h__ +#define __gtk2_ardour_startup_fsm_h__ + +#include + +#include + +#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& 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 _signal_response; +}; + +#endif /* __gtk2_ardour_startup_fsm_h__ */ diff --git a/gtk2_ardour/wscript b/gtk2_ardour/wscript index 7d5dbb1c2c..e80e5da4fc 100644 --- a/gtk2_ardour/wscript +++ b/gtk2_ardour/wscript @@ -175,6 +175,7 @@ gtk2_ardour_sources = [ 'mouse_cursors.cc', 'nag.cc', 'new_plugin_preset_dialog.cc', + 'new_user_wizard.cc', 'normalize_dialog.cc', 'note.cc', 'note_base.cc', @@ -251,7 +252,7 @@ gtk2_ardour_sources = [ 'soundcloud_export_selector.cc', 'splash.cc', 'speaker_dialog.cc', - 'startup.cc', + 'startup_fsm.cc', 'step_editor.cc', 'step_entry.cc', 'stereo_panner.cc',