Remove internal edit mode and add "content" tool.
[ardour.git] / gtk2_ardour / ardour_ui.cc
index 73982bb436ef17440a2d510b88a6f8b7b3ce58b8..3bfb204df49f1695ad07a0462ebeac21556ea612 100644 (file)
@@ -19,6 +19,7 @@
 
 #ifdef WAF_BUILD
 #include "gtk2ardour-config.h"
+#include "gtk2ardour-version.h"
 #endif
 
 #include <algorithm>
@@ -87,6 +88,9 @@
 #ifdef WINDOWS_VST_SUPPORT
 #include <fst.h>
 #endif
+#ifdef AUDIOUNIT_SUPPORT
+#include "ardour/audio_unit.h"
+#endif
 
 #include "timecode/time.h"
 
@@ -146,18 +150,18 @@ using namespace Gtk;
 using namespace std;
 
 ARDOUR_UI *ARDOUR_UI::theArdourUI = 0;
-UIConfiguration *ARDOUR_UI::ui_config = 0;
 
 sigc::signal<void,bool> ARDOUR_UI::Blink;
 sigc::signal<void>      ARDOUR_UI::RapidScreenUpdate;
 sigc::signal<void>      ARDOUR_UI::SuperRapidScreenUpdate;
+sigc::signal<void>      ARDOUR_UI::FPSUpdate;
 sigc::signal<void, framepos_t, bool, framepos_t> ARDOUR_UI::Clock;
 sigc::signal<void>      ARDOUR_UI::CloseAllDialogs;
 
 ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir)
 
        : Gtkmm2ext::UI (PROGRAM_NAME, argcp, argvp)
-       
+       , ui_config (new UIConfiguration)
        , gui_object_state (new GUIObjectState)
 
        , primary_clock (new MainClock (X_("primary"), false, X_("transport"), true, true, true, false, true))
@@ -198,7 +202,6 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir)
        , editor_meter_peak_display()
 
        , speaker_config_window (X_("speaker-config"), _("Speaker Configuration"))
-       , theme_manager (X_("theme-manager"), _("Theme Manager"))
        , key_editor (X_("key-editor"), _("Key Bindings"))
        , rc_option_editor (X_("rc-options-editor"), _("Preferences"))
        , add_route_dialog (X_("add-routes"), _("Add Tracks/Busses"))
@@ -228,8 +231,6 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir)
                theArdourUI = this;
        }
 
-       ui_config = new UIConfiguration();
-
        ui_config->ParameterChanged.connect (sigc::mem_fun (*this, &ARDOUR_UI::parameter_changed));
        boost::function<void (string)> pc (boost::bind (&ARDOUR_UI::parameter_changed, this, _1));
        ui_config->map_parameters (pc);
@@ -312,6 +313,9 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir)
 
        ARDOUR::GUIIdle.connect (forever_connections, MISSING_INVALIDATOR, boost::bind(&ARDOUR_UI::gui_idle_handler, this), gui_context());
 
+       Config->ParameterChanged.connect ( forever_connections, MISSING_INVALIDATOR, boost::bind(&ARDOUR_UI::set_flat_buttons, this), gui_context() );
+       set_flat_buttons();
+
        /* lets get this party started */
 
        setup_gtk_ardour_enums ();
@@ -344,7 +348,6 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir)
        const XMLNode* ui_xml = Config->extra_xml (X_("UI"));
 
        if (ui_xml) {
-               theme_manager.set_state (*ui_xml);
                key_editor.set_state (*ui_xml);
                rc_option_editor.set_state (*ui_xml);
                session_option_editor.set_state (*ui_xml);
@@ -360,7 +363,6 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir)
                midi_port_matrix.set_state (*ui_xml);
        }
 
-       WM::Manager::instance().register_window (&theme_manager);
        WM::Manager::instance().register_window (&key_editor);
        WM::Manager::instance().register_window (&rc_option_editor);
        WM::Manager::instance().register_window (&session_option_editor);
@@ -376,12 +378,9 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir)
        WM::Manager::instance().register_window (&audio_port_matrix);
        WM::Manager::instance().register_window (&midi_port_matrix);
 
-       /* We need to instantiate the theme manager because it loads our
-          theme files. This should really change so that its window
-          and its functionality are separate 
-       */
-       
-       (void) theme_manager.get (true);
+       /* Trigger setting up the color scheme and loading the GTK RC file */
+
+       ARDOUR_UI::config()->load_rc_file (false);
        
        _process_thread = new ProcessThread ();
        _process_thread->init ();
@@ -478,6 +477,14 @@ ARDOUR_UI::post_engine ()
 {
        /* Things to be done once (and once ONLY) after we have a backend running in the AudioEngine
         */
+#ifdef AUDIOUNIT_SUPPORT
+       std::string au_msg;
+       if (AUPluginInfo::au_get_crashlog(au_msg)) {
+               popup_error(_("Audio Unit Plugin Scan Failed. Automatic AU scanning has been disabled. Please see the log window for further details."));
+               error << _("Audio Unit Plugin Scan Failed:") << endmsg;
+               info << au_msg << endmsg;
+       }
+#endif
 
        ARDOUR::init_post_engine ();
        
@@ -549,18 +556,29 @@ ARDOUR_UI::post_engine ()
        Glib::signal_timeout().connect_seconds (sigc::mem_fun(*this, &ARDOUR_UI::update_wall_clock), 1);
 #endif
 
-       Config->ParameterChanged.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::parameter_changed, this, _1), gui_context());
-       boost::function<void (string)> pc (boost::bind (&ARDOUR_UI::parameter_changed, this, _1));
-       Config->map_parameters (pc);
+       {
+               DisplaySuspender ds;
+               Config->ParameterChanged.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::parameter_changed, this, _1), gui_context());
+               boost::function<void (string)> pc (boost::bind (&ARDOUR_UI::parameter_changed, this, _1));
+               Config->map_parameters (pc);
+       }
 }
 
 ARDOUR_UI::~ARDOUR_UI ()
 {
-       if (ui_config->dirty()) {
-               ui_config->save_state();
-       }
+       ui_config->save_state();
 
        stop_video_server();
+
+       if (getenv ("ARDOUR_RUNNING_UNDER_VALGRIND")) {
+               // don't bother at 'real' exit. the OS cleans up for us.
+               delete big_clock;
+               delete primary_clock;
+               delete secondary_clock;
+               delete _process_thread;
+               delete gui_object_state;
+               FastMeter::flush_pattern_cache ();
+       }
 }
 
 void
@@ -988,7 +1006,10 @@ If you still wish to quit, please use the\n\n\
 
                second_connection.disconnect ();
                point_one_second_connection.disconnect ();
+#ifndef PLATFORM_WINDOWS
                point_zero_something_second_connection.disconnect();
+#endif
+               fps_connection.disconnect();
        }
 
        delete ARDOUR_UI::instance()->video_timeline;
@@ -1136,6 +1157,48 @@ ARDOUR_UI::every_point_zero_something_seconds ()
        return TRUE;
 }
 
+gint
+ARDOUR_UI::every_fps ()
+{
+       FPSUpdate(); /* EMIT_SIGNAL */
+#ifdef PLATFORM_WINDOWS
+       every_point_zero_something_seconds();
+#endif
+       return TRUE;
+}
+
+void
+ARDOUR_UI::set_fps_timeout_connection ()
+{
+       unsigned int interval = 40;
+       if (!_session) return;
+       if (_session->timecode_frames_per_second() != 0) {
+               /* ideally we'll use a select() to sleep and not accumulate
+                * idle time to provide a regular periodic signal.
+                * See linux_vst_gui_support.cc 'elapsed_time_ms'.
+                * However, that'll require a dedicated thread and cross-thread
+                * signals to the GUI Thread..
+                */
+               interval = floor(500. /* update twice per FPS, since Glib::signal_timeout is very irregular */
+                               * _session->frame_rate() / _session->nominal_frame_rate()
+                               / _session->timecode_frames_per_second()
+                               );
+#ifdef PLATFORM_WINDOWS
+               // the smallest windows scheduler time-slice is ~15ms.
+               // periodic GUI timeouts shorter than that will cause
+               // WaitForSingleObject to spinlock (100% of one CPU Core)
+               // and gtk never enters idle mode.
+               // also changing timeBeginPeriod(1) does not affect that in
+               // any beneficial way, so we just limit the max rate for now.
+               interval = std::max(30u, interval); // at most ~33Hz.
+#else
+               interval = std::max(8u, interval); // at most 120Hz.
+#endif
+       }
+       fps_connection.disconnect();
+       fps_connection = Glib::signal_timeout().connect (sigc::mem_fun(*this, &ARDOUR_UI::every_fps), interval);
+}
+
 void
 ARDOUR_UI::update_sample_rate (framecnt_t)
 {
@@ -1233,7 +1296,7 @@ ARDOUR_UI::update_cpu_load ()
           should also be changed.
        */
 
-       float const c = AudioEngine::instance()->get_dsp_load ();
+       double const c = AudioEngine::instance()->get_dsp_load ();
        snprintf (buf, sizeof (buf), _("DSP: <span foreground=\"%s\">%5.1f%%</span>"), c >= 90 ? X_("red") : X_("green"), c);
        cpu_load_label.set_markup (buf);
 }
@@ -2206,7 +2269,7 @@ ARDOUR_UI::map_transport_state ()
 void
 ARDOUR_UI::update_clocks ()
 {
-       if (!editor || !editor->dragging_playhead()) {
+       if (editor && !editor->dragging_playhead()) {
                Clock (_session->audible_frame(), false, editor->get_preferred_edit_position()); /* EMIT_SIGNAL */
        }
 }
@@ -2215,7 +2278,7 @@ void
 ARDOUR_UI::start_clocking ()
 {
        if (Config->get_super_rapid_clock_update()) {
-               clock_signal_connection = SuperRapidScreenUpdate.connect (sigc::mem_fun(*this, &ARDOUR_UI::update_clocks));
+               clock_signal_connection = FPSUpdate.connect (sigc::mem_fun(*this, &ARDOUR_UI::update_clocks));
        } else {
                clock_signal_connection = RapidScreenUpdate.connect (sigc::mem_fun(*this, &ARDOUR_UI::update_clocks));
        }
@@ -2759,8 +2822,14 @@ ARDOUR_UI::get_session_parameters (bool quit_on_cancel, bool should_be_new, stri
                }
                
                if (session_name[0] == G_DIR_SEPARATOR ||
+#ifdef PLATFORM_WINDOWS
+                   (session_name.length() > 3 && session_name[1] == ':' && session_name[2] == G_DIR_SEPARATOR)
+#else
                    (session_name.length() > 2 && session_name[0] == '.' && session_name[1] == G_DIR_SEPARATOR) ||
-                   (session_name.length() > 3 && session_name[0] == '.' && session_name[1] == '.' && session_name[2] == G_DIR_SEPARATOR)) {
+                   (session_name.length() > 3 && session_name[0] == '.' && session_name[1] == '.' && session_name[2] == G_DIR_SEPARATOR)
+#endif
+                        )
+               {
                        
                        /* absolute path or cwd-relative path specified for session name: infer session folder
                           from what was given.
@@ -3071,6 +3140,8 @@ ARDOUR_UI::launch_chat ()
 {
 #ifdef __APPLE__
        open_uri("http://webchat.freenode.net/?channels=ardour-osx");
+#elif defined PLATFORM_WINDOWS
+       open_uri("http://webchat.freenode.net/?channels=ardour-windows");
 #else
        open_uri("http://webchat.freenode.net/?channels=ardour");
 #endif
@@ -3088,6 +3159,46 @@ ARDOUR_UI::launch_reference ()
        PBD::open_uri (Config->get_reference_manual_url());
 }
 
+void
+ARDOUR_UI::launch_tracker ()
+{
+       PBD::open_uri ("http://tracker.ardour.org/bug_report_page.php");
+}
+
+void
+ARDOUR_UI::launch_cheat_sheet ()
+{
+#ifdef __APPLE__
+       PBD::open_uri ("http://manual.ardour.org/files/a3_mnemonic_cheat_sheet_osx.pdf");
+#else
+       PBD::open_uri ("http://manual.ardour.org/files/a3_mnemonic_cheatsheet.pdf");
+#endif
+}
+
+void
+ARDOUR_UI::launch_website ()
+{
+       PBD::open_uri ("http://ardour.org");
+}
+
+void
+ARDOUR_UI::launch_website_dev ()
+{
+       PBD::open_uri ("http://ardour.org/development.html");
+}
+
+void
+ARDOUR_UI::launch_forums ()
+{
+       PBD::open_uri ("https://community.ardour.org/forums");
+}
+
+void
+ARDOUR_UI::launch_howto_report ()
+{
+       PBD::open_uri ("http://ardour.org/reporting_bugs");
+}
+
 void
 ARDOUR_UI::loading_message (const std::string& msg)
 {
@@ -3542,18 +3653,18 @@ ARDOUR_UI::start_video_server (Gtk::Window* float_window, bool popup_msg)
                if (icsd_docroot.empty()) {icsd_docroot = X_("/");}
 
                GStatBuf sb;
-               if (!g_lstat (icsd_docroot.c_str(), &sb) == 0 || !S_ISDIR(sb.st_mode)) {
+               if (g_lstat (icsd_docroot.c_str(), &sb) != 0 || !S_ISDIR(sb.st_mode)) {
                        warning << _("Specified docroot is not an existing directory.") << endmsg;
                        continue;
                }
 #ifndef PLATFORM_WINDOWS
-               if ( (!g_lstat (icsd_exec.c_str(), &sb) == 0)
+               if ( (g_lstat (icsd_exec.c_str(), &sb) != 0)
                     || (sb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) == 0 ) {
                        warning << _("Given Video Server is not an executable file.") << endmsg;
                        continue;
                }
 #else
-               if ( (!g_lstat (icsd_exec.c_str(), &sb) == 0)
+               if ( (g_lstat (icsd_exec.c_str(), &sb) != 0)
                     || (sb.st_mode & (S_IXUSR)) == 0 ) {
                        warning << _("Given Video Server is not an executable file.") << endmsg;
                        continue;
@@ -3820,8 +3931,8 @@ ARDOUR_UI::create_xrun_marker (framepos_t where)
 void
 ARDOUR_UI::halt_on_xrun_message ()
 {
-       MessageDialog msg (*editor,
-                          _("Recording was stopped because your system could not keep up."));
+        cerr << "HALT on xrun\n";
+       MessageDialog msg (*editor, _("Recording was stopped because your system could not keep up."));
        msg.run ();
 }
 
@@ -3947,6 +4058,8 @@ ARDOUR_UI::plugin_scan_dialog (std::string type, std::string plugin, bool can_ca
                scan_dlg->get_vbox()->pack_start (*scan_tbox, PACK_SHRINK, 4);
        }
 
+       assert(scan_dlg && scan_tbox && cancel_button);
+
        if (type == X_("closeme")) {
                scan_dlg->hide();
        } else {
@@ -4484,12 +4597,18 @@ ARDOUR_UI::transport_numpad_event (int num)
                        case 1:  transport_rewind(1);                           break;
                        case 2:  transport_forward(1);                          break;
                        case 3:  transport_record(true);                        break;
-                       case 4:  if (_session) _session->request_play_loop(true);                                       break;
-                       case 5:  if (_session) _session->request_play_loop(true); transport_record(false);      break;
-                       case 6:  toggle_punch();                                                break;
+                       case 4:  toggle_session_auto_loop();            break;
+                       case 5:  transport_record(false); toggle_session_auto_loop();   break;
+                       case 6:  toggle_punch();                                        break;
                        case 7:  toggle_click();                                break;
                        case 8:  toggle_auto_return();                  break;
                        case 9:  toggle_follow_edits();         break;
                }
        }
 }
+
+void
+ARDOUR_UI::set_flat_buttons ()
+{
+       CairoWidget::set_flat_buttons( config()->get_flat_buttons() );
+}