push2: first somewhat operational versions of menus

This commit is contained in:
Paul Davis
2016-07-10 08:37:45 -04:00
parent b37531e04f
commit 86578ea0cc
6 changed files with 418 additions and 12 deletions

View File

@@ -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
View 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;
}

View 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__ */

View File

@@ -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);
}

View File

@@ -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 */

View File

@@ -27,6 +27,7 @@ def build(bld):
leds.cc
gui.cc
mode.cc
menu.cc
'''
obj.export_includes = ['.']
obj.defines = [ 'PACKAGE="ardour_push2"' ]