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