Disable warnings around all wx includes.
[dcpomatic.git] / src / wx / content_widget.h
1 /*
2     Copyright (C) 2013-2016 Carl Hetherington <cth@carlh.net>
3
4     This file is part of DCP-o-matic.
5
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.
10
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.
15
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/>.
18
19 */
20
21 /** @file  src/wx/content_widget.h
22  *  @brief ContentWidget class.
23  */
24
25
26 #ifndef DCPOMATIC_CONTENT_WIDGET_H
27 #define DCPOMATIC_CONTENT_WIDGET_H
28
29
30 #include "wx_util.h"
31 #include "lib/content.h"
32 #include "lib/warnings.h"
33 DCPOMATIC_DISABLE_WARNINGS
34 #include <wx/gbsizer.h>
35 #include <wx/spinctrl.h>
36 #include <wx/wx.h>
37 DCPOMATIC_ENABLE_WARNINGS
38 #include <vector>
39
40
41 /** @class ContentWidget
42  *  @brief A widget which represents some Content state and which can be used
43  *  when multiple pieces of content are selected.
44  *
45  *  @param S Type of ContentPart being manipulated (e.g. VideoContent)
46  *  @param T Type of the widget (e.g. wxSpinCtrl)
47  *  @param U Data type of state as used by the model.
48  *  @param V Data type of state as used by the view.
49  */
50 template <class S, class T, typename U, typename V>
51 class ContentWidget
52 {
53 public:
54         /** @param parent Parent window.
55          *  @param wrapped Control widget that we are wrapping.
56          *  @param property ContentProperty that the widget is handling.
57          *  @param part Part of Content that the property is in (e.g. &Content::video)
58          *  @param model_getter Function on the Content to get the value.
59          *  @param model_setter Function on the Content to set the value.
60          *  @param view_changed Function called when the view has changed; useful for linking controls.
61          *  @param view_to_model Function to convert a view value to a model value.
62          *  @param model_to_view Function to convert a model value to a view value.
63          */
64         ContentWidget (
65                 wxWindow* parent,
66                 T* wrapped,
67                 int property,
68                 std::function<std::shared_ptr<S> (Content*)> part,
69                 std::function<U (S*)> model_getter,
70                 std::function<void (S*, U)> model_setter,
71                 std::function<void ()> view_changed,
72                 std::function<U (V)> view_to_model,
73                 std::function<V (U)> model_to_view
74                 )
75                 : _wrapped (wrapped)
76                 , _sizer (0)
77                 , _button (new wxButton (parent, wxID_ANY, _("Multiple values")))
78                 , _property (property)
79                 , _part (part)
80                 , _model_getter (model_getter)
81                 , _model_setter (model_setter)
82                 , _view_changed (view_changed)
83                 , _view_to_model (view_to_model)
84                 , _model_to_view (model_to_view)
85                 , _ignore_model_changes (false)
86         {
87                 _button->SetToolTip (_("Click the button to set all selected content to the same value."));
88                 _button->Hide ();
89                 _button->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&ContentWidget::button_clicked, this));
90         }
91
92         ContentWidget (ContentWidget const&) = delete;
93         ContentWidget& operator= (ContentWidget const&) = delete;
94
95         /** @return the widget that we are wrapping */
96         T* wrapped () const
97         {
98                 return _wrapped;
99         }
100
101         typedef std::vector<std::shared_ptr<Content> > List;
102
103         /** Set the content that this control is working on (i.e. the selected content) */
104         void set_content (List content)
105         {
106                 for (typename std::list<boost::signals2::connection>::iterator i = _connections.begin(); i != _connections.end(); ++i) {
107                         i->disconnect ();
108                 }
109
110                 _connections.clear ();
111
112                 _content = content;
113
114                 _wrapped->Enable (!_content.empty ());
115
116                 update_from_model ();
117
118                 for (typename List::iterator i = _content.begin(); i != _content.end(); ++i) {
119 #if BOOST_VERSION >= 106100
120                         _connections.push_back ((*i)->Change.connect (boost::bind (&ContentWidget::model_changed, this, boost::placeholders::_1, boost::placeholders::_3)));
121 #else
122                         _connections.push_back ((*i)->Change.connect (boost::bind (&ContentWidget::model_changed, this, _1, _3)));
123 #endif
124                 }
125         }
126
127         /** Add this widget to a wxGridBagSizer */
128         void add (wxGridBagSizer* sizer, wxGBPosition position, wxGBSpan span = wxDefaultSpan, int flag = 0)
129         {
130                 _sizer = sizer;
131                 _position = position;
132                 _span = span;
133                 _sizer->Add (_wrapped, _position, _span, flag);
134         }
135
136         /** Update the view from the model */
137         void update_from_model ()
138         {
139                 if (_content.empty ()) {
140                         set_single ();
141                         return;
142                 }
143
144                 typename List::iterator i = _content.begin ();
145                 U const v = boost::bind (_model_getter, _part(_content.front().get()).get())();
146                 while (i != _content.end() && boost::bind (_model_getter, _part(i->get()).get())() == v) {
147                         ++i;
148                 }
149
150                 if (i == _content.end ()) {
151                         set_single ();
152                         checked_set (_wrapped, _model_to_view (v));
153                 } else {
154                         set_multiple ();
155                 }
156         }
157
158         void view_changed ()
159         {
160                 _ignore_model_changes = true;
161                 for (size_t i = 0; i < _content.size(); ++i) {
162                         boost::bind (_model_setter, _part (_content[i].get()).get(), _view_to_model (wx_get (_wrapped))) ();
163                 }
164                 if (_view_changed) {
165                         _view_changed ();
166                 }
167                 _ignore_model_changes = false;
168         }
169
170         void show (bool s)
171         {
172                 _wrapped->Show (s);
173         }
174
175 private:
176
177         void set_single ()
178         {
179                 if (_wrapped->IsShown() || !_sizer) {
180                         return;
181                 }
182
183                 _sizer->Detach (_button);
184                 _button->Hide ();
185                 _sizer->Add (_wrapped, _position, _span);
186                 _wrapped->Show ();
187                 _sizer->Layout ();
188         }
189
190         void set_multiple ()
191         {
192                 if (_button->IsShown() || !_sizer) {
193                         return;
194                 }
195
196                 _wrapped->Hide ();
197                 _sizer->Detach (_wrapped);
198                 _button->Show ();
199                 _sizer->Add (_button, _position, _span);
200                 _sizer->Layout ();
201         }
202
203         void button_clicked ()
204         {
205                 U const v = boost::bind (_model_getter, _part(_content.front().get()).get())();
206                 for (auto const& i: _content) {
207                         boost::bind (_model_setter, _part(i.get()).get(), v)();
208                 }
209         }
210
211         void model_changed (ChangeType type, int property)
212         {
213                 if (type == ChangeType::DONE && property == _property && !_ignore_model_changes) {
214                         update_from_model ();
215                 }
216         }
217
218         T* _wrapped;
219         wxGridBagSizer* _sizer;
220         wxGBPosition _position;
221         wxGBSpan _span;
222         wxButton* _button;
223         List _content;
224         int _property;
225         std::function<std::shared_ptr<S> (Content *)> _part;
226         std::function<U (S*)> _model_getter;
227         std::function<void (S*, U)> _model_setter;
228         std::function<void ()> _view_changed;
229         std::function<U (V)> _view_to_model;
230         std::function<V (U)> _model_to_view;
231         std::list<boost::signals2::connection> _connections;
232         bool _ignore_model_changes;
233 };
234
235 template <typename U, typename V>
236 V caster (U x)
237 {
238         return static_cast<V> (x);
239 }
240
241 template <class S>
242 class ContentSpinCtrl : public ContentWidget<S, wxSpinCtrl, int, int>
243 {
244 public:
245         ContentSpinCtrl (
246                 wxWindow* parent,
247                 wxSpinCtrl* wrapped,
248                 int property,
249                 std::function<std::shared_ptr<S> (Content *)> part,
250                 std::function<int (S*)> getter,
251                 std::function<void (S*, int)> setter,
252                 std::function<void ()> view_changed = std::function<void ()>()
253                 )
254                 : ContentWidget<S, wxSpinCtrl, int, int> (
255                         parent,
256                         wrapped,
257                         property,
258                         part,
259                         getter, setter,
260                         view_changed,
261                         &caster<int, int>,
262                         &caster<int, int>
263                         )
264         {
265                 wrapped->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&ContentWidget<S, wxSpinCtrl, int, int>::view_changed, this));
266         }
267 };
268
269 template <class S>
270 class ContentSpinCtrlDouble : public ContentWidget<S, wxSpinCtrlDouble, double, double>
271 {
272 public:
273         ContentSpinCtrlDouble (
274                 wxWindow* parent,
275                 wxSpinCtrlDouble* wrapped,
276                 int property,
277                 std::function<std::shared_ptr<S> (Content *)> part,
278                 std::function<double (S*)> getter,
279                 std::function<void (S*, double)> setter,
280                 std::function<void ()> view_changed = std::function<void ()>()
281                 )
282                 : ContentWidget<S, wxSpinCtrlDouble, double, double> (
283                         parent,
284                         wrapped,
285                         property,
286                         part,
287                         getter, setter,
288                         view_changed,
289                         &caster<double, double>,
290                         &caster<double, double>
291                         )
292         {
293                 wrapped->Bind (wxEVT_COMMAND_SPINCTRLDOUBLE_UPDATED, boost::bind (&ContentWidget<S, wxSpinCtrlDouble, double, double>::view_changed, this));
294         }
295 };
296
297 template <class S, class U>
298 class ContentChoice : public ContentWidget<S, wxChoice, U, int>
299 {
300 public:
301         ContentChoice (
302                 wxWindow* parent,
303                 wxChoice* wrapped,
304                 int property,
305                 std::function<std::shared_ptr<S> (Content *)> part,
306                 std::function<U (S*)> getter,
307                 std::function<void (S*, U)> setter,
308                 std::function<U (int)> view_to_model,
309                 std::function<int (U)> model_to_view,
310                 std::function<void ()> view_changed = std::function<void()>()
311                 )
312                 : ContentWidget<S, wxChoice, U, int> (
313                         parent,
314                         wrapped,
315                         property,
316                         part,
317                         getter,
318                         setter,
319                         view_changed,
320                         view_to_model,
321                         model_to_view
322                         )
323         {
324                 wrapped->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&ContentWidget<S, wxChoice, U, int>::view_changed, this));
325         }
326
327 };
328
329 #endif