Plugin UI state should of course be saved per-session rather than globally.
[ardour.git] / gtk2_ardour / ardour_ui.cc
index bf8fcb070991d3dd9060735950e30654d3f15243..2cf6c8aaae0cf1c696950906afbb3d6b0e0be014 100644 (file)
@@ -55,6 +55,7 @@
 #include "midi++/manager.h"
 
 #include "ardour/ardour.h"
+#include "ardour/callback.h"
 #include "ardour/profile.h"
 #include "ardour/session_directory.h"
 #include "ardour/session_route.h"
@@ -96,6 +97,10 @@ typedef uint64_t microseconds_t;
 #include "startup.h"
 #include "engine_dialog.h"
 #include "processor_box.h"
+#include "time_axis_view_item.h"
+#include "window_proxy.h"
+#include "global_port_matrix.h"
+#include "location_ui.h"
 
 #include "i18n.h"
 
@@ -162,7 +167,6 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[])
 
          auditioning_alert_button (_("AUDITION")),
          solo_alert_button (_("SOLO")),
-         shown_flag (false),
          error_log_button (_("Errors"))
 
 {
@@ -270,7 +274,7 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[])
                SessionEvent::create_per_thread_pool ("GUI", 512);
 
        } catch (failed_constructor& err) {
-               error << _("could not initialize Ardour.") << endmsg;
+               error << string_compose (_("could not initialize %1."), PROGRAM_NAME) << endmsg;
                // pass it on up
                throw;
        }
@@ -286,6 +290,23 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[])
 
        reset_dpi();
 
+        TimeAxisViewItem::set_constant_heights ();
+
+       /* The following must happen after ARDOUR::init() so that Config is set up */
+       
+       location_ui = new ActionWindowProxy<LocationUIWindow> (X_("locations"), Config->extra_xml (X_("UI")), X_("ToggleLocations"));
+       big_clock_window = new ActionWindowProxy<Gtk::Window> (X_("bigclock"), Config->extra_xml (X_("UI")), X_("ToggleBigClock"));
+       
+       for (ARDOUR::DataType::iterator i = ARDOUR::DataType::begin(); i != ARDOUR::DataType::end(); ++i) {
+               _global_port_matrix[*i] = new ActionWindowProxy<GlobalPortMatrixWindow> (
+                       string_compose ("GlobalPortMatrix-%1", (*i).to_string()),
+                       Config->extra_xml (X_("UI")),
+                       string_compose ("toggle-%1-connection-manager", (*i).to_string())
+                       );
+       }
+
+       setup_clock ();
+
        starting.connect (sigc::mem_fun(*this, &ARDOUR_UI::startup));
        stopping.connect (sigc::mem_fun(*this, &ARDOUR_UI::shutdown));
 
@@ -296,12 +317,11 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[])
 bool
 ARDOUR_UI::run_startup (bool should_be_new, string load_template)
 {
-       if (_startup == 0) {
-               _startup = new ArdourStartup ();
-       }
-       
-       XMLNode* audio_setup = Config->extra_xml ("AudioSetup");
+       delete _startup;
+       _startup = new ArdourStartup ();
 
+       XMLNode* audio_setup = Config->extra_xml ("AudioSetup");
+       
        if (audio_setup && _startup->engine_control()) {
                _startup->engine_control()->set_state (*audio_setup);
        }
@@ -349,6 +369,8 @@ ARDOUR_UI::create_engine ()
 
        engine->Halted.connect_same_thread (forever_connections, boost::bind (&ARDOUR_UI::engine_halted, this, _1, false));
 
+        ARDOUR::Port::set_connecting_blocked (ARDOUR_COMMAND_LINE::no_connect_ports);
+
        post_engine ();
 
        return 0;
@@ -360,9 +382,6 @@ ARDOUR_UI::post_engine ()
        /* Things to be done once we create the AudioEngine
         */
 
-       MIDI::Manager::instance()->set_api_data (engine->jack());
-       setup_midi ();
-       
        ARDOUR::init_post_engine ();
 
        ActionManager::init ();
@@ -645,6 +664,11 @@ Please consider the possibilities, and perhaps (re)start JACK."));
 void
 ARDOUR_UI::startup ()
 {
+
+#ifdef PHONE_HOME
+        call_the_mothership (VERSIONSTRING);
+#endif
+
        if (get_session_parameters (true, ARDOUR_COMMAND_LINE::new_session, ARDOUR_COMMAND_LINE::load_template)) {
                exit (1);
        }
@@ -653,8 +677,16 @@ ARDOUR_UI::startup ()
 
        goto_editor_window ();
 
+       /* Add the window proxies here; their addition may cause windows to be opened, and we want them
+          to be opened on top of the editor window that goto_editor_window() just opened.
+       */
+       add_window_proxy (location_ui);
+       add_window_proxy (big_clock_window);
+       for (ARDOUR::DataType::iterator i = ARDOUR::DataType::begin(); i != ARDOUR::DataType::end(); ++i) {
+               add_window_proxy (_global_port_matrix[*i]);
+       }
+       
        BootMessage (string_compose (_("%1 is ready for use"), PROGRAM_NAME));
-       show ();
 }
 
 void
@@ -725,6 +757,19 @@ ARDOUR_UI::check_memory_locking ()
 }
 
 
+void
+ARDOUR_UI::queue_finish ()
+{
+       Glib::signal_idle().connect (mem_fun (*this, &ARDOUR_UI::idle_finish));
+}
+
+bool
+ARDOUR_UI::idle_finish ()
+{
+       finish ();
+       return false; /* do not call again */
+}
+
 void
 ARDOUR_UI::finish()
 {
@@ -764,16 +809,22 @@ If you still wish to quit, please use the\n\n\
                point_one_second_connection.disconnect ();
                point_oh_five_second_connection.disconnect ();
                point_zero_one_second_connection.disconnect();
-               
+       }
+
+       /* Save state before deleting the session, as that causes some
+          windows to be destroyed before their visible state can be
+          saved.
+       */
+       save_ardour_state ();
+
+       if (_session) {
                // _session->set_deletion_in_progress ();
+                _session->set_clean ();
                _session->remove_pending_capture_state ();
                delete _session;
                _session = 0;
        }
 
-        cerr << "Save before quit\n";
-       save_ardour_state ();
-
        ArdourDialog::close_all_dialogs ();
        engine->stop (true);
        quit ();
@@ -1281,10 +1332,10 @@ ARDOUR_UI::session_add_midi_route (bool disk, RouteGroup* route_group, uint32_t
 
        catch (...) {
                MessageDialog msg (*editor,
-                                  _("There are insufficient JACK ports available\n\
+                                  string_compose (_("There are insufficient JACK ports available\n\
 to create a new track or bus.\n\
-You should save Ardour, exit and\n\
-restart JACK with more ports."));
+You should save %1, exit and\n\
+restart JACK with more ports."), PROGRAM_NAME));
                msg.run ();
        }
 }
@@ -1330,10 +1381,10 @@ ARDOUR_UI::session_add_audio_route (bool track, bool aux, int32_t input_channels
 
        catch (...) {
                MessageDialog msg (*editor,
-                                  _("There are insufficient JACK ports available\n\
+                                  string_compose (_("There are insufficient JACK ports available\n\
 to create a new track or bus.\n\
-You should save Ardour, exit and\n\
-restart JACK with more ports."));
+You should save %1, exit and\n\
+restart JACK with more ports."), PROGRAM_NAME));
                pop_back_splash ();
                msg.run ();
        }
@@ -1406,7 +1457,7 @@ ARDOUR_UI::transport_goto_wallclock ()
                frames += tmnow.tm_min * (60 * _session->frame_rate());
                frames += tmnow.tm_sec * _session->frame_rate();
 
-               _session->request_locate (frames);
+               _session->request_locate (frames, _session->transport_rolling ());
 
                /* force displayed area in editor to start no matter
                   what "follow playhead" setting is.
@@ -1554,7 +1605,7 @@ ARDOUR_UI::toggle_roll (bool with_abort, bool roll_out_of_bounded_mode)
                _session->cancel_audition ();
                return;
        }
-       
+
        if (_session->config.get_external_sync()) {
                switch (_session->config.get_sync_source()) {
                case JACK:
@@ -1689,7 +1740,7 @@ ARDOUR_UI::transport_forward (int option)
 }
 
 void
-ARDOUR_UI::toggle_record_enable (uint32_t dstream)
+ARDOUR_UI::toggle_record_enable (uint32_t rid)
 {
        if (_session == 0) {
                return;
@@ -1697,12 +1748,12 @@ ARDOUR_UI::toggle_record_enable (uint32_t dstream)
 
        boost::shared_ptr<Route> r;
 
-       if ((r = _session->route_by_remote_id (dstream)) != 0) {
+       if ((r = _session->route_by_remote_id (rid)) != 0) {
 
                Track* t;
 
                if ((t = dynamic_cast<Track*>(r.get())) != 0) {
-                       t->set_record_enabled (!t->record_enabled());
+                       t->set_record_enabled (!t->record_enabled(), this);
                }
        }
        if (_session == 0) {
@@ -1860,11 +1911,11 @@ ARDOUR_UI::engine_halted (const char* reason, bool free_reason)
        if (strlen (reason)) {
                msgstr = string_compose (_("The audio backend (JACK) was shutdown because:\n\n%1"), reason);
        } else {
-               msgstr = _("\
+               msgstr = string_compose (_("\
 JACK has either been shutdown or it\n\
-disconnected Ardour because Ardour\n\
+disconnected %1 because %1\n\
 was not fast enough. Try to restart\n\
-JACK, reconnect and save the session.");
+JACK, reconnect and save the session."), PROGRAM_NAME);
        }
 
        MessageDialog msg (*editor, msgstr);
@@ -2049,6 +2100,16 @@ ARDOUR_UI::snapshot_session (bool switch_to_it)
 void
 ARDOUR_UI::save_state (const string & name, bool switch_to_it)
 {
+       XMLNode* node = new XMLNode (X_("UI"));
+
+       for (list<WindowProxyBase*>::iterator i = _window_proxies.begin(); i != _window_proxies.end(); ++i) {
+               if (!(*i)->rc_configured()) {
+                       node->add_child_nocopy (*((*i)->get_state ()));
+               }
+       }
+
+       _session->add_extra_xml (*node);
+       
        save_state_canfail (name, switch_to_it);
 }
 
@@ -2102,6 +2163,10 @@ ARDOUR_UI::transport_rec_enable_blink (bool onoff)
                return;
        }
 
+        if (_session->step_editing()) {
+                return;
+        }
+
        Session::RecordState const r = _session->record_status ();
        bool const h = _session->have_rec_enabled_track ();
 
@@ -2178,12 +2243,12 @@ ARDOUR_UI::fontconfig_dialog ()
 
        if (!Glib::file_test (fontconfig, Glib::FILE_TEST_EXISTS|Glib::FILE_TEST_IS_DIR)) {
                MessageDialog msg (*_startup,
-                                  _("Welcome to Ardour.\n\n"
-                                    "The program will take a bit longer to start up\n"
-                                    "while the system fonts are checked.\n\n"
-                                    "This will only be done once, and you will\n"
-                                    "not see this message again\n"),
-                                  true,
+                                  string_compose (_("Welcome to %1.\n\n"
+                                                     "The program will take a bit longer to start up\n"
+                                                     "while the system fonts are checked.\n\n"
+                                                     "This will only be done once, and you will\n"
+                                                     "not see this message again\n"), PROGRAM_NAME),
+                                   true,
                                   Gtk::MESSAGE_INFO,
                                   Gtk::BUTTONS_OK);
                pop_back_splash ();
@@ -2276,7 +2341,7 @@ ARDOUR_UI::ask_about_loading_existing_session (const Glib::ustring& session_path
 
        msg.set_name (X_("OpenExistingDialog"));
        msg.set_title (_("Open Existing Session"));
-       msg.set_wmclass (X_("existing_session"), "Ardour");
+       msg.set_wmclass (X_("existing_session"), PROGRAM_NAME);
        msg.set_position (Gtk::WIN_POS_MOUSE);
        pop_back_splash ();
 
@@ -2540,7 +2605,12 @@ ARDOUR_UI::close_session()
         }
 
        ARDOUR_COMMAND_LINE::session_name = "";
-       get_session_parameters (true, false);
+
+       if (get_session_parameters (true, false)) {
+               exit (1);
+       }
+
+       goto_editor_window ();
 }
 
 int
@@ -2625,6 +2695,15 @@ ARDOUR_UI::load_session (const Glib::ustring& path, const Glib::ustring& snap_na
                goto out;
        }
 
+       /* 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;
@@ -2685,20 +2764,6 @@ ARDOUR_UI::build_session (const Glib::ustring& path, const Glib::ustring& snap_n
        return 0;
 }
 
-void
-ARDOUR_UI::show ()
-{
-       if (editor) {
-               editor->show_window ();
-
-               if (!shown_flag) {
-                       editor->present ();
-               }
-
-               shown_flag = true;
-       }
-}
-
 void
 ARDOUR_UI::launch_chat ()
 {
@@ -2721,6 +2786,18 @@ ARDOUR_UI::show_about ()
        about->show_all ();
 }
 
+void
+ARDOUR_UI::launch_manual ()
+{
+       PBD::open_uri("http://ardour.org/flossmanual");
+}
+
+void
+ARDOUR_UI::launch_reference ()
+{
+       PBD::open_uri("http://ardour.org/refmanual");
+}
+
 void
 ARDOUR_UI::hide_about ()
 {
@@ -2908,7 +2985,7 @@ After cleanup, unused audio files will be moved to a \
        checker.set_default_response (RESPONSE_CANCEL);
 
        checker.set_name (_("CleanupDialog"));
-       checker.set_wmclass (X_("ardour_cleanup"), "Ardour");
+       checker.set_wmclass (X_("ardour_cleanup"), PROGRAM_NAME);
        checker.set_position (Gtk::WIN_POS_MOUSE);
 
        switch (checker.run()) {
@@ -3154,12 +3231,12 @@ ARDOUR_UI::disk_overrun_handler ()
 
        if (!have_disk_speed_dialog_displayed) {
                have_disk_speed_dialog_displayed = true;
-               MessageDialog* msg = new MessageDialog (*editor, _("\
+               MessageDialog* msg = new MessageDialog (*editor, string_compose (_("\
 The disk system on your computer\n\
-was not able to keep up with Ardour.\n\
+was not able to keep up with %1.\n\
 \n\
 Specifically, it failed to write data to disk\n\
-quickly enough to keep up with recording.\n"));
+quickly enough to keep up with recording.\n"), PROGRAM_NAME));
                msg->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &ARDOUR_UI::disk_speed_dialog_gone), msg));
                msg->show ();
        }
@@ -3173,11 +3250,11 @@ ARDOUR_UI::disk_underrun_handler ()
        if (!have_disk_speed_dialog_displayed) {
                have_disk_speed_dialog_displayed = true;
                MessageDialog* msg = new MessageDialog (*editor,
-                                  _("The disk system on your computer\n\
-was not able to keep up with Ardour.\n\
+                                                        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\
-quickly enough to keep up with playback.\n"));
+quickly enough to keep up with playback.\n"), PROGRAM_NAME));
                msg->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &ARDOUR_UI::disk_speed_dialog_gone), msg));
                msg->show ();
        }
@@ -3303,22 +3380,10 @@ ARDOUR_UI::reconnect_to_jack ()
 void
 ARDOUR_UI::use_config ()
 {
-
        XMLNode* node = Config->extra_xml (X_("TransportControllables"));
        if (node) {
                set_transport_controllable_state (*node);
        }
-
-       node = Config->extra_xml (X_("UI"));
-
-        if (node) {
-                const XMLProperty* prop = node->property (X_("show-big-clock"));
-                Glib::RefPtr<Action> act = ActionManager::get_action (X_("Common"), X_("ToggleBigClock"));
-                if (act) {
-                        Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
-                        tact->set_active (string_is_affirmative (prop->value()));
-                }
-        }
 }
 
 void
@@ -3336,17 +3401,33 @@ ARDOUR_UI::update_transport_clocks (nframes_t pos)
                secondary_clock.set (pos);
        }
 
-       if (big_clock_window) {
+       if (big_clock_window->get()) {
                big_clock.set (pos);
        }
 }
 
+
+void
+ARDOUR_UI::step_edit_status_change (bool yn)
+{
+        // XXX should really store pre-step edit status of things
+        // we make insensitive
+
+        if (yn) {
+                rec_button.set_visual_state (3);
+                rec_button.set_sensitive (false);
+        } else {
+                rec_button.set_visual_state (0);
+                rec_button.set_sensitive (true);
+        }
+}
+
 void
 ARDOUR_UI::record_state_changed ()
 {
        ENSURE_GUI_THREAD (*this, &ARDOUR_UI::record_state_changed);
 
-       if (!_session || !big_clock_window) {
+       if (!_session || !big_clock_window->get()) {
                /* why bother - the clock isn't visible */
                return;
        }
@@ -3398,18 +3479,18 @@ ARDOUR_UI::TransportControllable::TransportControllable (std::string name, ARDOU
 }
 
 void
-ARDOUR_UI::TransportControllable::set_value (float val)
+ARDOUR_UI::TransportControllable::set_value (double val)
 {
        if (type == ShuttleControl) {
                double fract;
 
-               if (val == 0.5f) {
+               if (val == 0.5) {
                        fract = 0.0;
                } else {
-                       if (val < 0.5f) {
-                               fract = -((0.5f - val)/0.5f);
+                       if (val < 0.5) {
+                               fract = -((0.5 - val)/0.5);
                        } else {
-                               fract = ((val - 0.5f)/0.5f);
+                               fract = ((val - 0.5)/0.5);
                        }
                }
 
@@ -3417,7 +3498,7 @@ ARDOUR_UI::TransportControllable::set_value (float val)
                return;
        }
 
-       if (val < 0.5f) {
+       if (val < 0.5) {
                /* do nothing: these are radio-style actions */
                return;
        }
@@ -3461,10 +3542,10 @@ ARDOUR_UI::TransportControllable::set_value (float val)
        }
 }
 
-float
+double
 ARDOUR_UI::TransportControllable::get_value (void) const
 {
-       float val = 0.0f;
+       float val = 0.0;
 
        switch (type) {
        case Roll:
@@ -3510,3 +3591,59 @@ ARDOUR_UI::setup_profile ()
        }
 }
 
+void
+ARDOUR_UI::toggle_translations ()
+{
+        using namespace Glib;
+        
+        RefPtr<Action> act = ActionManager::get_action (X_("Main"), X_("EnableTranslation"));
+        if (act) {
+                RefPtr<ToggleAction> ract = RefPtr<ToggleAction>::cast_dynamic (act);
+                if (ract) {
+                        
+                        string i18n_killer = ARDOUR::translation_kill_path();
+                        
+                        bool already_enabled = !ARDOUR::translations_are_disabled ();
+                        
+                        if (ract->get_active ()) {
+/* we don't care about errors */
+                                int fd = ::open (i18n_killer.c_str(), O_RDONLY|O_CREAT, 0644);
+                                close (fd);
+                        } else {
+/* we don't care about errors */
+                                unlink (i18n_killer.c_str());
+                        }
+                        
+                        if (already_enabled != ract->get_active()) {
+                                MessageDialog win (already_enabled ? _("Translations disabled") : _("Translations enabled"),
+                                                   false,
+                                                   Gtk::MESSAGE_WARNING,
+                                                   Gtk::BUTTONS_OK);
+                                win.set_secondary_text (string_compose (_("You must restart %1 for this to take effect."), PROGRAM_NAME));
+                                win.set_position (Gtk::WIN_POS_CENTER);
+                                win.present ();
+                                win.run ();
+                        }
+                }
+        }
+}        
+
+/** Add a window proxy to our list, so that its state will be saved.
+ *  This call also causes the window to be created and opened if its
+ *  state was saved as `visible'.
+ */
+void
+ARDOUR_UI::add_window_proxy (WindowProxyBase* p)
+{
+       _window_proxies.push_back (p);
+       p->maybe_show ();
+}
+
+/** Remove a window proxy from our list.  Must be called if a WindowProxy
+ *  is deleted, to prevent hanging pointers.
+ */
+void
+ARDOUR_UI::remove_window_proxy (WindowProxyBase* p)
+{
+       _window_proxies.remove (p);
+}