use X_() to mark binding names as non-translated
[ardour.git] / gtk2_ardour / ardour_ui.cc
index 31b79d1657e2444c83382ce42a1c7147c9478d99..9f886ed5125e5047804768bc5fa4847f37a887e3 100644 (file)
@@ -48,6 +48,7 @@
 #include "pbd/error.h"
 #include "pbd/basename.h"
 #include "pbd/compose.h"
+#include "pbd/convert.h"
 #include "pbd/failed_constructor.h"
 #include "pbd/enumwriter.h"
 #include "pbd/memento_command.h"
@@ -112,6 +113,7 @@ typedef uint64_t microseconds_t;
 #include "big_clock_window.h"
 #include "bundle_manager.h"
 #include "duplicate_routes_dialog.h"
+#include "debug.h"
 #include "engine_dialog.h"
 #include "export_video_dialog.h"
 #include "export_video_infobox.h"
@@ -224,8 +226,12 @@ libxml_structured_error_func (void* /* parsing_context*/,
 
 
 ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir)
+<<<<<<< HEAD
 
+       : Gtkmm2ext::UI (PROGRAM_NAME, X_("gui"), argcp, argvp)
+=======
        : Gtkmm2ext::UI (PROGRAM_NAME, argcp, argvp)
+>>>>>>> first compilable version of tabbable design.
        , session_loaded (false)
        , gui_object_state (new GUIObjectState)
        , primary_clock   (new MainClock (X_("primary"),   X_("transport"), true ))
@@ -261,9 +267,8 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir)
        , last_key_press_time (0)
        , save_as_dialog (0)
        , meterbridge (0)
+       , rc_option_editor (0)
        , speaker_config_window (X_("speaker-config"), _("Speaker Configuration"))
-       , key_editor (X_("key-editor"), _("Key Bindings"))
-       , rc_option_editor (X_("rc-options-editor"), _("Preferences"))
        , add_route_dialog (X_("add-routes"), _("Add Tracks/Busses"))
        , about (X_("about"), _("About"))
        , location_ui (X_("locations"), _("Locations"))
@@ -271,11 +276,12 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir)
        , audio_midi_setup (X_("audio-midi-setup"), _("Audio/MIDI Setup"))
        , export_video_dialog (X_("video-export"), _("Video Export Dialog"))
        , session_option_editor (X_("session-options-editor"), _("Properties"), boost::bind (&ARDOUR_UI::create_session_option_editor, this))
-       , add_video_dialog (X_("add-video"), _("Add Tracks/Busses"), boost::bind (&ARDOUR_UI::create_add_video_dialog, this))
+       , add_video_dialog (X_("add-video"), _("Add Video"), boost::bind (&ARDOUR_UI::create_add_video_dialog, this))
        , bundle_manager (X_("bundle-manager"), _("Bundle Manager"), boost::bind (&ARDOUR_UI::create_bundle_manager, this))
        , big_clock_window (X_("big-clock"), _("Big Clock"), boost::bind (&ARDOUR_UI::create_big_clock_window, this))
        , audio_port_matrix (X_("audio-connection-manager"), _("Audio Connections"), boost::bind (&ARDOUR_UI::create_global_port_matrix, this, ARDOUR::DataType::AUDIO))
        , midi_port_matrix (X_("midi-connection-manager"), _("MIDI Connections"), boost::bind (&ARDOUR_UI::create_global_port_matrix, this, ARDOUR::DataType::MIDI))
+       , key_editor (X_("key-editor"), _("Bindings Editor"), boost::bind (&ARDOUR_UI::create_key_editor, this))
        , video_server_process (0)
        , splash (0)
        , have_configure_timeout (false)
@@ -407,24 +413,24 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir)
        const XMLNode* ui_xml = Config->extra_xml (X_("UI"));
 
        if (ui_xml) {
-               key_editor.set_state (*ui_xml);
-               rc_option_editor.set_state (*ui_xml);
-               session_option_editor.set_state (*ui_xml);
-               speaker_config_window.set_state (*ui_xml);
-               about.set_state (*ui_xml);
-               add_route_dialog.set_state (*ui_xml);
-               add_video_dialog.set_state (*ui_xml);
-               route_params.set_state (*ui_xml);
-               bundle_manager.set_state (*ui_xml);
-               location_ui.set_state (*ui_xml);
-               big_clock_window.set_state (*ui_xml);
-               audio_port_matrix.set_state (*ui_xml);
-               midi_port_matrix.set_state (*ui_xml);
-               export_video_dialog.set_state (*ui_xml);
-       }
-
+               key_editor.set_state (*ui_xml, 0);
+               session_option_editor.set_state (*ui_xml, 0);
+               speaker_config_window.set_state (*ui_xml, 0);
+               about.set_state (*ui_xml, 0);
+               add_route_dialog.set_state (*ui_xml, 0);
+               add_video_dialog.set_state (*ui_xml, 0);
+               route_params.set_state (*ui_xml, 0);
+               bundle_manager.set_state (*ui_xml, 0);
+               location_ui.set_state (*ui_xml, 0);
+               big_clock_window.set_state (*ui_xml, 0);
+               audio_port_matrix.set_state (*ui_xml, 0);
+               midi_port_matrix.set_state (*ui_xml, 0);
+               export_video_dialog.set_state (*ui_xml, 0);
+       }
+
+       /* Separate windows */
+       
        WM::Manager::instance().register_window (&key_editor);
-       WM::Manager::instance().register_window (&rc_option_editor);
        WM::Manager::instance().register_window (&session_option_editor);
        WM::Manager::instance().register_window (&speaker_config_window);
        WM::Manager::instance().register_window (&about);
@@ -533,7 +539,7 @@ was not fast enough. Try to restart\n\
 the audio backend and save the session."), PROGRAM_NAME);
        }
 
-       MessageDialog msg (*editor, msgstr);
+       MessageDialog msg (_main_window, msgstr);
        pop_back_splash (msg);
        msg.run ();
 
@@ -794,10 +800,15 @@ ARDOUR_UI::autosave_session ()
 }
 
 void
-ARDOUR_UI::update_autosave ()
+ARDOUR_UI::session_dirty_changed ()
 {
-       ENSURE_GUI_THREAD (*this, &ARDOUR_UI::update_autosave)
+       update_autosave ();
+       update_title ();
+}
 
+void
+ARDOUR_UI::update_autosave ()
+{
        if (_session && _session->dirty()) {
                if (_autosave_connection.connected()) {
                        _autosave_connection.disconnect();
@@ -848,6 +859,13 @@ ARDOUR_UI::check_announcements ()
 #endif
 }
 
+static bool
+_hide_splash (gpointer arg)
+{
+       ((ARDOUR_UI*)arg)->hide_splash();
+       return false;
+}
+
 int
 ARDOUR_UI::starting ()
 {
@@ -1020,8 +1038,6 @@ ARDOUR_UI::starting ()
 
        use_config ();
 
-       goto_editor_window ();
-
        WM::Manager::instance().show_visible ();
 
        /* We have to do this here since goto_editor_window() ends up calling show_all() on the
@@ -1030,6 +1046,14 @@ ARDOUR_UI::starting ()
        _status_bar_visibility.update ();
 
        BootMessage (string_compose (_("%1 is ready for use"), PROGRAM_NAME));
+
+       if (splash && splash->is_visible()) {
+               // in 1 second, hide the splash screen
+               Glib::signal_timeout().connect (sigc::bind (sigc::ptr_fun (_hide_splash), this), 1000);
+       }
+
+       /* all other dialogs are created conditionally */
+
        return 0;
 }
 
@@ -1097,7 +1121,6 @@ ARDOUR_UI::check_memory_locking ()
 
                                pop_back_splash (msg);
 
-                               editor->ensure_float (msg);
                                msg.run ();
 
                                if (cb.get_active()) {
@@ -1143,7 +1166,7 @@ ARDOUR_UI::finish()
                                /* use the default name */
                                if (save_state_canfail ("")) {
                                        /* failed - don't quit */
-                                       MessageDialog msg (*editor,
+                                       MessageDialog msg (_main_window,
                                                           string_compose (_("\
 %1 was unable to save your session.\n\n\
 If you still wish to quit, please use the\n\n\
@@ -1758,7 +1781,7 @@ ARDOUR_UI::session_add_mixed_track (const ChanCount& input, const ChanCount& out
        }
 
        catch (...) {
-               MessageDialog msg (*editor,
+               MessageDialog msg (_main_window,
                                   string_compose (_("There are insufficient ports available\n\
 to create a new track or bus.\n\
 You should save %1, exit and\n\
@@ -1819,7 +1842,7 @@ ARDOUR_UI::session_add_audio_route (
        }
 
        catch (...) {
-               MessageDialog msg (*editor,
+               MessageDialog msg (_main_window,
                                   string_compose (_("There are insufficient ports available\n\
 to create a new track or bus.\n\
 You should save %1, exit and\n\
@@ -1965,7 +1988,7 @@ ARDOUR_UI::transport_record (bool roll)
                switch (_session->record_status()) {
                case Session::Disabled:
                        if (_session->ntracks() == 0) {
-                               MessageDialog msg (*editor, _("Please create one or more tracks before trying to record.\nYou can do this with the \"Add Track or Bus\" option in the Session menu."));
+                               MessageDialog msg (_main_window, _("Please create one or more tracks before trying to record.\nYou can do this with the \"Add Track or Bus\" option in the Session menu."));
                                msg.run ();
                                return;
                        }
@@ -2110,7 +2133,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 if (!_session->config.get_external_sync()) {
+               } else {
+                       /* the only external sync condition we can be in here
+                        * would be Engine (JACK) sync, in which case we still
+                        * want to do this.
+                        */
+
                        if (UIConfiguration::instance().get_follow_edits() && ( editor->get_selection().time.front().start == _session->transport_frame() ) ) {  //if playhead is exactly at the start of a range, we can assume it was placed there by follow_edits
                                _session->request_play_range (&editor->get_selection().time, true);
                                _session->set_requested_return_frame( editor->get_selection().time.front().start );  //force an auto-return here
@@ -2123,9 +2151,13 @@ ARDOUR_UI::toggle_roll (bool with_abort, bool roll_out_of_bounded_mode)
 void
 ARDOUR_UI::toggle_session_auto_loop ()
 {
+       if (!_session) {
+               return;
+       }
+
        Location * looploc = _session->locations()->auto_loop_location();
 
-       if (!_session || !looploc) {
+       if (!looploc) {
                return;
        }
 
@@ -2238,10 +2270,10 @@ ARDOUR_UI::toggle_record_enable (uint32_t rid)
 
        if ((r = _session->route_by_remote_id (rid)) != 0) {
 
-               Track* t;
+               boost::shared_ptr<Track> t;
 
-               if ((t = dynamic_cast<Track*>(r.get())) != 0) {
-                       t->set_record_enabled (!t->record_enabled(), this);
+               if ((t = boost::dynamic_pointer_cast<Track>(r)) != 0) {
+                       t->set_record_enabled (!t->record_enabled(), Controllable::UseGroup);
                }
        }
 }
@@ -2444,6 +2476,21 @@ ARDOUR_UI::save_session_as ()
        }
 }
 
+void
+ARDOUR_UI::quick_snapshot_session (bool switch_to_it)
+{
+               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);
+
+               save_state (timebuf, switch_to_it);
+}
+
+
 bool
 ARDOUR_UI::process_snapshot_session_prompter (ArdourPrompter& prompter, bool switch_to_it)
 {
@@ -2503,7 +2550,9 @@ ARDOUR_UI::snapshot_session (bool switch_to_it)
                prompter.set_prompt (_("Name of new snapshot"));
        }
 
-       if (!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;
@@ -2834,13 +2883,47 @@ void
 ARDOUR_UI::load_from_application_api (const std::string& path)
 {
        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
+        * race-condition:
+        *  - ShouldLoad does not arrive in time, ARDOUR_COMMAND_LINE::session_name is empty:
+        *    -> ARDOUR_UI::get_session_parameters starts a SessionDialog.
+        *  - ShouldLoad signal arrives, this function is called and sets ARDOUR_COMMAND_LINE::session_name
+        *    -> SessionDialog is not displayed
+        */
+
+       if (_session_dialog) {
+               std::string session_name = basename_nosuffix (ARDOUR_COMMAND_LINE::session_name);
+               std::string session_path = path;
+               if (Glib::file_test (session_path, Glib::FILE_TEST_IS_REGULAR)) {
+                       session_path = Glib::path_get_dirname (session_path);
+               }
+               // signal the existing dialog in ARDOUR_UI::get_session_parameters()
+               _session_dialog->set_provided_session (session_name, session_path);
+               _session_dialog->response (RESPONSE_NONE);
+               _session_dialog->hide();
+               return;
+       }
 
+       int rv;
        if (Glib::file_test (path, Glib::FILE_TEST_IS_DIR)) {
                /* /path/to/foo => /path/to/foo, foo */
-               load_session (path, basename_nosuffix (path));
+               rv = load_session (path, basename_nosuffix (path));
        } else {
                /* /path/to/foo/foo.ardour => /path/to/foo, foo */
-               load_session (Glib::path_get_dirname (path), basename_nosuffix (path));
+               rv =load_session (Glib::path_get_dirname (path), basename_nosuffix (path));
+       }
+
+       // if load_session fails -> pop up SessionDialog.
+       if (rv) {
+               ARDOUR_COMMAND_LINE::session_name = "";
+
+               if (get_session_parameters (true, false)) {
+                       exit (1);
+               }
+
+               goto_editor_window ();
        }
 }
 
@@ -2896,6 +2979,7 @@ ARDOUR_UI::get_session_parameters (bool quit_on_cancel, bool should_be_new, stri
 
        SessionDialog session_dialog (should_be_new, session_name, session_path, load_template, cancel_not_quit);
 
+       _session_dialog = &session_dialog;
        while (ret != 0) {
 
                if (!ARDOUR_COMMAND_LINE::session_name.empty()) {
@@ -2929,6 +3013,10 @@ ARDOUR_UI::get_session_parameters (bool quit_on_cancel, bool should_be_new, stri
                        switch (session_dialog.run()) {
                        case RESPONSE_ACCEPT:
                                break;
+                       case RESPONSE_NONE:
+                               /* this is used for async * app->ShouldLoad(). */
+                               continue; // while loop
+                               break;
                        default:
                                if (quit_on_cancel) {
                                        // JE - Currently (July 2014) this section can only get reached if the
@@ -3074,6 +3162,8 @@ ARDOUR_UI::get_session_parameters (bool quit_on_cancel, bool should_be_new, stri
                }
        }
 
+       _session_dialog = NULL;
+
        return ret;
 }
 
@@ -3093,8 +3183,6 @@ ARDOUR_UI::close_session()
        if (get_session_parameters (true, false)) {
                exit (1);
        }
-
-       goto_editor_window ();
 }
 
 /** @param snap_name Snapshot name (without .ardour suffix).
@@ -3231,8 +3319,6 @@ ARDOUR_UI::load_session (const std::string& path, const std::string& snap_name,
 
        session_loaded = true;
 
-       goto_editor_window ();
-
        if (_session) {
                _session->set_clean ();
        }
@@ -3362,7 +3448,7 @@ ARDOUR_UI::launch_reference ()
 void
 ARDOUR_UI::launch_tracker ()
 {
-       PBD::open_uri ("http://tracker.ardour.org/bug_report_page.php");
+       PBD::open_uri ("http://tracker.ardour.org");
 }
 
 void
@@ -3448,7 +3534,7 @@ ARDOUR_UI::display_cleanup_results (ARDOUR::CleanupReport& rep, const gchar* lis
        removed = rep.paths.size();
 
        if (removed == 0) {
-               MessageDialog msgd (*editor,
+               MessageDialog msgd (_main_window,
                                    _("No files were ready for clean-up"),
                                    true,
                                    Gtk::MESSAGE_INFO,
@@ -3757,101 +3843,15 @@ ARDOUR_UI::start_duplicate_routes ()
 {
        if (!duplicate_routes_dialog) {
                duplicate_routes_dialog = new DuplicateRouteDialog;
-               duplicate_routes_dialog->signal_response().connect (sigc::mem_fun (*this, &ARDOUR_UI::finish_duplicate_routes));
        }
 
-       TrackSelection& tracks  (editor->get_selection().tracks);
-       uint32_t ntracks = 0;
-       uint32_t nbusses = 0;
-
-       for (TrackSelection::iterator t = tracks.begin(); t != tracks.end(); ++t) {
-
-               RouteUI* rui = dynamic_cast<RouteUI*> (*t);
-
-               if (!rui) {
-                       /* some other type of timeaxis view, not a route */
-                       continue;
-               }
-
-               boost::shared_ptr<Route> r (rui->route());
-
-               if (boost::dynamic_pointer_cast<Track> (r)) {
-                       ntracks++;
-               } else {
-                       if (!r->is_master() && !r->is_monitor()) {
-                               nbusses++;
-                       }
-               }
-       }
-
-       if (ntracks == 0 && nbusses == 0) {
-               cerr << "You can't do this\n";
+       if (duplicate_routes_dialog->restart (_session)) {
                return;
        }
 
-       duplicate_routes_dialog->setup (ntracks, nbusses);
        duplicate_routes_dialog->present ();
 }
 
-void
-ARDOUR_UI::finish_duplicate_routes (int response)
-{
-       if (!duplicate_routes_dialog) {
-               /* how could this happen? */
-               return;
-       }
-
-       duplicate_routes_dialog->hide ();
-
-       if (response != Gtk::RESPONSE_OK) {
-               return;
-       }
-
-       ARDOUR::PlaylistDisposition playlist_disposition = duplicate_routes_dialog->playlist_disposition ();
-       uint32_t count = duplicate_routes_dialog->count ();
-
-       /* Copy the track selection because it will/may change as we add new ones */
-       TrackSelection tracks  (editor->get_selection().tracks);
-       int err = 0;
-
-       for (TrackSelection::iterator t = tracks.begin(); t != tracks.end(); ++t) {
-
-               RouteUI* rui = dynamic_cast<RouteUI*> (*t);
-
-               if (!rui) {
-                       /* some other type of timeaxis view, not a route */
-                       continue;
-               }
-
-               if (rui->route()->is_master() || rui->route()->is_monitor()) {
-                       /* no option to duplicate these */
-                       continue;
-               }
-
-               XMLNode& state (rui->route()->get_state());
-               RouteList rl = _session->new_route_from_template (count, state, string(), playlist_disposition);
-
-               /* normally the state node would be added to a parent, and
-                * ownership would transfer. Because we don't do that here,
-                * we need to delete the node ourselves.
-                */
-
-               delete &state;
-
-               if (rl.empty()) {
-                       err++;
-                       break;
-               }
-       }
-
-       if (err) {
-               MessageDialog msg (_("1 or more tracks/busses could not be duplicated"),
-                                    true, MESSAGE_ERROR, BUTTONS_OK, true);
-               msg.set_position (WIN_POS_MOUSE);
-               msg.run ();
-       }
-}
-
 void
 ARDOUR_UI::add_route (Gtk::Window* /* ignored */)
 {
@@ -4305,6 +4305,30 @@ ARDOUR_UI::mixer_settings () const
        return node;
 }
 
+XMLNode*
+ARDOUR_UI::main_window_settings () const
+{
+       XMLNode* node = 0;
+
+       if (_session) {
+               node = _session->instant_xml(X_("Main"));
+       } else {
+               node = Config->instant_xml(X_("Main"));
+       }
+
+       if (!node) {
+               if (getenv("ARDOUR_INSTANT_XML_PATH")) {
+                       node = Config->instant_xml(getenv("ARDOUR_INSTANT_XML_PATH"));
+               }
+       }
+
+       if (!node) {
+               node = new XMLNode (X_("Main"));
+       }
+
+       return node;
+}
+
 XMLNode*
 ARDOUR_UI::editor_settings () const
 {
@@ -4356,7 +4380,7 @@ void
 ARDOUR_UI::halt_on_xrun_message ()
 {
         cerr << "HALT on xrun\n";
-       MessageDialog msg (*editor, _("Recording was stopped because your system could not keep up."));
+        MessageDialog msg (_main_window, _("Recording was stopped because your system could not keep up."));
        msg.run ();
 }
 
@@ -4385,7 +4409,7 @@ ARDOUR_UI::disk_overrun_handler ()
 
        if (!have_disk_speed_dialog_displayed) {
                have_disk_speed_dialog_displayed = true;
-               MessageDialog* msg = new MessageDialog (*editor, string_compose (_("\
+               MessageDialog* msg = new MessageDialog (_main_window, string_compose (_("\
 The disk system on your computer\n\
 was not able to keep up with %1.\n\
 \n\
@@ -4520,7 +4544,7 @@ ARDOUR_UI::disk_underrun_handler ()
        if (!have_disk_speed_dialog_displayed) {
                have_disk_speed_dialog_displayed = true;
                MessageDialog* msg = new MessageDialog (
-                       *editor, string_compose (_("The disk system on your computer\n\
+                       _main_window, string_compose (_("The disk system on your computer\n\
 was not able to keep up with %1.\n\
 \n\
 Specifically, it failed to read data from disk\n\
@@ -4544,12 +4568,7 @@ ARDOUR_UI::session_dialog (std::string msg)
 
        MessageDialog* d;
 
-       if (editor) {
-               d = new MessageDialog (*editor, msg, false, MESSAGE_INFO, BUTTONS_OK, true);
-       } else {
-               d = new MessageDialog (msg, false, MESSAGE_INFO, BUTTONS_OK, true);
-       }
-
+       d = new MessageDialog (msg, false, MESSAGE_INFO, BUTTONS_OK, true);
        d->show_all ();
        d->run ();
        delete d;
@@ -4725,7 +4744,7 @@ ARDOUR_UI::TransportControllable::TransportControllable (std::string name, ARDOU
 }
 
 void
-ARDOUR_UI::TransportControllable::set_value (double val)
+ARDOUR_UI::TransportControllable::set_value (double val, PBD::Controllable::GroupControlDisposition /*group_override*/)
 {
        if (val < 0.5) {
                /* do nothing: these are radio-style actions */
@@ -5075,15 +5094,253 @@ ARDOUR_UI::hide_application ()
 }
 
 void
-ARDOUR_UI::cancel_solo ()
+ARDOUR_UI::setup_toplevel_window (Gtk::Window& window, const string& name, void* owner)
 {
-       if (_session) {
-               if (_session->soloing()) {
-                       _session->set_solo (_session->get_routes(), false);
-               } else if (_session->listening()) {
-                       _session->set_listen (_session->get_routes(), false);
+       /* icons, titles, WM stuff */
+
+       static list<Glib::RefPtr<Gdk::Pixbuf> > window_icons;
+
+       if (window_icons.empty()) {
+               Glib::RefPtr<Gdk::Pixbuf> icon;
+               if ((icon = ::get_icon ("ardour_icon_16px")) != 0) {
+                       window_icons.push_back (icon);
+               }
+               if ((icon = ::get_icon ("ardour_icon_22px")) != 0) {
+                       window_icons.push_back (icon);
+               }
+               if ((icon = ::get_icon ("ardour_icon_32px")) != 0) {
+                       window_icons.push_back (icon);
+               }
+               if ((icon = ::get_icon ("ardour_icon_48px")) != 0) {
+                       window_icons.push_back (icon);
                }
+       }
+
+       if (!window_icons.empty()) {
+               window.set_default_icon_list (window_icons);
+       }
+       
+       Gtkmm2ext::WindowTitle title (Glib::get_application_name());
 
-               _session->clear_all_solo_state (_session->get_routes()); // safeguard, ideally this won't do anything, check the log-window
+       if (!name.empty()) {
+               title += name;
        }
+
+       window.set_title (title.get_string());
+       window.set_wmclass (string_compose (X_("%1_%1"), downcase (PROGRAM_NAME), downcase (name)), PROGRAM_NAME);
+
+       window.set_flags (CAN_FOCUS);
+       window.add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK);
+
+       /* This is a hack to ensure that GTK-accelerators continue to
+        * work. Once we switch over to entirely native bindings, this will be
+        * unnecessary and should be removed
+        */
+       window.add_accel_group (ActionManager::ui_manager->get_accel_group());
+
+       window.signal_configure_event().connect (sigc::mem_fun (*this, &ARDOUR_UI::configure_handler));
+       window.signal_window_state_event().connect (sigc::bind (sigc::mem_fun (*this, &ARDOUR_UI::tabbed_window_state_event_handler), owner));
+       window.signal_key_press_event().connect (sigc::bind (sigc::mem_fun (*this, &ARDOUR_UI::key_event_handler), &window), false);
+       window.signal_key_release_event().connect (sigc::bind (sigc::mem_fun (*this, &ARDOUR_UI::key_event_handler), &window), false);
 }
+
+bool
+ARDOUR_UI::key_event_handler (GdkEventKey* ev, Gtk::Window* event_window)
+{
+       Gtkmm2ext::Bindings* bindings = 0;
+       Gtk::Window* window = 0;
+
+       /* until we get ardour bindings working, we are not handling key
+        * releases yet.
+        */
+       
+       if (ev->type != GDK_KEY_PRESS) {
+               return false;
+       }
+       
+       if (event_window == &_main_window) {
+
+               window = event_window;
+               
+               /* find current tab contents */
+
+               Gtk::Widget* w = _tabs.get_nth_page (_tabs.get_current_page());
+
+               /* see if it uses the ardour binding system */
+
+               if (w) {
+                       bindings = reinterpret_cast<Gtkmm2ext::Bindings*>(w->get_data ("ardour-bindings"));
+               } else {
+                       bindings = &global_bindings;
+               }
+
+               DEBUG_TRACE (DEBUG::Accelerators, string_compose ("main window key event, bindings = %1, global = %2\n", bindings, &global_bindings));
+               
+       } else if (event_window != 0) {
+
+               window = event_window;
+               
+               /* see if window uses ardour binding system */
+
+               bindings = reinterpret_cast<Gtkmm2ext::Bindings*>(window->get_data ("ardour-bindings"));
+               
+       } 
+
+       /* An empty binding set is treated as if it doesn't exist */
+       
+       if (bindings && bindings->empty()) {
+               bindings = 0;
+       }
+       
+       return key_press_focus_accelerator_handler (*window, ev, bindings);
+}
+                       
+bool
+ARDOUR_UI::key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev, Gtkmm2ext::Bindings* bindings)
+{
+       GtkWindow* win = window.gobj();
+       GtkWidget* focus = gtk_window_get_focus (win);
+       bool special_handling_of_unmodified_accelerators = false;
+       /* consider all relevant modifiers but not LOCK or SHIFT */
+       const guint mask = (Keyboard::RelevantModifierKeyMask & ~(Gdk::SHIFT_MASK|Gdk::LOCK_MASK));
+
+       GdkModifierType modifier = GdkModifierType (ev->state);
+        modifier = GdkModifierType (modifier & gtk_accelerator_get_default_mod_mask());
+        Gtkmm2ext::possibly_translate_mod_to_make_legal_accelerator(modifier);
+
+        if (focus) {
+               
+               /* some widget has keyboard focus */
+
+               if (GTK_IS_ENTRY(focus) || Keyboard::some_magic_widget_has_focus()) {
+
+                       /* A particular kind of focusable widget currently has keyboard
+                        * focus. All unmodified key events should go to that widget
+                        * first and not be used as an accelerator by default 
+                        */
+
+                       special_handling_of_unmodified_accelerators = true;
+               }
+       }
+
+        DEBUG_TRACE (DEBUG::Accelerators, string_compose ("Win = %1 focus = %7 (%8) Key event: code = %2  state = %3 special handling ? %4 magic widget focus ? %5 focus widget %6 named %7\n",
+                                                          win,
+                                                          ev->keyval,
+                                                         show_gdk_event_state (ev->state),
+                                                          special_handling_of_unmodified_accelerators,
+                                                          Keyboard::some_magic_widget_has_focus(),
+                                                         focus,
+                                                          (focus ? gtk_widget_get_name (focus) : "no focus widget")));
+
+       /* This exists to allow us to override the way GTK handles
+          key events. The normal sequence is:
+
+          a) event is delivered to a GtkWindow
+          b) accelerators/mnemonics are activated
+          c) if (b) didn't handle the event, propagate to
+              the focus widget and/or focus chain
+
+          The problem with this is that if the accelerators include
+          keys without modifiers, such as the space bar or the
+          letter "e", then pressing the key while typing into
+          a text entry widget results in the accelerator being
+          activated, instead of the desired letter appearing
+          in the text entry.
+
+          There is no good way of fixing this, but this
+          represents a compromise. The idea is that
+          key events involving modifiers (not Shift)
+          get routed into the activation pathway first, then
+          get propagated to the focus widget if necessary.
+
+          If the key event doesn't involve modifiers,
+          we deliver to the focus widget first, thus allowing
+          it to get "normal text" without interference
+          from acceleration.
+
+          Of course, this can also be problematic: if there
+          is a widget with focus, then it will swallow
+          all "normal text" accelerators.
+       */
+
+               
+       if (!special_handling_of_unmodified_accelerators || (ev->state & mask)) {
+               
+               /* no special handling or there are modifiers in effect: accelerate first */
+
+                DEBUG_TRACE (DEBUG::Accelerators, "\tactivate, then propagate\n");
+               DEBUG_TRACE (DEBUG::Accelerators, string_compose ("\tevent send-event:%1 time:%2 length:%3 name %7 string:%4 hardware_keycode:%5 group:%6\n",
+                                                                 ev->send_event, ev->time, ev->length, ev->string, ev->hardware_keycode, ev->group, gdk_keyval_name (ev->keyval)));
+
+               DEBUG_TRACE (DEBUG::Accelerators, "\tsending to window\n");
+               KeyboardKey k (ev->state, ev->keyval);
+
+               if (bindings) {
+
+                       DEBUG_TRACE (DEBUG::Accelerators, "\tusing Ardour bindings for this window\n");
+                       
+                       if (bindings->activate (k, Bindings::Press)) {
+                               DEBUG_TRACE (DEBUG::Accelerators, "\t\thandled\n");
+                               return true;
+                       }
+               }
+
+               DEBUG_TRACE (DEBUG::Accelerators, "\tnot yet handled, try global bindings\n");
+               
+               if (global_bindings.activate (k, Bindings::Press)) {
+                       DEBUG_TRACE (DEBUG::Accelerators, "\t\thandled\n");
+                       return true;
+               }
+
+                DEBUG_TRACE (DEBUG::Accelerators, "\tnot accelerated, now propagate\n");
+                
+                if (gtk_window_propagate_key_event (win, ev)) {
+                       DEBUG_TRACE (DEBUG::Accelerators, "\tpropagate handled\n");
+                       return true;
+                }
+
+       } else {
+               
+               /* no modifiers, propagate first */
+               
+               DEBUG_TRACE (DEBUG::Accelerators, "\tpropagate, then activate\n");
+               
+               if (gtk_window_propagate_key_event (win, ev)) {
+                       DEBUG_TRACE (DEBUG::Accelerators, "\thandled by propagate\n");
+                       return true;
+               }
+
+               DEBUG_TRACE (DEBUG::Accelerators, "\tpropagation didn't handle, so activate\n");
+               KeyboardKey k (ev->state, ev->keyval);          
+
+               if (bindings) {
+                       
+                       DEBUG_TRACE (DEBUG::Accelerators, "\tusing Ardour bindings for this window\n");
+
+                       
+                       if (bindings->activate (k, Bindings::Press)) {
+                               DEBUG_TRACE (DEBUG::Accelerators, "\t\thandled\n");
+                               return true;
+                       }
+                       
+               } 
+               
+               DEBUG_TRACE (DEBUG::Accelerators, "\tnot yet handled, try global bindings\n");
+               
+               if (global_bindings.activate (k, Bindings::Press)) {
+                       DEBUG_TRACE (DEBUG::Accelerators, "\t\thandled\n");
+                       return true;
+               }
+       }
+
+       DEBUG_TRACE (DEBUG::Accelerators, "\tnot handled\n");
+       return true;
+}
+
+void
+ARDOUR_UI::load_bindings ()
+{
+       global_bindings.set_action_map (global_actions);
+       global_bindings.load (X_("global"));
+}
+