Add priority control buttons to batch converter (#961).
authorCarl Hetherington <cth@carlh.net>
Sat, 28 Jan 2017 00:35:55 +0000 (00:35 +0000)
committerCarl Hetherington <cth@carlh.net>
Sat, 28 Jan 2017 00:35:55 +0000 (00:35 +0000)
12 files changed:
ChangeLog
src/lib/job.cc
src/lib/job.h
src/lib/job_manager.cc
src/lib/job_manager.h
src/wx/batch_job_view.cc
src/wx/batch_job_view.h
src/wx/job_manager_view.cc
src/wx/job_manager_view.h
src/wx/job_view.cc
src/wx/job_view.h
src/wx/normal_job_view.cc

index f9744368b8db84c8ffd3ab8ae1b4413ef77b85dd..28316e12e07c93d49f9f5e36ff7cf22aa85783a3 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2017-01-28  Carl Hetherington  <cth@carlh.net>
+
+       * Add priority control buttons to batch converter (#961).
+
 2017-01-18  Carl Hetherington  <cth@carlh.net>
 
        * Updated uk_UA translation from Igor Voytovich.
index dba21f91cdf031362116468df543cca9eda19062..dacc8bf311e4eb1fcbec1f4026d77b0395ecce2e 100644 (file)
@@ -239,10 +239,17 @@ Job::finished_cancelled () const
 }
 
 bool
-Job::paused () const
+Job::paused_by_user () const
 {
        boost::mutex::scoped_lock lm (_state_mutex);
-       return _state == PAUSED;
+       return _state == PAUSED_BY_USER;
+}
+
+bool
+Job::paused_by_priority () const
+{
+       boost::mutex::scoped_lock lm (_state_mutex);
+       return _state == PAUSED_BY_PRIORITY;
 }
 
 /** Set the state of this job.
@@ -287,7 +294,7 @@ Job::check_for_interruption_or_pause ()
        boost::this_thread::interruption_point ();
 
        boost::mutex::scoped_lock lm (_state_mutex);
-       while (_state == PAUSED) {
+       while (_state == PAUSED_BY_USER || _state == PAUSED_BY_PRIORITY) {
                emit (boost::bind (boost::ref (Progress)));
                _pause_changed.wait (lm);
        }
@@ -450,7 +457,8 @@ Job::json_status () const
                return N_("new");
        case RUNNING:
                return N_("running");
-       case PAUSED:
+       case PAUSED_BY_USER:
+       case PAUSED_BY_PRIORITY:
                return N_("paused");
        case FINISHED_OK:
                return N_("finished_ok");
@@ -481,7 +489,7 @@ Job::cancel ()
                return;
        }
 
-       if (paused ()) {
+       if (paused_by_user() || paused_by_priority()) {
                resume ();
        }
 
@@ -493,10 +501,19 @@ Job::cancel ()
 }
 
 void
-Job::pause ()
+Job::pause_by_user ()
+{
+       if (running ()) {
+               set_state (PAUSED_BY_USER);
+               _pause_changed.notify_all ();
+       }
+}
+
+void
+Job::pause_by_priority ()
 {
        if (running ()) {
-               set_state (PAUSED);
+               set_state (PAUSED_BY_PRIORITY);
                _pause_changed.notify_all ();
        }
 }
@@ -504,7 +521,7 @@ Job::pause ()
 void
 Job::resume ()
 {
-       if (paused ()) {
+       if (paused_by_user() || paused_by_priority()) {
                set_state (RUNNING);
                _pause_changed.notify_all ();
        }
index 660ddaa132715dc7e4f4dba1cb647344492de441..311f9cc9104045ef434ed52c5dee7d69459bb7e9 100644 (file)
@@ -50,7 +50,8 @@ public:
        virtual void run () = 0;
 
        void start ();
-       void pause ();
+       void pause_by_user ();
+       void pause_by_priority ();
        void resume ();
        void cancel ();
 
@@ -60,7 +61,8 @@ public:
        bool finished_ok () const;
        bool finished_in_error () const;
        bool finished_cancelled () const;
-       bool paused () const;
+       bool paused_by_user () const;
+       bool paused_by_priority () const;
 
        std::string error_summary () const;
        std::string error_details () const;
@@ -94,7 +96,8 @@ protected:
        enum State {
                NEW,            ///< the job hasn't been started yet
                RUNNING,        ///< the job is running
-               PAUSED,         ///< the job has been paused
+               PAUSED_BY_USER, ///< the job has been paused
+               PAUSED_BY_PRIORITY, ///< the job has been paused
                FINISHED_OK,    ///< the job has finished successfully
                FINISHED_ERROR, ///< the job has finished in error
                FINISHED_CANCELLED ///< the job was cancelled
index c9924d226426c0da60d358d9b362fcd6f4d356f4..3992e685ed4fe76c9ee4c5912ae7cd41fcaac873 100644 (file)
@@ -210,3 +210,72 @@ JobManager::analyse_audio (
 
        emit (boost::bind (boost::ref (JobAdded), weak_ptr<Job> (job)));
 }
+
+void
+JobManager::increase_priority (shared_ptr<Job> job)
+{
+       bool changed = false;
+
+       {
+               boost::mutex::scoped_lock lm (_mutex);
+               list<shared_ptr<Job> >::iterator last = _jobs.end ();
+               for (list<shared_ptr<Job> >::iterator i = _jobs.begin(); i != _jobs.end(); ++i) {
+                       if (*i == job && last != _jobs.end()) {
+                               swap (*i, *last);
+                               changed = true;
+                               break;
+                       }
+                       last = i;
+               }
+       }
+
+       if (changed) {
+               priority_changed ();
+       }
+}
+
+void
+JobManager::priority_changed ()
+{
+       {
+               boost::mutex::scoped_lock lm (_mutex);
+
+               bool first = true;
+               BOOST_FOREACH (shared_ptr<Job> i, _jobs) {
+                       if (first) {
+                               if (i->is_new ()) {
+                                       i->start ();
+                               } else if (i->paused_by_priority ()) {
+                                       i->resume ();
+                               }
+                               first = false;
+                       } else {
+                               if (i->running ()) {
+                                       i->pause_by_priority ();
+                               }
+                       }
+               }
+       }
+
+       emit (boost::bind (boost::ref (JobsReordered)));
+}
+
+void
+JobManager::decrease_priority (shared_ptr<Job> job)
+{
+       bool changed = false;
+
+       for (list<shared_ptr<Job> >::iterator i = _jobs.begin(); i != _jobs.end(); ++i) {
+               list<shared_ptr<Job> >::iterator next = i;
+               ++next;
+               if (*i == job && next != _jobs.end()) {
+                       swap (*i, *next);
+                       changed = true;
+                       break;
+               }
+       }
+
+       if (changed) {
+               priority_changed ();
+       }
+}
index c6be2a78e754a053609513897ade1eceee1fa977..c623b6ef98d7f7fd5831b9644281a10946b58084 100644 (file)
@@ -40,11 +40,12 @@ extern bool wait_for_jobs ();
 class JobManager : public Signaller, public boost::noncopyable
 {
 public:
-
        boost::shared_ptr<Job> add (boost::shared_ptr<Job>);
        std::list<boost::shared_ptr<Job> > get () const;
        bool work_to_do () const;
        bool errors () const;
+       void increase_priority (boost::shared_ptr<Job>);
+       void decrease_priority (boost::shared_ptr<Job>);
 
        void analyse_audio (
                boost::shared_ptr<const Film> film,
@@ -54,6 +55,7 @@ public:
                );
 
        boost::signals2::signal<void (boost::weak_ptr<Job>)> JobAdded;
+       boost::signals2::signal<void ()> JobsReordered;
        boost::signals2::signal<void (boost::optional<std::string>, boost::optional<std::string>)> ActiveJobsChanged;
 
        static JobManager* instance ();
@@ -67,8 +69,10 @@ private:
        ~JobManager ();
        void scheduler ();
        void start ();
+       void priority_changed ();
 
        mutable boost::mutex _mutex;
+       /** List of jobs in the order that they will be executed */
        std::list<boost::shared_ptr<Job> > _jobs;
        bool _terminate;
 
index 2a5e690d7e786790023b50998bb9f88fd426bbce..772c726f2ccf333bfeddb2c192975e72441ff6a0 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012-2016 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2017 Carl Hetherington <cth@carlh.net>
 
     This file is part of DCP-o-matic.
 
 */
 
 #include "batch_job_view.h"
+#include "lib/job_manager.h"
 #include <wx/sizer.h>
+#include <wx/button.h>
 
+using std::list;
 using boost::shared_ptr;
 
 BatchJobView::BatchJobView (shared_ptr<Job> job, wxWindow* parent, wxWindow* container, wxFlexGridSizer* table)
@@ -34,3 +37,44 @@ BatchJobView::insert_position () const
 {
        return _table->GetEffectiveRowsCount() * _table->GetEffectiveColsCount();
 }
+
+void
+BatchJobView::finish_setup (wxWindow* parent, wxSizer* sizer)
+{
+       _higher_priority = new wxButton (parent, wxID_ANY, _("Higher prioirity"));
+       _higher_priority->Bind (wxEVT_BUTTON, boost::bind (&BatchJobView::higher_priority_clicked, this));
+       sizer->Add (_higher_priority, 1, wxALIGN_CENTER_VERTICAL);
+       _lower_priority = new wxButton (parent, wxID_ANY, _("Lower prioirity"));
+       _lower_priority->Bind (wxEVT_BUTTON, boost::bind (&BatchJobView::lower_priority_clicked, this));
+       sizer->Add (_lower_priority, 1, wxALIGN_CENTER_VERTICAL);
+}
+void
+BatchJobView::higher_priority_clicked ()
+{
+       JobManager::instance()->increase_priority (_job);
+}
+
+void
+BatchJobView::lower_priority_clicked ()
+{
+       JobManager::instance()->decrease_priority (_job);
+}
+
+void
+BatchJobView::job_list_changed ()
+{
+       bool high = false;
+       bool low = false;
+       list<shared_ptr<Job> > jobs = JobManager::instance()->get();
+       if (!jobs.empty ()) {
+               if (_job != jobs.front()) {
+                       high = true;
+               }
+               if (_job != jobs.back()) {
+                       low = true;
+               }
+       }
+
+       _higher_priority->Enable (high);
+       _lower_priority->Enable (low);
+}
index d6559686402667723cd7b98c424b231e6f19ba5d..40dceff31bf8d11296d995b8119a8e7b5ca5921c 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012-2016 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2017 Carl Hetherington <cth@carlh.net>
 
     This file is part of DCP-o-matic.
 
@@ -27,4 +27,12 @@ public:
 
 private:
        int insert_position () const;
+       void job_list_changed ();
+
+       void finish_setup (wxWindow* parent, wxSizer* sizer);
+       void higher_priority_clicked ();
+       void lower_priority_clicked ();
+
+       wxButton* _higher_priority;
+       wxButton* _lower_priority;
 };
index 6cea40c8912859b36d55b2dad5df61dc022446b3..42d5f9dbeb5e5aade18cec6c3216abc3ff4a5234 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012-2015 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2017 Carl Hetherington <cth@carlh.net>
 
     This file is part of DCP-o-matic.
 
@@ -31,6 +31,7 @@
 #include "lib/util.h"
 #include "lib/exceptions.h"
 #include "lib/compose.hpp"
+#include <boost/foreach.hpp>
 #include <iostream>
 
 using std::string;
@@ -40,6 +41,7 @@ using std::min;
 using std::cout;
 using boost::shared_ptr;
 using boost::weak_ptr;
+using boost::bind;
 
 /** @param parent Parent window.
  *  @param batch true to use BatchJobView, false to use NormalJobView.
@@ -67,6 +69,7 @@ JobManagerView::JobManagerView (wxWindow* parent, bool batch)
        _timer->Start (1000);
 
        JobManager::instance()->JobAdded.connect (bind (&JobManagerView::job_added, this, _1));
+       JobManager::instance()->JobsReordered.connect (bind (&JobManagerView::replace, this));
 }
 
 void
@@ -85,6 +88,7 @@ JobManagerView::job_added (weak_ptr<Job> j)
        }
 
        FitInside();
+       job_list_changed ();
 }
 
 void
@@ -94,3 +98,41 @@ JobManagerView::periodic ()
                (*i)->maybe_pulse ();
        }
 }
+
+void
+JobManagerView::replace ()
+{
+       /* Make a new version of _job_records which reflects the order in JobManager's job list */
+
+       list<shared_ptr<JobView> > new_job_records;
+
+       BOOST_FOREACH (shared_ptr<Job> i, JobManager::instance()->get()) {
+               /* Find this job's JobView */
+               BOOST_FOREACH (shared_ptr<JobView> j, _job_records) {
+                       if (j->job() == i) {
+                               new_job_records.push_back (j);
+                               break;
+                       }
+               }
+       }
+
+       BOOST_FOREACH (shared_ptr<JobView> i, _job_records) {
+               i->detach ();
+       }
+
+       _job_records = new_job_records;
+
+       BOOST_FOREACH (shared_ptr<JobView> i, _job_records) {
+               i->insert (i->insert_position ());
+       }
+
+       job_list_changed ();
+}
+
+void
+JobManagerView::job_list_changed ()
+{
+       BOOST_FOREACH (shared_ptr<JobView> i, _job_records) {
+               i->job_list_changed ();
+       }
+}
index 7784c71eed3cc639a1393dc9001055c1fd5f7167..77114a97c382b170f06758d58d1d8f2d41fb52af 100644 (file)
@@ -40,6 +40,8 @@ public:
 private:
        void job_added (boost::weak_ptr<Job>);
        void periodic ();
+       void replace ();
+       void job_list_changed ();
 
        wxPanel* _panel;
        wxFlexGridSizer* _table;
index 8e7040c3b855cb1d0f3ae87db05da65b7b7b5d6e..13c3bc7ab691d2896e3bf4b6982ae02f75e04d27 100644 (file)
@@ -52,20 +52,20 @@ JobView::setup ()
        _table->Insert (n, _gauge_message, 1, wxEXPAND | wxLEFT | wxRIGHT);
        ++n;
 
-       wxBoxSizer* buttons = new wxBoxSizer (wxHORIZONTAL);
+       _buttons = new wxBoxSizer (wxHORIZONTAL);
 
        _cancel = new wxButton (_container, wxID_ANY, _("Cancel"));
        _cancel->Bind (wxEVT_BUTTON, &JobView::cancel_clicked, this);
-       buttons->Add (_cancel, 1, wxALIGN_CENTER_VERTICAL);
+       _buttons->Add (_cancel, 1, wxALIGN_CENTER_VERTICAL);
 
        _details = new wxButton (_container, wxID_ANY, _("Details..."));
        _details->Bind (wxEVT_BUTTON, &JobView::details_clicked, this);
        _details->Enable (false);
-       buttons->Add (_details, 1, wxALIGN_CENTER_VERTICAL);
+       _buttons->Add (_details, 1, wxALIGN_CENTER_VERTICAL);
 
-       finish_setup (_container, buttons);
+       finish_setup (_container, _buttons);
 
-       _table->Insert (n, buttons, 1, wxALIGN_CENTER_VERTICAL | wxALL, 3);
+       _table->Insert (n, _buttons, 1, wxALIGN_CENTER_VERTICAL | wxALL, 3);
 
        _progress_connection = _job->Progress.connect (boost::bind (&JobView::progress, this));
        _finished_connection = _job->Finished.connect (boost::bind (&JobView::finished, this));
@@ -134,3 +134,18 @@ JobView::cancel_clicked (wxCommandEvent &)
                _job->cancel ();
        }
 }
+
+void
+JobView::insert (int pos)
+{
+       _table->Insert (pos, _gauge_message, 1, wxEXPAND | wxLEFT | wxRIGHT);
+       _table->Insert (pos + 1, _buttons, 1, wxALIGN_CENTER_VERTICAL | wxALL, 3);
+       _table->Layout ();
+}
+
+void
+JobView::detach ()
+{
+       _table->Detach (_gauge_message);
+       _table->Detach (_buttons);
+}
index 8cd34fdabec809c75d9226fbc9b670e551ae7167..8c0214d9dc9438ad7e80a66ad68dbfc1559976ba 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012-2015 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2017 Carl Hetherington <cth@carlh.net>
 
     This file is part of DCP-o-matic.
 
@@ -42,19 +42,28 @@ public:
        JobView (boost::shared_ptr<Job> job, wxWindow* parent, wxWindow* container, wxFlexGridSizer* table);
        virtual ~JobView () {}
 
-       void setup ();
+       virtual int insert_position () const = 0;
+       virtual void job_list_changed () {}
 
+       void setup ();
        void maybe_pulse ();
+       void insert (int pos);
+       void detach ();
+
+       boost::shared_ptr<Job> job () const {
+               return _job;
+       }
 
 protected:
        virtual void finished ();
 
        boost::shared_ptr<Job> _job;
        wxFlexGridSizer* _table;
+       wxBoxSizer* _buttons;
+       wxBoxSizer* _gauge_message;
 
 private:
 
-       virtual int insert_position () const = 0;
        virtual void finish_setup (wxWindow *, wxSizer *) {}
 
        void progress ();
@@ -63,7 +72,6 @@ private:
 
        wxWindow* _parent;
        wxWindow* _container;
-       wxBoxSizer* _gauge_message;
        wxGauge* _gauge;
        wxStaticText* _message;
        wxButton* _cancel;
index 9bfa332c965b2bb90ba6afe7c88c17d7a1b683c6..22b3e1cc7e9ec07bc448eded42d82d15ad3f6d23 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012-2016 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2017 Carl Hetherington <cth@carlh.net>
 
     This file is part of DCP-o-matic.
 
@@ -48,11 +48,11 @@ NormalJobView::insert_position () const
 void
 NormalJobView::pause_clicked ()
 {
-       if (_job->paused()) {
+       if (_job->paused_by_user()) {
                _job->resume ();
                _pause->SetLabel (_("Pause"));
        } else {
-               _job->pause ();
+               _job->pause_by_user ();
                _pause->SetLabel (_("Resume"));
        }
 }