From: Carl Hetherington Date: Fri, 22 Apr 2022 23:59:10 +0000 (+0200) Subject: Add a simple DCP editor. X-Git-Tag: v2.16.10~8 X-Git-Url: https://git.carlh.net/gitweb/?p=dcpomatic.git;a=commitdiff_plain;h=f37c1e40dcd5186c5aee95a4b0e6607c1b0045e4 Add a simple DCP editor. --- diff --git a/cscript b/cscript index 3ac340796..dbcd3b7a2 100644 --- a/cscript +++ b/cscript @@ -424,8 +424,8 @@ def dependencies(target, options): # Use distro-provided FFmpeg on Arch deps = [] - deps.append(('libdcp', 'v1.8.16')) - deps.append(('libsub', 'v1.6.17')) + deps.append(('libdcp', 'c4c176e9d3bd72fe1746f22d7afb442ca72a72f2')) + deps.append(('libsub', '079cf6179c1a41284858feaf61c6e57384d72777')) deps.append(('leqm-nrt', '93ae9e6')) deps.append(('rtaudio', 'f619b76')) # We get our OpenSSL libraries from the environment, but we diff --git a/graphics/linux/128/dcpomatic2_editor.png b/graphics/linux/128/dcpomatic2_editor.png new file mode 100644 index 000000000..d05282c43 Binary files /dev/null and b/graphics/linux/128/dcpomatic2_editor.png differ diff --git a/graphics/linux/16/dcpomatic2_editor.png b/graphics/linux/16/dcpomatic2_editor.png new file mode 100644 index 000000000..2ec12f82b Binary files /dev/null and b/graphics/linux/16/dcpomatic2_editor.png differ diff --git a/graphics/linux/22/dcpomatic2_editor.png b/graphics/linux/22/dcpomatic2_editor.png new file mode 100644 index 000000000..3c492a78d Binary files /dev/null and b/graphics/linux/22/dcpomatic2_editor.png differ diff --git a/graphics/linux/256/dcpomatic2_editor.png b/graphics/linux/256/dcpomatic2_editor.png new file mode 100644 index 000000000..d78fcb77c Binary files /dev/null and b/graphics/linux/256/dcpomatic2_editor.png differ diff --git a/graphics/linux/32/dcpomatic2_editor.png b/graphics/linux/32/dcpomatic2_editor.png new file mode 100644 index 000000000..821af7de0 Binary files /dev/null and b/graphics/linux/32/dcpomatic2_editor.png differ diff --git a/graphics/linux/48/dcpomatic2_editor.png b/graphics/linux/48/dcpomatic2_editor.png new file mode 100644 index 000000000..6d6f03d32 Binary files /dev/null and b/graphics/linux/48/dcpomatic2_editor.png differ diff --git a/graphics/linux/512/dcpomatic2_editor.png b/graphics/linux/512/dcpomatic2_editor.png new file mode 100644 index 000000000..53bf93301 Binary files /dev/null and b/graphics/linux/512/dcpomatic2_editor.png differ diff --git a/graphics/linux/64/dcpomatic2_editor.png b/graphics/linux/64/dcpomatic2_editor.png new file mode 100644 index 000000000..a1cfa196f Binary files /dev/null and b/graphics/linux/64/dcpomatic2_editor.png differ diff --git a/graphics/osx/dcpomatic2_editor.icns b/graphics/osx/dcpomatic2_editor.icns new file mode 100644 index 000000000..30729b874 Binary files /dev/null and b/graphics/osx/dcpomatic2_editor.icns differ diff --git a/graphics/osx/dcpomatic2_editor.iconset/icon_128x128.png b/graphics/osx/dcpomatic2_editor.iconset/icon_128x128.png new file mode 100644 index 000000000..d05282c43 Binary files /dev/null and b/graphics/osx/dcpomatic2_editor.iconset/icon_128x128.png differ diff --git a/graphics/osx/dcpomatic2_editor.iconset/icon_128x128@2x.png b/graphics/osx/dcpomatic2_editor.iconset/icon_128x128@2x.png new file mode 100644 index 000000000..d05282c43 Binary files /dev/null and b/graphics/osx/dcpomatic2_editor.iconset/icon_128x128@2x.png differ diff --git a/graphics/osx/dcpomatic2_editor.iconset/icon_16x16.png b/graphics/osx/dcpomatic2_editor.iconset/icon_16x16.png new file mode 100644 index 000000000..2ec12f82b Binary files /dev/null and b/graphics/osx/dcpomatic2_editor.iconset/icon_16x16.png differ diff --git a/graphics/osx/dcpomatic2_editor.iconset/icon_16x16@2x.png b/graphics/osx/dcpomatic2_editor.iconset/icon_16x16@2x.png new file mode 100644 index 000000000..2ec12f82b Binary files /dev/null and b/graphics/osx/dcpomatic2_editor.iconset/icon_16x16@2x.png differ diff --git a/graphics/osx/dcpomatic2_editor.iconset/icon_256x256.png b/graphics/osx/dcpomatic2_editor.iconset/icon_256x256.png new file mode 100644 index 000000000..d78fcb77c Binary files /dev/null and b/graphics/osx/dcpomatic2_editor.iconset/icon_256x256.png differ diff --git a/graphics/osx/dcpomatic2_editor.iconset/icon_256x256@2x.png b/graphics/osx/dcpomatic2_editor.iconset/icon_256x256@2x.png new file mode 100644 index 000000000..d78fcb77c Binary files /dev/null and b/graphics/osx/dcpomatic2_editor.iconset/icon_256x256@2x.png differ diff --git a/graphics/osx/dcpomatic2_editor.iconset/icon_32x32.png b/graphics/osx/dcpomatic2_editor.iconset/icon_32x32.png new file mode 100644 index 000000000..821af7de0 Binary files /dev/null and b/graphics/osx/dcpomatic2_editor.iconset/icon_32x32.png differ diff --git a/graphics/osx/dcpomatic2_editor.iconset/icon_32x32@2x.png b/graphics/osx/dcpomatic2_editor.iconset/icon_32x32@2x.png new file mode 100644 index 000000000..821af7de0 Binary files /dev/null and b/graphics/osx/dcpomatic2_editor.iconset/icon_32x32@2x.png differ diff --git a/graphics/osx/dcpomatic2_editor.iconset/icon_512x512.png b/graphics/osx/dcpomatic2_editor.iconset/icon_512x512.png new file mode 100644 index 000000000..53bf93301 Binary files /dev/null and b/graphics/osx/dcpomatic2_editor.iconset/icon_512x512.png differ diff --git a/graphics/osx/dcpomatic2_editor.iconset/icon_512x512@2x.png b/graphics/osx/dcpomatic2_editor.iconset/icon_512x512@2x.png new file mode 100644 index 000000000..53bf93301 Binary files /dev/null and b/graphics/osx/dcpomatic2_editor.iconset/icon_512x512@2x.png differ diff --git a/graphics/src/dcpomatic2_editor.svg b/graphics/src/dcpomatic2_editor.svg new file mode 100644 index 000000000..e5bb7b135 --- /dev/null +++ b/graphics/src/dcpomatic2_editor.svg @@ -0,0 +1,250 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/graphics/update b/graphics/update index 35aec325a..f518b5b5b 100755 --- a/graphics/update +++ b/graphics/update @@ -25,7 +25,7 @@ function required_font() fi } -svg_apps="dcpomatic2_kdm dcpomatic2_server dcpomatic2_batch dcpomatic2_player dcpomatic2_playlist dcpomatic2_disk dcpomatic2_combiner" +svg_apps="dcpomatic2_kdm dcpomatic2_server dcpomatic2_batch dcpomatic2_player dcpomatic2_playlist dcpomatic2_disk dcpomatic2_combiner dcpomatic2_editor" if [ `uname -s` == "Darwin" ]; then diff --git a/graphics/windows/dcpomatic2_editor.ico b/graphics/windows/dcpomatic2_editor.ico new file mode 100644 index 000000000..f07998374 Binary files /dev/null and b/graphics/windows/dcpomatic2_editor.ico differ diff --git a/platform/osx/dcpomatic2_editor.Info.plist.in b/platform/osx/dcpomatic2_editor.Info.plist.in new file mode 100644 index 000000000..229202ec6 --- /dev/null +++ b/platform/osx/dcpomatic2_editor.Info.plist.in @@ -0,0 +1,36 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + dcpomatic2_editor + CFBundleGetInfoString + DCP-o-matic 2 Editor + CFBundleIconFile + dcpomatic2_editor.icns + CFBundleIdentifier + com.dcpomatic.editor + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + DCP-o-matic 2 Editor + CFBundlePackageType + APPL + CFBundleShortVersions + @VERSION@ + CFBundleSignature + DOMC + CFBundleVersion + @VERSION@ + CFBundleAllowMixedLocalizations + + LSUIElement + 0 + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/platform/osx/make_dmg.sh b/platform/osx/make_dmg.sh index 6f36839f7..c9ed43b94 100644 --- a/platform/osx/make_dmg.sh +++ b/platform/osx/make_dmg.sh @@ -202,6 +202,7 @@ function copy_resources { cp $prefix/src/dcpomatic/graphics/osx/dcpomatic2_playlist.icns "$dest" cp $prefix/src/dcpomatic/graphics/osx/dcpomatic2_disk.icns "$dest" cp $prefix/src/dcpomatic/graphics/osx/dcpomatic2_combiner.icns "$dest" + cp $prefix/src/dcpomatic/graphics/osx/dcpomatic2_editor.icns "$dest" cp $prefix/src/dcpomatic/graphics/osx/preferences/defaults.png "$dest" cp $prefix/src/dcpomatic/graphics/osx/preferences/defaults@2x.png "$dest" cp $prefix/src/dcpomatic/graphics/osx/preferences/kdm_email.png "$dest" @@ -537,6 +538,16 @@ rl=("$approot/MacOS/dcpomatic2_combiner" "$approot/Frameworks/"*.dylib) relink_relative "${rl[@]}" make_dmg "$appdir" "" "DCP-o-matic Combiner" "dcpomatic2_verify openssl dcpomatic2_combiner" +# DCP-o-matic Editor +setup "DCP-o-matic 2 Editor.app" +copy $ROOT src/dcpomatic/build/src/tools/dcpomatic2_editor "$approot/MacOS" +copy $ROOT src/openssl/apps/openssl "$approot/MacOS" +copy_verify +cp $prefix/src/dcpomatic/build/platform/osx/dcpomatic2_editor.Info.plist "$approot/Info.plist" +rl=("$approot/MacOS/dcpomatic2_editor" "$approot/Frameworks/"*.dylib) +relink_relative "${rl[@]}" +make_dmg "$appdir" "" "DCP-o-matic Editor" "dcpomatic2_verify openssl dcpomatic2_editor" + # DCP-o-matic Disk Writer .app setup "DCP-o-matic 2 Disk Writer.app" copy $ROOT src/dcpomatic/build/src/tools/dcpomatic2_disk "$approot/MacOS" diff --git a/platform/osx/wscript b/platform/osx/wscript index c0b3d9d10..fd6733a66 100644 --- a/platform/osx/wscript +++ b/platform/osx/wscript @@ -7,3 +7,4 @@ def build(bld): obj = bld(features='subst', source='dcpomatic2_playlist.Info.plist.in', target='dcpomatic2_playlist.Info.plist', version=bld.env.VERSION) obj = bld(features='subst', source='dcpomatic2_disk.Info.plist.in', target='dcpomatic2_disk.Info.plist', version=bld.env.VERSION) obj = bld(features='subst', source='dcpomatic2_combiner.Info.plist.in', target='dcpomatic2_combiner.Info.plist', version=bld.env.VERSION) + obj = bld(features='subst', source='dcpomatic2_editor.Info.plist.in', target='dcpomatic2_editor.Info.plist', version=bld.env.VERSION) diff --git a/platform/windows/dcpomatic_editor.rc b/platform/windows/dcpomatic_editor.rc new file mode 100644 index 000000000..865590f51 --- /dev/null +++ b/platform/windows/dcpomatic_editor.rc @@ -0,0 +1,2 @@ +id ICON "../../graphics/windows/dcpomatic2_editor.ico" +#include "wx-3.1/wx/msw/wx.rc" diff --git a/platform/windows/wscript b/platform/windows/wscript index 46974ed0f..a01d3dd5a 100644 --- a/platform/windows/wscript +++ b/platform/windows/wscript @@ -12,6 +12,7 @@ def write_installer(bits, dcpomatic_version, debug, disk): ('create', 'Creator'), ('playlist', 'Playlist Editor'), ('combiner', 'Combiner'), + ('editor', 'Editor'), ] if disk: diff --git a/run/dcpomatic_editor b/run/dcpomatic_editor new file mode 100755 index 000000000..28ef4a9d2 --- /dev/null +++ b/run/dcpomatic_editor @@ -0,0 +1,26 @@ +#!/bin/bash + +export LD_LIBRARY_PATH=build/src/lib:build/src/wx:build/src/asdcplib/src:$LD_LIBRARY_PATH +export DYLD_LIBRARY_PATH=$build/src/lib:$build/src/wx:$build/src/asdcplib/src:/Users/ci/osx-environment/x86_64/10.10/lib +export DCPOMATIC_GRAPHICS=graphics +if [ "$1" == "--debug" ]; then + shift + gdb --args build/src/tools/dcpomatic2_editor $* +elif [ "$1" == "--valgrind" ]; then + shift + valgrind --tool="memcheck" --suppressions=suppressions --track-fds=yes build/src/tools/dcpomatic2_editor $* +elif [ "$1" == "--callgrind" ]; then + shift + valgrind --tool="callgrind" build/src/tools/dcpomatic2_editor $* +elif [ "$1" == "--massif" ]; then + shift + valgrind --tool="massif" build/src/tools/dcpomatic2_editor $* +elif [ "$1" == "--i18n" ]; then + shift + LANGUAGE=fr_FR.UTF8 LANG=fr_FR.UTF8 LC_ALL=fr_FR.UTF8 build/src/tools/dcpomatic2_editor "$*" +elif [ "$1" == "--perf" ]; then + shift + perf record build/src/tools/dcpomatic2_editor $* +else + build/src/tools/dcpomatic2_editor $* +fi diff --git a/src/lib/writer.cc b/src/lib/writer.cc index f14ef49ef..954c135d3 100644 --- a/src/lib/writer.cc +++ b/src/lib/writer.cc @@ -692,14 +692,11 @@ Writer::finish (boost::filesystem::path output_dcp) throw InvalidSignerError (reason); } - dcp.write_xml ( - issuer, - creator, - dcp::LocalTime().as_string(), - film()->dcp_name(), - signer, - Config::instance()->dcp_metadata_filename_format() - ); + dcp.set_issuer(issuer); + dcp.set_creator(creator); + dcp.set_annotation_text(film()->dcp_name()); + + dcp.write_xml (signer, Config::instance()->dcp_metadata_filename_format()); LOG_GENERAL ( N_("Wrote %1 FULL, %2 FAKE, %3 REPEAT, %4 pushed to disk"), _full_written, _fake_written, _repeat_written, _pushed_to_disk diff --git a/src/tools/dcpomatic_editor.cc b/src/tools/dcpomatic_editor.cc new file mode 100644 index 000000000..075cf177f --- /dev/null +++ b/src/tools/dcpomatic_editor.cc @@ -0,0 +1,561 @@ +/* + Copyright (C) 2022 Carl Hetherington + + This file is part of DCP-o-matic. + + DCP-o-matic 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. + + DCP-o-matic 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 DCP-o-matic. If not, see . + +*/ + + +#include "wx/about_dialog.h" +#include "wx/editable_list.h" +#include "wx/wx_signal_manager.h" +#include "wx/wx_util.h" +#include "lib/cross.h" +#include "lib/dcpomatic_log.h" +#include "lib/null_log.h" +#include "lib/util.h" +#include +#include +#include +#include +#include +#include +#include +LIBDCP_DISABLE_WARNINGS +#include +#include +#include +#include +#include +#include +LIBDCP_ENABLE_WARNINGS +#ifdef __WXGTK__ +#include +#endif +#include + + +using std::exception; +using std::make_shared; +using std::shared_ptr; +using std::vector; +using boost::optional; +#if BOOST_VERSION >= 106100 +using namespace boost::placeholders; +#endif + + +enum { + ID_file_open = 1, + ID_file_save, +}; + + +class AssetPanel : public wxPanel +{ +public: + AssetPanel(wxWindow* parent, shared_ptr asset) + : wxPanel(parent, wxID_ANY) + , _asset(asset) + { + auto sizer = new wxGridBagSizer(DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP); + + int r = 0; + + add_label_to_sizer(sizer, this, _("Annotation text"), true, wxGBPosition(r, 0)); + _annotation_text = new wxTextCtrl(this, wxID_ANY, std_to_wx(asset->annotation_text().get_value_or("")), wxDefaultPosition, wxSize(600, -1)); + sizer->Add(_annotation_text, wxGBPosition(r, 1), wxDefaultSpan, wxEXPAND); + ++r; + + add_label_to_sizer(sizer, this, _("Entry point"), true, wxGBPosition(r, 0)); + _entry_point = new wxSpinCtrl(this, wxID_ANY); + sizer->Add(_entry_point, wxGBPosition(r, 1), wxDefaultSpan); + ++r; + + add_label_to_sizer(sizer, this, _("Duration"), true, wxGBPosition(r, 0)); + _duration = new wxSpinCtrl(this, wxID_ANY); + sizer->Add(_duration, wxGBPosition(r, 1), wxDefaultSpan); + ++r; + + add_label_to_sizer(sizer, this, _("Intrinsic duration"), true, wxGBPosition(r, 0)); + auto intrinsic_duration = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_READONLY); + sizer->Add(intrinsic_duration, wxGBPosition(r, 1), wxDefaultSpan); + ++r; + + auto space = new wxBoxSizer(wxVERTICAL); + space->Add(sizer, 1, wxEXPAND | wxALL, DCPOMATIC_DIALOG_BORDER); + SetSizerAndFit(space); + + _entry_point->SetRange(0, 259200); + _entry_point->SetValue(asset->entry_point().get_value_or(0)); + + _duration->SetRange(0, 259200); + _duration->SetValue(asset->duration().get_value_or(0)); + + intrinsic_duration->SetValue(wxString::Format("%ld", asset->intrinsic_duration())); + + _annotation_text->Bind(wxEVT_TEXT, boost::bind(&AssetPanel::annotation_text_changed, this)); + _entry_point->Bind(wxEVT_SPINCTRL, boost::bind(&AssetPanel::entry_point_changed, this)); + _duration->Bind(wxEVT_SPINCTRL, boost::bind(&AssetPanel::duration_changed, this)); + } + +private: + void annotation_text_changed() + { + _asset->set_annotation_text(wx_to_std(_annotation_text->GetValue())); + } + + void entry_point_changed() + { + _asset->set_entry_point(_entry_point->GetValue()); + auto const fixed_duration = std::min(_asset->intrinsic_duration() - _asset->entry_point().get_value_or(0LL), _asset->duration().get_value_or(_asset->intrinsic_duration())); + _duration->SetValue(fixed_duration); + _asset->set_duration(fixed_duration); + } + + void duration_changed() + { + _asset->set_duration(_duration->GetValue()); + auto const fixed_entry_point = std::min(_asset->intrinsic_duration() - _asset->duration().get_value_or(_asset->intrinsic_duration()), _asset->entry_point().get_value_or(0LL)); + _entry_point->SetValue(fixed_entry_point); + _asset->set_entry_point(fixed_entry_point); + } + + wxTextCtrl* _annotation_text = nullptr; + wxSpinCtrl* _entry_point = nullptr; + wxSpinCtrl* _duration = nullptr; + shared_ptr _asset; +}; + + +class ReelEditor : public wxDialog +{ +public: + ReelEditor(wxWindow* parent) + : wxDialog(parent, wxID_ANY, _("Edit reel")) + { + auto sizer = new wxBoxSizer(wxVERTICAL); + _notebook = new wxNotebook(this, wxID_ANY); + sizer->Add(_notebook, wxEXPAND | wxALL, 1, DCPOMATIC_DIALOG_BORDER); + SetSizerAndFit(sizer); + } + + optional> get() { + return _reel; + } + + void set(shared_ptr reel) + { + _reel = reel; + + _notebook->DeleteAllPages(); + if (_reel->main_picture()) { + _notebook->AddPage(new AssetPanel(_notebook, _reel->main_picture()), _("Picture")); + } + if (_reel->main_sound()) { + _notebook->AddPage(new AssetPanel(_notebook, _reel->main_sound()), _("Sound")); + } + if (_reel->main_subtitle()) { + _notebook->AddPage(new AssetPanel(_notebook, _reel->main_subtitle()), _("Subtitle")); + } + } + +private: + wxNotebook* _notebook = nullptr; + shared_ptr _reel; +}; + + +class CPLPanel : public wxPanel +{ +public: + CPLPanel(wxWindow* parent, shared_ptr cpl) + : wxPanel(parent, wxID_ANY) + , _cpl(cpl) + { + auto sizer = new wxGridBagSizer(DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP); + + int r = 0; + + add_label_to_sizer(sizer, this, _("Annotation text"), true, wxGBPosition(r, 0)); + _annotation_text = new wxTextCtrl(this, wxID_ANY, std_to_wx(cpl->annotation_text().get_value_or("")), wxDefaultPosition, wxSize(600, -1)); + sizer->Add(_annotation_text, wxGBPosition(r, 1), wxDefaultSpan, wxEXPAND); + ++r; + + add_label_to_sizer(sizer, this, _("Issuer"), true, wxGBPosition(r, 0)); + _issuer = new wxTextCtrl(this, wxID_ANY, std_to_wx(cpl->issuer()), wxDefaultPosition, wxSize(600, -1)); + sizer->Add(_issuer, wxGBPosition(r, 1), wxDefaultSpan, wxEXPAND); + ++r; + + add_label_to_sizer(sizer, this, _("Creator"), true, wxGBPosition(r, 0)); + _creator = new wxTextCtrl(this, wxID_ANY, std_to_wx(cpl->creator()), wxDefaultPosition, wxSize(600, -1)); + sizer->Add(_creator, wxGBPosition(r, 1), wxDefaultSpan, wxEXPAND); + ++r; + + add_label_to_sizer(sizer, this, _("Content title text"), true, wxGBPosition(r, 0)); + _content_title_text = new wxTextCtrl(this, wxID_ANY, std_to_wx(cpl->content_title_text()), wxDefaultPosition, wxSize(600, -1)); + sizer->Add(_content_title_text, wxGBPosition(r, 1), wxDefaultSpan, wxEXPAND); + ++r; + + add_label_to_sizer(sizer, this, _("Reels"), true, wxGBPosition(r, 0)); + _reels = new EditableList, ReelEditor>( + this, + { EditableListColumn("Name", 600, true) }, + [this]() { return _cpl->reels(); }, + [this](vector> reels) { + _cpl->set(reels); + }, + [](shared_ptr reel, int) { + return reel->id(); + }, + false, + EditableListButton::EDIT + ); + sizer->Add(_reels, wxGBPosition(r, 1), wxDefaultSpan, wxEXPAND); + + auto space = new wxBoxSizer(wxVERTICAL); + space->Add(sizer, 1, wxEXPAND | wxALL, DCPOMATIC_DIALOG_BORDER); + SetSizerAndFit(space); + + _annotation_text->Bind(wxEVT_TEXT, boost::bind(&CPLPanel::annotation_text_changed, this)); + _issuer->Bind(wxEVT_TEXT, boost::bind(&CPLPanel::issuer_changed, this)); + _creator->Bind(wxEVT_TEXT, boost::bind(&CPLPanel::creator_changed, this)); + _content_title_text->Bind(wxEVT_TEXT, boost::bind(&CPLPanel::content_title_text_changed, this)); + } + +private: + void annotation_text_changed() + { + _cpl->set_annotation_text(wx_to_std(_annotation_text->GetValue())); + } + + void issuer_changed() + { + _cpl->set_issuer(wx_to_std(_issuer->GetValue())); + } + + void creator_changed() + { + _cpl->set_creator(wx_to_std(_creator->GetValue())); + } + + void content_title_text_changed() + { + _cpl->set_content_title_text(wx_to_std(_content_title_text->GetValue())); + } + + std::shared_ptr _cpl; + wxTextCtrl* _annotation_text = nullptr; + wxTextCtrl* _issuer = nullptr; + wxTextCtrl* _creator = nullptr; + wxTextCtrl* _content_title_text = nullptr; + EditableList, ReelEditor>* _reels; +}; + + +class DummyPanel : public wxPanel +{ +public: + DummyPanel(wxWindow* parent) + : wxPanel(parent, wxID_ANY) + { + auto sizer = new wxBoxSizer(wxVERTICAL); + add_label_to_sizer(sizer, this, _("Open a DCP using File -> Open"), false); + auto space = new wxBoxSizer(wxVERTICAL); + space->Add(sizer, 1, wxEXPAND | wxALL, DCPOMATIC_DIALOG_BORDER); + SetSizerAndFit(space); + } +}; + + +class DOMFrame : public wxFrame +{ +public: + DOMFrame () + : wxFrame(nullptr, -1, _("DCP-o-matic Editor")) + , _main_sizer(new wxBoxSizer(wxVERTICAL)) + { + dcpomatic_log = make_shared(); + +#if defined(DCPOMATIC_WINDOWS) + maybe_open_console(); + std::cout << "DCP-o-matic Editor is starting." << "\n"; +#endif + + auto bar = new wxMenuBar; + setup_menu(bar); + SetMenuBar(bar); + +#ifdef DCPOMATIC_WINDOWS + SetIcon(wxIcon(std_to_wx("id"))); +#endif + + Bind(wxEVT_MENU, boost::bind(&DOMFrame::file_open, this), ID_file_open); + Bind(wxEVT_MENU, boost::bind(&DOMFrame::file_save, this), ID_file_save); + Bind(wxEVT_MENU, boost::bind(&DOMFrame::file_exit, this), wxID_EXIT); + Bind(wxEVT_MENU, boost::bind(&DOMFrame::help_about, this), wxID_ABOUT); + + /* Use a panel as the only child of the Frame so that we avoid + the dark-grey background on Windows. + */ + _overall_panel = new wxPanel (this, wxID_ANY); + + auto sizer = new wxBoxSizer(wxVERTICAL); + + _notebook = new wxNotebook(_overall_panel, wxID_ANY); + _notebook->AddPage(new DummyPanel(_notebook), _("CPL")); + + sizer->Add(_notebook, 1, wxEXPAND); + _overall_panel->SetSizerAndFit(sizer); + } + + void load_dcp (boost::filesystem::path path) + { + _notebook->DeleteAllPages(); + + _dcp = dcp::DCP(path); + _dcp->read(); + for (auto cpl: _dcp->cpls()) { + _notebook->AddPage(new CPLPanel(_notebook, cpl), wx_to_std(cpl->annotation_text().get_value_or(cpl->id()))); + } + } + +private: + + void setup_menu (wxMenuBar* m) + { + _file_menu = new wxMenu; + _file_menu->Append (ID_file_open, _("&Open...\tCtrl-O")); + _file_menu->AppendSeparator (); + _file_menu->Append (ID_file_save, _("&Save\tCtrl-S")); + _file_menu->AppendSeparator (); +#ifdef __WXOSX__ + _file_menu->Append (wxID_EXIT, _("&Exit")); +#else + _file_menu->Append (wxID_EXIT, _("&Quit")); +#endif + + auto help = new wxMenu; +#ifdef __WXOSX__ + help->Append (wxID_ABOUT, _("About DCP-o-matic")); +#else + help->Append (wxID_ABOUT, _("About")); +#endif + + m->Append (_file_menu, _("&File")); + m->Append (help, _("&Help")); + } + + void file_open () + { + auto d = wxStandardPaths::Get().GetDocumentsDir(); + auto c = new wxDirDialog (this, _("Select DCP to open"), d, wxDEFAULT_DIALOG_STYLE | wxDD_DIR_MUST_EXIST); + + int r; + while (true) { + r = c->ShowModal (); + if (r == wxID_OK && c->GetPath() == wxStandardPaths::Get().GetDocumentsDir()) { + error_dialog (this, _("You did not select a folder. Make sure that you select a folder before clicking Open.")); + } else { + break; + } + } + + if (r == wxID_OK) { + boost::filesystem::path const dcp (wx_to_std (c->GetPath ())); + load_dcp (dcp); + } + + c->Destroy (); + } + + void file_save () + { + _dcp->write_xml(); + } + + void file_exit () + { + Close (); + } + + void help_about () + { + auto d = new AboutDialog (this); + d->ShowModal (); + d->Destroy (); + } + + wxPanel* _overall_panel = nullptr; + wxMenu* _file_menu = nullptr; + wxSizer* _main_sizer = nullptr; + wxNotebook* _notebook = nullptr; + optional _dcp; +}; + + +static const wxCmdLineEntryDesc command_line_description[] = { + { wxCMD_LINE_PARAM, 0, 0, "DCP to edit", wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL }, + { wxCMD_LINE_NONE, "", "", "", wxCmdLineParamType (0), 0 } +}; + + +/** @class App + * @brief The magic App class for wxWidgets. + */ +class App : public wxApp +{ +public: + App () + : wxApp () + { +#ifdef DCPOMATIC_LINUX + XInitThreads (); +#endif + } + +private: + + bool OnInit () override + { + wxSplashScreen* splash = nullptr; + try { + wxInitAllImageHandlers (); + + splash = maybe_show_splash (); + + SetAppName (_("DCP-o-matic Editor")); + + if (!wxApp::OnInit()) { + return false; + } + +#ifdef DCPOMATIC_LINUX + unsetenv ("UBUNTU_MENUPROXY"); +#endif + +#ifdef DCPOMATIC_OSX + make_foreground_application (); +#endif + + dcpomatic_setup_path_encoding (); + + /* Enable i18n; this will create a Config object + to look for a force-configured language. This Config + object will be wrong, however, because dcpomatic_setup + hasn't yet been called and there aren't any filters etc. + set up yet. + */ + dcpomatic_setup_i18n (); + + /* Set things up, including filters etc. + which will now be internationalised correctly. + */ + dcpomatic_setup (); + + signal_manager = new wxSignalManager (this); + + _frame = new DOMFrame (); + SetTopWindow (_frame); + _frame->Maximize (); + if (splash) { + splash->Destroy (); + splash = nullptr; + } + _frame->Show (); + + if (_dcp_to_load) { + _frame->load_dcp(*_dcp_to_load); + } + + Bind (wxEVT_IDLE, boost::bind (&App::idle, this)); + } + catch (exception& e) + { + if (splash) { + splash->Destroy (); + } + error_dialog (0, _("DCP-o-matic Editor could not start."), std_to_wx(e.what())); + } + + return true; + } + + void OnInitCmdLine (wxCmdLineParser& parser) override + { + parser.SetDesc (command_line_description); + parser.SetSwitchChars (wxT ("-")); + } + + bool OnCmdLineParsed (wxCmdLineParser& parser) override + { + if (parser.GetParamCount() > 0) { + _dcp_to_load = wx_to_std(parser.GetParam(0)); + } + + return true; + } + + void report_exception () + { + try { + throw; + } catch (FileError& e) { + error_dialog ( + 0, + wxString::Format ( + _("An exception occurred: %s (%s)\n\n") + REPORT_PROBLEM, + std_to_wx (e.what()), + std_to_wx (e.file().string().c_str ()) + ) + ); + } catch (exception& e) { + error_dialog ( + 0, + wxString::Format ( + _("An exception occurred: %s.\n\n") + REPORT_PROBLEM, + std_to_wx (e.what ()) + ) + ); + } catch (...) { + error_dialog (0, _("An unknown exception occurred.") + " " + REPORT_PROBLEM); + } + } + + /* An unhandled exception has occurred inside the main event loop */ + bool OnExceptionInMainLoop () override + { + report_exception (); + /* This will terminate the program */ + return false; + } + + void OnUnhandledException () override + { + report_exception (); + } + + void idle () + { + signal_manager->ui_idle (); + } + + DOMFrame* _frame = nullptr; + optional _dcp_to_load; +}; + + +IMPLEMENT_APP (App) diff --git a/src/tools/wscript b/src/tools/wscript index bb3798aa7..be5297beb 100644 --- a/src/tools/wscript +++ b/src/tools/wscript @@ -62,7 +62,7 @@ def build(bld): gui_tools = [] if not bld.env.DISABLE_GUI: - gui_tools = ['dcpomatic', 'dcpomatic_batch', 'dcpomatic_server', 'dcpomatic_kdm', 'dcpomatic_player', 'dcpomatic_playlist', 'dcpomatic_combiner'] + gui_tools = ['dcpomatic', 'dcpomatic_batch', 'dcpomatic_server', 'dcpomatic_kdm', 'dcpomatic_player', 'dcpomatic_playlist', 'dcpomatic_combiner', 'dcpomatic_editor'] if bld.env.ENABLE_DISK: gui_tools.append('dcpomatic_disk')