fix crash on OS X el capitan when using command line arguments
[ardour.git] / gtk2_ardour / ardour_ui.cc
index 31450f2b23684944a1ee4fa1914ea8939d229f4b..62043ead74852bf084c661cf182f4f533ec549fb 100644 (file)
 #include <sys/resource.h>
 #endif
 
+#ifdef __FreeBSD__
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#endif
+
 #include <stdint.h>
 #include <fcntl.h>
 #include <signal.h>
 
 #include <gtkmm/messagedialog.h>
 #include <gtkmm/accelmap.h>
+#include <gtkmm/stock.h>
 
 #include "pbd/error.h"
 #include "pbd/basename.h"
 #include "pbd/compose.h"
 #include "pbd/convert.h"
 #include "pbd/failed_constructor.h"
+#include "pbd/file_archive.h"
 #include "pbd/enumwriter.h"
 #include "pbd/memento_command.h"
 #include "pbd/openuri.h"
@@ -85,6 +92,7 @@
 #include "ardour/process_thread.h"
 #include "ardour/profile.h"
 #include "ardour/recent_sessions.h"
+#include "ardour/record_enable_control.h"
 #include "ardour/session_directory.h"
 #include "ardour/session_route.h"
 #include "ardour/session_state_utils.h"
 #include "ardour/source_factory.h"
 #include "ardour/slave.h"
 #include "ardour/system_exec.h"
+#include "ardour/track.h"
+#include "ardour/vca_manager.h"
+#include "ardour/utils.h"
 
 #include "LuaBridge/LuaBridge.h"
 
@@ -152,6 +163,7 @@ typedef uint64_t microseconds_t;
 #include "route_params_ui.h"
 #include "save_as_dialog.h"
 #include "script_selector.h"
+#include "session_archive_dialog.h"
 #include "session_dialog.h"
 #include "session_metadata_dialog.h"
 #include "session_option_editor.h"
@@ -167,7 +179,7 @@ typedef uint64_t microseconds_t;
 #include "add_video_dialog.h"
 #include "transcode_video_dialog.h"
 
-#include "i18n.h"
+#include "pbd/i18n.h"
 
 using namespace ARDOUR;
 using namespace ARDOUR_UI_UTILS;
@@ -286,7 +298,7 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir)
        , speaker_config_window (X_("speaker-config"), _("Speaker Configuration"))
        , add_route_dialog (X_("add-routes"), _("Add Tracks/Busses"))
        , about (X_("about"), _("About"))
-       , location_ui (X_("locations"), _("Locations"))
+       , location_ui (X_("locations"), S_("Ranges|Locations"))
        , route_params (X_("inspector"), _("Tracks and Busses"))
        , audio_midi_setup (X_("audio-midi-setup"), _("Audio/MIDI Setup"))
        , export_video_dialog (X_("video-export"), _("Video Export Dialog"))
@@ -323,6 +335,14 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir)
                _exit (0);
        }
 
+
+       if (string (VERSIONSTRING).find (".pre") != string::npos) {
+               /* check this is not being run from ./ardev etc. */
+               if (!running_from_source_tree ()) {
+                       pre_release_dialog ();
+               }
+       }
+
        if (theArdourUI == 0) {
                theArdourUI = this;
        }
@@ -470,6 +490,9 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir)
        WM::Manager::instance().register_window (&audio_port_matrix);
        WM::Manager::instance().register_window (&midi_port_matrix);
 
+       /* do not retain position for add route dialog */
+       add_route_dialog.set_state_mask (WindowProxy::Size);
+
        /* Trigger setting up the color scheme and loading the GTK RC file */
 
        UIConfiguration::instance().load_rc_file (false);
@@ -482,6 +505,40 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir)
        attach_to_engine ();
 }
 
+void
+ARDOUR_UI::pre_release_dialog ()
+{
+       ArdourDialog d (_("Pre-Release Warning"), true, false);
+       d.add_button (Gtk::Stock::OK, Gtk::RESPONSE_OK);
+
+        Label* label = manage (new Label);
+        label->set_markup (string_compose (_("<b>Welcome to this pre-release build of %1 %2</b>\n\n\
+There are still several issues and bugs to be worked on,\n\
+as well as general workflow improvements, before this can be considered\n\
+release software. So, a few guidelines:\n\
+\n\
+1) Please do <b>NOT</b> use this software with the expectation that it is stable or reliable\n\
+   though it may be so, depending on your workflow.\n\
+2) Please wait for a helpful writeup of new features.\n\
+3) <b>Please do NOT use the forums at ardour.org to report issues</b>.\n\
+4) Please <b>DO</b> use the bugtracker at http://tracker.ardour.org/ to report issues\n\
+   making sure to note the product version number as 5.0-pre.\n\
+5) Please <b>DO</b> use the ardour-users mailing list to discuss ideas and pass on comments.\n\
+6) Please <b>DO</b> join us on IRC for real time discussions about %1 %2. You\n\
+   can get there directly from within the program via the Help->Chat menu option.\n\
+\n\
+Full information on all the above can be found on the support page at\n\
+\n\
+                http://ardour.org/support\n\
+"), PROGRAM_NAME, VERSIONSTRING));
+
+        d.get_vbox()->set_border_width (12);
+        d.get_vbox()->pack_start (*label, false, false, 12);
+        d.get_vbox()->show_all ();
+
+        d.run ();
+}
+
 GlobalPortMatrixWindow*
 ARDOUR_UI::create_global_port_matrix (ARDOUR::DataType type)
 {
@@ -1014,6 +1071,45 @@ ARDOUR_UI::starting ()
                        }
                }
 
+               // TODO: maybe IFF brand_new_user
+               if (ARDOUR::Profile->get_mixbus () && Config->get_copy_demo_sessions ()) {
+                       std::string dspd (Config->get_default_session_parent_dir());
+                       Searchpath ds (ARDOUR::ardour_data_search_path());
+                       ds.add_subdirectory_to_paths ("sessions");
+                       vector<string> demos;
+                       find_files_matching_pattern (demos, ds, "*.tar.xz");
+
+                       ARDOUR::RecentSessions rs;
+                       ARDOUR::read_recent_sessions (rs);
+
+                       for (vector<string>::iterator i = demos.begin(); i != demos.end (); ++i) {
+                               /* "demo-session" must be inside "demo-session.tar.xz"
+                                * strip ".tar.xz"
+                                */
+                               std::string name = basename_nosuffix (basename_nosuffix (*i));
+                               std::string path = Glib::build_filename (dspd, name);
+                               /* skip if session-dir already exists */
+                               if (Glib::file_test(path.c_str(), Glib::FILE_TEST_IS_DIR)) {
+                                       continue;
+                               }
+                               /* skip sessions that are already in 'recent'.
+                                * eg. a new user changed <session-default-dir> shorly after installation
+                                */
+                               for (ARDOUR::RecentSessions::iterator r = rs.begin(); r != rs.end(); ++r) {
+                                       if ((*r).first == name) {
+                                               continue;
+                                       }
+                               }
+                               try {
+                                       PBD::FileArchive ar (*i);
+                                       if (0 == ar.inflate (dspd)) {
+                                               store_recent_sessions (name, path);
+                                               info << string_compose (_("Copied Demo Session %1."), name) << endmsg;
+                                       }
+                               } catch (...) {}
+                       }
+               }
+
 #ifdef NO_PLUGIN_STATE
 
                ARDOUR::RecentSessions rs;
@@ -1229,6 +1325,10 @@ If you still wish to quit, please use the\n\n\
        */
        save_ardour_state ();
 
+       if (key_editor.get (false)) {
+               key_editor->disconnect ();
+       }
+
        close_all_dialogs ();
 
        if (_session) {
@@ -1569,7 +1669,7 @@ void
 ARDOUR_UI::count_recenabled_streams (Route& route)
 {
        Track* track = dynamic_cast<Track*>(&route);
-       if (track && track->record_enabled()) {
+       if (track && track->rec_enable_control()->get_value()) {
                rec_enabled_streams += track->n_inputs().n_total();
        }
 }
@@ -1796,6 +1896,16 @@ ARDOUR_UI::open_session ()
        }
 }
 
+void
+ARDOUR_UI::session_add_vca (const string& name_template, uint32_t n)
+{
+       if (!_session) {
+               return;
+       }
+
+       _session->vca_manager().create_vca (n, name_template);
+}
+
 void
 ARDOUR_UI::session_add_mixed_track (
                const ChanCount& input,
@@ -1805,7 +1915,8 @@ ARDOUR_UI::session_add_mixed_track (
                const string& name_template,
                bool strict_io,
                PluginInfoPtr instrument,
-               Plugin::PresetRecord* pset)
+               Plugin::PresetRecord* pset,
+               ARDOUR::PresentationInfo::order_t order)
 {
        list<boost::shared_ptr<MidiTrack> > tracks;
 
@@ -1815,7 +1926,7 @@ ARDOUR_UI::session_add_mixed_track (
        }
 
        try {
-               tracks = _session->new_midi_track (input, output, instrument, ARDOUR::Normal, route_group, how_many, name_template, pset);
+               tracks = _session->new_midi_track (input, output, instrument, pset, route_group, how_many, name_template, order, ARDOUR::Normal);
 
                if (tracks.size() != how_many) {
                        error << string_compose(P_("could not create %1 new mixed track", "could not create %1 new mixed tracks", how_many), how_many) << endmsg;
@@ -1841,7 +1952,8 @@ ARDOUR_UI::session_add_midi_bus (
                const string& name_template,
                bool strict_io,
                PluginInfoPtr instrument,
-               Plugin::PresetRecord* pset)
+               Plugin::PresetRecord* pset,
+               ARDOUR::PresentationInfo::order_t order)
 {
        RouteList routes;
 
@@ -1851,7 +1963,8 @@ ARDOUR_UI::session_add_midi_bus (
        }
 
        try {
-               routes = _session->new_midi_route (route_group, how_many, name_template, instrument, pset);
+
+               routes = _session->new_midi_route (route_group, how_many, name_template, instrument, pset, PresentationInfo::MidiBus, order);
                if (routes.size() != how_many) {
                        error << string_compose(P_("could not create %1 new Midi Bus", "could not create %1 new Midi Busses", how_many), how_many) << endmsg;
                }
@@ -1877,15 +1990,16 @@ ARDOUR_UI::session_add_midi_route (
                const string& name_template,
                bool strict_io,
                PluginInfoPtr instrument,
-               Plugin::PresetRecord* pset)
+               Plugin::PresetRecord* pset,
+               ARDOUR::PresentationInfo::order_t order)
 {
        ChanCount one_midi_channel;
        one_midi_channel.set (DataType::MIDI, 1);
 
        if (disk) {
-               session_add_mixed_track (one_midi_channel, one_midi_channel, route_group, how_many, name_template, strict_io, instrument, pset);
+               session_add_mixed_track (one_midi_channel, one_midi_channel, route_group, how_many, name_template, strict_io, instrument, pset, order);
        } else {
-               session_add_midi_bus (route_group, how_many, name_template, strict_io, instrument, pset);
+               session_add_midi_bus (route_group, how_many, name_template, strict_io, instrument, pset, order);
        }
 }
 
@@ -1898,8 +2012,8 @@ ARDOUR_UI::session_add_audio_route (
        RouteGroup* route_group,
        uint32_t how_many,
        string const & name_template,
-       bool strict_io
-       )
+       bool strict_io,
+       ARDOUR::PresentationInfo::order_t order)
 {
        list<boost::shared_ptr<AudioTrack> > tracks;
        RouteList routes;
@@ -1911,7 +2025,7 @@ ARDOUR_UI::session_add_audio_route (
 
        try {
                if (track) {
-                       tracks = _session->new_audio_track (input_channels, output_channels, mode, route_group, how_many, name_template);
+                       tracks = _session->new_audio_track (input_channels, output_channels, route_group, how_many, name_template, order, mode);
 
                        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)
@@ -1920,7 +2034,7 @@ ARDOUR_UI::session_add_audio_route (
 
                } else {
 
-                       routes = _session->new_audio_route (input_channels, output_channels, route_group, how_many, name_template);
+                       routes = _session->new_audio_route (input_channels, output_channels, route_group, how_many, name_template, PresentationInfo::AudioBus, order);
 
                        if (routes.size() != how_many) {
                                error << string_compose (P_("could not create %1 new audio bus", "could not create %1 new audio busses", how_many), how_many)
@@ -2072,14 +2186,14 @@ ARDOUR_UI::trx_record_enable_all_tracks ()
                boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (*r);
                assert (t);
 
-               if (t->record_enabled()) {
+               if (t->rec_enable_control()->get_value()) {
                        none_record_enabled = false;
                        break;
                }
        }
 
        if (none_record_enabled) {
-               _session->set_record_enabled (rl, true, Session::rt_cleanup);
+               _session->set_controls (route_list_to_control_list (rl, &Stripable::rec_enable_control), 1.0, Controllable::NoGroup);
        }
 
        return none_record_enabled;
@@ -2237,7 +2351,12 @@ ARDOUR_UI::toggle_roll (bool with_abort, bool roll_out_of_bounded_mode)
        if (affect_transport) {
                if (rolling) {
                        _session->request_stop (with_abort, true);
-               } else {
+
+               } else if (!with_abort) { /* with_abort == true means the
+                                          * command was intended to stop
+                                          * transport, not start.
+                                          */
+
                        /* the only external sync condition we can be in here
                         * would be Engine (JACK) sync, in which case we still
                         * want to do this.
@@ -2364,7 +2483,7 @@ ARDOUR_UI::transport_forward (int option)
 }
 
 void
-ARDOUR_UI::toggle_record_enable (uint32_t rid)
+ARDOUR_UI::toggle_record_enable (uint16_t rid)
 {
        if (!_session) {
                return;
@@ -2372,12 +2491,12 @@ ARDOUR_UI::toggle_record_enable (uint32_t rid)
 
        boost::shared_ptr<Route> r;
 
-       if ((r = _session->route_by_remote_id (rid)) != 0) {
+       if ((r = _session->get_remote_nth_route (rid)) != 0) {
 
                boost::shared_ptr<Track> t;
 
                if ((t = boost::dynamic_pointer_cast<Track>(r)) != 0) {
-                       t->set_record_enabled (!t->record_enabled(), Controllable::UseGroup);
+                       t->rec_enable_control()->set_value (!t->rec_enable_control()->get_value(), Controllable::UseGroup);
                }
        }
 }
@@ -2580,6 +2699,32 @@ ARDOUR_UI::save_session_as ()
        }
 }
 
+void
+ARDOUR_UI::archive_session ()
+{
+       if (!_session) {
+               return;
+       }
+
+       time_t n;
+       time (&n);
+       Glib::DateTime gdt (Glib::DateTime::create_now_local (n));
+
+       SessionArchiveDialog sad;
+       sad.set_name (_session->name() + gdt.format ("_%F_%H%M%S"));
+       int response = sad.run ();
+
+       if (response != Gtk::RESPONSE_OK) {
+               sad.hide ();
+               return;
+       }
+
+       if (_session->archive_session (sad.target_folder(), sad.name(), sad.encode_option (), sad.only_used_sources (), &sad)) {
+               MessageDialog msg (_("Session Archiving failed."));
+               msg.run ();
+       }
+}
+
 void
 ARDOUR_UI::quick_snapshot_session (bool switch_to_it)
 {
@@ -2657,14 +2802,8 @@ ARDOUR_UI::snapshot_session (bool switch_to_it)
        if (switch_to_it) {
                prompter.set_initial_text (_session->snap_name());
        } else {
-               char timebuf[128];
-               time_t n;
-               struct tm local_time;
-
-               time (&n);
-               localtime_r (&n, &local_time);
-               strftime (timebuf, sizeof(timebuf), "%FT%H.%M.%S", &local_time);
-               prompter.set_initial_text (timebuf);
+               Glib::DateTime tm (g_date_time_new_now_local ());
+               prompter.set_initial_text (tm.format ("%FT%H.%M.%S"));
        }
 
        bool finished = false;
@@ -2986,7 +3125,19 @@ ARDOUR_UI::build_session_from_dialog (SessionDialog& sd, const std::string& sess
 void
 ARDOUR_UI::load_from_application_api (const std::string& path)
 {
+       /* OS X El Capitan (and probably later) now somehow passes the command
+          line arguments to an app via the openFile delegate protocol. Ardour 
+          already does its own command line processing, and having both
+          pathways active causes crashes. So, if the command line was already
+          set, do nothing here.
+       */
+
+       if (!ARDOUR_COMMAND_LINE::session_name.empty()) {
+               return;
+       }
+
        ARDOUR_COMMAND_LINE::session_name = path;
+
        /* Cancel SessionDialog if it's visible to make OSX delegates work.
         *
         * ARDOUR_UI::starting connects app->ShouldLoad signal and then shows a SessionDialog
@@ -3435,7 +3586,7 @@ ARDOUR_UI::load_session (const std::string& path, const std::string& snap_name,
 
        {
                Timers::TimerSuspender t;
-               flush_pending ();
+               flush_pending (10);
        }
 
 #ifdef WINDOWS_VST_SUPPORT
@@ -3469,7 +3620,9 @@ ARDOUR_UI::build_session (const std::string& path, const std::string& snap_name,
        }
 
        catch (SessionException e) {
+               cerr << "Here are the errors associated with this failed session:\n";
                dump_errors (cerr);
+               cerr << "---------\n";
                MessageDialog msg (string_compose(_("Could not create session in \"%1\": %2"), path, e.what()));
                msg.set_title (_("Loading Error"));
                msg.set_position (Gtk::WIN_POS_CENTER);
@@ -3478,7 +3631,9 @@ ARDOUR_UI::build_session (const std::string& path, const std::string& snap_name,
                return -1;
        }
        catch (...) {
+               cerr << "Here are the errors associated with this failed session:\n";
                dump_errors (cerr);
+               cerr << "---------\n";
                MessageDialog msg (string_compose(_("Could not create session in \"%1\""), path));
                msg.set_title (_("Loading Error"));
                msg.set_position (Gtk::WIN_POS_CENTER);
@@ -3500,6 +3655,11 @@ ARDOUR_UI::build_session (const std::string& path, const std::string& snap_name,
                new_session->add_instant_xml (*n, false);
        }
 
+       n = Config->instant_xml (X_("Preferences"));
+       if (n) {
+               new_session->add_instant_xml (*n, false);
+       }
+
        /* Put the playhead at 0 and scroll fully left */
        n = new_session->instant_xml (X_("Editor"));
        if (n) {
@@ -3889,59 +4049,38 @@ ARDOUR_UI::cleanup_peakfiles ()
        }
 }
 
-void
-ARDOUR_UI::setup_order_hint (AddRouteDialog::InsertAt place)
+PresentationInfo::order_t
+ARDOUR_UI::translate_order (RouteDialogs::InsertAt place)
 {
-       uint32_t order_hint = UINT32_MAX;
-
        if (editor->get_selection().tracks.empty()) {
-               return;
+               return PresentationInfo::max_order;
        }
 
+       PresentationInfo::order_t order_hint = PresentationInfo::max_order;
+
        /*
          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::AfterSelection) {
+       if (place == RouteDialogs::AfterSelection) {
                RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView*> (editor->get_selection().tracks.back());
                if (rtav) {
-                       order_hint = rtav->route()->order_key();
+                       order_hint = rtav->route()->presentation_info().order();
                        order_hint++;
                }
-       } else if (place == AddRouteDialog::BeforeSelection) {
+       } else if (place == RouteDialogs::BeforeSelection) {
                RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView*> (editor->get_selection().tracks.front());
                if (rtav) {
-                       order_hint = rtav->route()->order_key();
+                       order_hint = rtav->route()->presentation_info().order();
                }
-       } else if (place == AddRouteDialog::First) {
+       } else if (place == RouteDialogs::First) {
                order_hint = 0;
        } else {
-               /* leave order_hint at UINT32_MAX */
+               /* leave order_hint at max_order */
        }
 
-       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;
-       }
-
-       _session->set_order_hint (order_hint);
-
-       /* create a gap in the existing route order keys to accomodate new routes.*/
-       boost::shared_ptr <RouteList> rd = _session->get_routes();
-       for (RouteList::iterator ri = rd->begin(); ri != rd->end(); ++ri) {
-               boost::shared_ptr<Route> rt (*ri);
-
-               if (rt->is_monitor()) {
-                       continue;
-               }
-
-               if (rt->order_key () >= order_hint) {
-                       rt->set_order_key (rt->order_key () + add_route_dialog->count());
-               }
-       }
+       return order_hint;
 }
 
 void
@@ -3961,7 +4100,9 @@ ARDOUR_UI::start_duplicate_routes ()
 void
 ARDOUR_UI::add_route ()
 {
-       int count;
+       if (!add_route_dialog.get (false)) {
+               add_route_dialog->signal_response().connect (sigc::mem_fun (*this, &ARDOUR_UI::add_route_dialog_finished));
+       }
 
        if (!_session) {
                return;
@@ -3972,7 +4113,14 @@ ARDOUR_UI::add_route ()
                return;
        }
 
-       ResponseType r = (ResponseType) add_route_dialog->run ();
+       add_route_dialog->set_position (WIN_POS_MOUSE);
+       add_route_dialog->present();
+}
+
+void
+ARDOUR_UI::add_route_dialog_finished (int r)
+{
+       int count;
 
        add_route_dialog->hide();
 
@@ -3988,15 +4136,15 @@ ARDOUR_UI::add_route ()
                return;
        }
 
-       setup_order_hint(add_route_dialog->insert_at());
+       PresentationInfo::order_t order = translate_order (add_route_dialog->insert_at());
        string template_path = add_route_dialog->track_template();
        DisplaySuspender ds;
 
        if (!template_path.empty()) {
                if (add_route_dialog->name_template_is_default())  {
-                       _session->new_route_from_template (count, template_path, string());
+                       _session->new_route_from_template (count, order, template_path, string());
                } else {
-                       _session->new_route_from_template (count, template_path, add_route_dialog->name_template());
+                       _session->new_route_from_template (count, order, template_path, add_route_dialog->name_template());
                }
                return;
        }
@@ -4020,19 +4168,22 @@ ARDOUR_UI::add_route ()
 
        switch (add_route_dialog->type_wanted()) {
        case AddRouteDialog::AudioTrack:
-               session_add_audio_track (input_chan.n_audio(), output_chan.n_audio(), add_route_dialog->mode(), route_group, count, name_template, strict_io);
+               session_add_audio_track (input_chan.n_audio(), output_chan.n_audio(), add_route_dialog->mode(), route_group, count, name_template, strict_io, order);
                break;
        case AddRouteDialog::MidiTrack:
-               session_add_midi_track (route_group, count, name_template, strict_io, instrument);
+               session_add_midi_track (route_group, count, name_template, strict_io, instrument, 0, order);
                break;
        case AddRouteDialog::MixedTrack:
-               session_add_mixed_track (input_chan, output_chan, route_group, count, name_template, strict_io, instrument, 0);
+               session_add_mixed_track (input_chan, output_chan, route_group, count, name_template, strict_io, instrument, 0, order);
                break;
        case AddRouteDialog::AudioBus:
-               session_add_audio_bus (input_chan.n_audio(), output_chan.n_audio(), route_group, count, name_template, strict_io);
+               session_add_audio_bus (input_chan.n_audio(), output_chan.n_audio(), route_group, count, name_template, strict_io, order);
                break;
        case AddRouteDialog::MidiBus:
-               session_add_midi_bus (route_group, count, name_template, strict_io, instrument, 0);
+               session_add_midi_bus (route_group, count, name_template, strict_io, instrument, 0, order);
+               break;
+       case AddRouteDialog::VCAMaster:
+               session_add_vca (name_template, count);
                break;
        }
 }
@@ -4479,6 +4630,24 @@ ARDOUR_UI::export_video (bool range)
        export_video_dialog->hide ();
 }
 
+XMLNode*
+ARDOUR_UI::preferences_settings () const
+{
+       XMLNode* node = 0;
+
+       if (_session) {
+               node = _session->instant_xml(X_("Preferences"));
+       } else {
+               node = Config->instant_xml(X_("Preferences"));
+       }
+
+       if (!node) {
+               node = new XMLNode (X_("Preferences"));
+       }
+
+       return node;
+}
+
 XMLNode*
 ARDOUR_UI::mixer_settings () const
 {
@@ -4898,16 +5067,21 @@ ARDOUR_UI::record_state_changed ()
 {
        ENSURE_GUI_THREAD (*this, &ARDOUR_UI::record_state_changed);
 
-       if (!_session || !big_clock_window) {
+       if (!_session) {
                /* why bother - the clock isn't visible */
                return;
        }
 
-       if (_session->record_status () == Session::Recording && _session->have_rec_enabled_track ()) {
-               big_clock->set_active (true);
-       } else {
-               big_clock->set_active (false);
+       ActionManager::set_sensitive (ActionManager::rec_sensitive_actions, !_session->actively_recording());
+
+       if (big_clock_window) {
+               if (_session->record_status () == Session::Recording && _session->have_rec_enabled_track ()) {
+                       big_clock->set_active (true);
+               } else {
+                       big_clock->set_active (false);
+               }
        }
+
 }
 
 bool
@@ -5120,7 +5294,7 @@ ARDOUR_UI::session_format_mismatch (std::string xml_path, std::string backup_pat
 
        MessageDialog msg (string_compose (_("%4This is a session from an older version of %3%5\n\n"
                                             "%3 has copied the old session file\n\n%6%1%7\n\nto\n\n%6%2%7\n\n"
-                                            "From now on, use the -2000 version with older versions of %3"),
+                                            "From now on, use the backup copy with older versions of %3"),
                                           xml_path, backup_path, PROGRAM_NAME,
                                           start_big, end_big,
                                           start_mono, end_mono), true);
@@ -5309,16 +5483,16 @@ ARDOUR_UI::setup_toplevel_window (Gtk::Window& window, const string& name, void*
 
        if (window_icons.empty()) {
                Glib::RefPtr<Gdk::Pixbuf> icon;
-               if ((icon = ::get_icon (PROGRAM_NAME "-icon_16px")) != 0) {
+               if ((icon = ::get_icon (PROGRAM_NAME "-icon_16px"))) {
                        window_icons.push_back (icon);
                }
-               if ((icon = ::get_icon (PROGRAM_NAME "-icon_22px")) != 0) {
+               if ((icon = ::get_icon (PROGRAM_NAME "-icon_22px"))) {
                        window_icons.push_back (icon);
                }
-               if ((icon = ::get_icon (PROGRAM_NAME "-icon_32px")) != 0) {
+               if ((icon = ::get_icon (PROGRAM_NAME "-icon_32px"))) {
                        window_icons.push_back (icon);
                }
-               if ((icon = ::get_icon (PROGRAM_NAME "-icon_48px")) != 0) {
+               if ((icon = ::get_icon (PROGRAM_NAME "-icon_48px"))) {
                        window_icons.push_back (icon);
                }
        }
@@ -5448,7 +5622,7 @@ ARDOUR_UI::key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey
         DEBUG_TRACE (DEBUG::Accelerators, string_compose ("Win = %1 [title = %9] focus = %7 (%8) Key event: code = %2  state = %3 special handling ? %4 magic widget focus ? %5 focus widget %6 named %7 mods ? %8\n",
                                                           win,
                                                           ev->keyval,
-                                                         show_gdk_event_state (ev->state),
+                                                         Gtkmm2ext::show_gdk_event_state (ev->state),
                                                           special_handling_of_unmodified_accelerators,
                                                           Keyboard::some_magic_widget_has_focus(),
                                                          focus,
@@ -5573,12 +5747,58 @@ void
 ARDOUR_UI::cancel_solo ()
 {
        if (_session) {
-               if (_session->soloing()) {
-                       _session->set_solo (_session->get_routes(), false);
-               } else if (_session->listening()) {
-                       _session->set_listen (_session->get_routes(), false);
+               _session->cancel_all_solo ();
+       }
+}
+
+void
+ARDOUR_UI::reset_focus (Gtk::Widget* w)
+{
+       /* this resets focus to the first focusable parent of the given widget,
+        * or, if there is no focusable parent, cancels focus in the toplevel
+        * window that the given widget is packed into (if there is one).
+        */
+
+       if (!w) {
+               return;
+       }
+
+       Gtk::Widget* top = w->get_toplevel();
+
+       if (!top || !top->is_toplevel()) {
+               return;
+       }
+
+       w = w->get_parent ();
+
+       while (w) {
+
+               if (w->is_toplevel()) {
+                       /* Setting the focus widget to a Gtk::Window causes all
+                        * subsequent calls to ::has_focus() on the nominal
+                        * focus widget in that window to return
+                        * false. Workaround: never set focus to the toplevel
+                        * itself.
+                        */
+                       break;
                }
 
-               _session->clear_all_solo_state (_session->get_routes()); // safeguard, ideally this won't do anything, check the log-window
+               if (w->get_can_focus ()) {
+                       Gtk::Window* win = dynamic_cast<Gtk::Window*> (top);
+                       win->set_focus (*w);
+                       return;
+               }
+               w = w->get_parent ();
+       }
+
+       if (top == &_main_window) {
+
        }
+
+       /* no focusable parent found, cancel focus in top level window.
+          C++ API cannot be used for this. Thanks, references.
+       */
+
+       gtk_window_set_focus (GTK_WINDOW(top->gobj()), 0);
+
 }