Pull more stuff into ContentWidget.
[dcpomatic.git] / src / wx / content_widget.h
1 /*
2     Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #ifndef DCPOMATIC_MULTIPLE_WIDGET_H
21 #define DCPOMATIC_MULTIPLE_WIDGET_H
22
23 #include <vector>
24 #include <wx/wx.h>
25 #include <wx/gbsizer.h>
26 #include <boost/function.hpp>
27 #include "wx_util.h"
28
29 /** A widget which represents some Content state and which can be used
30  *  when multiple pieces of content are selected.
31  *
32  *  @param S Type containing the content being represented (e.g. VideoContent)
33  *  @param T Type of the widget (e.g. wxSpinCtrl)
34  */
35 template <class S, class T>
36 class ContentWidget
37 {
38 public:
39         /** @param parent Parent window.
40          *  @param wrapped Control widget that we are wrapping.
41          *  @param property ContentProperty that the widget is handling.
42          *  @param getter Function on the Content to get the value.
43          *  @param setter Function on the Content to set the value.
44          */
45         ContentWidget (wxWindow* parent, T* wrapped, int property, boost::function<int (S*)> getter, boost::function<void (S*, int)> setter)
46                 : _wrapped (wrapped)
47                 , _sizer (0)
48                 , _button (new wxButton (parent, wxID_ANY, _("Multiple values")))
49                 , _property (property)
50                 , _getter (getter)
51                 , _setter (setter)
52                 , _ignore_model_changes (false)
53         {
54                 _button->SetToolTip (_("Click the button to set all selected content to the same value."));
55                 _button->Hide ();
56                 _button->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&ContentWidget::button_clicked, this));
57                 _wrapped->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&ContentWidget::view_changed, this));
58         }
59
60         T* wrapped () const {
61                 return _wrapped;
62         }
63
64         typedef std::vector<boost::shared_ptr<S> > List;
65
66         void set_content (List content)
67         {
68                 for (typename std::list<boost::signals2::connection>::iterator i = _connections.begin(); i != _connections.end(); ++i) {
69                         i->disconnect ();
70                 }
71                 
72                 _content = content;
73
74                 _wrapped->Enable (!_content.empty ());
75
76                 update_from_model ();
77
78                 for (typename List::iterator i = _content.begin(); i != _content.end(); ++i) {
79                         _connections.push_back ((*i)->Changed.connect (boost::bind (&ContentWidget::model_changed, this, _2)));
80                 }
81         }
82
83         void add (wxGridBagSizer* sizer, wxGBPosition position)
84         {
85                 _sizer = sizer;
86                 _position = position;
87                 _sizer->Add (_wrapped, _position);
88         }
89
90 private:
91         
92         void set_single ()
93         {
94                 if (_wrapped->IsShown ()) {
95                         return;
96                 }
97
98                 _sizer->Detach (_button);
99                 _button->Hide ();
100                 _sizer->Add (_wrapped, _position);
101                 _wrapped->Show ();
102                 _sizer->Layout ();
103         }
104
105         void set_multiple ()
106         {
107                 if (_button->IsShown ()) {
108                         return;
109                 }
110                 
111                 _wrapped->Hide ();
112                 _sizer->Detach (_wrapped);
113                 _button->Show ();
114                 _sizer->Add (_button, _position);
115                 _sizer->Layout ();
116         }
117
118         void button_clicked ()
119         {
120                 int const v = boost::bind (_getter, _content.front().get())();
121                 for (typename List::iterator i = _content.begin (); i != _content.end(); ++i) {
122                         boost::bind (_setter, i->get(), v) ();
123                 }
124         }
125
126         void view_changed ()
127         {
128                 for (size_t i = 0; i < _content.size(); ++i) {
129                         /* Only update our view on the last time round this loop */
130                         _ignore_model_changes = i < (_content.size() - 1);
131                         boost::bind (_setter, _content[i].get(), _wrapped->GetValue ()) ();
132                 }
133         }
134
135         void update_from_model ()
136         {
137                 if (_content.empty ()) {
138                         set_single ();
139                         return;
140                 }
141
142                 typename List::iterator i = _content.begin ();
143                 int const v = boost::bind (_getter, _content.front().get())();
144                 while (i != _content.end() && boost::bind (_getter, i->get())() == v) {
145                         ++i;
146                 }
147
148                 if (i == _content.end ()) {
149                         set_single ();
150                         checked_set (_wrapped, v);
151                 } else {
152                         set_multiple ();
153                 }
154         }
155
156         void model_changed (int property)
157         {
158                 if (property == _property && !_ignore_model_changes) {
159                         update_from_model ();
160                 }
161         }
162         
163         T* _wrapped;
164         wxGridBagSizer* _sizer;
165         wxGBPosition _position;
166         wxButton* _button;
167         List _content;
168         int _property;
169         boost::function<int (S*)> _getter;
170         boost::function<void (S*, int)> _setter;
171         std::list<boost::signals2::connection> _connections;
172         bool _ignore_model_changes;
173 };
174
175 #endif