push2: first somewhat operational versions of menus
This commit is contained in:
@@ -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 ();
|
||||
}
|
||||
}
|
||||
|
||||
218
libs/surfaces/push2/menu.cc
Normal file
218
libs/surfaces/push2/menu.cc
Normal file
@@ -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;
|
||||
}
|
||||
66
libs/surfaces/push2/menu.h
Normal file
66
libs/surfaces/push2/menu.h
Normal file
@@ -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__ */
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ def build(bld):
|
||||
leds.cc
|
||||
gui.cc
|
||||
mode.cc
|
||||
menu.cc
|
||||
'''
|
||||
obj.export_includes = ['.']
|
||||
obj.defines = [ 'PACKAGE="ardour_push2"' ]
|
||||
|
||||
Reference in New Issue
Block a user