cleanup a couple of audio file format names as reported by libsndfile
[ardour.git] / gtk2_ardour / ardour_ui.cc
index 829a7df6ada51b0f95495a0729f1b3d2f88cf461..2d4e94883494a1bf291d578e1b5bb2bbd489e667 100644 (file)
 #include <fcntl.h>
 #include <signal.h>
 #include <unistd.h>
+#include <time.h>
 #include <cerrno>
 #include <fstream>
+#include <stdlib.h>
 
 #include <iostream>
 
 #include <ardour/audioengine.h>
 #include <ardour/playlist.h>
 #include <ardour/utils.h>
+#include <ardour/plugin.h>
 #include <ardour/audio_diskstream.h>
 #include <ardour/audiofilesource.h>
 #include <ardour/recent_sessions.h>
 #include <ardour/port.h>
 #include <ardour/audio_track.h>
 
+typedef uint64_t microseconds_t;
+
 #include "actions.h"
 #include "ardour_ui.h"
 #include "public_editor.h"
 #include "new_session_dialog.h"
 #include "about.h"
 #include "splash.h"
+#include "nag.h"
 #include "utils.h"
 #include "gui_thread.h"
 #include "theme_manager.h"
 #include "engine_dialog.h"
+#include "gain_meter.h"
+#include "route_time_axis.h"
 
 #include "i18n.h"
 
@@ -105,10 +113,6 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[])
          preroll_clock (X_("preroll"), false, X_("PreRollClock"), true, true),
          postroll_clock (X_("postroll"), false, X_("PostRollClock"), true, true),
 
-         /* adjuster table */
-
-         adjuster_table (3, 3),
-
          /* preroll stuff */
 
          preroll_button (_("pre\nroll")),
@@ -130,13 +134,13 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[])
          shuttle_controllable ("shuttle", *this, TransportControllable::ShuttleControl),
          shuttle_controller_binding_proxy (shuttle_controllable),
 
-         roll_button (roll_controllable),
-         stop_button (stop_controllable),
-         goto_start_button (goto_start_controllable),
-         goto_end_button (goto_end_controllable),
-         auto_loop_button (auto_loop_controllable),
-         play_selection_button (play_selection_controllable),
-         rec_button (rec_controllable),
+         roll_button (&roll_controllable),
+         stop_button (&stop_controllable),
+         goto_start_button (&goto_start_controllable),
+         goto_end_button (&goto_end_controllable),
+         auto_loop_button (&auto_loop_controllable),
+         play_selection_button (&play_selection_controllable),
+         rec_button (&rec_controllable),
          
          shuttle_units_button (_("% ")),
 
@@ -198,9 +202,17 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[])
        session_loaded = false;
        last_speed_displayed = -1.0f;
        ignore_dual_punch = false;
+       _mixer_on_top = false;
+
+       roll_button.unset_flags (Gtk::CAN_FOCUS);
+       stop_button.unset_flags (Gtk::CAN_FOCUS);
+       goto_start_button.unset_flags (Gtk::CAN_FOCUS);
+       goto_end_button.unset_flags (Gtk::CAN_FOCUS);
+       auto_loop_button.unset_flags (Gtk::CAN_FOCUS);
+       play_selection_button.unset_flags (Gtk::CAN_FOCUS);
+       rec_button.unset_flags (Gtk::CAN_FOCUS);
 
-       last_configure_time.tv_sec = 0;
-       last_configure_time.tv_usec = 0;
+       last_configure_time= 0;
 
        shuttle_grabbed = false;
        shuttle_fract = 0.0;
@@ -209,12 +221,15 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[])
        shuttle_style_menu = 0;
        shuttle_unit_menu = 0;
 
-       gettimeofday (&last_peak_grab, 0);
-       gettimeofday (&last_shuttle_request, 0);
+        // We do not have jack linked in yet so;
+        
+       last_shuttle_request = last_peak_grab = 0; //  get_microseconds();
 
        ARDOUR::Diskstream::DiskOverrun.connect (mem_fun(*this, &ARDOUR_UI::disk_overrun_handler));
        ARDOUR::Diskstream::DiskUnderrun.connect (mem_fun(*this, &ARDOUR_UI::disk_underrun_handler));
 
+       ARDOUR::Plugin::PresetFileExists.connect (mem_fun(*this, &ARDOUR_UI::preset_file_exists_handler));
+
        /* handle dialog requests */
 
        ARDOUR::Session::Dialog.connect (mem_fun(*this, &ARDOUR_UI::session_dialog));
@@ -238,6 +253,9 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[])
                Config->set_current_owner (ConfigVariableBase::Interface);
                setup_profile ();
 
+               GainMeter::setup_slider_pix ();
+               RouteTimeAxisView::setup_slider_pix ();
+
        } catch (failed_constructor& err) {
                error << _("could not initialize Ardour.") << endmsg;
                // pass it on up
@@ -333,11 +351,12 @@ ARDOUR_UI::post_engine ()
        /* set default clock modes */
 
        if (Profile->get_sae()) {
-               primary_clock.set_mode (AudioClock::MinSec);
+               primary_clock.set_mode (AudioClock::BBT);
+               secondary_clock.set_mode (AudioClock::MinSec);
        }  else {
                primary_clock.set_mode (AudioClock::SMPTE);
+               secondary_clock.set_mode (AudioClock::BBT);
        }
-       secondary_clock.set_mode (AudioClock::BBT);
 
        /* start the time-of-day-clock */
        
@@ -402,21 +421,15 @@ ARDOUR_UI::pop_back_splash ()
 gint
 ARDOUR_UI::configure_timeout ()
 {
-       struct timeval now;
-       struct timeval diff;
-
-       if (last_configure_time.tv_sec == 0 && last_configure_time.tv_usec == 0) {
+       if (last_configure_time == 0) {
                /* no configure events yet */
                return TRUE;
        }
 
-       gettimeofday (&now, 0);
-       timersub (&now, &last_configure_time, &diff);
-
        /* force a gap of 0.5 seconds since the last configure event
         */
 
-       if (diff.tv_sec == 0 && diff.tv_usec < 500000) {
+       if (get_microseconds() - last_configure_time < 500000) {
                return TRUE;
        } else {
                have_configure_timeout = false;
@@ -429,7 +442,7 @@ gboolean
 ARDOUR_UI::configure_handler (GdkEventConfigure* conf)
 {
        if (have_configure_timeout) {
-               gettimeofday (&last_configure_time, 0);
+               last_configure_time = get_microseconds();
        } else {
                Glib::signal_timeout().connect (mem_fun(*this, &ARDOUR_UI::configure_timeout), 100);
                have_configure_timeout = true;
@@ -534,6 +547,14 @@ ARDOUR_UI::save_ardour_state ()
 gint
 ARDOUR_UI::autosave_session ()
 {
+       if (g_main_depth() > 1) {
+               /* inside a recursive main loop,
+                  give up because we may not be able to 
+                  take a lock.
+               */
+               return 1;
+       }
+
         if (!Config->get_periodic_safety_backups())
                 return 1;
         
@@ -549,7 +570,7 @@ ARDOUR_UI::update_autosave ()
 {
         ENSURE_GUI_THREAD (mem_fun (*this, &ARDOUR_UI::update_autosave));
         
-        if (session->dirty()) {
+        if (session && session->dirty()) {
                 if (_autosave_connection.connected()) {
                         _autosave_connection.disconnect();
                 }
@@ -621,7 +642,7 @@ void
 ARDOUR_UI::startup ()
 {
        string name, path;
-
+       
        new_session_dialog = new NewSessionDialog();
 
        bool backend_audio_is_running = EngineControl::engine_running();
@@ -740,6 +761,7 @@ If you still wish to quit, please use the\n\n\
                session->set_deletion_in_progress ();
        }
 
+       ArdourDialog::close_all_dialogs ();
        engine->stop (true);
        save_ardour_state ();
        quit ();
@@ -795,8 +817,6 @@ ARDOUR_UI::ask_about_saving_session (const string & what)
        window.set_resizable (false);
        window.show_all ();
 
-       save_the_session = 0;
-
        window.set_keep_above (true);
        window.present ();
 
@@ -883,8 +903,14 @@ void
 ARDOUR_UI::update_buffer_load ()
 {
        char buf[64];
+       uint32_t c, p;
 
        if (session) {
+               c = session->capture_load ();
+               p = session->playback_load ();
+               
+               push_buffer_stats (c, p);
+
                snprintf (buf, sizeof (buf), _("Buffers p:%" PRIu32 "%% c:%" PRIu32 "%%"), 
                          session->playback_load(), session->capture_load());
                buffer_load_label.set_text (buf);
@@ -1127,33 +1153,6 @@ ARDOUR_UI::open_recent_session ()
        }
 }
 
-bool
-ARDOUR_UI::filter_ardour_session_dirs (const FileFilter::Info& info) 
-{
-       struct stat statbuf;
-
-       if (stat (info.filename.c_str(), &statbuf) != 0) {
-               return false;
-       }
-
-       if (!S_ISDIR(statbuf.st_mode)) {
-               return false;
-       }
-
-        // XXX Portability
-        
-       string session_file = info.filename;
-       session_file += '/';
-       session_file += Glib::path_get_basename (info.filename);
-       session_file += ".ardour";
-       
-       if (stat (session_file.c_str(), &statbuf) != 0) {
-               return false;
-       }
-
-       return S_ISREG (statbuf.st_mode);
-}
-
 bool
 ARDOUR_UI::check_audioengine ()
 {
@@ -1162,6 +1161,7 @@ ARDOUR_UI::check_audioengine ()
                        MessageDialog msg (_("Ardour is not connected to JACK\n"
                                             "You cannot open or close sessions in this condition"));
                        pop_back_splash ();
+                       msg.set_position (WIN_POS_CENTER);
                        msg.run ();
                        return false;
                }
@@ -1184,7 +1184,7 @@ ARDOUR_UI::open_session ()
 
                /* ardour sessions are folders */
 
-               open_session_selector = new Gtk::FileChooserDialog (_("open session"), FILE_CHOOSER_ACTION_OPEN);
+               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);
@@ -1308,7 +1308,7 @@ ARDOUR_UI::do_transport_locate (nframes_t new_position)
 }
 
 void
-ARDOUR_UI::transport_goto_start ()
+ARDOUR_UI::transport_goto_start ()  
 {
        if (session) {
                session->goto_start();
@@ -1341,6 +1341,34 @@ ARDOUR_UI::transport_goto_zero ()
        }
 }
 
+void
+ARDOUR_UI::transport_goto_wallclock ()
+{
+       if (session && editor) {
+
+               time_t now;
+               struct tm tmnow;
+               nframes64_t frames;
+               
+               time (&now);
+               localtime_r (&now, &tmnow);
+       
+               frames = tmnow.tm_hour * (60 * 60 * session->frame_rate());
+               frames += tmnow.tm_min * (60 * session->frame_rate());
+               frames += tmnow.tm_sec * session->frame_rate();
+
+               session->request_locate (frames);
+
+               /* force displayed area in editor to start no matter
+                  what "follow playhead" setting is.
+               */
+               
+               if (editor) {
+                       editor->reset_x_origin (frames - (editor->current_page_frames()/2));
+               }
+       }
+}
+
 void
 ARDOUR_UI::transport_goto_end ()
 {
@@ -1662,8 +1690,8 @@ ARDOUR_UI::engine_halted ()
                           _("\
 JACK has either been shutdown or it\n\
 disconnected Ardour because Ardour\n\
-was not fast enough. You can save the\n\
-session and/or try to reconnect to JACK ."));
+was not fast enough. Try to restart\n\
+JACK, reconnect and save the session."));
        pop_back_splash ();
        msg.run ();
 }
@@ -1822,13 +1850,27 @@ ARDOUR_UI::snapshot_session ()
 
        prompter.set_name ("Prompter");
        prompter.add_button (Gtk::Stock::SAVE, Gtk::RESPONSE_ACCEPT);
+       prompter.set_title (_("Take Snapshot"));
        prompter.set_prompt (_("Name of New Snapshot"));
        prompter.set_initial_text (timebuf);
-       
+
+  again:
        switch (prompter.run()) {
        case RESPONSE_ACCEPT:
                prompter.get_result (snapname);
                if (snapname.length()){
+                       if (snapname.find ('/') != string::npos) {
+                               MessageDialog msg (_("To ensure compatibility with various systems\n"
+                                                    "snapshot names may not contain a '/' character"));
+                               msg.run ();
+                               goto again;
+                       }
+                       if (snapname.find ('\\') != string::npos) {
+                               MessageDialog msg (_("To ensure compatibility with various systems\n"
+                                                    "snapshot names may not contain a '\\' character"));
+                               msg.run ();
+                               goto again;
+                       }
                        save_state (snapname);
                }
                break;
@@ -1862,17 +1904,6 @@ ARDOUR_UI::save_state_canfail (string name)
        return 0;
 }
 
-void
-ARDOUR_UI::restore_state (string name)
-{
-       if (session) {
-               if (name.length() == 0) {
-                       name = session->name();
-               }
-               session->restore_state (name);
-       }
-}
-
 void
 ARDOUR_UI::primary_clock_value_changed ()
 {
@@ -1897,37 +1928,6 @@ ARDOUR_UI::secondary_clock_value_changed ()
        }
 }
 
-void
-ARDOUR_UI::rec_enable_button_blink (bool onoff, AudioDiskstream *dstream, Widget *w)
-{
-       if (session && dstream && dstream->record_enabled()) {
-
-               Session::RecordState rs;
-               
-               rs = session->record_status ();
-
-               switch (rs) {
-               case Session::Disabled:
-               case Session::Enabled:
-                       if (w->get_state() != STATE_SELECTED) {
-                               w->set_state (STATE_SELECTED);
-                       }
-                       break;
-
-               case Session::Recording:
-                       if (w->get_state() != STATE_ACTIVE) {
-                               w->set_state (STATE_ACTIVE);
-                       }
-                       break;
-               }
-
-       } else {
-               if (w->get_state() != STATE_NORMAL) {
-                       w->set_state (STATE_NORMAL);
-               }
-       }
-}
-
 void
 ARDOUR_UI::transport_rec_enable_blink (bool onoff) 
 {
@@ -1954,17 +1954,8 @@ ARDOUR_UI::transport_rec_enable_blink (bool onoff)
        }
 }
 
-gint
-ARDOUR_UI::hide_and_quit (GdkEventAny *ev, ArdourDialog *window)
-{
-       window->hide();
-       Gtk::Main::quit ();
-       return TRUE;
-}
-
 void
 ARDOUR_UI::save_template ()
-
 {
        ArdourPrompter prompter (true);
        string name;
@@ -1974,6 +1965,7 @@ ARDOUR_UI::save_template ()
        }
 
        prompter.set_name (X_("Prompter"));
+       prompter.set_title (_("Save Mix Template"));
        prompter.set_prompt (_("Name for mix template:"));
        prompter.set_initial_text(session->name() + _("-template"));
        prompter.add_button (Gtk::Stock::SAVE, Gtk::RESPONSE_ACCEPT);
@@ -1995,6 +1987,9 @@ ARDOUR_UI::save_template ()
 void
 ARDOUR_UI::fontconfig_dialog ()
 {
+#if 0
+  /* this issue seems to have gone away with changes to font handling in GTK/Quartz
+   */
 #ifdef GTKOSX
        /* X11 users will always have fontconfig info around, but new GTK-OSX users 
           may not and it can take a while to build it. Warn them.
@@ -2018,6 +2013,7 @@ ARDOUR_UI::fontconfig_dialog ()
                msg.run ();
        }
 #endif
+#endif
 }
 
 void
@@ -2101,6 +2097,7 @@ ARDOUR_UI::ask_about_loading_existing_session (const Glib::ustring& session_path
        
        
        msg.set_name (X_("CleanupDialog"));
+       msg.set_title (_("Cleanup Unused Sources"));
        msg.set_wmclass (X_("existing_session"), "Ardour");
        msg.set_position (Gtk::WIN_POS_MOUSE);
        pop_back_splash ();
@@ -2198,7 +2195,7 @@ ARDOUR_UI::loading_message (const std::string& msg)
        splash->message (msg);
        flush_pending ();
 }
-       
+
 void
 ARDOUR_UI::idle_load (const Glib::ustring& path)
 {
@@ -2211,11 +2208,16 @@ ARDOUR_UI::idle_load (const Glib::ustring& path)
                        load_session (Glib::path_get_dirname (path), basename_nosuffix (path));
                }
        } else {
+
                ARDOUR_COMMAND_LINE::session_name = path;
+
                if (new_session_dialog) {
+
+
                        /* make it break out of Dialog::run() and
                           start again.
                         */
+
                        new_session_dialog->response (1);
                }
        }
@@ -2229,7 +2231,7 @@ ARDOUR_UI::get_session_parameters (bool backend_audio_is_running, bool should_be
        Glib::ustring session_path;
        Glib::ustring template_name;
        int response;
-
+       
   begin:
        response = Gtk::RESPONSE_NONE;
 
@@ -2284,6 +2286,9 @@ ARDOUR_UI::get_session_parameters (bool backend_audio_is_running, bool should_be
                case Gtk::RESPONSE_CANCEL:
                case Gtk::RESPONSE_DELETE_EVENT:
                        if (!session) {
+                               if (engine && engine->running()) {
+                                       engine->stop (true);
+                               }
                                quit();
                        }
                        new_session_dialog->hide ();
@@ -2297,19 +2302,28 @@ ARDOUR_UI::get_session_parameters (bool backend_audio_is_running, bool should_be
                fontconfig_dialog();
 
                if (!backend_audio_is_running) {
-                       if (new_session_dialog->engine_control.setup_engine ()) {
-                               new_session_dialog->hide ();
+                       int ret = new_session_dialog->engine_control.setup_engine ();
+                       if (ret < 0) {
                                return false;
-                       } 
+                       } else if (ret > 0) {
+                               response = Gtk::RESPONSE_REJECT;
+                               goto try_again;
+                       }
+                       
+                       /* hide the NSD while we start up the engine */
+
+                       new_session_dialog->hide ();
+                       flush_pending ();
                }
-               
+
                if (create_engine ()) {
 
                        backend_audio_error (!backend_audio_is_running, new_session_dialog);
                        flush_pending ();
 
                        new_session_dialog->set_existing_session (false);
-                       new_session_dialog->set_current_page (2);
+                       new_session_dialog->set_current_page (0); // new engine page
+                       new_session_dialog->engine_control.unset_interface_chosen ();
 
                        response = Gtk::RESPONSE_NONE;
                        goto try_again;
@@ -2333,34 +2347,60 @@ ARDOUR_UI::get_session_parameters (bool backend_audio_is_running, bool should_be
                        if (session_name[0] == '/' || 
                            (session_name.length() > 2 && session_name[0] == '.' && session_name[1] == '/') ||
                            (session_name.length() > 3 && session_name[0] == '.' && session_name[1] == '.' && session_name[2] == '/')) {
-                               
+
                                session_path = Glib::path_get_dirname (session_name);
                                session_name = Glib::path_get_basename (session_name);
                                
                        } else {
 
                                session_path = new_session_dialog->session_folder();
+
                        }
 
                        template_name = Glib::ustring();                        
                        switch (new_session_dialog->which_page()) {
 
                        case NewSessionDialog::OpenPage: 
-                       case NewSessionDialog::EnginePage:
                                goto loadit;
                                break;
 
+                       case NewSessionDialog::EnginePage:
+                               if (new_session_dialog->engine_control.interface_chosen() && !session_path.empty()) {
+                                       goto loadit;
+                               } else {
+                                       goto try_again;
+                               }
+                               break;
+
                        case NewSessionDialog::NewPage: /* nominally the "new" session creator, but could be in use for an old session */
                                
                                should_be_new = true;
-                               
+
+                               if (session_name.find ('/') != Glib::ustring::npos) {
+                                       MessageDialog msg (*new_session_dialog, _("To ensure compatibility with various systems\n"
+                                                        "session names may not contain a '/' character"));
+                                       msg.run ();
+                                       response = RESPONSE_NONE;
+                                       goto try_again;
+                               }
+
+                               if (session_name.find ('\\') != Glib::ustring::npos) {
+                                       MessageDialog msg (*new_session_dialog, _("To ensure compatibility with various systems\n"
+                                                        "session names may not contain a '\\' character"));
+                                       msg.run ();
+                                       response = RESPONSE_NONE;
+                                       goto try_again;
+                               }
+
                                //XXX This is needed because session constructor wants a 
                                //non-existant path. hopefully this will be fixed at some point.
-                               
+
                                session_path = Glib::build_filename (session_path, session_name);
 
                                if (Glib::file_test (session_path, Glib::FileTest (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))) {
 
+                                       new_session_dialog->hide ();
+
                                        if (ask_about_loading_existing_session (session_path)) {
                                                goto loadit;
                                        } else {
@@ -2391,7 +2431,7 @@ ARDOUR_UI::get_session_parameters (bool backend_audio_is_running, bool should_be
                        
                  loadit:
                        new_session_dialog->hide ();
-                       
+
                        if (load_session (session_path, session_name, template_name)) {
                                /* force a retry */
                                response = Gtk::RESPONSE_NONE;
@@ -2404,7 +2444,7 @@ ARDOUR_UI::get_session_parameters (bool backend_audio_is_running, bool should_be
                        }
                }
 
-       } while (response == Gtk::RESPONSE_NONE);
+       } while (response == Gtk::RESPONSE_NONE || response == Gtk::RESPONSE_REJECT);
 
   done:
        show();
@@ -2421,7 +2461,9 @@ ARDOUR_UI::close_session ()
                return;
        }
 
-       unload_session (true);
+       if (unload_session (true)) {
+               return;
+       }
        
        get_session_parameters (true, false);
 }
@@ -2448,16 +2490,6 @@ ARDOUR_UI::load_session (const Glib::ustring& path, const Glib::ustring& snap_na
                goto out;
        }
 
-       /* if it already exists, we must have write access */
-
-       if (Glib::file_test (path.c_str(), Glib::FILE_TEST_EXISTS) && ::access (path.c_str(), W_OK)) {
-               MessageDialog msg (*editor, _("You do not have write access to this session.\n"
-                                             "This prevents the session from being loaded."));
-               pop_back_splash ();
-               msg.run ();
-               goto out;
-       }
-
        loading_message (_("Please wait while Ardour loads your session"));
 
        try {
@@ -2471,10 +2503,10 @@ ARDOUR_UI::load_session (const Glib::ustring& path, const Glib::ustring& snap_na
                MessageDialog msg (err.what(),
                                   true,
                                   Gtk::MESSAGE_INFO,
-                                  Gtk::BUTTONS_OK_CANCEL);
+                                  Gtk::BUTTONS_CLOSE);
                
-               msg.set_title (_("Loading Error"));
-               msg.set_secondary_text (_("Click the OK button to try again."));
+               msg.set_title (_("Port Registration Error"));
+               msg.set_secondary_text (_("Click the Close button to try again."));
                msg.set_position (Gtk::WIN_POS_CENTER);
                pop_back_splash ();
                msg.present ();
@@ -2492,15 +2524,21 @@ ARDOUR_UI::load_session (const Glib::ustring& path, const Glib::ustring& snap_na
                goto out;
        }
 
+       /* this exception is also special */
+
+       catch (Session::SRMismatchRejected& err) {
+               goto out; /* just go back and reload something else, etc. */
+       }
+
        catch (...) {
 
                MessageDialog msg (string_compose(_("Session \"%1 (snapshot %2)\" did not load successfully"), path, snap_name),
                                   true,
                                   Gtk::MESSAGE_INFO,
-                                  Gtk::BUTTONS_OK_CANCEL);
+                                  Gtk::BUTTONS_CLOSE);
                
                msg.set_title (_("Loading Error"));
-               msg.set_secondary_text (_("Click the OK button to try again."));
+               msg.set_secondary_text (_("Click the Close button to try again."));
                msg.set_position (Gtk::WIN_POS_CENTER);
                pop_back_splash ();
                msg.present ();
@@ -2582,6 +2620,9 @@ ARDOUR_UI::build_session (const Glib::ustring& path, const Glib::ustring& snap_n
        connect_to_session (new_session);
 
        session_loaded = true;
+
+       new_session->save_state(new_session->name());
+
        return 0;
 }
 
@@ -2607,9 +2648,21 @@ ARDOUR_UI::show_about ()
                about->signal_response().connect(mem_fun (*this, &ARDOUR_UI::about_signal_response) );
        }
 
+       about->set_transient_for(*editor);
+
        about->show_all ();
 }
 
+void
+ARDOUR_UI::launch_chat ()
+{
+#ifdef __APPLE__
+       NagScreen::open_uri("http://webchat.freenode.net/?channels=ardour-osx");
+#else
+       NagScreen::open_uri("http://webchat.freenode.net/?channels=ardour");
+#endif
+}
+
 void
 ARDOUR_UI::hide_about ()
 {
@@ -2652,7 +2705,8 @@ ARDOUR_UI::hide_splash ()
 }
 
 void
-ARDOUR_UI::display_cleanup_results (Session::cleanup_report& rep, const gchar* list_title, const string & msg)
+ARDOUR_UI::display_cleanup_results (Session::cleanup_report& rep, const gchar* list_title, 
+                                   const string& plural_msg, const string& singular_msg)
 {
        size_t removed;
 
@@ -2707,18 +2761,32 @@ require some unused files to continue to exist."));
 
        dimage->set_alignment(ALIGN_LEFT, ALIGN_TOP);
 
-       if (rep.space < 1048576.0f) {
-               if (removed > 1) {
-                 txt.set_text (string_compose (msg, removed, _("files were"), session->path() + "dead_sounds", (float) rep.space / 1024.0f, "kilo"));
-               } else {
-                       txt.set_text (string_compose (msg, removed, _("file was"), session->path() + "dead_sounds", (float) rep.space / 1024.0f, "kilo"));
-               }
+
+       /* subst:
+          %1 - number of files removed
+          %2 - location of "dead_sounds"
+          %3 - size of files affected
+          %4 - prefix for "bytes" to produce sensible results (e.g. mega, kilo, giga)
+       */
+
+       const char* bprefix;
+       float space_adjusted;
+
+       if (rep.space < 1000000.0f) {
+               bprefix = X_("kilo");
+               space_adjusted = truncf((float)rep.space / 1000.0f);
+       } else if (rep.space < (1000000.0f * 1000)) {
+               bprefix = X_("mega");
+               space_adjusted = truncf((float)rep.space / (1000000.0f));
        } else {
-               if (removed > 1) {
-                       txt.set_text (string_compose (msg, removed, _("files were"), session->path() + "dead_sounds", (float) rep.space / 1048576.0f, "mega"));
-               } else {
-                       txt.set_text (string_compose (msg, removed, _("file was"), session->path() + "dead_sounds", (float) rep.space / 1048576.0f, "mega"));
-               }
+               bprefix = X_("giga");
+               space_adjusted = truncf((float)rep.space / (1000000.0f * 1000));
+       }
+
+       if (removed > 1) {
+               txt.set_text (string_compose (plural_msg, removed, session->path() + "dead_sounds", space_adjusted, bprefix));
+       } else {
+               txt.set_text (string_compose (singular_msg, removed, session->path() + "dead_sounds", space_adjusted, bprefix));
        }
 
        dhbox.pack_start (*dimage, true, false, 5);
@@ -2795,23 +2863,31 @@ After cleanup, unused audio files will be moved to a \
        }
 
        if (session->cleanup_sources (rep)) {
+               editor->finish_cleanup ();
                return;
        }
+       
+       editor->finish_cleanup ();
 
        checker.hide();
        display_cleanup_results (rep, 
                                 _("cleaned files"),
                                 _("\
-The following %1 %2 not in use and \n\
+The following %1 files were not in use and \n\
 have been moved to:\n\
-%3. \n\n\
+%2. \n\n\
 Flushing the wastebasket will \n\
 release an additional\n\
-%4 %5bytes of disk space.\n"
+%3 %4bytes of disk space.\n"),
+                                _("\
+The following file was not in use and \n       \
+has been moved to:\n                           \
+%2. \n\n\
+Flushing the wastebasket will \n\
+release an additional\n\
+%3 %4bytes of disk space.\n"
                                         ));
 
-
-
 }
 
 void
@@ -2830,9 +2906,12 @@ ARDOUR_UI::flush_trash ()
 
        display_cleanup_results (rep, 
                                 _("deleted file"),
-                                _("The following %1 %2 deleted from\n\
-%3,\n\
-releasing %4 %5bytes of disk space"));
+                                _("The following %1 files were deleted from\n\
+%2,\n\
+releasing %3 %4bytes of disk space"),
+                                _("The following file was deleted from\n\
+%2,\n\
+releasing %3 %4bytes of disk space"));
 }
 
 void
@@ -2872,6 +2951,13 @@ ARDOUR_UI::add_route (Gtk::Window* float_window)
                return;
        }
 
+       string template_path = add_route_dialog->track_template();
+
+       if (!template_path.empty()) {
+         session->new_route_from_template (count, template_path);
+         return;
+       }
+       
        uint32_t input_chan = add_route_dialog->channels ();
        uint32_t output_chan;
        string name_template = add_route_dialog->name_template ();
@@ -2887,8 +2973,6 @@ ARDOUR_UI::add_route (Gtk::Window* float_window)
 
        /* XXX do something with name template */
 
-       cerr << "Adding with " << input_chan << " in and " << output_chan << "out\n";
-
        if (track) {
                session_add_audio_track (input_chan, output_chan, add_route_dialog->mode(), count);
        } else {
@@ -2924,6 +3008,12 @@ ARDOUR_UI::editor_settings () const
        } else {
                node = Config->instant_xml(X_("Editor"), get_user_ardour_path());
        }
+       
+       if (!node) {
+               if (getenv("ARDOUR_INSTANT_XML_PATH")) {
+                       node = Config->instant_xml(X_("Editor"), getenv("ARDOUR_INSTANT_XML_PATH"));
+               }
+       }
 
        if (!node) {
                node = new XMLNode (X_("Editor"));
@@ -2947,7 +3037,6 @@ ARDOUR_UI::keyboard_settings () const
 void
 ARDOUR_UI::create_xrun_marker(nframes_t where)
 {
-       ENSURE_GUI_THREAD (bind(mem_fun(*this, &ARDOUR_UI::create_xrun_marker), where));
        editor->mouse_add_new_marker (where, false, true);
 }
 
@@ -2962,20 +3051,118 @@ ARDOUR_UI::halt_on_xrun_message ()
 void
 ARDOUR_UI::xrun_handler(nframes_t where)
 {
-       if (Config->get_create_xrun_marker() && session->actively_recording()) {
+       if (!session) {
+               return;
+       }
+
+       ENSURE_GUI_THREAD (bind(mem_fun(*this, &ARDOUR_UI::xrun_handler), where));
+
+       if (session && Config->get_create_xrun_marker() && session->actively_recording()) {
                create_xrun_marker(where);
        }
 
-       if (Config->get_stop_recording_on_xrun() && session->actively_recording()) {
+       if (session && Config->get_stop_recording_on_xrun() && session->actively_recording()) {
                halt_on_xrun_message ();
        }
 }
 
+bool
+ARDOUR_UI::preset_file_exists_handler ()
+{
+       /* if driven from another thread, say "do not overwrite" and show the user nothing.
+        */
+
+       if (!Gtkmm2ext::UI::instance()->caller_is_ui_thread()) {        \
+               return false;
+       }
+       
+       HBox* hbox = new HBox();
+       Image* image = new Image (Stock::DIALOG_QUESTION, ICON_SIZE_DIALOG);
+       Gtk::Dialog dialog (_("Preset Exists"), true, false);
+       Label  message (_("\
+A preset with this name already exists for this plugin.\n\
+\n\
+What you would like to do?\n"));
+       image->set_alignment(ALIGN_CENTER, ALIGN_TOP);
+       hbox->pack_start (*image, PACK_EXPAND_WIDGET, 12);
+       hbox->pack_end (message, PACK_EXPAND_PADDING, 12);
+       dialog.get_vbox()->pack_start(*hbox, PACK_EXPAND_PADDING, 6);
+       dialog.add_button (_("Overwrite the existing preset"), RESPONSE_ACCEPT);
+       dialog.add_button (_("Leave the existing preset alone"), RESPONSE_REJECT);
+       dialog.set_default_response (RESPONSE_ACCEPT);
+       dialog.set_position (WIN_POS_MOUSE);
+       dialog.set_type_hint (Gdk::WINDOW_TYPE_HINT_UTILITY); // need to make it float above the preset name dialog
+
+       message.show();
+       image->show();
+       hbox->show();
+
+       switch (dialog.run ()) {
+       case RESPONSE_ACCEPT:
+               return true;
+       default:
+               return false;
+       }
+}
+
+void
+ARDOUR_UI::push_buffer_stats (uint32_t capture, uint32_t playback)
+{
+       time_t now;
+       time (&now);
+
+       while (disk_buffer_stats.size() > 60) {
+               disk_buffer_stats.pop_front ();
+       }
+
+       disk_buffer_stats.push_back (DiskBufferStat (now, capture, playback));
+}
+
+void
+ARDOUR_UI::write_buffer_stats ()
+{
+       std::ofstream fout;
+       struct tm tm;
+       char buf[64];
+       char path[PATH_MAX+1];  int fd;
+
+       strcpy (path, "ardourBufferingXXXXXX");
+
+       if ((fd = mkstemp (path )) < 0) {
+               cerr << X_("cannot find temporary name for ardour buffer stats") << endl;
+               return;
+       }
+       
+       fout.open (path);
+       close (fd);
+
+       if (!fout) {
+               cerr << string_compose (X_("cannot open file %1 for ardour buffer stats"), path) << endl;
+               return;
+       }
+
+       for (list<DiskBufferStat>::iterator i = disk_buffer_stats.begin(); i != disk_buffer_stats.end(); ++i) {
+               localtime_r (&(*i).when, &tm);
+               strftime (buf, sizeof (buf), "%T", &tm);
+               fout << buf << ' ' << (*i).capture << ' ' << (*i).playback << endl;
+       }
+       
+       disk_buffer_stats.clear ();
+
+       fout.close ();
+
+       cerr << "Ardour buffering statistics can be found in: " << path << endl;
+       free (path);
+}
+
 void
 ARDOUR_UI::disk_overrun_handler ()
 {
+
        ENSURE_GUI_THREAD (mem_fun(*this, &ARDOUR_UI::disk_overrun_handler));
 
+       write_buffer_stats ();
+
        if (!have_disk_speed_dialog_displayed) {
                have_disk_speed_dialog_displayed = true;
                MessageDialog* msg = new MessageDialog (*editor, _("\
@@ -2992,8 +3179,11 @@ quickly enough to keep up with recording.\n"));
 void
 ARDOUR_UI::disk_underrun_handler ()
 {
+
        ENSURE_GUI_THREAD (mem_fun(*this, &ARDOUR_UI::disk_underrun_handler));
 
+       write_buffer_stats ();
+
        if (!have_disk_speed_dialog_displayed) {
                have_disk_speed_dialog_displayed = true;
                MessageDialog* msg = new MessageDialog (*editor,
@@ -3058,6 +3248,8 @@ what you would like to do.\n"));
        image->show();
        hbox->show();
 
+       pop_back_splash ();
+
        switch (dialog.run ()) {
        case RESPONSE_ACCEPT:
                return 1;
@@ -3359,11 +3551,6 @@ ARDOUR_UI::TransportControllable::set_id (const string& str)
 void
 ARDOUR_UI::setup_profile ()
 {
-       cerr << "GDK screen dimensions: "
-            << gdk_screen_width() << " x " 
-            << gdk_screen_height()
-            << endl;
-
        if (gdk_screen_width() < 1200) {
                Profile->set_small_screen ();
        }