split out the logic behind step editing from MidiTimeAxisView as much as possible
[ardour.git] / gtk2_ardour / editor_route_groups.cc
1 /*
2     Copyright (C) 2000 Paul Davis
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 #include <cstdlib>
21 #include <cmath>
22
23 #include <gtkmm2ext/gtk_ui.h>
24 #include "ardour/route_group.h"
25
26 #include "editor.h"
27 #include "keyboard.h"
28 #include "marker.h"
29 #include "time_axis_view.h"
30 #include "prompter.h"
31 #include "gui_thread.h"
32 #include "editor_group_tabs.h"
33 #include "route_group_dialog.h"
34 #include "route_time_axis.h"
35 #include "editor_routes.h"
36 #include "editor_route_groups.h"
37
38 #include "ardour/route.h"
39 #include "ardour/session.h"
40
41 #include "i18n.h"
42
43 using namespace std;
44 using namespace ARDOUR;
45 using namespace PBD;
46 using namespace Gtk;
47 using Gtkmm2ext::Keyboard;
48
49 EditorRouteGroups::EditorRouteGroups (Editor* e)
50         : EditorComponent (e),
51           _in_row_change (false)
52
53 {
54         _model = ListStore::create (_columns);
55         _display.set_model (_model);
56
57         _display.append_column (_("Name"), _columns.text);
58
59         _display.append_column (_("G"), _columns.gain);
60         _display.append_column (_("R"), _columns.record);
61         _display.append_column (_("M"), _columns.mute);
62         _display.append_column (_("S"), _columns.solo);
63         _display.append_column (_("Sel"), _columns.select);
64         _display.append_column (_("E"), _columns.edits);
65
66         _display.append_column (_("Show"), _columns.is_visible);
67
68         _display.get_column (0)->set_data (X_("colnum"), GUINT_TO_POINTER(0));
69         _display.get_column (1)->set_data (X_("colnum"), GUINT_TO_POINTER(1));
70         _display.get_column (2)->set_data (X_("colnum"), GUINT_TO_POINTER(2));
71         _display.get_column (3)->set_data (X_("colnum"), GUINT_TO_POINTER(3));
72         _display.get_column (4)->set_data (X_("colnum"), GUINT_TO_POINTER(4));
73         _display.get_column (5)->set_data (X_("colnum"), GUINT_TO_POINTER(5));
74         _display.get_column (6)->set_data (X_("colnum"), GUINT_TO_POINTER(6));
75         _display.get_column (7)->set_data (X_("colnum"), GUINT_TO_POINTER(7));
76
77         _display.get_column (0)->set_expand (true);
78         _display.get_column (1)->set_expand (false);
79         _display.get_column (2)->set_expand (false);
80         _display.get_column (3)->set_expand (false);
81         _display.get_column (4)->set_expand (false);
82         _display.get_column (5)->set_expand (false);
83         _display.get_column (6)->set_expand (false);
84         _display.get_column (7)->set_expand (false);
85
86         _display.set_headers_visible (true);
87
88         /* name is directly editable */
89
90         CellRendererText* name_cell = dynamic_cast<CellRendererText*>(_display.get_column_cell_renderer (0));
91         name_cell->property_editable() = true;
92         name_cell->signal_edited().connect (sigc::mem_fun (*this, &EditorRouteGroups::name_edit));
93
94         /* use checkbox for the active + visible columns */
95
96         CellRendererToggle* active_cell = dynamic_cast<CellRendererToggle*>(_display.get_column_cell_renderer (1));
97         active_cell->property_activatable() = true;
98         active_cell->property_radio() = false;
99
100         active_cell = dynamic_cast<CellRendererToggle*>(_display.get_column_cell_renderer (2));
101         active_cell->property_activatable() = true;
102         active_cell->property_radio() = false;
103
104         active_cell = dynamic_cast<CellRendererToggle*>(_display.get_column_cell_renderer (3));
105         active_cell->property_activatable() = true;
106         active_cell->property_radio() = false;
107
108         active_cell = dynamic_cast<CellRendererToggle*>(_display.get_column_cell_renderer (4));
109         active_cell->property_activatable() = true;
110         active_cell->property_radio() = false;
111
112         active_cell = dynamic_cast<CellRendererToggle*>(_display.get_column_cell_renderer (5));
113         active_cell->property_activatable() = true;
114         active_cell->property_radio() = false;
115
116         active_cell = dynamic_cast<CellRendererToggle*>(_display.get_column_cell_renderer (6));
117         active_cell->property_activatable() = true;
118         active_cell->property_radio() = false;
119
120         active_cell = dynamic_cast<CellRendererToggle*>(_display.get_column_cell_renderer (7));
121         active_cell->property_activatable() = true;
122         active_cell->property_radio() = false;
123
124         _model->signal_row_changed().connect (sigc::mem_fun (*this, &EditorRouteGroups::row_change));
125
126         _display.set_name ("EditGroupList");
127         _display.get_selection()->set_mode (SELECTION_SINGLE);
128         _display.set_headers_visible (true);
129         _display.set_reorderable (false);
130         _display.set_rules_hint (true);
131         _display.set_size_request (75, -1);
132
133         _scroller.add (_display);
134         _scroller.set_policy (POLICY_AUTOMATIC, POLICY_AUTOMATIC);
135
136         _display.signal_button_press_event().connect (sigc::mem_fun(*this, &EditorRouteGroups::button_press_event), false);
137
138         _display_packer = new VBox;
139         HBox* button_box = manage (new HBox());
140         button_box->set_homogeneous (true);
141
142         Button* add_button = manage (new Button ());
143         Button* remove_button = manage (new Button ());
144
145         Widget* w;
146
147         w = manage (new Image (Stock::ADD, ICON_SIZE_BUTTON));
148         w->show();
149         add_button->add (*w);
150
151         w = manage (new Image (Stock::REMOVE, ICON_SIZE_BUTTON));
152         w->show();
153         remove_button->add (*w);
154
155         add_button->signal_clicked().connect (sigc::hide_return (sigc::mem_fun (*this, &EditorRouteGroups::run_new_group_dialog)));
156         remove_button->signal_clicked().connect (sigc::mem_fun (*this, &EditorRouteGroups::remove_selected));
157
158         button_box->pack_start (*add_button);
159         button_box->pack_start (*remove_button);
160
161         _display_packer->pack_start (_scroller, true, true);
162         _display_packer->pack_start (*button_box, false, false);
163 }
164
165 void
166 EditorRouteGroups::remove_selected ()
167 {
168         Glib::RefPtr<TreeSelection> selection = _display.get_selection();
169         TreeView::Selection::ListHandle_Path rows = selection->get_selected_rows ();
170
171         if (rows.empty()) {
172                 return;
173         }
174
175         TreeView::Selection::ListHandle_Path::iterator i = rows.begin();
176         TreeIter iter;
177
178         /* selection mode is single, so rows.begin() is it */
179
180         if ((iter = _model->get_iter (*i))) {
181
182                 RouteGroup* rg = (*iter)[_columns.routegroup];
183
184                 if (rg) {
185                         _session->remove_route_group (*rg);
186                 }
187         }
188 }
189
190 void
191 EditorRouteGroups::button_clicked ()
192 {
193         run_new_group_dialog ();
194 }
195
196 gint
197 EditorRouteGroups::button_press_event (GdkEventButton* ev)
198 {
199         TreeModel::Path path;
200         TreeIter iter;
201         RouteGroup* group = 0;
202         TreeViewColumn* column;
203         int cellx;
204         int celly;
205
206         bool const p = _display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly);
207
208         if (p) {
209                 iter = _model->get_iter (path);
210         }
211
212         if (iter) {
213                 group = (*iter)[_columns.routegroup];
214         }
215
216         if (Keyboard::is_context_menu_event (ev)) {
217                 _editor->_group_tabs->get_menu(group)->popup (1, ev->time);
218                 return true;
219         }
220
221         if (!p) {
222                 return 1;
223         }
224
225         switch (GPOINTER_TO_UINT (column->get_data (X_("colnum")))) {
226         case 0:
227                 if (Keyboard::is_edit_event (ev)) {
228                         if ((iter = _model->get_iter (path))) {
229                                 if ((group = (*iter)[_columns.routegroup]) != 0) {
230                                         // edit_route_group (group);
231 #ifdef GTKOSX
232                                         _display.queue_draw();
233 #endif
234                                         return true;
235                                 }
236                         }
237
238                 }
239                 break;
240
241         case 1:
242                 if ((iter = _model->get_iter (path))) {
243                         bool gain = (*iter)[_columns.gain];
244                         (*iter)[_columns.gain] = !gain;
245 #ifdef GTKOSX
246                         _display.queue_draw();
247 #endif
248                         return true;
249                 }
250                 break;
251
252         case 2:
253                 if ((iter = _model->get_iter (path))) {
254                         bool record = (*iter)[_columns.record];
255                         (*iter)[_columns.record] = !record;
256 #ifdef GTKOSX
257                         _display.queue_draw();
258 #endif
259                         return true;
260                 }
261                 break;
262
263         case 3:
264                 if ((iter = _model->get_iter (path))) {
265                         bool mute = (*iter)[_columns.mute];
266                         (*iter)[_columns.mute] = !mute;
267 #ifdef GTKOSX
268                         _display.queue_draw();
269 #endif
270                         return true;
271                 }
272                 break;
273
274         case 4:
275                 if ((iter = _model->get_iter (path))) {
276                         bool solo = (*iter)[_columns.solo];
277                         (*iter)[_columns.solo] = !solo;
278 #ifdef GTKOSX
279                         _display.queue_draw();
280 #endif
281                         return true;
282                 }
283                 break;
284
285         case 5:
286                 if ((iter = _model->get_iter (path))) {
287                         bool select = (*iter)[_columns.select];
288                         (*iter)[_columns.select] = !select;
289 #ifdef GTKOSX
290                         _display.queue_draw();
291 #endif
292                         return true;
293                 }
294                 break;
295
296         case 6:
297                 if ((iter = _model->get_iter (path))) {
298                         bool edits = (*iter)[_columns.edits];
299                         (*iter)[_columns.edits] = !edits;
300 #ifdef GTKOSX
301                         _display.queue_draw();
302 #endif
303                         return true;
304                 }
305                 break;
306
307         case 7:
308                 if ((iter = _model->get_iter (path))) {
309                         bool visible = (*iter)[_columns.is_visible];
310                         (*iter)[_columns.is_visible] = !visible;
311 #ifdef GTKOSX
312                         _display.queue_draw();
313 #endif
314                         return true;
315                 }
316                 break;
317
318         default:
319                 break;
320         }
321
322         return false;
323  }
324
325 void
326 EditorRouteGroups::row_change (const Gtk::TreeModel::Path&, const Gtk::TreeModel::iterator& iter)
327 {
328         RouteGroup* group;
329
330         if (_in_row_change) {
331                 return;
332         }
333
334         if ((group = (*iter)[_columns.routegroup]) == 0) {
335                 return;
336         }
337
338         if ((*iter)[_columns.is_visible]) {
339                 for (TrackViewList::const_iterator j = _editor->get_track_views().begin(); j != _editor->get_track_views().end(); ++j) {
340                         if ((*j)->route_group() == group) {
341                                 _editor->_routes->show_track_in_display (**j);
342                         }
343                 }
344         } else {
345                 for (TrackViewList::const_iterator j = _editor->get_track_views().begin(); j != _editor->get_track_views().end(); ++j) {
346                         if ((*j)->route_group() == group) {
347                                 _editor->hide_track_in_display (*j);
348                         }
349                 }
350         }
351
352         PropertyList plist;
353         bool val = (*iter)[_columns.gain];
354         plist.add (Properties::gain, val);
355         val = (*iter)[_columns.record];
356         plist.add (Properties::recenable, val);
357         val = (*iter)[_columns.mute];
358         plist.add (Properties::mute, val);
359         val = (*iter)[_columns.solo];
360         plist.add (Properties::solo, val);
361         val = (*iter)[_columns.select];
362         plist.add (Properties::select, val);
363         val = (*iter)[_columns.edits];
364         plist.add (Properties::edit, val);
365         plist.add (Properties::name, string ((*iter)[_columns.text]));
366         
367         group->set_hidden (!(*iter)[_columns.is_visible], this);
368         
369         group->set_properties (plist);
370 }
371
372 void
373 EditorRouteGroups::add (RouteGroup* group)
374 {
375         ENSURE_GUI_THREAD (*this, &EditorRouteGroups::add, group)
376         bool focus = false;
377
378         TreeModel::Row row = *(_model->append());
379
380         row[_columns.is_visible] = !group->is_hidden();
381         row[_columns.gain] = group->is_gain ();
382         row[_columns.record] = group->is_recenable();
383         row[_columns.mute] = group->is_mute ();
384         row[_columns.solo] = group->is_solo ();
385         row[_columns.select] = group->is_select ();
386         row[_columns.edits] = group->is_edit ();
387
388         _in_row_change = true;
389
390         row[_columns.routegroup] = group;
391
392         if (!group->name().empty()) {
393                 row[_columns.text] = group->name();
394         } else {
395                 row[_columns.text] = _("unnamed");
396                 focus = true;
397         }
398
399         group->PropertyChanged.connect (property_changed_connection, MISSING_INVALIDATOR, ui_bind (&EditorRouteGroups::property_changed, this, group, _1), gui_context());
400
401         if (focus) {
402                 TreeViewColumn* col = _display.get_column (0);
403                 CellRendererText* name_cell = dynamic_cast<CellRendererText*>(_display.get_column_cell_renderer (0));
404                 _display.set_cursor (_model->get_path (row), *col, *name_cell, true);
405         }
406
407         _in_row_change = false;
408
409         _editor->_group_tabs->set_dirty ();
410 }
411
412 void
413 EditorRouteGroups::groups_changed ()
414 {
415         ENSURE_GUI_THREAD (*this, &EditorRouteGroups::groups_changed)
416
417         /* just rebuild the while thing */
418
419         _model->clear ();
420
421         {
422                 TreeModel::Row row;
423                 row = *(_model->append());
424                 row[_columns.is_visible] = true;
425                 row[_columns.text] = (_("-all-"));
426                 row[_columns.routegroup] = 0;
427         }
428
429         if (_session) {
430                 _session->foreach_route_group (sigc::mem_fun (*this, &EditorRouteGroups::add));
431         }
432 }
433
434 void
435 EditorRouteGroups::property_changed (RouteGroup* group, const PropertyChange& change)
436 {
437         _in_row_change = true;
438
439         Gtk::TreeModel::Children children = _model->children();
440
441         for(Gtk::TreeModel::Children::iterator iter = children.begin(); iter != children.end(); ++iter) {
442                 if (group == (*iter)[_columns.routegroup]) {
443                         (*iter)[_columns.is_visible] = !group->is_hidden();
444                         (*iter)[_columns.text] = group->name();
445                         (*iter)[_columns.gain] = group->is_gain ();
446                         (*iter)[_columns.record] = group->is_recenable ();
447                         (*iter)[_columns.mute] = group->is_mute ();
448                         (*iter)[_columns.solo] = group->is_solo ();
449                         (*iter)[_columns.select] = group->is_select ();
450                         (*iter)[_columns.edits] = group->is_edit ();
451                 }
452         }
453
454         _in_row_change = false;
455
456         if (change.contains (Properties::name) || change.contains (Properties::active)) {
457                 _editor->_group_tabs->set_dirty ();
458         }
459 }
460
461 void
462 EditorRouteGroups::name_edit (const Glib::ustring& path, const Glib::ustring& new_text)
463 {
464         RouteGroup* group;
465         TreeIter iter;
466
467         if ((iter = _model->get_iter (path))) {
468
469                 if ((group = (*iter)[_columns.routegroup]) == 0) {
470                         return;
471                 }
472
473                 if (new_text != group->name()) {
474                         group->set_name (new_text);
475                 }
476         }
477 }
478
479 void
480 EditorRouteGroups::clear ()
481 {
482         _display.set_model (Glib::RefPtr<Gtk::TreeStore> (0));
483         _model->clear ();
484         _display.set_model (_model);
485 }
486
487 void
488 EditorRouteGroups::set_session (Session* s)
489 {
490         SessionHandlePtr::set_session (s);
491
492         if (_session) {
493                 _session->route_group_added.connect (_session_connections, MISSING_INVALIDATOR, ui_bind (&EditorRouteGroups::add, this, _1), gui_context());
494                 _session->route_group_removed.connect (_session_connections, MISSING_INVALIDATOR, boost::bind (&EditorRouteGroups::groups_changed, this), gui_context());
495         }
496
497         groups_changed ();
498 }
499
500 void
501 EditorRouteGroups::run_new_group_dialog ()
502 {
503         RouteList rl;
504         
505         return _editor->_group_tabs->run_new_group_dialog (rl);
506 }