/*
- Copyright (C) 1999-2002 Paul Davis
+ Copyright (C) 1999-2007 Paul Davis
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
#include <iostream>
+#include <sys/resource.h>
+
#include <gtkmm/messagedialog.h>
#include <gtkmm/accelmap.h>
#include <pbd/compose.h>
#include <pbd/pathscanner.h>
#include <pbd/failed_constructor.h>
+#include <pbd/enumwriter.h>
+#include <pbd/stacktrace.h>
#include <gtkmm2ext/gtk_ui.h>
#include <gtkmm2ext/utils.h>
#include <gtkmm2ext/click_box.h>
sigc::signal<void,bool> ARDOUR_UI::Blink;
sigc::signal<void> ARDOUR_UI::RapidScreenUpdate;
+sigc::signal<void> ARDOUR_UI::MidRapidScreenUpdate;
sigc::signal<void> ARDOUR_UI::SuperRapidScreenUpdate;
sigc::signal<void,nframes_t> ARDOUR_UI::Clock;
: Gtkmm2ext::UI ("ardour", argcp, argvp, rcfile),
- primary_clock (X_("TransportClockDisplay"), true, false, true),
- secondary_clock (X_("SecondaryClockDisplay"), true, false, true),
- preroll_clock (X_("PreRollClock"), true, true),
- postroll_clock (X_("PostRollClock"), true, true),
+ primary_clock (X_("primary"), false, X_("TransportClockDisplay"), true, false, true),
+ secondary_clock (X_("secondary"), false, X_("SecondaryClockDisplay"), true, false, true),
+ preroll_clock (X_("preroll"), false, X_("PreRollClock"), true, true),
+ postroll_clock (X_("postroll"), false, X_("PostRollClock"), true, true),
/* adjuster table */
/* big clock */
- big_clock ("BigClockDisplay", true),
+ big_clock (X_("bigclock"), false, "BigClockNonRecording", false, false, true),
/* transport */
color_manager = new ColorManager();
std::string color_file = ARDOUR::find_config_file("ardour.colors");
-
+
color_manager->load (color_file);
editor = 0;
route_params = 0;
option_editor = 0;
location_ui = 0;
- sfdb = 0;
open_session_selector = 0;
have_configure_timeout = false;
have_disk_overrun_displayed = false;
_will_create_new_session_automatically = false;
session_loaded = false;
last_speed_displayed = -1.0f;
+ keybindings_path = ARDOUR::find_config_file ("ardour.bindings");
+
+ can_save_keybindings = false;
+ Glib::signal_idle().connect (mem_fun (*this, &ARDOUR_UI::first_idle));
last_configure_time.tv_sec = 0;
last_configure_time.tv_usec = 0;
gettimeofday (&last_peak_grab, 0);
gettimeofday (&last_shuttle_request, 0);
- ARDOUR::AudioDiskstream::DeleteSources.connect (mem_fun(*this, &ARDOUR_UI::delete_sources_in_the_right_thread));
ARDOUR::Diskstream::DiskOverrun.connect (mem_fun(*this, &ARDOUR_UI::disk_overrun_handler));
ARDOUR::Diskstream::DiskUnderrun.connect (mem_fun(*this, &ARDOUR_UI::disk_underrun_handler));
throw failed_constructor();
}
+ /* set default clock modes */
+
+ primary_clock.set_mode (AudioClock::SMPTE);
+ secondary_clock.set_mode (AudioClock::BBT);
+
/* start the time-of-day-clock */
update_wall_clock ();
Config->add_instant_xml (mnode, get_user_ardour_path());
}
- /* keybindings */
-
- AccelMap::save ("ardour.saved_bindings");
+ save_keybindings ();
}
void
ARDOUR_UI::startup ()
{
- /* Once the UI is up and running, start the audio engine. Doing
- this before the UI is up and running can cause problems
- when not running with SCHED_FIFO, because the amount of
- CPU and disk work needed to get the UI started can interfere
- with the scheduling of the audio thread.
- */
+ if (engine->is_realtime()) {
- Glib::signal_idle().connect (mem_fun(*this, &ARDOUR_UI::start_engine));
+ struct rlimit limits;
+
+ if (getrlimit (RLIMIT_MEMLOCK, &limits)) {
+ return;
+ }
+
+ if (limits.rlim_cur != RLIM_INFINITY) {
+ MessageDialog msg (_("WARNING: Your system has a limit for maximum amount of locked memory. "
+ "This might cause Ardour to run out of memory before your system "
+ "runs out of memory. \n\n"
+ "You can view the memory limit with 'ulimit -l', "
+ "and it is normally controlled by /etc/security/limits.conf"));
+
+ editor->ensure_float (msg);
+ msg.run ();
+ }
+ }
}
void
break;
}
}
+
+ if (session) {
+ session->set_deletion_in_progress ();
+ }
+ engine->stop (true);
Config->save_state();
quit ();
}
save_the_session = 0;
- editor->ensure_float (window);
+ window.set_keep_above (true);
+ window.present ();
ResponseType r = (ResponseType) window.run();
return TRUE;
}
+gint
+ARDOUR_UI::every_point_oh_five_seconds ()
+{
+ MidRapidScreenUpdate(); /* EMIT_SIGNAL */
+ return true;
+}
+
gint
ARDOUR_UI::every_point_zero_one_seconds ()
{
}
void
-ARDOUR_UI::count_recenabled_diskstreams (Route& route)
+ARDOUR_UI::count_recenabled_streams (Route& route)
{
Track* track = dynamic_cast<Track*>(&route);
if (track && track->diskstream()->record_enabled()) {
- rec_enabled_diskstreams++;
+ rec_enabled_streams += track->n_inputs();
}
}
int secs;
nframes_t fr = session->frame_rate();
- if (session->actively_recording()){
-
- rec_enabled_diskstreams = 0;
- session->foreach_route (this, &ARDOUR_UI::count_recenabled_diskstreams);
-
- if (rec_enabled_diskstreams) {
- frames /= rec_enabled_diskstreams;
- }
-
- } else {
-
- /* hmmm. shall we divide by the route count? or the diskstream count?
- or what? for now, do nothing ...
- */
-
+ rec_enabled_streams = 0;
+ session->foreach_route (this, &ARDOUR_UI::count_recenabled_streams);
+
+ if (rec_enabled_streams) {
+ frames /= rec_enabled_streams;
}
hrs = frames / (fr * 3600);
*/
if (editor) {
- editor->reposition_x_origin (session->current_start_frame());
+ editor->reset_x_origin (session->current_start_frame());
}
}
}
*/
if (editor) {
- editor->reposition_x_origin (0);
+ editor->reset_x_origin (0);
}
}
}
*/
if (editor) {
- editor->reposition_x_origin (frame);
+ editor->reset_x_origin (frame);
}
}
}
return;
}
- if (Config->get_auto_loop()) {
+ if (session->get_play_loop ()) {
session->request_play_loop (false);
}
switch (session->record_status()) {
case Session::Disabled:
if (session->ntracks() == 0) {
- string txt = _("Please create 1 or more track\nbefore trying to record.\nCheck the Session menu.");
- MessageDialog msg (*editor, txt);
+ MessageDialog msg (*editor, _("Please create 1 or more track\nbefore trying to record.\nCheck the Session menu."));
msg.run ();
return;
}
rolling = session->transport_rolling ();
- if (Config->get_auto_loop()) {
+ if (session->get_play_loop()) {
session->request_play_loop (false);
auto_loop_button.set_active (false);
roll_button.set_active (true);
ARDOUR_UI::transport_loop()
{
if (session) {
- if (Config->get_auto_loop()) {
+ if (session->get_play_loop()) {
if (session->transport_rolling()) {
Location * looploc = session->locations()->auto_loop_location();
if (looploc) {
ActionManager::set_sensitive (ActionManager::jack_opposite_sensitive_actions, true);
}
-
void
ARDOUR_UI::engine_running ()
{
ENSURE_GUI_THREAD (mem_fun(*this, &ARDOUR_UI::engine_running));
ActionManager::set_sensitive (ActionManager::jack_sensitive_actions, true);
ActionManager::set_sensitive (ActionManager::jack_opposite_sensitive_actions, false);
+
+ Glib::RefPtr<Action> action;
+ char* action_name = 0;
+
+ switch (engine->frames_per_cycle()) {
+ case 32:
+ action_name = X_("JACKLatency32");
+ break;
+ case 64:
+ action_name = X_("JACKLatency64");
+ break;
+ case 128:
+ action_name = X_("JACKLatency128");
+ break;
+ case 512:
+ action_name = X_("JACKLatency512");
+ break;
+ case 1024:
+ action_name = X_("JACKLatency1024");
+ break;
+ case 2048:
+ action_name = X_("JACKLatency2048");
+ break;
+ case 4096:
+ action_name = X_("JACKLatency4096");
+ break;
+ case 8192:
+ action_name = X_("JACKLatency8192");
+ break;
+ default:
+ /* XXX can we do anything useful ? */
+ break;
+ }
+
+ if (action_name) {
+
+ action = ActionManager::get_action (X_("JACK"), action_name);
+
+ if (action) {
+ Glib::RefPtr<RadioAction> ract = Glib::RefPtr<RadioAction>::cast_dynamic (action);
+ ract->set_active ();
+ }
+ }
}
void
engine->start();
}
- catch (AudioEngine::PortRegistrationFailure& err) {
- engine->stop ();
- error << _("Unable to create all required ports")
- << endmsg;
- unload_session ();
- return -1;
- }
-
catch (...) {
engine->stop ();
error << _("Unable to start the session running")
*/
session->save_state ("");
}
-
- /* there is too much going on, in too many threads, for us to
- end up with a clean session. So wait 1 second after loading,
- and fix it up. its ugly, but until i come across a better
- solution, its what we have.
- */
-
- Glib::signal_timeout().connect (mem_fun(*this, &ARDOUR_UI::make_session_clean), 1000);
}
return FALSE;
void
ARDOUR_UI::update_clocks ()
{
- Clock (session->audible_frame()); /* EMIT_SIGNAL */
+ if (!editor || !editor->dragging_playhead()) {
+ Clock (session->audible_frame()); /* EMIT_SIGNAL */
+ }
}
void
{
ArdourPrompter prompter (true);
string snapname;
- string now;
+ char timebuf[128];
time_t n;
+ struct tm local_time;
time (&n);
- now = ctime (&n);
- now = now.substr (20, 4) + now.substr (3, 16) + " (" + now.substr (0, 3) + ")";
+ localtime_r (&n, &local_time);
+ strftime (timebuf, sizeof(timebuf), "%FT%T", &local_time);
prompter.set_name ("Prompter");
prompter.add_button (Gtk::Stock::SAVE, Gtk::RESPONSE_ACCEPT);
- prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
prompter.set_prompt (_("Name of New Snapshot"));
- prompter.set_initial_text (now);
+ prompter.set_initial_text (timebuf);
switch (prompter.run()) {
case RESPONSE_ACCEPT:
prompter.set_prompt (_("Name for mix template:"));
prompter.set_initial_text(session->name() + _("-template"));
prompter.add_button (Gtk::Stock::SAVE, Gtk::RESPONSE_ACCEPT);
- prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
switch (prompter.run()) {
case RESPONSE_ACCEPT:
}
void
-ARDOUR_UI::new_session (bool startup, std::string predetermined_path)
+ARDOUR_UI::new_session (std::string predetermined_path)
{
string session_name;
string session_path;
int response = Gtk::RESPONSE_NONE;
new_session_dialog->set_modal(true);
- new_session_dialog->set_name(predetermined_path);
+ new_session_dialog->set_name (predetermined_path);
new_session_dialog->reset_recent();
new_session_dialog->show();
do {
response = new_session_dialog->run ();
+
+ _session_is_new = false;
- if(response == Gtk::RESPONSE_CANCEL || response == Gtk::RESPONSE_DELETE_EVENT) {
- quit();
+ if (response == Gtk::RESPONSE_CANCEL || response == Gtk::RESPONSE_DELETE_EVENT) {
+
+ if (!session) {
+ quit();
+ }
+ new_session_dialog->hide ();
return;
} else if (response == Gtk::RESPONSE_NONE) {
+
/* Clear was pressed */
new_session_dialog->reset();
} else {
- _session_is_new = true;
-
if (session_name.empty()) {
response = Gtk::RESPONSE_NONE;
continue;
} else {
- std::string session_path = new_session_dialog->session_folder();
-
+ session_path = new_session_dialog->session_folder();
+
}
-
+
//XXX This is needed because session constructor wants a
//non-existant path. hopefully this will be fixed at some point.
session_path = Glib::build_filename (session_path, session_name);
+ if (g_file_test (session_path.c_str(), GFileTest (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))) {
+
+ Glib::ustring str = string_compose (_("This session\n%1\nalready exists. Do you want to open it?"), session_path);
+
+ MessageDialog msg (str,
+ false,
+ Gtk::MESSAGE_WARNING,
+ Gtk::BUTTONS_YES_NO,
+ true);
+
+
+ msg.set_name (X_("CleanupDialog"));
+ msg.set_wmclass (X_("existing_session"), "Ardour");
+ msg.set_position (Gtk::WIN_POS_MOUSE);
+
+ switch (msg.run()) {
+ case RESPONSE_YES:
+ load_session (session_path, session_name);
+ goto done;
+ break;
+ default:
+ response = RESPONSE_NONE;
+ new_session_dialog->reset ();
+ continue;
+ }
+ }
+
+ _session_is_new = true;
+
std::string template_name = new_session_dialog->session_template_name();
if (new_session_dialog->use_session_template()) {
} while (response == Gtk::RESPONSE_NONE);
+ done:
show();
new_session_dialog->get_window()->set_cursor();
new_session_dialog->hide();
Session *new_session;
int x;
session_loaded = false;
+
x = unload_session ();
if (x < 0) {
/* if it already exists, we must have write access */
if (::access (path.c_str(), F_OK) == 0 && ::access (path.c_str(), W_OK)) {
- MessageDialog msg (*editor, _("\
-You do not have write access to this session.\n\
-This prevents the session from being loaded."));
+ MessageDialog msg (*editor, _("You do not have write access to this session.\n"
+ "This prevents the session from being loaded."));
msg.run ();
return -1;
}
Config->set_current_owner (ConfigVariableBase::Interface);
session_loaded = true;
- return 0;
-}
+
+ goto_editor_window ();
-int
-ARDOUR_UI::make_session_clean ()
-{
if (session) {
session->set_clean ();
}
- show ();
-
- return FALSE;
+ return 0;
}
int
shown_flag = true;
}
-
- if (about) {
- about->present ();
- }
}
void
{
if (about == 0) {
about = new About();
+ about->signal_response().connect(mem_fun (*this, &ARDOUR_UI::about_signal_response) );
}
about->present();
+ flush_pending ();
+}
+
+void
+ARDOUR_UI::about_signal_response(int response)
+{
+ hide_splash();
}
void
_("No audio files were ready for cleanup"),
true,
Gtk::MESSAGE_INFO,
- (Gtk::ButtonsType)(Gtk::BUTTONS_CLOSE) );
+ (Gtk::ButtonsType)(Gtk::BUTTONS_OK) );
msgd.set_secondary_text (_("If this seems suprising, \n\
check for any existing snapshots.\n\
These may still include regions that\n\
checker.set_default_response (RESPONSE_CANCEL);
checker.set_name (_("CleanupDialog"));
- checker.set_wmclass (_("ardour_cleanup"), "Ardour");
+ checker.set_wmclass (X_("ardour_cleanup"), "Ardour");
checker.set_position (Gtk::WIN_POS_MOUSE);
switch (checker.run()) {
msg.run ();
}
-void
-ARDOUR_UI::delete_sources_in_the_right_thread (list<boost::shared_ptr<ARDOUR::Source> >* deletion_list)
-{
- ENSURE_GUI_THREAD (bind (mem_fun(*this, &ARDOUR_UI::delete_sources_in_the_right_thread), deletion_list));
-
- for (list<boost::shared_ptr<Source> >::iterator i = deletion_list->begin(); i != deletion_list->end(); ++i) {
- (*i)->drop_references ();
- }
-
- delete deletion_list;
-}
-
void
ARDOUR_UI::disk_overrun_handler ()
{
}
}
-void
-ARDOUR_UI::set_jack_buffer_size (nframes_t nframes)
-{
- engine->request_buffer_size (nframes);
- update_sample_rate (0);
-}
-
int
ARDOUR_UI::cmdline_new_session (string path)
{
path = str;
}
- new_session (false, path);
+ new_session (path);
_will_create_new_session_automatically = false; /* done it */
return FALSE; /* don't call it again */
ract->set_active ();
}
}
+
+void
+ARDOUR_UI::update_transport_clocks (nframes_t pos)
+{
+ primary_clock.set (pos);
+ secondary_clock.set (pos);
+
+ if (big_clock_window) {
+ big_clock.set (pos);
+ }
+}
+
+void
+ARDOUR_UI::record_state_changed ()
+{
+ ENSURE_GUI_THREAD (mem_fun (*this, &ARDOUR_UI::record_state_changed));
+
+ if (!session || !big_clock_window) {
+ /* why bother - the clock isn't visible */
+ return;
+ }
+
+ switch (session->record_status()) {
+ case Session::Recording:
+ big_clock.set_widget_name ("BigClockRecording");
+ break;
+ default:
+ big_clock.set_widget_name ("BigClockNonRecording");
+ break;
+ }
+}
+
+void
+ARDOUR_UI::set_keybindings_path (string path)
+{
+ keybindings_path = path;
+}
+
+void
+ARDOUR_UI::save_keybindings ()
+{
+ if (can_save_keybindings) {
+ AccelMap::save (keybindings_path);
+ }
+}
+
+bool
+ARDOUR_UI::first_idle ()
+{
+ can_save_keybindings = true;
+ return false;
+}
+
+void
+ARDOUR_UI::store_clock_modes ()
+{
+ XMLNode* node = new XMLNode(X_("ClockModes"));
+
+ for (vector<AudioClock*>::iterator x = AudioClock::clocks.begin(); x != AudioClock::clocks.end(); ++x) {
+ node->add_property ((*x)->name().c_str(), enum_2_string ((*x)->mode()));
+ }
+
+ session->add_extra_xml (*node);
+ session->set_dirty ();
+}
+
+
+