enough with umpteen "i18n.h" files. Consolidate on pbd/i18n.h
[ardour.git] / gtk2_ardour / visibility_group.cc
1 /*
2     Copyright (C) 2011 Paul Davis
3     Author: Carl Hetherington <cth@carlh.net>
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18
19 */
20
21 #include <gtkmm/menu.h>
22 #include <gtkmm/menushell.h>
23 #include <gtkmm/treeview.h>
24
25 #include "pbd/strsplit.h"
26 #include "pbd/xml++.h"
27
28 #include "visibility_group.h"
29
30 #include "pbd/i18n.h"
31
32 using namespace std;
33
34 VisibilityGroup::VisibilityGroup (std::string const & name)
35         : _xml_property_name (name)
36         , _ignore_list_view_change (false)
37 {
38
39 }
40
41 /** Add a widget to the group.
42  *  @param widget The widget.
43  *  @param id Some single-word ID to be used for the state of this member in XML.
44  *  @param name User-visible name for the widget.
45  *  @param visible true to default to visible, otherwise false.
46  *  @param override A functor to decide whether the visibility specified by the member should be
47  *  overridden by some external factor; if the returned optional value is given, it will be used
48  *  to override whatever visibility setting the member has.
49  */
50
51 void
52 VisibilityGroup::add (Gtk::Widget* widget, string const & id, string const & name, bool visible, boost::function<boost::optional<bool> ()> override)
53 {
54         Member m;
55         m.widget = widget;
56         m.id = id;
57         m.name = name;
58         m.visible = visible;
59         m.override = override;
60
61         _members.push_back (m);
62 }
63
64 /** Pop up a menu (on right-click) to configure visibility of members */
65 bool
66 VisibilityGroup::button_press_event (GdkEventButton* ev)
67 {
68         if (ev->button != 3) {
69                 return false;
70         }
71
72         menu()->popup (1, ev->time);
73         return true;
74 }
75
76 Gtk::Menu*
77 VisibilityGroup::menu ()
78 {
79         using namespace Gtk::Menu_Helpers;
80
81         Gtk::Menu* m = Gtk::manage (new Gtk::Menu);
82         MenuList& items = m->items ();
83
84         for (vector<Member>::iterator i = _members.begin(); i != _members.end(); ++i) {
85                 items.push_back (CheckMenuElem (i->name));
86                 Gtk::CheckMenuItem* j = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
87                 j->set_active (i->visible);
88                 j->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &VisibilityGroup::toggle), i));
89         }
90
91         return m;
92 }
93
94 /** @return true if the member should be visible, even taking into account any override functor */
95 bool
96 VisibilityGroup::should_actually_be_visible (Member const & m) const
97 {
98         if (m.override) {
99                 boost::optional<bool> o = m.override ();
100                 if (o) {
101                         return o.get ();
102                 }
103         }
104
105         return m.visible;
106 }
107
108 /** Update visible consequences of any changes to our _members vector */
109 void
110 VisibilityGroup::update ()
111 {
112         for (vector<Member>::iterator i = _members.begin(); i != _members.end(); ++i) {
113                 if (i->widget) {
114                         if (should_actually_be_visible (*i)) {
115                                 i->widget->show ();
116                         } else {
117                                 i->widget->hide ();
118                         }
119                 }
120         }
121
122         update_list_view ();
123
124         VisibilityChanged (); /* EMIT SIGNAL */
125 }
126
127 void
128 VisibilityGroup::toggle (vector<Member>::iterator m)
129 {
130         m->visible = !m->visible;
131         update ();
132 }
133
134 void
135 VisibilityGroup::set_state (XMLNode const & node)
136 {
137         XMLProperty const * p = node.property (_xml_property_name);
138         if (!p) {
139                 return;
140         }
141
142         set_state (p->value ());
143 }
144
145 void
146 VisibilityGroup::set_state (string v)
147 {
148         for (vector<Member>::iterator i = _members.begin(); i != _members.end(); ++i) {
149                 i->visible = false;
150         }
151
152         do {
153                 string::size_type const comma = v.find_first_of (',');
154                 string segment = v.substr (0, comma);
155
156                 for (vector<Member>::iterator i = _members.begin(); i != _members.end(); ++i) {
157                         if (segment == i->id) {
158                                 i->visible = true;
159                         }
160                 }
161
162                 if (comma == string::npos) {
163                         break;
164                 }
165
166                 v = v.substr (comma + 1);
167
168         } while (1);
169
170         update ();
171 }
172
173 string
174 VisibilityGroup::remove_element (std::string const& from, std::string const& element)
175 {
176         std::vector<string> s;
177         std::string ret;
178
179         split (from, s, ',');
180         for (std::vector<string>::const_iterator i = s.begin(); i != s.end(); ++i) {
181                 if ((*i) == element) {
182                         continue;
183                 }
184                 if (!ret.empty()) {
185                         ret += ',';
186                 }
187                 ret += *i;
188         }
189
190         return ret;
191 }
192
193 string
194 VisibilityGroup::add_element (std::string const& from, std::string const& element)
195 {
196         std::vector<string> s;
197         std::string ret;
198         
199         split (from, s, ',');
200
201         for (std::vector<string>::const_iterator i = s.begin(); i != s.end(); ++i) {
202                 if ((*i) == element) {
203                         /* already present, just return the original */
204                         return from;
205                 }
206         }
207
208         ret = from;
209
210         if (!ret.empty()) {
211                 ret += ',';
212         }
213
214         ret += element;
215
216         return ret;
217 }
218
219 string
220 VisibilityGroup::get_state_name () const
221 {
222         return _xml_property_name;
223 }
224
225 string
226 VisibilityGroup::get_state_value () const
227 {
228         string result;
229         for (vector<Member>::const_iterator i = _members.begin(); i != _members.end(); ++i) {
230                 if (i->visible) {
231                         if (!result.empty ()) {
232                                 result += ',';
233                         }
234                         result += i->id;
235                 }
236         }
237
238         return result;
239 }
240
241 void
242 VisibilityGroup::update_list_view ()
243 {
244         if (!_model) {
245                 return;
246         }
247
248         _ignore_list_view_change = true;
249
250         _model->clear ();
251
252         for (vector<Member>::iterator i = _members.begin(); i != _members.end(); ++i) {
253                 Gtk::TreeModel::iterator j = _model->append ();
254                 Gtk::TreeModel::Row row = *j;
255                 row[_model_columns._visible] = i->visible;
256                 row[_model_columns._name] = i->name;
257                 row[_model_columns._iterator] = i;
258         }
259
260         _ignore_list_view_change = false;
261 }
262
263 Gtk::Widget *
264 VisibilityGroup::list_view ()
265 {
266         _model = Gtk::ListStore::create (_model_columns);
267
268         update_list_view ();
269
270         Gtk::TreeView* v = Gtk::manage (new Gtk::TreeView (_model));
271         v->set_headers_visible (false);
272         v->append_column ("", _model_columns._visible);
273         v->append_column ("", _model_columns._name);
274
275         Gtk::CellRendererToggle* visible_cell = dynamic_cast<Gtk::CellRendererToggle*> (v->get_column_cell_renderer (0));
276         visible_cell->property_activatable() = true;
277         visible_cell->signal_toggled().connect (sigc::mem_fun (*this, &VisibilityGroup::list_view_visible_changed));
278         return v;
279 }
280
281 void
282 VisibilityGroup::list_view_visible_changed (string const & path)
283 {
284         if (_ignore_list_view_change) {
285                 return;
286         }
287
288         Gtk::TreeModel::iterator i = _model->get_iter (path);
289         if (!i) {
290                 return;
291         }
292
293         vector<Member>::iterator j = (*i)[_model_columns._iterator];
294         j->visible = !j->visible;
295         (*i)[_model_columns._visible] = j->visible;
296
297         update ();
298 }