First-time startup logic update.
[ardour.git] / gtk2_ardour / ardour_ui.cc
index d47723260e27763f010e47393199d0b09609e716..e507370513e6d7f08c4c3bcbd74c508121a21e3e 100644 (file)
@@ -56,6 +56,7 @@
 #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"
@@ -64,6 +65,7 @@
 #include "pbd/localtime_r.h"
 #include "pbd/pthread_utils.h"
 #include "pbd/replace_all.h"
+#include "pbd/scoped_file_descriptor.h"
 #include "pbd/xml++.h"
 
 #include "gtkmm2ext/application.h"
@@ -162,17 +164,18 @@ 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"
-#include "shuttle_control.h"
 #include "speaker_dialog.h"
 #include "splash.h"
 #include "startup.h"
-#include "theme_manager.h"
 #include "time_axis_view_item.h"
+#include "time_info_box.h"
 #include "timers.h"
 #include "utils.h"
+#include "utils_videotl.h"
 #include "video_server_dialog.h"
 #include "add_video_dialog.h"
 #include "transcode_video_dialog.h"
@@ -270,16 +273,11 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir)
        , _mixer_on_top (false)
        , _initial_verbose_plugin_scan (false)
        , first_time_engine_run (true)
-       , 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))
-       , goto_end_controllable (new TransportControllable ("transport goto end", *this, TransportControllable::GotoEnd))
-       , 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))
+       , secondary_clock_spacer (0)
+       , auto_input_button (ArdourButton::led_default_elements)
+       , time_info_box (0)
        , auto_return_button (ArdourButton::led_default_elements)
        , follow_edits_button (ArdourButton::led_default_elements)
-       , auto_input_button (ArdourButton::led_default_elements)
        , auditioning_alert_button (_("Audition"))
        , solo_alert_button (_("Solo"))
        , feedback_alert_button (_("Feedback"))
@@ -307,7 +305,7 @@ 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))
-       , key_editor (X_("key-editor"), _("Bindings Editor"), boost::bind (&ARDOUR_UI::create_key_editor, this))
+       , key_editor (X_("key-editor"), _("Keyboard Shortcuts"), boost::bind (&ARDOUR_UI::create_key_editor, this))
        , video_server_process (0)
        , splash (0)
        , have_configure_timeout (false)
@@ -327,6 +325,10 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir)
        UIConfiguration::instance().post_gui_init ();
 
        if (ARDOUR::handle_old_configuration_files (boost::bind (ask_about_configuration_copy, _1, _2, _3))) {
+               {
+                       /* "touch" the been-here-before path now that config has been migrated */
+                       PBD::ScopedFileDescriptor fout (g_open (been_here_before_path ().c_str(), O_CREAT|O_TRUNC|O_RDWR, 0666));
+               }
                MessageDialog msg (string_compose (_("Your configuration files were copied. You can now restart %1."), PROGRAM_NAME), true);
                msg.run ();
                /* configuration was modified, exit immediately */
@@ -358,13 +360,22 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir)
        boost::function<void (string)> pc (boost::bind (&ARDOUR_UI::parameter_changed, this, _1));
        UIConfiguration::instance().map_parameters (pc);
 
-       roll_button.set_controllable (roll_controllable);
-       stop_button.set_controllable (stop_controllable);
-       goto_start_button.set_controllable (goto_start_controllable);
-       goto_end_button.set_controllable (goto_end_controllable);
-       auto_loop_button.set_controllable (auto_loop_controllable);
-       play_selection_button.set_controllable (play_selection_controllable);
-       rec_button.set_controllable (rec_controllable);
+       Glib::RefPtr<Gtk::Action> act;
+
+       act = ActionManager::get_action ("Transport/Roll");
+       roll_button.set_related_action (act);
+       act = ActionManager::get_action ("Transport/Stop");
+       stop_button.set_related_action (act);
+       act = ActionManager::get_action ("Transport/GotoStart");
+       goto_start_button.set_related_action (act);
+       act = ActionManager::get_action ("Transport/GotoEnd");
+       goto_end_button.set_related_action (act);
+       act = ActionManager::get_action ("Transport/Loop");
+       auto_loop_button.set_related_action (act);
+       act = ActionManager::get_action ("Transport/PlaySelection");
+       play_selection_button.set_related_action (act);
+       act = ActionManager::get_action ("Transport/Record");
+       rec_button.set_related_action (act);
 
        roll_button.set_name ("transport button");
        stop_button.set_name ("transport button");
@@ -737,10 +748,12 @@ ARDOUR_UI::~ARDOUR_UI ()
                delete primary_clock; primary_clock = 0;
                delete secondary_clock; secondary_clock = 0;
                delete _process_thread; _process_thread = 0;
+               delete time_info_box; time_info_box = 0;
                delete meterbridge; meterbridge = 0;
                delete luawindow; luawindow = 0;
                delete editor; editor = 0;
                delete mixer; mixer = 0;
+               delete rc_option_editor; rc_option_editor = 0; // failed to wrap object warning
                delete nsm; nsm = 0;
                delete gui_object_state; gui_object_state = 0;
                delete main_window_visibility;
@@ -799,63 +812,6 @@ ARDOUR_UI::configure_handler (GdkEventConfigure* /*conf*/)
        return FALSE;
 }
 
-void
-ARDOUR_UI::set_transport_controllable_state (const XMLNode& node)
-{
-       XMLProperty const * prop;
-
-       if ((prop = node.property ("roll")) != 0) {
-               roll_controllable->set_id (prop->value());
-       }
-       if ((prop = node.property ("stop")) != 0) {
-               stop_controllable->set_id (prop->value());
-       }
-       if ((prop = node.property ("goto-start")) != 0) {
-               goto_start_controllable->set_id (prop->value());
-       }
-       if ((prop = node.property ("goto-end")) != 0) {
-               goto_end_controllable->set_id (prop->value());
-       }
-       if ((prop = node.property ("auto-loop")) != 0) {
-               auto_loop_controllable->set_id (prop->value());
-       }
-       if ((prop = node.property ("play-selection")) != 0) {
-               play_selection_controllable->set_id (prop->value());
-       }
-       if ((prop = node.property ("rec")) != 0) {
-               rec_controllable->set_id (prop->value());
-       }
-       if ((prop = node.property ("shuttle")) != 0) {
-               shuttle_box->controllable()->set_id (prop->value());
-       }
-}
-
-XMLNode&
-ARDOUR_UI::get_transport_controllable_state ()
-{
-       XMLNode* node = new XMLNode(X_("TransportControllables"));
-       char buf[64];
-
-       roll_controllable->id().print (buf, sizeof (buf));
-       node->add_property (X_("roll"), buf);
-       stop_controllable->id().print (buf, sizeof (buf));
-       node->add_property (X_("stop"), buf);
-       goto_start_controllable->id().print (buf, sizeof (buf));
-       node->add_property (X_("goto_start"), buf);
-       goto_end_controllable->id().print (buf, sizeof (buf));
-       node->add_property (X_("goto_end"), buf);
-       auto_loop_controllable->id().print (buf, sizeof (buf));
-       node->add_property (X_("auto_loop"), buf);
-       play_selection_controllable->id().print (buf, sizeof (buf));
-       node->add_property (X_("play_selection"), buf);
-       rec_controllable->id().print (buf, sizeof (buf));
-       node->add_property (X_("rec"), buf);
-       shuttle_box->controllable()->id().print (buf, sizeof (buf));
-       node->add_property (X_("shuttle"), buf);
-
-       return *node;
-}
-
 void
 ARDOUR_UI::save_session_at_its_request (std::string snapshot_name)
 {
@@ -1069,6 +1025,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;
@@ -1284,7 +1279,9 @@ If you still wish to quit, please use the\n\n\
        */
        save_ardour_state ();
 
-       key_editor->disconnect ();
+       if (key_editor.get (false)) {
+               key_editor->disconnect ();
+       }
 
        close_all_dialogs ();
 
@@ -1407,7 +1404,7 @@ ARDOUR_UI::every_point_zero_something_seconds ()
 {
        // august 2007: actual update frequency: 25Hz (40ms), not 100Hz
 
-       if (editor_meter && UIConfiguration::instance().get_show_editor_meter()) {
+       if (editor_meter && UIConfiguration::instance().get_show_editor_meter() && editor_meter_peak_display.is_mapped ()) {
                float mpeak = editor_meter->update_meters();
                if (mpeak > editor_meter_max_peak) {
                        if (mpeak >= UIConfiguration::instance().get_meter_peak()) {
@@ -1832,6 +1829,13 @@ ARDOUR_UI::open_session ()
        session_filter.add_pattern (string_compose(X_("*%1"), ARDOUR::statefile_suffix));
        session_filter.set_name (string_compose (_("%1 sessions"), PROGRAM_NAME));
        open_session_selector.add_filter (session_filter);
+
+       FileFilter archive_filter;
+       archive_filter.add_pattern (X_("*.tar.xz"));
+       archive_filter.set_name (_("Session Archives"));
+
+       open_session_selector.add_filter (archive_filter);
+
        open_session_selector.set_filter (session_filter);
 
        int response = open_session_selector.run();
@@ -1846,7 +1850,18 @@ ARDOUR_UI::open_session ()
        bool isnew;
 
        if (session_path.length() > 0) {
-               if (ARDOUR::find_session (session_path, path, name, isnew) == 0) {
+               int rv = ARDOUR::inflate_session (session_path,
+                               Config->get_default_session_parent_dir(), path, name);
+               if (rv == 0) {
+                       _session_is_new = false;
+                       load_session (path, name);
+               }
+               else if (rv < 0) {
+                       MessageDialog msg (_main_window,
+                                       string_compose (_("Extracting session-archive failed: %1"), inflate_error (rv)));
+                       msg.run ();
+               }
+               else if (ARDOUR::find_session (session_path, path, name, isnew) == 0) {
                        _session_is_new = isnew;
                        load_session (path, name);
                }
@@ -1875,15 +1890,18 @@ ARDOUR_UI::session_add_mixed_track (
                Plugin::PresetRecord* pset,
                ARDOUR::PresentationInfo::order_t order)
 {
-       list<boost::shared_ptr<MidiTrack> > tracks;
-
        if (_session == 0) {
                warning << _("You cannot add a track without a session already loaded.") << endmsg;
                return;
        }
 
+       if (Profile->get_mixbus ()) {
+               strict_io = true;
+       }
+
        try {
-               tracks = _session->new_midi_track (input, output, instrument, pset, route_group, how_many, name_template, order, ARDOUR::Normal);
+               list<boost::shared_ptr<MidiTrack> > tracks;
+               tracks = _session->new_midi_track (input, output, strict_io, instrument, pset, route_group, how_many, name_template, order, ARDOUR::Normal);
 
                if (tracks.size() != how_many) {
                        error << string_compose(P_("could not create %1 new mixed track", "could not create %1 new mixed tracks", how_many), how_many) << endmsg;
@@ -1894,12 +1912,6 @@ ARDOUR_UI::session_add_mixed_track (
                display_insufficient_ports_message ();
                return;
        }
-
-       if (strict_io) {
-               for (list<boost::shared_ptr<MidiTrack> >::iterator i = tracks.begin(); i != tracks.end(); ++i) {
-                       (*i)->set_strict_io (true);
-               }
-       }
 }
 
 void
@@ -1912,16 +1924,18 @@ ARDOUR_UI::session_add_midi_bus (
                Plugin::PresetRecord* pset,
                ARDOUR::PresentationInfo::order_t order)
 {
-       RouteList routes;
-
        if (_session == 0) {
                warning << _("You cannot add a track without a session already loaded.") << endmsg;
                return;
        }
 
-       try {
+       if (Profile->get_mixbus ()) {
+               strict_io = true;
+       }
 
-               routes = _session->new_midi_route (route_group, how_many, name_template, instrument, pset, PresentationInfo::MidiBus, order);
+       try {
+               RouteList routes;
+               routes = _session->new_midi_route (route_group, how_many, name_template, strict_io, instrument, pset, PresentationInfo::MidiBus, order);
                if (routes.size() != how_many) {
                        error << string_compose(P_("could not create %1 new Midi Bus", "could not create %1 new Midi Busses", how_many), how_many) << endmsg;
                }
@@ -1931,12 +1945,6 @@ ARDOUR_UI::session_add_midi_bus (
                display_insufficient_ports_message ();
                return;
        }
-
-       if (strict_io) {
-               for (RouteList::iterator i = routes.begin(); i != routes.end(); ++i) {
-                       (*i)->set_strict_io (true);
-               }
-       }
 }
 
 void
@@ -2385,6 +2393,24 @@ ARDOUR_UI::transport_play_preroll ()
        editor->play_with_preroll ();
 }
 
+void
+ARDOUR_UI::transport_rec_preroll ()
+{
+       if (!_session) {
+               return;
+       }
+       editor->rec_with_preroll ();
+}
+
+void
+ARDOUR_UI::transport_rec_count_in ()
+{
+       if (!_session) {
+               return;
+       }
+       editor->rec_with_count_in ();
+}
+
 void
 ARDOUR_UI::transport_rewind (int option)
 {
@@ -2466,10 +2492,11 @@ ARDOUR_UI::map_transport_state ()
                play_selection_button.unset_active_state ();
                roll_button.unset_active_state ();
                stop_button.set_active_state (Gtkmm2ext::ExplicitActive);
+               layered_button.set_sensitive (false);
                return;
        }
 
-       shuttle_box->map_transport_state ();
+       shuttle_box.map_transport_state ();
 
        float sp = _session->transport_speed();
 
@@ -2505,11 +2532,13 @@ ARDOUR_UI::map_transport_state ()
                        roll_button.set_active (true);
                        play_selection_button.set_active (true);
                }
+               layered_button.set_sensitive (!_session->actively_recording ());
 
                stop_button.set_active (false);
 
        } else {
 
+               layered_button.set_sensitive (true);
                stop_button.set_active (true);
                roll_button.set_active (false);
                play_selection_button.set_active (false);
@@ -2650,9 +2679,44 @@ ARDOUR_UI::save_session_as ()
                msg.run ();
        }
 
-       if (!sa.include_media) {
+       /* the logic here may seem odd: why isn't the condition sa.switch_to ?
+        * the trick is this: if the new session was copy with media included,
+        * then Session::save_as() will have already done a neat trick to avoid
+        * us having to unload and load the new state. But if the media was not
+        * included, then this is required (it avoids us having to otherwise
+        * drop all references to media (sources).
+        */
+
+       if (!sa.include_media && sa.switch_to) {
                unload_session (false);
                load_session (sa.final_session_folder_name, sa.new_name);
+               hide_splash ();
+       }
+}
+
+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 ();
        }
 }
 
@@ -2733,14 +2797,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;
@@ -3062,7 +3120,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
@@ -3224,6 +3294,21 @@ ARDOUR_UI::get_session_parameters (bool quit_on_cancel, bool should_be_new, stri
                        likely_new = true;
                }
 
+               if (!likely_new) {
+                       int rv = ARDOUR::inflate_session (session_name,
+                                       Config->get_default_session_parent_dir(), session_path, session_name);
+                       if (rv < 0) {
+                               MessageDialog msg (session_dialog,
+                                       string_compose (_("Extracting session-archive failed: %1"), inflate_error (rv)));
+                               msg.run ();
+                               continue;
+                       }
+                       else if (rv == 0) {
+                               session_dialog.set_provided_session (session_name, session_path);
+                       }
+               }
+
+               // XXX check archive, inflate
                string::size_type suffix = session_name.find (statefile_suffix);
 
                if (suffix != string::npos) {
@@ -3487,16 +3572,6 @@ ARDOUR_UI::load_session (const std::string& path, const std::string& snap_name,
                msg.hide ();
        }
 
-
-       /* Now the session been created, add the transport controls */
-       new_session->add_controllable(roll_controllable);
-       new_session->add_controllable(stop_controllable);
-       new_session->add_controllable(goto_start_controllable);
-       new_session->add_controllable(goto_end_controllable);
-       new_session->add_controllable(auto_loop_controllable);
-       new_session->add_controllable(play_selection_controllable);
-       new_session->add_controllable(rec_controllable);
-
        set_session (new_session);
 
        session_loaded = true;
@@ -3545,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);
@@ -3554,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);
@@ -4063,9 +4142,9 @@ ARDOUR_UI::add_route_dialog_finished (int r)
 
        if (!template_path.empty()) {
                if (add_route_dialog->name_template_is_default())  {
-                       _session->new_route_from_template (count, PresentationInfo::max_order, template_path, string());
+                       _session->new_route_from_template (count, order, template_path, string());
                } else {
-                       _session->new_route_from_template (count, PresentationInfo::max_order, template_path, add_route_dialog->name_template());
+                       _session->new_route_from_template (count, order, template_path, add_route_dialog->name_template());
                }
                return;
        }
@@ -4087,6 +4166,8 @@ ARDOUR_UI::add_route_dialog_finished (int r)
 
        /* XXX do something with name template */
 
+       Session::ProcessorChangeBlocker pcb (_session);
+
        switch (add_route_dialog->type_wanted()) {
        case AddRouteDialog::AudioTrack:
                session_add_audio_track (input_chan.n_audio(), output_chan.n_audio(), add_route_dialog->mode(), route_group, count, name_template, strict_io, order);
@@ -4109,89 +4190,6 @@ ARDOUR_UI::add_route_dialog_finished (int r)
        }
 }
 
-void
-ARDOUR_UI::add_lua_script ()
-{
-       if (!_session) {
-               return;
-       }
-
-       LuaScriptInfoPtr spi;
-       ScriptSelector ss ("Add Lua Session Script", LuaScriptInfo::Session);
-       switch (ss.run ()) {
-               case Gtk::RESPONSE_ACCEPT:
-                       spi = ss.script();
-                       break;
-               default:
-                       return;
-       }
-       ss.hide();
-
-       std::string script = "";
-
-       try {
-               script = Glib::file_get_contents (spi->path);
-       } catch (Glib::FileError e) {
-               string msg = string_compose (_("Cannot read session script '%1': %2"), spi->path, e.what());
-               MessageDialog am (msg);
-               am.run ();
-               return;
-       }
-
-       LuaScriptParamList lsp = LuaScriptParams::script_params (spi, "sess_params");
-       std::vector<std::string> reg = _session->registered_lua_functions ();
-
-       ScriptParameterDialog spd (_("Set Script Parameters"), spi, reg, lsp);
-       switch (spd.run ()) {
-               case Gtk::RESPONSE_ACCEPT:
-                       break;
-               default:
-                       return;
-       }
-
-       try {
-               _session->register_lua_function (spd.name(), script, lsp);
-       } catch (luabridge::LuaException const& e) {
-               string msg = string_compose (_("Session script '%1' instantiation failed: %2"), spd.name(), e.what ());
-               MessageDialog am (msg);
-               am.run ();
-       } catch (SessionException e) {
-               string msg = string_compose (_("Loading Session script '%1' failed: %2"), spd.name(), e.what ());
-               MessageDialog am (msg);
-               am.run ();
-       }
-}
-
-void
-ARDOUR_UI::remove_lua_script ()
-{
-       if (!_session) {
-               return;
-       }
-       if (_session->registered_lua_function_count () ==  0) {
-               string msg = _("There are no active Lua session scripts present in this session.");
-               MessageDialog am (msg);
-               am.run ();
-               return;
-       }
-
-       std::vector<std::string> reg = _session->registered_lua_functions ();
-       SessionScriptManager sm ("Remove Lua Session Script", reg);
-       switch (sm.run ()) {
-               case Gtk::RESPONSE_ACCEPT:
-                       break;
-               default:
-                       return;
-       }
-       try {
-               _session->unregister_lua_function (sm.name());
-       } catch (luabridge::LuaException const& e) {
-               string msg = string_compose (_("Session script '%1' removal failed: %2"), sm.name(), e.what ());
-               MessageDialog am (msg);
-               am.run ();
-       }
-}
-
 void
 ARDOUR_UI::stop_video_server (bool ask_confirm)
 {
@@ -4260,15 +4258,18 @@ ARDOUR_UI::start_video_server (Gtk::Window* float_window, bool popup_msg)
 
                std::string icsd_exec = video_server_dialog->get_exec_path();
                std::string icsd_docroot = video_server_dialog->get_docroot();
-               if (icsd_docroot.empty()) {
 #ifndef PLATFORM_WINDOWS
-                       icsd_docroot = X_("/");
-#else
-                       icsd_docroot = X_("C:\\");
-#endif
+               if (icsd_docroot.empty()) {
+                       icsd_docroot = VideoUtils::video_get_docroot (Config);
                }
+#endif
 
                GStatBuf sb;
+#ifdef PLATFORM_WINDOWS
+               if (VideoUtils::harvid_version >= 0x000802 && icsd_docroot.empty()) {
+                       /* OK, allow all drive letters */
+               } else
+#endif
                if (g_lstat (icsd_docroot.c_str(), &sb) != 0 || !S_ISDIR(sb.st_mode)) {
                        warning << _("Specified docroot is not an existing directory.") << endmsg;
                        continue;
@@ -4300,6 +4301,11 @@ ARDOUR_UI::start_video_server (Gtk::Window* float_window, bool popup_msg)
                argp[8] = 0;
                stop_video_server();
 
+#ifdef PLATFORM_WINDOWS
+               if (VideoUtils::harvid_version >= 0x000802 && icsd_docroot.empty()) {
+                       /* OK, allow all drive letters */
+               } else
+#endif
                if (icsd_docroot == X_("/") || icsd_docroot == X_("C:\\")) {
                        Config->set_video_advanced_setup(false);
                } else {
@@ -4653,7 +4659,7 @@ void
 ARDOUR_UI::create_xrun_marker (framepos_t where)
 {
        if (_session) {
-               Location *location = new Location (*_session, where, where, _("xrun"), Location::IsMark);
+               Location *location = new Location (*_session, where, where, _("xrun"), Location::IsMark, 0);
                _session->locations()->add (location);
        }
 }
@@ -4941,10 +4947,6 @@ Menu > Window > Audio/Midi Setup"),
 void
 ARDOUR_UI::use_config ()
 {
-       XMLNode* node = Config->extra_xml (X_("TransportControllables"));
-       if (node) {
-               set_transport_controllable_state (*node);
-       }
 }
 
 void
@@ -4988,16 +4990,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
@@ -5034,86 +5041,6 @@ ARDOUR_UI::store_clock_modes ()
        _session->set_dirty ();
 }
 
-ARDOUR_UI::TransportControllable::TransportControllable (std::string name, ARDOUR_UI& u, ToggleType tp)
-       : Controllable (name), ui (u), type(tp)
-{
-
-}
-
-void
-ARDOUR_UI::TransportControllable::set_value (double val, PBD::Controllable::GroupControlDisposition /*group_override*/)
-{
-       if (val < 0.5) {
-               /* do nothing: these are radio-style actions */
-               return;
-       }
-
-       const char *action = 0;
-
-       switch (type) {
-       case Roll:
-               action = X_("Roll");
-               break;
-       case Stop:
-               action = X_("Stop");
-               break;
-       case GotoStart:
-               action = X_("GotoStart");
-               break;
-       case GotoEnd:
-               action = X_("GotoEnd");
-               break;
-       case AutoLoop:
-               action = X_("Loop");
-               break;
-       case PlaySelection:
-               action = X_("PlaySelection");
-               break;
-       case RecordEnable:
-               action = X_("Record");
-               break;
-       default:
-               break;
-       }
-
-       if (action == 0) {
-               return;
-       }
-
-       Glib::RefPtr<Action> act = ActionManager::get_action ("Transport", action);
-
-       if (act) {
-               act->activate ();
-       }
-}
-
-double
-ARDOUR_UI::TransportControllable::get_value (void) const
-{
-       float val = 0.0;
-
-       switch (type) {
-       case Roll:
-               break;
-       case Stop:
-               break;
-       case GotoStart:
-               break;
-       case GotoEnd:
-               break;
-       case AutoLoop:
-               break;
-       case PlaySelection:
-               break;
-       case RecordEnable:
-               break;
-       default:
-               break;
-       }
-
-       return val;
-}
-
 void
 ARDOUR_UI::setup_profile ()
 {
@@ -5261,16 +5188,16 @@ ARDOUR_UI::do_audio_midi_setup (uint32_t desired_sample_rate)
 
        while (true) {
                int response = audio_midi_setup->run();
+               printf("RESPONSE %d\n", response);
                switch (response) {
-               case Gtk::RESPONSE_OK:
+               case Gtk::RESPONSE_DELETE_EVENT:
+                       return -1;
+               default:
                        if (!AudioEngine::instance()->running()) {
                                continue;
-                       } else {
-                               return 0;
                        }
+                       audio_midi_setup->hide ();
                        return 0;
-               default:
-                       return -1;
                }
        }
 }
@@ -5424,7 +5351,7 @@ ARDOUR_UI::setup_toplevel_window (Gtk::Window& window, const string& name, void*
        }
 
        window.set_title (title.get_string());
-       window.set_wmclass (string_compose (X_("%1_%1"), downcase (PROGRAM_NAME), downcase (name)), PROGRAM_NAME);
+       window.set_wmclass (string_compose (X_("%1_%1"), downcase (std::string(PROGRAM_NAME)), downcase (name)), PROGRAM_NAME);
 
        window.set_flags (CAN_FOCUS);
        window.add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK);
@@ -5490,15 +5417,15 @@ ARDOUR_UI::key_event_handler (GdkEventKey* ev, Gtk::Window* event_window)
 }
 
 static Gtkmm2ext::Bindings*
-get_bindings_from_widget_heirarchy (GtkWidget* w)
+get_bindings_from_widget_heirarchy (GtkWidget** w)
 {
        void* p = NULL;
 
-       while (w) {
-               if ((p = g_object_get_data (G_OBJECT(w), "ardour-bindings")) != 0) {
+       while (*w) {
+               if ((p = g_object_get_data (G_OBJECT(*w), "ardour-bindings")) != 0) {
                        break;
                }
-               w = gtk_widget_get_parent (w);
+               *w = gtk_widget_get_parent (*w);
        }
 
        return reinterpret_cast<Gtkmm2ext::Bindings*> (p);
@@ -5509,6 +5436,7 @@ ARDOUR_UI::key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey
 {
        GtkWindow* win = window.gobj();
        GtkWidget* focus = gtk_window_get_focus (win);
+       GtkWidget* binding_widget = focus;
        bool special_handling_of_unmodified_accelerators = false;
        const guint mask = (Keyboard::RelevantModifierKeyMask & ~(Gdk::SHIFT_MASK|Gdk::LOCK_MASK));
 
@@ -5527,7 +5455,7 @@ ARDOUR_UI::key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey
 
                } else {
 
-                       Gtkmm2ext::Bindings* focus_bindings = get_bindings_from_widget_heirarchy (focus);
+                       Gtkmm2ext::Bindings* focus_bindings = get_bindings_from_widget_heirarchy (&binding_widget);
                        if (focus_bindings) {
                                bindings = focus_bindings;
                                DEBUG_TRACE (DEBUG::Accelerators, string_compose ("Switch bindings based on focus widget, now using %1\n", bindings->name()));
@@ -5589,7 +5517,7 @@ ARDOUR_UI::key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey
                DEBUG_TRACE (DEBUG::Accelerators, "\tsending to window\n");
                KeyboardKey k (ev->state, ev->keyval);
 
-               if (bindings) {
+               while (bindings) {
 
                        DEBUG_TRACE (DEBUG::Accelerators, string_compose ("\tusing Ardour bindings %1 @ %2 for this event\n", bindings->name(), bindings));
 
@@ -5597,6 +5525,17 @@ ARDOUR_UI::key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey
                                DEBUG_TRACE (DEBUG::Accelerators, "\t\thandled\n");
                                return true;
                        }
+
+                       if (binding_widget) {
+                               binding_widget = gtk_widget_get_parent (binding_widget);
+                               if (binding_widget) {
+                                       bindings = get_bindings_from_widget_heirarchy (&binding_widget);
+                               } else {
+                                       bindings = 0;
+                               }
+                       } else {
+                               bindings = 0;
+                       }
                }
 
                DEBUG_TRACE (DEBUG::Accelerators, "\tnot yet handled, try global bindings\n");
@@ -5627,7 +5566,7 @@ ARDOUR_UI::key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey
                DEBUG_TRACE (DEBUG::Accelerators, "\tpropagation didn't handle, so activate\n");
                KeyboardKey k (ev->state, ev->keyval);
 
-               if (bindings) {
+               while (bindings) {
 
                        DEBUG_TRACE (DEBUG::Accelerators, "\tusing Ardour bindings for this window\n");
 
@@ -5637,6 +5576,16 @@ ARDOUR_UI::key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey
                                return true;
                        }
 
+                       if (binding_widget) {
+                               binding_widget = gtk_widget_get_parent (binding_widget);
+                               if (binding_widget) {
+                                       bindings = get_bindings_from_widget_heirarchy (&binding_widget);
+                               } else {
+                                       bindings = 0;
+                               }
+                       } else {
+                               bindings = 0;
+                       }
                }
 
                DEBUG_TRACE (DEBUG::Accelerators, "\tnot yet handled, try global bindings\n");
@@ -5666,3 +5615,55 @@ ARDOUR_UI::cancel_solo ()
                _session->cancel_all_solo ();
        }
 }
+
+void
+ARDOUR_UI::reset_focus (Gtk::Widget* w)
+{
+       /* this resets focus to the first focusable parent of the given widget,
+        * or, if there is no focusable parent, cancels focus in the toplevel
+        * window that the given widget is packed into (if there is one).
+        */
+
+       if (!w) {
+               return;
+       }
+
+       Gtk::Widget* top = w->get_toplevel();
+
+       if (!top || !top->is_toplevel()) {
+               return;
+       }
+
+       w = w->get_parent ();
+
+       while (w) {
+
+               if (w->is_toplevel()) {
+                       /* Setting the focus widget to a Gtk::Window causes all
+                        * subsequent calls to ::has_focus() on the nominal
+                        * focus widget in that window to return
+                        * false. Workaround: never set focus to the toplevel
+                        * itself.
+                        */
+                       break;
+               }
+
+               if (w->get_can_focus ()) {
+                       Gtk::Window* win = dynamic_cast<Gtk::Window*> (top);
+                       win->set_focus (*w);
+                       return;
+               }
+               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);
+
+}