When logging XML error messages, make sure we don't accidentally log an empty message
[ardour.git] / gtk2_ardour / ardour_ui.cc
index a40d192e72ded259631b7896a1450927909e9364..ea0586f5cea82be260fc964abe2d40035ab15540 100644 (file)
@@ -71,6 +71,7 @@
 
 #include "ardour/ardour.h"
 #include "ardour/audio_backend.h"
+#include "ardour/audio_track.h"
 #include "ardour/audioengine.h"
 #include "ardour/audiofilesource.h"
 #include "ardour/automation_watch.h"
@@ -78,6 +79,7 @@
 #include "ardour/filename_extensions.h"
 #include "ardour/filesystem_paths.h"
 #include "ardour/ltc_file_reader.h"
+#include "ardour/midi_track.h"
 #include "ardour/port.h"
 #include "ardour/plugin_manager.h"
 #include "ardour/process_thread.h"
 #include "ardour/audio_unit.h"
 #endif
 
+// fix for OSX (nsm.h has a check function, AU/Apple defines check)
+#ifdef check
+#undef check
+#endif
+
 #include "timecode/time.h"
 
 typedef uint64_t microseconds_t;
@@ -126,6 +133,7 @@ typedef uint64_t microseconds_t;
 #include "keyboard.h"
 #include "keyeditor.h"
 #include "location_ui.h"
+#include "lua_script_manager.h"
 #include "luawindow.h"
 #include "main_clock.h"
 #include "missing_file_dialog.h"
@@ -218,14 +226,19 @@ libxml_structured_error_func (void* /* parsing_context*/,
 
        replace_all (msg, "\n", "");
 
-       if (err->file && err->line) {
-               error << X_("XML error: ") << msg << " in " << err->file << " at line " << err->line;
+       if (!msg.empty()) {
+               if (err->file && err->line) {
+                       error << X_("XML error: ") << msg << " in " << err->file << " at line " << err->line;
 
-               if (err->int2) {
-                       error << ':' << err->int2;
+                       if (err->int2) {
+                               error << ':' << err->int2;
+                       }
+
+                       error << endmsg;
+               } else {
+                       error << msg << endmsg;
                }
        }
-       error << endmsg;
 }
 
 
@@ -276,6 +289,7 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir)
        , route_params (X_("inspector"), _("Tracks and Busses"))
        , audio_midi_setup (X_("audio-midi-setup"), _("Audio/MIDI Setup"))
        , export_video_dialog (X_("video-export"), _("Video Export Dialog"))
+       , lua_script_window (X_("script-manager"), _("Script Manager"))
        , session_option_editor (X_("session-options-editor"), _("Properties"), boost::bind (&ARDOUR_UI::create_session_option_editor, 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))
@@ -359,6 +373,9 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir)
 
        ARDOUR::Session::AskAboutSampleRateMismatch.connect_same_thread (forever_connections, boost::bind (&ARDOUR_UI::sr_mismatch_dialog, this, _1, _2));
 
+       /* handle sr mismatch with a dialog - cross-thread from engine */
+       ARDOUR::Session::NotifyAboutSampleRateMismatch.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::sr_mismatch_message, this, _1, _2), gui_context ());
+
        /* handle requests to quit (coming from JACK session) */
 
        ARDOUR::Session::Quit.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::finish, this), gui_context ());
@@ -427,6 +444,7 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir)
                audio_port_matrix.set_state (*ui_xml, 0);
                midi_port_matrix.set_state (*ui_xml, 0);
                export_video_dialog.set_state (*ui_xml, 0);
+               lua_script_window.set_state (*ui_xml, 0);
        }
 
        /* Separate windows */
@@ -440,6 +458,7 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir)
        WM::Manager::instance().register_window (&route_params);
        WM::Manager::instance().register_window (&audio_midi_setup);
        WM::Manager::instance().register_window (&export_video_dialog);
+       WM::Manager::instance().register_window (&lua_script_window);
        WM::Manager::instance().register_window (&bundle_manager);
        WM::Manager::instance().register_window (&location_ui);
        WM::Manager::instance().register_window (&big_clock_window);
@@ -654,16 +673,16 @@ ARDOUR_UI::~ARDOUR_UI ()
 
        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 meterbridge;
-               delete luawindow;
-               delete editor;
-               delete mixer;
-               delete nsm;
-               delete gui_object_state;
+               delete big_clock; big_clock = 0;
+               delete primary_clock; primary_clock = 0;
+               delete secondary_clock; secondary_clock = 0;
+               delete _process_thread; _process_thread = 0;
+               delete meterbridge; meterbridge = 0;
+               delete luawindow; luawindow = 0;
+               delete editor; editor = 0;
+               delete mixer; mixer = 0;
+               delete nsm; nsm = 0;
+               delete gui_object_state; gui_object_state = 0;
                FastMeter::flush_pattern_cache ();
                PixFader::flush_pattern_cache ();
        }
@@ -1767,10 +1786,15 @@ ARDOUR_UI::open_session ()
        }
 }
 
-
 void
-ARDOUR_UI::session_add_mixed_track (const ChanCount& input, const ChanCount& output, RouteGroup* route_group,
-                                   uint32_t how_many, const string& name_template, PluginInfoPtr instrument)
+ARDOUR_UI::session_add_mixed_track (
+               const ChanCount& input,
+               const ChanCount& output,
+               RouteGroup* route_group,
+               uint32_t how_many,
+               const string& name_template,
+               bool strict_io,
+               PluginInfoPtr instrument)
 {
        list<boost::shared_ptr<MidiTrack> > tracks;
 
@@ -1788,24 +1812,67 @@ ARDOUR_UI::session_add_mixed_track (const ChanCount& input, const ChanCount& out
        }
 
        catch (...) {
-               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\
-restart with more ports."), PROGRAM_NAME));
-               msg.run ();
+               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
+ARDOUR_UI::session_add_midi_bus (
+               RouteGroup* route_group,
+               uint32_t how_many,
+               const string& name_template,
+               bool strict_io,
+               PluginInfoPtr instrument)
+{
+       RouteList routes;
+
+       if (_session == 0) {
+               warning << _("You cannot add a track without a session already loaded.") << endmsg;
+               return;
+       }
+
+       try {
+               routes = _session->new_midi_route (route_group, how_many, name_template, instrument);
+               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;
+               }
+
+       }
+       catch (...) {
+               display_insufficient_ports_message ();
+               return;
+       }
+
+       if (strict_io) {
+               for (RouteList::iterator i = routes.begin(); i != routes.end(); ++i) {
+                       (*i)->set_strict_io (true);
+               }
+       }
+}
 
 void
-ARDOUR_UI::session_add_midi_route (bool disk, RouteGroup* route_group, uint32_t how_many, const string& name_template, PluginInfoPtr instrument)
+ARDOUR_UI::session_add_midi_route (
+               bool disk,
+               RouteGroup* route_group,
+               uint32_t how_many,
+               const string& name_template,
+               bool strict_io,
+               PluginInfoPtr instrument)
 {
        ChanCount one_midi_channel;
        one_midi_channel.set (DataType::MIDI, 1);
 
        if (disk) {
-               session_add_mixed_track (one_midi_channel, one_midi_channel, route_group, how_many, name_template, instrument);
+               session_add_mixed_track (one_midi_channel, one_midi_channel, route_group, how_many, name_template, strict_io, instrument);
+       } else {
+               session_add_midi_bus (route_group, how_many, name_template, strict_io, instrument);
        }
 }
 
@@ -1817,7 +1884,8 @@ ARDOUR_UI::session_add_audio_route (
        ARDOUR::TrackMode mode,
        RouteGroup* route_group,
        uint32_t how_many,
-       string const & name_template
+       string const & name_template,
+       bool strict_io
        )
 {
        list<boost::shared_ptr<AudioTrack> > tracks;
@@ -1849,14 +1917,30 @@ ARDOUR_UI::session_add_audio_route (
        }
 
        catch (...) {
-               MessageDialog msg (_main_window,
-                                  string_compose (_("There are insufficient ports available\n\
+               display_insufficient_ports_message ();
+               return;
+       }
+
+       if (strict_io) {
+               for (list<boost::shared_ptr<AudioTrack> >::iterator i = tracks.begin(); i != tracks.end(); ++i) {
+                       (*i)->set_strict_io (true);
+               }
+               for (RouteList::iterator i = routes.begin(); i != routes.end(); ++i) {
+                       (*i)->set_strict_io (true);
+               }
+       }
+}
+
+void
+ARDOUR_UI::display_insufficient_ports_message ()
+{
+       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\
 restart with more ports."), PROGRAM_NAME));
-               pop_back_splash (msg);
-               msg.run ();
-       }
+       pop_back_splash (msg);
+       msg.run ();
 }
 
 void
@@ -3888,7 +3972,6 @@ ARDOUR_UI::add_route (Gtk::Window* /* ignored */)
        }
 
        setup_order_hint(add_route_dialog->insert_at());
-
        string template_path = add_route_dialog->track_template();
        DisplaySuspender ds;
 
@@ -3907,6 +3990,7 @@ ARDOUR_UI::add_route (Gtk::Window* /* ignored */)
        PluginInfoPtr instrument = add_route_dialog->requested_instrument ();
        RouteGroup* route_group = add_route_dialog->route_group ();
        AutoConnectOption oac = Config->get_output_auto_connect();
+       bool strict_io = add_route_dialog->use_strict_io ();
 
        if (oac & AutoConnectMaster) {
                output_chan.set (DataType::AUDIO, (_session->master_out() ? _session->master_out()->n_inputs().n_audio() : input_chan.n_audio()));
@@ -3919,16 +4003,19 @@ ARDOUR_UI::add_route (Gtk::Window* /* ignored */)
 
        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);
+               session_add_audio_track (input_chan.n_audio(), output_chan.n_audio(), add_route_dialog->mode(), route_group, count, name_template, strict_io);
                break;
        case AddRouteDialog::MidiTrack:
-               session_add_midi_track (route_group, count, name_template, instrument);
+               session_add_midi_track (route_group, count, name_template, strict_io, instrument);
                break;
        case AddRouteDialog::MixedTrack:
-               session_add_mixed_track (input_chan, output_chan, route_group, count, name_template, instrument);
+               session_add_mixed_track (input_chan, output_chan, route_group, count, name_template, strict_io, instrument);
                break;
        case AddRouteDialog::AudioBus:
-               session_add_audio_bus (input_chan.n_audio(), output_chan.n_audio(), route_group, count, name_template);
+               session_add_audio_bus (input_chan.n_audio(), output_chan.n_audio(), route_group, count, name_template, strict_io);
+               break;
+       case AddRouteDialog::MidiBus:
+               session_add_midi_bus (route_group, count, name_template, strict_io, instrument);
                break;
        }
 }
@@ -3949,6 +4036,7 @@ ARDOUR_UI::add_lua_script ()
                default:
                        return;
        }
+       ss.hide();
 
        std::string script = "";
 
@@ -3961,7 +4049,7 @@ ARDOUR_UI::add_lua_script ()
                return;
        }
 
-       LuaScriptParamList lsp = LuaScripting::session_script_params (spi);
+       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);
@@ -4728,6 +4816,21 @@ audio may be played at the wrong sample rate.\n"), desired, PROGRAM_NAME, actual
         return 1;
 }
 
+void
+ARDOUR_UI::sr_mismatch_message (framecnt_t desired, framecnt_t actual)
+{
+       MessageDialog msg (string_compose (_("\
+This session was created with a sample rate of %1 Hz, but\n\
+%2 is currently running at %3 Hz.\n\
+Audio will be recorded and played at the wrong sample rate.\n\
+Re-Configure the Audio Engine in\n\
+Menu > Window > Audio/Midi Setup"),
+                               desired, PROGRAM_NAME, actual),
+                       true,
+                       Gtk::MESSAGE_WARNING);
+       msg.run ();
+}
+
 void
 ARDOUR_UI::use_config ()
 {
@@ -5042,10 +5145,15 @@ ARDOUR_UI::do_audio_midi_setup (uint32_t desired_sample_rate)
        audio_midi_setup->set_desired_sample_rate (desired_sample_rate);
        audio_midi_setup->set_position (WIN_POS_CENTER);
 
-       int response;
+       if (Config->get_try_autostart_engine () || getenv ("TRY_AUTOSTART_ENGINE")) {
+               audio_midi_setup->try_autostart ();
+               if (ARDOUR::AudioEngine::instance()->running()) {
+                       return 0;
+               }
+       }
 
        while (true) {
-               response = audio_midi_setup->run();
+               int response = audio_midi_setup->run();
                switch (response) {
                case Gtk::RESPONSE_OK:
                        if (!AudioEngine::instance()->running()) {
@@ -5274,6 +5382,21 @@ ARDOUR_UI::key_event_handler (GdkEventKey* ev, Gtk::Window* event_window)
        return key_press_focus_accelerator_handler (*window, ev, bindings);
 }
 
+static Gtkmm2ext::Bindings*
+get_bindings_from_widget_heirarchy (GtkWidget* w)
+{
+       void* p;
+
+       while (w) {
+               if ((p = g_object_get_data (G_OBJECT(w), "ardour-bindings")) != 0) {
+                       break;
+               }
+               w = gtk_widget_get_parent (w);
+       }
+
+       return reinterpret_cast<Gtkmm2ext::Bindings*> (p);
+}
+
 bool
 ARDOUR_UI::key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev, Gtkmm2ext::Bindings* bindings)
 {
@@ -5294,10 +5417,18 @@ ARDOUR_UI::key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey
                         */
 
                        special_handling_of_unmodified_accelerators = true;
+
+               } else {
+
+                       Gtkmm2ext::Bindings* focus_bindings = get_bindings_from_widget_heirarchy (focus);
+                       if (focus_bindings) {
+                               bindings = focus_bindings;
+                               DEBUG_TRACE (DEBUG::Accelerators, string_compose ("Switch bindings based on focus widget, now using %1\n", bindings->name()));
+                       }
                }
        }
 
-        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 mods ? %8\n",
+        DEBUG_TRACE (DEBUG::Accelerators, string_compose ("Win = %1 [title = %9] focus = %7 (%8) Key event: code = %2  state = %3 special handling ? %4 magic widget focus ? %5 focus widget %6 named %7 mods ? %8\n",
                                                           win,
                                                           ev->keyval,
                                                          show_gdk_event_state (ev->state),
@@ -5305,7 +5436,8 @@ ARDOUR_UI::key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey
                                                           Keyboard::some_magic_widget_has_focus(),
                                                          focus,
                                                           (focus ? gtk_widget_get_name (focus) : "no focus widget"),
-                                                          ((ev->state & mask) ? "yes" : "no")));
+                                                          ((ev->state & mask) ? "yes" : "no"),
+                                                          window.get_title()));
 
        /* This exists to allow us to override the way GTK handles
           key events. The normal sequence is:
@@ -5352,7 +5484,7 @@ ARDOUR_UI::key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey
 
                if (bindings) {
 
-                       DEBUG_TRACE (DEBUG::Accelerators, string_compose ("\tusing Ardour bindings %1 for this event\n", bindings));
+                       DEBUG_TRACE (DEBUG::Accelerators, string_compose ("\tusing Ardour bindings %1 @ %2 for this event\n", bindings->name(), bindings));
 
                        if (bindings->activate (k, Bindings::Press)) {
                                DEBUG_TRACE (DEBUG::Accelerators, "\t\thandled\n");
@@ -5415,7 +5547,7 @@ ARDOUR_UI::key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey
 void
 ARDOUR_UI::load_bindings ()
 {
-       if ((global_bindings = Bindings::get_bindings ("global", global_actions)) == 0) {
+       if ((global_bindings = Bindings::get_bindings (X_("Global"), global_actions)) == 0) {
                error << _("Global keybindings are missing") << endmsg;
        }
 }