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