Add support for scripted meta-templates.
[ardour.git] / gtk2_ardour / ardour_ui.cc
index 07a4e5a5b8e2517fd0430fd8daa26780dd83c6ea..0748d418c269a17aea0ad7253ea5c957815e21cb 100644 (file)
 #include <sys/resource.h>
 #endif
 
+#ifdef __FreeBSD__
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#endif
+
 #include <stdint.h>
 #include <fcntl.h>
 #include <signal.h>
 #include <glib.h>
 #include "pbd/gstdio_compat.h"
 
-#include <gtkmm/messagedialog.h>
 #include <gtkmm/accelmap.h>
+#include <gtkmm/messagedialog.h>
+#include <gtkmm/stock.h>
+#include <gtkmm/uimanager.h>
 
 #include "pbd/error.h"
 #include "pbd/basename.h"
 #include "pbd/compose.h"
 #include "pbd/convert.h"
 #include "pbd/failed_constructor.h"
+#include "pbd/file_archive.h"
 #include "pbd/enumwriter.h"
 #include "pbd/memento_command.h"
 #include "pbd/openuri.h"
 #include "pbd/stl_delete.h"
+#include "pbd/types_convert.h"
+#include "pbd/unwind.h"
 #include "pbd/file_utils.h"
 #include "pbd/localtime_r.h"
 #include "pbd/pthread_utils.h"
 #include "pbd/replace_all.h"
+#include "pbd/scoped_file_descriptor.h"
 #include "pbd/xml++.h"
 
 #include "gtkmm2ext/application.h"
 #include "gtkmm2ext/bindings.h"
 #include "gtkmm2ext/gtk_ui.h"
 #include "gtkmm2ext/utils.h"
-#include "gtkmm2ext/click_box.h"
-#include "gtkmm2ext/fastmeter.h"
-#include "gtkmm2ext/popup.h"
 #include "gtkmm2ext/window_title.h"
 
+#include "widgets/fastmeter.h"
+#include "widgets/prompter.h"
+
 #include "ardour/ardour.h"
 #include "ardour/audio_backend.h"
 #include "ardour/audio_track.h"
@@ -85,6 +96,8 @@
 #include "ardour/process_thread.h"
 #include "ardour/profile.h"
 #include "ardour/recent_sessions.h"
+#include "ardour/record_enable_control.h"
+#include "ardour/revision.h"
 #include "ardour/session_directory.h"
 #include "ardour/session_route.h"
 #include "ardour/session_state_utils.h"
@@ -116,6 +129,7 @@ typedef uint64_t microseconds_t;
 
 #include "about.h"
 #include "editing.h"
+#include "enums_convert.h"
 #include "actions.h"
 #include "add_route_dialog.h"
 #include "ambiguous_file_dialog.h"
@@ -133,6 +147,7 @@ typedef uint64_t microseconds_t;
 #include "global_port_matrix.h"
 #include "gui_object.h"
 #include "gui_thread.h"
+#include "idleometer.h"
 #include "keyboard.h"
 #include "keyeditor.h"
 #include "location_ui.h"
@@ -143,39 +158,42 @@ typedef uint64_t microseconds_t;
 #include "missing_plugin_dialog.h"
 #include "mixer_ui.h"
 #include "meterbridge.h"
+#include "meter_patterns.h"
 #include "mouse_cursors.h"
 #include "nsm.h"
 #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 "script_selector.h"
+#include "session_archive_dialog.h"
 #include "session_dialog.h"
 #include "session_metadata_dialog.h"
 #include "session_option_editor.h"
-#include "shuttle_control.h"
 #include "speaker_dialog.h"
 #include "splash.h"
 #include "startup.h"
-#include "theme_manager.h"
+#include "template_dialog.h"
 #include "time_axis_view_item.h"
+#include "time_info_box.h"
 #include "timers.h"
 #include "utils.h"
+#include "utils_videotl.h"
 #include "video_server_dialog.h"
 #include "add_video_dialog.h"
 #include "transcode_video_dialog.h"
 
-#include "i18n.h"
+#include "pbd/i18n.h"
 
 using namespace ARDOUR;
 using namespace ARDOUR_UI_UTILS;
 using namespace PBD;
 using namespace Gtkmm2ext;
+using namespace ArdourWidgets;
 using namespace Gtk;
 using namespace std;
 using namespace Editing;
@@ -183,7 +201,7 @@ using namespace Editing;
 ARDOUR_UI *ARDOUR_UI::theArdourUI = 0;
 
 sigc::signal<void, framepos_t, bool, framepos_t> ARDOUR_UI::Clock;
-sigc::signal<void>      ARDOUR_UI::CloseAllDialogs;
+sigc::signal<void> ARDOUR_UI::CloseAllDialogs;
 
 static bool
 ask_about_configuration_copy (string const & old_dir, string const & new_dir, int version)
@@ -196,7 +214,7 @@ ask_about_configuration_copy (string const & old_dir, string const & new_dir, in
                           Gtk::MESSAGE_INFO,
                           Gtk::BUTTONS_YES_NO,
                           true /* modal, though it hardly matters since it is the only window */
-               );
+       );
 
        msg.set_default_response (Gtk::RESPONSE_YES);
        msg.show_all ();
@@ -206,8 +224,8 @@ ask_about_configuration_copy (string const & old_dir, string const & new_dir, in
 
 static void
 libxml_generic_error_func (void* /* parsing_context*/,
-                   const char* msg,
-                   ...)
+                           const char* msg,
+                           ...)
 {
        va_list ap;
        char buf[2048];
@@ -248,6 +266,7 @@ libxml_structured_error_func (void* /* parsing_context*/,
 ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir)
        : Gtkmm2ext::UI (PROGRAM_NAME, X_("gui"), argcp, argvp)
        , session_loaded (false)
+       , session_load_in_progress (false)
        , gui_object_state (new GUIObjectState)
        , primary_clock   (new MainClock (X_("primary"),   X_("transport"), true ))
        , secondary_clock (new MainClock (X_("secondary"), X_("secondary"), false))
@@ -263,6 +282,7 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir)
        , _mixer_on_top (false)
        , _initial_verbose_plugin_scan (false)
        , first_time_engine_run (true)
+       , secondary_clock_spacer (0)
        , roll_controllable (new TransportControllable ("transport roll", *this, TransportControllable::Roll))
        , stop_controllable (new TransportControllable ("transport stop", *this, TransportControllable::Stop))
        , goto_start_controllable (new TransportControllable ("transport goto start", *this, TransportControllable::GotoStart))
@@ -270,15 +290,17 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir)
        , auto_loop_controllable (new TransportControllable ("transport auto loop", *this, TransportControllable::AutoLoop))
        , play_selection_controllable (new TransportControllable ("transport play selection", *this, TransportControllable::PlaySelection))
        , rec_controllable (new TransportControllable ("transport rec-enable", *this, TransportControllable::RecordEnable))
+       , auto_input_button (ArdourButton::led_default_elements)
+       , time_info_box (0)
        , auto_return_button (ArdourButton::led_default_elements)
        , follow_edits_button (ArdourButton::led_default_elements)
-       , auto_input_button (ArdourButton::led_default_elements)
        , auditioning_alert_button (_("Audition"))
        , solo_alert_button (_("Solo"))
        , feedback_alert_button (_("Feedback"))
        , error_alert_button ( ArdourButton::just_led_default_elements )
-       , editor_meter(0)
        , editor_meter_peak_display()
+       , editor_meter(0)
+       , _suspend_editor_meter_callbacks (false)
        , _numpad_locate_happening (false)
        , _session_is_new (false)
        , last_key_press_time (0)
@@ -289,18 +311,19 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir)
        , speaker_config_window (X_("speaker-config"), _("Speaker Configuration"))
        , add_route_dialog (X_("add-routes"), _("Add Tracks/Busses"))
        , about (X_("about"), _("About"))
-       , location_ui (X_("locations"), _("Locations"))
+       , location_ui (X_("locations"), S_("Ranges|Locations"))
        , route_params (X_("inspector"), _("Tracks and Busses"))
        , audio_midi_setup (X_("audio-midi-setup"), _("Audio/MIDI Setup"))
        , export_video_dialog (X_("video-export"), _("Video Export Dialog"))
        , lua_script_window (X_("script-manager"), _("Script Manager"))
+       , idleometer (X_("idle-o-meter"), _("Idle'o'Meter"))
        , session_option_editor (X_("session-options-editor"), _("Properties"), boost::bind (&ARDOUR_UI::create_session_option_editor, this))
        , add_video_dialog (X_("add-video"), _("Add Video"), boost::bind (&ARDOUR_UI::create_add_video_dialog, this))
        , bundle_manager (X_("bundle-manager"), _("Bundle Manager"), boost::bind (&ARDOUR_UI::create_bundle_manager, this))
        , big_clock_window (X_("big-clock"), _("Big Clock"), boost::bind (&ARDOUR_UI::create_big_clock_window, this))
        , audio_port_matrix (X_("audio-connection-manager"), _("Audio Connections"), boost::bind (&ARDOUR_UI::create_global_port_matrix, this, ARDOUR::DataType::AUDIO))
        , midi_port_matrix (X_("midi-connection-manager"), _("MIDI Connections"), boost::bind (&ARDOUR_UI::create_global_port_matrix, this, ARDOUR::DataType::MIDI))
-       , key_editor (X_("key-editor"), _("Bindings Editor"), boost::bind (&ARDOUR_UI::create_key_editor, this))
+       , key_editor (X_("key-editor"), _("Keyboard Shortcuts"), boost::bind (&ARDOUR_UI::create_key_editor, this))
        , video_server_process (0)
        , splash (0)
        , have_configure_timeout (false)
@@ -320,12 +343,24 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir)
        UIConfiguration::instance().post_gui_init ();
 
        if (ARDOUR::handle_old_configuration_files (boost::bind (ask_about_configuration_copy, _1, _2, _3))) {
+               {
+                       /* "touch" the been-here-before path now that config has been migrated */
+                       PBD::ScopedFileDescriptor fout (g_open (been_here_before_path ().c_str(), O_CREAT|O_TRUNC|O_RDWR, 0666));
+               }
                MessageDialog msg (string_compose (_("Your configuration files were copied. You can now restart %1."), PROGRAM_NAME), true);
                msg.run ();
                /* configuration was modified, exit immediately */
                _exit (0);
        }
 
+
+       if (string (VERSIONSTRING).find (".pre") != string::npos) {
+               /* check this is not being run from ./ardev etc. */
+               if (!running_from_source_tree ()) {
+                       pre_release_dialog ();
+               }
+       }
+
        if (theArdourUI == 0) {
                theArdourUI = this;
        }
@@ -430,7 +465,7 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir)
 
        TimeAxisViewItem::set_constant_heights ();
 
-        /* Set this up so that our window proxies can register actions */
+       /* Set this up so that our window proxies can register actions */
 
        ActionManager::init ();
 
@@ -453,6 +488,7 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir)
                midi_port_matrix.set_state (*ui_xml, 0);
                export_video_dialog.set_state (*ui_xml, 0);
                lua_script_window.set_state (*ui_xml, 0);
+               idleometer.set_state (*ui_xml, 0);
        }
 
        /* Separate windows */
@@ -472,6 +508,10 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir)
        WM::Manager::instance().register_window (&big_clock_window);
        WM::Manager::instance().register_window (&audio_port_matrix);
        WM::Manager::instance().register_window (&midi_port_matrix);
+       WM::Manager::instance().register_window (&idleometer);
+
+       /* do not retain position for add route dialog */
+       add_route_dialog.set_state_mask (WindowProxy::Size);
 
        /* Trigger setting up the color scheme and loading the GTK RC file */
 
@@ -485,6 +525,40 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir)
        attach_to_engine ();
 }
 
+void
+ARDOUR_UI::pre_release_dialog ()
+{
+       ArdourDialog d (_("Pre-Release Warning"), true, false);
+       d.add_button (Gtk::Stock::OK, Gtk::RESPONSE_OK);
+
+       Label* label = manage (new Label);
+       label->set_markup (string_compose (_("<b>Welcome to this pre-release build of %1 %2</b>\n\n\
+There are still several issues and bugs to be worked on,\n\
+as well as general workflow improvements, before this can be considered\n\
+release software. So, a few guidelines:\n\
+\n\
+1) Please do <b>NOT</b> use this software with the expectation that it is stable or reliable\n\
+   though it may be so, depending on your workflow.\n\
+2) Please wait for a helpful writeup of new features.\n\
+3) <b>Please do NOT use the forums at ardour.org to report issues</b>.\n\
+4) Please <b>DO</b> use the bugtracker at http://tracker.ardour.org/ to report issues\n\
+   making sure to note the product version number as 5.0-pre.\n\
+5) Please <b>DO</b> use the ardour-users mailing list to discuss ideas and pass on comments.\n\
+6) Please <b>DO</b> join us on IRC for real time discussions about %1 %2. You\n\
+   can get there directly from within the program via the Help->Chat menu option.\n\
+\n\
+Full information on all the above can be found on the support page at\n\
+\n\
+                http://ardour.org/support\n\
+"), PROGRAM_NAME, VERSIONSTRING));
+
+       d.get_vbox()->set_border_width (12);
+       d.get_vbox()->pack_start (*label, false, false, 12);
+       d.get_vbox()->show_all ();
+
+       d.run ();
+}
+
 GlobalPortMatrixWindow*
 ARDOUR_UI::create_global_port_matrix (ARDOUR::DataType type)
 {
@@ -618,6 +692,55 @@ ARDOUR_UI::post_engine ()
         */
 
        if (ARDOUR_COMMAND_LINE::show_key_actions) {
+               stringstream sstr;
+               Bindings::save_all_bindings_as_html (sstr);
+
+               if (sstr.str().empty()) {
+                       return;
+               }
+               gchar* file_name;
+               GError *err = NULL;
+               gint fd;
+
+               if ((fd = g_file_open_tmp ("akprintXXXXXX.html", &file_name, &err)) < 0) {
+                       if (err) {
+                               error << string_compose (_("Could not open temporary file to print bindings (%1)"), err->message) << endmsg;
+                               g_error_free (err);
+                       }
+                       return;
+               }
+
+#ifdef PLATFORM_WINDOWS
+               ::close (fd);
+#endif
+
+               err = NULL;
+
+               if (!g_file_set_contents (file_name, sstr.str().c_str(), sstr.str().size(), &err)) {
+#ifndef PLATFORM_WINDOWS
+                       ::close (fd);
+#endif
+                       g_unlink (file_name);
+                       if (err) {
+                               error << string_compose (_("Could not save bindings to file (%1)"), err->message) << endmsg;
+                               g_error_free (err);
+                       }
+                       return;
+               }
+
+#ifndef PLATFORM_WINDOWS
+               ::close (fd);
+#endif
+
+               PBD::open_uri (string_compose ("file:///%1", file_name));
+
+               halt_connection.disconnect ();
+               AudioEngine::instance()->stop ();
+               exit (0);
+
+       }
+
+       if (ARDOUR_COMMAND_LINE::show_actions) {
 
 
                vector<string> paths;
@@ -625,21 +748,70 @@ ARDOUR_UI::post_engine ()
                vector<string> tooltips;
                vector<string> keys;
                vector<Glib::RefPtr<Gtk::Action> > actions;
+               string ver_in = revision;
+               string ver = ver_in.substr(0, ver_in.find("-"));
+
+               stringstream output;
+               output << "\n<h2>Menu actions</h2>" << endl;
+               output << "<p>\n  Every single menu item in " << PROGRAM_NAME << "'s GUI is accessible by control" << endl;
+               output << "  surfaces or scripts.\n</p>\n" << endl;
+               output << "<p>\n  The list below shows all available values of <em>action-name</em> as of" << endl;
+               output << "  " << PROGRAM_NAME << " " << ver << ". You can get the current list at any" << endl;
+               output << "  time by running " << PROGRAM_NAME << " with the -A flag.\n</p>\n" << endl;
+               output << "<table class=\"dl\">\n  <thead>" << endl;
+               output << "      <tr><th>Action Name</th><th>Menu Name</th></tr>" << endl;
+               output << "  </thead>\n  <tbody>" << endl;
 
                Gtkmm2ext::ActionMap::get_all_actions (paths, labels, tooltips, keys, actions);
 
-               vector<string>::iterator k;
                vector<string>::iterator p;
+               vector<string>::iterator l;
 
-               for (p = paths.begin(), k = keys.begin(); p != paths.end(); ++k, ++p) {
+               for (p = paths.begin(), l = labels.begin(); p != paths.end(); ++p, ++l) {
+                       output << "     <tr><th><kbd class=\"osc\">" << (*p).substr (9, string::npos);
+                       output << "</kbd></th><td>" << *l << "</td></tr>" << endl;
+               }
+               output << "  </tbody>\n  </table>" << endl;
+
+               // output this mess to a browser for easiest X-platform use
+               // it is not pretty HTML, but it works and it's main purpose
+               // is to create raw html to fit in Ardour's manual with no editing
+               gchar* file_name;
+               GError *err = NULL;
+               gint fd;
+
+               if ((fd = g_file_open_tmp ("akprintXXXXXX.html", &file_name, &err)) < 0) {
+                       if (err) {
+                               error << string_compose (_("Could not open temporary file to print bindings (%1)"), err->message) << endmsg;
+                               g_error_free (err);
+                       }
+                       return;
+               }
 
-                       if ((*k).empty()) {
-                               cout << *p << endl;
-                       } else {
-                               cout << *p << " => " << *k << endl;
+#ifdef PLATFORM_WINDOWS
+               ::close (fd);
+#endif
+
+               err = NULL;
+
+               if (!g_file_set_contents (file_name, output.str().c_str(), output.str().size(), &err)) {
+#ifndef PLATFORM_WINDOWS
+                       ::close (fd);
+#endif
+                       g_unlink (file_name);
+                       if (err) {
+                               error << string_compose (_("Could not save bindings to file (%1)"), err->message) << endmsg;
+                               g_error_free (err);
                        }
+                       return;
                }
 
+#ifndef PLATFORM_WINDOWS
+               ::close (fd);
+#endif
+
+               PBD::open_uri (string_compose ("file:///%1", file_name));
+
                halt_connection.disconnect ();
                AudioEngine::instance()->stop ();
                exit (0);
@@ -685,15 +857,17 @@ ARDOUR_UI::~ARDOUR_UI ()
                delete primary_clock; primary_clock = 0;
                delete secondary_clock; secondary_clock = 0;
                delete _process_thread; _process_thread = 0;
+               delete time_info_box; time_info_box = 0;
                delete meterbridge; meterbridge = 0;
                delete luawindow; luawindow = 0;
                delete editor; editor = 0;
                delete mixer; mixer = 0;
+               delete rc_option_editor; rc_option_editor = 0; // failed to wrap object warning
                delete nsm; nsm = 0;
                delete gui_object_state; gui_object_state = 0;
                delete main_window_visibility;
                FastMeter::flush_pattern_cache ();
-               PixFader::flush_pattern_cache ();
+               ArdourFader::flush_pattern_cache ();
        }
 
 #ifndef NDEBUG
@@ -750,31 +924,31 @@ ARDOUR_UI::configure_handler (GdkEventConfigure* /*conf*/)
 void
 ARDOUR_UI::set_transport_controllable_state (const XMLNode& node)
 {
-       XMLProperty const * prop;
+       std::string str;
 
-       if ((prop = node.property ("roll")) != 0) {
-               roll_controllable->set_id (prop->value());
+       if (node.get_property ("roll", str)){
+               roll_controllable->set_id (str);
        }
-       if ((prop = node.property ("stop")) != 0) {
-               stop_controllable->set_id (prop->value());
+       if (node.get_property ("stop", str)) {
+               stop_controllable->set_id (str);
        }
-       if ((prop = node.property ("goto-start")) != 0) {
-               goto_start_controllable->set_id (prop->value());
+       if (node.get_property ("goto-start", str)) {
+               goto_start_controllable->set_id (str);
        }
-       if ((prop = node.property ("goto-end")) != 0) {
-               goto_end_controllable->set_id (prop->value());
+       if (node.get_property ("goto-end", str)) {
+               goto_end_controllable->set_id (str);
        }
-       if ((prop = node.property ("auto-loop")) != 0) {
-               auto_loop_controllable->set_id (prop->value());
+       if (node.get_property ("auto-loop", str)) {
+               auto_loop_controllable->set_id (str);
        }
-       if ((prop = node.property ("play-selection")) != 0) {
-               play_selection_controllable->set_id (prop->value());
+       if (node.get_property ("play-selection", str)) {
+               play_selection_controllable->set_id (str);
        }
-       if ((prop = node.property ("rec")) != 0) {
-               rec_controllable->set_id (prop->value());
+       if (node.get_property ("rec", str)) {
+               rec_controllable->set_id (str);
        }
-       if ((prop = node.property ("shuttle")) != 0) {
-               shuttle_box->controllable()->set_id (prop->value());
+       if (node.get_property ("shuttle", str)) {
+               shuttle_box.controllable()->set_id (str);
        }
 }
 
@@ -782,24 +956,15 @@ XMLNode&
 ARDOUR_UI::get_transport_controllable_state ()
 {
        XMLNode* node = new XMLNode(X_("TransportControllables"));
-       char buf[64];
 
-       roll_controllable->id().print (buf, sizeof (buf));
-       node->add_property (X_("roll"), buf);
-       stop_controllable->id().print (buf, sizeof (buf));
-       node->add_property (X_("stop"), buf);
-       goto_start_controllable->id().print (buf, sizeof (buf));
-       node->add_property (X_("goto_start"), buf);
-       goto_end_controllable->id().print (buf, sizeof (buf));
-       node->add_property (X_("goto_end"), buf);
-       auto_loop_controllable->id().print (buf, sizeof (buf));
-       node->add_property (X_("auto_loop"), buf);
-       play_selection_controllable->id().print (buf, sizeof (buf));
-       node->add_property (X_("play_selection"), buf);
-       rec_controllable->id().print (buf, sizeof (buf));
-       node->add_property (X_("rec"), buf);
-       shuttle_box->controllable()->id().print (buf, sizeof (buf));
-       node->add_property (X_("shuttle"), buf);
+       node->set_property (X_("roll"), roll_controllable->id());
+       node->set_property (X_("stop"), stop_controllable->id());
+       node->set_property (X_("goto-start"), goto_start_controllable->id());
+       node->set_property (X_("goto-end"), goto_end_controllable->id());
+       node->set_property (X_("auto-loop"), auto_loop_controllable->id());
+       node->set_property (X_("play-selection"), play_selection_controllable->id());
+       node->set_property (X_("rec"), rec_controllable->id());
+       node->set_property (X_("shuttle"), shuttle_box.controllable()->id());
 
        return *node;
 }
@@ -1017,6 +1182,45 @@ ARDOUR_UI::starting ()
                        }
                }
 
+               // 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, "*.tar.xz");
+
+                       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.tar.xz"
+                                * strip ".tar.xz"
+                                */
+                               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 (...) {}
+                       }
+               }
+
 #ifdef NO_PLUGIN_STATE
 
                ARDOUR::RecentSessions rs;
@@ -1082,11 +1286,6 @@ ARDOUR_UI::starting ()
 
        BootMessage (string_compose (_("%1 is ready for use"), PROGRAM_NAME));
 
-       if (splash && splash->is_visible()) {
-               // in 1 second, hide the splash screen
-               Glib::signal_timeout().connect (sigc::bind (sigc::ptr_fun (_hide_splash), this), 1000);
-       }
-
        /* all other dialogs are created conditionally */
 
        return 0;
@@ -1232,6 +1431,10 @@ If you still wish to quit, please use the\n\n\
        */
        save_ardour_state ();
 
+       if (key_editor.get (false)) {
+               key_editor->disconnect ();
+       }
+
        close_all_dialogs ();
 
        if (_session) {
@@ -1353,7 +1556,7 @@ ARDOUR_UI::every_point_zero_something_seconds ()
 {
        // august 2007: actual update frequency: 25Hz (40ms), not 100Hz
 
-       if (editor_meter && UIConfiguration::instance().get_show_editor_meter()) {
+       if (editor_meter && UIConfiguration::instance().get_show_editor_meter() && editor_meter_peak_display.is_mapped ()) {
                float mpeak = editor_meter->update_meters();
                if (mpeak > editor_meter_max_peak) {
                        if (mpeak >= UIConfiguration::instance().get_meter_peak()) {
@@ -1723,10 +1926,6 @@ ARDOUR_UI::open_recent_session ()
 
                can_return = false;
        }
-       if (splash && splash->is_visible()) {
-               // in 1 second, hide the splash screen
-               Glib::signal_timeout().connect (sigc::bind (sigc::ptr_fun (_hide_splash), this), 1000);
-       }
 }
 
 bool
@@ -1734,9 +1933,9 @@ ARDOUR_UI::check_audioengine (Gtk::Window& parent)
 {
        if (!AudioEngine::instance()->connected()) {
                MessageDialog msg (parent, string_compose (
-                                          _("%1 is not connected to any audio backend.\n"
-                                            "You cannot open or close sessions in this condition"),
-                                          PROGRAM_NAME));
+                                          _("%1 is not connected to any audio backend.\n"
+                                          "You cannot open or close sessions in this condition"),
+                                          PROGRAM_NAME));
                pop_back_splash (msg);
                msg.run ();
                return false;
@@ -1778,6 +1977,13 @@ ARDOUR_UI::open_session ()
        session_filter.add_pattern (string_compose(X_("*%1"), ARDOUR::statefile_suffix));
        session_filter.set_name (string_compose (_("%1 sessions"), PROGRAM_NAME));
        open_session_selector.add_filter (session_filter);
+
+       FileFilter archive_filter;
+       archive_filter.add_pattern (X_("*.tar.xz"));
+       archive_filter.set_name (_("Session Archives"));
+
+       open_session_selector.add_filter (archive_filter);
+
        open_session_selector.set_filter (session_filter);
 
        int response = open_session_selector.run();
@@ -1792,7 +1998,18 @@ ARDOUR_UI::open_session ()
        bool isnew;
 
        if (session_path.length() > 0) {
-               if (ARDOUR::find_session (session_path, path, name, isnew) == 0) {
+               int rv = ARDOUR::inflate_session (session_path,
+                               Config->get_default_session_parent_dir(), path, name);
+               if (rv == 0) {
+                       _session_is_new = false;
+                       load_session (path, name);
+               }
+               else if (rv < 0) {
+                       MessageDialog msg (_main_window,
+                                       string_compose (_("Extracting session-archive failed: %1"), inflate_error (rv)));
+                       msg.run ();
+               }
+               else if (ARDOUR::find_session (session_path, path, name, isnew) == 0) {
                        _session_is_new = isnew;
                        load_session (path, name);
                }
@@ -1821,15 +2038,18 @@ ARDOUR_UI::session_add_mixed_track (
                Plugin::PresetRecord* pset,
                ARDOUR::PresentationInfo::order_t order)
 {
-       list<boost::shared_ptr<MidiTrack> > tracks;
-
        if (_session == 0) {
                warning << _("You cannot add a track without a session already loaded.") << endmsg;
                return;
        }
 
+       if (Profile->get_mixbus ()) {
+               strict_io = true;
+       }
+
        try {
-               tracks = _session->new_midi_track (input, output, instrument, pset, route_group, how_many, name_template, order, ARDOUR::Normal);
+               list<boost::shared_ptr<MidiTrack> > tracks;
+               tracks = _session->new_midi_track (input, output, strict_io, instrument, pset, route_group, how_many, name_template, order, ARDOUR::Normal);
 
                if (tracks.size() != how_many) {
                        error << string_compose(P_("could not create %1 new mixed track", "could not create %1 new mixed tracks", how_many), how_many) << endmsg;
@@ -1840,12 +2060,6 @@ ARDOUR_UI::session_add_mixed_track (
                display_insufficient_ports_message ();
                return;
        }
-
-       if (strict_io) {
-               for (list<boost::shared_ptr<MidiTrack> >::iterator i = tracks.begin(); i != tracks.end(); ++i) {
-                       (*i)->set_strict_io (true);
-               }
-       }
 }
 
 void
@@ -1858,16 +2072,18 @@ ARDOUR_UI::session_add_midi_bus (
                Plugin::PresetRecord* pset,
                ARDOUR::PresentationInfo::order_t order)
 {
-       RouteList routes;
-
        if (_session == 0) {
                warning << _("You cannot add a track without a session already loaded.") << endmsg;
                return;
        }
 
-       try {
+       if (Profile->get_mixbus ()) {
+               strict_io = true;
+       }
 
-               routes = _session->new_midi_route (route_group, how_many, name_template, instrument, pset, PresentationInfo::MidiBus, order);
+       try {
+               RouteList routes;
+               routes = _session->new_midi_route (route_group, how_many, name_template, strict_io, instrument, pset, PresentationInfo::MidiBus, order);
                if (routes.size() != how_many) {
                        error << string_compose(P_("could not create %1 new Midi Bus", "could not create %1 new Midi Busses", how_many), how_many) << endmsg;
                }
@@ -1877,12 +2093,6 @@ ARDOUR_UI::session_add_midi_bus (
                display_insufficient_ports_message ();
                return;
        }
-
-       if (strict_io) {
-               for (RouteList::iterator i = routes.begin(); i != routes.end(); ++i) {
-                       (*i)->set_strict_io (true);
-               }
-       }
 }
 
 void
@@ -2096,7 +2306,7 @@ ARDOUR_UI::trx_record_enable_all_tracks ()
        }
 
        if (none_record_enabled) {
-               _session->set_controls (route_list_to_control_list (rl, &Track::rec_enable_control), 1.0, Controllable::NoGroup);
+               _session->set_controls (route_list_to_control_list (rl, &Stripable::rec_enable_control), 1.0, Controllable::NoGroup);
        }
 
        return none_record_enabled;
@@ -2254,7 +2464,12 @@ ARDOUR_UI::toggle_roll (bool with_abort, bool roll_out_of_bounded_mode)
        if (affect_transport) {
                if (rolling) {
                        _session->request_stop (with_abort, true);
-               } else {
+
+               } else if (!with_abort) { /* with_abort == true means the
+                                          * command was intended to stop
+                                          * transport, not start.
+                                          */
+
                        /* the only external sync condition we can be in here
                         * would be Engine (JACK) sync, in which case we still
                         * want to do this.
@@ -2326,6 +2541,24 @@ ARDOUR_UI::transport_play_preroll ()
        editor->play_with_preroll ();
 }
 
+void
+ARDOUR_UI::transport_rec_preroll ()
+{
+       if (!_session) {
+               return;
+       }
+       editor->rec_with_preroll ();
+}
+
+void
+ARDOUR_UI::transport_rec_count_in ()
+{
+       if (!_session) {
+               return;
+       }
+       editor->rec_with_count_in ();
+}
+
 void
 ARDOUR_UI::transport_rewind (int option)
 {
@@ -2407,10 +2640,11 @@ ARDOUR_UI::map_transport_state ()
                play_selection_button.unset_active_state ();
                roll_button.unset_active_state ();
                stop_button.set_active_state (Gtkmm2ext::ExplicitActive);
+               layered_button.set_sensitive (false);
                return;
        }
 
-       shuttle_box->map_transport_state ();
+       shuttle_box.map_transport_state ();
 
        float sp = _session->transport_speed();
 
@@ -2446,11 +2680,13 @@ ARDOUR_UI::map_transport_state ()
                        roll_button.set_active (true);
                        play_selection_button.set_active (true);
                }
+               layered_button.set_sensitive (!_session->actively_recording ());
 
                stop_button.set_active (false);
 
        } else {
 
+               layered_button.set_sensitive (true);
                stop_button.set_active (true);
                roll_button.set_active (false);
                play_selection_button.set_active (false);
@@ -2467,11 +2703,15 @@ void
 ARDOUR_UI::blink_handler (bool blink_on)
 {
        transport_rec_enable_blink (blink_on);
-       solo_blink (blink_on);
        sync_blink (blink_on);
+
+       if (!UIConfiguration::instance().get_blink_alert_indicators()) {
+               blink_on = true;
+       }
+       error_blink (blink_on);
+       solo_blink (blink_on);
        audition_blink (blink_on);
        feedback_blink (blink_on);
-       error_blink (blink_on);
 }
 
 void
@@ -2526,6 +2766,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;
        }
@@ -2560,16 +2827,17 @@ ARDOUR_UI::save_session_as ()
        */
 
        ArdourDialog progress_dialog (_("Save As"), true);
+       ScopedConnection c;
 
        if (sa.include_media && sa.copy_media) {
 
-               Gtk::Label label;
-               Gtk::ProgressBar progress_bar;
+               Gtk::Label* label = manage (new Gtk::Label());
+               Gtk::ProgressBar* progress_bar = manage (new Gtk::ProgressBar ());
 
-               progress_dialog.get_vbox()->pack_start (label);
-               progress_dialog.get_vbox()->pack_start (progress_bar);
-               label.show ();
-               progress_bar.show ();
+               progress_dialog.get_vbox()->pack_start (*label);
+               progress_dialog.get_vbox()->pack_start (*progress_bar);
+               label->show ();
+               progress_bar->show ();
 
                /* this signal will be emitted from within this, the calling thread,
                 * after every file is copied. It provides information on percentage
@@ -2577,9 +2845,7 @@ ARDOUR_UI::save_session_as ()
                 * copied so far, and the total number to copy.
                 */
 
-               ScopedConnection c;
-
-               sa.Progress.connect_same_thread (c, boost::bind (&ARDOUR_UI::save_as_progress_update, this, _1, _2, _3, &label, &progress_bar));
+               sa.Progress.connect_same_thread (c, boost::bind (&ARDOUR_UI::save_as_progress_update, this, _1, _2, _3, label, progress_bar));
 
                progress_dialog.show_all ();
                progress_dialog.present ();
@@ -2591,12 +2857,46 @@ ARDOUR_UI::save_session_as ()
                msg.run ();
        }
 
-       if (!sa.include_media) {
+       /* the logic here may seem odd: why isn't the condition sa.switch_to ?
+        * the trick is this: if the new session was copy with media included,
+        * then Session::save_as() will have already done a neat trick to avoid
+        * us having to unload and load the new state. But if the media was not
+        * included, then this is required (it avoids us having to otherwise
+        * drop all references to media (sources).
+        */
+
+       if (!sa.include_media && sa.switch_to) {
                unload_session (false);
                load_session (sa.final_session_folder_name, sa.new_name);
        }
 }
 
+void
+ARDOUR_UI::archive_session ()
+{
+       if (!_session) {
+               return;
+       }
+
+       time_t n;
+       time (&n);
+       Glib::DateTime gdt (Glib::DateTime::create_now_local (n));
+
+       SessionArchiveDialog sad;
+       sad.set_name (_session->name() + gdt.format ("_%F_%H%M%S"));
+       int response = sad.run ();
+
+       if (response != Gtk::RESPONSE_OK) {
+               sad.hide ();
+               return;
+       }
+
+       if (_session->archive_session (sad.target_folder(), sad.name(), sad.encode_option (), sad.only_used_sources (), &sad)) {
+               MessageDialog msg (_("Session Archiving failed."));
+               msg.run ();
+       }
+}
+
 void
 ARDOUR_UI::quick_snapshot_session (bool switch_to_it)
 {
@@ -2607,13 +2907,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;
 
@@ -2625,7 +2928,7 @@ ARDOUR_UI::process_snapshot_session_prompter (ArdourPrompter& prompter, bool swi
                char illegal = Session::session_name_is_legal(snapname);
                if (illegal) {
                        MessageDialog msg (string_compose (_("To ensure compatibility with various systems\n"
-                                            "snapshot names may not contain a '%1' character"), illegal));
+                                                            "snapshot names may not contain a '%1' character"), illegal));
                        msg.run ();
                        return false;
                }
@@ -2659,8 +2962,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) {
@@ -2674,14 +3003,8 @@ ARDOUR_UI::snapshot_session (bool switch_to_it)
        if (switch_to_it) {
                prompter.set_initial_text (_session->snap_name());
        } else {
-               char timebuf[128];
-               time_t n;
-               struct tm local_time;
-
-               time (&n);
-               localtime_r (&n, &local_time);
-               strftime (timebuf, sizeof(timebuf), "%FT%H.%M.%S", &local_time);
-               prompter.set_initial_text (timebuf);
+               Glib::DateTime tm (g_date_time_new_now_local ());
+               prompter.set_initial_text (tm.format ("%FT%H.%M.%S"));
        }
 
        bool finished = false;
@@ -2710,7 +3033,7 @@ ARDOUR_UI::rename_session ()
                return;
        }
 
-       ArdourPrompter prompter (true);
+       Prompter prompter (true);
        string name;
 
        prompter.set_name ("Prompter");
@@ -2731,7 +3054,7 @@ ARDOUR_UI::rename_session ()
 
                        if (illegal) {
                                MessageDialog msg (string_compose (_("To ensure compatibility with various systems\n"
-                                                                    "session names may not contain a '%1' character"), illegal));
+                                                                    "session names may not contain a '%1' character"), illegal));
                                msg.run ();
                                goto again;
                        }
@@ -2852,7 +3175,7 @@ ARDOUR_UI::transport_rec_enable_blink (bool onoff)
 }
 
 bool
-ARDOUR_UI::process_save_template_prompter (ArdourPrompter& prompter)
+ARDOUR_UI::process_save_template_prompter (Prompter& prompter)
 {
        string name;
 
@@ -2881,7 +3204,7 @@ ARDOUR_UI::process_save_template_prompter (ArdourPrompter& prompter)
 void
 ARDOUR_UI::save_template ()
 {
-       ArdourPrompter prompter (true);
+       Prompter prompter (true);
 
        if (!check_audioengine (_main_window)) {
                return;
@@ -2907,6 +3230,12 @@ ARDOUR_UI::save_template ()
        }
 }
 
+void ARDOUR_UI::manage_templates ()
+{
+       TemplateDialog td;
+       td.run();
+}
+
 void
 ARDOUR_UI::edit_metadata ()
 {
@@ -3003,7 +3332,19 @@ ARDOUR_UI::build_session_from_dialog (SessionDialog& sd, const std::string& sess
 void
 ARDOUR_UI::load_from_application_api (const std::string& path)
 {
+       /* OS X 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;
+       }
+
        ARDOUR_COMMAND_LINE::session_name = path;
+
        /* Cancel SessionDialog if it's visible to make OSX delegates work.
         *
         * ARDOUR_UI::starting connects app->ShouldLoad signal and then shows a SessionDialog
@@ -3069,7 +3410,7 @@ ARDOUR_UI::get_session_parameters (bool quit_on_cancel, bool should_be_new, stri
        /* if there is already a session, relabel the button
           on the SessionDialog so that we don't Quit directly
        */
-       cancel_not_quit = (_session != 0);
+       cancel_not_quit = (_session != 0) && !quit_on_cancel;
 
        if (_session && _session->dirty()) {
                if (unload_session (false)) {
@@ -3142,6 +3483,7 @@ ARDOUR_UI::get_session_parameters (bool quit_on_cancel, bool should_be_new, stri
                                        // 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()
+                                       assert (!_session);
                                        ARDOUR::cleanup ();
                                        pthread_cancel_all ();
 
@@ -3162,9 +3504,24 @@ ARDOUR_UI::get_session_parameters (bool quit_on_cancel, bool should_be_new, stri
                session_path = session_dialog.session_folder ();
 
                if (nsm) {
-                       likely_new = true;
+                       likely_new = true;
                }
 
+               if (!likely_new) {
+                       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 ();
+                               continue;
+                       }
+                       else if (rv == 0) {
+                               session_dialog.set_provided_session (session_name, session_path);
+                       }
+               }
+
+               // XXX check archive, inflate
                string::size_type suffix = session_name.find (statefile_suffix);
 
                if (suffix != string::npos) {
@@ -3184,12 +3541,12 @@ ARDOUR_UI::get_session_parameters (bool quit_on_cancel, bool should_be_new, stri
 
                if (session_name[0] == G_DIR_SEPARATOR ||
 #ifdef PLATFORM_WINDOWS
-                   (session_name.length() > 3 && session_name[1] == ':' && session_name[2] == G_DIR_SEPARATOR)
+                  (session_name.length() > 3 && session_name[1] == ':' && session_name[2] == G_DIR_SEPARATOR)
 #else
-                   (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)
+                  (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
-                        )
+               )
                {
 
                        /* absolute path or cwd-relative path specified for session name: infer session folder
@@ -3207,9 +3564,9 @@ ARDOUR_UI::get_session_parameters (bool quit_on_cancel, bool should_be_new, stri
 
                        if (illegal) {
                                MessageDialog msg (session_dialog,
-                                                  string_compose (_("To ensure compatibility with various systems\n"
-                                                                    "session names may not contain a '%1' character"),
-                                                                  illegal));
+                                                  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
                                continue;
@@ -3246,7 +3603,7 @@ ARDOUR_UI::get_session_parameters (bool quit_on_cancel, bool should_be_new, stri
                        if (illegal) {
                                pop_back_splash (session_dialog);
                                MessageDialog msg (session_dialog, string_compose(_("To ensure compatibility with various systems\n"
-                                                                                   "session names may not contain a '%1' character"), illegal));
+                                                                                   "session names may not contain a '%1' character"), illegal));
                                msg.run ();
                                ARDOUR_COMMAND_LINE::session_name = ""; // cancel that
                                continue;
@@ -3255,7 +3612,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);
 
@@ -3302,10 +3664,6 @@ ARDOUR_UI::close_session()
        if (get_session_parameters (true, false)) {
                exit (1);
        }
-       if (splash && splash->is_visible()) {
-               // in 1 second, hide the splash screen
-               Glib::signal_timeout().connect (sigc::bind (sigc::ptr_fun (_hide_splash), this), 1000);
-       }
 }
 
 /** @param snap_name Snapshot name (without .ardour suffix).
@@ -3314,6 +3672,18 @@ ARDOUR_UI::close_session()
 int
 ARDOUR_UI::load_session (const std::string& path, const std::string& snap_name, std::string mix_template)
 {
+       /* load_session calls flush_pending() which allows
+        * GUI interaction and potentially loading another session
+        * (that was easy via snapshot sidebar).
+        * Recursing into load_session() from load_session() and recusive
+        * event loops causes all kind of crashes.
+        */
+       assert (!session_load_in_progress);
+       if (session_load_in_progress) {
+               return -1;
+       }
+       PBD::Unwinder<bool> lsu (session_load_in_progress, true);
+
        Session *new_session;
        int unload_status;
        int retval = -1;
@@ -3387,8 +3757,8 @@ 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"),
-                                          path, snap_name),
+                                          _("Session \"%1 (snapshot %2)\" did not load successfully"),
+                                          path, snap_name),
                                   true,
                                   Gtk::MESSAGE_INFO,
                                   BUTTONS_OK);
@@ -3452,7 +3822,7 @@ ARDOUR_UI::load_session (const std::string& path, const std::string& snap_name,
 
        {
                Timers::TimerSuspender t;
-               flush_pending ();
+               flush_pending (10);
        }
 
 #ifdef WINDOWS_VST_SUPPORT
@@ -3460,7 +3830,22 @@ 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().
+        * If session-loading fails we hide it explicitly.
+        * This covers both cases in a central place.
+        */
+       if (retval) {
+               hide_splash ();
+       }
        return retval;
 }
 
@@ -3486,7 +3871,9 @@ ARDOUR_UI::build_session (const std::string& path, const std::string& snap_name,
        }
 
        catch (SessionException e) {
+               cerr << "Here are the errors associated with this failed session:\n";
                dump_errors (cerr);
+               cerr << "---------\n";
                MessageDialog msg (string_compose(_("Could not create session in \"%1\": %2"), path, e.what()));
                msg.set_title (_("Loading Error"));
                msg.set_position (Gtk::WIN_POS_CENTER);
@@ -3495,7 +3882,9 @@ ARDOUR_UI::build_session (const std::string& path, const std::string& snap_name,
                return -1;
        }
        catch (...) {
+               cerr << "Here are the errors associated with this failed session:\n";
                dump_errors (cerr);
+               cerr << "---------\n";
                MessageDialog msg (string_compose(_("Could not create session in \"%1\""), path));
                msg.set_title (_("Loading Error"));
                msg.set_position (Gtk::WIN_POS_CENTER);
@@ -3517,11 +3906,16 @@ ARDOUR_UI::build_session (const std::string& path, const std::string& snap_name,
                new_session->add_instant_xml (*n, false);
        }
 
+       n = Config->instant_xml (X_("Preferences"));
+       if (n) {
+               new_session->add_instant_xml (*n, false);
+       }
+
        /* Put the playhead at 0 and scroll fully left */
        n = new_session->instant_xml (X_("Editor"));
        if (n) {
-               n->add_property (X_("playhead"), X_("0"));
-               n->add_property (X_("left-frame"), X_("0"));
+               n->set_property (X_("playhead"), X_("0"));
+               n->set_property (X_("left-frame"), X_("0"));
        }
 
        set_session (new_session);
@@ -3533,6 +3927,34 @@ ARDOUR_UI::build_session (const std::string& path, const std::string& snap_name,
        return 0;
 }
 
+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 (&LuaInstance::_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, "session_setup");
+               if (fn.isFunction()) {
+                       fn ();
+               }
+       } catch (luabridge::LuaException const& e) { }
+}
+
 void
 ARDOUR_UI::launch_chat ()
 {
@@ -3645,8 +4067,8 @@ ARDOUR_UI::show_splash ()
 void
 ARDOUR_UI::hide_splash ()
 {
-        delete splash;
-        splash = 0;
+       delete splash;
+       splash = 0;
 }
 
 void
@@ -3658,12 +4080,12 @@ ARDOUR_UI::display_cleanup_results (ARDOUR::CleanupReport& rep, const gchar* lis
 
        if (removed == 0) {
                MessageDialog msgd (_main_window,
-                                   _("No files were ready for clean-up"),
-                                   true,
-                                   Gtk::MESSAGE_INFO,
-                                   Gtk::BUTTONS_OK);
+                                   _("No files were ready for clean-up"),
+                                   true,
+                                   Gtk::MESSAGE_INFO,
+                                   Gtk::BUTTONS_OK);
                msgd.set_title (_("Clean-up"));
-               msgd.set_secondary_text (_("If this seems suprising, \n\
+               msgd.set_secondary_text (_("If this seems surprising, \n\
 check for any existing snapshots.\n\
 These may still include regions that\n\
 require some unused files to continue to exist."));
@@ -3675,12 +4097,12 @@ require some unused files to continue to exist."));
        ArdourDialog results (_("Clean-up"), true, false);
 
        struct CleanupResultsModelColumns : public Gtk::TreeModel::ColumnRecord {
-           CleanupResultsModelColumns() {
-                   add (visible_name);
-                   add (fullpath);
-           }
-           Gtk::TreeModelColumn<std::string> visible_name;
-           Gtk::TreeModelColumn<std::string> fullpath;
+               CleanupResultsModelColumns() {
+                       add (visible_name);
+                       add (fullpath);
+               }
+               Gtk::TreeModelColumn<std::string> visible_name;
+               Gtk::TreeModelColumn<std::string> fullpath;
        };
 
 
@@ -3907,10 +4329,10 @@ ARDOUR_UI::cleanup_peakfiles ()
 }
 
 PresentationInfo::order_t
-ARDOUR_UI::translate_order (AddRouteDialog::InsertAt place)
+ARDOUR_UI::translate_order (RouteDialogs::InsertAt place)
 {
        if (editor->get_selection().tracks.empty()) {
-               return PresentationInfo::max_order;
+               return place == RouteDialogs::First ? 0 : PresentationInfo::max_order;
        }
 
        PresentationInfo::order_t order_hint = PresentationInfo::max_order;
@@ -3920,18 +4342,18 @@ ARDOUR_UI::translate_order (AddRouteDialog::InsertAt place)
          the highest order key in the selection + 1 (if available).
        */
 
-       if (place == AddRouteDialog::AfterSelection) {
+       if (place == RouteDialogs::AfterSelection) {
                RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView*> (editor->get_selection().tracks.back());
                if (rtav) {
-                       order_hint = rtav->route()->presentation_info().group_order();
+                       order_hint = rtav->route()->presentation_info().order();
                        order_hint++;
                }
-       } else if (place == AddRouteDialog::BeforeSelection) {
+       } else if (place == RouteDialogs::BeforeSelection) {
                RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView*> (editor->get_selection().tracks.front());
                if (rtav) {
-                       order_hint = rtav->route()->presentation_info().group_order();
+                       order_hint = rtav->route()->presentation_info().order();
                }
-       } else if (place == AddRouteDialog::First) {
+       } else if (place == RouteDialogs::First) {
                order_hint = 0;
        } else {
                /* leave order_hint at max_order */
@@ -3957,7 +4379,9 @@ ARDOUR_UI::start_duplicate_routes ()
 void
 ARDOUR_UI::add_route ()
 {
-       int count;
+       if (!add_route_dialog.get (false)) {
+               add_route_dialog->signal_response().connect (sigc::mem_fun (*this, &ARDOUR_UI::add_route_dialog_response));
+       }
 
        if (!_session) {
                return;
@@ -3968,16 +4392,24 @@ ARDOUR_UI::add_route ()
                return;
        }
 
-       ResponseType r = (ResponseType) add_route_dialog->run ();
+       add_route_dialog->set_position (WIN_POS_MOUSE);
+       add_route_dialog->present();
+}
 
-       add_route_dialog->hide();
+void
+ARDOUR_UI::add_route_dialog_response (int r)
+{
+       int count;
 
        switch (r) {
-               case RESPONSE_ACCEPT:
-                       break;
-               default:
-                       return;
-                       break;
+       case AddRouteDialog::Add:
+               break;
+       case AddRouteDialog::AddAndClose:
+               add_route_dialog->ArdourDialog::on_response (r);
+               break;
+       default:
+               add_route_dialog->ArdourDialog::on_response (r);
+               return;
        }
 
        if ((count = add_route_dialog->count()) <= 0) {
@@ -3990,9 +4422,9 @@ ARDOUR_UI::add_route ()
 
        if (!template_path.empty()) {
                if (add_route_dialog->name_template_is_default())  {
-                       _session->new_route_from_template (count, template_path, string());
+                       _session->new_route_from_template (count, order, template_path, string());
                } else {
-                       _session->new_route_from_template (count, template_path, add_route_dialog->name_template());
+                       _session->new_route_from_template (count, order, template_path, add_route_dialog->name_template());
                }
                return;
        }
@@ -4014,6 +4446,8 @@ ARDOUR_UI::add_route ()
 
        /* XXX do something with name template */
 
+       Session::ProcessorChangeBlocker pcb (_session);
+
        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);
@@ -4036,89 +4470,6 @@ ARDOUR_UI::add_route ()
        }
 }
 
-void
-ARDOUR_UI::add_lua_script ()
-{
-       if (!_session) {
-               return;
-       }
-
-       LuaScriptInfoPtr spi;
-       ScriptSelector ss ("Add Lua Session Script", LuaScriptInfo::Session);
-       switch (ss.run ()) {
-               case Gtk::RESPONSE_ACCEPT:
-                       spi = ss.script();
-                       break;
-               default:
-                       return;
-       }
-       ss.hide();
-
-       std::string script = "";
-
-       try {
-               script = Glib::file_get_contents (spi->path);
-       } catch (Glib::FileError e) {
-               string msg = string_compose (_("Cannot read session script '%1': %2"), spi->path, e.what());
-               MessageDialog am (msg);
-               am.run ();
-               return;
-       }
-
-       LuaScriptParamList lsp = LuaScriptParams::script_params (spi, "sess_params");
-       std::vector<std::string> reg = _session->registered_lua_functions ();
-
-       ScriptParameterDialog spd (_("Set Script Parameters"), spi, reg, lsp);
-       switch (spd.run ()) {
-               case Gtk::RESPONSE_ACCEPT:
-                       break;
-               default:
-                       return;
-       }
-
-       try {
-               _session->register_lua_function (spd.name(), script, lsp);
-       } catch (luabridge::LuaException const& e) {
-               string msg = string_compose (_("Session script '%1' instantiation failed: %2"), spd.name(), e.what ());
-               MessageDialog am (msg);
-               am.run ();
-       } catch (SessionException e) {
-               string msg = string_compose (_("Loading Session script '%1' failed: %2"), spd.name(), e.what ());
-               MessageDialog am (msg);
-               am.run ();
-       }
-}
-
-void
-ARDOUR_UI::remove_lua_script ()
-{
-       if (!_session) {
-               return;
-       }
-       if (_session->registered_lua_function_count () ==  0) {
-               string msg = _("There are no active Lua session scripts present in this session.");
-               MessageDialog am (msg);
-               am.run ();
-               return;
-       }
-
-       std::vector<std::string> reg = _session->registered_lua_functions ();
-       SessionScriptManager sm ("Remove Lua Session Script", reg);
-       switch (sm.run ()) {
-               case Gtk::RESPONSE_ACCEPT:
-                       break;
-               default:
-                       return;
-       }
-       try {
-               _session->unregister_lua_function (sm.name());
-       } catch (luabridge::LuaException const& e) {
-               string msg = string_compose (_("Session script '%1' removal failed: %2"), sm.name(), e.what ());
-               MessageDialog am (msg);
-               am.run ();
-       }
-}
-
 void
 ARDOUR_UI::stop_video_server (bool ask_confirm)
 {
@@ -4187,15 +4538,18 @@ ARDOUR_UI::start_video_server (Gtk::Window* float_window, bool popup_msg)
 
                std::string icsd_exec = video_server_dialog->get_exec_path();
                std::string icsd_docroot = video_server_dialog->get_docroot();
-               if (icsd_docroot.empty()) {
 #ifndef PLATFORM_WINDOWS
-                       icsd_docroot = X_("/");
-#else
-                       icsd_docroot = X_("C:\\");
-#endif
+               if (icsd_docroot.empty()) {
+                       icsd_docroot = VideoUtils::video_get_docroot (Config);
                }
+#endif
 
                GStatBuf sb;
+#ifdef PLATFORM_WINDOWS
+               if (VideoUtils::harvid_version >= 0x000802 && icsd_docroot.empty()) {
+                       /* OK, allow all drive letters */
+               } else
+#endif
                if (g_lstat (icsd_docroot.c_str(), &sb) != 0 || !S_ISDIR(sb.st_mode)) {
                        warning << _("Specified docroot is not an existing directory.") << endmsg;
                        continue;
@@ -4227,12 +4581,16 @@ ARDOUR_UI::start_video_server (Gtk::Window* float_window, bool popup_msg)
                argp[8] = 0;
                stop_video_server();
 
+#ifdef PLATFORM_WINDOWS
+               if (VideoUtils::harvid_version >= 0x000802 && icsd_docroot.empty()) {
+                       /* OK, allow all drive letters */
+               } else
+#endif
                if (icsd_docroot == X_("/") || icsd_docroot == X_("C:\\")) {
                        Config->set_video_advanced_setup(false);
                } else {
-                       std::ostringstream osstream;
-                       osstream << "http://127.0.0.1:" << video_server_dialog->get_listenport() << "/";
-                       Config->set_video_server_url(osstream.str());
+                       std::string url_str = "http://127.0.0.1:" + to_string(video_server_dialog->get_listenport()) + "/";
+                       Config->set_video_server_url(url_str);
                        Config->set_video_server_docroot(icsd_docroot);
                        Config->set_video_advanced_setup(true);
                }
@@ -4364,11 +4722,11 @@ ARDOUR_UI::add_video (Gtk::Window* float_window)
 
        if (video_timeline->video_file_info(path, local_file)) {
                XMLNode* node = new XMLNode(X_("Videotimeline"));
-               node->add_property (X_("Filename"), path);
-               node->add_property (X_("AutoFPS"), auto_set_session_fps?X_("1"):X_("0"));
-               node->add_property (X_("LocalFile"), local_file?X_("1"):X_("0"));
+               node->set_property (X_("Filename"), path);
+               node->set_property (X_("AutoFPS"), auto_set_session_fps);
+               node->set_property (X_("LocalFile"), local_file);
                if (orig_local_file) {
-                       node->add_property (X_("OriginalVideoFile"), orig_path);
+                       node->set_property (X_("OriginalVideoFile"), orig_path);
                } else {
                        node->remove_property (X_("OriginalVideoFile"));
                }
@@ -4478,6 +4836,24 @@ ARDOUR_UI::export_video (bool range)
        export_video_dialog->hide ();
 }
 
+XMLNode*
+ARDOUR_UI::preferences_settings () const
+{
+       XMLNode* node = 0;
+
+       if (_session) {
+               node = _session->instant_xml(X_("Preferences"));
+       } else {
+               node = Config->instant_xml(X_("Preferences"));
+       }
+
+       if (!node) {
+               node = new XMLNode (X_("Preferences"));
+       }
+
+       return node;
+}
+
 XMLNode*
 ARDOUR_UI::mixer_settings () const
 {
@@ -4562,7 +4938,7 @@ void
 ARDOUR_UI::create_xrun_marker (framepos_t where)
 {
        if (_session) {
-               Location *location = new Location (*_session, where, where, _("xrun"), Location::IsMark);
+               Location *location = new Location (*_session, where, where, _("xrun"), Location::IsMark, 0);
                _session->locations()->add (location);
        }
 }
@@ -4570,8 +4946,8 @@ ARDOUR_UI::create_xrun_marker (framepos_t where)
 void
 ARDOUR_UI::halt_on_xrun_message ()
 {
-        cerr << "HALT on xrun\n";
-        MessageDialog msg (_main_window, _("Recording was stopped because your system could not keep up."));
+       cerr << "HALT on xrun\n";
+       MessageDialog msg (_main_window, _("Recording was stopped because your system could not keep up."));
        msg.run ();
 }
 
@@ -4826,10 +5202,10 @@ audio may be played at the wrong sample rate.\n"), desired, PROGRAM_NAME, actual
        case RESPONSE_ACCEPT:
                return 0;
        default:
-                break;
+               break;
        }
 
-        return 1;
+       return 1;
 }
 
 void
@@ -4897,16 +5273,21 @@ ARDOUR_UI::record_state_changed ()
 {
        ENSURE_GUI_THREAD (*this, &ARDOUR_UI::record_state_changed);
 
-       if (!_session || !big_clock_window) {
+       if (!_session) {
                /* why bother - the clock isn't visible */
                return;
        }
 
-       if (_session->record_status () == Session::Recording && _session->have_rec_enabled_track ()) {
-               big_clock->set_active (true);
-       } else {
-               big_clock->set_active (false);
+       ActionManager::set_sensitive (ActionManager::rec_sensitive_actions, !_session->actively_recording());
+
+       if (big_clock_window) {
+               if (_session->record_status () == Session::Recording && _session->have_rec_enabled_track ()) {
+                       big_clock->set_active (true);
+               } else {
+                       big_clock->set_active (false);
+               }
        }
+
 }
 
 bool
@@ -4920,6 +5301,15 @@ ARDOUR_UI::first_idle ()
                editor->first_idle();
        }
 
+       /* in 1 second, hide the splash screen
+        *
+        * Consider hiding it *now*. If a user opens opens a dialog
+        * during that one second while the splash is still visible,
+        * the dialog will push-back the splash.
+        * Closing the dialog later will pop it back.
+        */
+       Glib::signal_timeout().connect (sigc::bind (sigc::ptr_fun (_hide_splash), this), 1000);
+
        Keyboard::set_can_save_keybindings (true);
        return false;
 }
@@ -4932,9 +5322,9 @@ ARDOUR_UI::store_clock_modes ()
        for (vector<AudioClock*>::iterator x = AudioClock::clocks.begin(); x != AudioClock::clocks.end(); ++x) {
                XMLNode* child = new XMLNode (X_("Clock"));
 
-               child->add_property (X_("name"), (*x)->name());
-               child->add_property (X_("mode"), enum_2_string ((*x)->mode()));
-               child->add_property (X_("on"), ((*x)->off() ? X_("no") : X_("yes")));
+               child->set_property (X_("name"), (*x)->name());
+               child->set_property (X_("mode"), (*x)->mode());
+               child->set_property (X_("on"), (*x)->on());
 
                node->add_child_nocopy (*child);
        }
@@ -5118,15 +5508,61 @@ ARDOUR_UI::session_format_mismatch (std::string xml_path, std::string backup_pat
        const char* end_mono = "</tt>";
 
        MessageDialog msg (string_compose (_("%4This is a session from an older version of %3%5\n\n"
-                                            "%3 has copied the old session file\n\n%6%1%7\n\nto\n\n%6%2%7\n\n"
-                                            "From now on, use the backup copy with older versions of %3"),
-                                          xml_path, backup_path, PROGRAM_NAME,
-                                          start_big, end_big,
-                                          start_mono, end_mono), true);
+                                            "%3 has copied the old session file\n\n%6%1%7\n\nto\n\n%6%2%7\n\n"
+                                            "From now on, use the backup copy with older versions of %3"),
+                                          xml_path, backup_path, PROGRAM_NAME,
+                                          start_big, end_big,
+                                          start_mono, end_mono), true);
 
        msg.run ();
 }
 
+void
+ARDOUR_UI::add_editor_meter_type_item (Menu_Helpers::MenuList& items, RadioMenuItem::Group& group, string const & name, MeterType type)
+{
+       using namespace Menu_Helpers;
+
+       items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (editor_meter, &LevelMeterHBox::set_meter_type), type)));
+       RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
+       i->set_active (editor_meter->meter_type () == type);
+}
+
+void
+ARDOUR_UI::popup_editor_meter_menu (GdkEventButton* ev)
+{
+       using namespace Gtk::Menu_Helpers;
+
+       Gtk::Menu* m = manage (new Menu);
+       MenuList& items = m->items ();
+
+       RadioMenuItem::Group group;
+
+       _suspend_editor_meter_callbacks = true;
+       add_editor_meter_type_item (items, group, ArdourMeter::meter_type_string(MeterPeak), MeterPeak);
+       add_editor_meter_type_item (items, group, ArdourMeter::meter_type_string(MeterPeak0dB), MeterPeak0dB);
+       add_editor_meter_type_item (items, group, ArdourMeter::meter_type_string(MeterKrms),  MeterKrms);
+       add_editor_meter_type_item (items, group, ArdourMeter::meter_type_string(MeterIEC1DIN), MeterIEC1DIN);
+       add_editor_meter_type_item (items, group, ArdourMeter::meter_type_string(MeterIEC1NOR), MeterIEC1NOR);
+       add_editor_meter_type_item (items, group, ArdourMeter::meter_type_string(MeterIEC2BBC), MeterIEC2BBC);
+       add_editor_meter_type_item (items, group, ArdourMeter::meter_type_string(MeterIEC2EBU), MeterIEC2EBU);
+       add_editor_meter_type_item (items, group, ArdourMeter::meter_type_string(MeterK20), MeterK20);
+       add_editor_meter_type_item (items, group, ArdourMeter::meter_type_string(MeterK14), MeterK14);
+       add_editor_meter_type_item (items, group, ArdourMeter::meter_type_string(MeterK12), MeterK12);
+       add_editor_meter_type_item (items, group, ArdourMeter::meter_type_string(MeterVU),  MeterVU);
+
+       m->popup (ev->button, ev->time);
+       _suspend_editor_meter_callbacks = false;
+}
+
+bool
+ARDOUR_UI::editor_meter_button_press (GdkEventButton* ev)
+{
+       if (ev->button == 3 && editor_meter) {
+               popup_editor_meter_menu (ev);
+               return true;
+       }
+       return false;
+}
 
 void
 ARDOUR_UI::reset_peak_display ()
@@ -5161,25 +5597,33 @@ ARDOUR_UI::do_audio_midi_setup (uint32_t desired_sample_rate)
        audio_midi_setup->set_desired_sample_rate (desired_sample_rate);
        audio_midi_setup->set_position (WIN_POS_CENTER);
 
-       if (Config->get_try_autostart_engine () || getenv ("TRY_AUTOSTART_ENGINE")) {
-               audio_midi_setup->try_autostart ();
-               if (ARDOUR::AudioEngine::instance()->running()) {
-                       return 0;
+       if (desired_sample_rate != 0) {
+               if (Config->get_try_autostart_engine () || getenv ("TRY_AUTOSTART_ENGINE")) {
+                       audio_midi_setup->try_autostart ();
+                       if (ARDOUR::AudioEngine::instance()->running()) {
+                               return 0;
+                       }
                }
        }
 
        while (true) {
                int response = audio_midi_setup->run();
                switch (response) {
-               case Gtk::RESPONSE_OK:
+               case Gtk::RESPONSE_DELETE_EVENT:
+                       // after latency callibration engine may run,
+                       // Running() signal was emitted, but dialog will not
+                       // have emitted a response. The user needs to close
+                       // the dialog -> Gtk::RESPONSE_DELETE_EVENT
+                       if (!AudioEngine::instance()->running()) {
+                               return -1;
+                       }
+                       // fall through
+               default:
                        if (!AudioEngine::instance()->running()) {
                                continue;
-                       } else {
-                               return 0;
                        }
+                       audio_midi_setup->hide ();
                        return 0;
-               default:
-                       return -1;
                }
        }
 }
@@ -5296,7 +5740,7 @@ ARDOUR_UI::audioengine_became_silent ()
 void
 ARDOUR_UI::hide_application ()
 {
-    Application::instance ()-> hide ();
+       Application::instance ()-> hide ();
 }
 
 void
@@ -5308,16 +5752,16 @@ ARDOUR_UI::setup_toplevel_window (Gtk::Window& window, const string& name, void*
 
        if (window_icons.empty()) {
                Glib::RefPtr<Gdk::Pixbuf> icon;
-               if ((icon = ::get_icon (PROGRAM_NAME "-icon_16px")) != 0) {
+               if ((icon = ::get_icon (PROGRAM_NAME "-icon_16px"))) {
                        window_icons.push_back (icon);
                }
-               if ((icon = ::get_icon (PROGRAM_NAME "-icon_22px")) != 0) {
+               if ((icon = ::get_icon (PROGRAM_NAME "-icon_22px"))) {
                        window_icons.push_back (icon);
                }
-               if ((icon = ::get_icon (PROGRAM_NAME "-icon_32px")) != 0) {
+               if ((icon = ::get_icon (PROGRAM_NAME "-icon_32px"))) {
                        window_icons.push_back (icon);
                }
-               if ((icon = ::get_icon (PROGRAM_NAME "-icon_48px")) != 0) {
+               if ((icon = ::get_icon (PROGRAM_NAME "-icon_48px"))) {
                        window_icons.push_back (icon);
                }
        }
@@ -5333,7 +5777,7 @@ ARDOUR_UI::setup_toplevel_window (Gtk::Window& window, const string& name, void*
        }
 
        window.set_title (title.get_string());
-       window.set_wmclass (string_compose (X_("%1_%1"), downcase (PROGRAM_NAME), downcase (name)), PROGRAM_NAME);
+       window.set_wmclass (string_compose (X_("%1_%1"), downcase (std::string(PROGRAM_NAME)), downcase (name)), PROGRAM_NAME);
 
        window.set_flags (CAN_FOCUS);
        window.add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK);
@@ -5399,15 +5843,15 @@ ARDOUR_UI::key_event_handler (GdkEventKey* ev, Gtk::Window* event_window)
 }
 
 static Gtkmm2ext::Bindings*
-get_bindings_from_widget_heirarchy (GtkWidget* w)
+get_bindings_from_widget_heirarchy (GtkWidget** w)
 {
        void* p = NULL;
 
-       while (w) {
-               if ((p = g_object_get_data (G_OBJECT(w), "ardour-bindings")) != 0) {
+       while (*w) {
+               if ((p = g_object_get_data (G_OBJECT(*w), "ardour-bindings")) != 0) {
                        break;
                }
-               w = gtk_widget_get_parent (w);
+               *w = gtk_widget_get_parent (*w);
        }
 
        return reinterpret_cast<Gtkmm2ext::Bindings*> (p);
@@ -5418,10 +5862,11 @@ ARDOUR_UI::key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey
 {
        GtkWindow* win = window.gobj();
        GtkWidget* focus = gtk_window_get_focus (win);
+       GtkWidget* binding_widget = focus;
        bool special_handling_of_unmodified_accelerators = false;
        const guint mask = (Keyboard::RelevantModifierKeyMask & ~(Gdk::SHIFT_MASK|Gdk::LOCK_MASK));
 
-        if (focus) {
+       if (focus) {
 
                /* some widget has keyboard focus */
 
@@ -5436,7 +5881,7 @@ ARDOUR_UI::key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey
 
                } else {
 
-                       Gtkmm2ext::Bindings* focus_bindings = get_bindings_from_widget_heirarchy (focus);
+                       Gtkmm2ext::Bindings* focus_bindings = get_bindings_from_widget_heirarchy (&binding_widget);
                        if (focus_bindings) {
                                bindings = focus_bindings;
                                DEBUG_TRACE (DEBUG::Accelerators, string_compose ("Switch bindings based on focus widget, now using %1\n", bindings->name()));
@@ -5444,13 +5889,13 @@ ARDOUR_UI::key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey
                }
        }
 
-        DEBUG_TRACE (DEBUG::Accelerators, string_compose ("Win = %1 [title = %9] focus = %7 (%8) Key event: code = %2  state = %3 special handling ? %4 magic widget focus ? %5 focus widget %6 named %7 mods ? %8\n",
-                                                          win,
-                                                          ev->keyval,
-                                                         show_gdk_event_state (ev->state),
+       DEBUG_TRACE (DEBUG::Accelerators, string_compose ("Win = %1 [title = %9] focus = %7 (%8) Key event: code = %2  state = %3 special handling ? %4 magic widget focus ? %5 focus widget %6 named %7 mods ? %8\n",
+                                                         win,
+                                                         ev->keyval,
+                                                         Gtkmm2ext::show_gdk_event_state (ev->state),
                                                           special_handling_of_unmodified_accelerators,
                                                           Keyboard::some_magic_widget_has_focus(),
-                                                         focus,
+                                                         focus,
                                                           (focus ? gtk_widget_get_name (focus) : "no focus widget"),
                                                           ((ev->state & mask) ? "yes" : "no"),
                                                           window.get_title()));
@@ -5491,14 +5936,14 @@ ARDOUR_UI::key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey
 
                /* no special handling or there are modifiers in effect: accelerate first */
 
-                DEBUG_TRACE (DEBUG::Accelerators, "\tactivate, then propagate\n");
+               DEBUG_TRACE (DEBUG::Accelerators, "\tactivate, then propagate\n");
                DEBUG_TRACE (DEBUG::Accelerators, string_compose ("\tevent send-event:%1 time:%2 length:%3 name %7 string:%4 hardware_keycode:%5 group:%6\n",
                                                                  ev->send_event, ev->time, ev->length, ev->string, ev->hardware_keycode, ev->group, gdk_keyval_name (ev->keyval)));
 
                DEBUG_TRACE (DEBUG::Accelerators, "\tsending to window\n");
                KeyboardKey k (ev->state, ev->keyval);
 
-               if (bindings) {
+               while (bindings) {
 
                        DEBUG_TRACE (DEBUG::Accelerators, string_compose ("\tusing Ardour bindings %1 @ %2 for this event\n", bindings->name(), bindings));
 
@@ -5506,6 +5951,17 @@ ARDOUR_UI::key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey
                                DEBUG_TRACE (DEBUG::Accelerators, "\t\thandled\n");
                                return true;
                        }
+
+                       if (binding_widget) {
+                               binding_widget = gtk_widget_get_parent (binding_widget);
+                               if (binding_widget) {
+                                       bindings = get_bindings_from_widget_heirarchy (&binding_widget);
+                               } else {
+                                       bindings = 0;
+                               }
+                       } else {
+                               bindings = 0;
+                       }
                }
 
                DEBUG_TRACE (DEBUG::Accelerators, "\tnot yet handled, try global bindings\n");
@@ -5515,12 +5971,12 @@ ARDOUR_UI::key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey
                        return true;
                }
 
-                DEBUG_TRACE (DEBUG::Accelerators, "\tnot accelerated, now propagate\n");
+               DEBUG_TRACE (DEBUG::Accelerators, "\tnot accelerated, now propagate\n");
 
-                if (gtk_window_propagate_key_event (win, ev)) {
-                       DEBUG_TRACE (DEBUG::Accelerators, "\tpropagate handled\n");
-                       return true;
-                }
+               if (gtk_window_propagate_key_event (win, ev)) {
+                       DEBUG_TRACE (DEBUG::Accelerators, "\tpropagate handled\n");
+                       return true;
+               }
 
        } else {
 
@@ -5536,7 +5992,7 @@ ARDOUR_UI::key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey
                DEBUG_TRACE (DEBUG::Accelerators, "\tpropagation didn't handle, so activate\n");
                KeyboardKey k (ev->state, ev->keyval);
 
-               if (bindings) {
+               while (bindings) {
 
                        DEBUG_TRACE (DEBUG::Accelerators, "\tusing Ardour bindings for this window\n");
 
@@ -5546,6 +6002,16 @@ ARDOUR_UI::key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey
                                return true;
                        }
 
+                       if (binding_widget) {
+                               binding_widget = gtk_widget_get_parent (binding_widget);
+                               if (binding_widget) {
+                                       bindings = get_bindings_from_widget_heirarchy (&binding_widget);
+                               } else {
+                                       bindings = 0;
+                               }
+                       } else {
+                               bindings = 0;
+                       }
                }
 
                DEBUG_TRACE (DEBUG::Accelerators, "\tnot yet handled, try global bindings\n");
@@ -5572,9 +6038,58 @@ void
 ARDOUR_UI::cancel_solo ()
 {
        if (_session) {
-               if (_session) {
-                       _session->set_controls (route_list_to_control_list (_session->get_routes(), &Route::solo_control), 0.0, Controllable::NoGroup);
+               _session->cancel_all_solo ();
+       }
+}
+
+void
+ARDOUR_UI::reset_focus (Gtk::Widget* w)
+{
+       /* this resets focus to the first focusable parent of the given widget,
+        * or, if there is no focusable parent, cancels focus in the toplevel
+        * window that the given widget is packed into (if there is one).
+        */
+
+       if (!w) {
+               return;
+       }
+
+       Gtk::Widget* top = w->get_toplevel();
+
+       if (!top || !top->is_toplevel()) {
+               return;
+       }
+
+       w = w->get_parent ();
+
+       while (w) {
+
+               if (w->is_toplevel()) {
+                       /* Setting the focus widget to a Gtk::Window causes all
+                        * subsequent calls to ::has_focus() on the nominal
+                        * focus widget in that window to return
+                        * false. Workaround: never set focus to the toplevel
+                        * itself.
+                        */
+                       break;
+               }
+
+               if (w->get_can_focus ()) {
+                       Gtk::Window* win = dynamic_cast<Gtk::Window*> (top);
+                       win->set_focus (*w);
+                       return;
                }
-               _session->clear_all_solo_state (_session->get_routes()); // safeguard, ideally this won't do anything, check the log-window
+               w = w->get_parent ();
+       }
+
+       if (top == &_main_window) {
+
        }
+
+       /* no focusable parent found, cancel focus in top level window.
+          C++ API cannot be used for this. Thanks, references.
+       */
+
+       gtk_window_set_focus (GTK_WINDOW(top->gobj()), 0);
+
 }