display active peak-builds
[ardour.git] / gtk2_ardour / ardour_ui.cc
index a81d817442ffa52a38782fb898170410fcc97217..042706322284db23784cb7fe3dd1c8ad3108a046 100644 (file)
@@ -28,6 +28,8 @@
 #include <cerrno>
 #include <fstream>
 
+#include <stdarg.h>
+
 #ifndef PLATFORM_WINDOWS
 #include <sys/resource.h>
 #endif
@@ -55,6 +57,8 @@
 #include "pbd/file_utils.h"
 #include "pbd/localtime_r.h"
 #include "pbd/pthread_utils.h"
+#include "pbd/replace_all.h"
+#include "pbd/xml++.h"
 
 #include "gtkmm2ext/application.h"
 #include "gtkmm2ext/bindings.h"
@@ -82,6 +86,7 @@
 #include "ardour/session_route.h"
 #include "ardour/session_state_utils.h"
 #include "ardour/session_utils.h"
+#include "ardour/source_factory.h"
 #include "ardour/slave.h"
 #include "ardour/system_exec.h"
 
 typedef uint64_t microseconds_t;
 
 #include "about.h"
+#include "editing.h"
 #include "actions.h"
 #include "add_route_dialog.h"
 #include "ambiguous_file_dialog.h"
 #include "ardour_ui.h"
 #include "audio_clock.h"
+#include "audio_region_view.h"
 #include "big_clock_window.h"
 #include "bundle_manager.h"
 #include "engine_dialog.h"
@@ -128,6 +135,7 @@ typedef uint64_t microseconds_t;
 #include "rc_option_editor.h"
 #include "route_time_axis.h"
 #include "route_params_ui.h"
+#include "save_as_dialog.h"
 #include "session_dialog.h"
 #include "session_metadata_dialog.h"
 #include "session_option_editor.h"
@@ -151,51 +159,88 @@ using namespace PBD;
 using namespace Gtkmm2ext;
 using namespace Gtk;
 using namespace std;
+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;
 
+float ARDOUR_UI::ui_scale = 1.0;
 
 static bool
 ask_about_configuration_copy (string const & old_dir, string const & new_dir, int version)
 {
        MessageDialog msg (string_compose (_("%1 %2.x has discovered configuration files from %1 %3.x.\n\n"
-                                            "Would you like to copy the relevant files before starting to use the program?\n\n"
+                                            "Would you like these files to be copied and used for %1 %2.x?\n\n"
                                             "(This will require you to restart %1.)"),
-                                          PROGRAM_NAME, PROGRAM_VERSION, version), true);
-
-       msg.add_button (Gtk::Stock::NO, Gtk::RESPONSE_NO);
+                                          PROGRAM_NAME, PROGRAM_VERSION, version),
+                          false, /* no markup */
+                          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 ();
 
-       return (msg.run() == Gtk::RESPONSE_OK);
+       return (msg.run() == Gtk::RESPONSE_YES);
 }
 
-ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir)
+static void
+libxml_generic_error_func (void* /* parsing_context*/,
+                   const char* msg,
+                   ...)
+{
+       va_list ap;
+       char buf[2048];
 
-       : Gtkmm2ext::UI (PROGRAM_NAME, argcp, argvp)
-       , ui_config (new UIConfiguration)
-       , gui_object_state (new GUIObjectState)
+       va_start (ap, msg);
+       vsnprintf (buf, sizeof (buf), msg, ap);
+       error << buf << endmsg;
+       va_end (ap);
+}
+
+static void
+libxml_structured_error_func (void* /* parsing_context*/,
+                              xmlErrorPtr err)
+{
+       string msg;
 
-       , primary_clock (new MainClock (X_("primary"), false, X_("transport"), true, true, true, false, true))
-       , secondary_clock (new MainClock (X_("secondary"), false, X_("secondary"), true, true, false, false, true))
-         
-         /* big clock */
+       if (err->message)
+               msg = err->message;
 
+       replace_all (msg, "\n", "");
+
+       if (err->file && err->line) {
+               error << X_("XML error: ") << msg << " in " << err->file << " at line " << err->line;
+
+               if (err->int2) {
+                       error << ':' << err->int2;
+               }
+       }
+       error << endmsg;
+}
+
+
+ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir, UIConfiguration* uic)
+
+       : Gtkmm2ext::UI (PROGRAM_NAME, argcp, argvp)
+       , ui_config (uic->post_gui_init ())
+       , session_loaded (false)
+       , gui_object_state (new GUIObjectState)
+       , primary_clock   (new MainClock (X_("primary"),   X_("transport"), true ))
+       , secondary_clock (new MainClock (X_("secondary"), X_("secondary"), false))
        , big_clock (new AudioClock (X_("bigclock"), false, "big", true, true, false, false))
        , video_timeline(0)
-
-         /* start of private members */
+       , ignore_dual_punch (false)
        , editor (0)
        , mixer (0)
        , nsm (0)
        , _was_dirty (false)
        , _mixer_on_top (false)
+       , _initial_verbose_plugin_scan (false)
        , first_time_engine_run (true)
-
-         /* transport */
-
        , 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))
@@ -203,19 +248,21 @@ 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_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()
-
+       , session_selector_window (0)
+       , _numpad_locate_happening (false)
+       , _session_is_new (false)
+       , last_key_press_time (0)
+       , save_as_dialog (0)
+       , meterbridge (0)
        , speaker_config_window (X_("speaker-config"), _("Speaker Configuration"))
        , key_editor (X_("key-editor"), _("Key Bindings"))
        , rc_option_editor (X_("rc-options-editor"), _("Preferences"))
@@ -231,12 +278,17 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir)
        , 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))
-
+       , video_server_process (0)
+       , splash (0)
+       , have_configure_timeout (false)
+       , last_configure_time (0)
+       , last_peak_grab (0)
+       , have_disk_speed_dialog_displayed (false)
        , _status_bar_visibility (X_("status-bar"))
        , _feedback_exists (false)
        , _log_not_acknowledged (LogLevelNone)
 {
-       Gtkmm2ext::init(localedir);
+       Gtkmm2ext::init (localedir);
 
        if (ARDOUR::handle_old_configuration_files (boost::bind (ask_about_configuration_copy, _1, _2, _3))) {
                MessageDialog msg (string_compose (_("Your configuration files were copied. You can now restart %1."), PROGRAM_NAME), true);
@@ -245,33 +297,19 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir)
                _exit (0);
        }
 
-       
-       splash = 0;
-
-       _numpad_locate_happening = false;
-
        if (theArdourUI == 0) {
                theArdourUI = this;
        }
 
+       /* stop libxml from spewing to stdout/stderr */
+
+       xmlSetGenericErrorFunc (this, libxml_generic_error_func);
+       xmlSetStructuredErrorFunc (this, libxml_structured_error_func);
+
        ui_config->ParameterChanged.connect (sigc::mem_fun (*this, &ARDOUR_UI::parameter_changed));
        boost::function<void (string)> pc (boost::bind (&ARDOUR_UI::parameter_changed, this, _1));
        ui_config->map_parameters (pc);
 
-       editor = 0;
-       mixer = 0;
-       meterbridge = 0;
-       editor = 0;
-       _session_is_new = false;
-       session_selector_window = 0;
-       last_key_press_time = 0;
-       video_server_process = 0;
-       open_session_selector = 0;
-       have_configure_timeout = false;
-       have_disk_speed_dialog_displayed = false;
-       session_loaded = false;
-       ignore_dual_punch = false;
-
        roll_button.set_controllable (roll_controllable);
        stop_button.set_controllable (stop_controllable);
        goto_start_button.set_controllable (goto_start_controllable);
@@ -289,9 +327,6 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir)
        rec_button.set_name ("transport recenable button");
        midi_panic_button.set_name ("transport button");
 
-       last_configure_time= 0;
-       last_peak_grab = 0;
-
        ARDOUR::Diskstream::DiskOverrun.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::disk_overrun_handler, this), gui_context());
        ARDOUR::Diskstream::DiskUnderrun.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::disk_underrun_handler, this), gui_context());
 
@@ -344,7 +379,7 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir)
        setup_gtk_ardour_enums ();
        setup_profile ();
 
-       SessionEvent::create_per_thread_pool ("GUI", 512);
+       SessionEvent::create_per_thread_pool ("GUI", 4096);
 
        /* we like keyboards */
 
@@ -406,7 +441,7 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir)
        /* Trigger setting up the color scheme and loading the GTK RC file */
 
        ARDOUR_UI::config()->load_rc_file (false);
-       
+
        _process_thread = new ProcessThread ();
        _process_thread->init ();
 
@@ -445,12 +480,17 @@ ARDOUR_UI::engine_running ()
        if (first_time_engine_run) {
                post_engine();
                first_time_engine_run = false;
-       } 
-       
+       }
+
+       if (_session) {
+               _session->reset_xrun_count ();
+       }
        update_disk_space ();
        update_cpu_load ();
+       update_xrun_count ();
        update_sample_rate (AudioEngine::instance()->sample_rate());
        update_timecode_format ();
+       update_peak_thread_work ();
 }
 
 void
@@ -490,7 +530,7 @@ the audio backend and save the session."), PROGRAM_NAME);
        MessageDialog msg (*editor, msgstr);
        pop_back_splash (msg);
        msg.run ();
-       
+
        if (free_reason) {
                free (const_cast<char*> (reason));
        }
@@ -511,13 +551,14 @@ ARDOUR_UI::post_engine ()
 #endif
 
        ARDOUR::init_post_engine ();
-       
+
        /* connect to important signals */
 
        AudioEngine::instance()->Stopped.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::engine_stopped, this), gui_context());
        AudioEngine::instance()->SampleRateChanged.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::update_sample_rate, this, _1), gui_context());
        AudioEngine::instance()->BufferSizeChanged.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::update_sample_rate, this, _1), gui_context());
        AudioEngine::instance()->Halted.connect_same_thread (halt_connection, boost::bind (&ARDOUR_UI::engine_halted, this, _1, false));
+       AudioEngine::instance()->BecameSilent.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::audioengine_became_silent, this), gui_context());
 
        _tooltips.enable();
 
@@ -530,7 +571,7 @@ ARDOUR_UI::post_engine ()
        if (n) {
                _status_bar_visibility.set_state (*n);
        }
-       
+
        check_memory_locking();
 
        /* this is the first point at which all the keybindings are available */
@@ -601,9 +642,23 @@ ARDOUR_UI::~ARDOUR_UI ()
                delete primary_clock;
                delete secondary_clock;
                delete _process_thread;
+               delete meterbridge;
+               delete editor;
+               delete mixer;
+               delete nsm;
                delete gui_object_state;
                FastMeter::flush_pattern_cache ();
+               PixFader::flush_pattern_cache ();
        }
+
+#ifndef NDEBUG
+       /* Small trick to flush main-thread event pool.
+        * Other thread-pools are destroyed at pthread_exit(),
+        * but tmain thread termination is too late to trigger Pool::~Pool()
+        */
+       SessionEvent* ev = new SessionEvent (SessionEvent::SetTransportSpeed, SessionEvent::Clear, SessionEvent::Immediate, 0, 0); // get the pool reference, values don't matter since the event is never queued.
+       delete ev->event_pool();
+#endif
 }
 
 void
@@ -762,19 +817,27 @@ ARDOUR_UI::check_announcements ()
 
 #ifdef __APPLE__
        _annc_filename = PROGRAM_NAME "_announcements_osx_";
+#elif defined PLATFORM_WINDOWS
+       _annc_filename = PROGRAM_NAME "_announcements_windows_";
 #else
        _annc_filename = PROGRAM_NAME "_announcements_linux_";
 #endif
        _annc_filename.append (VERSIONSTRING);
 
+       _announce_string = "";
+
        std::string path = Glib::build_filename (user_config_directory(), _annc_filename);
-       std::ifstream announce_file (path.c_str());
-       if ( announce_file.fail() )
-               _announce_string = "";
-       else {
-               std::stringstream oss;
-               oss << announce_file.rdbuf();
-               _announce_string = oss.str();
+       FILE* fin = g_fopen (path.c_str(), "rb");
+       if (fin) {
+               while (!feof (fin)) {
+                       char tmp[1024];
+                       size_t len;
+                       if ((len = fread (tmp, sizeof(char), 1024, fin)) == 0 || ferror (fin)) {
+                               break;
+                       }
+                       _announce_string.append (tmp, len);
+               }
+               fclose (fin);
        }
 
        pingback (VERSIONSTRING, path);
@@ -800,7 +863,7 @@ ARDOUR_UI::starting ()
        /* we need to create this early because it may need to set the
         *  audio backend end up.
         */
-       
+
        try {
                audio_midi_setup.get (true);
        } catch (...) {
@@ -811,7 +874,16 @@ ARDOUR_UI::starting ()
        if ((nsm_url = g_getenv ("NSM_URL")) != 0) {
                nsm = new NSM_Client;
                if (!nsm->init (nsm_url)) {
-                       nsm->announce (PROGRAM_NAME, ":dirty:", "ardour3");
+                       /* the ardour executable may have different names:
+                        *
+                        * waf's obj.target for distro versions: eg ardour4, ardourvst4
+                        * Ardour4, Mixbus3 for bundled versions + full path on OSX & windows
+                        * argv[0] does not apply since we need the wrapper-script (not the binary itself)
+                        *
+                        * The wrapper startup script should set the environment variable 'ARDOUR_SELF'
+                        */
+                       const char *process_name = g_getenv ("ARDOUR_SELF");
+                       nsm->announce (PROGRAM_NAME, ":dirty:", process_name ? process_name : "ardour4");
 
                        unsigned int i = 0;
                        // wait for announce reply from nsm server
@@ -872,12 +944,14 @@ ARDOUR_UI::starting ()
                }
 
        } else  {
-               
+
                if (brand_new_user) {
+                       _initial_verbose_plugin_scan = true;
                        ArdourStartup s;
                        s.present ();
                        main().run();
                        s.hide ();
+                       _initial_verbose_plugin_scan = false;
                        switch (s.response ()) {
                        case Gtk::RESPONSE_OK:
                                break;
@@ -892,16 +966,16 @@ ARDOUR_UI::starting ()
                ARDOUR::read_recent_sessions (rs);
 
                string path = Glib::build_filename (user_config_directory(), ".iknowaboutfreeversion");
-               
+
                if (!Glib::file_test (path, Glib::FILE_TEST_EXISTS) && !rs.empty()) {
-                       
+
                        /* already used Ardour, have sessions ... warn about plugin state */
-                       
+
                        ArdourDialog d (_("Free/Demo Version Warning"), true);
                        Label l;
                        Button b (string_compose (_("Subscribe and support development of %1"), PROGRAM_NAME));
                        CheckButton c (_("Don't warn me about this again"));
-                       
+
                        l.set_markup (string_compose (_("<span weight=\"bold\" size=\"large\">%1</span>\n\n<b>%2</b>\n\n<i>%3</i>\n\n%4"),
                                                      string_compose (_("This is a free/demo version of %1"), PROGRAM_NAME),
                                                      _("It will not restore OR save any plugin settings"),
@@ -910,26 +984,26 @@ ARDOUR_UI::starting ()
                                                      _("To get full access to updates without this limitation\n"
                                                        "consider becoming a subscriber for a low cost every month.")));
                        l.set_justify (JUSTIFY_CENTER);
-                       
+
                        b.signal_clicked().connect (mem_fun(*this, &ARDOUR_UI::launch_subscribe));
-                       
+
                        d.get_vbox()->pack_start (l, true, true);
                        d.get_vbox()->pack_start (b, false, false, 12);
                        d.get_vbox()->pack_start (c, false, false, 12);
-                       
+
                        d.add_button (_("Quit now"), RESPONSE_CANCEL);
                        d.add_button (string_compose (_("Continue using %1"), PROGRAM_NAME), RESPONSE_OK);
-                       
+
                        d.show_all ();
 
                        c.signal_toggled().connect (sigc::hide_return (sigc::bind (sigc::ptr_fun (toggle_file_existence), path)));
-                       
+
                        if (d.run () != RESPONSE_OK) {
                                _exit (0);
                        }
                }
 #endif
-                       
+
                /* go get a session */
 
                const bool new_session_required = (ARDOUR_COMMAND_LINE::new_session || brand_new_user);
@@ -998,7 +1072,7 @@ ARDOUR_UI::check_memory_locking ()
                                                  "runs out of memory. \n\n"
                                                  "You can view the memory limit with 'ulimit -l', "
                                                  "and it is normally controlled by %2"),
-                                               PROGRAM_NAME, 
+                                               PROGRAM_NAME,
 #ifdef __FreeBSD__
                                                X_("/etc/login.conf")
 #else
@@ -1185,9 +1259,11 @@ void
 ARDOUR_UI::every_second ()
 {
        update_cpu_load ();
+       update_xrun_count ();
        update_buffer_load ();
        update_disk_space ();
        update_timecode_format ();
+       update_peak_thread_work ();
 
        if (nsm && nsm->is_active ()) {
                nsm->check ();
@@ -1324,10 +1400,16 @@ ARDOUR_UI::update_format ()
        case RF64:
                s << _("RF64");
                break;
+       case RF64_WAV:
+               s << _("RF64/WAV");
+               break;
+       case MBWF:
+               s << _("MBWF");
+               break;
        }
 
        s << " ";
-       
+
        switch (_session->config.get_native_file_data_format ()) {
        case FormatFloat:
                s << _("32-float");
@@ -1345,6 +1427,29 @@ ARDOUR_UI::update_format ()
        format_label.set_markup (s.str ());
 }
 
+void
+ARDOUR_UI::update_xrun_count ()
+{
+       char buf[64];
+
+       /* If this text is changed, the set_size_request_to_display_given_text call in ARDOUR_UI::resize_text_widgets
+          should also be changed.
+       */
+
+       if (_session) {
+               const unsigned int x = _session->get_xrun_count ();
+               if (x > 9999) {
+                       snprintf (buf, sizeof (buf), _("X: <span foreground=\"%s\">&gt;10K</span>"), X_("red"));
+               } else {
+                       snprintf (buf, sizeof (buf), _("X: <span foreground=\"%s\">%u</span>"), x > 0 ? X_("red") : X_("green"), x);
+               }
+       } else {
+               snprintf (buf, sizeof (buf), _("X: <span foreground=\"%s\">?</span>"), X_("yellow"));
+       }
+       xrun_label.set_markup (buf);
+       set_tip (xrun_label, _("Audio dropouts. Shift+click to reset"));
+}
+
 void
 ARDOUR_UI::update_cpu_load ()
 {
@@ -1359,6 +1464,19 @@ ARDOUR_UI::update_cpu_load ()
        cpu_load_label.set_markup (buf);
 }
 
+void
+ARDOUR_UI::update_peak_thread_work ()
+{
+       char buf[64];
+       const int c = SourceFactory::peak_work_queue_length ();
+       if (c > 0) {
+               snprintf (buf, sizeof (buf), _("PkBld: <span foreground=\"%s\">%d</span>"), c >= 2 ? X_("red") : X_("green"), c);
+               peak_thread_work_label.set_markup (buf);
+       } else {
+               peak_thread_work_label.set_markup (X_(""));
+       }
+}
+
 void
 ARDOUR_UI::update_buffer_load ()
 {
@@ -1370,7 +1488,7 @@ ARDOUR_UI::update_buffer_load ()
        /* If this text is changed, the set_size_request_to_display_given_text call in ARDOUR_UI::resize_text_widgets
           should also be changed.
        */
-       
+
        if (_session) {
                snprintf (
                        buf, sizeof (buf),
@@ -1441,9 +1559,9 @@ ARDOUR_UI::update_disk_space()
                        mins = frames / (fr * 60);
                        frames -= mins * fr * 60;
                        secs = frames / fr;
-                       
+
                        bool const low = (hrs == 0 && mins <= 30);
-                       
+
                        snprintf (
                                buf, sizeof(buf),
                                _("Disk: <span foreground=\"%s\">%02dh:%02dm:%02ds</span>"),
@@ -1471,7 +1589,7 @@ ARDOUR_UI::update_timecode_format ()
                } else {
                        matching = true;
                }
-                       
+
                snprintf (buf, sizeof (buf), S_("Timecode|TC: <span foreground=\"%s\">%s</span>"),
                          matching ? X_("green") : X_("red"),
                          Timecode::timecode_format_name (_session->config.get_timecode_format()).c_str());
@@ -1480,7 +1598,7 @@ ARDOUR_UI::update_timecode_format ()
        }
 
        timecode_format_label.set_markup (buf);
-}      
+}
 
 gint
 ARDOUR_UI::update_wall_clock ()
@@ -1699,55 +1817,47 @@ ARDOUR_UI::open_session ()
 
        }
 
-       /* popup selector window */
+       /* ardour sessions are folders */
+       Gtk::FileChooserDialog open_session_selector(_("Open Session"), FILE_CHOOSER_ACTION_OPEN);
+       open_session_selector.add_button (Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
+       open_session_selector.add_button (Gtk::Stock::OPEN, Gtk::RESPONSE_ACCEPT);
+       open_session_selector.set_default_response(Gtk::RESPONSE_ACCEPT);
 
-       if (open_session_selector == 0) {
 
-               /* ardour sessions are folders */
 
-               open_session_selector = new Gtk::FileChooserDialog (_("Open Session"), FILE_CHOOSER_ACTION_OPEN);
-               open_session_selector->add_button (Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
-               open_session_selector->add_button (Gtk::Stock::OPEN, Gtk::RESPONSE_ACCEPT);
-               open_session_selector->set_default_response(Gtk::RESPONSE_ACCEPT);
-               
-               if (_session) {
-                       string session_parent_dir = Glib::path_get_dirname(_session->path());
-                       string::size_type last_dir_sep = session_parent_dir.rfind(G_DIR_SEPARATOR);
-                       session_parent_dir = session_parent_dir.substr(0, last_dir_sep);
-                       open_session_selector->set_current_folder(session_parent_dir);
-               } else {
-                       open_session_selector->set_current_folder(Config->get_default_session_parent_dir());
-               }
+       if (_session) {
+               string session_parent_dir = Glib::path_get_dirname(_session->path());
+               open_session_selector.set_current_folder(session_parent_dir);
+       } else {
+               open_session_selector.set_current_folder(Config->get_default_session_parent_dir());
+       }
 
+       try {
+               /* add_shortcut_folder throws an exception if the folder being added already has a shortcut */
+#ifdef GTKOSX
+               open_session_selector.add_shortcut_folder_uri("file:///Volumes");
+#endif
                string default_session_folder = Config->get_default_session_parent_dir();
-               try {
-                       /* add_shortcut_folder throws an exception if the folder being added already has a shortcut */
-                       open_session_selector->add_shortcut_folder (default_session_folder);
-               }
-               catch (Glib::Error & e) {
-                       std::cerr << "open_session_selector->add_shortcut_folder (" << default_session_folder << ") threw Glib::Error " << e.what() << std::endl;
-               }
+               open_session_selector.add_shortcut_folder (default_session_folder);
+       }
+       catch (Glib::Error & e) {
+               std::cerr << "open_session_selector.add_shortcut_folder() threw Glib::Error " << e.what() << std::endl;
+       }
 
-               FileFilter session_filter;
-               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);
-               open_session_selector->set_filter (session_filter);
-       }
+       FileFilter session_filter;
+       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);
+       open_session_selector.set_filter (session_filter);
 
-       int response = open_session_selector->run();
-       open_session_selector->hide ();
+       int response = open_session_selector.run();
+       open_session_selector.hide ();
 
-       switch (response) {
-       case RESPONSE_ACCEPT:
-               break;
-       default:
-               open_session_selector->hide();
+       if (response == Gtk::RESPONSE_CANCEL) {
                return;
        }
 
-       open_session_selector->hide();
-       string session_path = open_session_selector->get_filename();
+       string session_path = open_session_selector.get_filename();
        string path, name;
        bool isnew;
 
@@ -1761,7 +1871,7 @@ ARDOUR_UI::open_session ()
 
 
 void
-ARDOUR_UI::session_add_mixed_track (const ChanCount& input, const ChanCount& output, RouteGroup* route_group, 
+ARDOUR_UI::session_add_mixed_track (const ChanCount& input, const ChanCount& output, RouteGroup* route_group,
                                    uint32_t how_many, const string& name_template, PluginInfoPtr instrument)
 {
        list<boost::shared_ptr<MidiTrack> > tracks;
@@ -1773,7 +1883,7 @@ ARDOUR_UI::session_add_mixed_track (const ChanCount& input, const ChanCount& out
 
        try {
                tracks = _session->new_midi_track (input, output, instrument, ARDOUR::Normal, route_group, how_many, name_template);
-               
+
                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;
                }
@@ -1788,7 +1898,7 @@ restart with more ports."), PROGRAM_NAME));
                msg.run ();
        }
 }
-       
+
 
 void
 ARDOUR_UI::session_add_midi_route (bool disk, RouteGroup* route_group, uint32_t how_many, const string& name_template, PluginInfoPtr instrument)
@@ -1825,7 +1935,7 @@ ARDOUR_UI::session_add_audio_route (
                        tracks = _session->new_audio_track (input_channels, output_channels, mode, route_group, how_many, name_template);
 
                        if (tracks.size() != how_many) {
-                               error << string_compose (P_("could not create %1 new audio track", "could not create %1 new audio tracks", how_many), how_many) 
+                               error << string_compose (P_("could not create %1 new audio track", "could not create %1 new audio tracks", how_many), how_many)
                                      << endmsg;
                        }
 
@@ -1894,9 +2004,9 @@ ARDOUR_UI::transport_goto_wallclock ()
 
                time (&now);
                localtime_r (&now, &tmnow);
-               
-               int frame_rate = _session->frame_rate();
-               
+
+               framecnt_t frame_rate = _session->frame_rate();
+
                if (frame_rate == 0) {
                        /* no frame rate available */
                        return;
@@ -1952,7 +2062,7 @@ ARDOUR_UI::transport_stop ()
 
 /** Check if any tracks are record enabled. If none are, record enable all of them.
  * @return true if track record-enabled status was changed, false otherwise.
- */  
+ */
 bool
 ARDOUR_UI::trx_record_enable_all_tracks ()
 {
@@ -1975,7 +2085,7 @@ ARDOUR_UI::trx_record_enable_all_tracks ()
 
        if (none_record_enabled) {
                _session->set_record_enabled (rl, true, Session::rt_cleanup);
-       } 
+       }
 
        return none_record_enabled;
 }
@@ -2056,7 +2166,7 @@ ARDOUR_UI::transport_roll ()
                                /* stop loop playback but keep rolling */
                                _session->request_play_loop (false, false);
                        }
-               } 
+               }
 
        } else if (_session->get_play_range () ) {
                /* stop playing a range if we currently are */
@@ -2078,7 +2188,6 @@ ARDOUR_UI::get_smart_mode() const
 void
 ARDOUR_UI::toggle_roll (bool with_abort, bool roll_out_of_bounded_mode)
 {
-
        if (!_session) {
                return;
        }
@@ -2163,7 +2272,7 @@ ARDOUR_UI::toggle_session_auto_loop ()
                        _session->request_play_loop (true, true);
                }
        }
-       
+
        //show the loop markers
        looploc->set_hidden (false, this);
 }
@@ -2220,9 +2329,9 @@ ARDOUR_UI::transport_forward (int option)
        if (!_session) {
                return;
        }
-       
+
        float current_transport_speed = _session->transport_speed();
-       
+
        if (current_transport_speed <= 0.0f) {
                switch (option) {
                case 0:
@@ -2341,7 +2450,7 @@ ARDOUR_UI::update_clocks ()
        if (!_session) return;
 
        if (editor && !editor->dragging_playhead()) {
-               Clock (_session->audible_frame(), false, editor->get_preferred_edit_position()); /* EMIT_SIGNAL */
+               Clock (_session->audible_frame(), false, editor->get_preferred_edit_position (EDIT_IGNORE_PHEAD)); /* EMIT_SIGNAL */
        }
 }
 
@@ -2361,6 +2470,103 @@ ARDOUR_UI::stop_clocking ()
        clock_signal_connection.disconnect ();
 }
 
+bool
+ARDOUR_UI::save_as_progress_update (float fraction, int64_t cnt, int64_t total, Gtk::Label* label, Gtk::ProgressBar* bar)
+{
+       char buf[256];
+
+       snprintf (buf, sizeof (buf), _("Copied %" PRId64 " of %" PRId64), cnt, total);
+
+       label->set_text (buf);
+       bar->set_fraction (fraction);
+
+       /* process events, redraws, etc. */
+
+       while (gtk_events_pending()) {
+               gtk_main_iteration ();
+       }
+
+       return true; /* continue with save-as */
+}
+
+void
+ARDOUR_UI::save_session_as ()
+{
+       if (!_session) {
+               return;
+       }
+
+       if (!save_as_dialog) {
+               save_as_dialog = new SaveAsDialog;
+       }
+
+       save_as_dialog->set_name (_session->name());
+
+       int response = save_as_dialog->run ();
+
+       save_as_dialog->hide ();
+
+       switch (response) {
+       case Gtk::RESPONSE_OK:
+               break;
+       default:
+               return;
+       }
+
+
+       Session::SaveAs sa;
+
+       sa.new_parent_folder = save_as_dialog->new_parent_folder ();
+       sa.new_name = save_as_dialog->new_name ();
+       sa.switch_to = save_as_dialog->switch_to();
+       sa.copy_media = save_as_dialog->copy_media();
+       sa.copy_external = save_as_dialog->copy_external();
+       sa.include_media = save_as_dialog->include_media ();
+
+       /* Only bother with a progress dialog if we're going to copy
+          media into the save-as target. Without that choice, this
+          will be very fast because we're only talking about a few kB's to
+          perhaps a couple of MB's of data.
+       */
+
+       ArdourDialog progress_dialog (_("Save As"), true);
+
+       if (sa.include_media && sa.copy_media) {
+
+               Gtk::Label label;
+               Gtk::ProgressBar progress_bar;
+
+               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
+                * complete (in terms of total data to copy), the number of files
+                * 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));
+
+               progress_dialog.show_all ();
+               progress_dialog.present ();
+       }
+
+       if (_session->save_as (sa)) {
+               /* ERROR MESSAGE */
+               MessageDialog msg (string_compose (_("Save As failed: %1"), sa.failure_message));
+               msg.run ();
+       }
+
+       if (!sa.include_media) {
+               unload_session (false);
+               load_session (sa.final_session_folder_name, sa.new_name);
+       }
+}
+
 /** Ask the user for the name of a new snapshot and then take it.
  */
 
@@ -2491,7 +2697,7 @@ ARDOUR_UI::rename_session ()
                        }
                        }
                }
-               
+
                break;
        }
 
@@ -2737,7 +2943,7 @@ ARDOUR_UI::get_session_parameters (bool quit_on_cancel, bool should_be_new, stri
        bool cancel_not_quit;
 
        /* deal with any existing DIRTY session now, rather than later. don't
-        * treat a non-dirty session this way, so that it stays visible 
+        * treat a non-dirty session this way, so that it stays visible
         * as we bring up the new session dialog.
         */
 
@@ -2765,7 +2971,7 @@ ARDOUR_UI::get_session_parameters (bool quit_on_cancel, bool should_be_new, stri
 
        session_name = basename_nosuffix (ARDOUR_COMMAND_LINE::session_name);
        session_path = ARDOUR_COMMAND_LINE::session_name;
-       
+
        if (!session_path.empty()) {
                if (Glib::file_test (session_path.c_str(), Glib::FILE_TEST_EXISTS)) {
                        if (Glib::file_test (session_path.c_str(), Glib::FILE_TEST_IS_REGULAR)) {
@@ -2801,7 +3007,7 @@ ARDOUR_UI::get_session_parameters (bool quit_on_cancel, bool should_be_new, stri
                        session_name = "";
                        session_dialog.clear_given ();
                }
-               
+
                if (should_be_new || session_name.empty()) {
                        /* need the dialog to get info from user */
 
@@ -2829,9 +3035,9 @@ ARDOUR_UI::get_session_parameters (bool quit_on_cancel, bool should_be_new, stri
                }
 
                /* if we run the startup dialog again, offer more than just "new session" */
-               
+
                should_be_new = false;
-               
+
                session_name = session_dialog.session_name (likely_new);
                session_path = session_dialog.session_folder ();
 
@@ -2840,22 +3046,22 @@ ARDOUR_UI::get_session_parameters (bool quit_on_cancel, bool should_be_new, stri
                }
 
                string::size_type suffix = session_name.find (statefile_suffix);
-               
+
                if (suffix != string::npos) {
                        session_name = session_name.substr (0, suffix);
                }
-               
+
                /* this shouldn't happen, but we catch it just in case it does */
-               
+
                if (session_name.empty()) {
                        continue;
                }
-               
+
                if (session_dialog.use_session_template()) {
                        template_name = session_dialog.session_template_name();
                        _session_is_new = true;
                }
-               
+
                if (session_name[0] == G_DIR_SEPARATOR ||
 #ifdef PLATFORM_WINDOWS
                    (session_name.length() > 3 && session_name[1] == ':' && session_name[2] == G_DIR_SEPARATOR)
@@ -2865,20 +3071,20 @@ ARDOUR_UI::get_session_parameters (bool quit_on_cancel, bool should_be_new, stri
 #endif
                         )
                {
-                       
+
                        /* absolute path or cwd-relative path specified for session name: infer session folder
                           from what was given.
                        */
-                       
+
                        session_path = Glib::path_get_dirname (session_name);
                        session_name = Glib::path_get_basename (session_name);
-                       
+
                } else {
 
                        session_path = session_dialog.session_folder();
-                       
+
                        char illegal = Session::session_name_is_legal (session_name);
-                       
+
                        if (illegal) {
                                MessageDialog msg (session_dialog,
                                                   string_compose (_("To ensure compatibility with various systems\n"
@@ -2889,7 +3095,7 @@ ARDOUR_UI::get_session_parameters (bool quit_on_cancel, bool should_be_new, stri
                                continue;
                        }
                }
-       
+
                if (Glib::file_test (session_path, Glib::FileTest (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))) {
 
 
@@ -2990,7 +3196,7 @@ ARDOUR_UI::load_session (const std::string& path, const std::string& snap_name,
 
        if (_session) {
                unload_status = unload_session ();
-               
+
                if (unload_status < 0) {
                        goto out;
                } else if (unload_status > 0) {
@@ -3034,7 +3240,26 @@ ARDOUR_UI::load_session (const std::string& path, const std::string& snap_name,
                }
                goto out;
        }
+       catch (SessionException e) {
+               MessageDialog msg (string_compose(
+                                          _("Session \"%1 (snapshot %2)\" did not load successfully: %3"),
+                                          path, snap_name, e.what()),
+                                  true,
+                                  Gtk::MESSAGE_INFO,
+                                  BUTTONS_OK);
+
+               msg.set_title (_("Loading Error"));
+               msg.set_position (Gtk::WIN_POS_CENTER);
+               pop_back_splash (msg);
+               msg.present ();
+
+               dump_errors (cerr);
+
+               (void) msg.run ();
+               msg.hide ();
 
+               goto out;
+       }
        catch (...) {
 
                MessageDialog msg (string_compose(
@@ -3048,6 +3273,9 @@ ARDOUR_UI::load_session (const std::string& path, const std::string& snap_name,
                msg.set_position (Gtk::WIN_POS_CENTER);
                pop_back_splash (msg);
                msg.present ();
+
+               dump_errors (cerr);
+
                (void) msg.run ();
                msg.hide ();
 
@@ -3075,7 +3303,7 @@ ARDOUR_UI::load_session (const std::string& path, const std::string& snap_name,
                (void) msg.run ();
                msg.hide ();
        }
-       
+
 
        /* Now the session been created, add the transport controls */
        new_session->add_controllable(roll_controllable);
@@ -3132,9 +3360,20 @@ ARDOUR_UI::build_session (const std::string& path, const std::string& snap_name,
                new_session = new Session (*AudioEngine::instance(), path, snap_name, &bus_profile);
        }
 
+       catch (SessionException e) {
+               dump_errors (cerr);
+               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);
+               pop_back_splash (msg);
+               msg.run ();
+               return -1;
+       }
        catch (...) {
-
+               dump_errors (cerr);
                MessageDialog msg (string_compose(_("Could not create session in \"%1\""), path));
+               msg.set_title (_("Loading Error"));
+               msg.set_position (Gtk::WIN_POS_CENTER);
                pop_back_splash (msg);
                msg.run ();
                return -1;
@@ -3490,46 +3729,80 @@ ARDOUR_UI::flush_trash ()
        display_cleanup_results (rep, _("deleted file"), true);
 }
 
+void
+ARDOUR_UI::cleanup_peakfiles ()
+{
+       if (_session == 0) {
+               /* shouldn't happen: menu item is insensitive */
+               return;
+       }
+
+       if (! _session->can_cleanup_peakfiles ()) {
+               return;
+       }
+
+       // get all region-views in this session
+       RegionSelection rs;
+       TrackViewList empty;
+       empty.clear();
+       editor->get_regions_after(rs, (framepos_t) 0, empty);
+       std::list<RegionView*> views = rs.by_layer();
+
+       // remove displayed audio-region-views waveforms
+       for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
+               AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*i);
+               if (!arv) { continue ; }
+               arv->delete_waves();
+       }
+
+       // cleanup peak files:
+       // - stop pending peakfile threads
+       // - close peakfiles if any
+       // - remove peak dir in session
+       // - setup peakfiles (background thread)
+       _session->cleanup_peakfiles ();
+
+       // re-add waves to ARV
+       for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
+               AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*i);
+               if (!arv) { continue ; }
+               arv->create_waves();
+       }
+}
+
 void
 ARDOUR_UI::setup_order_hint (AddRouteDialog::InsertAt place)
 {
-       uint32_t order_hint = 0;
+       uint32_t order_hint = UINT32_MAX;
+
+       if (editor->get_selection().tracks.empty()) {
+               return;
+       }
 
        /*
-         we want the new routes to have their order keys set starting from 
+         we want the new routes to have their order keys set starting from
          the highest order key in the selection + 1 (if available).
        */
-       if (place == AddRouteDialog::MixerSelection) {
-               for (RouteUISelection::iterator s = mixer->selection().routes.begin(); s != mixer->selection().routes.end(); ++s) {
-                       if ((*s)->route()->order_key() > order_hint) {
-                               order_hint = (*s)->route()->order_key();
-                       }
-               }
 
-               if (!mixer->selection().routes.empty()) {
+       if (place == AddRouteDialog::AfterSelection) {
+               RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView*> (editor->get_selection().tracks.back());
+               if (rtav) {
+                       order_hint = rtav->route()->order_key();
                        order_hint++;
-               } else {
-                       return;
-               }
-
-       } else if (place == AddRouteDialog::EditorSelection){
-               for (TrackSelection::iterator s = editor->get_selection().tracks.begin(); s != editor->get_selection().tracks.end(); ++s) {
-                       RouteTimeAxisView* tav = dynamic_cast<RouteTimeAxisView*> (*s);
-                       if (tav && tav->route() && tav->route()->order_key() > order_hint) {
-                               order_hint = tav->route()->order_key();
-                       }
                }
-
-               if (!editor->get_selection().tracks.empty()) {
-                       order_hint++;
-               } else {
-                       return;
+       } else if (place == AddRouteDialog::BeforeSelection) {
+               RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView*> (editor->get_selection().tracks.front());
+               if (rtav) {
+                       order_hint = rtav->route()->order_key();
                }
-
        } else if (place == AddRouteDialog::First) {
                order_hint = 0;
        } else {
-               /** AddRouteDialog::Last
+               /* leave order_hint at UINT32_MAX */
+       }
+
+       if (order_hint == UINT32_MAX) {
+               /** AddRouteDialog::Last or selection with first/last not a RouteTimeAxisView
                 * not setting an order hint will place new routes last.
                 */
                return;
@@ -3740,7 +4013,7 @@ ARDOUR_UI::start_video_server (Gtk::Window* float_window, bool popup_msg)
                        Config->set_video_advanced_setup(false);
                } else {
                        std::ostringstream osstream;
-                       osstream << "http://localhost:" << video_server_dialog->get_listenport() << "/";
+                       osstream << "http://127.0.0.1:" << video_server_dialog->get_listenport() << "/";
                        Config->set_video_server_url(osstream.str());
                        Config->set_video_server_docroot(icsd_docroot);
                        Config->set_video_advanced_setup(true);
@@ -4002,7 +4275,10 @@ ARDOUR_UI::keyboard_settings () const
 void
 ARDOUR_UI::create_xrun_marker (framepos_t where)
 {
-       editor->mouse_add_new_marker (where, false, true);
+       if (_session) {
+               Location *location = new Location (*_session, where, where, _("xrun"), Location::IsMark);
+               _session->locations()->add (location);
+       }
 }
 
 void
@@ -4054,6 +4330,7 @@ quickly enough to keep up with recording.\n"), PROGRAM_NAME));
 static MessageDialog *scan_dlg = NULL;
 static ProgressBar   *scan_pbar = NULL;
 static HBox          *scan_tbox = NULL;
+static Gtk::Button   *scan_timeout_button;
 
 void
 ARDOUR_UI::cancel_plugin_scan ()
@@ -4065,7 +4342,7 @@ void
 ARDOUR_UI::cancel_plugin_timeout ()
 {
        PluginManager::instance().cancel_plugin_timeout();
-       scan_tbox->hide();
+       scan_timeout_button->set_sensitive (false);
 }
 
 void
@@ -4075,10 +4352,13 @@ ARDOUR_UI::plugin_scan_timeout (int timeout)
                return;
        }
        if (timeout > 0) {
+               scan_pbar->set_sensitive (false);
+               scan_timeout_button->set_sensitive (true);
                scan_pbar->set_fraction ((float) timeout / (float) Config->get_vst_scan_timeout());
                scan_tbox->show();
        } else {
-               scan_tbox->hide();
+               scan_pbar->set_sensitive (false);
+               scan_timeout_button->set_sensitive (false);
        }
        gui_idle_handler();
 }
@@ -4091,7 +4371,7 @@ ARDOUR_UI::plugin_scan_dialog (std::string type, std::string plugin, bool can_ca
        }
 
        const bool cancelled = PluginManager::instance().cancelled();
-       if (type != X_("closeme") && !ui_config->get_show_plugin_scan_window()) {
+       if (type != X_("closeme") && (!ui_config->get_show_plugin_scan_window()) && !_initial_verbose_plugin_scan) {
                if (cancelled && scan_dlg->is_mapped()) {
                        scan_dlg->hide();
                        gui_idle_handler();
@@ -4103,7 +4383,6 @@ ARDOUR_UI::plugin_scan_dialog (std::string type, std::string plugin, bool can_ca
        }
 
        static Gtk::Button *cancel_button;
-       static Gtk::Button *timeout_button;
        if (!scan_dlg) {
                scan_dlg = new MessageDialog("", false, MESSAGE_INFO, BUTTONS_NONE); // TODO manage
                VBox* vbox = scan_dlg->get_vbox();
@@ -4119,10 +4398,10 @@ ARDOUR_UI::plugin_scan_dialog (std::string type, std::string plugin, bool can_ca
 
                scan_tbox = manage( new HBox() );
 
-               timeout_button = manage(new Gtk::Button(_("Stop Timeout")));
-               timeout_button->set_name ("EditorGTKButton");
-               timeout_button->signal_clicked().connect ( mem_fun (*this, &ARDOUR_UI::cancel_plugin_timeout) );
-               timeout_button->show();
+               scan_timeout_button = manage(new Gtk::Button(_("Stop Timeout")));
+               scan_timeout_button->set_name ("EditorGTKButton");
+               scan_timeout_button->signal_clicked().connect ( mem_fun (*this, &ARDOUR_UI::cancel_plugin_timeout) );
+               scan_timeout_button->show();
 
                scan_pbar = manage(new ProgressBar());
                scan_pbar->set_orientation(Gtk::PROGRESS_RIGHT_TO_LEFT);
@@ -4130,7 +4409,7 @@ ARDOUR_UI::plugin_scan_dialog (std::string type, std::string plugin, bool can_ca
                scan_pbar->show();
 
                scan_tbox->pack_start (*scan_pbar, PACK_EXPAND_WIDGET, 4);
-               scan_tbox->pack_start (*timeout_button, PACK_SHRINK, 4);
+               scan_tbox->pack_start (*scan_timeout_button, PACK_SHRINK, 4);
 
                scan_dlg->get_vbox()->pack_start (*scan_tbox, PACK_SHRINK, 4);
        }
@@ -4138,13 +4417,14 @@ ARDOUR_UI::plugin_scan_dialog (std::string type, std::string plugin, bool can_ca
        assert(scan_dlg && scan_tbox && cancel_button);
 
        if (type == X_("closeme")) {
+               scan_tbox->hide();
                scan_dlg->hide();
        } else {
                scan_dlg->set_message(type + ": " + Glib::path_get_basename(plugin));
                scan_dlg->show();
        }
        if (!can_cancel || !cancelled) {
-               scan_tbox->hide();
+               scan_timeout_button->set_sensitive(false);
        }
        cancel_button->set_sensitive(can_cancel && !cancelled);
 
@@ -4279,7 +4559,7 @@ ARDOUR_UI::disconnect_from_engine ()
         */
 
        halt_connection.disconnect ();
-       
+
        if (AudioEngine::instance()->stop ()) {
                MessageDialog msg (*editor, _("Could not disconnect from Audio/MIDI engine"));
                msg.run ();
@@ -4287,7 +4567,7 @@ ARDOUR_UI::disconnect_from_engine ()
        } else {
                AudioEngine::instance()->Halted.connect_same_thread (halt_connection, boost::bind (&ARDOUR_UI::engine_halted, this, _1, false));
        }
-       
+
        update_sample_rate (0);
        return 0;
 }
@@ -4306,7 +4586,7 @@ ARDOUR_UI::reconnect_to_engine ()
                }
                return -1;
        }
-       
+
        update_sample_rate (0);
        return 0;
 }
@@ -4324,13 +4604,13 @@ void
 ARDOUR_UI::update_transport_clocks (framepos_t pos)
 {
        if (ui_config->get_primary_clock_delta_edit_cursor()) {
-               primary_clock->set (pos, false, editor->get_preferred_edit_position());
+               primary_clock->set (pos, false, editor->get_preferred_edit_position (EDIT_IGNORE_PHEAD));
        } else {
                primary_clock->set (pos);
        }
 
        if (ui_config->get_secondary_clock_delta_edit_cursor()) {
-               secondary_clock->set (pos, false, editor->get_preferred_edit_position());
+               secondary_clock->set (pos, false, editor->get_preferred_edit_position (EDIT_IGNORE_PHEAD));
        } else {
                secondary_clock->set (pos);
        }
@@ -4395,7 +4675,7 @@ 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")));
@@ -4670,7 +4950,7 @@ ARDOUR_UI::transport_numpad_event (int num)
        if ( _numpad_locate_happening ) {
                _pending_locate_num = _pending_locate_num*10 + num;
        } else {
-               switch (num) {          
+               switch (num) {
                        case 0:  toggle_roll(false, false);             break;
                        case 1:  transport_rewind(1);                           break;
                        case 2:  transport_forward(1);                          break;
@@ -4690,3 +4970,66 @@ ARDOUR_UI::set_flat_buttons ()
 {
        CairoWidget::set_flat_buttons( config()->get_flat_buttons() );
 }
+
+void
+ARDOUR_UI::audioengine_became_silent ()
+{
+       MessageDialog msg (string_compose (_("This is a free/demo copy of %1. It has just switched to silent mode."), PROGRAM_NAME),
+                          true,
+                          Gtk::MESSAGE_WARNING,
+                          Gtk::BUTTONS_NONE,
+                          true);
+
+       msg.set_title (string_compose (_("%1 is now silent"), PROGRAM_NAME));
+
+       Gtk::Label pay_label (string_compose (_("Please consider paying for a copy of %1 - you can pay whatever you want."), PROGRAM_NAME));
+       Gtk::Label subscribe_label (_("Better yet become a subscriber - subscriptions start at US$1 per month."));
+       Gtk::Button pay_button (_("Pay for a copy (via the web)"));
+       Gtk::Button subscribe_button (_("Become a subscriber (via the web)"));
+       Gtk::HBox pay_button_box;
+       Gtk::HBox subscribe_button_box;
+
+       pay_button_box.pack_start (pay_button, true, false);
+       subscribe_button_box.pack_start (subscribe_button, true, false);
+
+       bool (*openuri)(const char*) = PBD::open_uri; /* this forces selection of the const char* variant of PBD::open_uri(), which we need to avoid ambiguity below */
+
+       pay_button.signal_clicked().connect (sigc::hide_return (sigc::bind (sigc::ptr_fun (openuri), (const char*) "https://ardour.org/download")));
+       subscribe_button.signal_clicked().connect (sigc::hide_return (sigc::bind (sigc::ptr_fun (openuri), (const char*) "https://community.ardour.org/s/subscribe")));
+
+       msg.get_vbox()->pack_start (pay_label);
+       msg.get_vbox()->pack_start (pay_button_box);
+       msg.get_vbox()->pack_start (subscribe_label);
+       msg.get_vbox()->pack_start (subscribe_button_box);
+
+       msg.get_vbox()->show_all ();
+
+       msg.add_button (_("Remain silent"), Gtk::RESPONSE_CANCEL);
+       msg.add_button (_("Save and quit"), Gtk::RESPONSE_NO);
+       msg.add_button (_("Give me more time"), Gtk::RESPONSE_YES);
+
+       int r = msg.run ();
+
+       switch (r) {
+       case Gtk::RESPONSE_YES:
+               AudioEngine::instance()->reset_silence_countdown ();
+               break;
+
+       case Gtk::RESPONSE_NO:
+               /* save and quit */
+               save_state_canfail ("");
+               exit (0);
+               break;
+
+       case Gtk::RESPONSE_CANCEL:
+       default:
+               /* don't reset, save session and exit */
+               break;
+       }
+}
+
+void
+ARDOUR_UI::hide_application ()
+{
+    Application::instance ()-> hide ();
+}