X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=gtk2_ardour%2Fardour_ui.cc;h=2d4e94883494a1bf291d578e1b5bb2bbd489e667;hb=cc15fb071f3c327f17bbe1b0613974d5060b7f90;hp=d25e65030e41b7943c12aead9725084b61569f7f;hpb=11190e19e27538fb9354812bb5584f699d657c55;p=ardour.git diff --git a/gtk2_ardour/ardour_ui.cc b/gtk2_ardour/ardour_ui.cc index d25e65030e..2d4e948834 100644 --- a/gtk2_ardour/ardour_ui.cc +++ b/gtk2_ardour/ardour_ui.cc @@ -22,8 +22,10 @@ #include #include #include +#include #include #include +#include #include @@ -35,7 +37,6 @@ #include #include #include -#include #include #include #include @@ -58,12 +59,15 @@ #include #include #include +#include #include #include #include #include #include +typedef uint64_t microseconds_t; + #include "actions.h" #include "ardour_ui.h" #include "public_editor.h" @@ -75,10 +79,14 @@ #include "add_route_dialog.h" #include "new_session_dialog.h" #include "about.h" +#include "splash.h" +#include "nag.h" #include "utils.h" #include "gui_thread.h" #include "theme_manager.h" #include "engine_dialog.h" +#include "gain_meter.h" +#include "route_time_axis.h" #include "i18n.h" @@ -105,10 +113,6 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[]) preroll_clock (X_("preroll"), false, X_("PreRollClock"), true, true), postroll_clock (X_("postroll"), false, X_("PostRollClock"), true, true), - /* adjuster table */ - - adjuster_table (3, 3), - /* preroll stuff */ preroll_button (_("pre\nroll")), @@ -130,13 +134,13 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[]) shuttle_controllable ("shuttle", *this, TransportControllable::ShuttleControl), shuttle_controller_binding_proxy (shuttle_controllable), - roll_button (roll_controllable), - stop_button (stop_controllable), - goto_start_button (goto_start_controllable), - goto_end_button (goto_end_controllable), - auto_loop_button (auto_loop_controllable), - play_selection_button (play_selection_controllable), - rec_button (rec_controllable), + roll_button (&roll_controllable), + stop_button (&stop_controllable), + goto_start_button (&goto_start_controllable), + goto_end_button (&goto_end_controllable), + auto_loop_button (&auto_loop_controllable), + play_selection_button (&play_selection_controllable), + rec_button (&rec_controllable), shuttle_units_button (_("% ")), @@ -161,11 +165,13 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[]) _auto_display_errors = false; #endif - if (getenv ("ARDOUR_DEBUG_UPDATES")) { - gdk_window_set_debug_updates (true); - } - about = 0; + splash = 0; + + if (ARDOUR_COMMAND_LINE::session_name.length()) { + /* only show this if we're not going to post the new session dialog */ + show_splash (); + } if (theArdourUI == 0) { theArdourUI = this; @@ -194,19 +200,19 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[]) have_disk_speed_dialog_displayed = false; _will_create_new_session_automatically = false; session_loaded = false; - loading_dialog = 0; last_speed_displayed = -1.0f; + ignore_dual_punch = false; + _mixer_on_top = false; - keybindings_path = ARDOUR::find_config_file ("ardour.bindings"); - /* all changes go to the user directory */ - user_keybindings_path = get_user_ardour_path (); - user_keybindings_path += '/'; - user_keybindings_path += "ardour.bindings"; - - can_save_keybindings = false; + roll_button.unset_flags (Gtk::CAN_FOCUS); + stop_button.unset_flags (Gtk::CAN_FOCUS); + goto_start_button.unset_flags (Gtk::CAN_FOCUS); + goto_end_button.unset_flags (Gtk::CAN_FOCUS); + auto_loop_button.unset_flags (Gtk::CAN_FOCUS); + play_selection_button.unset_flags (Gtk::CAN_FOCUS); + rec_button.unset_flags (Gtk::CAN_FOCUS); - last_configure_time.tv_sec = 0; - last_configure_time.tv_usec = 0; + last_configure_time= 0; shuttle_grabbed = false; shuttle_fract = 0.0; @@ -215,39 +221,57 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[]) shuttle_style_menu = 0; shuttle_unit_menu = 0; - gettimeofday (&last_peak_grab, 0); - gettimeofday (&last_shuttle_request, 0); + // We do not have jack linked in yet so; + + last_shuttle_request = last_peak_grab = 0; // get_microseconds(); ARDOUR::Diskstream::DiskOverrun.connect (mem_fun(*this, &ARDOUR_UI::disk_overrun_handler)); ARDOUR::Diskstream::DiskUnderrun.connect (mem_fun(*this, &ARDOUR_UI::disk_underrun_handler)); + ARDOUR::Plugin::PresetFileExists.connect (mem_fun(*this, &ARDOUR_UI::preset_file_exists_handler)); + + /* handle dialog requests */ + + ARDOUR::Session::Dialog.connect (mem_fun(*this, &ARDOUR_UI::session_dialog)); + /* handle pending state with a dialog */ ARDOUR::Session::AskAboutPendingState.connect (mem_fun(*this, &ARDOUR_UI::pending_state_dialog)); + /* handle sr mismatch with a dialog */ + + ARDOUR::Session::AskAboutSampleRateMismatch.connect (mem_fun(*this, &ARDOUR_UI::sr_mismatch_dialog)); + /* lets get this party started */ try { - ARDOUR::init (ARDOUR_COMMAND_LINE::use_vst, ARDOUR_COMMAND_LINE::try_hw_optimization); + if (ARDOUR::init (ARDOUR_COMMAND_LINE::use_vst, ARDOUR_COMMAND_LINE::try_hw_optimization)) { + throw failed_constructor (); + } + setup_gtk_ardour_enums (); Config->set_current_owner (ConfigVariableBase::Interface); setup_profile (); + GainMeter::setup_slider_pix (); + RouteTimeAxisView::setup_slider_pix (); + } catch (failed_constructor& err) { error << _("could not initialize Ardour.") << endmsg; // pass it on up - throw err; + throw; } /* we like keyboards */ keyboard = new Keyboard; + reset_dpi(); + starting.connect (mem_fun(*this, &ARDOUR_UI::startup)); stopping.connect (mem_fun(*this, &ARDOUR_UI::shutdown)); platform_setup (); - } int @@ -259,6 +283,8 @@ ARDOUR_UI::create_engine () return 0; } + loading_message (_("Starting audio engine")); + try { engine = new ARDOUR::AudioEngine (ARDOUR_COMMAND_LINE::jack_client_name); @@ -324,8 +350,13 @@ ARDOUR_UI::post_engine () /* set default clock modes */ - primary_clock.set_mode (AudioClock::SMPTE); - secondary_clock.set_mode (AudioClock::BBT); + if (Profile->get_sae()) { + primary_clock.set_mode (AudioClock::BBT); + secondary_clock.set_mode (AudioClock::MinSec); + } else { + primary_clock.set_mode (AudioClock::SMPTE); + secondary_clock.set_mode (AudioClock::BBT); + } /* start the time-of-day-clock */ @@ -378,24 +409,27 @@ ARDOUR_UI::~ARDOUR_UI () } } +void +ARDOUR_UI::pop_back_splash () +{ + if (Splash::instance()) { + // Splash::instance()->pop_back(); + Splash::instance()->hide (); + } +} + gint ARDOUR_UI::configure_timeout () { - struct timeval now; - struct timeval diff; - - if (last_configure_time.tv_sec == 0 && last_configure_time.tv_usec == 0) { + if (last_configure_time == 0) { /* no configure events yet */ return TRUE; } - gettimeofday (&now, 0); - timersub (&now, &last_configure_time, &diff); - /* force a gap of 0.5 seconds since the last configure event */ - if (diff.tv_sec == 0 && diff.tv_usec < 500000) { + if (get_microseconds() - last_configure_time < 500000) { return TRUE; } else { have_configure_timeout = false; @@ -408,7 +442,7 @@ gboolean ARDOUR_UI::configure_handler (GdkEventConfigure* conf) { if (have_configure_timeout) { - gettimeofday (&last_configure_time, 0); + last_configure_time = get_microseconds(); } else { Glib::signal_timeout().connect (mem_fun(*this, &ARDOUR_UI::configure_timeout), 100); have_configure_timeout = true; @@ -507,12 +541,20 @@ ARDOUR_UI::save_ardour_state () Config->add_instant_xml (mnode, get_user_ardour_path()); } - save_keybindings (); + Keyboard::save_keybindings (); } gint ARDOUR_UI::autosave_session () { + if (g_main_depth() > 1) { + /* inside a recursive main loop, + give up because we may not be able to + take a lock. + */ + return 1; + } + if (!Config->get_periodic_safety_backups()) return 1; @@ -528,7 +570,7 @@ ARDOUR_UI::update_autosave () { ENSURE_GUI_THREAD (mem_fun (*this, &ARDOUR_UI::update_autosave)); - if (session->dirty()) { + if (session && session->dirty()) { if (_autosave_connection.connected()) { _autosave_connection.disconnect(); } @@ -589,10 +631,7 @@ Please consider the possibilities, and perhaps (re)start JACK.")); win.show_all (); win.set_position (Gtk::WIN_POS_CENTER); - - if (!ARDOUR_COMMAND_LINE::no_splash) { - hide_splash (); - } + pop_back_splash (); /* we just don't care about the result, but we want to block */ @@ -603,64 +642,21 @@ void ARDOUR_UI::startup () { string name, path; - bool isnew; - - new_session_dialog = new NewSessionDialog(); - - /* If no session name is given: we're not loading a session yet, nor creating a new one */ - if (ARDOUR_COMMAND_LINE::session_name.length()) { - - /* Load session or start the new session dialog */ - - if (Session::find_session (ARDOUR_COMMAND_LINE::session_name, path, name, isnew)) { - - MessageDialog msg (string_compose(_("Could not find command line session \"%1\""), - ARDOUR_COMMAND_LINE::session_name), - true, - Gtk::MESSAGE_ERROR, - Gtk::BUTTONS_OK); - - msg.set_position (Gtk::WIN_POS_MOUSE); - msg.present (); - msg.run (); - - exit (1); - } - - if (!ARDOUR_COMMAND_LINE::new_session) { - - /* Supposed to be loading an existing session, but the session doesn't exist */ - - if (isnew) { - MessageDialog msg (string_compose (_("\n\nNo session named \"%1\" exists.\n" - "To create it from the command line, start ardour as:\n ardour --new %1"), path), - true, - Gtk::MESSAGE_ERROR, - Gtk::BUTTONS_OK); - - msg.set_position (Gtk::WIN_POS_MOUSE); - msg.present (); - msg.run (); - - exit (1); - } - } - } - - hide_splash (); + new_session_dialog = new NewSessionDialog(); - bool have_backend = EngineControl::engine_running(); + bool backend_audio_is_running = EngineControl::engine_running(); XMLNode* audio_setup = Config->extra_xml ("AudioSetup"); if (audio_setup) { new_session_dialog->engine_control.set_state (*audio_setup); } - if (!get_session_parameters (ARDOUR_COMMAND_LINE::session_name, have_backend, ARDOUR_COMMAND_LINE::new_session)) { + if (!get_session_parameters (backend_audio_is_running, ARDOUR_COMMAND_LINE::new_session)) { return; } + BootMessage (_("Ardour is ready for use")); show (); } @@ -717,6 +713,8 @@ ARDOUR_UI::check_memory_locking () hbox.pack_start (cb, true, false); vbox->pack_start (hbox); hbox.show_all (); + + pop_back_splash (); msg.run (); } @@ -729,32 +727,41 @@ ARDOUR_UI::check_memory_locking () void ARDOUR_UI::finish() { - if (session && session->dirty()) { - switch (ask_about_saving_session(_("quit"))) { - case -1: - return; - break; - case 1: - /* use the default name */ - if (save_state_canfail ("")) { - /* failed - don't quit */ - MessageDialog msg (*editor, - _("\ + if (session) { + + if (session->transport_rolling()) { + session->request_stop (); + usleep (2500000); + } + + if (session->dirty()) { + switch (ask_about_saving_session(_("quit"))) { + case -1: + return; + break; + case 1: + /* use the default name */ + if (save_state_canfail ("")) { + /* failed - don't quit */ + MessageDialog msg (*editor, + _("\ Ardour was unable to save your session.\n\n\ If you still wish to quit, please use the\n\n\ \"Just quit\" option.")); - msg.run (); - return; + pop_back_splash(); + msg.run (); + return; + } + break; + case 0: + break; } - break; - case 0: - break; } - } - - if (session) { + session->set_deletion_in_progress (); } + + ArdourDialog::close_all_dialogs (); engine->stop (true); save_ardour_state (); quit (); @@ -810,8 +817,6 @@ ARDOUR_UI::ask_about_saving_session (const string & what) window.set_resizable (false); window.show_all (); - save_the_session = 0; - window.set_keep_above (true); window.present (); @@ -898,8 +903,14 @@ void ARDOUR_UI::update_buffer_load () { char buf[64]; + uint32_t c, p; if (session) { + c = session->capture_load (); + p = session->playback_load (); + + push_buffer_stats (c, p); + snprintf (buf, sizeof (buf), _("Buffers p:%" PRIu32 "%% c:%" PRIu32 "%%"), session->playback_load(), session->capture_load()); buffer_load_label.set_text (buf); @@ -1015,6 +1026,13 @@ ARDOUR_UI::redisplay_recent_sessions () fullpath = fullpath.substr (0, fullpath.length()-1); } + /* check whether session still exists */ + if (!Glib::file_test(fullpath.c_str(), Glib::FILE_TEST_EXISTS)) { + /* session doesn't exist */ + cerr << "skipping non-existent session " << fullpath << endl; + continue; + } + /* now get available states for this session */ if ((states = Session::possible_states (fullpath)) == 0) { @@ -1063,8 +1081,7 @@ ARDOUR_UI::build_session_selector () recent_session_display.set_model (recent_session_model); recent_session_display.append_column (_("Recent Sessions"), recent_session_columns.visible_name); recent_session_display.set_headers_visible (false); - recent_session_display.get_selection()->set_mode (SELECTION_SINGLE); - + recent_session_display.get_selection()->set_mode (SELECTION_BROWSE); recent_session_display.signal_row_activated().connect (mem_fun (*this, &ARDOUR_UI::recent_session_row_activated)); scroller->add (recent_session_display); @@ -1085,64 +1102,55 @@ ARDOUR_UI::recent_session_row_activated (const TreePath& path, TreeViewColumn* c void ARDOUR_UI::open_recent_session () { - /* popup selector window */ + bool can_return = (session != 0); if (session_selector_window == 0) { build_session_selector (); } - - redisplay_recent_sessions (); - - ResponseType r = (ResponseType) session_selector_window->run (); - - session_selector_window->hide(); - - switch (r) { - case RESPONSE_ACCEPT: - break; - default: - return; - } - - Gtk::TreeModel::iterator i = recent_session_display.get_selection()->get_selected(); - - if (i == recent_session_model->children().end()) { - return; - } - Glib::ustring path = (*i)[recent_session_columns.fullpath]; - Glib::ustring state = (*i)[recent_session_columns.visible_name]; - - _session_is_new = false; + redisplay_recent_sessions (); - load_session (path, state); -} + while (true) { + + session_selector_window->set_position (WIN_POS_MOUSE); -bool -ARDOUR_UI::filter_ardour_session_dirs (const FileFilter::Info& info) -{ - struct stat statbuf; + ResponseType r = (ResponseType) session_selector_window->run (); + + switch (r) { + case RESPONSE_ACCEPT: + break; + default: + if (can_return) { + session_selector_window->hide(); + return; + } else { + exit (1); + } + } - if (stat (info.filename.c_str(), &statbuf) != 0) { - return false; - } + if (recent_session_display.get_selection()->count_selected_rows() == 0) { + continue; + } + + session_selector_window->hide(); - if (!S_ISDIR(statbuf.st_mode)) { - return false; - } + Gtk::TreeModel::iterator i = recent_session_display.get_selection()->get_selected(); + + if (i == recent_session_model->children().end()) { + return; + } + + Glib::ustring path = (*i)[recent_session_columns.fullpath]; + Glib::ustring state = (*i)[recent_session_columns.visible_name]; + + _session_is_new = false; + + if (load_session (path, state) == 0) { + break; + } - // XXX Portability - - string session_file = info.filename; - session_file += '/'; - session_file += Glib::path_get_basename (info.filename); - session_file += ".ardour"; - - if (stat (session_file.c_str(), &statbuf) != 0) { - return false; + can_return = false; } - - return S_ISREG (statbuf.st_mode); } bool @@ -1152,6 +1160,8 @@ ARDOUR_UI::check_audioengine () if (!engine->connected()) { MessageDialog msg (_("Ardour is not connected to JACK\n" "You cannot open or close sessions in this condition")); + pop_back_splash (); + msg.set_position (WIN_POS_CENTER); msg.run (); return false; } @@ -1174,7 +1184,7 @@ ARDOUR_UI::open_session () /* ardour sessions are folders */ - open_session_selector = new Gtk::FileChooserDialog (_("open session"), FILE_CHOOSER_ACTION_OPEN); + open_session_selector = new Gtk::FileChooserDialog (_("Open Session"), FILE_CHOOSER_ACTION_OPEN); open_session_selector->add_button (Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); open_session_selector->add_button (Gtk::Stock::OPEN, Gtk::RESPONSE_ACCEPT); open_session_selector->set_default_response(Gtk::RESPONSE_ACCEPT); @@ -1268,12 +1278,12 @@ ARDOUR_UI::session_add_audio_route (bool track, int32_t input_channels, int32_t } catch (...) { - cerr << "About to complain about JACK\n"; MessageDialog msg (*editor, _("There are insufficient JACK ports available\n\ to create a new track or bus.\n\ You should save Ardour, exit and\n\ restart JACK with more ports.")); + pop_back_splash (); msg.run (); } } @@ -1298,7 +1308,7 @@ ARDOUR_UI::do_transport_locate (nframes_t new_position) } void -ARDOUR_UI::transport_goto_start () +ARDOUR_UI::transport_goto_start () { if (session) { session->goto_start(); @@ -1331,6 +1341,34 @@ ARDOUR_UI::transport_goto_zero () } } +void +ARDOUR_UI::transport_goto_wallclock () +{ + if (session && editor) { + + time_t now; + struct tm tmnow; + nframes64_t frames; + + time (&now); + localtime_r (&now, &tmnow); + + frames = tmnow.tm_hour * (60 * 60 * session->frame_rate()); + frames += tmnow.tm_min * (60 * session->frame_rate()); + frames += tmnow.tm_sec * session->frame_rate(); + + session->request_locate (frames); + + /* force displayed area in editor to start no matter + what "follow playhead" setting is. + */ + + if (editor) { + editor->reset_x_origin (frames - (editor->current_page_frames()/2)); + } + } +} + void ARDOUR_UI::transport_goto_end () { @@ -1386,6 +1424,7 @@ ARDOUR_UI::remove_last_capture() void ARDOUR_UI::transport_record (bool roll) { + if (session) { switch (session->record_status()) { case Session::Disabled: @@ -1411,6 +1450,7 @@ ARDOUR_UI::transport_record (bool roll) session->disable_record (false, true); } } + //cerr << "ARDOUR_UI::transport_record () called roll = " << roll << " session->record_status() = " << session->record_status() << endl; } void @@ -1424,6 +1464,8 @@ ARDOUR_UI::transport_roll () rolling = session->transport_rolling (); + //cerr << "ARDOUR_UI::transport_roll () called session->record_status() = " << session->record_status() << endl; + if (session->get_play_loop()) { session->request_play_loop (false); auto_loop_button.set_visual_state (1); @@ -1591,7 +1633,7 @@ ARDOUR_UI::engine_running () ActionManager::set_sensitive (ActionManager::jack_opposite_sensitive_actions, false); Glib::RefPtr action; - char* action_name = 0; + const char* action_name = 0; switch (engine->frames_per_cycle()) { case 32: @@ -1648,8 +1690,9 @@ ARDOUR_UI::engine_halted () _("\ JACK has either been shutdown or it\n\ disconnected Ardour because Ardour\n\ -was not fast enough. You can save the\n\ -session and/or try to reconnect to JACK .")); +was not fast enough. Try to restart\n\ +JACK, reconnect and save the session.")); + pop_back_splash (); msg.run (); } @@ -1807,13 +1850,27 @@ ARDOUR_UI::snapshot_session () prompter.set_name ("Prompter"); prompter.add_button (Gtk::Stock::SAVE, Gtk::RESPONSE_ACCEPT); + prompter.set_title (_("Take Snapshot")); prompter.set_prompt (_("Name of New Snapshot")); prompter.set_initial_text (timebuf); - + + again: switch (prompter.run()) { case RESPONSE_ACCEPT: prompter.get_result (snapname); if (snapname.length()){ + if (snapname.find ('/') != string::npos) { + MessageDialog msg (_("To ensure compatibility with various systems\n" + "snapshot names may not contain a '/' character")); + msg.run (); + goto again; + } + if (snapname.find ('\\') != string::npos) { + MessageDialog msg (_("To ensure compatibility with various systems\n" + "snapshot names may not contain a '\\' character")); + msg.run (); + goto again; + } save_state (snapname); } break; @@ -1847,17 +1904,6 @@ ARDOUR_UI::save_state_canfail (string name) return 0; } -void -ARDOUR_UI::restore_state (string name) -{ - if (session) { - if (name.length() == 0) { - name = session->name(); - } - session->restore_state (name); - } -} - void ARDOUR_UI::primary_clock_value_changed () { @@ -1882,37 +1928,6 @@ ARDOUR_UI::secondary_clock_value_changed () } } -void -ARDOUR_UI::rec_enable_button_blink (bool onoff, AudioDiskstream *dstream, Widget *w) -{ - if (session && dstream && dstream->record_enabled()) { - - Session::RecordState rs; - - rs = session->record_status (); - - switch (rs) { - case Session::Disabled: - case Session::Enabled: - if (w->get_state() != STATE_SELECTED) { - w->set_state (STATE_SELECTED); - } - break; - - case Session::Recording: - if (w->get_state() != STATE_ACTIVE) { - w->set_state (STATE_ACTIVE); - } - break; - } - - } else { - if (w->get_state() != STATE_NORMAL) { - w->set_state (STATE_NORMAL); - } - } -} - void ARDOUR_UI::transport_rec_enable_blink (bool onoff) { @@ -1939,17 +1954,8 @@ ARDOUR_UI::transport_rec_enable_blink (bool onoff) } } -gint -ARDOUR_UI::hide_and_quit (GdkEventAny *ev, ArdourDialog *window) -{ - window->hide(); - Gtk::Main::quit (); - return TRUE; -} - void ARDOUR_UI::save_template () - { ArdourPrompter prompter (true); string name; @@ -1959,6 +1965,7 @@ ARDOUR_UI::save_template () } prompter.set_name (X_("Prompter")); + prompter.set_title (_("Save Mix Template")); prompter.set_prompt (_("Name for mix template:")); prompter.set_initial_text(session->name() + _("-template")); prompter.add_button (Gtk::Stock::SAVE, Gtk::RESPONSE_ACCEPT); @@ -1980,6 +1987,9 @@ ARDOUR_UI::save_template () void ARDOUR_UI::fontconfig_dialog () { +#if 0 + /* this issue seems to have gone away with changes to font handling in GTK/Quartz + */ #ifdef GTKOSX /* X11 users will always have fontconfig info around, but new GTK-OSX users may not and it can take a while to build it. Warn them. @@ -1997,405 +2007,452 @@ ARDOUR_UI::fontconfig_dialog () true, Gtk::MESSAGE_INFO, Gtk::BUTTONS_OK); + pop_back_splash (); msg.show_all (); msg.present (); msg.run (); } #endif +#endif } -bool -ARDOUR_UI::get_session_parameters (Glib::ustring predetermined_path, bool have_engine, bool should_be_new) +void +ARDOUR_UI::parse_cmdline_path (const Glib::ustring& cmdline_path, Glib::ustring& session_name, Glib::ustring& session_path, bool& existing_session) { - bool existing_session = false; - Glib::ustring session_name; - Glib::ustring session_path; - Glib::ustring template_name; + existing_session = false; - if (!loading_dialog) { - loading_dialog = new MessageDialog (*new_session_dialog, - "", - false, - Gtk::MESSAGE_INFO, - Gtk::BUTTONS_NONE); + if (Glib::file_test (cmdline_path, Glib::FILE_TEST_IS_DIR)) { + session_path = cmdline_path; + existing_session = true; + } else if (Glib::file_test (cmdline_path, Glib::FILE_TEST_IS_REGULAR)) { + session_path = Glib::path_get_dirname (string (cmdline_path)); + existing_session = true; + } else { + /* it doesn't exist, assume the best */ + session_path = Glib::path_get_dirname (string (cmdline_path)); } + + session_name = basename_nosuffix (string (cmdline_path)); +} +int +ARDOUR_UI::load_cmdline_session (const Glib::ustring& session_name, const Glib::ustring& session_path, bool& existing_session) +{ + /* when this is called, the backend audio system must be running */ - cerr << "PDP = " << predetermined_path << endl; - - int response = Gtk::RESPONSE_NONE; - - if (predetermined_path.length()) { - - /* before we start, lets see if the given path looks like - an existing ardour session. if it does, skip the - tabs that we don't need - */ - - if (Glib::file_test (predetermined_path, Glib::FILE_TEST_IS_DIR)) { - session_path = predetermined_path; - } else { - session_path = Glib::path_get_dirname (string (predetermined_path)); - } + /* the main idea here is to deal with the fact that a cmdline argument for the session + can be interpreted in different ways - it could be a directory or a file, and before + we load, we need to know both the session directory and the snapshot (statefile) within it + that we are supposed to use. + */ - session_name = basename_nosuffix (string (predetermined_path)); + if (session_name.length() == 0 || session_path.length() == 0) { + return false; + } + + if (Glib::file_test (session_path, Glib::FILE_TEST_IS_DIR)) { - if (session_name.length() == 0 || session_path.length() == 0) { - error << string_compose (_("Ardour cannot understand \"%1\" as a session name"), predetermined_path) << endmsg; - return false; + Glib::ustring predicted_session_file; + + predicted_session_file = session_path; + predicted_session_file += '/'; + predicted_session_file += session_name; + predicted_session_file += Session::statefile_suffix(); + + if (Glib::file_test (predicted_session_file, Glib::FILE_TEST_EXISTS)) { + existing_session = true; } - - new_session_dialog->set_session_name (session_name); - new_session_dialog->set_session_folder (session_path); - - cerr << "Set name to " << session_name << " and dir to " << session_path << endl; - - if (Glib::file_test (predetermined_path, Glib::FILE_TEST_IS_DIR)) { - Glib::ustring predicted_session_file; - - predicted_session_file = predetermined_path; - predicted_session_file += '/'; - predicted_session_file += session_name; - predicted_session_file += Session::statefile_suffix(); - - if (Glib::file_test (predicted_session_file, Glib::FILE_TEST_EXISTS)) { - existing_session = true; - } - - } else if (Glib::file_test (predetermined_path, Glib::FILE_TEST_EXISTS)) { - - if (predetermined_path.find (Session::statefile_suffix()) == predetermined_path.length() - 7) { - /* existing .ardour file */ - existing_session = true; - } + + } else if (Glib::file_test (session_path, Glib::FILE_TEST_EXISTS)) { + + if (session_path.find (Session::statefile_suffix()) == session_path.length() - 7) { + /* existing .ardour file */ + existing_session = true; } - new_session_dialog->set_modal (true); + } else { + existing_session = false; } - - if (existing_session && have_engine) { - /* lets just try to load it */ - - loading_dialog->set_message (_("Starting audio engine")); - loading_dialog->show_all (); - flush_pending (); - - if (create_engine ()) { - backend_audio_error (!have_engine, new_session_dialog); - loading_dialog->hide (); - return false; - } - - if (load_session (session_path, session_name) == 0) { - goto done; - } + + /* lets just try to load it */ + + if (create_engine ()) { + backend_audio_error (false, new_session_dialog); + return -1; } + + return load_session (session_path, session_name); +} - /* loading failed, or we need the NSD for something */ - - new_session_dialog->set_position (WIN_POS_CENTER); - new_session_dialog->set_current_page (0); - new_session_dialog->set_existing_session (existing_session); +bool +ARDOUR_UI::ask_about_loading_existing_session (const Glib::ustring& session_path) +{ + 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_title (_("Cleanup Unused Sources")); + msg.set_wmclass (X_("existing_session"), "Ardour"); + msg.set_position (Gtk::WIN_POS_MOUSE); + pop_back_splash (); + + switch (msg.run()) { + case RESPONSE_YES: + return true; + break; + } + return false; +} + +int +ARDOUR_UI::build_session_from_nsd (const Glib::ustring& session_path, const Glib::ustring& session_name) +{ + + uint32_t cchns; + uint32_t mchns; + AutoConnectOption iconnect; + AutoConnectOption oconnect; + uint32_t nphysin; + uint32_t nphysout; + + if (Profile->get_sae()) { + + cchns = 0; + mchns = 2; + iconnect = AutoConnectPhysical; + oconnect = AutoConnectMaster; + nphysin = 0; // use all available + nphysout = 0; // use all available + + } else { + + /* get settings from advanced section of NSD */ + + if (new_session_dialog->create_control_bus()) { + cchns = (uint32_t) new_session_dialog->control_channel_count(); + } else { + cchns = 0; + } + + if (new_session_dialog->create_master_bus()) { + mchns = (uint32_t) new_session_dialog->master_channel_count(); + } else { + mchns = 0; + } + + if (new_session_dialog->connect_inputs()) { + iconnect = AutoConnectPhysical; + } else { + iconnect = AutoConnectOption (0); + } + + /// @todo some minor tweaks. + + if (new_session_dialog->connect_outs_to_master()) { + oconnect = AutoConnectMaster; + } else if (new_session_dialog->connect_outs_to_physical()) { + oconnect = AutoConnectPhysical; + } else { + oconnect = AutoConnectOption (0); + } + + nphysin = (uint32_t) new_session_dialog->input_limit_count(); + nphysout = (uint32_t) new_session_dialog->output_limit_count(); + } + + if (build_session (session_path, + session_name, + cchns, + mchns, + iconnect, + oconnect, + nphysin, + nphysout, + engine->frame_rate() * 60 * 5)) { + + return -1; + } + + return 0; +} + +void +ARDOUR_UI::end_loading_messages () +{ + // hide_splash (); +} + +void +ARDOUR_UI::loading_message (const std::string& msg) +{ + show_splash (); + splash->message (msg); + flush_pending (); +} + +void +ARDOUR_UI::idle_load (const Glib::ustring& path) +{ + if (session) { + if (Glib::file_test (path, Glib::FILE_TEST_IS_DIR)) { + /* /path/to/foo => /path/to/foo, foo */ + load_session (path, basename_nosuffix (path)); + } else { + /* /path/to/foo/foo.ardour => /path/to/foo, foo */ + load_session (Glib::path_get_dirname (path), basename_nosuffix (path)); + } + } else { + + ARDOUR_COMMAND_LINE::session_name = path; + + if (new_session_dialog) { + + + /* make it break out of Dialog::run() and + start again. + */ + + new_session_dialog->response (1); + } + } +} + +bool +ARDOUR_UI::get_session_parameters (bool backend_audio_is_running, bool should_be_new) +{ + bool existing_session = false; + Glib::ustring session_name; + Glib::ustring session_path; + Glib::ustring template_name; + int response; + + begin: + response = Gtk::RESPONSE_NONE; + + if (!ARDOUR_COMMAND_LINE::session_name.empty()) { + + parse_cmdline_path (ARDOUR_COMMAND_LINE::session_name, session_name, session_path, existing_session); + + /* don't ever reuse this */ + + ARDOUR_COMMAND_LINE::session_name = string(); + + if (existing_session && backend_audio_is_running) { + + /* just load the thing already */ + + if (load_cmdline_session (session_name, session_path, existing_session) == 0) { + return true; + } + } + + /* make the NSD use whatever information we have */ + + new_session_dialog->set_session_name (session_name); + new_session_dialog->set_session_folder (session_path); + } + + /* loading failed, or we need the NSD for something */ + + new_session_dialog->set_modal (false); + new_session_dialog->set_position (WIN_POS_CENTER); + new_session_dialog->set_current_page (0); + new_session_dialog->set_existing_session (existing_session); new_session_dialog->reset_recent(); do { - new_session_dialog->set_have_engine (have_engine); + new_session_dialog->set_have_engine (backend_audio_is_running); new_session_dialog->present (); response = new_session_dialog->run (); - loading_dialog->hide (); _session_is_new = false; /* handle possible negative responses */ - if (response == Gtk::RESPONSE_CANCEL || response == Gtk::RESPONSE_DELETE_EVENT) { - + switch (response) { + case 1: + /* sent by idle_load, meaning restart the whole process again */ + new_session_dialog->hide(); + new_session_dialog->reset(); + goto begin; + break; + + case Gtk::RESPONSE_CANCEL: + case Gtk::RESPONSE_DELETE_EVENT: if (!session) { + if (engine && engine->running()) { + engine->stop (true); + } quit(); } new_session_dialog->hide (); return false; - } else if (response == Gtk::RESPONSE_NONE) { + case Gtk::RESPONSE_NONE: /* "Clear" was pressed */ - goto try_again; } fontconfig_dialog(); - /* if we're here to help set up audio parameters this is where want to do that. - */ - - if (!have_engine) { - if (new_session_dialog->engine_control.setup_engine ()) { - new_session_dialog->hide (); + if (!backend_audio_is_running) { + int ret = new_session_dialog->engine_control.setup_engine (); + if (ret < 0) { return false; - } + } else if (ret > 0) { + response = Gtk::RESPONSE_REJECT; + goto try_again; + } + + /* hide the NSD while we start up the engine */ - loading_dialog->set_message (_("Starting audio engine")); - loading_dialog->show_all (); + new_session_dialog->hide (); flush_pending (); } if (create_engine ()) { - backend_audio_error (!have_engine, new_session_dialog); - loading_dialog->hide (); + + backend_audio_error (!backend_audio_is_running, new_session_dialog); flush_pending (); - /* audio setup page */ + new_session_dialog->set_existing_session (false); - new_session_dialog->set_current_page (2); + new_session_dialog->set_current_page (0); // new engine page + new_session_dialog->engine_control.unset_interface_chosen (); + response = Gtk::RESPONSE_NONE; goto try_again; } - loading_dialog->hide (); - have_engine = true; + backend_audio_is_running = true; - /* now handle possible affirmative responses */ + if (response == Gtk::RESPONSE_OK) { - if (response == Gtk::RESPONSE_YES) { - - /* YES == OPEN from the session selector */ + session_name = new_session_dialog->session_name(); - session_name = new_session_dialog->session_name(); - if (session_name.empty()) { response = Gtk::RESPONSE_NONE; goto try_again; } + /* if the user mistakenly typed path information into the session filename entry, + convert what they typed into a path & a name + */ + if (session_name[0] == '/' || (session_name.length() > 2 && session_name[0] == '.' && session_name[1] == '/') || (session_name.length() > 3 && session_name[0] == '.' && session_name[1] == '.' && session_name[2] == '/')) { - if (load_session (Glib::path_get_dirname (session_name), session_name)) { - response = Gtk::RESPONSE_NONE; - goto try_again; - } + + session_path = Glib::path_get_dirname (session_name); + session_name = Glib::path_get_basename (session_name); + } else { + session_path = new_session_dialog->session_folder(); - if (load_session (session_path, session_name)) { - response = Gtk::RESPONSE_NONE; - goto try_again; - } - } - - } else if (response == Gtk::RESPONSE_OK) { - /* OK == OPEN button */ + } - session_name = new_session_dialog->session_name(); - - if (session_name.empty()) { - response = Gtk::RESPONSE_NONE; - goto try_again; - } + template_name = Glib::ustring(); + switch (new_session_dialog->which_page()) { - cerr << "nsd now on page " << new_session_dialog->get_current_page() << endl; - - switch (new_session_dialog->get_current_page()) { - case 1: /* recent session selector */ - case 2: /* audio engine control */ - - if (session_name[0] == '/' || - (session_name.length() > 2 && session_name[0] == '.' && session_name[1] == '/') || - (session_name.length() > 3 && session_name[0] == '.' && session_name[1] == '.' && session_name[2] == '/')) { - if (load_session (Glib::path_get_dirname (session_name), session_name)) { - response = Gtk::RESPONSE_NONE; - goto try_again; - } + case NewSessionDialog::OpenPage: + goto loadit; + break; + case NewSessionDialog::EnginePage: + if (new_session_dialog->engine_control.interface_chosen() && !session_path.empty()) { + goto loadit; } else { - session_path = new_session_dialog->session_folder(); - if (load_session (session_path, session_name)) { - response = Gtk::RESPONSE_NONE; - goto try_again; - } + goto try_again; } break; - case 0: /* nominally the "new" session creator, but could be in use for an old session */ + case NewSessionDialog::NewPage: /* nominally the "new" session creator, but could be in use for an old session */ + + should_be_new = true; - if (new_session_dialog->get_current_page() == 0 && ARDOUR_COMMAND_LINE::session_name.empty()) { - should_be_new = true; + if (session_name.find ('/') != Glib::ustring::npos) { + MessageDialog msg (*new_session_dialog, _("To ensure compatibility with various systems\n" + "session names may not contain a '/' character")); + msg.run (); + response = RESPONSE_NONE; + goto try_again; } - /* handle what appear to be paths rather than just a name */ - - if (session_name[0] == '/' || - (session_name.length() > 2 && session_name[0] == '.' && session_name[1] == '/') || - (session_name.length() > 3 && session_name[0] == '.' && session_name[1] == '.' && session_name[2] == '/')) { - - session_path = Glib::path_get_dirname (session_name); - session_name = Glib::path_get_basename (session_name); - - } else { - - session_path = new_session_dialog->session_folder(); - + if (session_name.find ('\\') != Glib::ustring::npos) { + MessageDialog msg (*new_session_dialog, _("To ensure compatibility with various systems\n" + "session names may not contain a '\\' character")); + msg.run (); + response = RESPONSE_NONE; + goto try_again; } //XXX This is needed because session constructor wants a //non-existant path. hopefully this will be fixed at some point. - - if (!predetermined_path.length()) { - session_path = Glib::build_filename (session_path, session_name); - } - if (!should_be_new) { + session_path = Glib::build_filename (session_path, session_name); - if (load_session (session_path, session_name)) { - response = Gtk::RESPONSE_NONE; - goto try_again; - } + if (Glib::file_test (session_path, Glib::FileTest (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))) { + + new_session_dialog->hide (); - continue; /* leaves while() loop because response != NONE */ - - } else if (Glib::file_test (session_path, Glib::FileTest (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: - new_session_dialog->hide (); - goto_editor_window (); - flush_pending (); - if (load_session (session_path, session_name)) { - response = Gtk::RESPONSE_NONE; - goto try_again; - } - goto done; - break; - default: + if (ask_about_loading_existing_session (session_path)) { + goto loadit; + } else { response = RESPONSE_NONE; - new_session_dialog->reset (); - new_session_dialog->set_existing_session (false); - loading_dialog->hide (); - continue; - } - } + goto try_again; + } + } _session_is_new = true; if (new_session_dialog->use_session_template()) { template_name = new_session_dialog->session_template_name(); - - new_session_dialog->hide (); - goto_editor_window (); - flush_pending (); - - if (load_session (session_path, session_name, template_name)) { - response = Gtk::RESPONSE_NONE; - goto try_again; - } - + goto loadit; } else { - - uint32_t cchns; - uint32_t mchns; - AutoConnectOption iconnect; - AutoConnectOption oconnect; - uint32_t nphysin; - uint32_t nphysout; - - if (Profile->get_sae()) { - - cchns = 0; - mchns = 2; - iconnect = AutoConnectPhysical; - oconnect = AutoConnectMaster; - nphysin = 0; // use all available - nphysout = 0; // use all available - - } else { - - /* get settings from advanced section of NSD */ - - if (new_session_dialog->create_control_bus()) { - cchns = (uint32_t) new_session_dialog->control_channel_count(); - } else { - cchns = 0; - } - - if (new_session_dialog->create_master_bus()) { - mchns = (uint32_t) new_session_dialog->master_channel_count(); - } else { - mchns = 0; - } - - if (new_session_dialog->connect_inputs()) { - iconnect = AutoConnectPhysical; - } else { - iconnect = AutoConnectOption (0); - } - - /// @todo some minor tweaks. - - if (new_session_dialog->connect_outs_to_master()) { - oconnect = AutoConnectMaster; - } else if (new_session_dialog->connect_outs_to_physical()) { - oconnect = AutoConnectPhysical; - } else { - oconnect = AutoConnectOption (0); - } - - nphysin = (uint32_t) new_session_dialog->input_limit_count(); - nphysout = (uint32_t) new_session_dialog->output_limit_count(); - } - - if (build_session (session_path, - session_name, - cchns, - mchns, - iconnect, - oconnect, - nphysin, - nphysout, - engine->frame_rate() * 60 * 5)) { - - response = Gtk::RESPONSE_NONE; + if (build_session_from_nsd (session_path, session_name)) { + response = RESPONSE_NONE; goto try_again; } - - new_session_dialog->hide (); - goto_editor_window (); - flush_pending (); + goto done; } break; - + default: break; } - } + + loadit: + new_session_dialog->hide (); - try_again: - if (response == Gtk::RESPONSE_NONE) { - loading_dialog->hide (); - new_session_dialog->set_existing_session (false); - new_session_dialog->reset (); + if (load_session (session_path, session_name, template_name)) { + /* force a retry */ + response = Gtk::RESPONSE_NONE; + } + + try_again: + if (response == Gtk::RESPONSE_NONE) { + new_session_dialog->set_existing_session (false); + new_session_dialog->reset (); + } } - - } while (response == Gtk::RESPONSE_NONE); + + } while (response == Gtk::RESPONSE_NONE || response == Gtk::RESPONSE_REJECT); done: show(); - loading_dialog->hide (); new_session_dialog->hide(); + new_session_dialog->reset(); + goto_editor_window (); return true; -} +} void ARDOUR_UI::close_session () @@ -2404,9 +2461,11 @@ ARDOUR_UI::close_session () return; } - unload_session (true); + if (unload_session (true)) { + return; + } - get_session_parameters ("", true, false); + get_session_parameters (true, false); } int @@ -2431,21 +2490,7 @@ ARDOUR_UI::load_session (const Glib::ustring& path, const Glib::ustring& snap_na goto out; } - /* if it already exists, we must have write access */ - - if (Glib::file_test (path.c_str(), Glib::FILE_TEST_EXISTS) && ::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.")); - msg.run (); - goto out; - } - - if (loading_dialog) { - loading_dialog->set_markup (_("Please wait while Ardour loads your session")); - flush_pending (); - } - - disable_screen_updates (); + loading_message (_("Please wait while Ardour loads your session")); try { new_session = new Session (*engine, path, snap_name, mix_template); @@ -2458,11 +2503,12 @@ ARDOUR_UI::load_session (const Glib::ustring& path, const Glib::ustring& snap_na MessageDialog msg (err.what(), true, Gtk::MESSAGE_INFO, - Gtk::BUTTONS_OK_CANCEL); + Gtk::BUTTONS_CLOSE); - msg.set_title (_("Loading Error")); - msg.set_secondary_text (_("Click the OK button to try again.")); + msg.set_title (_("Port Registration Error")); + msg.set_secondary_text (_("Click the Close button to try again.")); msg.set_position (Gtk::WIN_POS_CENTER); + pop_back_splash (); msg.present (); int response = msg.run (); @@ -2478,16 +2524,23 @@ ARDOUR_UI::load_session (const Glib::ustring& path, const Glib::ustring& snap_na goto out; } + /* this exception is also special */ + + catch (Session::SRMismatchRejected& err) { + goto out; /* just go back and reload something else, etc. */ + } + catch (...) { - cerr << "Caught something\n"; + MessageDialog msg (string_compose(_("Session \"%1 (snapshot %2)\" did not load successfully"), path, snap_name), true, Gtk::MESSAGE_INFO, - Gtk::BUTTONS_OK_CANCEL); + Gtk::BUTTONS_CLOSE); msg.set_title (_("Loading Error")); - msg.set_secondary_text (_("Click the OK button to try again.")); + msg.set_secondary_text (_("Click the Close button to try again.")); msg.set_position (Gtk::WIN_POS_CENTER); + pop_back_splash (); msg.present (); int response = msg.run (); @@ -2515,7 +2568,6 @@ ARDOUR_UI::load_session (const Glib::ustring& path, const Glib::ustring& snap_na session->set_clean (); } - enable_screen_updates (); flush_pending (); retval = 0; @@ -2560,6 +2612,7 @@ ARDOUR_UI::build_session (const Glib::ustring& path, const Glib::ustring& snap_n catch (...) { MessageDialog msg (string_compose(_("Could not create session in \"%1\""), path)); + pop_back_splash (); msg.run (); return -1; } @@ -2567,6 +2620,9 @@ ARDOUR_UI::build_session (const Glib::ustring& path, const Glib::ustring& snap_n connect_to_session (new_session); session_loaded = true; + + new_session->save_state(new_session->name()); + return 0; } @@ -2585,33 +2641,72 @@ ARDOUR_UI::show () } void -ARDOUR_UI::show_splash () +ARDOUR_UI::show_about () { if (about == 0) { - about = new About(); + about = new About; about->signal_response().connect(mem_fun (*this, &ARDOUR_UI::about_signal_response) ); } - about->present(); - flush_pending (); + + about->set_transient_for(*editor); + + about->show_all (); } void -ARDOUR_UI::about_signal_response(int response) +ARDOUR_UI::launch_chat () { - hide_splash(); +#ifdef __APPLE__ + NagScreen::open_uri("http://webchat.freenode.net/?channels=ardour-osx"); +#else + NagScreen::open_uri("http://webchat.freenode.net/?channels=ardour"); +#endif } void -ARDOUR_UI::hide_splash () +ARDOUR_UI::hide_about () { if (about) { about->get_window()->set_cursor (); - about->hide(); + about->hide (); + } +} + +void +ARDOUR_UI::about_signal_response(int response) +{ + hide_about(); +} + +void +ARDOUR_UI::show_splash () +{ + if (splash == 0) { + try { + splash = new Splash; + } catch (...) { + return; + } + } + + splash->show (); + splash->present (); + splash->queue_draw (); + splash->get_window()->process_updates (true); + flush_pending (); +} + +void +ARDOUR_UI::hide_splash () +{ + if (splash) { + splash->hide(); } } void -ARDOUR_UI::display_cleanup_results (Session::cleanup_report& rep, const gchar* list_title, const string & msg) +ARDOUR_UI::display_cleanup_results (Session::cleanup_report& rep, const gchar* list_title, + const string& plural_msg, const string& singular_msg) { size_t removed; @@ -2666,18 +2761,32 @@ require some unused files to continue to exist.")); dimage->set_alignment(ALIGN_LEFT, ALIGN_TOP); - if (rep.space < 1048576.0f) { - if (removed > 1) { - txt.set_text (string_compose (msg, removed, _("files were"), session->path() + "dead_sounds", (float) rep.space / 1024.0f, "kilo")); - } else { - txt.set_text (string_compose (msg, removed, _("file was"), session->path() + "dead_sounds", (float) rep.space / 1024.0f, "kilo")); - } + + /* subst: + %1 - number of files removed + %2 - location of "dead_sounds" + %3 - size of files affected + %4 - prefix for "bytes" to produce sensible results (e.g. mega, kilo, giga) + */ + + const char* bprefix; + float space_adjusted; + + if (rep.space < 1000000.0f) { + bprefix = X_("kilo"); + space_adjusted = truncf((float)rep.space / 1000.0f); + } else if (rep.space < (1000000.0f * 1000)) { + bprefix = X_("mega"); + space_adjusted = truncf((float)rep.space / (1000000.0f)); } else { - if (removed > 1) { - txt.set_text (string_compose (msg, removed, _("files were"), session->path() + "dead_sounds", (float) rep.space / 1048576.0f, "mega")); - } else { - txt.set_text (string_compose (msg, removed, _("file was"), session->path() + "dead_sounds", (float) rep.space / 1048576.0f, "mega")); - } + bprefix = X_("giga"); + space_adjusted = truncf((float)rep.space / (1000000.0f * 1000)); + } + + if (removed > 1) { + txt.set_text (string_compose (plural_msg, removed, session->path() + "dead_sounds", space_adjusted, bprefix)); + } else { + txt.set_text (string_compose (singular_msg, removed, session->path() + "dead_sounds", space_adjusted, bprefix)); } dhbox.pack_start (*dimage, true, false, 5); @@ -2754,23 +2863,31 @@ After cleanup, unused audio files will be moved to a \ } if (session->cleanup_sources (rep)) { + editor->finish_cleanup (); return; } + + editor->finish_cleanup (); checker.hide(); display_cleanup_results (rep, _("cleaned files"), _("\ -The following %1 %2 not in use and \n\ +The following %1 files were not in use and \n\ have been moved to:\n\ -%3. \n\n\ +%2. \n\n\ Flushing the wastebasket will \n\ release an additional\n\ -%4 %5bytes of disk space.\n" +%3 %4bytes of disk space.\n"), + _("\ +The following file was not in use and \n \ +has been moved to:\n \ +%2. \n\n\ +Flushing the wastebasket will \n\ +release an additional\n\ +%3 %4bytes of disk space.\n" )); - - } void @@ -2789,9 +2906,12 @@ ARDOUR_UI::flush_trash () display_cleanup_results (rep, _("deleted file"), - _("The following %1 %2 deleted from\n\ -%3,\n\ -releasing %4 %5bytes of disk space")); + _("The following %1 files were deleted from\n\ +%2,\n\ +releasing %3 %4bytes of disk space"), + _("The following file was deleted from\n\ +%2,\n\ +releasing %3 %4bytes of disk space")); } void @@ -2831,6 +2951,13 @@ ARDOUR_UI::add_route (Gtk::Window* float_window) return; } + string template_path = add_route_dialog->track_template(); + + if (!template_path.empty()) { + session->new_route_from_template (count, template_path); + return; + } + uint32_t input_chan = add_route_dialog->channels (); uint32_t output_chan; string name_template = add_route_dialog->name_template (); @@ -2881,6 +3008,12 @@ ARDOUR_UI::editor_settings () const } else { node = Config->instant_xml(X_("Editor"), get_user_ardour_path()); } + + if (!node) { + if (getenv("ARDOUR_INSTANT_XML_PATH")) { + node = Config->instant_xml(X_("Editor"), getenv("ARDOUR_INSTANT_XML_PATH")); + } + } if (!node) { node = new XMLNode (X_("Editor")); @@ -2902,20 +3035,134 @@ ARDOUR_UI::keyboard_settings () const } void -ARDOUR_UI::halt_on_xrun_message () +ARDOUR_UI::create_xrun_marker(nframes_t where) { - ENSURE_GUI_THREAD (mem_fun(*this, &ARDOUR_UI::halt_on_xrun_message)); + editor->mouse_add_new_marker (where, false, true); +} +void +ARDOUR_UI::halt_on_xrun_message () +{ MessageDialog msg (*editor, _("Recording was stopped because your system could not keep up.")); msg.run (); } +void +ARDOUR_UI::xrun_handler(nframes_t where) +{ + if (!session) { + return; + } + + ENSURE_GUI_THREAD (bind(mem_fun(*this, &ARDOUR_UI::xrun_handler), where)); + + if (session && Config->get_create_xrun_marker() && session->actively_recording()) { + create_xrun_marker(where); + } + + if (session && Config->get_stop_recording_on_xrun() && session->actively_recording()) { + halt_on_xrun_message (); + } +} + +bool +ARDOUR_UI::preset_file_exists_handler () +{ + /* if driven from another thread, say "do not overwrite" and show the user nothing. + */ + + if (!Gtkmm2ext::UI::instance()->caller_is_ui_thread()) { \ + return false; + } + + HBox* hbox = new HBox(); + Image* image = new Image (Stock::DIALOG_QUESTION, ICON_SIZE_DIALOG); + Gtk::Dialog dialog (_("Preset Exists"), true, false); + Label message (_("\ +A preset with this name already exists for this plugin.\n\ +\n\ +What you would like to do?\n")); + image->set_alignment(ALIGN_CENTER, ALIGN_TOP); + hbox->pack_start (*image, PACK_EXPAND_WIDGET, 12); + hbox->pack_end (message, PACK_EXPAND_PADDING, 12); + dialog.get_vbox()->pack_start(*hbox, PACK_EXPAND_PADDING, 6); + dialog.add_button (_("Overwrite the existing preset"), RESPONSE_ACCEPT); + dialog.add_button (_("Leave the existing preset alone"), RESPONSE_REJECT); + dialog.set_default_response (RESPONSE_ACCEPT); + dialog.set_position (WIN_POS_MOUSE); + dialog.set_type_hint (Gdk::WINDOW_TYPE_HINT_UTILITY); // need to make it float above the preset name dialog + + message.show(); + image->show(); + hbox->show(); + + switch (dialog.run ()) { + case RESPONSE_ACCEPT: + return true; + default: + return false; + } +} + +void +ARDOUR_UI::push_buffer_stats (uint32_t capture, uint32_t playback) +{ + time_t now; + time (&now); + + while (disk_buffer_stats.size() > 60) { + disk_buffer_stats.pop_front (); + } + + disk_buffer_stats.push_back (DiskBufferStat (now, capture, playback)); +} + +void +ARDOUR_UI::write_buffer_stats () +{ + std::ofstream fout; + struct tm tm; + char buf[64]; + char path[PATH_MAX+1]; int fd; + + strcpy (path, "ardourBufferingXXXXXX"); + + if ((fd = mkstemp (path )) < 0) { + cerr << X_("cannot find temporary name for ardour buffer stats") << endl; + return; + } + + fout.open (path); + close (fd); + + if (!fout) { + cerr << string_compose (X_("cannot open file %1 for ardour buffer stats"), path) << endl; + return; + } + + for (list::iterator i = disk_buffer_stats.begin(); i != disk_buffer_stats.end(); ++i) { + localtime_r (&(*i).when, &tm); + strftime (buf, sizeof (buf), "%T", &tm); + fout << buf << ' ' << (*i).capture << ' ' << (*i).playback << endl; + } + + disk_buffer_stats.clear (); + + fout.close (); + + cerr << "Ardour buffering statistics can be found in: " << path << endl; + free (path); +} + void ARDOUR_UI::disk_overrun_handler () { + ENSURE_GUI_THREAD (mem_fun(*this, &ARDOUR_UI::disk_overrun_handler)); + write_buffer_stats (); + if (!have_disk_speed_dialog_displayed) { have_disk_speed_dialog_displayed = true; MessageDialog* msg = new MessageDialog (*editor, _("\ @@ -2932,8 +3179,11 @@ quickly enough to keep up with recording.\n")); void ARDOUR_UI::disk_underrun_handler () { + ENSURE_GUI_THREAD (mem_fun(*this, &ARDOUR_UI::disk_underrun_handler)); + write_buffer_stats (); + if (!have_disk_speed_dialog_displayed) { have_disk_speed_dialog_displayed = true; MessageDialog* msg = new MessageDialog (*editor, @@ -2954,10 +3204,30 @@ ARDOUR_UI::disk_speed_dialog_gone (int ignored_response, MessageDialog* msg) delete msg; } +void +ARDOUR_UI::session_dialog (std::string msg) +{ + ENSURE_GUI_THREAD (bind (mem_fun(*this, &ARDOUR_UI::session_dialog), msg)); + + MessageDialog* d; + + if (editor) { + d = new MessageDialog (*editor, msg, false, MESSAGE_INFO, BUTTONS_OK, true); + } else { + d = new MessageDialog (msg, false, MESSAGE_INFO, BUTTONS_OK, true); + } + + d->show_all (); + d->run (); + delete d; +} + int ARDOUR_UI::pending_state_dialog () { - ArdourDialog dialog ("pending state dialog"); + HBox* hbox = new HBox(); + Image* image = new Image (Stock::DIALOG_QUESTION, ICON_SIZE_DIALOG); + ArdourDialog dialog (_("Crash Recovery"), true); Label message (_("\ This session appears to have been in\n\ middle of recording when ardour or\n\ @@ -2966,14 +3236,20 @@ the computer was shutdown.\n\ Ardour can recover any captured audio for\n\ you, or it can ignore it. Please decide\n\ what you would like to do.\n")); - - dialog.get_vbox()->pack_start (message); - dialog.add_button (_("Recover from crash"), RESPONSE_ACCEPT); + image->set_alignment(ALIGN_CENTER, ALIGN_TOP); + hbox->pack_start (*image, PACK_EXPAND_WIDGET, 12); + hbox->pack_end (message, PACK_EXPAND_PADDING, 12); + dialog.get_vbox()->pack_start(*hbox, PACK_EXPAND_PADDING, 6); dialog.add_button (_("Ignore crash data"), RESPONSE_REJECT); - + dialog.add_button (_("Recover from crash"), RESPONSE_ACCEPT); + dialog.set_default_response (RESPONSE_ACCEPT); dialog.set_position (WIN_POS_CENTER); - dialog.show_all (); - + message.show(); + image->show(); + hbox->show(); + + pop_back_splash (); + switch (dialog.run ()) { case RESPONSE_ACCEPT: return 1; @@ -2981,6 +3257,38 @@ what you would like to do.\n")); return 0; } } + +int +ARDOUR_UI::sr_mismatch_dialog (nframes_t desired, nframes_t actual) +{ + HBox* hbox = new HBox(); + Image* image = new Image (Stock::DIALOG_QUESTION, ICON_SIZE_DIALOG); + ArdourDialog dialog (_("Sample Rate Mismatch"), true); + Label message (string_compose (_("\ +This session was created with a sample rate of %1 Hz\n\ +\n\ +The audioengine is currently running at %2 Hz\n"), desired, actual)); + + image->set_alignment(ALIGN_CENTER, ALIGN_TOP); + hbox->pack_start (*image, PACK_EXPAND_WIDGET, 12); + hbox->pack_end (message, PACK_EXPAND_PADDING, 12); + dialog.get_vbox()->pack_start(*hbox, PACK_EXPAND_PADDING, 6); + dialog.add_button (_("Do not load session"), RESPONSE_REJECT); + dialog.add_button (_("Load session anyway"), RESPONSE_ACCEPT); + dialog.set_default_response (RESPONSE_ACCEPT); + dialog.set_position (WIN_POS_CENTER); + message.show(); + image->show(); + hbox->show(); + + switch (dialog.run ()) { + case RESPONSE_ACCEPT: + return 0; + default: + return 1; + } +} + void ARDOUR_UI::disconnect_from_jack () @@ -3008,26 +3316,6 @@ ARDOUR_UI::reconnect_to_jack () } } -int -ARDOUR_UI::cmdline_new_session (string path) -{ - if (path[0] != '/') { - char buf[PATH_MAX+1]; - string str; - - getcwd (buf, sizeof (buf)); - str = buf; - str += '/'; - str += path; - path = str; - } - - get_session_parameters (path, false, true); - - _will_create_new_session_automatically = false; /* done it */ - return FALSE; /* don't call it again */ -} - void ARDOUR_UI::use_config () { @@ -3125,27 +3413,18 @@ ARDOUR_UI::record_state_changed () } } -void -ARDOUR_UI::set_keybindings_path (string path) -{ - keybindings_path = path; -} - -void -ARDOUR_UI::save_keybindings () -{ - if (can_save_keybindings) { - AccelMap::save (user_keybindings_path); - } -} - bool ARDOUR_UI::first_idle () { if (session) { session->allow_auto_play (true); } - can_save_keybindings = true; + + if (editor) { + editor->first_idle(); + } + + Keyboard::set_can_save_keybindings (true); return false; } @@ -3195,7 +3474,7 @@ ARDOUR_UI::TransportControllable::set_value (float val) return; } - char *action = 0; + const char *action = 0; switch (type) { case Roll: