X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=gtk2_ardour%2Fardour_ui.cc;h=524d16e017c002e8191ba0ec0959a74028b54f28;hb=5e7d9d30e28e8022661497244ccd43a1f5a836c6;hp=d4f696c3ee5ea2b2d42b7b6df025595c8a78b217;hpb=3be99984b3175090dbce24e38be719d6e6358b6f;p=ardour.git diff --git a/gtk2_ardour/ardour_ui.cc b/gtk2_ardour/ardour_ui.cc index d4f696c3ee..524d16e017 100644 --- a/gtk2_ardour/ardour_ui.cc +++ b/gtk2_ardour/ardour_ui.cc @@ -28,6 +28,8 @@ #include #include +#include + #ifndef PLATFORM_WINDOWS #include #endif @@ -55,6 +57,8 @@ #include "pbd/file_utils.h" #include "pbd/localtime_r.h" #include "pbd/pthread_utils.h" +#include "pbd/replace_all.h" +#include "pbd/xml++.h" #include "gtkmm2ext/application.h" #include "gtkmm2ext/bindings.h" @@ -97,6 +101,7 @@ typedef uint64_t microseconds_t; #include "about.h" +#include "editing.h" #include "actions.h" #include "add_route_dialog.h" #include "ambiguous_file_dialog.h" @@ -128,6 +133,7 @@ typedef uint64_t microseconds_t; #include "rc_option_editor.h" #include "route_time_axis.h" #include "route_params_ui.h" +#include "save_as_dialog.h" #include "session_dialog.h" #include "session_metadata_dialog.h" #include "session_option_editor.h" @@ -151,51 +157,81 @@ using namespace PBD; using namespace Gtkmm2ext; using namespace Gtk; using namespace std; +using namespace Editing; ARDOUR_UI *ARDOUR_UI::theArdourUI = 0; sigc::signal ARDOUR_UI::Clock; sigc::signal ARDOUR_UI::CloseAllDialogs; +float ARDOUR_UI::ui_scale = 1.0; static bool ask_about_configuration_copy (string const & old_dir, string const & new_dir, int version) { MessageDialog msg (string_compose (_("%1 %2.x has discovered configuration files from %1 %3.x.\n\n" - "Would you like to copy the relevant files before starting to use the program?\n\n" + "Would you like these files to be copied and used for %1 %2.x?\n\n" "(This will require you to restart %1.)"), - PROGRAM_NAME, PROGRAM_VERSION, version), true); - - msg.add_button (Gtk::Stock::NO, Gtk::RESPONSE_NO); + PROGRAM_NAME, PROGRAM_VERSION, version), + false, /* no markup */ + Gtk::MESSAGE_INFO, + Gtk::BUTTONS_YES_NO, + true /* modal, though it hardly matters since it is the only window */ + ); + + msg.set_default_response (Gtk::RESPONSE_YES); msg.show_all (); - return (msg.run() == Gtk::RESPONSE_OK); + return (msg.run() == Gtk::RESPONSE_YES); } -ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir) +static void +libxml_generic_error_func (void* /* parsing_context*/, + const char* msg, + ...) +{ + va_list ap; + char buf[2048]; - : Gtkmm2ext::UI (PROGRAM_NAME, argcp, argvp) - , ui_config (new UIConfiguration) - , gui_object_state (new GUIObjectState) + va_start (ap, msg); + vsnprintf (buf, sizeof (buf), msg, ap); + error << buf << endmsg; + va_end (ap); +} + +static void +libxml_structured_error_func (void* /* parsing_context*/, + xmlErrorPtr err) +{ + string msg = err->message; - , primary_clock (new MainClock (X_("primary"), false, X_("transport"), true, true, true, false, true)) - , secondary_clock (new MainClock (X_("secondary"), false, X_("secondary"), true, true, false, false, true)) - - /* big clock */ + replace_all (msg, "\n", ""); + error << X_("XML error: ") << msg << " in " << err->file << " at line " << err->line; + if (err->int2) { + error << ':' << err->int2; + } + error << endmsg; +} + + +ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir, UIConfiguration* uic) + + : Gtkmm2ext::UI (PROGRAM_NAME, argcp, argvp) + , ui_config (uic->post_gui_init ()) + , session_loaded (false) + , gui_object_state (new GUIObjectState) + , primary_clock (new MainClock (X_("primary"), X_("transport"), true )) + , secondary_clock (new MainClock (X_("secondary"), X_("secondary"), false)) , big_clock (new AudioClock (X_("bigclock"), false, "big", true, true, false, false)) , video_timeline(0) - - /* start of private members */ + , ignore_dual_punch (false) , editor (0) , mixer (0) , nsm (0) , _was_dirty (false) , _mixer_on_top (false) , first_time_engine_run (true) - - /* transport */ - , 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)) @@ -203,19 +239,22 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir) , 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)) - , 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")) , error_alert_button ( ArdourButton::just_led_default_elements ) - , editor_meter(0) , editor_meter_peak_display() - + , session_selector_window (0) + , open_session_selector (0) + , _numpad_locate_happening (false) + , _session_is_new (false) + , last_key_press_time (0) + , save_as_dialog (0) + , meterbridge (0) , speaker_config_window (X_("speaker-config"), _("Speaker Configuration")) , key_editor (X_("key-editor"), _("Key Bindings")) , rc_option_editor (X_("rc-options-editor"), _("Preferences")) @@ -231,12 +270,17 @@ 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)) - + , video_server_process (0) + , splash (0) + , have_configure_timeout (false) + , last_configure_time (0) + , last_peak_grab (0) + , have_disk_speed_dialog_displayed (false) , _status_bar_visibility (X_("status-bar")) , _feedback_exists (false) , _log_not_acknowledged (LogLevelNone) { - Gtkmm2ext::init(localedir); + Gtkmm2ext::init (localedir); if (ARDOUR::handle_old_configuration_files (boost::bind (ask_about_configuration_copy, _1, _2, _3))) { MessageDialog msg (string_compose (_("Your configuration files were copied. You can now restart %1."), PROGRAM_NAME), true); @@ -244,34 +288,20 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir) /* configuration was modified, exit immediately */ _exit (0); } - - splash = 0; - - _numpad_locate_happening = false; - if (theArdourUI == 0) { theArdourUI = this; } + /* stop libxml from spewing to stdout/stderr */ + + xmlSetGenericErrorFunc (this, libxml_generic_error_func); + xmlSetStructuredErrorFunc (this, libxml_structured_error_func); + ui_config->ParameterChanged.connect (sigc::mem_fun (*this, &ARDOUR_UI::parameter_changed)); boost::function pc (boost::bind (&ARDOUR_UI::parameter_changed, this, _1)); ui_config->map_parameters (pc); - editor = 0; - mixer = 0; - meterbridge = 0; - editor = 0; - _session_is_new = false; - session_selector_window = 0; - last_key_press_time = 0; - video_server_process = 0; - open_session_selector = 0; - have_configure_timeout = false; - have_disk_speed_dialog_displayed = false; - session_loaded = false; - ignore_dual_punch = false; - roll_button.set_controllable (roll_controllable); stop_button.set_controllable (stop_controllable); goto_start_button.set_controllable (goto_start_controllable); @@ -289,9 +319,6 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir) rec_button.set_name ("transport recenable button"); midi_panic_button.set_name ("transport button"); - last_configure_time= 0; - last_peak_grab = 0; - ARDOUR::Diskstream::DiskOverrun.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::disk_overrun_handler, this), gui_context()); ARDOUR::Diskstream::DiskUnderrun.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::disk_underrun_handler, this), gui_context()); @@ -447,8 +474,12 @@ ARDOUR_UI::engine_running () first_time_engine_run = false; } + if (_session) { + _session->reset_xrun_count (); + } update_disk_space (); update_cpu_load (); + update_xrun_count (); update_sample_rate (AudioEngine::instance()->sample_rate()); update_timecode_format (); } @@ -518,6 +549,7 @@ ARDOUR_UI::post_engine () AudioEngine::instance()->SampleRateChanged.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::update_sample_rate, this, _1), gui_context()); AudioEngine::instance()->BufferSizeChanged.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::update_sample_rate, this, _1), gui_context()); AudioEngine::instance()->Halted.connect_same_thread (halt_connection, boost::bind (&ARDOUR_UI::engine_halted, this, _1, false)); + AudioEngine::instance()->BecameSilent.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::audioengine_became_silent, this), gui_context()); _tooltips.enable(); @@ -811,7 +843,16 @@ ARDOUR_UI::starting () if ((nsm_url = g_getenv ("NSM_URL")) != 0) { nsm = new NSM_Client; if (!nsm->init (nsm_url)) { - nsm->announce (PROGRAM_NAME, ":dirty:", "ardour3"); + /* the ardour executable may have different names: + * + * waf's obj.target for distro versions: eg ardour4, ardourvst4 + * Ardour4, Mixbus3 for bundled versions + full path on OSX & windows + * argv[0] does not apply since we need the wrapper-script (not the binary itself) + * + * The wrapper startup script should set the environment variable 'ARDOUR_SELF' + */ + const char *process_name = g_getenv ("ARDOUR_SELF"); + nsm->announce (PROGRAM_NAME, ":dirty:", process_name ? process_name : "ardour4"); unsigned int i = 0; // wait for announce reply from nsm server @@ -1185,6 +1226,7 @@ void ARDOUR_UI::every_second () { update_cpu_load (); + update_xrun_count (); update_buffer_load (); update_disk_space (); update_timecode_format (); @@ -1345,6 +1387,29 @@ ARDOUR_UI::update_format () format_label.set_markup (s.str ()); } +void +ARDOUR_UI::update_xrun_count () +{ + char buf[64]; + + /* If this text is changed, the set_size_request_to_display_given_text call in ARDOUR_UI::resize_text_widgets + should also be changed. + */ + + if (_session) { + const unsigned int x = _session->get_xrun_count (); + if (x > 9999) { + snprintf (buf, sizeof (buf), _("X: >10K"), X_("red")); + } else { + snprintf (buf, sizeof (buf), _("X: %u"), x > 0 ? X_("red") : X_("green"), x); + } + } else { + snprintf (buf, sizeof (buf), _("X: ?"), X_("yellow")); + } + xrun_label.set_markup (buf); + set_tip (xrun_label, _("Audio dropouts. Shift+click to reset")); +} + void ARDOUR_UI::update_cpu_load () { @@ -1420,7 +1485,7 @@ ARDOUR_UI::update_disk_space() snprintf (buf, sizeof (buf), "%s", _("Disk: 24hrs+")); } else { rec_enabled_streams = 0; - _session->foreach_route (this, &ARDOUR_UI::count_recenabled_streams); + _session->foreach_route (this, &ARDOUR_UI::count_recenabled_streams, false); framecnt_t frames = opt_frames.get_value_or (0); @@ -2078,7 +2143,6 @@ ARDOUR_UI::get_smart_mode() const void ARDOUR_UI::toggle_roll (bool with_abort, bool roll_out_of_bounded_mode) { - if (!_session) { return; } @@ -2341,7 +2405,7 @@ ARDOUR_UI::update_clocks () if (!_session) return; if (editor && !editor->dragging_playhead()) { - Clock (_session->audible_frame(), false, editor->get_preferred_edit_position()); /* EMIT_SIGNAL */ + Clock (_session->audible_frame(), false, editor->get_preferred_edit_position (EDIT_IGNORE_PHEAD)); /* EMIT_SIGNAL */ } } @@ -2361,6 +2425,103 @@ ARDOUR_UI::stop_clocking () clock_signal_connection.disconnect (); } +bool +ARDOUR_UI::save_as_progress_update (float fraction, int64_t cnt, int64_t total, Gtk::Label* label, Gtk::ProgressBar* bar) +{ + char buf[256]; + + snprintf (buf, sizeof (buf), _("Copied %" PRId64 " of %" PRId64), cnt, total); + + label->set_text (buf); + bar->set_fraction (fraction); + + /* process events, redraws, etc. */ + + while (gtk_events_pending()) { + gtk_main_iteration (); + } + + return true; /* continue with save-as */ +} + +void +ARDOUR_UI::save_session_as () +{ + if (!_session) { + return; + } + + if (!save_as_dialog) { + save_as_dialog = new SaveAsDialog; + } else { + save_as_dialog->clear_name (); + } + + int response = save_as_dialog->run (); + + save_as_dialog->hide (); + + switch (response) { + case Gtk::RESPONSE_OK: + break; + default: + return; + } + + + Session::SaveAs sa; + + sa.new_parent_folder = save_as_dialog->new_parent_folder (); + sa.new_name = save_as_dialog->new_name (); + sa.switch_to = save_as_dialog->switch_to(); + sa.copy_media = save_as_dialog->copy_media(); + sa.copy_external = save_as_dialog->copy_external(); + sa.include_media = save_as_dialog->include_media (); + + /* Only bother with a progress dialog if we're going to copy + media into the save-as target. Without that choice, this + will be very fast because we're only talking about a few kB's to + perhaps a couple of MB's of data. + */ + + ArdourDialog progress_dialog (_("Save As"), true); + + if (sa.include_media && sa.copy_media) { + + Gtk::Label label; + Gtk::ProgressBar progress_bar; + + progress_dialog.get_vbox()->pack_start (label); + progress_dialog.get_vbox()->pack_start (progress_bar); + label.show (); + progress_bar.show (); + + /* this signal will be emitted from within this, the calling thread, + * after every file is copied. It provides information on percentage + * complete (in terms of total data to copy), the number of files + * copied so far, and the total number to copy. + */ + + ScopedConnection c; + + sa.Progress.connect_same_thread (c, boost::bind (&ARDOUR_UI::save_as_progress_update, this, _1, _2, _3, &label, &progress_bar)); + + progress_dialog.show_all (); + progress_dialog.present (); + } + + if (_session->save_as (sa)) { + /* ERROR MESSAGE */ + MessageDialog msg (string_compose (_("Save As failed: %1"), sa.failure_message)); + msg.run (); + } + + if (!sa.include_media) { + unload_session (false); + load_session (sa.final_session_folder_name, sa.new_name); + } +} + /** Ask the user for the name of a new snapshot and then take it. */ @@ -2623,10 +2784,8 @@ ARDOUR_UI::edit_metadata () { SessionMetadataEditor dialog; dialog.set_session (_session); - Keyboard::magic_widget_grab_focus (); dialog.grab_focus (); dialog.run (); - Keyboard::magic_widget_drop_focus (); } void @@ -3050,6 +3209,9 @@ ARDOUR_UI::load_session (const std::string& path, const std::string& snap_name, msg.set_position (Gtk::WIN_POS_CENTER); pop_back_splash (msg); msg.present (); + + dump_errors (cerr); + (void) msg.run (); msg.hide (); @@ -3495,43 +3657,36 @@ ARDOUR_UI::flush_trash () void ARDOUR_UI::setup_order_hint (AddRouteDialog::InsertAt place) { - uint32_t order_hint = 0; + uint32_t order_hint = UINT32_MAX; + if (editor->get_selection().tracks.empty()) { + return; + } + /* we want the new routes to have their order keys set starting from the highest order key in the selection + 1 (if available). */ - if (place == AddRouteDialog::MixerSelection) { - for (RouteUISelection::iterator s = mixer->selection().routes.begin(); s != mixer->selection().routes.end(); ++s) { - if ((*s)->route()->order_key() > order_hint) { - order_hint = (*s)->route()->order_key(); - } - } - - if (!mixer->selection().routes.empty()) { + + if (place == AddRouteDialog::AfterSelection) { + RouteTimeAxisView *rtav = dynamic_cast (editor->get_selection().tracks.back()); + if (rtav) { + order_hint = rtav->route()->order_key(); order_hint++; - } else { - return; - } - - } else if (place == AddRouteDialog::EditorSelection){ - for (TrackSelection::iterator s = editor->get_selection().tracks.begin(); s != editor->get_selection().tracks.end(); ++s) { - RouteTimeAxisView* tav = dynamic_cast (*s); - if (tav && tav->route() && tav->route()->order_key() > order_hint) { - order_hint = tav->route()->order_key(); - } } - - if (!editor->get_selection().tracks.empty()) { - order_hint++; - } else { - return; + } else if (place == AddRouteDialog::BeforeSelection) { + RouteTimeAxisView *rtav = dynamic_cast (editor->get_selection().tracks.front()); + if (rtav) { + order_hint = rtav->route()->order_key(); } - } else if (place == AddRouteDialog::First) { order_hint = 0; } else { - /** AddRouteDialog::Last + /* leave order_hint at UINT32_MAX */ + } + + if (order_hint == UINT32_MAX) { + /** AddRouteDialog::Last or selection with first/last not a RouteTimeAxisView * not setting an order hint will place new routes last. */ return; @@ -3678,7 +3833,7 @@ ARDOUR_UI::start_video_server (Gtk::Window* float_window, bool popup_msg) int firsttime = 0; while (!ARDOUR_UI::instance()->video_timeline->check_server()) { if (firsttime++) { - warning << _("Could not connect to the Video Server. Start it or configure its access URL in Edit -> Preferences.") << endmsg; + warning << _("Could not connect to the Video Server. Start it or configure its access URL in Preferences.") << endmsg; } VideoServerDialog *video_server_dialog = new VideoServerDialog (_session); if (float_window) { @@ -3783,7 +3938,7 @@ ARDOUR_UI::add_video (Gtk::Window* float_window) } if (!start_video_server(float_window, false)) { - warning << _("Could not connect to the Video Server. Start it or configure its access URL in Edit -> Preferences.") << endmsg; + warning << _("Could not connect to the Video Server. Start it or configure its access URL in Preferences.") << endmsg; return; } @@ -4004,7 +4159,10 @@ ARDOUR_UI::keyboard_settings () const void ARDOUR_UI::create_xrun_marker (framepos_t where) { - editor->mouse_add_new_marker (where, false, true); + if (_session) { + Location *location = new Location (*_session, where, where, _("xrun"), Location::IsMark); + _session->locations()->add (location); + } } void @@ -4326,13 +4484,13 @@ void ARDOUR_UI::update_transport_clocks (framepos_t pos) { if (ui_config->get_primary_clock_delta_edit_cursor()) { - primary_clock->set (pos, false, editor->get_preferred_edit_position()); + primary_clock->set (pos, false, editor->get_preferred_edit_position (EDIT_IGNORE_PHEAD)); } else { primary_clock->set (pos); } if (ui_config->get_secondary_clock_delta_edit_cursor()) { - secondary_clock->set (pos, false, editor->get_preferred_edit_position()); + secondary_clock->set (pos, false, editor->get_preferred_edit_position (EDIT_IGNORE_PHEAD)); } else { secondary_clock->set (pos); } @@ -4692,3 +4850,66 @@ ARDOUR_UI::set_flat_buttons () { CairoWidget::set_flat_buttons( config()->get_flat_buttons() ); } + +void +ARDOUR_UI::audioengine_became_silent () +{ + MessageDialog msg (string_compose (_("This is a free/demo copy of %1. It has just switched to silent mode."), PROGRAM_NAME), + true, + Gtk::MESSAGE_WARNING, + Gtk::BUTTONS_NONE, + true); + + msg.set_title (string_compose (_("%1 is now silent"), PROGRAM_NAME)); + + Gtk::Label pay_label (string_compose (_("Please consider paying for a copy of %1 - you can pay whatever you want."), PROGRAM_NAME)); + Gtk::Label subscribe_label (_("Better yet become a subscriber - subscriptions start at US$1 per month.")); + Gtk::Button pay_button (_("Pay for a copy (via the web)")); + Gtk::Button subscribe_button (_("Become a subscriber (via the web)")); + Gtk::HBox pay_button_box; + Gtk::HBox subscribe_button_box; + + pay_button_box.pack_start (pay_button, true, false); + subscribe_button_box.pack_start (subscribe_button, true, false); + + bool (*openuri)(const char*) = PBD::open_uri; /* this forces selection of the const char* variant of PBD::open_uri(), which we need to avoid ambiguity below */ + + pay_button.signal_clicked().connect (sigc::hide_return (sigc::bind (sigc::ptr_fun (openuri), (const char*) "https://ardour.org/download"))); + subscribe_button.signal_clicked().connect (sigc::hide_return (sigc::bind (sigc::ptr_fun (openuri), (const char*) "https://community.ardour.org/s/subscribe"))); + + msg.get_vbox()->pack_start (pay_label); + msg.get_vbox()->pack_start (pay_button_box); + msg.get_vbox()->pack_start (subscribe_label); + msg.get_vbox()->pack_start (subscribe_button_box); + + msg.get_vbox()->show_all (); + + msg.add_button (_("Remain silent"), Gtk::RESPONSE_CANCEL); + msg.add_button (_("Save and quit"), Gtk::RESPONSE_NO); + msg.add_button (_("Give me more time"), Gtk::RESPONSE_YES); + + int r = msg.run (); + + switch (r) { + case Gtk::RESPONSE_YES: + AudioEngine::instance()->reset_silence_countdown (); + break; + + case Gtk::RESPONSE_NO: + /* save and quit */ + save_state_canfail (""); + exit (0); + break; + + case Gtk::RESPONSE_CANCEL: + default: + /* don't reset, save session and exit */ + break; + } +} + +void +ARDOUR_UI::hide_application () +{ + Application::instance ()-> hide (); +}