2 Copyright (C) 2013-2016 Carl Hetherington <cth@carlh.net>
4 This file is part of DCP-o-matic.
6 DCP-o-matic is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 DCP-o-matic is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
22 /** @file src/wx/content_widget.h
23 * @brief ContentWidget class.
27 #ifndef DCPOMATIC_CONTENT_WIDGET_H
28 #define DCPOMATIC_CONTENT_WIDGET_H
32 #include "lib/content.h"
33 #include <dcp/warnings.h>
34 LIBDCP_DISABLE_WARNINGS
35 #include <wx/gbsizer.h>
36 #include <wx/spinctrl.h>
38 LIBDCP_ENABLE_WARNINGS
42 /** @class ContentWidget
43 * @brief A widget which represents some Content state and which can be used
44 * when multiple pieces of content are selected.
46 * @param S Type of ContentPart being manipulated (e.g. VideoContent)
47 * @param T Type of the widget (e.g. wxSpinCtrl)
48 * @param U Data type of state as used by the model.
49 * @param V Data type of state as used by the view.
51 template <class S, class T, typename U, typename V>
55 /** @param parent Parent window.
56 * @param wrapped Control widget that we are wrapping.
57 * @param property ContentProperty that the widget is handling.
58 * @param part Part of Content that the property is in (e.g. &Content::video)
59 * @param model_getter Function on the Content to get the value.
60 * @param model_setter Function on the Content to set the value.
61 * @param view_changed Function called when the view has changed; useful for linking controls.
62 * @param view_to_model Function to convert a view value to a model value.
63 * @param model_to_view Function to convert a model value to a view value.
69 std::function<std::shared_ptr<S> (Content*)> part,
70 std::function<U (S*)> model_getter,
71 std::function<void (S*, U)> model_setter,
72 std::function<void ()> view_changed,
73 std::function<U (V)> view_to_model,
74 std::function<V (U)> model_to_view
78 , _button (new wxButton (parent, wxID_ANY, _("Multiple values")))
79 , _property (property)
80 , _part(std::move(part))
81 , _model_getter(std::move(model_getter))
82 , _model_setter(std::move(model_setter))
83 , _view_changed(std::move(view_changed))
84 , _view_to_model(std::move(view_to_model))
85 , _model_to_view(std::move(model_to_view))
86 , _ignore_model_changes (false)
88 _button->SetToolTip (_("Click the button to set all selected content to the same value."));
90 _button->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&ContentWidget::button_clicked, this));
93 ContentWidget (ContentWidget const&) = delete;
94 ContentWidget& operator= (ContentWidget const&) = delete;
96 /** @return the widget that we are wrapping */
102 typedef std::vector<std::shared_ptr<Content> > List;
104 /** Set the content that this control is working on (i.e. the selected content) */
105 void set_content (List content)
107 for (typename std::list<boost::signals2::connection>::iterator i = _connections.begin(); i != _connections.end(); ++i) {
111 _connections.clear ();
115 _wrapped->Enable (!_content.empty ());
117 update_from_model ();
119 for (typename List::iterator i = _content.begin(); i != _content.end(); ++i) {
120 #if BOOST_VERSION >= 106100
121 _connections.push_back ((*i)->Change.connect (boost::bind (&ContentWidget::model_changed, this, boost::placeholders::_1, boost::placeholders::_3)));
123 _connections.push_back ((*i)->Change.connect (boost::bind (&ContentWidget::model_changed, this, _1, _3)));
128 /** Add this widget to a wxGridBagSizer */
129 void add (wxGridBagSizer* sizer, wxGBPosition position, wxGBSpan span = wxDefaultSpan, int flag = 0)
132 _position = position;
134 _sizer->Add (_wrapped, _position, _span, flag);
137 /** Update the view from the model */
138 void update_from_model ()
140 if (_content.empty ()) {
145 typename List::iterator i = _content.begin ();
146 U const v = boost::bind (_model_getter, _part(_content.front().get()).get())();
147 while (i != _content.end() && boost::bind (_model_getter, _part(i->get()).get())() == v) {
151 if (i == _content.end ()) {
153 checked_set (_wrapped, _model_to_view (v));
161 _ignore_model_changes = true;
162 for (size_t i = 0; i < _content.size(); ++i) {
163 boost::bind (_model_setter, _part (_content[i].get()).get(), _view_to_model (wx_get (_wrapped))) ();
168 _ignore_model_changes = false;
180 if (_wrapped->IsShown() || !_sizer) {
184 _sizer->Detach (_button);
186 _sizer->Add (_wrapped, _position, _span);
193 if (_button->IsShown() || !_sizer) {
198 _sizer->Detach (_wrapped);
200 _sizer->Add (_button, _position, _span);
204 void button_clicked ()
206 U const v = boost::bind (_model_getter, _part(_content.front().get()).get())();
207 for (auto const& i: _content) {
208 boost::bind (_model_setter, _part(i.get()).get(), v)();
212 void model_changed (ChangeType type, int property)
214 if (type == ChangeType::DONE && property == _property && !_ignore_model_changes) {
215 update_from_model ();
220 wxGridBagSizer* _sizer;
221 wxGBPosition _position;
226 std::function<std::shared_ptr<S> (Content *)> _part;
227 std::function<U (S*)> _model_getter;
228 std::function<void (S*, U)> _model_setter;
229 std::function<void ()> _view_changed;
230 std::function<U (V)> _view_to_model;
231 std::function<V (U)> _model_to_view;
232 std::list<boost::signals2::connection> _connections;
233 bool _ignore_model_changes;
236 template <typename U, typename V>
239 return static_cast<V> (x);
243 class ContentSpinCtrl : public ContentWidget<S, wxSpinCtrl, int, int>
250 std::function<std::shared_ptr<S> (Content *)> part,
251 std::function<int (S*)> getter,
252 std::function<void (S*, int)> setter,
253 std::function<void ()> view_changed = std::function<void ()>()
255 : ContentWidget<S, wxSpinCtrl, int, int> (
266 wrapped->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&ContentWidget<S, wxSpinCtrl, int, int>::view_changed, this));
271 class ContentSpinCtrlDouble : public ContentWidget<S, wxSpinCtrlDouble, double, double>
274 ContentSpinCtrlDouble (
276 wxSpinCtrlDouble* wrapped,
278 std::function<std::shared_ptr<S> (Content *)> part,
279 std::function<double (S*)> getter,
280 std::function<void (S*, double)> setter,
281 std::function<void ()> view_changed = std::function<void ()>()
283 : ContentWidget<S, wxSpinCtrlDouble, double, double> (
290 &caster<double, double>,
291 &caster<double, double>
294 wrapped->Bind (wxEVT_COMMAND_SPINCTRLDOUBLE_UPDATED, boost::bind (&ContentWidget<S, wxSpinCtrlDouble, double, double>::view_changed, this));
298 template <class S, class U>
299 class ContentChoice : public ContentWidget<S, wxChoice, U, int>
306 std::function<std::shared_ptr<S> (Content *)> part,
307 std::function<U (S*)> getter,
308 std::function<void (S*, U)> setter,
309 std::function<U (int)> view_to_model,
310 std::function<int (U)> model_to_view,
311 std::function<void ()> view_changed = std::function<void()>()
313 : ContentWidget<S, wxChoice, U, int> (
325 wrapped->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&ContentWidget<S, wxChoice, U, int>::view_changed, this));