2 Copyright (C) 1999-2013 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include "gtk2ardour-config.h"
22 #include "gtk2ardour-version.h"
32 #ifndef PLATFORM_WINDOWS
33 #include <sys/resource.h>
37 #include <sys/types.h>
38 #include <sys/sysctl.h>
48 #include "pbd/gstdio_compat.h"
50 #include <gtkmm/messagedialog.h>
51 #include <gtkmm/accelmap.h>
52 #include <gtkmm/stock.h>
54 #include "pbd/error.h"
55 #include "pbd/basename.h"
56 #include "pbd/compose.h"
57 #include "pbd/convert.h"
58 #include "pbd/failed_constructor.h"
59 #include "pbd/file_archive.h"
60 #include "pbd/enumwriter.h"
61 #include "pbd/memento_command.h"
62 #include "pbd/openuri.h"
63 #include "pbd/stl_delete.h"
64 #include "pbd/file_utils.h"
65 #include "pbd/localtime_r.h"
66 #include "pbd/pthread_utils.h"
67 #include "pbd/replace_all.h"
68 #include "pbd/xml++.h"
70 #include "gtkmm2ext/application.h"
71 #include "gtkmm2ext/bindings.h"
72 #include "gtkmm2ext/gtk_ui.h"
73 #include "gtkmm2ext/utils.h"
74 #include "gtkmm2ext/click_box.h"
75 #include "gtkmm2ext/fastmeter.h"
76 #include "gtkmm2ext/popup.h"
77 #include "gtkmm2ext/window_title.h"
79 #include "ardour/ardour.h"
80 #include "ardour/audio_backend.h"
81 #include "ardour/audio_track.h"
82 #include "ardour/audioengine.h"
83 #include "ardour/audiofilesource.h"
84 #include "ardour/automation_watch.h"
85 #include "ardour/diskstream.h"
86 #include "ardour/filename_extensions.h"
87 #include "ardour/filesystem_paths.h"
88 #include "ardour/ltc_file_reader.h"
89 #include "ardour/midi_track.h"
90 #include "ardour/port.h"
91 #include "ardour/plugin_manager.h"
92 #include "ardour/process_thread.h"
93 #include "ardour/profile.h"
94 #include "ardour/recent_sessions.h"
95 #include "ardour/record_enable_control.h"
96 #include "ardour/session_directory.h"
97 #include "ardour/session_route.h"
98 #include "ardour/session_state_utils.h"
99 #include "ardour/session_utils.h"
100 #include "ardour/source_factory.h"
101 #include "ardour/slave.h"
102 #include "ardour/system_exec.h"
103 #include "ardour/track.h"
104 #include "ardour/vca_manager.h"
105 #include "ardour/utils.h"
107 #include "LuaBridge/LuaBridge.h"
109 #ifdef WINDOWS_VST_SUPPORT
112 #ifdef AUDIOUNIT_SUPPORT
113 #include "ardour/audio_unit.h"
116 // fix for OSX (nsm.h has a check function, AU/Apple defines check)
121 #include "timecode/time.h"
123 typedef uint64_t microseconds_t;
128 #include "add_route_dialog.h"
129 #include "ambiguous_file_dialog.h"
130 #include "ardour_ui.h"
131 #include "audio_clock.h"
132 #include "audio_region_view.h"
133 #include "big_clock_window.h"
134 #include "bundle_manager.h"
135 #include "duplicate_routes_dialog.h"
137 #include "engine_dialog.h"
138 #include "export_video_dialog.h"
139 #include "export_video_infobox.h"
140 #include "gain_meter.h"
141 #include "global_port_matrix.h"
142 #include "gui_object.h"
143 #include "gui_thread.h"
144 #include "keyboard.h"
145 #include "keyeditor.h"
146 #include "location_ui.h"
147 #include "lua_script_manager.h"
148 #include "luawindow.h"
149 #include "main_clock.h"
150 #include "missing_file_dialog.h"
151 #include "missing_plugin_dialog.h"
152 #include "mixer_ui.h"
153 #include "meterbridge.h"
154 #include "mouse_cursors.h"
157 #include "pingback.h"
158 #include "processor_box.h"
159 #include "prompter.h"
160 #include "public_editor.h"
161 #include "rc_option_editor.h"
162 #include "route_time_axis.h"
163 #include "route_params_ui.h"
164 #include "save_as_dialog.h"
165 #include "script_selector.h"
166 #include "session_archive_dialog.h"
167 #include "session_dialog.h"
168 #include "session_metadata_dialog.h"
169 #include "session_option_editor.h"
170 #include "speaker_dialog.h"
173 #include "time_axis_view_item.h"
174 #include "time_info_box.h"
177 #include "utils_videotl.h"
178 #include "video_server_dialog.h"
179 #include "add_video_dialog.h"
180 #include "transcode_video_dialog.h"
182 #include "pbd/i18n.h"
184 using namespace ARDOUR;
185 using namespace ARDOUR_UI_UTILS;
187 using namespace Gtkmm2ext;
190 using namespace Editing;
192 ARDOUR_UI *ARDOUR_UI::theArdourUI = 0;
194 sigc::signal<void, framepos_t, bool, framepos_t> ARDOUR_UI::Clock;
195 sigc::signal<void> ARDOUR_UI::CloseAllDialogs;
198 ask_about_configuration_copy (string const & old_dir, string const & new_dir, int version)
200 MessageDialog msg (string_compose (_("%1 %2.x has discovered configuration files from %1 %3.x.\n\n"
201 "Would you like these files to be copied and used for %1 %2.x?\n\n"
202 "(This will require you to restart %1.)"),
203 PROGRAM_NAME, PROGRAM_VERSION, version),
204 false, /* no markup */
207 true /* modal, though it hardly matters since it is the only window */
210 msg.set_default_response (Gtk::RESPONSE_YES);
213 return (msg.run() == Gtk::RESPONSE_YES);
217 libxml_generic_error_func (void* /* parsing_context*/,
225 vsnprintf (buf, sizeof (buf), msg, ap);
226 error << buf << endmsg;
231 libxml_structured_error_func (void* /* parsing_context*/,
239 replace_all (msg, "\n", "");
242 if (err->file && err->line) {
243 error << X_("XML error: ") << msg << " in " << err->file << " at line " << err->line;
246 error << ':' << err->int2;
251 error << X_("XML error: ") << msg << endmsg;
257 ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir)
258 : Gtkmm2ext::UI (PROGRAM_NAME, X_("gui"), argcp, argvp)
259 , session_loaded (false)
260 , gui_object_state (new GUIObjectState)
261 , primary_clock (new MainClock (X_("primary"), X_("transport"), true ))
262 , secondary_clock (new MainClock (X_("secondary"), X_("secondary"), false))
263 , big_clock (new AudioClock (X_("bigclock"), false, "big", true, true, false, false))
265 , global_actions (X_("global"))
266 , ignore_dual_punch (false)
267 , main_window_visibility (0)
272 , _mixer_on_top (false)
273 , _initial_verbose_plugin_scan (false)
274 , first_time_engine_run (true)
275 , secondary_clock_spacer (0)
276 , auto_input_button (ArdourButton::led_default_elements)
278 , auto_return_button (ArdourButton::led_default_elements)
279 , follow_edits_button (ArdourButton::led_default_elements)
280 , auditioning_alert_button (_("Audition"))
281 , solo_alert_button (_("Solo"))
282 , feedback_alert_button (_("Feedback"))
283 , error_alert_button ( ArdourButton::just_led_default_elements )
285 , editor_meter_peak_display()
286 , _numpad_locate_happening (false)
287 , _session_is_new (false)
288 , last_key_press_time (0)
292 , rc_option_editor (0)
293 , speaker_config_window (X_("speaker-config"), _("Speaker Configuration"))
294 , add_route_dialog (X_("add-routes"), _("Add Tracks/Busses"))
295 , about (X_("about"), _("About"))
296 , location_ui (X_("locations"), S_("Ranges|Locations"))
297 , route_params (X_("inspector"), _("Tracks and Busses"))
298 , audio_midi_setup (X_("audio-midi-setup"), _("Audio/MIDI Setup"))
299 , export_video_dialog (X_("video-export"), _("Video Export Dialog"))
300 , lua_script_window (X_("script-manager"), _("Script Manager"))
301 , session_option_editor (X_("session-options-editor"), _("Properties"), boost::bind (&ARDOUR_UI::create_session_option_editor, this))
302 , add_video_dialog (X_("add-video"), _("Add Video"), boost::bind (&ARDOUR_UI::create_add_video_dialog, this))
303 , bundle_manager (X_("bundle-manager"), _("Bundle Manager"), boost::bind (&ARDOUR_UI::create_bundle_manager, this))
304 , big_clock_window (X_("big-clock"), _("Big Clock"), boost::bind (&ARDOUR_UI::create_big_clock_window, this))
305 , audio_port_matrix (X_("audio-connection-manager"), _("Audio Connections"), boost::bind (&ARDOUR_UI::create_global_port_matrix, this, ARDOUR::DataType::AUDIO))
306 , midi_port_matrix (X_("midi-connection-manager"), _("MIDI Connections"), boost::bind (&ARDOUR_UI::create_global_port_matrix, this, ARDOUR::DataType::MIDI))
307 , key_editor (X_("key-editor"), _("Keyboard Shortcuts"), boost::bind (&ARDOUR_UI::create_key_editor, this))
308 , video_server_process (0)
310 , have_configure_timeout (false)
311 , last_configure_time (0)
313 , have_disk_speed_dialog_displayed (false)
314 , _status_bar_visibility (X_("status-bar"))
315 , _feedback_exists (false)
316 , _log_not_acknowledged (LogLevelNone)
317 , duplicate_routes_dialog (0)
318 , editor_visibility_button (S_("Window|Editor"))
319 , mixer_visibility_button (S_("Window|Mixer"))
320 , prefs_visibility_button (S_("Window|Preferences"))
322 Gtkmm2ext::init (localedir);
324 UIConfiguration::instance().post_gui_init ();
326 if (ARDOUR::handle_old_configuration_files (boost::bind (ask_about_configuration_copy, _1, _2, _3))) {
327 MessageDialog msg (string_compose (_("Your configuration files were copied. You can now restart %1."), PROGRAM_NAME), true);
329 /* configuration was modified, exit immediately */
334 if (string (VERSIONSTRING).find (".pre") != string::npos) {
335 /* check this is not being run from ./ardev etc. */
336 if (!running_from_source_tree ()) {
337 pre_release_dialog ();
341 if (theArdourUI == 0) {
345 /* track main window visibility */
347 main_window_visibility = new VisibilityTracker (_main_window);
349 /* stop libxml from spewing to stdout/stderr */
351 xmlSetGenericErrorFunc (this, libxml_generic_error_func);
352 xmlSetStructuredErrorFunc (this, libxml_structured_error_func);
354 UIConfiguration::instance().ParameterChanged.connect (sigc::mem_fun (*this, &ARDOUR_UI::parameter_changed));
355 boost::function<void (string)> pc (boost::bind (&ARDOUR_UI::parameter_changed, this, _1));
356 UIConfiguration::instance().map_parameters (pc);
358 Glib::RefPtr<Gtk::Action> act;
360 act = ActionManager::get_action ("Transport/Roll");
361 roll_button.set_related_action (act);
362 act = ActionManager::get_action ("Transport/Stop");
363 stop_button.set_related_action (act);
364 act = ActionManager::get_action ("Transport/GotoStart");
365 goto_start_button.set_related_action (act);
366 act = ActionManager::get_action ("Transport/GotoEnd");
367 goto_end_button.set_related_action (act);
368 act = ActionManager::get_action ("Transport/Loop");
369 auto_loop_button.set_related_action (act);
370 act = ActionManager::get_action ("Transport/PlaySelection");
371 play_selection_button.set_related_action (act);
372 act = ActionManager::get_action ("Transport/Record");
373 rec_button.set_related_action (act);
375 roll_button.set_name ("transport button");
376 stop_button.set_name ("transport button");
377 goto_start_button.set_name ("transport button");
378 goto_end_button.set_name ("transport button");
379 auto_loop_button.set_name ("transport button");
380 play_selection_button.set_name ("transport button");
381 rec_button.set_name ("transport recenable button");
382 midi_panic_button.set_name ("transport button");
384 ARDOUR::Diskstream::DiskOverrun.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::disk_overrun_handler, this), gui_context());
385 ARDOUR::Diskstream::DiskUnderrun.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::disk_underrun_handler, this), gui_context());
387 ARDOUR::Session::VersionMismatch.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::session_format_mismatch, this, _1, _2), gui_context());
389 /* handle dialog requests */
391 ARDOUR::Session::Dialog.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::session_dialog, this, _1), gui_context());
393 /* handle pending state with a dialog (PROBLEM: needs to return a value and thus cannot be x-thread) */
395 ARDOUR::Session::AskAboutPendingState.connect_same_thread (forever_connections, boost::bind (&ARDOUR_UI::pending_state_dialog, this));
397 /* handle Audio/MIDI setup when session requires it */
399 ARDOUR::Session::AudioEngineSetupRequired.connect_same_thread (forever_connections, boost::bind (&ARDOUR_UI::do_audio_midi_setup, this, _1));
401 /* handle sr mismatch with a dialog (PROBLEM: needs to return a value and thus cannot be x-thread) */
403 ARDOUR::Session::AskAboutSampleRateMismatch.connect_same_thread (forever_connections, boost::bind (&ARDOUR_UI::sr_mismatch_dialog, this, _1, _2));
405 /* handle sr mismatch with a dialog - cross-thread from engine */
406 ARDOUR::Session::NotifyAboutSampleRateMismatch.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::sr_mismatch_message, this, _1, _2), gui_context ());
408 /* handle requests to quit (coming from JACK session) */
410 ARDOUR::Session::Quit.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::finish, this), gui_context ());
412 /* tell the user about feedback */
414 ARDOUR::Session::FeedbackDetected.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::feedback_detected, this), gui_context ());
415 ARDOUR::Session::SuccessfulGraphSort.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::successful_graph_sort, this), gui_context ());
417 /* handle requests to deal with missing files */
419 ARDOUR::Session::MissingFile.connect_same_thread (forever_connections, boost::bind (&ARDOUR_UI::missing_file, this, _1, _2, _3));
421 /* and ambiguous files */
423 ARDOUR::FileSource::AmbiguousFileName.connect_same_thread (forever_connections, boost::bind (&ARDOUR_UI::ambiguous_file, this, _1, _2));
425 /* also plugin scan messages */
426 ARDOUR::PluginScanMessage.connect (forever_connections, MISSING_INVALIDATOR, boost::bind(&ARDOUR_UI::plugin_scan_dialog, this, _1, _2, _3), gui_context());
427 ARDOUR::PluginScanTimeout.connect (forever_connections, MISSING_INVALIDATOR, boost::bind(&ARDOUR_UI::plugin_scan_timeout, this, _1), gui_context());
429 ARDOUR::GUIIdle.connect (forever_connections, MISSING_INVALIDATOR, boost::bind(&ARDOUR_UI::gui_idle_handler, this), gui_context());
431 Config->ParameterChanged.connect ( forever_connections, MISSING_INVALIDATOR, boost::bind(&ARDOUR_UI::set_flat_buttons, this), gui_context() );
434 /* lets get this party started */
436 setup_gtk_ardour_enums ();
439 SessionEvent::create_per_thread_pool ("GUI", 4096);
441 /* we like keyboards */
443 keyboard = new ArdourKeyboard(*this);
445 XMLNode* node = ARDOUR_UI::instance()->keyboard_settings();
447 keyboard->set_state (*node, Stateful::loading_state_version);
450 UIConfiguration::instance().reset_dpi ();
452 TimeAxisViewItem::set_constant_heights ();
454 /* Set this up so that our window proxies can register actions */
456 ActionManager::init ();
458 /* The following must happen after ARDOUR::init() so that Config is set up */
460 const XMLNode* ui_xml = Config->extra_xml (X_("UI"));
463 key_editor.set_state (*ui_xml, 0);
464 session_option_editor.set_state (*ui_xml, 0);
465 speaker_config_window.set_state (*ui_xml, 0);
466 about.set_state (*ui_xml, 0);
467 add_route_dialog.set_state (*ui_xml, 0);
468 add_video_dialog.set_state (*ui_xml, 0);
469 route_params.set_state (*ui_xml, 0);
470 bundle_manager.set_state (*ui_xml, 0);
471 location_ui.set_state (*ui_xml, 0);
472 big_clock_window.set_state (*ui_xml, 0);
473 audio_port_matrix.set_state (*ui_xml, 0);
474 midi_port_matrix.set_state (*ui_xml, 0);
475 export_video_dialog.set_state (*ui_xml, 0);
476 lua_script_window.set_state (*ui_xml, 0);
479 /* Separate windows */
481 WM::Manager::instance().register_window (&key_editor);
482 WM::Manager::instance().register_window (&session_option_editor);
483 WM::Manager::instance().register_window (&speaker_config_window);
484 WM::Manager::instance().register_window (&about);
485 WM::Manager::instance().register_window (&add_route_dialog);
486 WM::Manager::instance().register_window (&add_video_dialog);
487 WM::Manager::instance().register_window (&route_params);
488 WM::Manager::instance().register_window (&audio_midi_setup);
489 WM::Manager::instance().register_window (&export_video_dialog);
490 WM::Manager::instance().register_window (&lua_script_window);
491 WM::Manager::instance().register_window (&bundle_manager);
492 WM::Manager::instance().register_window (&location_ui);
493 WM::Manager::instance().register_window (&big_clock_window);
494 WM::Manager::instance().register_window (&audio_port_matrix);
495 WM::Manager::instance().register_window (&midi_port_matrix);
497 /* do not retain position for add route dialog */
498 add_route_dialog.set_state_mask (WindowProxy::Size);
500 /* Trigger setting up the color scheme and loading the GTK RC file */
502 UIConfiguration::instance().load_rc_file (false);
504 _process_thread = new ProcessThread ();
505 _process_thread->init ();
507 UIConfiguration::instance().DPIReset.connect (sigc::mem_fun (*this, &ARDOUR_UI::resize_text_widgets));
513 ARDOUR_UI::pre_release_dialog ()
515 ArdourDialog d (_("Pre-Release Warning"), true, false);
516 d.add_button (Gtk::Stock::OK, Gtk::RESPONSE_OK);
518 Label* label = manage (new Label);
519 label->set_markup (string_compose (_("<b>Welcome to this pre-release build of %1 %2</b>\n\n\
520 There are still several issues and bugs to be worked on,\n\
521 as well as general workflow improvements, before this can be considered\n\
522 release software. So, a few guidelines:\n\
524 1) Please do <b>NOT</b> use this software with the expectation that it is stable or reliable\n\
525 though it may be so, depending on your workflow.\n\
526 2) Please wait for a helpful writeup of new features.\n\
527 3) <b>Please do NOT use the forums at ardour.org to report issues</b>.\n\
528 4) Please <b>DO</b> use the bugtracker at http://tracker.ardour.org/ to report issues\n\
529 making sure to note the product version number as 5.0-pre.\n\
530 5) Please <b>DO</b> use the ardour-users mailing list to discuss ideas and pass on comments.\n\
531 6) Please <b>DO</b> join us on IRC for real time discussions about %1 %2. You\n\
532 can get there directly from within the program via the Help->Chat menu option.\n\
534 Full information on all the above can be found on the support page at\n\
536 http://ardour.org/support\n\
537 "), PROGRAM_NAME, VERSIONSTRING));
539 d.get_vbox()->set_border_width (12);
540 d.get_vbox()->pack_start (*label, false, false, 12);
541 d.get_vbox()->show_all ();
546 GlobalPortMatrixWindow*
547 ARDOUR_UI::create_global_port_matrix (ARDOUR::DataType type)
552 return new GlobalPortMatrixWindow (_session, type);
556 ARDOUR_UI::attach_to_engine ()
558 AudioEngine::instance()->Running.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::engine_running, this), gui_context());
559 ARDOUR::Port::set_connecting_blocked (ARDOUR_COMMAND_LINE::no_connect_ports);
563 ARDOUR_UI::engine_stopped ()
565 ENSURE_GUI_THREAD (*this, &ARDOUR_UI::engine_stopped)
566 ActionManager::set_sensitive (ActionManager::engine_sensitive_actions, false);
567 ActionManager::set_sensitive (ActionManager::engine_opposite_sensitive_actions, true);
568 update_sample_rate (0);
573 ARDOUR_UI::engine_running ()
575 ENSURE_GUI_THREAD (*this, &ARDOUR_UI::engine_running)
576 if (first_time_engine_run) {
578 first_time_engine_run = false;
582 _session->reset_xrun_count ();
584 update_disk_space ();
586 update_xrun_count ();
587 update_sample_rate (AudioEngine::instance()->sample_rate());
588 update_timecode_format ();
589 update_peak_thread_work ();
590 ActionManager::set_sensitive (ActionManager::engine_sensitive_actions, true);
591 ActionManager::set_sensitive (ActionManager::engine_opposite_sensitive_actions, false);
595 ARDOUR_UI::engine_halted (const char* reason, bool free_reason)
597 if (!Gtkmm2ext::UI::instance()->caller_is_ui_thread()) {
598 /* we can't rely on the original string continuing to exist when we are called
599 again in the GUI thread, so make a copy and note that we need to
602 char *copy = strdup (reason);
603 Gtkmm2ext::UI::instance()->call_slot (MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::engine_halted, this, copy, true));
607 ActionManager::set_sensitive (ActionManager::engine_sensitive_actions, false);
608 ActionManager::set_sensitive (ActionManager::engine_opposite_sensitive_actions, true);
610 update_sample_rate (0);
614 /* if the reason is a non-empty string, it means that the backend was shutdown
615 rather than just Ardour.
618 if (strlen (reason)) {
619 msgstr = string_compose (_("The audio backend was shutdown because:\n\n%1"), reason);
621 msgstr = string_compose (_("\
622 The audio backend has either been shutdown or it\n\
623 disconnected %1 because %1\n\
624 was not fast enough. Try to restart\n\
625 the audio backend and save the session."), PROGRAM_NAME);
628 MessageDialog msg (_main_window, msgstr);
629 pop_back_splash (msg);
633 free (const_cast<char*> (reason));
638 ARDOUR_UI::post_engine ()
640 /* Things to be done once (and once ONLY) after we have a backend running in the AudioEngine
642 #ifdef AUDIOUNIT_SUPPORT
644 if (AUPluginInfo::au_get_crashlog(au_msg)) {
645 popup_error(_("Audio Unit Plugin Scan Failed. Automatic AU scanning has been disabled. Please see the log window for further details."));
646 error << _("Audio Unit Plugin Scan Failed:") << endmsg;
647 info << au_msg << endmsg;
651 ARDOUR::init_post_engine ();
653 /* connect to important signals */
655 AudioEngine::instance()->Stopped.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::engine_stopped, this), gui_context());
656 AudioEngine::instance()->SampleRateChanged.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::update_sample_rate, this, _1), gui_context());
657 AudioEngine::instance()->BufferSizeChanged.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::update_sample_rate, this, _1), gui_context());
658 AudioEngine::instance()->Halted.connect_same_thread (halt_connection, boost::bind (&ARDOUR_UI::engine_halted, this, _1, false));
659 AudioEngine::instance()->BecameSilent.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::audioengine_became_silent, this), gui_context());
661 if (setup_windows ()) {
662 throw failed_constructor ();
665 /* Do this after setup_windows (), as that's when the _status_bar_visibility is created */
666 XMLNode* n = Config->extra_xml (X_("UI"));
668 _status_bar_visibility.set_state (*n);
671 check_memory_locking();
673 /* this is the first point at which all the possible actions are
674 * available, because some of the available actions are dependent on
675 * aspects of the engine/backend.
678 if (ARDOUR_COMMAND_LINE::show_key_actions) {
681 vector<string> paths;
682 vector<string> labels;
683 vector<string> tooltips;
685 vector<Glib::RefPtr<Gtk::Action> > actions;
687 Gtkmm2ext::ActionMap::get_all_actions (paths, labels, tooltips, keys, actions);
689 vector<string>::iterator k;
690 vector<string>::iterator p;
692 for (p = paths.begin(), k = keys.begin(); p != paths.end(); ++k, ++p) {
697 cout << *p << " => " << *k << endl;
701 halt_connection.disconnect ();
702 AudioEngine::instance()->stop ();
706 /* this being a GUI and all, we want peakfiles */
708 AudioFileSource::set_build_peakfiles (true);
709 AudioFileSource::set_build_missing_peakfiles (true);
711 /* set default clock modes */
713 primary_clock->set_mode (AudioClock::Timecode);
714 secondary_clock->set_mode (AudioClock::BBT);
716 /* start the time-of-day-clock */
719 /* OS X provides a nearly-always visible wallclock, so don't be stupid */
720 update_wall_clock ();
721 Glib::signal_timeout().connect_seconds (sigc::mem_fun(*this, &ARDOUR_UI::update_wall_clock), 1);
726 Config->ParameterChanged.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::parameter_changed, this, _1), gui_context());
727 boost::function<void (string)> pc (boost::bind (&ARDOUR_UI::parameter_changed, this, _1));
728 Config->map_parameters (pc);
730 UIConfiguration::instance().map_parameters (pc);
734 ARDOUR_UI::~ARDOUR_UI ()
736 UIConfiguration::instance().save_state();
740 if (getenv ("ARDOUR_RUNNING_UNDER_VALGRIND")) {
741 // don't bother at 'real' exit. the OS cleans up for us.
742 delete big_clock; big_clock = 0;
743 delete primary_clock; primary_clock = 0;
744 delete secondary_clock; secondary_clock = 0;
745 delete _process_thread; _process_thread = 0;
746 delete time_info_box; time_info_box = 0;
747 delete meterbridge; meterbridge = 0;
748 delete luawindow; luawindow = 0;
749 delete editor; editor = 0;
750 delete mixer; mixer = 0;
751 delete rc_option_editor; rc_option_editor = 0; // failed to wrap object warning
753 delete gui_object_state; gui_object_state = 0;
754 delete main_window_visibility;
755 FastMeter::flush_pattern_cache ();
756 PixFader::flush_pattern_cache ();
760 /* Small trick to flush main-thread event pool.
761 * Other thread-pools are destroyed at pthread_exit(),
762 * but tmain thread termination is too late to trigger Pool::~Pool()
764 SessionEvent* ev = new SessionEvent (SessionEvent::SetTransportSpeed, SessionEvent::Clear, SessionEvent::Immediate, 0, 0); // get the pool reference, values don't matter since the event is never queued.
765 delete ev->event_pool();
770 ARDOUR_UI::pop_back_splash (Gtk::Window& win)
772 if (Splash::instance()) {
773 Splash::instance()->pop_back_for (win);
778 ARDOUR_UI::configure_timeout ()
780 if (last_configure_time == 0) {
781 /* no configure events yet */
785 /* force a gap of 0.5 seconds since the last configure event
788 if (get_microseconds() - last_configure_time < 500000) {
791 have_configure_timeout = false;
792 save_ardour_state ();
798 ARDOUR_UI::configure_handler (GdkEventConfigure* /*conf*/)
800 if (have_configure_timeout) {
801 last_configure_time = get_microseconds();
803 Glib::signal_timeout().connect (sigc::mem_fun(*this, &ARDOUR_UI::configure_timeout), 100);
804 have_configure_timeout = true;
811 ARDOUR_UI::save_session_at_its_request (std::string snapshot_name)
814 _session->save_state (snapshot_name);
819 ARDOUR_UI::autosave_session ()
821 if (g_main_depth() > 1) {
822 /* inside a recursive main loop,
823 give up because we may not be able to
829 if (!Config->get_periodic_safety_backups()) {
834 _session->maybe_write_autosave();
841 ARDOUR_UI::session_dirty_changed ()
848 ARDOUR_UI::update_autosave ()
850 if (_session && _session->dirty()) {
851 if (_autosave_connection.connected()) {
852 _autosave_connection.disconnect();
855 _autosave_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &ARDOUR_UI::autosave_session),
856 Config->get_periodic_safety_backup_interval() * 1000);
859 if (_autosave_connection.connected()) {
860 _autosave_connection.disconnect();
866 ARDOUR_UI::check_announcements ()
869 string _annc_filename;
872 _annc_filename = PROGRAM_NAME "_announcements_osx_";
873 #elif defined PLATFORM_WINDOWS
874 _annc_filename = PROGRAM_NAME "_announcements_windows_";
876 _annc_filename = PROGRAM_NAME "_announcements_linux_";
878 _annc_filename.append (VERSIONSTRING);
880 _announce_string = "";
882 std::string path = Glib::build_filename (user_config_directory(), _annc_filename);
883 FILE* fin = g_fopen (path.c_str(), "rb");
885 while (!feof (fin)) {
888 if ((len = fread (tmp, sizeof(char), 1024, fin)) == 0 || ferror (fin)) {
891 _announce_string.append (tmp, len);
896 pingback (VERSIONSTRING, path);
901 _hide_splash (gpointer arg)
903 ((ARDOUR_UI*)arg)->hide_splash();
908 ARDOUR_UI::starting ()
910 Application* app = Application::instance ();
912 bool brand_new_user = ArdourStartup::required ();
914 app->ShouldQuit.connect (sigc::mem_fun (*this, &ARDOUR_UI::queue_finish));
915 app->ShouldLoad.connect (sigc::mem_fun (*this, &ARDOUR_UI::load_from_application_api));
917 if (ARDOUR_COMMAND_LINE::check_announcements) {
918 check_announcements ();
923 /* we need to create this early because it may need to set the
924 * audio backend end up.
928 audio_midi_setup.get (true);
930 std::cerr << "audio-midi engine setup failed."<< std::endl;
934 if ((nsm_url = g_getenv ("NSM_URL")) != 0) {
935 nsm = new NSM_Client;
936 if (!nsm->init (nsm_url)) {
937 /* the ardour executable may have different names:
939 * waf's obj.target for distro versions: eg ardour4, ardourvst4
940 * Ardour4, Mixbus3 for bundled versions + full path on OSX & windows
941 * argv[0] does not apply since we need the wrapper-script (not the binary itself)
943 * The wrapper startup script should set the environment variable 'ARDOUR_SELF'
945 const char *process_name = g_getenv ("ARDOUR_SELF");
946 nsm->announce (PROGRAM_NAME, ":dirty:", process_name ? process_name : "ardour4");
949 // wait for announce reply from nsm server
950 for ( i = 0; i < 5000; ++i) {
954 if (nsm->is_active()) {
959 error << _("NSM server did not announce itself") << endmsg;
962 // wait for open command from nsm server
963 for ( i = 0; i < 5000; ++i) {
966 if (nsm->client_id ()) {
972 error << _("NSM: no client ID provided") << endmsg;
976 if (_session && nsm) {
977 _session->set_nsm_state( nsm->is_active() );
979 error << _("NSM: no session created") << endmsg;
983 // nsm requires these actions disabled
984 vector<string> action_names;
985 action_names.push_back("SaveAs");
986 action_names.push_back("Rename");
987 action_names.push_back("New");
988 action_names.push_back("Open");
989 action_names.push_back("Recent");
990 action_names.push_back("Close");
992 for (vector<string>::const_iterator n = action_names.begin(); n != action_names.end(); ++n) {
993 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Main"), (*n).c_str());
995 act->set_sensitive (false);
1002 error << _("NSM: initialization failed") << endmsg;
1008 if (brand_new_user) {
1009 _initial_verbose_plugin_scan = true;
1014 _initial_verbose_plugin_scan = false;
1015 switch (s.response ()) {
1016 case Gtk::RESPONSE_OK:
1023 // TODO: maybe IFF brand_new_user
1024 if (ARDOUR::Profile->get_mixbus () && Config->get_copy_demo_sessions ()) {
1025 std::string dspd (Config->get_default_session_parent_dir());
1026 Searchpath ds (ARDOUR::ardour_data_search_path());
1027 ds.add_subdirectory_to_paths ("sessions");
1028 vector<string> demos;
1029 find_files_matching_pattern (demos, ds, "*.tar.xz");
1031 ARDOUR::RecentSessions rs;
1032 ARDOUR::read_recent_sessions (rs);
1034 for (vector<string>::iterator i = demos.begin(); i != demos.end (); ++i) {
1035 /* "demo-session" must be inside "demo-session.tar.xz"
1038 std::string name = basename_nosuffix (basename_nosuffix (*i));
1039 std::string path = Glib::build_filename (dspd, name);
1040 /* skip if session-dir already exists */
1041 if (Glib::file_test(path.c_str(), Glib::FILE_TEST_IS_DIR)) {
1044 /* skip sessions that are already in 'recent'.
1045 * eg. a new user changed <session-default-dir> shorly after installation
1047 for (ARDOUR::RecentSessions::iterator r = rs.begin(); r != rs.end(); ++r) {
1048 if ((*r).first == name) {
1053 PBD::FileArchive ar (*i);
1054 if (0 == ar.inflate (dspd)) {
1055 store_recent_sessions (name, path);
1056 info << string_compose (_("Copied Demo Session %1."), name) << endmsg;
1062 #ifdef NO_PLUGIN_STATE
1064 ARDOUR::RecentSessions rs;
1065 ARDOUR::read_recent_sessions (rs);
1067 string path = Glib::build_filename (user_config_directory(), ".iknowaboutfreeversion");
1069 if (!Glib::file_test (path, Glib::FILE_TEST_EXISTS) && !rs.empty()) {
1071 /* already used Ardour, have sessions ... warn about plugin state */
1073 ArdourDialog d (_("Free/Demo Version Warning"), true);
1075 Button b (string_compose (_("Subscribe and support development of %1"), PROGRAM_NAME));
1076 CheckButton c (_("Don't warn me about this again"));
1078 l.set_markup (string_compose (_("<span weight=\"bold\" size=\"large\">%1</span>\n\n<b>%2</b>\n\n<i>%3</i>\n\n%4"),
1079 string_compose (_("This is a free/demo version of %1"), PROGRAM_NAME),
1080 _("It will not restore OR save any plugin settings"),
1081 _("If you load an existing session with plugin settings\n"
1082 "they will not be used and will be lost."),
1083 _("To get full access to updates without this limitation\n"
1084 "consider becoming a subscriber for a low cost every month.")));
1085 l.set_justify (JUSTIFY_CENTER);
1087 b.signal_clicked().connect (mem_fun(*this, &ARDOUR_UI::launch_subscribe));
1089 d.get_vbox()->pack_start (l, true, true);
1090 d.get_vbox()->pack_start (b, false, false, 12);
1091 d.get_vbox()->pack_start (c, false, false, 12);
1093 d.add_button (_("Quit now"), RESPONSE_CANCEL);
1094 d.add_button (string_compose (_("Continue using %1"), PROGRAM_NAME), RESPONSE_OK);
1098 c.signal_toggled().connect (sigc::hide_return (sigc::bind (sigc::ptr_fun (toggle_file_existence), path)));
1100 if (d.run () != RESPONSE_OK) {
1106 /* go get a session */
1108 const bool new_session_required = (ARDOUR_COMMAND_LINE::new_session || brand_new_user);
1110 if (get_session_parameters (false, new_session_required, ARDOUR_COMMAND_LINE::load_template)) {
1111 std::cerr << "Cannot get session parameters."<< std::endl;
1118 WM::Manager::instance().show_visible ();
1120 /* We have to do this here since goto_editor_window() ends up calling show_all() on the
1121 * editor window, and we may want stuff to be hidden.
1123 _status_bar_visibility.update ();
1125 BootMessage (string_compose (_("%1 is ready for use"), PROGRAM_NAME));
1127 if (splash && splash->is_visible()) {
1128 // in 1 second, hide the splash screen
1129 Glib::signal_timeout().connect (sigc::bind (sigc::ptr_fun (_hide_splash), this), 1000);
1132 /* all other dialogs are created conditionally */
1138 ARDOUR_UI::check_memory_locking ()
1140 #if defined(__APPLE__) || defined(PLATFORM_WINDOWS)
1141 /* OS X doesn't support mlockall(2), and so testing for memory locking capability there is pointless */
1145 XMLNode* memory_warning_node = Config->instant_xml (X_("no-memory-warning"));
1147 if (AudioEngine::instance()->is_realtime() && memory_warning_node == 0) {
1149 struct rlimit limits;
1151 long pages, page_size;
1153 size_t pages_len=sizeof(pages);
1154 if ((page_size = getpagesize()) < 0 ||
1155 sysctlbyname("hw.availpages", &pages, &pages_len, NULL, 0))
1157 if ((page_size = sysconf (_SC_PAGESIZE)) < 0 ||(pages = sysconf (_SC_PHYS_PAGES)) < 0)
1162 ram = (int64_t) pages * (int64_t) page_size;
1165 if (getrlimit (RLIMIT_MEMLOCK, &limits)) {
1169 if (limits.rlim_cur != RLIM_INFINITY) {
1171 if (ram == 0 || ((double) limits.rlim_cur / ram) < 0.75) {
1175 _("WARNING: Your system has a limit for maximum amount of locked memory. "
1176 "This might cause %1 to run out of memory before your system "
1177 "runs out of memory. \n\n"
1178 "You can view the memory limit with 'ulimit -l', "
1179 "and it is normally controlled by %2"),
1182 X_("/etc/login.conf")
1184 X_(" /etc/security/limits.conf")
1188 msg.set_default_response (RESPONSE_OK);
1190 VBox* vbox = msg.get_vbox();
1192 CheckButton cb (_("Do not show this window again"));
1193 hbox.pack_start (cb, true, false);
1194 vbox->pack_start (hbox);
1199 pop_back_splash (msg);
1203 if (cb.get_active()) {
1204 XMLNode node (X_("no-memory-warning"));
1205 Config->add_instant_xml (node);
1210 #endif // !__APPLE__
1215 ARDOUR_UI::queue_finish ()
1217 Glib::signal_idle().connect (mem_fun (*this, &ARDOUR_UI::idle_finish));
1221 ARDOUR_UI::idle_finish ()
1224 return false; /* do not call again */
1231 ARDOUR_UI::instance()->video_timeline->sync_session_state();
1233 if (_session->dirty()) {
1234 vector<string> actions;
1235 actions.push_back (_("Don't quit"));
1236 actions.push_back (_("Just quit"));
1237 actions.push_back (_("Save and quit"));
1238 switch (ask_about_saving_session(actions)) {
1243 /* use the default name */
1244 if (save_state_canfail ("")) {
1245 /* failed - don't quit */
1246 MessageDialog msg (_main_window,
1247 string_compose (_("\
1248 %1 was unable to save your session.\n\n\
1249 If you still wish to quit, please use the\n\n\
1250 \"Just quit\" option."), PROGRAM_NAME));
1251 pop_back_splash(msg);
1261 second_connection.disconnect ();
1262 point_one_second_connection.disconnect ();
1263 point_zero_something_second_connection.disconnect();
1264 fps_connection.disconnect();
1267 delete ARDOUR_UI::instance()->video_timeline;
1268 ARDOUR_UI::instance()->video_timeline = NULL;
1269 stop_video_server();
1271 /* Save state before deleting the session, as that causes some
1272 windows to be destroyed before their visible state can be
1275 save_ardour_state ();
1277 if (key_editor.get (false)) {
1278 key_editor->disconnect ();
1281 close_all_dialogs ();
1284 _session->set_clean ();
1285 _session->remove_pending_capture_state ();
1290 halt_connection.disconnect ();
1291 AudioEngine::instance()->stop ();
1292 #ifdef WINDOWS_VST_SUPPORT
1293 fst_stop_threading();
1299 ARDOUR_UI::ask_about_saving_session (const vector<string>& actions)
1301 ArdourDialog window (_("Unsaved Session"));
1302 Gtk::HBox dhbox; // the hbox for the image and text
1303 Gtk::Label prompt_label;
1304 Gtk::Image* dimage = manage (new Gtk::Image(Stock::DIALOG_WARNING, Gtk::ICON_SIZE_DIALOG));
1308 assert (actions.size() >= 3);
1310 window.add_button (actions[0], RESPONSE_REJECT);
1311 window.add_button (actions[1], RESPONSE_APPLY);
1312 window.add_button (actions[2], RESPONSE_ACCEPT);
1314 window.set_default_response (RESPONSE_ACCEPT);
1316 Gtk::Button noquit_button (msg);
1317 noquit_button.set_name ("EditorGTKButton");
1321 if (_session->snap_name() == _session->name()) {
1322 prompt = string_compose(_("The session \"%1\"\nhas not been saved.\n\nAny changes made this time\nwill be lost unless you save it.\n\nWhat do you want to do?"),
1323 _session->snap_name());
1325 prompt = string_compose(_("The snapshot \"%1\"\nhas not been saved.\n\nAny changes made this time\nwill be lost unless you save it.\n\nWhat do you want to do?"),
1326 _session->snap_name());
1329 prompt_label.set_text (prompt);
1330 prompt_label.set_name (X_("PrompterLabel"));
1331 prompt_label.set_alignment(ALIGN_LEFT, ALIGN_TOP);
1333 dimage->set_alignment(ALIGN_CENTER, ALIGN_TOP);
1334 dhbox.set_homogeneous (false);
1335 dhbox.pack_start (*dimage, false, false, 5);
1336 dhbox.pack_start (prompt_label, true, false, 5);
1337 window.get_vbox()->pack_start (dhbox);
1339 window.set_name (_("Prompter"));
1340 window.set_modal (true);
1341 window.set_resizable (false);
1344 prompt_label.show();
1349 ResponseType r = (ResponseType) window.run();
1354 case RESPONSE_ACCEPT: // save and get out of here
1356 case RESPONSE_APPLY: // get out of here
1367 ARDOUR_UI::every_second ()
1370 update_xrun_count ();
1371 update_buffer_load ();
1372 update_disk_space ();
1373 update_timecode_format ();
1374 update_peak_thread_work ();
1376 if (nsm && nsm->is_active ()) {
1379 if (!_was_dirty && _session->dirty ()) {
1383 else if (_was_dirty && !_session->dirty ()){
1391 ARDOUR_UI::every_point_one_seconds ()
1393 // TODO get rid of this..
1394 // ShuttleControl is updated directly via TransportStateChange signal
1398 ARDOUR_UI::every_point_zero_something_seconds ()
1400 // august 2007: actual update frequency: 25Hz (40ms), not 100Hz
1402 if (editor_meter && UIConfiguration::instance().get_show_editor_meter() && editor_meter_peak_display.is_mapped ()) {
1403 float mpeak = editor_meter->update_meters();
1404 if (mpeak > editor_meter_max_peak) {
1405 if (mpeak >= UIConfiguration::instance().get_meter_peak()) {
1406 editor_meter_peak_display.set_active_state ( Gtkmm2ext::ExplicitActive );
1413 ARDOUR_UI::set_fps_timeout_connection ()
1415 unsigned int interval = 40;
1416 if (!_session) return;
1417 if (_session->timecode_frames_per_second() != 0) {
1418 /* ideally we'll use a select() to sleep and not accumulate
1419 * idle time to provide a regular periodic signal.
1420 * See linux_vst_gui_support.cc 'elapsed_time_ms'.
1421 * However, that'll require a dedicated thread and cross-thread
1422 * signals to the GUI Thread..
1424 interval = floor(500. /* update twice per FPS, since Glib::signal_timeout is very irregular */
1425 * _session->frame_rate() / _session->nominal_frame_rate()
1426 / _session->timecode_frames_per_second()
1428 #ifdef PLATFORM_WINDOWS
1429 // the smallest windows scheduler time-slice is ~15ms.
1430 // periodic GUI timeouts shorter than that will cause
1431 // WaitForSingleObject to spinlock (100% of one CPU Core)
1432 // and gtk never enters idle mode.
1433 // also changing timeBeginPeriod(1) does not affect that in
1434 // any beneficial way, so we just limit the max rate for now.
1435 interval = std::max(30u, interval); // at most ~33Hz.
1437 interval = std::max(8u, interval); // at most 120Hz.
1440 fps_connection.disconnect();
1441 Timers::set_fps_interval (interval);
1445 ARDOUR_UI::update_sample_rate (framecnt_t)
1449 ENSURE_GUI_THREAD (*this, &ARDOUR_UI::update_sample_rate, ignored)
1451 if (!AudioEngine::instance()->connected()) {
1453 snprintf (buf, sizeof (buf), "%s", _("Audio: <span foreground=\"red\">none</span>"));
1457 framecnt_t rate = AudioEngine::instance()->sample_rate();
1460 /* no sample rate available */
1461 snprintf (buf, sizeof (buf), "%s", _("Audio: <span foreground=\"red\">none</span>"));
1464 if (fmod (rate, 1000.0) != 0.0) {
1465 snprintf (buf, sizeof (buf), _("Audio: <span foreground=\"green\">%.1f kHz / %4.1f ms</span>"),
1466 (float) rate / 1000.0f,
1467 (AudioEngine::instance()->usecs_per_cycle() / 1000.0f));
1469 snprintf (buf, sizeof (buf), _("Audio: <span foreground=\"green\">%" PRId64 " kHz / %4.1f ms</span>"),
1471 (AudioEngine::instance()->usecs_per_cycle() / 1000.0f));
1475 sample_rate_label.set_markup (buf);
1479 ARDOUR_UI::update_format ()
1482 format_label.set_text ("");
1487 s << _("File:") << X_(" <span foreground=\"green\">");
1489 switch (_session->config.get_native_file_header_format ()) {
1521 switch (_session->config.get_native_file_data_format ()) {
1535 format_label.set_markup (s.str ());
1539 ARDOUR_UI::update_xrun_count ()
1543 /* If this text is changed, the set_size_request_to_display_given_text call in ARDOUR_UI::resize_text_widgets
1544 should also be changed.
1548 const unsigned int x = _session->get_xrun_count ();
1550 snprintf (buf, sizeof (buf), _("X: <span foreground=\"%s\">>10K</span>"), X_("red"));
1552 snprintf (buf, sizeof (buf), _("X: <span foreground=\"%s\">%u</span>"), x > 0 ? X_("red") : X_("green"), x);
1555 snprintf (buf, sizeof (buf), _("X: <span foreground=\"%s\">?</span>"), X_("yellow"));
1557 xrun_label.set_markup (buf);
1558 set_tip (xrun_label, _("Audio dropouts. Shift+click to reset"));
1562 ARDOUR_UI::update_cpu_load ()
1566 /* If this text is changed, the set_size_request_to_display_given_text call in ARDOUR_UI::resize_text_widgets
1567 should also be changed.
1570 double const c = AudioEngine::instance()->get_dsp_load ();
1571 snprintf (buf, sizeof (buf), _("DSP: <span foreground=\"%s\">%5.1f%%</span>"), c >= 90 ? X_("red") : X_("green"), c);
1572 cpu_load_label.set_markup (buf);
1576 ARDOUR_UI::update_peak_thread_work ()
1579 const int c = SourceFactory::peak_work_queue_length ();
1581 snprintf (buf, sizeof (buf), _("PkBld: <span foreground=\"%s\">%d</span>"), c >= 2 ? X_("red") : X_("green"), c);
1582 peak_thread_work_label.set_markup (buf);
1584 peak_thread_work_label.set_markup (X_(""));
1589 ARDOUR_UI::update_buffer_load ()
1593 uint32_t const playback = _session ? _session->playback_load () : 100;
1594 uint32_t const capture = _session ? _session->capture_load () : 100;
1596 /* If this text is changed, the set_size_request_to_display_given_text call in ARDOUR_UI::resize_text_widgets
1597 should also be changed.
1603 _("Buffers: <span foreground=\"green\">p:</span><span foreground=\"%s\">%" PRIu32 "%%</span> "
1604 "<span foreground=\"green\">c:</span><span foreground=\"%s\">%" PRIu32 "%%</span>"),
1605 playback <= 5 ? X_("red") : X_("green"),
1607 capture <= 5 ? X_("red") : X_("green"),
1611 buffer_load_label.set_markup (buf);
1613 buffer_load_label.set_text ("");
1618 ARDOUR_UI::count_recenabled_streams (Route& route)
1620 Track* track = dynamic_cast<Track*>(&route);
1621 if (track && track->rec_enable_control()->get_value()) {
1622 rec_enabled_streams += track->n_inputs().n_total();
1627 ARDOUR_UI::update_disk_space()
1629 if (_session == 0) {
1633 boost::optional<framecnt_t> opt_frames = _session->available_capture_duration();
1635 framecnt_t fr = _session->frame_rate();
1638 /* skip update - no SR available */
1643 /* Available space is unknown */
1644 snprintf (buf, sizeof (buf), "%s", _("Disk: <span foreground=\"green\">Unknown</span>"));
1645 } else if (opt_frames.get_value_or (0) == max_framecnt) {
1646 snprintf (buf, sizeof (buf), "%s", _("Disk: <span foreground=\"green\">24hrs+</span>"));
1648 rec_enabled_streams = 0;
1649 _session->foreach_route (this, &ARDOUR_UI::count_recenabled_streams, false);
1651 framecnt_t frames = opt_frames.get_value_or (0);
1653 if (rec_enabled_streams) {
1654 frames /= rec_enabled_streams;
1661 hrs = frames / (fr * 3600);
1664 snprintf (buf, sizeof (buf), "%s", _("Disk: <span foreground=\"green\">>24 hrs</span>"));
1666 frames -= hrs * fr * 3600;
1667 mins = frames / (fr * 60);
1668 frames -= mins * fr * 60;
1671 bool const low = (hrs == 0 && mins <= 30);
1675 _("Disk: <span foreground=\"%s\">%02dh:%02dm:%02ds</span>"),
1676 low ? X_("red") : X_("green"),
1682 disk_space_label.set_markup (buf);
1686 ARDOUR_UI::update_timecode_format ()
1692 TimecodeSlave* tcslave;
1693 SyncSource sync_src = Config->get_sync_source();
1695 if ((sync_src == LTC || sync_src == MTC) && (tcslave = dynamic_cast<TimecodeSlave*>(_session->slave())) != 0) {
1696 matching = (tcslave->apparent_timecode_format() == _session->config.get_timecode_format());
1701 snprintf (buf, sizeof (buf), S_("Timecode|TC: <span foreground=\"%s\">%s</span>"),
1702 matching ? X_("green") : X_("red"),
1703 Timecode::timecode_format_name (_session->config.get_timecode_format()).c_str());
1705 snprintf (buf, sizeof (buf), "TC: n/a");
1708 timecode_format_label.set_markup (buf);
1712 ARDOUR_UI::update_wall_clock ()
1716 static int last_min = -1;
1719 tm_now = localtime (&now);
1720 if (last_min != tm_now->tm_min) {
1722 sprintf (buf, "%02d:%02d", tm_now->tm_hour, tm_now->tm_min);
1723 wall_clock_label.set_text (buf);
1724 last_min = tm_now->tm_min;
1731 ARDOUR_UI::open_recent_session ()
1733 bool can_return = (_session != 0);
1735 SessionDialog recent_session_dialog;
1739 ResponseType r = (ResponseType) recent_session_dialog.run ();
1742 case RESPONSE_ACCEPT:
1746 recent_session_dialog.hide();
1753 recent_session_dialog.hide();
1757 std::string path = recent_session_dialog.session_folder();
1758 std::string state = recent_session_dialog.session_name (should_be_new);
1760 if (should_be_new == true) {
1764 _session_is_new = false;
1766 if (load_session (path, state) == 0) {
1772 if (splash && splash->is_visible()) {
1773 // in 1 second, hide the splash screen
1774 Glib::signal_timeout().connect (sigc::bind (sigc::ptr_fun (_hide_splash), this), 1000);
1779 ARDOUR_UI::check_audioengine (Gtk::Window& parent)
1781 if (!AudioEngine::instance()->connected()) {
1782 MessageDialog msg (parent, string_compose (
1783 _("%1 is not connected to any audio backend.\n"
1784 "You cannot open or close sessions in this condition"),
1786 pop_back_splash (msg);
1794 ARDOUR_UI::open_session ()
1796 if (!check_audioengine (_main_window)) {
1800 /* ardour sessions are folders */
1801 Gtk::FileChooserDialog open_session_selector(_("Open Session"), FILE_CHOOSER_ACTION_OPEN);
1802 open_session_selector.add_button (Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
1803 open_session_selector.add_button (Gtk::Stock::OPEN, Gtk::RESPONSE_ACCEPT);
1804 open_session_selector.set_default_response(Gtk::RESPONSE_ACCEPT);
1807 string session_parent_dir = Glib::path_get_dirname(_session->path());
1808 open_session_selector.set_current_folder(session_parent_dir);
1810 open_session_selector.set_current_folder(Config->get_default_session_parent_dir());
1813 Gtkmm2ext::add_volume_shortcuts (open_session_selector);
1815 /* add_shortcut_folder throws an exception if the folder being added already has a shortcut */
1816 string default_session_folder = Config->get_default_session_parent_dir();
1817 open_session_selector.add_shortcut_folder (default_session_folder);
1819 catch (Glib::Error & e) {
1820 std::cerr << "open_session_selector.add_shortcut_folder() threw Glib::Error " << e.what() << std::endl;
1823 FileFilter session_filter;
1824 session_filter.add_pattern (string_compose(X_("*%1"), ARDOUR::statefile_suffix));
1825 session_filter.set_name (string_compose (_("%1 sessions"), PROGRAM_NAME));
1826 open_session_selector.add_filter (session_filter);
1828 FileFilter archive_filter;
1829 archive_filter.add_pattern (X_("*.tar.xz"));
1830 archive_filter.set_name (_("Session Archives"));
1832 open_session_selector.add_filter (archive_filter);
1834 open_session_selector.set_filter (session_filter);
1836 int response = open_session_selector.run();
1837 open_session_selector.hide ();
1839 if (response == Gtk::RESPONSE_CANCEL) {
1843 string session_path = open_session_selector.get_filename();
1847 if (session_path.length() > 0) {
1848 int rv = ARDOUR::inflate_session (session_path,
1849 Config->get_default_session_parent_dir(), path, name);
1851 _session_is_new = false;
1852 load_session (path, name);
1855 MessageDialog msg (_main_window,
1856 string_compose (_("Extracting session-archive failed: %1"), inflate_error (rv)));
1859 else if (ARDOUR::find_session (session_path, path, name, isnew) == 0) {
1860 _session_is_new = isnew;
1861 load_session (path, name);
1867 ARDOUR_UI::session_add_vca (const string& name_template, uint32_t n)
1873 _session->vca_manager().create_vca (n, name_template);
1877 ARDOUR_UI::session_add_mixed_track (
1878 const ChanCount& input,
1879 const ChanCount& output,
1880 RouteGroup* route_group,
1882 const string& name_template,
1884 PluginInfoPtr instrument,
1885 Plugin::PresetRecord* pset,
1886 ARDOUR::PresentationInfo::order_t order)
1888 if (_session == 0) {
1889 warning << _("You cannot add a track without a session already loaded.") << endmsg;
1893 if (Profile->get_mixbus ()) {
1898 list<boost::shared_ptr<MidiTrack> > tracks;
1899 tracks = _session->new_midi_track (input, output, strict_io, instrument, pset, route_group, how_many, name_template, order, ARDOUR::Normal);
1901 if (tracks.size() != how_many) {
1902 error << string_compose(P_("could not create %1 new mixed track", "could not create %1 new mixed tracks", how_many), how_many) << endmsg;
1907 display_insufficient_ports_message ();
1913 ARDOUR_UI::session_add_midi_bus (
1914 RouteGroup* route_group,
1916 const string& name_template,
1918 PluginInfoPtr instrument,
1919 Plugin::PresetRecord* pset,
1920 ARDOUR::PresentationInfo::order_t order)
1922 if (_session == 0) {
1923 warning << _("You cannot add a track without a session already loaded.") << endmsg;
1927 if (Profile->get_mixbus ()) {
1933 routes = _session->new_midi_route (route_group, how_many, name_template, strict_io, instrument, pset, PresentationInfo::MidiBus, order);
1934 if (routes.size() != how_many) {
1935 error << string_compose(P_("could not create %1 new Midi Bus", "could not create %1 new Midi Busses", how_many), how_many) << endmsg;
1940 display_insufficient_ports_message ();
1946 ARDOUR_UI::session_add_midi_route (
1948 RouteGroup* route_group,
1950 const string& name_template,
1952 PluginInfoPtr instrument,
1953 Plugin::PresetRecord* pset,
1954 ARDOUR::PresentationInfo::order_t order)
1956 ChanCount one_midi_channel;
1957 one_midi_channel.set (DataType::MIDI, 1);
1960 session_add_mixed_track (one_midi_channel, one_midi_channel, route_group, how_many, name_template, strict_io, instrument, pset, order);
1962 session_add_midi_bus (route_group, how_many, name_template, strict_io, instrument, pset, order);
1967 ARDOUR_UI::session_add_audio_route (
1969 int32_t input_channels,
1970 int32_t output_channels,
1971 ARDOUR::TrackMode mode,
1972 RouteGroup* route_group,
1974 string const & name_template,
1976 ARDOUR::PresentationInfo::order_t order)
1978 list<boost::shared_ptr<AudioTrack> > tracks;
1981 if (_session == 0) {
1982 warning << _("You cannot add a track or bus without a session already loaded.") << endmsg;
1988 tracks = _session->new_audio_track (input_channels, output_channels, route_group, how_many, name_template, order, mode);
1990 if (tracks.size() != how_many) {
1991 error << string_compose (P_("could not create %1 new audio track", "could not create %1 new audio tracks", how_many), how_many)
1997 routes = _session->new_audio_route (input_channels, output_channels, route_group, how_many, name_template, PresentationInfo::AudioBus, order);
1999 if (routes.size() != how_many) {
2000 error << string_compose (P_("could not create %1 new audio bus", "could not create %1 new audio busses", how_many), how_many)
2007 display_insufficient_ports_message ();
2012 for (list<boost::shared_ptr<AudioTrack> >::iterator i = tracks.begin(); i != tracks.end(); ++i) {
2013 (*i)->set_strict_io (true);
2015 for (RouteList::iterator i = routes.begin(); i != routes.end(); ++i) {
2016 (*i)->set_strict_io (true);
2022 ARDOUR_UI::display_insufficient_ports_message ()
2024 MessageDialog msg (_main_window,
2025 string_compose (_("There are insufficient ports available\n\
2026 to create a new track or bus.\n\
2027 You should save %1, exit and\n\
2028 restart with more ports."), PROGRAM_NAME));
2029 pop_back_splash (msg);
2034 ARDOUR_UI::transport_goto_start ()
2037 _session->goto_start();
2039 /* force displayed area in editor to start no matter
2040 what "follow playhead" setting is.
2044 editor->center_screen (_session->current_start_frame ());
2050 ARDOUR_UI::transport_goto_zero ()
2053 _session->request_locate (0);
2055 /* force displayed area in editor to start no matter
2056 what "follow playhead" setting is.
2060 editor->reset_x_origin (0);
2066 ARDOUR_UI::transport_goto_wallclock ()
2068 if (_session && editor) {
2075 localtime_r (&now, &tmnow);
2077 framecnt_t frame_rate = _session->frame_rate();
2079 if (frame_rate == 0) {
2080 /* no frame rate available */
2084 frames = tmnow.tm_hour * (60 * 60 * frame_rate);
2085 frames += tmnow.tm_min * (60 * frame_rate);
2086 frames += tmnow.tm_sec * frame_rate;
2088 _session->request_locate (frames, _session->transport_rolling ());
2090 /* force displayed area in editor to start no matter
2091 what "follow playhead" setting is.
2095 editor->center_screen (frames);
2101 ARDOUR_UI::transport_goto_end ()
2104 framepos_t const frame = _session->current_end_frame();
2105 _session->request_locate (frame);
2107 /* force displayed area in editor to start no matter
2108 what "follow playhead" setting is.
2112 editor->center_screen (frame);
2118 ARDOUR_UI::transport_stop ()
2124 if (_session->is_auditioning()) {
2125 _session->cancel_audition ();
2129 _session->request_stop (false, true);
2132 /** Check if any tracks are record enabled. If none are, record enable all of them.
2133 * @return true if track record-enabled status was changed, false otherwise.
2136 ARDOUR_UI::trx_record_enable_all_tracks ()
2142 boost::shared_ptr<RouteList> rl = _session->get_tracks ();
2143 bool none_record_enabled = true;
2145 for (RouteList::iterator r = rl->begin(); r != rl->end(); ++r) {
2146 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (*r);
2149 if (t->rec_enable_control()->get_value()) {
2150 none_record_enabled = false;
2155 if (none_record_enabled) {
2156 _session->set_controls (route_list_to_control_list (rl, &Stripable::rec_enable_control), 1.0, Controllable::NoGroup);
2159 return none_record_enabled;
2163 ARDOUR_UI::transport_record (bool roll)
2166 switch (_session->record_status()) {
2167 case Session::Disabled:
2168 if (_session->ntracks() == 0) {
2169 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."));
2173 if (Profile->get_trx()) {
2174 roll = trx_record_enable_all_tracks ();
2176 _session->maybe_enable_record ();
2181 case Session::Recording:
2183 _session->request_stop();
2185 _session->disable_record (false, true);
2189 case Session::Enabled:
2190 _session->disable_record (false, true);
2196 ARDOUR_UI::transport_roll ()
2202 if (_session->is_auditioning()) {
2207 if (_session->config.get_external_sync()) {
2208 switch (Config->get_sync_source()) {
2212 /* transport controlled by the master */
2218 bool rolling = _session->transport_rolling();
2220 if (_session->get_play_loop()) {
2222 /* If loop playback is not a mode, then we should cancel
2223 it when this action is requested. If it is a mode
2224 we just leave it in place.
2227 if (!Config->get_loop_is_mode()) {
2228 /* XXX it is not possible to just leave seamless loop and keep
2229 playing at present (nov 4th 2009)
2231 if (!Config->get_seamless_loop()) {
2232 /* stop loop playback and stop rolling */
2233 _session->request_play_loop (false, true);
2234 } else if (rolling) {
2235 /* stop loop playback but keep rolling */
2236 _session->request_play_loop (false, false);
2240 } else if (_session->get_play_range () ) {
2241 /* stop playing a range if we currently are */
2242 _session->request_play_range (0, true);
2246 _session->request_transport_speed (1.0f);
2251 ARDOUR_UI::get_smart_mode() const
2253 return ( editor->get_smart_mode() );
2258 ARDOUR_UI::toggle_roll (bool with_abort, bool roll_out_of_bounded_mode)
2264 if (_session->is_auditioning()) {
2265 _session->cancel_audition ();
2269 if (_session->config.get_external_sync()) {
2270 switch (Config->get_sync_source()) {
2274 /* transport controlled by the master */
2279 bool rolling = _session->transport_rolling();
2280 bool affect_transport = true;
2282 if (rolling && roll_out_of_bounded_mode) {
2283 /* drop out of loop/range playback but leave transport rolling */
2284 if (_session->get_play_loop()) {
2285 if (_session->actively_recording()) {
2287 /* just stop using the loop, then actually stop
2290 _session->request_play_loop (false, affect_transport);
2293 if (Config->get_seamless_loop()) {
2294 /* the disk buffers contain copies of the loop - we can't
2295 just keep playing, so stop the transport. the user
2296 can restart as they wish.
2298 affect_transport = true;
2300 /* disk buffers are normal, so we can keep playing */
2301 affect_transport = false;
2303 _session->request_play_loop (false, affect_transport);
2305 } else if (_session->get_play_range ()) {
2306 affect_transport = false;
2307 _session->request_play_range (0, true);
2311 if (affect_transport) {
2313 _session->request_stop (with_abort, true);
2315 } else if (!with_abort) { /* with_abort == true means the
2316 * command was intended to stop
2317 * transport, not start.
2320 /* the only external sync condition we can be in here
2321 * would be Engine (JACK) sync, in which case we still
2325 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
2326 _session->request_play_range (&editor->get_selection().time, true);
2327 _session->set_requested_return_frame( editor->get_selection().time.front().start ); //force an auto-return here
2329 _session->request_transport_speed (1.0f);
2335 ARDOUR_UI::toggle_session_auto_loop ()
2341 Location * looploc = _session->locations()->auto_loop_location();
2347 if (_session->get_play_loop()) {
2349 /* looping enabled, our job is to disable it */
2351 _session->request_play_loop (false);
2355 /* looping not enabled, our job is to enable it.
2357 loop-is-NOT-mode: this action always starts the transport rolling.
2358 loop-IS-mode: this action simply sets the loop play mechanism, but
2359 does not start transport.
2361 if (Config->get_loop_is_mode()) {
2362 _session->request_play_loop (true, false);
2364 _session->request_play_loop (true, true);
2368 //show the loop markers
2369 looploc->set_hidden (false, this);
2373 ARDOUR_UI::transport_play_selection ()
2379 editor->play_selection ();
2383 ARDOUR_UI::transport_play_preroll ()
2388 editor->play_with_preroll ();
2392 ARDOUR_UI::transport_rec_preroll ()
2397 editor->rec_with_preroll ();
2401 ARDOUR_UI::transport_rec_count_in ()
2406 editor->rec_with_count_in ();
2410 ARDOUR_UI::transport_rewind (int option)
2412 float current_transport_speed;
2415 current_transport_speed = _session->transport_speed();
2417 if (current_transport_speed >= 0.0f) {
2420 _session->request_transport_speed (-1.0f);
2423 _session->request_transport_speed (-4.0f);
2426 _session->request_transport_speed (-0.5f);
2431 _session->request_transport_speed (current_transport_speed * 1.5f);
2437 ARDOUR_UI::transport_forward (int option)
2443 float current_transport_speed = _session->transport_speed();
2445 if (current_transport_speed <= 0.0f) {
2448 _session->request_transport_speed (1.0f);
2451 _session->request_transport_speed (4.0f);
2454 _session->request_transport_speed (0.5f);
2459 _session->request_transport_speed (current_transport_speed * 1.5f);
2464 ARDOUR_UI::toggle_record_enable (uint16_t rid)
2470 boost::shared_ptr<Route> r;
2472 if ((r = _session->get_remote_nth_route (rid)) != 0) {
2474 boost::shared_ptr<Track> t;
2476 if ((t = boost::dynamic_pointer_cast<Track>(r)) != 0) {
2477 t->rec_enable_control()->set_value (!t->rec_enable_control()->get_value(), Controllable::UseGroup);
2483 ARDOUR_UI::map_transport_state ()
2486 auto_loop_button.unset_active_state ();
2487 play_selection_button.unset_active_state ();
2488 roll_button.unset_active_state ();
2489 stop_button.set_active_state (Gtkmm2ext::ExplicitActive);
2490 layered_button.set_sensitive (false);
2494 shuttle_box.map_transport_state ();
2496 float sp = _session->transport_speed();
2502 if (_session->get_play_range()) {
2504 play_selection_button.set_active_state (Gtkmm2ext::ExplicitActive);
2505 roll_button.unset_active_state ();
2506 auto_loop_button.unset_active_state ();
2508 } else if (_session->get_play_loop ()) {
2510 auto_loop_button.set_active (true);
2511 play_selection_button.set_active (false);
2512 if (Config->get_loop_is_mode()) {
2513 roll_button.set_active (true);
2515 roll_button.set_active (false);
2520 roll_button.set_active (true);
2521 play_selection_button.set_active (false);
2522 auto_loop_button.set_active (false);
2525 if (UIConfiguration::instance().get_follow_edits() && !_session->config.get_external_sync()) {
2526 /* light up both roll and play-selection if they are joined */
2527 roll_button.set_active (true);
2528 play_selection_button.set_active (true);
2530 layered_button.set_sensitive (!_session->actively_recording ());
2532 stop_button.set_active (false);
2536 layered_button.set_sensitive (true);
2537 stop_button.set_active (true);
2538 roll_button.set_active (false);
2539 play_selection_button.set_active (false);
2540 if (Config->get_loop_is_mode ()) {
2541 auto_loop_button.set_active (_session->get_play_loop());
2543 auto_loop_button.set_active (false);
2545 update_disk_space ();
2550 ARDOUR_UI::blink_handler (bool blink_on)
2552 transport_rec_enable_blink (blink_on);
2553 solo_blink (blink_on);
2554 sync_blink (blink_on);
2555 audition_blink (blink_on);
2556 feedback_blink (blink_on);
2557 error_blink (blink_on);
2561 ARDOUR_UI::update_clocks ()
2563 if (!_session) return;
2565 if (editor && !editor->dragging_playhead()) {
2566 Clock (_session->audible_frame(), false, editor->get_preferred_edit_position (EDIT_IGNORE_PHEAD)); /* EMIT_SIGNAL */
2571 ARDOUR_UI::start_clocking ()
2573 if (UIConfiguration::instance().get_super_rapid_clock_update()) {
2574 clock_signal_connection = Timers::fps_connect (sigc::mem_fun(*this, &ARDOUR_UI::update_clocks));
2576 clock_signal_connection = Timers::rapid_connect (sigc::mem_fun(*this, &ARDOUR_UI::update_clocks));
2581 ARDOUR_UI::stop_clocking ()
2583 clock_signal_connection.disconnect ();
2587 ARDOUR_UI::save_as_progress_update (float fraction, int64_t cnt, int64_t total, Gtk::Label* label, Gtk::ProgressBar* bar)
2591 snprintf (buf, sizeof (buf), _("Copied %" PRId64 " of %" PRId64), cnt, total);
2593 label->set_text (buf);
2594 bar->set_fraction (fraction);
2596 /* process events, redraws, etc. */
2598 while (gtk_events_pending()) {
2599 gtk_main_iteration ();
2602 return true; /* continue with save-as */
2606 ARDOUR_UI::save_session_as ()
2612 if (!save_as_dialog) {
2613 save_as_dialog = new SaveAsDialog;
2616 save_as_dialog->set_name (_session->name());
2618 int response = save_as_dialog->run ();
2620 save_as_dialog->hide ();
2623 case Gtk::RESPONSE_OK:
2632 sa.new_parent_folder = save_as_dialog->new_parent_folder ();
2633 sa.new_name = save_as_dialog->new_name ();
2634 sa.switch_to = save_as_dialog->switch_to();
2635 sa.copy_media = save_as_dialog->copy_media();
2636 sa.copy_external = save_as_dialog->copy_external();
2637 sa.include_media = save_as_dialog->include_media ();
2639 /* Only bother with a progress dialog if we're going to copy
2640 media into the save-as target. Without that choice, this
2641 will be very fast because we're only talking about a few kB's to
2642 perhaps a couple of MB's of data.
2645 ArdourDialog progress_dialog (_("Save As"), true);
2647 if (sa.include_media && sa.copy_media) {
2650 Gtk::ProgressBar progress_bar;
2652 progress_dialog.get_vbox()->pack_start (label);
2653 progress_dialog.get_vbox()->pack_start (progress_bar);
2655 progress_bar.show ();
2657 /* this signal will be emitted from within this, the calling thread,
2658 * after every file is copied. It provides information on percentage
2659 * complete (in terms of total data to copy), the number of files
2660 * copied so far, and the total number to copy.
2665 sa.Progress.connect_same_thread (c, boost::bind (&ARDOUR_UI::save_as_progress_update, this, _1, _2, _3, &label, &progress_bar));
2667 progress_dialog.show_all ();
2668 progress_dialog.present ();
2671 if (_session->save_as (sa)) {
2673 MessageDialog msg (string_compose (_("Save As failed: %1"), sa.failure_message));
2677 /* the logic here may seem odd: why isn't the condition sa.switch_to ?
2678 * the trick is this: if the new session was copy with media included,
2679 * then Session::save_as() will have already done a neat trick to avoid
2680 * us having to unload and load the new state. But if the media was not
2681 * included, then this is required (it avoids us having to otherwise
2682 * drop all references to media (sources).
2685 if (!sa.include_media && sa.switch_to) {
2686 unload_session (false);
2687 load_session (sa.final_session_folder_name, sa.new_name);
2693 ARDOUR_UI::archive_session ()
2701 Glib::DateTime gdt (Glib::DateTime::create_now_local (n));
2703 SessionArchiveDialog sad;
2704 sad.set_name (_session->name() + gdt.format ("_%F_%H%M%S"));
2705 int response = sad.run ();
2707 if (response != Gtk::RESPONSE_OK) {
2712 if (_session->archive_session (sad.target_folder(), sad.name(), sad.encode_option (), sad.only_used_sources (), &sad)) {
2713 MessageDialog msg (_("Session Archiving failed."));
2719 ARDOUR_UI::quick_snapshot_session (bool switch_to_it)
2723 struct tm local_time;
2726 localtime_r (&n, &local_time);
2727 strftime (timebuf, sizeof(timebuf), "%FT%H.%M.%S", &local_time);
2729 save_state (timebuf, switch_to_it);
2734 ARDOUR_UI::process_snapshot_session_prompter (ArdourPrompter& prompter, bool switch_to_it)
2738 prompter.get_result (snapname);
2740 bool do_save = (snapname.length() != 0);
2743 char illegal = Session::session_name_is_legal(snapname);
2745 MessageDialog msg (string_compose (_("To ensure compatibility with various systems\n"
2746 "snapshot names may not contain a '%1' character"), illegal));
2752 vector<std::string> p;
2753 get_state_files_in_directory (_session->session_directory().root_path(), p);
2754 vector<string> n = get_file_names_no_extension (p);
2756 if (find (n.begin(), n.end(), snapname) != n.end()) {
2758 do_save = overwrite_file_dialog (prompter,
2759 _("Confirm Snapshot Overwrite"),
2760 _("A snapshot already exists with that name. Do you want to overwrite it?"));
2764 save_state (snapname, switch_to_it);
2774 /** Ask the user for the name of a new snapshot and then take it.
2778 ARDOUR_UI::snapshot_session (bool switch_to_it)
2780 ArdourPrompter prompter (true);
2782 prompter.set_name ("Prompter");
2783 prompter.add_button (Gtk::Stock::SAVE, Gtk::RESPONSE_ACCEPT);
2785 prompter.set_title (_("Snapshot and switch"));
2786 prompter.set_prompt (_("New session name"));
2788 prompter.set_title (_("Take Snapshot"));
2789 prompter.set_prompt (_("Name of new snapshot"));
2793 prompter.set_initial_text (_session->snap_name());
2795 Glib::DateTime tm (g_date_time_new_now_local ());
2796 prompter.set_initial_text (tm.format ("%FT%H.%M.%S"));
2799 bool finished = false;
2801 switch (prompter.run()) {
2802 case RESPONSE_ACCEPT:
2804 finished = process_snapshot_session_prompter (prompter, switch_to_it);
2815 /** Ask the user for a new session name and then rename the session to it.
2819 ARDOUR_UI::rename_session ()
2825 ArdourPrompter prompter (true);
2828 prompter.set_name ("Prompter");
2829 prompter.add_button (Gtk::Stock::SAVE, Gtk::RESPONSE_ACCEPT);
2830 prompter.set_title (_("Rename Session"));
2831 prompter.set_prompt (_("New session name"));
2834 switch (prompter.run()) {
2835 case RESPONSE_ACCEPT:
2837 prompter.get_result (name);
2839 bool do_rename = (name.length() != 0);
2842 char illegal = Session::session_name_is_legal (name);
2845 MessageDialog msg (string_compose (_("To ensure compatibility with various systems\n"
2846 "session names may not contain a '%1' character"), illegal));
2851 switch (_session->rename (name)) {
2853 MessageDialog msg (_("That name is already in use by another directory/folder. Please try again."));
2854 msg.set_position (WIN_POS_MOUSE);
2862 MessageDialog msg (_("Renaming this session failed.\nThings could be seriously messed up at this point"));
2863 msg.set_position (WIN_POS_MOUSE);
2879 ARDOUR_UI::save_state (const string & name, bool switch_to_it)
2881 if (!_session || _session->deletion_in_progress()) {
2885 XMLNode* node = new XMLNode (X_("UI"));
2887 WM::Manager::instance().add_state (*node);
2889 node->add_child_nocopy (gui_object_state->get_state());
2891 _session->add_extra_xml (*node);
2893 if (export_video_dialog) {
2894 _session->add_extra_xml (export_video_dialog->get_state());
2897 save_state_canfail (name, switch_to_it);
2901 ARDOUR_UI::save_state_canfail (string name, bool switch_to_it)
2906 if ((ret = _session->save_state (name, false, switch_to_it)) != 0) {
2911 save_ardour_state (); /* XXX cannot fail? yeah, right ... */
2916 ARDOUR_UI::primary_clock_value_changed ()
2919 _session->request_locate (primary_clock->current_time ());
2924 ARDOUR_UI::big_clock_value_changed ()
2927 _session->request_locate (big_clock->current_time ());
2932 ARDOUR_UI::secondary_clock_value_changed ()
2935 _session->request_locate (secondary_clock->current_time ());
2940 ARDOUR_UI::transport_rec_enable_blink (bool onoff)
2942 if (_session == 0) {
2946 if (_session->step_editing()) {
2950 Session::RecordState const r = _session->record_status ();
2951 bool const h = _session->have_rec_enabled_track ();
2953 if (r == Session::Enabled || (r == Session::Recording && !h)) {
2955 rec_button.set_active_state (Gtkmm2ext::ExplicitActive);
2957 rec_button.set_active_state (Gtkmm2ext::Off);
2959 } else if (r == Session::Recording && h) {
2960 rec_button.set_active_state (Gtkmm2ext::ExplicitActive);
2962 rec_button.unset_active_state ();
2967 ARDOUR_UI::process_save_template_prompter (ArdourPrompter& prompter)
2971 prompter.get_result (name);
2973 if (name.length()) {
2974 int failed = _session->save_template (name);
2976 if (failed == -2) { /* file already exists. */
2977 bool overwrite = overwrite_file_dialog (prompter,
2978 _("Confirm Template Overwrite"),
2979 _("A template already exists with that name. Do you want to overwrite it?"));
2982 _session->save_template (name, true);
2994 ARDOUR_UI::save_template ()
2996 ArdourPrompter prompter (true);
2998 if (!check_audioengine (_main_window)) {
3002 prompter.set_name (X_("Prompter"));
3003 prompter.set_title (_("Save Template"));
3004 prompter.set_prompt (_("Name for template:"));
3005 prompter.set_initial_text(_session->name() + _("-template"));
3006 prompter.add_button (Gtk::Stock::SAVE, Gtk::RESPONSE_ACCEPT);
3008 bool finished = false;
3010 switch (prompter.run()) {
3011 case RESPONSE_ACCEPT:
3012 finished = process_save_template_prompter (prompter);
3023 ARDOUR_UI::edit_metadata ()
3025 SessionMetadataEditor dialog;
3026 dialog.set_session (_session);
3027 dialog.grab_focus ();
3032 ARDOUR_UI::import_metadata ()
3034 SessionMetadataImporter dialog;
3035 dialog.set_session (_session);
3040 ARDOUR_UI::ask_about_loading_existing_session (const std::string& session_path)
3042 std::string str = string_compose (_("This session\n%1\nalready exists. Do you want to open it?"), session_path);
3044 MessageDialog msg (str,
3046 Gtk::MESSAGE_WARNING,
3047 Gtk::BUTTONS_YES_NO,
3051 msg.set_name (X_("OpenExistingDialog"));
3052 msg.set_title (_("Open Existing Session"));
3053 msg.set_wmclass (X_("existing_session"), PROGRAM_NAME);
3054 msg.set_position (Gtk::WIN_POS_CENTER);
3055 pop_back_splash (msg);
3057 switch (msg.run()) {
3066 ARDOUR_UI::build_session_from_dialog (SessionDialog& sd, const std::string& session_path, const std::string& session_name)
3068 BusProfile bus_profile;
3072 bus_profile.master_out_channels = 2;
3073 bus_profile.input_ac = AutoConnectPhysical;
3074 bus_profile.output_ac = AutoConnectMaster;
3075 bus_profile.requested_physical_in = 0; // use all available
3076 bus_profile.requested_physical_out = 0; // use all available
3080 /* get settings from advanced section of NSD */
3082 if (sd.create_master_bus()) {
3083 bus_profile.master_out_channels = (uint32_t) sd.master_channel_count();
3085 bus_profile.master_out_channels = 0;
3088 if (sd.connect_inputs()) {
3089 bus_profile.input_ac = AutoConnectPhysical;
3091 bus_profile.input_ac = AutoConnectOption (0);
3094 bus_profile.output_ac = AutoConnectOption (0);
3096 if (sd.connect_outputs ()) {
3097 if (sd.connect_outs_to_master()) {
3098 bus_profile.output_ac = AutoConnectMaster;
3099 } else if (sd.connect_outs_to_physical()) {
3100 bus_profile.output_ac = AutoConnectPhysical;
3104 bus_profile.requested_physical_in = (uint32_t) sd.input_limit_count();
3105 bus_profile.requested_physical_out = (uint32_t) sd.output_limit_count();
3108 if (build_session (session_path, session_name, bus_profile)) {
3116 ARDOUR_UI::load_from_application_api (const std::string& path)
3118 /* OS X El Capitan (and probably later) now somehow passes the command
3119 line arguments to an app via the openFile delegate protocol. Ardour
3120 already does its own command line processing, and having both
3121 pathways active causes crashes. So, if the command line was already
3122 set, do nothing here.
3125 if (!ARDOUR_COMMAND_LINE::session_name.empty()) {
3129 ARDOUR_COMMAND_LINE::session_name = path;
3131 /* Cancel SessionDialog if it's visible to make OSX delegates work.
3133 * ARDOUR_UI::starting connects app->ShouldLoad signal and then shows a SessionDialog
3135 * - ShouldLoad does not arrive in time, ARDOUR_COMMAND_LINE::session_name is empty:
3136 * -> ARDOUR_UI::get_session_parameters starts a SessionDialog.
3137 * - ShouldLoad signal arrives, this function is called and sets ARDOUR_COMMAND_LINE::session_name
3138 * -> SessionDialog is not displayed
3141 if (_session_dialog) {
3142 std::string session_name = basename_nosuffix (ARDOUR_COMMAND_LINE::session_name);
3143 std::string session_path = path;
3144 if (Glib::file_test (session_path, Glib::FILE_TEST_IS_REGULAR)) {
3145 session_path = Glib::path_get_dirname (session_path);
3147 // signal the existing dialog in ARDOUR_UI::get_session_parameters()
3148 _session_dialog->set_provided_session (session_name, session_path);
3149 _session_dialog->response (RESPONSE_NONE);
3150 _session_dialog->hide();
3155 if (Glib::file_test (path, Glib::FILE_TEST_IS_DIR)) {
3156 /* /path/to/foo => /path/to/foo, foo */
3157 rv = load_session (path, basename_nosuffix (path));
3159 /* /path/to/foo/foo.ardour => /path/to/foo, foo */
3160 rv =load_session (Glib::path_get_dirname (path), basename_nosuffix (path));
3163 // if load_session fails -> pop up SessionDialog.
3165 ARDOUR_COMMAND_LINE::session_name = "";
3167 if (get_session_parameters (true, false)) {
3173 /** @param quit_on_cancel true if exit() should be called if the user clicks `cancel' in the new session dialog */
3175 ARDOUR_UI::get_session_parameters (bool quit_on_cancel, bool should_be_new, string load_template)
3177 string session_name;
3178 string session_path;
3179 string template_name;
3181 bool likely_new = false;
3182 bool cancel_not_quit;
3184 /* deal with any existing DIRTY session now, rather than later. don't
3185 * treat a non-dirty session this way, so that it stays visible
3186 * as we bring up the new session dialog.
3189 if (_session && ARDOUR_UI::instance()->video_timeline) {
3190 ARDOUR_UI::instance()->video_timeline->sync_session_state();
3193 /* if there is already a session, relabel the button
3194 on the SessionDialog so that we don't Quit directly
3196 cancel_not_quit = (_session != 0);
3198 if (_session && _session->dirty()) {
3199 if (unload_session (false)) {
3200 /* unload cancelled by user */
3203 ARDOUR_COMMAND_LINE::session_name = "";
3206 if (!load_template.empty()) {
3207 should_be_new = true;
3208 template_name = load_template;
3211 session_name = basename_nosuffix (ARDOUR_COMMAND_LINE::session_name);
3212 session_path = ARDOUR_COMMAND_LINE::session_name;
3214 if (!session_path.empty()) {
3215 if (Glib::file_test (session_path.c_str(), Glib::FILE_TEST_EXISTS)) {
3216 if (Glib::file_test (session_path.c_str(), Glib::FILE_TEST_IS_REGULAR)) {
3217 /* session/snapshot file, change path to be dir */
3218 session_path = Glib::path_get_dirname (session_path);
3223 SessionDialog session_dialog (should_be_new, session_name, session_path, load_template, cancel_not_quit);
3225 _session_dialog = &session_dialog;
3228 if (!ARDOUR_COMMAND_LINE::session_name.empty()) {
3230 /* if they named a specific statefile, use it, otherwise they are
3231 just giving a session folder, and we want to use it as is
3232 to find the session.
3235 string::size_type suffix = ARDOUR_COMMAND_LINE::session_name.find (statefile_suffix);
3237 if (suffix != string::npos) {
3238 session_path = Glib::path_get_dirname (ARDOUR_COMMAND_LINE::session_name);
3239 session_name = ARDOUR_COMMAND_LINE::session_name.substr (0, suffix);
3240 session_name = Glib::path_get_basename (session_name);
3242 session_path = ARDOUR_COMMAND_LINE::session_name;
3243 session_name = Glib::path_get_basename (ARDOUR_COMMAND_LINE::session_name);
3248 session_dialog.clear_given ();
3251 if (should_be_new || session_name.empty()) {
3252 /* need the dialog to get info from user */
3254 cerr << "run dialog\n";
3256 switch (session_dialog.run()) {
3257 case RESPONSE_ACCEPT:
3260 /* this is used for async * app->ShouldLoad(). */
3261 continue; // while loop
3264 if (quit_on_cancel) {
3265 // JE - Currently (July 2014) this section can only get reached if the
3266 // user quits from the main 'Session Setup' dialog (i.e. reaching this
3267 // point does NOT indicate an abnormal termination). Therefore, let's
3268 // behave gracefully (i.e. let's do some cleanup) before we call exit()
3270 pthread_cancel_all ();
3278 session_dialog.hide ();
3281 /* if we run the startup dialog again, offer more than just "new session" */
3283 should_be_new = false;
3285 session_name = session_dialog.session_name (likely_new);
3286 session_path = session_dialog.session_folder ();
3293 int rv = ARDOUR::inflate_session (session_name,
3294 Config->get_default_session_parent_dir(), session_path, session_name);
3296 MessageDialog msg (session_dialog,
3297 string_compose (_("Extracting session-archive failed: %1"), inflate_error (rv)));
3302 session_dialog.set_provided_session (session_name, session_path);
3306 // XXX check archive, inflate
3307 string::size_type suffix = session_name.find (statefile_suffix);
3309 if (suffix != string::npos) {
3310 session_name = session_name.substr (0, suffix);
3313 /* this shouldn't happen, but we catch it just in case it does */
3315 if (session_name.empty()) {
3319 if (session_dialog.use_session_template()) {
3320 template_name = session_dialog.session_template_name();
3321 _session_is_new = true;
3324 if (session_name[0] == G_DIR_SEPARATOR ||
3325 #ifdef PLATFORM_WINDOWS
3326 (session_name.length() > 3 && session_name[1] == ':' && session_name[2] == G_DIR_SEPARATOR)
3328 (session_name.length() > 2 && session_name[0] == '.' && session_name[1] == G_DIR_SEPARATOR) ||
3329 (session_name.length() > 3 && session_name[0] == '.' && session_name[1] == '.' && session_name[2] == G_DIR_SEPARATOR)
3334 /* absolute path or cwd-relative path specified for session name: infer session folder
3335 from what was given.
3338 session_path = Glib::path_get_dirname (session_name);
3339 session_name = Glib::path_get_basename (session_name);
3343 session_path = session_dialog.session_folder();
3345 char illegal = Session::session_name_is_legal (session_name);
3348 MessageDialog msg (session_dialog,
3349 string_compose (_("To ensure compatibility with various systems\n"
3350 "session names may not contain a '%1' character"),
3353 ARDOUR_COMMAND_LINE::session_name = ""; // cancel that
3358 if (Glib::file_test (session_path, Glib::FileTest (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))) {
3361 if (likely_new && !nsm) {
3363 std::string existing = Glib::build_filename (session_path, session_name);
3365 if (!ask_about_loading_existing_session (existing)) {
3366 ARDOUR_COMMAND_LINE::session_name = ""; // cancel that
3371 _session_is_new = false;
3376 pop_back_splash (session_dialog);
3377 MessageDialog msg (string_compose (_("There is no existing session at \"%1\""), session_path));
3379 ARDOUR_COMMAND_LINE::session_name = ""; // cancel that
3383 char illegal = Session::session_name_is_legal(session_name);
3386 pop_back_splash (session_dialog);
3387 MessageDialog msg (session_dialog, string_compose(_("To ensure compatibility with various systems\n"
3388 "session names may not contain a '%1' character"), illegal));
3390 ARDOUR_COMMAND_LINE::session_name = ""; // cancel that
3394 _session_is_new = true;
3397 if (likely_new && template_name.empty()) {
3399 ret = build_session_from_dialog (session_dialog, session_path, session_name);
3403 ret = load_session (session_path, session_name, template_name);
3406 /* not connected to the AudioEngine, so quit to avoid an infinite loop */
3410 if (!ARDOUR_COMMAND_LINE::immediate_save.empty()) {
3411 _session->save_state (ARDOUR_COMMAND_LINE::immediate_save, false);
3415 /* clear this to avoid endless attempts to load the
3419 ARDOUR_COMMAND_LINE::session_name = "";
3423 _session_dialog = NULL;
3429 ARDOUR_UI::close_session()
3431 if (!check_audioengine (_main_window)) {
3435 if (unload_session (true)) {
3439 ARDOUR_COMMAND_LINE::session_name = "";
3441 if (get_session_parameters (true, false)) {
3444 if (splash && splash->is_visible()) {
3445 // in 1 second, hide the splash screen
3446 Glib::signal_timeout().connect (sigc::bind (sigc::ptr_fun (_hide_splash), this), 1000);
3450 /** @param snap_name Snapshot name (without .ardour suffix).
3451 * @return -2 if the load failed because we are not connected to the AudioEngine.
3454 ARDOUR_UI::load_session (const std::string& path, const std::string& snap_name, std::string mix_template)
3456 Session *new_session;
3461 unload_status = unload_session ();
3463 if (unload_status < 0) {
3465 } else if (unload_status > 0) {
3471 session_loaded = false;
3473 loading_message (string_compose (_("Please wait while %1 loads your session"), PROGRAM_NAME));
3476 new_session = new Session (*AudioEngine::instance(), path, snap_name, 0, mix_template);
3479 /* this one is special */
3481 catch (AudioEngine::PortRegistrationFailure& err) {
3483 MessageDialog msg (err.what(),
3486 Gtk::BUTTONS_CLOSE);
3488 msg.set_title (_("Port Registration Error"));
3489 msg.set_secondary_text (_("Click the Close button to try again."));
3490 msg.set_position (Gtk::WIN_POS_CENTER);
3491 pop_back_splash (msg);
3494 int response = msg.run ();
3499 case RESPONSE_CANCEL:
3506 catch (SessionException e) {
3507 MessageDialog msg (string_compose(
3508 _("Session \"%1 (snapshot %2)\" did not load successfully: %3"),
3509 path, snap_name, e.what()),
3514 msg.set_title (_("Loading Error"));
3515 msg.set_position (Gtk::WIN_POS_CENTER);
3516 pop_back_splash (msg);
3528 MessageDialog msg (string_compose(
3529 _("Session \"%1 (snapshot %2)\" did not load successfully"),
3535 msg.set_title (_("Loading Error"));
3536 msg.set_position (Gtk::WIN_POS_CENTER);
3537 pop_back_splash (msg);
3549 list<string> const u = new_session->unknown_processors ();
3551 MissingPluginDialog d (_session, u);
3556 if (!new_session->writable()) {
3557 MessageDialog msg (_("This session has been opened in read-only mode.\n\nYou will not be able to record or save."),
3562 msg.set_title (_("Read-only Session"));
3563 msg.set_position (Gtk::WIN_POS_CENTER);
3564 pop_back_splash (msg);
3570 set_session (new_session);
3572 session_loaded = true;
3575 _session->set_clean ();
3578 #ifdef WINDOWS_VST_SUPPORT
3579 fst_stop_threading();
3583 Timers::TimerSuspender t;
3587 #ifdef WINDOWS_VST_SUPPORT
3588 fst_start_threading();
3597 ARDOUR_UI::build_session (const std::string& path, const std::string& snap_name, BusProfile& bus_profile)
3599 Session *new_session;
3602 session_loaded = false;
3603 x = unload_session ();
3611 _session_is_new = true;
3614 new_session = new Session (*AudioEngine::instance(), path, snap_name, &bus_profile);
3617 catch (SessionException e) {
3618 cerr << "Here are the errors associated with this failed session:\n";
3620 cerr << "---------\n";
3621 MessageDialog msg (string_compose(_("Could not create session in \"%1\": %2"), path, e.what()));
3622 msg.set_title (_("Loading Error"));
3623 msg.set_position (Gtk::WIN_POS_CENTER);
3624 pop_back_splash (msg);
3629 cerr << "Here are the errors associated with this failed session:\n";
3631 cerr << "---------\n";
3632 MessageDialog msg (string_compose(_("Could not create session in \"%1\""), path));
3633 msg.set_title (_("Loading Error"));
3634 msg.set_position (Gtk::WIN_POS_CENTER);
3635 pop_back_splash (msg);
3640 /* Give the new session the default GUI state, if such things exist */
3643 n = Config->instant_xml (X_("Editor"));
3645 n->remove_nodes_and_delete ("Selection"); // no not apply selection to new sessions.
3646 new_session->add_instant_xml (*n, false);
3648 n = Config->instant_xml (X_("Mixer"));
3650 new_session->add_instant_xml (*n, false);
3653 n = Config->instant_xml (X_("Preferences"));
3655 new_session->add_instant_xml (*n, false);
3658 /* Put the playhead at 0 and scroll fully left */
3659 n = new_session->instant_xml (X_("Editor"));
3661 n->add_property (X_("playhead"), X_("0"));
3662 n->add_property (X_("left-frame"), X_("0"));
3665 set_session (new_session);
3667 session_loaded = true;
3669 new_session->save_state(new_session->name());
3675 ARDOUR_UI::launch_chat ()
3677 MessageDialog dialog(_("<b>Just ask and wait for an answer.\nIt may take from minutes to hours.</b>"), true);
3679 dialog.set_title (_("About the Chat"));
3680 dialog.set_secondary_text (_("When you're inside the chat just ask your question and wait for an answer. The chat is occupied by real people with real lives so many of them are passively online and might not read your question before minutes or hours later.\nSo please be patient and wait for an answer.\n\nYou should just leave the chat window open and check back regularly until someone has answered your question."));
3682 switch (dialog.run()) {
3685 open_uri("http://webchat.freenode.net/?channels=ardour-osx");
3686 #elif defined PLATFORM_WINDOWS
3687 open_uri("http://webchat.freenode.net/?channels=ardour-windows");
3689 open_uri("http://webchat.freenode.net/?channels=ardour");
3698 ARDOUR_UI::launch_manual ()
3700 PBD::open_uri (Config->get_tutorial_manual_url());
3704 ARDOUR_UI::launch_reference ()
3706 PBD::open_uri (Config->get_reference_manual_url());
3710 ARDOUR_UI::launch_tracker ()
3712 PBD::open_uri ("http://tracker.ardour.org");
3716 ARDOUR_UI::launch_subscribe ()
3718 PBD::open_uri ("https://community.ardour.org/s/subscribe");
3722 ARDOUR_UI::launch_cheat_sheet ()
3725 PBD::open_uri ("http://manual.ardour.org/files/a3_mnemonic_cheat_sheet_osx.pdf");
3727 PBD::open_uri ("http://manual.ardour.org/files/a3_mnemonic_cheatsheet.pdf");
3732 ARDOUR_UI::launch_website ()
3734 PBD::open_uri ("http://ardour.org");
3738 ARDOUR_UI::launch_website_dev ()
3740 PBD::open_uri ("http://ardour.org/development.html");
3744 ARDOUR_UI::launch_forums ()
3746 PBD::open_uri ("https://community.ardour.org/forums");
3750 ARDOUR_UI::launch_howto_report ()
3752 PBD::open_uri ("http://ardour.org/reporting_bugs");
3756 ARDOUR_UI::loading_message (const std::string& msg)
3758 if (ARDOUR_COMMAND_LINE::no_splash) {
3766 splash->message (msg);
3770 ARDOUR_UI::show_splash ()
3774 splash = new Splash;
3784 ARDOUR_UI::hide_splash ()
3791 ARDOUR_UI::display_cleanup_results (ARDOUR::CleanupReport& rep, const gchar* list_title, const bool msg_delete)
3795 removed = rep.paths.size();
3798 MessageDialog msgd (_main_window,
3799 _("No files were ready for clean-up"),
3803 msgd.set_title (_("Clean-up"));
3804 msgd.set_secondary_text (_("If this seems suprising, \n\
3805 check for any existing snapshots.\n\
3806 These may still include regions that\n\
3807 require some unused files to continue to exist."));
3813 ArdourDialog results (_("Clean-up"), true, false);
3815 struct CleanupResultsModelColumns : public Gtk::TreeModel::ColumnRecord {
3816 CleanupResultsModelColumns() {
3820 Gtk::TreeModelColumn<std::string> visible_name;
3821 Gtk::TreeModelColumn<std::string> fullpath;
3825 CleanupResultsModelColumns results_columns;
3826 Glib::RefPtr<Gtk::ListStore> results_model;
3827 Gtk::TreeView results_display;
3829 results_model = ListStore::create (results_columns);
3830 results_display.set_model (results_model);
3831 results_display.append_column (list_title, results_columns.visible_name);
3833 results_display.set_name ("CleanupResultsList");
3834 results_display.set_headers_visible (true);
3835 results_display.set_headers_clickable (false);
3836 results_display.set_reorderable (false);
3838 Gtk::ScrolledWindow list_scroller;
3841 Gtk::HBox dhbox; // the hbox for the image and text
3842 Gtk::HBox ddhbox; // the hbox we eventually pack into the dialog's vbox
3843 Gtk::Image* dimage = manage (new Gtk::Image(Stock::DIALOG_INFO, Gtk::ICON_SIZE_DIALOG));
3845 dimage->set_alignment(ALIGN_LEFT, ALIGN_TOP);
3847 const string dead_directory = _session->session_directory().dead_path();
3850 %1 - number of files removed
3851 %2 - location of "dead"
3852 %3 - size of files affected
3853 %4 - prefix for "bytes" to produce sensible results (e.g. mega, kilo, giga)
3856 const char* bprefix;
3857 double space_adjusted = 0;
3859 if (rep.space < 1000) {
3861 space_adjusted = rep.space;
3862 } else if (rep.space < 1000000) {
3863 bprefix = _("kilo");
3864 space_adjusted = floorf((float)rep.space / 1000.0);
3865 } else if (rep.space < 1000000 * 1000) {
3866 bprefix = _("mega");
3867 space_adjusted = floorf((float)rep.space / (1000.0 * 1000.0));
3869 bprefix = _("giga");
3870 space_adjusted = floorf((float)rep.space / (1000.0 * 1000 * 1000.0));
3874 txt.set_markup (string_compose (P_("\
3875 The following file was deleted from %2,\n\
3876 releasing %3 %4bytes of disk space", "\
3877 The following %1 files were deleted from %2,\n\
3878 releasing %3 %4bytes of disk space", removed),
3879 removed, Gtkmm2ext::markup_escape_text (dead_directory), space_adjusted, bprefix, PROGRAM_NAME));
3881 txt.set_markup (string_compose (P_("\
3882 The following file was not in use and \n\
3883 has been moved to: %2\n\n\
3884 After a restart of %5\n\n\
3885 <span face=\"mono\">Session -> Clean-up -> Flush Wastebasket</span>\n\n\
3886 will release an additional %3 %4bytes of disk space.\n", "\
3887 The following %1 files were not in use and \n\
3888 have been moved to: %2\n\n\
3889 After a restart of %5\n\n\
3890 <span face=\"mono\">Session -> Clean-up -> Flush Wastebasket</span>\n\n\
3891 will release an additional %3 %4bytes of disk space.\n", removed),
3892 removed, Gtkmm2ext::markup_escape_text (dead_directory), space_adjusted, bprefix, PROGRAM_NAME));
3895 dhbox.pack_start (*dimage, true, false, 5);
3896 dhbox.pack_start (txt, true, false, 5);
3898 for (vector<string>::iterator i = rep.paths.begin(); i != rep.paths.end(); ++i) {
3899 TreeModel::Row row = *(results_model->append());
3900 row[results_columns.visible_name] = *i;
3901 row[results_columns.fullpath] = *i;
3904 list_scroller.add (results_display);
3905 list_scroller.set_size_request (-1, 150);
3906 list_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
3908 dvbox.pack_start (dhbox, true, false, 5);
3909 dvbox.pack_start (list_scroller, true, false, 5);
3910 ddhbox.pack_start (dvbox, true, false, 5);
3912 results.get_vbox()->pack_start (ddhbox, true, false, 5);
3913 results.add_button (Stock::CLOSE, RESPONSE_CLOSE);
3914 results.set_default_response (RESPONSE_CLOSE);
3915 results.set_position (Gtk::WIN_POS_MOUSE);
3917 results_display.show();
3918 list_scroller.show();
3925 //results.get_vbox()->show();
3926 results.set_resizable (false);
3933 ARDOUR_UI::cleanup ()
3935 if (_session == 0) {
3936 /* shouldn't happen: menu item is insensitive */
3941 MessageDialog checker (_("Are you sure you want to clean-up?"),
3943 Gtk::MESSAGE_QUESTION,
3946 checker.set_title (_("Clean-up"));
3948 checker.set_secondary_text(_("Clean-up is a destructive operation.\n\
3949 ALL undo/redo information will be lost if you clean-up.\n\
3950 Clean-up will move all unused files to a \"dead\" location."));
3952 checker.add_button (Stock::CANCEL, RESPONSE_CANCEL);
3953 checker.add_button (_("Clean-up"), RESPONSE_ACCEPT);
3954 checker.set_default_response (RESPONSE_CANCEL);
3956 checker.set_name (_("CleanupDialog"));
3957 checker.set_wmclass (X_("ardour_cleanup"), PROGRAM_NAME);
3958 checker.set_position (Gtk::WIN_POS_MOUSE);
3960 switch (checker.run()) {
3961 case RESPONSE_ACCEPT:
3967 ARDOUR::CleanupReport rep;
3969 editor->prepare_for_cleanup ();
3971 /* do not allow flush until a session is reloaded */
3973 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Main"), X_("FlushWastebasket"));
3975 act->set_sensitive (false);
3978 if (_session->cleanup_sources (rep)) {
3979 editor->finish_cleanup ();
3983 editor->finish_cleanup ();
3986 display_cleanup_results (rep, _("Cleaned Files"), false);
3990 ARDOUR_UI::flush_trash ()
3992 if (_session == 0) {
3993 /* shouldn't happen: menu item is insensitive */
3997 ARDOUR::CleanupReport rep;
3999 if (_session->cleanup_trash_sources (rep)) {
4003 display_cleanup_results (rep, _("deleted file"), true);
4007 ARDOUR_UI::cleanup_peakfiles ()
4009 if (_session == 0) {
4010 /* shouldn't happen: menu item is insensitive */
4014 if (! _session->can_cleanup_peakfiles ()) {
4018 // get all region-views in this session
4020 TrackViewList empty;
4022 editor->get_regions_after(rs, (framepos_t) 0, empty);
4023 std::list<RegionView*> views = rs.by_layer();
4025 // remove displayed audio-region-views waveforms
4026 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
4027 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*i);
4028 if (!arv) { continue ; }
4029 arv->delete_waves();
4032 // cleanup peak files:
4033 // - stop pending peakfile threads
4034 // - close peakfiles if any
4035 // - remove peak dir in session
4036 // - setup peakfiles (background thread)
4037 _session->cleanup_peakfiles ();
4039 // re-add waves to ARV
4040 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
4041 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*i);
4042 if (!arv) { continue ; }
4043 arv->create_waves();
4047 PresentationInfo::order_t
4048 ARDOUR_UI::translate_order (RouteDialogs::InsertAt place)
4050 if (editor->get_selection().tracks.empty()) {
4051 return PresentationInfo::max_order;
4054 PresentationInfo::order_t order_hint = PresentationInfo::max_order;
4057 we want the new routes to have their order keys set starting from
4058 the highest order key in the selection + 1 (if available).
4061 if (place == RouteDialogs::AfterSelection) {
4062 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView*> (editor->get_selection().tracks.back());
4064 order_hint = rtav->route()->presentation_info().order();
4067 } else if (place == RouteDialogs::BeforeSelection) {
4068 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView*> (editor->get_selection().tracks.front());
4070 order_hint = rtav->route()->presentation_info().order();
4072 } else if (place == RouteDialogs::First) {
4075 /* leave order_hint at max_order */
4082 ARDOUR_UI::start_duplicate_routes ()
4084 if (!duplicate_routes_dialog) {
4085 duplicate_routes_dialog = new DuplicateRouteDialog;
4088 if (duplicate_routes_dialog->restart (_session)) {
4092 duplicate_routes_dialog->present ();
4096 ARDOUR_UI::add_route ()
4098 if (!add_route_dialog.get (false)) {
4099 add_route_dialog->signal_response().connect (sigc::mem_fun (*this, &ARDOUR_UI::add_route_dialog_finished));
4106 if (add_route_dialog->is_visible()) {
4107 /* we're already doing this */
4111 add_route_dialog->set_position (WIN_POS_MOUSE);
4112 add_route_dialog->present();
4116 ARDOUR_UI::add_route_dialog_finished (int r)
4120 add_route_dialog->hide();
4123 case RESPONSE_ACCEPT:
4130 if ((count = add_route_dialog->count()) <= 0) {
4134 PresentationInfo::order_t order = translate_order (add_route_dialog->insert_at());
4135 string template_path = add_route_dialog->track_template();
4136 DisplaySuspender ds;
4138 if (!template_path.empty()) {
4139 if (add_route_dialog->name_template_is_default()) {
4140 _session->new_route_from_template (count, order, template_path, string());
4142 _session->new_route_from_template (count, order, template_path, add_route_dialog->name_template());
4147 ChanCount input_chan= add_route_dialog->channels ();
4148 ChanCount output_chan;
4149 string name_template = add_route_dialog->name_template ();
4150 PluginInfoPtr instrument = add_route_dialog->requested_instrument ();
4151 RouteGroup* route_group = add_route_dialog->route_group ();
4152 AutoConnectOption oac = Config->get_output_auto_connect();
4153 bool strict_io = add_route_dialog->use_strict_io ();
4155 if (oac & AutoConnectMaster) {
4156 output_chan.set (DataType::AUDIO, (_session->master_out() ? _session->master_out()->n_inputs().n_audio() : input_chan.n_audio()));
4157 output_chan.set (DataType::MIDI, 0);
4159 output_chan = input_chan;
4162 /* XXX do something with name template */
4164 Session::ProcessorChangeBlocker pcb (_session);
4166 switch (add_route_dialog->type_wanted()) {
4167 case AddRouteDialog::AudioTrack:
4168 session_add_audio_track (input_chan.n_audio(), output_chan.n_audio(), add_route_dialog->mode(), route_group, count, name_template, strict_io, order);
4170 case AddRouteDialog::MidiTrack:
4171 session_add_midi_track (route_group, count, name_template, strict_io, instrument, 0, order);
4173 case AddRouteDialog::MixedTrack:
4174 session_add_mixed_track (input_chan, output_chan, route_group, count, name_template, strict_io, instrument, 0, order);
4176 case AddRouteDialog::AudioBus:
4177 session_add_audio_bus (input_chan.n_audio(), output_chan.n_audio(), route_group, count, name_template, strict_io, order);
4179 case AddRouteDialog::MidiBus:
4180 session_add_midi_bus (route_group, count, name_template, strict_io, instrument, 0, order);
4182 case AddRouteDialog::VCAMaster:
4183 session_add_vca (name_template, count);
4189 ARDOUR_UI::add_lua_script ()
4195 LuaScriptInfoPtr spi;
4196 ScriptSelector ss ("Add Lua Session Script", LuaScriptInfo::Session);
4197 switch (ss.run ()) {
4198 case Gtk::RESPONSE_ACCEPT:
4206 std::string script = "";
4209 script = Glib::file_get_contents (spi->path);
4210 } catch (Glib::FileError e) {
4211 string msg = string_compose (_("Cannot read session script '%1': %2"), spi->path, e.what());
4212 MessageDialog am (msg);
4217 LuaScriptParamList lsp = LuaScriptParams::script_params (spi, "sess_params");
4218 std::vector<std::string> reg = _session->registered_lua_functions ();
4220 ScriptParameterDialog spd (_("Set Script Parameters"), spi, reg, lsp);
4221 switch (spd.run ()) {
4222 case Gtk::RESPONSE_ACCEPT:
4229 _session->register_lua_function (spd.name(), script, lsp);
4230 } catch (luabridge::LuaException const& e) {
4231 string msg = string_compose (_("Session script '%1' instantiation failed: %2"), spd.name(), e.what ());
4232 MessageDialog am (msg);
4234 } catch (SessionException e) {
4235 string msg = string_compose (_("Loading Session script '%1' failed: %2"), spd.name(), e.what ());
4236 MessageDialog am (msg);
4242 ARDOUR_UI::remove_lua_script ()
4247 if (_session->registered_lua_function_count () == 0) {
4248 string msg = _("There are no active Lua session scripts present in this session.");
4249 MessageDialog am (msg);
4254 std::vector<std::string> reg = _session->registered_lua_functions ();
4255 SessionScriptManager sm ("Remove Lua Session Script", reg);
4256 switch (sm.run ()) {
4257 case Gtk::RESPONSE_ACCEPT:
4263 _session->unregister_lua_function (sm.name());
4264 } catch (luabridge::LuaException const& e) {
4265 string msg = string_compose (_("Session script '%1' removal failed: %2"), sm.name(), e.what ());
4266 MessageDialog am (msg);
4272 ARDOUR_UI::stop_video_server (bool ask_confirm)
4274 if (!video_server_process && ask_confirm) {
4275 warning << string_compose (_("Video-Server was not launched by %1. The request to stop it is ignored."), PROGRAM_NAME) << endmsg;
4277 if (video_server_process) {
4279 ArdourDialog confirm (_("Stop Video-Server"), true);
4280 Label m (_("Do you really want to stop the Video Server?"));
4281 confirm.get_vbox()->pack_start (m, true, true);
4282 confirm.add_button (Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
4283 confirm.add_button (_("Yes, Stop It"), Gtk::RESPONSE_ACCEPT);
4284 confirm.show_all ();
4285 if (confirm.run() == RESPONSE_CANCEL) {
4289 delete video_server_process;
4290 video_server_process =0;
4295 ARDOUR_UI::start_video_server_menu (Gtk::Window* float_window)
4297 ARDOUR_UI::start_video_server( float_window, true);
4301 ARDOUR_UI::start_video_server (Gtk::Window* float_window, bool popup_msg)
4307 if (ARDOUR_UI::instance()->video_timeline->check_server()) {
4308 if (video_server_process) {
4309 popup_error(_("The Video Server is already started."));
4311 popup_error(_("An external Video Server is configured and can be reached. Not starting a new instance."));
4317 while (!ARDOUR_UI::instance()->video_timeline->check_server()) {
4319 warning << _("Could not connect to the Video Server. Start it or configure its access URL in Preferences.") << endmsg;
4321 VideoServerDialog *video_server_dialog = new VideoServerDialog (_session);
4323 video_server_dialog->set_transient_for (*float_window);
4326 if (!Config->get_show_video_server_dialog() && firsttime < 2) {
4327 video_server_dialog->hide();
4329 ResponseType r = (ResponseType) video_server_dialog->run ();
4330 video_server_dialog->hide();
4331 if (r != RESPONSE_ACCEPT) { return false; }
4332 if (video_server_dialog->show_again()) {
4333 Config->set_show_video_server_dialog(false);
4337 std::string icsd_exec = video_server_dialog->get_exec_path();
4338 std::string icsd_docroot = video_server_dialog->get_docroot();
4339 #ifndef PLATFORM_WINDOWS
4340 if (icsd_docroot.empty()) {
4341 icsd_docroot = VideoUtils::video_get_docroot (Config);
4346 #ifdef PLATFORM_WINDOWS
4347 if (VideoUtils::harvid_version >= 0x000802 && icsd_docroot.empty()) {
4348 /* OK, allow all drive letters */
4351 if (g_lstat (icsd_docroot.c_str(), &sb) != 0 || !S_ISDIR(sb.st_mode)) {
4352 warning << _("Specified docroot is not an existing directory.") << endmsg;
4355 #ifndef PLATFORM_WINDOWS
4356 if ( (g_lstat (icsd_exec.c_str(), &sb) != 0)
4357 || (sb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) == 0 ) {
4358 warning << _("Given Video Server is not an executable file.") << endmsg;
4362 if ( (g_lstat (icsd_exec.c_str(), &sb) != 0)
4363 || (sb.st_mode & (S_IXUSR)) == 0 ) {
4364 warning << _("Given Video Server is not an executable file.") << endmsg;
4370 argp=(char**) calloc(9,sizeof(char*));
4371 argp[0] = strdup(icsd_exec.c_str());
4372 argp[1] = strdup("-P");
4373 argp[2] = (char*) calloc(16,sizeof(char)); snprintf(argp[2], 16, "%s", video_server_dialog->get_listenaddr().c_str());
4374 argp[3] = strdup("-p");
4375 argp[4] = (char*) calloc(6,sizeof(char)); snprintf(argp[4], 6, "%i", video_server_dialog->get_listenport());
4376 argp[5] = strdup("-C");
4377 argp[6] = (char*) calloc(6,sizeof(char)); snprintf(argp[6], 6, "%i", video_server_dialog->get_cachesize());
4378 argp[7] = strdup(icsd_docroot.c_str());
4380 stop_video_server();
4382 #ifdef PLATFORM_WINDOWS
4383 if (VideoUtils::harvid_version >= 0x000802 && icsd_docroot.empty()) {
4384 /* OK, allow all drive letters */
4387 if (icsd_docroot == X_("/") || icsd_docroot == X_("C:\\")) {
4388 Config->set_video_advanced_setup(false);
4390 std::ostringstream osstream;
4391 osstream << "http://127.0.0.1:" << video_server_dialog->get_listenport() << "/";
4392 Config->set_video_server_url(osstream.str());
4393 Config->set_video_server_docroot(icsd_docroot);
4394 Config->set_video_advanced_setup(true);
4397 if (video_server_process) {
4398 delete video_server_process;
4401 video_server_process = new ARDOUR::SystemExec(icsd_exec, argp);
4402 if (video_server_process->start()) {
4403 warning << _("Cannot launch the video-server") << endmsg;
4406 int timeout = 120; // 6 sec
4407 while (!ARDOUR_UI::instance()->video_timeline->check_server()) {
4408 Glib::usleep (50000);
4410 if (--timeout <= 0 || !video_server_process->is_running()) break;
4413 warning << _("Video-server was started but does not respond to requests...") << endmsg;
4415 if (!ARDOUR_UI::instance()->video_timeline->check_server_docroot()) {
4416 delete video_server_process;
4417 video_server_process = 0;
4425 ARDOUR_UI::add_video (Gtk::Window* float_window)
4431 if (!start_video_server(float_window, false)) {
4432 warning << _("Could not connect to the Video Server. Start it or configure its access URL in Preferences.") << endmsg;
4437 add_video_dialog->set_transient_for (*float_window);
4440 if (add_video_dialog->is_visible()) {
4441 /* we're already doing this */
4445 ResponseType r = (ResponseType) add_video_dialog->run ();
4446 add_video_dialog->hide();
4447 if (r != RESPONSE_ACCEPT) { return; }
4449 bool local_file, orig_local_file;
4450 std::string path = add_video_dialog->file_name(local_file);
4452 std::string orig_path = path;
4453 orig_local_file = local_file;
4455 bool auto_set_session_fps = add_video_dialog->auto_set_session_fps();
4457 if (local_file && !Glib::file_test(path, Glib::FILE_TEST_EXISTS)) {
4458 warning << string_compose(_("could not open %1"), path) << endmsg;
4461 if (!local_file && path.length() == 0) {
4462 warning << _("no video-file selected") << endmsg;
4466 std::string audio_from_video;
4467 bool detect_ltc = false;
4469 switch (add_video_dialog->import_option()) {
4470 case VTL_IMPORT_TRANSCODE:
4472 TranscodeVideoDialog *transcode_video_dialog;
4473 transcode_video_dialog = new TranscodeVideoDialog (_session, path);
4474 ResponseType r = (ResponseType) transcode_video_dialog->run ();
4475 transcode_video_dialog->hide();
4476 if (r != RESPONSE_ACCEPT) {
4477 delete transcode_video_dialog;
4481 audio_from_video = transcode_video_dialog->get_audiofile();
4483 if (!audio_from_video.empty() && transcode_video_dialog->detect_ltc()) {
4486 else if (!audio_from_video.empty()) {
4487 editor->embed_audio_from_video(
4489 video_timeline->get_offset(),
4490 (transcode_video_dialog->import_option() != VTL_IMPORT_NO_VIDEO)
4493 switch (transcode_video_dialog->import_option()) {
4494 case VTL_IMPORT_TRANSCODED:
4495 path = transcode_video_dialog->get_filename();
4498 case VTL_IMPORT_REFERENCE:
4501 delete transcode_video_dialog;
4504 delete transcode_video_dialog;
4508 case VTL_IMPORT_NONE:
4512 /* strip _session->session_directory().video_path() from video file if possible */
4513 if (local_file && !path.compare(0, _session->session_directory().video_path().size(), _session->session_directory().video_path())) {
4514 path=path.substr(_session->session_directory().video_path().size());
4515 if (path.at(0) == G_DIR_SEPARATOR) {
4516 path=path.substr(1);
4520 video_timeline->set_update_session_fps(auto_set_session_fps);
4522 if (video_timeline->video_file_info(path, local_file)) {
4523 XMLNode* node = new XMLNode(X_("Videotimeline"));
4524 node->add_property (X_("Filename"), path);
4525 node->add_property (X_("AutoFPS"), auto_set_session_fps?X_("1"):X_("0"));
4526 node->add_property (X_("LocalFile"), local_file?X_("1"):X_("0"));
4527 if (orig_local_file) {
4528 node->add_property (X_("OriginalVideoFile"), orig_path);
4530 node->remove_property (X_("OriginalVideoFile"));
4532 _session->add_extra_xml (*node);
4533 _session->set_dirty ();
4535 if (!audio_from_video.empty() && detect_ltc) {
4536 std::vector<LTCFileReader::LTCMap> ltc_seq;
4539 /* TODO ask user about TV standard (LTC alignment if any) */
4540 LTCFileReader ltcr (audio_from_video, video_timeline->get_video_file_fps());
4541 /* TODO ASK user which channel: 0 .. ltcr->channels() - 1 */
4543 ltc_seq = ltcr.read_ltc (/*channel*/ 0, /*max LTC frames to decode*/ 15);
4545 /* TODO seek near end of file, and read LTC until end.
4546 * if it fails to find any LTC frames, scan complete file
4548 * calculate drift of LTC compared to video-duration,
4549 * ask user for reference (timecode from start/mid/end)
4552 // LTCFileReader will have written error messages
4555 ::g_unlink(audio_from_video.c_str());
4557 if (ltc_seq.size() == 0) {
4558 PBD::error << _("No LTC detected, video will not be aligned.") << endmsg;
4560 /* the very first TC in the file is somteimes not aligned properly */
4561 int i = ltc_seq.size() -1;
4562 ARDOUR::frameoffset_t video_start_offset =
4563 _session->nominal_frame_rate() * (ltc_seq[i].timecode_sec - ltc_seq[i].framepos_sec);
4564 PBD::info << string_compose (_("Align video-start to %1 [samples]"), video_start_offset) << endmsg;
4565 video_timeline->set_offset(video_start_offset);
4569 _session->maybe_update_session_range(
4570 std::max(video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
4571 std::max(video_timeline->get_offset() + video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0));
4574 if (add_video_dialog->launch_xjadeo() && local_file) {
4575 editor->set_xjadeo_sensitive(true);
4576 editor->toggle_xjadeo_proc(1);
4578 editor->toggle_xjadeo_proc(0);
4580 editor->toggle_ruler_video(true);
4585 ARDOUR_UI::remove_video ()
4587 video_timeline->close_session();
4588 editor->toggle_ruler_video(false);
4591 video_timeline->set_offset_locked(false);
4592 video_timeline->set_offset(0);
4594 /* delete session state */
4595 XMLNode* node = new XMLNode(X_("Videotimeline"));
4596 _session->add_extra_xml(*node);
4597 node = new XMLNode(X_("Videomonitor"));
4598 _session->add_extra_xml(*node);
4599 node = new XMLNode(X_("Videoexport"));
4600 _session->add_extra_xml(*node);
4601 stop_video_server();
4605 ARDOUR_UI::flush_videotimeline_cache (bool localcacheonly)
4607 if (localcacheonly) {
4608 video_timeline->vmon_update();
4610 video_timeline->flush_cache();
4612 editor->queue_visual_videotimeline_update();
4616 ARDOUR_UI::export_video (bool range)
4618 if (ARDOUR::Config->get_show_video_export_info()) {
4619 ExportVideoInfobox infobox (_session);
4620 Gtk::ResponseType rv = (Gtk::ResponseType) infobox.run();
4621 if (infobox.show_again()) {
4622 ARDOUR::Config->set_show_video_export_info(false);
4625 case GTK_RESPONSE_YES:
4626 PBD::open_uri (ARDOUR::Config->get_reference_manual_url() + "/video-timeline/operations/#export");
4632 export_video_dialog->set_session (_session);
4633 export_video_dialog->apply_state(editor->get_selection().time, range);
4634 export_video_dialog->run ();
4635 export_video_dialog->hide ();
4639 ARDOUR_UI::preferences_settings () const
4644 node = _session->instant_xml(X_("Preferences"));
4646 node = Config->instant_xml(X_("Preferences"));
4650 node = new XMLNode (X_("Preferences"));
4657 ARDOUR_UI::mixer_settings () const
4662 node = _session->instant_xml(X_("Mixer"));
4664 node = Config->instant_xml(X_("Mixer"));
4668 node = new XMLNode (X_("Mixer"));
4675 ARDOUR_UI::main_window_settings () const
4680 node = _session->instant_xml(X_("Main"));
4682 node = Config->instant_xml(X_("Main"));
4686 if (getenv("ARDOUR_INSTANT_XML_PATH")) {
4687 node = Config->instant_xml(getenv("ARDOUR_INSTANT_XML_PATH"));
4692 node = new XMLNode (X_("Main"));
4699 ARDOUR_UI::editor_settings () const
4704 node = _session->instant_xml(X_("Editor"));
4706 node = Config->instant_xml(X_("Editor"));
4710 if (getenv("ARDOUR_INSTANT_XML_PATH")) {
4711 node = Config->instant_xml(getenv("ARDOUR_INSTANT_XML_PATH"));
4716 node = new XMLNode (X_("Editor"));
4723 ARDOUR_UI::keyboard_settings () const
4727 node = Config->extra_xml(X_("Keyboard"));
4730 node = new XMLNode (X_("Keyboard"));
4737 ARDOUR_UI::create_xrun_marker (framepos_t where)
4740 Location *location = new Location (*_session, where, where, _("xrun"), Location::IsMark, 0);
4741 _session->locations()->add (location);
4746 ARDOUR_UI::halt_on_xrun_message ()
4748 cerr << "HALT on xrun\n";
4749 MessageDialog msg (_main_window, _("Recording was stopped because your system could not keep up."));
4754 ARDOUR_UI::xrun_handler (framepos_t where)
4760 ENSURE_GUI_THREAD (*this, &ARDOUR_UI::xrun_handler, where)
4762 if (_session && Config->get_create_xrun_marker() && _session->actively_recording()) {
4763 create_xrun_marker(where);
4766 if (_session && Config->get_stop_recording_on_xrun() && _session->actively_recording()) {
4767 halt_on_xrun_message ();
4772 ARDOUR_UI::disk_overrun_handler ()
4774 ENSURE_GUI_THREAD (*this, &ARDOUR_UI::disk_overrun_handler)
4776 if (!have_disk_speed_dialog_displayed) {
4777 have_disk_speed_dialog_displayed = true;
4778 MessageDialog* msg = new MessageDialog (_main_window, string_compose (_("\
4779 The disk system on your computer\n\
4780 was not able to keep up with %1.\n\
4782 Specifically, it failed to write data to disk\n\
4783 quickly enough to keep up with recording.\n"), PROGRAM_NAME));
4784 msg->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &ARDOUR_UI::disk_speed_dialog_gone), msg));
4790 /* TODO: this is getting elaborate enough to warrant being split into a dedicated class */
4791 static MessageDialog *scan_dlg = NULL;
4792 static ProgressBar *scan_pbar = NULL;
4793 static HBox *scan_tbox = NULL;
4794 static Gtk::Button *scan_timeout_button;
4797 ARDOUR_UI::cancel_plugin_scan ()
4799 PluginManager::instance().cancel_plugin_scan();
4803 ARDOUR_UI::cancel_plugin_timeout ()
4805 PluginManager::instance().cancel_plugin_timeout();
4806 scan_timeout_button->set_sensitive (false);
4810 ARDOUR_UI::plugin_scan_timeout (int timeout)
4812 if (!scan_dlg || !scan_dlg->is_mapped() || !scan_pbar) {
4816 scan_pbar->set_sensitive (false);
4817 scan_timeout_button->set_sensitive (true);
4818 scan_pbar->set_fraction ((float) timeout / (float) Config->get_vst_scan_timeout());
4821 scan_pbar->set_sensitive (false);
4822 scan_timeout_button->set_sensitive (false);
4828 ARDOUR_UI::plugin_scan_dialog (std::string type, std::string plugin, bool can_cancel)
4830 if (type == X_("closeme") && !(scan_dlg && scan_dlg->is_mapped())) {
4834 const bool cancelled = PluginManager::instance().cancelled();
4835 if (type != X_("closeme") && (!UIConfiguration::instance().get_show_plugin_scan_window()) && !_initial_verbose_plugin_scan) {
4836 if (cancelled && scan_dlg->is_mapped()) {
4841 if (cancelled || !can_cancel) {
4846 static Gtk::Button *cancel_button;
4848 scan_dlg = new MessageDialog("", false, MESSAGE_INFO, BUTTONS_NONE); // TODO manage
4849 VBox* vbox = scan_dlg->get_vbox();
4850 vbox->set_size_request(400,-1);
4851 scan_dlg->set_title (_("Scanning for plugins"));
4853 cancel_button = manage(new Gtk::Button(_("Cancel plugin scan")));
4854 cancel_button->set_name ("EditorGTKButton");
4855 cancel_button->signal_clicked().connect ( mem_fun (*this, &ARDOUR_UI::cancel_plugin_scan) );
4856 cancel_button->show();
4858 scan_dlg->get_vbox()->pack_start ( *cancel_button, PACK_SHRINK);
4860 scan_tbox = manage( new HBox() );
4862 scan_timeout_button = manage(new Gtk::Button(_("Stop Timeout")));
4863 scan_timeout_button->set_name ("EditorGTKButton");
4864 scan_timeout_button->signal_clicked().connect ( mem_fun (*this, &ARDOUR_UI::cancel_plugin_timeout) );
4865 scan_timeout_button->show();
4867 scan_pbar = manage(new ProgressBar());
4868 scan_pbar->set_orientation(Gtk::PROGRESS_RIGHT_TO_LEFT);
4869 scan_pbar->set_text(_("Scan Timeout"));
4872 scan_tbox->pack_start (*scan_pbar, PACK_EXPAND_WIDGET, 4);
4873 scan_tbox->pack_start (*scan_timeout_button, PACK_SHRINK, 4);
4875 scan_dlg->get_vbox()->pack_start (*scan_tbox, PACK_SHRINK, 4);
4878 assert(scan_dlg && scan_tbox && cancel_button);
4880 if (type == X_("closeme")) {
4884 scan_dlg->set_message(type + ": " + Glib::path_get_basename(plugin));
4887 if (!can_cancel || !cancelled) {
4888 scan_timeout_button->set_sensitive(false);
4890 cancel_button->set_sensitive(can_cancel && !cancelled);
4896 ARDOUR_UI::gui_idle_handler ()
4899 /* due to idle calls, gtk_events_pending() may always return true */
4900 while (gtk_events_pending() && --timeout) {
4901 gtk_main_iteration ();
4906 ARDOUR_UI::disk_underrun_handler ()
4908 ENSURE_GUI_THREAD (*this, &ARDOUR_UI::disk_underrun_handler)
4910 if (!have_disk_speed_dialog_displayed) {
4911 have_disk_speed_dialog_displayed = true;
4912 MessageDialog* msg = new MessageDialog (
4913 _main_window, string_compose (_("The disk system on your computer\n\
4914 was not able to keep up with %1.\n\
4916 Specifically, it failed to read data from disk\n\
4917 quickly enough to keep up with playback.\n"), PROGRAM_NAME));
4918 msg->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &ARDOUR_UI::disk_speed_dialog_gone), msg));
4924 ARDOUR_UI::disk_speed_dialog_gone (int /*ignored_response*/, MessageDialog* msg)
4926 have_disk_speed_dialog_displayed = false;
4931 ARDOUR_UI::session_dialog (std::string msg)
4933 ENSURE_GUI_THREAD (*this, &ARDOUR_UI::session_dialog, msg)
4937 d = new MessageDialog (msg, false, MESSAGE_INFO, BUTTONS_OK, true);
4944 ARDOUR_UI::pending_state_dialog ()
4946 HBox* hbox = manage (new HBox());
4947 Image* image = manage (new Image (Stock::DIALOG_QUESTION, ICON_SIZE_DIALOG));
4948 ArdourDialog dialog (_("Crash Recovery"), true);
4949 Label message (string_compose (_("\
4950 This session appears to have been in the\n\
4951 middle of recording when %1 or\n\
4952 the computer was shutdown.\n\
4954 %1 can recover any captured audio for\n\
4955 you, or it can ignore it. Please decide\n\
4956 what you would like to do.\n"), PROGRAM_NAME));
4957 image->set_alignment(ALIGN_CENTER, ALIGN_TOP);
4958 hbox->pack_start (*image, PACK_EXPAND_WIDGET, 12);
4959 hbox->pack_end (message, PACK_EXPAND_PADDING, 12);
4960 dialog.get_vbox()->pack_start(*hbox, PACK_EXPAND_PADDING, 6);
4961 dialog.add_button (_("Ignore crash data"), RESPONSE_REJECT);
4962 dialog.add_button (_("Recover from crash"), RESPONSE_ACCEPT);
4963 dialog.set_default_response (RESPONSE_ACCEPT);
4964 dialog.set_position (WIN_POS_CENTER);
4969 switch (dialog.run ()) {
4970 case RESPONSE_ACCEPT:
4978 ARDOUR_UI::sr_mismatch_dialog (framecnt_t desired, framecnt_t actual)
4980 HBox* hbox = new HBox();
4981 Image* image = new Image (Stock::DIALOG_WARNING, ICON_SIZE_DIALOG);
4982 ArdourDialog dialog (_("Sample Rate Mismatch"), true);
4983 Label message (string_compose (_("\
4984 This session was created with a sample rate of %1 Hz, but\n\
4985 %2 is currently running at %3 Hz. If you load this session,\n\
4986 audio may be played at the wrong sample rate.\n"), desired, PROGRAM_NAME, actual));
4988 image->set_alignment(ALIGN_CENTER, ALIGN_TOP);
4989 hbox->pack_start (*image, PACK_EXPAND_WIDGET, 12);
4990 hbox->pack_end (message, PACK_EXPAND_PADDING, 12);
4991 dialog.get_vbox()->pack_start(*hbox, PACK_EXPAND_PADDING, 6);
4992 dialog.add_button (_("Do not load session"), RESPONSE_REJECT);
4993 dialog.add_button (_("Load session anyway"), RESPONSE_ACCEPT);
4994 dialog.set_default_response (RESPONSE_ACCEPT);
4995 dialog.set_position (WIN_POS_CENTER);
5000 switch (dialog.run()) {
5001 case RESPONSE_ACCEPT:
5011 ARDOUR_UI::sr_mismatch_message (framecnt_t desired, framecnt_t actual)
5013 MessageDialog msg (string_compose (_("\
5014 This session was created with a sample rate of %1 Hz, but\n\
5015 %2 is currently running at %3 Hz.\n\
5016 Audio will be recorded and played at the wrong sample rate.\n\
5017 Re-Configure the Audio Engine in\n\
5018 Menu > Window > Audio/Midi Setup"),
5019 desired, PROGRAM_NAME, actual),
5021 Gtk::MESSAGE_WARNING);
5026 ARDOUR_UI::use_config ()
5031 ARDOUR_UI::update_transport_clocks (framepos_t pos)
5033 if (UIConfiguration::instance().get_primary_clock_delta_edit_cursor()) {
5034 primary_clock->set (pos, false, editor->get_preferred_edit_position (EDIT_IGNORE_PHEAD));
5036 primary_clock->set (pos);
5039 if (UIConfiguration::instance().get_secondary_clock_delta_edit_cursor()) {
5040 secondary_clock->set (pos, false, editor->get_preferred_edit_position (EDIT_IGNORE_PHEAD));
5042 secondary_clock->set (pos);
5045 if (big_clock_window) {
5046 big_clock->set (pos);
5048 ARDOUR_UI::instance()->video_timeline->manual_seek_video_monitor(pos);
5052 ARDOUR_UI::step_edit_status_change (bool yn)
5054 // XXX should really store pre-step edit status of things
5055 // we make insensitive
5058 rec_button.set_active_state (Gtkmm2ext::ImplicitActive);
5059 rec_button.set_sensitive (false);
5061 rec_button.unset_active_state ();;
5062 rec_button.set_sensitive (true);
5067 ARDOUR_UI::record_state_changed ()
5069 ENSURE_GUI_THREAD (*this, &ARDOUR_UI::record_state_changed);
5072 /* why bother - the clock isn't visible */
5076 ActionManager::set_sensitive (ActionManager::rec_sensitive_actions, !_session->actively_recording());
5078 if (big_clock_window) {
5079 if (_session->record_status () == Session::Recording && _session->have_rec_enabled_track ()) {
5080 big_clock->set_active (true);
5082 big_clock->set_active (false);
5089 ARDOUR_UI::first_idle ()
5092 _session->allow_auto_play (true);
5096 editor->first_idle();
5099 Keyboard::set_can_save_keybindings (true);
5104 ARDOUR_UI::store_clock_modes ()
5106 XMLNode* node = new XMLNode(X_("ClockModes"));
5108 for (vector<AudioClock*>::iterator x = AudioClock::clocks.begin(); x != AudioClock::clocks.end(); ++x) {
5109 XMLNode* child = new XMLNode (X_("Clock"));
5111 child->add_property (X_("name"), (*x)->name());
5112 child->add_property (X_("mode"), enum_2_string ((*x)->mode()));
5113 child->add_property (X_("on"), ((*x)->off() ? X_("no") : X_("yes")));
5115 node->add_child_nocopy (*child);
5118 _session->add_extra_xml (*node);
5119 _session->set_dirty ();
5123 ARDOUR_UI::setup_profile ()
5125 if (gdk_screen_width() < 1200 || getenv ("ARDOUR_NARROW_SCREEN")) {
5126 Profile->set_small_screen ();
5129 if (g_getenv ("TRX")) {
5130 Profile->set_trx ();
5133 if (g_getenv ("MIXBUS")) {
5134 Profile->set_mixbus ();
5139 ARDOUR_UI::missing_file (Session*s, std::string str, DataType type)
5141 MissingFileDialog dialog (s, str, type);
5146 int result = dialog.run ();
5153 return 1; // quit entire session load
5156 result = dialog.get_action ();
5162 ARDOUR_UI::ambiguous_file (std::string file, std::vector<std::string> hits)
5164 AmbiguousFileDialog dialog (file, hits);
5171 return dialog.get_which ();
5174 /** Allocate our thread-local buffers */
5176 ARDOUR_UI::get_process_buffers ()
5178 _process_thread->get_buffers ();
5181 /** Drop our thread-local buffers */
5183 ARDOUR_UI::drop_process_buffers ()
5185 _process_thread->drop_buffers ();
5189 ARDOUR_UI::feedback_detected ()
5191 _feedback_exists = true;
5195 ARDOUR_UI::successful_graph_sort ()
5197 _feedback_exists = false;
5201 ARDOUR_UI::midi_panic ()
5204 _session->midi_panic();
5209 ARDOUR_UI::session_format_mismatch (std::string xml_path, std::string backup_path)
5211 const char* start_big = "<span size=\"x-large\" weight=\"bold\">";
5212 const char* end_big = "</span>";
5213 const char* start_mono = "<tt>";
5214 const char* end_mono = "</tt>";
5216 MessageDialog msg (string_compose (_("%4This is a session from an older version of %3%5\n\n"
5217 "%3 has copied the old session file\n\n%6%1%7\n\nto\n\n%6%2%7\n\n"
5218 "From now on, use the backup copy with older versions of %3"),
5219 xml_path, backup_path, PROGRAM_NAME,
5221 start_mono, end_mono), true);
5228 ARDOUR_UI::reset_peak_display ()
5230 if (!_session || !_session->master_out() || !editor_meter) return;
5231 editor_meter->clear_meters();
5232 editor_meter_max_peak = -INFINITY;
5233 editor_meter_peak_display.set_active_state ( Gtkmm2ext::Off );
5237 ARDOUR_UI::reset_group_peak_display (RouteGroup* group)
5239 if (!_session || !_session->master_out()) return;
5240 if (group == _session->master_out()->route_group()) {
5241 reset_peak_display ();
5246 ARDOUR_UI::reset_route_peak_display (Route* route)
5248 if (!_session || !_session->master_out()) return;
5249 if (_session->master_out().get() == route) {
5250 reset_peak_display ();
5255 ARDOUR_UI::do_audio_midi_setup (uint32_t desired_sample_rate)
5257 audio_midi_setup->set_desired_sample_rate (desired_sample_rate);
5258 audio_midi_setup->set_position (WIN_POS_CENTER);
5260 if (Config->get_try_autostart_engine () || getenv ("TRY_AUTOSTART_ENGINE")) {
5261 audio_midi_setup->try_autostart ();
5262 if (ARDOUR::AudioEngine::instance()->running()) {
5268 int response = audio_midi_setup->run();
5269 printf("RESPONSE %d\n", response);
5271 case Gtk::RESPONSE_DELETE_EVENT:
5274 if (!AudioEngine::instance()->running()) {
5277 audio_midi_setup->hide ();
5285 ARDOUR_UI::transport_numpad_timeout ()
5287 _numpad_locate_happening = false;
5288 if (_numpad_timeout_connection.connected() )
5289 _numpad_timeout_connection.disconnect();
5294 ARDOUR_UI::transport_numpad_decimal ()
5296 _numpad_timeout_connection.disconnect();
5298 if (_numpad_locate_happening) {
5299 if (editor) editor->goto_nth_marker(_pending_locate_num - 1);
5300 _numpad_locate_happening = false;
5302 _pending_locate_num = 0;
5303 _numpad_locate_happening = true;
5304 _numpad_timeout_connection = Glib::signal_timeout().connect (mem_fun(*this, &ARDOUR_UI::transport_numpad_timeout), 2*1000);
5309 ARDOUR_UI::transport_numpad_event (int num)
5311 if ( _numpad_locate_happening ) {
5312 _pending_locate_num = _pending_locate_num*10 + num;
5315 case 0: toggle_roll(false, false); break;
5316 case 1: transport_rewind(1); break;
5317 case 2: transport_forward(1); break;
5318 case 3: transport_record(true); break;
5319 case 4: toggle_session_auto_loop(); break;
5320 case 5: transport_record(false); toggle_session_auto_loop(); break;
5321 case 6: toggle_punch(); break;
5322 case 7: toggle_click(); break;
5323 case 8: toggle_auto_return(); break;
5324 case 9: toggle_follow_edits(); break;
5330 ARDOUR_UI::set_flat_buttons ()
5332 CairoWidget::set_flat_buttons( UIConfiguration::instance().get_flat_buttons() );
5336 ARDOUR_UI::audioengine_became_silent ()
5338 MessageDialog msg (string_compose (_("This is a free/demo copy of %1. It has just switched to silent mode."), PROGRAM_NAME),
5340 Gtk::MESSAGE_WARNING,
5344 msg.set_title (string_compose (_("%1 is now silent"), PROGRAM_NAME));
5346 Gtk::Label pay_label (string_compose (_("Please consider paying for a copy of %1 - you can pay whatever you want."), PROGRAM_NAME));
5347 Gtk::Label subscribe_label (_("Better yet become a subscriber - subscriptions start at US$1 per month."));
5348 Gtk::Button pay_button (_("Pay for a copy (via the web)"));
5349 Gtk::Button subscribe_button (_("Become a subscriber (via the web)"));
5350 Gtk::HBox pay_button_box;
5351 Gtk::HBox subscribe_button_box;
5353 pay_button_box.pack_start (pay_button, true, false);
5354 subscribe_button_box.pack_start (subscribe_button, true, false);
5356 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 */
5358 pay_button.signal_clicked().connect (sigc::hide_return (sigc::bind (sigc::ptr_fun (openuri), (const char*) "https://ardour.org/download")));
5359 subscribe_button.signal_clicked().connect (sigc::hide_return (sigc::bind (sigc::ptr_fun (openuri), (const char*) "https://community.ardour.org/s/subscribe")));
5361 msg.get_vbox()->pack_start (pay_label);
5362 msg.get_vbox()->pack_start (pay_button_box);
5363 msg.get_vbox()->pack_start (subscribe_label);
5364 msg.get_vbox()->pack_start (subscribe_button_box);
5366 msg.get_vbox()->show_all ();
5368 msg.add_button (_("Remain silent"), Gtk::RESPONSE_CANCEL);
5369 msg.add_button (_("Save and quit"), Gtk::RESPONSE_NO);
5370 msg.add_button (_("Give me more time"), Gtk::RESPONSE_YES);
5375 case Gtk::RESPONSE_YES:
5376 AudioEngine::instance()->reset_silence_countdown ();
5379 case Gtk::RESPONSE_NO:
5381 save_state_canfail ("");
5385 case Gtk::RESPONSE_CANCEL:
5387 /* don't reset, save session and exit */
5393 ARDOUR_UI::hide_application ()
5395 Application::instance ()-> hide ();
5399 ARDOUR_UI::setup_toplevel_window (Gtk::Window& window, const string& name, void* owner)
5401 /* icons, titles, WM stuff */
5403 static list<Glib::RefPtr<Gdk::Pixbuf> > window_icons;
5405 if (window_icons.empty()) {
5406 Glib::RefPtr<Gdk::Pixbuf> icon;
5407 if ((icon = ::get_icon (PROGRAM_NAME "-icon_16px"))) {
5408 window_icons.push_back (icon);
5410 if ((icon = ::get_icon (PROGRAM_NAME "-icon_22px"))) {
5411 window_icons.push_back (icon);
5413 if ((icon = ::get_icon (PROGRAM_NAME "-icon_32px"))) {
5414 window_icons.push_back (icon);
5416 if ((icon = ::get_icon (PROGRAM_NAME "-icon_48px"))) {
5417 window_icons.push_back (icon);
5421 if (!window_icons.empty()) {
5422 window.set_default_icon_list (window_icons);
5425 Gtkmm2ext::WindowTitle title (Glib::get_application_name());
5427 if (!name.empty()) {
5431 window.set_title (title.get_string());
5432 window.set_wmclass (string_compose (X_("%1_%1"), downcase (std::string(PROGRAM_NAME)), downcase (name)), PROGRAM_NAME);
5434 window.set_flags (CAN_FOCUS);
5435 window.add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK);
5437 /* This is a hack to ensure that GTK-accelerators continue to
5438 * work. Once we switch over to entirely native bindings, this will be
5439 * unnecessary and should be removed
5441 window.add_accel_group (ActionManager::ui_manager->get_accel_group());
5443 window.signal_configure_event().connect (sigc::mem_fun (*this, &ARDOUR_UI::configure_handler));
5444 window.signal_window_state_event().connect (sigc::bind (sigc::mem_fun (*this, &ARDOUR_UI::tabbed_window_state_event_handler), owner));
5445 window.signal_key_press_event().connect (sigc::bind (sigc::mem_fun (*this, &ARDOUR_UI::key_event_handler), &window), false);
5446 window.signal_key_release_event().connect (sigc::bind (sigc::mem_fun (*this, &ARDOUR_UI::key_event_handler), &window), false);
5450 ARDOUR_UI::key_event_handler (GdkEventKey* ev, Gtk::Window* event_window)
5452 Gtkmm2ext::Bindings* bindings = 0;
5453 Gtk::Window* window = 0;
5455 /* until we get ardour bindings working, we are not handling key
5459 if (ev->type != GDK_KEY_PRESS) {
5463 if (event_window == &_main_window) {
5465 window = event_window;
5467 /* find current tab contents */
5469 Gtk::Widget* w = _tabs.get_nth_page (_tabs.get_current_page());
5471 /* see if it uses the ardour binding system */
5474 bindings = reinterpret_cast<Gtkmm2ext::Bindings*>(w->get_data ("ardour-bindings"));
5477 DEBUG_TRACE (DEBUG::Accelerators, string_compose ("main window key event, bindings = %1, global = %2\n", bindings, &global_bindings));
5481 window = event_window;
5483 /* see if window uses ardour binding system */
5485 bindings = reinterpret_cast<Gtkmm2ext::Bindings*>(window->get_data ("ardour-bindings"));
5488 /* An empty binding set is treated as if it doesn't exist */
5490 if (bindings && bindings->empty()) {
5494 return key_press_focus_accelerator_handler (*window, ev, bindings);
5497 static Gtkmm2ext::Bindings*
5498 get_bindings_from_widget_heirarchy (GtkWidget** w)
5503 if ((p = g_object_get_data (G_OBJECT(*w), "ardour-bindings")) != 0) {
5506 *w = gtk_widget_get_parent (*w);
5509 return reinterpret_cast<Gtkmm2ext::Bindings*> (p);
5513 ARDOUR_UI::key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev, Gtkmm2ext::Bindings* bindings)
5515 GtkWindow* win = window.gobj();
5516 GtkWidget* focus = gtk_window_get_focus (win);
5517 GtkWidget* binding_widget = focus;
5518 bool special_handling_of_unmodified_accelerators = false;
5519 const guint mask = (Keyboard::RelevantModifierKeyMask & ~(Gdk::SHIFT_MASK|Gdk::LOCK_MASK));
5523 /* some widget has keyboard focus */
5525 if (GTK_IS_ENTRY(focus) || Keyboard::some_magic_widget_has_focus()) {
5527 /* A particular kind of focusable widget currently has keyboard
5528 * focus. All unmodified key events should go to that widget
5529 * first and not be used as an accelerator by default
5532 special_handling_of_unmodified_accelerators = true;
5536 Gtkmm2ext::Bindings* focus_bindings = get_bindings_from_widget_heirarchy (&binding_widget);
5537 if (focus_bindings) {
5538 bindings = focus_bindings;
5539 DEBUG_TRACE (DEBUG::Accelerators, string_compose ("Switch bindings based on focus widget, now using %1\n", bindings->name()));
5544 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",
5547 Gtkmm2ext::show_gdk_event_state (ev->state),
5548 special_handling_of_unmodified_accelerators,
5549 Keyboard::some_magic_widget_has_focus(),
5551 (focus ? gtk_widget_get_name (focus) : "no focus widget"),
5552 ((ev->state & mask) ? "yes" : "no"),
5553 window.get_title()));
5555 /* This exists to allow us to override the way GTK handles
5556 key events. The normal sequence is:
5558 a) event is delivered to a GtkWindow
5559 b) accelerators/mnemonics are activated
5560 c) if (b) didn't handle the event, propagate to
5561 the focus widget and/or focus chain
5563 The problem with this is that if the accelerators include
5564 keys without modifiers, such as the space bar or the
5565 letter "e", then pressing the key while typing into
5566 a text entry widget results in the accelerator being
5567 activated, instead of the desired letter appearing
5570 There is no good way of fixing this, but this
5571 represents a compromise. The idea is that
5572 key events involving modifiers (not Shift)
5573 get routed into the activation pathway first, then
5574 get propagated to the focus widget if necessary.
5576 If the key event doesn't involve modifiers,
5577 we deliver to the focus widget first, thus allowing
5578 it to get "normal text" without interference
5581 Of course, this can also be problematic: if there
5582 is a widget with focus, then it will swallow
5583 all "normal text" accelerators.
5587 if (!special_handling_of_unmodified_accelerators || (ev->state & mask)) {
5589 /* no special handling or there are modifiers in effect: accelerate first */
5591 DEBUG_TRACE (DEBUG::Accelerators, "\tactivate, then propagate\n");
5592 DEBUG_TRACE (DEBUG::Accelerators, string_compose ("\tevent send-event:%1 time:%2 length:%3 name %7 string:%4 hardware_keycode:%5 group:%6\n",
5593 ev->send_event, ev->time, ev->length, ev->string, ev->hardware_keycode, ev->group, gdk_keyval_name (ev->keyval)));
5595 DEBUG_TRACE (DEBUG::Accelerators, "\tsending to window\n");
5596 KeyboardKey k (ev->state, ev->keyval);
5600 DEBUG_TRACE (DEBUG::Accelerators, string_compose ("\tusing Ardour bindings %1 @ %2 for this event\n", bindings->name(), bindings));
5602 if (bindings->activate (k, Bindings::Press)) {
5603 DEBUG_TRACE (DEBUG::Accelerators, "\t\thandled\n");
5607 if (binding_widget) {
5608 binding_widget = gtk_widget_get_parent (binding_widget);
5609 if (binding_widget) {
5610 bindings = get_bindings_from_widget_heirarchy (&binding_widget);
5619 DEBUG_TRACE (DEBUG::Accelerators, "\tnot yet handled, try global bindings\n");
5621 if (global_bindings && global_bindings->activate (k, Bindings::Press)) {
5622 DEBUG_TRACE (DEBUG::Accelerators, "\t\thandled\n");
5626 DEBUG_TRACE (DEBUG::Accelerators, "\tnot accelerated, now propagate\n");
5628 if (gtk_window_propagate_key_event (win, ev)) {
5629 DEBUG_TRACE (DEBUG::Accelerators, "\tpropagate handled\n");
5635 /* no modifiers, propagate first */
5637 DEBUG_TRACE (DEBUG::Accelerators, "\tpropagate, then activate\n");
5639 if (gtk_window_propagate_key_event (win, ev)) {
5640 DEBUG_TRACE (DEBUG::Accelerators, "\thandled by propagate\n");
5644 DEBUG_TRACE (DEBUG::Accelerators, "\tpropagation didn't handle, so activate\n");
5645 KeyboardKey k (ev->state, ev->keyval);
5649 DEBUG_TRACE (DEBUG::Accelerators, "\tusing Ardour bindings for this window\n");
5652 if (bindings->activate (k, Bindings::Press)) {
5653 DEBUG_TRACE (DEBUG::Accelerators, "\t\thandled\n");
5657 if (binding_widget) {
5658 binding_widget = gtk_widget_get_parent (binding_widget);
5659 if (binding_widget) {
5660 bindings = get_bindings_from_widget_heirarchy (&binding_widget);
5669 DEBUG_TRACE (DEBUG::Accelerators, "\tnot yet handled, try global bindings\n");
5671 if (global_bindings && global_bindings->activate (k, Bindings::Press)) {
5672 DEBUG_TRACE (DEBUG::Accelerators, "\t\thandled\n");
5677 DEBUG_TRACE (DEBUG::Accelerators, "\tnot handled\n");
5682 ARDOUR_UI::load_bindings ()
5684 if ((global_bindings = Bindings::get_bindings (X_("Global"), global_actions)) == 0) {
5685 error << _("Global keybindings are missing") << endmsg;
5690 ARDOUR_UI::cancel_solo ()
5693 _session->cancel_all_solo ();
5698 ARDOUR_UI::reset_focus (Gtk::Widget* w)
5700 /* this resets focus to the first focusable parent of the given widget,
5701 * or, if there is no focusable parent, cancels focus in the toplevel
5702 * window that the given widget is packed into (if there is one).
5709 Gtk::Widget* top = w->get_toplevel();
5711 if (!top || !top->is_toplevel()) {
5715 w = w->get_parent ();
5719 if (w->is_toplevel()) {
5720 /* Setting the focus widget to a Gtk::Window causes all
5721 * subsequent calls to ::has_focus() on the nominal
5722 * focus widget in that window to return
5723 * false. Workaround: never set focus to the toplevel
5729 if (w->get_can_focus ()) {
5730 Gtk::Window* win = dynamic_cast<Gtk::Window*> (top);
5731 win->set_focus (*w);
5734 w = w->get_parent ();
5737 if (top == &_main_window) {
5741 /* no focusable parent found, cancel focus in top level window.
5742 C++ API cannot be used for this. Thanks, references.
5745 gtk_window_set_focus (GTK_WINDOW(top->gobj()), 0);