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