Add a couple of audio properties to the multi-select list.
[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
91         void update_from_model ()
92         {
93                 if (_content.empty ()) {
94                         set_single ();
95                         return;
96                 }
97
98                 typename List::iterator i = _content.begin ();
99                 int const v = boost::bind (_getter, _content.front().get())();
100                 while (i != _content.end() && boost::bind (_getter, i->get())() == v) {
101                         ++i;
102                 }
103
104                 if (i == _content.end ()) {
105                         set_single ();
106                         checked_set (_wrapped, v);
107                 } else {
108                         set_multiple ();
109                 }
110         }
111
112 private:
113         
114         void set_single ()
115         {
116                 if (_wrapped->IsShown ()) {
117                         return;
118                 }
119
120                 _sizer->Detach (_button);
121                 _button->Hide ();
122                 _sizer->Add (_wrapped, _position);
123                 _wrapped->Show ();
124                 _sizer->Layout ();
125         }
126
127         void set_multiple ()
128         {
129                 if (_button->IsShown ()) {
130                         return;
131                 }
132                 
133                 _wrapped->Hide ();
134                 _sizer->Detach (_wrapped);
135                 _button->Show ();
136                 _sizer->Add (_button, _position);
137                 _sizer->Layout ();
138         }
139
140         void button_clicked ()
141         {
142                 int const v = boost::bind (_getter, _content.front().get())();
143                 for (typename List::iterator i = _content.begin (); i != _content.end(); ++i) {
144                         boost::bind (_setter, i->get(), v) ();
145                 }
146         }
147
148         void view_changed ()
149         {
150                 for (size_t i = 0; i < _content.size(); ++i) {
151                         /* Only update our view on the last time round this loop */
152                         _ignore_model_changes = i < (_content.size() - 1);
153                         boost::bind (_setter, _content[i].get(), _wrapped->GetValue ()) ();
154                 }
155         }
156
157         void model_changed (int property)
158         {
159                 if (property == _property && !_ignore_model_changes) {
160                         update_from_model ();
161                 }
162         }
163         
164         T* _wrapped;
165         wxGridBagSizer* _sizer;
166         wxGBPosition _position;
167         wxButton* _button;
168         List _content;
169         int _property;
170         boost::function<int (S*)> _getter;
171         boost::function<void (S*, int)> _setter;
172         std::list<boost::signals2::connection> _connections;
173         bool _ignore_model_changes;
174 };
175
176 #endif