fix mis-handling of button press events on rec-enable that ought to forward to Bindab...
[ardour2.git] / gtk2_ardour / ardour_ui.cc
blob2170532bf26358b36710467ad5898e27ef525cfe
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 #include <algorithm>
21 #include <cmath>
22 #include <fcntl.h>
23 #include <signal.h>
24 #include <unistd.h>
25 #include <time.h>
26 #include <cerrno>
27 #include <fstream>
28 #include <stdlib.h>
30 #include <iostream>
32 #include <sys/resource.h>
34 #include <gtkmm/messagedialog.h>
35 #include <gtkmm/accelmap.h>
37 #include <pbd/error.h>
38 #include <pbd/basename.h>
39 #include <pbd/compose.h>
40 #include <pbd/pathscanner.h>
41 #include <pbd/failed_constructor.h>
42 #include <pbd/enumwriter.h>
43 #include <pbd/stacktrace.h>
44 #include <gtkmm2ext/gtk_ui.h>
45 #include <gtkmm2ext/utils.h>
46 #include <gtkmm2ext/click_box.h>
47 #include <gtkmm2ext/fastmeter.h>
48 #include <gtkmm2ext/stop_signal.h>
49 #include <gtkmm2ext/popup.h>
50 #include <gtkmm2ext/window_title.h>
52 #include <midi++/port.h>
53 #include <midi++/mmc.h>
55 #include <ardour/ardour.h>
56 #include <ardour/profile.h>
57 #include <ardour/session_route.h>
58 #include <ardour/port.h>
59 #include <ardour/audioengine.h>
60 #include <ardour/playlist.h>
61 #include <ardour/utils.h>
62 #include <ardour/plugin.h>
63 #include <ardour/audio_diskstream.h>
64 #include <ardour/audiofilesource.h>
65 #include <ardour/recent_sessions.h>
66 #include <ardour/port.h>
67 #include <ardour/audio_track.h>
69 typedef uint64_t microseconds_t;
71 #include "actions.h"
72 #include "ardour_ui.h"
73 #include "public_editor.h"
74 #include "audio_clock.h"
75 #include "keyboard.h"
76 #include "mixer_ui.h"
77 #include "prompter.h"
78 #include "opts.h"
79 #include "add_route_dialog.h"
80 #include "new_session_dialog.h"
81 #include "about.h"
82 #include "splash.h"
83 #include "nag.h"
84 #include "utils.h"
85 #include "gui_thread.h"
86 #include "theme_manager.h"
87 #include "engine_dialog.h"
88 #include "gain_meter.h"
89 #include "route_time_axis.h"
91 #include "i18n.h"
93 using namespace ARDOUR;
94 using namespace PBD;
95 using namespace Gtkmm2ext;
96 using namespace Gtk;
97 using namespace sigc;
99 ARDOUR_UI *ARDOUR_UI::theArdourUI = 0;
100 UIConfiguration *ARDOUR_UI::ui_config = 0;
102 sigc::signal<void,bool> ARDOUR_UI::Blink;
103 sigc::signal<void> ARDOUR_UI::RapidScreenUpdate;
104 sigc::signal<void> ARDOUR_UI::SuperRapidScreenUpdate;
105 sigc::signal<void,nframes_t, bool, nframes_t> ARDOUR_UI::Clock;
107 ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[])
109 : Gtkmm2ext::UI (X_("Ardour"), argcp, argvp),
111 primary_clock (X_("primary"), false, X_("TransportClockDisplay"), true, false, true),
112 secondary_clock (X_("secondary"), false, X_("SecondaryClockDisplay"), true, false, true),
113 preroll_clock (X_("preroll"), false, X_("PreRollClock"), true, true),
114 postroll_clock (X_("postroll"), false, X_("PostRollClock"), true, true),
116 /* preroll stuff */
118 preroll_button (_("pre\nroll")),
119 postroll_button (_("post\nroll")),
121 /* big clock */
123 big_clock (X_("bigclock"), false, "BigClockNonRecording", true, false, true),
125 /* transport */
127 roll_controllable ("transport roll", *this, TransportControllable::Roll),
128 stop_controllable ("transport stop", *this, TransportControllable::Stop),
129 goto_start_controllable ("transport goto start", *this, TransportControllable::GotoStart),
130 goto_end_controllable ("transport goto end", *this, TransportControllable::GotoEnd),
131 auto_loop_controllable ("transport auto loop", *this, TransportControllable::AutoLoop),
132 play_selection_controllable ("transport play selection", *this, TransportControllable::PlaySelection),
133 rec_controllable ("transport rec-enable", *this, TransportControllable::RecordEnable),
134 shuttle_controllable ("shuttle", *this, TransportControllable::ShuttleControl),
135 shuttle_controller_binding_proxy (shuttle_controllable),
137 roll_button (&roll_controllable),
138 stop_button (&stop_controllable),
139 goto_start_button (&goto_start_controllable),
140 goto_end_button (&goto_end_controllable),
141 auto_loop_button (&auto_loop_controllable),
142 play_selection_button (&play_selection_controllable),
143 rec_button (&rec_controllable),
145 shuttle_units_button (_("% ")),
147 punch_in_button (_("Punch In")),
148 punch_out_button (_("Punch Out")),
149 auto_return_button (_("Auto Return")),
150 auto_play_button (_("Auto Play")),
151 auto_input_button (_("Auto Input")),
152 click_button (_("Click")),
153 time_master_button (_("time\nmaster")),
155 auditioning_alert_button (_("AUDITION")),
156 solo_alert_button (_("SOLO")),
157 shown_flag (false),
158 error_log_button (_("Errors"))
160 using namespace Gtk::Menu_Helpers;
162 Gtkmm2ext::init();
164 #ifdef TOP_MENUBAR
165 _auto_display_errors = false;
166 #endif
168 about = 0;
169 splash = 0;
171 if (ARDOUR_COMMAND_LINE::session_name.length()) {
172 /* only show this if we're not going to post the new session dialog */
173 show_splash ();
176 if (theArdourUI == 0) {
177 theArdourUI = this;
180 ui_config = new UIConfiguration();
181 theme_manager = new ThemeManager();
183 engine = 0;
184 editor = 0;
185 mixer = 0;
186 session = 0;
187 _session_is_new = false;
188 big_clock_window = 0;
189 session_selector_window = 0;
190 new_session_dialog = 0;
191 last_key_press_time = 0;
192 connection_editor = 0;
193 add_route_dialog = 0;
194 route_params = 0;
195 option_editor = 0;
196 location_ui = 0;
197 key_editor = 0;
198 open_session_selector = 0;
199 have_configure_timeout = false;
200 have_disk_speed_dialog_displayed = false;
201 _will_create_new_session_automatically = false;
202 session_loaded = false;
203 last_speed_displayed = -1.0f;
204 ignore_dual_punch = false;
205 _mixer_on_top = false;
207 roll_button.unset_flags (Gtk::CAN_FOCUS);
208 stop_button.unset_flags (Gtk::CAN_FOCUS);
209 goto_start_button.unset_flags (Gtk::CAN_FOCUS);
210 goto_end_button.unset_flags (Gtk::CAN_FOCUS);
211 auto_loop_button.unset_flags (Gtk::CAN_FOCUS);
212 play_selection_button.unset_flags (Gtk::CAN_FOCUS);
213 rec_button.unset_flags (Gtk::CAN_FOCUS);
215 last_configure_time= 0;
217 shuttle_grabbed = false;
218 shuttle_fract = 0.0;
219 shuttle_max_speed = 8.0f;
221 shuttle_style_menu = 0;
222 shuttle_unit_menu = 0;
224 // We do not have jack linked in yet so;
226 last_shuttle_request = last_peak_grab = 0; // get_microseconds();
228 ARDOUR::Diskstream::DiskOverrun.connect (mem_fun(*this, &ARDOUR_UI::disk_overrun_handler));
229 ARDOUR::Diskstream::DiskUnderrun.connect (mem_fun(*this, &ARDOUR_UI::disk_underrun_handler));
231 ARDOUR::Plugin::PresetFileExists.connect (mem_fun(*this, &ARDOUR_UI::preset_file_exists_handler));
233 /* handle dialog requests */
235 ARDOUR::Session::Dialog.connect (mem_fun(*this, &ARDOUR_UI::session_dialog));
237 /* handle pending state with a dialog */
239 ARDOUR::Session::AskAboutPendingState.connect (mem_fun(*this, &ARDOUR_UI::pending_state_dialog));
241 /* handle sr mismatch with a dialog */
243 ARDOUR::Session::AskAboutSampleRateMismatch.connect (mem_fun(*this, &ARDOUR_UI::sr_mismatch_dialog));
245 /* lets get this party started */
247 try {
248 if (ARDOUR::init (ARDOUR_COMMAND_LINE::use_vst, ARDOUR_COMMAND_LINE::try_hw_optimization)) {
249 throw failed_constructor ();
252 setup_gtk_ardour_enums ();
253 Config->set_current_owner (ConfigVariableBase::Interface);
254 setup_profile ();
256 GainMeter::setup_slider_pix ();
257 RouteTimeAxisView::setup_slider_pix ();
259 } catch (failed_constructor& err) {
260 error << _("could not initialize Ardour.") << endmsg;
261 // pass it on up
262 throw;
265 /* we like keyboards */
267 keyboard = new Keyboard;
269 reset_dpi();
271 starting.connect (mem_fun(*this, &ARDOUR_UI::startup));
272 stopping.connect (mem_fun(*this, &ARDOUR_UI::shutdown));
274 platform_setup ();
278 ARDOUR_UI::create_engine ()
280 // this gets called every time by new_session()
282 if (engine) {
283 return 0;
286 loading_message (_("Starting audio engine"));
288 try {
289 engine = new ARDOUR::AudioEngine (ARDOUR_COMMAND_LINE::jack_client_name);
291 } catch (...) {
293 return -1;
296 engine->Stopped.connect (mem_fun(*this, &ARDOUR_UI::engine_stopped));
297 engine->Running.connect (mem_fun(*this, &ARDOUR_UI::engine_running));
298 engine->Halted.connect (mem_fun(*this, &ARDOUR_UI::engine_halted));
299 engine->SampleRateChanged.connect (mem_fun(*this, &ARDOUR_UI::update_sample_rate));
301 post_engine ();
303 return 0;
306 void
307 ARDOUR_UI::post_engine ()
309 /* Things to be done once we create the AudioEngine
312 check_memory_locking();
314 ActionManager::init ();
315 _tooltips.enable();
317 if (setup_windows ()) {
318 throw failed_constructor ();
321 /* this is the first point at which all the keybindings are available */
323 if (ARDOUR_COMMAND_LINE::show_key_actions) {
324 vector<string> names;
325 vector<string> paths;
326 vector<string> keys;
327 vector<AccelKey> bindings;
329 ActionManager::get_all_actions (names, paths, keys, bindings);
331 vector<string>::iterator n;
332 vector<string>::iterator k;
333 for (n = names.begin(), k = keys.begin(); n != names.end(); ++n, ++k) {
334 cerr << "Action: " << (*n) << " bound to " << (*k) << endl;
337 exit (0);
340 blink_timeout_tag = -1;
342 /* the global configuration object is now valid */
344 use_config ();
346 /* this being a GUI and all, we want peakfiles */
348 AudioFileSource::set_build_peakfiles (true);
349 AudioFileSource::set_build_missing_peakfiles (true);
351 /* set default clock modes */
353 if (Profile->get_sae()) {
354 primary_clock.set_mode (AudioClock::BBT);
355 secondary_clock.set_mode (AudioClock::MinSec);
356 } else {
357 primary_clock.set_mode (AudioClock::SMPTE);
358 secondary_clock.set_mode (AudioClock::BBT);
361 /* start the time-of-day-clock */
363 #ifndef GTKOSX
364 /* OS X provides an always visible wallclock, so don't be stupid */
365 update_wall_clock ();
366 Glib::signal_timeout().connect (mem_fun(*this, &ARDOUR_UI::update_wall_clock), 60000);
367 #endif
369 update_disk_space ();
370 update_cpu_load ();
371 update_sample_rate (engine->frame_rate());
373 platform_specific ();
375 /* now start and maybe save state */
377 if (do_engine_start () == 0) {
378 if (session && _session_is_new) {
379 /* we need to retain initial visual
380 settings for a new session
382 session->save_state ("");
387 ARDOUR_UI::~ARDOUR_UI ()
389 save_ardour_state ();
391 if (keyboard) {
392 delete keyboard;
395 if (editor) {
396 delete editor;
399 if (mixer) {
400 delete mixer;
403 if (add_route_dialog) {
404 delete add_route_dialog;
407 if (new_session_dialog) {
408 delete new_session_dialog;
412 void
413 ARDOUR_UI::pop_back_splash ()
415 if (Splash::instance()) {
416 // Splash::instance()->pop_back();
417 Splash::instance()->hide ();
421 gint
422 ARDOUR_UI::configure_timeout ()
424 if (last_configure_time == 0) {
425 /* no configure events yet */
426 return TRUE;
429 /* force a gap of 0.5 seconds since the last configure event
432 if (get_microseconds() - last_configure_time < 500000) {
433 return TRUE;
434 } else {
435 have_configure_timeout = false;
436 save_ardour_state ();
437 return FALSE;
441 gboolean
442 ARDOUR_UI::configure_handler (GdkEventConfigure* conf)
444 if (have_configure_timeout) {
445 last_configure_time = get_microseconds();
446 } else {
447 Glib::signal_timeout().connect (mem_fun(*this, &ARDOUR_UI::configure_timeout), 100);
448 have_configure_timeout = true;
451 return FALSE;
454 void
455 ARDOUR_UI::set_transport_controllable_state (const XMLNode& node)
457 const XMLProperty* prop;
459 if ((prop = node.property ("roll")) != 0) {
460 roll_controllable.set_id (prop->value());
462 if ((prop = node.property ("stop")) != 0) {
463 stop_controllable.set_id (prop->value());
465 if ((prop = node.property ("goto_start")) != 0) {
466 goto_start_controllable.set_id (prop->value());
468 if ((prop = node.property ("goto_end")) != 0) {
469 goto_end_controllable.set_id (prop->value());
471 if ((prop = node.property ("auto_loop")) != 0) {
472 auto_loop_controllable.set_id (prop->value());
474 if ((prop = node.property ("play_selection")) != 0) {
475 play_selection_controllable.set_id (prop->value());
477 if ((prop = node.property ("rec")) != 0) {
478 rec_controllable.set_id (prop->value());
480 if ((prop = node.property ("shuttle")) != 0) {
481 shuttle_controllable.set_id (prop->value());
485 XMLNode&
486 ARDOUR_UI::get_transport_controllable_state ()
488 XMLNode* node = new XMLNode(X_("TransportControllables"));
489 char buf[64];
491 roll_controllable.id().print (buf, sizeof (buf));
492 node->add_property (X_("roll"), buf);
493 stop_controllable.id().print (buf, sizeof (buf));
494 node->add_property (X_("stop"), buf);
495 goto_start_controllable.id().print (buf, sizeof (buf));
496 node->add_property (X_("goto_start"), buf);
497 goto_end_controllable.id().print (buf, sizeof (buf));
498 node->add_property (X_("goto_end"), buf);
499 auto_loop_controllable.id().print (buf, sizeof (buf));
500 node->add_property (X_("auto_loop"), buf);
501 play_selection_controllable.id().print (buf, sizeof (buf));
502 node->add_property (X_("play_selection"), buf);
503 rec_controllable.id().print (buf, sizeof (buf));
504 node->add_property (X_("rec"), buf);
505 shuttle_controllable.id().print (buf, sizeof (buf));
506 node->add_property (X_("shuttle"), buf);
508 return *node;
511 void
512 ARDOUR_UI::save_ardour_state ()
514 if (!keyboard || !mixer || !editor) {
515 return;
518 /* XXX this is all a bit dubious. add_extra_xml() uses
519 a different lifetime model from add_instant_xml().
522 XMLNode* node = new XMLNode (keyboard->get_state());
523 Config->add_extra_xml (*node);
524 Config->add_extra_xml (get_transport_controllable_state());
525 if (new_session_dialog) {
526 if (new_session_dialog->engine_control.was_used()) {
527 Config->add_extra_xml (new_session_dialog->engine_control.get_state());
530 Config->save_state();
531 ui_config->save_state ();
533 XMLNode enode(static_cast<Stateful*>(editor)->get_state());
534 XMLNode mnode(mixer->get_state());
536 if (session) {
537 session->add_instant_xml (enode, session->path());
538 session->add_instant_xml (mnode, session->path());
539 } else {
540 Config->add_instant_xml (enode, get_user_ardour_path());
541 Config->add_instant_xml (mnode, get_user_ardour_path());
544 Keyboard::save_keybindings ();
547 gint
548 ARDOUR_UI::autosave_session ()
550 if (g_main_depth() > 1) {
551 /* inside a recursive main loop,
552 give up because we may not be able to
553 take a lock.
555 return 1;
558 if (!Config->get_periodic_safety_backups())
559 return 1;
561 if (session) {
562 session->maybe_write_autosave();
565 return 1;
568 void
569 ARDOUR_UI::update_autosave ()
571 ENSURE_GUI_THREAD (mem_fun (*this, &ARDOUR_UI::update_autosave));
573 if (session->dirty()) {
574 if (_autosave_connection.connected()) {
575 _autosave_connection.disconnect();
578 _autosave_connection = Glib::signal_timeout().connect (mem_fun (*this, &ARDOUR_UI::autosave_session),
579 Config->get_periodic_safety_backup_interval() * 1000);
581 } else {
582 if (_autosave_connection.connected()) {
583 _autosave_connection.disconnect();
588 void
589 ARDOUR_UI::backend_audio_error (bool we_set_params, Gtk::Window* toplevel)
591 string title;
592 if (we_set_params) {
593 title = _("Ardour could not start JACK");
594 } else {
595 title = _("Ardour could not connect to JACK.");
598 MessageDialog win (title,
599 false,
600 Gtk::MESSAGE_INFO,
601 Gtk::BUTTONS_NONE);
603 if (we_set_params) {
604 win.set_secondary_text(_("There are several possible reasons:\n\
606 1) You requested audio parameters that are not supported..\n\
607 2) JACK is running as another user.\n\
609 Please consider the possibilities, and perhaps try different parameters."));
610 } else {
611 win.set_secondary_text(_("There are several possible reasons:\n\
613 1) JACK is not running.\n\
614 2) JACK is running as another user, perhaps root.\n\
615 3) There is already another client called \"ardour\".\n\
617 Please consider the possibilities, and perhaps (re)start JACK."));
620 if (toplevel) {
621 win.set_transient_for (*toplevel);
624 if (we_set_params) {
625 win.add_button (Stock::OK, RESPONSE_CLOSE);
626 } else {
627 win.add_button (Stock::QUIT, RESPONSE_CLOSE);
630 win.set_default_response (RESPONSE_CLOSE);
632 win.show_all ();
633 win.set_position (Gtk::WIN_POS_CENTER);
634 pop_back_splash ();
636 /* we just don't care about the result, but we want to block */
638 win.run ();
641 void
642 ARDOUR_UI::startup ()
644 string name, path;
646 new_session_dialog = new NewSessionDialog();
648 bool backend_audio_is_running = EngineControl::engine_running();
649 XMLNode* audio_setup = Config->extra_xml ("AudioSetup");
651 if (audio_setup) {
652 new_session_dialog->engine_control.set_state (*audio_setup);
655 if (!get_session_parameters (backend_audio_is_running, ARDOUR_COMMAND_LINE::new_session)) {
656 return;
659 BootMessage (_("Ardour is ready for use"));
660 show ();
663 void
664 ARDOUR_UI::no_memory_warning ()
666 XMLNode node (X_("no-memory-warning"));
667 Config->add_instant_xml (node, get_user_ardour_path());
670 void
671 ARDOUR_UI::check_memory_locking ()
673 #ifdef __APPLE__
674 /* OS X doesn't support mlockall(2), and so testing for memory locking capability there is pointless */
675 return;
676 #else // !__APPLE__
678 XMLNode* memory_warning_node = Config->instant_xml (X_("no-memory-warning"), get_user_ardour_path());
680 if (engine->is_realtime() && memory_warning_node == 0) {
682 struct rlimit limits;
683 int64_t ram;
684 long pages, page_size;
686 if ((page_size = sysconf (_SC_PAGESIZE)) < 0 ||(pages = sysconf (_SC_PHYS_PAGES)) < 0) {
687 ram = 0;
688 } else {
689 ram = (int64_t) pages * (int64_t) page_size;
692 if (getrlimit (RLIMIT_MEMLOCK, &limits)) {
693 return;
696 if (limits.rlim_cur != RLIM_INFINITY) {
698 if (ram == 0 || ((double) limits.rlim_cur / ram) < 0.75) {
701 MessageDialog msg (_("WARNING: Your system has a limit for maximum amount of locked memory. "
702 "This might cause Ardour to run out of memory before your system "
703 "runs out of memory. \n\n"
704 "You can view the memory limit with 'ulimit -l', "
705 "and it is normally controlled by /etc/security/limits.conf"));
707 VBox* vbox = msg.get_vbox();
708 HBox hbox;
709 CheckButton cb (_("Do not show this window again"));
711 cb.signal_toggled().connect (mem_fun (*this, &ARDOUR_UI::no_memory_warning));
713 hbox.pack_start (cb, true, false);
714 vbox->pack_start (hbox);
715 hbox.show_all ();
717 pop_back_splash ();
719 msg.run ();
723 #endif // !__APPLE__
727 void
728 ARDOUR_UI::finish()
730 if (session) {
732 if (session->transport_rolling()) {
733 session->request_stop ();
734 usleep (2500000);
737 if (session->dirty()) {
738 switch (ask_about_saving_session(_("quit"))) {
739 case -1:
740 return;
741 break;
742 case 1:
743 /* use the default name */
744 if (save_state_canfail ("")) {
745 /* failed - don't quit */
746 MessageDialog msg (*editor,
747 _("\
748 Ardour was unable to save your session.\n\n\
749 If you still wish to quit, please use the\n\n\
750 \"Just quit\" option."));
751 pop_back_splash();
752 msg.run ();
753 return;
755 break;
756 case 0:
757 break;
761 session->set_deletion_in_progress ();
764 ArdourDialog::close_all_dialogs ();
765 engine->stop (true);
766 save_ardour_state ();
767 quit ();
771 ARDOUR_UI::ask_about_saving_session (const string & what)
773 ArdourDialog window (_("ardour: save session?"));
774 Gtk::HBox dhbox; // the hbox for the image and text
775 Gtk::Label prompt_label;
776 Gtk::Image* dimage = manage (new Gtk::Image(Stock::DIALOG_WARNING, Gtk::ICON_SIZE_DIALOG));
778 string msg;
780 msg = string_compose(_("Don't %1"), what);
781 window.add_button (msg, RESPONSE_REJECT);
782 msg = string_compose(_("Just %1"), what);
783 window.add_button (msg, RESPONSE_APPLY);
784 msg = string_compose(_("Save and %1"), what);
785 window.add_button (msg, RESPONSE_ACCEPT);
787 window.set_default_response (RESPONSE_ACCEPT);
789 Gtk::Button noquit_button (msg);
790 noquit_button.set_name ("EditorGTKButton");
792 string prompt;
793 string type;
795 if (session->snap_name() == session->name()) {
796 type = _("session");
797 } else {
798 type = _("snapshot");
800 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?"),
801 type, session->snap_name());
803 prompt_label.set_text (prompt);
804 prompt_label.set_name (X_("PrompterLabel"));
805 prompt_label.set_alignment(ALIGN_LEFT, ALIGN_TOP);
807 dimage->set_alignment(ALIGN_CENTER, ALIGN_TOP)
809 dhbox.set_homogeneous (false);
810 dhbox.pack_start (*dimage, false, false, 5);
811 dhbox.pack_start (prompt_label, true, false, 5);
812 window.get_vbox()->pack_start (dhbox);
814 window.set_name (_("Prompter"));
815 window.set_position (Gtk::WIN_POS_MOUSE);
816 window.set_modal (true);
817 window.set_resizable (false);
818 window.show_all ();
820 window.set_keep_above (true);
821 window.present ();
823 ResponseType r = (ResponseType) window.run();
825 window.hide ();
827 switch (r) {
828 case RESPONSE_ACCEPT: // save and get out of here
829 return 1;
830 case RESPONSE_APPLY: // get out of here
831 return 0;
832 default:
833 break;
836 return -1;
840 ARDOUR_UI::every_second ()
842 update_cpu_load ();
843 update_buffer_load ();
844 update_disk_space ();
845 return TRUE;
848 gint
849 ARDOUR_UI::every_point_one_seconds ()
851 update_speed_display ();
852 RapidScreenUpdate(); /* EMIT_SIGNAL */
853 return TRUE;
856 gint
857 ARDOUR_UI::every_point_zero_one_seconds ()
859 // august 2007: actual update frequency: 40Hz, not 100Hz
861 SuperRapidScreenUpdate(); /* EMIT_SIGNAL */
862 return TRUE;
865 void
866 ARDOUR_UI::update_sample_rate (nframes_t ignored)
868 char buf[32];
870 ENSURE_GUI_THREAD (bind (mem_fun(*this, &ARDOUR_UI::update_sample_rate), ignored));
872 if (!engine->connected()) {
874 snprintf (buf, sizeof (buf), _("disconnected"));
876 } else {
878 nframes_t rate = engine->frame_rate();
880 if (fmod (rate, 1000.0) != 0.0) {
881 snprintf (buf, sizeof (buf), _("%.1f kHz / %4.1f ms"),
882 (float) rate/1000.0f,
883 (engine->frames_per_cycle() / (float) rate) * 1000.0f);
884 } else {
885 snprintf (buf, sizeof (buf), _("%u kHz / %4.1f ms"),
886 rate/1000,
887 (engine->frames_per_cycle() / (float) rate) * 1000.0f);
891 sample_rate_label.set_text (buf);
894 void
895 ARDOUR_UI::update_cpu_load ()
897 char buf[32];
898 snprintf (buf, sizeof (buf), _("DSP: %5.1f%%"), engine->get_cpu_load());
899 cpu_load_label.set_text (buf);
902 void
903 ARDOUR_UI::update_buffer_load ()
905 char buf[64];
906 uint32_t c, p;
908 if (session) {
909 c = session->capture_load ();
910 p = session->playback_load ();
912 push_buffer_stats (c, p);
914 snprintf (buf, sizeof (buf), _("Buffers p:%" PRIu32 "%% c:%" PRIu32 "%%"),
915 session->playback_load(), session->capture_load());
916 buffer_load_label.set_text (buf);
917 } else {
918 buffer_load_label.set_text ("");
922 void
923 ARDOUR_UI::count_recenabled_streams (Route& route)
925 Track* track = dynamic_cast<Track*>(&route);
926 if (track && track->diskstream()->record_enabled()) {
927 rec_enabled_streams += track->n_inputs();
931 void
932 ARDOUR_UI::update_disk_space()
934 if (session == 0) {
935 return;
938 nframes_t frames = session->available_capture_duration();
939 char buf[64];
941 if (frames == max_frames) {
942 strcpy (buf, _("Disk: 24hrs+"));
943 } else {
944 int hrs;
945 int mins;
946 int secs;
947 nframes_t fr = session->frame_rate();
949 rec_enabled_streams = 0;
950 session->foreach_route (this, &ARDOUR_UI::count_recenabled_streams);
952 if (rec_enabled_streams) {
953 frames /= rec_enabled_streams;
956 hrs = frames / (fr * 3600);
957 frames -= hrs * fr * 3600;
958 mins = frames / (fr * 60);
959 frames -= mins * fr * 60;
960 secs = frames / fr;
962 snprintf (buf, sizeof(buf), _("Disk: %02dh:%02dm:%02ds"), hrs, mins, secs);
965 disk_space_label.set_text (buf);
968 gint
969 ARDOUR_UI::update_wall_clock ()
971 time_t now;
972 struct tm *tm_now;
973 char buf[16];
975 time (&now);
976 tm_now = localtime (&now);
978 sprintf (buf, "%02d:%02d", tm_now->tm_hour, tm_now->tm_min);
979 wall_clock_label.set_text (buf);
981 return TRUE;
984 gint
985 ARDOUR_UI::session_menu (GdkEventButton *ev)
987 session_popup_menu->popup (0, 0);
988 return TRUE;
991 void
992 ARDOUR_UI::redisplay_recent_sessions ()
994 vector<string *> *sessions;
995 vector<string *>::iterator i;
996 RecentSessionsSorter cmp;
998 recent_session_display.set_model (Glib::RefPtr<TreeModel>(0));
999 recent_session_model->clear ();
1001 RecentSessions rs;
1002 ARDOUR::read_recent_sessions (rs);
1004 if (rs.empty()) {
1005 recent_session_display.set_model (recent_session_model);
1006 return;
1009 /* sort them alphabetically */
1010 sort (rs.begin(), rs.end(), cmp);
1011 sessions = new vector<string*>;
1013 for (RecentSessions::iterator i = rs.begin(); i != rs.end(); ++i) {
1014 sessions->push_back (new string ((*i).second));
1017 for (i = sessions->begin(); i != sessions->end(); ++i) {
1019 vector<string*>* states;
1020 vector<const gchar*> item;
1021 string fullpath = *(*i);
1023 /* remove any trailing / */
1025 if (fullpath[fullpath.length()-1] == '/') {
1026 fullpath = fullpath.substr (0, fullpath.length()-1);
1029 /* check whether session still exists */
1030 if (!Glib::file_test(fullpath.c_str(), Glib::FILE_TEST_EXISTS)) {
1031 /* session doesn't exist */
1032 cerr << "skipping non-existent session " << fullpath << endl;
1033 continue;
1036 /* now get available states for this session */
1038 if ((states = Session::possible_states (fullpath)) == 0) {
1039 /* no state file? */
1040 continue;
1043 TreeModel::Row row = *(recent_session_model->append());
1045 row[recent_session_columns.visible_name] = Glib::path_get_basename (fullpath);
1046 row[recent_session_columns.fullpath] = fullpath;
1048 if (states->size() > 1) {
1050 /* add the children */
1052 for (vector<string*>::iterator i2 = states->begin(); i2 != states->end(); ++i2) {
1054 TreeModel::Row child_row = *(recent_session_model->append (row.children()));
1056 child_row[recent_session_columns.visible_name] = **i2;
1057 child_row[recent_session_columns.fullpath] = fullpath;
1059 delete *i2;
1063 delete states;
1066 recent_session_display.set_model (recent_session_model);
1067 delete sessions;
1070 void
1071 ARDOUR_UI::build_session_selector ()
1073 session_selector_window = new ArdourDialog ("session selector");
1075 Gtk::ScrolledWindow *scroller = manage (new Gtk::ScrolledWindow);
1077 session_selector_window->add_button (Stock::CANCEL, RESPONSE_CANCEL);
1078 session_selector_window->add_button (Stock::OPEN, RESPONSE_ACCEPT);
1079 session_selector_window->set_default_response (RESPONSE_ACCEPT);
1080 recent_session_model = TreeStore::create (recent_session_columns);
1081 recent_session_display.set_model (recent_session_model);
1082 recent_session_display.append_column (_("Recent Sessions"), recent_session_columns.visible_name);
1083 recent_session_display.set_headers_visible (false);
1084 recent_session_display.get_selection()->set_mode (SELECTION_BROWSE);
1085 recent_session_display.signal_row_activated().connect (mem_fun (*this, &ARDOUR_UI::recent_session_row_activated));
1087 scroller->add (recent_session_display);
1088 scroller->set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
1090 session_selector_window->set_name ("SessionSelectorWindow");
1091 session_selector_window->set_size_request (200, 400);
1092 session_selector_window->get_vbox()->pack_start (*scroller);
1093 session_selector_window->show_all_children();
1096 void
1097 ARDOUR_UI::recent_session_row_activated (const TreePath& path, TreeViewColumn* col)
1099 session_selector_window->response (RESPONSE_ACCEPT);
1102 void
1103 ARDOUR_UI::open_recent_session ()
1105 bool can_return = (session != 0);
1107 if (session_selector_window == 0) {
1108 build_session_selector ();
1111 redisplay_recent_sessions ();
1113 while (true) {
1115 session_selector_window->set_position (WIN_POS_MOUSE);
1117 ResponseType r = (ResponseType) session_selector_window->run ();
1119 switch (r) {
1120 case RESPONSE_ACCEPT:
1121 break;
1122 default:
1123 if (can_return) {
1124 session_selector_window->hide();
1125 return;
1126 } else {
1127 exit (1);
1131 if (recent_session_display.get_selection()->count_selected_rows() == 0) {
1132 continue;
1135 session_selector_window->hide();
1137 Gtk::TreeModel::iterator i = recent_session_display.get_selection()->get_selected();
1139 if (i == recent_session_model->children().end()) {
1140 return;
1143 Glib::ustring path = (*i)[recent_session_columns.fullpath];
1144 Glib::ustring state = (*i)[recent_session_columns.visible_name];
1146 _session_is_new = false;
1148 if (load_session (path, state) == 0) {
1149 break;
1152 can_return = false;
1156 bool
1157 ARDOUR_UI::check_audioengine ()
1159 if (engine) {
1160 if (!engine->connected()) {
1161 MessageDialog msg (_("Ardour is not connected to JACK\n"
1162 "You cannot open or close sessions in this condition"));
1163 pop_back_splash ();
1164 msg.set_position (WIN_POS_CENTER);
1165 msg.run ();
1166 return false;
1168 return true;
1169 } else {
1170 return false;
1174 void
1175 ARDOUR_UI::open_session ()
1177 if (!check_audioengine()) {
1178 return;
1181 /* popup selector window */
1183 if (open_session_selector == 0) {
1185 /* ardour sessions are folders */
1187 open_session_selector = new Gtk::FileChooserDialog (_("open session"), FILE_CHOOSER_ACTION_OPEN);
1188 open_session_selector->add_button (Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
1189 open_session_selector->add_button (Gtk::Stock::OPEN, Gtk::RESPONSE_ACCEPT);
1190 open_session_selector->set_default_response(Gtk::RESPONSE_ACCEPT);
1192 FileFilter session_filter;
1193 session_filter.add_pattern ("*.ardour");
1194 session_filter.set_name (_("Ardour sessions"));
1195 open_session_selector->add_filter (session_filter);
1196 open_session_selector->set_filter (session_filter);
1199 int response = open_session_selector->run();
1200 open_session_selector->hide ();
1202 switch (response) {
1203 case RESPONSE_ACCEPT:
1204 break;
1205 default:
1206 open_session_selector->hide();
1207 return;
1210 open_session_selector->hide();
1211 string session_path = open_session_selector->get_filename();
1212 string path, name;
1213 bool isnew;
1215 if (session_path.length() > 0) {
1216 if (Session::find_session (session_path, path, name, isnew) == 0) {
1217 _session_is_new = isnew;
1218 load_session (path, name);
1224 void
1225 ARDOUR_UI::session_add_midi_track ()
1227 cerr << _("Patience is a virtue.\n");
1230 void
1231 ARDOUR_UI::session_add_audio_route (bool track, int32_t input_channels, int32_t output_channels, ARDOUR::TrackMode mode, uint32_t how_many)
1233 list<boost::shared_ptr<AudioTrack> > tracks;
1234 Session::RouteList routes;
1236 if (session == 0) {
1237 warning << _("You cannot add a track or bus without a session already loaded.") << endmsg;
1238 return;
1241 try {
1242 if (track) {
1243 tracks = session->new_audio_track (input_channels, output_channels, mode, how_many);
1245 if (tracks.size() != how_many) {
1246 if (how_many == 1) {
1247 error << _("could not create a new audio track") << endmsg;
1248 } else {
1249 error << string_compose (_("could only create %1 of %2 new audio %3"),
1250 tracks.size(), how_many, (track ? _("tracks") : _("busses"))) << endmsg;
1254 } else {
1256 routes = session->new_audio_route (input_channels, output_channels, how_many);
1258 if (routes.size() != how_many) {
1259 if (how_many == 1) {
1260 error << _("could not create a new audio track") << endmsg;
1261 } else {
1262 error << string_compose (_("could not create %1 new audio tracks"), how_many) << endmsg;
1267 #if CONTROLOUTS
1268 if (need_control_room_outs) {
1269 pan_t pans[2];
1271 pans[0] = 0.5;
1272 pans[1] = 0.5;
1274 route->set_stereo_control_outs (control_lr_channels);
1275 route->control_outs()->set_stereo_pan (pans, this);
1277 #endif /* CONTROLOUTS */
1280 catch (...) {
1281 MessageDialog msg (*editor,
1282 _("There are insufficient JACK ports available\n\
1283 to create a new track or bus.\n\
1284 You should save Ardour, exit and\n\
1285 restart JACK with more ports."));
1286 pop_back_splash ();
1287 msg.run ();
1291 void
1292 ARDOUR_UI::do_transport_locate (nframes_t new_position)
1294 nframes_t _preroll = 0;
1296 if (session) {
1297 // XXX CONFIG_CHANGE FIX - requires AnyTime handling
1298 // _preroll = session->convert_to_frames_at (new_position, Config->get_preroll());
1300 if (new_position > _preroll) {
1301 new_position -= _preroll;
1302 } else {
1303 new_position = 0;
1306 session->request_locate (new_position);
1310 void
1311 ARDOUR_UI::transport_goto_start ()
1313 if (session) {
1314 session->goto_start();
1317 /* force displayed area in editor to start no matter
1318 what "follow playhead" setting is.
1321 if (editor) {
1322 editor->reset_x_origin (session->current_start_frame());
1327 void
1328 ARDOUR_UI::transport_goto_zero ()
1330 if (session) {
1331 session->request_locate (0);
1334 /* force displayed area in editor to start no matter
1335 what "follow playhead" setting is.
1338 if (editor) {
1339 editor->reset_x_origin (0);
1344 void
1345 ARDOUR_UI::transport_goto_wallclock ()
1347 if (session && editor) {
1349 time_t now;
1350 struct tm tmnow;
1351 nframes64_t frames;
1353 time (&now);
1354 localtime_r (&now, &tmnow);
1356 frames = tmnow.tm_hour * (60 * 60 * session->frame_rate());
1357 frames += tmnow.tm_min * (60 * session->frame_rate());
1358 frames += tmnow.tm_sec * session->frame_rate();
1360 session->request_locate (frames);
1362 /* force displayed area in editor to start no matter
1363 what "follow playhead" setting is.
1366 if (editor) {
1367 editor->reset_x_origin (frames - (editor->current_page_frames()/2));
1372 void
1373 ARDOUR_UI::transport_goto_end ()
1375 if (session) {
1376 nframes_t frame = session->current_end_frame();
1377 session->request_locate (frame);
1379 /* force displayed area in editor to start no matter
1380 what "follow playhead" setting is.
1383 if (editor) {
1384 editor->reset_x_origin (frame);
1389 void
1390 ARDOUR_UI::transport_stop ()
1392 if (!session) {
1393 return;
1396 if (session->is_auditioning()) {
1397 session->cancel_audition ();
1398 return;
1401 if (session->get_play_loop ()) {
1402 session->request_play_loop (false);
1405 session->request_stop ();
1408 void
1409 ARDOUR_UI::transport_stop_and_forget_capture ()
1411 if (session) {
1412 session->request_stop (true);
1416 void
1417 ARDOUR_UI::remove_last_capture()
1419 if (editor) {
1420 editor->remove_last_capture();
1424 void
1425 ARDOUR_UI::transport_record (bool roll)
1428 if (session) {
1429 switch (session->record_status()) {
1430 case Session::Disabled:
1431 if (session->ntracks() == 0) {
1432 MessageDialog msg (*editor, _("Please create 1 or more track\nbefore trying to record.\nCheck the Session menu."));
1433 msg.run ();
1434 return;
1436 session->maybe_enable_record ();
1437 if (roll) {
1438 transport_roll ();
1440 break;
1441 case Session::Recording:
1442 if (roll) {
1443 session->request_stop();
1444 } else {
1445 session->disable_record (false, true);
1447 break;
1449 case Session::Enabled:
1450 session->disable_record (false, true);
1453 //cerr << "ARDOUR_UI::transport_record () called roll = " << roll << " session->record_status() = " << session->record_status() << endl;
1456 void
1457 ARDOUR_UI::transport_roll ()
1459 bool rolling;
1461 if (!session) {
1462 return;
1465 rolling = session->transport_rolling ();
1467 //cerr << "ARDOUR_UI::transport_roll () called session->record_status() = " << session->record_status() << endl;
1469 if (session->get_play_loop()) {
1470 session->request_play_loop (false);
1471 auto_loop_button.set_visual_state (1);
1472 roll_button.set_visual_state (1);
1473 } else if (session->get_play_range ()) {
1474 session->request_play_range (false);
1475 play_selection_button.set_visual_state (0);
1476 } else if (rolling) {
1477 session->request_locate (session->last_transport_start(), true);
1480 session->request_transport_speed (1.0f);
1483 void
1484 ARDOUR_UI::transport_loop()
1486 if (session) {
1487 if (session->get_play_loop()) {
1488 if (session->transport_rolling()) {
1489 Location * looploc = session->locations()->auto_loop_location();
1490 if (looploc) {
1491 session->request_locate (looploc->start(), true);
1495 else {
1496 session->request_play_loop (true);
1501 void
1502 ARDOUR_UI::transport_play_selection ()
1504 if (!session) {
1505 return;
1508 if (!session->get_play_range()) {
1509 session->request_stop ();
1512 editor->play_selection ();
1515 void
1516 ARDOUR_UI::transport_rewind (int option)
1518 float current_transport_speed;
1520 if (session) {
1521 current_transport_speed = session->transport_speed();
1523 if (current_transport_speed >= 0.0f) {
1524 switch (option) {
1525 case 0:
1526 session->request_transport_speed (-1.0f);
1527 break;
1528 case 1:
1529 session->request_transport_speed (-4.0f);
1530 break;
1531 case -1:
1532 session->request_transport_speed (-0.5f);
1533 break;
1535 } else {
1536 /* speed up */
1537 session->request_transport_speed (current_transport_speed * 1.5f);
1542 void
1543 ARDOUR_UI::transport_forward (int option)
1545 float current_transport_speed;
1547 if (session) {
1548 current_transport_speed = session->transport_speed();
1550 if (current_transport_speed <= 0.0f) {
1551 switch (option) {
1552 case 0:
1553 session->request_transport_speed (1.0f);
1554 break;
1555 case 1:
1556 session->request_transport_speed (4.0f);
1557 break;
1558 case -1:
1559 session->request_transport_speed (0.5f);
1560 break;
1562 } else {
1563 /* speed up */
1564 session->request_transport_speed (current_transport_speed * 1.5f);
1569 void
1570 ARDOUR_UI::toggle_record_enable (uint32_t dstream)
1572 if (session == 0) {
1573 return;
1576 boost::shared_ptr<Route> r;
1578 if ((r = session->route_by_remote_id (dstream)) != 0) {
1580 Track* t;
1582 if ((t = dynamic_cast<Track*>(r.get())) != 0) {
1583 t->diskstream()->set_record_enabled (!t->diskstream()->record_enabled());
1586 if (session == 0) {
1587 return;
1591 void
1592 ARDOUR_UI::queue_transport_change ()
1594 Gtkmm2ext::UI::instance()->call_slot (mem_fun(*this, &ARDOUR_UI::map_transport_state));
1597 void
1598 ARDOUR_UI::map_transport_state ()
1600 float sp = session->transport_speed();
1602 if (sp == 1.0f) {
1603 transport_rolling ();
1604 } else if (sp < 0.0f) {
1605 transport_rewinding ();
1606 } else if (sp > 0.0f) {
1607 transport_forwarding ();
1608 } else {
1609 transport_stopped ();
1613 void
1614 ARDOUR_UI::GlobalClickBox::printer (char buf[32], Adjustment &adj, void *arg)
1616 snprintf (buf, sizeof(buf), "%s", ((GlobalClickBox *) arg)->strings[
1617 (int) adj.get_value()].c_str());
1620 void
1621 ARDOUR_UI::engine_stopped ()
1623 ENSURE_GUI_THREAD (mem_fun(*this, &ARDOUR_UI::engine_stopped));
1624 ActionManager::set_sensitive (ActionManager::jack_sensitive_actions, false);
1625 ActionManager::set_sensitive (ActionManager::jack_opposite_sensitive_actions, true);
1628 void
1629 ARDOUR_UI::engine_running ()
1631 ENSURE_GUI_THREAD (mem_fun(*this, &ARDOUR_UI::engine_running));
1632 ActionManager::set_sensitive (ActionManager::jack_sensitive_actions, true);
1633 ActionManager::set_sensitive (ActionManager::jack_opposite_sensitive_actions, false);
1635 Glib::RefPtr<Action> action;
1636 const char* action_name = 0;
1638 switch (engine->frames_per_cycle()) {
1639 case 32:
1640 action_name = X_("JACKLatency32");
1641 break;
1642 case 64:
1643 action_name = X_("JACKLatency64");
1644 break;
1645 case 128:
1646 action_name = X_("JACKLatency128");
1647 break;
1648 case 512:
1649 action_name = X_("JACKLatency512");
1650 break;
1651 case 1024:
1652 action_name = X_("JACKLatency1024");
1653 break;
1654 case 2048:
1655 action_name = X_("JACKLatency2048");
1656 break;
1657 case 4096:
1658 action_name = X_("JACKLatency4096");
1659 break;
1660 case 8192:
1661 action_name = X_("JACKLatency8192");
1662 break;
1663 default:
1664 /* XXX can we do anything useful ? */
1665 break;
1668 if (action_name) {
1670 action = ActionManager::get_action (X_("JACK"), action_name);
1672 if (action) {
1673 Glib::RefPtr<RadioAction> ract = Glib::RefPtr<RadioAction>::cast_dynamic (action);
1674 ract->set_active ();
1679 void
1680 ARDOUR_UI::engine_halted ()
1682 ENSURE_GUI_THREAD (mem_fun(*this, &ARDOUR_UI::engine_halted));
1684 ActionManager::set_sensitive (ActionManager::jack_sensitive_actions, false);
1685 ActionManager::set_sensitive (ActionManager::jack_opposite_sensitive_actions, true);
1687 update_sample_rate (0);
1689 MessageDialog msg (*editor,
1690 _("\
1691 JACK has either been shutdown or it\n\
1692 disconnected Ardour because Ardour\n\
1693 was not fast enough. Try to restart\n\
1694 JACK, reconnect and save the session."));
1695 pop_back_splash ();
1696 msg.run ();
1699 int32_t
1700 ARDOUR_UI::do_engine_start ()
1702 try {
1703 engine->start();
1706 catch (...) {
1707 engine->stop ();
1708 error << _("Unable to start the session running")
1709 << endmsg;
1710 unload_session ();
1711 return -2;
1714 return 0;
1717 void
1718 ARDOUR_UI::setup_theme ()
1720 theme_manager->setup_theme();
1723 void
1724 ARDOUR_UI::update_clocks ()
1726 if (!editor || !editor->dragging_playhead()) {
1727 Clock (session->audible_frame(), false, editor->get_preferred_edit_position()); /* EMIT_SIGNAL */
1731 void
1732 ARDOUR_UI::start_clocking ()
1734 clock_signal_connection = RapidScreenUpdate.connect (mem_fun(*this, &ARDOUR_UI::update_clocks));
1737 void
1738 ARDOUR_UI::stop_clocking ()
1740 clock_signal_connection.disconnect ();
1743 void
1744 ARDOUR_UI::toggle_clocking ()
1746 #if 0
1747 if (clock_button.get_active()) {
1748 start_clocking ();
1749 } else {
1750 stop_clocking ();
1752 #endif
1755 gint
1756 ARDOUR_UI::_blink (void *arg)
1759 ((ARDOUR_UI *) arg)->blink ();
1760 return TRUE;
1763 void
1764 ARDOUR_UI::blink ()
1766 Blink (blink_on = !blink_on); /* EMIT_SIGNAL */
1769 void
1770 ARDOUR_UI::start_blinking ()
1772 /* Start the blink signal. Everybody with a blinking widget
1773 uses Blink to drive the widget's state.
1776 if (blink_timeout_tag < 0) {
1777 blink_on = false;
1778 blink_timeout_tag = g_timeout_add (240, _blink, this);
1782 void
1783 ARDOUR_UI::stop_blinking ()
1785 if (blink_timeout_tag >= 0) {
1786 g_source_remove (blink_timeout_tag);
1787 blink_timeout_tag = -1;
1791 void
1792 ARDOUR_UI::name_io_setup (AudioEngine& engine,
1793 string& buf,
1794 IO& io,
1795 bool in)
1797 if (in) {
1798 if (io.n_inputs() == 0) {
1799 buf = _("none");
1800 return;
1803 /* XXX we're not handling multiple ports yet. */
1805 const char **connections = io.input(0)->get_connections();
1807 if (connections == 0 || connections[0] == '\0') {
1808 buf = _("off");
1809 } else {
1810 buf = connections[0];
1813 free (connections);
1815 } else {
1817 if (io.n_outputs() == 0) {
1818 buf = _("none");
1819 return;
1822 /* XXX we're not handling multiple ports yet. */
1824 const char **connections = io.output(0)->get_connections();
1826 if (connections == 0 || connections[0] == '\0') {
1827 buf = _("off");
1828 } else {
1829 buf = connections[0];
1832 free (connections);
1836 /** Ask the user for the name of a new shapshot and then take it.
1838 void
1839 ARDOUR_UI::snapshot_session ()
1841 ArdourPrompter prompter (true);
1842 string snapname;
1843 char timebuf[128];
1844 time_t n;
1845 struct tm local_time;
1847 time (&n);
1848 localtime_r (&n, &local_time);
1849 strftime (timebuf, sizeof(timebuf), "%FT%T", &local_time);
1851 prompter.set_name ("Prompter");
1852 prompter.add_button (Gtk::Stock::SAVE, Gtk::RESPONSE_ACCEPT);
1853 prompter.set_prompt (_("Name of New Snapshot"));
1854 prompter.set_initial_text (timebuf);
1856 again:
1857 switch (prompter.run()) {
1858 case RESPONSE_ACCEPT:
1859 prompter.get_result (snapname);
1860 if (snapname.length()){
1861 if (snapname.find ('/') != string::npos) {
1862 MessageDialog msg (_("To ensure compatibility with various systems\n"
1863 "snapshot names may not contain a '/' character"));
1864 msg.run ();
1865 goto again;
1867 if (snapname.find ('\\') != string::npos) {
1868 MessageDialog msg (_("To ensure compatibility with various systems\n"
1869 "snapshot names may not contain a '\\' character"));
1870 msg.run ();
1871 goto again;
1873 save_state (snapname);
1875 break;
1877 default:
1878 break;
1882 void
1883 ARDOUR_UI::save_state (const string & name)
1885 (void) save_state_canfail (name);
1889 ARDOUR_UI::save_state_canfail (string name)
1891 if (session) {
1892 int ret;
1894 if (name.length() == 0) {
1895 name = session->snap_name();
1898 if ((ret = session->save_state (name)) != 0) {
1899 return ret;
1902 save_ardour_state (); /* XXX cannot fail? yeah, right ... */
1903 return 0;
1906 void
1907 ARDOUR_UI::primary_clock_value_changed ()
1909 if (session) {
1910 session->request_locate (primary_clock.current_time ());
1914 void
1915 ARDOUR_UI::big_clock_value_changed ()
1917 if (session) {
1918 session->request_locate (big_clock.current_time ());
1922 void
1923 ARDOUR_UI::secondary_clock_value_changed ()
1925 if (session) {
1926 session->request_locate (secondary_clock.current_time ());
1930 void
1931 ARDOUR_UI::transport_rec_enable_blink (bool onoff)
1933 if (session == 0) {
1934 return;
1937 switch (session->record_status()) {
1938 case Session::Enabled:
1939 if (onoff) {
1940 rec_button.set_visual_state (2);
1941 } else {
1942 rec_button.set_visual_state (0);
1944 break;
1946 case Session::Recording:
1947 rec_button.set_visual_state (1);
1948 break;
1950 default:
1951 rec_button.set_visual_state (0);
1952 break;
1956 void
1957 ARDOUR_UI::save_template ()
1960 ArdourPrompter prompter (true);
1961 string name;
1963 if (!check_audioengine()) {
1964 return;
1967 prompter.set_name (X_("Prompter"));
1968 prompter.set_prompt (_("Name for mix template:"));
1969 prompter.set_initial_text(session->name() + _("-template"));
1970 prompter.add_button (Gtk::Stock::SAVE, Gtk::RESPONSE_ACCEPT);
1972 switch (prompter.run()) {
1973 case RESPONSE_ACCEPT:
1974 prompter.get_result (name);
1976 if (name.length()) {
1977 session->save_template (name);
1979 break;
1981 default:
1982 break;
1986 void
1987 ARDOUR_UI::fontconfig_dialog ()
1989 #if 0
1990 /* this issue seems to have gone away with changes to font handling in GTK/Quartz
1992 #ifdef GTKOSX
1993 /* X11 users will always have fontconfig info around, but new GTK-OSX users
1994 may not and it can take a while to build it. Warn them.
1997 Glib::ustring fontconfig = Glib::build_filename (Glib::get_home_dir(), ".fontconfig");
1999 if (!Glib::file_test (fontconfig, Glib::FILE_TEST_EXISTS|Glib::FILE_TEST_IS_DIR)) {
2000 MessageDialog msg (*new_session_dialog,
2001 _("Welcome to Ardour.\n\n"
2002 "The program will take a bit longer to start up\n"
2003 "while the system fonts are checked.\n\n"
2004 "This will only be done once, and you will\n"
2005 "not see this message again\n"),
2006 true,
2007 Gtk::MESSAGE_INFO,
2008 Gtk::BUTTONS_OK);
2009 pop_back_splash ();
2010 msg.show_all ();
2011 msg.present ();
2012 msg.run ();
2014 #endif
2015 #endif
2018 void
2019 ARDOUR_UI::parse_cmdline_path (const Glib::ustring& cmdline_path, Glib::ustring& session_name, Glib::ustring& session_path, bool& existing_session)
2021 existing_session = false;
2023 if (Glib::file_test (cmdline_path, Glib::FILE_TEST_IS_DIR)) {
2024 session_path = cmdline_path;
2025 existing_session = true;
2026 } else if (Glib::file_test (cmdline_path, Glib::FILE_TEST_IS_REGULAR)) {
2027 session_path = Glib::path_get_dirname (string (cmdline_path));
2028 existing_session = true;
2029 } else {
2030 /* it doesn't exist, assume the best */
2031 session_path = Glib::path_get_dirname (string (cmdline_path));
2034 session_name = basename_nosuffix (string (cmdline_path));
2038 ARDOUR_UI::load_cmdline_session (const Glib::ustring& session_name, const Glib::ustring& session_path, bool& existing_session)
2040 /* when this is called, the backend audio system must be running */
2042 /* the main idea here is to deal with the fact that a cmdline argument for the session
2043 can be interpreted in different ways - it could be a directory or a file, and before
2044 we load, we need to know both the session directory and the snapshot (statefile) within it
2045 that we are supposed to use.
2048 if (session_name.length() == 0 || session_path.length() == 0) {
2049 return false;
2052 if (Glib::file_test (session_path, Glib::FILE_TEST_IS_DIR)) {
2054 Glib::ustring predicted_session_file;
2056 predicted_session_file = session_path;
2057 predicted_session_file += '/';
2058 predicted_session_file += session_name;
2059 predicted_session_file += Session::statefile_suffix();
2061 if (Glib::file_test (predicted_session_file, Glib::FILE_TEST_EXISTS)) {
2062 existing_session = true;
2065 } else if (Glib::file_test (session_path, Glib::FILE_TEST_EXISTS)) {
2067 if (session_path.find (Session::statefile_suffix()) == session_path.length() - 7) {
2068 /* existing .ardour file */
2069 existing_session = true;
2072 } else {
2073 existing_session = false;
2076 /* lets just try to load it */
2078 if (create_engine ()) {
2079 backend_audio_error (false, new_session_dialog);
2080 return -1;
2083 return load_session (session_path, session_name);
2086 bool
2087 ARDOUR_UI::ask_about_loading_existing_session (const Glib::ustring& session_path)
2089 Glib::ustring str = string_compose (_("This session\n%1\nalready exists. Do you want to open it?"), session_path);
2091 MessageDialog msg (str,
2092 false,
2093 Gtk::MESSAGE_WARNING,
2094 Gtk::BUTTONS_YES_NO,
2095 true);
2098 msg.set_name (X_("CleanupDialog"));
2099 msg.set_wmclass (X_("existing_session"), "Ardour");
2100 msg.set_position (Gtk::WIN_POS_MOUSE);
2101 pop_back_splash ();
2103 switch (msg.run()) {
2104 case RESPONSE_YES:
2105 return true;
2106 break;
2108 return false;
2112 ARDOUR_UI::build_session_from_nsd (const Glib::ustring& session_path, const Glib::ustring& session_name)
2115 uint32_t cchns;
2116 uint32_t mchns;
2117 AutoConnectOption iconnect;
2118 AutoConnectOption oconnect;
2119 uint32_t nphysin;
2120 uint32_t nphysout;
2122 if (Profile->get_sae()) {
2124 cchns = 0;
2125 mchns = 2;
2126 iconnect = AutoConnectPhysical;
2127 oconnect = AutoConnectMaster;
2128 nphysin = 0; // use all available
2129 nphysout = 0; // use all available
2131 } else {
2133 /* get settings from advanced section of NSD */
2135 if (new_session_dialog->create_control_bus()) {
2136 cchns = (uint32_t) new_session_dialog->control_channel_count();
2137 } else {
2138 cchns = 0;
2141 if (new_session_dialog->create_master_bus()) {
2142 mchns = (uint32_t) new_session_dialog->master_channel_count();
2143 } else {
2144 mchns = 0;
2147 if (new_session_dialog->connect_inputs()) {
2148 iconnect = AutoConnectPhysical;
2149 } else {
2150 iconnect = AutoConnectOption (0);
2153 /// @todo some minor tweaks.
2155 if (new_session_dialog->connect_outs_to_master()) {
2156 oconnect = AutoConnectMaster;
2157 } else if (new_session_dialog->connect_outs_to_physical()) {
2158 oconnect = AutoConnectPhysical;
2159 } else {
2160 oconnect = AutoConnectOption (0);
2163 nphysin = (uint32_t) new_session_dialog->input_limit_count();
2164 nphysout = (uint32_t) new_session_dialog->output_limit_count();
2167 if (build_session (session_path,
2168 session_name,
2169 cchns,
2170 mchns,
2171 iconnect,
2172 oconnect,
2173 nphysin,
2174 nphysout,
2175 engine->frame_rate() * 60 * 5)) {
2177 return -1;
2180 return 0;
2183 void
2184 ARDOUR_UI::end_loading_messages ()
2186 // hide_splash ();
2189 void
2190 ARDOUR_UI::loading_message (const std::string& msg)
2192 show_splash ();
2193 splash->message (msg);
2194 flush_pending ();
2197 void
2198 ARDOUR_UI::idle_load (const Glib::ustring& path)
2200 if (session) {
2201 if (Glib::file_test (path, Glib::FILE_TEST_IS_DIR)) {
2202 /* /path/to/foo => /path/to/foo, foo */
2203 load_session (path, basename_nosuffix (path));
2204 } else {
2205 /* /path/to/foo/foo.ardour => /path/to/foo, foo */
2206 load_session (Glib::path_get_dirname (path), basename_nosuffix (path));
2208 } else {
2210 ARDOUR_COMMAND_LINE::session_name = path;
2212 if (new_session_dialog) {
2215 /* make it break out of Dialog::run() and
2216 start again.
2219 new_session_dialog->response (1);
2224 bool
2225 ARDOUR_UI::get_session_parameters (bool backend_audio_is_running, bool should_be_new)
2227 bool existing_session = false;
2228 Glib::ustring session_name;
2229 Glib::ustring session_path;
2230 Glib::ustring template_name;
2231 int response;
2233 begin:
2234 response = Gtk::RESPONSE_NONE;
2236 if (!ARDOUR_COMMAND_LINE::session_name.empty()) {
2238 parse_cmdline_path (ARDOUR_COMMAND_LINE::session_name, session_name, session_path, existing_session);
2240 /* don't ever reuse this */
2242 ARDOUR_COMMAND_LINE::session_name = string();
2244 if (existing_session && backend_audio_is_running) {
2246 /* just load the thing already */
2248 if (load_cmdline_session (session_name, session_path, existing_session) == 0) {
2249 return true;
2253 /* make the NSD use whatever information we have */
2255 new_session_dialog->set_session_name (session_name);
2256 new_session_dialog->set_session_folder (session_path);
2259 /* loading failed, or we need the NSD for something */
2261 new_session_dialog->set_modal (false);
2262 new_session_dialog->set_position (WIN_POS_CENTER);
2263 new_session_dialog->set_current_page (0);
2264 new_session_dialog->set_existing_session (existing_session);
2265 new_session_dialog->reset_recent();
2267 do {
2268 new_session_dialog->set_have_engine (backend_audio_is_running);
2269 new_session_dialog->present ();
2270 response = new_session_dialog->run ();
2272 _session_is_new = false;
2274 /* handle possible negative responses */
2276 switch (response) {
2277 case 1:
2278 /* sent by idle_load, meaning restart the whole process again */
2279 new_session_dialog->hide();
2280 new_session_dialog->reset();
2281 goto begin;
2282 break;
2284 case Gtk::RESPONSE_CANCEL:
2285 case Gtk::RESPONSE_DELETE_EVENT:
2286 if (!session) {
2287 if (engine && engine->running()) {
2288 engine->stop (true);
2290 quit();
2292 new_session_dialog->hide ();
2293 return false;
2295 case Gtk::RESPONSE_NONE:
2296 /* "Clear" was pressed */
2297 goto try_again;
2300 fontconfig_dialog();
2302 if (!backend_audio_is_running) {
2303 int ret = new_session_dialog->engine_control.setup_engine ();
2304 if (ret < 0) {
2305 return false;
2306 } else if (ret > 0) {
2307 response = Gtk::RESPONSE_REJECT;
2308 goto try_again;
2311 /* hide the NSD while we start up the engine */
2313 new_session_dialog->hide ();
2314 flush_pending ();
2317 if (create_engine ()) {
2319 backend_audio_error (!backend_audio_is_running, new_session_dialog);
2320 flush_pending ();
2322 new_session_dialog->set_existing_session (false);
2323 new_session_dialog->set_current_page (0); // new engine page
2324 new_session_dialog->engine_control.unset_interface_chosen ();
2326 response = Gtk::RESPONSE_NONE;
2327 goto try_again;
2330 backend_audio_is_running = true;
2332 if (response == Gtk::RESPONSE_OK) {
2334 session_name = new_session_dialog->session_name();
2336 if (session_name.empty()) {
2337 response = Gtk::RESPONSE_NONE;
2338 goto try_again;
2341 /* if the user mistakenly typed path information into the session filename entry,
2342 convert what they typed into a path & a name
2345 if (session_name[0] == '/' ||
2346 (session_name.length() > 2 && session_name[0] == '.' && session_name[1] == '/') ||
2347 (session_name.length() > 3 && session_name[0] == '.' && session_name[1] == '.' && session_name[2] == '/')) {
2349 session_path = Glib::path_get_dirname (session_name);
2350 session_name = Glib::path_get_basename (session_name);
2352 } else {
2354 session_path = new_session_dialog->session_folder();
2358 template_name = Glib::ustring();
2359 switch (new_session_dialog->which_page()) {
2361 case NewSessionDialog::OpenPage:
2362 goto loadit;
2363 break;
2365 case NewSessionDialog::EnginePage:
2366 if (new_session_dialog->engine_control.interface_chosen() && !session_path.empty()) {
2367 goto loadit;
2368 } else {
2369 goto try_again;
2371 break;
2373 case NewSessionDialog::NewPage: /* nominally the "new" session creator, but could be in use for an old session */
2375 should_be_new = true;
2377 if (session_name.find ('/') != Glib::ustring::npos) {
2378 MessageDialog msg (*new_session_dialog, _("To ensure compatibility with various systems\n"
2379 "session names may not contain a '/' character"));
2380 msg.run ();
2381 response = RESPONSE_NONE;
2382 goto try_again;
2385 if (session_name.find ('\\') != Glib::ustring::npos) {
2386 MessageDialog msg (*new_session_dialog, _("To ensure compatibility with various systems\n"
2387 "session names may not contain a '\\' character"));
2388 msg.run ();
2389 response = RESPONSE_NONE;
2390 goto try_again;
2393 //XXX This is needed because session constructor wants a
2394 //non-existant path. hopefully this will be fixed at some point.
2396 session_path = Glib::build_filename (session_path, session_name);
2398 if (Glib::file_test (session_path, Glib::FileTest (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))) {
2400 new_session_dialog->hide ();
2402 if (ask_about_loading_existing_session (session_path)) {
2403 goto loadit;
2404 } else {
2405 response = RESPONSE_NONE;
2406 goto try_again;
2410 _session_is_new = true;
2412 if (new_session_dialog->use_session_template()) {
2414 template_name = new_session_dialog->session_template_name();
2415 goto loadit;
2417 } else {
2418 if (build_session_from_nsd (session_path, session_name)) {
2419 response = RESPONSE_NONE;
2420 goto try_again;
2422 goto done;
2424 break;
2426 default:
2427 break;
2430 loadit:
2431 new_session_dialog->hide ();
2433 if (load_session (session_path, session_name, template_name)) {
2434 /* force a retry */
2435 response = Gtk::RESPONSE_NONE;
2438 try_again:
2439 if (response == Gtk::RESPONSE_NONE) {
2440 new_session_dialog->set_existing_session (false);
2441 new_session_dialog->reset ();
2445 } while (response == Gtk::RESPONSE_NONE || response == Gtk::RESPONSE_REJECT);
2447 done:
2448 show();
2449 new_session_dialog->hide();
2450 new_session_dialog->reset();
2451 goto_editor_window ();
2452 return true;
2455 void
2456 ARDOUR_UI::close_session ()
2458 if (!check_audioengine()) {
2459 return;
2462 if (unload_session (true)) {
2463 return;
2466 get_session_parameters (true, false);
2470 ARDOUR_UI::load_session (const Glib::ustring& path, const Glib::ustring& snap_name, Glib::ustring mix_template)
2472 Session *new_session;
2473 int unload_status;
2474 int retval = -1;
2476 session_loaded = false;
2478 if (!check_audioengine()) {
2479 return -1;
2482 unload_status = unload_session ();
2484 if (unload_status < 0) {
2485 goto out;
2486 } else if (unload_status > 0) {
2487 retval = 0;
2488 goto out;
2491 loading_message (_("Please wait while Ardour loads your session"));
2493 try {
2494 new_session = new Session (*engine, path, snap_name, mix_template);
2497 /* this one is special */
2499 catch (AudioEngine::PortRegistrationFailure& err) {
2501 MessageDialog msg (err.what(),
2502 true,
2503 Gtk::MESSAGE_INFO,
2504 Gtk::BUTTONS_CLOSE);
2506 msg.set_title (_("Port Registration Error"));
2507 msg.set_secondary_text (_("Click the Close button to try again."));
2508 msg.set_position (Gtk::WIN_POS_CENTER);
2509 pop_back_splash ();
2510 msg.present ();
2512 int response = msg.run ();
2514 msg.hide ();
2516 switch (response) {
2517 case RESPONSE_CANCEL:
2518 exit (1);
2519 default:
2520 break;
2522 goto out;
2525 /* this exception is also special */
2527 catch (Session::SRMismatchRejected& err) {
2528 goto out; /* just go back and reload something else, etc. */
2531 catch (...) {
2533 MessageDialog msg (string_compose(_("Session \"%1 (snapshot %2)\" did not load successfully"), path, snap_name),
2534 true,
2535 Gtk::MESSAGE_INFO,
2536 Gtk::BUTTONS_CLOSE);
2538 msg.set_title (_("Loading Error"));
2539 msg.set_secondary_text (_("Click the Close button to try again."));
2540 msg.set_position (Gtk::WIN_POS_CENTER);
2541 pop_back_splash ();
2542 msg.present ();
2544 int response = msg.run ();
2546 msg.hide ();
2548 switch (response) {
2549 case RESPONSE_CANCEL:
2550 exit (1);
2551 default:
2552 break;
2554 goto out;
2557 connect_to_session (new_session);
2559 Config->set_current_owner (ConfigVariableBase::Interface);
2561 session_loaded = true;
2563 goto_editor_window ();
2565 if (session) {
2566 session->set_clean ();
2569 flush_pending ();
2570 retval = 0;
2572 out:
2573 return retval;
2577 ARDOUR_UI::build_session (const Glib::ustring& path, const Glib::ustring& snap_name,
2578 uint32_t control_channels,
2579 uint32_t master_channels,
2580 AutoConnectOption input_connect,
2581 AutoConnectOption output_connect,
2582 uint32_t nphysin,
2583 uint32_t nphysout,
2584 nframes_t initial_length)
2586 Session *new_session;
2587 int x;
2589 if (!check_audioengine()) {
2590 return -1;
2593 session_loaded = false;
2595 x = unload_session ();
2597 if (x < 0) {
2598 return -1;
2599 } else if (x > 0) {
2600 return 0;
2603 _session_is_new = true;
2605 try {
2606 new_session = new Session (*engine, path, snap_name, input_connect, output_connect,
2607 control_channels, master_channels, nphysin, nphysout, initial_length);
2610 catch (...) {
2612 MessageDialog msg (string_compose(_("Could not create session in \"%1\""), path));
2613 pop_back_splash ();
2614 msg.run ();
2615 return -1;
2618 connect_to_session (new_session);
2620 session_loaded = true;
2622 new_session->save_state(new_session->name());
2624 return 0;
2627 void
2628 ARDOUR_UI::show ()
2630 if (editor) {
2631 editor->show_window ();
2633 if (!shown_flag) {
2634 editor->present ();
2637 shown_flag = true;
2641 void
2642 ARDOUR_UI::show_about ()
2644 if (about == 0) {
2645 about = new About;
2646 about->signal_response().connect(mem_fun (*this, &ARDOUR_UI::about_signal_response) );
2649 about->set_transient_for(*editor);
2651 about->show_all ();
2654 void
2655 ARDOUR_UI::launch_chat ()
2657 #ifdef __APPLE__
2658 NagScreen::open_uri("http://webchat.freenode.net/?channels=ardour-osx");
2659 #else
2660 NagScreen::open_uri("http://webchat.freenode.net/?channels=ardour");
2661 #endif
2664 void
2665 ARDOUR_UI::hide_about ()
2667 if (about) {
2668 about->get_window()->set_cursor ();
2669 about->hide ();
2673 void
2674 ARDOUR_UI::about_signal_response(int response)
2676 hide_about();
2679 void
2680 ARDOUR_UI::show_splash ()
2682 if (splash == 0) {
2683 try {
2684 splash = new Splash;
2685 } catch (...) {
2686 return;
2690 splash->show ();
2691 splash->present ();
2692 splash->queue_draw ();
2693 splash->get_window()->process_updates (true);
2694 flush_pending ();
2697 void
2698 ARDOUR_UI::hide_splash ()
2700 if (splash) {
2701 splash->hide();
2705 void
2706 ARDOUR_UI::display_cleanup_results (Session::cleanup_report& rep, const gchar* list_title,
2707 const string& plural_msg, const string& singular_msg)
2709 size_t removed;
2711 removed = rep.paths.size();
2713 if (removed == 0) {
2714 MessageDialog msgd (*editor,
2715 _("No audio files were ready for cleanup"),
2716 true,
2717 Gtk::MESSAGE_INFO,
2718 (Gtk::ButtonsType)(Gtk::BUTTONS_OK) );
2719 msgd.set_secondary_text (_("If this seems suprising, \n\
2720 check for any existing snapshots.\n\
2721 These may still include regions that\n\
2722 require some unused files to continue to exist."));
2724 msgd.run ();
2725 return;
2728 ArdourDialog results (_("ardour: cleanup"), true, false);
2730 struct CleanupResultsModelColumns : public Gtk::TreeModel::ColumnRecord {
2731 CleanupResultsModelColumns() {
2732 add (visible_name);
2733 add (fullpath);
2735 Gtk::TreeModelColumn<Glib::ustring> visible_name;
2736 Gtk::TreeModelColumn<Glib::ustring> fullpath;
2740 CleanupResultsModelColumns results_columns;
2741 Glib::RefPtr<Gtk::ListStore> results_model;
2742 Gtk::TreeView results_display;
2744 results_model = ListStore::create (results_columns);
2745 results_display.set_model (results_model);
2746 results_display.append_column (list_title, results_columns.visible_name);
2748 results_display.set_name ("CleanupResultsList");
2749 results_display.set_headers_visible (true);
2750 results_display.set_headers_clickable (false);
2751 results_display.set_reorderable (false);
2753 Gtk::ScrolledWindow list_scroller;
2754 Gtk::Label txt;
2755 Gtk::VBox dvbox;
2756 Gtk::HBox dhbox; // the hbox for the image and text
2757 Gtk::HBox ddhbox; // the hbox we eventually pack into the dialog's vbox
2758 Gtk::Image* dimage = manage (new Gtk::Image(Stock::DIALOG_INFO, Gtk::ICON_SIZE_DIALOG));
2760 dimage->set_alignment(ALIGN_LEFT, ALIGN_TOP);
2763 /* subst:
2764 %1 - number of files removed
2765 %2 - location of "dead_sounds"
2766 %3 - size of files affected
2767 %4 - prefix for "bytes" to produce sensible results (e.g. mega, kilo, giga)
2770 const char* bprefix;
2771 float space_adjusted;
2773 if (rep.space < 1000000.0f) {
2774 bprefix = X_("kilo");
2775 space_adjusted = truncf((float)rep.space / 1000.0f);
2776 } else if (rep.space < (1000000.0f * 1000)) {
2777 bprefix = X_("mega");
2778 space_adjusted = truncf((float)rep.space / (1000000.0f));
2779 } else {
2780 bprefix = X_("giga");
2781 space_adjusted = truncf((float)rep.space / (1000000.0f * 1000));
2784 if (removed > 1) {
2785 txt.set_text (string_compose (plural_msg, removed, session->path() + "dead_sounds", space_adjusted, bprefix));
2786 } else {
2787 txt.set_text (string_compose (singular_msg, removed, session->path() + "dead_sounds", space_adjusted, bprefix));
2790 dhbox.pack_start (*dimage, true, false, 5);
2791 dhbox.pack_start (txt, true, false, 5);
2793 for (vector<string>::iterator i = rep.paths.begin(); i != rep.paths.end(); ++i) {
2794 TreeModel::Row row = *(results_model->append());
2795 row[results_columns.visible_name] = *i;
2796 row[results_columns.fullpath] = *i;
2799 list_scroller.add (results_display);
2800 list_scroller.set_size_request (-1, 150);
2801 list_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
2803 dvbox.pack_start (dhbox, true, false, 5);
2804 dvbox.pack_start (list_scroller, true, false, 5);
2805 ddhbox.pack_start (dvbox, true, false, 5);
2807 results.get_vbox()->pack_start (ddhbox, true, false, 5);
2808 results.add_button (Stock::CLOSE, RESPONSE_CLOSE);
2809 results.set_default_response (RESPONSE_CLOSE);
2810 results.set_position (Gtk::WIN_POS_MOUSE);
2811 results.show_all_children ();
2812 results.set_resizable (false);
2814 results.run ();
2818 void
2819 ARDOUR_UI::cleanup ()
2821 if (session == 0) {
2822 /* shouldn't happen: menu item is insensitive */
2823 return;
2827 MessageDialog checker (_("Are you sure you want to cleanup?"),
2828 true,
2829 Gtk::MESSAGE_QUESTION,
2830 (Gtk::ButtonsType)(Gtk::BUTTONS_NONE));
2832 checker.set_secondary_text(_("Cleanup is a destructive operation.\n\
2833 ALL undo/redo information will be lost if you cleanup.\n\
2834 After cleanup, unused audio files will be moved to a \
2835 \"dead sounds\" location."));
2837 checker.add_button (Stock::CANCEL, RESPONSE_CANCEL);
2838 checker.add_button (_("Clean Up"), RESPONSE_ACCEPT);
2839 checker.set_default_response (RESPONSE_CANCEL);
2841 checker.set_name (_("CleanupDialog"));
2842 checker.set_wmclass (X_("ardour_cleanup"), "Ardour");
2843 checker.set_position (Gtk::WIN_POS_MOUSE);
2845 switch (checker.run()) {
2846 case RESPONSE_ACCEPT:
2847 break;
2848 default:
2849 return;
2852 Session::cleanup_report rep;
2854 editor->prepare_for_cleanup ();
2856 /* do not allow flush until a session is reloaded */
2858 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Main"), X_("FlushWastebasket"));
2859 if (act) {
2860 act->set_sensitive (false);
2863 if (session->cleanup_sources (rep)) {
2864 editor->finish_cleanup ();
2865 return;
2868 editor->finish_cleanup ();
2870 checker.hide();
2871 display_cleanup_results (rep,
2872 _("cleaned files"),
2873 _("\
2874 The following %1 files were not in use and \n\
2875 have been moved to:\n\
2876 %2. \n\n\
2877 Flushing the wastebasket will \n\
2878 release an additional\n\
2879 %3 %4bytes of disk space.\n"),
2880 _("\
2881 The following file was not in use and \n \
2882 has been moved to:\n \
2883 %2. \n\n\
2884 Flushing the wastebasket will \n\
2885 release an additional\n\
2886 %3 %4bytes of disk space.\n"
2891 void
2892 ARDOUR_UI::flush_trash ()
2894 if (session == 0) {
2895 /* shouldn't happen: menu item is insensitive */
2896 return;
2899 Session::cleanup_report rep;
2901 if (session->cleanup_trash_sources (rep)) {
2902 return;
2905 display_cleanup_results (rep,
2906 _("deleted file"),
2907 _("The following %1 files were deleted from\n\
2908 %2,\n\
2909 releasing %3 %4bytes of disk space"),
2910 _("The following file was deleted from\n\
2911 %2,\n\
2912 releasing %3 %4bytes of disk space"));
2915 void
2916 ARDOUR_UI::add_route (Gtk::Window* float_window)
2918 int count;
2920 if (!session) {
2921 return;
2924 if (add_route_dialog == 0) {
2925 add_route_dialog = new AddRouteDialog;
2926 if (float_window) {
2927 add_route_dialog->set_transient_for (*float_window);
2931 if (add_route_dialog->is_visible()) {
2932 /* we're already doing this */
2933 return;
2936 ResponseType r = (ResponseType) add_route_dialog->run ();
2938 add_route_dialog->hide();
2940 switch (r) {
2941 case RESPONSE_ACCEPT:
2942 break;
2943 default:
2944 return;
2945 break;
2948 if ((count = add_route_dialog->count()) <= 0) {
2949 return;
2952 string template_path = add_route_dialog->track_template();
2954 if (!template_path.empty()) {
2955 session->new_route_from_template (count, template_path);
2956 return;
2959 uint32_t input_chan = add_route_dialog->channels ();
2960 uint32_t output_chan;
2961 string name_template = add_route_dialog->name_template ();
2962 bool track = add_route_dialog->track ();
2964 AutoConnectOption oac = Config->get_output_auto_connect();
2966 if (oac & AutoConnectMaster) {
2967 output_chan = (session->master_out() ? session->master_out()->n_inputs() : input_chan);
2968 } else {
2969 output_chan = input_chan;
2972 /* XXX do something with name template */
2974 if (track) {
2975 session_add_audio_track (input_chan, output_chan, add_route_dialog->mode(), count);
2976 } else {
2977 session_add_audio_bus (input_chan, output_chan, count);
2981 XMLNode*
2982 ARDOUR_UI::mixer_settings () const
2984 XMLNode* node = 0;
2986 if (session) {
2987 node = session->instant_xml(X_("Mixer"), session->path());
2988 } else {
2989 node = Config->instant_xml(X_("Mixer"), get_user_ardour_path());
2992 if (!node) {
2993 node = new XMLNode (X_("Mixer"));
2996 return node;
2999 XMLNode*
3000 ARDOUR_UI::editor_settings () const
3002 XMLNode* node = 0;
3004 if (session) {
3005 node = session->instant_xml(X_("Editor"), session->path());
3006 } else {
3007 node = Config->instant_xml(X_("Editor"), get_user_ardour_path());
3010 if (!node) {
3011 if (getenv("ARDOUR_INSTANT_XML_PATH")) {
3012 node = Config->instant_xml(X_("Editor"), getenv("ARDOUR_INSTANT_XML_PATH"));
3016 if (!node) {
3017 node = new XMLNode (X_("Editor"));
3019 return node;
3022 XMLNode*
3023 ARDOUR_UI::keyboard_settings () const
3025 XMLNode* node = 0;
3027 node = Config->extra_xml(X_("Keyboard"));
3029 if (!node) {
3030 node = new XMLNode (X_("Keyboard"));
3032 return node;
3035 void
3036 ARDOUR_UI::create_xrun_marker(nframes_t where)
3038 editor->mouse_add_new_marker (where, false, true);
3041 void
3042 ARDOUR_UI::halt_on_xrun_message ()
3044 MessageDialog msg (*editor,
3045 _("Recording was stopped because your system could not keep up."));
3046 msg.run ();
3049 void
3050 ARDOUR_UI::xrun_handler(nframes_t where)
3052 if (!session) {
3053 return;
3056 ENSURE_GUI_THREAD (bind(mem_fun(*this, &ARDOUR_UI::xrun_handler), where));
3058 if (session && Config->get_create_xrun_marker() && session->actively_recording()) {
3059 create_xrun_marker(where);
3062 if (session && Config->get_stop_recording_on_xrun() && session->actively_recording()) {
3063 halt_on_xrun_message ();
3067 bool
3068 ARDOUR_UI::preset_file_exists_handler ()
3070 /* if driven from another thread, say "do not overwrite" and show the user nothing.
3073 if (!Gtkmm2ext::UI::instance()->caller_is_ui_thread()) { \
3074 return false;
3077 HBox* hbox = new HBox();
3078 Image* image = new Image (Stock::DIALOG_QUESTION, ICON_SIZE_DIALOG);
3079 Gtk::Dialog dialog (_("Preset Exists"), true, false);
3080 Label message (_("\
3081 A preset with this name already exists for this plugin.\n\
3083 What you would like to do?\n"));
3084 image->set_alignment(ALIGN_CENTER, ALIGN_TOP);
3085 hbox->pack_start (*image, PACK_EXPAND_WIDGET, 12);
3086 hbox->pack_end (message, PACK_EXPAND_PADDING, 12);
3087 dialog.get_vbox()->pack_start(*hbox, PACK_EXPAND_PADDING, 6);
3088 dialog.add_button (_("Overwrite the existing preset"), RESPONSE_ACCEPT);
3089 dialog.add_button (_("Leave the existing preset alone"), RESPONSE_REJECT);
3090 dialog.set_default_response (RESPONSE_ACCEPT);
3091 dialog.set_position (WIN_POS_MOUSE);
3092 dialog.set_type_hint (Gdk::WINDOW_TYPE_HINT_UTILITY); // need to make it float above the preset name dialog
3094 message.show();
3095 image->show();
3096 hbox->show();
3098 switch (dialog.run ()) {
3099 case RESPONSE_ACCEPT:
3100 return true;
3101 default:
3102 return false;
3106 void
3107 ARDOUR_UI::push_buffer_stats (uint32_t capture, uint32_t playback)
3109 time_t now;
3110 time (&now);
3112 while (disk_buffer_stats.size() > 60) {
3113 disk_buffer_stats.pop_front ();
3116 disk_buffer_stats.push_back (DiskBufferStat (now, capture, playback));
3119 void
3120 ARDOUR_UI::write_buffer_stats ()
3122 std::ofstream fout;
3123 struct tm tm;
3124 char buf[64];
3125 char path[PATH_MAX+1]; int fd;
3127 strcpy (path, "ardourBufferingXXXXXX");
3129 if ((fd = mkstemp (path )) < 0) {
3130 cerr << X_("cannot find temporary name for ardour buffer stats") << endl;
3131 return;
3134 fout.open (path);
3135 close (fd);
3137 if (!fout) {
3138 cerr << string_compose (X_("cannot open file %1 for ardour buffer stats"), path) << endl;
3139 return;
3142 for (list<DiskBufferStat>::iterator i = disk_buffer_stats.begin(); i != disk_buffer_stats.end(); ++i) {
3143 localtime_r (&(*i).when, &tm);
3144 strftime (buf, sizeof (buf), "%T", &tm);
3145 fout << buf << ' ' << (*i).capture << ' ' << (*i).playback << endl;
3148 disk_buffer_stats.clear ();
3150 fout.close ();
3152 cerr << "Ardour buffering statistics can be found in: " << path << endl;
3153 free (path);
3156 void
3157 ARDOUR_UI::disk_overrun_handler ()
3160 ENSURE_GUI_THREAD (mem_fun(*this, &ARDOUR_UI::disk_overrun_handler));
3162 write_buffer_stats ();
3164 if (!have_disk_speed_dialog_displayed) {
3165 have_disk_speed_dialog_displayed = true;
3166 MessageDialog* msg = new MessageDialog (*editor, _("\
3167 The disk system on your computer\n\
3168 was not able to keep up with Ardour.\n\
3170 Specifically, it failed to write data to disk\n\
3171 quickly enough to keep up with recording.\n"));
3172 msg->signal_response().connect (bind (mem_fun (*this, &ARDOUR_UI::disk_speed_dialog_gone), msg));
3173 msg->show_all ();
3177 void
3178 ARDOUR_UI::disk_underrun_handler ()
3181 ENSURE_GUI_THREAD (mem_fun(*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 (bind (mem_fun (*this, &ARDOUR_UI::disk_speed_dialog_gone), msg));
3194 msg->show_all ();
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 (bind (mem_fun(*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 pop_back_splash ();
3251 switch (dialog.run ()) {
3252 case RESPONSE_ACCEPT:
3253 return 1;
3254 default:
3255 return 0;
3260 ARDOUR_UI::sr_mismatch_dialog (nframes_t desired, nframes_t actual)
3262 HBox* hbox = new HBox();
3263 Image* image = new Image (Stock::DIALOG_QUESTION, ICON_SIZE_DIALOG);
3264 ArdourDialog dialog (_("Sample Rate Mismatch"), true);
3265 Label message (string_compose (_("\
3266 This session was created with a sample rate of %1 Hz\n\
3268 The audioengine is currently running at %2 Hz\n"), desired, actual));
3270 image->set_alignment(ALIGN_CENTER, ALIGN_TOP);
3271 hbox->pack_start (*image, PACK_EXPAND_WIDGET, 12);
3272 hbox->pack_end (message, PACK_EXPAND_PADDING, 12);
3273 dialog.get_vbox()->pack_start(*hbox, PACK_EXPAND_PADDING, 6);
3274 dialog.add_button (_("Do not load session"), RESPONSE_REJECT);
3275 dialog.add_button (_("Load session anyway"), RESPONSE_ACCEPT);
3276 dialog.set_default_response (RESPONSE_ACCEPT);
3277 dialog.set_position (WIN_POS_CENTER);
3278 message.show();
3279 image->show();
3280 hbox->show();
3282 switch (dialog.run ()) {
3283 case RESPONSE_ACCEPT:
3284 return 0;
3285 default:
3286 return 1;
3291 void
3292 ARDOUR_UI::disconnect_from_jack ()
3294 if (engine) {
3295 if( engine->disconnect_from_jack ()) {
3296 MessageDialog msg (*editor, _("Could not disconnect from JACK"));
3297 msg.run ();
3300 update_sample_rate (0);
3304 void
3305 ARDOUR_UI::reconnect_to_jack ()
3307 if (engine) {
3308 if (engine->reconnect_to_jack ()) {
3309 MessageDialog msg (*editor, _("Could not reconnect to JACK"));
3310 msg.run ();
3313 update_sample_rate (0);
3317 void
3318 ARDOUR_UI::use_config ()
3320 Glib::RefPtr<Action> act;
3322 switch (Config->get_native_file_data_format ()) {
3323 case FormatFloat:
3324 act = ActionManager::get_action (X_("options"), X_("FileDataFormatFloat"));
3325 break;
3326 case FormatInt24:
3327 act = ActionManager::get_action (X_("options"), X_("FileDataFormat24bit"));
3328 break;
3329 case FormatInt16:
3330 act = ActionManager::get_action (X_("options"), X_("FileDataFormat16bit"));
3331 break;
3334 if (act) {
3335 Glib::RefPtr<RadioAction> ract = Glib::RefPtr<RadioAction>::cast_dynamic(act);
3336 ract->set_active ();
3339 switch (Config->get_native_file_header_format ()) {
3340 case BWF:
3341 act = ActionManager::get_action (X_("options"), X_("FileHeaderFormatBWF"));
3342 break;
3343 case WAVE:
3344 act = ActionManager::get_action (X_("options"), X_("FileHeaderFormatWAVE"));
3345 break;
3346 case WAVE64:
3347 act = ActionManager::get_action (X_("options"), X_("FileHeaderFormatWAVE64"));
3348 break;
3349 case iXML:
3350 act = ActionManager::get_action (X_("options"), X_("FileHeaderFormatiXML"));
3351 break;
3352 case RF64:
3353 act = ActionManager::get_action (X_("options"), X_("FileHeaderFormatRF64"));
3354 break;
3355 case CAF:
3356 act = ActionManager::get_action (X_("options"), X_("FileHeaderFormatCAF"));
3357 break;
3358 case AIFF:
3359 act = ActionManager::get_action (X_("options"), X_("FileHeaderFormatAIFF"));
3360 break;
3363 if (act) {
3364 Glib::RefPtr<RadioAction> ract = Glib::RefPtr<RadioAction>::cast_dynamic(act);
3365 ract->set_active ();
3368 XMLNode* node = Config->extra_xml (X_("TransportControllables"));
3369 if (node) {
3370 set_transport_controllable_state (*node);
3374 void
3375 ARDOUR_UI::update_transport_clocks (nframes_t pos)
3377 if (Config->get_primary_clock_delta_edit_cursor()) {
3378 primary_clock.set (pos, false, editor->get_preferred_edit_position(), 1);
3379 } else {
3380 primary_clock.set (pos, 0, true);
3383 if (Config->get_secondary_clock_delta_edit_cursor()) {
3384 secondary_clock.set (pos, false, editor->get_preferred_edit_position(), 2);
3385 } else {
3386 secondary_clock.set (pos);
3389 if (big_clock_window) {
3390 big_clock.set (pos);
3394 void
3395 ARDOUR_UI::record_state_changed ()
3397 ENSURE_GUI_THREAD (mem_fun (*this, &ARDOUR_UI::record_state_changed));
3399 if (!session || !big_clock_window) {
3400 /* why bother - the clock isn't visible */
3401 return;
3404 switch (session->record_status()) {
3405 case Session::Recording:
3406 big_clock.set_widget_name ("BigClockRecording");
3407 break;
3408 default:
3409 big_clock.set_widget_name ("BigClockNonRecording");
3410 break;
3414 bool
3415 ARDOUR_UI::first_idle ()
3417 if (session) {
3418 session->allow_auto_play (true);
3421 if (editor) {
3422 editor->first_idle();
3425 Keyboard::set_can_save_keybindings (true);
3426 return false;
3429 void
3430 ARDOUR_UI::store_clock_modes ()
3432 XMLNode* node = new XMLNode(X_("ClockModes"));
3434 for (vector<AudioClock*>::iterator x = AudioClock::clocks.begin(); x != AudioClock::clocks.end(); ++x) {
3435 node->add_property ((*x)->name().c_str(), enum_2_string ((*x)->mode()));
3438 session->add_extra_xml (*node);
3439 session->set_dirty ();
3444 ARDOUR_UI::TransportControllable::TransportControllable (std::string name, ARDOUR_UI& u, ToggleType tp)
3445 : Controllable (name), ui (u), type(tp)
3450 void
3451 ARDOUR_UI::TransportControllable::set_value (float val)
3453 if (type == ShuttleControl) {
3454 double fract;
3456 if (val == 0.5f) {
3457 fract = 0.0;
3458 } else {
3459 if (val < 0.5f) {
3460 fract = -((0.5f - val)/0.5f);
3461 } else {
3462 fract = ((val - 0.5f)/0.5f);
3466 ui.set_shuttle_fract (fract);
3467 return;
3470 if (val < 0.5f) {
3471 /* do nothing: these are radio-style actions */
3472 return;
3475 const char *action = 0;
3477 switch (type) {
3478 case Roll:
3479 action = X_("Roll");
3480 break;
3481 case Stop:
3482 action = X_("Stop");
3483 break;
3484 case GotoStart:
3485 action = X_("Goto Start");
3486 break;
3487 case GotoEnd:
3488 action = X_("Goto End");
3489 break;
3490 case AutoLoop:
3491 action = X_("Loop");
3492 break;
3493 case PlaySelection:
3494 action = X_("Play Selection");
3495 break;
3496 case RecordEnable:
3497 action = X_("Record");
3498 break;
3499 default:
3500 break;
3503 if (action == 0) {
3504 return;
3507 Glib::RefPtr<Action> act = ActionManager::get_action ("Transport", action);
3509 if (act) {
3510 act->activate ();
3514 float
3515 ARDOUR_UI::TransportControllable::get_value (void) const
3517 float val = 0.0f;
3519 switch (type) {
3520 case Roll:
3521 break;
3522 case Stop:
3523 break;
3524 case GotoStart:
3525 break;
3526 case GotoEnd:
3527 break;
3528 case AutoLoop:
3529 break;
3530 case PlaySelection:
3531 break;
3532 case RecordEnable:
3533 break;
3534 case ShuttleControl:
3535 break;
3536 default:
3537 break;
3540 return val;
3543 void
3544 ARDOUR_UI::TransportControllable::set_id (const string& str)
3546 _id = str;
3549 void
3550 ARDOUR_UI::setup_profile ()
3552 if (gdk_screen_width() < 1200) {
3553 Profile->set_small_screen ();
3556 if (getenv ("ARDOUR_SAE")) {
3557 Profile->set_sae ();
3558 Profile->set_single_package ();