From 14b8b29e6660f5a7fb21135fb5f90b4c1ce51a4b Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Thu, 21 Mar 2019 00:10:08 +0000 Subject: [PATCH] Basics of metadata dialog - ratings. --- src/lib/film.cc | 14 +++++ src/lib/film.h | 11 +++- src/lib/util.h | 11 ++++ src/lib/writer.cc | 1 + src/wx/cinema_dialog.cc | 4 +- src/wx/dcp_panel.cc | 27 +++++++++- src/wx/dcp_panel.h | 4 ++ src/wx/editable_list.h | 81 ++++++++++++++++++++++------ src/wx/full_config_dialog.cc | 12 ++--- src/wx/metadata_dialog.cc | 101 +++++++++++++++++++++++++++++++++++ src/wx/metadata_dialog.h | 42 +++++++++++++++ src/wx/rating_dialog.cc | 49 +++++++++++++++++ src/wx/rating_dialog.h | 35 ++++++++++++ src/wx/screen_dialog.cc | 4 +- src/wx/wscript | 4 +- 15 files changed, 370 insertions(+), 30 deletions(-) create mode 100644 src/wx/metadata_dialog.cc create mode 100644 src/wx/metadata_dialog.h create mode 100644 src/wx/rating_dialog.cc create mode 100644 src/wx/rating_dialog.h diff --git a/src/lib/film.cc b/src/lib/film.cc index 0b2b67801..0b9e57117 100644 --- a/src/lib/film.cc +++ b/src/lib/film.cc @@ -408,6 +408,9 @@ Film::metadata (bool with_content_paths) const m->set_attribute("Type", dcp::marker_to_string(i->first)); m->add_child_text(raw_convert(i->second.get())); } + BOOST_FOREACH (dcp::Rating i, _ratings) { + i.as_xml (root->add_child("Rating")); + } _playlist->as_xml (root->add_child ("Playlist"), with_content_paths); return doc; @@ -540,6 +543,10 @@ Film::read_metadata (optional path) _markers[dcp::marker_from_string(i->string_attribute("Type"))] = DCPTime(dcp::raw_convert(i->content())); } + BOOST_FOREACH (cxml::ConstNodePtr i, f.node_children("Rating")) { + _ratings.push_back (dcp::Rating(i)); + } + list notes; /* This method is the only one that can return notes (so far) */ _playlist->set_from_xml (shared_from_this(), f.node_child ("Playlist"), _state_version, notes); @@ -1709,6 +1716,13 @@ Film::unset_marker (dcp::Marker type) _markers.erase (type); } +void +Film::set_ratings (vector r) +{ + ChangeSignaller ch (this, RATINGS); + _ratings = r; +} + optional Film::marker (dcp::Marker type) const { diff --git a/src/lib/film.h b/src/lib/film.h index d61612e47..e5d29145c 100644 --- a/src/lib/film.h +++ b/src/lib/film.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2012-2018 Carl Hetherington + Copyright (C) 2012-2019 Carl Hetherington This file is part of DCP-o-matic. @@ -201,7 +201,8 @@ public: REEL_LENGTH, UPLOAD_AFTER_MAKE_DCP, REENCODE_J2K, - MARKERS + MARKERS, + RATINGS }; @@ -302,6 +303,10 @@ public: return _markers; } + std::vector ratings () const { + return _ratings; + } + /* SET */ void set_directory (boost::filesystem::path); @@ -334,6 +339,7 @@ public: void set_reencode_j2k (bool); void set_marker (dcp::Marker type, DCPTime time); void unset_marker (dcp::Marker type); + void set_ratings (std::vector r); /** Emitted when some property has of the Film is about to change or has changed */ mutable boost::signals2::signal Change; @@ -414,6 +420,7 @@ private: /** true if the user has ever explicitly set the video frame rate of this film */ bool _user_explicit_video_frame_rate; std::map _markers; + std::vector _ratings; int _state_version; diff --git a/src/lib/util.h b/src/lib/util.h index 5ffdae450..6cb818b39 100644 --- a/src/lib/util.h +++ b/src/lib/util.h @@ -111,4 +111,15 @@ extern boost::shared_ptr read_swaroop_chain (boost::files extern void write_swaroop_chain (boost::shared_ptr chain, boost::filesystem::path output); #endif +template +std::list +vector_to_list (std::vector v) +{ + std::list l; + BOOST_FOREACH (T& i, v) { + l.push_back (i); + } + return l; +} + #endif diff --git a/src/lib/writer.cc b/src/lib/writer.cc index d0f0825f1..79e5c8c36 100644 --- a/src/lib/writer.cc +++ b/src/lib/writer.cc @@ -558,6 +558,7 @@ Writer::finish () meta.set_issue_date_now (); cpl->set_metadata (meta); + cpl->set_ratings (vector_to_list(_film->ratings())); shared_ptr signer; if (_film->is_signed ()) { diff --git a/src/wx/cinema_dialog.cc b/src/wx/cinema_dialog.cc index 36aabf776..ff5d1faf6 100644 --- a/src/wx/cinema_dialog.cc +++ b/src/wx/cinema_dialog.cc @@ -67,8 +67,8 @@ CinemaDialog::CinemaDialog (wxWindow* parent, wxString title, string name, list< copy (emails.begin(), emails.end(), back_inserter (_emails)); - vector columns; - columns.push_back (wx_to_std (_("Address"))); + vector columns; + columns.push_back (EditableListColumn(wx_to_std(_("Address")))); _email_list = new EditableList ( this, columns, bind (&CinemaDialog::get_emails, this), bind (&CinemaDialog::set_emails, this, _1), bind (&column, _1) ); diff --git a/src/wx/dcp_panel.cc b/src/wx/dcp_panel.cc index 314081d3d..0a38f7344 100644 --- a/src/wx/dcp_panel.cc +++ b/src/wx/dcp_panel.cc @@ -29,6 +29,7 @@ #include "check_box.h" #include "dcpomatic_button.h" #include "markers_dialog.h" +#include "metadata_dialog.h" #include "lib/ratio.h" #include "lib/config.h" #include "lib/dcp_content_type.h" @@ -65,6 +66,7 @@ using dcp::locale_convert; DCPPanel::DCPPanel (wxNotebook* n, shared_ptr film, weak_ptr viewer) : _audio_dialog (0) , _markers_dialog (0) + , _metadata_dialog (0) , _film (film) , _viewer (viewer) , _generally_sensitive (true) @@ -119,6 +121,7 @@ DCPPanel::DCPPanel (wxNotebook* n, shared_ptr film, weak_ptr v _upload_after_make_dcp = new CheckBox (_panel, _("Upload DCP to TMS after it is made")); _markers = new Button (_panel, _("Markers...")); + _metadata = new Button (_panel, _("Metadata...")); _notebook = new wxNotebook (_panel, wxID_ANY); _sizer->Add (_notebook, 1, wxEXPAND | wxTOP, 6); @@ -139,6 +142,7 @@ DCPPanel::DCPPanel (wxNotebook* n, shared_ptr film, weak_ptr v _standard->Bind (wxEVT_CHOICE, boost::bind (&DCPPanel::standard_changed, this)); _upload_after_make_dcp->Bind (wxEVT_CHECKBOX, boost::bind (&DCPPanel::upload_after_make_dcp_changed, this)); _markers->Bind (wxEVT_BUTTON, boost::bind (&DCPPanel::markers_clicked, this)); + _metadata->Bind (wxEVT_BUTTON, boost::bind (&DCPPanel::metadata_clicked, this)); BOOST_FOREACH (DCPContentType const * i, DCPContentType::all()) { _dcp_content_type->Append (std_to_wx (i->pretty_name ())); @@ -221,6 +225,7 @@ DCPPanel::add_to_grid () _standard->Show (full); _upload_after_make_dcp->Show (full); _markers->Show (full); + _metadata->Show (full); _reencode_j2k->Show (full); _encrypted->Show (full); @@ -254,7 +259,10 @@ DCPPanel::add_to_grid () _grid->Add (_upload_after_make_dcp, wxGBPosition (r, 0), wxGBSpan (1, 2)); ++r; - _grid->Add (_markers, wxGBPosition(r, 0), wxGBSpan(1, 2)); + wxBoxSizer* extra = new wxBoxSizer (wxHORIZONTAL); + extra->Add (_markers, 1, wxRIGHT, DCPOMATIC_SIZER_X_GAP); + extra->Add (_metadata, 1, wxRIGHT, DCPOMATIC_SIZER_X_GAP); + _grid->Add (extra, wxGBPosition(r, 0), wxGBSpan(1, 2)); ++r; } } @@ -388,6 +396,18 @@ DCPPanel::markers_clicked () _markers_dialog->Show(); } +void +DCPPanel::metadata_clicked () +{ + if (_metadata_dialog) { + _metadata_dialog->Destroy (); + _metadata_dialog = 0; + } + + _metadata_dialog = new MetadataDialog (_panel, _film); + _metadata_dialog->Show (); +} + void DCPPanel::film_changed (int p) { @@ -591,6 +611,10 @@ DCPPanel::set_film (shared_ptr film) _markers_dialog->Destroy (); _markers_dialog = 0; } + if (_metadata_dialog) { + _metadata_dialog->Destroy (); + _metadata_dialog = 0; + } _film = film; @@ -655,6 +679,7 @@ DCPPanel::setup_sensitivity () _reel_length->Enable (_generally_sensitive && _film && _film->reel_type() == REELTYPE_BY_LENGTH); _upload_after_make_dcp->Enable (_generally_sensitive); _markers->Enable (_generally_sensitive); + _metadata->Enable (_generally_sensitive); _frame_rate_choice->Enable (_generally_sensitive && _film && !_film->references_dcp_video()); _frame_rate_spin->Enable (_generally_sensitive && _film && !_film->references_dcp_video()); _audio_channels->Enable (_generally_sensitive && _film && !_film->references_dcp_audio()); diff --git a/src/wx/dcp_panel.h b/src/wx/dcp_panel.h index 27049962c..f717e8327 100644 --- a/src/wx/dcp_panel.h +++ b/src/wx/dcp_panel.h @@ -36,6 +36,7 @@ class wxGridBagSizer; class AudioDialog; class MarkersDialog; +class MetadataDialog; class Film; class FilmViewer; class Ratio; @@ -80,6 +81,7 @@ private: void reel_length_changed (); void upload_after_make_dcp_changed (); void markers_clicked (); + void metadata_clicked (); void reencode_j2k_changed (); void setup_frame_rate_widget (); @@ -148,9 +150,11 @@ private: wxSpinCtrl* _reel_length; wxCheckBox* _upload_after_make_dcp; wxButton* _markers; + wxButton* _metadata; AudioDialog* _audio_dialog; MarkersDialog* _markers_dialog; + MetadataDialog* _metadata_dialog; boost::shared_ptr _film; boost::weak_ptr _viewer; diff --git a/src/wx/editable_list.h b/src/wx/editable_list.h index a7c1a6102..3ee4359bf 100644 --- a/src/wx/editable_list.h +++ b/src/wx/editable_list.h @@ -28,8 +28,30 @@ #include #include +class EditableListColumn +{ +public: + EditableListColumn (std::string name_) + : name (name_) + , growable (false) + {} + + EditableListColumn (std::string name_, boost::optional width_, bool growable_) + : name (name_) + , width (width_) + , growable (growable_) + {} + + std::string name; + boost::optional width; + bool growable; +}; + /** @param T type of things being edited. * @param S dialog to edit a thing. + * @param get Function to get a std::vector of the things being edited. + * @param set Function set the things from a a std::vector. + * @param column Function to get the display string for a given column in a given item. */ template class EditableList : public wxPanel @@ -37,20 +59,20 @@ class EditableList : public wxPanel public: EditableList ( wxWindow* parent, - std::vector columns, + std::vector columns, boost::function ()> get, boost::function)> set, boost::function column, bool can_edit = true, - bool title = true, - int column_width = 200 + bool title = true ) : wxPanel (parent) , _get (get) , _set (set) - , _columns (columns.size ()) + , _columns (columns) , _column (column) , _edit (0) + , _default_width (200) { _sizer = new wxBoxSizer (wxHORIZONTAL); SetSizer (_sizer); @@ -59,14 +81,21 @@ public: if (title) { style |= wxLC_NO_HEADER; } - _list = new wxListCtrl (this, wxID_ANY, wxDefaultPosition, wxSize (columns.size() * column_width, 100), style); - for (size_t i = 0; i < columns.size(); ++i) { + int total_width = 0; + BOOST_FOREACH (EditableListColumn i, _columns) { + total_width += i.width.get_value_or (_default_width); + } + + _list = new wxListCtrl (this, wxID_ANY, wxDefaultPosition, wxSize(total_width, 100), style); + + int j = 0; + BOOST_FOREACH (EditableListColumn i, _columns) { wxListItem ip; - ip.SetId (i); - ip.SetText (std_to_wx (columns[i])); - ip.SetWidth (column_width); - _list->InsertColumn (i, ip); + ip.SetId (j); + ip.SetText (std_to_wx(i.name)); + _list->InsertColumn (j, ip); + ++j; } _sizer->Add (_list, 1, wxEXPAND); @@ -136,7 +165,7 @@ private: list_item.SetId (n); _list->InsertItem (list_item); - for (int i = 0; i < _columns; ++i) { + for (size_t i = 0; i < _columns.size(); ++i) { _list->SetItem (n, i, std_to_wx (_column (item, i))); } } @@ -191,7 +220,7 @@ private: } dialog->Destroy (); - for (int i = 0; i < _columns; ++i) { + for (size_t i = 0; i < _columns.size(); ++i) { _list->SetItem (item, i, std_to_wx (_column (all[item], i))); } @@ -215,16 +244,35 @@ private: void resized (wxSizeEvent& ev) { - int const w = GetSize().GetWidth() / _columns; - for (int i = 0; i < _columns; ++i) { - _list->SetColumnWidth (i, w); + int const w = _list->GetSize().GetWidth() - 2; + + int fixed_width = 0; + int growable = 0; + int j = 0; + BOOST_FOREACH (EditableListColumn i, _columns) { + fixed_width += i.width.get_value_or (_default_width); + if (!i.growable) { + _list->SetColumnWidth (j, i.width.get_value_or(_default_width)); + } else { + ++growable; + } + ++j; + } + + j = 0; + BOOST_FOREACH (EditableListColumn i, _columns) { + if (i.growable) { + _list->SetColumnWidth (j, i.width.get_value_or(_default_width) + (w - fixed_width) / growable); + } + ++j; } + ev.Skip (); } boost::function ()> _get; boost::function )> _set; - int _columns; + std::vector _columns; boost::function _column; wxButton* _add; @@ -232,6 +280,7 @@ private: wxButton* _remove; wxListCtrl* _list; wxBoxSizer* _sizer; + int _default_width; }; #endif diff --git a/src/wx/full_config_dialog.cc b/src/wx/full_config_dialog.cc index 9d4270542..27c969cc2 100644 --- a/src/wx/full_config_dialog.cc +++ b/src/wx/full_config_dialog.cc @@ -592,8 +592,8 @@ private: _use_any_servers = new CheckBox (_panel, _("Search network for servers")); _panel->GetSizer()->Add (_use_any_servers, 0, wxALL, _border); - vector columns; - columns.push_back (wx_to_std (_("IP address / host name"))); + vector columns; + columns.push_back (EditableListColumn(wx_to_std(_("IP address / host name")))); _servers_list = new EditableList ( _panel, columns, @@ -902,8 +902,8 @@ private: _from = new wxTextCtrl (_panel, wxID_ANY); table->Add (_from, 1, wxEXPAND | wxALL); - vector columns; - columns.push_back (wx_to_std (_("Address"))); + vector columns; + columns.push_back (EditableListColumn(wx_to_std(_("Address")))); add_label_to_sizer (table, _panel, _("CC addresses"), true); _cc = new EditableList ( _panel, @@ -1173,8 +1173,8 @@ private: _to = new wxTextCtrl (_panel, wxID_ANY); table->Add (_to, 1, wxEXPAND | wxALL); - vector columns; - columns.push_back (wx_to_std (_("Address"))); + vector columns; + columns.push_back (EditableListColumn(wx_to_std(_("Address")))); add_label_to_sizer (table, _panel, _("CC addresses"), true); _cc = new EditableList ( _panel, diff --git a/src/wx/metadata_dialog.cc b/src/wx/metadata_dialog.cc new file mode 100644 index 000000000..7cfcd5ca0 --- /dev/null +++ b/src/wx/metadata_dialog.cc @@ -0,0 +1,101 @@ +/* + Copyright (C) 2019 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 "metadata_dialog.h" +#include "editable_list.h" +#include "rating_dialog.h" +#include "lib/film.h" +#include +#include + +using std::string; +using std::vector; +using boost::weak_ptr; +using boost::shared_ptr; + +static string +column (dcp::Rating r, int c) +{ + if (c == 0) { + return r.agency; + } + + return r.label; +} + +MetadataDialog::MetadataDialog (wxWindow* parent, weak_ptr film) + : wxDialog (parent, wxID_ANY, _("Metadata")) + , _film (film) +{ + wxBoxSizer* overall_sizer = new wxBoxSizer (wxVERTICAL); + SetSizer (overall_sizer); + + wxFlexGridSizer* sizer = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP); + sizer->AddGrowableCol (1, 1); + + { + int flags = wxALIGN_TOP | wxLEFT | wxRIGHT | wxTOP; +#ifdef __WXOSX__ + flags |= wxALIGN_RIGHT; +#endif + wxStaticText* m = create_label (this, _("Ratings"), true); + sizer->Add (m, 0, flags, DCPOMATIC_SIZER_GAP); + } + + vector columns; + columns.push_back (EditableListColumn("Agency", 200, true)); + columns.push_back (EditableListColumn("Label", 50, false)); + _ratings = new EditableList ( + this, + columns, + bind(&MetadataDialog::ratings, this), + bind(&MetadataDialog::set_ratings, this, _1), + bind(&column, _1, _2), + true, + false + ); + sizer->Add (_ratings, 1, wxEXPAND); + + overall_sizer->Add (sizer, 1, wxEXPAND | wxALL, DCPOMATIC_DIALOG_BORDER); + + wxSizer* buttons = CreateSeparatedButtonSizer (wxCLOSE); + if (buttons) { + overall_sizer->Add (buttons, wxSizerFlags().Expand().DoubleBorder()); + } + + overall_sizer->Layout (); + overall_sizer->SetSizeHints (this); +} + +vector +MetadataDialog::ratings () const +{ + shared_ptr film = _film.lock (); + DCPOMATIC_ASSERT (film); + return film->ratings (); +} + +void +MetadataDialog::set_ratings (vector r) +{ + shared_ptr film = _film.lock (); + DCPOMATIC_ASSERT (film); + film->set_ratings (r); +} diff --git a/src/wx/metadata_dialog.h b/src/wx/metadata_dialog.h new file mode 100644 index 000000000..5c574a32b --- /dev/null +++ b/src/wx/metadata_dialog.h @@ -0,0 +1,42 @@ +/* + Copyright (C) 2019 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 "editable_list.h" +#include +#include +#include +#include +#include + +class Film; +class RatingDialog; + +class MetadataDialog : public wxDialog +{ +public: + MetadataDialog (wxWindow* parent, boost::weak_ptr film); + +private: + std::vector ratings () const; + void set_ratings (std::vector r); + + boost::weak_ptr _film; + EditableList* _ratings; +}; diff --git a/src/wx/rating_dialog.cc b/src/wx/rating_dialog.cc new file mode 100644 index 000000000..4fba353b7 --- /dev/null +++ b/src/wx/rating_dialog.cc @@ -0,0 +1,49 @@ +/* + Copyright (C) 2019 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 "rating_dialog.h" +#include "wx_util.h" + +RatingDialog::RatingDialog (wxWindow* parent) + : TableDialog (parent, _("Ratings"), 2, 1, true) +{ + add (_("Agency"), true); + _agency = new wxTextCtrl (this, wxID_ANY); + add (_agency); + + add (_("Label"), true); + _label = new wxTextCtrl (this, wxID_ANY); + add (_label); + + layout (); +} + +void +RatingDialog::set (dcp::Rating r) +{ + _agency->SetValue (std_to_wx(r.agency)); + _label->SetValue (std_to_wx(r.label)); +} + +dcp::Rating +RatingDialog::get () const +{ + return dcp::Rating(wx_to_std(_agency->GetValue()), wx_to_std(_label->GetValue())); +} diff --git a/src/wx/rating_dialog.h b/src/wx/rating_dialog.h new file mode 100644 index 000000000..51869c0ab --- /dev/null +++ b/src/wx/rating_dialog.h @@ -0,0 +1,35 @@ +/* + Copyright (C) 2019 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 "table_dialog.h" +#include + +class RatingDialog : public TableDialog +{ +public: + RatingDialog (wxWindow* parent); + + void set (dcp::Rating r); + dcp::Rating get () const; + +private: + wxTextCtrl* _agency; + wxTextCtrl* _label; +}; diff --git a/src/wx/screen_dialog.cc b/src/wx/screen_dialog.cc index 6bfc0da4c..71d0ed99d 100644 --- a/src/wx/screen_dialog.cc +++ b/src/wx/screen_dialog.cc @@ -142,8 +142,8 @@ ScreenDialog::ScreenDialog ( add_label_to_sizer (_sizer, this, _("Other trusted devices"), true, wxGBPosition (r, 0)); ++r; - vector columns; - columns.push_back (wx_to_std (_("Thumbprint"))); + vector columns; + columns.push_back (EditableListColumn(wx_to_std(_("Thumbprint")))); _trusted_device_list = new EditableList ( this, columns, diff --git a/src/wx/wscript b/src/wx/wscript index aad7ecc63..0985423bc 100644 --- a/src/wx/wscript +++ b/src/wx/wscript @@ -1,5 +1,5 @@ # -# Copyright (C) 2012-2018 Carl Hetherington +# Copyright (C) 2012-2019 Carl Hetherington # # This file is part of DCP-o-matic. # @@ -87,6 +87,7 @@ sources = """ make_chain_dialog.cc markers_dialog.cc message_dialog.cc + metadata_dialog.cc monitor_dialog.cc move_to_dialog.cc nag_dialog.cc @@ -100,6 +101,7 @@ sources = """ playhead_to_timecode_dialog.cc playhead_to_frame_dialog.cc question_dialog.cc + rating_dialog.cc recreate_chain_dialog.cc repeat_dialog.cc report_problem_dialog.cc -- 2.30.2