summaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
authorPaul Davis <paul@linuxaudiosystems.com>2016-07-10 08:37:45 -0400
committerPaul Davis <paul@linuxaudiosystems.com>2016-09-27 14:59:30 -0500
commit86578ea0cc1daef464953fdb80820d40a8167f78 (patch)
treeb1f626a0e11f5db70516c59cba9837664a0e953c /libs
parentb37531e04f1f8786ffcc8599759aa93411d42c1b (diff)
push2: first somewhat operational versions of menus
Diffstat (limited to 'libs')
-rw-r--r--libs/surfaces/push2/buttons.cc22
-rw-r--r--libs/surfaces/push2/menu.cc218
-rw-r--r--libs/surfaces/push2/menu.h66
-rw-r--r--libs/surfaces/push2/push2.cc105
-rw-r--r--libs/surfaces/push2/push2.h18
-rw-r--r--libs/surfaces/push2/wscript1
6 files changed, 418 insertions, 12 deletions
diff --git a/libs/surfaces/push2/buttons.cc b/libs/surfaces/push2/buttons.cc
index 6925517f68..8e916d126f 100644
--- a/libs/surfaces/push2/buttons.cc
+++ b/libs/surfaces/push2/buttons.cc
@@ -193,7 +193,7 @@ Push2::build_maps ()
MAKE_WHITE_BUTTON_PRESS (Left, 44, &Push2::button_left);
MAKE_WHITE_BUTTON_PRESS (Repeat, 56, &Push2::button_repeat);
MAKE_WHITE_BUTTON (Accent, 57);
- MAKE_WHITE_BUTTON (Scale, 58);
+ MAKE_WHITE_BUTTON_PRESS (Scale, 58, &Push2::button_scale_press);
MAKE_WHITE_BUTTON_PRESS (Layout, 31, &Push2::button_layout_press);
MAKE_WHITE_BUTTON (Note, 50);
MAKE_WHITE_BUTTON (Session, 51);
@@ -608,3 +608,23 @@ Push2::button_octave_up ()
build_pad_table ();
}
}
+
+void
+Push2::button_layout_press ()
+{
+ if (percussion) {
+ set_percussive_mode (false);
+ } else {
+ set_percussive_mode (true);
+ }
+}
+
+void
+Push2::button_scale_press ()
+{
+ if (current_menu != scale_menu) {
+ show_scale_menu ();
+ } else {
+ cancel_menu ();
+ }
+}
diff --git a/libs/surfaces/push2/menu.cc b/libs/surfaces/push2/menu.cc
new file mode 100644
index 0000000000..583c0a235c
--- /dev/null
+++ b/libs/surfaces/push2/menu.cc
@@ -0,0 +1,218 @@
+/*
+ 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 <cairomm/context.h>
+#include <cairomm/surface.h>
+#include <pangomm/layout.h>
+
+#include "push2.h"
+#include "gui.h"
+
+using namespace ARDOUR;
+using namespace std;
+using namespace PBD;
+using namespace Glib;
+using namespace ArdourSurface;
+
+#include "i18n.h"
+#include "menu.h"
+
+Push2Menu::Push2Menu (Cairo::RefPtr<Cairo::Context> context)
+ : _dirty (true)
+{
+ Pango::FontDescription fd2 ("Sans 10");
+
+ {
+ Glib::RefPtr<Pango::Layout> throwaway = Pango::Layout::create (context);
+ throwaway->set_font_description (fd2);
+ throwaway->set_text (X_("Hg")); /* ascender + descender) */
+ int h, w;
+ throwaway->get_pixel_size (w, h);
+ baseline = h;
+ nrows = Push2::rows / baseline;
+ }
+
+ for (int n = 0; n < 8; ++n) {
+ columns[n].layout = Pango::Layout::create (context);
+ columns[n].layout->set_font_description (fd2);
+ columns[n].top = -1;
+ columns[n].active = -1;
+ }
+}
+
+void
+Push2Menu::fill_column (int col, vector<string> v)
+{
+ if (col < 0 || col > 7) {
+ return;
+ }
+
+ columns[col].text = v;
+
+ if (v.empty()) {
+ columns[col].active = -1;
+ } else {
+ columns[col].active = 0;
+ }
+
+ set_text (col, 0);
+
+ _dirty = true;
+}
+
+void
+Push2Menu::set_text (int col, int top_row)
+{
+ if (top_row > (int) columns[col].text.size() - nrows || top_row < 0) {
+ return;
+ }
+
+ if (top_row == columns[col].top) {
+ return;
+ }
+
+ vector<string>::iterator s = columns[col].text.begin();
+ s += top_row;
+
+ string rows;
+
+ while (true) {
+ rows += *s;
+ ++s;
+ if (s != columns[col].text.end()) {
+ rows += '\n';
+ } else {
+ break;
+ }
+ }
+
+ columns[col].layout->set_text (rows);
+ columns[col].top = top_row;
+
+ _dirty = true;
+}
+
+void
+Push2Menu::scroll (int col, int dir)
+{
+ if (dir > 0) {
+ set_text (col, columns[col].top + 1);
+ } else {
+ set_text (col, columns[col].top - 1);
+ }
+}
+
+void
+Push2Menu::set_active (int col, int index)
+{
+ if (col < 0 || col > 7) {
+ return;
+ }
+
+ if (index < 0 || index > (int) columns[col].text.size()) {
+ return;
+ }
+
+ columns[col].active = index;
+
+ ActiveChanged (); /* emit signal */
+
+ _dirty = true;
+}
+
+void
+Push2Menu::step_active (int col, int dir)
+{
+ if (col < 0 || col > 7) {
+ return;
+ }
+
+ if (columns[col].text.empty()) {
+ return;
+ }
+
+
+ if (dir < 0) {
+ if (columns[col].active == -1) {
+ columns[col].active = 0;
+ } else {
+ columns[col].active = columns[col].active - 1;
+ if (columns[col].active < 0) {
+ columns[col].active = columns[col].text.size() - 1;
+ }
+ }
+ } else {
+ if (columns[col].active == -1) {
+ columns[col].active = 0;
+ } else {
+ columns[col].active = columns[col].active + 1;
+ if (columns[col].active >= (int) columns[col].text.size()) {
+ columns[col].active = 0;
+ }
+ }
+ }
+
+ if (columns[col].active < nrows/2) {
+ set_text (col, 0);
+ } else {
+ set_text (col, columns[col].active - (nrows/2) + 1);
+ }
+
+ _dirty = true;
+}
+
+int
+Push2Menu::get_active (int col)
+{
+ if (col < 0 || col > 7) {
+ return -1;
+ }
+
+ return columns[col].active;
+}
+
+void
+Push2Menu::redraw (Cairo::RefPtr<Cairo::Context> context) const
+{
+ for (int n = 0; n < 8; ++n) {
+
+ /* Active: move to column/now, draw background indicator
+ for active row.
+ */
+
+ const double x = 10.0 + (n * 120.0);
+ const double y = 2.0;
+
+ if (columns[n].active >= 0) {
+ int effective_row = columns[n].active - columns[n].top;
+ context->rectangle (x, y + (effective_row * baseline), 120.0, baseline);
+ context->set_source_rgb (1.0, 1.0, 1.0);
+ context->fill ();
+ }
+
+ /* now draw all the text, in one go */
+
+ context->move_to (x, y);
+ context->set_source_rgb (0.23, 0.0, 0.349);
+ columns[n].layout->update_from_cairo_context (context);
+ columns[n].layout->show_in_cairo_context (context);
+
+ }
+
+ _dirty = false;
+}
diff --git a/libs/surfaces/push2/menu.h b/libs/surfaces/push2/menu.h
new file mode 100644
index 0000000000..a76bba170d
--- /dev/null
+++ b/libs/surfaces/push2/menu.h
@@ -0,0 +1,66 @@
+/*
+ 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.
+*/
+
+#ifndef __ardour_push2_menu_h__
+#define __ardour_push2_menu_h__
+
+#include <cairomm/context.h>
+#include <cairomm/surface.h>
+#include <pangomm/layout.h>
+
+#include "pbd/signals.h"
+
+namespace ArdourSurface {
+
+class Push2Menu {
+ public:
+ Push2Menu (Cairo::RefPtr<Cairo::Context>);
+
+ void redraw (Cairo::RefPtr<Cairo::Context>) const;
+ bool dirty () const { return _dirty; }
+
+ void fill_column (int col, std::vector<std::string>);
+ void set_active (int col, int index);
+ void step_active (int col, int dir);
+ int get_active (int col);
+
+ PBD::Signal0<void> ActiveChanged;
+ PBD::Signal0<void> Selected;
+
+ private:
+ struct Column {
+ std::vector<std::string> text;
+ Glib::RefPtr<Pango::Layout> layout;
+ int top;
+ int active;
+ };
+
+ Column columns[8];
+
+ void scroll (int col, int dir);
+ void set_text (int col, int top);
+
+ int nrows;
+ double baseline;
+
+ mutable bool _dirty;
+};
+
+} // namespace
+
+#endif /* __ardour_push2_menu_h__ */
diff --git a/libs/surfaces/push2/push2.cc b/libs/surfaces/push2/push2.cc
index d4fe17bbd0..d2e4be3fde 100644
--- a/libs/surfaces/push2/push2.cc
+++ b/libs/surfaces/push2/push2.cc
@@ -44,6 +44,7 @@
#include "push2.h"
#include "gui.h"
+#include "menu.h"
using namespace ARDOUR;
using namespace std;
@@ -132,6 +133,8 @@ 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);
@@ -159,6 +162,7 @@ Push2::Push2 (ARDOUR::Session& s)
build_pad_table ();
build_maps ();
+ build_scale_menu ();
if (open ()) {
throw failed_constructor ();
@@ -340,7 +344,7 @@ Push2::init_buttons (bool startup)
ButtonID buttons[] = { Mute, Solo, Master, Up, Right, Left, Down, Note, Session, Mix, AddTrack, Delete, Undo,
Metronome, Shift, Select, Play, RecordEnable, Automate, Repeat, Note, Session, DoubleLoop,
- Quantize, Duplicate, Browse, PageRight, PageLeft, OctaveUp, OctaveDown, Layout
+ Quantize, Duplicate, Browse, PageRight, PageLeft, OctaveUp, OctaveDown, Layout, Scale
};
for (size_t n = 0; n < sizeof (buttons) / sizeof (buttons[0]); ++n) {
@@ -376,7 +380,7 @@ Push2::init_buttons (bool startup)
ButtonID off_buttons[] = { TapTempo, Setup, User, Stop, Convert, New, FixedLength,
Fwd32ndT, Fwd32nd, Fwd16thT, Fwd16th, Fwd8thT, Fwd8th, Fwd4trT, Fwd4tr,
- Accent, Scale, Note, Session, };
+ Accent, Note, Session, };
for (size_t n = 0; n < sizeof (off_buttons) / sizeof (off_buttons[0]); ++n) {
Button* b = id_button_map[off_buttons[n]];
@@ -516,6 +520,22 @@ Push2::redraw ()
}
}
+ 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;
@@ -1492,6 +1512,11 @@ Push2::mute_change (int n)
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) {
@@ -1503,6 +1528,10 @@ Push2::strip_vpot (int n, int delta)
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) {
@@ -1977,11 +2006,71 @@ Push2::set_percussive_mode (bool yn)
}
void
-Push2::button_layout_press ()
+Push2::set_menu (Push2Menu* m)
{
- if (percussion) {
- set_percussive_mode (false);
- } else {
- set_percussive_mode (true);
- }
+ current_menu = m;
+ drawn_menu = 0;
+}
+
+void
+Push2::build_scale_menu ()
+{
+ 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 ();
+}
+
+void
+Push2::show_scale_menu ()
+{
+ set_menu (scale_menu);
+}
+
+void
+Push2::cancel_menu ()
+{
+ set_menu (0);
}
diff --git a/libs/surfaces/push2/push2.h b/libs/surfaces/push2/push2.h
index d1655d1d60..4ced88b67f 100644
--- a/libs/surfaces/push2/push2.h
+++ b/libs/surfaces/push2/push2.h
@@ -71,6 +71,7 @@ public:
};
class P2GUI;
+class Push2Menu;
class Push2 : public ARDOUR::ControlProtocol
, public AbstractUI<Push2Request>
@@ -107,6 +108,9 @@ class Push2 : public ARDOUR::ControlProtocol
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];
@@ -124,8 +128,6 @@ class Push2 : public ARDOUR::ControlProtocol
ModifierState modifier_state;
- static const int cols;
- static const int rows;
static const int pixels_per_row;
void do_request (Push2Request*);
@@ -460,6 +462,7 @@ class Push2 : public ARDOUR::ControlProtocol
void button_octave_up ();
void button_octave_down ();
void button_layout_press ();
+ void button_scale_press ();
void start_shift ();
void end_shift ();
@@ -536,8 +539,17 @@ class Push2 : public ARDOUR::ControlProtocol
bool percussion;
void set_percussive_mode (bool);
-};
+ /* menus */
+ Push2Menu* current_menu;
+ Push2Menu* drawn_menu;
+ Push2Menu* scale_menu;
+
+ void build_scale_menu ();
+ void set_menu (Push2Menu*);
+ void show_scale_menu ();
+ void cancel_menu ();
+};
} /* namespace */
diff --git a/libs/surfaces/push2/wscript b/libs/surfaces/push2/wscript
index b69d4a22ce..409311e0f5 100644
--- a/libs/surfaces/push2/wscript
+++ b/libs/surfaces/push2/wscript
@@ -27,6 +27,7 @@ def build(bld):
leds.cc
gui.cc
mode.cc
+ menu.cc
'''
obj.export_includes = ['.']
obj.defines = [ 'PACKAGE="ardour_push2"' ]