Various update bits.
authorCarl Hetherington <cth@carlh.net>
Tue, 7 Jan 2014 16:47:44 +0000 (16:47 +0000)
committerCarl Hetherington <cth@carlh.net>
Tue, 7 Jan 2014 16:47:44 +0000 (16:47 +0000)
src/lib/config.cc
src/lib/config.h
src/lib/image_examiner.cc
src/lib/update.cc
src/lib/update.h
src/tools/dcpomatic.cc
src/wx/config_dialog.cc
src/wx/config_dialog.h
src/wx/update_dialog.cc [new file with mode: 0644]
src/wx/update_dialog.h [new file with mode: 0644]
src/wx/wscript

index 454b03e3a9f849f0044a21892dde7b543017d365..5a9e1619a091efacf0ce2be7a5c89e1fd7b440a6 100644 (file)
@@ -68,6 +68,8 @@ Config::Config ()
        , _kdm_email (
                _("Dear Projectionist\n\nPlease find attached KDMs for $CPL_NAME.\n\nThe KDMs are valid from $START_TIME until $END_TIME.\n\nBest regards,\nDCP-o-matic")
                )
+       , _check_for_updates (false)
+       , _check_for_test_updates (false)
 {
        _allowed_dcp_frame_rates.push_back (24);
        _allowed_dcp_frame_rates.push_back (25);
@@ -180,6 +182,9 @@ Config::read ()
        _mail_password = f.optional_string_child("MailPassword").get_value_or ("");
        _kdm_from = f.string_child ("KDMFrom");
        _kdm_email = f.string_child ("KDMEmail");
+
+       _check_for_updates = f.optional_bool_child("CheckForUpdates").get_value_or (false);
+       _check_for_test_updates = f.optional_bool_child("CheckForTestUpdates").get_value_or (false);
 }
 
 void
@@ -354,6 +359,9 @@ Config::write () const
        root->add_child("KDMFrom")->add_child_text (_kdm_from);
        root->add_child("KDMEmail")->add_child_text (_kdm_email);
 
+       root->add_child("CheckForUpdates")->add_child_text (_check_for_updates ? "1" : "0");
+       root->add_child("CheckForTestUpdates")->add_child_text (_check_for_test_updates ? "1" : "0");
+
        doc.write_to_file_formatted (file(false).string ());
 }
 
index 67d29388461f4a688eb4ec982f2954efa2899da6..791e41e8f6f27a14b5626e492fc714c824da027f 100644 (file)
@@ -171,6 +171,14 @@ public:
                return _kdm_email;
        }
 
+       bool check_for_updates () const {
+               return _check_for_updates;
+       }
+
+       bool check_for_test_updates () const {
+               return _check_for_test_updates;
+       }
+       
        /** @param n New number of local encoding threads */
        void set_num_local_encoding_threads (int n) {
                _num_local_encoding_threads = n;
@@ -284,6 +292,14 @@ public:
        void set_kdm_email (std::string e) {
                _kdm_email = e;
        }
+
+       void set_check_for_updates (bool c) {
+               _check_for_updates = c;
+       }
+
+       void set_check_for_test_updates (bool c) {
+               _check_for_test_updates = c;
+       }
        
        void write () const;
 
@@ -341,6 +357,9 @@ private:
        std::string _mail_password;
        std::string _kdm_from;
        std::string _kdm_email;
+       /** true to check for updates on startup */
+       bool _check_for_updates;
+       bool _check_for_test_updates;
 
        /** Singleton instance, or 0 */
        static Config* _instance;
index 17064fd45ce10b0827fdb5c6ffddce51c363ed4c..12fe2b8a61d757f4c561bb4bcd867782d13edfdf 100644 (file)
@@ -36,7 +36,7 @@ using boost::shared_ptr;
 using boost::lexical_cast;
 using boost::bad_lexical_cast;
 
-ImageExaminer::ImageExaminer (shared_ptr<const Film> film, shared_ptr<const ImageContent> content, shared_ptr<Job> job)
+ImageExaminer::ImageExaminer (shared_ptr<const Film> film, shared_ptr<const ImageContent> content, shared_ptr<Job>)
        : _film (film)
        , _image_content (content)
        , _video_length (0)
index d68979e60d7e44a8da0b3d58e252539802bb1720..6e5f92edc9eee06d9ea86a56502312bcfd1e6d08 100644 (file)
@@ -47,7 +47,7 @@ UpdateChecker::UpdateChecker ()
        , _offset (0)
        , _curl (0)
        , _state (NOT_RUN)
-       , _startup (true)
+       , _emits (0)
 {
        curl_global_init (CURL_GLOBAL_ALL);
        _curl = curl_easy_init ();
@@ -59,66 +59,78 @@ UpdateChecker::UpdateChecker ()
        
        string const agent = "dcpomatic/" + string (dcpomatic_version);
        curl_easy_setopt (_curl, CURLOPT_USERAGENT, agent.c_str ());
+
+       _thread = new boost::thread (boost::bind (&UpdateChecker::thread, this));
 }
 
 UpdateChecker::~UpdateChecker ()
 {
+       /* We are not cleaning up our thread, but hey well */
+       
        curl_easy_cleanup (_curl);
        curl_global_cleanup ();
        delete[] _buffer;
 }
 
 void
-UpdateChecker::run (bool startup)
-try
+UpdateChecker::run ()
 {
-       boost::mutex::scoped_lock lm (_single_thread_mutex);
-
-       {
-               boost::mutex::scoped_lock lm (_data_mutex);
-               _startup = startup;
-       }
-       
-       _offset = 0;
-       
-       int r = curl_easy_perform (_curl);
-       if (r != CURLE_OK) {
-               set_state (FAILED);
-               return;
-       }
-       
-       _buffer[_offset] = '\0';
-       stringstream s;
-       s << _buffer;
-       cxml::Document doc ("Update");
-       doc.read_stream (s);
-
-       {
-               boost::mutex::scoped_lock lm (_data_mutex);
-               _stable = doc.string_child ("Stable");
-       }
-       
-       string current = string (dcpomatic_version);
-       bool current_pre = false;
-       if (boost::algorithm::ends_with (current, "pre")) {
-               current = current.substr (0, current.length() - 3);
-               current_pre = true;
-       }
-
-       float current_float = lexical_cast<float> (current);
-       if (current_pre) {
-               current_float -= 0.005;
-       }
+       boost::mutex::scoped_lock lm (_process_mutex);
+       _condition.notify_one ();
+}
 
-       if (current_float < lexical_cast<float> (_stable)) {
-               set_state (YES);
-       } else {
-               set_state (NO);
+void
+UpdateChecker::thread ()
+{
+       while (1) {
+               boost::mutex::scoped_lock lock (_process_mutex);
+               _condition.wait (lock);
+               lock.unlock ();
+               
+               try {
+                       _offset = 0;
+                       
+                       int r = curl_easy_perform (_curl);
+                       if (r != CURLE_OK) {
+                               set_state (FAILED);
+                               return;
+                       }
+                       
+                       _buffer[_offset] = '\0';
+                       stringstream s;
+                       s << _buffer;
+                       cxml::Document doc ("Update");
+                       doc.read_stream (s);
+                       
+                       {
+                               boost::mutex::scoped_lock lm (_data_mutex);
+                               _stable = doc.string_child ("Stable");
+                               _test = doc.string_child ("Test");
+                       }
+                       
+                       string current = string (dcpomatic_version);
+                       bool current_pre = false;
+                       if (boost::algorithm::ends_with (current, "pre")) {
+                               current = current.substr (0, current.length() - 3);
+                               current_pre = true;
+                       }
+                       
+                       float current_float = lexical_cast<float> (current);
+                       if (current_pre) {
+                               current_float -= 0.005;
+                       }
+                       
+                       if (current_float < lexical_cast<float> (_stable)) {
+                               set_state (YES);
+                       } else {
+                               set_state (NO);
+                       }
+               } catch (...) {
+                       set_state (FAILED);
+               }
        }
-} catch (...) {
-       set_state (FAILED);
 }
-
+       
 size_t
 UpdateChecker::write_callback (void* data, size_t size, size_t nmemb)
 {
@@ -134,8 +146,9 @@ UpdateChecker::set_state (State s)
        {
                boost::mutex::scoped_lock lm (_data_mutex);
                _state = s;
+               _emits++;
        }
-       
+
        ui_signaller->emit (boost::bind (boost::ref (StateChanged)));
 }
 
index b879e902696d1c83e7e01c448545448d15b428e9..a1aefd0a1477bb4b7063105010d5de8bfd61e5b8 100644 (file)
@@ -19,6 +19,7 @@
 
 #include <boost/signals2.hpp>
 #include <boost/thread/mutex.hpp>
+#include <boost/thread/condition.hpp>
 #include <curl/curl.h>
 
 class UpdateChecker
@@ -27,7 +28,7 @@ public:
        UpdateChecker ();
        ~UpdateChecker ();
 
-       void run (bool);
+       void run ();
 
        enum State {
                YES,
@@ -46,10 +47,15 @@ public:
                return _stable;
        }
 
-       /** @return true if this check was run at startup, otherwise false */
-       bool startup () const {
+       std::string test () {
                boost::mutex::scoped_lock lm (_data_mutex);
-               return _startup;
+               return _test;
+       }
+       
+       /** @return true if the list signal emission was the first */
+       bool last_emit_was_first () const {
+               boost::mutex::scoped_lock lm (_data_mutex);
+               return _emits == 1;
        }
 
        size_t write_callback (void *, size_t, size_t);
@@ -62,18 +68,20 @@ private:
        static UpdateChecker* _instance;
 
        void set_state (State);
+       void thread ();
 
        char* _buffer;
        int _offset;
        CURL* _curl;
 
-       /** mutex to protect _state, _stable and _startup */
+       /** mutex to protect _state, _stable, _test and _emits */
        mutable boost::mutex _data_mutex;
        State _state;
        std::string _stable;
-       /** true if this check was run at startup, otherwise false */
-       bool _startup;
+       std::string _test;
+       int _emits;
 
-       /** mutex to ensure that only one query runs at once */
-       boost::mutex _single_thread_mutex;
+       boost::thread* _thread;
+       boost::mutex _process_mutex;
+       boost::condition _condition;
 };
index fcb47f2d1b95fe7f714445c806f3693084e425c0..4e23cf09d7a034bfbc4a6d5ab9e2017ede019390 100644 (file)
@@ -42,6 +42,7 @@
 #include "wx/kdm_dialog.h"
 #include "wx/servers_list_dialog.h"
 #include "wx/hints_dialog.h"
+#include "wx/update_dialog.h"
 #include "lib/film.h"
 #include "lib/config.h"
 #include "lib/util.h"
@@ -536,7 +537,7 @@ private:
 
        void tools_check_for_updates ()
        {
-               UpdateChecker::instance()->run (false);
+               UpdateChecker::instance()->run ();
        }
 
        void help_about ()
@@ -650,15 +651,18 @@ class App : public wxApp
                _frame->Maximize ();
                _frame->Show ();
 
-               UpdateChecker::instance()->StateChanged.connect (boost::bind (&App::update_checker_state_changed, this));
-
                ui_signaller = new wxUISignaller (this);
                Bind (wxEVT_IDLE, boost::bind (&App::idle, this));
 
                Bind (wxEVT_TIMER, boost::bind (&App::check, this));
                _timer.reset (new wxTimer (this));
                _timer->Start (1000);
-               
+
+               UpdateChecker::instance()->StateChanged.connect (boost::bind (&App::update_checker_state_changed, this));
+               if (Config::instance()->check_for_updates ()) {
+                       UpdateChecker::instance()->run ();
+               }
+
                return true;
        }
        catch (exception& e)
@@ -709,21 +713,19 @@ class App : public wxApp
        {
                switch (UpdateChecker::instance()->state ()) {
                case UpdateChecker::YES:
-                       error_dialog (
-                               _frame,
-                               wxString::Format (
-                                       _("A new version %s of DCP-o-matic is available from http://dcpomatic.com/download"),
-                                       std_to_wx (UpdateChecker::instance()->stable()).wx_str ()
-                                       )
-                               );
+               {
+                       UpdateDialog* dialog = new UpdateDialog (_frame, UpdateChecker::instance()->stable (), UpdateChecker::instance()->test ());
+                       dialog->ShowModal ();
+                       dialog->Destroy ();
                        break;
+               }
                case UpdateChecker::NO:
-                       if (!UpdateChecker::instance()->startup ()) {
+                       if (!UpdateChecker::instance()->last_emit_was_first ()) {
                                error_dialog (_frame, _("There are no new versions of DCP-o-matic available."));
                        }
                        break;
                case UpdateChecker::FAILED:
-                       if (!UpdateChecker::instance()->startup ()) {
+                       if (!UpdateChecker::instance()->last_emit_was_first ()) {
                                error_dialog (_frame, _("The DCP-o-matic download server could not be contacted."));
                        }
                default:
index 52c08018f7724c2284d4ad14b9e87a97b18b9e68..2b07dd1dc6242836f1bb02ed15cf95848a201f19 100644 (file)
@@ -135,6 +135,14 @@ ConfigDialog::make_misc_panel ()
        add_label_to_sizer (table, _misc_panel, _("From address for KDM emails"), true);
        _kdm_from = new wxTextCtrl (_misc_panel, wxID_ANY);
        table->Add (_kdm_from, 1, wxEXPAND | wxALL);
+
+       _check_for_updates = new wxCheckBox (_misc_panel, wxID_ANY, _("Check for updates on startup"));
+       table->Add (_check_for_updates, 1, wxEXPAND | wxALL);
+       table->AddSpacer (0);
+
+       _check_for_test_updates = new wxCheckBox (_misc_panel, wxID_ANY, _("Check for testing updates as well as stable ones"));
+       table->Add (_check_for_test_updates, 1, wxEXPAND | wxALL);
+       table->AddSpacer (0);
        
        Config* config = Config::instance ();
 
@@ -171,6 +179,10 @@ ConfigDialog::make_misc_panel ()
        _mail_password->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&ConfigDialog::mail_password_changed, this));
        _kdm_from->SetValue (std_to_wx (config->kdm_from ()));
        _kdm_from->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&ConfigDialog::kdm_from_changed, this));
+       _check_for_updates->SetValue (config->check_for_updates ());
+       _check_for_updates->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&ConfigDialog::check_for_updates_changed, this));
+       _check_for_test_updates->SetValue (config->check_for_test_updates ());
+       _check_for_test_updates->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&ConfigDialog::check_for_test_updates_changed, this));
 }
 
 void
@@ -578,3 +590,15 @@ ConfigDialog::kdm_email_changed ()
 {
        Config::instance()->set_kdm_email (wx_to_std (_kdm_email->GetValue ()));
 }
+
+void
+ConfigDialog::check_for_updates_changed ()
+{
+       Config::instance()->set_check_for_updates (_check_for_updates->GetValue ());
+}
+
+void
+ConfigDialog::check_for_test_updates_changed ()
+{
+       Config::instance()->set_check_for_test_updates (_check_for_test_updates->GetValue ());
+}
index 49b466bcb9d31d4a080b240d5e9fc6cef3308024..8a17de58db9b460abf130692bf1a7a9fa7a656b4 100644 (file)
@@ -76,6 +76,9 @@ private:
        void make_colour_conversions_panel ();
        void make_kdm_email_panel ();
 
+       void check_for_updates_changed ();
+       void check_for_test_updates_changed ();
+
        wxNotebook* _notebook;
        wxPanel* _misc_panel;
        wxPanel* _defaults_panel;
@@ -110,6 +113,8 @@ private:
        wxPanel* _kdm_email_panel;
        wxTextCtrl* _kdm_email;
        wxCheckBox* _use_any_servers;
+       wxCheckBox* _check_for_updates;
+       wxCheckBox* _check_for_test_updates;
        EditableList<std::string, ServerDialog>* _servers_list;
 };
 
diff --git a/src/wx/update_dialog.cc b/src/wx/update_dialog.cc
new file mode 100644 (file)
index 0000000..271c417
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+
+    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
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <wx/hyperlink.h>
+#include "update_dialog.h"
+#include "wx_util.h"
+
+using std::string;
+
+UpdateDialog::UpdateDialog (wxWindow* parent, string stable, string test)
+       : wxDialog (parent, wxID_ANY, _("Update"))
+{
+       wxBoxSizer* overall_sizer = new wxBoxSizer (wxVERTICAL);
+
+       wxStaticText* message;
+
+       if (test.empty ()) {
+               message = new wxStaticText (this, wxID_ANY, _("A new version of DCP-o-matic is available."));
+       } else {
+               message = new wxStaticText (this, wxID_ANY, _("New versions of DCP-o-matic are available."));
+       }
+
+       overall_sizer->Add (message, 1, wxTOP | wxLEFT | wxRIGHT, DCPOMATIC_DIALOG_BORDER);
+
+       wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
+
+       add_label_to_sizer (table, this, _("Stable version ") + std_to_wx (stable), true);
+       wxHyperlinkCtrl* h = new wxHyperlinkCtrl (this, wxID_ANY, "dcpomatic.com/download", "http://dcpomatic.com/download");
+       table->Add (h);
+       
+       if (!test.empty ()) {
+               add_label_to_sizer (table, this, _("Test version ") + std_to_wx (test), true);
+               wxHyperlinkCtrl* h = new wxHyperlinkCtrl (this, wxID_ANY, "dcpomatic.com/test-download", "http://dcpomatic.com/test-download");
+               table->Add (h);
+       }
+       
+       overall_sizer->Add (table, 1, wxEXPAND | wxLEFT | wxRIGHT, DCPOMATIC_DIALOG_BORDER);
+
+       wxSizer* buttons = CreateButtonSizer (wxOK);
+       if (buttons) {
+               overall_sizer->Add (buttons, 1, wxEXPAND | wxALL);
+       }
+       
+       SetSizerAndFit (overall_sizer);
+}
diff --git a/src/wx/update_dialog.h b/src/wx/update_dialog.h
new file mode 100644 (file)
index 0000000..d9c7b85
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+    Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+
+    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
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <wx/wx.h>
+
+class UpdateDialog : public wxDialog
+{
+public:
+       UpdateDialog (wxWindow *, std::string, std::string);
+};
+
index 26bf32bdcc6d002b1e6439f2404c734b7007e64c..9de32d39ebdb1b769c074a1443d8977e174436f7 100644 (file)
@@ -39,6 +39,7 @@ sources = """
           timeline.cc
           timeline_dialog.cc
           timing_panel.cc
+          update_dialog.cc
           video_panel.cc
           wx_util.cc
           wx_ui_signaller.cc