push2: remove Knob::set_position() overload
[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/vca_manager.h"
44
45 #include "canvas/colors.h"
46 #include "canvas/rectangle.h"
47
48 #include "gtkmm2ext/gui_thread.h"
49
50 #include "canvas.h"
51 #include "mix.h"
52 #include "knob.h"
53 #include "push2.h"
54 #include "utils.h"
55
56 #include "pbd/i18n.h"
57
58 using namespace ARDOUR;
59 using namespace std;
60 using namespace PBD;
61 using namespace Glib;
62 using namespace ArdourSurface;
63 using namespace ArdourCanvas;
64
65 MixLayout::MixLayout (Push2& p, Session& s)
66         : Push2Layout (p, s)
67         , bank_start (0)
68         , vpot_mode (Volume)
69 {
70         selection_bg = new Rectangle (this);
71         selection_bg->hide ();
72
73         Pango::FontDescription fd2 ("Sans 10");
74         for (int n = 0; n < 8; ++n) {
75                 Text* t = new Text (this);
76                 upper_text.push_back (t);
77                 t->set_font_description (fd2);
78                 t->set_color (p2.get_color (Push2::ParameterName));
79
80                 string txt;
81                 switch (n) {
82                 case 0:
83                         txt = _("Volumes");
84                         break;
85                 case 1:
86                         txt = _("Pans");
87                         break;
88                 case 2:
89                         txt = _("Pan Widths");
90                         break;
91                 case 3:
92                         txt = _("A Sends");
93                         break;
94                 case 4:
95                         txt = _("B Sends");
96                         break;
97                 case 5:
98                         txt = _("C Sends");
99                         break;
100                 case 6:
101                         txt = _("D Sends");
102                         break;
103                 case 7:
104                         txt = _("E Sends");
105                         break;
106                 }
107                 t->set (txt);
108
109                 t = new Text (this);
110                 lower_text.push_back (t);
111                 t->set_font_description (fd2);
112                 t->set_color (p2.get_color (Push2::ParameterName));
113
114                 Rectangle* r = new Rectangle (this);
115                 r->set (Rect (10 + (n*Push2Canvas::inter_button_spacing()) - 5, 2, Push2Canvas::inter_button_spacing(), 21));
116                 backgrounds.push_back (r);
117
118                 knobs[n] = new Push2Knob (p2, this);
119                 knobs[n]->set_position (Duple (60 + (n*Push2Canvas::inter_button_spacing()), 95));
120                 knobs[n]->set_radius (25);
121         }
122
123         mode_button = p2.button_by_id (Push2::Upper1);
124
125         session.RouteAdded.connect (session_connections, invalidator(*this), boost::bind (&MixLayout::stripables_added, this), &p2);
126         session.vca_manager().VCAAdded.connect (session_connections, invalidator (*this), boost::bind (&MixLayout::stripables_added, this), &p2);
127 }
128
129 MixLayout::~MixLayout ()
130 {
131         // Item destructor deletes all children
132 }
133
134 void
135 MixLayout::show ()
136 {
137         Item::show ();
138
139         mode_button->set_color (Push2::LED::White);
140         mode_button->set_state (Push2::LED::OneShot24th);
141         p2.write (mode_button->state_msg());
142
143         switch_bank (bank_start);
144 }
145
146 void
147 MixLayout::render (Rect const& area, Cairo::RefPtr<Cairo::Context> context) const
148 {
149         set_source_rgb (context, p2.get_color (Push2::DarkBackground));
150         context->rectangle (0, 0, display_width(), display_height());
151         context->fill ();
152
153         context->move_to (0, 22.5);
154         context->line_to (display_width(), 22.5);
155         context->set_line_width (1.0);
156         context->stroke ();
157
158         render_children (area, context);
159 }
160
161 void
162 MixLayout::button_upper (uint32_t n)
163 {
164         Push2::Button* b;
165         switch (n) {
166         case 0:
167                 vpot_mode = Volume;
168                 b = p2.button_by_id (Push2::Upper1);
169                 break;
170         case 1:
171                 vpot_mode = PanAzimuth;
172                 b = p2.button_by_id (Push2::Upper2);
173                 break;
174         case 2:
175                 vpot_mode = PanWidth;
176                 b = p2.button_by_id (Push2::Upper3);
177                 break;
178         case 3:
179                 vpot_mode = Send1;
180                 b = p2.button_by_id (Push2::Upper4);
181                 break;
182         case 4:
183                 vpot_mode = Send2;
184                 b = p2.button_by_id (Push2::Upper5);
185                 break;
186         case 5:
187                 vpot_mode = Send3;
188                 b = p2.button_by_id (Push2::Upper6);
189                 break;
190         case 6:
191                 vpot_mode = Send4;
192                 b = p2.button_by_id (Push2::Upper7);
193                 break;
194         case 7:
195                 vpot_mode = Send5;
196                 b = p2.button_by_id (Push2::Upper8);
197                 break;
198         }
199
200         if (b != mode_button) {
201                 mode_button->set_color (Push2::LED::Black);
202                 mode_button->set_state (Push2::LED::OneShot24th);
203                 p2.write (mode_button->state_msg());
204         }
205
206         mode_button = b;
207
208         show_vpot_mode ();
209 }
210
211 void
212 MixLayout::show_vpot_mode ()
213 {
214         mode_button->set_color (Push2::LED::White);
215         mode_button->set_state (Push2::LED::OneShot24th);
216         p2.write (mode_button->state_msg());
217
218         boost::shared_ptr<AutomationControl> ac;
219         switch (vpot_mode) {
220         case Volume:
221                 for (int s = 0; s < 8; ++s) {
222                         if (stripable[s]) {
223                                 knobs[s]->set_controllable (stripable[s]->gain_control());
224                         } else {
225                                 knobs[s]->set_controllable (boost::shared_ptr<AutomationControl>());
226                         }
227                         knobs[s]->remove_flag (Push2Knob::ArcToZero);
228                 }
229                 break;
230         case PanAzimuth:
231                 for (int s = 0; s < 8; ++s) {
232                         if (stripable[s]) {
233                                 knobs[s]->set_controllable (stripable[s]->pan_azimuth_control());
234                                 knobs[s]->add_flag (Push2Knob::ArcToZero);
235                         } else {
236                                 knobs[s]->set_controllable (boost::shared_ptr<AutomationControl>());
237
238                         }
239                 }
240                 break;
241         case PanWidth:
242                 for (int s = 0; s < 8; ++s) {
243                         if (stripable[s]) {
244                                 knobs[s]->set_controllable (stripable[s]->pan_width_control());
245                         } else {
246                                 knobs[s]->set_controllable (boost::shared_ptr<AutomationControl>());
247
248                         }
249                         knobs[s]->remove_flag (Push2Knob::ArcToZero);
250                 }
251                 break;
252         case Send1:
253                 for (int s = 0; s < 8; ++s) {
254                         if (stripable[s]) {
255                                 knobs[s]->set_controllable (stripable[s]->send_level_controllable (0));
256                         } else {
257                                 knobs[s]->set_controllable (boost::shared_ptr<AutomationControl>());
258
259                         }
260                         knobs[s]->remove_flag (Push2Knob::ArcToZero);
261                 }
262                 break;
263         case Send2:
264                 for (int s = 0; s < 8; ++s) {
265                         if (stripable[s]) {
266                                 knobs[s]->set_controllable (stripable[s]->send_level_controllable (1));
267                         } else {
268                                 knobs[s]->set_controllable (boost::shared_ptr<AutomationControl>());
269
270                         }
271                         knobs[s]->remove_flag (Push2Knob::ArcToZero);
272                 }
273                 break;
274         case Send3:
275                 for (int s = 0; s < 8; ++s) {
276                         if (stripable[s]) {
277                                 knobs[s]->set_controllable (stripable[s]->send_level_controllable (2));
278                         } else {
279                                 knobs[s]->set_controllable (boost::shared_ptr<AutomationControl>());
280
281                         }
282                         knobs[s]->remove_flag (Push2Knob::ArcToZero);
283                 }
284                 break;
285         case Send4:
286                 for (int s = 0; s < 8; ++s) {
287                         if (stripable[s]) {
288                                 knobs[s]->set_controllable (stripable[s]->send_level_controllable (3));
289                         } else {
290                                 knobs[s]->set_controllable (boost::shared_ptr<AutomationControl>());
291
292                         }
293                         knobs[s]->remove_flag (Push2Knob::ArcToZero);
294                 }
295                 break;
296         case Send5:
297                 for (int s = 0; s < 8; ++s) {
298                         if (stripable[s]) {
299                                 knobs[s]->set_controllable (stripable[s]->send_level_controllable (4));
300                         } else {
301                                 knobs[s]->set_controllable (boost::shared_ptr<AutomationControl>());
302
303                         }
304                         knobs[s]->remove_flag (Push2Knob::ArcToZero);
305                 }
306                 break;
307         default:
308                 break;
309         }
310
311         _dirty = true;
312 }
313
314 void
315 MixLayout::button_mute ()
316 {
317         boost::shared_ptr<Stripable> s = ControlProtocol::first_selected_stripable();
318         if (s) {
319                 boost::shared_ptr<AutomationControl> ac = s->mute_control();
320                 if (ac) {
321                         ac->set_value (!ac->get_value(), PBD::Controllable::UseGroup);
322                 }
323         }
324 }
325
326 void
327 MixLayout::button_solo ()
328 {
329         boost::shared_ptr<Stripable> s = ControlProtocol::first_selected_stripable();
330         if (s) {
331                 boost::shared_ptr<AutomationControl> ac = s->solo_control();
332                 if (ac) {
333                         ac->set_value (!ac->get_value(), PBD::Controllable::UseGroup);
334                 }
335         }
336 }
337
338 void
339 MixLayout::button_lower (uint32_t n)
340 {
341         if (!stripable[n]) {
342                 return;
343         }
344
345         ControlProtocol::SetStripableSelection (stripable[n]);
346 }
347
348 void
349 MixLayout::strip_vpot (int n, int delta)
350 {
351         boost::shared_ptr<Controllable> ac = knobs[n]->controllable();
352
353         if (ac) {
354                 ac->set_value (ac->get_value() + ((2.0/64.0) * delta), PBD::Controllable::UseGroup);
355         }
356 }
357
358 void
359 MixLayout::strip_vpot_touch (int n, bool touching)
360 {
361         if (stripable[n]) {
362                 boost::shared_ptr<AutomationControl> ac = stripable[n]->gain_control();
363                 if (ac) {
364                         if (touching) {
365                                 ac->start_touch (session.audible_frame());
366                         } else {
367                                 ac->stop_touch (true, session.audible_frame());
368                         }
369                 }
370         }
371 }
372
373 void
374 MixLayout::stripable_property_change (PropertyChange const& what_changed, int which)
375 {
376         if (what_changed.contains (Properties::selected)) {
377                 if (!stripable[which]) {
378                         return;
379                 }
380
381                 if (stripable[which]->presentation_info().selected()) {
382                         selection_bg->show ();
383                         selection_bg->set_fill_color (stripable[which]->presentation_info().color());
384                         selection_bg->set (Rect (10  + (which*Push2Canvas::inter_button_spacing()) - 5, 137,
385                                                  10 + (which*Push2Canvas::inter_button_spacing()) - 5 + Push2Canvas::inter_button_spacing(),
386                                                  137 + 21));
387                         lower_text[which]->set_color (ArdourCanvas::contrasting_text_color (selection_bg->fill_color()));
388                 } else {
389                         selection_bg->hide ();
390                         lower_text[which]->set_color (stripable[which]->presentation_info().color());
391                 }
392         }
393 }
394
395 void
396 MixLayout::solo_changed (uint32_t n)
397 {
398         solo_mute_changed (n);
399 }
400
401 void
402 MixLayout::mute_changed (uint32_t n)
403 {
404         solo_mute_changed (n);
405 }
406
407 void
408 MixLayout::solo_mute_changed (uint32_t n)
409 {
410         string shortname = short_version (stripable[n]->name(), 10);
411         string text;
412         boost::shared_ptr<AutomationControl> ac;
413         ac = stripable[n]->solo_control();
414         if (ac && ac->get_value()) {
415                 text += "* ";
416         }
417         boost::shared_ptr<MuteControl> mc;
418         mc = stripable[n]->mute_control ();
419         if (mc) {
420                 if (mc->muted_by_self_or_masters()) {
421                         text += "! ";
422                 } else if (mc->muted_by_others_soloing()) {
423                         text += "- "; // it would be nice to use Unicode mute"\uD83D\uDD07 ";
424                 }
425         }
426         text += shortname;
427         lower_text[n]->set (text);
428 }
429
430 void
431 MixLayout::switch_bank (uint32_t base)
432 {
433         stripable_connections.drop_connections ();
434
435         /* work backwards so we can tell if we should actually switch banks */
436
437         boost::shared_ptr<Stripable> s[8];
438         uint32_t old_empty = 0;
439         uint32_t new_empty = 0;
440
441         for (int n = 0; n < 8; ++n) {
442                 if (!stripable[n]) {
443                         old_empty++;
444                 }
445                 s[n] = session.get_remote_nth_stripable (base+n, PresentationInfo::Flag (PresentationInfo::Route|PresentationInfo::VCA));
446                 if (!s[n]) {
447                         new_empty++;
448                 }
449         }
450
451         if ((new_empty != 0) && (new_empty >= old_empty)) {
452                 /* some missing strips; new bank the same or more empty stripables than the old one, do
453                    nothing since we had already reached the end.
454                 */
455                 for (int n = 0; n < 8; ++n) {
456                         upper_text[n]->hide ();
457                         lower_text[n]->hide ();
458                         backgrounds[n]->hide ();
459                 }
460                 return;
461         }
462
463         for (int n = 0; n < 8; ++n) {
464                 stripable[n] = s[n];
465         }
466
467         /* at least one stripable in this bank */
468
469         bank_start = base;
470
471         for (int n = 0; n < 8; ++n) {
472                 if (!stripable[n]) {
473                         upper_text[n]->hide ();
474                         lower_text[n]->hide ();
475                         backgrounds[n]->hide ();
476                         continue;
477                 }
478
479                 upper_text[n]->show ();
480                 lower_text[n]->show ();
481                 backgrounds[n]->show ();
482                 backgrounds[n]->set_fill_color (stripable[n]->presentation_info().color());
483
484                 /* stripable goes away? refill the bank, starting at the same point */
485
486                 stripable[n]->DropReferences.connect (stripable_connections, invalidator (*this), boost::bind (&MixLayout::switch_bank, this, bank_start), &p2);
487                 stripable[n]->presentation_info().PropertyChanged.connect (stripable_connections, invalidator (*this), boost::bind (&MixLayout::stripable_property_change, this, _1, n), &p2);
488                 stripable[n]->solo_control()->Changed.connect (stripable_connections, invalidator (*this), boost::bind (&MixLayout::solo_changed, this, n), &p2);
489                 stripable[n]->mute_control()->Changed.connect (stripable_connections, invalidator (*this), boost::bind (&MixLayout::mute_changed, this, n), &p2);
490
491                 solo_mute_changed (n);
492
493                 Push2::Button* b;
494
495                 switch (n) {
496                 case 0:
497                         b = p2.button_by_id (Push2::Lower1);
498                         break;
499                 case 1:
500                         b = p2.button_by_id (Push2::Lower2);
501                         break;
502                 case 2:
503                         b = p2.button_by_id (Push2::Lower3);
504                         break;
505                 case 3:
506                         b = p2.button_by_id (Push2::Lower4);
507                         break;
508                 case 4:
509                         b = p2.button_by_id (Push2::Lower5);
510                         break;
511                 case 5:
512                         b = p2.button_by_id (Push2::Lower6);
513                         break;
514                 case 6:
515                         b = p2.button_by_id (Push2::Lower7);
516                         break;
517                 case 7:
518                         b = p2.button_by_id (Push2::Lower8);
519                         break;
520                 }
521
522                 b->set_color (p2.get_color_index (stripable[n]->presentation_info().color()));
523                 b->set_state (Push2::LED::OneShot24th);
524                 p2.write (b->state_msg());
525
526                 knobs[n]->set_text_color (stripable[n]->presentation_info().color());
527                 knobs[n]->set_arc_start_color (stripable[n]->presentation_info().color());
528                 knobs[n]->set_arc_end_color (stripable[n]->presentation_info().color());
529         }
530
531         show_vpot_mode ();
532 }
533
534 void
535 MixLayout::button_right ()
536 {
537         switch_bank (max (0, bank_start + 8));
538 }
539
540 void
541 MixLayout::button_left ()
542 {
543         switch_bank (max (0, bank_start - 8));
544 }
545
546
547 void
548 MixLayout::button_select_press ()
549 {
550 }
551
552 void
553 MixLayout::button_select_release ()
554 {
555         if (!(p2.modifier_state() & Push2::ModSelect)) {
556                 /* somebody else used us as a modifier */
557                 return;
558         }
559
560         int selected = -1;
561
562         for (int n = 0; n < 8; ++n) {
563                 if (stripable[n]) {
564                         if (stripable[n]->presentation_info().selected()) {
565                                         selected = n;
566                                         break;
567                         }
568                 }
569         }
570
571         if (selected < 0) {
572
573                 /* no visible track selected, select first (if any) */
574
575                 if (stripable[0]) {
576                         ControlProtocol::SetStripableSelection (stripable[0]);
577                 }
578
579         } else {
580
581                 if (p2.modifier_state() & Push2::ModShift) {
582                         std::cerr << "select prev\n";
583                         /* select prev */
584
585                         if (selected == 0) {
586                                 /* current selected is leftmost ... cancel selection,
587                                    switch banks by one, and select leftmost
588                                 */
589                                 if (bank_start != 0) {
590                                         ControlProtocol::ClearStripableSelection ();
591                                         switch_bank (bank_start-1);
592                                         if (stripable[0]) {
593                                                 ControlProtocol::SetStripableSelection (stripable[0]);
594                                         }
595                                 }
596                         } else {
597                                 /* select prev, if any */
598                                 int n = selected - 1;
599                                 while (n >= 0 && !stripable[n]) {
600                                         --n;
601                                 }
602                                 if (n >= 0) {
603                                         ControlProtocol::SetStripableSelection (stripable[n]);
604                                 }
605                         }
606
607                 } else {
608
609                         std::cerr << "select next\n";
610                         /* select next */
611
612                         if (selected == 7) {
613                                 /* current selected is rightmost ... cancel selection,
614                                    switch banks by one, and select righmost
615                                 */
616                                 ControlProtocol::ToggleStripableSelection (stripable[selected]);
617                                 switch_bank (bank_start+1);
618                                 if (stripable[7]) {
619                                         ControlProtocol::SetStripableSelection (stripable[7]);
620                                 }
621                         } else {
622                                 /* select next, if any */
623                                 int n = selected + 1;
624                                 while (n < 8 && !stripable[n]) {
625                                         ++n;
626                                 }
627
628                                 if (n != 8) {
629                                         ControlProtocol::SetStripableSelection (stripable[n]);
630                                 }
631                         }
632                 }
633         }
634 }
635
636 void
637 MixLayout::stripables_added ()
638 {
639         /* reload current bank */
640         switch_bank (bank_start);
641 }