Improve ratings dialog to allow only valid values (#2199).
authorCarl Hetherington <cth@carlh.net>
Tue, 1 Mar 2022 21:41:56 +0000 (22:41 +0100)
committerCarl Hetherington <cth@carlh.net>
Wed, 9 Mar 2022 16:04:02 +0000 (17:04 +0100)
19 files changed:
cscript
platform/osx/make_dmg.sh
platform/windows/wscript
run/tests
run/tests.bat
src/lib/cross.h
src/lib/cross_linux.cc
src/lib/cross_osx.cc
src/lib/cross_windows.cc
src/lib/dcp_content.h
src/lib/dcp_examiner.h
src/lib/film.h
src/lib/util.cc
src/lib/verify_dcp_job.cc
src/wx/rating_dialog.cc
src/wx/rating_dialog.h
src/wx/wscript
src/wx/wx_util.cc
wscript

diff --git a/cscript b/cscript
index e5b88109817689e810a70b62d892db19de4dcf0a..06bf0eb51c69fe187169939aae63c224b14d9f37 100644 (file)
--- a/cscript
+++ b/cscript
@@ -354,6 +354,7 @@ def make_spec(filename, version, target, options, requires=None):
         print('%%{_datadir}/locale/%s/LC_MESSAGES/libdcpomatic2.mo' % l, file=f)
     print('%{_datadir}/libdcp/tags/*', file=f)
     print('%{_datadir}/libdcp/xsd/*', file=f)
+    print('%{_datadir}/libdcp/ratings', file=f)
     print('%{_datadir}/polkit-1/actions/com.dcpomatic.write-drive.policy', file=f)
     print('', file=f)
     print('%prep', file=f)
@@ -372,6 +373,7 @@ def make_spec(filename, version, target, options, requires=None):
     print('/bin/mkdir -p %{buildroot}/usr/share/libdcp', file=f)
     print('/bin/cp -r %s/src/libdcp/tags %%{buildroot}/usr/share/libdcp' % target.directory, file=f)
     print('/bin/cp -r %s/src/libdcp/xsd %%{buildroot}/usr/share/libdcp' % target.directory, file=f)
+    print('/bin/cp %s/src/libdcp/ratings %%{buildroot}/usr/share/libdcp' % target.directory, file=f)
     print('/bin/mv %s/bin/dcpverify %%{buildroot}/usr/bin/dcpomatic2_verify' % target.directory, file=f)
     print('', file=f)
     print('%post', file=f)
@@ -400,8 +402,8 @@ def dependencies(target, options):
         # Use distro-provided FFmpeg on Arch
         deps = []
 
-    deps.append(('libdcp', 'v1.8.8'))
-    deps.append(('libsub', 'v1.6.8'))
+    deps.append(('libdcp', 'v1.8.10'))
+    deps.append(('libsub', 'v1.6.10'))
     deps.append(('leqm-nrt', '93ae9e6'))
     deps.append(('rtaudio', 'f619b76'))
     # We get our OpenSSL libraries from the environment, but we
@@ -676,6 +678,7 @@ def make_appimage(target, nice_name, internal_name, version):
     target.command(f'cp -r {target.directory}/share/dcpomatic2 {appdir}/usr/share/')
     target.command(f'cp -r {target.directory}/share/libdcp/xsd {appdir}/usr/share/libdcp/')
     target.command(f'cp -r {target.directory}/share/libdcp/tags {appdir}/usr/share/libdcp/')
+    target.command(f'cp {target.directory}/share/libdcp/ratings {appdir}/usr/share/libdcp/')
     lib = 'usr/lib/x86_64-linux-gnu'
     target.command(f'mkdir -p build/{nice_filename}.AppDir/{lib}/gdk-pixbuf-2.0/2.10.0')
     target.command(f'cp -a /{lib}/gdk-pixbuf-2.0 build/{nice_filename}.AppDir/usr/lib/x86_64-linux-gnu/')
index f997af8175713f2ce2e2cf08b8ae9cb95783acf3..6f36839f77a10683000e3fcdbba754f63e532eb7 100644 (file)
@@ -244,6 +244,7 @@ function copy_resources {
     cp $prefix/src/dcpomatic/graphics/no_tick.png "$dest"
     cp -r $prefix/share/libdcp/xsd "$dest"
     cp -r $prefix/share/libdcp/tags "$dest"
+    cp -r $prefix/share/libdcp/ratings "$dest"
 
     # i18n: DCP-o-matic .mo files
     for lang in de_DE es_ES fr_FR it_IT sv_SE nl_NL ru_RU pl_PL da_DK pt_PT pt_BR sk_SK cs_CZ uk_UA zh_CN tr_TR; do
index 426b9b7aedc61ba4969b1097a9979dcb5c6f5bdf..46974ed0f553c4dba607597a88f22b069255088d 100644 (file)
@@ -336,6 +336,8 @@ File "%cdist_deps%/share/libdcp/tags/region"
 File "%cdist_deps%/share/libdcp/tags/script"
 File "%cdist_deps%/share/libdcp/tags/variant"
 File "%cdist_deps%/share/libdcp/tags/dcnc"
+SetOutPath "$INSTDIR"
+File "%cdist_deps%/share/libdcp/ratings"
 
 SectionEnd
     """, file=f)
index aa8c8c1934332eb0b151017e27dcabc29d0f934e..ab0fcf2f51ba43432e796e3c8d12ae1674430010 100755 (executable)
--- a/run/tests
+++ b/run/tests
@@ -18,6 +18,7 @@ if [ "$(uname)" == "Darwin" ]; then
   cp fonts/*.ttf $resources
   cp -r ../libdcp/tags $resources
   cp -r ../libdcp/xsd $resources
+  cp ../libdcp/ratings $resources
   rm -f build/test/openssl
   ln -s ../../../openssl/apps/openssl build/test/openssl
   # SIP stops this being passed in from the caller's environment
index a6ae38fa7a9cadc6755be0f12880596d7f9a8640..598338f1782b1deb859ed283f3774b13e32cd03a 100644 (file)
@@ -1,6 +1,7 @@
 set PATH=%PATH%;c:\users\ci\bin;c:\users\ci\workspace\dcpomatic\bin;c:\users\ci\workspace\dcpomatic\lib
 set DCPOMATIC_TEST_PRIVATE=c:\users\ci\dcpomatic-test-private
 xcopy ..\libdcp\tags build\tags\
+copy ..\libdcp\ratings build\
 copy ..\openssl\apps\openssl.exe build\test\
 xcopy fonts build\fonts\
 build\test\unit-tests.exe --log_level=test_suite %1 %2
index aad223d60fd4d392310124e07ff2192cfdbdc4c7..99c6bc29979c3a938c2a91b40f858c2d6a49e3b4 100644 (file)
@@ -53,8 +53,7 @@ extern boost::filesystem::path disk_writer_path ();
 extern void maybe_open_console ();
 #endif
 extern boost::filesystem::path resources_path ();
-extern boost::filesystem::path xsd_path ();
-extern boost::filesystem::path tags_path ();
+extern boost::filesystem::path libdcp_resources_path ();
 extern FILE * fopen_boost (boost::filesystem::path, std::string);
 extern int dcpomatic_fseek (FILE *, int64_t, int);
 extern void start_batch_converter ();
index 07a70cc0b871a1920d6fdfef9facc753c4a7aec2..14b8c71ba7bd9e23aa9425b5eaf27d1c8177ddbf 100644 (file)
@@ -109,22 +109,12 @@ resources_path ()
 
 
 boost::filesystem::path
-xsd_path ()
+libdcp_resources_path ()
 {
        if (auto appdir = getenv("APPDIR")) {
-               return boost::filesystem::path(appdir) / "usr" / "share" / "libdcp" / "xsd";
+               return boost::filesystem::path(appdir) / "usr" / "share" / "libdcp";
        }
-       return boost::filesystem::canonical(LINUX_SHARE_PREFIX) / "libdcp" / "xsd";
-}
-
-
-boost::filesystem::path
-tags_path ()
-{
-       if (auto appdir = getenv("APPDIR")) {
-               return boost::filesystem::path(appdir) / "usr" / "share" / "libdcp" / "tags";
-       }
-       return boost::filesystem::canonical(LINUX_SHARE_PREFIX) / "libdcp" / "tags";
+       return boost::filesystem::canonical(LINUX_SHARE_PREFIX) / "libdcp";
 }
 
 
index b06f45a77e82c14b86ec3c1c88bc3535e84f4c67..b11ac90ef3251fee485e81ae0ffa4ab87c5eaf37 100644 (file)
@@ -115,16 +115,9 @@ resources_path ()
 
 
 boost::filesystem::path
-xsd_path ()
+libdcp_resources_path ()
 {
-       return resources_path() / "xsd";
-}
-
-
-boost::filesystem::path
-tags_path ()
-{
-       return resources_path() / "tags";
+       return resources_path();
 }
 
 
index 4990f53eecb3f1634f81545919b6f34fc55c8678..324b8cd8bd5cab03e3eb59266f7fbb2ea12f499a 100644 (file)
@@ -210,16 +210,9 @@ resources_path ()
 
 
 boost::filesystem::path
-xsd_path ()
+libdcp_resources_path ()
 {
-       return directory_containing_executable().parent_path() / "xsd";
-}
-
-
-boost::filesystem::path
-tags_path ()
-{
-       return directory_containing_executable().parent_path() / "tags";
+       return resources_path ();
 }
 
 
index 69520fbd65a017fde85da1099105a3945d0286d2..40ed181fe7f383d6489f2e14c9d88e2d2b19b788 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2014-2018 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2014-2022 Carl Hetherington <cth@carlh.net>
 
     This file is part of DCP-o-matic.
 
 
 */
 
+
 #ifndef DCPOMATIC_DCP_CONTENT_H
 #define DCPOMATIC_DCP_CONTENT_H
 
+
 /** @file  src/lib/dcp_content.h
  *  @brief DCPContent class.
  */
 
+
 #include "content.h"
 #include <libcxml/cxml.h>
 #include <dcp/encrypted_kdm.h>
+#include <dcp/rating.h>
+
 
 class DCPContentProperty
 {
@@ -42,7 +47,6 @@ public:
        static int const CPL;
 };
 
-class ContentPart;
 
 /** @class DCPContent
  *  @brief An existing DCP used as input.
@@ -217,4 +221,5 @@ private:
        std::vector<std::string> _content_versions;
 };
 
+
 #endif
index fd643a754ff78cfa2caaa7bf80a332173810360f..757e3ff031a279724487b67c98662c021b4f2b8e 100644 (file)
 
 */
 
+
 /** @file  src/lib/dcp_examiner.h
  *  @brief DCPExaminer class.
  */
 
-#include "video_examiner.h"
+
 #include "audio_examiner.h"
 #include "dcp.h"
 #include "dcp_text_track.h"
 #include "dcpomatic_assert.h"
+#include "video_examiner.h"
 #include <dcp/dcp_time.h>
+#include <dcp/rating.h>
+
 
 class DCPContent;
 
+
 class DCPExaminer : public DCP, public VideoExaminer, public AudioExaminer
 {
 public:
index 78a66e17f24d9903e65afcde30a259681bf2a77b..a1bec3d53b93763a298c467232e1bad28f3d00ec 100644 (file)
@@ -36,6 +36,7 @@
 #include <dcp/encrypted_kdm.h>
 #include <dcp/key.h>
 #include <dcp/language_tag.h>
+#include <dcp/rating.h>
 #include <boost/filesystem.hpp>
 #include <boost/signals2.hpp>
 #include <boost/thread.hpp>
index c165a5129bae42bfe646b772d56d246a7102935f..ccd505e57d8ee263eee8c20b484f9b0e55760d32 100644 (file)
@@ -399,7 +399,7 @@ DCPOMATIC_ENABLE_WARNINGS
 #endif
 
        Pango::init ();
-       dcp::init (tags_path());
+       dcp::init (libdcp_resources_path());
 
 #if defined(DCPOMATIC_WINDOWS) || defined(DCPOMATIC_OSX)
        /* Render something to fontconfig to create its cache */
index 1b30b2112d6395329ab640cdebcb02356d6768a2..b59f5bfabb34eb43964295fd35cad2865500465c 100644 (file)
@@ -76,7 +76,7 @@ VerifyDCPJob::update_stage (string s, optional<boost::filesystem::path> path)
 void
 VerifyDCPJob::run ()
 {
-       _notes = dcp::verify (_directories, bind (&VerifyDCPJob::update_stage, this, _1, _2), bind (&VerifyDCPJob::set_progress, this, _1, false), xsd_path());
+       _notes = dcp::verify (_directories, bind (&VerifyDCPJob::update_stage, this, _1, _2), bind (&VerifyDCPJob::set_progress, this, _1, false), libdcp_resources_path() / "xsd");
 
        bool failed = false;
        for (auto i: _notes) {
index 6f574e83bab97cab4687d81e99778487c81a04cf..9886436a062c0f4e0ed3445f17d190585de19fc9 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2019 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2019-2022 Carl Hetherington <cth@carlh.net>
 
     This file is part of DCP-o-matic.
 
 
 */
 
+
+#include "dcpomatic_spin_ctrl.h"
 #include "rating_dialog.h"
 #include "wx_util.h"
+#include <unicode/unistr.h>
+#include <wx/listctrl.h>
+#include <wx/notebook.h>
+#include <wx/srchctrl.h>
+
+
+using std::string;
+using std::vector;
+using boost::optional;
+#if BOOST_VERSION >= 106100
+using namespace boost::placeholders;
+#endif
+
 
 RatingDialog::RatingDialog (wxWindow* parent)
-       : TableDialog (parent, _("Ratings"), 2, 1, true)
+       : wxDialog (parent, wxID_ANY, _("Rating"))
 {
-       add (_("Agency"), true);
-       _agency = new wxTextCtrl (this, wxID_ANY);
-       add (_agency);
+       _notebook = new wxNotebook (this, wxID_ANY);
+
+       _standard_page = new StandardRatingDialogPage (_notebook);
+       _custom_page = new CustomRatingDialogPage (_notebook);
+
+       _notebook->AddPage (_standard_page, _("Standard"));
+       _notebook->AddPage (_custom_page, _("Custom"));
+
+       _active_page = _standard_page;
 
-       add (_("Label"), true);
-       _label = new wxTextCtrl (this, wxID_ANY);
-       add (_label);
+       auto overall_sizer = new wxBoxSizer (wxVERTICAL);
+       overall_sizer->Add (_notebook, 1, wxEXPAND | wxALL, DCPOMATIC_DIALOG_BORDER);
 
-       layout ();
+       auto buttons = CreateSeparatedButtonSizer (wxOK | wxCANCEL);
+       if (buttons) {
+               overall_sizer->Add (buttons, wxSizerFlags().Expand().DoubleBorder());
+       }
 
-       _agency->SetFocus ();
+       SetSizerAndFit (overall_sizer);
+
+       _notebook->Bind(wxEVT_NOTEBOOK_PAGE_CHANGED, boost::bind(&RatingDialog::page_changed, this));
+
+       _standard_page->Changed.connect(boost::bind(&RatingDialog::setup_sensitivity, this, _1));
+       _custom_page->Changed.connect(boost::bind(&RatingDialog::setup_sensitivity, this, _1));
 }
 
+
 void
-RatingDialog::set (dcp::Rating r)
+RatingDialog::page_changed ()
 {
-       _agency->SetValue (std_to_wx(r.agency));
-       _label->SetValue (std_to_wx(r.label));
+       if (_notebook->GetSelection() == 0) {
+               _active_page = _standard_page;
+       } else {
+               _active_page = _custom_page;
+       }
 }
 
+
+void
+RatingDialog::set (dcp::Rating rating)
+{
+       if (_standard_page->set(rating)) {
+               _notebook->SetSelection(0);
+       } else {
+               _custom_page->set(rating);
+               _notebook->SetSelection(1);
+       }
+}
+
+
 dcp::Rating
 RatingDialog::get () const
 {
-       return dcp::Rating(wx_to_std(_agency->GetValue()), wx_to_std(_label->GetValue()));
+       return _active_page->get();
+}
+
+
+void
+RatingDialog::setup_sensitivity (bool ok_valid)
+{
+       auto ok = dynamic_cast<wxButton *>(FindWindowById(wxID_OK, this));
+       if (ok) {
+               ok->Enable (ok_valid);
+       }
+}
+
+
+RatingDialogPage::RatingDialogPage (wxNotebook* notebook)
+       : wxPanel (notebook, wxID_ANY)
+{
+
 }
+
+
+StandardRatingDialogPage::StandardRatingDialogPage (wxNotebook* notebook)
+       : RatingDialogPage (notebook)
+{
+       _search = new wxSearchCtrl (this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(200, search_ctrl_height()));
+#ifndef __WXGTK3__
+       /* The cancel button seems to be strangely broken in GTK3; clicking on it twice sometimes works */
+       _search->ShowCancelButton (true);
+#endif
+
+       _found_systems_view = new wxListView (this, wxID_ANY, wxDefaultPosition, wxSize(600, 400), wxLC_REPORT | wxLC_SINGLE_SEL | wxLC_NO_HEADER);
+       _found_systems_view->AppendColumn (wxT(""), wxLIST_FORMAT_LEFT, 150);
+       _found_systems_view->AppendColumn (wxT(""), wxLIST_FORMAT_LEFT, 50);
+       _found_systems_view->AppendColumn (wxT(""), wxLIST_FORMAT_LEFT, 400);
+       _rating = new wxChoice (this, wxID_ANY);
+
+       auto sizer = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
+
+       add_label_to_sizer (sizer, this, _("Agency"), true, 0, wxALIGN_CENTER_VERTICAL);
+       sizer->Add (_search, 0, wxEXPAND, DCPOMATIC_SIZER_Y_GAP);
+
+       sizer->AddSpacer (0);
+       sizer->Add (_found_systems_view, 1, wxEXPAND | wxBOTTOM, DCPOMATIC_SIZER_Y_GAP);
+
+       add_label_to_sizer (sizer, this, _("Rating"), true, 0, wxALIGN_CENTER_VERTICAL);
+       sizer->Add (_rating, 1, wxEXPAND);
+
+       auto pad_sizer = new wxBoxSizer (wxVERTICAL);
+       pad_sizer->Add (sizer, 1, wxEXPAND | wxALL, DCPOMATIC_DIALOG_BORDER);
+
+       SetSizerAndFit (pad_sizer);
+
+       _search->Bind (wxEVT_TEXT, boost::bind(&StandardRatingDialogPage::search_changed, this));
+       _found_systems_view->Bind (wxEVT_LIST_ITEM_SELECTED, boost::bind(&StandardRatingDialogPage::found_systems_view_selection_changed, this));
+       _found_systems_view->Bind (wxEVT_LIST_ITEM_DESELECTED, boost::bind(&StandardRatingDialogPage::found_systems_view_selection_changed, this));
+
+       search_changed ();
+}
+
+
+/** The user clicked something different in the list of systems found by the search */
+void
+StandardRatingDialogPage::found_systems_view_selection_changed ()
+{
+       auto selected_index = _found_systems_view->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
+       if (selected_index < 0 || selected_index >= static_cast<int>(_found_systems.size())) {
+               _selected_system = boost::none;
+       } else {
+               _selected_system = _found_systems[selected_index];
+       }
+
+       /* Update the ratings dropdown */
+       vector<wxString> items;
+       if (_selected_system) {
+               for (auto rating: _selected_system->ratings) {
+                       items.push_back(std_to_wx(rating.label));
+               }
+       }
+
+       _rating->Set(items);
+
+       if (!items.empty()) {
+               _rating->SetSelection(0);
+       }
+
+       Changed (static_cast<bool>(_selected_system));
+}
+
+
+void
+StandardRatingDialogPage::search_changed ()
+{
+       _found_systems_view->DeleteAllItems();
+       _found_systems.clear();
+
+       icu::UnicodeString term(wx_to_std(_search->GetValue()).c_str(), "UTF-8");
+       term = term.toLower();
+
+       int N = 0;
+       for (auto const& system: dcp::rating_systems()) {
+               icu::UnicodeString name(system.name.c_str(), "UTF-8");
+               name = name.toLower();
+               icu::UnicodeString country_and_region_names(system.country_and_region_names.c_str(), "UTF-8");
+               country_and_region_names = country_and_region_names.toLower();
+               icu::UnicodeString country_code(system.country_code.c_str(), "UTF-8");
+               country_code = country_code.toLower();
+               if (term.isEmpty() || name.indexOf(term) != -1 || country_and_region_names.indexOf(term) != -1 || country_code.indexOf(term) != -1) {
+                       wxListItem item;
+                       item.SetId(N);
+                       _found_systems_view->InsertItem(item);
+                       _found_systems_view->SetItem(N, 0, std_to_wx(system.name));
+                       _found_systems_view->SetItem(N, 1, std_to_wx(system.country_code));
+                       _found_systems_view->SetItem(N, 2, std_to_wx(system.country_and_region_names));
+                       _found_systems.push_back(system);
+                       ++N;
+               }
+       }
+
+       update_found_system_selection ();
+}
+
+
+/** Reflect _selected_system in the current _found_systems_view */
+void
+StandardRatingDialogPage::update_found_system_selection ()
+{
+       if (!_selected_system) {
+               for (auto i = 0; i < _found_systems_view->GetItemCount(); ++i) {
+                       _found_systems_view->Select(i, false);
+               }
+               return;
+       }
+
+       int index = 0;
+       for (auto const& system: _found_systems) {
+               bool const selected = system.agency == _selected_system->agency;
+               _found_systems_view->Select(index, selected);
+               if (selected) {
+                       _found_systems_view->EnsureVisible(index);
+               }
+               ++index;
+       }
+}
+
+
+bool
+StandardRatingDialogPage::set (dcp::Rating rating)
+{
+       _selected_system = boost::none;
+       for (auto const& system: dcp::rating_systems()) {
+               if (system.agency == rating.agency) {
+                       _selected_system = system;
+                       break;
+               }
+       }
+
+       if (!_selected_system) {
+               return false;
+       }
+
+       update_found_system_selection ();
+
+       int rating_index = 0;
+       for (auto const& possible_rating: _selected_system->ratings) {
+               if (possible_rating.label == rating.label) {
+                       _rating->SetSelection (rating_index);
+                       return true;
+               }
+               ++rating_index;
+       }
+
+       return false;
+}
+
+
+dcp::Rating
+StandardRatingDialogPage::get () const
+{
+       DCPOMATIC_ASSERT (_selected_system);
+       auto selected_rating = _rating->GetSelection();
+       DCPOMATIC_ASSERT (selected_rating >= 0);
+       DCPOMATIC_ASSERT (selected_rating < static_cast<int>(_selected_system->ratings.size()));
+       return dcp::Rating(_selected_system->agency, _selected_system->ratings[selected_rating].label);
+}
+
+
+CustomRatingDialogPage::CustomRatingDialogPage (wxNotebook* notebook)
+       : RatingDialogPage (notebook)
+{
+       auto sizer = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
+
+       _agency = new wxTextCtrl (this, wxID_ANY, wxT(""), wxDefaultPosition, wxSize(400, -1));
+       _rating = new wxTextCtrl (this, wxID_ANY, wxT(""), wxDefaultPosition, wxSize(400, -1));
+
+       add_label_to_sizer (sizer, this, _("Agency"), true, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL);
+       sizer->Add (_agency, 1, wxEXPAND);
+       add_label_to_sizer (sizer, this, _("Rating"), true, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL);
+       sizer->Add (_rating, 1, wxEXPAND);
+
+       auto pad_sizer = new wxBoxSizer (wxVERTICAL);
+       pad_sizer->Add (sizer, 1, wxEXPAND | wxALL, DCPOMATIC_DIALOG_BORDER);
+
+       SetSizerAndFit (pad_sizer);
+
+       _agency->Bind(wxEVT_TEXT, boost::bind(&CustomRatingDialogPage::changed, this));
+       _rating->Bind(wxEVT_TEXT, boost::bind(&CustomRatingDialogPage::changed, this));
+}
+
+
+void
+CustomRatingDialogPage::changed ()
+{
+       Changed (!_agency->IsEmpty() && !_rating->IsEmpty());
+}
+
+
+dcp::Rating
+CustomRatingDialogPage::get () const
+{
+       return dcp::Rating(wx_to_std(_agency->GetValue()), wx_to_std(_rating->GetValue()));
+}
+
+
+bool
+CustomRatingDialogPage::set (dcp::Rating rating)
+{
+       _agency->SetValue(std_to_wx(rating.agency));
+       _rating->SetValue(std_to_wx(rating.label));
+       return true;
+}
+
index 51869c0abd89041fa22df1d15bb74ac867625be0..27887baa2386a2fc25cb744e1109d7561f5a4fcd 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2019 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2019-2022 Carl Hetherington <cth@carlh.net>
 
     This file is part of DCP-o-matic.
 
 
 */
 
-#include "table_dialog.h"
-#include <dcp/types.h>
 
-class RatingDialog : public TableDialog
+#include <dcp/rating.h>
+#include <wx/wx.h>
+#include <boost/signals2.hpp>
+
+
+class wxChoice;
+class wxListView;
+class wxNotebook;
+class wxSearchCtrl;
+
+
+class RatingDialogPage : public wxPanel
+{
+public:
+       RatingDialogPage (wxNotebook* notebook);
+       virtual dcp::Rating get () const = 0;
+       virtual bool set (dcp::Rating rating) = 0;
+
+       /** Emitted when the page has been changed, the parameter being true if OK
+        *  should now be enabled in the main dialogue.
+        */
+       boost::signals2::signal<void (bool)> Changed;
+};
+
+
+class StandardRatingDialogPage : public RatingDialogPage
+{
+public:
+       StandardRatingDialogPage (wxNotebook* notebook);
+
+       dcp::Rating get () const override;
+       bool set (dcp::Rating rating) override;
+
+private:
+       void search_changed ();
+       void found_systems_view_selection_changed ();
+       void update_found_system_selection ();
+
+       wxSearchCtrl* _search;
+       wxListView* _found_systems_view;
+       boost::optional<dcp::RatingSystem> _selected_system;
+       wxChoice* _rating;
+       std::vector<dcp::RatingSystem> _found_systems;
+};
+
+
+class CustomRatingDialogPage : public RatingDialogPage
+{
+public:
+       CustomRatingDialogPage (wxNotebook* notebook);
+
+       dcp::Rating get () const override;
+       bool set (dcp::Rating rating) override;
+
+private:
+       void changed ();
+
+       wxTextCtrl* _agency;
+       wxTextCtrl* _rating;
+};
+
+
+class RatingDialog : public wxDialog
 {
 public:
        RatingDialog (wxWindow* parent);
@@ -30,6 +90,13 @@ public:
        dcp::Rating get () const;
 
 private:
-       wxTextCtrl* _agency;
-       wxTextCtrl* _label;
+       void setup_sensitivity (bool ok_valid);
+       void page_changed ();
+
+       wxNotebook* _notebook;
+
+       StandardRatingDialogPage* _standard_page;
+       CustomRatingDialogPage* _custom_page;
+       RatingDialogPage* _active_page;
 };
+
index 93afaeda2fd1444f9ba4e37af2871fc602ea4b41..7e028274e188324d611266a5387a08309d7fe645 100644 (file)
@@ -309,7 +309,7 @@ def build(bld):
 
     obj.name   = 'libdcpomatic2-wx'
     obj.export_includes = ['..']
-    obj.uselib = 'BOOST_FILESYSTEM BOOST_THREAD BOOST_REGEX WXWIDGETS DCP SUB ZIP CXML RTAUDIO '
+    obj.uselib = 'BOOST_FILESYSTEM BOOST_THREAD BOOST_REGEX WXWIDGETS DCP SUB ZIP CXML RTAUDIO ICU '
     if bld.env.TARGET_LINUX:
         obj.uselib += 'GTK GL GLU '
     if bld.env.TARGET_WINDOWS_64 or bld.env.TARGET_WINDOWS_32:
index 988adeecc2f64fa0a58f84e4f2a49726d77f8c70..fad3779bd07ed5339ff96b7915a897ba3ae12307 100644 (file)
@@ -86,7 +86,7 @@ setup_osx_flags (wxSizer* s, bool left, int& flags)
 #endif
 
 
-/** Add a wxStaticText to a wxSizer, aligning it at vertical centre.
+/** Add a wxStaticText to a wxSizer.
  *  @param s Sizer to add to.
  *  @param p Parent window for the wxStaticText.
  *  @param t Text for the wxStaticText.
diff --git a/wscript b/wscript
index d1b9fd46d7d2d34c7483f90973ff85459d30ab25..91a0ee2433311500e2b57b29f45700769e8f0a2b 100644 (file)
--- a/wscript
+++ b/wscript
@@ -35,8 +35,8 @@ except ImportError:
 from waflib import Logs, Context
 
 APPNAME = 'dcpomatic'
-libdcp_version = '1.8.5'
-libsub_version = '1.6.5'
+libdcp_version = '1.8.9'
+libsub_version = '1.6.9'
 
 this_version = subprocess.Popen(shlex.split('git tag -l --points-at HEAD'), stdout=subprocess.PIPE).communicate()[0]
 last_version = subprocess.Popen(shlex.split('git describe --tags --match v* --abbrev=0'), stdout=subprocess.PIPE).communicate()[0]