Update classkeys to match new total LuaSignal count (windows only)
[ardour.git] / libs / surfaces / push2 / mix.cc
1 /*
2   Copyright (C) 2016 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 #include <cairomm/region.h>
20 #include <pangomm/layout.h>
21
22 #include "pbd/compose.h"
23 #include "pbd/convert.h"
24 #include "pbd/debug.h"
25 #include "pbd/failed_constructor.h"
26 #include "pbd/file_utils.h"
27 #include "pbd/search_path.h"
28 #include "pbd/enumwriter.h"
29
30 #include "midi++/parser.h"
31
32 #include "temporal/time.h"
33 #include "temporal/bbt_time.h"
34
35 #include "ardour/async_midi_port.h"
36 #include "ardour/audioengine.h"
37 #include "ardour/debug.h"
38 #include "ardour/filesystem_paths.h"
39 #include "ardour/midiport_manager.h"
40 #include "ardour/midi_track.h"
41 #include "ardour/midi_port.h"
42 #include "ardour/session.h"
43 #include "ardour/tempo.h"
44 #include "ardour/utils.h"
45 #include "ardour/vca_manager.h"
46
47 #include "gtkmm2ext/colors.h"
48 #include "canvas/line.h"
49 #include "canvas/rectangle.h"
50 #include "canvas/text.h"
51
52 #include "gtkmm2ext/gui_thread.h"
53
54 #include "canvas.h"
55 #include "knob.h"
56 #include "level_meter.h"
57 #include "mix.h"
58 #include "push2.h"
59 #include "utils.h"
60
61 #include "pbd/i18n.h"
62
63 #ifdef __APPLE__
64 #define Rect ArdourCanvas::Rect
65 #endif
66
67 using namespace ARDOUR;
68 using namespace std;
69 using namespace PBD;
70 using namespace Glib;
71 using namespace ArdourSurface;
72 using namespace Gtkmm2ext;
73 using namespace ArdourCanvas;
74
75 MixLayout::MixLayout (Push2& p, Session & s, std::string const & name)
76         : Push2Layout (p, s, name)
77         , bank_start (0)
78         , vpot_mode (Volume)
79 {
80         /* background */
81
82         bg = new ArdourCanvas::Rectangle (this);
83         bg->set (Rect (0, 0, display_width(), display_height()));
84         bg->set_fill_color (p2.get_color (Push2::DarkBackground));
85
86         /* upper line */
87
88         upper_line = new Line (this);
89         upper_line->set (Duple (0, 22.5), Duple (display_width(), 22.5));
90         upper_line->set_outline_color (p2.get_color (Push2::LightBackground));
91
92         Pango::FontDescription fd2 ("Sans 10");
93
94         for (int n = 0; n < 8; ++n) {
95
96                 /* background for text labels for knob function */
97
98                 ArdourCanvas::Rectangle* r = new ArdourCanvas::Rectangle (this);
99                 Coord x0 = 10 + (n*Push2Canvas::inter_button_spacing()) - 5;
100                 r->set (Rect (x0, 2, x0 + Push2Canvas::inter_button_spacing(), 2 + 21));
101                 upper_backgrounds.push_back (r);
102
103                 r = new ArdourCanvas::Rectangle (this);
104                 r->set (Rect (x0, 137, x0 + Push2Canvas::inter_button_spacing(), 137 + 21));
105                 lower_backgrounds.push_back (r);
106
107                 /* text labels for knob function*/
108
109                 Text* t = new Text (this);
110                 t->set_font_description (fd2);
111                 t->set_color (p2.get_color (Push2::ParameterName));
112                 t->set_position (Duple (10 + (n*Push2Canvas::inter_button_spacing()), 5));
113
114                 string txt;
115                 switch (n) {
116                 case 0:
117                         txt = _("Volumes");
118                         break;
119                 case 1:
120                         txt = _("Pans");
121                         break;
122                 case 2:
123                         txt = _("Pan Widths");
124                         break;
125                 case 3:
126                         txt = _("A Sends");
127                         break;
128                 case 4:
129                         txt = _("B Sends");
130                         break;
131                 case 5:
132                         txt = _("C Sends");
133                         break;
134                 case 6:
135                         txt = _("D Sends");
136                         break;
137                 case 7:
138                         txt = _("E Sends");
139                         break;
140                 }
141                 t->set (txt);
142                 upper_text.push_back (t);
143
144                 /* GainMeters */
145
146                 gain_meter[n] = new GainMeter (this, p2);
147                 gain_meter[n]->set_position (Duple (40 + (n * Push2Canvas::inter_button_spacing()), 95));
148
149                 /* stripable names */
150
151                 t = new Text (this);
152                 t->set_font_description (fd2);
153                 t->set_color (p2.get_color (Push2::ParameterName));
154                 t->set_position (Duple (10 + (n*Push2Canvas::inter_button_spacing()), 140));
155                 lower_text.push_back (t);
156
157         }
158
159         mode_button = p2.button_by_id (Push2::Upper1);
160
161         session.RouteAdded.connect (session_connections, invalidator(*this), boost::bind (&MixLayout::stripables_added, this), &p2);
162         session.vca_manager().VCAAdded.connect (session_connections, invalidator (*this), boost::bind (&MixLayout::stripables_added, this), &p2);
163 }
164
165 MixLayout::~MixLayout ()
166 {
167         // Item destructor deletes all children
168 }
169
170 void
171 MixLayout::show ()
172 {
173         Push2::ButtonID upper_buttons[] = { Push2::Upper1, Push2::Upper2, Push2::Upper3, Push2::Upper4,
174                                             Push2::Upper5, Push2::Upper6, Push2::Upper7, Push2::Upper8 };
175
176
177         for (size_t n = 0; n < sizeof (upper_buttons) / sizeof (upper_buttons[0]); ++n) {
178                 boost::shared_ptr<Push2::Button> b = p2.button_by_id (upper_buttons[n]);
179
180                 if (b != mode_button) {
181                         b->set_color (Push2::LED::DarkGray);
182                 } else {
183                         b->set_color (Push2::LED::White);
184                 }
185                 b->set_state (Push2::LED::OneShot24th);
186                 p2.write (b->state_msg());
187         }
188
189         switch_bank (bank_start);
190
191         Container::show ();
192 }
193
194 void
195 MixLayout::render (Rect const& area, Cairo::RefPtr<Cairo::Context> context) const
196 {
197         Container::render (area, context);
198 }
199
200 void
201 MixLayout::button_upper (uint32_t n)
202 {
203         boost::shared_ptr<Push2::Button> b;
204         switch (n) {
205         case 0:
206                 vpot_mode = Volume;
207                 b = p2.button_by_id (Push2::Upper1);
208                 break;
209         case 1:
210                 vpot_mode = PanAzimuth;
211                 b = p2.button_by_id (Push2::Upper2);
212                 break;
213         case 2:
214                 vpot_mode = PanWidth;
215                 b = p2.button_by_id (Push2::Upper3);
216                 break;
217         case 3:
218                 vpot_mode = Send1;
219                 b = p2.button_by_id (Push2::Upper4);
220                 break;
221         case 4:
222                 vpot_mode = Send2;
223                 b = p2.button_by_id (Push2::Upper5);
224                 break;
225         case 5:
226                 vpot_mode = Send3;
227                 b = p2.button_by_id (Push2::Upper6);
228                 break;
229         case 6:
230                 vpot_mode = Send4;
231                 b = p2.button_by_id (Push2::Upper7);
232                 break;
233         case 7:
234                 vpot_mode = Send5;
235                 b = p2.button_by_id (Push2::Upper8);
236                 break;
237         }
238
239         if (b != mode_button) {
240                 mode_button->set_color (Push2::LED::Black);
241                 mode_button->set_state (Push2::LED::OneShot24th);
242                 p2.write (mode_button->state_msg());
243         }
244
245         mode_button = b;
246
247         show_vpot_mode ();
248 }
249
250 void
251 MixLayout::show_vpot_mode ()
252 {
253         mode_button->set_color (Push2::LED::White);
254         mode_button->set_state (Push2::LED::OneShot24th);
255         p2.write (mode_button->state_msg());
256
257         for (int s = 0; s < 8; ++s) {
258                 upper_backgrounds[s]->hide ();
259                 upper_text[s]->set_color (p2.get_color (Push2::ParameterName));
260         }
261
262         uint32_t n = 0;
263
264         boost::shared_ptr<AutomationControl> ac;
265         switch (vpot_mode) {
266         case Volume:
267                 for (int s = 0; s < 8; ++s) {
268                         if (stripable[s]) {
269                                 gain_meter[s]->knob->set_controllable (stripable[s]->gain_control());
270                                 boost::shared_ptr<PeakMeter> pm = stripable[s]->peak_meter();
271                                 if (pm) {
272                                         gain_meter[s]->meter->set_meter (pm.get());
273                                 } else {
274                                         gain_meter[s]->meter->set_meter (0);
275                                 }
276                         } else {
277                                 gain_meter[s]->knob->set_controllable (boost::shared_ptr<AutomationControl>());
278                                 gain_meter[s]->meter->set_meter (0);
279                         }
280                         gain_meter[s]->knob->remove_flag (Push2Knob::ArcToZero);
281                         gain_meter[s]->meter->show ();
282                 }
283                 n = 0;
284                 break;
285         case PanAzimuth:
286                 for (int s = 0; s < 8; ++s) {
287                         if (stripable[s]) {
288                                 gain_meter[s]->knob->set_controllable (stripable[s]->pan_azimuth_control());
289                                 gain_meter[s]->knob->add_flag (Push2Knob::ArcToZero);
290                         } else {
291                                 gain_meter[s]->knob->set_controllable (boost::shared_ptr<AutomationControl>());
292
293                         }
294                         gain_meter[s]->meter->hide ();
295                 }
296                 n = 1;
297                 break;
298         case PanWidth:
299                 for (int s = 0; s < 8; ++s) {
300                         if (stripable[s]) {
301                                 gain_meter[s]->knob->set_controllable (stripable[s]->pan_width_control());
302                         } else {
303                                 gain_meter[s]->knob->set_controllable (boost::shared_ptr<AutomationControl>());
304
305                         }
306                         gain_meter[s]->knob->remove_flag (Push2Knob::ArcToZero);
307                         gain_meter[s]->meter->hide ();
308                 }
309                 n = 2;
310                 break;
311         case Send1:
312                 for (int s = 0; s < 8; ++s) {
313                         if (stripable[s]) {
314                                 gain_meter[s]->knob->set_controllable (stripable[s]->send_level_controllable (0));
315                         } else {
316                                 gain_meter[s]->knob->set_controllable (boost::shared_ptr<AutomationControl>());
317
318                         }
319                         gain_meter[s]->knob->remove_flag (Push2Knob::ArcToZero);
320                         gain_meter[s]->meter->hide ();
321                 }
322                 n = 3;
323                 break;
324         case Send2:
325                 for (int s = 0; s < 8; ++s) {
326                         if (stripable[s]) {
327                                 gain_meter[s]->knob->set_controllable (stripable[s]->send_level_controllable (1));
328                         } else {
329                                 gain_meter[s]->knob->set_controllable (boost::shared_ptr<AutomationControl>());
330
331                         }
332                         gain_meter[s]->knob->remove_flag (Push2Knob::ArcToZero);
333                         gain_meter[s]->meter->hide ();
334                 }
335                 n = 4;
336                 break;
337         case Send3:
338                 for (int s = 0; s < 8; ++s) {
339                         if (stripable[s]) {
340                                 gain_meter[s]->knob->set_controllable (stripable[s]->send_level_controllable (2));
341                         } else {
342                                 gain_meter[s]->knob->set_controllable (boost::shared_ptr<AutomationControl>());
343
344                         }
345                         gain_meter[s]->knob->remove_flag (Push2Knob::ArcToZero);
346                         gain_meter[s]->meter->hide ();
347                 }
348                 n = 5;
349                 break;
350         case Send4:
351                 for (int s = 0; s < 8; ++s) {
352                         if (stripable[s]) {
353                                 gain_meter[s]->knob->set_controllable (stripable[s]->send_level_controllable (3));
354                         } else {
355                                 gain_meter[s]->knob->set_controllable (boost::shared_ptr<AutomationControl>());
356
357                         }
358                         gain_meter[s]->knob->remove_flag (Push2Knob::ArcToZero);
359                         gain_meter[s]->meter->hide ();
360                 }
361                 n = 6;
362                 break;
363         case Send5:
364                 for (int s = 0; s < 8; ++s) {
365                         if (stripable[s]) {
366                                 gain_meter[s]->knob->set_controllable (stripable[s]->send_level_controllable (4));
367                         } else {
368                                 gain_meter[s]->knob->set_controllable (boost::shared_ptr<AutomationControl>());
369
370                         }
371                         gain_meter[s]->knob->remove_flag (Push2Knob::ArcToZero);
372                         gain_meter[s]->meter->hide ();
373                 }
374                 n = 7;
375                 break;
376         default:
377                 break;
378         }
379
380         upper_backgrounds[n]->set_fill_color (p2.get_color (Push2::ParameterName));
381         upper_backgrounds[n]->set_outline_color (p2.get_color (Push2::ParameterName));
382         upper_backgrounds[n]->show ();
383         upper_text[n]->set_color (contrasting_text_color (p2.get_color (Push2::ParameterName)));
384 }
385
386 void
387 MixLayout::button_mute ()
388 {
389         boost::shared_ptr<Stripable> s = ControlProtocol::first_selected_stripable();
390         if (s) {
391                 boost::shared_ptr<AutomationControl> ac = s->mute_control();
392                 if (ac) {
393                         ac->set_value (!ac->get_value(), PBD::Controllable::UseGroup);
394                 }
395         }
396 }
397
398 void
399 MixLayout::button_solo ()
400 {
401         boost::shared_ptr<Stripable> s = ControlProtocol::first_selected_stripable();
402         if (s) {
403                 boost::shared_ptr<AutomationControl> ac = s->solo_control();
404                 if (ac) {
405                         session.set_control (ac, !ac->get_value(), PBD::Controllable::UseGroup);
406                 }
407         }
408 }
409
410 void
411 MixLayout::button_lower (uint32_t n)
412 {
413         if (!stripable[n]) {
414                 return;
415         }
416
417         ControlProtocol::SetStripableSelection (stripable[n]);
418 }
419
420 void
421 MixLayout::strip_vpot (int n, int delta)
422 {
423         boost::shared_ptr<Controllable> ac = gain_meter[n]->knob->controllable();
424
425         if (ac) {
426                 ac->set_value (ac->interface_to_internal (
427                                        min (ac->upper(), max (ac->lower(), ac->internal_to_interface (ac->get_value()) + (delta/256.0)))),
428                                PBD::Controllable::UseGroup);
429         }
430 }
431
432 void
433 MixLayout::strip_vpot_touch (int n, bool touching)
434 {
435         if (stripable[n]) {
436                 boost::shared_ptr<AutomationControl> ac = stripable[n]->gain_control();
437                 if (ac) {
438                         if (touching) {
439                                 ac->start_touch (session.audible_sample());
440                         } else {
441                                 ac->stop_touch (session.audible_sample());
442                         }
443                 }
444         }
445 }
446
447 void
448 MixLayout::stripable_property_change (PropertyChange const& what_changed, uint32_t which)
449 {
450         if (what_changed.contains (Properties::color)) {
451                 lower_backgrounds[which]->set_fill_color (stripable[which]->presentation_info().color());
452
453                 if (stripable[which]->is_selected()) {
454                         lower_text[which]->set_fill_color (contrasting_text_color (stripable[which]->presentation_info().color()));
455                         /* might not be a MIDI track, in which case this will
456                            do nothing
457                         */
458                         p2.update_selection_color ();
459                 }
460         }
461
462         if (what_changed.contains (Properties::hidden)) {
463                 switch_bank (bank_start);
464         }
465
466         if (what_changed.contains (Properties::selected)) {
467
468                 if (!stripable[which]) {
469                         return;
470                 }
471
472                 if (stripable[which]->is_selected()) {
473                         show_selection (which);
474                 } else {
475                         hide_selection (which);
476                 }
477         }
478
479 }
480
481 void
482 MixLayout::show_selection (uint32_t n)
483 {
484         lower_backgrounds[n]->show ();
485         lower_backgrounds[n]->set_fill_color (stripable[n]->presentation_info().color());
486         lower_text[n]->set_color (contrasting_text_color (lower_backgrounds[n]->fill_color()));
487 }
488
489 void
490 MixLayout::hide_selection (uint32_t n)
491 {
492         lower_backgrounds[n]->hide ();
493         if (stripable[n]) {
494                 lower_text[n]->set_color (stripable[n]->presentation_info().color());
495         }
496 }
497
498 void
499 MixLayout::solo_changed (uint32_t n)
500 {
501         solo_mute_changed (n);
502 }
503
504 void
505 MixLayout::mute_changed (uint32_t n)
506 {
507         solo_mute_changed (n);
508 }
509
510 void
511 MixLayout::solo_mute_changed (uint32_t n)
512 {
513         string shortname = short_version (stripable[n]->name(), 10);
514         string text;
515         boost::shared_ptr<AutomationControl> ac;
516         ac = stripable[n]->solo_control();
517         if (ac && ac->get_value()) {
518                 text += "* ";
519         }
520         boost::shared_ptr<MuteControl> mc;
521         mc = stripable[n]->mute_control ();
522         if (mc) {
523                 if (mc->muted_by_self_or_masters()) {
524                         text += "! ";
525                 } else if (mc->muted_by_others_soloing()) {
526                         text += "- "; // it would be nice to use Unicode mute"\uD83D\uDD07 ";
527                 }
528         }
529         text += shortname;
530         lower_text[n]->set (text);
531 }
532
533 void
534 MixLayout::switch_bank (uint32_t base)
535 {
536         stripable_connections.drop_connections ();
537
538         /* work backwards so we can tell if we should actually switch banks */
539
540         boost::shared_ptr<Stripable> s[8];
541         uint32_t different = 0;
542
543         for (int n = 0; n < 8; ++n) {
544                 s[n] = session.get_remote_nth_stripable (base+n, PresentationInfo::Flag (PresentationInfo::Route|PresentationInfo::VCA));
545                 if (s[n] != stripable[n]) {
546                         different++;
547                 }
548         }
549
550         if (!s[0]) {
551                 /* not even the first stripable exists, do nothing */
552                 for (int n = 0; n < 8; ++n) {
553                         stripable[n].reset ();
554                         gain_meter[n]->knob->set_controllable (boost::shared_ptr<AutomationControl>());
555                         gain_meter[n]->meter->set_meter (0);
556                 }
557                 return;
558         }
559
560         for (int n = 0; n < 8; ++n) {
561                 stripable[n] = s[n];
562         }
563
564         /* at least one stripable in this bank */
565
566         bank_start = base;
567
568         for (int n = 0; n < 8; ++n) {
569
570                 if (!stripable[n]) {
571                         lower_text[n]->hide ();
572                         hide_selection (n);
573                         gain_meter[n]->knob->set_controllable (boost::shared_ptr<AutomationControl>());
574                         gain_meter[n]->meter->set_meter (0);
575                 } else {
576
577                         lower_text[n]->show ();
578
579                         /* stripable goes away? refill the bank, starting at the same point */
580
581                         stripable[n]->DropReferences.connect (stripable_connections, invalidator (*this), boost::bind (&MixLayout::switch_bank, this, bank_start), &p2);
582                         stripable[n]->presentation_info().PropertyChanged.connect (stripable_connections, invalidator (*this), boost::bind (&MixLayout::stripable_property_change, this, _1, n), &p2);
583                         stripable[n]->solo_control()->Changed.connect (stripable_connections, invalidator (*this), boost::bind (&MixLayout::solo_changed, this, n), &p2);
584                         stripable[n]->mute_control()->Changed.connect (stripable_connections, invalidator (*this), boost::bind (&MixLayout::mute_changed, this, n), &p2);
585
586                         if (stripable[n]->is_selected()) {
587                                 show_selection (n);
588                         } else {
589                                 hide_selection (n);
590                         }
591
592                         /* this will set lower text to the correct value (basically
593                            the stripable name)
594                         */
595
596                         solo_mute_changed (n);
597
598                         gain_meter[n]->knob->set_text_color (stripable[n]->presentation_info().color());
599                         gain_meter[n]->knob->set_arc_start_color (stripable[n]->presentation_info().color());
600                         gain_meter[n]->knob->set_arc_end_color (stripable[n]->presentation_info().color());
601                 }
602
603
604                 boost::shared_ptr<Push2::Button> b;
605
606                 switch (n) {
607                 case 0:
608                         b = p2.button_by_id (Push2::Lower1);
609                         break;
610                 case 1:
611                         b = p2.button_by_id (Push2::Lower2);
612                         break;
613                 case 2:
614                         b = p2.button_by_id (Push2::Lower3);
615                         break;
616                 case 3:
617                         b = p2.button_by_id (Push2::Lower4);
618                         break;
619                 case 4:
620                         b = p2.button_by_id (Push2::Lower5);
621                         break;
622                 case 5:
623                         b = p2.button_by_id (Push2::Lower6);
624                         break;
625                 case 6:
626                         b = p2.button_by_id (Push2::Lower7);
627                         break;
628                 case 7:
629                         b = p2.button_by_id (Push2::Lower8);
630                         break;
631                 }
632
633                 if (stripable[n]) {
634                         b->set_color (p2.get_color_index (stripable[n]->presentation_info().color()));
635                 } else {
636                         b->set_color (Push2::LED::Black);
637                 }
638
639                 b->set_state (Push2::LED::OneShot24th);
640                 p2.write (b->state_msg());
641         }
642
643         show_vpot_mode ();
644 }
645
646 void
647 MixLayout::button_right ()
648 {
649         switch_bank (max (0, bank_start + 8));
650 }
651
652 void
653 MixLayout::button_left ()
654 {
655         switch_bank (max (0, bank_start - 8));
656 }
657
658
659 void
660 MixLayout::button_select_press ()
661 {
662 }
663
664 void
665 MixLayout::button_select_release ()
666 {
667         if (!(p2.modifier_state() & Push2::ModSelect)) {
668                 /* somebody else used us as a modifier */
669                 return;
670         }
671
672         int selected = -1;
673
674         for (int n = 0; n < 8; ++n) {
675                 if (stripable[n]) {
676                         if (stripable[n]->is_selected()) {
677                                         selected = n;
678                                         break;
679                         }
680                 }
681         }
682
683         if (selected < 0) {
684
685                 /* no visible track selected, select first (if any) */
686
687                 if (stripable[0]) {
688                         ControlProtocol::SetStripableSelection (stripable[0]);
689                 }
690
691         } else {
692
693                 if (p2.modifier_state() & Push2::ModShift) {
694                         /* select prev */
695
696                         if (selected == 0) {
697                                 /* current selected is leftmost ... cancel selection,
698                                    switch banks by one, and select leftmost
699                                 */
700                                 if (bank_start != 0) {
701                                         ControlProtocol::ClearStripableSelection ();
702                                         switch_bank (bank_start-1);
703                                         if (stripable[0]) {
704                                                 ControlProtocol::SetStripableSelection (stripable[0]);
705                                         }
706                                 }
707                         } else {
708                                 /* select prev, if any */
709                                 int n = selected - 1;
710                                 while (n >= 0 && !stripable[n]) {
711                                         --n;
712                                 }
713                                 if (n >= 0) {
714                                         ControlProtocol::SetStripableSelection (stripable[n]);
715                                 }
716                         }
717
718                 } else {
719
720                         /* select next */
721
722                         if (selected == 7) {
723                                 /* current selected is rightmost ... cancel selection,
724                                    switch banks by one, and select righmost
725                                 */
726                                 ControlProtocol::ToggleStripableSelection (stripable[selected]);
727                                 switch_bank (bank_start+1);
728                                 if (stripable[7]) {
729                                         ControlProtocol::SetStripableSelection (stripable[7]);
730                                 }
731                         } else {
732                                 /* select next, if any */
733                                 int n = selected + 1;
734                                 while (n < 8 && !stripable[n]) {
735                                         ++n;
736                                 }
737
738                                 if (n != 8) {
739                                         ControlProtocol::SetStripableSelection (stripable[n]);
740                                 }
741                         }
742                 }
743         }
744 }
745
746 void
747 MixLayout::stripables_added ()
748 {
749         /* reload current bank */
750         switch_bank (bank_start);
751 }
752
753 void
754 MixLayout::button_down ()
755 {
756         p2.scroll_dn_1_track ();
757 }
758
759 void
760 MixLayout::button_up ()
761 {
762         p2.scroll_up_1_track ();
763 }
764
765 void
766 MixLayout::update_meters ()
767 {
768         if (vpot_mode != Volume) {
769                 return;
770         }
771
772         for (uint32_t n = 0; n < 8; ++n) {
773                 gain_meter[n]->meter->update_meters ();
774         }
775 }
776
777 MixLayout::GainMeter::GainMeter (Item* parent, Push2& p2)
778         : Container (parent)
779 {
780         /* knob and meter become owned by their parent on the canvas */
781
782         knob = new Push2Knob (p2, this);
783         knob->set_radius (25);
784         /* leave position at (0,0) */
785
786         meter = new LevelMeter (p2, this, 90, ArdourCanvas::Meter::Vertical);
787         meter->set_position (Duple (40, -60));
788 }
789