Allow to send immediate PC messages without closing the dialog.
[ardour.git] / gtk2_ardour / ardour_ui.cc
index b24fbafb05d530f9791bb26a0e76b0d87fac6efd..0ba520217fb8392eb0d62b307644886bdfe77b91 100644 (file)
@@ -78,6 +78,7 @@
 #include "gtkmm2ext/window_title.h"
 
 #include "widgets/fastmeter.h"
+#include "widgets/prompter.h"
 
 #include "ardour/ardour.h"
 #include "ardour/audio_backend.h"
@@ -163,12 +164,12 @@ typedef uint64_t microseconds_t;
 #include "opts.h"
 #include "pingback.h"
 #include "processor_box.h"
-#include "prompter.h"
 #include "public_editor.h"
 #include "rc_option_editor.h"
 #include "route_time_axis.h"
 #include "route_params_ui.h"
 #include "save_as_dialog.h"
+#include "save_template_dialog.h"
 #include "script_selector.h"
 #include "session_archive_dialog.h"
 #include "session_dialog.h"
@@ -2016,16 +2017,6 @@ ARDOUR_UI::open_session ()
        }
 }
 
-void
-ARDOUR_UI::session_add_vca (const string& name_template, uint32_t n)
-{
-       if (!_session) {
-               return;
-       }
-
-       _session->vca_manager().create_vca (n, name_template);
-}
-
 void
 ARDOUR_UI::session_add_mixed_track (
                const ChanCount& input,
@@ -2038,10 +2029,7 @@ ARDOUR_UI::session_add_mixed_track (
                Plugin::PresetRecord* pset,
                ARDOUR::PresentationInfo::order_t order)
 {
-       if (_session == 0) {
-               warning << _("You cannot add a track without a session already loaded.") << endmsg;
-               return;
-       }
+       assert (_session);
 
        if (Profile->get_mixbus ()) {
                strict_io = true;
@@ -2131,10 +2119,7 @@ ARDOUR_UI::session_add_audio_route (
        list<boost::shared_ptr<AudioTrack> > tracks;
        RouteList routes;
 
-       if (_session == 0) {
-               warning << _("You cannot add a track or bus without a session already loaded.") << endmsg;
-               return;
-       }
+       assert (_session);
 
        try {
                if (track) {
@@ -2766,6 +2751,33 @@ ARDOUR_UI::save_session_as ()
                return;
        }
 
+       if (_session->dirty()) {
+               vector<string> actions;
+               actions.push_back (_("Abort save-as"));
+               actions.push_back (_("Don't save now, just save-as"));
+               actions.push_back (_("Save it first"));
+               switch (ask_about_saving_session(actions)) {
+                       case -1:
+                               return;
+                               break;
+                       case 1:
+                               if (save_state_canfail ("")) {
+                                       MessageDialog msg (_main_window,
+                                                       string_compose (_("\
+%1 was unable to save your session.\n\n\
+If you still wish to proceeed, please use the\n\n\
+\"Don't save now\" option."), PROGRAM_NAME));
+                                       pop_back_splash(msg);
+                                       msg.run ();
+                                       return;
+                               }
+                               // no break
+                       case 0:
+                               _session->remove_pending_capture_state ();
+                               break;
+               }
+       }
+
        if (!save_as_dialog) {
                save_as_dialog = new SaveAsDialog;
        }
@@ -2880,13 +2892,16 @@ ARDOUR_UI::quick_snapshot_session (bool switch_to_it)
                time (&n);
                localtime_r (&n, &local_time);
                strftime (timebuf, sizeof(timebuf), "%FT%H.%M.%S", &local_time);
+               if (switch_to_it && _session->dirty ()) {
+                       save_state_canfail ("");
+               }
 
                save_state (timebuf, switch_to_it);
 }
 
 
 bool
-ARDOUR_UI::process_snapshot_session_prompter (ArdourPrompter& prompter, bool switch_to_it)
+ARDOUR_UI::process_snapshot_session_prompter (Prompter& prompter, bool switch_to_it)
 {
        string snapname;
 
@@ -2932,8 +2947,34 @@ ARDOUR_UI::process_snapshot_session_prompter (ArdourPrompter& prompter, bool swi
 void
 ARDOUR_UI::snapshot_session (bool switch_to_it)
 {
-       ArdourPrompter prompter (true);
+       if (switch_to_it && _session->dirty()) {
+               vector<string> actions;
+               actions.push_back (_("Abort saving snapshot"));
+               actions.push_back (_("Don't save now, just snapshot"));
+               actions.push_back (_("Save it first"));
+               switch (ask_about_saving_session(actions)) {
+                       case -1:
+                               return;
+                               break;
+                       case 1:
+                               if (save_state_canfail ("")) {
+                                       MessageDialog msg (_main_window,
+                                                       string_compose (_("\
+%1 was unable to save your session.\n\n\
+If you still wish to proceeed, please use the\n\n\
+\"Don't save now\" option."), PROGRAM_NAME));
+                                       pop_back_splash(msg);
+                                       msg.run ();
+                                       return;
+                               }
+                               // no break
+                       case 0:
+                               _session->remove_pending_capture_state ();
+                               break;
+               }
+       }
 
+       Prompter prompter (true);
        prompter.set_name ("Prompter");
        prompter.add_button (Gtk::Stock::SAVE, Gtk::RESPONSE_ACCEPT);
        if (switch_to_it) {
@@ -2977,7 +3018,7 @@ ARDOUR_UI::rename_session ()
                return;
        }
 
-       ArdourPrompter prompter (true);
+       Prompter prompter (true);
        string name;
 
        prompter.set_name ("Prompter");
@@ -3118,60 +3159,43 @@ ARDOUR_UI::transport_rec_enable_blink (bool onoff)
        }
 }
 
-bool
-ARDOUR_UI::process_save_template_prompter (ArdourPrompter& prompter)
+void
+ARDOUR_UI::save_template_dialog_response (int response, SaveTemplateDialog* d)
 {
-       string name;
-
-       prompter.get_result (name);
+       if (response == RESPONSE_ACCEPT) {
+               const string name = d->get_template_name ();
+               const string desc = d->get_description ();
 
-       if (name.length()) {
-               int failed = _session->save_template (name);
+               int failed = _session->save_template (name, desc);
 
                if (failed == -2) { /* file already exists. */
-                       bool overwrite = overwrite_file_dialog (prompter,
+                       bool overwrite = overwrite_file_dialog (*d,
                                                                _("Confirm Template Overwrite"),
                                                                _("A template already exists with that name. Do you want to overwrite it?"));
 
                        if (overwrite) {
-                               _session->save_template (name, true);
+                               _session->save_template (name, desc, true);
                        }
                        else {
-                               return false;
+                               d->show ();
+                               return;
                        }
                }
        }
-
-       return true;
+       delete d;
 }
 
 void
 ARDOUR_UI::save_template ()
 {
-       ArdourPrompter prompter (true);
-
        if (!check_audioengine (_main_window)) {
                return;
        }
 
-       prompter.set_name (X_("Prompter"));
-       prompter.set_title (_("Save Template"));
-       prompter.set_prompt (_("Name for template:"));
-       prompter.set_initial_text(_session->name() + _("-template"));
-       prompter.add_button (Gtk::Stock::SAVE, Gtk::RESPONSE_ACCEPT);
-
-       bool finished = false;
-       while (!finished) {
-               switch (prompter.run()) {
-               case RESPONSE_ACCEPT:
-                       finished = process_save_template_prompter (prompter);
-                       break;
-
-               default:
-                       finished = true;
-                       break;
-               }
-       }
+       const std::string desc = SessionMetadata::Metadata()->description ();
+       SaveTemplateDialog* d = new SaveTemplateDialog (_session->name (), desc);
+       d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &ARDOUR_UI::save_template_dialog_response), d));
+       d->show ();
 }
 
 void ARDOUR_UI::manage_templates ()
@@ -3229,44 +3253,14 @@ ARDOUR_UI::build_session_from_dialog (SessionDialog& sd, const std::string& sess
        BusProfile bus_profile;
 
        if (nsm) {
-
                bus_profile.master_out_channels = 2;
-               bus_profile.input_ac = AutoConnectPhysical;
-               bus_profile.output_ac = AutoConnectMaster;
-               bus_profile.requested_physical_in = 0; // use all available
-               bus_profile.requested_physical_out = 0; // use all available
-
        } else {
-
                /* get settings from advanced section of NSD */
-
-               if (sd.create_master_bus()) {
-                       bus_profile.master_out_channels = (uint32_t) sd.master_channel_count();
-               } else {
-                       bus_profile.master_out_channels = 0;
-               }
-
-               if (sd.connect_inputs()) {
-                       bus_profile.input_ac = AutoConnectPhysical;
-               } else {
-                       bus_profile.input_ac = AutoConnectOption (0);
-               }
-
-               bus_profile.output_ac = AutoConnectOption (0);
-
-               if (sd.connect_outputs ()) {
-                       if (sd.connect_outs_to_master()) {
-                               bus_profile.output_ac = AutoConnectMaster;
-                       } else if (sd.connect_outs_to_physical()) {
-                               bus_profile.output_ac = AutoConnectPhysical;
-                       }
-               }
-
-               bus_profile.requested_physical_in = (uint32_t) sd.input_limit_count();
-               bus_profile.requested_physical_out = (uint32_t) sd.output_limit_count();
+               bus_profile.master_out_channels = (uint32_t) sd.master_channel_count();
        }
 
-       if (build_session (session_path, session_name, bus_profile)) {
+       // NULL profile: no master, no monitor
+       if (build_session (session_path, session_name, bus_profile.master_out_channels > 0 ? &bus_profile : NULL)) {
                return -1;
        }
 
@@ -3423,14 +3417,11 @@ ARDOUR_UI::get_session_parameters (bool quit_on_cancel, bool should_be_new, stri
                                break;
                        default:
                                if (quit_on_cancel) {
-                                       // JE - Currently (July 2014) this section can only get reached if the
-                                       // user quits from the main 'Session Setup' dialog (i.e. reaching this
-                                       // point does NOT indicate an abnormal termination). Therefore, let's
-                                       // behave gracefully (i.e. let's do some cleanup) before we call exit()
+                                       ARDOUR_UI::finish ();
+                                       Gtkmm2ext::Application::instance()->cleanup();
                                        ARDOUR::cleanup ();
                                        pthread_cancel_all ();
-
-                                       exit (1);
+                                       return -1; // caller is responsible to call exit()
                                } else {
                                        return ret;
                                }
@@ -3555,7 +3546,12 @@ ARDOUR_UI::get_session_parameters (bool quit_on_cancel, bool should_be_new, stri
                        _session_is_new = true;
                }
 
-               if (likely_new && template_name.empty()) {
+               if (!template_name.empty() && template_name.substr (0, 11) == "urn:ardour:") {
+
+                       ret = build_session_from_dialog (session_dialog, session_path, session_name);
+                       meta_session_setup (template_name.substr (11));
+
+               } else if (likely_new && template_name.empty()) {
 
                        ret = build_session_from_dialog (session_dialog, session_path, session_name);
 
@@ -3674,7 +3670,7 @@ ARDOUR_UI::load_session (const std::string& path, const std::string& snap_name,
        }
        catch (SessionException e) {
                MessageDialog msg (string_compose(
-                                          _("Session \"%1 (snapshot %2)\" did not load successfully: %3"),
+                                          _("Session \"%1 (snapshot %2)\" did not load successfully:\n%3"),
                                           path, snap_name, e.what()),
                                   true,
                                   Gtk::MESSAGE_INFO,
@@ -3695,7 +3691,7 @@ ARDOUR_UI::load_session (const std::string& path, const std::string& snap_name,
        catch (...) {
 
                MessageDialog msg (string_compose(
-                                          _("Session \"%1 (snapshot %2)\" did not load successfully"),
+                                          _("Session \"%1 (snapshot %2)\" did not load successfully."),
                                           path, snap_name),
                                   true,
                                   Gtk::MESSAGE_INFO,
@@ -3768,6 +3764,13 @@ ARDOUR_UI::load_session (const std::string& path, const std::string& snap_name,
 #endif
        retval = 0;
 
+       if (!mix_template.empty ()) {
+               /* if mix_template is given, assume this is a new session */
+               string metascript = Glib::build_filename (mix_template, "template.lua");
+               meta_session_setup (metascript);
+       }
+
+
   out:
        /* For successful session load the splash is hidden by ARDOUR_UI::first_idle,
         * which is queued by set_session().
@@ -3781,7 +3784,7 @@ ARDOUR_UI::load_session (const std::string& path, const std::string& snap_name,
 }
 
 int
-ARDOUR_UI::build_session (const std::string& path, const std::string& snap_name, BusProfile& bus_profile)
+ARDOUR_UI::build_session (const std::string& path, const std::string& snap_name, BusProfile* bus_profile)
 {
        Session *new_session;
        int x;
@@ -3798,7 +3801,7 @@ ARDOUR_UI::build_session (const std::string& path, const std::string& snap_name,
        _session_is_new = true;
 
        try {
-               new_session = new Session (*AudioEngine::instance(), path, snap_name, &bus_profile);
+               new_session = new Session (*AudioEngine::instance(), path, snap_name, bus_profile);
        }
 
        catch (SessionException e) {
@@ -3858,6 +3861,141 @@ ARDOUR_UI::build_session (const std::string& path, const std::string& snap_name,
        return 0;
 }
 
+
+static void _lua_print (std::string s) {
+#ifndef NDEBUG
+       std::cout << "LuaInstance: " << s << "\n";
+#endif
+       PBD::info << "LuaInstance: " << s << endmsg;
+}
+
+std::map<std::string, std::string>
+ARDOUR_UI::route_setup_info (const std::string& script_path)
+{
+       std::map<std::string, std::string> rv;
+
+       if (!Glib::file_test (script_path, Glib::FILE_TEST_EXISTS | Glib::FILE_TEST_IS_REGULAR)) {
+               return rv;
+       }
+
+       LuaState lua;
+       lua.Print.connect (&_lua_print);
+       lua.sandbox (true);
+
+       lua_State* L = lua.getState();
+       LuaInstance::register_classes (L);
+       LuaBindings::set_session (L, _session);
+       luabridge::push <PublicEditor *> (L, &PublicEditor::instance());
+       lua_setglobal (L, "Editor");
+
+       lua.do_command ("function ardour () end");
+       lua.do_file (script_path);
+
+       try {
+               luabridge::LuaRef fn = luabridge::getGlobal (L, "route_setup");
+               if (!fn.isFunction ()) {
+                       return rv;
+               }
+               luabridge::LuaRef rs = fn ();
+               if (!rs.isTable ()) {
+                       return rv;
+               }
+               for (luabridge::Iterator i(rs); !i.isNil (); ++i) {
+                       if (!i.key().isString()) {
+                               continue;
+                       }
+                       std::string key = i.key().tostring();
+                       if (i.value().isString() || i.value().isNumber() || i.value().isBoolean()) {
+                               rv[key] = i.value().tostring();
+                       }
+               }
+       } catch (luabridge::LuaException const& e) {
+               cerr << "LuaException:" << e.what () << endl;
+       } catch (...) { }
+       return rv;
+}
+
+void
+ARDOUR_UI::meta_route_setup (const std::string& script_path)
+{
+       if (!Glib::file_test (script_path, Glib::FILE_TEST_EXISTS | Glib::FILE_TEST_IS_REGULAR)) {
+               return;
+       }
+       assert (add_route_dialog);
+
+       int count;
+       if ((count = add_route_dialog->count()) <= 0) {
+               return;
+       }
+
+       LuaState lua;
+       lua.Print.connect (&_lua_print);
+       lua.sandbox (true);
+
+       lua_State* L = lua.getState();
+       LuaInstance::register_classes (L);
+       LuaBindings::set_session (L, _session);
+       luabridge::push <PublicEditor *> (L, &PublicEditor::instance());
+       lua_setglobal (L, "Editor");
+
+       lua.do_command ("function ardour () end");
+       lua.do_file (script_path);
+
+       luabridge::LuaRef args (luabridge::newTable (L));
+
+       args["name"]       = add_route_dialog->name_template ();
+       args["insert_at"]  = translate_order (add_route_dialog->insert_at());
+       args["group"]      = add_route_dialog->route_group ();
+       args["strict_io"]  = add_route_dialog->use_strict_io ();
+       args["instrument"] = add_route_dialog->requested_instrument ();
+       args["track_mode"] = add_route_dialog->mode ();
+       args["channels"]   = add_route_dialog->channel_count ();
+       args["how_many"]   = count;
+
+       try {
+               luabridge::LuaRef fn = luabridge::getGlobal (L, "factory");
+               if (fn.isFunction()) {
+                       fn (args)();
+               }
+       } catch (luabridge::LuaException const& e) {
+               cerr << "LuaException:" << e.what () << endl;
+       } catch (...) {
+               display_insufficient_ports_message ();
+       }
+}
+
+void
+ARDOUR_UI::meta_session_setup (const std::string& script_path)
+{
+       if (!Glib::file_test (script_path, Glib::FILE_TEST_EXISTS | Glib::FILE_TEST_IS_REGULAR)) {
+               return;
+       }
+
+       LuaState lua;
+       lua.Print.connect (&_lua_print);
+       lua.sandbox (true);
+
+       lua_State* L = lua.getState();
+       LuaInstance::register_classes (L);
+       LuaBindings::set_session (L, _session);
+       luabridge::push <PublicEditor *> (L, &PublicEditor::instance());
+       lua_setglobal (L, "Editor");
+
+       lua.do_command ("function ardour () end");
+       lua.do_file (script_path);
+
+       try {
+               luabridge::LuaRef fn = luabridge::getGlobal (L, "factory");
+               if (fn.isFunction()) {
+                       fn ()();
+               }
+       } catch (luabridge::LuaException const& e) {
+               cerr << "LuaException:" << e.what () << endl;
+       } catch (...) {
+               display_insufficient_ports_message ();
+       }
+}
+
 void
 ARDOUR_UI::launch_chat ()
 {
@@ -4302,10 +4440,16 @@ ARDOUR_UI::add_route ()
 void
 ARDOUR_UI::add_route_dialog_response (int r)
 {
+       if (!_session) {
+               warning << _("You cannot add tracks or busses without a session already loaded.") << endmsg;
+               return;
+       }
+
        int count;
 
        switch (r) {
        case AddRouteDialog::Add:
+               add_route_dialog->reset_name_edited ();
                break;
        case AddRouteDialog::AddAndClose:
                add_route_dialog->ArdourDialog::on_response (r);
@@ -4315,26 +4459,31 @@ ARDOUR_UI::add_route_dialog_response (int r)
                return;
        }
 
+       std::string template_path = add_route_dialog->get_template_path();
+       if (!template_path.empty() && template_path.substr (0, 11) == "urn:ardour:") {
+               meta_route_setup (template_path.substr (11));
+               return;
+       }
+
        if ((count = add_route_dialog->count()) <= 0) {
                return;
        }
 
        PresentationInfo::order_t order = translate_order (add_route_dialog->insert_at());
-       string template_path = add_route_dialog->track_template();
+       const string name_template = add_route_dialog->name_template ();
        DisplaySuspender ds;
 
-       if (!template_path.empty()) {
-               if (add_route_dialog->name_template_is_default())  {
-                       _session->new_route_from_template (count, order, template_path, string());
+       if (!template_path.empty ()) {
+               if (add_route_dialog->name_template_is_default ()) {
+                       _session->new_route_from_template (count, order, template_path, string ());
                } else {
-                       _session->new_route_from_template (count, order, template_path, add_route_dialog->name_template());
+                       _session->new_route_from_template (count, order, template_path, name_template);
                }
                return;
        }
 
        ChanCount input_chan= add_route_dialog->channels ();
        ChanCount output_chan;
-       string name_template = add_route_dialog->name_template ();
        PluginInfoPtr instrument = add_route_dialog->requested_instrument ();
        RouteGroup* route_group = add_route_dialog->route_group ();
        AutoConnectOption oac = Config->get_output_auto_connect();
@@ -4353,22 +4502,22 @@ ARDOUR_UI::add_route_dialog_response (int r)
 
        switch (add_route_dialog->type_wanted()) {
        case AddRouteDialog::AudioTrack:
-               session_add_audio_track (input_chan.n_audio(), output_chan.n_audio(), add_route_dialog->mode(), route_group, count, name_template, strict_io, order);
+               session_add_audio_route (true, input_chan.n_audio(), output_chan.n_audio(), add_route_dialog->mode(), route_group, count, name_template, strict_io, order);
                break;
        case AddRouteDialog::MidiTrack:
-               session_add_midi_track (route_group, count, name_template, strict_io, instrument, 0, order);
+               session_add_midi_route (true, route_group, count, name_template, strict_io, instrument, 0, order);
                break;
        case AddRouteDialog::MixedTrack:
                session_add_mixed_track (input_chan, output_chan, route_group, count, name_template, strict_io, instrument, 0, order);
                break;
        case AddRouteDialog::AudioBus:
-               session_add_audio_bus (input_chan.n_audio(), output_chan.n_audio(), route_group, count, name_template, strict_io, order);
+               session_add_audio_route (false, input_chan.n_audio(), output_chan.n_audio(), ARDOUR::Normal, route_group, count, name_template, strict_io, order);
                break;
        case AddRouteDialog::MidiBus:
                session_add_midi_bus (route_group, count, name_template, strict_io, instrument, 0, order);
                break;
        case AddRouteDialog::VCAMaster:
-               session_add_vca (name_template, count);
+               _session->vca_manager().create_vca (count, name_template);
                break;
        }
 }