Split route group list out of Editor.
[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/stop_signal.h>
24 #include <gtkmm2ext/gtk_ui.h>
25 #include "ardour/route_group.h"
26
27 #include "editor.h"
28 #include "keyboard.h"
29 #include "marker.h"
30 #include "time_axis_view.h"
31 #include "prompter.h"
32 #include "gui_thread.h"
33 #include "editor_group_tabs.h"
34 #include "route_group_dialog.h"
35 #include "route_time_axis.h"
36 #include "editor_routes.h"
37 #include "editor_route_groups.h"
38
39 #include "ardour/route.h"
40
41 #include "i18n.h"
42
43 using namespace std;
44 using namespace sigc;
45 using namespace ARDOUR;
46 using namespace PBD;
47 using namespace Gtk;
48
49 EditorRouteGroups::EditorRouteGroups (Editor* e)
50         : EditorComponent (e),
51           _menu (0),
52           _in_row_change (false)
53
54 {
55         _model = ListStore::create (_columns);
56         _display.set_model (_model);
57
58         _display.append_column (_("Name"), _columns.text);
59
60         _display.append_column (_("G"), _columns.gain);
61         _display.append_column (_("R"), _columns.record);
62         _display.append_column (_("M"), _columns.mute);
63         _display.append_column (_("S"), _columns.solo);
64         _display.append_column (_("Sel"), _columns.select);
65         _display.append_column (_("E"), _columns.edits);
66
67         _display.append_column (_("Show"), _columns.is_visible);
68
69         _display.get_column (0)->set_data (X_("colnum"), GUINT_TO_POINTER(0));
70         _display.get_column (1)->set_data (X_("colnum"), GUINT_TO_POINTER(1));
71         _display.get_column (2)->set_data (X_("colnum"), GUINT_TO_POINTER(2));
72         _display.get_column (3)->set_data (X_("colnum"), GUINT_TO_POINTER(3));
73         _display.get_column (4)->set_data (X_("colnum"), GUINT_TO_POINTER(4));
74         _display.get_column (5)->set_data (X_("colnum"), GUINT_TO_POINTER(5));
75         _display.get_column (6)->set_data (X_("colnum"), GUINT_TO_POINTER(6));
76         _display.get_column (7)->set_data (X_("colnum"), GUINT_TO_POINTER(7));
77
78         _display.get_column (0)->set_expand (true);
79         _display.get_column (1)->set_expand (false);
80         _display.get_column (2)->set_expand (false);
81         _display.get_column (3)->set_expand (false);
82         _display.get_column (4)->set_expand (false);
83         _display.get_column (5)->set_expand (false);
84         _display.get_column (6)->set_expand (false);
85         _display.get_column (7)->set_expand (false);
86
87         _display.set_headers_visible (true);
88
89         /* name is directly editable */
90
91         CellRendererText* name_cell = dynamic_cast<CellRendererText*>(_display.get_column_cell_renderer (0));
92         name_cell->property_editable() = true;
93         name_cell->signal_edited().connect (mem_fun (*this, &EditorRouteGroups::name_edit));
94
95         /* use checkbox for the active + visible columns */
96
97         CellRendererToggle* active_cell = dynamic_cast<CellRendererToggle*>(_display.get_column_cell_renderer (1));
98         active_cell->property_activatable() = true;
99         active_cell->property_radio() = false;
100
101         active_cell = dynamic_cast<CellRendererToggle*>(_display.get_column_cell_renderer (2));
102         active_cell->property_activatable() = true;
103         active_cell->property_radio() = false;
104
105         active_cell = dynamic_cast<CellRendererToggle*>(_display.get_column_cell_renderer (3));
106         active_cell->property_activatable() = true;
107         active_cell->property_radio() = false;
108
109         active_cell = dynamic_cast<CellRendererToggle*>(_display.get_column_cell_renderer (4));
110         active_cell->property_activatable() = true;
111         active_cell->property_radio() = false;
112
113         active_cell = dynamic_cast<CellRendererToggle*>(_display.get_column_cell_renderer (5));
114         active_cell->property_activatable() = true;
115         active_cell->property_radio() = false;
116
117         active_cell = dynamic_cast<CellRendererToggle*>(_display.get_column_cell_renderer (6));
118         active_cell->property_activatable() = true;
119         active_cell->property_radio() = false;
120
121         active_cell = dynamic_cast<CellRendererToggle*>(_display.get_column_cell_renderer (7));
122         active_cell->property_activatable() = true;
123         active_cell->property_radio() = false;
124
125         _model->signal_row_changed().connect (mem_fun (*this, &EditorRouteGroups::row_change));
126
127         _display.set_name ("EditGroupList");
128         _display.get_selection()->set_mode (SELECTION_SINGLE);
129         _display.set_headers_visible (true);
130         _display.set_reorderable (false);
131         _display.set_rules_hint (true);
132         _display.set_size_request (75, -1);
133
134         _scroller.add (_display);
135         _scroller.set_policy (POLICY_AUTOMATIC, POLICY_AUTOMATIC);
136
137         _display.signal_button_press_event().connect (mem_fun(*this, &EditorRouteGroups::button_press_event), false);
138
139         _display_packer = new VBox;
140         HBox* button_box = manage (new HBox());
141         button_box->set_homogeneous (true);
142
143         Button* add_button = manage (new Button ());
144         Button* remove_button = manage (new Button ());
145
146         Widget* w;
147
148         w = manage (new Image (Stock::ADD, ICON_SIZE_BUTTON));
149         w->show();
150         add_button->add (*w);
151
152         w = manage (new Image (Stock::REMOVE, ICON_SIZE_BUTTON));
153         w->show();
154         remove_button->add (*w);
155
156         add_button->signal_clicked().connect (mem_fun (*this, &EditorRouteGroups::new_route_group));
157         remove_button->signal_clicked().connect (mem_fun (*this, &EditorRouteGroups::remove_selected));
158         
159         button_box->pack_start (*add_button);
160         button_box->pack_start (*remove_button);
161
162         _display_packer->pack_start (_scroller, true, true);
163         _display_packer->pack_start (*button_box, false, false);
164 }
165         
166
167 Gtk::Menu*
168 EditorRouteGroups::menu (RouteGroup* g)
169 {
170         using namespace Gtk::Menu_Helpers;
171
172         delete _menu;
173
174         Menu* new_from = new Menu;
175         MenuList& f = new_from->items ();
176         f.push_back (MenuElem (_("Selection..."), mem_fun (*this, &EditorRouteGroups::new_from_selection)));
177         f.push_back (MenuElem (_("Record Enabled..."), mem_fun (*this, &EditorRouteGroups::new_from_rec_enabled)));
178         f.push_back (MenuElem (_("Soloed..."), mem_fun (*this, &EditorRouteGroups::new_from_soloed)));
179
180         _menu = new Menu;
181         _menu->set_name ("ArdourContextMenu");
182         MenuList& items = _menu->items();
183
184         items.push_back (MenuElem (_("New..."), mem_fun(*this, &EditorRouteGroups::new_route_group)));
185         items.push_back (MenuElem (_("New From"), *new_from));
186         if (g) {
187                 items.push_back (MenuElem (_("Edit..."), bind (mem_fun (*this, &EditorRouteGroups::edit), g)));
188                 items.push_back (MenuElem (_("Fit to Window"), bind (mem_fun (*_editor, &Editor::fit_route_group), g)));
189                 items.push_back (MenuElem (_("Subgroup"), bind (mem_fun (*this, &EditorRouteGroups::subgroup), g)));
190         }
191         items.push_back (SeparatorElem());
192         items.push_back (MenuElem (_("Activate All"), mem_fun(*this, &EditorRouteGroups::activate_all)));
193         items.push_back (MenuElem (_("Disable All"), mem_fun(*this, &EditorRouteGroups::disable_all)));
194
195         return _menu;
196 }
197
198 void
199 EditorRouteGroups::subgroup (RouteGroup* g)
200 {
201         g->make_subgroup ();
202 }
203
204 void
205 EditorRouteGroups::unsubgroup (RouteGroup* g)
206 {
207         g->destroy_subgroup ();
208 }
209
210 void
211 EditorRouteGroups::activate_all ()
212 {
213         _session->foreach_route_group (
214                 bind (mem_fun (*this, &EditorRouteGroups::set_activation), true)
215                 );
216 }
217
218 void
219 EditorRouteGroups::disable_all ()
220 {
221         _session->foreach_route_group (
222                 bind (mem_fun (*this, &EditorRouteGroups::set_activation), false)
223                 );
224 }
225
226 void
227 EditorRouteGroups::set_activation (RouteGroup* g, bool a)
228 {
229         g->set_active (a, this);
230 }
231
232 void
233 EditorRouteGroups::new_route_group ()
234 {
235         RouteGroup* g = new RouteGroup (
236                 *_session,
237                 "",
238                 RouteGroup::Active,
239                 (RouteGroup::Property) (RouteGroup::Mute | RouteGroup::Solo | RouteGroup::Edit)
240                 );
241
242         RouteGroupDialog d (g, Gtk::Stock::NEW);
243         int const r = d.do_run ();
244
245         if (r == Gtk::RESPONSE_OK) {
246                 _session->add_route_group (g);
247         } else {
248                 delete g;
249         }
250 }
251
252 void
253 EditorRouteGroups::new_from_selection ()
254 {
255         RouteGroup* g = new RouteGroup (
256                 *_session,
257                 "",
258                 RouteGroup::Active,
259                 (RouteGroup::Property) (RouteGroup::Mute | RouteGroup::Solo | RouteGroup::Edit | RouteGroup::Select)
260                 );
261
262         RouteGroupDialog d (g, Gtk::Stock::NEW);
263         int const r = d.do_run ();
264
265         if (r == Gtk::RESPONSE_OK) {
266                 _session->add_route_group (g);
267
268                 for (TrackSelection::iterator i = _editor->get_selection().tracks.begin(); i != _editor->get_selection().tracks.end(); ++i) {
269                         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
270                         if (rtv) {
271                                 rtv->route()->set_route_group (g, this);
272                         }
273                 }
274                 
275         } else {
276                 delete g;
277         }
278 }
279
280 void
281 EditorRouteGroups::new_from_rec_enabled ()
282 {
283         RouteGroup* g = new RouteGroup (
284                 *_session,
285                 "",
286                 RouteGroup::Active,
287                 (RouteGroup::Property) (RouteGroup::Mute | RouteGroup::Solo | RouteGroup::Edit | RouteGroup::RecEnable)
288                 );
289
290         RouteGroupDialog d (g, Gtk::Stock::NEW);
291         int const r = d.do_run ();
292
293         if (r == Gtk::RESPONSE_OK) {
294                 _session->add_route_group (g);
295
296                 for (Editor::TrackViewList::const_iterator i = _editor->get_track_views().begin(); i != _editor->get_track_views().end(); ++i) {
297                         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
298                         if (rtv && rtv->route()->record_enabled()) {
299                                 rtv->route()->set_route_group (g, this);
300                         }
301                 }
302                 
303         } else {
304                 delete g;
305         }
306 }
307
308 void
309 EditorRouteGroups::new_from_soloed ()
310 {
311         RouteGroup* g = new RouteGroup (
312                 *_session,
313                 "",
314                 RouteGroup::Active,
315                 (RouteGroup::Property) (RouteGroup::Mute | RouteGroup::Solo | RouteGroup::Edit)
316                 );
317
318         RouteGroupDialog d (g, Gtk::Stock::NEW);
319         int const r = d.do_run ();
320
321         if (r == Gtk::RESPONSE_OK) {
322                 _session->add_route_group (g);
323
324                 for (Editor::TrackViewList::const_iterator i = _editor->get_track_views().begin(); i != _editor->get_track_views().end(); ++i) {
325                         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
326                         if (rtv && !rtv->route()->is_master() && rtv->route()->soloed()) {
327                                 rtv->route()->set_route_group (g, this);
328                         }
329                 }
330                 
331         } else {
332                 delete g;
333         }
334 }
335
336 void
337 EditorRouteGroups::edit (RouteGroup* g)
338 {
339         RouteGroupDialog d (g, Gtk::Stock::APPLY);
340         d.do_run ();
341 }
342
343 void
344 EditorRouteGroups::remove_selected ()
345 {
346         Glib::RefPtr<TreeSelection> selection = _display.get_selection();
347         TreeView::Selection::ListHandle_Path rows = selection->get_selected_rows ();
348
349         if (rows.empty()) {
350                 return;
351         }
352
353         TreeView::Selection::ListHandle_Path::iterator i = rows.begin();
354         TreeIter iter;
355         
356         /* selection mode is single, so rows.begin() is it */
357
358         if ((iter = _model->get_iter (*i))) {
359
360                 RouteGroup* rg = (*iter)[_columns.routegroup];
361
362                 if (rg) {
363                         _session->remove_route_group (*rg);
364                 }
365         }
366 }
367
368 void
369 EditorRouteGroups::button_clicked ()
370 {
371         new_route_group ();
372 }
373
374 gint
375 EditorRouteGroups::button_press_event (GdkEventButton* ev)
376 {
377         TreeModel::Path path;
378         TreeIter iter;
379         RouteGroup* group = 0;
380         TreeViewColumn* column;
381         int cellx;
382         int celly;
383
384         bool const p = _display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly);
385
386         if (p) {
387                 iter = _model->get_iter (path);
388         }
389         
390         if (iter) {
391                 group = (*iter)[_columns.routegroup];
392         }
393
394         if (Keyboard::is_context_menu_event (ev)) {
395                 menu(group)->popup (1, ev->time);
396                 return true;
397         }
398
399         if (!p) {
400                 return 1;
401         }
402
403         switch (GPOINTER_TO_UINT (column->get_data (X_("colnum")))) {
404         case 0:
405                 if (Keyboard::is_edit_event (ev)) {
406                         if ((iter = _model->get_iter (path))) {
407                                 if ((group = (*iter)[_columns.routegroup]) != 0) {
408                                         // edit_route_group (group);
409 #ifdef GTKOSX
410                                         _display.queue_draw();
411 #endif
412                                         return true;
413                                 }
414                         }
415                         
416                 } 
417                 break;
418
419         case 1:
420                 if ((iter = _model->get_iter (path))) {
421                         bool gain = (*iter)[_columns.gain];
422                         (*iter)[_columns.gain] = !gain;
423 #ifdef GTKOSX
424                         _display.queue_draw();
425 #endif
426                         return true;
427                 }
428                 break;
429
430         case 2:
431                 if ((iter = _model->get_iter (path))) {
432                         bool record = (*iter)[_columns.record];
433                         (*iter)[_columns.record] = !record;
434 #ifdef GTKOSX
435                         _display.queue_draw();
436 #endif
437                         return true;
438                 }
439                 break;
440
441         case 3:
442                 if ((iter = _model->get_iter (path))) {
443                         bool mute = (*iter)[_columns.mute];
444                         (*iter)[_columns.mute] = !mute;
445 #ifdef GTKOSX
446                         _display.queue_draw();
447 #endif
448                         return true;
449                 }
450                 break;
451
452         case 4:
453                 if ((iter = _model->get_iter (path))) {
454                         bool solo = (*iter)[_columns.solo];
455                         (*iter)[_columns.solo] = !solo;
456 #ifdef GTKOSX
457                         _display.queue_draw();
458 #endif
459                         return true;
460                 }
461                 break;
462
463         case 5:
464                 if ((iter = _model->get_iter (path))) {
465                         bool select = (*iter)[_columns.select];
466                         (*iter)[_columns.select] = !select;
467 #ifdef GTKOSX
468                         _display.queue_draw();
469 #endif
470                         return true;
471                 }
472                 break;
473
474         case 6:
475                 if ((iter = _model->get_iter (path))) {
476                         bool edits = (*iter)[_columns.edits];
477                         (*iter)[_columns.edits] = !edits;
478 #ifdef GTKOSX
479                         _display.queue_draw();
480 #endif
481                         return true;
482                 }
483                 break;
484
485         case 7:
486                 if ((iter = _model->get_iter (path))) {
487                         bool visible = (*iter)[_columns.is_visible];
488                         (*iter)[_columns.is_visible] = !visible;
489 #ifdef GTKOSX
490                         _display.queue_draw();
491 #endif
492                         return true;
493                 }
494                 break;
495
496         default:
497                 break;
498         }
499         
500         return false;
501  }
502
503 void 
504 EditorRouteGroups::row_change (const Gtk::TreeModel::Path& path,const Gtk::TreeModel::iterator& iter)
505 {
506         RouteGroup* group;
507
508         if (_in_row_change) {
509                 return;
510         }
511
512         if ((group = (*iter)[_columns.routegroup]) == 0) {
513                 return;
514         }
515
516         if ((*iter)[_columns.is_visible]) {
517                 for (Editor::TrackViewList::const_iterator j = _editor->get_track_views().begin(); j != _editor->get_track_views().end(); ++j) {
518                         if ((*j)->route_group() == group) {
519                                 _editor->_routes->show_track_in_display (**j);
520                         }
521                 }
522         } else {
523                 for (Editor::TrackViewList::const_iterator j = _editor->get_track_views().begin(); j != _editor->get_track_views().end(); ++j) {
524                         if ((*j)->route_group() == group) {
525                                 _editor->hide_track_in_display (**j);
526                         }
527                 }
528         }
529
530         group->set_property (RouteGroup::Gain, (*iter)[_columns.gain]);
531         group->set_property (RouteGroup::RecEnable, (*iter)[_columns.record]);
532         group->set_property (RouteGroup::Mute, (*iter)[_columns.mute]);
533         group->set_property (RouteGroup::Solo, (*iter)[_columns.solo]);
534         group->set_property (RouteGroup::Select, (*iter)[_columns.select]);
535         group->set_property (RouteGroup::Edit, (*iter)[_columns.edits]);
536
537         string name = (*iter)[_columns.text];
538
539         if (name != group->name()) {
540                 group->set_name (name);
541         }
542 }
543
544 void
545 EditorRouteGroups::add (RouteGroup* group)
546 {
547         ENSURE_GUI_THREAD (bind (mem_fun(*this, &EditorRouteGroups::add), group));
548         bool focus = false;
549
550         TreeModel::Row row = *(_model->append());
551         
552         row[_columns.is_visible] = !group->is_hidden();
553         row[_columns.gain] = group->property(RouteGroup::Gain);
554         row[_columns.record] = group->property(RouteGroup::RecEnable);
555         row[_columns.mute] = group->property(RouteGroup::Mute);
556         row[_columns.solo] = group->property(RouteGroup::Solo);
557         row[_columns.select] = group->property(RouteGroup::Select);
558         row[_columns.edits] = group->property(RouteGroup::Edit);
559
560         _in_row_change = true;
561
562         row[_columns.routegroup] = group;
563
564         if (!group->name().empty()) {
565                 row[_columns.text] = group->name();
566         } else {
567                 row[_columns.text] = _("unnamed");
568                 focus = true;
569         }
570
571         group->FlagsChanged.connect (bind (mem_fun (*this, &EditorRouteGroups::flags_changed), group));
572
573         if (focus) {  
574                 TreeViewColumn* col = _display.get_column (0);
575                 CellRendererText* name_cell = dynamic_cast<CellRendererText*>(_display.get_column_cell_renderer (0));
576                 _display.set_cursor (_model->get_path (row), *col, *name_cell, true);
577         }
578
579         _in_row_change = false;
580
581         _editor->_group_tabs->set_dirty ();
582 }
583
584 void
585 EditorRouteGroups::groups_changed ()
586 {
587         ENSURE_GUI_THREAD (mem_fun (*this, &EditorRouteGroups::groups_changed));
588
589         /* just rebuild the while thing */
590
591         _model->clear ();
592
593         {
594                 TreeModel::Row row;
595                 row = *(_model->append());
596                 row[_columns.is_visible] = true;
597                 row[_columns.text] = (_("-all-"));
598                 row[_columns.routegroup] = 0;
599         }
600
601         _session->foreach_route_group (mem_fun (*this, &EditorRouteGroups::add));
602 }
603
604 void
605 EditorRouteGroups::flags_changed (void* src, RouteGroup* group)
606 {
607         ENSURE_GUI_THREAD (bind (mem_fun(*this, &EditorRouteGroups::flags_changed), src, group));
608
609         _in_row_change = true;
610
611         Gtk::TreeModel::Children children = _model->children();
612
613         for(Gtk::TreeModel::Children::iterator iter = children.begin(); iter != children.end(); ++iter) {
614                 if (group == (*iter)[_columns.routegroup]) {
615                         (*iter)[_columns.is_visible] = !group->is_hidden();
616                         (*iter)[_columns.text] = group->name();
617                         (*iter)[_columns.gain] = group->property(RouteGroup::Gain);
618                         (*iter)[_columns.record] = group->property(RouteGroup::RecEnable);
619                         (*iter)[_columns.mute] = group->property(RouteGroup::Mute);
620                         (*iter)[_columns.solo] = group->property(RouteGroup::Solo);
621                         (*iter)[_columns.select] = group->property(RouteGroup::Select);
622                         (*iter)[_columns.edits] = group->property(RouteGroup::Edit);
623                 }
624         }
625
626         _in_row_change = false;
627
628         _editor->_group_tabs->set_dirty ();
629 }
630
631 void
632 EditorRouteGroups::name_edit (const Glib::ustring& path, const Glib::ustring& new_text)
633 {
634         RouteGroup* group;
635         TreeIter iter;
636         
637         if ((iter = _model->get_iter (path))) {
638         
639                 if ((group = (*iter)[_columns.routegroup]) == 0) {
640                         return;
641                 }
642                 
643                 if (new_text != group->name()) {
644                         group->set_name (new_text);
645                 }
646         }
647 }
648
649 void
650 EditorRouteGroups::clear ()
651 {
652         _display.set_model (Glib::RefPtr<Gtk::TreeStore> (0));
653         _model->clear ();
654         _display.set_model (_model);
655 }
656
657 void
658 EditorRouteGroups::connect_to_session (Session* s)
659 {
660         EditorComponent::connect_to_session (s);
661
662         _session_connections.push_back (_session->route_group_added.connect (mem_fun (*this, &EditorRouteGroups::add)));
663         _session_connections.push_back (_session->route_group_removed.connect (mem_fun (*this, &EditorRouteGroups::groups_changed)));
664
665         groups_changed ();
666 }
667
668