Allow dragging of the border between the controls and the preview (#2350).
[dcpomatic.git] / src / tools / dcpomatic.cc
index ebcd05a682770c20677d4ca1db22b893d3567303..a10cb9b35505f9b322f2033f79bafaa2c17ce34f 100644 (file)
@@ -78,6 +78,7 @@
 #include "lib/kdm_with_metadata.h"
 #include "lib/log.h"
 #include "lib/make_dcp.h"
+#include "lib/release_notes.h"
 #include "lib/screen.h"
 #include "lib/send_kdm_email_job.h"
 #include "lib/signal_manager.h"
@@ -269,11 +270,54 @@ enum {
 };
 
 
+class LimitedFrameSplitter : public wxSplitterWindow
+{
+public:
+       LimitedFrameSplitter(wxWindow* parent)
+               : wxSplitterWindow(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSP_NOBORDER | wxSP_3DSASH | wxSP_LIVE_UPDATE)
+       {
+               /* This value doesn't really mean much but we just want to stop double-click on the
+                  divider from shrinking the left panel.
+                  */
+               SetMinimumPaneSize(64);
+
+               Bind(wxEVT_SIZE, boost::bind(&LimitedFrameSplitter::sized, this, _1));
+       }
+
+       bool OnSashPositionChange(int new_position) override
+       {
+               /* Try to stop the left bit of the splitter getting too small */
+               return new_position > _left_panel_minimum_size;
+       }
+
+private:
+       void sized(wxSizeEvent& ev)
+       {
+               if (GetSize().GetWidth() > _left_panel_minimum_size && GetSashPosition() < _left_panel_minimum_size) {
+                       /* The window is now fairly big but the left panel is small; this happens when the DCP-o-matic window
+                        * is shrunk and then made larger again.  Try to set a sensible left panel size in this case.
+                        */
+                       SetSashPosition(_left_panel_minimum_size);
+               }
+
+               ev.Skip();
+       }
+
+       int const _left_panel_minimum_size = 200;
+};
+
+
 class DOMFrame : public wxFrame
 {
 public:
        explicit DOMFrame (wxString const& title)
                : wxFrame (nullptr, -1, title)
+               /* Use a panel as the only child of the Frame so that we avoid
+                  the dark-grey background on Windows.
+               */
+               , _splitter(new LimitedFrameSplitter(this))
+               , _right_panel(new wxPanel(_splitter, wxID_ANY))
+               , _film_viewer(_right_panel)
        {
 #if defined(DCPOMATIC_WINDOWS)
                if (Config::instance()->win32_console()) {
@@ -347,33 +391,33 @@ public:
                Bind (wxEVT_CLOSE_WINDOW, boost::bind (&DOMFrame::close, this, _1));
                Bind (wxEVT_SHOW, boost::bind (&DOMFrame::show, this, _1));
 
-               /* Use a panel as the only child of the Frame so that we avoid
-                  the dark-grey background on Windows.
-               */
-               auto overall_panel = new wxPanel (this, wxID_ANY);
+               auto left_panel = new wxPanel(_splitter, wxID_ANY);
+
+               _film_editor = new FilmEditor(left_panel, _film_viewer);
+
+               auto left_sizer = new wxBoxSizer(wxHORIZONTAL);
+               left_sizer->Add(_film_editor, 1, wxEXPAND);
 
-               _film_viewer.reset (new FilmViewer (overall_panel));
-               _controls = new StandardControls (overall_panel, _film_viewer, true);
-               _film_editor = new FilmEditor (overall_panel, _film_viewer);
-               auto job_manager_view = new JobManagerView (overall_panel, false);
+               left_panel->SetSizerAndFit(left_sizer);
+
+               _controls = new StandardControls(_right_panel, _film_viewer, true);
+               auto job_manager_view = new JobManagerView(_right_panel, false);
 
                auto right_sizer = new wxBoxSizer (wxVERTICAL);
-               right_sizer->Add (_film_viewer->panel(), 2, wxEXPAND | wxALL, 6);
+               right_sizer->Add(_film_viewer.panel(), 2, wxEXPAND | wxALL, 6);
                right_sizer->Add (_controls, 0, wxEXPAND | wxALL, 6);
                right_sizer->Add (job_manager_view, 1, wxEXPAND | wxALL, 6);
 
-               wxBoxSizer* main_sizer = new wxBoxSizer (wxHORIZONTAL);
-               main_sizer->Add (_film_editor, 0, wxEXPAND | wxALL, 6);
-               main_sizer->Add (right_sizer, 1, wxEXPAND | wxALL, 6);
+               _right_panel->SetSizer(right_sizer);
+
+               _splitter->SplitVertically(left_panel, _right_panel, left_panel->GetSize().GetWidth() + 8);
 
                set_menu_sensitivity ();
 
                _film_editor->content_panel()->SelectionChanged.connect (boost::bind (&DOMFrame::set_menu_sensitivity, this));
                set_title ();
 
-               JobManager::instance()->ActiveJobsChanged.connect (boost::bind (&DOMFrame::set_menu_sensitivity, this));
-
-               overall_panel->SetSizer (main_sizer);
+               JobManager::instance()->ActiveJobsChanged.connect(boost::bind(&DOMFrame::active_jobs_changed, this));
 
                UpdateChecker::instance()->StateChanged.connect(boost::bind(&DOMFrame::update_checker_state_changed, this));
 
@@ -480,8 +524,8 @@ public:
        void set_film (shared_ptr<Film> film)
        {
                _film = film;
-               _film_viewer->set_film (_film);
-               _film_editor->set_film (_film);
+               _film_viewer.set_film(_film);
+               _film_editor->set_film(_film);
                _controls->set_film (_film);
                if (_video_waveform_dialog) {
                        _video_waveform_dialog->Destroy ();
@@ -1041,7 +1085,7 @@ private:
 
        void view_closed_captions ()
        {
-               _film_viewer->show_closed_captions ();
+               _film_viewer.show_closed_captions ();
        }
 
        void view_video_waveform ()
@@ -1199,6 +1243,15 @@ private:
                ev.Skip ();
        }
 
+       void active_jobs_changed()
+       {
+               /* ActiveJobsChanged can be called while JobManager holds a lock on its mutex, making
+                * the call to JobManager::get() in set_menu_sensitivity() deadlock unless we work around
+                * it by using an idle callback.  This feels quite unpleasant.
+                */
+               signal_manager->when_idle(boost::bind(&DOMFrame::set_menu_sensitivity, this));
+       }
+
        void set_menu_sensitivity ()
        {
                auto jobs = JobManager::instance()->get ();
@@ -1496,10 +1549,10 @@ private:
 
        void start_stop_pressed ()
        {
-               if (_film_viewer->playing()) {
-                       _film_viewer->stop();
+               if (_film_viewer.playing()) {
+                       _film_viewer.stop();
                } else {
-                       _film_viewer->start();
+                       _film_viewer.start();
                }
        }
 
@@ -1510,12 +1563,12 @@ private:
 
        void back_frame ()
        {
-               _film_viewer->seek_by (-_film_viewer->one_video_frame(), true);
+               _film_viewer.seek_by(-_film_viewer.one_video_frame(), true);
        }
 
        void forward_frame ()
        {
-               _film_viewer->seek_by (_film_viewer->one_video_frame(), true);
+               _film_viewer.seek_by(_film_viewer.one_video_frame(), true);
        }
 
        void analytics_message (string title, string html)
@@ -1541,7 +1594,9 @@ private:
        }
 
        FilmEditor* _film_editor;
-       std::shared_ptr<FilmViewer> _film_viewer;
+       LimitedFrameSplitter* _splitter;
+       wxPanel* _right_panel;
+       FilmViewer _film_viewer;
        StandardControls* _controls;
        VideoWaveformDialog* _video_waveform_dialog = nullptr;
        SystemInformationDialog* _system_information_dialog = nullptr;
@@ -1596,7 +1651,7 @@ private:
                try {
                        wxInitAllImageHandlers ();
 
-                       Config::FailedToLoad.connect (boost::bind (&App::config_failed_to_load, this));
+                       Config::FailedToLoad.connect(boost::bind(&App::config_failed_to_load, this, _1));
                        Config::Warning.connect (boost::bind (&App::config_warning, this, _1));
 
                        _splash = maybe_show_splash ();
@@ -1686,6 +1741,14 @@ private:
                        if (Config::instance()->check_for_updates ()) {
                                UpdateChecker::instance()->run ();
                        }
+
+                       auto release_notes = find_release_notes();
+                       if (release_notes) {
+                               auto notes = new HTMLDialog(nullptr, _("Release notes"), std_to_wx(*release_notes), true);
+                               notes->Centre();
+                               notes->ShowModal();
+                               notes->Destroy();
+                       }
                }
                catch (exception& e)
                {
@@ -1810,9 +1873,9 @@ private:
                }
        }
 
-       void config_failed_to_load ()
+       void config_failed_to_load (Config::LoadFailure what)
        {
-               message_dialog (_frame, _("The existing configuration failed to load.  Default values will be used instead.  These may take a short time to create."));
+               report_config_load_failure(_frame, what);
        }
 
        void config_warning (string m)