NOOP, remove trailing tabs/whitespace.
[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 #include "pbd/xml++.h"
25 #include "visibility_group.h"
26
27 #include "i18n.h"
28
29 using namespace std;
30
31 VisibilityGroup::VisibilityGroup (std::string const & name)
32         : _xml_property_name (name)
33         , _ignore_list_view_change (false)
34 {
35
36 }
37
38 /** Add a widget to the group.
39  *  @param widget The widget.
40  *  @param id Some single-word ID to be used for the state of this member in XML.
41  *  @param name User-visible name for the widget.
42  *  @param visible true to default to visible, otherwise false.
43  *  @param override A functor to decide whether the visibility specified by the member should be
44  *  overridden by some external factor; if the returned optional value is given, it will be used
45  *  to override whatever visibility setting the member has.
46  */
47
48 void
49 VisibilityGroup::add (Gtk::Widget* widget, string const & id, string const & name, bool visible, boost::function<boost::optional<bool> ()> override)
50 {
51         Member m;
52         m.widget = widget;
53         m.id = id;
54         m.name = name;
55         m.visible = visible;
56         m.override = override;
57
58         _members.push_back (m);
59 }
60
61 /** Pop up a menu (on right-click) to configure visibility of members */
62 bool
63 VisibilityGroup::button_press_event (GdkEventButton* ev)
64 {
65         if (ev->button != 3) {
66                 return false;
67         }
68
69         menu()->popup (1, ev->time);
70         return true;
71 }
72
73 Gtk::Menu*
74 VisibilityGroup::menu ()
75 {
76         using namespace Gtk::Menu_Helpers;
77
78         Gtk::Menu* m = Gtk::manage (new Gtk::Menu);
79         MenuList& items = m->items ();
80
81         for (vector<Member>::iterator i = _members.begin(); i != _members.end(); ++i) {
82                 items.push_back (CheckMenuElem (i->name));
83                 Gtk::CheckMenuItem* j = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
84                 j->set_active (i->visible);
85                 j->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &VisibilityGroup::toggle), i));
86         }
87
88         return m;
89 }
90
91 /** @return true if the member should be visible, even taking into account any override functor */
92 bool
93 VisibilityGroup::should_actually_be_visible (Member const & m) const
94 {
95         if (m.override) {
96                 boost::optional<bool> o = m.override ();
97                 if (o) {
98                         return o.get ();
99                 }
100         }
101
102         return m.visible;
103 }
104
105 /** Update visible consequences of any changes to our _members vector */
106 void
107 VisibilityGroup::update ()
108 {
109         for (vector<Member>::iterator i = _members.begin(); i != _members.end(); ++i) {
110                 if (i->widget) {
111                         if (should_actually_be_visible (*i)) {
112                                 i->widget->show ();
113                         } else {
114                                 i->widget->hide ();
115                         }
116                 }
117         }
118
119         update_list_view ();
120
121         VisibilityChanged (); /* EMIT SIGNAL */
122 }
123
124 void
125 VisibilityGroup::toggle (vector<Member>::iterator m)
126 {
127         m->visible = !m->visible;
128         update ();
129 }
130
131 void
132 VisibilityGroup::set_state (XMLNode const & node)
133 {
134         XMLProperty const * p = node.property (_xml_property_name);
135         if (!p) {
136                 return;
137         }
138
139         set_state (p->value ());
140 }
141
142 void
143 VisibilityGroup::set_state (string v)
144 {
145         for (vector<Member>::iterator i = _members.begin(); i != _members.end(); ++i) {
146                 i->visible = false;
147         }
148
149         do {
150                 string::size_type const comma = v.find_first_of (',');
151                 string segment = v.substr (0, comma);
152
153                 for (vector<Member>::iterator i = _members.begin(); i != _members.end(); ++i) {
154                         if (segment == i->id) {
155                                 i->visible = true;
156                         }
157                 }
158
159                 if (comma == string::npos) {
160                         break;
161                 }
162
163                 v = v.substr (comma + 1);
164
165         } while (1);
166
167         update ();
168 }
169
170 string
171 VisibilityGroup::get_state_name () const
172 {
173         return _xml_property_name;
174 }
175
176 string
177 VisibilityGroup::get_state_value () const
178 {
179         string result;
180         for (vector<Member>::const_iterator i = _members.begin(); i != _members.end(); ++i) {
181                 if (i->visible) {
182                         if (!result.empty ()) {
183                                 result += ',';
184                         }
185                         result += i->id;
186                 }
187         }
188
189         return result;
190 }
191
192 void
193 VisibilityGroup::update_list_view ()
194 {
195         if (!_model) {
196                 return;
197         }
198
199         _ignore_list_view_change = true;
200
201         _model->clear ();
202
203         for (vector<Member>::iterator i = _members.begin(); i != _members.end(); ++i) {
204                 Gtk::TreeModel::iterator j = _model->append ();
205                 Gtk::TreeModel::Row row = *j;
206                 row[_model_columns._visible] = i->visible;
207                 row[_model_columns._name] = i->name;
208                 row[_model_columns._iterator] = i;
209         }
210
211         _ignore_list_view_change = false;
212 }
213
214 Gtk::Widget *
215 VisibilityGroup::list_view ()
216 {
217         _model = Gtk::ListStore::create (_model_columns);
218
219         update_list_view ();
220
221         Gtk::TreeView* v = Gtk::manage (new Gtk::TreeView (_model));
222         v->set_headers_visible (false);
223         v->append_column ("", _model_columns._visible);
224         v->append_column ("", _model_columns._name);
225
226         Gtk::CellRendererToggle* visible_cell = dynamic_cast<Gtk::CellRendererToggle*> (v->get_column_cell_renderer (0));
227         visible_cell->property_activatable() = true;
228         visible_cell->signal_toggled().connect (sigc::mem_fun (*this, &VisibilityGroup::list_view_visible_changed));
229         return v;
230 }
231
232 void
233 VisibilityGroup::list_view_visible_changed (string const & path)
234 {
235         if (_ignore_list_view_change) {
236                 return;
237         }
238
239         Gtk::TreeModel::iterator i = _model->get_iter (path);
240         if (!i) {
241                 return;
242         }
243
244         vector<Member>::iterator j = (*i)[_model_columns._iterator];
245         j->visible = !j->visible;
246         (*i)[_model_columns._visible] = j->visible;
247
248         update ();
249 }