expand bbox for Canvas::Line to get rid of artifacts caused when moving them around...
[ardour.git] / gtk2_ardour / ardour_ui.cc
index 2b981ace8e0c2314ec9a1ef4054fd06ebce20ed4..eebd35aefbc041c1449979c5670b0e3f30cafe78 100644 (file)
@@ -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
@@ -67,6 +67,7 @@
 #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"
@@ -101,6 +102,7 @@ typedef uint64_t microseconds_t;
 #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"
@@ -114,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"
 
@@ -193,6 +199,8 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir)
        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;
@@ -459,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
@@ -602,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<string> 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<string>::const_iterator n = action_names.begin(); n != action_names.end(); ++n) {
+                               Glib::RefPtr<Action> 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);
        }
 
@@ -741,6 +831,7 @@ void
 ARDOUR_UI::finish()
 {
        if (_session) {
+               ARDOUR_UI::instance()->video_timeline->sync_session_state();
 
                if (_session->dirty()) {
                        vector<string> actions;
@@ -776,6 +867,9 @@ 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.
@@ -874,6 +968,19 @@ ARDOUR_UI::every_second ()
        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;
 }
 
@@ -2361,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;
@@ -2438,6 +2545,10 @@ ARDOUR_UI::get_session_parameters (bool quit_on_cancel, bool should_be_new, stri
         * 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 */
@@ -2501,6 +2612,10 @@ ARDOUR_UI::get_session_parameters (bool quit_on_cancel, bool should_be_new, stri
                
                session_name = _startup->session_name (likely_new);
                
+               if (nsm) {
+                       likely_new = true;
+               }
+
                string::size_type suffix = session_name.find (statefile_suffix);
                
                if (suffix != string::npos) {
@@ -2552,7 +2667,7 @@ ARDOUR_UI::get_session_parameters (bool quit_on_cancel, bool should_be_new, stri
 
                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);
 
@@ -2845,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
@@ -3167,7 +3282,11 @@ ARDOUR_UI::add_route (Gtk::Window* float_window)
        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;
        }
 
@@ -3205,6 +3324,247 @@ ARDOUR_UI::add_route (Gtk::Window* float_window)
        /* 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*
 ARDOUR_UI::mixer_settings () const
 {
@@ -3473,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);
 }
 
 
@@ -3727,10 +4088,17 @@ ARDOUR_UI::midi_panic ()
 void
 ARDOUR_UI::session_format_mismatch (std::string xml_path, std::string backup_path)
 {
-       MessageDialog msg (string_compose (_("This is a session from an older version of Ardour.\n\n"
-                                            "Ardour has copied the old session file\n\n%1\n\nto\n\n%2\n\n"
-                                            "Use %2 with older versions of %3 from now on"),
-                                          xml_path, backup_path, PROGRAM_NAME));
+       const char* start_big = "<span size=\"x-large\" weight=\"bold\">";
+       const char* end_big = "</span>";
+       const char* start_mono = "<tt>";
+       const char* end_mono = "</tt>";
+
+       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 ();
 }