don't override user naming of send/return/port inserts
[ardour2.git] / gtk2_ardour / ardour_ui.cc
blob870a068745206fb24d6d92f7089e1ba9c9e22113
1 /*
2 Copyright (C) 1999-2007 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.
20 #define __STDC_FORMAT_MACROS 1
21 #include <stdint.h>
23 #include <algorithm>
24 #include <cmath>
25 #include <fcntl.h>
26 #include <signal.h>
27 #include <unistd.h>
28 #include <time.h>
29 #include <cerrno>
30 #include <fstream>
32 #include <iostream>
34 #include <sys/resource.h>
36 #include <gtkmm/messagedialog.h>
37 #include <gtkmm/accelmap.h>
39 #include "pbd/error.h"
40 #include "pbd/basename.h"
41 #include "pbd/compose.h"
42 #include "pbd/failed_constructor.h"
43 #include "pbd/enumwriter.h"
44 #include "pbd/memento_command.h"
45 #include "pbd/openuri.h"
46 #include "pbd/file_utils.h"
48 #include "gtkmm2ext/gtk_ui.h"
49 #include "gtkmm2ext/utils.h"
50 #include "gtkmm2ext/click_box.h"
51 #include "gtkmm2ext/fastmeter.h"
52 #include "gtkmm2ext/stop_signal.h"
53 #include "gtkmm2ext/popup.h"
54 #include "gtkmm2ext/window_title.h"
56 #include "midi++/manager.h"
58 #include "ardour/ardour.h"
59 #include "ardour/profile.h"
60 #include "ardour/session_directory.h"
61 #include "ardour/session_route.h"
62 #include "ardour/session_state_utils.h"
63 #include "ardour/session_utils.h"
64 #include "ardour/port.h"
65 #include "ardour/audioengine.h"
66 #include "ardour/playlist.h"
67 #include "ardour/utils.h"
68 #include "ardour/audio_diskstream.h"
69 #include "ardour/audiofilesource.h"
70 #include "ardour/recent_sessions.h"
71 #include "ardour/port.h"
72 #include "ardour/audio_track.h"
73 #include "ardour/midi_track.h"
74 #include "ardour/filesystem_paths.h"
75 #include "ardour/filename_extensions.h"
77 typedef uint64_t microseconds_t;
79 #include "actions.h"
80 #include "ardour_ui.h"
81 #include "public_editor.h"
82 #include "audio_clock.h"
83 #include "keyboard.h"
84 #include "mixer_ui.h"
85 #include "prompter.h"
86 #include "opts.h"
87 #include "add_route_dialog.h"
88 #include "about.h"
89 #include "splash.h"
90 #include "utils.h"
91 #include "gui_thread.h"
92 #include "theme_manager.h"
93 #include "bundle_manager.h"
94 #include "session_metadata_dialog.h"
95 #include "gain_meter.h"
96 #include "route_time_axis.h"
97 #include "startup.h"
98 #include "engine_dialog.h"
99 #include "processor_box.h"
101 #include "i18n.h"
103 using namespace ARDOUR;
104 using namespace PBD;
105 using namespace Gtkmm2ext;
106 using namespace Gtk;
108 ARDOUR_UI *ARDOUR_UI::theArdourUI = 0;
109 UIConfiguration *ARDOUR_UI::ui_config = 0;
111 sigc::signal<void,bool> ARDOUR_UI::Blink;
112 sigc::signal<void> ARDOUR_UI::RapidScreenUpdate;
113 sigc::signal<void> ARDOUR_UI::SuperRapidScreenUpdate;
114 sigc::signal<void,nframes_t, bool, nframes_t> ARDOUR_UI::Clock;
116 ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[])
118 : Gtkmm2ext::UI (PROGRAM_NAME, argcp, argvp),
120 primary_clock (X_("primary"), false, X_("TransportClockDisplay"), true, true, false, true),
121 secondary_clock (X_("secondary"), false, X_("SecondaryClockDisplay"), true, true, false, true),
122 preroll_clock (X_("preroll"), false, X_("PreRollClock"), true, false, true),
123 postroll_clock (X_("postroll"), false, X_("PostRollClock"), true, false, true),
125 /* preroll stuff */
127 preroll_button (_("pre\nroll")),
128 postroll_button (_("post\nroll")),
130 /* big clock */
132 big_clock (X_("bigclock"), false, "BigClockNonRecording", true, true, false, false),
134 /* transport */
136 roll_controllable (new TransportControllable ("transport roll", *this, TransportControllable::Roll)),
137 stop_controllable (new TransportControllable ("transport stop", *this, TransportControllable::Stop)),
138 goto_start_controllable (new TransportControllable ("transport goto start", *this, TransportControllable::GotoStart)),
139 goto_end_controllable (new TransportControllable ("transport goto end", *this, TransportControllable::GotoEnd)),
140 auto_loop_controllable (new TransportControllable ("transport auto loop", *this, TransportControllable::AutoLoop)),
141 play_selection_controllable (new TransportControllable ("transport play selection", *this, TransportControllable::PlaySelection)),
142 rec_controllable (new TransportControllable ("transport rec-enable", *this, TransportControllable::RecordEnable)),
143 shuttle_controllable (new TransportControllable ("shuttle", *this, TransportControllable::ShuttleControl)),
144 shuttle_controller_binding_proxy (shuttle_controllable),
146 roll_button (roll_controllable),
147 stop_button (stop_controllable),
148 goto_start_button (goto_start_controllable),
149 goto_end_button (goto_end_controllable),
150 auto_loop_button (auto_loop_controllable),
151 play_selection_button (play_selection_controllable),
152 rec_button (rec_controllable),
154 shuttle_units_button (_("% ")),
156 punch_in_button (_("Punch In")),
157 punch_out_button (_("Punch Out")),
158 auto_return_button (_("Auto Return")),
159 auto_play_button (_("Auto Play")),
160 auto_input_button (_("Auto Input")),
161 click_button (_("Click")),
162 time_master_button (_("time\nmaster")),
164 auditioning_alert_button (_("AUDITION")),
165 solo_alert_button (_("SOLO")),
166 shown_flag (false),
167 error_log_button (_("Errors"))
170 using namespace Gtk::Menu_Helpers;
172 Gtkmm2ext::init();
175 #ifdef TOP_MENUBAR
176 // _auto_display_errors = false;
178 * This was commented out as it wasn't defined
179 * in A3 IIRC. If this is not needed it should
180 * be completely removed.
182 #endif
184 about = 0;
185 splash = 0;
186 _startup = 0;
188 if (theArdourUI == 0) {
189 theArdourUI = this;
192 ui_config = new UIConfiguration();
193 theme_manager = new ThemeManager();
195 editor = 0;
196 mixer = 0;
197 editor = 0;
198 engine = 0;
199 _session_is_new = false;
200 big_clock_window = 0;
201 big_clock_height = 0;
202 big_clock_resize_in_progress = false;
203 session_selector_window = 0;
204 last_key_press_time = 0;
205 _will_create_new_session_automatically = false;
206 add_route_dialog = 0;
207 route_params = 0;
208 rc_option_editor = 0;
209 session_option_editor = 0;
210 location_ui = 0;
211 open_session_selector = 0;
212 have_configure_timeout = false;
213 have_disk_speed_dialog_displayed = false;
214 session_loaded = false;
215 last_speed_displayed = -1.0f;
216 ignore_dual_punch = false;
217 _mixer_on_top = false;
218 original_big_clock_width = -1;
219 original_big_clock_height = -1;
220 original_big_clock_font_size = 0;
222 roll_button.unset_flags (Gtk::CAN_FOCUS);
223 stop_button.unset_flags (Gtk::CAN_FOCUS);
224 goto_start_button.unset_flags (Gtk::CAN_FOCUS);
225 goto_end_button.unset_flags (Gtk::CAN_FOCUS);
226 auto_loop_button.unset_flags (Gtk::CAN_FOCUS);
227 play_selection_button.unset_flags (Gtk::CAN_FOCUS);
228 rec_button.unset_flags (Gtk::CAN_FOCUS);
230 last_configure_time= 0;
232 shuttle_grabbed = false;
233 shuttle_fract = 0.0;
234 shuttle_max_speed = 8.0f;
236 shuttle_style_menu = 0;
237 shuttle_unit_menu = 0;
239 // We do not have jack linked in yet so;
241 last_shuttle_request = last_peak_grab = 0; // get_microseconds();
243 ARDOUR::Diskstream::DiskOverrun.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::disk_overrun_handler, this), gui_context());
244 ARDOUR::Diskstream::DiskUnderrun.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::disk_underrun_handler, this), gui_context());
246 /* handle dialog requests */
248 ARDOUR::Session::Dialog.connect (forever_connections, MISSING_INVALIDATOR, ui_bind (&ARDOUR_UI::session_dialog, this, _1), gui_context());
250 /* handle pending state with a dialog (PROBLEM: needs to return a value and thus cannot be x-thread) */
252 ARDOUR::Session::AskAboutPendingState.connect_same_thread (forever_connections, boost::bind (&ARDOUR_UI::pending_state_dialog, this));
254 /* handle sr mismatch with a dialog (PROBLEM: needs to return a value and thus cannot be x-thread) */
256 ARDOUR::Session::AskAboutSampleRateMismatch.connect_same_thread (forever_connections, boost::bind (&ARDOUR_UI::sr_mismatch_dialog, this, _1, _2));
258 /* lets get this party started */
260 try {
261 if (ARDOUR::init (ARDOUR_COMMAND_LINE::use_vst, ARDOUR_COMMAND_LINE::try_hw_optimization)) {
262 throw failed_constructor ();
265 setup_gtk_ardour_enums ();
266 setup_profile ();
268 GainMeter::setup_slider_pix ();
269 RouteTimeAxisView::setup_slider_pix ();
270 SendProcessorEntry::setup_slider_pix ();
271 SessionEvent::create_per_thread_pool ("GUI", 512);
273 } catch (failed_constructor& err) {
274 error << _("could not initialize Ardour.") << endmsg;
275 // pass it on up
276 throw;
279 /* we like keyboards */
281 keyboard = new ArdourKeyboard(*this);
283 XMLNode* node = ARDOUR_UI::instance()->keyboard_settings();
284 if (node) {
285 keyboard->set_state (*node, Stateful::loading_state_version);
288 reset_dpi();
290 starting.connect (sigc::mem_fun(*this, &ARDOUR_UI::startup));
291 stopping.connect (sigc::mem_fun(*this, &ARDOUR_UI::shutdown));
293 platform_setup ();
296 /** @return true if a session was chosen and `apply' clicked, otherwise false if `cancel' was clicked */
297 bool
298 ARDOUR_UI::run_startup (bool should_be_new)
300 if (_startup == 0) {
301 _startup = new ArdourStartup ();
304 _startup->set_new_only (should_be_new);
305 _startup->present ();
307 main().run();
309 _startup->hide ();
311 switch (_startup->response()) {
312 case RESPONSE_OK:
313 return true;
314 default:
315 return false;
320 ARDOUR_UI::create_engine ()
322 // this gets called every time by new_session()
324 if (engine) {
325 return 0;
328 loading_message (_("Starting audio engine"));
330 try {
331 engine = new ARDOUR::AudioEngine (ARDOUR_COMMAND_LINE::jack_client_name);
333 } catch (...) {
335 return -1;
338 engine->Stopped.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::engine_stopped, this), gui_context());
339 engine->Running.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::engine_running, this), gui_context());
340 engine->Halted.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::engine_halted, this), gui_context());
341 engine->SampleRateChanged.connect (forever_connections, MISSING_INVALIDATOR, ui_bind (&ARDOUR_UI::update_sample_rate, this, _1), gui_context());
343 post_engine ();
345 return 0;
348 void
349 ARDOUR_UI::post_engine ()
351 /* Things to be done once we create the AudioEngine
354 MIDI::Manager::instance()->set_api_data (engine->jack());
355 setup_midi ();
357 ARDOUR::init_post_engine ();
359 ActionManager::init ();
360 _tooltips.enable();
362 if (setup_windows ()) {
363 throw failed_constructor ();
366 check_memory_locking();
368 /* this is the first point at which all the keybindings are available */
370 if (ARDOUR_COMMAND_LINE::show_key_actions) {
371 vector<string> names;
372 vector<string> paths;
373 vector<string> keys;
374 vector<AccelKey> bindings;
376 ActionManager::get_all_actions (names, paths, keys, bindings);
378 vector<string>::iterator n;
379 vector<string>::iterator k;
380 for (n = names.begin(), k = keys.begin(); n != names.end(); ++n, ++k) {
381 cerr << "Action: " << (*n) << " bound to " << (*k) << endl;
384 exit (0);
387 blink_timeout_tag = -1;
389 /* this being a GUI and all, we want peakfiles */
391 AudioFileSource::set_build_peakfiles (true);
392 AudioFileSource::set_build_missing_peakfiles (true);
394 /* set default clock modes */
396 if (Profile->get_sae()) {
397 primary_clock.set_mode (AudioClock::BBT);
398 secondary_clock.set_mode (AudioClock::MinSec);
399 } else {
400 primary_clock.set_mode (AudioClock::Timecode);
401 secondary_clock.set_mode (AudioClock::BBT);
404 /* start the time-of-day-clock */
406 #ifndef GTKOSX
407 /* OS X provides a nearly-always visible wallclock, so don't be stupid */
408 update_wall_clock ();
409 Glib::signal_timeout().connect (sigc::mem_fun(*this, &ARDOUR_UI::update_wall_clock), 60000);
410 #endif
412 update_disk_space ();
413 update_cpu_load ();
414 update_sample_rate (engine->frame_rate());
416 Config->ParameterChanged.connect (forever_connections, MISSING_INVALIDATOR, ui_bind (&ARDOUR_UI::parameter_changed, this, _1), gui_context());
417 boost::function<void (string)> pc (boost::bind (&ARDOUR_UI::parameter_changed, this, _1));
418 Config->map_parameters (pc);
420 /* now start and maybe save state */
422 if (do_engine_start () == 0) {
423 if (_session && _session_is_new) {
424 /* we need to retain initial visual
425 settings for a new session
427 _session->save_state ("");
432 ARDOUR_UI::~ARDOUR_UI ()
434 delete keyboard;
435 delete editor;
436 delete mixer;
437 delete add_route_dialog;
440 void
441 ARDOUR_UI::pop_back_splash ()
443 if (Splash::instance()) {
444 // Splash::instance()->pop_back();
445 Splash::instance()->hide ();
449 gint
450 ARDOUR_UI::configure_timeout ()
452 if (last_configure_time == 0) {
453 /* no configure events yet */
454 return true;
457 /* force a gap of 0.5 seconds since the last configure event
460 if (get_microseconds() - last_configure_time < 500000) {
461 return true;
462 } else {
463 have_configure_timeout = false;
464 cerr << "config event-driven save\n";
465 save_ardour_state ();
466 return false;
470 gboolean
471 ARDOUR_UI::configure_handler (GdkEventConfigure* /*conf*/)
473 if (have_configure_timeout) {
474 last_configure_time = get_microseconds();
475 } else {
476 Glib::signal_timeout().connect (sigc::mem_fun(*this, &ARDOUR_UI::configure_timeout), 100);
477 have_configure_timeout = true;
480 return FALSE;
483 void
484 ARDOUR_UI::set_transport_controllable_state (const XMLNode& node)
486 const XMLProperty* prop;
488 if ((prop = node.property ("roll")) != 0) {
489 roll_controllable->set_id (prop->value());
491 if ((prop = node.property ("stop")) != 0) {
492 stop_controllable->set_id (prop->value());
494 if ((prop = node.property ("goto-start")) != 0) {
495 goto_start_controllable->set_id (prop->value());
497 if ((prop = node.property ("goto-end")) != 0) {
498 goto_end_controllable->set_id (prop->value());
500 if ((prop = node.property ("auto-loop")) != 0) {
501 auto_loop_controllable->set_id (prop->value());
503 if ((prop = node.property ("play-selection")) != 0) {
504 play_selection_controllable->set_id (prop->value());
506 if ((prop = node.property ("rec")) != 0) {
507 rec_controllable->set_id (prop->value());
509 if ((prop = node.property ("shuttle")) != 0) {
510 shuttle_controllable->set_id (prop->value());
514 XMLNode&
515 ARDOUR_UI::get_transport_controllable_state ()
517 XMLNode* node = new XMLNode(X_("TransportControllables"));
518 char buf[64];
520 roll_controllable->id().print (buf, sizeof (buf));
521 node->add_property (X_("roll"), buf);
522 stop_controllable->id().print (buf, sizeof (buf));
523 node->add_property (X_("stop"), buf);
524 goto_start_controllable->id().print (buf, sizeof (buf));
525 node->add_property (X_("goto_start"), buf);
526 goto_end_controllable->id().print (buf, sizeof (buf));
527 node->add_property (X_("goto_end"), buf);
528 auto_loop_controllable->id().print (buf, sizeof (buf));
529 node->add_property (X_("auto_loop"), buf);
530 play_selection_controllable->id().print (buf, sizeof (buf));
531 node->add_property (X_("play_selection"), buf);
532 rec_controllable->id().print (buf, sizeof (buf));
533 node->add_property (X_("rec"), buf);
534 shuttle_controllable->id().print (buf, sizeof (buf));
535 node->add_property (X_("shuttle"), buf);
537 return *node;
541 gint
542 ARDOUR_UI::autosave_session ()
544 if (g_main_depth() > 1) {
545 /* inside a recursive main loop,
546 give up because we may not be able to
547 take a lock.
549 return 1;
552 if (!Config->get_periodic_safety_backups()) {
553 return 1;
556 if (_session) {
557 _session->maybe_write_autosave();
560 return 1;
563 void
564 ARDOUR_UI::update_autosave ()
566 ENSURE_GUI_THREAD (*this, &ARDOUR_UI::update_autosave)
568 if (_session && _session->dirty()) {
569 if (_autosave_connection.connected()) {
570 _autosave_connection.disconnect();
573 _autosave_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &ARDOUR_UI::autosave_session),
574 Config->get_periodic_safety_backup_interval() * 1000);
576 } else {
577 if (_autosave_connection.connected()) {
578 _autosave_connection.disconnect();
583 void
584 ARDOUR_UI::backend_audio_error (bool we_set_params, Gtk::Window* toplevel)
586 string title;
587 if (we_set_params) {
588 title = string_compose (_("%1 could not start JACK"), PROGRAM_NAME);
589 } else {
590 title = string_compose (_("%1 could not connect to JACK."), PROGRAM_NAME);
593 MessageDialog win (title,
594 false,
595 Gtk::MESSAGE_INFO,
596 Gtk::BUTTONS_NONE);
598 if (we_set_params) {
599 win.set_secondary_text(_("There are several possible reasons:\n\
601 1) You requested audio parameters that are not supported..\n\
602 2) JACK is running as another user.\n\
604 Please consider the possibilities, and perhaps try different parameters."));
605 } else {
606 win.set_secondary_text(_("There are several possible reasons:\n\
608 1) JACK is not running.\n\
609 2) JACK is running as another user, perhaps root.\n\
610 3) There is already another client called \"ardour\".\n\
612 Please consider the possibilities, and perhaps (re)start JACK."));
615 if (toplevel) {
616 win.set_transient_for (*toplevel);
619 if (we_set_params) {
620 win.add_button (Stock::OK, RESPONSE_CLOSE);
621 } else {
622 win.add_button (Stock::QUIT, RESPONSE_CLOSE);
625 win.set_default_response (RESPONSE_CLOSE);
627 win.show_all ();
628 win.set_position (Gtk::WIN_POS_CENTER);
629 pop_back_splash ();
631 /* we just don't care about the result, but we want to block */
633 win.run ();
636 void
637 ARDOUR_UI::startup ()
639 XMLNode* audio_setup = Config->extra_xml ("AudioSetup");
641 if (audio_setup && _startup && _startup->engine_control()) {
642 _startup->engine_control()->set_state (*audio_setup);
645 if (get_session_parameters (true, ARDOUR_COMMAND_LINE::new_session)) {
646 exit (1);
649 use_config ();
651 goto_editor_window ();
653 BootMessage (string_compose (_("%1 is ready for use"), PROGRAM_NAME));
654 show ();
657 void
658 ARDOUR_UI::no_memory_warning ()
660 XMLNode node (X_("no-memory-warning"));
661 Config->add_instant_xml (node);
664 void
665 ARDOUR_UI::check_memory_locking ()
667 #ifdef __APPLE__
668 /* OS X doesn't support mlockall(2), and so testing for memory locking capability there is pointless */
669 return;
670 #else // !__APPLE__
672 XMLNode* memory_warning_node = Config->instant_xml (X_("no-memory-warning"));
674 if (engine->is_realtime() && memory_warning_node == 0) {
676 struct rlimit limits;
677 int64_t ram;
678 long pages, page_size;
680 if ((page_size = sysconf (_SC_PAGESIZE)) < 0 ||(pages = sysconf (_SC_PHYS_PAGES)) < 0) {
681 ram = 0;
682 } else {
683 ram = (int64_t) pages * (int64_t) page_size;
686 if (getrlimit (RLIMIT_MEMLOCK, &limits)) {
687 return;
690 if (limits.rlim_cur != RLIM_INFINITY) {
692 if (ram == 0 || ((double) limits.rlim_cur / ram) < 0.75) {
695 MessageDialog msg (string_compose (_("WARNING: Your system has a limit for maximum amount of locked memory. "
696 "This might cause %1 to run out of memory before your system "
697 "runs out of memory. \n\n"
698 "You can view the memory limit with 'ulimit -l', "
699 "and it is normally controlled by /etc/security/limits.conf"),
700 PROGRAM_NAME).c_str());
702 VBox* vbox = msg.get_vbox();
703 HBox hbox;
704 CheckButton cb (_("Do not show this window again"));
706 cb.signal_toggled().connect (sigc::mem_fun (*this, &ARDOUR_UI::no_memory_warning));
708 hbox.pack_start (cb, true, false);
709 vbox->pack_start (hbox);
710 cb.show();
711 vbox->show();
712 hbox.show ();
714 pop_back_splash ();
716 editor->ensure_float (msg);
717 msg.run ();
721 #endif // !__APPLE__
725 void
726 ARDOUR_UI::finish()
728 if (_session) {
730 if (_session->transport_rolling()) {
731 _session->request_stop ();
732 usleep (250000);
735 if (_session->dirty()) {
736 switch (ask_about_saving_session(_("quit"))) {
737 case -1:
738 return;
739 break;
740 case 1:
741 /* use the default name */
742 if (save_state_canfail ("")) {
743 /* failed - don't quit */
744 MessageDialog msg (*editor,
745 _("\
746 Ardour was unable to save your session.\n\n\
747 If you still wish to quit, please use the\n\n\
748 \"Just quit\" option."));
749 pop_back_splash();
750 msg.run ();
751 return;
753 break;
754 case 0:
755 break;
759 second_connection.disconnect ();
760 point_one_second_connection.disconnect ();
761 point_oh_five_second_connection.disconnect ();
762 point_zero_one_second_connection.disconnect();
764 // _session->set_deletion_in_progress ();
765 _session->remove_pending_capture_state ();
766 delete _session;
767 _session = 0;
770 ArdourDialog::close_all_dialogs ();
771 engine->stop (true);
772 cerr << "Save before quit\n";
773 save_ardour_state ();
774 quit ();
778 ARDOUR_UI::ask_about_saving_session (const string & what)
780 ArdourDialog window (_("ardour: save session?"));
781 Gtk::HBox dhbox; // the hbox for the image and text
782 Gtk::Label prompt_label;
783 Gtk::Image* dimage = manage (new Gtk::Image(Stock::DIALOG_WARNING, Gtk::ICON_SIZE_DIALOG));
785 string msg;
787 msg = string_compose(_("Don't %1"), what);
788 window.add_button (msg, RESPONSE_REJECT);
789 msg = string_compose(_("Just %1"), what);
790 window.add_button (msg, RESPONSE_APPLY);
791 msg = string_compose(_("Save and %1"), what);
792 window.add_button (msg, RESPONSE_ACCEPT);
794 window.set_default_response (RESPONSE_ACCEPT);
796 Gtk::Button noquit_button (msg);
797 noquit_button.set_name ("EditorGTKButton");
799 string prompt;
800 string type;
802 if (_session->snap_name() == _session->name()) {
803 type = _("session");
804 } else {
805 type = _("snapshot");
807 prompt = string_compose(_("The %1 \"%2\"\nhas not been saved.\n\nAny changes made this time\nwill be lost unless you save it.\n\nWhat do you want to do?"),
808 type, _session->snap_name());
810 prompt_label.set_text (prompt);
811 prompt_label.set_name (X_("PrompterLabel"));
812 prompt_label.set_alignment(ALIGN_LEFT, ALIGN_TOP);
814 dimage->set_alignment(ALIGN_CENTER, ALIGN_TOP);
815 dhbox.set_homogeneous (false);
816 dhbox.pack_start (*dimage, false, false, 5);
817 dhbox.pack_start (prompt_label, true, false, 5);
818 window.get_vbox()->pack_start (dhbox);
820 window.set_name (_("Prompter"));
821 window.set_position (Gtk::WIN_POS_MOUSE);
822 window.set_modal (true);
823 window.set_resizable (false);
825 dhbox.show();
826 prompt_label.show();
827 dimage->show();
828 window.show();
829 window.set_keep_above (true);
830 window.present ();
832 ResponseType r = (ResponseType) window.run();
834 window.hide ();
836 switch (r) {
837 case RESPONSE_ACCEPT: // save and get out of here
838 return 1;
839 case RESPONSE_APPLY: // get out of here
840 return 0;
841 default:
842 break;
845 return -1;
848 gint
849 ARDOUR_UI::every_second ()
851 update_cpu_load ();
852 update_buffer_load ();
853 update_disk_space ();
854 return TRUE;
857 gint
858 ARDOUR_UI::every_point_one_seconds ()
860 update_speed_display ();
861 RapidScreenUpdate(); /* EMIT_SIGNAL */
862 return TRUE;
865 gint
866 ARDOUR_UI::every_point_zero_one_seconds ()
868 // august 2007: actual update frequency: 40Hz, not 100Hz
870 SuperRapidScreenUpdate(); /* EMIT_SIGNAL */
871 return TRUE;
874 void
875 ARDOUR_UI::update_sample_rate (nframes_t)
877 char buf[32];
879 ENSURE_GUI_THREAD (*this, &ARDOUR_UI::update_sample_rate, ignored)
881 if (!engine->connected()) {
883 snprintf (buf, sizeof (buf), _("disconnected"));
885 } else {
887 nframes_t rate = engine->frame_rate();
889 if (fmod (rate, 1000.0) != 0.0) {
890 snprintf (buf, sizeof (buf), _("%.1f kHz / %4.1f ms"),
891 (float) rate/1000.0f,
892 (engine->frames_per_cycle() / (float) rate) * 1000.0f);
893 } else {
894 snprintf (buf, sizeof (buf), _("%u kHz / %4.1f ms"),
895 rate/1000,
896 (engine->frames_per_cycle() / (float) rate) * 1000.0f);
900 sample_rate_label.set_text (buf);
903 void
904 ARDOUR_UI::update_cpu_load ()
906 char buf[32];
907 snprintf (buf, sizeof (buf), _("DSP: %5.1f%%"), engine->get_cpu_load());
908 cpu_load_label.set_text (buf);
911 void
912 ARDOUR_UI::update_buffer_load ()
914 char buf[64];
915 uint32_t c, p;
917 if (_session) {
918 c = _session->capture_load ();
919 p = _session->playback_load ();
921 push_buffer_stats (c, p);
923 snprintf (buf, sizeof (buf), _("Buffers p:%" PRIu32 "%% c:%" PRIu32 "%%"),
924 _session->playback_load(), _session->capture_load());
925 buffer_load_label.set_text (buf);
926 } else {
927 buffer_load_label.set_text ("");
931 void
932 ARDOUR_UI::count_recenabled_streams (Route& route)
934 Track* track = dynamic_cast<Track*>(&route);
935 if (track && track->diskstream()->record_enabled()) {
936 rec_enabled_streams += track->n_inputs().n_total();
940 void
941 ARDOUR_UI::update_disk_space()
943 if (_session == 0) {
944 return;
947 nframes_t frames = _session->available_capture_duration();
948 char buf[64];
949 nframes_t fr = _session->frame_rate();
951 if (frames == max_frames) {
952 strcpy (buf, _("Disk: 24hrs+"));
953 } else {
954 rec_enabled_streams = 0;
955 _session->foreach_route (this, &ARDOUR_UI::count_recenabled_streams);
957 if (rec_enabled_streams) {
958 frames /= rec_enabled_streams;
961 int hrs;
962 int mins;
963 int secs;
965 hrs = frames / (fr * 3600);
966 frames -= hrs * fr * 3600;
967 mins = frames / (fr * 60);
968 frames -= mins * fr * 60;
969 secs = frames / fr;
971 snprintf (buf, sizeof(buf), _("Disk: %02dh:%02dm:%02ds"), hrs, mins, secs);
974 disk_space_label.set_text (buf);
976 // An attempt to make the disk space label flash red when space has run out.
978 if (frames < fr * 60 * 5) {
979 /* disk_space_box.style ("disk_space_label_empty"); */
980 } else {
981 /* disk_space_box.style ("disk_space_label"); */
986 gint
987 ARDOUR_UI::update_wall_clock ()
989 time_t now;
990 struct tm *tm_now;
991 char buf[16];
993 time (&now);
994 tm_now = localtime (&now);
996 sprintf (buf, "%02d:%02d", tm_now->tm_hour, tm_now->tm_min);
997 wall_clock_label.set_text (buf);
999 return TRUE;
1002 gint
1003 ARDOUR_UI::session_menu (GdkEventButton */*ev*/)
1005 session_popup_menu->popup (0, 0);
1006 return TRUE;
1009 void
1010 ARDOUR_UI::redisplay_recent_sessions ()
1012 std::vector<sys::path> session_directories;
1013 RecentSessionsSorter cmp;
1015 recent_session_display.set_model (Glib::RefPtr<TreeModel>(0));
1016 recent_session_model->clear ();
1018 ARDOUR::RecentSessions rs;
1019 ARDOUR::read_recent_sessions (rs);
1021 if (rs.empty()) {
1022 recent_session_display.set_model (recent_session_model);
1023 return;
1026 // sort them alphabetically
1027 sort (rs.begin(), rs.end(), cmp);
1029 for (ARDOUR::RecentSessions::iterator i = rs.begin(); i != rs.end(); ++i) {
1030 session_directories.push_back ((*i).second);
1033 for (vector<sys::path>::const_iterator i = session_directories.begin();
1034 i != session_directories.end(); ++i)
1036 std::vector<sys::path> state_file_paths;
1038 // now get available states for this session
1040 get_state_files_in_directory (*i, state_file_paths);
1042 vector<string*>* states;
1043 vector<const gchar*> item;
1044 string fullpath = (*i).to_string();
1046 /* remove any trailing / */
1048 if (fullpath[fullpath.length()-1] == '/') {
1049 fullpath = fullpath.substr (0, fullpath.length()-1);
1052 /* check whether session still exists */
1053 if (!Glib::file_test(fullpath.c_str(), Glib::FILE_TEST_EXISTS)) {
1054 /* session doesn't exist */
1055 cerr << "skipping non-existent session " << fullpath << endl;
1056 continue;
1059 /* now get available states for this session */
1061 if ((states = Session::possible_states (fullpath)) == 0) {
1062 /* no state file? */
1063 continue;
1066 std::vector<string> state_file_names(get_file_names_no_extension (state_file_paths));
1068 Gtk::TreeModel::Row row = *(recent_session_model->append());
1070 row[recent_session_columns.visible_name] = Glib::path_get_basename (fullpath);
1071 row[recent_session_columns.fullpath] = fullpath;
1073 if (state_file_names.size() > 1) {
1075 // add the children
1077 for (std::vector<std::string>::iterator i2 = state_file_names.begin();
1078 i2 != state_file_names.end(); ++i2)
1081 Gtk::TreeModel::Row child_row = *(recent_session_model->append (row.children()));
1083 child_row[recent_session_columns.visible_name] = *i2;
1084 child_row[recent_session_columns.fullpath] = fullpath;
1089 recent_session_display.set_model (recent_session_model);
1092 void
1093 ARDOUR_UI::build_session_selector ()
1095 session_selector_window = new ArdourDialog (_("Recent Sessions"));
1097 Gtk::ScrolledWindow *scroller = manage (new Gtk::ScrolledWindow);
1099 session_selector_window->add_button (Stock::CANCEL, RESPONSE_CANCEL);
1100 session_selector_window->add_button (Stock::OPEN, RESPONSE_ACCEPT);
1101 session_selector_window->set_default_response (RESPONSE_ACCEPT);
1102 recent_session_model = TreeStore::create (recent_session_columns);
1103 recent_session_display.set_model (recent_session_model);
1104 recent_session_display.append_column (_("Recent Sessions"), recent_session_columns.visible_name);
1105 recent_session_display.set_headers_visible (false);
1106 recent_session_display.get_selection()->set_mode (SELECTION_BROWSE);
1107 recent_session_display.signal_row_activated().connect (sigc::mem_fun (*this, &ARDOUR_UI::recent_session_row_activated));
1109 scroller->add (recent_session_display);
1110 scroller->set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
1112 session_selector_window->set_name ("SessionSelectorWindow");
1113 session_selector_window->set_size_request (200, 400);
1114 session_selector_window->get_vbox()->pack_start (*scroller);
1116 recent_session_display.show();
1117 scroller->show();
1118 //session_selector_window->get_vbox()->show();
1121 void
1122 ARDOUR_UI::recent_session_row_activated (const TreePath& /*path*/, TreeViewColumn* /*col*/)
1124 session_selector_window->response (RESPONSE_ACCEPT);
1127 void
1128 ARDOUR_UI::open_recent_session ()
1130 bool can_return = (_session != 0);
1132 if (session_selector_window == 0) {
1133 build_session_selector ();
1136 redisplay_recent_sessions ();
1138 while (true) {
1140 session_selector_window->set_position (WIN_POS_MOUSE);
1142 ResponseType r = (ResponseType) session_selector_window->run ();
1144 switch (r) {
1145 case RESPONSE_ACCEPT:
1146 break;
1147 default:
1148 if (can_return) {
1149 session_selector_window->hide();
1150 return;
1151 } else {
1152 exit (1);
1156 if (recent_session_display.get_selection()->count_selected_rows() == 0) {
1157 continue;
1160 session_selector_window->hide();
1162 Gtk::TreeModel::iterator i = recent_session_display.get_selection()->get_selected();
1164 if (i == recent_session_model->children().end()) {
1165 return;
1168 Glib::ustring path = (*i)[recent_session_columns.fullpath];
1169 Glib::ustring state = (*i)[recent_session_columns.visible_name];
1171 _session_is_new = false;
1173 if (load_session (path, state) == 0) {
1174 break;
1177 can_return = false;
1181 bool
1182 ARDOUR_UI::check_audioengine ()
1184 if (engine) {
1185 if (!engine->connected()) {
1186 MessageDialog msg (string_compose (_("%1 is not connected to JACK\n"
1187 "You cannot open or close sessions in this condition"),
1188 PROGRAM_NAME));
1189 pop_back_splash ();
1190 msg.run ();
1191 return false;
1193 return true;
1194 } else {
1195 return false;
1199 void
1200 ARDOUR_UI::open_session ()
1202 if (!check_audioengine()) {
1203 return;
1207 /* popup selector window */
1209 if (open_session_selector == 0) {
1211 /* ardour sessions are folders */
1213 open_session_selector = new Gtk::FileChooserDialog (_("Open Session"), FILE_CHOOSER_ACTION_OPEN);
1214 open_session_selector->add_button (Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
1215 open_session_selector->add_button (Gtk::Stock::OPEN, Gtk::RESPONSE_ACCEPT);
1216 open_session_selector->set_default_response(Gtk::RESPONSE_ACCEPT);
1218 FileFilter session_filter;
1219 session_filter.add_pattern ("*.ardour");
1220 session_filter.set_name (string_compose (_("%1 sessions"), PROGRAM_NAME));
1221 open_session_selector->add_filter (session_filter);
1222 open_session_selector->set_filter (session_filter);
1225 int response = open_session_selector->run();
1226 open_session_selector->hide ();
1228 switch (response) {
1229 case RESPONSE_ACCEPT:
1230 break;
1231 default:
1232 open_session_selector->hide();
1233 return;
1236 open_session_selector->hide();
1237 string session_path = open_session_selector->get_filename();
1238 string path, name;
1239 bool isnew;
1241 if (session_path.length() > 0) {
1242 if (ARDOUR::find_session (session_path, path, name, isnew) == 0) {
1243 _session_is_new = isnew;
1244 load_session (path, name);
1250 void
1251 ARDOUR_UI::session_add_midi_route (bool disk, RouteGroup* route_group, uint32_t how_many)
1253 list<boost::shared_ptr<MidiTrack> > tracks;
1255 if (_session == 0) {
1256 warning << _("You cannot add a track without a session already loaded.") << endmsg;
1257 return;
1260 try {
1261 if (disk) {
1263 tracks = _session->new_midi_track (ARDOUR::Normal, route_group, how_many);
1265 if (tracks.size() != how_many) {
1266 if (how_many == 1) {
1267 error << _("could not create a new midi track") << endmsg;
1268 } else {
1269 error << string_compose (_("could not create %1 new midi tracks"), how_many) << endmsg;
1272 } /*else {
1273 if ((route = _session->new_midi_route ()) == 0) {
1274 error << _("could not create new midi bus") << endmsg;
1279 catch (...) {
1280 MessageDialog msg (*editor,
1281 _("There are insufficient JACK ports available\n\
1282 to create a new track or bus.\n\
1283 You should save Ardour, exit and\n\
1284 restart JACK with more ports."));
1285 msg.run ();
1290 void
1291 ARDOUR_UI::session_add_audio_route (bool track, bool aux, int32_t input_channels, int32_t output_channels, ARDOUR::TrackMode mode, RouteGroup* route_group, uint32_t how_many)
1293 list<boost::shared_ptr<AudioTrack> > tracks;
1294 RouteList routes;
1296 if (_session == 0) {
1297 warning << _("You cannot add a track or bus without a session already loaded.") << endmsg;
1298 return;
1301 try {
1302 if (track) {
1303 tracks = _session->new_audio_track (input_channels, output_channels, mode, route_group, how_many);
1305 if (tracks.size() != how_many) {
1306 if (how_many == 1) {
1307 error << _("could not create a new audio track") << endmsg;
1308 } else {
1309 error << string_compose (_("could only create %1 of %2 new audio %3"),
1310 tracks.size(), how_many, (track ? _("tracks") : _("busses"))) << endmsg;
1314 } else {
1316 routes = _session->new_audio_route (aux, input_channels, output_channels, route_group, how_many);
1318 if (routes.size() != how_many) {
1319 if (how_many == 1) {
1320 error << _("could not create a new audio track") << endmsg;
1321 } else {
1322 error << string_compose (_("could not create %1 new audio tracks"), how_many) << endmsg;
1328 catch (...) {
1329 MessageDialog msg (*editor,
1330 _("There are insufficient JACK ports available\n\
1331 to create a new track or bus.\n\
1332 You should save Ardour, exit and\n\
1333 restart JACK with more ports."));
1334 pop_back_splash ();
1335 msg.run ();
1339 void
1340 ARDOUR_UI::do_transport_locate (nframes_t new_position)
1342 nframes_t _preroll = 0;
1344 if (_session) {
1345 // XXX CONFIG_CHANGE FIX - requires AnyTime handling
1346 // _preroll = _session->convert_to_frames_at (new_position, Config->get_preroll());
1348 if (new_position > _preroll) {
1349 new_position -= _preroll;
1350 } else {
1351 new_position = 0;
1354 _session->request_locate (new_position);
1358 void
1359 ARDOUR_UI::transport_goto_start ()
1361 if (_session) {
1362 _session->goto_start();
1364 /* force displayed area in editor to start no matter
1365 what "follow playhead" setting is.
1368 if (editor) {
1369 editor->center_screen (_session->current_start_frame ());
1374 void
1375 ARDOUR_UI::transport_goto_zero ()
1377 if (_session) {
1378 _session->request_locate (0);
1380 /* force displayed area in editor to start no matter
1381 what "follow playhead" setting is.
1384 if (editor) {
1385 editor->reset_x_origin (0);
1390 void
1391 ARDOUR_UI::transport_goto_wallclock ()
1393 if (_session && editor) {
1395 time_t now;
1396 struct tm tmnow;
1397 nframes64_t frames;
1399 time (&now);
1400 localtime_r (&now, &tmnow);
1402 frames = tmnow.tm_hour * (60 * 60 * _session->frame_rate());
1403 frames += tmnow.tm_min * (60 * _session->frame_rate());
1404 frames += tmnow.tm_sec * _session->frame_rate();
1406 _session->request_locate (frames);
1408 /* force displayed area in editor to start no matter
1409 what "follow playhead" setting is.
1412 if (editor) {
1413 editor->center_screen (frames);
1418 void
1419 ARDOUR_UI::transport_goto_end ()
1421 if (_session) {
1422 nframes_t const frame = _session->current_end_frame();
1423 _session->request_locate (frame);
1425 /* force displayed area in editor to start no matter
1426 what "follow playhead" setting is.
1429 if (editor) {
1430 editor->center_screen (frame);
1435 void
1436 ARDOUR_UI::transport_stop ()
1438 if (!_session) {
1439 return;
1442 if (_session->is_auditioning()) {
1443 _session->cancel_audition ();
1444 return;
1447 _session->request_stop ();
1450 void
1451 ARDOUR_UI::transport_stop_and_forget_capture ()
1453 if (_session) {
1454 _session->request_stop (true);
1458 void
1459 ARDOUR_UI::remove_last_capture()
1461 if (editor) {
1462 editor->remove_last_capture();
1466 void
1467 ARDOUR_UI::transport_record (bool roll)
1470 if (_session) {
1471 switch (_session->record_status()) {
1472 case Session::Disabled:
1473 if (_session->ntracks() == 0) {
1474 MessageDialog msg (*editor, _("Please create 1 or more track\nbefore trying to record.\nCheck the Session menu."));
1475 msg.run ();
1476 return;
1478 _session->maybe_enable_record ();
1479 if (roll) {
1480 transport_roll ();
1482 break;
1483 case Session::Recording:
1484 if (roll) {
1485 _session->request_stop();
1486 } else {
1487 _session->disable_record (false, true);
1489 break;
1491 case Session::Enabled:
1492 _session->disable_record (false, true);
1495 //cerr << "ARDOUR_UI::transport_record () called roll = " << roll << " _session->record_status() = " << _session->record_status() << endl;
1498 void
1499 ARDOUR_UI::transport_roll ()
1501 if (!_session) {
1502 return;
1505 if (_session->is_auditioning()) {
1506 return;
1509 if (_session->config.get_external_sync()) {
1510 switch (_session->config.get_sync_source()) {
1511 case JACK:
1512 break;
1513 default:
1514 /* transport controlled by the master */
1515 return;
1519 bool rolling = _session->transport_rolling();
1521 if (_session->get_play_loop()) {
1522 _session->request_play_loop (false, true);
1523 } else if (_session->get_play_range () && !join_play_range_button.get_active()) {
1524 /* stop playing a range if we currently are */
1525 _session->request_play_range (0, true);
1528 if (join_play_range_button.get_active()) {
1529 _session->request_play_range (&editor->get_selection().time, true);
1532 if (!rolling) {
1533 _session->request_transport_speed (1.0f);
1537 void
1538 ARDOUR_UI::toggle_roll (bool with_abort, bool roll_out_of_bounded_mode)
1541 if (!_session) {
1542 return;
1545 if (_session->is_auditioning()) {
1546 _session->cancel_audition ();
1547 return;
1550 if (_session->config.get_external_sync()) {
1551 switch (_session->config.get_sync_source()) {
1552 case JACK:
1553 break;
1554 default:
1555 /* transport controlled by the master */
1556 return;
1560 bool rolling = _session->transport_rolling();
1561 bool affect_transport = true;
1563 if (rolling && roll_out_of_bounded_mode) {
1564 /* drop out of loop/range playback but leave transport rolling */
1565 if (_session->get_play_loop()) {
1566 if (Config->get_seamless_loop()) {
1567 /* the disk buffers contain copies of the loop - we can't
1568 just keep playing, so stop the transport. the user
1569 can restart as they wish.
1571 affect_transport = true;
1572 } else {
1573 /* disk buffers are normal, so we can keep playing */
1574 affect_transport = false;
1576 _session->request_play_loop (false, true);
1577 } else if (_session->get_play_range ()) {
1578 affect_transport = false;
1579 _session->request_play_range (0, true);
1583 if (affect_transport) {
1584 if (rolling) {
1585 _session->request_stop (with_abort, true);
1586 } else {
1587 if (join_play_range_button.get_active()) {
1588 _session->request_play_range (&editor->get_selection().time, true);
1591 _session->request_transport_speed (1.0f);
1596 void
1597 ARDOUR_UI::toggle_session_auto_loop ()
1599 if (_session) {
1600 if (_session->get_play_loop()) {
1601 if (_session->transport_rolling()) {
1602 Location * looploc = _session->locations()->auto_loop_location();
1603 if (looploc) {
1604 _session->request_locate (looploc->start(), true);
1606 } else {
1607 _session->request_play_loop (false);
1609 } else {
1610 Location * looploc = _session->locations()->auto_loop_location();
1611 if (looploc) {
1612 _session->request_play_loop (true);
1618 void
1619 ARDOUR_UI::transport_play_selection ()
1621 if (!_session) {
1622 return;
1625 editor->play_selection ();
1628 void
1629 ARDOUR_UI::transport_rewind (int option)
1631 float current_transport_speed;
1633 if (_session) {
1634 current_transport_speed = _session->transport_speed();
1636 if (current_transport_speed >= 0.0f) {
1637 switch (option) {
1638 case 0:
1639 _session->request_transport_speed (-1.0f);
1640 break;
1641 case 1:
1642 _session->request_transport_speed (-4.0f);
1643 break;
1644 case -1:
1645 _session->request_transport_speed (-0.5f);
1646 break;
1648 } else {
1649 /* speed up */
1650 _session->request_transport_speed (current_transport_speed * 1.5f);
1655 void
1656 ARDOUR_UI::transport_forward (int option)
1658 float current_transport_speed;
1660 if (_session) {
1661 current_transport_speed = _session->transport_speed();
1663 if (current_transport_speed <= 0.0f) {
1664 switch (option) {
1665 case 0:
1666 _session->request_transport_speed (1.0f);
1667 break;
1668 case 1:
1669 _session->request_transport_speed (4.0f);
1670 break;
1671 case -1:
1672 _session->request_transport_speed (0.5f);
1673 break;
1675 } else {
1676 /* speed up */
1677 _session->request_transport_speed (current_transport_speed * 1.5f);
1683 void
1684 ARDOUR_UI::toggle_record_enable (uint32_t dstream)
1686 if (_session == 0) {
1687 return;
1690 boost::shared_ptr<Route> r;
1692 if ((r = _session->route_by_remote_id (dstream)) != 0) {
1694 Track* t;
1696 if ((t = dynamic_cast<Track*>(r.get())) != 0) {
1697 t->diskstream()->set_record_enabled (!t->diskstream()->record_enabled());
1700 if (_session == 0) {
1701 return;
1705 void
1706 ARDOUR_UI::map_transport_state ()
1708 ENSURE_GUI_THREAD (*this, &ARDOUR_UI::map_transport_state)
1710 if (!_session) {
1711 auto_loop_button.set_visual_state (0);
1712 play_selection_button.set_visual_state (0);
1713 roll_button.set_visual_state (0);
1714 stop_button.set_visual_state (1);
1715 return;
1718 float sp = _session->transport_speed();
1720 if (sp == 1.0f) {
1721 shuttle_fract = SHUTTLE_FRACT_SPEED1; /* speed = 1.0, believe it or not */
1722 shuttle_box.queue_draw ();
1723 } else if (sp == 0.0f) {
1724 shuttle_fract = 0;
1725 shuttle_box.queue_draw ();
1726 update_disk_space ();
1729 if (sp != 0.0) {
1731 /* we're rolling */
1733 if (_session->get_play_range()) {
1735 play_selection_button.set_visual_state (1);
1736 roll_button.set_visual_state (0);
1737 auto_loop_button.set_visual_state (0);
1739 } else if (_session->get_play_loop ()) {
1741 auto_loop_button.set_visual_state (1);
1742 play_selection_button.set_visual_state (0);
1743 roll_button.set_visual_state (0);
1745 } else {
1747 roll_button.set_visual_state (1);
1748 play_selection_button.set_visual_state (0);
1749 auto_loop_button.set_visual_state (0);
1752 if (join_play_range_button.get_active()) {
1753 /* light up both roll and play-selection if they are joined */
1754 roll_button.set_visual_state (1);
1755 play_selection_button.set_visual_state (1);
1758 stop_button.set_visual_state (0);
1760 } else {
1762 stop_button.set_visual_state (1);
1763 roll_button.set_visual_state (0);
1764 play_selection_button.set_visual_state (0);
1765 auto_loop_button.set_visual_state (0);
1769 void
1770 ARDOUR_UI::engine_stopped ()
1772 ENSURE_GUI_THREAD (*this, &ARDOUR_UI::engine_stopped)
1773 ActionManager::set_sensitive (ActionManager::jack_sensitive_actions, false);
1774 ActionManager::set_sensitive (ActionManager::jack_opposite_sensitive_actions, true);
1777 void
1778 ARDOUR_UI::engine_running ()
1780 ENSURE_GUI_THREAD (*this, &ARDOUR_UI::engine_running)
1781 ActionManager::set_sensitive (ActionManager::jack_sensitive_actions, true);
1782 ActionManager::set_sensitive (ActionManager::jack_opposite_sensitive_actions, false);
1784 Glib::RefPtr<Action> action;
1785 const char* action_name = 0;
1787 switch (engine->frames_per_cycle()) {
1788 case 32:
1789 action_name = X_("JACKLatency32");
1790 break;
1791 case 64:
1792 action_name = X_("JACKLatency64");
1793 break;
1794 case 128:
1795 action_name = X_("JACKLatency128");
1796 break;
1797 case 512:
1798 action_name = X_("JACKLatency512");
1799 break;
1800 case 1024:
1801 action_name = X_("JACKLatency1024");
1802 break;
1803 case 2048:
1804 action_name = X_("JACKLatency2048");
1805 break;
1806 case 4096:
1807 action_name = X_("JACKLatency4096");
1808 break;
1809 case 8192:
1810 action_name = X_("JACKLatency8192");
1811 break;
1812 default:
1813 /* XXX can we do anything useful ? */
1814 break;
1817 if (action_name) {
1819 action = ActionManager::get_action (X_("JACK"), action_name);
1821 if (action) {
1822 Glib::RefPtr<RadioAction> ract = Glib::RefPtr<RadioAction>::cast_dynamic (action);
1823 ract->set_active ();
1828 void
1829 ARDOUR_UI::engine_halted ()
1831 ENSURE_GUI_THREAD (*this, &ARDOUR_UI::engine_halted)
1833 ActionManager::set_sensitive (ActionManager::jack_sensitive_actions, false);
1834 ActionManager::set_sensitive (ActionManager::jack_opposite_sensitive_actions, true);
1836 update_sample_rate (0);
1838 MessageDialog msg (*editor,
1839 _("\
1840 JACK has either been shutdown or it\n\
1841 disconnected Ardour because Ardour\n\
1842 was not fast enough. Try to restart\n\
1843 JACK, reconnect and save the session."));
1844 pop_back_splash ();
1845 msg.run ();
1848 int32_t
1849 ARDOUR_UI::do_engine_start ()
1851 try {
1852 engine->start();
1855 catch (...) {
1856 engine->stop ();
1857 error << _("Unable to start the session running")
1858 << endmsg;
1859 unload_session ();
1860 return -2;
1863 return 0;
1866 void
1867 ARDOUR_UI::setup_theme ()
1869 theme_manager->setup_theme();
1872 void
1873 ARDOUR_UI::update_clocks ()
1875 if (!editor || !editor->dragging_playhead()) {
1876 Clock (_session->audible_frame(), false, editor->get_preferred_edit_position()); /* EMIT_SIGNAL */
1880 void
1881 ARDOUR_UI::start_clocking ()
1883 clock_signal_connection = RapidScreenUpdate.connect (sigc::mem_fun(*this, &ARDOUR_UI::update_clocks));
1886 void
1887 ARDOUR_UI::stop_clocking ()
1889 clock_signal_connection.disconnect ();
1892 void
1893 ARDOUR_UI::toggle_clocking ()
1895 #if 0
1896 if (clock_button.get_active()) {
1897 start_clocking ();
1898 } else {
1899 stop_clocking ();
1901 #endif
1904 gint
1905 ARDOUR_UI::_blink (void *arg)
1908 ((ARDOUR_UI *) arg)->blink ();
1909 return TRUE;
1912 void
1913 ARDOUR_UI::blink ()
1915 Blink (blink_on = !blink_on); /* EMIT_SIGNAL */
1918 void
1919 ARDOUR_UI::start_blinking ()
1921 /* Start the blink signal. Everybody with a blinking widget
1922 uses Blink to drive the widget's state.
1925 if (blink_timeout_tag < 0) {
1926 blink_on = false;
1927 blink_timeout_tag = g_timeout_add (240, _blink, this);
1931 void
1932 ARDOUR_UI::stop_blinking ()
1934 if (blink_timeout_tag >= 0) {
1935 g_source_remove (blink_timeout_tag);
1936 blink_timeout_tag = -1;
1941 /** Ask the user for the name of a new shapshot and then take it.
1943 void
1944 ARDOUR_UI::snapshot_session ()
1946 ArdourPrompter prompter (true);
1947 string snapname;
1948 char timebuf[128];
1949 time_t n;
1950 struct tm local_time;
1952 time (&n);
1953 localtime_r (&n, &local_time);
1954 strftime (timebuf, sizeof(timebuf), "%FT%T", &local_time);
1956 prompter.set_name ("Prompter");
1957 prompter.add_button (Gtk::Stock::SAVE, Gtk::RESPONSE_ACCEPT);
1958 prompter.set_title (_("Take Snapshot"));
1959 prompter.set_prompt (_("Name of New Snapshot"));
1960 prompter.set_initial_text (timebuf);
1962 again:
1963 switch (prompter.run()) {
1964 case RESPONSE_ACCEPT:
1966 prompter.get_result (snapname);
1968 bool do_save = (snapname.length() != 0);
1970 if (do_save) {
1971 if (snapname.find ('/') != string::npos) {
1972 MessageDialog msg (_("To ensure compatibility with various systems\n"
1973 "snapshot names may not contain a '/' character"));
1974 msg.run ();
1975 goto again;
1977 if (snapname.find ('\\') != string::npos) {
1978 MessageDialog msg (_("To ensure compatibility with various systems\n"
1979 "snapshot names may not contain a '\\' character"));
1980 msg.run ();
1981 goto again;
1985 vector<sys::path> p;
1986 get_state_files_in_directory (_session->session_directory().root_path(), p);
1987 vector<string> n = get_file_names_no_extension (p);
1988 if (find (n.begin(), n.end(), snapname) != n.end()) {
1990 ArdourDialog confirm (_("Confirm snapshot overwrite"), true);
1991 Label m (_("A snapshot already exists with that name. Do you want to overwrite it?"));
1992 confirm.get_vbox()->pack_start (m, true, true);
1993 confirm.add_button (Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
1994 confirm.add_button (_("Overwrite"), Gtk::RESPONSE_ACCEPT);
1995 confirm.show_all ();
1996 switch (confirm.run()) {
1997 case RESPONSE_CANCEL:
1998 do_save = false;
2002 if (do_save) {
2003 save_state (snapname);
2005 break;
2008 default:
2009 break;
2013 void
2014 ARDOUR_UI::save_state (const string & name)
2016 save_state_canfail (name);
2020 ARDOUR_UI::save_state_canfail (string name)
2022 if (_session) {
2023 int ret;
2025 if (name.length() == 0) {
2026 name = _session->snap_name();
2029 if ((ret = _session->save_state (name)) != 0) {
2030 return ret;
2033 cerr << "SS canfail\n";
2034 save_ardour_state (); /* XXX cannot fail? yeah, right ... */
2035 return 0;
2038 void
2039 ARDOUR_UI::primary_clock_value_changed ()
2041 if (_session) {
2042 _session->request_locate (primary_clock.current_time ());
2046 void
2047 ARDOUR_UI::big_clock_value_changed ()
2049 if (_session) {
2050 _session->request_locate (big_clock.current_time ());
2054 void
2055 ARDOUR_UI::secondary_clock_value_changed ()
2057 if (_session) {
2058 _session->request_locate (secondary_clock.current_time ());
2062 void
2063 ARDOUR_UI::transport_rec_enable_blink (bool onoff)
2065 if (_session == 0) {
2066 return;
2069 Session::RecordState const r = _session->record_status ();
2070 bool const h = _session->have_rec_enabled_diskstream ();
2072 if (r == Session::Enabled || (r == Session::Recording && !h)) {
2073 if (onoff) {
2074 rec_button.set_visual_state (2);
2075 } else {
2076 rec_button.set_visual_state (0);
2078 } else if (r == Session::Recording && h) {
2079 rec_button.set_visual_state (1);
2080 } else {
2081 rec_button.set_visual_state (0);
2085 void
2086 ARDOUR_UI::save_template ()
2088 ArdourPrompter prompter (true);
2089 string name;
2091 if (!check_audioengine()) {
2092 return;
2095 prompter.set_name (X_("Prompter"));
2096 prompter.set_title (_("Save Mix Template"));
2097 prompter.set_prompt (_("Name for mix template:"));
2098 prompter.set_initial_text(_session->name() + _("-template"));
2099 prompter.add_button (Gtk::Stock::SAVE, Gtk::RESPONSE_ACCEPT);
2101 switch (prompter.run()) {
2102 case RESPONSE_ACCEPT:
2103 prompter.get_result (name);
2105 if (name.length()) {
2106 _session->save_template (name);
2108 break;
2110 default:
2111 break;
2115 void
2116 ARDOUR_UI::edit_metadata ()
2118 SessionMetadataEditor dialog;
2119 dialog.set_session (_session);
2120 editor->ensure_float (dialog);
2121 dialog.run ();
2124 void
2125 ARDOUR_UI::import_metadata ()
2127 SessionMetadataImporter dialog;
2128 dialog.set_session (_session);
2129 editor->ensure_float (dialog);
2130 dialog.run ();
2133 void
2134 ARDOUR_UI::fontconfig_dialog ()
2136 #ifdef GTKOSX
2137 /* X11 users will always have fontconfig info around, but new GTK-OSX users
2138 may not and it can take a while to build it. Warn them.
2141 Glib::ustring fontconfig = Glib::build_filename (Glib::get_home_dir(), ".fontconfig");
2143 if (!Glib::file_test (fontconfig, Glib::FILE_TEST_EXISTS|Glib::FILE_TEST_IS_DIR)) {
2144 MessageDialog msg (*_startup,
2145 _("Welcome to Ardour.\n\n"
2146 "The program will take a bit longer to start up\n"
2147 "while the system fonts are checked.\n\n"
2148 "This will only be done once, and you will\n"
2149 "not see this message again\n"),
2150 true,
2151 Gtk::MESSAGE_INFO,
2152 Gtk::BUTTONS_OK);
2153 pop_back_splash ();
2154 msg.show_all ();
2155 msg.present ();
2156 msg.run ();
2158 #endif
2161 void
2162 ARDOUR_UI::parse_cmdline_path (const Glib::ustring& cmdline_path, Glib::ustring& session_name, Glib::ustring& session_path, bool& existing_session)
2164 existing_session = false;
2166 if (Glib::file_test (cmdline_path, Glib::FILE_TEST_IS_DIR)) {
2167 session_path = cmdline_path;
2168 existing_session = true;
2169 } else if (Glib::file_test (cmdline_path, Glib::FILE_TEST_IS_REGULAR)) {
2170 session_path = Glib::path_get_dirname (string (cmdline_path));
2171 existing_session = true;
2172 } else {
2173 /* it doesn't exist, assume the best */
2174 session_path = Glib::path_get_dirname (string (cmdline_path));
2177 session_name = basename_nosuffix (string (cmdline_path));
2181 ARDOUR_UI::load_cmdline_session (const Glib::ustring& session_name, const Glib::ustring& session_path, bool& existing_session)
2183 /* when this is called, the backend audio system must be running */
2185 /* the main idea here is to deal with the fact that a cmdline argument for the session
2186 can be interpreted in different ways - it could be a directory or a file, and before
2187 we load, we need to know both the session directory and the snapshot (statefile) within it
2188 that we are supposed to use.
2191 if (session_name.length() == 0 || session_path.length() == 0) {
2192 return false;
2195 if (Glib::file_test (session_path, Glib::FILE_TEST_IS_DIR)) {
2197 Glib::ustring predicted_session_file;
2199 predicted_session_file = session_path;
2200 predicted_session_file += '/';
2201 predicted_session_file += session_name;
2202 predicted_session_file += ARDOUR::statefile_suffix;
2204 if (Glib::file_test (predicted_session_file, Glib::FILE_TEST_EXISTS)) {
2205 existing_session = true;
2208 } else if (Glib::file_test (session_path, Glib::FILE_TEST_EXISTS)) {
2210 if (session_path.find (ARDOUR::statefile_suffix) == session_path.length() - 7) {
2211 /* existing .ardour file */
2212 existing_session = true;
2215 } else {
2216 existing_session = false;
2219 /* lets just try to load it */
2221 if (create_engine ()) {
2222 backend_audio_error (false, _startup);
2223 return -1;
2226 return load_session (session_path, session_name);
2229 bool
2230 ARDOUR_UI::ask_about_loading_existing_session (const Glib::ustring& session_path)
2232 Glib::ustring str = string_compose (_("This session\n%1\nalready exists. Do you want to open it?"), session_path);
2234 MessageDialog msg (str,
2235 false,
2236 Gtk::MESSAGE_WARNING,
2237 Gtk::BUTTONS_YES_NO,
2238 true);
2241 msg.set_name (X_("OpenExistingDialog"));
2242 msg.set_title (_("Open Existing Session"));
2243 msg.set_wmclass (X_("existing_session"), "Ardour");
2244 msg.set_position (Gtk::WIN_POS_MOUSE);
2245 pop_back_splash ();
2247 switch (msg.run()) {
2248 case RESPONSE_YES:
2249 return true;
2250 break;
2252 return false;
2256 ARDOUR_UI::build_session_from_nsd (const Glib::ustring& session_path, const Glib::ustring& session_name)
2258 BusProfile bus_profile;
2260 if (Profile->get_sae()) {
2262 bus_profile.master_out_channels = 2;
2263 bus_profile.input_ac = AutoConnectPhysical;
2264 bus_profile.output_ac = AutoConnectMaster;
2265 bus_profile.requested_physical_in = 0; // use all available
2266 bus_profile.requested_physical_out = 0; // use all available
2268 } else {
2270 /* get settings from advanced section of NSD */
2272 if (_startup->create_master_bus()) {
2273 bus_profile.master_out_channels = (uint32_t) _startup->master_channel_count();
2274 } else {
2275 bus_profile.master_out_channels = 0;
2278 if (_startup->connect_inputs()) {
2279 bus_profile.input_ac = AutoConnectPhysical;
2280 } else {
2281 bus_profile.input_ac = AutoConnectOption (0);
2284 /// @todo some minor tweaks.
2286 bus_profile.output_ac = AutoConnectOption (0);
2288 if (_startup->connect_outputs ()) {
2289 if (_startup->connect_outs_to_master()) {
2290 bus_profile.output_ac = AutoConnectMaster;
2291 } else if (_startup->connect_outs_to_physical()) {
2292 bus_profile.output_ac = AutoConnectPhysical;
2296 bus_profile.requested_physical_in = (uint32_t) _startup->input_limit_count();
2297 bus_profile.requested_physical_out = (uint32_t) _startup->output_limit_count();
2300 if (build_session (session_path, session_name, bus_profile)) {
2301 return -1;
2304 return 0;
2307 void
2308 ARDOUR_UI::idle_load (const Glib::ustring& path)
2310 if (_session) {
2311 if (Glib::file_test (path, Glib::FILE_TEST_IS_DIR)) {
2312 /* /path/to/foo => /path/to/foo, foo */
2313 load_session (path, basename_nosuffix (path));
2314 } else {
2315 /* /path/to/foo/foo.ardour => /path/to/foo, foo */
2316 load_session (Glib::path_get_dirname (path), basename_nosuffix (path));
2318 } else {
2320 ARDOUR_COMMAND_LINE::session_name = path;
2323 * new_session_dialog doens't exist in A3
2324 * Try to remove all references to it to
2325 * see if it will compile. NOTE: this will
2326 * likely cause a runtime issue is my somewhat
2327 * uneducated guess.
2330 //if (new_session_dialog) {
2333 /* make it break out of Dialog::run() and
2334 start again.
2337 //new_session_dialog->response (1);
2342 void
2343 ARDOUR_UI::end_loading_messages ()
2345 // hide_splash ();
2348 void
2349 ARDOUR_UI::loading_message (const std::string& /*msg*/)
2351 // show_splash ();
2352 // splash->message (msg);
2353 flush_pending ();
2356 /** @param quit_on_cancel true if exit() should be called if the user clicks `cancel' in the new session dialog */
2358 ARDOUR_UI::get_session_parameters (bool quit_on_cancel, bool should_be_new)
2360 Glib::ustring session_name;
2361 Glib::ustring session_path;
2362 Glib::ustring template_name;
2363 int ret = -1;
2364 bool likely_new = false;
2366 while (ret != 0) {
2368 if (!should_be_new && !ARDOUR_COMMAND_LINE::session_name.empty()) {
2370 /* if they named a specific statefile, use it, otherwise they are
2371 just giving a session folder, and we want to use it as is
2372 to find the session.
2375 if (ARDOUR_COMMAND_LINE::session_name.find (statefile_suffix) != string::npos) {
2376 session_path = Glib::path_get_dirname (ARDOUR_COMMAND_LINE::session_name);
2377 } else {
2378 session_path = ARDOUR_COMMAND_LINE::session_name;
2381 session_name = Glib::path_get_basename (ARDOUR_COMMAND_LINE::session_name);
2383 } else {
2385 bool const apply = run_startup (should_be_new);
2386 if (!apply) {
2387 if (quit_on_cancel) {
2388 exit (1);
2389 } else {
2390 return ret;
2394 /* if we run the startup dialog again, offer more than just "new session" */
2396 should_be_new = false;
2398 session_name = _startup->session_name (likely_new);
2400 /* this shouldn't happen, but we catch it just in case it does */
2402 if (session_name.empty()) {
2403 break;
2405 if (_startup->use_session_template()) {
2406 template_name = _startup->session_template_name();
2407 _session_is_new = true;
2410 if (session_name[0] == '/' ||
2411 (session_name.length() > 2 && session_name[0] == '.' && session_name[1] == '/') ||
2412 (session_name.length() > 3 && session_name[0] == '.' && session_name[1] == '.' && session_name[2] == '/')) {
2414 /* absolute path or cwd-relative path specified for session name: infer session folder
2415 from what was given.
2418 session_path = Glib::path_get_dirname (session_name);
2419 session_name = Glib::path_get_basename (session_name);
2421 } else {
2423 session_path = _startup->session_folder();
2427 if (create_engine ()) {
2428 break;
2431 if (Glib::file_test (session_path, Glib::FileTest (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))) {
2433 if (likely_new) {
2435 Glib::ustring existing = Glib::build_filename (session_path, session_name);
2437 if (!ask_about_loading_existing_session (existing)) {
2438 ARDOUR_COMMAND_LINE::session_name = ""; // cancel that
2439 continue;
2443 _session_is_new = false;
2445 } else {
2447 if (!likely_new) {
2448 MessageDialog msg (string_compose (_("There is no existing session at \"%1\""), session_path));
2449 msg.run ();
2450 ARDOUR_COMMAND_LINE::session_name = ""; // cancel that
2451 continue;
2454 if (session_name.find ('/') != Glib::ustring::npos) {
2455 MessageDialog msg (*_startup, _("To ensure compatibility with various systems\n"
2456 "session names may not contain a '/' character"));
2457 msg.run ();
2458 ARDOUR_COMMAND_LINE::session_name = ""; // cancel that
2459 continue;
2462 if (session_name.find ('\\') != Glib::ustring::npos) {
2463 MessageDialog msg (*_startup, _("To ensure compatibility with various systems\n"
2464 "session names may not contain a '\\' character"));
2465 msg.run ();
2466 ARDOUR_COMMAND_LINE::session_name = ""; // cancel that
2467 continue;
2470 _session_is_new = true;
2473 if (likely_new && template_name.empty()) {
2475 ret = build_session_from_nsd (session_path, session_name);
2477 } else {
2479 ret = load_session (session_path, session_name, template_name);
2480 if (!ARDOUR_COMMAND_LINE::immediate_save.empty()) {
2481 _session->save_state (ARDOUR_COMMAND_LINE::immediate_save, false);
2482 exit (1);
2487 return ret;
2490 void
2491 ARDOUR_UI::close_session()
2493 if (!check_audioengine()) {
2494 return;
2497 if (unload_session (true)) {
2498 return;
2501 ARDOUR_COMMAND_LINE::session_name = "";
2502 get_session_parameters (true, false);
2506 ARDOUR_UI::load_session (const Glib::ustring& path, const Glib::ustring& snap_name, Glib::ustring mix_template)
2508 Session *new_session;
2509 int unload_status;
2510 int retval = -1;
2512 session_loaded = false;
2514 if (!check_audioengine()) {
2515 return -1;
2518 unload_status = unload_session ();
2520 if (unload_status < 0) {
2521 goto out;
2522 } else if (unload_status > 0) {
2523 retval = 0;
2524 goto out;
2527 loading_message (string_compose (_("Please wait while %1loads your session"), PROGRAM_NAME));
2529 try {
2530 new_session = new Session (*engine, path, snap_name, 0, mix_template);
2533 /* this one is special */
2535 catch (AudioEngine::PortRegistrationFailure& err) {
2537 MessageDialog msg (err.what(),
2538 true,
2539 Gtk::MESSAGE_INFO,
2540 Gtk::BUTTONS_CLOSE);
2542 msg.set_title (_("Port Registration Error"));
2543 msg.set_secondary_text (_("Click the Close button to try again."));
2544 msg.set_position (Gtk::WIN_POS_CENTER);
2545 pop_back_splash ();
2546 msg.present ();
2548 int response = msg.run ();
2550 msg.hide ();
2552 switch (response) {
2553 case RESPONSE_CANCEL:
2554 exit (1);
2555 default:
2556 break;
2558 goto out;
2561 catch (...) {
2563 MessageDialog msg (string_compose(_("Session \"%1 (snapshot %2)\" did not load successfully"), path, snap_name),
2564 true,
2565 Gtk::MESSAGE_INFO,
2566 Gtk::BUTTONS_CLOSE);
2568 msg.set_title (_("Loading Error"));
2569 msg.set_secondary_text (_("Click the Close button to try again."));
2570 msg.set_position (Gtk::WIN_POS_CENTER);
2571 pop_back_splash ();
2572 msg.present ();
2574 int response = msg.run ();
2576 msg.hide ();
2578 switch (response) {
2579 case RESPONSE_CANCEL:
2580 exit (1);
2581 default:
2582 break;
2584 goto out;
2587 set_session (new_session);
2589 session_loaded = true;
2591 goto_editor_window ();
2593 if (_session) {
2594 _session->set_clean ();
2597 flush_pending ();
2598 retval = 0;
2600 out:
2601 return retval;
2605 ARDOUR_UI::build_session (const Glib::ustring& path, const Glib::ustring& snap_name, BusProfile& bus_profile)
2607 Session *new_session;
2608 int x;
2610 if (!check_audioengine()) {
2611 return -1;
2614 session_loaded = false;
2616 x = unload_session ();
2618 if (x < 0) {
2619 return -1;
2620 } else if (x > 0) {
2621 return 0;
2624 _session_is_new = true;
2626 try {
2627 new_session = new Session (*engine, path, snap_name, &bus_profile);
2630 catch (...) {
2632 MessageDialog msg (string_compose(_("Could not create session in \"%1\""), path));
2633 pop_back_splash ();
2634 msg.run ();
2635 return -1;
2638 set_session (new_session);
2640 session_loaded = true;
2642 new_session->save_state(new_session->name());
2644 return 0;
2647 void
2648 ARDOUR_UI::show ()
2650 if (editor) {
2651 editor->show_window ();
2653 if (!shown_flag) {
2654 editor->present ();
2657 shown_flag = true;
2661 void
2662 ARDOUR_UI::launch_chat ()
2664 #ifdef __APPLE__
2665 open_uri("http://webchat.freenode.net/?channels=ardour-osx");
2666 #else
2667 open_uri("http://webchat.freenode.net/?channels=ardour");
2668 #endif
2671 void
2672 ARDOUR_UI::show_about ()
2674 if (about == 0) {
2675 about = new About;
2676 about->signal_response().connect(sigc::mem_fun (*this, &ARDOUR_UI::about_signal_response) );
2679 about->set_transient_for(*editor);
2680 about->show_all ();
2683 void
2684 ARDOUR_UI::hide_about ()
2686 if (about) {
2687 about->get_window()->set_cursor ();
2688 about->hide ();
2692 void
2693 ARDOUR_UI::about_signal_response (int /*response*/)
2695 hide_about();
2698 void
2699 ARDOUR_UI::show_splash ()
2701 if (splash == 0) {
2702 try {
2703 splash = new Splash;
2704 } catch (...) {
2705 return;
2709 splash->show ();
2710 splash->present ();
2711 splash->queue_draw ();
2712 splash->get_window()->process_updates (true);
2713 flush_pending ();
2716 void
2717 ARDOUR_UI::hide_splash ()
2719 if (splash) {
2720 splash->hide();
2724 void
2725 ARDOUR_UI::display_cleanup_results (ARDOUR::CleanupReport& rep, const gchar* list_title,
2726 const string& plural_msg, const string& singular_msg)
2728 size_t removed;
2730 removed = rep.paths.size();
2732 if (removed == 0) {
2733 MessageDialog msgd (*editor,
2734 _("No audio files were ready for cleanup"),
2735 true,
2736 Gtk::MESSAGE_INFO,
2737 (Gtk::ButtonsType)(Gtk::BUTTONS_OK) );
2738 msgd.set_secondary_text (_("If this seems suprising, \n\
2739 check for any existing snapshots.\n\
2740 These may still include regions that\n\
2741 require some unused files to continue to exist."));
2743 msgd.run ();
2744 return;
2747 ArdourDialog results (_("ardour: cleanup"), true, false);
2749 struct CleanupResultsModelColumns : public Gtk::TreeModel::ColumnRecord {
2750 CleanupResultsModelColumns() {
2751 add (visible_name);
2752 add (fullpath);
2754 Gtk::TreeModelColumn<Glib::ustring> visible_name;
2755 Gtk::TreeModelColumn<Glib::ustring> fullpath;
2759 CleanupResultsModelColumns results_columns;
2760 Glib::RefPtr<Gtk::ListStore> results_model;
2761 Gtk::TreeView results_display;
2763 results_model = ListStore::create (results_columns);
2764 results_display.set_model (results_model);
2765 results_display.append_column (list_title, results_columns.visible_name);
2767 results_display.set_name ("CleanupResultsList");
2768 results_display.set_headers_visible (true);
2769 results_display.set_headers_clickable (false);
2770 results_display.set_reorderable (false);
2772 Gtk::ScrolledWindow list_scroller;
2773 Gtk::Label txt;
2774 Gtk::VBox dvbox;
2775 Gtk::HBox dhbox; // the hbox for the image and text
2776 Gtk::HBox ddhbox; // the hbox we eventually pack into the dialog's vbox
2777 Gtk::Image* dimage = manage (new Gtk::Image(Stock::DIALOG_INFO, Gtk::ICON_SIZE_DIALOG));
2779 dimage->set_alignment(ALIGN_LEFT, ALIGN_TOP);
2781 const string dead_sound_directory = _session->session_directory().dead_sound_path().to_string();
2783 /* subst:
2784 %1 - number of files removed
2785 %2 - location of "dead_sounds"
2786 %3 - size of files affected
2787 %4 - prefix for "bytes" to produce sensible results (e.g. mega, kilo, giga)
2790 const char* bprefix;
2791 double space_adjusted = 0;
2793 if (rep.space < 100000.0f) {
2794 bprefix = X_("kilo");
2795 } else if (rep.space < 1000000.0f * 1000) {
2796 bprefix = X_("mega");
2797 space_adjusted = truncf((float)rep.space / 1000.0);
2798 } else {
2799 bprefix = X_("giga");
2800 space_adjusted = truncf((float)rep.space / (1000000.0 * 1000));
2803 if (removed > 1) {
2804 txt.set_text (string_compose (plural_msg, removed, _session->path() + "dead_sounds", space_adjusted, bprefix));
2805 } else {
2806 txt.set_text (string_compose (singular_msg, removed, _session->path() + "dead_sounds", space_adjusted, bprefix));
2809 dhbox.pack_start (*dimage, true, false, 5);
2810 dhbox.pack_start (txt, true, false, 5);
2812 for (vector<string>::iterator i = rep.paths.begin(); i != rep.paths.end(); ++i) {
2813 TreeModel::Row row = *(results_model->append());
2814 row[results_columns.visible_name] = *i;
2815 row[results_columns.fullpath] = *i;
2818 list_scroller.add (results_display);
2819 list_scroller.set_size_request (-1, 150);
2820 list_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
2822 dvbox.pack_start (dhbox, true, false, 5);
2823 dvbox.pack_start (list_scroller, true, false, 5);
2824 ddhbox.pack_start (dvbox, true, false, 5);
2826 results.get_vbox()->pack_start (ddhbox, true, false, 5);
2827 results.add_button (Stock::CLOSE, RESPONSE_CLOSE);
2828 results.set_default_response (RESPONSE_CLOSE);
2829 results.set_position (Gtk::WIN_POS_MOUSE);
2831 results_display.show();
2832 list_scroller.show();
2833 txt.show();
2834 dvbox.show();
2835 dhbox.show();
2836 ddhbox.show();
2837 dimage->show();
2839 //results.get_vbox()->show();
2840 results.set_resizable (false);
2842 results.run ();
2846 void
2847 ARDOUR_UI::cleanup ()
2849 if (_session == 0) {
2850 /* shouldn't happen: menu item is insensitive */
2851 return;
2855 MessageDialog checker (_("Are you sure you want to cleanup?"),
2856 true,
2857 Gtk::MESSAGE_QUESTION,
2858 (Gtk::ButtonsType)(Gtk::BUTTONS_NONE));
2860 checker.set_secondary_text(_("Cleanup is a destructive operation.\n\
2861 ALL undo/redo information will be lost if you cleanup.\n\
2862 After cleanup, unused audio files will be moved to a \
2863 \"dead sounds\" location."));
2865 checker.add_button (Stock::CANCEL, RESPONSE_CANCEL);
2866 checker.add_button (_("Clean Up"), RESPONSE_ACCEPT);
2867 checker.set_default_response (RESPONSE_CANCEL);
2869 checker.set_name (_("CleanupDialog"));
2870 checker.set_wmclass (X_("ardour_cleanup"), "Ardour");
2871 checker.set_position (Gtk::WIN_POS_MOUSE);
2873 switch (checker.run()) {
2874 case RESPONSE_ACCEPT:
2875 break;
2876 default:
2877 return;
2880 ARDOUR::CleanupReport rep;
2882 editor->prepare_for_cleanup ();
2884 /* do not allow flush until a session is reloaded */
2886 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Main"), X_("FlushWastebasket"));
2887 if (act) {
2888 act->set_sensitive (false);
2891 if (_session->cleanup_sources (rep)) {
2892 editor->finish_cleanup ();
2893 return;
2896 editor->finish_cleanup ();
2898 checker.hide();
2899 display_cleanup_results (rep,
2900 _("cleaned files"),
2901 _("\
2902 The following %1 files were not in use and \n\
2903 have been moved to:\n\
2904 %2. \n\n\
2905 Flushing the wastebasket will \n\
2906 release an additional\n\
2907 %3 %4bytes of disk space.\n"),
2908 _("\
2909 The following file was not in use and \n \
2910 has been moved to:\n \
2911 %2. \n\n\
2912 Flushing the wastebasket will \n\
2913 release an additional\n\
2914 %3 %4bytes of disk space.\n"
2919 void
2920 ARDOUR_UI::flush_trash ()
2922 if (_session == 0) {
2923 /* shouldn't happen: menu item is insensitive */
2924 return;
2927 ARDOUR::CleanupReport rep;
2929 if (_session->cleanup_trash_sources (rep)) {
2930 return;
2933 display_cleanup_results (rep,
2934 _("deleted file"),
2935 _("The following %1 files were deleted from\n\
2936 %2,\n\
2937 releasing %3 %4bytes of disk space"),
2938 _("The following file was deleted from\n\
2939 %2,\n\
2940 releasing %3 %4bytes of disk space"));
2943 void
2944 ARDOUR_UI::add_route (Gtk::Window* float_window)
2946 int count;
2948 if (!_session) {
2949 return;
2952 if (add_route_dialog == 0) {
2953 add_route_dialog = new AddRouteDialog (_session);
2954 if (float_window) {
2955 add_route_dialog->set_transient_for (*float_window);
2959 if (add_route_dialog->is_visible()) {
2960 /* we're already doing this */
2961 return;
2964 ResponseType r = (ResponseType) add_route_dialog->run ();
2966 add_route_dialog->hide();
2968 switch (r) {
2969 case RESPONSE_ACCEPT:
2970 break;
2971 default:
2972 return;
2973 break;
2976 if ((count = add_route_dialog->count()) <= 0) {
2977 return;
2980 string template_path = add_route_dialog->track_template();
2982 if (!template_path.empty()) {
2983 _session->new_route_from_template (count, template_path);
2984 return;
2987 uint32_t input_chan = add_route_dialog->channels ();
2988 uint32_t output_chan;
2989 string name_template = add_route_dialog->name_template ();
2990 bool track = add_route_dialog->track ();
2991 bool aux = !track && add_route_dialog->aux();
2992 RouteGroup* route_group = add_route_dialog->route_group ();
2994 AutoConnectOption oac = Config->get_output_auto_connect();
2996 if (oac & AutoConnectMaster) {
2997 output_chan = (_session->master_out() ? _session->master_out()->n_inputs().n_audio() : input_chan);
2998 } else {
2999 output_chan = input_chan;
3002 /* XXX do something with name template */
3004 if (add_route_dialog->type() == ARDOUR::DataType::MIDI) {
3005 if (track) {
3006 session_add_midi_track (route_group, count);
3007 } else {
3008 MessageDialog msg (*editor,
3009 _("Sorry, MIDI Busses are not supported at this time."));
3010 msg.run ();
3011 //session_add_midi_bus();
3013 } else {
3014 if (track) {
3015 session_add_audio_track (input_chan, output_chan, add_route_dialog->mode(), route_group, count);
3016 } else {
3017 session_add_audio_bus (aux, input_chan, output_chan, route_group, count);
3022 XMLNode*
3023 ARDOUR_UI::mixer_settings () const
3025 XMLNode* node = 0;
3027 if (_session) {
3028 node = _session->instant_xml(X_("Mixer"));
3029 } else {
3030 node = Config->instant_xml(X_("Mixer"));
3033 if (!node) {
3034 node = new XMLNode (X_("Mixer"));
3037 return node;
3040 XMLNode*
3041 ARDOUR_UI::editor_settings () const
3043 XMLNode* node = 0;
3045 if (_session) {
3046 node = _session->instant_xml(X_("Editor"));
3047 } else {
3048 node = Config->instant_xml(X_("Editor"));
3051 if (!node) {
3052 if (getenv("ARDOUR_INSTANT_XML_PATH")) {
3053 node = Config->instant_xml(getenv("ARDOUR_INSTANT_XML_PATH"));
3057 if (!node) {
3058 node = new XMLNode (X_("Editor"));
3061 return node;
3064 XMLNode*
3065 ARDOUR_UI::keyboard_settings () const
3067 XMLNode* node = 0;
3069 node = Config->extra_xml(X_("Keyboard"));
3071 if (!node) {
3072 node = new XMLNode (X_("Keyboard"));
3074 return node;
3077 void
3078 ARDOUR_UI::create_xrun_marker(nframes_t where)
3080 editor->mouse_add_new_marker (where, false, true);
3083 void
3084 ARDOUR_UI::halt_on_xrun_message ()
3086 MessageDialog msg (*editor,
3087 _("Recording was stopped because your system could not keep up."));
3088 msg.run ();
3091 void
3092 ARDOUR_UI::xrun_handler(nframes_t where)
3094 if (!_session) {
3095 return;
3098 ENSURE_GUI_THREAD (*this, &ARDOUR_UI::xrun_handler, where)
3100 if (_session && Config->get_create_xrun_marker() && _session->actively_recording()) {
3101 create_xrun_marker(where);
3104 if (_session && Config->get_stop_recording_on_xrun() && _session->actively_recording()) {
3105 halt_on_xrun_message ();
3109 void
3110 ARDOUR_UI::push_buffer_stats (uint32_t capture, uint32_t playback)
3112 time_t now;
3113 time (&now);
3115 while (disk_buffer_stats.size() > 60) {
3116 disk_buffer_stats.pop_front ();
3119 disk_buffer_stats.push_back (DiskBufferStat (now, capture, playback));
3122 void
3123 ARDOUR_UI::write_buffer_stats ()
3125 std::ofstream fout;
3126 struct tm tm;
3127 char buf[64];
3128 char path[PATH_MAX+1]; int fd;
3130 strcpy (path, "ardourBufferingXXXXXX");
3132 if ((fd = mkstemp (path )) < 0) {
3133 cerr << X_("cannot find temporary name for buffer stats") << endl;
3134 return;
3137 fout.open (path);
3138 close (fd);
3140 if (!fout) {
3141 cerr << string_compose (X_("cannot open file %1 for buffer stats"), path) << endl;
3142 return;
3145 for (list<DiskBufferStat>::iterator i = disk_buffer_stats.begin(); i != disk_buffer_stats.end(); ++i) {
3146 localtime_r (&(*i).when, &tm);
3147 strftime (buf, sizeof (buf), "%T", &tm);
3148 fout << buf << ' ' << (*i).capture << ' ' << (*i).playback << endl;
3151 disk_buffer_stats.clear ();
3153 fout.close ();
3155 cerr << "buffering statistics can be found in: " << path << endl;
3158 void
3159 ARDOUR_UI::disk_overrun_handler ()
3161 ENSURE_GUI_THREAD (*this, &ARDOUR_UI::disk_overrun_handler)
3163 write_buffer_stats ();
3165 if (!have_disk_speed_dialog_displayed) {
3166 have_disk_speed_dialog_displayed = true;
3167 MessageDialog* msg = new MessageDialog (*editor, _("\
3168 The disk system on your computer\n\
3169 was not able to keep up with Ardour.\n\
3171 Specifically, it failed to write data to disk\n\
3172 quickly enough to keep up with recording.\n"));
3173 msg->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &ARDOUR_UI::disk_speed_dialog_gone), msg));
3174 msg->show ();
3178 void
3179 ARDOUR_UI::disk_underrun_handler ()
3181 ENSURE_GUI_THREAD (*this, &ARDOUR_UI::disk_underrun_handler)
3183 write_buffer_stats ();
3185 if (!have_disk_speed_dialog_displayed) {
3186 have_disk_speed_dialog_displayed = true;
3187 MessageDialog* msg = new MessageDialog (*editor,
3188 _("The disk system on your computer\n\
3189 was not able to keep up with Ardour.\n\
3191 Specifically, it failed to read data from disk\n\
3192 quickly enough to keep up with playback.\n"));
3193 msg->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &ARDOUR_UI::disk_speed_dialog_gone), msg));
3194 msg->show ();
3198 void
3199 ARDOUR_UI::disk_speed_dialog_gone (int /*ignored_response*/, MessageDialog* msg)
3201 have_disk_speed_dialog_displayed = false;
3202 delete msg;
3205 void
3206 ARDOUR_UI::session_dialog (std::string msg)
3208 ENSURE_GUI_THREAD (*this, &ARDOUR_UI::session_dialog, msg)
3210 MessageDialog* d;
3212 if (editor) {
3213 d = new MessageDialog (*editor, msg, false, MESSAGE_INFO, BUTTONS_OK, true);
3214 } else {
3215 d = new MessageDialog (msg, false, MESSAGE_INFO, BUTTONS_OK, true);
3218 d->show_all ();
3219 d->run ();
3220 delete d;
3224 ARDOUR_UI::pending_state_dialog ()
3226 HBox* hbox = new HBox();
3227 Image* image = new Image (Stock::DIALOG_QUESTION, ICON_SIZE_DIALOG);
3228 ArdourDialog dialog (_("Crash Recovery"), true);
3229 Label message (_("\
3230 This session appears to have been in\n\
3231 middle of recording when ardour or\n\
3232 the computer was shutdown.\n\
3234 Ardour can recover any captured audio for\n\
3235 you, or it can ignore it. Please decide\n\
3236 what you would like to do.\n"));
3237 image->set_alignment(ALIGN_CENTER, ALIGN_TOP);
3238 hbox->pack_start (*image, PACK_EXPAND_WIDGET, 12);
3239 hbox->pack_end (message, PACK_EXPAND_PADDING, 12);
3240 dialog.get_vbox()->pack_start(*hbox, PACK_EXPAND_PADDING, 6);
3241 dialog.add_button (_("Ignore crash data"), RESPONSE_REJECT);
3242 dialog.add_button (_("Recover from crash"), RESPONSE_ACCEPT);
3243 dialog.set_default_response (RESPONSE_ACCEPT);
3244 dialog.set_position (WIN_POS_CENTER);
3245 message.show();
3246 image->show();
3247 hbox->show();
3249 switch (dialog.run ()) {
3250 case RESPONSE_ACCEPT:
3251 return 1;
3252 default:
3253 return 0;
3258 ARDOUR_UI::sr_mismatch_dialog (nframes_t desired, nframes_t actual)
3260 HBox* hbox = new HBox();
3261 Image* image = new Image (Stock::DIALOG_QUESTION, ICON_SIZE_DIALOG);
3262 ArdourDialog dialog (_("Sample Rate Mismatch"), true);
3263 Label message (string_compose (_("\
3264 This session was created with a sample rate of %1 Hz\n\
3266 The audioengine is currently running at %2 Hz\n"), desired, actual));
3268 image->set_alignment(ALIGN_CENTER, ALIGN_TOP);
3269 hbox->pack_start (*image, PACK_EXPAND_WIDGET, 12);
3270 hbox->pack_end (message, PACK_EXPAND_PADDING, 12);
3271 dialog.get_vbox()->pack_start(*hbox, PACK_EXPAND_PADDING, 6);
3272 dialog.add_button (_("Do not load session"), RESPONSE_REJECT);
3273 dialog.add_button (_("Load session anyway"), RESPONSE_ACCEPT);
3274 dialog.set_default_response (RESPONSE_ACCEPT);
3275 dialog.set_position (WIN_POS_CENTER);
3276 message.show();
3277 image->show();
3278 hbox->show();
3280 switch (dialog.run ()) {
3281 case RESPONSE_ACCEPT:
3282 return 0;
3283 default:
3284 return 1;
3289 void
3290 ARDOUR_UI::disconnect_from_jack ()
3292 if (engine) {
3293 if( engine->disconnect_from_jack ()) {
3294 MessageDialog msg (*editor, _("Could not disconnect from JACK"));
3295 msg.run ();
3298 update_sample_rate (0);
3302 void
3303 ARDOUR_UI::reconnect_to_jack ()
3305 if (engine) {
3306 if (engine->reconnect_to_jack ()) {
3307 MessageDialog msg (*editor, _("Could not reconnect to JACK"));
3308 msg.run ();
3311 update_sample_rate (0);
3315 void
3316 ARDOUR_UI::use_config ()
3319 XMLNode* node = Config->extra_xml (X_("TransportControllables"));
3320 if (node) {
3321 set_transport_controllable_state (*node);
3324 node = Config->extra_xml (X_("UI"));
3326 if (node) {
3327 const XMLProperty* prop = node->property (X_("show-big-clock"));
3328 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Common"), X_("ToggleBigClock"));
3329 if (act) {
3330 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
3331 tact->set_active (string_is_affirmative (prop->value()));
3336 void
3337 ARDOUR_UI::update_transport_clocks (nframes_t pos)
3339 if (Config->get_primary_clock_delta_edit_cursor()) {
3340 primary_clock.set (pos, false, editor->get_preferred_edit_position(), 1);
3341 } else {
3342 primary_clock.set (pos, 0, true);
3345 if (Config->get_secondary_clock_delta_edit_cursor()) {
3346 secondary_clock.set (pos, false, editor->get_preferred_edit_position(), 2);
3347 } else {
3348 secondary_clock.set (pos);
3351 if (big_clock_window) {
3352 big_clock.set (pos);
3356 void
3357 ARDOUR_UI::record_state_changed ()
3359 ENSURE_GUI_THREAD (*this, &ARDOUR_UI::record_state_changed);
3361 if (!_session || !big_clock_window) {
3362 /* why bother - the clock isn't visible */
3363 return;
3366 Session::RecordState const r = _session->record_status ();
3367 bool const h = _session->have_rec_enabled_diskstream ();
3369 if (r == Session::Recording && h) {
3370 big_clock.set_widget_name ("BigClockRecording");
3371 } else {
3372 big_clock.set_widget_name ("BigClockNonRecording");
3376 bool
3377 ARDOUR_UI::first_idle ()
3379 if (_session) {
3380 _session->allow_auto_play (true);
3383 if (editor) {
3384 editor->first_idle();
3387 Keyboard::set_can_save_keybindings (true);
3388 return false;
3391 void
3392 ARDOUR_UI::store_clock_modes ()
3394 XMLNode* node = new XMLNode(X_("ClockModes"));
3396 for (vector<AudioClock*>::iterator x = AudioClock::clocks.begin(); x != AudioClock::clocks.end(); ++x) {
3397 node->add_property ((*x)->name().c_str(), enum_2_string ((*x)->mode()));
3400 _session->add_extra_xml (*node);
3401 _session->set_dirty ();
3406 ARDOUR_UI::TransportControllable::TransportControllable (std::string name, ARDOUR_UI& u, ToggleType tp)
3407 : Controllable (name), ui (u), type(tp)
3412 void
3413 ARDOUR_UI::TransportControllable::set_value (float val)
3415 if (type == ShuttleControl) {
3416 double fract;
3418 if (val == 0.5f) {
3419 fract = 0.0;
3420 } else {
3421 if (val < 0.5f) {
3422 fract = -((0.5f - val)/0.5f);
3423 } else {
3424 fract = ((val - 0.5f)/0.5f);
3428 ui.set_shuttle_fract (fract);
3429 return;
3432 if (val < 0.5f) {
3433 /* do nothing: these are radio-style actions */
3434 return;
3437 const char *action = 0;
3439 switch (type) {
3440 case Roll:
3441 action = X_("Roll");
3442 break;
3443 case Stop:
3444 action = X_("Stop");
3445 break;
3446 case GotoStart:
3447 action = X_("Goto Start");
3448 break;
3449 case GotoEnd:
3450 action = X_("Goto End");
3451 break;
3452 case AutoLoop:
3453 action = X_("Loop");
3454 break;
3455 case PlaySelection:
3456 action = X_("Play Selection");
3457 break;
3458 case RecordEnable:
3459 action = X_("Record");
3460 break;
3461 default:
3462 break;
3465 if (action == 0) {
3466 return;
3469 Glib::RefPtr<Action> act = ActionManager::get_action ("Transport", action);
3471 if (act) {
3472 act->activate ();
3476 float
3477 ARDOUR_UI::TransportControllable::get_value (void) const
3479 float val = 0.0f;
3481 switch (type) {
3482 case Roll:
3483 break;
3484 case Stop:
3485 break;
3486 case GotoStart:
3487 break;
3488 case GotoEnd:
3489 break;
3490 case AutoLoop:
3491 break;
3492 case PlaySelection:
3493 break;
3494 case RecordEnable:
3495 break;
3496 case ShuttleControl:
3497 break;
3498 default:
3499 break;
3502 return val;
3505 void
3506 ARDOUR_UI::TransportControllable::set_id (const string& str)
3508 _id = str;
3511 void
3512 ARDOUR_UI::setup_profile ()
3514 if (gdk_screen_width() < 1200) {
3515 Profile->set_small_screen ();
3519 if (getenv ("ARDOUR_SAE")) {
3520 Profile->set_sae ();
3521 Profile->set_single_package ();