X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=gtk2_ardour%2Fardour_ui.cc;h=eebd35aefbc041c1449979c5670b0e3f30cafe78;hb=74126c48c0726e5491a160d2b0f04e406fc80231;hp=96e309ed014c3661b2661fcd7b3eead536e33aa2;hpb=11415b49be02f16495c95d6286c485ac8afc5189;p=ardour.git diff --git a/gtk2_ardour/ardour_ui.cc b/gtk2_ardour/ardour_ui.cc index 96e309ed01..eebd35aefb 100644 --- a/gtk2_ardour/ardour_ui.cc +++ b/gtk2_ardour/ardour_ui.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 1999-2007 Paul Davis + Copyright (C) 1999-2013 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 @@ -35,6 +35,8 @@ #include #include +#include +#include #include #include @@ -62,8 +64,10 @@ #include "ardour/ardour.h" #include "ardour/audioengine.h" #include "ardour/audiofilesource.h" +#include "ardour/automation_watch.h" #include "ardour/diskstream.h" #include "ardour/filename_extensions.h" +#include "ardour/filesystem_paths.h" #include "ardour/port.h" #include "ardour/process_thread.h" #include "ardour/profile.h" @@ -72,6 +76,9 @@ #include "ardour/session_route.h" #include "ardour/session_state_utils.h" #include "ardour/session_utils.h" +#include "ardour/slave.h" + +#include "timecode/time.h" typedef uint64_t microseconds_t; @@ -93,7 +100,9 @@ typedef uint64_t microseconds_t; #include "missing_file_dialog.h" #include "missing_plugin_dialog.h" #include "mixer_ui.h" +#include "mouse_cursors.h" #include "opts.h" +#include "pingback.h" #include "processor_box.h" #include "prompter.h" #include "public_editor.h" @@ -107,6 +116,10 @@ typedef uint64_t microseconds_t; #include "time_axis_view_item.h" #include "utils.h" #include "window_proxy.h" +#include "video_server_dialog.h" +#include "add_video_dialog.h" +#include "transcode_video_dialog.h" +#include "system_exec.h" #include "i18n.h" @@ -124,7 +137,7 @@ sigc::signal ARDOUR_UI::RapidScreenUpdate; sigc::signal ARDOUR_UI::SuperRapidScreenUpdate; sigc::signal ARDOUR_UI::Clock; -ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[]) +ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir) : Gtkmm2ext::UI (PROGRAM_NAME, argcp, argvp) @@ -147,7 +160,7 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[]) , rec_controllable (new TransportControllable ("transport rec-enable", *this, TransportControllable::RecordEnable)) , auto_return_button (ArdourButton::led_default_elements) - , auto_play_button (ArdourButton::led_default_elements) + , follow_edits_button (ArdourButton::led_default_elements) , auto_input_button (ArdourButton::led_default_elements) , auditioning_alert_button (_("audition")) @@ -160,7 +173,7 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[]) , _feedback_exists (false) { - Gtkmm2ext::init(); + Gtkmm2ext::init(localedir); about = 0; splash = 0; @@ -186,6 +199,8 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[]) session_selector_window = 0; last_key_press_time = 0; add_route_dialog = 0; + add_video_dialog = 0; + video_server_process = 0; route_params = 0; bundle_manager = 0; rc_option_editor = 0; @@ -200,9 +215,6 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[]) original_big_clock_height = -1; original_big_clock_font_size = 0; - roll_button.set_elements (ArdourButton::Element (ArdourButton::Body|ArdourButton::Text)); - play_selection_button.set_elements (ArdourButton::Element (ArdourButton::Body|ArdourButton::Text)); - roll_button.set_controllable (roll_controllable); stop_button.set_controllable (stop_controllable); goto_start_button.set_controllable (goto_start_controllable); @@ -230,6 +242,8 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[]) ARDOUR::Diskstream::DiskOverrun.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::disk_overrun_handler, this), gui_context()); ARDOUR::Diskstream::DiskUnderrun.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::disk_underrun_handler, this), gui_context()); + ARDOUR::Session::VersionMismatch.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::session_format_mismatch, this, _1, _2), gui_context()); + /* handle dialog requests */ ARDOUR::Session::Dialog.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::session_dialog, this, _1), gui_context()); @@ -262,16 +276,13 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[]) /* lets get this party started */ try { - if (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, localedir)) { throw failed_constructor (); } setup_gtk_ardour_enums (); setup_profile (); - GainMeter::setup_slider_pix (); - RouteTimeAxisView::setup_slider_pix (); - ProcessorEntry::setup_slider_pix (); SessionEvent::create_per_thread_pool ("GUI", 512); } catch (failed_constructor& err) { @@ -325,36 +336,6 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[]) DPIReset.connect (sigc::mem_fun (*this, &ARDOUR_UI::resize_text_widgets)); } -/** @return true if a session was chosen and `apply' clicked, otherwise false if `cancel' was clicked */ -bool -ARDOUR_UI::run_startup (bool should_be_new, std::string load_template) -{ - delete _startup; - _startup = new ArdourStartup (); - - XMLNode* audio_setup = Config->extra_xml ("AudioSetup"); - - if (audio_setup && _startup->engine_control()) { - _startup->engine_control()->set_state (*audio_setup); - } - - _startup->set_new_only (should_be_new); - if (!load_template.empty()) { - _startup->set_load_template (load_template); - } - _startup->present (); - - main().run(); - - _startup->hide (); - - switch (_startup->response()) { - case RESPONSE_OK: - return true; - default: - return false; - } -} int ARDOUR_UI::create_engine () @@ -428,7 +409,7 @@ ARDOUR_UI::post_engine () vector::iterator n; vector::iterator k; for (n = names.begin(), k = keys.begin(); n != names.end(); ++n, ++k) { - cerr << "Action: " << (*n) << " bound to " << (*k) << endl; + cout << "Action: " << (*n) << " bound to " << (*k) << endl; } exit (0); @@ -456,12 +437,13 @@ ARDOUR_UI::post_engine () #ifndef GTKOSX /* OS X provides a nearly-always visible wallclock, so don't be stupid */ update_wall_clock (); - Glib::signal_timeout().connect (sigc::mem_fun(*this, &ARDOUR_UI::update_wall_clock), 60000); + Glib::signal_timeout().connect_seconds (sigc::mem_fun(*this, &ARDOUR_UI::update_wall_clock), 1); #endif update_disk_space (); update_cpu_load (); update_sample_rate (engine->frame_rate()); + update_timecode_format (); Config->ParameterChanged.connect (forever_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::parameter_changed, this, _1), gui_context()); boost::function pc (boost::bind (&ARDOUR_UI::parameter_changed, this, _1)); @@ -485,6 +467,10 @@ ARDOUR_UI::~ARDOUR_UI () delete editor; delete mixer; delete add_route_dialog; + if (add_video_dialog) { + delete add_video_dialog; + } + stop_video_server(); } void @@ -628,21 +614,99 @@ ARDOUR_UI::update_autosave () } } +void +ARDOUR_UI::check_announcements () +{ +#ifdef PHONE_HOME + string _annc_filename; + +#ifdef __APPLE__ + _annc_filename = PROGRAM_NAME "_announcements_osx_"; +#else + _annc_filename = PROGRAM_NAME "_announcements_linux_"; +#endif + _annc_filename.append (VERSIONSTRING); + + std::string path = Glib::build_filename (user_config_directory(), _annc_filename); + std::ifstream announce_file (path.c_str()); + if ( announce_file.fail() ) + _announce_string = ""; + else { + std::stringstream oss; + oss << announce_file.rdbuf(); + _announce_string = oss.str(); + } + + pingback (VERSIONSTRING, path); +#endif +} + void ARDOUR_UI::startup () { Application* app = Application::instance (); - + char *nsm_url; app->ShouldQuit.connect (sigc::mem_fun (*this, &ARDOUR_UI::queue_finish)); app->ShouldLoad.connect (sigc::mem_fun (*this, &ARDOUR_UI::idle_load)); -#ifdef PHONE_HOME - call_the_mothership (VERSIONSTRING); -#endif + if (ARDOUR_COMMAND_LINE::check_announcements) { + check_announcements (); + } app->ready (); - if (get_session_parameters (true, ARDOUR_COMMAND_LINE::new_session, ARDOUR_COMMAND_LINE::load_template)) { + nsm_url = getenv ("NSM_URL"); + nsm = 0; + + if (nsm_url) { + nsm = new NSM_Client; + if (!nsm->init (nsm_url)) { + nsm->announce (PROGRAM_NAME, ":dirty:", "ardour3"); + + unsigned int i = 0; + // wait for announce reply from nsm server + for ( i = 0; i < 5000; ++i) { + nsm->check (); + usleep (i); + if (nsm->is_active()) + break; + } + // wait for open command from nsm server + for ( i = 0; i < 5000; ++i) { + nsm->check (); + usleep (1000); + if (nsm->client_id ()) + break; + } + + if (_session && nsm) { + _session->set_nsm_state( nsm->is_active() ); + } + + // nsm requires these actions disabled + vector action_names; + action_names.push_back("SaveAs"); + action_names.push_back("Rename"); + action_names.push_back("New"); + action_names.push_back("Open"); + action_names.push_back("Recent"); + action_names.push_back("Close"); + + for (vector::const_iterator n = action_names.begin(); n != action_names.end(); ++n) { + Glib::RefPtr act = ActionManager::get_action (X_("Main"), (*n).c_str()); + if (act) { + act->set_sensitive (false); + } + } + + } + else { + delete nsm; + nsm = 0; + } + } + + else if (get_session_parameters (true, ARDOUR_COMMAND_LINE::new_session, ARDOUR_COMMAND_LINE::load_template)) { exit (1); } @@ -689,8 +753,14 @@ ARDOUR_UI::check_memory_locking () struct rlimit limits; int64_t ram; long pages, page_size; - - if ((page_size = sysconf (_SC_PAGESIZE)) < 0 ||(pages = sysconf (_SC_PHYS_PAGES)) < 0) { +#ifdef __FreeBSD__ + size_t pages_len=sizeof(pages); + if ((page_size = getpagesize()) < 0 || + sysctlbyname("hw.availpages", &pages, &pages_len, NULL, 0)) +#else + if ((page_size = sysconf (_SC_PAGESIZE)) < 0 ||(pages = sysconf (_SC_PHYS_PAGES)) < 0) +#endif + { ram = 0; } else { ram = (int64_t) pages * (int64_t) page_size; @@ -710,15 +780,23 @@ ARDOUR_UI::check_memory_locking () "This might cause %1 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"), - PROGRAM_NAME).c_str()); + "and it is normally controlled by %2"), + PROGRAM_NAME).c_str(), +#ifdef __FreeBSD__ + X_("/etc/login.conf") +#else + X_(" /etc/security/limits.conf") +#endif + ); + + msg.set_default_response (RESPONSE_OK); VBox* vbox = msg.get_vbox(); HBox hbox; CheckButton cb (_("Do not show this window again")); cb.signal_toggled().connect (sigc::mem_fun (*this, &ARDOUR_UI::no_memory_warning)); - + hbox.pack_start (cb, true, false); vbox->pack_start (hbox); cb.show(); @@ -753,6 +831,7 @@ void ARDOUR_UI::finish() { if (_session) { + ARDOUR_UI::instance()->video_timeline->sync_session_state(); if (_session->dirty()) { vector actions; @@ -788,12 +867,17 @@ If you still wish to quit, please use the\n\n\ point_zero_one_second_connection.disconnect(); } + delete ARDOUR_UI::instance()->video_timeline; + stop_video_server(); + /* Save state before deleting the session, as that causes some windows to be destroyed before their visible state can be saved. */ save_ardour_state (); + loading_message (string_compose (_("Please wait while %1 cleans up..."), PROGRAM_NAME)); + if (_session) { // _session->set_deletion_in_progress (); _session->set_clean (); @@ -876,12 +960,27 @@ ARDOUR_UI::ask_about_saving_session (const vector& actions) return -1; } + gint ARDOUR_UI::every_second () { update_cpu_load (); update_buffer_load (); update_disk_space (); + update_timecode_format (); + + if (nsm && nsm->is_active ()) { + nsm->check (); + + if (!_was_dirty && _session->dirty ()) { + nsm->is_dirty (); + _was_dirty = true; + } + else if (_was_dirty && !_session->dirty ()){ + nsm->is_clean (); + _was_dirty = false; + } + } return TRUE; } @@ -1091,18 +1190,47 @@ ARDOUR_UI::update_disk_space() disk_space_label.set_markup (buf); } +void +ARDOUR_UI::update_timecode_format () +{ + char buf[64]; + + if (_session) { + bool matching; + TimecodeSlave* tcslave; + SyncSource sync_src = Config->get_sync_source(); + + if ((sync_src == LTC || sync_src == MTC) && (tcslave = dynamic_cast(_session->slave())) != 0) { + matching = (tcslave->apparent_timecode_format() == _session->config.get_timecode_format()); + } else { + matching = true; + } + + snprintf (buf, sizeof (buf), S_("Timecode|TC: %s"), + matching ? X_("green") : X_("red"), + Timecode::timecode_format_name (_session->config.get_timecode_format()).c_str()); + } else { + snprintf (buf, sizeof (buf), "TC: n/a"); + } + + timecode_format_label.set_markup (buf); +} + gint ARDOUR_UI::update_wall_clock () { time_t now; struct tm *tm_now; - char buf[16]; + static int last_min = -1; time (&now); tm_now = localtime (&now); - - sprintf (buf, "%02d:%02d", tm_now->tm_hour, tm_now->tm_min); - wall_clock_label.set_text (buf); + if (last_min != tm_now->tm_min) { + char buf[16]; + sprintf (buf, "%02d:%02d", tm_now->tm_hour, tm_now->tm_min); + wall_clock_label.set_text (buf); + last_min = tm_now->tm_min; + } return TRUE; } @@ -1169,6 +1297,7 @@ ARDOUR_UI::redisplay_recent_sessions () row[recent_session_columns.visible_name] = Glib::path_get_basename (fullpath); row[recent_session_columns.fullpath] = fullpath; + row[recent_session_columns.tip] = Glib::Markup::escape_text (fullpath); if (state_file_names.size() > 1) { @@ -1182,11 +1311,12 @@ ARDOUR_UI::redisplay_recent_sessions () child_row[recent_session_columns.visible_name] = *i2; child_row[recent_session_columns.fullpath] = fullpath; + child_row[recent_session_columns.tip] = Glib::Markup::escape_text (fullpath); } } } - recent_session_display.set_tooltip_column(1); // recent_session_columns.fullpath + recent_session_display.set_tooltip_column(1); // recent_session_columns.tip recent_session_display.set_model (recent_session_model); } @@ -1374,11 +1504,7 @@ ARDOUR_UI::session_add_mixed_track (const ChanCount& input, const ChanCount& out tracks = _session->new_midi_track (input, output, instrument, ARDOUR::Normal, route_group, how_many, name_template); if (tracks.size() != how_many) { - if (how_many == 1) { - error << _("could not create a new mixed track") << endmsg; - } else { - error << string_compose (_("could not create %1 new mixed tracks"), how_many) << endmsg; - } + error << string_compose(P_("could not create %1 new mixed track", "could not create %1 new mixed tracks", how_many), how_many) << endmsg; } } @@ -1428,12 +1554,8 @@ ARDOUR_UI::session_add_audio_route ( tracks = _session->new_audio_track (input_channels, output_channels, mode, route_group, how_many, name_template); if (tracks.size() != how_many) { - if (how_many == 1) { - error << _("could not create a new audio track") << endmsg; - } else { - error << string_compose (_("could only create %1 of %2 new audio %3"), - tracks.size(), how_many, (track ? _("tracks") : _("busses"))) << endmsg; - } + error << string_compose (P_("could not create %1 new audio track", "could not create %1 new audio tracks", how_many), how_many) + << endmsg; } } else { @@ -1441,11 +1563,8 @@ ARDOUR_UI::session_add_audio_route ( routes = _session->new_audio_route (input_channels, output_channels, route_group, how_many, name_template); if (routes.size() != how_many) { - if (how_many == 1) { - error << _("could not create a new audio bus") << endmsg; - } else { - error << string_compose (_("could not create %1 new audio busses"), how_many) << endmsg; - } + error << string_compose (P_("could not create %1 new audio bus", "could not create %1 new audio busses", how_many), how_many) + << endmsg; } } } @@ -1597,7 +1716,7 @@ ARDOUR_UI::transport_roll () #if 0 if (_session->config.get_external_sync()) { - switch (_session->config.get_sync_source()) { + switch (Config->get_sync_source()) { case JACK: break; default: @@ -1621,15 +1740,18 @@ ARDOUR_UI::transport_roll () _session->request_play_range (0, true); } - if (Config->get_always_play_range()) { - _session->request_play_range (&editor->get_selection().time, true); - } - if (!rolling) { _session->request_transport_speed (1.0f); } } +bool +ARDOUR_UI::get_smart_mode() const +{ + return ( editor->get_smart_mode() ); +} + + void ARDOUR_UI::toggle_roll (bool with_abort, bool roll_out_of_bounded_mode) { @@ -1644,7 +1766,7 @@ ARDOUR_UI::toggle_roll (bool with_abort, bool roll_out_of_bounded_mode) } if (_session->config.get_external_sync()) { - switch (_session->config.get_sync_source()) { + switch (Config->get_sync_source()) { case JACK: break; default: @@ -1680,7 +1802,7 @@ ARDOUR_UI::toggle_roll (bool with_abort, bool roll_out_of_bounded_mode) if (rolling) { _session->request_stop (with_abort, true); } else { - if (Config->get_always_play_range ()) { + if ( Config->get_always_play_range() ) { _session->request_play_range (&editor->get_selection().time, true); } @@ -1726,6 +1848,15 @@ ARDOUR_UI::transport_play_selection () editor->play_selection (); } +void +ARDOUR_UI::transport_play_preroll () +{ + if (!_session) { + return; + } + editor->play_with_preroll (); +} + void ARDOUR_UI::transport_rewind (int option) { @@ -1950,10 +2081,11 @@ JACK, reconnect and save the session."), PROGRAM_NAME); MessageDialog msg (*editor, msgstr); pop_back_splash (msg); + msg.set_keep_above (true); msg.run (); - + if (free_reason) { - free ((char*) reason); + free (const_cast (reason)); } } @@ -1986,7 +2118,11 @@ ARDOUR_UI::update_clocks () void ARDOUR_UI::start_clocking () { - clock_signal_connection = RapidScreenUpdate.connect (sigc::mem_fun(*this, &ARDOUR_UI::update_clocks)); + if (Config->get_super_rapid_clock_update()) { + clock_signal_connection = SuperRapidScreenUpdate.connect (sigc::mem_fun(*this, &ARDOUR_UI::update_clocks)); + } else { + clock_signal_connection = RapidScreenUpdate.connect (sigc::mem_fun(*this, &ARDOUR_UI::update_clocks)); + } } void @@ -2332,7 +2468,7 @@ ARDOUR_UI::build_session_from_nsd (const std::string& session_path, const std::s { BusProfile bus_profile; - if (Profile->get_sae()) { + if (nsm || Profile->get_sae()) { bus_profile.master_out_channels = 2; bus_profile.input_ac = AutoConnectPhysical; @@ -2394,20 +2530,6 @@ ARDOUR_UI::idle_load (const std::string& path) } } -void -ARDOUR_UI::loading_message (const std::string& msg) -{ - if (ARDOUR_COMMAND_LINE::no_splash) { - return; - } - - show_splash (); - if (splash) { - splash->message (msg); - flush_pending (); - } -} - /** @param quit_on_cancel true if exit() should be called if the user clicks `cancel' in the new session dialog */ int ARDOUR_UI::get_session_parameters (bool quit_on_cancel, bool should_be_new, string load_template) @@ -2418,6 +2540,23 @@ ARDOUR_UI::get_session_parameters (bool quit_on_cancel, bool should_be_new, stri int ret = -1; bool likely_new = false; + /* deal with any existing DIRTY session now, rather than later. don't + * treat a non-dirty session this way, so that it stays visible + * as we bring up the new session dialog. + */ + + if (_session && ARDOUR_UI::instance()->video_timeline) { + ARDOUR_UI::instance()->video_timeline->sync_session_state(); + } + + if (_session && _session->dirty()) { + if (unload_session (false)) { + /* unload cancelled by user */ + return 0; + } + ARDOUR_COMMAND_LINE::session_name = ""; + } + if (!load_template.empty()) { should_be_new = true; template_name = load_template; @@ -2425,7 +2564,7 @@ ARDOUR_UI::get_session_parameters (bool quit_on_cancel, bool should_be_new, stri while (ret != 0) { - if (!should_be_new && !ARDOUR_COMMAND_LINE::session_name.empty()) { + if (!ARDOUR_COMMAND_LINE::session_name.empty()) { /* if they named a specific statefile, use it, otherwise they are just giving a session folder, and we want to use it as is @@ -2442,78 +2581,93 @@ ARDOUR_UI::get_session_parameters (bool quit_on_cancel, bool should_be_new, stri session_path = ARDOUR_COMMAND_LINE::session_name; session_name = Glib::path_get_basename (ARDOUR_COMMAND_LINE::session_name); } - } else { + session_path = ""; + session_name = ""; + } - bool const apply = run_startup (should_be_new, load_template); - - if (!apply) { - if (quit_on_cancel) { - exit (1); - } else { - return ret; - } + delete _startup; + _startup = new ArdourStartup (should_be_new, session_name, session_path, load_template); + + if (!_startup->ready_without_display()) { + _startup->present (); + main().run(); + _startup->hide (); + } + + switch (_startup->response()) { + case RESPONSE_OK: + break; + default: + if (quit_on_cancel) { + exit (1); + } else { + return ret; } + } - /* if we run the startup dialog again, offer more than just "new session" */ - - should_be_new = false; - - session_name = _startup->session_name (likely_new); - - string::size_type suffix = session_name.find (statefile_suffix); - - if (suffix != string::npos) { - session_name = session_name.substr (0, suffix); - } + /* if we run the startup dialog again, offer more than just "new session" */ + + should_be_new = false; + + session_name = _startup->session_name (likely_new); + + if (nsm) { + likely_new = true; + } - /* this shouldn't happen, but we catch it just in case it does */ + string::size_type suffix = session_name.find (statefile_suffix); + + if (suffix != string::npos) { + session_name = session_name.substr (0, suffix); + } + + /* this shouldn't happen, but we catch it just in case it does */ + + if (session_name.empty()) { + continue; + } + + if (_startup->use_session_template()) { + template_name = _startup->session_template_name(); + _session_is_new = true; + } + + if (session_name[0] == G_DIR_SEPARATOR || + (session_name.length() > 2 && session_name[0] == '.' && session_name[1] == G_DIR_SEPARATOR) || + (session_name.length() > 3 && session_name[0] == '.' && session_name[1] == '.' && session_name[2] == G_DIR_SEPARATOR)) { + + /* absolute path or cwd-relative path specified for session name: infer session folder + from what was given. + */ + + session_path = Glib::path_get_dirname (session_name); + session_name = Glib::path_get_basename (session_name); + + } else { - if (session_name.empty()) { + session_path = _startup->session_folder(); + + char illegal = Session::session_name_is_legal (session_name); + + if (illegal) { + MessageDialog msg (*_startup, + string_compose (_("To ensure compatibility with various systems\n" + "session names may not contain a '%1' character"), + illegal)); + msg.run (); + ARDOUR_COMMAND_LINE::session_name = ""; // cancel that continue; } - - if (_startup->use_session_template()) { - template_name = _startup->session_template_name(); - _session_is_new = true; - } - - if (session_name[0] == G_DIR_SEPARATOR || - (session_name.length() > 2 && session_name[0] == '.' && session_name[1] == G_DIR_SEPARATOR) || - (session_name.length() > 3 && session_name[0] == '.' && session_name[1] == '.' && session_name[2] == G_DIR_SEPARATOR)) { - - /* absolute path or cwd-relative path specified for session name: infer session folder - from what was given. - */ - - session_path = Glib::path_get_dirname (session_name); - session_name = Glib::path_get_basename (session_name); - - } else { - - session_path = _startup->session_folder(); - - char illegal = Session::session_name_is_legal (session_name); - - if (illegal) { - MessageDialog msg (*_startup, - string_compose (_("To ensure compatibility with various systems\n" - "session names may not contain a '%1' character"), - illegal)); - msg.run (); - ARDOUR_COMMAND_LINE::session_name = ""; // cancel that - continue; - } - } } - + if (create_engine ()) { break; } if (Glib::file_test (session_path, Glib::FileTest (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))) { - if (likely_new) { + if (likely_new && !nsm) { std::string existing = Glib::build_filename (session_path, session_name); @@ -2806,13 +2960,13 @@ ARDOUR_UI::show_about () void ARDOUR_UI::launch_manual () { - PBD::open_uri("http://ardour.org/flossmanual"); + PBD::open_uri (Config->get_tutorial_manual_url()); } void ARDOUR_UI::launch_reference () { - PBD::open_uri("http://ardour.org/refmanual"); + PBD::open_uri (Config->get_reference_manual_url()); } void @@ -2830,6 +2984,20 @@ ARDOUR_UI::about_signal_response (int /*response*/) hide_about(); } +void +ARDOUR_UI::loading_message (const std::string& msg) +{ + if (ARDOUR_COMMAND_LINE::no_splash) { + return; + } + + if (!splash) { + show_splash (); + } + + splash->message (msg); +} + void ARDOUR_UI::show_splash () { @@ -2841,11 +3009,7 @@ ARDOUR_UI::show_splash () } } - splash->present (); - splash->pop_front (); - splash->queue_draw (); - splash->get_window()->process_updates (true); - flush_pending (); + splash->display (); } void @@ -2856,8 +3020,7 @@ ARDOUR_UI::hide_splash () } void -ARDOUR_UI::display_cleanup_results (ARDOUR::CleanupReport& rep, const gchar* list_title, - const string& plural_msg, const string& singular_msg) +ARDOUR_UI::display_cleanup_results (ARDOUR::CleanupReport& rep, const gchar* list_title, const bool msg_delete) { size_t removed; @@ -2929,20 +3092,36 @@ require some unused files to continue to exist.")); bprefix = X_(""); space_adjusted = rep.space; } else if (rep.space < 1000000) { - bprefix = X_("kilo"); + bprefix = _("kilo"); space_adjusted = truncf((float)rep.space / 1000.0); } else if (rep.space < 1000000 * 1000) { - bprefix = X_("mega"); + bprefix = _("mega"); space_adjusted = truncf((float)rep.space / (1000.0 * 1000.0)); } else { - bprefix = X_("giga"); + bprefix = _("giga"); space_adjusted = truncf((float)rep.space / (1000.0 * 1000 * 1000.0)); } - if (removed > 1) { - txt.set_text (string_compose (plural_msg, removed, dead_directory, space_adjusted, bprefix, PROGRAM_NAME)); + if (msg_delete) { + txt.set_markup (string_compose (P_("\ +The following file was deleted from %2,\n\ +releasing %3 %4bytes of disk space", "\ +The following %1 files were deleted from %2,\n\ +releasing %3 %4bytes of disk space", removed), + removed, Glib::Markup::escape_text (dead_directory), space_adjusted, bprefix, PROGRAM_NAME)); } else { - txt.set_text (string_compose (singular_msg, removed, dead_directory, space_adjusted, bprefix, PROGRAM_NAME)); + txt.set_markup (string_compose (P_("\ +The following file was not in use and \n\ +has been moved to: %2\n\n\ +After a restart of %5\n\n\ +Session -> Clean-up -> Flush Wastebasket\n\n\ +will release an additional %3 %4bytes of disk space.\n", "\ +The following %1 files were not in use and \n\ +have been moved to: %2\n\n\ +After a restart of %5\n\n\ +Session -> Clean-up -> Flush Wastebasket\n\n\ +will release an additional %3 %4bytes of disk space.\n", removed), + removed, Glib::Markup::escape_text (dead_directory), space_adjusted, bprefix, PROGRAM_NAME)); } dhbox.pack_start (*dimage, true, false, 5); @@ -3036,26 +3215,7 @@ Clean-up will move all unused files to a \"dead\" location.")); editor->finish_cleanup (); checker.hide(); - display_cleanup_results (rep, - _("Cleaned Files"), - _("\ -The following %1 files were not in use and \n\ -have been moved to:\n\n\ -%2\n\n\ -After a restart of %5,\n\n\ -Session -> Clean-up -> Flush Wastebasket\n\n\ -will release an additional\n\ -%3 %4bytes of disk space.\n"), - _("\ -The following file was not in use and \n\ -has been moved to:\n \ -%2\n\n\ -After a restart of %5,\n\n\ -Session -> Clean-up -> Flush Wastebasket\n\n\ -will release an additional\n\ -%3 %4bytes of disk space.\n" - )); - + display_cleanup_results (rep, _("Cleaned Files"), false); } void @@ -3072,14 +3232,7 @@ ARDOUR_UI::flush_trash () return; } - display_cleanup_results (rep, - _("deleted file"), - _("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")); + display_cleanup_results (rep, _("deleted file"), true); } void @@ -3120,10 +3273,20 @@ ARDOUR_UI::add_route (Gtk::Window* float_window) return; } + PBD::ScopedConnection idle_connection; + + if (count > 8) { + ARDOUR::GUIIdle.connect (idle_connection, MISSING_INVALIDATOR, boost::bind (&Gtkmm2ext::UI::flush_pending, this), gui_context()); + } + string template_path = add_route_dialog->track_template(); if (!template_path.empty()) { - _session->new_route_from_template (count, template_path); + if (add_route_dialog->name_template_is_default()) { + _session->new_route_from_template (count, template_path, string()); + } else { + _session->new_route_from_template (count, template_path, add_route_dialog->name_template()); + } return; } @@ -3132,7 +3295,6 @@ ARDOUR_UI::add_route (Gtk::Window* float_window) string name_template = add_route_dialog->name_template (); PluginInfoPtr instrument = add_route_dialog->requested_instrument (); RouteGroup* route_group = add_route_dialog->route_group (); - AutoConnectOption oac = Config->get_output_auto_connect(); if (oac & AutoConnectMaster) { @@ -3158,6 +3320,249 @@ ARDOUR_UI::add_route (Gtk::Window* float_window) session_add_audio_bus (input_chan.n_audio(), output_chan.n_audio(), route_group, count, name_template); break; } + + /* idle connection will end at scope end */ +} + +void +ARDOUR_UI::stop_video_server (bool ask_confirm) +{ + if (!video_server_process && ask_confirm) { + warning << _("Video-Server was not launched by Ardour. The request to stop it is ignored.") << endmsg; + } + if (video_server_process) { + if(ask_confirm) { + ArdourDialog confirm (_("Stop Video-Server"), true); + Label m (_("Do you really want to stop the Video Server?")); + confirm.get_vbox()->pack_start (m, true, true); + confirm.add_button (Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); + confirm.add_button (_("Yes, Stop It"), Gtk::RESPONSE_ACCEPT); + confirm.show_all (); + if (confirm.run() == RESPONSE_CANCEL) { + return; + } + } + delete video_server_process; + video_server_process =0; + } +} + +void +ARDOUR_UI::start_video_server_menu (Gtk::Window* float_window) +{ + ARDOUR_UI::start_video_server( float_window, true); +} + +bool +ARDOUR_UI::start_video_server (Gtk::Window* float_window, bool popup_msg) +{ + if (!_session) { + return false; + } + if (popup_msg) { + if (ARDOUR_UI::instance()->video_timeline->check_server()) { + if (video_server_process) { + popup_error(_("The Video Server is already started.")); + } else { + popup_error(_("An external Video Server is configured and can be reached. Not starting a new instance.")); + } + } + } + + int firsttime = 0; + while (!ARDOUR_UI::instance()->video_timeline->check_server()) { + if (firsttime++) { + warning << _("Could not connect to the Video Server. Start it or configure its access URL in Edit -> Preferences.") << endmsg; + } + VideoServerDialog *video_server_dialog = new VideoServerDialog (_session); + if (float_window) { + video_server_dialog->set_transient_for (*float_window); + } + + if (!Config->get_show_video_server_dialog() && firsttime < 2) { + video_server_dialog->hide(); + } else { + ResponseType r = (ResponseType) video_server_dialog->run (); + video_server_dialog->hide(); + if (r != RESPONSE_ACCEPT) { return false; } + if (video_server_dialog->show_again()) { + Config->set_show_video_server_dialog(false); + } + } + + std::string icsd_exec = video_server_dialog->get_exec_path(); + std::string icsd_docroot = video_server_dialog->get_docroot(); + if (icsd_docroot.empty()) {icsd_docroot = X_("/");} + + struct stat sb; + if (!lstat (icsd_docroot.c_str(), &sb) == 0 || !S_ISDIR(sb.st_mode)) { + warning << _("Specified docroot is not an existing directory.") << endmsg; + continue; + } + if ( (!lstat (icsd_exec.c_str(), &sb) == 0) + || (sb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) == 0 ) { + warning << _("Given Video Server is not an executable file.") << endmsg; + continue; + } + + char **argp; + argp=(char**) calloc(9,sizeof(char*)); + argp[0] = strdup(icsd_exec.c_str()); + argp[1] = strdup("-P"); + argp[2] = (char*) calloc(16,sizeof(char)); snprintf(argp[2], 16, "%s", video_server_dialog->get_listenaddr().c_str()); + argp[3] = strdup("-p"); + argp[4] = (char*) calloc(6,sizeof(char)); snprintf(argp[4], 6, "%i", video_server_dialog->get_listenport()); + argp[5] = strdup("-C"); + argp[6] = (char*) calloc(6,sizeof(char)); snprintf(argp[6], 6, "%i", video_server_dialog->get_cachesize()); + argp[7] = strdup(icsd_docroot.c_str()); + argp[8] = 0; + stop_video_server(); + + if (icsd_docroot == X_("/")) { + Config->set_video_advanced_setup(false); + } else { + std::ostringstream osstream; + osstream << "http://localhost:" << video_server_dialog->get_listenport() << "/"; + Config->set_video_server_url(osstream.str()); + Config->set_video_server_docroot(icsd_docroot); + Config->set_video_advanced_setup(true); + } + + video_server_process = new SystemExec(icsd_exec, argp); + video_server_process->start(); + sleep(1); + } + return true; +} + +void +ARDOUR_UI::add_video (Gtk::Window* float_window) +{ + if (!_session) { + return; + } + + if (!start_video_server(float_window, false)) { + warning << _("Could not connect to the Video Server. Start it or configure its access URL in Edit -> Preferences.") << endmsg; + return; + } + + if (add_video_dialog == 0) { + add_video_dialog = new AddVideoDialog (_session); + if (float_window) { + add_video_dialog->set_transient_for (*float_window); + } + } + + if (add_video_dialog->is_visible()) { + /* we're already doing this */ + return; + } + ResponseType r = (ResponseType) add_video_dialog->run (); + add_video_dialog->hide(); + if (r != RESPONSE_ACCEPT) { return; } + + bool local_file; + std::string path = add_video_dialog->file_name(local_file); + bool auto_set_session_fps = add_video_dialog->auto_set_session_fps(); + + if (local_file && !Glib::file_test(path, Glib::FILE_TEST_EXISTS)) { + warning << string_compose(_("could not open %1"), path) << endmsg; + return; + } + if (!local_file && path.length() == 0) { + warning << _("no video-file selected") << endmsg; + return; + } + + switch (add_video_dialog->import_option()) { + case VTL_IMPORT_TRANSCODE: + { + TranscodeVideoDialog *transcode_video_dialog; + transcode_video_dialog = new TranscodeVideoDialog (_session, path); + ResponseType r = (ResponseType) transcode_video_dialog->run (); + transcode_video_dialog->hide(); + if (r != RESPONSE_ACCEPT) { + delete transcode_video_dialog; + return; + } + if (!transcode_video_dialog->get_audiofile().empty()) { + editor->embed_audio_from_video(transcode_video_dialog->get_audiofile()); + } + switch (transcode_video_dialog->import_option()) { + case VTL_IMPORT_TRANSCODED: + path = transcode_video_dialog->get_filename(); + local_file = true; + break; + case VTL_IMPORT_REFERENCE: + break; + default: + delete transcode_video_dialog; + return; + } + delete transcode_video_dialog; + } + break; + default: + case VTL_IMPORT_NONE: + break; + } + + /* strip _session->session_directory().video_path() from video file if possible */ + if (local_file && !path.compare(0, _session->session_directory().video_path().size(), _session->session_directory().video_path())) { + path=path.substr(_session->session_directory().video_path().size()); + if (path.at(0) == G_DIR_SEPARATOR) { + path=path.substr(1); + } + } + + video_timeline->set_update_session_fps(auto_set_session_fps); + if (video_timeline->video_file_info(path, local_file)) { + XMLNode* node = new XMLNode(X_("Videotimeline")); + node->add_property (X_("Filename"), path); + node->add_property (X_("AutoFPS"), auto_set_session_fps?X_("1"):X_("0")); + node->add_property (X_("LocalFile"), local_file?X_("1"):X_("0")); + _session->add_extra_xml (*node); + _session->set_dirty (); + + _session->maybe_update_session_range( + std::max(video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0), + std::max(video_timeline->get_offset() + video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)); + + + if (add_video_dialog->launch_xjadeo() && local_file) { + editor->set_xjadeo_sensitive(true); + editor->toggle_xjadeo_proc(1); + } else { + editor->toggle_xjadeo_proc(0); + } + editor->toggle_ruler_video(true); + } +} + +void +ARDOUR_UI::remove_video () +{ + video_timeline->close_session(); + editor->toggle_ruler_video(false); + + /* delete session state */ + XMLNode* node = new XMLNode(X_("Videotimeline")); + _session->add_extra_xml(*node); + node = new XMLNode(X_("Videomonitor")); + _session->add_extra_xml(*node); + stop_video_server(); +} + +void +ARDOUR_UI::flush_videotimeline_cache (bool localcacheonly) +{ + if (localcacheonly) { + video_timeline->vmon_update(); + } else { + video_timeline->flush_cache(); + } + editor->queue_visual_videotimeline_update(); } XMLNode* @@ -3316,8 +3721,8 @@ ARDOUR_UI::pending_state_dialog () Image* image = new Image (Stock::DIALOG_QUESTION, ICON_SIZE_DIALOG); ArdourDialog dialog (_("Crash Recovery"), true); Label message (string_compose (_("\ -This session appears to have been in\n\ -middle of recording when ardour or\n\ +This session appears to have been in the\n\ +middle of recording when %1 or\n\ the computer was shutdown.\n\ \n\ %1 can recover any captured audio for\n\ @@ -3347,7 +3752,7 @@ int ARDOUR_UI::sr_mismatch_dialog (framecnt_t desired, framecnt_t actual) { HBox* hbox = new HBox(); - Image* image = new Image (Stock::DIALOG_QUESTION, ICON_SIZE_DIALOG); + Image* image = new Image (Stock::DIALOG_WARNING, ICON_SIZE_DIALOG); ArdourDialog dialog (_("Sample Rate Mismatch"), true); Label message (string_compose (_("\ This session was created with a sample rate of %1 Hz, but\n\ @@ -3428,6 +3833,7 @@ ARDOUR_UI::update_transport_clocks (framepos_t pos) if (big_clock_window->get()) { big_clock->set (pos); } + ARDOUR_UI::instance()->video_timeline->manual_seek_video_monitor(pos); } @@ -3521,16 +3927,16 @@ ARDOUR_UI::TransportControllable::set_value (double val) action = X_("Stop"); break; case GotoStart: - action = X_("Goto Start"); + action = X_("GotoStart"); break; case GotoEnd: - action = X_("Goto End"); + action = X_("GotoEnd"); break; case AutoLoop: action = X_("Loop"); break; case PlaySelection: - action = X_("Play Selection"); + action = X_("PlaySelection"); break; case RecordEnable: action = X_("Record"); @@ -3590,43 +3996,6 @@ ARDOUR_UI::setup_profile () } } -void -ARDOUR_UI::toggle_translations () -{ - using namespace Glib; - - RefPtr act = ActionManager::get_action (X_("Main"), X_("EnableTranslation")); - if (act) { - RefPtr ract = RefPtr::cast_dynamic (act); - if (ract) { - - string i18n_killer = ARDOUR::translation_kill_path(); - - bool already_enabled = !ARDOUR::translations_are_disabled (); - - if (ract->get_active ()) { - /* we don't care about errors */ - int fd = ::open (i18n_killer.c_str(), O_RDONLY|O_CREAT, 0644); - close (fd); - } else { - /* we don't care about errors */ - unlink (i18n_killer.c_str()); - } - - if (already_enabled != ract->get_active()) { - MessageDialog win (already_enabled ? _("Translations disabled") : _("Translations enabled"), - false, - Gtk::MESSAGE_WARNING, - Gtk::BUTTONS_OK); - win.set_secondary_text (string_compose (_("You must restart %1 for this to take effect."), PROGRAM_NAME)); - win.set_position (Gtk::WIN_POS_CENTER); - win.present (); - win.run (); - } - } - } -} - /** Add a window proxy to our list, so that its state will be saved. * This call also causes the window to be created and opened if its * state was saved as `visible'. @@ -3715,3 +4084,21 @@ ARDOUR_UI::midi_panic () _session->midi_panic(); } } + +void +ARDOUR_UI::session_format_mismatch (std::string xml_path, std::string backup_path) +{ + const char* start_big = ""; + const char* end_big = ""; + const char* start_mono = ""; + const char* end_mono = ""; + + MessageDialog msg (string_compose (_("%4This is a session from an older version of %3%5\n\n" + "%3 has copied the old session file\n\n%6%1%7\n\nto\n\n%6%2%7\n\n" + "From now on, use the -2000 version with older versions of %3"), + xml_path, backup_path, PROGRAM_NAME, + start_big, end_big, + start_mono, end_mono), true); + + msg.run (); +}