refactor Push2 GUI into modular "layouts" that encapsulate a given screen, its drawin...
authorPaul Davis <paul@linuxaudiosystems.com>
Sun, 10 Jul 2016 14:49:39 +0000 (10:49 -0400)
committerPaul Davis <paul@linuxaudiosystems.com>
Tue, 27 Sep 2016 19:59:30 +0000 (14:59 -0500)
libs/surfaces/push2/buttons.cc
libs/surfaces/push2/layout.cc [new file with mode: 0644]
libs/surfaces/push2/mix.cc [new file with mode: 0644]
libs/surfaces/push2/push2.cc
libs/surfaces/push2/push2.h
libs/surfaces/push2/scale.cc [new file with mode: 0644]
libs/surfaces/push2/wscript

index 8e916d126f740c7531e77d999cee866bbaab3bed..fe9e3322f4e69397111a072162f7769908b05624 100644 (file)
@@ -213,7 +213,7 @@ Push2::button_play ()
                return;
        }
 
-       if (modifier_state & ModShift) {
+       if (_modifier_state & ModShift) {
                goto_start (session->transport_rolling());
                return;
        }
@@ -258,13 +258,13 @@ Push2::button_page_left ()
 void
 Push2::button_right ()
 {
-       switch_bank (max (0, bank_start + 8));
+       _current_layout->button_right ();
 }
 
 void
 Push2::button_left ()
 {
-       switch_bank (max (0, bank_start - 8));
+       _current_layout->button_left ();
 }
 
 void
@@ -331,45 +331,19 @@ Push2::button_clip ()
 void
 Push2::button_upper (uint32_t n)
 {
-       if (!stripable[n]) {
-               return;
-       }
-
-       if (modifier_state & ModShift) {
-               boost::shared_ptr<AutomationControl> sc = stripable[n]->rec_enable_control ();
-               if (sc) {
-                       sc->set_value (!sc->get_value(), PBD::Controllable::UseGroup);
-               }
-       } else {
-               boost::shared_ptr<SoloControl> sc = stripable[n]->solo_control ();
-               if (sc) {
-                       sc->set_value (!sc->self_soloed(), PBD::Controllable::UseGroup);
-               }
-       }
+       _current_layout->button_upper (n);
 }
 
 void
 Push2::button_lower (uint32_t n)
 {
-       if (!stripable[n]) {
-               return;
-       }
-
-       if (modifier_state & ModSelect) {
-               SetStripableSelection (stripable[n]);
-       } else {
-               boost::shared_ptr<MuteControl> mc = stripable[n]->mute_control ();
-
-               if (mc) {
-                       mc->set_value (!mc->muted_by_self(), PBD::Controllable::UseGroup);
-               }
-       }
+       _current_layout->button_lower (n);
 }
 
 void
 Push2::button_undo ()
 {
-       if (modifier_state & ModShift) {
+       if (_modifier_state & ModShift) {
                ControlProtocol::Redo ();
        } else {
                ControlProtocol::Undo ();
@@ -379,56 +353,56 @@ Push2::button_undo ()
 void
 Push2::button_fwd32t ()
 {
-       const int n = (modifier_state & ModShift) ? 8 : 0;
+       const int n = (_modifier_state & ModShift) ? 8 : 0;
        goto_nth_marker (0+n);
 }
 
 void
 Push2::button_fwd32 ()
 {
-       const int n = (modifier_state & ModShift) ? 8 : 0;
+       const int n = (_modifier_state & ModShift) ? 8 : 0;
        goto_nth_marker (1+n);
 }
 
 void
 Push2::button_fwd16t ()
 {
-       const int n = (modifier_state & ModShift) ? 8 : 0;
+       const int n = (_modifier_state & ModShift) ? 8 : 0;
        goto_nth_marker (2+n);
 }
 
 void
 Push2::button_fwd16 ()
 {
-       const int n = (modifier_state & ModShift) ? 8 : 0;
+       const int n = (_modifier_state & ModShift) ? 8 : 0;
        goto_nth_marker (3+n);
 }
 
 void
 Push2::button_fwd8t ()
 {
-       const int n = (modifier_state & ModShift) ? 8 : 0;
+       const int n = (_modifier_state & ModShift) ? 8 : 0;
        goto_nth_marker (4+n);
 }
 
 void
 Push2::button_fwd8 ()
 {
-       const int n = (modifier_state & ModShift) ? 8 : 0;
+       const int n = (_modifier_state & ModShift) ? 8 : 0;
        goto_nth_marker (5+n);
 }
 
 void
 Push2::button_fwd4t ()
 {
-       const int n = (modifier_state & ModShift) ? 8 : 0;
+       const int n = (_modifier_state & ModShift) ? 8 : 0;
        goto_nth_marker (6+n);
 }
 
 void
 Push2::button_fwd4 ()
 {
-       const int n = (modifier_state & ModShift) ? 8 : 0;
+       const int n = (_modifier_state & ModShift) ? 8 : 0;
        goto_nth_marker (7+n);
 }
 
@@ -466,93 +440,30 @@ Push2::button_shift_long_press ()
 void
 Push2::button_select_press ()
 {
-       start_select ();
+       cerr << "start select\n";
+       _modifier_state = ModifierState (_modifier_state | ModSelect);
+       Button* b = id_button_map[Select];
+       b->set_color (Push2::LED::White);
+       b->set_state (Push2::LED::Blinking16th);
+       write (b->state_msg());
+
+       _current_layout->button_select_press ();
 }
 
 void
 Push2::button_select_release ()
 {
-       if (!(modifier_state & ModSelect)) {
-               /* somebody else used us as a modifier */
-               return;
-       }
-
-       end_select ();
-
-       int selected = -1;
-
-       for (int n = 0; n < 8; ++n) {
-               if (stripable[n]) {
-                       if (stripable[n]->presentation_info().selected()) {
-                                       selected = n;
-                                       break;
-                       }
-               }
+       if (_modifier_state & ModSelect) {
+               cerr << "end select\n";
+               _modifier_state = ModifierState (_modifier_state & ~(ModSelect));
+               Button* b = id_button_map[Select];
+               b->timeout_connection.disconnect ();
+               b->set_color (Push2::LED::White);
+               b->set_state (Push2::LED::OneShot24th);
+               write (b->state_msg());
        }
 
-       if (selected < 0) {
-
-               /* no visible track selected, select first (if any) */
-
-               if (stripable[0]) {
-                       SetStripableSelection (stripable[0]);
-               }
-
-       } else {
-
-               if (modifier_state & ModShift) {
-                       std::cerr << "select prev\n";
-                       /* select prev */
-
-                       if (selected == 0) {
-                               /* current selected is leftmost ... cancel selection,
-                                  switch banks by one, and select leftmost
-                               */
-                               if (bank_start != 0) {
-                                       ClearStripableSelection ();
-                                       switch_bank (bank_start-1);
-                                       if (stripable[0]) {
-                                               SetStripableSelection (stripable[0]);
-                                       }
-                               }
-                       } else {
-                               /* select prev, if any */
-                               int n = selected - 1;
-                               while (n >= 0 && !stripable[n]) {
-                                       --n;
-                               }
-                               if (n >= 0) {
-                                       SetStripableSelection (stripable[n]);
-                               }
-                       }
-
-               } else {
-
-                       std::cerr << "select next\n";
-                       /* select next */
-
-                       if (selected == 7) {
-                               /* current selected is rightmost ... cancel selection,
-                                  switch banks by one, and select righmost
-                               */
-                               ToggleStripableSelection (stripable[selected]);
-                               switch_bank (bank_start+1);
-                               if (stripable[7]) {
-                                       SetStripableSelection (stripable[7]);
-                               }
-                       } else {
-                               /* select next, if any */
-                               int n = selected + 1;
-                               while (n < 8 && !stripable[n]) {
-                                       ++n;
-                               }
-
-                               if (n != 8) {
-                                       SetStripableSelection (stripable[n]);
-                               }
-                       }
-               }
-       }
+       _current_layout->button_select_release ();
 }
 
 void
@@ -622,9 +533,9 @@ Push2::button_layout_press ()
 void
 Push2::button_scale_press ()
 {
-       if (current_menu != scale_menu) {
-               show_scale_menu ();
+       if (_current_layout != scale_layout) {
+               _current_layout = scale_layout;
        } else {
-               cancel_menu ();
+               _current_layout = mix_layout;
        }
 }
diff --git a/libs/surfaces/push2/layout.cc b/libs/surfaces/push2/layout.cc
new file mode 100644 (file)
index 0000000..e27ea0f
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+  Copyright (C) 2016 Paul Davis
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software
+  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "push2.h"
+
+using namespace ARDOUR;
+using namespace ArdourSurface;
+
+Push2Layout::Push2Layout (Push2& p, Session& s)
+       : p2 (p)
+       , session (s)
+{
+}
+
+Push2Layout::~Push2Layout ()
+{
+}
+
+bool
+Push2Layout::mapped () const
+{
+       return p2.current_layout() == this;
+}
diff --git a/libs/surfaces/push2/mix.cc b/libs/surfaces/push2/mix.cc
new file mode 100644 (file)
index 0000000..a4bebf4
--- /dev/null
@@ -0,0 +1,570 @@
+/*
+  Copyright (C) 2016 Paul Davis
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software
+  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <pangomm/layout.h>
+
+#include "pbd/compose.h"
+#include "pbd/convert.h"
+#include "pbd/debug.h"
+#include "pbd/failed_constructor.h"
+#include "pbd/file_utils.h"
+#include "pbd/search_path.h"
+#include "pbd/enumwriter.h"
+
+#include "midi++/parser.h"
+#include "timecode/time.h"
+#include "timecode/bbt_time.h"
+
+#include "ardour/async_midi_port.h"
+#include "ardour/audioengine.h"
+#include "ardour/debug.h"
+#include "ardour/filesystem_paths.h"
+#include "ardour/midiport_manager.h"
+#include "ardour/midi_track.h"
+#include "ardour/midi_port.h"
+#include "ardour/session.h"
+#include "ardour/tempo.h"
+
+#include "push2.h"
+
+#include "i18n.h"
+
+using namespace ARDOUR;
+using namespace std;
+using namespace PBD;
+using namespace Glib;
+using namespace ArdourSurface;
+
+MixLayout::MixLayout (Push2& p, Session& s, Cairo::RefPtr<Cairo::Context> context)
+       : Push2Layout (p, s)
+       , bank_start (0)
+{
+       tc_clock_layout = Pango::Layout::create (context);
+       bbt_clock_layout = Pango::Layout::create (context);
+
+       Pango::FontDescription fd ("Sans Bold 24");
+       tc_clock_layout->set_font_description (fd);
+       bbt_clock_layout->set_font_description (fd);
+
+       Pango::FontDescription fd2 ("Sans 10");
+       for (int n = 0; n < 8; ++n) {
+               upper_layout[n] = Pango::Layout::create (context);
+               upper_layout[n]->set_font_description (fd2);
+               upper_layout[n]->set_text ("solo");
+               lower_layout[n] = Pango::Layout::create (context);
+               lower_layout[n]->set_font_description (fd2);
+               lower_layout[n]->set_text ("mute");
+       }
+
+       Pango::FontDescription fd3 ("Sans Bold 10");
+       for (int n = 0; n < 8; ++n) {
+               mid_layout[n] = Pango::Layout::create (context);
+               mid_layout[n]->set_font_description (fd3);
+       }
+
+       switch_bank (0);
+}
+
+MixLayout::~MixLayout ()
+{
+}
+
+bool
+MixLayout::redraw (Cairo::RefPtr<Cairo::Context> context) const
+{
+       framepos_t audible = session.audible_frame();
+       Timecode::Time TC;
+       bool negative = false;
+       string tc_clock_text;
+       string bbt_clock_text;
+
+       if (audible < 0) {
+               audible = -audible;
+               negative = true;
+       }
+
+       session.timecode_time (audible, TC);
+
+       TC.negative = TC.negative || negative;
+
+       tc_clock_text = Timecode::timecode_format_time(TC);
+
+       Timecode::BBT_Time bbt = session.tempo_map().bbt_at_frame (audible);
+       char buf[16];
+
+#define BBT_BAR_CHAR "|"
+
+       if (negative) {
+               snprintf (buf, sizeof (buf), "-%03" PRIu32 BBT_BAR_CHAR "%02" PRIu32 BBT_BAR_CHAR "%04" PRIu32,
+                         bbt.bars, bbt.beats, bbt.ticks);
+       } else {
+               snprintf (buf, sizeof (buf), " %03" PRIu32 BBT_BAR_CHAR "%02" PRIu32 BBT_BAR_CHAR "%04" PRIu32,
+                         bbt.bars, bbt.beats, bbt.ticks);
+       }
+
+       bbt_clock_text = buf;
+
+       bool dirty = false;
+
+       if (tc_clock_text != tc_clock_layout->get_text()) {
+               dirty = true;
+               tc_clock_layout->set_text (tc_clock_text);
+       }
+
+       if (bbt_clock_text != tc_clock_layout->get_text()) {
+               dirty = true;
+               bbt_clock_layout->set_text (bbt_clock_text);
+       }
+
+       string mid_text;
+
+       for (int n = 0; n < 8; ++n) {
+               if (stripable[n]) {
+                       mid_text = short_version (stripable[n]->name(), 10);
+                       if (mid_text != mid_layout[n]->get_text()) {
+                               mid_layout[n]->set_text (mid_text);
+                               dirty = true;
+                       }
+               }
+       }
+
+       if (!dirty) {
+               return false;
+       }
+
+       context->set_source_rgb (0.764, 0.882, 0.882);
+       context->rectangle (0, 0, 960, 160);
+       context->fill ();
+
+       /* clocks */
+
+       context->set_source_rgb (0.23, 0.0, 0.349);
+       context->move_to (650, 25);
+       tc_clock_layout->update_from_cairo_context (context);
+       tc_clock_layout->show_in_cairo_context (context);
+       context->move_to (650, 60);
+       bbt_clock_layout->update_from_cairo_context (context);
+       bbt_clock_layout->show_in_cairo_context (context);
+
+       for (int n = 0; n < 8; ++n) {
+               context->move_to (10 + (n*120), 2);
+               upper_layout[n]->update_from_cairo_context (context);
+               upper_layout[n]->show_in_cairo_context (context);
+       }
+
+       for (int n = 0; n < 8; ++n) {
+               context->move_to (10 + (n*120), 140);
+               lower_layout[n]->update_from_cairo_context (context);
+               lower_layout[n]->show_in_cairo_context (context);
+       }
+
+       for (int n = 0; n < 8; ++n) {
+               if (stripable[n] && stripable[n]->presentation_info().selected()) {
+                       context->rectangle (10 + (n*120) - 5, 115, 120, 22);
+                       context->set_source_rgb (1.0, 0.737, 0.172);
+                       context->fill();
+               }
+               context->set_source_rgb (0.0, 0.0, 0.0);
+               context->move_to (10 + (n*120), 120);
+               mid_layout[n]->update_from_cairo_context (context);
+               mid_layout[n]->show_in_cairo_context (context);
+       }
+
+       return true;
+}
+
+void
+MixLayout::button_upper (uint32_t n)
+{
+       if (!stripable[n]) {
+               return;
+       }
+
+       if (p2.modifier_state() & Push2::ModShift) {
+               boost::shared_ptr<AutomationControl> sc = stripable[n]->rec_enable_control ();
+               if (sc) {
+                       sc->set_value (!sc->get_value(), PBD::Controllable::UseGroup);
+               }
+       } else {
+               boost::shared_ptr<SoloControl> sc = stripable[n]->solo_control ();
+               if (sc) {
+                       sc->set_value (!sc->self_soloed(), PBD::Controllable::UseGroup);
+               }
+       }
+}
+
+void
+MixLayout::button_lower (uint32_t n)
+{
+       if (!stripable[n]) {
+               return;
+       }
+
+       if (p2.modifier_state() & Push2::ModSelect) {
+               ControlProtocol::SetStripableSelection (stripable[n]);
+       } else {
+               boost::shared_ptr<MuteControl> mc = stripable[n]->mute_control ();
+
+               if (mc) {
+                       mc->set_value (!mc->muted_by_self(), PBD::Controllable::UseGroup);
+               }
+       }
+}
+
+void
+MixLayout::strip_vpot (int n, int delta)
+{
+       if (stripable[n]) {
+               boost::shared_ptr<AutomationControl> ac = stripable[n]->gain_control();
+               if (ac) {
+                       ac->set_value (ac->get_value() + ((2.0/64.0) * delta), PBD::Controllable::UseGroup);
+               }
+       }
+}
+
+void
+MixLayout::strip_vpot_touch (int n, bool touching)
+{
+       if (stripable[n]) {
+               boost::shared_ptr<AutomationControl> ac = stripable[n]->gain_control();
+               if (ac) {
+                       if (touching) {
+                               ac->start_touch (session.audible_frame());
+                       } else {
+                               ac->stop_touch (true, session.audible_frame());
+                       }
+               }
+       }
+}
+
+void
+MixLayout::stripable_property_change (PropertyChange const& what_changed, int which)
+{
+       if (what_changed.contains (Properties::selected)) {
+               if (!stripable[which]) {
+                       return;
+               }
+
+               /* cancel string, which will cause a redraw on the next update
+                * cycle. The redraw will reflect selected status
+                */
+
+               mid_layout[which]->set_text (string());
+       }
+}
+
+
+void
+MixLayout::solo_change (int n)
+{
+       Push2::ButtonID bid;
+
+       switch (n) {
+       case 0:
+               bid = Push2::Upper1;
+               break;
+       case 1:
+               bid = Push2::Upper2;
+               break;
+       case 2:
+               bid = Push2::Upper3;
+               break;
+       case 3:
+               bid = Push2::Upper4;
+               break;
+       case 4:
+               bid = Push2::Upper5;
+               break;
+       case 5:
+               bid = Push2::Upper6;
+               break;
+       case 6:
+               bid = Push2::Upper7;
+               break;
+       case 7:
+               bid = Push2::Upper8;
+               break;
+       default:
+               return;
+       }
+
+       boost::shared_ptr<SoloControl> ac = stripable[n]->solo_control ();
+       if (!ac) {
+               return;
+       }
+
+       Push2::Button* b = p2.button_by_id (bid);
+
+       if (ac->soloed()) {
+               b->set_color (Push2::LED::Green);
+       } else {
+               b->set_color (Push2::LED::Black);
+       }
+
+       if (ac->soloed_by_others_upstream() || ac->soloed_by_others_downstream()) {
+               b->set_state (Push2::LED::Blinking4th);
+       } else {
+               b->set_state (Push2::LED::OneShot24th);
+       }
+
+       p2.write (b->state_msg());
+}
+
+void
+MixLayout::mute_change (int n)
+{
+       Push2::ButtonID bid;
+
+       if (!stripable[n]) {
+               return;
+       }
+
+       cerr << "Mute changed on " << n << ' ' << stripable[n]->name() << endl;
+
+       switch (n) {
+       case 0:
+               bid = Push2::Lower1;
+               break;
+       case 1:
+               bid = Push2::Lower2;
+               break;
+       case 2:
+               bid = Push2::Lower3;
+               break;
+       case 3:
+               bid = Push2::Lower4;
+               break;
+       case 4:
+               bid = Push2::Lower5;
+               break;
+       case 5:
+               bid = Push2::Lower6;
+               break;
+       case 6:
+               bid = Push2::Lower7;
+               break;
+       case 7:
+               bid = Push2::Lower8;
+               break;
+       default:
+               return;
+       }
+
+       boost::shared_ptr<MuteControl> mc = stripable[n]->mute_control ();
+
+       if (!mc) {
+               return;
+       }
+
+       Push2::Button* b = p2.button_by_id (bid);
+
+       if (Config->get_show_solo_mutes() && !Config->get_solo_control_is_listen_control ()) {
+
+               if (mc->muted_by_self ()) {
+                       /* full mute */
+                       b->set_color (Push2::LED::Blue);
+                       b->set_state (Push2::LED::OneShot24th);
+                       cerr << "FULL MUTE1\n";
+               } else if (mc->muted_by_others_soloing () || mc->muted_by_masters ()) {
+                       /* this will reflect both solo mutes AND master mutes */
+                       b->set_color (Push2::LED::Blue);
+                       b->set_state (Push2::LED::Blinking4th);
+                       cerr << "OTHER MUTE1\n";
+               } else {
+                       /* no mute at all */
+                       b->set_color (Push2::LED::Black);
+                       b->set_state (Push2::LED::OneShot24th);
+                       cerr << "NO MUTE1\n";
+               }
+
+       } else {
+
+               if (mc->muted_by_self()) {
+                       /* full mute */
+                       b->set_color (Push2::LED::Blue);
+                       b->set_state (Push2::LED::OneShot24th);
+                       cerr << "FULL MUTE2\n";
+               } else if (mc->muted_by_masters ()) {
+                       /* this shows only master mutes, not mute-by-others-soloing */
+                       b->set_color (Push2::LED::Blue);
+                       b->set_state (Push2::LED::Blinking4th);
+                       cerr << "OTHER MUTE1\n";
+               } else {
+                       /* no mute at all */
+                       b->set_color (Push2::LED::Black);
+                       b->set_state (Push2::LED::OneShot24th);
+                       cerr << "NO MUTE2\n";
+               }
+       }
+
+       p2.write (b->state_msg());
+}
+
+void
+MixLayout::switch_bank (uint32_t base)
+{
+       stripable_connections.drop_connections ();
+
+       /* try to get the first stripable for the requested bank */
+
+       stripable[0] = session.get_remote_nth_stripable (base, PresentationInfo::Flag (PresentationInfo::Route|PresentationInfo::VCA));
+
+       if (!stripable[0]) {
+               return;
+       }
+
+       /* at least one stripable in this bank */
+       bank_start = base;
+
+       stripable[1] = session.get_remote_nth_stripable (base+1, PresentationInfo::Flag (PresentationInfo::Route|PresentationInfo::VCA));
+       stripable[2] = session.get_remote_nth_stripable (base+2, PresentationInfo::Flag (PresentationInfo::Route|PresentationInfo::VCA));
+       stripable[3] = session.get_remote_nth_stripable (base+3, PresentationInfo::Flag (PresentationInfo::Route|PresentationInfo::VCA));
+       stripable[4] = session.get_remote_nth_stripable (base+4, PresentationInfo::Flag (PresentationInfo::Route|PresentationInfo::VCA));
+       stripable[5] = session.get_remote_nth_stripable (base+5, PresentationInfo::Flag (PresentationInfo::Route|PresentationInfo::VCA));
+       stripable[6] = session.get_remote_nth_stripable (base+6, PresentationInfo::Flag (PresentationInfo::Route|PresentationInfo::VCA));
+       stripable[7] = session.get_remote_nth_stripable (base+7, PresentationInfo::Flag (PresentationInfo::Route|PresentationInfo::VCA));
+
+
+       for (int n = 0; n < 8; ++n) {
+               if (!stripable[n]) {
+                       continue;
+               }
+
+               /* stripable goes away? refill the bank, starting at the same point */
+
+               stripable[n]->DropReferences.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&MixLayout::switch_bank, this, bank_start), &p2);
+               boost::shared_ptr<AutomationControl> sc = stripable[n]->solo_control();
+               if (sc) {
+                       sc->Changed.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&MixLayout::solo_change, this, n), &p2);
+               }
+
+               boost::shared_ptr<AutomationControl> mc = stripable[n]->mute_control();
+               if (mc) {
+                       mc->Changed.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&MixLayout::mute_change, this, n), &p2);
+               }
+
+               stripable[n]->presentation_info().PropertyChanged.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&MixLayout::stripable_property_change, this, _1, n), &p2);
+
+               solo_change (n);
+               mute_change (n);
+
+       }
+}
+
+void
+MixLayout::button_right ()
+{
+       switch_bank (max (0, bank_start + 8));
+}
+
+void
+MixLayout::button_left ()
+{
+       switch_bank (max (0, bank_start - 8));
+}
+
+void
+MixLayout::button_select_press ()
+{
+}
+
+void
+MixLayout::button_select_release ()
+{
+       if (!(p2.modifier_state() & Push2::ModSelect)) {
+               /* somebody else used us as a modifier */
+               return;
+       }
+
+       int selected = -1;
+
+       for (int n = 0; n < 8; ++n) {
+               if (stripable[n]) {
+                       if (stripable[n]->presentation_info().selected()) {
+                                       selected = n;
+                                       break;
+                       }
+               }
+       }
+
+       if (selected < 0) {
+
+               /* no visible track selected, select first (if any) */
+
+               if (stripable[0]) {
+                       ControlProtocol::SetStripableSelection (stripable[0]);
+               }
+
+       } else {
+
+               if (p2.modifier_state() & Push2::ModShift) {
+                       std::cerr << "select prev\n";
+                       /* select prev */
+
+                       if (selected == 0) {
+                               /* current selected is leftmost ... cancel selection,
+                                  switch banks by one, and select leftmost
+                               */
+                               if (bank_start != 0) {
+                                       ControlProtocol::ClearStripableSelection ();
+                                       switch_bank (bank_start-1);
+                                       if (stripable[0]) {
+                                               ControlProtocol::SetStripableSelection (stripable[0]);
+                                       }
+                               }
+                       } else {
+                               /* select prev, if any */
+                               int n = selected - 1;
+                               while (n >= 0 && !stripable[n]) {
+                                       --n;
+                               }
+                               if (n >= 0) {
+                                       ControlProtocol::SetStripableSelection (stripable[n]);
+                               }
+                       }
+
+               } else {
+
+                       std::cerr << "select next\n";
+                       /* select next */
+
+                       if (selected == 7) {
+                               /* current selected is rightmost ... cancel selection,
+                                  switch banks by one, and select righmost
+                               */
+                               ControlProtocol::ToggleStripableSelection (stripable[selected]);
+                               switch_bank (bank_start+1);
+                               if (stripable[7]) {
+                                       ControlProtocol::SetStripableSelection (stripable[7]);
+                               }
+                       } else {
+                               /* select next, if any */
+                               int n = selected + 1;
+                               while (n < 8 && !stripable[n]) {
+                                       ++n;
+                               }
+
+                               if (n != 8) {
+                                       ControlProtocol::SetStripableSelection (stripable[n]);
+                               }
+                       }
+               }
+       }
+}
+
+
index d2e4be3fdee639a9adc3b59faa1b75118bc86cf5..0a00ba8d951560f697493701d7b4eee37ee910e2 100644 (file)
@@ -1,25 +1,21 @@
 /*
-       Copyright (C) 2016 Paul Davis
+  Copyright (C) 2016 Paul Davis
 
-       This program is free software; you can redistribute it and/or modify
-       it under the terms of the GNU General Public License as published by
-       the Free Software Foundation; either version 2 of the License, or
-       (at your option) any later version.
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
 
-       This program is distributed in the hope that it will be useful,
-       but WITHOUT ANY WARRANTY; without even the implied warranty of
-       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-       GNU General Public License for more details.
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+  GNU General Public License for more details.
 
-       You should have received a copy of the GNU General Public License
-       along with this program; if not, write to the Free Software
-       Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software
+  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
 
-#include <cairomm/context.h>
-#include <cairomm/surface.h>
-#include <pangomm/layout.h>
-
 #include "pbd/compose.h"
 #include "pbd/convert.h"
 #include "pbd/debug.h"
 #include "gui.h"
 #include "menu.h"
 
+#include "i18n.h"
+
 using namespace ARDOUR;
 using namespace std;
 using namespace PBD;
 using namespace Glib;
 using namespace ArdourSurface;
 
-#include "i18n.h"
-
 #include "pbd/abstract_ui.cc" // instantiate template
 
 const int Push2::cols = 960;
@@ -122,9 +118,10 @@ Push2::Push2 (ARDOUR::Session& s)
        , handle (0)
        , device_buffer (0)
        , frame_buffer (Cairo::ImageSurface::create (Cairo::FORMAT_ARGB32, cols, rows))
-       , modifier_state (None)
+       , _modifier_state (None)
        , splash_start (0)
-       , bank_start (0)
+       , _current_layout (0)
+       , drawn_layout (0)
        , connection_state (ConnectionState (0))
        , gui (0)
        , _mode (MusicalMode::IonianMajor)
@@ -133,42 +130,26 @@ Push2::Push2 (ARDOUR::Session& s)
        , _in_key (true)
        , octave_shift (0)
        , percussion (false)
-       , current_menu (0)
-       , drawn_menu (0)
 {
        context = Cairo::Context::create (frame_buffer);
-       tc_clock_layout = Pango::Layout::create (context);
-       bbt_clock_layout = Pango::Layout::create (context);
-
-       Pango::FontDescription fd ("Sans Bold 24");
-       tc_clock_layout->set_font_description (fd);
-       bbt_clock_layout->set_font_description (fd);
-
-       Pango::FontDescription fd2 ("Sans 10");
-       for (int n = 0; n < 8; ++n) {
-               upper_layout[n] = Pango::Layout::create (context);
-               upper_layout[n]->set_font_description (fd2);
-               upper_layout[n]->set_text ("solo");
-               lower_layout[n] = Pango::Layout::create (context);
-               lower_layout[n]->set_font_description (fd2);
-               lower_layout[n]->set_text ("mute");
-       }
-
-       Pango::FontDescription fd3 ("Sans Bold 10");
-       for (int n = 0; n < 8; ++n) {
-               mid_layout[n] = Pango::Layout::create (context);
-               mid_layout[n]->set_font_description (fd3);
-       }
 
        build_pad_table ();
        build_maps ();
-       build_scale_menu ();
+
+       /* master cannot be removed, so no need to connect to going-away signal */
+       master = session->master_out ();
 
        if (open ()) {
                throw failed_constructor ();
        }
 
-       StripableSelectionChanged.connect (selection_connection, MISSING_INVALIDATOR, boost::bind (&Push2::stripable_selection_change, this, _1), this);
+       ControlProtocol::StripableSelectionChanged.connect (selection_connection, MISSING_INVALIDATOR, boost::bind (&Push2::stripable_selection_change, this, _1), this);
+
+       /* catch current selection, if any */
+       {
+               StripableNotificationListPtr sp (new StripableNotificationList (ControlProtocol::last_selected()));
+               stripable_selection_change (sp);
+       }
 
        /* catch arrival and departure of Push2 itself */
        ARDOUR::AudioEngine::instance()->PortRegisteredOrUnregistered.connect (port_reg_connection, MISSING_INVALIDATOR, boost::bind (&Push2::port_registration_handler, this), this);
@@ -281,6 +262,10 @@ Push2::open ()
 
        connect_to_parser ();
 
+       mix_layout = new MixLayout (*this, *session, context);
+       scale_layout = new ScaleLayout (*this, *session, context);
+       _current_layout = mix_layout;
+
        return 0;
 }
 
@@ -317,7 +302,6 @@ Push2::close ()
        vblank_connection.disconnect ();
        periodic_connection.disconnect ();
        session_connections.drop_connections ();
-       stripable_connections.drop_connections ();
 
        if (handle) {
                libusb_release_interface (handle, 0x00);
@@ -325,10 +309,6 @@ Push2::close ()
                handle = 0;
        }
 
-       for (int n = 0; n < 8; ++n) {
-               stripable[n].reset ();
-       }
-
        delete [] device_frame_buffer;
        device_frame_buffer = 0;
 
@@ -509,133 +489,34 @@ Push2::blit_to_device_frame_buffer ()
 bool
 Push2::redraw ()
 {
-       string tc_clock_text;
-       string bbt_clock_text;
-
        if (splash_start) {
-               if (get_microseconds() - splash_start > 4000000) {
-                       splash_start = 0;
-               } else {
-                       return false;
-               }
-       }
-
-       if (current_menu) {
-               if (current_menu->dirty() || drawn_menu != current_menu) {
-                       /* fill background */
-                       context->set_source_rgb (0.764, 0.882, 0.882);
-                       context->rectangle (0, 0, 960, 160);
-                       context->fill ();
-                       /* now menu */
-                       current_menu->redraw (context);
-                       drawn_menu = current_menu;
-                       return true;
-               }
-               return false;
-       } else {
-               drawn_menu = 0;
-       }
-
-       if (session) {
-               framepos_t audible = session->audible_frame();
-               Timecode::Time TC;
-               bool negative = false;
-
-               if (audible < 0) {
-                       audible = -audible;
-                       negative = true;
-               }
 
-               session->timecode_time (audible, TC);
+               /* display splash for 3 seconds */
 
-               TC.negative = TC.negative || negative;
-
-               tc_clock_text = Timecode::timecode_format_time(TC);
-
-               Timecode::BBT_Time bbt = session->tempo_map().bbt_at_frame (audible);
-               char buf[16];
-
-#define BBT_BAR_CHAR "|"
-
-               if (negative) {
-                       snprintf (buf, sizeof (buf), "-%03" PRIu32 BBT_BAR_CHAR "%02" PRIu32 BBT_BAR_CHAR "%04" PRIu32,
-                                 bbt.bars, bbt.beats, bbt.ticks);
+               if (get_microseconds() - splash_start > 3000000) {
+                       splash_start = 0;
                } else {
-                       snprintf (buf, sizeof (buf), " %03" PRIu32 BBT_BAR_CHAR "%02" PRIu32 BBT_BAR_CHAR "%04" PRIu32,
-                                 bbt.bars, bbt.beats, bbt.ticks);
+                       return false;
                }
-
-               bbt_clock_text = buf;
        }
 
-       bool dirty = false;
+       Glib::Threads::Mutex::Lock lm (layout_lock, Glib::Threads::TRY_LOCK);
 
-       if (tc_clock_text != tc_clock_layout->get_text()) {
-               dirty = true;
-               tc_clock_layout->set_text (tc_clock_text);
-       }
-
-       if (bbt_clock_text != tc_clock_layout->get_text()) {
-               dirty = true;
-               bbt_clock_layout->set_text (bbt_clock_text);
-       }
-
-       string mid_text;
-
-       for (int n = 0; n < 8; ++n) {
-               if (stripable[n]) {
-                       mid_text = short_version (stripable[n]->name(), 10);
-                       if (mid_text != mid_layout[n]->get_text()) {
-                               mid_layout[n]->set_text (mid_text);
-                               dirty = true;
-                       }
-               }
-       }
-
-       if (!dirty) {
+       if (!lm.locked()) {
+               /* can't get layout, no re-render needed */
                return false;
        }
 
-       context->set_source_rgb (0.764, 0.882, 0.882);
-       context->rectangle (0, 0, 960, 160);
-       context->fill ();
-       context->set_source_rgb (0.23, 0.0, 0.349);
-       context->move_to (650, 25);
-       tc_clock_layout->update_from_cairo_context (context);
-       tc_clock_layout->show_in_cairo_context (context);
-       context->move_to (650, 60);
-       bbt_clock_layout->update_from_cairo_context (context);
-       bbt_clock_layout->show_in_cairo_context (context);
-
-       for (int n = 0; n < 8; ++n) {
-               context->move_to (10 + (n*120), 2);
-               upper_layout[n]->update_from_cairo_context (context);
-               upper_layout[n]->show_in_cairo_context (context);
-       }
-
-       for (int n = 0; n < 8; ++n) {
-               context->move_to (10 + (n*120), 140);
-               lower_layout[n]->update_from_cairo_context (context);
-               lower_layout[n]->show_in_cairo_context (context);
-       }
-
-       for (int n = 0; n < 8; ++n) {
-               if (stripable[n] && stripable[n]->presentation_info().selected()) {
-                       context->rectangle (10 + (n*120) - 5, 115, 120, 22);
-                       context->set_source_rgb (1.0, 0.737, 0.172);
-                       context->fill();
-               }
-               context->set_source_rgb (0.0, 0.0, 0.0);
-               context->move_to (10 + (n*120), 120);
-               mid_layout[n]->update_from_cairo_context (context);
-               mid_layout[n]->show_in_cairo_context (context);
+       bool render_needed = false;
+
+       if (drawn_layout != _current_layout) {
+               render_needed = true;
        }
 
-       /* render clock */
-       /* render foo */
-       /* render bar */
+       bool dirty = _current_layout->redraw (context);
+       drawn_layout = _current_layout;
 
-       return true;
+       return dirty || render_needed;
 }
 
 bool
@@ -709,14 +590,8 @@ Push2::set_active (bool yn)
                init_buttons (true);
                init_touch_strip ();
                set_pad_scale (_scale_root, _root_octave, _mode, _in_key);
-               switch_bank (0);
                splash ();
 
-               /* catch current selection, if any */
-               {
-                       StripableNotificationListPtr sp (new StripableNotificationList (ControlProtocol::last_selected()));
-                       stripable_selection_change (sp);
-               }
 
        } else {
 
@@ -861,33 +736,33 @@ Push2::handle_midi_controller_message (MIDI::Parser&, MIDI::EventTwoBytes* ev)
 
                switch (ev->controller_number) {
                case 71:
-                       strip_vpot (0, delta);
+                       _current_layout->strip_vpot (0, delta);
                        break;
                case 72:
-                       strip_vpot (1, delta);
+                       _current_layout->strip_vpot (1, delta);
                        break;
                case 73:
-                       strip_vpot (2, delta);
+                       _current_layout->strip_vpot (2, delta);
                        break;
                case 74:
-                       strip_vpot (3, delta);
+                       _current_layout->strip_vpot (3, delta);
                        break;
                case 75:
-                       strip_vpot (4, delta);
+                       _current_layout->strip_vpot (4, delta);
                        break;
                case 76:
-                       strip_vpot (5, delta);
+                       _current_layout->strip_vpot (5, delta);
                        break;
                case 77:
-                       strip_vpot (6, delta);
+                       _current_layout->strip_vpot (6, delta);
                        break;
                case 78:
-                       strip_vpot (7, delta);
+                       _current_layout->strip_vpot (7, delta);
                        break;
 
                        /* left side pair */
                case 14:
-                       strip_vpot (8, delta);
+                       other_vpot (8, delta);
                        break;
                case 15:
                        other_vpot (1, delta);
@@ -913,28 +788,28 @@ Push2::handle_midi_note_on_message (MIDI::Parser& parser, MIDI::EventTwoBytes* e
 
        switch (ev->note_number) {
        case 0:
-               strip_vpot_touch (0, ev->velocity > 64);
+               _current_layout->strip_vpot_touch (0, ev->velocity > 64);
                break;
        case 1:
-               strip_vpot_touch (1, ev->velocity > 64);
+               _current_layout->strip_vpot_touch (1, ev->velocity > 64);
                break;
        case 2:
-               strip_vpot_touch (2, ev->velocity > 64);
+               _current_layout->strip_vpot_touch (2, ev->velocity > 64);
                break;
        case 3:
-               strip_vpot_touch (3, ev->velocity > 64);
+               _current_layout->strip_vpot_touch (3, ev->velocity > 64);
                break;
        case 4:
-               strip_vpot_touch (4, ev->velocity > 64);
+               _current_layout->strip_vpot_touch (4, ev->velocity > 64);
                break;
        case 5:
-               strip_vpot_touch (5, ev->velocity > 64);
+               _current_layout->strip_vpot_touch (5, ev->velocity > 64);
                break;
        case 6:
-               strip_vpot_touch (6, ev->velocity > 64);
+               _current_layout->strip_vpot_touch (6, ev->velocity > 64);
                break;
        case 7:
-               strip_vpot_touch (7, ev->velocity > 64);
+               _current_layout->strip_vpot_touch (7, ev->velocity > 64);
                break;
 
                /* left side */
@@ -1233,317 +1108,6 @@ Push2::set_state (const XMLNode & node, int version)
        return retval;
 }
 
-void
-Push2::switch_bank (uint32_t base)
-{
-       if (!session) {
-               return;
-       }
-
-       stripable_connections.drop_connections ();
-
-       /* try to get the first stripable for the requested bank */
-
-       stripable[0] = session->get_remote_nth_stripable (base, PresentationInfo::Flag (PresentationInfo::Route|PresentationInfo::VCA));
-
-       if (!stripable[0]) {
-               return;
-       }
-
-       /* at least one stripable in this bank */
-       bank_start = base;
-
-       stripable[1] = session->get_remote_nth_stripable (base+1, PresentationInfo::Flag (PresentationInfo::Route|PresentationInfo::VCA));
-       stripable[2] = session->get_remote_nth_stripable (base+2, PresentationInfo::Flag (PresentationInfo::Route|PresentationInfo::VCA));
-       stripable[3] = session->get_remote_nth_stripable (base+3, PresentationInfo::Flag (PresentationInfo::Route|PresentationInfo::VCA));
-       stripable[4] = session->get_remote_nth_stripable (base+4, PresentationInfo::Flag (PresentationInfo::Route|PresentationInfo::VCA));
-       stripable[5] = session->get_remote_nth_stripable (base+5, PresentationInfo::Flag (PresentationInfo::Route|PresentationInfo::VCA));
-       stripable[6] = session->get_remote_nth_stripable (base+6, PresentationInfo::Flag (PresentationInfo::Route|PresentationInfo::VCA));
-       stripable[7] = session->get_remote_nth_stripable (base+7, PresentationInfo::Flag (PresentationInfo::Route|PresentationInfo::VCA));
-
-
-       for (int n = 0; n < 8; ++n) {
-               if (!stripable[n]) {
-                       continue;
-               }
-
-               /* stripable goes away? refill the bank, starting at the same point */
-
-               stripable[n]->DropReferences.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&Push2::switch_bank, this, bank_start), this);
-               boost::shared_ptr<AutomationControl> sc = stripable[n]->solo_control();
-               if (sc) {
-                       sc->Changed.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&Push2::solo_change, this, n), this);
-               }
-
-               boost::shared_ptr<AutomationControl> mc = stripable[n]->mute_control();
-               if (mc) {
-                       mc->Changed.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&Push2::mute_change, this, n), this);
-               }
-
-               stripable[n]->presentation_info().PropertyChanged.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&Push2::stripable_property_change, this, _1, n), this);
-
-               solo_change (n);
-               mute_change (n);
-
-       }
-
-       /* master cannot be removed, so no need to connect to going-away signal */
-       master = session->master_out ();
-}
-
-void
-Push2::stripable_property_change (PropertyChange const& what_changed, int which)
-{
-       if (what_changed.contains (Properties::selected)) {
-               if (!stripable[which]) {
-                       return;
-               }
-
-               /* cancel string, which will cause a redraw on the next update
-                * cycle. The redraw will reflect selected status
-                */
-
-               mid_layout[which]->set_text (string());
-       }
-}
-
-void
-Push2::stripable_selection_change (StripableNotificationListPtr selected)
-{
-
-       boost::shared_ptr<MidiPort> pad_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in)->shadow_port();
-       boost::shared_ptr<MidiTrack> current_midi_track = current_pad_target.lock();
-       boost::shared_ptr<MidiTrack> new_pad_target;
-
-       /* See if there's a MIDI track selected */
-
-       for (StripableNotificationList::iterator si = selected->begin(); si != selected->end(); ++si) {
-
-               new_pad_target = boost::dynamic_pointer_cast<MidiTrack> ((*si).lock());
-
-               if (new_pad_target) {
-                       break;
-               }
-       }
-
-       if (new_pad_target) {
-               cerr << "new midi pad target " << new_pad_target->name() << endl;
-       } else {
-               cerr << "no midi pad target\n";
-       }
-
-       if (current_midi_track == new_pad_target) {
-               /* nothing to do */
-               return;
-       }
-
-       if (!new_pad_target) {
-               /* leave existing connection alone */
-               return;
-       }
-
-       /* disconnect from pad port, if appropriate */
-
-       if (current_midi_track && pad_port) {
-               cerr << "Disconnect pads from " << current_midi_track->name() << endl;
-               current_midi_track->input()->disconnect (current_midi_track->input()->nth(0), pad_port->name(), this);
-       }
-
-       /* now connect the pad port to this (newly) selected midi
-        * track, if indeed there is one.
-        */
-
-       if (new_pad_target && pad_port) {
-               cerr << "Reconnect pads to " << new_pad_target->name() << endl;
-               new_pad_target->input()->connect (new_pad_target->input()->nth (0), pad_port->name(), this);
-               current_pad_target = new_pad_target;
-       } else {
-               current_pad_target.reset ();
-       }
-}
-
-
-void
-Push2::solo_change (int n)
-{
-       ButtonID bid;
-
-       switch (n) {
-       case 0:
-               bid = Upper1;
-               break;
-       case 1:
-               bid = Upper2;
-               break;
-       case 2:
-               bid = Upper3;
-               break;
-       case 3:
-               bid = Upper4;
-               break;
-       case 4:
-               bid = Upper5;
-               break;
-       case 5:
-               bid = Upper6;
-               break;
-       case 6:
-               bid = Upper7;
-               break;
-       case 7:
-               bid = Upper8;
-               break;
-       default:
-               return;
-       }
-
-       boost::shared_ptr<SoloControl> ac = stripable[n]->solo_control ();
-       if (!ac) {
-               return;
-       }
-
-       Button* b = id_button_map[bid];
-
-       if (ac->soloed()) {
-               b->set_color (LED::Green);
-       } else {
-               b->set_color (LED::Black);
-       }
-
-       if (ac->soloed_by_others_upstream() || ac->soloed_by_others_downstream()) {
-               b->set_state (LED::Blinking4th);
-       } else {
-               b->set_state (LED::OneShot24th);
-       }
-
-       write (b->state_msg());
-}
-
-void
-Push2::mute_change (int n)
-{
-       ButtonID bid;
-
-       if (!stripable[n]) {
-               return;
-       }
-
-       cerr << "Mute changed on " << n << ' ' << stripable[n]->name() << endl;
-
-       switch (n) {
-       case 0:
-               bid = Lower1;
-               break;
-       case 1:
-               bid = Lower2;
-               break;
-       case 2:
-               bid = Lower3;
-               break;
-       case 3:
-               bid = Lower4;
-               break;
-       case 4:
-               bid = Lower5;
-               break;
-       case 5:
-               bid = Lower6;
-               break;
-       case 6:
-               bid = Lower7;
-               break;
-       case 7:
-               bid = Lower8;
-               break;
-       default:
-               return;
-       }
-
-       boost::shared_ptr<MuteControl> mc = stripable[n]->mute_control ();
-
-       if (!mc) {
-               return;
-       }
-
-       Button* b = id_button_map[bid];
-
-       if (Config->get_show_solo_mutes() && !Config->get_solo_control_is_listen_control ()) {
-
-               if (mc->muted_by_self ()) {
-                       /* full mute */
-                       b->set_color (LED::Blue);
-                       b->set_state (LED::OneShot24th);
-                       cerr << "FULL MUTE1\n";
-               } else if (mc->muted_by_others_soloing () || mc->muted_by_masters ()) {
-                       /* this will reflect both solo mutes AND master mutes */
-                       b->set_color (LED::Blue);
-                       b->set_state (LED::Blinking4th);
-                       cerr << "OTHER MUTE1\n";
-               } else {
-                       /* no mute at all */
-                       b->set_color (LED::Black);
-                       b->set_state (LED::OneShot24th);
-                       cerr << "NO MUTE1\n";
-               }
-
-       } else {
-
-               if (mc->muted_by_self()) {
-                       /* full mute */
-                       b->set_color (LED::Blue);
-                       b->set_state (LED::OneShot24th);
-                       cerr << "FULL MUTE2\n";
-               } else if (mc->muted_by_masters ()) {
-                       /* this shows only master mutes, not mute-by-others-soloing */
-                       b->set_color (LED::Blue);
-                       b->set_state (LED::Blinking4th);
-                       cerr << "OTHER MUTE1\n";
-               } else {
-                       /* no mute at all */
-                       b->set_color (LED::Black);
-                       b->set_state (LED::OneShot24th);
-                       cerr << "NO MUTE2\n";
-               }
-       }
-
-       write (b->state_msg());
-}
-
-void
-Push2::strip_vpot (int n, int delta)
-{
-       if (current_menu) {
-               current_menu->step_active (n, delta);
-               return;
-       }
-
-       if (stripable[n]) {
-               boost::shared_ptr<AutomationControl> ac = stripable[n]->gain_control();
-               if (ac) {
-                       ac->set_value (ac->get_value() + ((2.0/64.0) * delta), PBD::Controllable::UseGroup);
-               }
-       }
-}
-
-void
-Push2::strip_vpot_touch (int n, bool touching)
-{
-       if (current_menu) {
-               return;
-       }
-
-       if (stripable[n]) {
-               boost::shared_ptr<AutomationControl> ac = stripable[n]->gain_control();
-               if (ac) {
-                       if (touching) {
-                               ac->start_touch (session->audible_frame());
-                       } else {
-                               ac->stop_touch (true, session->audible_frame());
-                       }
-               }
-       }
-}
-
 void
 Push2::other_vpot (int n, int delta)
 {
@@ -1590,7 +1154,7 @@ void
 Push2::start_shift ()
 {
        cerr << "start shift\n";
-       modifier_state = ModifierState (modifier_state | ModShift);
+       _modifier_state = ModifierState (_modifier_state | ModShift);
        Button* b = id_button_map[Shift];
        b->set_color (LED::White);
        b->set_state (LED::Blinking16th);
@@ -1600,9 +1164,9 @@ Push2::start_shift ()
 void
 Push2::end_shift ()
 {
-       if (modifier_state & ModShift) {
+       if (_modifier_state & ModShift) {
                cerr << "end shift\n";
-               modifier_state = ModifierState (modifier_state & ~(ModShift));
+               _modifier_state = ModifierState (_modifier_state & ~(ModShift));
                Button* b = id_button_map[Shift];
                b->timeout_connection.disconnect ();
                b->set_color (LED::White);
@@ -1611,31 +1175,6 @@ Push2::end_shift ()
        }
 }
 
-void
-Push2::start_select ()
-{
-       cerr << "start select\n";
-       modifier_state = ModifierState (modifier_state | ModSelect);
-       Button* b = id_button_map[Select];
-       b->set_color (LED::White);
-       b->set_state (LED::Blinking16th);
-       write (b->state_msg());
-}
-
-void
-Push2::end_select ()
-{
-       if (modifier_state & ModSelect) {
-               cerr << "end select\n";
-               modifier_state = ModifierState (modifier_state & ~(ModSelect));
-               Button* b = id_button_map[Select];
-               b->timeout_connection.disconnect ();
-               b->set_color (LED::White);
-               b->set_state (LED::OneShot24th);
-               write (b->state_msg());
-       }
-}
-
 void
 Push2::splash ()
 {
@@ -2005,72 +1544,69 @@ Push2::set_percussive_mode (bool yn)
        PadChange (); /* EMIT SIGNAL */
 }
 
-void
-Push2::set_menu (Push2Menu* m)
+Push2Layout*
+Push2::current_layout () const
 {
-       current_menu = m;
-       drawn_menu = 0;
+       Glib::Threads::Mutex::Lock lm (layout_lock);
+       return _current_layout;
 }
 
 void
-Push2::build_scale_menu ()
+Push2::stripable_selection_change (StripableNotificationListPtr selected)
 {
-       vector<string> v;
-
-       scale_menu = new Push2Menu (context);
-
-       v.push_back ("Dorian");
-       v.push_back ("IonianMajor");
-       v.push_back ("Minor");
-       v.push_back ("HarmonicMinor");
-       v.push_back ("MelodicMinorAscending");
-       v.push_back ("MelodicMinorDescending");
-       v.push_back ("Phrygian");
-       v.push_back ("Lydian");
-       v.push_back ("Mixolydian");
-       v.push_back ("Aeolian");
-       v.push_back ("Locrian");
-       v.push_back ("PentatonicMajor");
-       v.push_back ("PentatonicMinor");
-       v.push_back ("Chromatic");
-       v.push_back ("BluesScale");
-       v.push_back ("NeapolitanMinor");
-       v.push_back ("NeapolitanMajor");
-       v.push_back ("Oriental");
-       v.push_back ("DoubleHarmonic");
-       v.push_back ("Enigmatic");
-       v.push_back ("Hirajoshi");
-       v.push_back ("HungarianMinor");
-       v.push_back ("HungarianMajor");
-       v.push_back ("Kumoi");
-       v.push_back ("Iwato");
-       v.push_back ("Hindu");
-       v.push_back ("Spanish8Tone");
-       v.push_back ("Pelog");
-       v.push_back ("HungarianGypsy");
-       v.push_back ("Overtone");
-       v.push_back ("LeadingWholeTone");
-       v.push_back ("Arabian");
-       v.push_back ("Balinese");
-       v.push_back ("Gypsy");
-       v.push_back ("Mohammedan");
-       v.push_back ("Javanese");
-       v.push_back ("Persian");
-       v.push_back ("Algeria");
-
-       scale_menu->fill_column (0, v);
-
-       v.clear ();
-}
+       boost::shared_ptr<MidiPort> pad_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in)->shadow_port();
+       boost::shared_ptr<MidiTrack> current_midi_track = current_pad_target.lock();
+       boost::shared_ptr<MidiTrack> new_pad_target;
 
-void
-Push2::show_scale_menu ()
-{
-       set_menu (scale_menu);
+       /* See if there's a MIDI track selected */
+
+       for (StripableNotificationList::iterator si = selected->begin(); si != selected->end(); ++si) {
+
+               new_pad_target = boost::dynamic_pointer_cast<MidiTrack> ((*si).lock());
+
+               if (new_pad_target) {
+                       break;
+               }
+       }
+
+       if (new_pad_target) {
+               cerr << "new midi pad target " << new_pad_target->name() << endl;
+       } else {
+               cerr << "no midi pad target\n";
+       }
+
+       if (current_midi_track == new_pad_target) {
+               /* nothing to do */
+               return;
+       }
+
+       if (!new_pad_target) {
+               /* leave existing connection alone */
+               return;
+       }
+
+       /* disconnect from pad port, if appropriate */
+
+       if (current_midi_track && pad_port) {
+               cerr << "Disconnect pads from " << current_midi_track->name() << endl;
+               current_midi_track->input()->disconnect (current_midi_track->input()->nth(0), pad_port->name(), this);
+       }
+
+       /* now connect the pad port to this (newly) selected midi
+        * track, if indeed there is one.
+        */
+
+       if (new_pad_target && pad_port) {
+               cerr << "Reconnect pads to " << new_pad_target->name() << endl;
+               new_pad_target->input()->connect (new_pad_target->input()->nth (0), pad_port->name(), this);
+               current_pad_target = new_pad_target;
+       } else {
+               current_pad_target.reset ();
+       }
 }
 
-void
-Push2::cancel_menu ()
+Push2::Button*
+Push2::button_by_id (ButtonID bid)
 {
-       set_menu (0);
+       return id_button_map[bid];
 }
index 4ced88b67f82b56c20e3faed51996bd1c9c3f1b2..e3fe7213598745a615a9ffb0ddabb67ad41b47c1 100644 (file)
@@ -72,72 +72,12 @@ public:
 
 class P2GUI;
 class Push2Menu;
+class Push2Layout;
 
 class Push2 : public ARDOUR::ControlProtocol
             , public AbstractUI<Push2Request>
 {
-   public:
-       Push2 (ARDOUR::Session&);
-       ~Push2 ();
-
-       static bool probe ();
-       static void* request_factory (uint32_t);
-
-       std::list<boost::shared_ptr<ARDOUR::Bundle> > bundles ();
-
-       bool has_editor () const { return true; }
-       void* get_gui () const;
-       void  tear_down_gui ();
-
-       int set_active (bool yn);
-       XMLNode& get_state();
-       int set_state (const XMLNode & node, int version);
-
-       PBD::Signal0<void> ConnectionChange;
-
-       boost::shared_ptr<ARDOUR::Port> input_port();
-       boost::shared_ptr<ARDOUR::Port> output_port();
-
-       int pad_note (int row, int col) const;
-       PBD::Signal0<void> PadChange;
-
-       void set_pad_scale (int root, int octave, MusicalMode::Type mode, bool inkey);
-
-       MusicalMode::Type mode() const { return  _mode; }
-       int scale_root() const { return _scale_root; }
-       int root_octave() const { return _root_octave; }
-       bool in_key() const { return _in_key; }
-
-       static const int cols;
-       static const int rows;
-
-  private:
-       libusb_device_handle *handle;
-       uint8_t   frame_header[16];
-       uint16_t* device_frame_buffer;
-       int  device_buffer;
-       Cairo::RefPtr<Cairo::ImageSurface> frame_buffer;
-       sigc::connection vblank_connection;
-       sigc::connection periodic_connection;
-
-       enum ModifierState {
-               None = 0,
-               ModShift = 0x1,
-               ModSelect = 0x2,
-       };
-
-       ModifierState modifier_state;
-
-       static const int pixels_per_row;
-
-       void do_request (Push2Request*);
-       int stop ();
-       int open ();
-       int close ();
-       bool redraw ();
-       int blit_to_device_frame_buffer ();
-       bool vblank ();
-
+  public:
        enum ButtonID {
                TapTempo,
                Metronome,
@@ -341,6 +281,76 @@ class Push2 : public ARDOUR::ControlProtocol
                        : Button (bb, ex, press, release, long_press) {}
        };
 
+  public:
+       Push2 (ARDOUR::Session&);
+       ~Push2 ();
+
+       static bool probe ();
+       static void* request_factory (uint32_t);
+
+       std::list<boost::shared_ptr<ARDOUR::Bundle> > bundles ();
+
+       bool has_editor () const { return true; }
+       void* get_gui () const;
+       void  tear_down_gui ();
+
+       int set_active (bool yn);
+       XMLNode& get_state();
+       int set_state (const XMLNode & node, int version);
+
+       PBD::Signal0<void> ConnectionChange;
+
+       boost::shared_ptr<ARDOUR::Port> input_port();
+       boost::shared_ptr<ARDOUR::Port> output_port();
+
+       int pad_note (int row, int col) const;
+       PBD::Signal0<void> PadChange;
+
+       void set_pad_scale (int root, int octave, MusicalMode::Type mode, bool inkey);
+
+       MusicalMode::Type mode() const { return  _mode; }
+       int scale_root() const { return _scale_root; }
+       int root_octave() const { return _root_octave; }
+       bool in_key() const { return _in_key; }
+
+       Push2Layout* current_layout() const;
+
+       enum ModifierState {
+               None = 0,
+               ModShift = 0x1,
+               ModSelect = 0x2,
+       };
+
+       ModifierState modifier_state() const { return _modifier_state; }
+
+       Button* button_by_id (ButtonID);
+
+       void write (const MidiByteArray&);
+
+       static const int cols;
+       static const int rows;
+
+  private:
+       libusb_device_handle *handle;
+       uint8_t   frame_header[16];
+       uint16_t* device_frame_buffer;
+       int  device_buffer;
+       Cairo::RefPtr<Cairo::ImageSurface> frame_buffer;
+       sigc::connection vblank_connection;
+       sigc::connection periodic_connection;
+
+       ModifierState _modifier_state;
+
+       static const int pixels_per_row;
+
+       void do_request (Push2Request*);
+       int stop ();
+       int open ();
+       int close ();
+       bool redraw ();
+       int blit_to_device_frame_buffer ();
+       bool vblank ();
+
        void relax () {}
 
        /* map of Buttons by CC */
@@ -394,7 +404,6 @@ class Push2 : public ARDOUR::ControlProtocol
        void handle_midi_note_off_message (MIDI::Parser&, MIDI::EventTwoBytes*);
        void handle_midi_sysex (MIDI::Parser&, MIDI::byte *, size_t count);
 
-       void write (const MidiByteArray&);
        bool midi_input_handler (Glib::IOCondition ioc, MIDI::Port* port);
        bool periodic ();
 
@@ -422,24 +431,6 @@ class Push2 : public ARDOUR::ControlProtocol
        void button_new ();
        void button_browse ();
        void button_clip ();
-       void button_upper (uint32_t n);
-       void button_lower (uint32_t n);
-       void button_upper_1 () { button_upper (0); }
-       void button_upper_2 () { button_upper (1); }
-       void button_upper_3 () { button_upper (2); }
-       void button_upper_4 () { button_upper (3); }
-       void button_upper_5 () { button_upper (4); }
-       void button_upper_6 () { button_upper (5); }
-       void button_upper_7 () { button_upper (6); }
-       void button_upper_8 () { button_upper (7); }
-       void button_lower_1 () { button_lower (0); }
-       void button_lower_2 () { button_lower (1); }
-       void button_lower_3 () { button_lower (2); }
-       void button_lower_4 () { button_lower (3); }
-       void button_lower_5 () { button_lower (4); }
-       void button_lower_6 () { button_lower (5); }
-       void button_lower_7 () { button_lower (6); }
-       void button_lower_8 () { button_lower (7); }
        void button_undo ();
        void button_fwd32t ();
        void button_fwd32 ();
@@ -464,49 +455,57 @@ class Push2 : public ARDOUR::ControlProtocol
        void button_layout_press ();
        void button_scale_press ();
 
+       void button_upper (uint32_t n);
+       void button_lower (uint32_t n);
+
+       void button_upper_1 () { button_upper (0); }
+       void button_upper_2 () { button_upper (1); }
+       void button_upper_3 () { button_upper (2); }
+       void button_upper_4 () { button_upper (3); }
+       void button_upper_5 () { button_upper (4); }
+       void button_upper_6 () { button_upper (5); }
+       void button_upper_7 () { button_upper (6); }
+       void button_upper_8 () { button_upper (7); }
+       void button_lower_1 () { button_lower (0); }
+       void button_lower_2 () { button_lower (1); }
+       void button_lower_3 () { button_lower (2); }
+       void button_lower_4 () { button_lower (3); }
+       void button_lower_5 () { button_lower (4); }
+       void button_lower_6 () { button_lower (5); }
+       void button_lower_7 () { button_lower (6); }
+       void button_lower_8 () { button_lower (7); }
+
        void start_shift ();
        void end_shift ();
-       void start_select ();
-       void end_select ();
 
-       /* encoders */
+       /* non-strip encoders */
 
-       void strip_vpot (int, int);
        void other_vpot (int, int);
-       void strip_vpot_touch (int, bool);
        void other_vpot_touch (int, bool);
 
-       /* widgets */
+       /* special Stripables */
+
+       boost::shared_ptr<ARDOUR::Stripable> master;
+       boost::shared_ptr<ARDOUR::Stripable> monitor;
+
+       /* Cairo graphics context */
 
        Cairo::RefPtr<Cairo::Context> context;
-       Glib::RefPtr<Pango::Layout> tc_clock_layout;
-       Glib::RefPtr<Pango::Layout> bbt_clock_layout;
-       Glib::RefPtr<Pango::Layout> upper_layout[8];
-       Glib::RefPtr<Pango::Layout> mid_layout[8];
-       Glib::RefPtr<Pango::Layout> lower_layout[8];
 
        void splash ();
        ARDOUR::microseconds_t splash_start;
 
-       /* stripables */
-
-       int32_t bank_start;
-       PBD::ScopedConnectionList stripable_connections;
-       boost::shared_ptr<ARDOUR::Stripable> stripable[8];
-       boost::shared_ptr<ARDOUR::Stripable> master;
-       boost::shared_ptr<ARDOUR::Stripable> monitor;
-
-       void solo_change (int);
-       void mute_change (int);
-       void stripable_property_change (PBD::PropertyChange const& what_changed, int which);
+       /* Layouts */
 
-       void switch_bank (uint32_t base);
+       mutable Glib::Threads::Mutex layout_lock;
+       Push2Layout* _current_layout;
+       Push2Layout* drawn_layout;
+       Push2Layout* mix_layout;
+       Push2Layout* scale_layout;
 
        bool pad_filter (ARDOUR::MidiBuffer& in, ARDOUR::MidiBuffer& out) const;
 
        boost::weak_ptr<ARDOUR::MidiTrack> current_pad_target;
-       PBD::ScopedConnection selection_connection;
-       void stripable_selection_change (ARDOUR::StripableNotificationListPtr);
 
        PBD::ScopedConnection port_reg_connection;
        void port_registration_handler ();
@@ -527,6 +526,9 @@ class Push2 : public ARDOUR::ControlProtocol
 
        /* pad mapping */
 
+       PBD::ScopedConnection selection_connection;
+       void stripable_selection_change (ARDOUR::StripableNotificationListPtr);
+
        std::map<int,int> pad_map;
        void build_pad_table();
 
@@ -539,16 +541,92 @@ class Push2 : public ARDOUR::ControlProtocol
 
        bool percussion;
        void set_percussive_mode (bool);
+};
 
-       /* menus */
-       Push2Menu* current_menu;
-       Push2Menu* drawn_menu;
-       Push2Menu* scale_menu;
+class Push2Layout
+{
+  public:
+       Push2Layout (Push2& p, ARDOUR::Session& s);
+       virtual ~Push2Layout ();
+
+       bool mapped() const;
+
+       virtual bool redraw (Cairo::RefPtr<Cairo::Context>) const = 0;
+
+       virtual void button_upper (uint32_t n) {}
+       virtual void button_lower (uint32_t n) {}
+       virtual void button_up ()  {}
+       virtual void button_down ()  {}
+       virtual void button_right ()  {}
+       virtual void button_left ()  {}
+       virtual void button_select_press () {}
+       virtual void button_select_release () {}
+
+       virtual void strip_vpot (int, int) = 0;
+       virtual void strip_vpot_touch (int, bool) = 0;
+
+  protected:
+       Push2& p2;
+       ARDOUR::Session& session;
+};
+
+
+class MixLayout : public Push2Layout
+{
+   public:
+       MixLayout (Push2& p, ARDOUR::Session&, Cairo::RefPtr<Cairo::Context>);
+       ~MixLayout ();
+
+       bool redraw (Cairo::RefPtr<Cairo::Context>) const;
+
+       void button_upper (uint32_t n);
+       void button_lower (uint32_t n);
+       void button_left ();
+       void button_right ();
+       void button_select_press ();
+       void button_select_release ();
+
+       void strip_vpot (int, int);
+       void strip_vpot_touch (int, bool);
+
+  private:
+       Glib::RefPtr<Pango::Layout> tc_clock_layout;
+       Glib::RefPtr<Pango::Layout> bbt_clock_layout;
+       Glib::RefPtr<Pango::Layout> upper_layout[8];
+       Glib::RefPtr<Pango::Layout> mid_layout[8];
+       Glib::RefPtr<Pango::Layout> lower_layout[8];
+
+       /* stripables */
 
-       void build_scale_menu ();
-       void set_menu (Push2Menu*);
-       void show_scale_menu ();
-       void cancel_menu ();
+       int32_t bank_start;
+       PBD::ScopedConnectionList stripable_connections;
+       boost::shared_ptr<ARDOUR::Stripable> stripable[8];
+
+       void solo_change (int);
+       void mute_change (int);
+
+       void stripable_property_change (PBD::PropertyChange const& what_changed, int which);
+
+       void switch_bank (uint32_t base);
+};
+
+class ScaleLayout : public Push2Layout
+{
+   public:
+       ScaleLayout (Push2& p, ARDOUR::Session&, Cairo::RefPtr<Cairo::Context>);
+       ~ScaleLayout ();
+
+       bool redraw (Cairo::RefPtr<Cairo::Context>) const;
+
+       void button_upper (uint32_t n);
+       void button_lower (uint32_t n);
+
+       void strip_vpot (int, int);
+       void strip_vpot_touch (int, bool);
+
+   private:
+       Push2Menu* scale_menu;
+       void build_scale_menu (Cairo::RefPtr<Cairo::Context>);
 };
 
 } /* namespace */
diff --git a/libs/surfaces/push2/scale.cc b/libs/surfaces/push2/scale.cc
new file mode 100644 (file)
index 0000000..da9c4fa
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+  Copyright (C) 2016 Paul Davis
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software
+  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <pangomm/layout.h>
+
+#include "pbd/compose.h"
+#include "pbd/convert.h"
+#include "pbd/debug.h"
+#include "pbd/failed_constructor.h"
+#include "pbd/file_utils.h"
+#include "pbd/search_path.h"
+#include "pbd/enumwriter.h"
+
+#include "midi++/parser.h"
+#include "timecode/time.h"
+#include "timecode/bbt_time.h"
+
+#include "ardour/async_midi_port.h"
+#include "ardour/audioengine.h"
+#include "ardour/debug.h"
+#include "ardour/filesystem_paths.h"
+#include "ardour/midiport_manager.h"
+#include "ardour/midi_track.h"
+#include "ardour/midi_port.h"
+#include "ardour/session.h"
+#include "ardour/tempo.h"
+
+#include "push2.h"
+#include "menu.h"
+
+#include "i18n.h"
+
+using namespace ARDOUR;
+using namespace std;
+using namespace PBD;
+using namespace Glib;
+using namespace ArdourSurface;
+
+ScaleLayout::ScaleLayout (Push2& p, Session& s, Cairo::RefPtr<Cairo::Context> context)
+       : Push2Layout (p, s)
+{
+       build_scale_menu (context);
+}
+
+ScaleLayout::~ScaleLayout ()
+{
+}
+
+bool
+ScaleLayout::redraw (Cairo::RefPtr<Cairo::Context> context) const
+{
+       bool draw = false;
+
+       if (scale_menu->dirty()) {
+               draw = true;
+       }
+
+       if (!draw) {
+               return false;
+       }
+
+       context->set_source_rgb (0.764, 0.882, 0.882);
+       context->rectangle (0, 0, 960, 160);
+       context->fill ();
+
+       scale_menu->redraw (context);
+
+       return true;
+}
+
+void
+ScaleLayout::button_upper (uint32_t n)
+{
+}
+
+void
+ScaleLayout::button_lower (uint32_t n)
+{
+}
+
+void
+ScaleLayout::strip_vpot (int n, int delta)
+{
+       if (n == 0) {
+               scale_menu->step_active (n, delta);
+               return;
+       }
+}
+
+void
+ScaleLayout::strip_vpot_touch (int, bool)
+{
+}
+
+void
+ScaleLayout::build_scale_menu (Cairo::RefPtr<Cairo::Context> context)
+{
+       vector<string> v;
+
+       scale_menu = new Push2Menu (context);
+
+       v.push_back ("Dorian");
+       v.push_back ("IonianMajor");
+       v.push_back ("Minor");
+       v.push_back ("HarmonicMinor");
+       v.push_back ("MelodicMinorAscending");
+       v.push_back ("MelodicMinorDescending");
+       v.push_back ("Phrygian");
+       v.push_back ("Lydian");
+       v.push_back ("Mixolydian");
+       v.push_back ("Aeolian");
+       v.push_back ("Locrian");
+       v.push_back ("PentatonicMajor");
+       v.push_back ("PentatonicMinor");
+       v.push_back ("Chromatic");
+       v.push_back ("BluesScale");
+       v.push_back ("NeapolitanMinor");
+       v.push_back ("NeapolitanMajor");
+       v.push_back ("Oriental");
+       v.push_back ("DoubleHarmonic");
+       v.push_back ("Enigmatic");
+       v.push_back ("Hirajoshi");
+       v.push_back ("HungarianMinor");
+       v.push_back ("HungarianMajor");
+       v.push_back ("Kumoi");
+       v.push_back ("Iwato");
+       v.push_back ("Hindu");
+       v.push_back ("Spanish8Tone");
+       v.push_back ("Pelog");
+       v.push_back ("HungarianGypsy");
+       v.push_back ("Overtone");
+       v.push_back ("LeadingWholeTone");
+       v.push_back ("Arabian");
+       v.push_back ("Balinese");
+       v.push_back ("Gypsy");
+       v.push_back ("Mohammedan");
+       v.push_back ("Javanese");
+       v.push_back ("Persian");
+       v.push_back ("Algeria");
+
+       scale_menu->fill_column (0, v);
+
+       v.clear ();
+}
index 409311e0f5f3f1af448457a47d43837d82012d3e..1469bd523c3a6402ba22b1f5a882f2c18cb17487 100644 (file)
@@ -26,8 +26,11 @@ def build(bld):
             midi_byte_array.cc
             leds.cc
            gui.cc
+           layout.cc
            mode.cc
            menu.cc
+           mix.cc
+           scale.cc
     '''
     obj.export_includes = ['.']
     obj.defines      = [ 'PACKAGE="ardour_push2"' ]