Bsongis/move everything (#3669)
* Directories moved * Directories moved * Directories moved * Directories moved * Directories moved * rename horus directories in gui to 480x272 * Directories moved * Rename taranis to 212x64 * Directories moved - TARANIS target removed, it's now X9E / X9D+ / X9D * Directories moved - TARANIS target removed, it's now X9E / X9D+ / X9D * Directories moved - TARANIS target removed, it's now X9E / X9D+ / X9D * Directories moved - TARANIS target removed, it's now X9E / X9D+ / X9D * Directories moved - TARANIS target removed, it's now X9E / X9D+ / X9D * Directories moved - TARANIS target removed, it's now X9E / X9D+ / X9D * Directories moved - TARANIS target removed, it's now X9E / X9D+ / X9D * Fix x9e compile * Directories moved - TARANIS target removed, it's now X9E / X9D+ / X9D * Directories moved - TARANIS target removed, it's now X9E / X9D+ / X9D * Directories moved - TARANIS target removed, it's now X9E / X9D+ / X9D * Directories moved - TARANIS target removed, it's now X9E / X9D+ / X9D * Directories moved - TARANIS target removed, it's now X9E / X9D+ / X9D * Minor improvement of path handling * Another bunch of files renamed :) * Another bunch of files renamed :) * Another bunch of files renamed :) * Another bunch of files renamed :) * Another bunch of files renamed :) * Rename 9x to 128x64 * Compilation fixes * Compilation fixes * Compilation fixes * Compilation fixes * Compilation fixes * Compilation fixes
This commit is contained in:
@@ -0,0 +1,826 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include "opentx.h"
|
||||
|
||||
void BitmapBuffer::drawAlphaPixel(display_t * p, uint8_t opacity, uint16_t color)
|
||||
{
|
||||
if (opacity == OPACITY_MAX) {
|
||||
drawPixel(p, color);
|
||||
}
|
||||
else if (opacity != 0) {
|
||||
uint8_t bgWeight = OPACITY_MAX - opacity;
|
||||
RGB_SPLIT(color, red, green, blue);
|
||||
RGB_SPLIT(*p, bgRed, bgGreen, bgBlue);
|
||||
uint16_t r = (bgRed * bgWeight + red * opacity) / OPACITY_MAX;
|
||||
uint16_t g = (bgGreen * bgWeight + green * opacity) / OPACITY_MAX;
|
||||
uint16_t b = (bgBlue * bgWeight + blue * opacity) / OPACITY_MAX;
|
||||
drawPixel(p, RGB_JOIN(r, g, b));
|
||||
}
|
||||
}
|
||||
|
||||
void BitmapBuffer::drawHorizontalLine(coord_t x, coord_t y, coord_t w, uint8_t pat, LcdFlags att)
|
||||
{
|
||||
if (y >= height) return;
|
||||
if (x+w > width) { w = width - x; }
|
||||
|
||||
display_t * p = getPixelPtr(x, y);
|
||||
display_t color = lcdColorTable[COLOR_IDX(att)];
|
||||
uint8_t opacity = 0x0F - (att >> 24);
|
||||
|
||||
if (pat == SOLID) {
|
||||
while (w--) {
|
||||
drawAlphaPixel(p, opacity, color);
|
||||
p++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
while (w--) {
|
||||
if (pat & 1) {
|
||||
drawAlphaPixel(p, opacity, color);
|
||||
pat = (pat >> 1) | 0x80;
|
||||
}
|
||||
else {
|
||||
pat = pat >> 1;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BitmapBuffer::drawVerticalLine(coord_t x, coord_t y, coord_t h, uint8_t pat, LcdFlags att)
|
||||
{
|
||||
if (x >= width) return;
|
||||
if (y >= height) return;
|
||||
if (h<0) { y+=h; h=-h; }
|
||||
if (y<0) { h+=y; y=0; if (h<=0) return; }
|
||||
if (y+h > height) { h = height - y; }
|
||||
|
||||
display_t color = lcdColorTable[COLOR_IDX(att)];
|
||||
uint8_t opacity = 0x0F - (att >> 24);
|
||||
|
||||
if (pat == SOLID) {
|
||||
while (h--) {
|
||||
drawAlphaPixel(x, y, opacity, color);
|
||||
y++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (pat==DOTTED && !(y%2)) {
|
||||
pat = ~pat;
|
||||
}
|
||||
while (h--) {
|
||||
if (pat & 1) {
|
||||
drawAlphaPixel(x, y, opacity, color);
|
||||
pat = (pat >> 1) | 0x80;
|
||||
}
|
||||
else {
|
||||
pat = pat >> 1;
|
||||
}
|
||||
y++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BitmapBuffer::drawRect(coord_t x, coord_t y, coord_t w, coord_t h, uint8_t thickness, uint8_t pat, LcdFlags att)
|
||||
{
|
||||
for (int i=0; i<thickness; i++) {
|
||||
drawVerticalLine(x+i, y, h, pat, att);
|
||||
drawVerticalLine(x+w-1-i, y, h, pat, att);
|
||||
drawHorizontalLine(x, y+h-1-i, w, pat, att);
|
||||
drawHorizontalLine(x, y+i, w, pat, att);
|
||||
}
|
||||
}
|
||||
|
||||
void BitmapBuffer::drawFilledRect(coord_t x, coord_t y, coord_t w, coord_t h, uint8_t pat, LcdFlags att)
|
||||
{
|
||||
for (coord_t i=y; i<y+h; i++) {
|
||||
if ((att & ROUND) && (i==y || i==y+h-1))
|
||||
drawHorizontalLine(x+1, i, w-2, pat, att);
|
||||
else
|
||||
drawHorizontalLine(x, i, w, pat, att);
|
||||
}
|
||||
}
|
||||
|
||||
void BitmapBuffer::invertRect(coord_t x, coord_t y, coord_t w, coord_t h, LcdFlags att)
|
||||
{
|
||||
display_t color = lcdColorTable[COLOR_IDX(att)];
|
||||
RGB_SPLIT(color, red, green, blue);
|
||||
|
||||
for (int i=y; i<y+h; i++) {
|
||||
display_t * p = getPixelPtr(x, i);
|
||||
for (int j=0; j<w; j++) {
|
||||
// TODO ASSERT_IN_DISPLAY(p);
|
||||
RGB_SPLIT(*p, bgRed, bgGreen, bgBlue);
|
||||
drawPixel(p++, RGB_JOIN(0x1F + red - bgRed, 0x3F + green - bgGreen, 0x1F + blue - bgBlue));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
void BitmapBuffer::drawCircle(int x0, int y0, int radius)
|
||||
{
|
||||
int x = radius;
|
||||
int y = 0;
|
||||
int decisionOver2 = 1 - x;
|
||||
|
||||
while (y <= x) {
|
||||
drawPixel(x+x0, y+y0, WHITE);
|
||||
drawPixel(y+x0, x+y0, WHITE);
|
||||
drawPixel(-x+x0, y+y0, WHITE);
|
||||
drawPixel(-y+x0, x+y0, WHITE);
|
||||
drawPixel(-x+x0, -y+y0, WHITE);
|
||||
drawPixel(-y+x0, -x+y0, WHITE);
|
||||
drawPixel(x+x0, -y+y0, WHITE);
|
||||
drawPixel(y+x0, -x+y0, WHITE);
|
||||
y++;
|
||||
if (decisionOver2 <= 0) {
|
||||
decisionOver2 += 2*y + 1;
|
||||
}
|
||||
else {
|
||||
x--;
|
||||
decisionOver2 += 2 * (y-x) + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#define PI 3.14159265
|
||||
|
||||
bool evalSlopes(int * slopes, int startAngle, int endAngle)
|
||||
{
|
||||
if (startAngle >= 360 || endAngle <= 0)
|
||||
return false;
|
||||
|
||||
if (startAngle == 0) {
|
||||
slopes[1] = 100000;
|
||||
slopes[2] = -100000;
|
||||
}
|
||||
else {
|
||||
float angle1 = float(startAngle) * PI / 180;
|
||||
if (startAngle >= 180) {
|
||||
slopes[1] = -100000;
|
||||
slopes[2] = cos(angle1) * 100 / sin(angle1);
|
||||
}
|
||||
else {
|
||||
slopes[1] = cos(angle1) * 100 / sin(angle1);
|
||||
slopes[2] = -100000;
|
||||
}
|
||||
}
|
||||
|
||||
if (endAngle == 360) {
|
||||
slopes[0] = -100000;
|
||||
slopes[3] = 100000;
|
||||
}
|
||||
else {
|
||||
float angle2 = float(endAngle) * PI / 180;
|
||||
if (endAngle >= 180) {
|
||||
slopes[0] = -100000;
|
||||
slopes[3] = -cos(angle2) * 100 / sin(angle2);
|
||||
}
|
||||
else {
|
||||
slopes[0] = cos(angle2) * 100 / sin(angle2);
|
||||
slopes[3] = -100000;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void BitmapBuffer::drawPie(int x0, int y0, int radius, int startAngle, int endAngle)
|
||||
{
|
||||
int slopes[4];
|
||||
if (!evalSlopes(slopes, startAngle, endAngle))
|
||||
return;
|
||||
|
||||
for (int y=0; y<=radius; y++) {
|
||||
for (int x=0; x<=radius; x++) {
|
||||
if (x*x+y*y <= radius*radius) {
|
||||
int slope = (x==0 ? (y<0 ? -99000 : 99000) : y*100/x);
|
||||
if (slope >= slopes[0] && slope < slopes[1]) {
|
||||
drawPixel(x0+x, y0-y, WHITE);
|
||||
}
|
||||
if (-slope >= slopes[0] && -slope < slopes[1]) {
|
||||
drawPixel(x0+x, y0+y, WHITE);
|
||||
}
|
||||
if (slope >= slopes[2] && slope < slopes[3]) {
|
||||
drawPixel(x0-x, y0-y, WHITE);
|
||||
}
|
||||
if (-slope >= slopes[2] && -slope < slopes[3]) {
|
||||
drawPixel(x0-x, y0+y, WHITE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BitmapBuffer::drawMask(coord_t x, coord_t y, BitmapBuffer * mask, LcdFlags flags, coord_t offset, coord_t width)
|
||||
{
|
||||
if (mask == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
coord_t w = mask->getWidth();
|
||||
coord_t height = mask->getHeight();
|
||||
|
||||
if (!width || width > w) {
|
||||
width = w;
|
||||
}
|
||||
|
||||
if (x+width > this->width) {
|
||||
width = this->width-x;
|
||||
}
|
||||
|
||||
display_t color = lcdColorTable[COLOR_IDX(flags)];
|
||||
|
||||
for (coord_t row=0; row<height; row++) {
|
||||
display_t * p = getPixelPtr(x, y+row);
|
||||
display_t * q = mask->getPixelPtr(offset, row);
|
||||
for (coord_t col=0; col<width; col++) {
|
||||
drawAlphaPixel(p, *((uint8_t *)q), color);
|
||||
p++; q++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BitmapBuffer::drawBitmapPattern(coord_t x, coord_t y, const uint8_t * bmp, LcdFlags flags, coord_t offset, coord_t width)
|
||||
{
|
||||
coord_t w = *((uint16_t *)bmp);
|
||||
coord_t height = *(((uint16_t *)bmp)+1);
|
||||
|
||||
if (!width || width > w) {
|
||||
width = w;
|
||||
}
|
||||
|
||||
if (x+width > this->width) {
|
||||
width = this->width-x;
|
||||
}
|
||||
|
||||
display_t color = lcdColorTable[COLOR_IDX(flags)];
|
||||
|
||||
for (coord_t row=0; row<height; row++) {
|
||||
const uint8_t * q = bmp + 4 + row*w + offset;
|
||||
for (coord_t col=0; col<width; col++) {
|
||||
display_t * p;
|
||||
if (flags & VERTICAL)
|
||||
p = getPixelPtr(x+row, y-col);
|
||||
else
|
||||
p = getPixelPtr(x+col, y+row);
|
||||
drawAlphaPixel(p, *q, color);
|
||||
q++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t BitmapBuffer::drawCharWithoutCache(coord_t x, coord_t y, const uint8_t * font, const uint16_t * spec, int index, LcdFlags flags)
|
||||
{
|
||||
coord_t offset = spec[index];
|
||||
coord_t width = spec[index+1] - offset;
|
||||
if (width > 0) drawBitmapPattern(x, y, font, flags, offset, width);
|
||||
return width;
|
||||
}
|
||||
|
||||
uint8_t BitmapBuffer::drawCharWithCache(coord_t x, coord_t y, const BitmapBuffer * font, const uint16_t * spec, int index, LcdFlags flags)
|
||||
{
|
||||
coord_t offset = spec[index];
|
||||
coord_t width = spec[index+1] - offset;
|
||||
drawBitmap(x, y, font, offset, 0, width);
|
||||
return width;
|
||||
}
|
||||
|
||||
void BitmapBuffer::drawSizedText(coord_t x, coord_t y, const char * s, uint8_t len, LcdFlags flags)
|
||||
{
|
||||
#define INCREMENT_POS(delta) \
|
||||
do { if (flags & VERTICAL) y -= delta; else x += delta; } while(0)
|
||||
|
||||
int width = getTextWidth(s, len, flags);
|
||||
int height = getFontHeight(flags);
|
||||
uint32_t fontindex = FONTINDEX(flags);
|
||||
const pm_uchar * font = fontsTable[fontindex];
|
||||
const uint16_t * fontspecs = fontspecsTable[fontindex];
|
||||
BitmapBuffer * fontcache = NULL;
|
||||
|
||||
if (flags & RIGHT)
|
||||
INCREMENT_POS(-width);
|
||||
else if (flags & CENTERED)
|
||||
INCREMENT_POS(-width/2);
|
||||
|
||||
coord_t & pos = (flags & VERTICAL) ? y : x;
|
||||
|
||||
if ((flags & INVERS) && ((~flags & BLINK) || BLINK_ON_PHASE)) {
|
||||
uint16_t fgColor = lcdColorTable[COLOR_IDX(flags)];
|
||||
if (fgColor == lcdColorTable[TEXT_COLOR_INDEX]) {
|
||||
flags = TEXT_INVERTED_COLOR | (flags & 0x0ffff);
|
||||
}
|
||||
if (fontindex == STDSIZE_INDEX) {
|
||||
if (fgColor == lcdColorTable[TEXT_COLOR_INDEX]) {
|
||||
drawSolidFilledRect(x-INVERT_HORZ_MARGIN, y, INVERT_HORZ_MARGIN-1, INVERT_LINE_HEIGHT, TEXT_INVERTED_BGCOLOR);
|
||||
drawSolidFilledRect(x+width-1, y, INVERT_HORZ_MARGIN, INVERT_LINE_HEIGHT, TEXT_INVERTED_BGCOLOR);
|
||||
fontcache = fontCache[1];
|
||||
}
|
||||
else {
|
||||
drawSolidFilledRect(x-INVERT_HORZ_MARGIN, y, width+2*INVERT_HORZ_MARGIN-1, INVERT_LINE_HEIGHT, TEXT_INVERTED_BGCOLOR);
|
||||
}
|
||||
}
|
||||
else if (fontindex == TINSIZE_INDEX) {
|
||||
drawSolidFilledRect(x-INVERT_HORZ_MARGIN+2, y-INVERT_VERT_MARGIN+2, width+2*INVERT_HORZ_MARGIN-5, INVERT_LINE_HEIGHT-7, TEXT_INVERTED_BGCOLOR);
|
||||
}
|
||||
else if (fontindex == SMLSIZE_INDEX) {
|
||||
drawSolidFilledRect(x-INVERT_HORZ_MARGIN, y+1, width+2*INVERT_HORZ_MARGIN-2, INVERT_LINE_HEIGHT-5, TEXT_INVERTED_BGCOLOR);
|
||||
}
|
||||
else {
|
||||
drawSolidFilledRect(x-INVERT_HORZ_MARGIN, y, width+2*INVERT_HORZ_MARGIN, INVERT_LINE_HEIGHT, TEXT_INVERTED_BGCOLOR);
|
||||
}
|
||||
}
|
||||
else if (!(flags & NO_FONTCACHE)) {
|
||||
if (fontindex == STDSIZE_INDEX) {
|
||||
uint16_t fgColor = lcdColorTable[COLOR_IDX(flags)];
|
||||
uint16_t bgColor = *getPixelPtr(x, y);
|
||||
if (fgColor == lcdColorTable[TEXT_COLOR_INDEX] && bgColor == lcdColorTable[TEXT_BGCOLOR_INDEX]) {
|
||||
fontcache = fontCache[0];
|
||||
}
|
||||
else if (fgColor == lcdColorTable[TEXT_INVERTED_COLOR_INDEX] && bgColor == lcdColorTable[TEXT_INVERTED_BGCOLOR_INDEX]) {
|
||||
fontcache = fontCache[1];
|
||||
}
|
||||
else {
|
||||
// TRACE("No cache for \"%s\"", s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool setpos = false;
|
||||
const coord_t orig_pos = pos;
|
||||
while (len--) {
|
||||
unsigned char c;
|
||||
if (flags & ZCHAR)
|
||||
c = idx2char(*s);
|
||||
else
|
||||
c = pgm_read_byte(s);
|
||||
if (setpos) {
|
||||
pos = c;
|
||||
setpos = false;
|
||||
}
|
||||
else if (!c) {
|
||||
break;
|
||||
}
|
||||
else if (c >= 0x20) {
|
||||
#if defined(TRANSLATIONS_FR)
|
||||
if (c >= 0x80 && c <= 0x85) {
|
||||
c = 0x20 + 115 + c - 0x80;
|
||||
}
|
||||
#elif defined(TRANSLATIONS_DE)
|
||||
if (c >= 0x80 && c <= 0x86) {
|
||||
c = 0x20 + 120 + c - 0x80;
|
||||
}
|
||||
#endif
|
||||
uint8_t width;
|
||||
if (fontcache)
|
||||
width = drawCharWithCache(x-1, y, fontcache, fontspecs, getMappedChar(c), flags);
|
||||
else
|
||||
width = drawCharWithoutCache(x-1, y, font, fontspecs, getMappedChar(c), flags);
|
||||
INCREMENT_POS(width);
|
||||
}
|
||||
else if (c == 0x1F) { // X-coord prefix
|
||||
setpos = true;
|
||||
}
|
||||
else if (c == 0x1E) {
|
||||
pos = orig_pos;
|
||||
if (flags & VERTICAL)
|
||||
x += height;
|
||||
else
|
||||
y += height;
|
||||
}
|
||||
else if (c == 1) {
|
||||
INCREMENT_POS(1);
|
||||
}
|
||||
else {
|
||||
INCREMENT_POS(2*(c-1));
|
||||
}
|
||||
s++;
|
||||
}
|
||||
lcdNextPos = pos;
|
||||
}
|
||||
|
||||
void BitmapBuffer::drawBitmapPie(int x0, int y0, const uint16_t * img, int startAngle, int endAngle)
|
||||
{
|
||||
const uint16_t * q = img;
|
||||
coord_t width = *q++;
|
||||
coord_t height = *q++;
|
||||
|
||||
int slopes[4];
|
||||
if (!evalSlopes(slopes, startAngle, endAngle))
|
||||
return;
|
||||
|
||||
int w2 = width/2;
|
||||
int h2 = height/2;
|
||||
|
||||
for (int y=h2-1; y>=0; y--) {
|
||||
for (int x=w2-1; x>=0; x--) {
|
||||
int slope = (x==0 ? (y<0 ? -99000 : 99000) : y*100/x);
|
||||
if (slope >= slopes[0] && slope < slopes[1]) {
|
||||
*getPixelPtr(x0+w2+x, y0+h2-y) = q[(h2-y)*width + w2+x];
|
||||
}
|
||||
if (-slope >= slopes[0] && -slope < slopes[1]) {
|
||||
*getPixelPtr(x0+w2+x, y0+h2+y) = q[(h2+y)*width + w2+x];
|
||||
}
|
||||
if (slope >= slopes[2] && slope < slopes[3]) {
|
||||
*getPixelPtr(x0+w2-x, y0+h2-y) = q[(h2-y)*width + w2-x];
|
||||
}
|
||||
if (-slope >= slopes[2] && -slope < slopes[3]) {
|
||||
*getPixelPtr(x0+w2-x, y0+h2+y) = q[(h2+y)*width + w2-x];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BitmapBuffer::drawBitmapPatternPie(coord_t x0, coord_t y0, const uint8_t * img, LcdFlags flags, int startAngle, int endAngle)
|
||||
{
|
||||
coord_t width = *((uint16_t *)img);
|
||||
coord_t height = *(((uint16_t *)img)+1);
|
||||
const uint8_t * q = img+4;
|
||||
|
||||
int slopes[4];
|
||||
if (!evalSlopes(slopes, startAngle, endAngle))
|
||||
return;
|
||||
|
||||
display_t color = lcdColorTable[COLOR_IDX(flags)];
|
||||
|
||||
int w2 = width/2;
|
||||
int h2 = height/2;
|
||||
|
||||
for (int y=h2-1; y>=0; y--) {
|
||||
for (int x=w2-1; x>=0; x--) {
|
||||
int slope = (x==0 ? (y<0 ? -99000 : 99000) : y*100/x);
|
||||
if (slope >= slopes[0] && slope < slopes[1]) {
|
||||
drawAlphaPixel(x0+w2+x, y0+h2-y, q[(h2-y)*width + w2+x], color);
|
||||
}
|
||||
if (-slope >= slopes[0] && -slope < slopes[1]) {
|
||||
drawAlphaPixel(x0+w2+x, y0+h2+y, q[(h2+y)*width + w2+x], color);
|
||||
}
|
||||
if (slope >= slopes[2] && slope < slopes[3]) {
|
||||
drawAlphaPixel(x0+w2-x, y0+h2-y, q[(h2-y)*width + w2-x], color);
|
||||
}
|
||||
if (-slope >= slopes[2] && -slope < slopes[3]) {
|
||||
drawAlphaPixel(x0+w2-x, y0+h2+y, q[(h2+y)*width + w2-x], color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BitmapBuffer * BitmapBuffer::load(const char * filename)
|
||||
{
|
||||
const char * ext = getFileExtension(filename);
|
||||
if (ext && !strcmp(ext, ".bmp"))
|
||||
return load_bmp(filename);
|
||||
else
|
||||
return load_stb(filename);
|
||||
}
|
||||
|
||||
BitmapBuffer * BitmapBuffer::loadMask(const char * filename)
|
||||
{
|
||||
BitmapBuffer * bitmap = BitmapBuffer::load(filename);
|
||||
if (bitmap) {
|
||||
display_t * p = bitmap->getData();
|
||||
for (int i = bitmap->getWidth() * bitmap->getHeight(); i > 0; i--) {
|
||||
*((uint8_t *)p) = OPACITY_MAX - ((*p) >> 12);
|
||||
p++;
|
||||
}
|
||||
}
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
BitmapBuffer * BitmapBuffer::loadMaskOnBackground(const char * filename, LcdFlags foreground, LcdFlags background)
|
||||
{
|
||||
BitmapBuffer * result = NULL;
|
||||
BitmapBuffer * mask = BitmapBuffer::loadMask(getThemePath(filename));
|
||||
if (mask) {
|
||||
result = new BitmapBuffer(BMP_RGB565, mask->getWidth(), mask->getHeight());
|
||||
result->clear(background);
|
||||
result->drawMask(0, 0, mask, foreground);
|
||||
delete mask;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
FIL imgFile __DMA;
|
||||
|
||||
BitmapBuffer * BitmapBuffer::load_bmp(const char * filename)
|
||||
{
|
||||
UINT read;
|
||||
uint8_t palette[16];
|
||||
uint8_t bmpBuf[LCD_W]; /* maximum with LCD_W */
|
||||
uint8_t * buf = &bmpBuf[0];
|
||||
|
||||
FRESULT result = f_open(&imgFile, filename, FA_OPEN_EXISTING | FA_READ);
|
||||
if (result != FR_OK) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (f_size(&imgFile) < 14) {
|
||||
f_close(&imgFile);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
result = f_read(&imgFile, buf, 14, &read);
|
||||
if (result != FR_OK || read != 14) {
|
||||
f_close(&imgFile);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (buf[0] != 'B' || buf[1] != 'M') {
|
||||
f_close(&imgFile);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint32_t fsize = *((uint32_t *)&buf[2]);
|
||||
uint32_t hsize = *((uint32_t *)&buf[10]); /* header size */
|
||||
|
||||
uint32_t len = limit((uint32_t)4, (uint32_t)(hsize-14), (uint32_t)32);
|
||||
result = f_read(&imgFile, buf, len, &read);
|
||||
if (result != FR_OK || read != len) {
|
||||
f_close(&imgFile);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint32_t ihsize = *((uint32_t *)&buf[0]); /* more header size */
|
||||
|
||||
/* invalid header size */
|
||||
if (ihsize + 14 > hsize) {
|
||||
f_close(&imgFile);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* sometimes file size is set to some headers size, set a real size in that case */
|
||||
if (fsize == 14 || fsize == ihsize + 14)
|
||||
fsize = f_size(&imgFile) - 2;
|
||||
|
||||
/* declared file size less than header size */
|
||||
if (fsize <= hsize) {
|
||||
f_close(&imgFile);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint32_t w, h;
|
||||
|
||||
switch (ihsize){
|
||||
case 40: // windib
|
||||
case 56: // windib v3
|
||||
case 64: // OS/2 v2
|
||||
case 108: // windib v4
|
||||
case 124: // windib v5
|
||||
w = *((uint32_t *)&buf[4]);
|
||||
h = *((uint32_t *)&buf[8]);
|
||||
buf += 12;
|
||||
break;
|
||||
case 12: // OS/2 v1
|
||||
w = *((uint16_t *)&buf[4]);
|
||||
h = *((uint16_t *)&buf[6]);
|
||||
buf += 8;
|
||||
break;
|
||||
default:
|
||||
f_close(&imgFile);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (*((uint16_t *)&buf[0]) != 1) { /* planes */
|
||||
f_close(&imgFile);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint16_t depth = *((uint16_t *)&buf[2]);
|
||||
|
||||
buf = &bmpBuf[0];
|
||||
|
||||
if (depth == 4) {
|
||||
if (f_lseek(&imgFile, hsize-64) != FR_OK || f_read(&imgFile, buf, 64, &read) != FR_OK || read != 64) {
|
||||
f_close(&imgFile);
|
||||
return NULL;
|
||||
}
|
||||
for (uint8_t i=0; i<16; i++) {
|
||||
palette[i] = buf[4*i];
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (f_lseek(&imgFile, hsize) != FR_OK) {
|
||||
f_close(&imgFile);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
BitmapBuffer * bmp = new BitmapBuffer(BMP_RGB565, w, h);
|
||||
if (bmp == NULL) {
|
||||
f_close(&imgFile);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint16_t * dest = bmp->getData();
|
||||
uint32_t rowSize;
|
||||
bool hasAlpha = false;
|
||||
|
||||
switch (depth) {
|
||||
case 32:
|
||||
for (int i=h-1; i>=0; i--) {
|
||||
uint8_t * dst = ((uint8_t *)dest) + i*w*2;
|
||||
for (unsigned int j=0; j<w; j++) {
|
||||
uint32_t pixel;
|
||||
result = f_read(&imgFile, (uint8_t *)&pixel, 4, &read);
|
||||
if (result != FR_OK || read != 4) {
|
||||
f_close(&imgFile);
|
||||
delete bmp;
|
||||
return NULL;
|
||||
}
|
||||
if (hasAlpha) {
|
||||
*((uint16_t *)dst) = ARGB(pixel & 0xff, (pixel >> 24) & 0xff, (pixel >> 16) & 0xff, (pixel >> 8) & 0xff);
|
||||
}
|
||||
else {
|
||||
if ((pixel & 0xff) == 0xff) {
|
||||
*((uint16_t *)dst) = RGB(pixel >> 24, (pixel >> 16) & 0xff, (pixel >> 8) & 0xff);
|
||||
}
|
||||
else {
|
||||
hasAlpha = true;
|
||||
bmp->setFormat(BMP_ARGB4444);
|
||||
for (uint16_t * p = dest + i*w; p<dest + h*w; p++) {
|
||||
uint16_t tmp = *p;
|
||||
*p = ((tmp >> 1) & 0x0f) + (((tmp >> 7) & 0x0f) << 4) + (((tmp >> 12) & 0x0f) << 8);
|
||||
}
|
||||
*((uint16_t *)dst) = ARGB(pixel & 0xff, (pixel >> 24) & 0xff, (pixel >> 16) & 0xff, (pixel >> 8) & 0xff);
|
||||
}
|
||||
}
|
||||
dst += 2;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
break;
|
||||
|
||||
case 4:
|
||||
rowSize = ((4*w+31)/32)*4;
|
||||
for (int32_t i=h-1; i>=0; i--) {
|
||||
result = f_read(&imgFile, buf, rowSize, &read);
|
||||
if (result != FR_OK || read != rowSize) {
|
||||
f_close(&imgFile);
|
||||
delete bmp;
|
||||
return NULL;
|
||||
}
|
||||
uint8_t * dst = ((uint8_t *)dest) + i*w*2;
|
||||
for (uint32_t j=0; j<w; j++) {
|
||||
uint8_t index = (buf[j/2] >> ((j & 1) ? 0 : 4)) & 0x0F;
|
||||
uint8_t val = palette[index];
|
||||
*((uint16_t *)dst) = RGB(val, val, val);
|
||||
dst += 2;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
f_close(&imgFile);
|
||||
delete bmp;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
f_close(&imgFile);
|
||||
return bmp;
|
||||
}
|
||||
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#define STBI_ONLY_PNG
|
||||
#define STBI_ONLY_JPEG
|
||||
#define STBI_ONLY_BMP
|
||||
#define STBI_ONLY_GIF
|
||||
#define STBI_NO_STDIO
|
||||
|
||||
// #define TRACE_STB_MALLOC
|
||||
|
||||
#if defined(TRACE_STB_MALLOC)
|
||||
#define STBI_MALLOC(sz) stb_malloc(sz)
|
||||
#define STBI_REALLOC_SIZED(p,oldsz,newsz) stb_realloc(p,oldsz,newsz)
|
||||
#define STBI_FREE(p) stb_free(p)
|
||||
|
||||
void * stb_malloc(unsigned int size)
|
||||
{
|
||||
void * res = malloc(size);
|
||||
TRACE("malloc %d = %p", size, res);
|
||||
return res;
|
||||
}
|
||||
|
||||
void stb_free(void *ptr)
|
||||
{
|
||||
TRACE("free %p", ptr);
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
void *stb_realloc(void *ptr, unsigned int oldsz, unsigned int newsz)
|
||||
{
|
||||
void * res = realloc(ptr, newsz);
|
||||
TRACE("realloc %p, %d -> %d = %p", ptr, oldsz, newsz, res);
|
||||
return res;
|
||||
}
|
||||
#endif // #if defined(TRACE_STB_MALLOC)
|
||||
|
||||
|
||||
#include "thirdparty/Stb/stb_image.h"
|
||||
|
||||
// fill 'data' with 'size' bytes. return number of bytes actually read
|
||||
int stbc_read(void *user, char *data, int size)
|
||||
{
|
||||
FIL * fp = (FIL *)user;
|
||||
UINT br = 0;
|
||||
FRESULT res = f_read(fp, data, size, &br);
|
||||
if (res == FR_OK) {
|
||||
return (int)br;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// skip the next 'n' bytes, or 'unget' the last -n bytes if negative
|
||||
void stbc_skip(void *user, int n)
|
||||
{
|
||||
FIL * fp = (FIL *)user;
|
||||
f_lseek(fp, f_tell(fp) + n);
|
||||
}
|
||||
|
||||
// returns nonzero if we are at end of file/data
|
||||
int stbc_eof(void *user)
|
||||
{
|
||||
FIL * fp = (FIL *)user;
|
||||
int res = f_eof(fp);
|
||||
return res;
|
||||
}
|
||||
|
||||
// callbacks for stb-image
|
||||
const stbi_io_callbacks stbCallbacks = {
|
||||
stbc_read,
|
||||
stbc_skip,
|
||||
stbc_eof
|
||||
};
|
||||
|
||||
BitmapBuffer * BitmapBuffer::load_stb(const char * filename)
|
||||
{
|
||||
FRESULT result = f_open(&imgFile, filename, FA_OPEN_EXISTING | FA_READ);
|
||||
if (result != FR_OK) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int w, h, n;
|
||||
unsigned char * img = stbi_load_from_callbacks(&stbCallbacks, &imgFile, &w, &h, &n, 4);
|
||||
f_close(&imgFile);
|
||||
|
||||
if (!img) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// convert to RGB565 or ARGB4444 format
|
||||
BitmapBuffer * bmp = new BitmapBuffer(n == 4 ? BMP_ARGB4444 : BMP_RGB565, w, h);
|
||||
if (bmp == NULL) {
|
||||
TRACE("load_stb() malloc failed");
|
||||
stbi_image_free(img);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#if 0
|
||||
DMABitmapConvert(bmp->data, img, w, h, n == 4 ? DMA2D_ARGB4444 : DMA2D_RGB565);
|
||||
#else
|
||||
uint16_t * dest = bmp->getData();
|
||||
const uint8_t * p = img;
|
||||
if (n == 4) {
|
||||
for(int row = 0; row < h; ++row) {
|
||||
for(int col = 0; col < w; ++col) {
|
||||
*dest = ARGB(p[3], p[0], p[1], p[2]);
|
||||
++dest;
|
||||
p += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for(int row = 0; row < h; ++row) {
|
||||
for(int col = 0; col < w; ++col) {
|
||||
*dest = RGB(p[0], p[1], p[2]);
|
||||
++dest;
|
||||
p += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
stbi_image_free(img);
|
||||
return bmp;
|
||||
}
|
||||
@@ -0,0 +1,279 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _BITMAP_BUFFER_H_
|
||||
#define _BITMAP_BUFFER_H_
|
||||
|
||||
#include <inttypes.h>
|
||||
#include "colors.h"
|
||||
|
||||
#define USE_STB
|
||||
|
||||
// TODO should go to lcd.h again
|
||||
typedef int coord_t;
|
||||
typedef uint32_t LcdFlags;
|
||||
typedef uint16_t display_t;
|
||||
|
||||
enum BitmapFormats
|
||||
{
|
||||
BMP_RGB565,
|
||||
BMP_ARGB4444
|
||||
};
|
||||
|
||||
template<class T>
|
||||
class BitmapBufferBase
|
||||
{
|
||||
public:
|
||||
BitmapBufferBase(uint8_t format, uint16_t width, uint16_t height, T * data):
|
||||
format(format),
|
||||
width(width),
|
||||
height(height),
|
||||
data(data)
|
||||
{
|
||||
}
|
||||
|
||||
inline uint8_t getFormat() const
|
||||
{
|
||||
return format;
|
||||
}
|
||||
|
||||
inline uint16_t getWidth() const
|
||||
{
|
||||
return width;
|
||||
}
|
||||
|
||||
inline uint16_t getHeight() const
|
||||
{
|
||||
return height;
|
||||
}
|
||||
|
||||
inline T * getData() const
|
||||
{
|
||||
return data;
|
||||
}
|
||||
|
||||
protected:
|
||||
uint8_t format;
|
||||
uint16_t width;
|
||||
uint16_t height;
|
||||
T * data;
|
||||
};
|
||||
|
||||
typedef BitmapBufferBase<const uint16_t> Bitmap;
|
||||
|
||||
class BitmapBuffer: public BitmapBufferBase<uint16_t>
|
||||
{
|
||||
private:
|
||||
bool dataAllocated;
|
||||
|
||||
public:
|
||||
|
||||
BitmapBuffer(uint8_t format, uint16_t width, uint16_t height):
|
||||
BitmapBufferBase<uint16_t>(format, width, height, NULL),
|
||||
dataAllocated(true)
|
||||
{
|
||||
data = (uint16_t *)malloc(width*height*sizeof(uint16_t));
|
||||
}
|
||||
|
||||
BitmapBuffer(uint8_t format, uint16_t width, uint16_t height, uint16_t * data):
|
||||
BitmapBufferBase<uint16_t>(format, width, height, data),
|
||||
dataAllocated(false)
|
||||
{
|
||||
}
|
||||
|
||||
~BitmapBuffer()
|
||||
{
|
||||
if (dataAllocated) {
|
||||
free(data);
|
||||
}
|
||||
}
|
||||
|
||||
inline void setFormat(uint8_t format)
|
||||
{
|
||||
this->format = format;
|
||||
}
|
||||
|
||||
inline void clear(LcdFlags flags=0)
|
||||
{
|
||||
drawSolidFilledRect(0, 0, width, height, flags);
|
||||
}
|
||||
|
||||
inline void drawPixel(display_t * p, display_t value)
|
||||
{
|
||||
*p = value;
|
||||
}
|
||||
|
||||
inline display_t * getPixelPtr(coord_t x, coord_t y)
|
||||
{
|
||||
return &data[y*width + x];
|
||||
}
|
||||
|
||||
inline void drawPixel(coord_t x, coord_t y, display_t value)
|
||||
{
|
||||
display_t * p = getPixelPtr(x, y);
|
||||
drawPixel(p, value);
|
||||
}
|
||||
|
||||
void drawAlphaPixel(display_t * p, uint8_t opacity, uint16_t color);
|
||||
|
||||
inline void drawAlphaPixel(coord_t x, coord_t y, uint8_t opacity, uint16_t color)
|
||||
{
|
||||
display_t * p = getPixelPtr(x, y);
|
||||
drawAlphaPixel(p, opacity, color);
|
||||
}
|
||||
|
||||
void drawHorizontalLine(coord_t x, coord_t y, coord_t w, uint8_t pat, LcdFlags att);
|
||||
|
||||
void drawVerticalLine(coord_t x, coord_t y, coord_t h, uint8_t pat, LcdFlags att);
|
||||
|
||||
inline void drawSolidHorizontalLine(coord_t x, coord_t y, coord_t w, LcdFlags att)
|
||||
{
|
||||
drawSolidFilledRect(x, y, w, 1, att);
|
||||
}
|
||||
|
||||
inline void drawSolidVerticalLine(coord_t x, coord_t y, coord_t h, LcdFlags att)
|
||||
{
|
||||
drawSolidFilledRect(x, y, 1, h, att);
|
||||
}
|
||||
|
||||
void drawRect(coord_t x, coord_t y, coord_t w, coord_t h, uint8_t thickness, uint8_t pat, LcdFlags att);
|
||||
|
||||
inline void drawSolidFilledRect(coord_t x, coord_t y, coord_t w, coord_t h, LcdFlags flags)
|
||||
{
|
||||
if (h==0 || w==0) return;
|
||||
if (h<0) { y+=h; h=-h; }
|
||||
DMAFillRect(data, width, x, y, w, h, lcdColorTable[COLOR_IDX(flags)]);
|
||||
}
|
||||
|
||||
void drawFilledRect(coord_t x, coord_t y, coord_t w, coord_t h, uint8_t pat, LcdFlags att);
|
||||
|
||||
void invertRect(coord_t x, coord_t y, coord_t w, coord_t h, LcdFlags att);
|
||||
|
||||
void drawCircle(int x0, int y0, int radius);
|
||||
|
||||
void drawPie(int x0, int y0, int radius, int startAngle, int endAngle);
|
||||
|
||||
void drawBitmapPie(int x0, int y0, const uint16_t * img, int startAngle, int endAngle);
|
||||
|
||||
void drawBitmapPatternPie(coord_t x0, coord_t y0, const uint8_t * img, LcdFlags flags, int startAngle, int endAngle);
|
||||
|
||||
static BitmapBuffer * load(const char * filename);
|
||||
|
||||
static BitmapBuffer * loadMask(const char * filename);
|
||||
|
||||
static BitmapBuffer * loadMaskOnBackground(const char * filename, LcdFlags foreground, LcdFlags background);
|
||||
|
||||
void drawMask(coord_t x, coord_t y, BitmapBuffer * mask, LcdFlags flags, coord_t offset=0, coord_t width=0);
|
||||
|
||||
void drawBitmapPattern(coord_t x, coord_t y, const uint8_t * bmp, LcdFlags flags, coord_t offset=0, coord_t width=0);
|
||||
|
||||
uint8_t drawCharWithoutCache(coord_t x, coord_t y, const uint8_t * font, const uint16_t * spec, int index, LcdFlags flags);
|
||||
|
||||
uint8_t drawCharWithCache(coord_t x, coord_t y, const BitmapBuffer * font, const uint16_t * spec, int index, LcdFlags flags);
|
||||
|
||||
void drawText(coord_t x, coord_t y, const char * s, LcdFlags flags)
|
||||
{
|
||||
drawSizedText(x, y, s, 255, flags);
|
||||
}
|
||||
|
||||
void drawSizedText(coord_t x, coord_t y, const char * s, uint8_t len, LcdFlags flags);
|
||||
|
||||
template<class T>
|
||||
void drawBitmap(coord_t x, coord_t y, const T * bmp, coord_t srcx=0, coord_t srcy=0, coord_t w=0, coord_t h=0, float scale=0)
|
||||
{
|
||||
if (!bmp) return;
|
||||
|
||||
int srcw = bmp->getWidth();
|
||||
int srch = bmp->getHeight();
|
||||
|
||||
if (w == 0)
|
||||
w = srcw;
|
||||
if (h == 0)
|
||||
h = srch;
|
||||
if (srcx+w > srcw)
|
||||
w = srcw - srcx;
|
||||
if (srcy+h > srch)
|
||||
h = srch - srcy;
|
||||
|
||||
if (scale == 0) {
|
||||
if (x + w > width) {
|
||||
w = width - x;
|
||||
}
|
||||
if (y + h > height) {
|
||||
h = height - y;
|
||||
}
|
||||
if (bmp->getFormat() == BMP_ARGB4444) {
|
||||
DMACopyAlphaBitmap(data, this->width, x, y, bmp->getData(), srcw, srcx, srcy, w, h);
|
||||
}
|
||||
else {
|
||||
DMACopyBitmap(data, width, x, y, bmp->getData(), srcw, srcx, srcy, w, h);
|
||||
}
|
||||
}
|
||||
else {
|
||||
int scaledw = w * scale;
|
||||
int scaledh = h * scale;
|
||||
|
||||
if (x + scaledw > width)
|
||||
scaledw = width - x;
|
||||
if (y + scaledh > height)
|
||||
scaledh = height - y;
|
||||
|
||||
for (int i = 0; i < scaledh; i++) {
|
||||
uint16_t * p = &data[(y + i) * width + x];
|
||||
const uint16_t * qstart = &bmp->getData()[(srcy + int(i / scale)) * bmp->getWidth() + srcx];
|
||||
for (int j = 0; j < scaledw; j++) {
|
||||
const uint16_t * q = qstart + int(j / scale);
|
||||
if (bmp->getFormat() == BMP_ARGB4444) {
|
||||
ARGB_SPLIT(*q, a, r, g, b);
|
||||
drawAlphaPixel(p, a, RGB_JOIN(r<<1, g<<2, b<<1));
|
||||
}
|
||||
else {
|
||||
drawPixel(p, *q);
|
||||
}
|
||||
p++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void drawScaledBitmap(const T * bitmap, coord_t x, coord_t y, coord_t w, coord_t h)
|
||||
{
|
||||
coord_t bitmapWidth = bitmap->getWidth();
|
||||
|
||||
float scale = float(h) / bitmap->getHeight();
|
||||
int width = (0.5 + bitmapWidth) * scale;
|
||||
if (width > w) {
|
||||
int ww = (0.5 + w) / scale;
|
||||
drawBitmap(x, y, bitmap, (bitmapWidth - ww)/2, 0, ww, 0, scale);
|
||||
}
|
||||
else {
|
||||
drawBitmap(x+(w-width)/2, y, bitmap, 0, 0, 0, 0, scale);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
static BitmapBuffer * load_bmp(const char * filename);
|
||||
static BitmapBuffer * load_stb(const char * filename);
|
||||
};
|
||||
|
||||
extern BitmapBuffer * lcd;
|
||||
|
||||
#endif // _BITMAP_BUFFER_H_
|
||||
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "opentx.h"
|
||||
|
||||
/*
|
||||
* Header bitmaps
|
||||
*/
|
||||
|
||||
const uint8_t LBM_DOT[] = {
|
||||
#include "mask_dot.lbm"
|
||||
};
|
||||
|
||||
/*
|
||||
* Main view bitmaps
|
||||
*/
|
||||
|
||||
const uint8_t LBM_TOPMENU_USB[] = {
|
||||
#include "mask_topmenu_usb.lbm"
|
||||
};
|
||||
|
||||
const uint8_t LBM_TOPMENU_VOLUME_0[] = {
|
||||
#include "mask_volume_0.lbm"
|
||||
};
|
||||
|
||||
const uint8_t LBM_TOPMENU_VOLUME_1[] = {
|
||||
#include "mask_volume_1.lbm"
|
||||
};
|
||||
|
||||
const uint8_t LBM_TOPMENU_VOLUME_2[] = {
|
||||
#include "mask_volume_2.lbm"
|
||||
};
|
||||
|
||||
const uint8_t LBM_TOPMENU_VOLUME_3[] = {
|
||||
#include "mask_volume_3.lbm"
|
||||
};
|
||||
|
||||
const uint8_t LBM_TOPMENU_VOLUME_4[] = {
|
||||
#include "mask_volume_4.lbm"
|
||||
};
|
||||
|
||||
const uint8_t LBM_TOPMENU_VOLUME_SCALE[] = {
|
||||
#include "mask_volume_scale.lbm"
|
||||
};
|
||||
|
||||
const uint8_t LBM_TOPMENU_TXBATT[] = {
|
||||
#include "mask_txbat.lbm"
|
||||
};
|
||||
|
||||
const uint8_t LBM_HTRIM_FRAME[] = {
|
||||
#include "mask_htrim_frame.lbm"
|
||||
};
|
||||
|
||||
const uint8_t LBM_VTRIM_FRAME[] = {
|
||||
#include "mask_vtrim_frame.lbm"
|
||||
};
|
||||
|
||||
const uint8_t LBM_TRIM_SHADOW[] = {
|
||||
#include "mask_trim_shadow.lbm"
|
||||
};
|
||||
|
||||
const uint8_t LBM_TIMER_BACKGROUND[] = {
|
||||
#include "mask_timer_bg.lbm"
|
||||
};
|
||||
|
||||
const uint8_t LBM_TIMER[] = {
|
||||
#include "mask_timer.lbm"
|
||||
};
|
||||
|
||||
const uint8_t LBM_RSCALE[] = {
|
||||
#include "mask_rscale.lbm"
|
||||
};
|
||||
|
||||
/*
|
||||
* Model selection screen bitmaps
|
||||
*/
|
||||
|
||||
const uint8_t LBM_LIBRARY_SLOT[] = {
|
||||
#include "mask_library_slot.lbm"
|
||||
};
|
||||
|
||||
const uint8_t LBM_ACTIVE_MODEL[] = {
|
||||
#include "mask_library_active_model.lbm"
|
||||
};
|
||||
|
||||
const uint8_t LBM_SCORE0[] = {
|
||||
#include "mask_library_score_0.lbm"
|
||||
};
|
||||
|
||||
const uint8_t LBM_SCORE1[] = {
|
||||
#include "mask_library_score_1.lbm"
|
||||
};
|
||||
|
||||
const uint8_t LBM_STAR0[] = {
|
||||
#include "mask_library_star_0.lbm"
|
||||
};
|
||||
|
||||
const uint8_t LBM_STAR1[] = {
|
||||
#include "mask_library_star_1.lbm"
|
||||
};
|
||||
|
||||
/*
|
||||
* Other
|
||||
*/
|
||||
|
||||
const uint8_t LBM_POINT[] = {
|
||||
#include "mask_point.lbm"
|
||||
};
|
||||
|
||||
const uint8_t LBM_CURVE_POINT[] = {
|
||||
#include "mask_cvpoint.lbm"
|
||||
};
|
||||
|
||||
const uint8_t LBM_CURVE_POINT_CENTER[] = {
|
||||
#include "mask_cvpoint_center.lbm"
|
||||
};
|
||||
|
||||
const uint8_t LBM_CURVE_COORD_SHADOW[] = {
|
||||
#include "mask_coord_shadow.lbm"
|
||||
};
|
||||
|
||||
const uint8_t LBM_SHUTDOWN_CIRCLE[] = {
|
||||
#include "mask_shutdown_circle.lbm"
|
||||
};
|
||||
|
||||
const uint8_t LBM_SLIDER_BAR_LEFT[] __ALIGNED = {
|
||||
#include "bar_left.lbm"
|
||||
};
|
||||
|
||||
const uint8_t LBM_SLIDER_BAR_RIGHT[] __ALIGNED = {
|
||||
#include "bar_right.lbm"
|
||||
};
|
||||
|
||||
const uint8_t LBM_SLIDER_POINT_OUT[] __ALIGNED = {
|
||||
#include "point_out.lbm"
|
||||
};
|
||||
|
||||
const uint8_t LBM_SLIDER_POINT_MID[] __ALIGNED = {
|
||||
#include "point_mid.lbm"
|
||||
};
|
||||
|
||||
const uint8_t LBM_SLIDER_POINT_IN[] __ALIGNED = {
|
||||
#include "point_in.lbm"
|
||||
};
|
||||
|
||||
const uint8_t LBM_CARROUSSEL_LEFT[] = {
|
||||
#include "mask_carroussel_left.lbm"
|
||||
};
|
||||
|
||||
const uint8_t LBM_CARROUSSEL_RIGHT[] = {
|
||||
#include "mask_carroussel_right.lbm"
|
||||
};
|
||||
|
||||
const uint8_t LBM_SWIPE_CIRCLE[] = {
|
||||
#include "mask_swipe_circle.lbm"
|
||||
};
|
||||
|
||||
const uint8_t LBM_SWIPE_LEFT[] = {
|
||||
#include "mask_swipe_left.lbm"
|
||||
};
|
||||
|
||||
const uint8_t LBM_SWIPE_RIGHT[] = {
|
||||
#include "mask_swipe_right.lbm"
|
||||
};
|
||||
|
||||
BitmapBuffer * modelselIconBitmap = NULL;
|
||||
BitmapBuffer * modelselSdFreeBitmap = NULL;
|
||||
BitmapBuffer * modelselModelQtyBitmap = NULL;
|
||||
BitmapBuffer * modelselModelMoveBackground = NULL;
|
||||
BitmapBuffer * modelselModelMoveIcon = NULL;
|
||||
BitmapBuffer * chanMonLockedBitmap = NULL;
|
||||
BitmapBuffer * chanMonInvertedBitmap = NULL;
|
||||
BitmapBuffer * mixerSetupMixerBitmap = NULL;
|
||||
BitmapBuffer * mixerSetupToBitmap = NULL;
|
||||
BitmapBuffer * mixerSetupOutputBitmap = NULL;
|
||||
BitmapBuffer * mixerSetupAddBitmap = NULL;
|
||||
BitmapBuffer * mixerSetupMultiBitmap = NULL;
|
||||
BitmapBuffer * mixerSetupReplaceBitmap = NULL;
|
||||
BitmapBuffer * mixerSetupLabelBitmap = NULL;
|
||||
BitmapBuffer * mixerSetupCurveBitmap = NULL;
|
||||
BitmapBuffer * mixerSetupSwitchBitmap = NULL;
|
||||
BitmapBuffer * mixerSetupDelayBitmap = NULL;
|
||||
BitmapBuffer * mixerSetupSlowBitmap = NULL;
|
||||
BitmapBuffer * mixerSetupDelaySlowBitmap = NULL;
|
||||
BitmapBuffer * mixerSetupFlightmodeBitmap = NULL;
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _BITMAPS_H_
|
||||
#define _BITMAPS_H_
|
||||
|
||||
// Header bitmaps
|
||||
extern const uint8_t LBM_DOT[];
|
||||
|
||||
// Main view icons
|
||||
extern const uint8_t LBM_TOPMENU_USB[];
|
||||
extern const uint8_t LBM_TOPMENU_VOLUME_0[];
|
||||
extern const uint8_t LBM_TOPMENU_VOLUME_1[];
|
||||
extern const uint8_t LBM_TOPMENU_VOLUME_2[];
|
||||
extern const uint8_t LBM_TOPMENU_VOLUME_3[];
|
||||
extern const uint8_t LBM_TOPMENU_VOLUME_4[];
|
||||
extern const uint8_t LBM_TOPMENU_VOLUME_SCALE[];
|
||||
extern const uint8_t LBM_TOPMENU_TXBATT[];
|
||||
extern const uint8_t LBM_HTRIM_FRAME[];
|
||||
extern const uint8_t LBM_VTRIM_FRAME[];
|
||||
extern const uint8_t LBM_TRIM_SHADOW[];
|
||||
extern const uint8_t LBM_TIMER_BACKGROUND[];
|
||||
extern const uint8_t LBM_TIMER[];
|
||||
extern const uint8_t LBM_RSCALE[];
|
||||
|
||||
// Model selection icons
|
||||
extern const uint8_t LBM_LIBRARY_SLOT[];
|
||||
extern const uint8_t LBM_ACTIVE_MODEL[];
|
||||
extern const uint8_t LBM_SCORE0[];
|
||||
extern const uint8_t LBM_SCORE1[];
|
||||
extern const uint8_t LBM_STAR0[];
|
||||
extern const uint8_t LBM_STAR1[];
|
||||
|
||||
// Other icons
|
||||
extern const uint8_t LBM_SPLASH[];
|
||||
extern const uint8_t LBM_POINT[];
|
||||
extern const uint8_t LBM_CURVE_POINT[];
|
||||
extern const uint8_t LBM_CURVE_POINT_CENTER[];
|
||||
extern const uint8_t LBM_CURVE_COORD_SHADOW[];
|
||||
extern const uint8_t LBM_SHUTDOWN_CIRCLE[];
|
||||
|
||||
// Slider bitmaps
|
||||
extern const uint8_t LBM_SLIDER_BAR_LEFT[];
|
||||
extern const uint8_t LBM_SLIDER_BAR_RIGHT[];
|
||||
extern const uint8_t LBM_SLIDER_POINT_OUT[];
|
||||
extern const uint8_t LBM_SLIDER_POINT_MID[];
|
||||
extern const uint8_t LBM_SLIDER_POINT_IN[];
|
||||
|
||||
// Carroussel bitmaps
|
||||
extern const uint8_t LBM_CARROUSSEL_LEFT[];
|
||||
extern const uint8_t LBM_CARROUSSEL_RIGHT[];
|
||||
|
||||
extern const uint8_t LBM_SWIPE_CIRCLE[];
|
||||
extern const uint8_t LBM_SWIPE_LEFT[];
|
||||
extern const uint8_t LBM_SWIPE_RIGHT[];
|
||||
|
||||
extern BitmapBuffer * modelselIconBitmap;
|
||||
extern BitmapBuffer * modelselSdFreeBitmap;
|
||||
extern BitmapBuffer * modelselModelQtyBitmap;
|
||||
extern BitmapBuffer * modelselModelMoveBackground;
|
||||
extern BitmapBuffer * modelselModelMoveIcon;
|
||||
|
||||
// Channels monitor bitmaps
|
||||
extern BitmapBuffer * chanMonLockedBitmap;
|
||||
extern BitmapBuffer * chanMonInvertedBitmap;
|
||||
|
||||
// Mixer setup bitmaps
|
||||
extern BitmapBuffer * mixerSetupMixerBitmap;
|
||||
extern BitmapBuffer * mixerSetupToBitmap;
|
||||
extern BitmapBuffer * mixerSetupOutputBitmap;
|
||||
extern BitmapBuffer * mixerSetupAddBitmap;
|
||||
extern BitmapBuffer * mixerSetupMultiBitmap;
|
||||
extern BitmapBuffer * mixerSetupReplaceBitmap;
|
||||
extern BitmapBuffer * mixerSetupLabelBitmap;
|
||||
extern BitmapBuffer * mixerSetupFlightmodeBitmap;
|
||||
extern BitmapBuffer * mixerSetupCurveBitmap;
|
||||
extern BitmapBuffer * mixerSetupSwitchBitmap;
|
||||
extern BitmapBuffer * mixerSetupDelayBitmap;
|
||||
extern BitmapBuffer * mixerSetupSlowBitmap;
|
||||
extern BitmapBuffer * mixerSetupDelaySlowBitmap;
|
||||
|
||||
#endif // _BITMAPS_H_
|
||||
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _COLORS_H_
|
||||
#define _COLORS_H_
|
||||
|
||||
// remove windows default definitions
|
||||
#undef OPAQUE
|
||||
#undef RGB
|
||||
|
||||
#define TO4BITS(x) (uint32_t(limit<int>(0, x, 255)) >> 4)
|
||||
#define TO5BITS(x) (uint32_t(limit<int>(0, x, 255)) >> 3)
|
||||
#define TO6BITS(x) (uint32_t(limit<int>(0, x, 255)) >> 2)
|
||||
#define RGB(r, g, b) ((TO5BITS(r) << 11) + (TO6BITS(g) << 5) + (TO5BITS(b) << 0))
|
||||
#define ARGB(a, r, g, b) ((TO4BITS(a) << 12) + (TO4BITS(r) << 8) + (TO4BITS(g) << 4) + (TO4BITS(b) << 0))
|
||||
#define WHITE RGB(0xFF, 0xFF, 0xFF)
|
||||
#define BLACK RGB(0, 0, 0)
|
||||
#define YELLOW RGB(0xF0, 0xD0, 0x10)
|
||||
#define BLUE RGB(0x30, 0xA0, 0xE0)
|
||||
#define GREY RGB(96, 96, 96)
|
||||
#define DARKGREY RGB(64, 64, 64)
|
||||
#define LIGHTGREY RGB(180, 180, 180)
|
||||
#define RED RGB(229, 32, 30)
|
||||
#define DARKRED RGB(160, 0, 6)
|
||||
#define GREEN RGB(25, 150, 50)
|
||||
|
||||
#define OPACITY_MAX 0x0F
|
||||
#define OPACITY(x) ((x)<<24)
|
||||
|
||||
enum LcdColorIndex
|
||||
{
|
||||
TEXT_COLOR_INDEX,
|
||||
TEXT_BGCOLOR_INDEX,
|
||||
TEXT_INVERTED_COLOR_INDEX,
|
||||
TEXT_INVERTED_BGCOLOR_INDEX,
|
||||
TEXT_STATUSBAR_COLOR_INDEX,
|
||||
LINE_COLOR_INDEX,
|
||||
SCROLLBOX_COLOR_INDEX,
|
||||
MENU_TITLE_BGCOLOR_INDEX,
|
||||
MENU_TITLE_COLOR_INDEX,
|
||||
MENU_TITLE_DISABLE_COLOR_INDEX,
|
||||
HEADER_COLOR_INDEX,
|
||||
ALARM_COLOR_INDEX,
|
||||
WARNING_COLOR_INDEX,
|
||||
TEXT_DISABLE_COLOR_INDEX,
|
||||
CURVE_AXIS_COLOR_INDEX,
|
||||
CURVE_COLOR_INDEX,
|
||||
CURVE_CURSOR_COLOR_INDEX,
|
||||
HEADER_BGCOLOR_INDEX,
|
||||
HEADER_ICON_BGCOLOR_INDEX,
|
||||
HEADER_CURRENT_BGCOLOR_INDEX,
|
||||
TITLE_BGCOLOR_INDEX,
|
||||
TRIM_BGCOLOR_INDEX,
|
||||
TRIM_SHADOW_COLOR_INDEX,
|
||||
MAINVIEW_PANES_COLOR_INDEX,
|
||||
MAINVIEW_GRAPHICS_COLOR_INDEX,
|
||||
OVERLAY_COLOR_INDEX,
|
||||
CUSTOM_COLOR_INDEX,
|
||||
BARGRAPH1_COLOR_INDEX,
|
||||
BARGRAPH2_COLOR_INDEX,
|
||||
BARGRAPH_BGCOLOR_INDEX,
|
||||
LCD_COLOR_COUNT
|
||||
};
|
||||
|
||||
extern uint16_t lcdColorTable[LCD_COLOR_COUNT];
|
||||
|
||||
#define COLOR(index) ((index) << 16)
|
||||
#define COLOR_IDX(att) uint8_t((att) >> 16)
|
||||
|
||||
#define TEXT_COLOR COLOR(TEXT_COLOR_INDEX)
|
||||
#define TEXT_BGCOLOR COLOR(TEXT_BGCOLOR_INDEX)
|
||||
#define TEXT_INVERTED_COLOR COLOR(TEXT_INVERTED_COLOR_INDEX)
|
||||
#define TEXT_INVERTED_BGCOLOR COLOR(TEXT_INVERTED_BGCOLOR_INDEX)
|
||||
#define TEXT_STATUSBAR_COLOR COLOR(TEXT_STATUSBAR_COLOR_INDEX)
|
||||
#define LINE_COLOR COLOR(LINE_COLOR_INDEX)
|
||||
#define SCROLLBOX_COLOR COLOR(SCROLLBOX_COLOR_INDEX)
|
||||
#define HEADER_SEPARATOR_COLOR COLOR(HEADER_SEPARATOR_COLOR_INDEX)
|
||||
#define MENU_TITLE_BGCOLOR COLOR(MENU_TITLE_BGCOLOR_INDEX)
|
||||
#define MENU_TITLE_COLOR COLOR(MENU_TITLE_COLOR_INDEX)
|
||||
#define MENU_TITLE_DISABLE_COLOR COLOR(MENU_TITLE_DISABLE_COLOR_INDEX)
|
||||
#define HEADER_COLOR COLOR(HEADER_COLOR_INDEX)
|
||||
#define ALARM_COLOR COLOR(ALARM_COLOR_INDEX)
|
||||
#define WARNING_COLOR COLOR(WARNING_COLOR_INDEX)
|
||||
#define TEXT_DISABLE_COLOR COLOR(TEXT_DISABLE_COLOR_INDEX)
|
||||
#define CURVE_AXIS_COLOR COLOR(CURVE_AXIS_COLOR_INDEX)
|
||||
#define CURVE_COLOR COLOR(CURVE_COLOR_INDEX)
|
||||
#define CURVE_CURSOR_COLOR COLOR(CURVE_CURSOR_COLOR_INDEX)
|
||||
#define TITLE_BGCOLOR COLOR(TITLE_BGCOLOR_INDEX)
|
||||
#define TRIM_BGCOLOR COLOR(TRIM_BGCOLOR_INDEX)
|
||||
#define TRIM_SHADOW_COLOR COLOR(TRIM_SHADOW_COLOR_INDEX)
|
||||
#define HEADER_BGCOLOR COLOR(HEADER_BGCOLOR_INDEX)
|
||||
#define HEADER_ICON_BGCOLOR COLOR(HEADER_ICON_BGCOLOR_INDEX)
|
||||
#define HEADER_CURRENT_BGCOLOR COLOR(HEADER_CURRENT_BGCOLOR_INDEX)
|
||||
#define MAINVIEW_PANES_COLOR COLOR(MAINVIEW_PANES_COLOR_INDEX)
|
||||
#define MAINVIEW_GRAPHICS_COLOR COLOR(MAINVIEW_GRAPHICS_COLOR_INDEX)
|
||||
#define OVERLAY_COLOR COLOR(OVERLAY_COLOR_INDEX)
|
||||
#define BARGRAPH1_COLOR COLOR(BARGRAPH1_COLOR_INDEX)
|
||||
#define BARGRAPH2_COLOR COLOR(BARGRAPH2_COLOR_INDEX)
|
||||
#define BARGRAPH_BGCOLOR COLOR(BARGRAPH_BGCOLOR_INDEX)
|
||||
#define CUSTOM_COLOR COLOR(CUSTOM_COLOR_INDEX)
|
||||
|
||||
#define ARGB_SPLIT(color, a, r, g, b) \
|
||||
uint16_t a = ((color) & 0xF000) >> 12; \
|
||||
uint16_t r = ((color) & 0x0F00) >> 8; \
|
||||
uint16_t g = ((color) & 0x00F0) >> 4; \
|
||||
uint16_t b = ((color) & 0x000F)
|
||||
|
||||
#define RGB_SPLIT(color, r, g, b) \
|
||||
uint16_t r = ((color) & 0xF800) >> 11; \
|
||||
uint16_t g = ((color) & 0x07E0) >> 5; \
|
||||
uint16_t b = ((color) & 0x001F)
|
||||
|
||||
#define RGB_JOIN(r, g, b) \
|
||||
(((r) << 11) + ((g) << 5) + (b))
|
||||
|
||||
#define GET_RED(color) \
|
||||
(((color) & 0xF800) >> 8)
|
||||
|
||||
#define GET_GREEN(color) \
|
||||
(((color) & 0x07E0) >> 3)
|
||||
|
||||
#define GET_BLUE(color) \
|
||||
(((color) & 0x001F) << 3)
|
||||
|
||||
#endif // _COLORS_H_
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "../../opentx.h"
|
||||
|
||||
coord_t getCurveYCoord(FnFuncP fn, int x, int width)
|
||||
{
|
||||
return limit(-width, -divRoundClosest(fn(divRoundClosest(x * RESX, width)) * width, RESX), +width);
|
||||
}
|
||||
|
||||
void drawFunction(FnFuncP fn, int x, int y, int width)
|
||||
{
|
||||
int left = x - width;
|
||||
int right = x + width;
|
||||
|
||||
// Axis
|
||||
lcdDrawSolidHorizontalLine(left, y, width*2+1, CURVE_AXIS_COLOR);
|
||||
lcdDrawSolidVerticalLine(x, y-width, width*2, CURVE_AXIS_COLOR);
|
||||
|
||||
// Extra lines
|
||||
lcdDrawVerticalLine(left+width/2, y-width, width*2, STASHED, CURVE_AXIS_COLOR);
|
||||
lcdDrawVerticalLine(right-width/2, y-width, width*2, STASHED, CURVE_AXIS_COLOR);
|
||||
lcdDrawHorizontalLine(left, y-width/2, width*2+1, STASHED, CURVE_AXIS_COLOR);
|
||||
lcdDrawHorizontalLine(left, y+width/2, width*2+1, STASHED, CURVE_AXIS_COLOR);
|
||||
|
||||
// Outside border
|
||||
lcdDrawSolidVerticalLine(left, y-width, width*2, TEXT_COLOR);
|
||||
lcdDrawSolidVerticalLine(right, y-width, width*2, TEXT_COLOR);
|
||||
lcdDrawSolidHorizontalLine(left, y-width, width*2+1, TEXT_COLOR);
|
||||
lcdDrawSolidHorizontalLine(left, y+width, width*2+1, TEXT_COLOR);
|
||||
|
||||
coord_t prev_yv = (coord_t)-1;
|
||||
|
||||
for (int xv=-width; xv<=width; xv+=1) {
|
||||
coord_t yv = y + getCurveYCoord(fn, xv, width);
|
||||
if (prev_yv != (coord_t)-1) {
|
||||
if (prev_yv < yv) {
|
||||
for (int y=prev_yv; y<=yv; y+=1) {
|
||||
lcdDrawBitmapPattern(x+xv-2, y-2, LBM_POINT, TEXT_COLOR);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int y=yv; y<=prev_yv; y+=1) {
|
||||
lcdDrawBitmapPattern(x+xv-2, y-2, LBM_POINT, TEXT_COLOR);
|
||||
}
|
||||
}
|
||||
}
|
||||
prev_yv = yv;
|
||||
}
|
||||
}
|
||||
|
||||
void drawCurveVerticalScale(int x)
|
||||
{
|
||||
for (int i=0; i<=20; i++) {
|
||||
lcdDrawSolidHorizontalLine(x, CURVE_CENTER_Y-CURVE_SIDE_WIDTH+i*CURVE_SIDE_WIDTH/10, 10, TEXT_COLOR);
|
||||
}
|
||||
}
|
||||
|
||||
void drawCurveHorizontalScale()
|
||||
{
|
||||
for (int i=0; i<=20; i++) {
|
||||
lcdDrawSolidVerticalLine(CURVE_CENTER_X-CURVE_SIDE_WIDTH+i*CURVE_SIDE_WIDTH/10, CURVE_CENTER_Y+CURVE_SIDE_WIDTH+5, 10, TEXT_COLOR);
|
||||
}
|
||||
}
|
||||
|
||||
void drawCurveCoord(int x, int y, const char * text, bool active)
|
||||
{
|
||||
lcd->drawSolidFilledRect(x, y, CURVE_COORD_WIDTH, CURVE_COORD_HEIGHT, CURVE_CURSOR_COLOR);
|
||||
lcdDrawText(x+3+(CURVE_COORD_WIDTH-1-getTextWidth(text, SMLSIZE))/2, y+1, text, LEFT|SMLSIZE|TEXT_BGCOLOR);
|
||||
if (active) {
|
||||
lcdDrawBitmapPattern(x, y, LBM_CURVE_COORD_SHADOW, TEXT_COLOR);
|
||||
}
|
||||
}
|
||||
|
||||
void drawCurvePoint(int x, int y, LcdFlags color)
|
||||
{
|
||||
lcdDrawBitmapPattern(x, y, LBM_CURVE_POINT, color);
|
||||
lcdDrawBitmapPattern(x, y, LBM_CURVE_POINT_CENTER, TEXT_BGCOLOR);
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "opentx.h"
|
||||
|
||||
uint16_t font_tinsize_specs[] = {
|
||||
#include "font_tinsize.specs"
|
||||
};
|
||||
|
||||
pm_uchar font_tinsize[] = {
|
||||
#include "font_tinsize.lbm"
|
||||
};
|
||||
|
||||
uint16_t font_smlsize_specs[] = {
|
||||
#include "font_smlsize.specs"
|
||||
};
|
||||
|
||||
pm_uchar font_smlsize[] = {
|
||||
#include "font_smlsize.lbm"
|
||||
};
|
||||
|
||||
uint16_t font_stdsize_specs[] = {
|
||||
#include "font_stdsize.specs"
|
||||
};
|
||||
|
||||
pm_uchar font_stdsize[] = {
|
||||
#include "font_stdsize.lbm"
|
||||
};
|
||||
|
||||
uint16_t font_midsize_specs[] = {
|
||||
#include "font_midsize.specs"
|
||||
};
|
||||
|
||||
pm_uchar font_midsize[] = {
|
||||
#include "font_midsize.lbm"
|
||||
};
|
||||
|
||||
uint16_t font_dblsize_specs[] = {
|
||||
#include "font_dblsize.specs"
|
||||
};
|
||||
|
||||
pm_uchar font_dblsize[] = {
|
||||
#include "font_dblsize.lbm"
|
||||
};
|
||||
|
||||
uint16_t font_xxlsize_specs[] = {
|
||||
#include "font_xxlsize.specs"
|
||||
};
|
||||
|
||||
pm_uchar font_xxlsize[] = {
|
||||
#include "font_xxlsize.lbm"
|
||||
};
|
||||
|
||||
uint16_t font_stdsizebold_specs[] = {
|
||||
#include "font_stdsizebold.specs"
|
||||
};
|
||||
|
||||
pm_uchar font_stdsizebold[] = {
|
||||
#include "font_stdsizebold.lbm"
|
||||
};
|
||||
|
||||
const uint16_t * const fontspecsTable[16] = {
|
||||
font_stdsize_specs, font_tinsize_specs, font_smlsize_specs, font_midsize_specs, font_dblsize_specs, font_xxlsize_specs, font_stdsize_specs, font_stdsize_specs,
|
||||
font_stdsizebold_specs, font_tinsize_specs, font_smlsize_specs, font_midsize_specs, font_dblsize_specs, font_xxlsize_specs, font_stdsize_specs, font_stdsize_specs
|
||||
};
|
||||
|
||||
const uint8_t * const fontsTable[16] = {
|
||||
font_stdsize, font_tinsize, font_smlsize, font_midsize, font_dblsize, font_xxlsize, font_stdsize, font_stdsize,
|
||||
font_stdsizebold, font_tinsize, font_smlsize, font_midsize, font_dblsize, font_xxlsize, font_stdsize, font_stdsize
|
||||
};
|
||||
|
||||
BitmapBuffer * fontCache[2] = { NULL, NULL };
|
||||
|
||||
BitmapBuffer * createFontCache(const uint8_t * font, LcdFlags fg, LcdFlags bg)
|
||||
{
|
||||
coord_t width = *((uint16_t *)font);
|
||||
coord_t height = *(((uint16_t *)font)+1);
|
||||
|
||||
BitmapBuffer * buffer = new BitmapBuffer(BMP_RGB565, width, height);
|
||||
buffer->clear(bg);
|
||||
buffer->drawBitmapPattern(0, 0, font, fg);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void loadFontCache()
|
||||
{
|
||||
delete fontCache[0];
|
||||
delete fontCache[1];
|
||||
fontCache[0] = createFontCache(fontsTable[0], TEXT_COLOR, TEXT_BGCOLOR);
|
||||
fontCache[1] = createFontCache(fontsTable[0], TEXT_INVERTED_COLOR, TEXT_INVERTED_BGCOLOR);
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _GUI_H_
|
||||
#define _GUI_H_
|
||||
|
||||
#include "gui_helpers.h"
|
||||
#include "lcd.h"
|
||||
#include "menus.h"
|
||||
#include "widgets.h"
|
||||
#include "bitmaps.h"
|
||||
#include "theme.h"
|
||||
|
||||
#define MENU_HEADER_HEIGHT 45
|
||||
#define MENU_TITLE_TOP 48
|
||||
#define MENU_TITLE_HEIGHT 21
|
||||
#define MENU_BODY_TOP (MENU_TITLE_TOP+MENU_TITLE_HEIGHT)
|
||||
#define MENU_CONTENT_TOP (MENU_BODY_TOP+1)
|
||||
#define MENU_FOOTER_HEIGHT 21
|
||||
#define MENU_FOOTER_TOP (LCD_H-MENU_FOOTER_HEIGHT)
|
||||
#define MENU_BODY_HEIGHT (MENU_FOOTER_TOP-MENU_BODY_TOP)
|
||||
#define MENUS_MARGIN_LEFT 6
|
||||
|
||||
#define DEFAULT_SCROLLBAR_X LCD_W-10
|
||||
#define DEFAULT_SCROLLBAR_Y MENU_CONTENT_TOP
|
||||
#define DEFAULT_SCROLLBAR_H (MENU_FOOTER_TOP - DEFAULT_SCROLLBAR_Y - 6)
|
||||
|
||||
#define FH 20
|
||||
#define NUM_BODY_LINES (MENU_BODY_HEIGHT / FH)
|
||||
|
||||
#define INVERT_HORZ_MARGIN 2
|
||||
#define INVERT_VERT_MARGIN 1
|
||||
#define INVERT_LINE_HEIGHT 20
|
||||
|
||||
#define CURVE_SIDE_WIDTH 100
|
||||
#define CURVE_CENTER_X (LCD_W-CURVE_SIDE_WIDTH-7)
|
||||
#define CURVE_CENTER_Y 151
|
||||
#define CURVE_COORD_WIDTH 36
|
||||
#define CURVE_COORD_HEIGHT 17
|
||||
|
||||
#define DATETIME_SEPARATOR_X (LCD_W-53)
|
||||
#define DATETIME_LINE1 9
|
||||
#define DATETIME_LINE2 23
|
||||
#define DATETIME_MIDDLE (LCD_W+DATETIME_SEPARATOR_X+4)/2
|
||||
|
||||
#define MENU_TITLE_NEXT_POS (lcdNextPos + 10)
|
||||
#define MENU_INIT_VPOS -1
|
||||
|
||||
#if defined(HELI) && defined(GVARS) && defined(LUA_MODEL_SCRIPTS)
|
||||
#define MENU_ICONS_SPACING 31
|
||||
#else
|
||||
#define MENU_ICONS_SPACING 33
|
||||
#endif
|
||||
|
||||
#define SUBMENU_LINE_WIDTH 230
|
||||
|
||||
#define LOAD_MODEL_BITMAP()
|
||||
|
||||
#if defined(FLIGHT_MODES)
|
||||
void displayFlightModes(coord_t x, coord_t y, FlightModesType value, uint8_t attr);
|
||||
FlightModesType editFlightModes(coord_t x, coord_t y, evt_t event, FlightModesType value, uint8_t attr);
|
||||
#else
|
||||
#define displayFlightModes(...)
|
||||
#endif
|
||||
|
||||
// Curve functions
|
||||
coord_t getCurveYCoord(FnFuncP fn, int x, int width);
|
||||
void drawFunction(FnFuncP fn, int offset);
|
||||
void drawCurveVerticalScale(int x);
|
||||
void drawCurveHorizontalScale();
|
||||
void drawCurveCoord(int x, int y, const char * text, bool active=false);
|
||||
void drawCurvePoint(int x, int y, LcdFlags color);
|
||||
|
||||
extern Layout * customScreens[MAX_CUSTOM_SCREENS];
|
||||
extern Topbar * topbar;
|
||||
|
||||
#endif // _GUI_H_
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "opentx.h"
|
||||
|
||||
const LayoutFactory * registeredLayouts[MAX_REGISTERED_LAYOUTS]; // TODO dynamic
|
||||
unsigned int countRegisteredLayouts = 0;
|
||||
void registerLayout(const LayoutFactory * factory)
|
||||
{
|
||||
if (countRegisteredLayouts < MAX_REGISTERED_LAYOUTS) {
|
||||
TRACE("register layout %s", factory->getName());
|
||||
registeredLayouts[countRegisteredLayouts++] = factory;
|
||||
}
|
||||
}
|
||||
|
||||
const LayoutFactory * getLayoutFactory(const char * name)
|
||||
{
|
||||
for (unsigned int i=0; i<countRegisteredLayouts; i++) {
|
||||
const LayoutFactory * factory = registeredLayouts[i];
|
||||
if (!strcmp(name, factory->getName())) {
|
||||
return factory;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Layout * loadLayout(const char * name, Layout::PersistentData * persistentData)
|
||||
{
|
||||
const LayoutFactory * factory = getLayoutFactory(name);
|
||||
if (factory) {
|
||||
return factory->load(persistentData);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void loadCustomScreens()
|
||||
{
|
||||
for (unsigned int i=0; i<MAX_CUSTOM_SCREENS; i++) {
|
||||
delete customScreens[i];
|
||||
char name[sizeof(g_model.screenData[i].layoutName)+1];
|
||||
memset(name, 0, sizeof(name));
|
||||
strncpy(name, g_model.screenData[i].layoutName, sizeof(g_model.screenData[i].layoutName));
|
||||
customScreens[i] = loadLayout(name, &g_model.screenData[i].layoutData);
|
||||
}
|
||||
|
||||
if (customScreens[0] == NULL) {
|
||||
customScreens[0] = registeredLayouts[0]->create(&g_model.screenData[0].layoutData);
|
||||
}
|
||||
|
||||
topbar->load();
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _LAYOUT_H_
|
||||
#define _LAYOUT_H_
|
||||
|
||||
#include "widgets_container.h"
|
||||
|
||||
#define MAX_LAYOUT_ZONES 10
|
||||
#define MAX_LAYOUT_OPTIONS 10
|
||||
|
||||
class LayoutFactory;
|
||||
|
||||
class Layout: public WidgetsContainer<MAX_LAYOUT_ZONES, MAX_LAYOUT_OPTIONS>
|
||||
{
|
||||
friend class LayoutFactory;
|
||||
|
||||
public:
|
||||
Layout(const LayoutFactory * factory, PersistentData * persistentData):
|
||||
WidgetsContainer<MAX_LAYOUT_ZONES, MAX_LAYOUT_OPTIONS>(persistentData),
|
||||
factory(factory)
|
||||
{
|
||||
}
|
||||
|
||||
inline const LayoutFactory * getFactory() const
|
||||
{
|
||||
return factory;
|
||||
}
|
||||
|
||||
virtual void update()
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
const LayoutFactory * factory;
|
||||
};
|
||||
|
||||
void registerLayout(const LayoutFactory * factory);
|
||||
|
||||
class LayoutFactory
|
||||
{
|
||||
public:
|
||||
LayoutFactory(const char * name):
|
||||
name(name)
|
||||
{
|
||||
registerLayout(this);
|
||||
}
|
||||
const char * getName() const { return name; }
|
||||
virtual void drawThumb(uint16_t x, uint16_t y, uint32_t flags) const = 0;
|
||||
virtual const ZoneOption * getOptions() const = 0;
|
||||
virtual Layout * create(Layout::PersistentData * persistentData) const = 0;
|
||||
virtual Layout * load(Layout::PersistentData * persistentData) const = 0;
|
||||
|
||||
protected:
|
||||
const char * name;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
class BaseLayoutFactory: public LayoutFactory
|
||||
{
|
||||
public:
|
||||
BaseLayoutFactory(const char * name, const uint8_t * bitmap, const ZoneOption * options):
|
||||
LayoutFactory(name),
|
||||
bitmap(bitmap),
|
||||
options(options)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void drawThumb(uint16_t x, uint16_t y, uint32_t flags) const
|
||||
{
|
||||
extern void lcdDrawBitmapPattern(int x, int y, const uint8_t * bitmap, uint32_t flags, int width=0, int offset=0);
|
||||
lcdDrawBitmapPattern(x, y, bitmap, flags);
|
||||
}
|
||||
|
||||
virtual const ZoneOption * getOptions() const
|
||||
{
|
||||
return options;
|
||||
}
|
||||
|
||||
virtual Layout * create(Layout::PersistentData * persistentData) const
|
||||
{
|
||||
Layout * layout = new T(this, persistentData);
|
||||
layout->create();
|
||||
return layout;
|
||||
}
|
||||
|
||||
virtual Layout * load(Layout::PersistentData * persistentData) const
|
||||
{
|
||||
Layout * layout = new T(this, persistentData);
|
||||
layout->load();
|
||||
return layout;
|
||||
}
|
||||
|
||||
protected:
|
||||
const uint8_t * bitmap;
|
||||
const ZoneOption * options;
|
||||
};
|
||||
|
||||
#define MAX_REGISTERED_LAYOUTS 10
|
||||
|
||||
extern unsigned int countRegisteredLayouts;
|
||||
extern const LayoutFactory * registeredLayouts[MAX_REGISTERED_LAYOUTS];
|
||||
Layout * loadLayout(const char * name, Layout::PersistentData * persistentData);
|
||||
void loadCustomScreens();
|
||||
|
||||
#endif // _LAYOUT_H_
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "opentx.h"
|
||||
|
||||
const uint8_t LBM_LAYOUT_1x1[] = {
|
||||
#include "mask_layout1x1.lbm"
|
||||
};
|
||||
|
||||
const ZoneOption OPTIONS_LAYOUT_1x1[] = {
|
||||
{ "Top bar", ZoneOption::Bool },
|
||||
{ "Sliders+Trims", ZoneOption::Bool },
|
||||
{ NULL, ZoneOption::Bool }
|
||||
};
|
||||
|
||||
class Layout1x1: public Layout
|
||||
{
|
||||
public:
|
||||
Layout1x1(const LayoutFactory * factory, Layout::PersistentData * persistentData):
|
||||
Layout(factory, persistentData)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void create()
|
||||
{
|
||||
Layout::create();
|
||||
persistentData->options[0].boolValue = true;
|
||||
persistentData->options[1].boolValue = true;
|
||||
}
|
||||
|
||||
virtual unsigned int getZonesCount() const
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
virtual Zone getZone(unsigned int index) const
|
||||
{
|
||||
Zone zone = { 10, 10, LCD_W - 2*10, LCD_H - 2*10 };
|
||||
if (persistentData->options[0].boolValue) {
|
||||
zone.y += MENU_HEADER_HEIGHT;
|
||||
zone.h -= MENU_HEADER_HEIGHT;
|
||||
}
|
||||
if (persistentData->options[1].boolValue) {
|
||||
zone.x += 35;
|
||||
zone.w -= 2*35;
|
||||
zone.h -= 35;
|
||||
}
|
||||
return zone;
|
||||
}
|
||||
|
||||
virtual void refresh();
|
||||
};
|
||||
|
||||
void Layout1x1::refresh()
|
||||
{
|
||||
theme->drawBackground();
|
||||
|
||||
if (persistentData->options[0].boolValue) {
|
||||
drawTopBar();
|
||||
}
|
||||
|
||||
if (persistentData->options[1].boolValue) {
|
||||
// Sliders + Trims + Flight mode
|
||||
lcdDrawSizedText(LCD_W / 2 - getTextWidth(g_model.flightModeData[mixerCurrentFlightMode].name, sizeof(g_model.flightModeData[mixerCurrentFlightMode].name), ZCHAR | SMLSIZE) / 2,
|
||||
237,
|
||||
g_model.flightModeData[mixerCurrentFlightMode].name,
|
||||
sizeof(g_model.flightModeData[mixerCurrentFlightMode].name), ZCHAR | SMLSIZE);
|
||||
drawMainPots();
|
||||
drawTrims(mixerCurrentFlightMode);
|
||||
}
|
||||
|
||||
Layout::refresh();
|
||||
}
|
||||
|
||||
BaseLayoutFactory<Layout1x1> layout1x1("Layout1x1", LBM_LAYOUT_1x1, OPTIONS_LAYOUT_1x1);
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "opentx.h"
|
||||
|
||||
const uint8_t LBM_LAYOUT_2P1[] = {
|
||||
#include "mask_layout2+1.lbm"
|
||||
};
|
||||
|
||||
const ZoneOption OPTIONS_LAYOUT_2P1[] = {
|
||||
{ "Top bar", ZoneOption::Bool },
|
||||
{ "Flight mode", ZoneOption::Bool },
|
||||
{ "Sliders", ZoneOption::Bool },
|
||||
{ "Trims", ZoneOption::Bool },
|
||||
{ NULL, ZoneOption::Bool }
|
||||
};
|
||||
|
||||
const Zone ZONES_LAYOUT_2P1[3] = {
|
||||
{ 240, 60, 192, 152 },
|
||||
{ 48, 60, 180, 70 },
|
||||
{ 48, 142, 180, 70 }
|
||||
};
|
||||
|
||||
class Layout2P1: public Layout
|
||||
{
|
||||
public:
|
||||
Layout2P1(const LayoutFactory * factory, Layout::PersistentData * persistentData):
|
||||
Layout(factory, persistentData)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void create()
|
||||
{
|
||||
Layout::create();
|
||||
persistentData->options[0].boolValue = true;
|
||||
persistentData->options[1].boolValue = true;
|
||||
persistentData->options[2].boolValue = true;
|
||||
persistentData->options[3].boolValue = true;
|
||||
}
|
||||
|
||||
virtual unsigned int getZonesCount() const
|
||||
{
|
||||
return DIM(ZONES_LAYOUT_2P1);
|
||||
}
|
||||
|
||||
virtual Zone getZone(unsigned int index) const
|
||||
{
|
||||
return ZONES_LAYOUT_2P1[index];
|
||||
}
|
||||
|
||||
virtual void refresh();
|
||||
};
|
||||
|
||||
void Layout2P1::refresh()
|
||||
{
|
||||
theme->drawBackground();
|
||||
|
||||
if (persistentData->options[0].boolValue) {
|
||||
drawTopBar();
|
||||
}
|
||||
|
||||
if (persistentData->options[1].boolValue) {
|
||||
// Flight mode
|
||||
lcdDrawSizedText(LCD_W / 2 - getTextWidth(g_model.flightModeData[mixerCurrentFlightMode].name,
|
||||
sizeof(g_model.flightModeData[mixerCurrentFlightMode].name),
|
||||
ZCHAR | SMLSIZE) / 2,
|
||||
237,
|
||||
g_model.flightModeData[mixerCurrentFlightMode].name,
|
||||
sizeof(g_model.flightModeData[mixerCurrentFlightMode].name), ZCHAR | SMLSIZE);
|
||||
}
|
||||
|
||||
if (persistentData->options[2].boolValue) {
|
||||
// Pots and rear sliders positions
|
||||
drawMainPots();
|
||||
}
|
||||
|
||||
if (persistentData->options[3].boolValue) {
|
||||
// Trims
|
||||
drawTrims(mixerCurrentFlightMode);
|
||||
}
|
||||
|
||||
Layout::refresh();
|
||||
}
|
||||
|
||||
BaseLayoutFactory<Layout2P1> layout2P1("Layout2P1", LBM_LAYOUT_2P1, OPTIONS_LAYOUT_2P1);
|
||||
const LayoutFactory * defaultLayout = &layout2P1;
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "opentx.h"
|
||||
|
||||
const uint8_t LBM_LAYOUT_2x1[] = {
|
||||
#include "mask_layout2x1.lbm"
|
||||
};
|
||||
|
||||
const ZoneOption OPTIONS_LAYOUT_2x1[] = {
|
||||
{ "Top bar", ZoneOption::Bool },
|
||||
{ NULL, ZoneOption::Bool }
|
||||
};
|
||||
|
||||
class Layout2x1: public Layout
|
||||
{
|
||||
public:
|
||||
Layout2x1(const LayoutFactory * factory, Layout::PersistentData * persistentData):
|
||||
Layout(factory, persistentData)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void create()
|
||||
{
|
||||
Layout::create();
|
||||
persistentData->options[0].boolValue = true;
|
||||
}
|
||||
|
||||
virtual unsigned int getZonesCount() const
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
|
||||
virtual Zone getZone(unsigned int index) const
|
||||
{
|
||||
Zone zone;
|
||||
zone.w = (LCD_W-3*10) / 2;
|
||||
zone.x = (index & 1) ? 20 + zone.w : 10;
|
||||
if (persistentData->options[0].boolValue) {
|
||||
zone.h = (LCD_H-MENU_HEADER_HEIGHT-2*10);
|
||||
zone.y = MENU_HEADER_HEIGHT + 10;
|
||||
}
|
||||
else {
|
||||
zone.h = (LCD_H-2*10);
|
||||
zone.y = 10;
|
||||
}
|
||||
return zone;
|
||||
}
|
||||
|
||||
virtual void refresh();
|
||||
};
|
||||
|
||||
void Layout2x1::refresh()
|
||||
{
|
||||
theme->drawBackground();
|
||||
|
||||
if (persistentData->options[0].boolValue) {
|
||||
drawTopBar();
|
||||
}
|
||||
|
||||
Layout::refresh();
|
||||
}
|
||||
|
||||
BaseLayoutFactory<Layout2x1> Layout2x1("Layout2x1", LBM_LAYOUT_2x1, OPTIONS_LAYOUT_2x1);
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "opentx.h"
|
||||
|
||||
const uint8_t LBM_LAYOUT_2x2[] = {
|
||||
#include "mask_layout2x2.lbm"
|
||||
};
|
||||
|
||||
const ZoneOption OPTIONS_LAYOUT_2x2[] = {
|
||||
{ "Top bar", ZoneOption::Bool },
|
||||
{ NULL, ZoneOption::Bool }
|
||||
};
|
||||
|
||||
class Layout2x2: public Layout
|
||||
{
|
||||
public:
|
||||
Layout2x2(const LayoutFactory * factory, Layout::PersistentData * persistentData):
|
||||
Layout(factory, persistentData)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void create()
|
||||
{
|
||||
Layout::create();
|
||||
persistentData->options[0].boolValue = true;
|
||||
}
|
||||
|
||||
virtual unsigned int getZonesCount() const
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
|
||||
virtual Zone getZone(unsigned int index) const
|
||||
{
|
||||
Zone zone;
|
||||
zone.w = (LCD_W-3*10) / 2;
|
||||
zone.x = (index & 1) ? 20 + zone.w : 10;
|
||||
if (persistentData->options[0].boolValue) {
|
||||
zone.h = (LCD_H-MENU_HEADER_HEIGHT-3*10) / 2;
|
||||
zone.y = MENU_HEADER_HEIGHT + 10;
|
||||
}
|
||||
else {
|
||||
zone.h = (LCD_H-3*10) / 2;
|
||||
zone.y = 10;
|
||||
}
|
||||
if (index >= 2) {
|
||||
zone.y += 10 + zone.h;
|
||||
}
|
||||
return zone;
|
||||
}
|
||||
|
||||
virtual void refresh();
|
||||
};
|
||||
|
||||
void Layout2x2::refresh()
|
||||
{
|
||||
theme->drawBackground();
|
||||
|
||||
if (persistentData->options[0].boolValue) {
|
||||
drawTopBar();
|
||||
}
|
||||
|
||||
Layout::refresh();
|
||||
}
|
||||
|
||||
BaseLayoutFactory<Layout2x2> layout2x2("Layout2x2", LBM_LAYOUT_2x2, OPTIONS_LAYOUT_2x2);
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "opentx.h"
|
||||
|
||||
const uint8_t LBM_LAYOUT_2x4[] = {
|
||||
#include "mask_layout2x4.lbm"
|
||||
};
|
||||
|
||||
const ZoneOption OPTIONS_LAYOUT_2x4[] = {
|
||||
{ "Top bar", ZoneOption::Bool },
|
||||
{ "Flight mode", ZoneOption::Bool },
|
||||
{ "Sliders", ZoneOption::Bool },
|
||||
{ "Trims", ZoneOption::Bool },
|
||||
{ "Panel1 background", ZoneOption::Bool },
|
||||
{ " Color", ZoneOption::Color },
|
||||
{ "Panel2 background", ZoneOption::Bool },
|
||||
{ " Color", ZoneOption::Color },
|
||||
{ NULL, ZoneOption::Bool }
|
||||
};
|
||||
|
||||
class Layout2x4: public Layout
|
||||
{
|
||||
public:
|
||||
Layout2x4(const LayoutFactory * factory, Layout::PersistentData * persistentData):
|
||||
Layout(factory, persistentData)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void create()
|
||||
{
|
||||
Layout::create();
|
||||
persistentData->options[0].boolValue = true;
|
||||
persistentData->options[1].boolValue = true;
|
||||
persistentData->options[2].boolValue = true;
|
||||
persistentData->options[3].boolValue = true;
|
||||
persistentData->options[4].boolValue = true;
|
||||
persistentData->options[5].unsignedValue = RGB(77, 112, 203);
|
||||
persistentData->options[6].boolValue = false;
|
||||
persistentData->options[7].unsignedValue = RGB(77, 112, 203);
|
||||
}
|
||||
|
||||
virtual unsigned int getZonesCount() const
|
||||
{
|
||||
return 8;
|
||||
}
|
||||
|
||||
virtual Zone getZone(unsigned int index) const
|
||||
{
|
||||
Zone zone;
|
||||
zone.x = (index >= 4) ? 260 : 60;
|
||||
zone.y = 56 + (index % 4) * 42;
|
||||
zone.w = 160;
|
||||
zone.h = 32;
|
||||
return zone;
|
||||
}
|
||||
|
||||
virtual void refresh();
|
||||
};
|
||||
|
||||
void Layout2x4::refresh()
|
||||
{
|
||||
theme->drawBackground();
|
||||
|
||||
if (persistentData->options[0].boolValue) {
|
||||
drawTopBar();
|
||||
}
|
||||
|
||||
if (persistentData->options[1].boolValue) {
|
||||
// Flight mode
|
||||
lcdDrawSizedText(LCD_W / 2 - getTextWidth(g_model.flightModeData[mixerCurrentFlightMode].name,
|
||||
sizeof(g_model.flightModeData[mixerCurrentFlightMode].name),
|
||||
ZCHAR | SMLSIZE) / 2,
|
||||
237,
|
||||
g_model.flightModeData[mixerCurrentFlightMode].name,
|
||||
sizeof(g_model.flightModeData[mixerCurrentFlightMode].name), ZCHAR | SMLSIZE);
|
||||
}
|
||||
|
||||
if (persistentData->options[2].boolValue) {
|
||||
// Pots and rear sliders positions
|
||||
drawMainPots();
|
||||
}
|
||||
|
||||
if (persistentData->options[3].boolValue) {
|
||||
// Trims
|
||||
drawTrims(mixerCurrentFlightMode);
|
||||
}
|
||||
|
||||
if (persistentData->options[4].boolValue) {
|
||||
lcdSetColor(persistentData->options[5].unsignedValue);
|
||||
lcdDrawSolidFilledRect(50, 50, 180, 170, CUSTOM_COLOR);
|
||||
}
|
||||
|
||||
if (persistentData->options[6].boolValue) {
|
||||
lcdSetColor(persistentData->options[7].unsignedValue);
|
||||
lcdDrawSolidFilledRect(250, 50, 180, 170, CUSTOM_COLOR);
|
||||
}
|
||||
|
||||
Layout::refresh();
|
||||
}
|
||||
|
||||
BaseLayoutFactory<Layout2x4> layout2x4("Layout2x4", LBM_LAYOUT_2x4, OPTIONS_LAYOUT_2x4);
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 169 B |
Binary file not shown.
|
After Width: | Height: | Size: 233 B |
Binary file not shown.
|
After Width: | Height: | Size: 178 B |
Binary file not shown.
|
After Width: | Height: | Size: 208 B |
Binary file not shown.
|
After Width: | Height: | Size: 212 B |
@@ -0,0 +1,688 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include "opentx.h"
|
||||
|
||||
#if defined(SIMU)
|
||||
display_t displayBuf[DISPLAY_BUFFER_SIZE];
|
||||
#endif
|
||||
|
||||
|
||||
uint16_t lcdColorTable[LCD_COLOR_COUNT];
|
||||
|
||||
coord_t lcdNextPos;
|
||||
|
||||
#define FONT_CHARS_COUNT 103
|
||||
|
||||
char getMappedChar(unsigned char c)
|
||||
{
|
||||
if (c == 0)
|
||||
return 0;
|
||||
else if (c >= 0x80 && c <= 0x85)
|
||||
return 115 + c - 0x80;
|
||||
else if (c < 0xC0)
|
||||
return c - 0x20;
|
||||
else
|
||||
return c - 0xC0 + 96;
|
||||
}
|
||||
|
||||
int getFontPatternWidth(const uint16_t * spec, int index)
|
||||
{
|
||||
return spec[index+1] - spec[index];
|
||||
}
|
||||
|
||||
int getCharWidth(char c, const uint16_t * spec)
|
||||
{
|
||||
return getFontPatternWidth(spec, getMappedChar(c));
|
||||
}
|
||||
|
||||
#define FONT_MAX_HEIGHT 42
|
||||
void lcdPutFontPattern(coord_t x, coord_t y, const uint8_t * font, const uint16_t * spec, int index, LcdFlags flags)
|
||||
{
|
||||
coord_t offset = spec[index];
|
||||
coord_t width = spec[index+1] - offset;
|
||||
if (width > 0) lcdDrawBitmapPattern(x, y, font, flags, offset, width);
|
||||
lcdNextPos = x + width;
|
||||
}
|
||||
|
||||
void lcdDrawChar(coord_t x, coord_t y, const unsigned char c, LcdFlags flags)
|
||||
{
|
||||
uint32_t fontindex = FONTINDEX(flags);
|
||||
const pm_uchar * font = fontsTable[fontindex];
|
||||
const uint16_t * fontspecs = fontspecsTable[fontindex];
|
||||
lcdPutFontPattern(x, y, font, fontspecs, getMappedChar(c), flags);
|
||||
}
|
||||
|
||||
uint8_t getStringInfo(const char *s)
|
||||
{
|
||||
uint8_t result = 0;
|
||||
for (int i=0; s[i]; i++) {
|
||||
result += s[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
uint8_t getFontHeight(LcdFlags flags)
|
||||
{
|
||||
static const uint8_t heightTable[16] = { 12, 10, 11, 14, 32, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12 };
|
||||
return heightTable[FONTINDEX(flags)];
|
||||
}
|
||||
|
||||
int getTextWidth(const char * s, int len, LcdFlags flags)
|
||||
{
|
||||
const uint16_t * specs = fontspecsTable[FONTINDEX(flags)];
|
||||
|
||||
int result = 0;
|
||||
for (int i=0; len==0 || i<len; ++i) {
|
||||
char c;
|
||||
if (flags & ZCHAR)
|
||||
c = idx2char(*s);
|
||||
else
|
||||
c = *s;
|
||||
if (c == '\0')
|
||||
break;
|
||||
result += getCharWidth(c, specs);
|
||||
++s;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void lcdDrawTextAtIndex(coord_t x, coord_t y, const pm_char * s, uint8_t idx, LcdFlags flags)
|
||||
{
|
||||
uint8_t length;
|
||||
length = pgm_read_byte(s++);
|
||||
lcdDrawSizedText(x, y, s+length*idx, length, flags & ~ZCHAR);
|
||||
}
|
||||
|
||||
void lcdDrawHexNumber(coord_t x, coord_t y, uint32_t val, LcdFlags flags)
|
||||
{
|
||||
char s[5];
|
||||
for (int i=0; i<4; i++) {
|
||||
char c = val & 0xf;
|
||||
s[3-i] = c>9 ? c+'A'-10 : c+'0';
|
||||
val >>= 4;
|
||||
}
|
||||
s[4] = '\0';
|
||||
lcdDrawText(x, y, s, flags);
|
||||
}
|
||||
|
||||
void lcdDrawNumber(coord_t x, coord_t y, int32_t val, LcdFlags flags, uint8_t len, const char * prefix, const char * suffix)
|
||||
{
|
||||
char str[48+1]; // max=16 for the prefix, 16 chars for the number, 16 chars for the suffix
|
||||
char *s = str+32;
|
||||
*s = '\0';
|
||||
int idx = 0;
|
||||
int mode = MODE(flags);
|
||||
bool neg = false;
|
||||
if (val < 0) {
|
||||
val = -val;
|
||||
neg = true;
|
||||
}
|
||||
do {
|
||||
*--s = '0' + (val % 10);
|
||||
++idx;
|
||||
val /= 10;
|
||||
if (mode!=0 && idx==mode) {
|
||||
mode = 0;
|
||||
*--s = '.';
|
||||
if (val==0)
|
||||
*--s = '0';
|
||||
}
|
||||
} while (val!=0 || mode>0 || (mode==MODE(LEADING0) && idx<len));
|
||||
if (neg) *--s = '-';
|
||||
|
||||
// TODO needs check on all string lengths ...
|
||||
if (prefix) {
|
||||
int len = strlen(prefix);
|
||||
if (len <= 16) {
|
||||
s -= len;
|
||||
strncpy(s, prefix, len);
|
||||
}
|
||||
}
|
||||
if (suffix) {
|
||||
strncpy(&str[32], suffix, 16);
|
||||
}
|
||||
flags &= ~LEADING0;
|
||||
lcdDrawText(x, y, s, flags);
|
||||
}
|
||||
|
||||
#if !defined(BOOT)
|
||||
void lcdDrawLine(coord_t x1, coord_t y1, coord_t x2, coord_t y2, uint8_t pat, LcdFlags att)
|
||||
{
|
||||
int dx = x2-x1; /* the horizontal distance of the line */
|
||||
int dy = y2-y1; /* the vertical distance of the line */
|
||||
int dxabs = abs(dx);
|
||||
int dyabs = abs(dy);
|
||||
int sdx = sgn(dx);
|
||||
int sdy = sgn(dy);
|
||||
int x = dyabs>>1;
|
||||
int y = dxabs>>1;
|
||||
int px = x1;
|
||||
int py = y1;
|
||||
|
||||
if (dxabs >= dyabs) {
|
||||
/* the line is more horizontal than vertical */
|
||||
for (int i=0; i<=dxabs; i++) {
|
||||
y += dyabs;
|
||||
if (y>=dxabs) {
|
||||
y -= dxabs;
|
||||
py += sdy;
|
||||
}
|
||||
if ((1<<(px%8)) & pat) {
|
||||
lcdDrawPoint(px, py, att);
|
||||
}
|
||||
px += sdx;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* the line is more vertical than horizontal */
|
||||
for (int i=0; i<=dyabs; i++) {
|
||||
x += dxabs;
|
||||
if (x >= dyabs) {
|
||||
x -= dyabs;
|
||||
px += sdx;
|
||||
}
|
||||
if ((1<<(py%8)) & pat) {
|
||||
lcdDrawPoint(px, py, att);
|
||||
}
|
||||
py += sdy;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void putsRtcTime(coord_t x, coord_t y, LcdFlags att)
|
||||
{
|
||||
putsTimer(x, y, getValue(MIXSRC_TX_TIME), att);
|
||||
}
|
||||
|
||||
void getTimerString(char * str, putstime_t tme, LcdFlags att)
|
||||
{
|
||||
div_t qr;
|
||||
|
||||
if (tme < 0) {
|
||||
// TODO lcdDrawChar(x - ((att & DBLSIZE) ? FW+2 : ((att & MIDSIZE) ? FW+0 : FWNUM)), y, '-', att);
|
||||
tme = -tme;
|
||||
*str++ = '-';
|
||||
}
|
||||
|
||||
qr = div(tme, 60);
|
||||
|
||||
if (att & TIMEHOUR) {
|
||||
div_t qr2 = div(qr.quot, 60);
|
||||
*str++ = '0' + (qr2.quot/10);
|
||||
*str++ = '0' + (qr2.quot%10);
|
||||
*str++ = ':';
|
||||
qr.quot = qr2.rem;
|
||||
}
|
||||
|
||||
*str++ = '0' + (qr.quot/10);
|
||||
*str++ = '0' + (qr.quot%10);
|
||||
*str++ = ':';
|
||||
*str++ = '0' + (qr.rem/10);
|
||||
*str++ = '0' + (qr.rem%10);
|
||||
*str = '\0';
|
||||
}
|
||||
|
||||
void putsTimer(coord_t x, coord_t y, putstime_t tme, LcdFlags att)
|
||||
{
|
||||
char str[LEN_TIMER_STRING]; // "-00:00:00"
|
||||
getTimerString(str, tme, att);
|
||||
lcdDrawText(x, y, str, att);
|
||||
}
|
||||
|
||||
void drawStringWithIndex(coord_t x, coord_t y, const pm_char * str, int idx, LcdFlags att, const char * prefix)
|
||||
{
|
||||
char s[64];
|
||||
strAppendUnsigned(strAppend(strAppend(s, prefix), str), abs(idx));
|
||||
lcdDrawText(x, y, s, att);
|
||||
}
|
||||
|
||||
void putsStickName(coord_t x, coord_t y, uint8_t idx, LcdFlags att)
|
||||
{
|
||||
uint8_t length = STR_VSRCRAW[0];
|
||||
lcdDrawSizedText(x, y, STR_VSRCRAW+2+length*(idx+1), length-1, att);
|
||||
}
|
||||
|
||||
char * getStringAtIndex(char * dest, const char * s, int idx)
|
||||
{
|
||||
uint8_t len = s[0];
|
||||
strncpy(dest, s+1+len*idx, len);
|
||||
dest[len] = '\0';
|
||||
return dest;
|
||||
}
|
||||
|
||||
char * getStringWithIndex(char * dest, const char * s, int idx)
|
||||
{
|
||||
strAppendUnsigned(strAppend(dest, s), abs(idx));
|
||||
return dest;
|
||||
}
|
||||
|
||||
char * getSwitchString(char * dest, swsrc_t idx)
|
||||
{
|
||||
if (idx == SWSRC_NONE) {
|
||||
return getStringAtIndex(dest, STR_VSWITCHES, 0);
|
||||
}
|
||||
else if (idx == SWSRC_OFF) {
|
||||
return getStringAtIndex(dest, STR_OFFON, 0);
|
||||
}
|
||||
|
||||
char * s = dest;
|
||||
if (idx < 0) {
|
||||
*s++ = '!';
|
||||
idx = -idx;
|
||||
}
|
||||
|
||||
if (idx <= SWSRC_LAST_SWITCH) {
|
||||
div_t swinfo = switchInfo(idx);
|
||||
if (ZEXIST(g_eeGeneral.switchNames[swinfo.quot])) {
|
||||
s += zchar2str(s, g_eeGeneral.switchNames[swinfo.quot], LEN_SWITCH_NAME);
|
||||
// TODO tous zchar2str
|
||||
}
|
||||
else {
|
||||
*s++ = 'S';
|
||||
*s++ = 'A'+swinfo.quot;
|
||||
}
|
||||
*s++ = "\300-\301"[swinfo.rem];
|
||||
*s = '\0';
|
||||
}
|
||||
else if (idx <= SWSRC_LAST_MULTIPOS_SWITCH) {
|
||||
div_t swinfo = div(idx - SWSRC_FIRST_MULTIPOS_SWITCH, XPOTS_MULTIPOS_COUNT);
|
||||
getStringWithIndex(s, "S", swinfo.quot*10+swinfo.rem+11);
|
||||
}
|
||||
else if (idx <= SWSRC_LAST_TRIM) {
|
||||
getStringAtIndex(s, STR_VSWITCHES, idx-SWSRC_FIRST_TRIM+1);
|
||||
}
|
||||
else if (idx <= SWSRC_LAST_LOGICAL_SWITCH) {
|
||||
getStringWithIndex(s, "L", idx-SWSRC_FIRST_LOGICAL_SWITCH+1);
|
||||
}
|
||||
else if (idx <= SWSRC_ONE) {
|
||||
getStringAtIndex(s, STR_VSWITCHES, idx-SWSRC_ON+2+(SWSRC_LAST_TRIM-SWSRC_FIRST_TRIM));
|
||||
}
|
||||
else if (idx <= SWSRC_LAST_FLIGHT_MODE) {
|
||||
getStringWithIndex(s, STR_FP, idx-SWSRC_FIRST_FLIGHT_MODE);
|
||||
}
|
||||
else if (idx == SWSRC_TELEMETRY_STREAMING) {
|
||||
strcpy(s, "Tele");
|
||||
}
|
||||
else {
|
||||
zchar2str(s, g_model.telemetrySensors[idx-SWSRC_FIRST_SENSOR].label, TELEM_LABEL_LEN);
|
||||
}
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
char * getSourceString(char * dest, mixsrc_t idx)
|
||||
{
|
||||
if (idx == MIXSRC_NONE) {
|
||||
return getStringAtIndex(dest, STR_VSRCRAW, 0);
|
||||
}
|
||||
else if (idx <= MIXSRC_LAST_INPUT) {
|
||||
idx -= MIXSRC_FIRST_INPUT;
|
||||
*dest++ = '\314';
|
||||
if (ZEXIST(g_model.inputNames[idx])) {
|
||||
zchar2str(dest, g_model.inputNames[idx], LEN_INPUT_NAME);
|
||||
dest[LEN_INPUT_NAME] = '\0';
|
||||
}
|
||||
else {
|
||||
strAppendUnsigned(dest, idx, 2);
|
||||
}
|
||||
}
|
||||
else if (idx <= MIXSRC_LAST_LUA) {
|
||||
#if defined(LUA_MODEL_SCRIPTS)
|
||||
div_t qr = div(idx-MIXSRC_FIRST_LUA, MAX_SCRIPT_OUTPUTS);
|
||||
if (qr.quot < MAX_SCRIPTS && qr.rem < scriptInputsOutputs[qr.quot].outputsCount) {
|
||||
*dest++ = '\322';
|
||||
// *dest++ = '1'+qr.quot;
|
||||
strcpy(dest, scriptInputsOutputs[qr.quot].outputs[qr.rem].name);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else if (idx <= MIXSRC_LAST_POT) {
|
||||
idx -= MIXSRC_Rud;
|
||||
if (ZEXIST(g_eeGeneral.anaNames[idx])) {
|
||||
zchar2str(dest, g_eeGeneral.anaNames[idx], LEN_ANA_NAME);
|
||||
dest[LEN_ANA_NAME] = '\0';
|
||||
}
|
||||
else {
|
||||
getStringAtIndex(dest, STR_VSRCRAW, idx + 1);
|
||||
}
|
||||
}
|
||||
else if (idx <= MIXSRC_LAST_TRIM) {
|
||||
idx -= MIXSRC_Rud;
|
||||
getStringAtIndex(dest, STR_VSRCRAW, idx + 1);
|
||||
}
|
||||
else if (idx <= MIXSRC_LAST_SWITCH) {
|
||||
idx -= MIXSRC_FIRST_SWITCH;
|
||||
if (ZEXIST(g_eeGeneral.switchNames[idx])) {
|
||||
zchar2str(dest, g_eeGeneral.switchNames[idx], LEN_SWITCH_NAME);
|
||||
dest[LEN_SWITCH_NAME] = '\0';
|
||||
}
|
||||
else {
|
||||
getStringAtIndex(dest, STR_VSRCRAW, idx + MIXSRC_FIRST_SWITCH - MIXSRC_Rud + 1);
|
||||
}
|
||||
}
|
||||
else if (idx <= MIXSRC_LAST_LOGICAL_SWITCH) {
|
||||
getSwitchString(dest, SWSRC_SW1 + idx - MIXSRC_SW1);
|
||||
}
|
||||
else if (idx <= MIXSRC_LAST_TRAINER) {
|
||||
getStringWithIndex(dest, STR_PPM_TRAINER, idx - MIXSRC_FIRST_TRAINER + 1);
|
||||
}
|
||||
else if (idx <= MIXSRC_LAST_CH) {
|
||||
getStringWithIndex(dest, STR_CH, idx - MIXSRC_CH1 + 1);
|
||||
}
|
||||
else if (idx <= MIXSRC_LAST_GVAR) {
|
||||
getStringWithIndex(dest, STR_GV, idx - MIXSRC_GVAR1 + 1);
|
||||
}
|
||||
else if (idx < MIXSRC_FIRST_TELEM) {
|
||||
getStringAtIndex(dest, STR_VSRCRAW, idx-MIXSRC_Rud+1-NUM_LOGICAL_SWITCH-NUM_TRAINER-NUM_CHNOUT-MAX_GVARS);
|
||||
}
|
||||
else {
|
||||
idx -= MIXSRC_FIRST_TELEM;
|
||||
div_t qr = div(idx, 3);
|
||||
dest[0] = '\321';
|
||||
int pos = 1 + zchar2str(&dest[1], g_model.telemetrySensors[qr.quot].label, sizeof(g_model.telemetrySensors[qr.quot].label));
|
||||
if (qr.rem) dest[pos++] = (qr.rem==2 ? '+' : '-');
|
||||
dest[pos] = '\0';
|
||||
}
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
void putsMixerSource(coord_t x, coord_t y, uint32_t idx, LcdFlags flags)
|
||||
{
|
||||
char s[16];
|
||||
getSourceString(s, idx);
|
||||
lcdDrawText(x, y, s, flags);
|
||||
}
|
||||
|
||||
void putsChnLetter(coord_t x, coord_t y, uint8_t idx, LcdFlags att)
|
||||
{
|
||||
lcdDrawTextAtIndex(x, y, STR_RETA123, idx-1, att);
|
||||
}
|
||||
|
||||
void putsModelName(coord_t x, coord_t y, char *name, uint8_t id, LcdFlags att)
|
||||
{
|
||||
uint8_t len = sizeof(g_model.header.name);
|
||||
while (len>0 && !name[len-1]) --len;
|
||||
if (len==0) {
|
||||
drawStringWithIndex(x, y, STR_MODEL, id+1, att|LEADING0);
|
||||
}
|
||||
else {
|
||||
lcdDrawSizedText(x, y, name, sizeof(g_model.header.name), ZCHAR|att);
|
||||
}
|
||||
}
|
||||
|
||||
void putsSwitches(coord_t x, coord_t y, swsrc_t idx, LcdFlags flags)
|
||||
{
|
||||
char s[8];
|
||||
getSwitchString(s, idx);
|
||||
lcdDrawText(x, y, s, flags);
|
||||
}
|
||||
|
||||
void putsFlightMode(coord_t x, coord_t y, int8_t idx, LcdFlags att)
|
||||
{
|
||||
if (idx==0) {
|
||||
lcdDrawTextAtIndex(x, y, STR_MMMINV, 0, att);
|
||||
}
|
||||
else {
|
||||
drawStringWithIndex(x, y, STR_FP, abs(idx)-1, att);
|
||||
}
|
||||
}
|
||||
|
||||
void putsCurveRef(coord_t x, coord_t y, CurveRef &curve, LcdFlags att)
|
||||
{
|
||||
if (curve.value != 0) {
|
||||
switch (curve.type) {
|
||||
case CURVE_REF_DIFF:
|
||||
lcdDrawText(x, y, "D", att);
|
||||
GVAR_MENU_ITEM(lcdNextPos, y, curve.value, -100, 100, LEFT|att, 0, 0);
|
||||
break;
|
||||
|
||||
case CURVE_REF_EXPO:
|
||||
lcdDrawText(x, y, "E", att);
|
||||
GVAR_MENU_ITEM(lcdNextPos, y, curve.value, -100, 100, LEFT|att, 0, 0);
|
||||
break;
|
||||
|
||||
case CURVE_REF_FUNC:
|
||||
lcdDrawTextAtIndex(x, y, STR_VCURVEFUNC, curve.value, att);
|
||||
break;
|
||||
|
||||
case CURVE_REF_CUSTOM:
|
||||
drawCurveName(x, y, curve.value, att);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void drawCurveName(coord_t x, coord_t y, int8_t idx, LcdFlags flags)
|
||||
{
|
||||
if (idx == 0) {
|
||||
return lcdDrawTextAtIndex(x, y, STR_MMMINV, 0, flags);
|
||||
}
|
||||
bool neg = false;
|
||||
if (idx < 0) {
|
||||
idx = -idx;
|
||||
neg = true;
|
||||
}
|
||||
if (ZEXIST(g_model.curves[idx-1].name))
|
||||
lcdDrawSizedText(x, y, g_model.curves[idx-1].name, LEN_CURVE_NAME, ZCHAR|flags);
|
||||
else
|
||||
drawStringWithIndex(x, y, STR_CV, idx, flags);
|
||||
if (neg) {
|
||||
if ((flags&INVERS) && ((~flags&BLINK) || BLINK_ON_PHASE))
|
||||
flags &= ~(INVERS|BLINK);
|
||||
lcdDrawChar(x-3, y, '!', flags);
|
||||
}
|
||||
}
|
||||
|
||||
void putsTimerMode(coord_t x, coord_t y, int8_t mode, LcdFlags att)
|
||||
{
|
||||
if (mode >= 0) {
|
||||
if (mode < TMRMODE_COUNT)
|
||||
return lcdDrawTextAtIndex(x, y, STR_VTMRMODES, mode, att);
|
||||
else
|
||||
mode -= (TMRMODE_COUNT-1);
|
||||
}
|
||||
putsSwitches(x, y, mode, att);
|
||||
}
|
||||
|
||||
void putsTrimMode(coord_t x, coord_t y, uint8_t phase, uint8_t idx, LcdFlags att)
|
||||
{
|
||||
trim_t v = getRawTrimValue(phase, idx);
|
||||
unsigned int mode = v.mode;
|
||||
unsigned int p = mode >> 1;
|
||||
|
||||
if (mode == TRIM_MODE_NONE) {
|
||||
lcdDrawText(x, y, "--", att);
|
||||
}
|
||||
else {
|
||||
char s[2];
|
||||
s[0] = (mode % 2 == 0) ? ':' : '+';
|
||||
s[1] = '0'+p;
|
||||
lcdDrawSizedText(x, y, s, 2, att);
|
||||
}
|
||||
}
|
||||
|
||||
const pm_uint8_t bchunit_ar[] PROGMEM = {
|
||||
UNIT_DIST, // Alt
|
||||
UNIT_RAW, // Rpm
|
||||
UNIT_PERCENT, // Fuel
|
||||
UNIT_TEMPERATURE, // T1
|
||||
UNIT_TEMPERATURE, // T2
|
||||
UNIT_KTS, // Speed
|
||||
UNIT_DIST, // Dist
|
||||
UNIT_DIST, // GPS Alt
|
||||
};
|
||||
|
||||
void putsValueWithUnit(coord_t x, coord_t y, int32_t val, uint8_t unit, LcdFlags att)
|
||||
{
|
||||
// convertUnit(val, unit);
|
||||
if (!(att & NO_UNIT) && unit != UNIT_RAW) {
|
||||
char unitStr[8];
|
||||
strAppend(unitStr, STR_VTELEMUNIT+1+unit*STR_VTELEMUNIT[0], STR_VTELEMUNIT[0]);
|
||||
lcdDrawNumber(x, y, val, att, 0, NULL, unitStr);
|
||||
}
|
||||
else {
|
||||
lcdDrawNumber(x, y, val, att);
|
||||
}
|
||||
}
|
||||
|
||||
void displayDate(coord_t x, coord_t y, TelemetryItem & telemetryItem, LcdFlags att)
|
||||
{
|
||||
}
|
||||
|
||||
void displayGpsCoords(coord_t x, coord_t y, TelemetryItem & telemetryItem, LcdFlags att)
|
||||
{
|
||||
}
|
||||
|
||||
void putsTelemetryChannelValue(coord_t x, coord_t y, uint8_t channel, int32_t value, LcdFlags att)
|
||||
{
|
||||
TelemetryItem & telemetryItem = telemetryItems[channel];
|
||||
TelemetrySensor & telemetrySensor = g_model.telemetrySensors[channel];
|
||||
|
||||
if (telemetrySensor.unit == UNIT_DATETIME) {
|
||||
displayDate(x, y, telemetryItem, att);
|
||||
}
|
||||
else if (telemetrySensor.unit == UNIT_GPS) {
|
||||
displayGpsCoords(x, y, telemetryItem, att);
|
||||
}
|
||||
else {
|
||||
LcdFlags flags = att;
|
||||
if (telemetrySensor.prec==2)
|
||||
flags |= PREC2;
|
||||
else if (telemetrySensor.prec==1)
|
||||
flags |= PREC1;
|
||||
putsValueWithUnit(x, y, value, telemetrySensor.unit == UNIT_CELLS ? UNIT_VOLTS : telemetrySensor.unit, flags);
|
||||
}
|
||||
}
|
||||
|
||||
void putsChannelValue(coord_t x, coord_t y, source_t channel, int32_t value, LcdFlags att)
|
||||
{
|
||||
if (channel >= MIXSRC_FIRST_TELEM) {
|
||||
channel = (channel-MIXSRC_FIRST_TELEM) / 3;
|
||||
putsTelemetryChannelValue(x, y, channel, value, att);
|
||||
}
|
||||
else if (channel >= MIXSRC_FIRST_TIMER || channel == MIXSRC_TX_TIME) {
|
||||
putsTimer(x, y, value, att);
|
||||
}
|
||||
else if (channel == MIXSRC_TX_VOLTAGE) {
|
||||
lcdDrawNumber(x, y, value, att|PREC1);
|
||||
}
|
||||
else if (channel < MIXSRC_FIRST_CH) {
|
||||
lcdDrawNumber(x, y, calcRESXto100(value), att);
|
||||
}
|
||||
else if (channel <= MIXSRC_LAST_CH) {
|
||||
#if defined(PPM_UNIT_PERCENT_PREC1)
|
||||
lcdDrawNumber(x, y, calcRESXto1000(value), att|PREC1);
|
||||
#else
|
||||
lcdDrawNumber(x, y, calcRESXto100(value), att);
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
lcdDrawNumber(x, y, value, att);
|
||||
}
|
||||
}
|
||||
|
||||
void putsChannel(coord_t x, coord_t y, source_t channel, LcdFlags att)
|
||||
{
|
||||
getvalue_t value = getValue(channel);
|
||||
putsChannelValue(x, y, channel, value, att);
|
||||
}
|
||||
|
||||
void lcdSetContrast()
|
||||
{
|
||||
lcdSetRefVolt(g_eeGeneral.contrast);
|
||||
}
|
||||
|
||||
void lcdDrawPoint(coord_t x, coord_t y, LcdFlags att)
|
||||
{
|
||||
display_t * p = PIXEL_PTR(x, y);
|
||||
display_t color = lcdColorTable[COLOR_IDX(att)];
|
||||
if (p < DISPLAY_END) {
|
||||
*p = color;
|
||||
}
|
||||
}
|
||||
|
||||
void lcdDrawBlackOverlay()
|
||||
{
|
||||
lcdDrawFilledRect(0, 0, LCD_W, LCD_H, SOLID, OVERLAY_COLOR | OPACITY(8));
|
||||
}
|
||||
|
||||
#if defined(SIMU)
|
||||
BitmapBuffer _lcd(BMP_RGB565, LCD_W, LCD_H, displayBuf);
|
||||
BitmapBuffer * lcd = &_lcd;
|
||||
|
||||
void DMAFillRect(uint16_t * dest, uint16_t destw, uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t color)
|
||||
{
|
||||
for (int i=0; i<h; i++) {
|
||||
for (int j=0; j<w; j++) {
|
||||
dest[(y+i)*destw+x+j] = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DMACopyBitmap(uint16_t * dest, uint16_t destw, uint16_t x, uint16_t y, const uint16_t * src, uint16_t srcw, uint16_t srcx, uint16_t srcy, uint16_t w, uint16_t h)
|
||||
{
|
||||
for (int i=0; i<h; i++) {
|
||||
memcpy(dest+(y+i)*destw+x, src+(srcy+i)*srcw+srcx, 2*w);
|
||||
}
|
||||
}
|
||||
|
||||
void DMACopyAlphaBitmap(uint16_t * dest, uint16_t destw, uint16_t x, uint16_t y, const uint16_t * src, uint16_t srcw, uint16_t srcx, uint16_t srcy, uint16_t w, uint16_t h)
|
||||
{
|
||||
for (coord_t line=0; line<h; line++) {
|
||||
uint16_t * p = dest + (y+line)*destw + x;
|
||||
const uint16_t * q = src + (srcy+line)*srcw + srcx;
|
||||
for (coord_t col=0; col<w; col++) {
|
||||
uint8_t alpha = *q >> 12;
|
||||
uint8_t red = ((((*q >> 8) & 0x0f) << 1) * alpha + (*p >> 11) * (0x0f-alpha)) / 0x0f;
|
||||
uint8_t green = ((((*q >> 4) & 0x0f) << 2) * alpha + ((*p >> 5) & 0x3f) * (0x0f-alpha)) / 0x0f;
|
||||
uint8_t blue = ((((*q >> 0) & 0x0f) << 1) * alpha + ((*p >> 0) & 0x1f) * (0x0f-alpha)) / 0x0f;
|
||||
*p = (red << 11) + (green << 5) + (blue << 0);
|
||||
p++; q++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DMABitmapConvert(uint16_t * dest, const uint8_t * src, uint16_t w, uint16_t h, uint32_t format)
|
||||
{
|
||||
if (format == DMA2D_ARGB4444) {
|
||||
for (int row = 0; row < h; ++row) {
|
||||
for (int col = 0; col < w; ++col) {
|
||||
*dest = ARGB(src[0], src[1], src[2], src[3]);
|
||||
++dest;
|
||||
src += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int row = 0; row < h; ++row) {
|
||||
for(int col = 0; col < w; ++col) {
|
||||
*dest = RGB(src[1], src[2], src[3]);
|
||||
++dest;
|
||||
src += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,273 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _LCD_H_
|
||||
#define _LCD_H_
|
||||
|
||||
#include "bitmapbuffer.h"
|
||||
|
||||
#if defined(PCBHORUS)
|
||||
#define LCD_W 480
|
||||
#define LCD_H 272
|
||||
#define LCD_COLS 30
|
||||
#else
|
||||
#define LCD_W 800
|
||||
#define LCD_H 480
|
||||
#define LCD_COLS 40
|
||||
#endif
|
||||
|
||||
#define CENTER
|
||||
|
||||
#define lcdint_t int32_t
|
||||
|
||||
#define BSS 0x00
|
||||
|
||||
/* lcd common flags */
|
||||
#define BLINK 0x01
|
||||
|
||||
/* drawText flags */
|
||||
#define INVERS 0x02
|
||||
#define LEFT 0x00
|
||||
#define CENTERED 0x04 /* align left */
|
||||
#define RIGHT 0x08 /* align left */
|
||||
|
||||
/* drawNumber flags */
|
||||
#define LEADING0 0x10
|
||||
#define PREC1 0x20
|
||||
#define PREC2 0x30
|
||||
#define MODE(flags) ((((int8_t)(flags) & 0x30) - 0x10) >> 4)
|
||||
|
||||
#define ZCHAR 0x10
|
||||
|
||||
/* rect, square flags */
|
||||
#define ROUND 0x04
|
||||
|
||||
/* telemetry flags */
|
||||
#define NO_UNIT 0x40
|
||||
|
||||
enum FontSizeIndex {
|
||||
STDSIZE_INDEX,
|
||||
TINSIZE_INDEX,
|
||||
SMLSIZE_INDEX,
|
||||
MIDSIZE_INDEX,
|
||||
DBLSIZE_INDEX,
|
||||
XXLSIZE_INDEX,
|
||||
SPARE6_INDEX,
|
||||
SPARE7_INDEX,
|
||||
STDSIZE_BOLD_INDEX,
|
||||
SPARE9_INDEX,
|
||||
SPAREa_INDEX,
|
||||
SPAREb_INDEX,
|
||||
SPAREc_INDEX,
|
||||
SPAREd_INDEX,
|
||||
SPAREe_INDEX,
|
||||
SPAREf_INDEX,
|
||||
};
|
||||
|
||||
#define TINSIZE (TINSIZE_INDEX << 8)
|
||||
#define SMLSIZE (SMLSIZE_INDEX << 8)
|
||||
#define MIDSIZE (MIDSIZE_INDEX << 8)
|
||||
#define DBLSIZE (DBLSIZE_INDEX << 8)
|
||||
#define XXLSIZE (XXLSIZE_INDEX << 8)
|
||||
#define BOLD (STDSIZE_BOLD_INDEX << 8)
|
||||
#define FONTSIZE(flags) ((flags) & 0x0f00)
|
||||
#define FONTINDEX(flags) (FONTSIZE(flags) >> 8)
|
||||
|
||||
#define TIMEBLINK 0x1000
|
||||
#define TIMEHOUR 0x2000
|
||||
#define VERTICAL 0x4000
|
||||
#define NO_FONTCACHE 0x8000
|
||||
|
||||
#include "colors.h"
|
||||
|
||||
#define DISPLAY_PIXELS_COUNT (LCD_W*LCD_H)
|
||||
#define DISPLAY_BUFFER_SIZE (sizeof(display_t)*DISPLAY_PIXELS_COUNT)
|
||||
|
||||
#if defined(SIMU)
|
||||
extern display_t displayBuf[DISPLAY_BUFFER_SIZE];
|
||||
#else
|
||||
#define displayBuf lcd->getData()
|
||||
#endif
|
||||
|
||||
#define lcdRefreshWait()
|
||||
|
||||
#define DISPLAY_END (displayBuf + DISPLAY_BUFFER_SIZE)
|
||||
#define ASSERT_IN_DISPLAY(p) assert((p) >= displayBuf && (p) < DISPLAY_END)
|
||||
|
||||
extern coord_t lcdNextPos;
|
||||
|
||||
void lcdDrawChar(coord_t x, coord_t y, const unsigned char c, LcdFlags attr=0);
|
||||
|
||||
void lcdDrawTextAtIndex(coord_t x, coord_t y, const pm_char * s, uint8_t idx, LcdFlags attr=0);
|
||||
|
||||
inline void lcdClear()
|
||||
{
|
||||
lcd->clear();
|
||||
}
|
||||
|
||||
inline void lcdDrawText(coord_t x, coord_t y, const char * s, LcdFlags attr=0)
|
||||
{
|
||||
lcd->drawText(x, y, s, attr);
|
||||
}
|
||||
|
||||
inline void lcdDrawSizedText(coord_t x, coord_t y, const pm_char * s, uint8_t len, LcdFlags attr=0)
|
||||
{
|
||||
lcd->drawSizedText(x, y, s, len, attr);
|
||||
}
|
||||
|
||||
void lcdDrawHexNumber(coord_t x, coord_t y, uint32_t val, LcdFlags mode=0);
|
||||
void lcdDrawNumber(coord_t x, coord_t y, int32_t val, LcdFlags flags=0, uint8_t len=0, const char * prefix=NULL, const char * suffix=NULL);
|
||||
|
||||
void drawStringWithIndex(coord_t x, coord_t y, const pm_char *str, int idx, LcdFlags att=0, const char *prefix="");
|
||||
void putsModelName(coord_t x, coord_t y, char *name, uint8_t id, LcdFlags att);
|
||||
void putsStickName(coord_t x, coord_t y, uint8_t idx, LcdFlags att=0);
|
||||
void putsSwitches(coord_t x, coord_t y, swsrc_t swtch, LcdFlags flags=0);
|
||||
void putsMixerSource(coord_t x, coord_t y, uint32_t idx, LcdFlags att=0);
|
||||
void putsFlightMode(coord_t x, coord_t y, int8_t idx, LcdFlags att=0);
|
||||
void putsCurveRef(coord_t x, coord_t y, CurveRef &curve, LcdFlags att=0);
|
||||
void drawCurveName(coord_t x, coord_t y, int8_t idx, LcdFlags att=0);
|
||||
void putsTimerMode(coord_t x, coord_t y, int8_t mode, LcdFlags att=0);
|
||||
void putsTrimMode(coord_t x, coord_t y, uint8_t phase, uint8_t idx, LcdFlags att);
|
||||
|
||||
#define putsChn(x, y, idx, att) putsMixerSource(x, y, MIXSRC_CH1+idx-1, att)
|
||||
void putsChnLetter(coord_t x, coord_t y, uint8_t idx, LcdFlags attr);
|
||||
|
||||
void putsChannelValue(coord_t x, coord_t y, source_t channel, int32_t val, LcdFlags att=0);
|
||||
void putsChannel(coord_t x, coord_t y, source_t channel, LcdFlags att=0);
|
||||
void putsTelemetryChannelValue(coord_t x, coord_t y, uint8_t channel, int32_t val, LcdFlags att=0);
|
||||
|
||||
#define putstime_t int32_t
|
||||
|
||||
#define LEN_TIMER_STRING 10
|
||||
void getTimerString(char * str, putstime_t tme, LcdFlags att=0);
|
||||
void putsRtcTime(coord_t x, coord_t y, LcdFlags att=0);
|
||||
void putsTimer(coord_t x, coord_t y, putstime_t tme, LcdFlags att=0);
|
||||
|
||||
#define SOLID 0xff
|
||||
#define DOTTED 0x55
|
||||
#define STASHED 0x33
|
||||
|
||||
#define PIXEL_PTR(x, y) &displayBuf[(y)*LCD_W + (x)]
|
||||
|
||||
void lcdDrawAlphaPixel(display_t * p, uint8_t opacity, uint16_t color);
|
||||
void lcdDrawPoint(coord_t x, coord_t y, LcdFlags att=0);
|
||||
|
||||
inline void lcdDrawSolidHorizontalLine(coord_t x, coord_t y, coord_t w, LcdFlags att)
|
||||
{
|
||||
lcd->drawSolidHorizontalLine(x, y, w, att);
|
||||
}
|
||||
|
||||
inline void lcdDrawSolidVerticalLine(coord_t x, coord_t y, coord_t h, LcdFlags att)
|
||||
{
|
||||
lcd->drawSolidVerticalLine(x, y, h, att);
|
||||
}
|
||||
|
||||
inline void lcdDrawSolidFilledRect(coord_t x, coord_t y, coord_t w, coord_t h, LcdFlags flags)
|
||||
{
|
||||
lcd->drawSolidFilledRect(x, y, w, h, flags);
|
||||
}
|
||||
|
||||
inline void lcdDrawHorizontalLine(coord_t x, coord_t y, coord_t w, uint8_t pat, LcdFlags att=0)
|
||||
{
|
||||
lcd->drawHorizontalLine(x, y, w, pat, att);
|
||||
}
|
||||
|
||||
inline void lcdDrawVerticalLine(coord_t x, coord_t y, coord_t h, uint8_t pat, LcdFlags att=0)
|
||||
{
|
||||
lcd->drawVerticalLine(x, y, h, pat, att);
|
||||
}
|
||||
|
||||
void lcdDrawLine(coord_t x1, coord_t y1, coord_t x2, coord_t y2, uint8_t pat=SOLID, LcdFlags att=0);
|
||||
|
||||
inline void lcdDrawAlphaPixel(coord_t x, coord_t y, uint8_t opacity, uint16_t color)
|
||||
{
|
||||
display_t * p = PIXEL_PTR(x, y);
|
||||
lcdDrawAlphaPixel(p, opacity, color);
|
||||
}
|
||||
|
||||
inline void lcdSetColor(uint16_t color)
|
||||
{
|
||||
lcdColorTable[CUSTOM_COLOR_INDEX] = color;
|
||||
}
|
||||
|
||||
inline void lcdDrawSolidRect(coord_t x, coord_t y, coord_t w, coord_t h, uint8_t thickness=1, LcdFlags att=0)
|
||||
{
|
||||
lcd->drawSolidFilledRect(x, y, thickness, h, att);
|
||||
lcd->drawSolidFilledRect(x+w-thickness, y, thickness, h, att);
|
||||
lcd->drawSolidFilledRect(x, y, w, thickness, att);
|
||||
lcd->drawSolidFilledRect(x, y+h-thickness, w, thickness, att);
|
||||
}
|
||||
|
||||
inline void lcdDrawFilledRect(coord_t x, coord_t y, coord_t w, coord_t h, uint8_t pat, LcdFlags att=0)
|
||||
{
|
||||
lcd->drawFilledRect(x, y, w, h, pat, att);
|
||||
}
|
||||
|
||||
void lcdDrawBlackOverlay();
|
||||
inline void lcdDrawRect(coord_t x, coord_t y, coord_t w, coord_t h, uint8_t thickness=1, uint8_t pat=SOLID, LcdFlags att=0)
|
||||
{
|
||||
lcd->drawRect(x, y, w, h, thickness, pat, att);
|
||||
}
|
||||
|
||||
void lcdDrawCircle(int x0, int y0, int radius);
|
||||
void lcdDrawPie(int x0, int y0, int radius, int angle1=0, int angle2=360);
|
||||
void lcdDrawBitmapPie(int x0, int y0, const uint16_t * img, int startAngle, int endAngle);
|
||||
|
||||
inline void lcdDrawBitmapPatternPie(coord_t x0, coord_t y0, const uint8_t * bmp, LcdFlags flags=0, int startAngle=0, int endAngle=360)
|
||||
{
|
||||
lcd->drawBitmapPatternPie(x0, y0, bmp, flags, startAngle, endAngle);
|
||||
}
|
||||
|
||||
inline void lcdDrawSquare(coord_t x, coord_t y, coord_t w, LcdFlags att=0)
|
||||
{
|
||||
lcdDrawSolidRect(x, y, w, w, att);
|
||||
}
|
||||
|
||||
inline int getBitmapWidth(const uint8_t * bmp)
|
||||
{
|
||||
return *((const uint16_t *)bmp);
|
||||
}
|
||||
|
||||
inline int getBitmapHeight(const uint8_t * bmp)
|
||||
{
|
||||
return *(((const uint16_t *)bmp)+1);
|
||||
}
|
||||
|
||||
char getMappedChar(unsigned char c);
|
||||
uint8_t getFontHeight(LcdFlags flags);
|
||||
int getTextWidth(const pm_char *s, int len=0, LcdFlags flags=0);
|
||||
|
||||
inline void lcdDrawBitmapPattern(coord_t x, coord_t y, const uint8_t * img, LcdFlags flags=0, coord_t offset=0, coord_t width=0)
|
||||
{
|
||||
lcd->drawBitmapPattern(x, y, img, flags, offset, width);
|
||||
}
|
||||
|
||||
#define lcdSetRefVolt(...)
|
||||
void lcdSetContrast();
|
||||
#define lcdOff(...)
|
||||
|
||||
#if defined(BOOT)
|
||||
#define BLINK_ON_PHASE (0)
|
||||
#else
|
||||
#define BLINK_ON_PHASE (g_blinkTmr10ms & (1<<6))
|
||||
#define SLOW_BLINK_ON_PHASE (g_blinkTmr10ms & (1<<7))
|
||||
#endif
|
||||
|
||||
#endif // _LCD_H_
|
||||
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "opentx.h"
|
||||
|
||||
const MenuHandlerFunc menuTabModel[] = {
|
||||
menuModelSetup,
|
||||
CASE_HELI(menuModelHeli)
|
||||
CASE_FLIGHT_MODES(menuModelFlightModesAll)
|
||||
menuModelExposAll,
|
||||
menuModelMixAll,
|
||||
menuModelLimits,
|
||||
CASE_CURVES(menuModelCurvesAll)
|
||||
#if defined(GVARS) && defined(FLIGHT_MODES)
|
||||
CASE_GVARS(menuModelGVars)
|
||||
#endif
|
||||
menuModelLogicalSwitches,
|
||||
menuModelCustomFunctions,
|
||||
#if defined(LUA_MODEL_SCRIPTS)
|
||||
menuModelCustomScripts,
|
||||
#endif
|
||||
CASE_FRSKY(menuModelTelemetry)
|
||||
CASE_MAVLINK(menuTelemetryMavlinkSetup)
|
||||
CASE_TEMPLATES(menuModelTemplates)
|
||||
};
|
||||
|
||||
extern uint8_t s_curveChan;
|
||||
|
||||
void editCurveRef(coord_t x, coord_t y, CurveRef & curve, evt_t event, uint8_t attr);
|
||||
|
||||
uint8_t editDelay(const coord_t x, const coord_t y, const evt_t event, const uint8_t attr, uint8_t delay)
|
||||
{
|
||||
lcdDrawNumber(x+MIXES_2ND_COLUMN, y, (10/DELAY_STEP)*delay, attr|PREC1|LEFT);
|
||||
if (attr) CHECK_INCDEC_MODELVAR_ZERO(event, delay, DELAY_MAX);
|
||||
return delay;
|
||||
}
|
||||
|
||||
uint8_t s_copyMode = 0;
|
||||
int8_t s_copySrcRow;
|
||||
int8_t s_copyTgtOfs;
|
||||
uint8_t s_copySrcIdx;
|
||||
uint8_t s_copySrcCh;
|
||||
|
||||
uint8_t editNameCursorPos = 0;
|
||||
|
||||
void editName(coord_t x, coord_t y, char * name, uint8_t size, evt_t event, uint8_t active, LcdFlags flags)
|
||||
{
|
||||
if (active && s_editMode <= 0) {
|
||||
flags |= INVERS;
|
||||
}
|
||||
|
||||
if (!active || s_editMode <= 0) {
|
||||
if (flags & ZCHAR) {
|
||||
uint8_t len = zlen(name, size);
|
||||
if (len == 0) {
|
||||
char tmp[] = "---";
|
||||
lcdDrawSizedText(x, y, tmp, size, flags-ZCHAR);
|
||||
}
|
||||
else {
|
||||
lcdDrawSizedText(x, y, name, len, flags);
|
||||
}
|
||||
}
|
||||
else {
|
||||
lcdDrawText(x, y, name, flags);
|
||||
}
|
||||
}
|
||||
|
||||
if (active) {
|
||||
if (s_editMode > 0) {
|
||||
int8_t c = name[editNameCursorPos];
|
||||
if (!(flags & ZCHAR)) {
|
||||
c = char2idx(c);
|
||||
}
|
||||
int8_t v = c;
|
||||
|
||||
if (event==EVT_ROTARY_RIGHT || event==EVT_ROTARY_LEFT) {
|
||||
v = checkIncDec(event, abs(v), 0, ZCHAR_MAX, 0);
|
||||
if (c <= 0) v = -v;
|
||||
}
|
||||
|
||||
switch (event) {
|
||||
case EVT_KEY_BREAK(KEY_LEFT):
|
||||
if (editNameCursorPos>0) editNameCursorPos--;
|
||||
break;
|
||||
|
||||
case EVT_KEY_BREAK(KEY_RIGHT):
|
||||
if (editNameCursorPos<size-1) editNameCursorPos++;
|
||||
break;
|
||||
|
||||
case EVT_KEY_BREAK(KEY_ENTER):
|
||||
if (s_editMode == EDIT_MODIFY_FIELD) {
|
||||
s_editMode = EDIT_MODIFY_STRING;
|
||||
editNameCursorPos = 0;
|
||||
}
|
||||
else if (editNameCursorPos<size-1)
|
||||
editNameCursorPos++;
|
||||
else
|
||||
s_editMode = 0;
|
||||
break;
|
||||
|
||||
case EVT_KEY_LONG(KEY_ENTER):
|
||||
if (v == 0) {
|
||||
s_editMode = 0;
|
||||
killEvents(event);
|
||||
break;
|
||||
}
|
||||
// no break
|
||||
|
||||
case EVT_KEY_LONG(KEY_LEFT):
|
||||
case EVT_KEY_LONG(KEY_RIGHT):
|
||||
if (v>=-26 && v<=26) {
|
||||
v = -v; // toggle case
|
||||
if (event==EVT_KEY_LONG(KEY_LEFT))
|
||||
killEvents(KEY_LEFT);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (c != v) {
|
||||
if (!(flags & ZCHAR)) {
|
||||
if (v != '\0' || name[editNameCursorPos+1] != '\0')
|
||||
v = idx2char(v);
|
||||
}
|
||||
name[editNameCursorPos] = v;
|
||||
storageDirty(menuVerticalPositions[0] == 0 ? EE_MODEL : EE_GENERAL);
|
||||
}
|
||||
|
||||
lcdDrawSizedText(x, y, name, size, flags);
|
||||
coord_t left = (editNameCursorPos == 0 ? 0 : getTextWidth(name, editNameCursorPos, flags));
|
||||
char s[] = { (flags & ZCHAR) ? idx2char(name[editNameCursorPos]) : name[editNameCursorPos], '\0' };
|
||||
lcdDrawSolidFilledRect(x+left-1, y, getTextWidth(s, 1)+1, INVERT_LINE_HEIGHT, TEXT_INVERTED_BGCOLOR);
|
||||
lcdDrawText(x+left, y, s, TEXT_INVERTED_COLOR);
|
||||
}
|
||||
else {
|
||||
editNameCursorPos = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t s_currIdx;
|
||||
|
||||
void copySelection(char * dst, const char * src, uint8_t size)
|
||||
{
|
||||
if (memcmp(src, "---", 3) == 0)
|
||||
memset(dst, 0, size);
|
||||
else
|
||||
memcpy(dst, src, size);
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "opentx.h"
|
||||
|
||||
const MenuHandlerFunc menuTabGeneral[] = {
|
||||
menuGeneralSetup,
|
||||
menuGeneralSdManager,
|
||||
menuGeneralCustomFunctions,
|
||||
menuGeneralTrainer,
|
||||
menuGeneralHardware,
|
||||
menuGeneralVersion,
|
||||
};
|
||||
|
||||
bool menuGeneralCustomFunctions(evt_t event)
|
||||
{
|
||||
MENU(STR_MENUGLOBALFUNCS, RADIO_ICONS, menuTabGeneral, e_GeneralCustomFunctions, NUM_CFN, { NAVIGATION_LINE_BY_LINE|4/*repeated*/ });
|
||||
return menuCustomFunctions(event, g_eeGeneral.customFn, globalFunctionsContext);
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "opentx.h"
|
||||
|
||||
MenuHandlerFunc menuHandlers[5];
|
||||
evt_t menuEvent = 0;
|
||||
uint8_t menuVerticalPositions[4];
|
||||
uint8_t menuLevel = 0;
|
||||
|
||||
void popMenu()
|
||||
{
|
||||
assert(menuLevel>0);
|
||||
menuLevel = menuLevel-1;
|
||||
menuEvent = EVT_ENTRY_UP;
|
||||
AUDIO_KEY_PRESS();
|
||||
TRACE("popMenu(%d)", menuLevel);
|
||||
}
|
||||
|
||||
void chainMenu(MenuHandlerFunc newMenu)
|
||||
{
|
||||
menuHandlers[menuLevel] = newMenu;
|
||||
menuEvent = EVT_ENTRY;
|
||||
AUDIO_KEY_PRESS();
|
||||
TRACE("chainMenu(%d, %p)", menuLevel, newMenu);
|
||||
}
|
||||
|
||||
void pushMenu(MenuHandlerFunc newMenu)
|
||||
{
|
||||
killEvents(KEY_ENTER);
|
||||
|
||||
if (menuLevel == 0) {
|
||||
if (newMenu == menuGeneralSetup)
|
||||
menuVerticalPositions[0] = 1;
|
||||
if (newMenu == menuModelSetup)
|
||||
menuVerticalPositions[0] = 0;
|
||||
}
|
||||
else {
|
||||
menuVerticalPositions[menuLevel] = menuVerticalPosition;
|
||||
}
|
||||
|
||||
menuLevel++;
|
||||
|
||||
assert(menuLevel < DIM(menuHandlers));
|
||||
|
||||
menuHandlers[menuLevel] = newMenu;
|
||||
menuEvent = EVT_ENTRY;
|
||||
AUDIO_KEY_PRESS();
|
||||
TRACE("pushMenu(%d, %p)", menuLevel, newMenu);
|
||||
}
|
||||
|
||||
bool menuModelNotes(evt_t event)
|
||||
{
|
||||
if (event == EVT_ENTRY) {
|
||||
strcpy(s_text_file, MODELS_PATH "/");
|
||||
char *buf = strcat_currentmodelname(&s_text_file[sizeof(MODELS_PATH)]);
|
||||
strcpy(buf, TEXT_EXT);
|
||||
}
|
||||
|
||||
return menuTextView(event);
|
||||
}
|
||||
|
||||
void pushModelNotes()
|
||||
{
|
||||
pushMenu(menuModelNotes);
|
||||
}
|
||||
@@ -0,0 +1,531 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _MENUS_H_
|
||||
#define _MENUS_H_
|
||||
|
||||
#include "keys.h"
|
||||
|
||||
#define MENU_COLUMNS 2
|
||||
|
||||
#define MENU_COLUMN2_X 280
|
||||
#define MIXES_2ND_COLUMN 140
|
||||
#define COLUMN_HEADER_X 150
|
||||
|
||||
typedef evt_t & check_event_t;
|
||||
|
||||
extern uint8_t menuPageIndex;
|
||||
extern uint8_t menuPageCount;
|
||||
extern uint16_t linesCount;
|
||||
extern int menuVerticalPosition;
|
||||
extern int menuHorizontalPosition;
|
||||
extern int menuVerticalOffset;
|
||||
extern uint8_t calibrationState;
|
||||
|
||||
// Temporary no highlight
|
||||
extern uint8_t noHighlightCounter;
|
||||
#define NO_HIGHLIGHT() (noHighlightCounter > 0)
|
||||
#define START_NO_HIGHLIGHT() do { noHighlightCounter = 25; } while(0)
|
||||
|
||||
typedef bool (*MenuHandlerFunc)(evt_t event);
|
||||
|
||||
extern MenuHandlerFunc menuHandlers[5];
|
||||
extern uint8_t menuVerticalPositions[4];
|
||||
extern uint8_t menuLevel;
|
||||
extern evt_t menuEvent;
|
||||
|
||||
/// goto given Menu, but substitute current menu in menuStack
|
||||
void chainMenu(MenuHandlerFunc newMenu);
|
||||
/// goto given Menu, store current menu in menuStack
|
||||
void pushMenu(MenuHandlerFunc newMenu);
|
||||
/// return to last menu in menuStack
|
||||
void popMenu();
|
||||
|
||||
enum MenuIcons {
|
||||
ICON_OPENTX,
|
||||
ICON_RADIO,
|
||||
ICON_RADIO_SETUP,
|
||||
ICON_RADIO_SD_BROWSER,
|
||||
ICON_RADIO_GLOBAL_FUNCTIONS,
|
||||
ICON_RADIO_TRAINER,
|
||||
ICON_RADIO_HARDWARE,
|
||||
ICON_RADIO_CALIBRATION,
|
||||
ICON_RADIO_VERSION,
|
||||
ICON_MODEL,
|
||||
ICON_MODEL_SETUP,
|
||||
ICON_MODEL_HELI,
|
||||
ICON_MODEL_FLIGHT_MODES,
|
||||
ICON_MODEL_INPUTS,
|
||||
ICON_MODEL_MIXER,
|
||||
ICON_MODEL_OUTPUTS,
|
||||
ICON_MODEL_CURVES,
|
||||
ICON_MODEL_GVARS,
|
||||
ICON_MODEL_LOGICAL_SWITCHES,
|
||||
ICON_MODEL_SPECIAL_FUNCTIONS,
|
||||
ICON_MODEL_LUA_SCRIPTS,
|
||||
ICON_MODEL_TELEMETRY,
|
||||
ICON_THEME,
|
||||
ICON_THEME_SETUP,
|
||||
ICON_THEME_VIEW1,
|
||||
ICON_THEME_VIEW2,
|
||||
ICON_THEME_VIEW3,
|
||||
ICON_THEME_VIEW4,
|
||||
ICON_THEME_VIEW5,
|
||||
ICON_THEME_ADD_VIEW,
|
||||
ICON_STATS,
|
||||
ICON_STATS_THROTTLE_GRAPH,
|
||||
ICON_STATS_TIMERS,
|
||||
ICON_STATS_ANALOGS,
|
||||
ICON_STATS_DEBUG,
|
||||
ICON_MONITOR,
|
||||
ICON_MONITOR_CHANNELS1,
|
||||
ICON_MONITOR_CHANNELS2,
|
||||
ICON_MONITOR_CHANNELS3,
|
||||
ICON_MONITOR_CHANNELS4,
|
||||
ICON_MONITOR_LOGICAL_SWITCHES,
|
||||
MENUS_ICONS_COUNT
|
||||
};
|
||||
|
||||
enum EnumTabModel {
|
||||
e_ModelSetup,
|
||||
CASE_HELI(e_Heli)
|
||||
CASE_FLIGHT_MODES(e_FlightModesAll)
|
||||
e_InputsAll,
|
||||
e_MixAll,
|
||||
e_Limits,
|
||||
CASE_CURVES(e_CurvesAll)
|
||||
CASE_GVARS(e_GVars)
|
||||
e_LogicalSwitches,
|
||||
e_CustomFunctions,
|
||||
#if defined(LUA_MODEL_SCRIPTS)
|
||||
e_CustomScripts,
|
||||
#endif
|
||||
CASE_FRSKY(e_Telemetry)
|
||||
e_TabModelPagesCount
|
||||
};
|
||||
|
||||
const uint8_t RADIO_ICONS[] = {
|
||||
ICON_RADIO,
|
||||
ICON_RADIO_SETUP,
|
||||
ICON_RADIO_SD_BROWSER,
|
||||
ICON_RADIO_GLOBAL_FUNCTIONS,
|
||||
ICON_RADIO_TRAINER,
|
||||
ICON_RADIO_HARDWARE,
|
||||
ICON_RADIO_VERSION
|
||||
};
|
||||
|
||||
const uint8_t MODEL_ICONS[] = {
|
||||
ICON_MODEL,
|
||||
ICON_MODEL_SETUP,
|
||||
CASE_HELI(ICON_MODEL_HELI)
|
||||
CASE_FLIGHT_MODES(ICON_MODEL_FLIGHT_MODES)
|
||||
ICON_MODEL_INPUTS,
|
||||
ICON_MODEL_MIXER,
|
||||
ICON_MODEL_OUTPUTS,
|
||||
CASE_CURVES(ICON_MODEL_CURVES)
|
||||
CASE_GVARS(ICON_MODEL_GVARS)
|
||||
ICON_MODEL_LOGICAL_SWITCHES,
|
||||
ICON_MODEL_SPECIAL_FUNCTIONS,
|
||||
#if defined(LUA_MODEL_SCRIPTS)
|
||||
ICON_MODEL_LUA_SCRIPTS,
|
||||
#endif
|
||||
ICON_MODEL_TELEMETRY
|
||||
};
|
||||
|
||||
const uint8_t STATS_ICONS[] = {
|
||||
ICON_STATS,
|
||||
ICON_STATS_THROTTLE_GRAPH,
|
||||
ICON_STATS_TIMERS,
|
||||
ICON_STATS_ANALOGS,
|
||||
#if defined(DEBUG_TRACE_BUFFER)
|
||||
ICON_STATS_DEBUG
|
||||
#endif
|
||||
};
|
||||
|
||||
const uint8_t MONITOR_ICONS[] = {
|
||||
ICON_MONITOR,
|
||||
ICON_MONITOR_CHANNELS1,
|
||||
ICON_MONITOR_CHANNELS2,
|
||||
ICON_MONITOR_CHANNELS3,
|
||||
ICON_MONITOR_CHANNELS4,
|
||||
ICON_MONITOR_LOGICAL_SWITCHES
|
||||
};
|
||||
|
||||
bool menuModelSetup(evt_t event);
|
||||
bool menuModelHeli(evt_t event);
|
||||
bool menuModelFlightModesAll(evt_t event);
|
||||
bool menuModelExposAll(evt_t event);
|
||||
bool menuModelMixAll(evt_t event);
|
||||
bool menuModelLimits(evt_t event);
|
||||
bool menuModelCurvesAll(evt_t event);
|
||||
bool menuModelCurveOne(evt_t event);
|
||||
bool menuModelGVars(evt_t event);
|
||||
bool menuModelLogicalSwitches(evt_t event);
|
||||
bool menuModelCustomFunctions(evt_t event);
|
||||
bool menuModelCustomScripts(evt_t event);
|
||||
bool menuModelTelemetry(evt_t event);
|
||||
bool menuModelExpoOne(evt_t event);
|
||||
|
||||
extern const MenuHandlerFunc menuTabModel[e_TabModelPagesCount];
|
||||
|
||||
enum EnumTabRadio {
|
||||
e_Setup,
|
||||
e_Sd,
|
||||
e_GeneralCustomFunctions,
|
||||
e_Trainer,
|
||||
e_Hardware,
|
||||
e_Vers,
|
||||
e_TabRadioPagesCount
|
||||
};
|
||||
|
||||
bool menuGeneralSetup(evt_t event);
|
||||
bool menuGeneralSdManager(evt_t event);
|
||||
bool menuGeneralCustomFunctions(evt_t event);
|
||||
bool menuGeneralTrainer(evt_t event);
|
||||
bool menuGeneralVersion(evt_t event);
|
||||
bool menuGeneralHardware(evt_t event);
|
||||
bool menuGeneralCalib(evt_t event);
|
||||
|
||||
extern const MenuHandlerFunc menuTabGeneral[e_TabRadioPagesCount];
|
||||
|
||||
enum EnumTabDiag
|
||||
{
|
||||
e_StatsGraph,
|
||||
e_StatsDebug,
|
||||
e_StatsAnalogs,
|
||||
#if defined(DEBUG_TRACE_BUFFER)
|
||||
e_StatsTraces,
|
||||
#endif
|
||||
};
|
||||
|
||||
bool menuStatsGraph(evt_t event);
|
||||
bool menuStatsDebug(evt_t event);
|
||||
bool menuStatsAnalogs(evt_t event);
|
||||
bool menuStatsTraces(evt_t event);
|
||||
|
||||
static const MenuHandlerFunc menuTabStats[] PROGMEM = {
|
||||
menuStatsGraph,
|
||||
menuStatsDebug,
|
||||
menuStatsAnalogs,
|
||||
#if defined(DEBUG_TRACE_BUFFER)
|
||||
menuStatsTraces,
|
||||
#endif
|
||||
};
|
||||
|
||||
enum EnumTabMonitors
|
||||
{
|
||||
e_MonChannelsFirst,
|
||||
e_MonChannels1 = e_MonChannelsFirst,
|
||||
e_MonChannels2,
|
||||
e_MonChannels3,
|
||||
e_MonChannels4,
|
||||
e_MonLogicalSwitches,
|
||||
e_MonTabChannelsPagesCount
|
||||
};
|
||||
|
||||
extern const MenuHandlerFunc menuTabMonitors[e_MonTabChannelsPagesCount];
|
||||
extern uint8_t lastMonitorPage;
|
||||
extern void drawSingleMixerBar(coord_t, coord_t, coord_t, coord_t, uint8_t);
|
||||
extern void drawSingleOutputBar(coord_t, coord_t, coord_t, coord_t, uint8_t);
|
||||
|
||||
extern const MenuHandlerFunc menuTabScreensSetup[1+MAX_CUSTOM_SCREENS] PROGMEM;
|
||||
|
||||
bool menuFirstCalib(evt_t event);
|
||||
bool menuMainView(evt_t event);
|
||||
bool menuCustomFunctions(evt_t event, CustomFunctionData * functions, CustomFunctionsContext & functionsContext);
|
||||
bool menuModelSelect(evt_t event);
|
||||
bool menuAboutView(evt_t event);
|
||||
bool menuMainViewChannelsMonitor(evt_t event);
|
||||
bool menuTextView(evt_t event);
|
||||
bool menuScreensTheme(evt_t event);
|
||||
|
||||
typedef uint16_t FlightModesType;
|
||||
|
||||
extern int8_t checkIncDec_Ret; // global helper vars
|
||||
|
||||
#define EDIT_SELECT_MENU -1
|
||||
#define EDIT_SELECT_FIELD 0
|
||||
#define EDIT_MODIFY_FIELD 1
|
||||
#define EDIT_MODIFY_STRING 2
|
||||
extern int8_t s_editMode; // global editmode
|
||||
|
||||
// checkIncDec flags
|
||||
#define EE_GENERAL 0x01
|
||||
#define EE_MODEL 0x02
|
||||
#define NO_INCDEC_MARKS 0x04
|
||||
#define INCDEC_SWITCH 0x08
|
||||
#define INCDEC_SOURCE 0x10
|
||||
#define INCDEC_REP10 0x40
|
||||
#define NO_DBLKEYS 0x80
|
||||
|
||||
// mawrow special values
|
||||
#define TITLE_ROW ((uint8_t)-1)
|
||||
#define ORPHAN_ROW ((uint8_t)-2)
|
||||
#define HIDDEN_ROW ((uint8_t)-3)
|
||||
#define NAVIGATION_LINE_BY_LINE 0x40
|
||||
#define CURSOR_ON_LINE() (menuHorizontalPosition<0)
|
||||
|
||||
struct CheckIncDecStops {
|
||||
const int count;
|
||||
const int stops[];
|
||||
int min() const
|
||||
{
|
||||
return stops[0];
|
||||
}
|
||||
int max() const
|
||||
{
|
||||
return stops[count-1];
|
||||
}
|
||||
bool contains(int value) const
|
||||
{
|
||||
for (int i=0; i<count; ++i) {
|
||||
int stop = stops[i];
|
||||
if (value == stop)
|
||||
return true;
|
||||
else if (value < stop)
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
extern const CheckIncDecStops &stops100;
|
||||
extern const CheckIncDecStops &stops1000;
|
||||
extern const CheckIncDecStops &stopsSwitch;
|
||||
|
||||
#define INIT_STOPS(var, ...) \
|
||||
const int _ ## var[] = { __VA_ARGS__ }; \
|
||||
const CheckIncDecStops &var = (const CheckIncDecStops&)_ ## var;
|
||||
#define CATEGORY_END(val) \
|
||||
(val), (val+1)
|
||||
|
||||
int checkIncDec(evt_t event, int val, int i_min, int i_max, unsigned int i_flags=0, IsValueAvailable isValueAvailable=NULL, const CheckIncDecStops &stops=stops100);
|
||||
swsrc_t checkIncDecMovedSwitch(swsrc_t val);
|
||||
#define checkIncDecModel(event, i_val, i_min, i_max) checkIncDec(event, i_val, i_min, i_max, EE_MODEL)
|
||||
#define checkIncDecModelZero(event, i_val, i_max) checkIncDec(event, i_val, 0, i_max, EE_MODEL)
|
||||
#define checkIncDecGen(event, i_val, i_min, i_max) checkIncDec(event, i_val, i_min, i_max, EE_GENERAL)
|
||||
|
||||
#define CHECK_INCDEC_MODELVAR(event, var, min, max) \
|
||||
var = checkIncDecModel(event, var, min, max)
|
||||
|
||||
#define CHECK_INCDEC_MODELVAR_ZERO(event, var, max) \
|
||||
var = checkIncDecModelZero(event, var, max)
|
||||
|
||||
#define CHECK_INCDEC_MODELVAR_CHECK(event, var, min, max, check) \
|
||||
var = checkIncDec(event, var, min, max, EE_MODEL, check)
|
||||
|
||||
#define CHECK_INCDEC_MODELVAR_ZERO_CHECK(event, var, max, check) \
|
||||
var = checkIncDec(event, var, 0, max, EE_MODEL, check)
|
||||
|
||||
#define AUTOSWITCH_ENTER_LONG() (attr && event==EVT_KEY_LONG(KEY_ENTER))
|
||||
#define CHECK_INCDEC_SWITCH(event, var, min, max, flags, available) \
|
||||
var = checkIncDec(event, var, min, max, (flags)|INCDEC_SWITCH, available)
|
||||
#define CHECK_INCDEC_MODELSWITCH(event, var, min, max, available) \
|
||||
CHECK_INCDEC_SWITCH(event, var, min, max, EE_MODEL, available)
|
||||
|
||||
#define CHECK_INCDEC_MODELSOURCE(event, var, min, max) \
|
||||
var = checkIncDec(event, var, min, max, EE_MODEL|INCDEC_SOURCE|NO_INCDEC_MARKS, isSourceAvailable)
|
||||
|
||||
#define CHECK_INCDEC_GENVAR(event, var, min, max) \
|
||||
var = checkIncDecGen(event, var, min, max)
|
||||
|
||||
#define INCDEC_DECLARE_VARS(f) uint8_t incdecFlag = (f); IsValueAvailable isValueAvailable = NULL
|
||||
#define INCDEC_SET_FLAG(f) incdecFlag = (f)
|
||||
#define INCDEC_ENABLE_CHECK(fn) isValueAvailable = fn
|
||||
#define CHECK_INCDEC_PARAM(event, var, min, max) \
|
||||
checkIncDec(event, var, min, max, incdecFlag, isValueAvailable)
|
||||
|
||||
int8_t navigate(evt_t event, int count, int rows, int columns=1, bool loop=true);
|
||||
bool check(check_event_t event, uint8_t curr, const MenuHandlerFunc * menuTab, uint8_t menuTabSize, const pm_uint8_t *horTab, uint8_t horTabMax, int maxrow, uint8_t flags=0);
|
||||
bool check_simple(check_event_t event, uint8_t curr, const MenuHandlerFunc * menuTab, uint8_t menuTabSize, int maxrow);
|
||||
bool check_submenu_simple(check_event_t event, uint8_t maxrow);
|
||||
|
||||
#define MENU_TAB(...) const uint8_t mstate_tab[] = __VA_ARGS__
|
||||
|
||||
#define MENU_WITH_OPTIONS(title, icons, tab, tabCount, menu, lines_count, ...) \
|
||||
MENU_TAB(__VA_ARGS__); \
|
||||
if (event == EVT_ENTRY || event == EVT_ENTRY_UP) TRACE("Menu %s displayed ...", title); \
|
||||
if (!check(event, menu, tab, tabCount, mstate_tab, DIM(mstate_tab)-1, lines_count)) return false; \
|
||||
drawMenuTemplate(title, 0, icons, OPTION_MENU_TITLE_BAR);
|
||||
|
||||
#define CUSTOM_MENU_WITH_OPTIONS(title, icons, tab, tabCount, menu, lines_count) \
|
||||
if (event == EVT_ENTRY || event == EVT_ENTRY_UP) TRACE("Menu %s displayed ...", title); \
|
||||
if (!check(event, menu, tab, tabCount, mstate_tab, DIM(mstate_tab)-1, lines_count)) return false; \
|
||||
drawMenuTemplate(title, 0, icons, OPTION_MENU_TITLE_BAR);
|
||||
|
||||
#define MENU(title, icons, tab, menu, lines_count, ...) \
|
||||
MENU_WITH_OPTIONS(title, icons, tab, DIM(tab), menu, lines_count, __VA_ARGS__)
|
||||
|
||||
#define SIMPLE_MENU_WITH_OPTIONS(title, icons, tab, tabCount, menu, lines_count) \
|
||||
if (event == EVT_ENTRY || event == EVT_ENTRY_UP) TRACE("Menu %s displayed ...", title); \
|
||||
if (!check_simple(event, menu, tab, tabCount, lines_count)) return false; \
|
||||
drawMenuTemplate(title, 0, icons, OPTION_MENU_TITLE_BAR);
|
||||
|
||||
#define SIMPLE_MENU(title, icons, tab, menu, lines_count) \
|
||||
SIMPLE_MENU_WITH_OPTIONS(title, icons, tab, DIM(tab), menu, lines_count)
|
||||
|
||||
#define SUBMENU(title, icon, lines_count, ...) \
|
||||
MENU_TAB(__VA_ARGS__); \
|
||||
if (!check(event, 0, NULL, 0, mstate_tab, DIM(mstate_tab)-1, lines_count)) return false; \
|
||||
drawMenuTemplate(title, icon);
|
||||
|
||||
#define SUBMENU_WITH_OPTIONS(title, icon, lines_count, options, ...) \
|
||||
MENU_TAB(__VA_ARGS__); \
|
||||
if (!check(event, 0, NULL, 0, mstate_tab, DIM(mstate_tab)-1, lines_count)) return false; \
|
||||
drawMenuTemplate(title, icon, NULL, options);
|
||||
|
||||
#define CUSTOM_SUBMENU_WITH_OPTIONS(title, icon, lines_count, options) \
|
||||
if (!check(event, 0, NULL, 0, mstate_tab, DIM(mstate_tab)-1, lines_count)) return false; \
|
||||
drawMenuTemplate(title, icon, NULL, options);
|
||||
|
||||
#define SIMPLE_SUBMENU(title, icon, lines_count) \
|
||||
if (!check_submenu_simple(event, lines_count)) return false; \
|
||||
drawMenuTemplate(title, icon, NULL)
|
||||
|
||||
#define SIMPLE_SUBMENU_WITH_OPTIONS(title, icon, lines_count, options) \
|
||||
if (!check_submenu_simple(event, lines_count)) return false; \
|
||||
drawMenuTemplate(title, icon, NULL, options)
|
||||
|
||||
typedef int select_menu_value_t;
|
||||
|
||||
select_menu_value_t selectMenuItem(coord_t x, coord_t y, const pm_char * values, select_menu_value_t value, select_menu_value_t min, select_menu_value_t max, LcdFlags attr, evt_t event);
|
||||
uint8_t editCheckBox(uint8_t value, coord_t x, coord_t y, LcdFlags attr, evt_t event);
|
||||
|
||||
#if defined(GVARS)
|
||||
#define GVAR_MENU_ITEM(x, y, v, min, max, lcdattr, editflags, event) editGVarFieldValue(x, y, v, min, max, lcdattr, editflags, event)
|
||||
#else
|
||||
#define GVAR_MENU_ITEM(x, y, v, min, max, lcdattr, editflags, event) editGVarFieldValue(x, y, v, min, max, lcdattr, event)
|
||||
#endif
|
||||
|
||||
#if defined(GVARS)
|
||||
int16_t editGVarFieldValue(coord_t x, coord_t y, int16_t value, int16_t min, int16_t max, LcdFlags attr, uint8_t editflags, evt_t event);
|
||||
#define displayGVar(x, y, v, min, max) GVAR_MENU_ITEM(x, y, v, min, max, 0, 0, 0)
|
||||
#else
|
||||
int16_t editGVarFieldValue(coord_t x, coord_t y, int16_t value, int16_t min, int16_t max, LcdFlags attr, evt_t event);
|
||||
#define displayGVar(x, y, v, min, max) lcdDrawNumber(x, y, v)
|
||||
#endif
|
||||
|
||||
extern uint8_t editNameCursorPos;
|
||||
void editName(coord_t x, coord_t y, char *name, uint8_t size, evt_t event, uint8_t active, LcdFlags flags=ZCHAR);
|
||||
|
||||
uint8_t editDelay(const coord_t x, const coord_t y, const evt_t event, const uint8_t attr, uint8_t delay);
|
||||
void editCurveRef(coord_t x, coord_t y, CurveRef & curve, evt_t event, uint8_t attr);
|
||||
|
||||
extern uint8_t s_curveChan;
|
||||
|
||||
#define WARNING_TYPE_ALERT 0
|
||||
#define WARNING_TYPE_ASTERISK 1
|
||||
#define WARNING_TYPE_CONFIRM 2
|
||||
#define WARNING_TYPE_INPUT 3
|
||||
#define WARNING_TYPE_INFO 4
|
||||
|
||||
extern const pm_char * warningText;
|
||||
extern const pm_char * warningInfoText;
|
||||
extern uint8_t warningInfoLength;
|
||||
extern uint8_t warningResult;
|
||||
extern uint8_t warningType;
|
||||
|
||||
#define COPY_MODE 1
|
||||
#define MOVE_MODE 2
|
||||
extern uint8_t s_copyMode;
|
||||
extern int8_t s_copySrcRow;
|
||||
extern int8_t s_copyTgtOfs;
|
||||
extern uint8_t s_currIdx;
|
||||
extern int8_t s_currCh;
|
||||
extern uint8_t s_copySrcIdx;
|
||||
extern uint8_t s_copySrcCh;
|
||||
|
||||
uint8_t getExposCount();
|
||||
void deleteExpo(uint8_t idx);
|
||||
void insertExpo(uint8_t idx);
|
||||
|
||||
uint8_t getMixesCount();
|
||||
void deleteMix(uint8_t idx);
|
||||
void insertMix(uint8_t idx);
|
||||
|
||||
#define MENU_X 80
|
||||
#define MENU_W LCD_W-(2*MENU_X)
|
||||
#define POPUP_X 120
|
||||
#define POPUP_Y 70
|
||||
#define POPUP_W 300
|
||||
#define POPUP_H 130
|
||||
#define WARNING_LINE_LEN 32
|
||||
#define WARNING_LINE_X (POPUP_X+66)
|
||||
#define WARNING_LINE_Y (POPUP_Y+9)
|
||||
#define WARNING_INFOLINE_Y (WARNING_LINE_Y+68)
|
||||
|
||||
void copySelection(char * dst, const char * src, uint8_t size);
|
||||
|
||||
void displayPopup(const char * title);
|
||||
void displayWarning(evt_t event);
|
||||
|
||||
extern void (* popupFunc)(evt_t event);
|
||||
extern int16_t warningInputValue;
|
||||
extern int16_t warningInputValueMin;
|
||||
extern int16_t warningInputValueMax;
|
||||
extern uint8_t warningInfoFlags;
|
||||
|
||||
#define DISPLAY_WARNING (*popupFunc)
|
||||
#define POPUP_WARNING(s) (warningType = WARNING_TYPE_ASTERISK, warningText = s, warningInfoText = 0, popupFunc = displayWarning)
|
||||
#define POPUP_CONFIRMATION(s) (warningText = s, warningType = WARNING_TYPE_CONFIRM, warningInfoText = 0, popupFunc = displayWarning)
|
||||
#define POPUP_INPUT(s, func, start, min, max) (warningText = s, warningType = WARNING_TYPE_INPUT, popupFunc = func, warningInputValue = start, warningInputValueMin = min, warningInputValueMax = max)
|
||||
#define WARNING_INFO_FLAGS warningInfoFlags
|
||||
#define SET_WARNING_INFO(info, len, flags) (warningInfoText = info, warningInfoLength = len, warningInfoFlags = flags)
|
||||
|
||||
#define NAVIGATION_MENUS
|
||||
#define POPUP_MENU_ADD_ITEM(s) do { popupMenuOffsetType = MENU_OFFSET_INTERNAL; if (popupMenuNoItems < POPUP_MENU_MAX_LINES) popupMenuItems[popupMenuNoItems++] = s; } while (0)
|
||||
#define POPUP_MENU_START(func) do { popupMenuHandler = (func); AUDIO_KEY_PRESS(); } while(0)
|
||||
#define POPUP_MENU_MAX_LINES 12
|
||||
#define MENU_MAX_DISPLAY_LINES 9
|
||||
#define MENU_LINE_LENGTH (LEN_MODEL_NAME+12)
|
||||
#define POPUP_MENU_ITEMS_FROM_BSS()
|
||||
extern const char * popupMenuItems[POPUP_MENU_MAX_LINES];
|
||||
extern uint16_t popupMenuNoItems;
|
||||
extern uint16_t popupMenuOffset;
|
||||
enum {
|
||||
MENU_OFFSET_INTERNAL,
|
||||
MENU_OFFSET_EXTERNAL
|
||||
};
|
||||
extern uint8_t popupMenuOffsetType;
|
||||
const char * displayPopupMenu(evt_t event);
|
||||
extern void (*popupMenuHandler)(const char * result);
|
||||
|
||||
#define TEXT_FILENAME_MAXLEN 40
|
||||
extern char s_text_file[TEXT_FILENAME_MAXLEN];
|
||||
void pushMenuTextView(const char * filename);
|
||||
void pushModelNotes();
|
||||
|
||||
#define LABEL(...) (uint8_t)-1
|
||||
|
||||
#define CURSOR_MOVED_LEFT(event) (event==EVT_ROTARY_LEFT || EVT_KEY_MASK(event) == KEY_LEFT)
|
||||
#define CURSOR_MOVED_RIGHT(event) (event==EVT_ROTARY_RIGHT || EVT_KEY_MASK(event) == KEY_RIGHT)
|
||||
#define REPEAT_LAST_CURSOR_MOVE(last, refresh) { if (CURSOR_MOVED_RIGHT(event)) menuHorizontalPosition = (menuHorizontalPosition >= (last) ? 0 : menuHorizontalPosition + 1); else menuHorizontalPosition = (menuHorizontalPosition <= (0) ? last : menuHorizontalPosition - 1); if (refresh) putEvent(EVT_REFRESH); }
|
||||
|
||||
#define MENU_FIRST_LINE_EDIT (MAXCOL((uint16_t)0) >= HIDDEN_ROW ? (MAXCOL((uint16_t)1) >= HIDDEN_ROW ? 2 : 1) : 0)
|
||||
#define POS_HORZ_INIT(posVert) ((COLATTR(posVert) & NAVIGATION_LINE_BY_LINE) ? -1 : 0)
|
||||
#define EDIT_MODE_INIT 0 // TODO enum
|
||||
|
||||
typedef int (*FnFuncP) (int x);
|
||||
void drawFunction(FnFuncP fn, int x, int y, int width);
|
||||
|
||||
void onSourceLongEnterPress(const char *result);
|
||||
|
||||
uint8_t switchToMix(uint8_t source);
|
||||
|
||||
#endif // _MENUS_H_
|
||||
@@ -0,0 +1,382 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "opentx.h"
|
||||
|
||||
uint8_t s_curveChan;
|
||||
|
||||
int curveFn(int x)
|
||||
{
|
||||
return applyCustomCurve(x, s_curveChan);
|
||||
}
|
||||
|
||||
struct point_t {
|
||||
coord_t x;
|
||||
coord_t y;
|
||||
};
|
||||
|
||||
point_t getPoint(uint8_t i)
|
||||
{
|
||||
point_t result = {0, 0};
|
||||
CurveInfo & crv = g_model.curves[s_curveChan];
|
||||
int8_t * points = curveAddress(s_curveChan);
|
||||
bool custom = (crv.type == CURVE_TYPE_CUSTOM);
|
||||
uint8_t count = 5+crv.points;
|
||||
if (i < count) {
|
||||
result.x = CURVE_CENTER_X-1-CURVE_SIDE_WIDTH+i*CURVE_SIDE_WIDTH*2/(count-1);
|
||||
result.y = CURVE_CENTER_Y - (points[i]) * (CURVE_SIDE_WIDTH-1) / 100;
|
||||
if (custom && i>0 && i<count-1) {
|
||||
result.x = CURVE_CENTER_X - 1 - CURVE_SIDE_WIDTH + (100 + (100 + points[count + i - 1]) * (2 * CURVE_SIDE_WIDTH)) / 200;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void drawCurve(int x, int y, int width)
|
||||
{
|
||||
drawFunction(curveFn, x, y, width);
|
||||
}
|
||||
|
||||
extern int8_t * curveEnd[MAX_CURVES];
|
||||
bool moveCurve(uint8_t index, int8_t shift)
|
||||
{
|
||||
if (curveEnd[MAX_CURVES-1] + shift > g_model.points + sizeof(g_model.points)) {
|
||||
AUDIO_WARNING2();
|
||||
return false;
|
||||
}
|
||||
|
||||
int8_t *nextCrv = curveAddress(index+1);
|
||||
memmove(nextCrv+shift, nextCrv, 5*(MAX_CURVES-index-1)+curveEnd[MAX_CURVES-1]-curveEnd[index]);
|
||||
if (shift < 0) memclear(&g_model.points[NUM_POINTS-1] + shift, -shift);
|
||||
while (index<MAX_CURVES) {
|
||||
curveEnd[index++] += shift;
|
||||
}
|
||||
|
||||
storageDirty(EE_MODEL);
|
||||
return true;
|
||||
}
|
||||
|
||||
int8_t getCurveX(int noPoints, int point)
|
||||
{
|
||||
return -100 + div_and_round((point*2000) / (noPoints-1), 10);
|
||||
}
|
||||
|
||||
void resetCustomCurveX(int8_t * points, int noPoints)
|
||||
{
|
||||
for (int i=0; i<noPoints-2; i++) {
|
||||
points[noPoints+i] = getCurveX(noPoints, i+1);
|
||||
}
|
||||
}
|
||||
|
||||
void displayPresetChoice(evt_t event)
|
||||
{
|
||||
displayWarning(event);
|
||||
lcdDrawNumber(WARNING_LINE_X, WARNING_INFOLINE_Y-10, 45*warningInputValue/4, LEFT|INVERS, 0, NULL, "@");
|
||||
|
||||
if (warningResult) {
|
||||
warningResult = 0;
|
||||
CurveInfo & crv = g_model.curves[s_curveChan];
|
||||
int8_t * points = curveAddress(s_curveChan);
|
||||
int k = 25 * warningInputValue;
|
||||
int dx = 2000 / (5+crv.points-1);
|
||||
for (uint8_t i=0; i<5+crv.points; i++) {
|
||||
int x = -1000 + i * dx;
|
||||
points[i] = div_and_round(div_and_round(k * x, 100), 10);
|
||||
}
|
||||
if (crv.type == CURVE_TYPE_CUSTOM) {
|
||||
resetCustomCurveX(points, 5+crv.points);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void onCurveOneMenu(const char * result)
|
||||
{
|
||||
if (result == STR_CURVE_PRESET) {
|
||||
POPUP_INPUT(STR_PRESET, displayPresetChoice, 0, -4, 4);
|
||||
}
|
||||
else if (result == STR_MIRROR) {
|
||||
CurveInfo & crv = g_model.curves[s_curveChan];
|
||||
int8_t * points = curveAddress(s_curveChan);
|
||||
for (int i=0; i<5+crv.points; i++)
|
||||
points[i] = -points[i];
|
||||
}
|
||||
else if (result == STR_CLEAR) {
|
||||
CurveInfo & crv = g_model.curves[s_curveChan];
|
||||
int8_t * points = curveAddress(s_curveChan);
|
||||
for (int i=0; i<5+crv.points; i++)
|
||||
points[i] = 0;
|
||||
if (crv.type == CURVE_TYPE_CUSTOM) {
|
||||
resetCustomCurveX(points, 5+crv.points);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define MODEL_CURVE_ONE_2ND_COLUMN 130
|
||||
|
||||
enum MenuModelCurveOneItems {
|
||||
ITEM_CURVE_NAME,
|
||||
ITEM_CURVE_TYPE,
|
||||
ITEM_CURVE_POINTS,
|
||||
ITEM_CURVE_SMOOTH,
|
||||
ITEM_CURVE_COORDS1,
|
||||
ITEM_CURVE_COORDS2,
|
||||
};
|
||||
|
||||
bool menuModelCurveOne(evt_t event)
|
||||
{
|
||||
static uint8_t pointsOfs = 0;
|
||||
CurveData & crv = g_model.curves[s_curveChan];
|
||||
int8_t * points = curveAddress(s_curveChan);
|
||||
|
||||
SUBMENU_WITH_OPTIONS(STR_MENUCURVE, ICON_MODEL_CURVES, crv.type==CURVE_TYPE_CUSTOM ? 6 : 5, OPTION_MENU_NO_FOOTER, { 0, 0, 0, 0, uint8_t(5+crv.points-1), uint8_t(5+crv.points-1) });
|
||||
drawStringWithIndex(50, 3+FH, STR_CV, s_curveChan+1, MENU_TITLE_COLOR);
|
||||
lcdDrawSolidFilledRect(0, MENU_FOOTER_TOP, 250, MENU_FOOTER_HEIGHT, HEADER_BGCOLOR);
|
||||
|
||||
if (crv.type==CURVE_TYPE_CUSTOM && menuVerticalPosition == ITEM_CURVE_COORDS1) {
|
||||
if (menuHorizontalPosition == 0) {
|
||||
if (CURSOR_MOVED_RIGHT(event))
|
||||
menuHorizontalPosition = 1;
|
||||
else
|
||||
menuVerticalPosition -= 1;
|
||||
}
|
||||
else if (menuHorizontalPosition == 4+crv.points) {
|
||||
if (CURSOR_MOVED_RIGHT(event))
|
||||
(menuHorizontalPosition = 0, menuVerticalPosition += 1);
|
||||
else
|
||||
menuHorizontalPosition -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Curve name
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, MENU_CONTENT_TOP-FH, STR_NAME);
|
||||
editName(MODEL_CURVE_ONE_2ND_COLUMN, MENU_CONTENT_TOP-FH, crv.name, sizeof(crv.name), event, menuVerticalPosition==ITEM_CURVE_NAME);
|
||||
|
||||
// Curve type
|
||||
LcdFlags attr = (menuVerticalPosition==ITEM_CURVE_TYPE ? (s_editMode>0 ? INVERS|BLINK : INVERS) : 0);
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, MENU_CONTENT_TOP, "Type");
|
||||
lcdDrawTextAtIndex(MODEL_CURVE_ONE_2ND_COLUMN, MENU_CONTENT_TOP, STR_CURVE_TYPES, crv.type, attr);
|
||||
if (attr) {
|
||||
uint8_t newType = checkIncDecModelZero(event, crv.type, CURVE_TYPE_LAST);
|
||||
if (newType != crv.type) {
|
||||
for (int i=1; i<4+crv.points; i++) {
|
||||
points[i] = calcRESXto100(applyCustomCurve(calc100toRESX(-100 + i * 200 / (4 + crv.points)), s_curveChan));
|
||||
}
|
||||
moveCurve(s_curveChan, checkIncDec_Ret > 0 ? 3+crv.points : -3-crv.points);
|
||||
if (newType == CURVE_TYPE_CUSTOM) {
|
||||
resetCustomCurveX(points, 5+crv.points);
|
||||
}
|
||||
crv.type = newType;
|
||||
}
|
||||
}
|
||||
|
||||
// Curve points count
|
||||
attr = (menuVerticalPosition==ITEM_CURVE_POINTS ? (s_editMode>0 ? INVERS|BLINK : INVERS) : 0);
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, MENU_CONTENT_TOP + FH, STR_COUNT);
|
||||
lcdDrawNumber(MODEL_CURVE_ONE_2ND_COLUMN, MENU_CONTENT_TOP + FH, 5+crv.points, LEFT|attr, 0, NULL, STR_PTS);
|
||||
if (attr) {
|
||||
int count = checkIncDecModel(event, crv.points, -3, 12); // 2pts - 17pts
|
||||
if (checkIncDec_Ret) {
|
||||
int newPoints[MAX_POINTS];
|
||||
newPoints[0] = points[0];
|
||||
newPoints[4+count] = points[4+crv.points];
|
||||
for (int i=1; i<4+count; i++)
|
||||
newPoints[i] = calcRESXto100(applyCustomCurve(calc100toRESX(-100 + (i*200) / (4+count)), s_curveChan));
|
||||
moveCurve(s_curveChan, checkIncDec_Ret*(crv.type==CURVE_TYPE_CUSTOM?2:1));
|
||||
for (int i=0; i<5+count; i++) {
|
||||
points[i] = newPoints[i];
|
||||
if (crv.type == CURVE_TYPE_CUSTOM && i!=0 && i!=4+count)
|
||||
points[5+count+i-1] = -100 + (i*200) / (4+count);
|
||||
}
|
||||
crv.points = count;
|
||||
}
|
||||
}
|
||||
|
||||
// Curve smooth
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, MENU_CONTENT_TOP + 2*FH, STR_SMOOTH);
|
||||
drawCheckBox(MODEL_CURVE_ONE_2ND_COLUMN, MENU_CONTENT_TOP + 2*FH, crv.smooth, menuVerticalPosition==ITEM_CURVE_SMOOTH ? INVERS : 0);
|
||||
if (menuVerticalPosition==ITEM_CURVE_SMOOTH) crv.smooth = checkIncDecModel(event, crv.smooth, 0, 1);
|
||||
|
||||
switch(event) {
|
||||
case EVT_ENTRY:
|
||||
pointsOfs = 0;
|
||||
break;
|
||||
case EVT_KEY_LONG(KEY_ENTER):
|
||||
if (menuVerticalPosition > ITEM_CURVE_POINTS) {
|
||||
killEvents(event);
|
||||
POPUP_MENU_ADD_ITEM(STR_CURVE_PRESET);
|
||||
POPUP_MENU_ADD_ITEM(STR_MIRROR);
|
||||
POPUP_MENU_ADD_ITEM(STR_CLEAR);
|
||||
POPUP_MENU_START(onCurveOneMenu);
|
||||
}
|
||||
break;
|
||||
// TODO?
|
||||
// case EVT_KEY_LONG(KEY_MENU):
|
||||
// pushMenu(menuChannelsView);
|
||||
// killEvents(event);
|
||||
}
|
||||
|
||||
drawCurve(CURVE_CENTER_X, CURVE_CENTER_Y, CURVE_SIDE_WIDTH);
|
||||
drawCurveHorizontalScale();
|
||||
if (menuVerticalPosition < ITEM_CURVE_COORDS1) drawCurveVerticalScale(CURVE_CENTER_X-CURVE_SIDE_WIDTH-15);
|
||||
|
||||
coord_t posX = 47;
|
||||
attr = (s_editMode > 0 ? INVERS|BLINK : INVERS);
|
||||
for (int i=0; i<5+crv.points; i++) {
|
||||
point_t point = getPoint(i);
|
||||
uint8_t selectionMode = 0;
|
||||
if (menuHorizontalPosition == i) {
|
||||
if (menuVerticalPosition == ITEM_CURVE_COORDS1)
|
||||
selectionMode = (crv.type==CURVE_TYPE_CUSTOM ? 1 : 2);
|
||||
else if (menuVerticalPosition == ITEM_CURVE_COORDS2)
|
||||
selectionMode = 2;
|
||||
}
|
||||
|
||||
int8_t x = -100 + 200*i/(5+crv.points-1);
|
||||
if (crv.type==CURVE_TYPE_CUSTOM && i>0 && i<5+crv.points-1) x = points[5+crv.points+i-1];
|
||||
|
||||
if (i>=pointsOfs && i<pointsOfs+5) {
|
||||
lcdDrawNumber(posX, MENU_CONTENT_TOP + 5*FH-12, i+1, TEXT_DISABLE_COLOR|RIGHT);
|
||||
lcdDrawNumber(posX, MENU_CONTENT_TOP + 6*FH-10, x, RIGHT|(selectionMode==1 ? attr : 0));
|
||||
lcdDrawNumber(posX, MENU_CONTENT_TOP + 7*FH-6, points[i], RIGHT|(selectionMode==2 ? attr : 0));
|
||||
posX += 45;
|
||||
}
|
||||
|
||||
if (selectionMode > 0) {
|
||||
lcdDrawSolidFilledRect(point.x, CURVE_CENTER_Y-CURVE_SIDE_WIDTH, 2, 2*CURVE_SIDE_WIDTH+2, CURVE_CURSOR_COLOR);
|
||||
|
||||
char text[5];
|
||||
strAppendSigned(text, points[i]);
|
||||
|
||||
if (point.x >= CURVE_CENTER_X) {
|
||||
drawCurveVerticalScale(point.x-15);
|
||||
if (points[i-1] > points[i]) {
|
||||
drawCurveCoord(point.x+1-CURVE_COORD_WIDTH, point.y, text, selectionMode==2);
|
||||
}
|
||||
else {
|
||||
drawCurveCoord(point.x+1-CURVE_COORD_WIDTH, point.y-CURVE_COORD_HEIGHT-1, text, selectionMode==2);
|
||||
}
|
||||
}
|
||||
else {
|
||||
drawCurveVerticalScale(point.x+7);
|
||||
if (points[i+1] > points[i]) {
|
||||
drawCurveCoord(point.x+1, point.y, text, selectionMode==2);
|
||||
}
|
||||
else {
|
||||
drawCurveCoord(point.x+1, point.y-CURVE_COORD_HEIGHT-1, text, selectionMode==2);
|
||||
}
|
||||
}
|
||||
|
||||
drawCurvePoint(point.x-3, point.y-4, CURVE_CURSOR_COLOR);
|
||||
|
||||
strAppendSigned(text, x);
|
||||
drawCurveCoord(limit(CURVE_CENTER_X-CURVE_SIDE_WIDTH-1, point.x-CURVE_COORD_WIDTH/2, CURVE_CENTER_X+CURVE_SIDE_WIDTH-CURVE_COORD_WIDTH+1), CURVE_CENTER_Y+CURVE_SIDE_WIDTH+2, text, selectionMode==1);
|
||||
|
||||
if (s_editMode > 0) {
|
||||
if (selectionMode == 1)
|
||||
CHECK_INCDEC_MODELVAR(event, points[5+crv.points+i-1], i==1 ? -100 : points[5+crv.points+i-2], i==5+crv.points-2 ? 100 : points[5+crv.points+i]); // edit X
|
||||
else if (selectionMode == 2)
|
||||
CHECK_INCDEC_MODELVAR(event, points[i], -100, 100);
|
||||
}
|
||||
if (i < pointsOfs)
|
||||
pointsOfs = i;
|
||||
else if (i > pointsOfs+5-1)
|
||||
pointsOfs = i-5+1;
|
||||
}
|
||||
else {
|
||||
drawCurvePoint(point.x-3, point.y-4, TEXT_COLOR);
|
||||
}
|
||||
}
|
||||
|
||||
lcdDrawHorizontalLine(MENUS_MARGIN_LEFT, MENU_CONTENT_TOP + 6*FH - 13, SUBMENU_LINE_WIDTH, DOTTED, CURVE_AXIS_COLOR);
|
||||
lcdDrawHorizontalLine(MENUS_MARGIN_LEFT, MENU_CONTENT_TOP + 7*FH - 10, SUBMENU_LINE_WIDTH, DOTTED, CURVE_AXIS_COLOR);
|
||||
drawHorizontalScrollbar(MENUS_MARGIN_LEFT, MENU_CONTENT_TOP + 9*FH - 15, SUBMENU_LINE_WIDTH, pointsOfs, 5+crv.points, 5);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void editCurveRef(coord_t x, coord_t y, CurveRef & curve, evt_t event, uint8_t attr)
|
||||
{
|
||||
lcdDrawTextAtIndex(x, y, "\004DiffExpoFuncCstm", curve.type, (menuHorizontalPosition==0 ? attr : 0));
|
||||
if (attr && menuHorizontalPosition==0) {
|
||||
CHECK_INCDEC_MODELVAR_ZERO(event, curve.type, CURVE_REF_CUSTOM);
|
||||
if (checkIncDec_Ret) curve.value = 0;
|
||||
}
|
||||
switch (curve.type) {
|
||||
case CURVE_REF_DIFF:
|
||||
case CURVE_REF_EXPO:
|
||||
curve.value = GVAR_MENU_ITEM(lcdNextPos+10, y, curve.value, -100, 100, menuHorizontalPosition==1 ? LEFT|attr : LEFT, 0, event);
|
||||
break;
|
||||
case CURVE_REF_FUNC:
|
||||
lcdDrawTextAtIndex(lcdNextPos+10, y, STR_VCURVEFUNC, curve.value, (menuHorizontalPosition==1 ? attr : 0));
|
||||
if (attr && menuHorizontalPosition==1) CHECK_INCDEC_MODELVAR_ZERO(event, curve.value, CURVE_BASE-1);
|
||||
break;
|
||||
case CURVE_REF_CUSTOM:
|
||||
drawCurveName(lcdNextPos+10, y, curve.value, (menuHorizontalPosition==1 ? attr : 0));
|
||||
if (attr && menuHorizontalPosition==1) {
|
||||
if (event==EVT_KEY_LONG(KEY_ENTER) && curve.value!=0) {
|
||||
s_curveChan = (curve.value<0 ? -curve.value-1 : curve.value-1);
|
||||
pushMenu(menuModelCurveOne);
|
||||
}
|
||||
else {
|
||||
CHECK_INCDEC_MODELVAR(event, curve.value, -MAX_CURVES, MAX_CURVES);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#define CURVES_NAME_POS 60
|
||||
#define CURVES_POINTS_POS 120
|
||||
|
||||
bool menuModelCurvesAll(evt_t event)
|
||||
{
|
||||
SIMPLE_MENU(STR_MENUCURVES, MODEL_ICONS, menuTabModel, e_CurvesAll, MAX_CURVES);
|
||||
|
||||
int8_t sub = menuVerticalPosition;
|
||||
|
||||
switch (event) {
|
||||
case EVT_KEY_BREAK(KEY_ENTER):
|
||||
if (!READ_ONLY()) {
|
||||
s_curveChan = sub;
|
||||
pushMenu(menuModelCurveOne);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
for (int i=0; i<NUM_BODY_LINES; ++i) {
|
||||
coord_t y = MENU_CONTENT_TOP + i*FH;
|
||||
uint8_t k = i + menuVerticalOffset;
|
||||
LcdFlags attr = (sub == k ? INVERS : 0);
|
||||
{
|
||||
drawStringWithIndex(MENUS_MARGIN_LEFT, y, STR_CV, k+1, attr);
|
||||
CurveData & crv = g_model.curves[k];
|
||||
editName(CURVES_NAME_POS, y, crv.name, sizeof(crv.name), 0, 0);
|
||||
lcdDrawNumber(CURVES_POINTS_POS, y, 5+crv.points, LEFT, 0, NULL, STR_PTS);
|
||||
}
|
||||
}
|
||||
|
||||
if (sub >= 0) {
|
||||
s_curveChan = sub;
|
||||
drawCurve(CURVE_CENTER_X, CURVE_CENTER_Y+10, 80);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "opentx.h"
|
||||
|
||||
void onModelCustomScriptMenu(const char *result)
|
||||
{
|
||||
ScriptData &sd = g_model.scriptsData[s_currIdx];
|
||||
|
||||
if (result == STR_UPDATE_LIST) {
|
||||
if (!sdListFiles(SCRIPTS_MIXES_PATH, SCRIPTS_EXT, sizeof(sd.file), NULL)) {
|
||||
POPUP_WARNING(STR_NO_SCRIPTS_ON_SD);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// The user choosed a lua file in the list
|
||||
copySelection(sd.file, result, sizeof(sd.file));
|
||||
memset(sd.inputs, 0, sizeof(sd.inputs));
|
||||
storageDirty(EE_MODEL);
|
||||
LUA_LOAD_MODEL_SCRIPT(s_currIdx);
|
||||
}
|
||||
}
|
||||
|
||||
enum menuModelCustomScriptItems {
|
||||
ITEM_MODEL_CUSTOMSCRIPT_FILE,
|
||||
ITEM_MODEL_CUSTOMSCRIPT_NAME,
|
||||
ITEM_MODEL_CUSTOMSCRIPT_PARAMS_LABEL,
|
||||
};
|
||||
|
||||
#define SCRIPT_ONE_2ND_COLUMN_POS (120)
|
||||
#define SCRIPT_ONE_3RD_COLUMN_POS (240)
|
||||
|
||||
bool menuModelCustomScriptOne(evt_t event)
|
||||
{
|
||||
ScriptData &sd = g_model.scriptsData[s_currIdx];
|
||||
|
||||
// drawStringWithIndex(lcdLastPos+FW, 0, "LUA", s_currIdx+1, 0);
|
||||
|
||||
SUBMENU(STR_MENUCUSTOMSCRIPTS, ICON_MODEL_LUA_SCRIPTS, 3+scriptInputsOutputs[s_currIdx].inputsCount, { 0, 0, LABEL(inputs), 0/*repeated*/ });
|
||||
|
||||
int8_t sub = menuVerticalPosition;
|
||||
|
||||
for (int k=0; k<NUM_BODY_LINES; k++) {
|
||||
coord_t y = MENU_CONTENT_TOP + k*FH;
|
||||
int i = k + menuVerticalOffset;
|
||||
LcdFlags attr = (sub==i ? (s_editMode>0 ? BLINK|INVERS : INVERS) : 0);
|
||||
|
||||
if (i == ITEM_MODEL_CUSTOMSCRIPT_FILE) {
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_SCRIPT);
|
||||
if (ZEXIST(sd.file))
|
||||
lcdDrawSizedText(SCRIPT_ONE_2ND_COLUMN_POS, y, sd.file, sizeof(sd.file), attr);
|
||||
else
|
||||
lcdDrawTextAtIndex(SCRIPT_ONE_2ND_COLUMN_POS, y, STR_VCSWFUNC, 0, attr);
|
||||
if (attr) s_editMode = 0;
|
||||
if (attr && event==EVT_KEY_FIRST(KEY_ENTER) && !READ_ONLY()) {
|
||||
killEvents(KEY_ENTER);
|
||||
if (sdListFiles(SCRIPTS_MIXES_PATH, SCRIPTS_EXT, sizeof(sd.file), sd.file, LIST_NONE_SD_FILE)) {
|
||||
POPUP_MENU_START(onModelCustomScriptMenu);
|
||||
}
|
||||
else {
|
||||
POPUP_WARNING(STR_NO_SCRIPTS_ON_SD);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (i == ITEM_MODEL_CUSTOMSCRIPT_NAME) {
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, TR_NAME);
|
||||
editName(SCRIPT_ONE_2ND_COLUMN_POS, y, sd.name, sizeof(sd.name), event, attr);
|
||||
}
|
||||
else if (i == ITEM_MODEL_CUSTOMSCRIPT_PARAMS_LABEL) {
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_INPUTS);
|
||||
}
|
||||
else if (i <= ITEM_MODEL_CUSTOMSCRIPT_PARAMS_LABEL+scriptInputsOutputs[s_currIdx].inputsCount) {
|
||||
int inputIdx = i-ITEM_MODEL_CUSTOMSCRIPT_PARAMS_LABEL-1;
|
||||
lcdDrawSizedText(INDENT_WIDTH, y, scriptInputsOutputs[s_currIdx].inputs[inputIdx].name, 10, 0);
|
||||
if (scriptInputsOutputs[s_currIdx].inputs[inputIdx].type == 0) {
|
||||
lcdDrawNumber(SCRIPT_ONE_2ND_COLUMN_POS, y, g_model.scriptsData[s_currIdx].inputs[inputIdx]+scriptInputsOutputs[s_currIdx].inputs[inputIdx].def, attr|LEFT);
|
||||
if (attr) {
|
||||
CHECK_INCDEC_MODELVAR(event, g_model.scriptsData[s_currIdx].inputs[inputIdx], scriptInputsOutputs[s_currIdx].inputs[inputIdx].min-scriptInputsOutputs[s_currIdx].inputs[inputIdx].def, scriptInputsOutputs[s_currIdx].inputs[inputIdx].max-scriptInputsOutputs[s_currIdx].inputs[inputIdx].def);
|
||||
}
|
||||
}
|
||||
else {
|
||||
uint8_t *source = (uint8_t *)&g_model.scriptsData[s_currIdx].inputs[inputIdx];
|
||||
putsMixerSource(SCRIPT_ONE_2ND_COLUMN_POS, y, *source + scriptInputsOutputs[s_currIdx].inputs[inputIdx].def, attr);
|
||||
if (attr) {
|
||||
CHECK_INCDEC_MODELSOURCE(event, *source, scriptInputsOutputs[s_currIdx].inputs[inputIdx].min-scriptInputsOutputs[s_currIdx].inputs[inputIdx].def, scriptInputsOutputs[s_currIdx].inputs[inputIdx].max-scriptInputsOutputs[s_currIdx].inputs[inputIdx].def);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (scriptInputsOutputs[s_currIdx].outputsCount > 0) {
|
||||
lcdDrawSolidVerticalLine(SCRIPT_ONE_3RD_COLUMN_POS-4, DEFAULT_SCROLLBAR_Y, DEFAULT_SCROLLBAR_H+5, TEXT_COLOR);
|
||||
// lcdDrawText(SCRIPT_ONE_3RD_COLUMN_POS, FH+1, STR_OUTPUTS);
|
||||
|
||||
for (int i=0; i<scriptInputsOutputs[s_currIdx].outputsCount; i++) {
|
||||
putsMixerSource(SCRIPT_ONE_3RD_COLUMN_POS+INDENT_WIDTH, MENU_CONTENT_TOP+i*FH, MIXSRC_FIRST_LUA+(s_currIdx*MAX_SCRIPT_OUTPUTS)+i, 0);
|
||||
lcdDrawNumber(SCRIPT_ONE_3RD_COLUMN_POS+130, MENU_CONTENT_TOP+i*FH, calcRESXto1000(scriptInputsOutputs[s_currIdx].outputs[i].value), PREC1);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#define SCRIPTS_COLUMN_FILE 70
|
||||
#define SCRIPTS_COLUMN_NAME 160
|
||||
#define SCRIPTS_COLUMN_STATE 300
|
||||
|
||||
bool menuModelCustomScripts(evt_t event)
|
||||
{
|
||||
// lcdDrawNumber(19*FW, 0, luaGetMemUsed(), 0);
|
||||
// lcdDrawText(19*FW+1, 0, STR_BYTES);
|
||||
|
||||
MENU(STR_MENUCUSTOMSCRIPTS, MODEL_ICONS, menuTabModel, e_CustomScripts, MAX_SCRIPTS, { NAVIGATION_LINE_BY_LINE|3/*repeated*/ });
|
||||
|
||||
int8_t sub = menuVerticalPosition;
|
||||
|
||||
if (event == EVT_KEY_FIRST(KEY_ENTER) && sub >= 0) {
|
||||
s_currIdx = sub;
|
||||
pushMenu(menuModelCustomScriptOne);
|
||||
}
|
||||
|
||||
for (int i=0, scriptIndex=0; i<MAX_SCRIPTS; i++) {
|
||||
coord_t y = MENU_CONTENT_TOP + i*FH;
|
||||
|
||||
ScriptData &sd = g_model.scriptsData[i];
|
||||
|
||||
// LUAx header
|
||||
drawStringWithIndex(MENUS_MARGIN_LEFT, y, "LUA", i+1, sub==i ? INVERS : 0);
|
||||
|
||||
// LUA script
|
||||
if (ZEXIST(sd.file)) {
|
||||
lcdDrawSizedText(SCRIPTS_COLUMN_FILE, y, sd.file, sizeof(sd.file), 0);
|
||||
switch (scriptInternalData[scriptIndex].state) {
|
||||
case SCRIPT_SYNTAX_ERROR:
|
||||
lcdDrawText(SCRIPTS_COLUMN_STATE, y, "(error)");
|
||||
break;
|
||||
case SCRIPT_KILLED:
|
||||
lcdDrawText(SCRIPTS_COLUMN_STATE, y, "(killed)");
|
||||
break;
|
||||
default:
|
||||
lcdDrawNumber(SCRIPTS_COLUMN_STATE, y, luaGetCpuUsed(scriptIndex), LEFT|TEXT_COLOR, 0, NULL, "%");
|
||||
break;
|
||||
}
|
||||
scriptIndex++;
|
||||
}
|
||||
else {
|
||||
lcdDrawTextAtIndex(SCRIPTS_COLUMN_FILE, y, STR_VCSWFUNC, 0, 0);
|
||||
}
|
||||
|
||||
// Script name
|
||||
lcdDrawSizedText(SCRIPTS_COLUMN_NAME, y, sd.name, sizeof(sd.name), ZCHAR);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "opentx.h"
|
||||
|
||||
void displayFlightModes(coord_t x, coord_t y, FlightModesType value, uint8_t attr)
|
||||
{
|
||||
for (int i=0; i<MAX_FLIGHT_MODES; i++) {
|
||||
LcdFlags flags = ((menuHorizontalPosition==i && attr) ? INVERS : 0);
|
||||
flags |= ((value & (1<<i))) ? TEXT_DISABLE_COLOR : TEXT_COLOR;
|
||||
if (attr && menuHorizontalPosition < 0) flags |= INVERS;
|
||||
char s[] = " ";
|
||||
s[0] = '0' + i;
|
||||
lcdDrawText(x, y, s, flags);
|
||||
x += 12;
|
||||
}
|
||||
}
|
||||
|
||||
FlightModesType editFlightModes(coord_t x, coord_t y, evt_t event, FlightModesType value, uint8_t attr)
|
||||
{
|
||||
displayFlightModes(x, y, value, attr);
|
||||
|
||||
if (attr) {
|
||||
if (s_editMode && event==EVT_KEY_BREAK(KEY_ENTER)) {
|
||||
s_editMode = 0;
|
||||
value ^= (1<<menuHorizontalPosition);
|
||||
storageDirty(EE_MODEL);
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
enum FlightModesItems {
|
||||
ITEM_FLIGHT_MODES_NAME,
|
||||
ITEM_FLIGHT_MODES_SWITCH,
|
||||
ITEM_FLIGHT_MODES_TRIM_RUD,
|
||||
ITEM_FLIGHT_MODES_TRIM_ELE,
|
||||
ITEM_FLIGHT_MODES_TRIM_THR,
|
||||
ITEM_FLIGHT_MODES_TRIM_AIL,
|
||||
ITEM_FLIGHT_MODES_TRIM_T5,
|
||||
ITEM_FLIGHT_MODES_TRIM_T6,
|
||||
ITEM_FLIGHT_MODES_FADE_IN,
|
||||
ITEM_FLIGHT_MODES_FADE_OUT,
|
||||
ITEM_FLIGHT_MODES_COUNT,
|
||||
ITEM_FLIGHT_MODES_LAST = ITEM_FLIGHT_MODES_COUNT-1
|
||||
};
|
||||
|
||||
bool isTrimModeAvailable(int mode)
|
||||
{
|
||||
return (mode < 0 || (mode%2) == 0 || (mode/2) != menuVerticalPosition);
|
||||
}
|
||||
|
||||
#define FLIGHT_MODES_NAME_COLUMN 50
|
||||
#define FLIGHT_MODES_SWITCH_COLUMN 180
|
||||
#define FLIGHT_MODES_TRIMS_COLUMN 175
|
||||
#define FLIGHT_MODES_TRIM_WIDTH 26
|
||||
#define FLIGHT_MODES_FADEIN_COLUMN 420
|
||||
#define FLIGHT_MODES_FADEOUT_COLUMN 460
|
||||
|
||||
bool menuModelFlightModesAll(evt_t event)
|
||||
{
|
||||
MENU(STR_MENUFLIGHTPHASES, MODEL_ICONS, menuTabModel, e_FlightModesAll, MAX_FLIGHT_MODES+1, { NAVIGATION_LINE_BY_LINE|ITEM_FLIGHT_MODES_LAST, NAVIGATION_LINE_BY_LINE|ITEM_FLIGHT_MODES_LAST, NAVIGATION_LINE_BY_LINE|ITEM_FLIGHT_MODES_LAST, NAVIGATION_LINE_BY_LINE|NAVIGATION_LINE_BY_LINE|ITEM_FLIGHT_MODES_LAST, NAVIGATION_LINE_BY_LINE|ITEM_FLIGHT_MODES_LAST, NAVIGATION_LINE_BY_LINE|ITEM_FLIGHT_MODES_LAST, NAVIGATION_LINE_BY_LINE|ITEM_FLIGHT_MODES_LAST, NAVIGATION_LINE_BY_LINE|ITEM_FLIGHT_MODES_LAST, NAVIGATION_LINE_BY_LINE|ITEM_FLIGHT_MODES_LAST, 0});
|
||||
|
||||
if (menuVerticalPosition==0 && menuHorizontalPosition==ITEM_FLIGHT_MODES_SWITCH) {
|
||||
menuHorizontalPosition += CURSOR_MOVED_LEFT(event) ? -1 : +1;
|
||||
}
|
||||
|
||||
if (menuVerticalPosition<MAX_FLIGHT_MODES && menuHorizontalPosition>=0) {
|
||||
drawColumnHeader(STR_PHASES_HEADERS, NULL, menuHorizontalPosition);
|
||||
}
|
||||
|
||||
for (uint8_t i=0; i<NUM_BODY_LINES; i++) {
|
||||
coord_t y = MENU_CONTENT_TOP + i*FH;
|
||||
int k = i+menuVerticalOffset;
|
||||
|
||||
if (k==MAX_FLIGHT_MODES) {
|
||||
// last line available - add the "check trims" line
|
||||
LcdFlags attr = 0;
|
||||
if (menuVerticalPosition==MAX_FLIGHT_MODES) {
|
||||
if (!trimsCheckTimer) {
|
||||
if (event == EVT_KEY_FIRST(KEY_ENTER)) {
|
||||
trimsCheckTimer = 200; // 2 seconds trims cancelled
|
||||
s_editMode = 1;
|
||||
killEvents(event);
|
||||
}
|
||||
else {
|
||||
attr |= INVERS;
|
||||
s_editMode = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (event == EVT_KEY_FIRST(KEY_EXIT)) {
|
||||
trimsCheckTimer = 0;
|
||||
s_editMode = 0;
|
||||
killEvents(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
char s[32];
|
||||
sprintf(s, "Check FM%d Trims", mixerCurrentFlightMode);
|
||||
lcdDrawText(LCD_W/2, y, s, CENTERED|attr);
|
||||
return true;
|
||||
}
|
||||
|
||||
FlightModeData *p = flightModeAddress(k);
|
||||
|
||||
putsFlightMode(MENUS_MARGIN_LEFT, y, k+1, (getFlightMode()==k ? BOLD : 0) | ((menuVerticalPosition==k && menuHorizontalPosition<0) ? INVERS : 0));
|
||||
|
||||
for (int j=0; j<ITEM_FLIGHT_MODES_COUNT; j++) {
|
||||
LcdFlags attr = ((menuVerticalPosition==k && menuHorizontalPosition==j) ? ((s_editMode>0) ? BLINK|INVERS : INVERS) : 0);
|
||||
LcdFlags active = (attr && s_editMode>0) ;
|
||||
switch (j) {
|
||||
case ITEM_FLIGHT_MODES_NAME:
|
||||
editName(FLIGHT_MODES_NAME_COLUMN, y, p->name, sizeof(p->name), event, attr);
|
||||
break;
|
||||
|
||||
case ITEM_FLIGHT_MODES_SWITCH:
|
||||
if (active) CHECK_INCDEC_MODELSWITCH(event, p->swtch, SWSRC_FIRST_IN_MIXES, SWSRC_LAST_IN_MIXES, isSwitchAvailableInMixes);
|
||||
if (k == 0)
|
||||
lcdDrawText(FLIGHT_MODES_SWITCH_COLUMN, y, "N/A");
|
||||
else
|
||||
putsSwitches(FLIGHT_MODES_SWITCH_COLUMN, y, p->swtch, attr);
|
||||
break;
|
||||
|
||||
case ITEM_FLIGHT_MODES_TRIM_RUD:
|
||||
case ITEM_FLIGHT_MODES_TRIM_ELE:
|
||||
case ITEM_FLIGHT_MODES_TRIM_THR:
|
||||
case ITEM_FLIGHT_MODES_TRIM_AIL:
|
||||
case ITEM_FLIGHT_MODES_TRIM_T5:
|
||||
case ITEM_FLIGHT_MODES_TRIM_T6:
|
||||
{
|
||||
uint8_t t = j-ITEM_FLIGHT_MODES_TRIM_RUD;
|
||||
if (active) {
|
||||
trim_t & v = p->trim[t];
|
||||
v.mode = checkIncDec(event, v.mode==TRIM_MODE_NONE ? -1 : v.mode, -1, k==0 ? 0 : 2*MAX_FLIGHT_MODES-1, EE_MODEL, isTrimModeAvailable);
|
||||
}
|
||||
putsTrimMode(FLIGHT_MODES_TRIMS_COLUMN+j*FLIGHT_MODES_TRIM_WIDTH, y, k, t, attr);
|
||||
break;
|
||||
}
|
||||
|
||||
case ITEM_FLIGHT_MODES_FADE_IN:
|
||||
if (active) p->fadeIn = checkIncDec(event, p->fadeIn, 0, DELAY_MAX, EE_MODEL|NO_INCDEC_MARKS);
|
||||
lcdDrawNumber(FLIGHT_MODES_FADEIN_COLUMN, y, (10/DELAY_STEP)*p->fadeIn, attr|PREC1|RIGHT);
|
||||
break;
|
||||
|
||||
case ITEM_FLIGHT_MODES_FADE_OUT:
|
||||
if (active) p->fadeOut = checkIncDec(event, p->fadeOut, 0, DELAY_MAX, EE_MODEL|NO_INCDEC_MARKS);
|
||||
lcdDrawNumber(FLIGHT_MODES_FADEOUT_COLUMN, y, (10/DELAY_STEP)*p->fadeOut, attr|PREC1|RIGHT);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "../../opentx.h"
|
||||
|
||||
void onGVARSMenu(const char *result)
|
||||
{
|
||||
int8_t sub = menuVerticalPosition;
|
||||
|
||||
if (result == STR_ENABLE_POPUP) {
|
||||
g_model.gvars[sub].popup = true;
|
||||
storageDirty(EE_MODEL);
|
||||
}
|
||||
else if (result == STR_DISABLE_POPUP) {
|
||||
g_model.gvars[sub].popup = false;
|
||||
storageDirty(EE_MODEL);
|
||||
}
|
||||
else if (result == STR_CLEAR) {
|
||||
for (int i=0; i<MAX_FLIGHT_MODES; i++) {
|
||||
g_model.flightModeData[i].gvars[sub] = 0;
|
||||
}
|
||||
storageDirty(EE_MODEL);
|
||||
}
|
||||
}
|
||||
|
||||
#define GVARS_FM_COLUMN(p) (160 + (p)*38)
|
||||
|
||||
bool menuModelGVars(evt_t event)
|
||||
{
|
||||
MENU(STR_MENUGLOBALVARS, MODEL_ICONS, menuTabModel, e_GVars/* TODO, first2seconds ? CHECK_FLAG_NO_SCREEN_INDEX : 0*/, MAX_GVARS, { NAVIGATION_LINE_BY_LINE|MAX_FLIGHT_MODES, NAVIGATION_LINE_BY_LINE|MAX_FLIGHT_MODES, NAVIGATION_LINE_BY_LINE|MAX_FLIGHT_MODES, NAVIGATION_LINE_BY_LINE|MAX_FLIGHT_MODES, NAVIGATION_LINE_BY_LINE|MAX_FLIGHT_MODES, NAVIGATION_LINE_BY_LINE|MAX_FLIGHT_MODES, NAVIGATION_LINE_BY_LINE|MAX_FLIGHT_MODES, NAVIGATION_LINE_BY_LINE|MAX_FLIGHT_MODES, NAVIGATION_LINE_BY_LINE|MAX_FLIGHT_MODES});
|
||||
|
||||
int sub = menuVerticalPosition;
|
||||
|
||||
for (int l=0; l<NUM_BODY_LINES; l++) {
|
||||
int i = l+menuVerticalOffset;
|
||||
coord_t y = MENU_CONTENT_TOP + l*FH;
|
||||
if (g_model.gvars[i].popup) lcdDrawText(MENUS_MARGIN_LEFT+25, y, "!");
|
||||
drawStringWithIndex(MENUS_MARGIN_LEFT, y, STR_GV, i+1, ((sub==i && menuHorizontalPosition<0) ? INVERS : 0));
|
||||
|
||||
for (int j=0; j<1+MAX_FLIGHT_MODES; j++) {
|
||||
LcdFlags attr = ((sub==i && menuHorizontalPosition==j) ? ((s_editMode>0) ? BLINK|INVERS : INVERS) : 0);
|
||||
coord_t x = GVARS_FM_COLUMN(j-1);
|
||||
switch(j)
|
||||
{
|
||||
case 0:
|
||||
editName(MENUS_MARGIN_LEFT+50, y, g_model.gvars[i].name, LEN_GVAR_NAME, event, attr);
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
FlightModeData *fm = &g_model.flightModeData[j-1];
|
||||
int16_t & v = fm->gvars[i];
|
||||
int16_t vmin, vmax;
|
||||
if (v > GVAR_MAX) {
|
||||
uint8_t p = v - GVAR_MAX - 1;
|
||||
if (p >= j-1) p++;
|
||||
putsFlightMode(x-21, y, p+1, attr);
|
||||
vmin = GVAR_MAX+1; vmax = GVAR_MAX+MAX_FLIGHT_MODES-1;
|
||||
}
|
||||
else {
|
||||
if (abs(v) >= 1000)
|
||||
lcdDrawNumber(x, y+1, v, TINSIZE|attr|RIGHT);
|
||||
else
|
||||
lcdDrawNumber(x, y, v, attr|RIGHT);
|
||||
vmin = -GVAR_MAX; vmax = GVAR_MAX;
|
||||
}
|
||||
if (attr) {
|
||||
if (event == EVT_KEY_LONG(KEY_ENTER)) {
|
||||
v = (v > GVAR_MAX ? 0 : GVAR_MAX+1);
|
||||
storageDirty(EE_MODEL);
|
||||
}
|
||||
else if (s_editMode>0) {
|
||||
v = checkIncDec(event, v, vmin, vmax, EE_MODEL);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (menuHorizontalPosition < 0 && event==EVT_KEY_LONG(KEY_ENTER)) {
|
||||
killEvents(event);
|
||||
if (g_model.gvars[sub].popup)
|
||||
POPUP_MENU_ADD_ITEM(STR_DISABLE_POPUP);
|
||||
else
|
||||
POPUP_MENU_ADD_ITEM(STR_ENABLE_POPUP);
|
||||
POPUP_MENU_ADD_ITEM(STR_CLEAR);
|
||||
POPUP_MENU_START(onGVARSMenu);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "../../opentx.h"
|
||||
|
||||
enum menuModelHeliItems {
|
||||
ITEM_HELI_SWASHTYPE,
|
||||
ITEM_HELI_SWASHRING,
|
||||
ITEM_HELI_ELE,
|
||||
ITEM_HELI_ELE_WEIGHT,
|
||||
ITEM_HELI_AIL,
|
||||
ITEM_HELI_AIL_WEIGHT,
|
||||
ITEM_HELI_COL,
|
||||
ITEM_HELI_COL_WEIGHT,
|
||||
ITEM_HELI_MAX
|
||||
};
|
||||
|
||||
#define MODEL_HELI_2ND_COLUMN (230)
|
||||
|
||||
bool menuModelHeli(evt_t event)
|
||||
{
|
||||
SIMPLE_MENU(STR_MENUHELISETUP, MODEL_ICONS, menuTabModel, e_Heli, ITEM_HELI_MAX);
|
||||
|
||||
int sub = menuVerticalPosition;
|
||||
|
||||
for (unsigned int i=0; i<NUM_BODY_LINES; i++) {
|
||||
coord_t y = MENU_CONTENT_TOP + i*FH;
|
||||
int k = i+menuVerticalOffset;
|
||||
LcdFlags blink = ((s_editMode>0) ? BLINK|INVERS : INVERS);
|
||||
LcdFlags attr = (sub == k ? blink : 0);
|
||||
|
||||
switch(k) {
|
||||
case ITEM_HELI_SWASHTYPE:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_SWASHTYPE);
|
||||
g_model.swashR.type = selectMenuItem(MODEL_HELI_2ND_COLUMN, y, STR_VSWASHTYPE, g_model.swashR.type, 0, SWASH_TYPE_MAX, attr, event);
|
||||
break;
|
||||
|
||||
case ITEM_HELI_SWASHRING:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_SWASHRING);
|
||||
lcdDrawNumber(MODEL_HELI_2ND_COLUMN, y, g_model.swashR.value, LEFT|attr);
|
||||
if (attr) CHECK_INCDEC_MODELVAR_ZERO(event, g_model.swashR.value, 100);
|
||||
break;
|
||||
|
||||
case ITEM_HELI_ELE:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_ELEVATOR);
|
||||
putsMixerSource(MODEL_HELI_2ND_COLUMN, y, g_model.swashR.elevatorSource, attr);
|
||||
if (attr) CHECK_INCDEC_MODELSOURCE(event, g_model.swashR.elevatorSource, 0, MIXSRC_LAST_CH);
|
||||
break;
|
||||
|
||||
case ITEM_HELI_ELE_WEIGHT:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT+INDENT_WIDTH, y, STR_WEIGHT);
|
||||
lcdDrawNumber(MODEL_HELI_2ND_COLUMN, y, g_model.swashR.elevatorWeight, LEFT|attr);
|
||||
if (attr) CHECK_INCDEC_MODELVAR(event, g_model.swashR.elevatorWeight, -100, 100);
|
||||
break;
|
||||
|
||||
case ITEM_HELI_AIL:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_AILERON);
|
||||
putsMixerSource(MODEL_HELI_2ND_COLUMN, y, g_model.swashR.aileronSource, attr);
|
||||
if (attr) CHECK_INCDEC_MODELSOURCE(event, g_model.swashR.aileronSource, 0, MIXSRC_LAST_CH);
|
||||
break;
|
||||
|
||||
case ITEM_HELI_AIL_WEIGHT:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT+INDENT_WIDTH, y, STR_WEIGHT);
|
||||
lcdDrawNumber(MODEL_HELI_2ND_COLUMN, y, g_model.swashR.aileronWeight, LEFT|attr);
|
||||
if (attr) CHECK_INCDEC_MODELVAR(event, g_model.swashR.aileronWeight, -100, 100);
|
||||
break;
|
||||
|
||||
case ITEM_HELI_COL:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_COLLECTIVE);
|
||||
putsMixerSource(MODEL_HELI_2ND_COLUMN, y, g_model.swashR.collectiveSource, attr);
|
||||
if (attr) CHECK_INCDEC_MODELSOURCE(event, g_model.swashR.collectiveSource, 0, MIXSRC_LAST_CH);
|
||||
break;
|
||||
|
||||
case ITEM_HELI_COL_WEIGHT:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT+INDENT_WIDTH, y, STR_WEIGHT);
|
||||
lcdDrawNumber(MODEL_HELI_2ND_COLUMN, y, g_model.swashR.collectiveWeight, LEFT|attr);
|
||||
if (attr) CHECK_INCDEC_MODELVAR(event, g_model.swashR.collectiveWeight, -100, 100);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,559 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "opentx.h"
|
||||
|
||||
#define EXPO_ONE_2ND_COLUMN 115
|
||||
|
||||
int expoFn(int x)
|
||||
{
|
||||
ExpoData * ed = expoAddress(s_currIdx);
|
||||
int16_t anas[NUM_INPUTS] = {0};
|
||||
applyExpos(anas, e_perout_mode_inactive_flight_mode, ed->srcRaw, x);
|
||||
return anas[ed->chn];
|
||||
}
|
||||
|
||||
int getExposLinesCount()
|
||||
{
|
||||
int lastch = -1;
|
||||
uint8_t count = MAX_INPUTS;
|
||||
for (int i=0; i<MAX_EXPOS; i++) {
|
||||
bool valid = EXPO_VALID(expoAddress(i));
|
||||
if (!valid)
|
||||
break;
|
||||
int ch = expoAddress(i)->chn;
|
||||
if (ch == lastch) {
|
||||
count++;
|
||||
}
|
||||
else {
|
||||
lastch = ch;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
uint8_t getExposCount()
|
||||
{
|
||||
uint8_t count = 0;
|
||||
uint8_t ch ;
|
||||
|
||||
for (int i=MAX_EXPOS-1 ; i>=0; i--) {
|
||||
ch = EXPO_VALID(expoAddress(i));
|
||||
if (ch != 0) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
bool reachExposLimit()
|
||||
{
|
||||
if (getExposCount() >= MAX_EXPOS) {
|
||||
POPUP_WARNING(STR_NOFREEEXPO);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void deleteExpo(uint8_t idx)
|
||||
{
|
||||
pauseMixerCalculations();
|
||||
ExpoData * expo = expoAddress(idx);
|
||||
int input = expo->chn;
|
||||
memmove(expo, expo+1, (MAX_EXPOS-(idx+1))*sizeof(ExpoData));
|
||||
memclear(&g_model.expoData[MAX_EXPOS-1], sizeof(ExpoData));
|
||||
if (!isInputAvailable(input)) {
|
||||
memclear(&g_model.inputNames[input], LEN_INPUT_NAME);
|
||||
}
|
||||
resumeMixerCalculations();
|
||||
storageDirty(EE_MODEL);
|
||||
}
|
||||
|
||||
// TODO avoid this global s_currCh on ARM boards ...
|
||||
int8_t s_currCh;
|
||||
void insertExpo(uint8_t idx)
|
||||
{
|
||||
pauseMixerCalculations();
|
||||
ExpoData * expo = expoAddress(idx);
|
||||
memmove(expo+1, expo, (MAX_EXPOS-(idx+1))*sizeof(ExpoData));
|
||||
memclear(expo, sizeof(ExpoData));
|
||||
expo->srcRaw = (s_currCh > 4 ? MIXSRC_Rud - 1 + s_currCh : MIXSRC_Rud - 1 + channel_order(s_currCh));
|
||||
expo->curve.type = CURVE_REF_EXPO;
|
||||
expo->mode = 3; // pos+neg
|
||||
expo->chn = s_currCh - 1;
|
||||
expo->weight = 100;
|
||||
resumeMixerCalculations();
|
||||
storageDirty(EE_MODEL);
|
||||
}
|
||||
|
||||
void copyExpo(uint8_t idx)
|
||||
{
|
||||
pauseMixerCalculations();
|
||||
ExpoData * expo = expoAddress(idx);
|
||||
memmove(expo+1, expo, (MAX_EXPOS-(idx+1))*sizeof(ExpoData));
|
||||
resumeMixerCalculations();
|
||||
storageDirty(EE_MODEL);
|
||||
}
|
||||
|
||||
bool swapExpos(uint8_t & idx, uint8_t up)
|
||||
{
|
||||
ExpoData * x, * y;
|
||||
int8_t tgt_idx = (up ? idx-1 : idx+1);
|
||||
|
||||
x = expoAddress(idx);
|
||||
|
||||
if (tgt_idx < 0) {
|
||||
if (x->chn == 0)
|
||||
return false;
|
||||
x->chn--;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (tgt_idx == MAX_EXPOS) {
|
||||
if (x->chn == NUM_INPUTS-1)
|
||||
return false;
|
||||
x->chn++;
|
||||
return true;
|
||||
}
|
||||
|
||||
y = expoAddress(tgt_idx);
|
||||
if (x->chn != y->chn || !EXPO_VALID(y)) {
|
||||
if (up) {
|
||||
if (x->chn>0) x->chn--;
|
||||
else return false;
|
||||
}
|
||||
else {
|
||||
if (x->chn<NUM_INPUTS-1) x->chn++;
|
||||
else return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
pauseMixerCalculations();
|
||||
memswap(x, y, sizeof(ExpoData));
|
||||
resumeMixerCalculations();
|
||||
|
||||
idx = tgt_idx;
|
||||
return true;
|
||||
}
|
||||
|
||||
enum ExposFields {
|
||||
EXPO_FIELD_INPUT_NAME,
|
||||
EXPO_FIELD_NAME,
|
||||
EXPO_FIELD_SOURCE,
|
||||
EXPO_FIELD_WEIGHT,
|
||||
EXPO_FIELD_OFFSET,
|
||||
CASE_CURVES(EXPO_FIELD_CURVE)
|
||||
CASE_FLIGHT_MODES(EXPO_FIELD_FLIGHT_MODES)
|
||||
EXPO_FIELD_SWITCH,
|
||||
EXPO_FIELD_SIDE,
|
||||
EXPO_FIELD_TRIM,
|
||||
EXPO_FIELD_MAX
|
||||
};
|
||||
|
||||
#define CURVE_ROWS 1
|
||||
|
||||
bool menuModelExpoOne(evt_t event)
|
||||
{
|
||||
ExpoData * ed = expoAddress(s_currIdx);
|
||||
|
||||
SUBMENU_WITH_OPTIONS(STR_MENUINPUTS, ICON_MODEL_INPUTS, EXPO_FIELD_MAX, OPTION_MENU_NO_FOOTER|OPTION_MENU_NO_SCROLLBAR, { 0, 0, (ed->srcRaw >= MIXSRC_FIRST_TELEM ? (uint8_t)1 : (uint8_t)0), 0, 0, CASE_CURVES(CURVE_ROWS) CASE_FLIGHT_MODES((MAX_FLIGHT_MODES-1) | NAVIGATION_LINE_BY_LINE) 0 /*, ...*/});
|
||||
lcdDrawSizedText(50, 3+FH, g_model.inputNames[ed->chn], LEN_INPUT_NAME, ZCHAR|MENU_TITLE_COLOR);
|
||||
lcdDrawSolidFilledRect(0, MENU_FOOTER_TOP, 230, MENU_FOOTER_HEIGHT, HEADER_BGCOLOR);
|
||||
|
||||
int sub = menuVerticalPosition;
|
||||
|
||||
coord_t y = MENU_CONTENT_TOP-FH-2;
|
||||
|
||||
drawFunction(expoFn, CURVE_CENTER_X, CURVE_CENTER_Y, CURVE_SIDE_WIDTH);
|
||||
drawCurveHorizontalScale();
|
||||
drawCurveVerticalScale(CURVE_CENTER_X-CURVE_SIDE_WIDTH-15);
|
||||
|
||||
{
|
||||
char textx[5];
|
||||
char texty[5];
|
||||
int x = getValue(ed->srcRaw);
|
||||
if (ed->srcRaw >= MIXSRC_FIRST_TELEM) {
|
||||
strAppendUnsigned(textx, calcRESXto100(x));
|
||||
// TODO putsTelemetryChannelValue(LCD_W-8, 6*FH, ed->srcRaw - MIXSRC_FIRST_TELEM, x);
|
||||
if (ed->scale > 0) x = (x * 1024) / convertTelemValue(ed->srcRaw - MIXSRC_FIRST_TELEM + 1, ed->scale);
|
||||
}
|
||||
else {
|
||||
strAppendSigned(textx, calcRESXto100(x));
|
||||
}
|
||||
|
||||
x = limit(-1024, x, 1024);
|
||||
int y = limit<int>(-1024, expoFn(x), 1024);
|
||||
strAppendSigned(texty, calcRESXto100(y));
|
||||
|
||||
x = divRoundClosest(x*CURVE_SIDE_WIDTH, RESX);
|
||||
y = CURVE_CENTER_Y + getCurveYCoord(expoFn, x, CURVE_SIDE_WIDTH);
|
||||
|
||||
lcdDrawSolidFilledRect(CURVE_CENTER_X+x, CURVE_CENTER_Y-CURVE_SIDE_WIDTH, 2, 2*CURVE_SIDE_WIDTH+2, CURVE_CURSOR_COLOR);
|
||||
lcdDrawSolidFilledRect(CURVE_CENTER_X-CURVE_SIDE_WIDTH-2, y-1, 2*CURVE_SIDE_WIDTH+2, 2, CURVE_CURSOR_COLOR);
|
||||
lcdDrawBitmapPattern(CURVE_CENTER_X+x-4, y-4, LBM_CURVE_POINT, CURVE_CURSOR_COLOR);
|
||||
lcdDrawBitmapPattern(CURVE_CENTER_X+x-4, y-4, LBM_CURVE_POINT_CENTER, TEXT_BGCOLOR);
|
||||
|
||||
int left = limit(CURVE_CENTER_X-CURVE_SIDE_WIDTH, CURVE_CENTER_X-CURVE_COORD_WIDTH/2+x, CURVE_CENTER_X+CURVE_SIDE_WIDTH-CURVE_COORD_WIDTH+2);
|
||||
drawCurveCoord(left, CURVE_CENTER_Y+CURVE_SIDE_WIDTH+2, textx);
|
||||
int top = limit(CURVE_CENTER_Y-CURVE_SIDE_WIDTH-1, -CURVE_COORD_HEIGHT/2+y, CURVE_CENTER_Y+CURVE_SIDE_WIDTH-CURVE_COORD_HEIGHT+1);
|
||||
drawCurveCoord(CURVE_CENTER_X-CURVE_SIDE_WIDTH-37, top, texty);
|
||||
}
|
||||
|
||||
for (int i=0; i<NUM_BODY_LINES+1; i++) {
|
||||
LcdFlags attr = (sub==i ? (s_editMode>0 ? BLINK|INVERS : INVERS) : 0);
|
||||
switch (i) {
|
||||
case EXPO_FIELD_INPUT_NAME:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_INPUTNAME);
|
||||
editName(EXPO_ONE_2ND_COLUMN, y, g_model.inputNames[ed->chn], sizeof(g_model.inputNames[ed->chn]), event, attr);
|
||||
break;
|
||||
|
||||
case EXPO_FIELD_NAME:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_EXPONAME);
|
||||
editName(EXPO_ONE_2ND_COLUMN, y, ed->name, sizeof(ed->name), event, attr);
|
||||
break;
|
||||
|
||||
case EXPO_FIELD_SOURCE:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, NO_INDENT(STR_SOURCE));
|
||||
putsMixerSource(EXPO_ONE_2ND_COLUMN, y, ed->srcRaw, menuHorizontalPosition==0 ? attr : 0);
|
||||
if (attr && menuHorizontalPosition==0) ed->srcRaw = checkIncDec(event, ed->srcRaw, INPUTSRC_FIRST, INPUTSRC_LAST, EE_MODEL|INCDEC_SOURCE|NO_INCDEC_MARKS, isInputSourceAvailable);
|
||||
if (ed->srcRaw >= MIXSRC_FIRST_TELEM) {
|
||||
putsTelemetryChannelValue(EXPO_ONE_2ND_COLUMN+60, y, (ed->srcRaw - MIXSRC_FIRST_TELEM)/3, convertTelemValue(ed->srcRaw - MIXSRC_FIRST_TELEM + 1, ed->scale), LEFT|(menuHorizontalPosition==1?attr:0));
|
||||
if (attr && menuHorizontalPosition == 1) ed->scale = checkIncDec(event, ed->scale, 0, maxTelemValue(ed->srcRaw - MIXSRC_FIRST_TELEM + 1), EE_MODEL);
|
||||
}
|
||||
else if (attr) {
|
||||
menuHorizontalPosition = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case EXPO_FIELD_WEIGHT:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_WEIGHT);
|
||||
ed->weight = GVAR_MENU_ITEM(EXPO_ONE_2ND_COLUMN, y, ed->weight, MIN_EXPO_WEIGHT, 100, LEFT|attr, 0, event);
|
||||
break;
|
||||
|
||||
case EXPO_FIELD_OFFSET:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, NO_INDENT(STR_OFFSET));
|
||||
ed->offset = GVAR_MENU_ITEM(EXPO_ONE_2ND_COLUMN, y, ed->offset, -100, 100, LEFT|attr, 0, event);
|
||||
break;
|
||||
|
||||
#if defined(CURVES)
|
||||
case EXPO_FIELD_CURVE:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_CURVE);
|
||||
editCurveRef(EXPO_ONE_2ND_COLUMN, y, ed->curve, event, attr);
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if defined(FLIGHT_MODES)
|
||||
case EXPO_FIELD_FLIGHT_MODES:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_FLMODE);
|
||||
ed->flightModes = editFlightModes(EXPO_ONE_2ND_COLUMN, y, event, ed->flightModes, attr);
|
||||
break;
|
||||
#endif
|
||||
|
||||
case EXPO_FIELD_SWITCH:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_SWITCH);
|
||||
ed->swtch = switchMenuItem(EXPO_ONE_2ND_COLUMN, y, ed->swtch, attr, event);
|
||||
break;
|
||||
|
||||
case EXPO_FIELD_SIDE:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_SIDE);
|
||||
ed->mode = 4 - selectMenuItem(EXPO_ONE_2ND_COLUMN, y, STR_VSIDE, 4-ed->mode, 1, 3, attr, event);
|
||||
break;
|
||||
|
||||
case EXPO_FIELD_TRIM:
|
||||
uint8_t not_stick = (ed->srcRaw > MIXSRC_Ail);
|
||||
int8_t carryTrim = -ed->carryTrim;
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_TRIM);
|
||||
lcdDrawTextAtIndex(EXPO_ONE_2ND_COLUMN, y, STR_VMIXTRIMS, (not_stick && carryTrim == 0) ? 0 : carryTrim+1, menuHorizontalPosition==0 ? attr : 0);
|
||||
if (attr) ed->carryTrim = -checkIncDecModel(event, carryTrim, not_stick ? TRIM_ON : -TRIM_OFF, -TRIM_LAST);
|
||||
break;
|
||||
}
|
||||
y += FH;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#define _STR_MAX(x) PSTR("/" #x)
|
||||
#define STR_MAX(x) _STR_MAX(x)
|
||||
|
||||
#define EXPO_LINE_WEIGHT_POS 110
|
||||
#define EXPO_LINE_SRC_POS 115
|
||||
#define EXPO_LINE_CURVE_POS 162
|
||||
#define EXPO_LINE_SWITCH_POS 210
|
||||
#define EXPO_LINE_SIDE_POS 250
|
||||
#define EXPO_LINE_FM_POS 270
|
||||
#define EXPO_LINE_NAME_POS 384
|
||||
#define EXPO_LINE_SELECT_POS 50
|
||||
#define EXPO_LINE_SELECT_WIDTH (LCD_W-EXPO_LINE_SELECT_POS-15)
|
||||
|
||||
void lineExpoSurround(coord_t y, LcdFlags flags=CURVE_AXIS_COLOR)
|
||||
{
|
||||
lcdDrawRect(EXPO_LINE_SELECT_POS, y-INVERT_VERT_MARGIN+1, EXPO_LINE_SELECT_WIDTH, INVERT_LINE_HEIGHT, 1, s_copyMode == COPY_MODE ? SOLID : DOTTED, flags);
|
||||
}
|
||||
|
||||
void onExposMenu(const char * result)
|
||||
{
|
||||
uint8_t chn = expoAddress(s_currIdx)->chn + 1;
|
||||
|
||||
if (result == STR_EDIT) {
|
||||
pushMenu(menuModelExpoOne);
|
||||
}
|
||||
else if (result == STR_INSERT_BEFORE || result == STR_INSERT_AFTER) {
|
||||
if (!reachExposLimit()) {
|
||||
s_currCh = chn;
|
||||
if (result == STR_INSERT_AFTER) { s_currIdx++; menuVerticalPosition++; }
|
||||
insertExpo(s_currIdx);
|
||||
pushMenu(menuModelExpoOne);
|
||||
}
|
||||
}
|
||||
else if (result == STR_COPY || result == STR_MOVE) {
|
||||
s_copyMode = (result == STR_COPY ? COPY_MODE : MOVE_MODE);
|
||||
s_copySrcIdx = s_currIdx;
|
||||
s_copySrcCh = chn;
|
||||
s_copySrcRow = menuVerticalPosition;
|
||||
}
|
||||
else if (result == STR_DELETE) {
|
||||
deleteExpo(s_currIdx);
|
||||
}
|
||||
}
|
||||
|
||||
void displayExpoInfos(coord_t y, ExpoData *ed)
|
||||
{
|
||||
putsCurveRef(EXPO_LINE_CURVE_POS, y, ed->curve);
|
||||
if (ed->swtch) {
|
||||
putsSwitches(EXPO_LINE_SWITCH_POS, y, ed->swtch);
|
||||
}
|
||||
if (ed->mode != 3) {
|
||||
lcdDrawText(EXPO_LINE_SIDE_POS, y, ed->mode == 2 ? "\176" : "\177");
|
||||
}
|
||||
}
|
||||
|
||||
void displayExpoLine(coord_t y, ExpoData *ed)
|
||||
{
|
||||
putsMixerSource(EXPO_LINE_SRC_POS, y, ed->srcRaw);
|
||||
|
||||
displayExpoInfos(y, ed);
|
||||
displayFlightModes(EXPO_LINE_FM_POS, y, ed->flightModes, 0);
|
||||
|
||||
if (ed->name[0]) {
|
||||
lcdDrawSizedText(EXPO_LINE_NAME_POS, y+2, ed->name, sizeof(ed->name), ZCHAR|SMLSIZE);
|
||||
}
|
||||
}
|
||||
|
||||
bool menuModelExposAll(evt_t event)
|
||||
{
|
||||
int sub = menuVerticalPosition;
|
||||
|
||||
if (s_editMode > 0) {
|
||||
s_editMode = 0;
|
||||
}
|
||||
|
||||
uint8_t chn = expoAddress(s_currIdx)->chn + 1;
|
||||
|
||||
int linesCount = getExposLinesCount();
|
||||
SIMPLE_MENU(STR_MENUINPUTS, MODEL_ICONS, menuTabModel, e_InputsAll, linesCount);
|
||||
|
||||
switch (event) {
|
||||
case EVT_ENTRY:
|
||||
case EVT_ENTRY_UP:
|
||||
s_copyMode = 0;
|
||||
s_copyTgtOfs = 0;
|
||||
break;
|
||||
case EVT_KEY_LONG(KEY_EXIT):
|
||||
if (s_copyMode && s_copyTgtOfs == 0) {
|
||||
deleteExpo(s_currIdx);
|
||||
killEvents(event);
|
||||
event = 0;
|
||||
}
|
||||
// no break
|
||||
case EVT_KEY_BREAK(KEY_EXIT):
|
||||
if (s_copyMode) {
|
||||
if (s_copyTgtOfs) {
|
||||
// cancel the current copy / move operation
|
||||
if (s_copyMode == COPY_MODE) {
|
||||
deleteExpo(s_currIdx);
|
||||
}
|
||||
else {
|
||||
do {
|
||||
swapExpos(s_currIdx, s_copyTgtOfs > 0);
|
||||
s_copyTgtOfs += (s_copyTgtOfs < 0 ? +1 : -1);
|
||||
} while (s_copyTgtOfs != 0);
|
||||
storageDirty(EE_MODEL);
|
||||
}
|
||||
menuVerticalPosition = s_copySrcRow;
|
||||
s_copyTgtOfs = 0;
|
||||
}
|
||||
s_copyMode = 0;
|
||||
event = 0;
|
||||
}
|
||||
break;
|
||||
case EVT_KEY_BREAK(KEY_ENTER):
|
||||
if ((!s_currCh || (s_copyMode && !s_copyTgtOfs)) && !READ_ONLY()) {
|
||||
s_copyMode = (s_copyMode == COPY_MODE ? MOVE_MODE : COPY_MODE);
|
||||
s_copySrcIdx = s_currIdx;
|
||||
s_copySrcCh = chn;
|
||||
s_copySrcRow = sub;
|
||||
break;
|
||||
}
|
||||
// no break
|
||||
|
||||
case EVT_KEY_LONG(KEY_ENTER):
|
||||
killEvents(event);
|
||||
if (s_copyTgtOfs) {
|
||||
s_copyMode = 0;
|
||||
s_copyTgtOfs = 0;
|
||||
}
|
||||
else {
|
||||
if (READ_ONLY()) {
|
||||
if (!s_currCh) {
|
||||
pushMenu(menuModelExpoOne);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (s_copyMode) s_currCh = 0;
|
||||
if (s_currCh) {
|
||||
if (reachExposLimit()) break;
|
||||
insertExpo(s_currIdx);
|
||||
pushMenu(menuModelExpoOne);
|
||||
s_copyMode = 0;
|
||||
}
|
||||
else if (menuVerticalPosition >= 0) {
|
||||
event = 0;
|
||||
s_copyMode = 0;
|
||||
POPUP_MENU_ADD_ITEM(STR_EDIT);
|
||||
POPUP_MENU_ADD_ITEM(STR_INSERT_BEFORE);
|
||||
POPUP_MENU_ADD_ITEM(STR_INSERT_AFTER);
|
||||
POPUP_MENU_ADD_ITEM(STR_COPY);
|
||||
POPUP_MENU_ADD_ITEM(STR_MOVE);
|
||||
POPUP_MENU_ADD_ITEM(STR_DELETE);
|
||||
POPUP_MENU_START(onExposMenu);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case EVT_ROTARY_LEFT:
|
||||
case EVT_ROTARY_RIGHT:
|
||||
if (s_copyMode) {
|
||||
uint8_t next_ofs = ((event==EVT_ROTARY_LEFT) ? s_copyTgtOfs - 1 : s_copyTgtOfs + 1);
|
||||
|
||||
if (s_copyTgtOfs==0 && s_copyMode==COPY_MODE) {
|
||||
// insert a mix on the same channel (just above / just below)
|
||||
if (reachExposLimit()) break;
|
||||
copyExpo(s_currIdx);
|
||||
if (event==EVT_ROTARY_RIGHT) s_currIdx++;
|
||||
else if (sub-menuVerticalOffset >= 6) menuVerticalOffset++;
|
||||
}
|
||||
else if (next_ofs==0 && s_copyMode==COPY_MODE) {
|
||||
// delete the mix
|
||||
deleteExpo(s_currIdx);
|
||||
if (event==EVT_ROTARY_LEFT) s_currIdx--;
|
||||
}
|
||||
else {
|
||||
// only swap the mix with its neighbor
|
||||
if (!swapExpos(s_currIdx, event==EVT_ROTARY_LEFT)) break;
|
||||
storageDirty(EE_MODEL);
|
||||
}
|
||||
|
||||
s_copyTgtOfs = next_ofs;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
char str[6];
|
||||
sprintf(str, "%d/%d", getExposCount(), MAX_EXPOS);
|
||||
lcdDrawText(MENU_TITLE_NEXT_POS, MENU_TITLE_TOP+2, str, HEADER_COLOR);
|
||||
|
||||
sub = menuVerticalPosition;
|
||||
s_currCh = 0;
|
||||
int cur = 0;
|
||||
int i = 0;
|
||||
|
||||
for (int ch=1; ch<=NUM_INPUTS; ch++) {
|
||||
ExpoData * ed;
|
||||
coord_t y = MENU_CONTENT_TOP + (cur-menuVerticalOffset)*FH;
|
||||
if ((i<MAX_EXPOS && (ed=expoAddress(i))->chn+1 == ch && EXPO_VALID(ed))) {
|
||||
if (cur-menuVerticalOffset >= 0 && cur-menuVerticalOffset < NUM_BODY_LINES) {
|
||||
putsMixerSource(MENUS_MARGIN_LEFT, y, ch);
|
||||
}
|
||||
uint8_t mixCnt = 0;
|
||||
do {
|
||||
if (s_copyMode) {
|
||||
if (s_copyMode == MOVE_MODE && cur-menuVerticalOffset >= 0 && cur-menuVerticalOffset < NUM_BODY_LINES && s_copySrcCh == ch && s_copyTgtOfs != 0 && i == (s_copySrcIdx + (s_copyTgtOfs<0))) {
|
||||
lineExpoSurround(y);
|
||||
cur++; y+=FH;
|
||||
}
|
||||
if (s_currIdx == i) {
|
||||
sub = menuVerticalPosition = cur;
|
||||
s_currCh = ch;
|
||||
}
|
||||
}
|
||||
else if (sub == cur) {
|
||||
s_currIdx = i;
|
||||
}
|
||||
if (cur-menuVerticalOffset >= 0 && cur-menuVerticalOffset < NUM_BODY_LINES) {
|
||||
LcdFlags attr = ((s_copyMode || sub != cur) ? 0 : INVERS);
|
||||
|
||||
GVAR_MENU_ITEM(EXPO_LINE_WEIGHT_POS, y, ed->weight, MIN_EXPO_WEIGHT, 100, RIGHT | attr | (isExpoActive(i) ? BOLD : 0), 0, 0);
|
||||
displayExpoLine(y, ed);
|
||||
|
||||
if (s_copyMode) {
|
||||
if ((s_copyMode==COPY_MODE || s_copyTgtOfs == 0) && s_copySrcCh == ch && i == (s_copySrcIdx + (s_copyTgtOfs<0))) {
|
||||
/* draw a border around the raw on selection mode (copy/move) */
|
||||
lineExpoSurround(y);
|
||||
}
|
||||
if (cur == sub) {
|
||||
/* invert the raw when it's the current one */
|
||||
lineExpoSurround(y, ALARM_COLOR);
|
||||
}
|
||||
}
|
||||
}
|
||||
cur++; y+=FH; mixCnt++; i++; ed++;
|
||||
} while (i<MAX_EXPOS && ed->chn+1 == ch && EXPO_VALID(ed));
|
||||
if (s_copyMode == MOVE_MODE && cur-menuVerticalOffset >= 0 && cur-menuVerticalOffset < NUM_BODY_LINES && s_copySrcCh == ch && i == (s_copySrcIdx + (s_copyTgtOfs<0))) {
|
||||
lineExpoSurround(y);
|
||||
cur++; y+=FH;
|
||||
}
|
||||
}
|
||||
else {
|
||||
uint8_t attr = 0;
|
||||
if (sub == cur) {
|
||||
s_currIdx = i;
|
||||
s_currCh = ch;
|
||||
if (!s_copyMode) {
|
||||
attr = INVERS;
|
||||
}
|
||||
}
|
||||
if (cur-menuVerticalOffset >= 0 && cur-menuVerticalOffset < NUM_BODY_LINES) {
|
||||
putsMixerSource(MENUS_MARGIN_LEFT, y, ch, attr);
|
||||
if (s_copyMode == MOVE_MODE && s_copySrcCh == ch) {
|
||||
lineExpoSurround(y);
|
||||
}
|
||||
}
|
||||
cur++; y+=FH;
|
||||
}
|
||||
}
|
||||
|
||||
if (sub >= linesCount-1) menuVerticalPosition = linesCount-1;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,249 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "opentx.h"
|
||||
|
||||
enum LogicalSwitchFields {
|
||||
LS_FIELD_FUNCTION,
|
||||
LS_FIELD_V1,
|
||||
LS_FIELD_V2,
|
||||
LS_FIELD_V3,
|
||||
LS_FIELD_ANDSW,
|
||||
LS_FIELD_DURATION,
|
||||
LS_FIELD_DELAY,
|
||||
LS_FIELD_COUNT,
|
||||
LS_FIELD_LAST = LS_FIELD_COUNT-1
|
||||
};
|
||||
|
||||
#define CSW_1ST_COLUMN 50
|
||||
#define CSW_2ND_COLUMN 120
|
||||
#define CSW_3RD_COLUMN 200
|
||||
#define CSW_4TH_COLUMN 280
|
||||
#define CSW_5TH_COLUMN 340
|
||||
#define CSW_6TH_COLUMN 390
|
||||
|
||||
void putsEdgeDelayParam(coord_t x, coord_t y, LogicalSwitchData *cs, uint8_t lattr, uint8_t rattr)
|
||||
{
|
||||
lcdDrawChar(x, y, '[');
|
||||
lcdDrawNumber(lcdNextPos+3, y, lswTimerValue(cs->v2), LEFT|PREC1|lattr);
|
||||
lcdDrawChar(lcdNextPos+3, y, ':');
|
||||
if (cs->v3 < 0)
|
||||
lcdDrawText(lcdNextPos+3, y, "<<", rattr);
|
||||
else if (cs->v3 == 0)
|
||||
lcdDrawText(lcdNextPos+3, y, "--", rattr);
|
||||
else
|
||||
lcdDrawNumber(lcdNextPos+3, y, lswTimerValue(cs->v2+cs->v3), LEFT|PREC1|rattr);
|
||||
lcdDrawChar(lcdNextPos+3, y, ']');
|
||||
}
|
||||
|
||||
void onLogicalSwitchesMenu(const char *result)
|
||||
{
|
||||
LogicalSwitchData * cs = lswAddress(menuVerticalPosition);
|
||||
|
||||
if (result == STR_COPY) {
|
||||
clipboard.type = CLIPBOARD_TYPE_CUSTOM_SWITCH;
|
||||
clipboard.data.csw = *cs;
|
||||
}
|
||||
else if (result == STR_PASTE) {
|
||||
*cs = clipboard.data.csw;
|
||||
storageDirty(EE_MODEL);
|
||||
}
|
||||
else if (result == STR_CLEAR) {
|
||||
memset(cs, 0, sizeof(LogicalSwitchData));
|
||||
storageDirty(EE_MODEL);
|
||||
}
|
||||
}
|
||||
|
||||
bool menuModelLogicalSwitches(evt_t event)
|
||||
{
|
||||
INCDEC_DECLARE_VARS(EE_MODEL);
|
||||
|
||||
MENU(STR_MENULOGICALSWITCHES, MODEL_ICONS, menuTabModel, e_LogicalSwitches, NUM_LOGICAL_SWITCH, { NAVIGATION_LINE_BY_LINE|LS_FIELD_LAST/*repeated...*/} );
|
||||
|
||||
LogicalSwitchData * cs = lswAddress(menuVerticalPosition);
|
||||
uint8_t cstate = lswFamily(cs->func);
|
||||
if ((cstate == LS_FAMILY_EDGE && menuHorizontalPosition == LS_FIELD_DELAY) ||
|
||||
(cstate != LS_FAMILY_EDGE && menuHorizontalPosition == LS_FIELD_V3)) {
|
||||
REPEAT_LAST_CURSOR_MOVE(LS_FIELD_LAST, false);
|
||||
}
|
||||
|
||||
if (menuVerticalPosition>= 0 && menuHorizontalPosition>=0) {
|
||||
drawColumnHeader(STR_LSW_HEADERS, STR_LSW_DESCRIPTIONS, menuHorizontalPosition);
|
||||
}
|
||||
|
||||
if (menuHorizontalPosition<0 && event==EVT_KEY_LONG(KEY_ENTER) && !READ_ONLY()) {
|
||||
killEvents(event);
|
||||
if (cs->func)
|
||||
POPUP_MENU_ADD_ITEM(STR_COPY);
|
||||
if (clipboard.type == CLIPBOARD_TYPE_CUSTOM_SWITCH)
|
||||
POPUP_MENU_ADD_ITEM(STR_PASTE);
|
||||
if (cs->func || cs->v1 || cs->v2 || cs->delay || cs->duration || cs->andsw)
|
||||
POPUP_MENU_ADD_ITEM(STR_CLEAR);
|
||||
POPUP_MENU_START(onLogicalSwitchesMenu);
|
||||
}
|
||||
|
||||
for (int i=0; i<NUM_BODY_LINES; ++i) {
|
||||
coord_t y = MENU_CONTENT_TOP + i*FH;
|
||||
int k = i+menuVerticalOffset;
|
||||
LcdFlags attr = (menuVerticalPosition==k ? ((s_editMode>0) ? BLINK|INVERS : INVERS) : 0);
|
||||
LcdFlags attr1 = (menuHorizontalPosition==1 ? attr : 0);
|
||||
LcdFlags attr2 = (menuHorizontalPosition==2 ? attr : 0);
|
||||
cs = lswAddress(k);
|
||||
|
||||
// CSW name
|
||||
unsigned int sw = SWSRC_SW1+k;
|
||||
putsSwitches(MENUS_MARGIN_LEFT, y, sw, (getSwitch(sw) ? BOLD : 0) | ((menuVerticalPosition==k && CURSOR_ON_LINE()) ? INVERS : 0));
|
||||
|
||||
// CSW func
|
||||
lcdDrawTextAtIndex(CSW_1ST_COLUMN, y, STR_VCSWFUNC, cs->func, menuHorizontalPosition==0 ? attr : 0);
|
||||
|
||||
// CSW params
|
||||
cstate = lswFamily(cs->func);
|
||||
int v1_val=cs->v1, v1_min=0, v1_max=MIXSRC_LAST_TELEM;
|
||||
int v2_min=0, v2_max=MIXSRC_LAST_TELEM;
|
||||
int v3_min=-1, v3_max=100;
|
||||
|
||||
if (cstate == LS_FAMILY_BOOL || cstate == LS_FAMILY_STICKY) {
|
||||
putsSwitches(CSW_2ND_COLUMN, y, cs->v1, attr1);
|
||||
putsSwitches(CSW_3RD_COLUMN, y, cs->v2, attr2);
|
||||
v1_min = SWSRC_FIRST_IN_LOGICAL_SWITCHES; v1_max = SWSRC_LAST_IN_LOGICAL_SWITCHES;
|
||||
v2_min = SWSRC_FIRST_IN_LOGICAL_SWITCHES; v2_max = SWSRC_LAST_IN_LOGICAL_SWITCHES;
|
||||
INCDEC_SET_FLAG(EE_MODEL | INCDEC_SWITCH);
|
||||
INCDEC_ENABLE_CHECK(isSwitchAvailableInLogicalSwitches);
|
||||
}
|
||||
else if (cstate == LS_FAMILY_EDGE) {
|
||||
putsSwitches(CSW_2ND_COLUMN, y, cs->v1, attr1);
|
||||
putsEdgeDelayParam(CSW_3RD_COLUMN, y, cs, attr2, (menuHorizontalPosition==LS_FIELD_V3 ? attr : 0));
|
||||
v1_min = SWSRC_FIRST_IN_LOGICAL_SWITCHES; v1_max = SWSRC_LAST_IN_LOGICAL_SWITCHES;
|
||||
v2_min=-129; v2_max = 122;
|
||||
v3_max = 222 - cs->v2;
|
||||
if (menuHorizontalPosition == 1) {
|
||||
INCDEC_SET_FLAG(EE_MODEL | INCDEC_SWITCH);
|
||||
INCDEC_ENABLE_CHECK(isSwitchAvailableInLogicalSwitches);
|
||||
}
|
||||
else {
|
||||
INCDEC_SET_FLAG(EE_MODEL);
|
||||
INCDEC_ENABLE_CHECK(NULL);
|
||||
}
|
||||
}
|
||||
else if (cstate == LS_FAMILY_COMP) {
|
||||
v1_val = cs->v1;
|
||||
putsMixerSource(CSW_2ND_COLUMN, y, v1_val, attr1);
|
||||
putsMixerSource(CSW_3RD_COLUMN, y, cs->v2, attr2);
|
||||
INCDEC_SET_FLAG(EE_MODEL | INCDEC_SOURCE);
|
||||
INCDEC_ENABLE_CHECK(isSourceAvailable);
|
||||
}
|
||||
else if (cstate == LS_FAMILY_TIMER) {
|
||||
lcdDrawNumber(CSW_2ND_COLUMN, y, lswTimerValue(cs->v1), LEFT|PREC1|attr1);
|
||||
lcdDrawNumber(CSW_3RD_COLUMN, y, lswTimerValue(cs->v2), LEFT|PREC1|attr2);
|
||||
v1_min = v2_min = -128;
|
||||
v1_max = v2_max = 122;
|
||||
INCDEC_SET_FLAG(EE_MODEL);
|
||||
INCDEC_ENABLE_CHECK(NULL);
|
||||
}
|
||||
else {
|
||||
v1_val = cs->v1;
|
||||
putsMixerSource(CSW_2ND_COLUMN, y, v1_val, attr1);
|
||||
if (menuHorizontalPosition == 1) {
|
||||
INCDEC_SET_FLAG(EE_MODEL | INCDEC_SOURCE);
|
||||
INCDEC_ENABLE_CHECK(isSourceAvailableInCustomSwitches);
|
||||
}
|
||||
else {
|
||||
INCDEC_SET_FLAG(EE_MODEL);
|
||||
INCDEC_ENABLE_CHECK(NULL);
|
||||
}
|
||||
v2_max = getMaximumValue(v1_val);
|
||||
v2_min = - v2_max;
|
||||
putsChannelValue(CSW_3RD_COLUMN, y, v1_val, v1_val <= MIXSRC_LAST_CH ? calc100toRESX(cs->v2) : cs->v2, LEFT|attr2);
|
||||
}
|
||||
|
||||
// CSW AND switch
|
||||
putsSwitches(CSW_4TH_COLUMN, y, cs->andsw, menuHorizontalPosition==LS_FIELD_ANDSW ? attr : 0);
|
||||
|
||||
// CSW duration
|
||||
if (cs->duration > 0)
|
||||
lcdDrawNumber(CSW_5TH_COLUMN, y, cs->duration, (menuHorizontalPosition==LS_FIELD_DURATION ? attr : 0)|PREC1|LEFT);
|
||||
else
|
||||
lcdDrawTextAtIndex(CSW_5TH_COLUMN, y, STR_MMMINV, 0, menuHorizontalPosition==LS_FIELD_DURATION ? attr : 0);
|
||||
|
||||
// CSW delay
|
||||
if (cstate == LS_FAMILY_EDGE) {
|
||||
lcdDrawText(CSW_6TH_COLUMN, y, STR_NA);
|
||||
}
|
||||
else if (cs->delay > 0) {
|
||||
lcdDrawNumber(CSW_6TH_COLUMN, y, cs->delay, (menuHorizontalPosition==LS_FIELD_DELAY ? attr : 0)|PREC1|LEFT);
|
||||
}
|
||||
else {
|
||||
lcdDrawTextAtIndex(CSW_6TH_COLUMN, y, STR_MMMINV, 0, menuHorizontalPosition==LS_FIELD_DELAY ? attr : 0);
|
||||
}
|
||||
|
||||
if (s_editMode>0 && attr) {
|
||||
switch (menuHorizontalPosition) {
|
||||
case LS_FIELD_FUNCTION:
|
||||
{
|
||||
cs->func = checkIncDec(event, cs->func, 0, LS_FUNC_MAX, EE_MODEL, isLogicalSwitchFunctionAvailable);
|
||||
uint8_t new_cstate = lswFamily(cs->func);
|
||||
if (cstate != new_cstate) {
|
||||
if (new_cstate == LS_FAMILY_TIMER) {
|
||||
cs->v1 = cs->v2 = -119;
|
||||
}
|
||||
else if (new_cstate == LS_FAMILY_EDGE) {
|
||||
cs->v1 = 0; cs->v2 = -129; cs->v3 = 0;
|
||||
}
|
||||
else {
|
||||
cs->v1 = cs->v2 = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case LS_FIELD_V1:
|
||||
cs->v1 = CHECK_INCDEC_PARAM(event, v1_val, v1_min, v1_max);
|
||||
break;
|
||||
case LS_FIELD_V2:
|
||||
cs->v2 = CHECK_INCDEC_PARAM(event, cs->v2, v2_min, v2_max);
|
||||
if (cstate==LS_FAMILY_OFS && cs->v1!=0 && event==EVT_KEY_LONG(KEY_ENTER)) {
|
||||
killEvents(event);
|
||||
getvalue_t x = getValue(v1_val);
|
||||
if (v1_val <= MIXSRC_LAST_CH)
|
||||
cs->v2 = calcRESXto100(x);
|
||||
storageDirty(EE_MODEL);
|
||||
}
|
||||
break;
|
||||
case LS_FIELD_V3:
|
||||
cs->v3 = CHECK_INCDEC_PARAM(event, cs->v3, v3_min, v3_max);
|
||||
break;
|
||||
case LS_FIELD_ANDSW:
|
||||
INCDEC_SET_FLAG(EE_MODEL | INCDEC_SWITCH);
|
||||
INCDEC_ENABLE_CHECK(isSwitchAvailableInLogicalSwitches);
|
||||
cs->andsw = CHECK_INCDEC_PARAM(event, cs->andsw, -MAX_LS_ANDSW, MAX_LS_ANDSW);
|
||||
break;
|
||||
case LS_FIELD_DURATION:
|
||||
CHECK_INCDEC_MODELVAR_ZERO(event, cs->duration, MAX_LS_DURATION);
|
||||
break;
|
||||
case LS_FIELD_DELAY:
|
||||
CHECK_INCDEC_MODELVAR_ZERO(event, cs->delay, MAX_LS_DELAY);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,605 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "opentx.h"
|
||||
#include <stdio.h>
|
||||
|
||||
int getMixesLinesCount()
|
||||
{
|
||||
int lastch = -1;
|
||||
uint8_t count = NUM_CHNOUT;
|
||||
for (int i=0; i<MAX_MIXERS; i++) {
|
||||
bool valid = mixAddress(i)->srcRaw;
|
||||
if (!valid)
|
||||
break;
|
||||
int ch = mixAddress(i)->destCh;
|
||||
if (ch == lastch) {
|
||||
count++;
|
||||
}
|
||||
else {
|
||||
lastch = ch;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
uint8_t getMixesCount()
|
||||
{
|
||||
uint8_t count = 0;
|
||||
uint8_t ch ;
|
||||
|
||||
for (int i=MAX_MIXERS-1; i>=0; i--) {
|
||||
ch = mixAddress(i)->srcRaw;
|
||||
if (ch != 0) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
bool reachMixesLimit()
|
||||
{
|
||||
if (getMixesCount() >= MAX_MIXERS) {
|
||||
POPUP_WARNING(STR_NOFREEMIXER);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void deleteMix(uint8_t idx)
|
||||
{
|
||||
pauseMixerCalculations();
|
||||
MixData * mix = mixAddress(idx);
|
||||
memmove(mix, mix+1, (MAX_MIXERS-(idx+1))*sizeof(MixData));
|
||||
memclear(&g_model.mixData[MAX_MIXERS-1], sizeof(MixData));
|
||||
resumeMixerCalculations();
|
||||
storageDirty(EE_MODEL);
|
||||
}
|
||||
|
||||
void insertMix(uint8_t idx)
|
||||
{
|
||||
pauseMixerCalculations();
|
||||
MixData * mix = mixAddress(idx);
|
||||
memmove(mix+1, mix, (MAX_MIXERS-(idx+1))*sizeof(MixData));
|
||||
memclear(mix, sizeof(MixData));
|
||||
mix->destCh = s_currCh-1;
|
||||
mix->srcRaw = s_currCh;
|
||||
if (!isSourceAvailable(mix->srcRaw)) {
|
||||
mix->srcRaw = (s_currCh > 4 ? MIXSRC_Rud - 1 + s_currCh : MIXSRC_Rud - 1 + channel_order(s_currCh));
|
||||
while (!isSourceAvailable(mix->srcRaw)) {
|
||||
mix->srcRaw += 1;
|
||||
}
|
||||
}
|
||||
mix->weight = 100;
|
||||
resumeMixerCalculations();
|
||||
storageDirty(EE_MODEL);
|
||||
}
|
||||
|
||||
void copyMix(uint8_t idx)
|
||||
{
|
||||
pauseMixerCalculations();
|
||||
MixData * mix = mixAddress(idx);
|
||||
memmove(mix+1, mix, (MAX_MIXERS-(idx+1))*sizeof(MixData));
|
||||
resumeMixerCalculations();
|
||||
storageDirty(EE_MODEL);
|
||||
}
|
||||
|
||||
bool swapMixes(uint8_t & idx, uint8_t up)
|
||||
{
|
||||
MixData * x, * y;
|
||||
int8_t tgt_idx = (up ? idx-1 : idx+1);
|
||||
|
||||
x = mixAddress(idx);
|
||||
|
||||
if (tgt_idx < 0) {
|
||||
if (x->destCh == 0)
|
||||
return false;
|
||||
x->destCh--;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (tgt_idx == MAX_MIXERS) {
|
||||
if (x->destCh == NUM_CHNOUT-1)
|
||||
return false;
|
||||
x->destCh++;
|
||||
return true;
|
||||
}
|
||||
|
||||
y = mixAddress(tgt_idx);
|
||||
uint8_t destCh = x->destCh;
|
||||
if(!y->srcRaw || destCh != y->destCh) {
|
||||
if (up) {
|
||||
if (destCh>0) x->destCh--;
|
||||
else return false;
|
||||
}
|
||||
else {
|
||||
if (destCh<NUM_CHNOUT-1) x->destCh++;
|
||||
else return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
pauseMixerCalculations();
|
||||
memswap(x, y, sizeof(MixData));
|
||||
resumeMixerCalculations();
|
||||
|
||||
idx = tgt_idx;
|
||||
return true;
|
||||
}
|
||||
|
||||
enum MixFields {
|
||||
MIX_FIELD_NAME,
|
||||
MIX_FIELD_SOURCE,
|
||||
MIX_FIELD_WEIGHT,
|
||||
MIX_FIELD_OFFSET,
|
||||
MIX_FIELD_TRIM,
|
||||
CASE_CURVES(MIX_FIELD_CURVE)
|
||||
CASE_FLIGHT_MODES(MIX_FIELD_FLIGHT_PHASE)
|
||||
MIX_FIELD_SWITCH,
|
||||
// MIX_FIELD_WARNING,
|
||||
MIX_FIELD_MLTPX,
|
||||
MIX_FIELD_DELAY_UP,
|
||||
MIX_FIELD_DELAY_DOWN,
|
||||
MIX_FIELD_SLOW_UP,
|
||||
MIX_FIELD_SLOW_DOWN,
|
||||
MIX_FIELD_COUNT
|
||||
};
|
||||
|
||||
void gvarWeightItem(coord_t x, coord_t y, MixData *md, LcdFlags attr, evt_t event)
|
||||
{
|
||||
u_int8int16_t weight;
|
||||
MD_WEIGHT_TO_UNION(md, weight);
|
||||
weight.word = GVAR_MENU_ITEM(x, y, weight.word, GV_RANGELARGE_WEIGHT_NEG, GV_RANGELARGE_WEIGHT, attr, 0, event);
|
||||
MD_UNION_TO_WEIGHT(weight, md);
|
||||
}
|
||||
|
||||
bool menuModelMixOne(evt_t event)
|
||||
{
|
||||
MixData * md2 = mixAddress(s_currIdx) ;
|
||||
|
||||
SUBMENU_WITH_OPTIONS(STR_MIXER, ICON_MODEL_MIXER, MIX_FIELD_COUNT, OPTION_MENU_NO_SCROLLBAR, { 0, 0, 0, 0, 0, CASE_CURVES(1) CASE_FLIGHT_MODES((MAX_FLIGHT_MODES-1) | NAVIGATION_LINE_BY_LINE) 0 /*, ...*/ });
|
||||
putsChn(50, 3+FH, md2->destCh+1, MENU_TITLE_COLOR);
|
||||
|
||||
// The separation line between 2 columns
|
||||
lcdDrawSolidVerticalLine(MENU_COLUMN2_X-10, DEFAULT_SCROLLBAR_Y-FH, DEFAULT_SCROLLBAR_H+5, TEXT_COLOR);
|
||||
|
||||
int8_t sub = menuVerticalPosition;
|
||||
int8_t editMode = s_editMode;
|
||||
|
||||
for (int k=0; k<2*NUM_BODY_LINES; k++) {
|
||||
coord_t y;
|
||||
if (k >= NUM_BODY_LINES) {
|
||||
y = MENU_CONTENT_TOP - FH + (k-NUM_BODY_LINES)*FH;
|
||||
}
|
||||
else {
|
||||
y = MENU_CONTENT_TOP - FH + k*FH;
|
||||
}
|
||||
int8_t i = k;
|
||||
|
||||
LcdFlags attr = (sub==i ? (editMode>0 ? BLINK|INVERS : INVERS) : 0);
|
||||
switch(i) {
|
||||
case MIX_FIELD_NAME:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_MIXNAME);
|
||||
editName(MIXES_2ND_COLUMN, y, md2->name, sizeof(md2->name), event, attr);
|
||||
break;
|
||||
case MIX_FIELD_SOURCE:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, NO_INDENT(STR_SOURCE));
|
||||
putsMixerSource(MIXES_2ND_COLUMN, y, md2->srcRaw, attr);
|
||||
if (attr) CHECK_INCDEC_MODELSOURCE(event, md2->srcRaw, 1, MIXSRC_LAST);
|
||||
break;
|
||||
case MIX_FIELD_WEIGHT:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_WEIGHT);
|
||||
gvarWeightItem(MIXES_2ND_COLUMN, y, md2, attr|LEFT, event);
|
||||
break;
|
||||
case MIX_FIELD_OFFSET:
|
||||
{
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, NO_INDENT(STR_OFFSET));
|
||||
u_int8int16_t offset;
|
||||
MD_OFFSET_TO_UNION(md2, offset);
|
||||
offset.word = GVAR_MENU_ITEM(MIXES_2ND_COLUMN, y, offset.word, GV_RANGELARGE_OFFSET_NEG, GV_RANGELARGE_OFFSET, attr|LEFT, 0, event);
|
||||
MD_UNION_TO_OFFSET(offset, md2);
|
||||
#if 0
|
||||
drawOffsetBar(x+MIXES_2ND_COLUMN+22, y, md2);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case MIX_FIELD_TRIM:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_TRIM);
|
||||
drawCheckBox(MIXES_2ND_COLUMN, y, !md2->carryTrim, attr);
|
||||
if (attr) md2->carryTrim = !checkIncDecModel(event, !md2->carryTrim, 0, 1);
|
||||
break;
|
||||
#if defined(CURVES)
|
||||
case MIX_FIELD_CURVE:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_CURVE);
|
||||
editCurveRef(MIXES_2ND_COLUMN, y, md2->curve, event, attr);
|
||||
break;
|
||||
#endif
|
||||
#if defined(FLIGHT_MODES)
|
||||
case MIX_FIELD_FLIGHT_PHASE:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_FLMODE);
|
||||
md2->flightModes = editFlightModes(MIXES_2ND_COLUMN, y, event, md2->flightModes, attr);
|
||||
break;
|
||||
#endif
|
||||
case MIX_FIELD_SWITCH:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_SWITCH);
|
||||
md2->swtch = switchMenuItem(MIXES_2ND_COLUMN, y, md2->swtch, attr, event);
|
||||
break;
|
||||
case MIX_FIELD_MLTPX:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_MULTPX);
|
||||
md2->mltpx = selectMenuItem(MIXES_2ND_COLUMN, y, STR_VMLTPX, md2->mltpx, 0, 2, attr, event);
|
||||
break;
|
||||
case MIX_FIELD_DELAY_UP:
|
||||
lcdDrawText(MENU_COLUMN2_X+MENUS_MARGIN_LEFT, y, STR_DELAYUP);
|
||||
md2->delayUp = editDelay(MENU_COLUMN2_X, y, event, attr, md2->delayUp);
|
||||
break;
|
||||
case MIX_FIELD_DELAY_DOWN:
|
||||
lcdDrawText(MENU_COLUMN2_X+MENUS_MARGIN_LEFT, y, STR_DELAYDOWN);
|
||||
md2->delayDown = editDelay(MENU_COLUMN2_X, y, event, attr, md2->delayDown);
|
||||
break;
|
||||
case MIX_FIELD_SLOW_UP:
|
||||
lcdDrawText(MENU_COLUMN2_X+MENUS_MARGIN_LEFT, y, STR_SLOWUP);
|
||||
md2->speedUp = editDelay(MENU_COLUMN2_X, y, event, attr, md2->speedUp);
|
||||
break;
|
||||
case MIX_FIELD_SLOW_DOWN:
|
||||
lcdDrawText(MENU_COLUMN2_X+MENUS_MARGIN_LEFT, y, STR_SLOWDOWN);
|
||||
md2->speedDown = editDelay(MENU_COLUMN2_X, y, event, attr, md2->speedDown);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#define _STR_MAX(x) PSTR("/" #x)
|
||||
#define STR_MAX(x) _STR_MAX(x)
|
||||
|
||||
#define MIX_LINE_WEIGHT_POS 105
|
||||
#define MIX_LINE_SRC_POS 120
|
||||
#define MIX_LINE_CURVE_ICON 175
|
||||
#define MIX_LINE_CURVE_POS 195
|
||||
#define MIX_LINE_SWITCH_ICON 260
|
||||
#define MIX_LINE_SWITCH_POS 280
|
||||
#define MIX_LINE_DELAY_SLOW_POS 340
|
||||
#define MIX_LINE_NAME_FM_ICON 370
|
||||
#define MIX_LINE_NAME_FM_POS 390
|
||||
#define MIX_LINE_SELECT_POS 50
|
||||
#define MIX_LINE_SELECT_WIDTH (LCD_W-MIX_LINE_SELECT_POS-15)
|
||||
#define MIX_STATUS_MARGIN_LEFT MENUS_MARGIN_LEFT + 45
|
||||
#define MIX_STATUS_ICON_MIXER MENUS_MARGIN_LEFT + 185
|
||||
#define MIX_STATUS_ICON_TO MENUS_MARGIN_LEFT + 205
|
||||
#define MIX_STATUS_ICON_OUTPUT MENUS_MARGIN_LEFT + 240
|
||||
#define MIX_STATUS_OUT_NAME MENUS_MARGIN_LEFT + 265
|
||||
#define MIX_STATUS_OUT_BAR MENUS_MARGIN_LEFT + 320
|
||||
#define MIX_STATUS_BAR_W 130
|
||||
#define MIX_STATUS_BAR_H 13
|
||||
|
||||
void lineMixSurround(coord_t y, LcdFlags flags=CURVE_AXIS_COLOR)
|
||||
{
|
||||
lcdDrawRect(MIX_LINE_SELECT_POS, y-INVERT_VERT_MARGIN+1, MIX_LINE_SELECT_WIDTH, INVERT_LINE_HEIGHT, 1, s_copyMode == COPY_MODE ? SOLID : DOTTED, flags);
|
||||
}
|
||||
|
||||
void onMixesMenu(const char * result)
|
||||
{
|
||||
uint8_t chn = mixAddress(s_currIdx)->destCh + 1;
|
||||
|
||||
if (result == STR_EDIT) {
|
||||
pushMenu(menuModelMixOne);
|
||||
}
|
||||
else if (result == STR_INSERT_BEFORE || result == STR_INSERT_AFTER) {
|
||||
if (!reachMixesLimit()) {
|
||||
s_currCh = chn;
|
||||
if (result == STR_INSERT_AFTER) { s_currIdx++; menuVerticalPosition++; }
|
||||
insertMix(s_currIdx);
|
||||
pushMenu(menuModelMixOne);
|
||||
}
|
||||
}
|
||||
else if (result == STR_COPY || result == STR_MOVE) {
|
||||
s_copyMode = (result == STR_COPY ? COPY_MODE : MOVE_MODE);
|
||||
s_copySrcIdx = s_currIdx;
|
||||
s_copySrcCh = chn;
|
||||
s_copySrcRow = menuVerticalPosition;
|
||||
}
|
||||
else if (result == STR_DELETE) {
|
||||
deleteMix(s_currIdx);
|
||||
}
|
||||
}
|
||||
|
||||
void displayMixInfos(coord_t y, MixData *md)
|
||||
{
|
||||
if (md->curve.value != 0 ) lcd->drawBitmap(MIX_LINE_CURVE_ICON, y + 2, mixerSetupCurveBitmap);
|
||||
putsCurveRef(MIX_LINE_CURVE_POS, y, md->curve);
|
||||
|
||||
if (md->swtch) {
|
||||
lcd->drawBitmap(MIX_LINE_SWITCH_ICON, y + 2, mixerSetupSwitchBitmap);
|
||||
putsSwitches(MIX_LINE_SWITCH_POS, y, md->swtch);
|
||||
}
|
||||
}
|
||||
|
||||
void displayMixSmallFlightModes(coord_t x, coord_t y, FlightModesType value)
|
||||
{
|
||||
for (int i=0; i<MAX_FLIGHT_MODES; i++) {
|
||||
char s[] = " ";
|
||||
s[0] = '0' + i;
|
||||
if (value & (1<<i)) lcd->drawFilledRect(x, y+2, 8, 12 , SOLID, CURVE_AXIS_COLOR);
|
||||
lcdDrawText(x, y, s, SMLSIZE);
|
||||
x += 8;
|
||||
}
|
||||
}
|
||||
|
||||
void displayMixLine(coord_t y, MixData *md)
|
||||
{
|
||||
if (md->name[0] && md->flightModes) {
|
||||
if (SLOW_BLINK_ON_PHASE) {
|
||||
lcd->drawBitmap(MIX_LINE_NAME_FM_ICON, y + 2, mixerSetupFlightmodeBitmap);
|
||||
displayMixSmallFlightModes(MIX_LINE_NAME_FM_POS, y + 2, md->flightModes);
|
||||
}
|
||||
else {
|
||||
lcd->drawBitmap(MIX_LINE_NAME_FM_ICON, y + 2, mixerSetupLabelBitmap);
|
||||
lcdDrawSizedText(MIX_LINE_NAME_FM_POS, y, md->name, sizeof(md->name), ZCHAR);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (md->name[0]) {
|
||||
lcd->drawBitmap(MIX_LINE_NAME_FM_ICON, y + 2, mixerSetupLabelBitmap);
|
||||
lcdDrawSizedText(MIX_LINE_NAME_FM_POS, y, md->name, sizeof(md->name), ZCHAR);
|
||||
}
|
||||
if (md->flightModes) {
|
||||
lcd->drawBitmap(MIX_LINE_NAME_FM_ICON, y + 2, mixerSetupFlightmodeBitmap);
|
||||
displayMixSmallFlightModes(MIX_LINE_NAME_FM_POS, y + 2, md->flightModes);
|
||||
}
|
||||
}
|
||||
displayMixInfos(y, md);
|
||||
}
|
||||
|
||||
void displayMixStatus(uint8_t channel)
|
||||
{
|
||||
lcdDrawNumber(MENUS_MARGIN_LEFT, MENU_FOOTER_TOP, channel + 1, MENU_TITLE_COLOR, 0, "CH", NULL);
|
||||
drawSingleMixerBar(MIX_STATUS_MARGIN_LEFT, MENU_FOOTER_TOP + 4, MIX_STATUS_BAR_W, MIX_STATUS_BAR_H, channel);
|
||||
|
||||
lcd->drawBitmap(MIX_STATUS_ICON_MIXER, MENU_FOOTER_TOP, mixerSetupMixerBitmap);
|
||||
lcd->drawBitmap(MIX_STATUS_ICON_TO, MENU_FOOTER_TOP, mixerSetupToBitmap);
|
||||
lcd->drawBitmap(MIX_STATUS_ICON_OUTPUT, MENU_FOOTER_TOP, mixerSetupOutputBitmap);
|
||||
|
||||
lcdDrawSizedText(MIX_STATUS_OUT_NAME, MENU_FOOTER_TOP, g_model.limitData[channel].name, sizeof(g_model.limitData[channel].name), MENU_TITLE_COLOR | LEFT | ZCHAR);
|
||||
drawSingleOutputBar(MIX_STATUS_OUT_BAR, MENU_FOOTER_TOP + 4, MIX_STATUS_BAR_W, MIX_STATUS_BAR_H, channel);
|
||||
}
|
||||
|
||||
bool menuModelMixAll(evt_t event)
|
||||
{
|
||||
BitmapBuffer * mpx_mode[] = {
|
||||
mixerSetupAddBitmap,
|
||||
mixerSetupMultiBitmap,
|
||||
mixerSetupReplaceBitmap
|
||||
};
|
||||
|
||||
|
||||
int sub = menuVerticalPosition;
|
||||
|
||||
if (s_editMode > 0) {
|
||||
s_editMode = 0;
|
||||
}
|
||||
|
||||
uint8_t chn = mixAddress(s_currIdx)->destCh + 1;
|
||||
|
||||
int linesCount = getMixesLinesCount();
|
||||
SIMPLE_MENU(STR_MIXER, MODEL_ICONS, menuTabModel, e_MixAll, linesCount);
|
||||
|
||||
switch (event) {
|
||||
case EVT_ENTRY:
|
||||
case EVT_ENTRY_UP:
|
||||
s_copyMode = 0;
|
||||
s_copyTgtOfs = 0;
|
||||
break;
|
||||
case EVT_KEY_LONG(KEY_EXIT):
|
||||
if (s_copyMode && s_copyTgtOfs == 0) {
|
||||
deleteMix(s_currIdx);
|
||||
killEvents(event);
|
||||
event = 0;
|
||||
}
|
||||
// no break
|
||||
case EVT_KEY_BREAK(KEY_EXIT):
|
||||
if (s_copyMode) {
|
||||
if (s_copyTgtOfs) {
|
||||
// cancel the current copy / move operation
|
||||
if (s_copyMode == COPY_MODE) {
|
||||
deleteMix(s_currIdx);
|
||||
}
|
||||
else {
|
||||
do {
|
||||
swapMixes(s_currIdx, s_copyTgtOfs > 0);
|
||||
s_copyTgtOfs += (s_copyTgtOfs < 0 ? +1 : -1);
|
||||
} while (s_copyTgtOfs != 0);
|
||||
storageDirty(EE_MODEL);
|
||||
}
|
||||
menuVerticalPosition = s_copySrcRow;
|
||||
s_copyTgtOfs = 0;
|
||||
}
|
||||
s_copyMode = 0;
|
||||
event = 0;
|
||||
}
|
||||
break;
|
||||
case EVT_KEY_BREAK(KEY_ENTER):
|
||||
if ((!s_currCh || (s_copyMode && !s_copyTgtOfs)) && !READ_ONLY()) {
|
||||
s_copyMode = (s_copyMode == COPY_MODE ? MOVE_MODE : COPY_MODE);
|
||||
s_copySrcIdx = s_currIdx;
|
||||
s_copySrcCh = chn;
|
||||
s_copySrcRow = sub;
|
||||
break;
|
||||
}
|
||||
// no break
|
||||
|
||||
case EVT_KEY_LONG(KEY_ENTER):
|
||||
killEvents(event);
|
||||
if (s_copyTgtOfs) {
|
||||
s_copyMode = 0;
|
||||
s_copyTgtOfs = 0;
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
if (READ_ONLY()) {
|
||||
if (!s_currCh) {
|
||||
pushMenu(menuModelMixOne);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (s_copyMode) s_currCh = 0;
|
||||
if (s_currCh) {
|
||||
if (reachMixesLimit()) break;
|
||||
insertMix(s_currIdx);
|
||||
pushMenu(menuModelMixOne);
|
||||
s_copyMode = 0;
|
||||
return true;
|
||||
}
|
||||
else if (menuVerticalPosition >= 0) {
|
||||
event = 0;
|
||||
s_copyMode = 0;
|
||||
POPUP_MENU_ADD_ITEM(STR_EDIT);
|
||||
POPUP_MENU_ADD_ITEM(STR_INSERT_BEFORE);
|
||||
POPUP_MENU_ADD_ITEM(STR_INSERT_AFTER);
|
||||
POPUP_MENU_ADD_ITEM(STR_COPY);
|
||||
POPUP_MENU_ADD_ITEM(STR_MOVE);
|
||||
POPUP_MENU_ADD_ITEM(STR_DELETE);
|
||||
POPUP_MENU_START(onMixesMenu);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case EVT_ROTARY_RIGHT:
|
||||
case EVT_ROTARY_LEFT:
|
||||
if (s_copyMode) {
|
||||
uint8_t next_ofs = (event==EVT_ROTARY_LEFT ? s_copyTgtOfs - 1 : s_copyTgtOfs + 1);
|
||||
|
||||
if (s_copyTgtOfs==0 && s_copyMode==COPY_MODE) {
|
||||
// insert a mix on the same channel (just above / just below)
|
||||
if (reachMixesLimit()) break;
|
||||
copyMix(s_currIdx);
|
||||
if (event==EVT_ROTARY_RIGHT) s_currIdx++;
|
||||
else if (sub-menuVerticalOffset >= 6) menuVerticalOffset++;
|
||||
}
|
||||
else if (next_ofs==0 && s_copyMode==COPY_MODE) {
|
||||
// delete the mix
|
||||
deleteMix(s_currIdx);
|
||||
if (event==EVT_ROTARY_LEFT) s_currIdx--;
|
||||
}
|
||||
else {
|
||||
// only swap the mix with its neighbor
|
||||
if (!swapMixes(s_currIdx, event==EVT_ROTARY_LEFT)) break;
|
||||
storageDirty(EE_MODEL);
|
||||
}
|
||||
|
||||
s_copyTgtOfs = next_ofs;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
char str[6];
|
||||
sprintf(str, "%d/%d", getMixesCount(), MAX_MIXERS);
|
||||
lcdDrawText(MENU_TITLE_NEXT_POS, MENU_TITLE_TOP+1, str, HEADER_COLOR);
|
||||
|
||||
sub = menuVerticalPosition;
|
||||
s_currCh = 0;
|
||||
int cur = 0;
|
||||
int i = 0;
|
||||
|
||||
for (int ch=1; ch<=NUM_CHNOUT; ch++) {
|
||||
MixData * md;
|
||||
coord_t y = MENU_CONTENT_TOP + (cur-menuVerticalOffset)*FH;
|
||||
if (i<MAX_MIXERS && (md=mixAddress(i))->srcRaw && md->destCh+1 == ch) {
|
||||
if (cur-menuVerticalOffset >= 0 && cur-menuVerticalOffset < NUM_BODY_LINES) {
|
||||
putsChn(MENUS_MARGIN_LEFT, y, ch, 0); // show CHx
|
||||
}
|
||||
uint8_t mixCnt = 0;
|
||||
do {
|
||||
if (s_copyMode) {
|
||||
if (s_copyMode == MOVE_MODE && cur-menuVerticalOffset >= 0 && cur-menuVerticalOffset < NUM_BODY_LINES && s_copySrcCh == ch && s_copyTgtOfs != 0 && i == (s_copySrcIdx + (s_copyTgtOfs<0))) {
|
||||
lineMixSurround(y);
|
||||
cur++; y+=FH;
|
||||
}
|
||||
if (s_currIdx == i) {
|
||||
sub = menuVerticalPosition = cur;
|
||||
s_currCh = ch;
|
||||
}
|
||||
}
|
||||
else if (sub == cur) {
|
||||
s_currIdx = i;
|
||||
displayMixStatus(ch - 1);
|
||||
}
|
||||
if (cur-menuVerticalOffset >= 0 && cur-menuVerticalOffset < NUM_BODY_LINES) {
|
||||
LcdFlags attr = ((s_copyMode || sub != cur) ? 0 : INVERS);
|
||||
|
||||
if (mixCnt > 0) lcd->drawBitmap(10, y, mpx_mode[md->mltpx]);
|
||||
|
||||
putsMixerSource(MIX_LINE_SRC_POS, y, md->srcRaw);
|
||||
|
||||
gvarWeightItem(MIX_LINE_WEIGHT_POS, y, md, RIGHT | attr | (isMixActive(i) ? BOLD : 0), event);
|
||||
|
||||
displayMixLine(y, md);
|
||||
|
||||
BitmapBuffer *delayslowbmp[] = {mixerSetupSlowBitmap, mixerSetupDelayBitmap, mixerSetupDelaySlowBitmap};
|
||||
uint8_t delayslow = 0;
|
||||
if (md->speedDown || md->speedUp)
|
||||
delayslow = 1;
|
||||
if (md->delayUp || md->delayDown)
|
||||
delayslow += 2;
|
||||
if (delayslow)
|
||||
lcd->drawBitmap(MIX_LINE_DELAY_SLOW_POS, y + 2, delayslowbmp[delayslow - 1]);
|
||||
|
||||
if (s_copyMode) {
|
||||
if ((s_copyMode==COPY_MODE || s_copyTgtOfs == 0) && s_copySrcCh == ch && i == (s_copySrcIdx + (s_copyTgtOfs<0))) {
|
||||
/* draw a border around the raw on selection mode (copy/move) */
|
||||
lineMixSurround(y);
|
||||
}
|
||||
if (cur == sub) {
|
||||
/* invert the raw when it's the current one */
|
||||
lineMixSurround(y, ALARM_COLOR);
|
||||
}
|
||||
}
|
||||
}
|
||||
cur++; y+=FH; mixCnt++; i++; md++;
|
||||
} while (i<MAX_MIXERS && md->srcRaw && md->destCh+1 == ch);
|
||||
if (s_copyMode == MOVE_MODE && cur-menuVerticalOffset >= 0 && cur-menuVerticalOffset < NUM_BODY_LINES && s_copySrcCh == ch && i == (s_copySrcIdx + (s_copyTgtOfs<0))) {
|
||||
lineMixSurround(y);
|
||||
cur++; y+=FH;
|
||||
}
|
||||
}
|
||||
else {
|
||||
uint8_t attr = 0;
|
||||
if (sub == cur) {
|
||||
s_currIdx = i;
|
||||
s_currCh = ch;
|
||||
if (!s_copyMode) {
|
||||
attr = INVERS;
|
||||
}
|
||||
}
|
||||
if (cur-menuVerticalOffset >= 0 && cur-menuVerticalOffset < NUM_BODY_LINES) {
|
||||
putsChn(MENUS_MARGIN_LEFT, y, ch, attr); // show CHx
|
||||
if (s_copyMode == MOVE_MODE && s_copySrcCh == ch) {
|
||||
lineMixSurround(y);
|
||||
}
|
||||
}
|
||||
cur++; y+=FH;
|
||||
}
|
||||
}
|
||||
|
||||
if (sub >= linesCount-1) menuVerticalPosition = linesCount-1;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,256 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "opentx.h"
|
||||
|
||||
bool isThrottleOutput(uint8_t ch)
|
||||
{
|
||||
for (uint8_t i=0; i<MAX_MIXERS; i++) {
|
||||
MixData *mix = mixAddress(i);
|
||||
if (mix->destCh==ch && mix->srcRaw==MIXSRC_Thr)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
enum LimitsItems {
|
||||
ITEM_LIMITS_CH_NAME,
|
||||
ITEM_LIMITS_OFFSET,
|
||||
ITEM_LIMITS_MIN,
|
||||
ITEM_LIMITS_MAX,
|
||||
ITEM_LIMITS_DIRECTION,
|
||||
#if defined(CURVES)
|
||||
ITEM_LIMITS_CURVE,
|
||||
#endif
|
||||
#if defined(PPM_CENTER_ADJUSTABLE)
|
||||
ITEM_LIMITS_PPM_CENTER,
|
||||
#endif
|
||||
#if defined(PPM_LIMITS_SYMETRICAL)
|
||||
ITEM_LIMITS_SYMETRICAL,
|
||||
#endif
|
||||
ITEM_LIMITS_COUNT,
|
||||
ITEM_LIMITS_MAXROW = ITEM_LIMITS_COUNT-1
|
||||
};
|
||||
|
||||
#define LIMITS_NAME_POS 52
|
||||
#define LIMITS_OFFSET_POS 160
|
||||
#define LIMITS_MIN_POS 220
|
||||
#define LIMITS_DIRECTION_POS 240
|
||||
#define LIMITS_MAX_POS 300
|
||||
#define LIMITS_REVERT_POS 320
|
||||
#define LIMITS_CURVE_POS 350
|
||||
#define LIMITS_PPM_CENTER_POS 440
|
||||
#define LIMITS_SYMETRICAL_POS 450
|
||||
|
||||
#define LIMITS_MIN_MAX_OFFSET 1000
|
||||
#define CONVERT_US_MIN_MAX(x) (((x)*1280)/250)
|
||||
|
||||
#if defined(PPM_UNIT_US)
|
||||
#define SET_MIN_MAX(x, val) x = ((val)*250)/128
|
||||
#define MIN_MAX_DISPLAY(x) CONVERT_US_MIN_MAX(x)
|
||||
#else
|
||||
#define MIN_MAX_DISPLAY(x) (x)
|
||||
#define SET_MIN_MAX(x, val) x = (val)
|
||||
#endif
|
||||
|
||||
void onLimitsMenu(const char *result)
|
||||
{
|
||||
uint8_t ch = menuVerticalPosition - 1;
|
||||
|
||||
if (result == STR_RESET) {
|
||||
LimitData *ld = limitAddress(ch);
|
||||
ld->min = 0;
|
||||
ld->max = 0;
|
||||
ld->offset = 0;
|
||||
ld->ppmCenter = 0;
|
||||
ld->revert = false;
|
||||
ld->curve = 0;
|
||||
}
|
||||
else if (result == STR_COPY_STICKS_TO_OFS) {
|
||||
copySticksToOffset(ch);
|
||||
}
|
||||
else if (result == STR_COPY_TRIMS_TO_OFS) {
|
||||
copyTrimsToOffset(ch);
|
||||
}
|
||||
}
|
||||
|
||||
bool menuModelLimits(evt_t event)
|
||||
{
|
||||
MENU(STR_MENULIMITS, MODEL_ICONS, menuTabModel, e_Limits, NUM_CHNOUT+1,
|
||||
{ NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW,
|
||||
NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW,
|
||||
NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW,
|
||||
NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW, NAVIGATION_LINE_BY_LINE|ITEM_LIMITS_MAXROW,
|
||||
0 });
|
||||
|
||||
uint32_t sub = menuVerticalPosition;
|
||||
|
||||
if (sub<NUM_CHNOUT && menuHorizontalPosition>=0) {
|
||||
drawColumnHeader(STR_LIMITS_HEADERS, NULL, menuHorizontalPosition);
|
||||
}
|
||||
|
||||
if (warningResult) {
|
||||
warningResult = 0;
|
||||
LimitData *ld = limitAddress(sub);
|
||||
ld->revert = !ld->revert;
|
||||
storageDirty(EE_MODEL);
|
||||
}
|
||||
|
||||
for (int i=0; i<NUM_BODY_LINES; i++) {
|
||||
coord_t y = MENU_CONTENT_TOP + i*FH;
|
||||
uint8_t k = i+menuVerticalOffset;
|
||||
|
||||
if (k==NUM_CHNOUT) {
|
||||
// last line available - add the "copy trim menu" line
|
||||
uint8_t attr = (sub==NUM_CHNOUT) ? INVERS : 0;
|
||||
// TODO CENTER attribute
|
||||
lcdDrawText(100, y, STR_TRIMS2OFFSETS, NO_HIGHLIGHT() ? 0 : attr);
|
||||
if (attr) {
|
||||
s_editMode = 0;
|
||||
if (event==EVT_KEY_LONG(KEY_ENTER)) {
|
||||
START_NO_HIGHLIGHT();
|
||||
killEvents(event);
|
||||
moveTrimsToOffsets(); // if highlighted and menu pressed - move trims to offsets
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
LimitData *ld = limitAddress(k);
|
||||
|
||||
int16_t v = (ld->revert) ? -LIMIT_OFS(ld) : LIMIT_OFS(ld);
|
||||
char swVal[2] = "-"; // '-', '<', '>'
|
||||
if ((channelOutputs[k] - v) > 50) swVal[0] = (ld->revert ? 127 : 126); // Switch to raw inputs? - remove trim!
|
||||
if ((channelOutputs[k] - v) < -50) swVal[0] = (ld->revert ? 126 : 127);
|
||||
lcdDrawText(LIMITS_DIRECTION_POS, y, swVal);
|
||||
|
||||
int limit = (g_model.extendedLimits ? LIMIT_EXT_MAX : 1000);
|
||||
|
||||
putsChn(MENUS_MARGIN_LEFT, y, k+1, (sub==k && menuHorizontalPosition < 0) ? INVERS : 0);
|
||||
if (sub==k && menuHorizontalPosition < 0 && event==EVT_KEY_LONG(KEY_ENTER) && !READ_ONLY()) {
|
||||
killEvents(event);
|
||||
POPUP_MENU_ADD_ITEM(STR_RESET);
|
||||
POPUP_MENU_ADD_ITEM(STR_COPY_TRIMS_TO_OFS);
|
||||
POPUP_MENU_ADD_ITEM(STR_COPY_STICKS_TO_OFS);
|
||||
POPUP_MENU_START(onLimitsMenu);
|
||||
}
|
||||
|
||||
for (uint8_t j=0; j<ITEM_LIMITS_COUNT; j++) {
|
||||
uint8_t attr = ((sub==k && menuHorizontalPosition==j) ? ((s_editMode>0) ? BLINK|INVERS : INVERS) : 0);
|
||||
uint8_t active = (attr && s_editMode>0) ;
|
||||
if (active) STICK_SCROLL_DISABLE();
|
||||
switch(j)
|
||||
{
|
||||
case ITEM_LIMITS_CH_NAME:
|
||||
editName(LIMITS_NAME_POS, y, ld->name, sizeof(ld->name), event, attr);
|
||||
break;
|
||||
|
||||
case ITEM_LIMITS_OFFSET:
|
||||
if (GV_IS_GV_VALUE(ld->offset, -1000, 1000) || (attr && event == EVT_KEY_LONG(KEY_ENTER))) {
|
||||
ld->offset = GVAR_MENU_ITEM(LIMITS_OFFSET_POS, y, ld->offset, -1000, 1000, attr|PREC1|RIGHT, 0, event);
|
||||
break;
|
||||
}
|
||||
|
||||
#if defined(PPM_UNIT_US)
|
||||
lcdDrawNumber(LIMITS_OFFSET_POS, y, ((int32_t)ld->offset*128) / 25, attr|PREC1|RIGHT);
|
||||
#else
|
||||
lcdDrawNumber(LIMITS_OFFSET_POS, y, ld->offset, attr|PREC1|RIGHT);
|
||||
#endif
|
||||
if (active) {
|
||||
ld->offset = checkIncDec(event, ld->offset, -1000, 1000, EE_MODEL, NULL, stops1000);
|
||||
}
|
||||
|
||||
// TODO with the contextual menu I think
|
||||
// else if (attr && event==EVT_KEY_LONG(KEY_MENU)) {
|
||||
// copySticksToOffset(k);
|
||||
// s_editMode = 0;
|
||||
// }
|
||||
break;
|
||||
|
||||
case ITEM_LIMITS_MIN:
|
||||
if (GV_IS_GV_VALUE(ld->min, -GV_RANGELARGE, GV_RANGELARGE) || (attr && event == EVT_KEY_LONG(KEY_ENTER))) {
|
||||
ld->min = GVAR_MENU_ITEM(LIMITS_MIN_POS, y, ld->min, -LIMIT_EXT_MAX, LIMIT_EXT_MAX, attr|PREC1|RIGHT, 0, event);
|
||||
break;
|
||||
}
|
||||
lcdDrawNumber(LIMITS_MIN_POS, y, MIN_MAX_DISPLAY(ld->min-LIMITS_MIN_MAX_OFFSET), attr|PREC1|RIGHT);
|
||||
if (active) ld->min = LIMITS_MIN_MAX_OFFSET + checkIncDec(event, ld->min-LIMITS_MIN_MAX_OFFSET, -limit, 0, EE_MODEL, NULL, stops1000);
|
||||
break;
|
||||
|
||||
case ITEM_LIMITS_MAX:
|
||||
if (GV_IS_GV_VALUE(ld->max, -GV_RANGELARGE, GV_RANGELARGE) || (attr && event == EVT_KEY_LONG(KEY_ENTER))) {
|
||||
ld->max = GVAR_MENU_ITEM(LIMITS_MAX_POS, y, ld->max, -LIMIT_EXT_MAX, LIMIT_EXT_MAX, attr|PREC1|RIGHT, 0, event);
|
||||
break;
|
||||
}
|
||||
lcdDrawNumber(LIMITS_MAX_POS, y, MIN_MAX_DISPLAY(ld->max+LIMITS_MIN_MAX_OFFSET), attr|PREC1|RIGHT);
|
||||
if (active) ld->max = -LIMITS_MIN_MAX_OFFSET + checkIncDec(event, ld->max+LIMITS_MIN_MAX_OFFSET, 0, +limit, EE_MODEL, NULL, stops1000);
|
||||
break;
|
||||
|
||||
case ITEM_LIMITS_DIRECTION:
|
||||
{
|
||||
uint8_t revert = ld->revert;
|
||||
lcdDrawText(LIMITS_REVERT_POS, y, revert ? "\177" : "\176", attr);
|
||||
if (active) {
|
||||
uint8_t revert_new = checkIncDecModel(event, revert, 0, 1);
|
||||
if (checkIncDec_Ret && isThrottleOutput(k)) {
|
||||
POPUP_CONFIRMATION(STR_INVERT_THR);
|
||||
}
|
||||
else {
|
||||
ld->revert = revert_new;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
#if defined(CURVES)
|
||||
case ITEM_LIMITS_CURVE:
|
||||
drawCurveName(LIMITS_CURVE_POS, y, ld->curve, attr);
|
||||
if (attr && event==EVT_KEY_LONG(KEY_ENTER) && ld->curve>0) {
|
||||
s_curveChan = (ld->curve<0 ? -ld->curve-1 : ld->curve-1);
|
||||
pushMenu(menuModelCurveOne);
|
||||
}
|
||||
if (active) {
|
||||
CHECK_INCDEC_MODELVAR(event, ld->curve, -MAX_CURVES, +MAX_CURVES);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if defined(PPM_CENTER_ADJUSTABLE)
|
||||
case ITEM_LIMITS_PPM_CENTER:
|
||||
lcdDrawNumber(LIMITS_PPM_CENTER_POS, y, PPM_CENTER+ld->ppmCenter, attr|RIGHT);
|
||||
if (active) {
|
||||
CHECK_INCDEC_MODELVAR(event, ld->ppmCenter, -PPM_CENTER_MAX, +PPM_CENTER_MAX);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if defined(PPM_LIMITS_SYMETRICAL)
|
||||
case ITEM_LIMITS_SYMETRICAL:
|
||||
lcdDrawText(LIMITS_SYMETRICAL_POS, y, ld->symetrical ? "=" : "\306", attr);
|
||||
if (active) {
|
||||
CHECK_INCDEC_MODELVAR_ZERO(event, ld->symetrical, 1);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,356 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "opentx.h"
|
||||
#include "storage/modelslist.h"
|
||||
|
||||
#define CATEGORIES_WIDTH 127
|
||||
#define MODELS_LEFT 147
|
||||
#define MODELS_COLUMN_WIDTH 162
|
||||
|
||||
enum ModelSelectMode {
|
||||
MODE_SELECT_MODEL,
|
||||
MODE_RENAME_CATEGORY,
|
||||
MODE_MOVE_MODEL,
|
||||
};
|
||||
|
||||
uint8_t selectMode;
|
||||
ModelsList modelslist;
|
||||
|
||||
ModelsCategory * currentCategory;
|
||||
int currentCategoryIndex;
|
||||
ModelCell * currentModel;
|
||||
|
||||
void drawCategory(coord_t y, const char * name, bool selected)
|
||||
{
|
||||
if (selected) {
|
||||
lcdDrawSolidFilledRect(5, y-INVERT_VERT_MARGIN, CATEGORIES_WIDTH-10, INVERT_LINE_HEIGHT+2, TEXT_INVERTED_BGCOLOR);
|
||||
lcdDrawText(10, y, name, TEXT_COLOR | INVERS);
|
||||
}
|
||||
else {
|
||||
lcdDrawText(10, y, name, TEXT_COLOR);
|
||||
}
|
||||
}
|
||||
|
||||
void drawModel(coord_t x, coord_t y, ModelCell * model, bool current, bool selected)
|
||||
{
|
||||
lcd->drawBitmap(x+1, y+1, &model->buffer);
|
||||
if (current) {
|
||||
lcd->drawBitmapPattern(x+66, y+43, LBM_ACTIVE_MODEL, TITLE_BGCOLOR);
|
||||
}
|
||||
if (selected) {
|
||||
lcdDrawSolidRect(x, y, MODELCELL_WIDTH+2, MODELCELL_HEIGHT+2, 1, TITLE_BGCOLOR);
|
||||
drawShadow(x, y, MODELCELL_WIDTH+2, MODELCELL_HEIGHT+2);
|
||||
if (selectMode == MODE_MOVE_MODEL) {
|
||||
lcd->drawMask(x+MODELCELL_WIDTH+2-modelselModelMoveBackground->getWidth(), y, modelselModelMoveBackground, TITLE_BGCOLOR);
|
||||
lcd->drawMask(x+MODELCELL_WIDTH+2-modelselModelMoveBackground->getWidth()+12, y+5, modelselModelMoveIcon, TEXT_BGCOLOR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t categoriesVerticalOffset = 0;
|
||||
uint16_t categoriesVerticalPosition = 0;
|
||||
#define MODEL_INDEX() (menuVerticalPosition*2+menuHorizontalPosition)
|
||||
|
||||
void setCurrentModel(unsigned int index)
|
||||
{
|
||||
std::list<ModelCell *>::iterator it = currentCategory->begin();
|
||||
std::advance(it, index);
|
||||
currentModel = *it;
|
||||
menuVerticalPosition = index / 2;
|
||||
menuHorizontalPosition = index & 1;
|
||||
menuVerticalOffset = limit<int>(menuVerticalPosition-2, menuVerticalOffset, min<int>(menuVerticalPosition, max<int>(0, (currentCategory->size()-7)/2)));
|
||||
}
|
||||
|
||||
void setCurrentCategory(unsigned int index)
|
||||
{
|
||||
currentCategoryIndex = index;
|
||||
std::list<ModelsCategory *>::iterator it = modelslist.categories.begin();
|
||||
std::advance(it, index);
|
||||
currentCategory = *it;
|
||||
categoriesVerticalPosition = index;
|
||||
categoriesVerticalOffset = limit<int>(categoriesVerticalPosition-4, categoriesVerticalOffset, min<int>(categoriesVerticalPosition, max<int>(0, modelslist.categories.size()-5)));
|
||||
if (currentCategory->size() > 0)
|
||||
setCurrentModel(0);
|
||||
else
|
||||
currentModel = NULL;
|
||||
}
|
||||
|
||||
void onModelSelectMenu(const char * result)
|
||||
{
|
||||
if (result == STR_SELECT_MODEL) {
|
||||
// we store the latest changes if any
|
||||
storageCheck(true);
|
||||
memcpy(g_eeGeneral.currModelFilename, currentModel->name, LEN_MODEL_FILENAME);
|
||||
loadModel(g_eeGeneral.currModelFilename);
|
||||
storageDirty(EE_GENERAL);
|
||||
storageCheck(true);
|
||||
chainMenu(menuMainView);
|
||||
}
|
||||
else if (result == STR_DELETE_MODEL) {
|
||||
POPUP_CONFIRMATION(STR_DELETEMODEL);
|
||||
SET_WARNING_INFO(currentModel->name, LEN_MODEL_FILENAME, 0);
|
||||
}
|
||||
else if (result == STR_CREATE_MODEL) {
|
||||
storageCheck(true);
|
||||
currentModel = modelslist.currentModel = modelslist.addModel(currentCategory, createModel());
|
||||
selectMode = MODE_SELECT_MODEL;
|
||||
setCurrentModel(currentCategory->size() - 1);
|
||||
}
|
||||
else if (result == STR_DUPLICATE_MODEL) {
|
||||
char duplicatedFilename[LEN_MODEL_FILENAME+1];
|
||||
memcpy(duplicatedFilename, currentModel->name, sizeof(duplicatedFilename));
|
||||
if (findNextFileIndex(duplicatedFilename, MODELS_PATH)) {
|
||||
sdCopyFile(currentModel->name, MODELS_PATH, duplicatedFilename, MODELS_PATH);
|
||||
modelslist.addModel(currentCategory, duplicatedFilename);
|
||||
unsigned int index = currentCategory->size() - 1;
|
||||
setCurrentModel(index);
|
||||
}
|
||||
else {
|
||||
POPUP_WARNING("Invalid File");
|
||||
}
|
||||
}
|
||||
else if (result == STR_MOVE_MODEL) {
|
||||
selectMode = MODE_MOVE_MODEL;
|
||||
}
|
||||
else if (result == STR_CREATE_CATEGORY) {
|
||||
currentCategory = modelslist.createCategory();
|
||||
setCurrentCategory(modelslist.categories.size() - 1);
|
||||
}
|
||||
else if (result == STR_RENAME_CATEGORY) {
|
||||
selectMode = MODE_RENAME_CATEGORY;
|
||||
s_editMode = EDIT_MODIFY_STRING;
|
||||
editNameCursorPos = 0;
|
||||
}
|
||||
else if (result == STR_DELETE_CATEGORY) {
|
||||
modelslist.removeCategory(currentCategory);
|
||||
modelslist.save();
|
||||
setCurrentCategory(currentCategoryIndex > 0 ? currentCategoryIndex-1 : currentCategoryIndex);
|
||||
}
|
||||
}
|
||||
|
||||
void initModelsList()
|
||||
{
|
||||
modelslist.load();
|
||||
|
||||
categoriesVerticalOffset = 0;
|
||||
bool found = false;
|
||||
int index = 0;
|
||||
for (std::list<ModelsCategory *>::iterator it = modelslist.categories.begin(); it != modelslist.categories.end(); ++it, ++index) {
|
||||
if (*it == modelslist.currentCategory) {
|
||||
setCurrentCategory(index);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
setCurrentCategory(0);
|
||||
}
|
||||
|
||||
menuVerticalOffset = 0;
|
||||
found = false;
|
||||
index = 0;
|
||||
for (ModelsCategory::iterator it = currentCategory->begin(); it != currentCategory->end(); ++it, ++index) {
|
||||
if (*it == modelslist.currentModel) {
|
||||
setCurrentModel(index);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
setCurrentModel(0);
|
||||
}
|
||||
}
|
||||
|
||||
bool menuModelSelect(evt_t event)
|
||||
{
|
||||
if (warningResult) {
|
||||
warningResult = 0;
|
||||
int modelIndex = MODEL_INDEX();
|
||||
modelslist.removeModel(currentCategory, currentModel);
|
||||
s_copyMode = 0;
|
||||
event = EVT_REFRESH;
|
||||
if (modelIndex > 0) {
|
||||
modelIndex--;
|
||||
}
|
||||
setCurrentModel(modelIndex);
|
||||
}
|
||||
|
||||
switch(event) {
|
||||
case 0:
|
||||
// no need to refresh the screen
|
||||
return false;
|
||||
|
||||
case EVT_ENTRY:
|
||||
selectMode = MODE_SELECT_MODEL;
|
||||
initModelsList();
|
||||
break;
|
||||
|
||||
case EVT_KEY_BREAK(KEY_ENTER):
|
||||
if (selectMode == MODE_MOVE_MODEL)
|
||||
selectMode = MODE_SELECT_MODEL;
|
||||
break;
|
||||
|
||||
case EVT_KEY_FIRST(KEY_EXIT):
|
||||
switch (selectMode) {
|
||||
case MODE_MOVE_MODEL:
|
||||
selectMode = MODE_SELECT_MODEL;
|
||||
break;
|
||||
case MODE_SELECT_MODEL:
|
||||
chainMenu(menuMainView);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case EVT_KEY_FIRST(KEY_PGUP):
|
||||
if (selectMode == MODE_SELECT_MODEL) {
|
||||
if (categoriesVerticalPosition == 0)
|
||||
categoriesVerticalPosition = modelslist.categories.size() - 1;
|
||||
else
|
||||
categoriesVerticalPosition -= 1;
|
||||
setCurrentCategory(categoriesVerticalPosition);
|
||||
}
|
||||
else if (selectMode == MODE_MOVE_MODEL && categoriesVerticalPosition > 0) {
|
||||
ModelsCategory * previous_category = currentCategory;
|
||||
ModelCell * model = currentModel;
|
||||
categoriesVerticalPosition -= 1;
|
||||
setCurrentCategory(categoriesVerticalPosition);
|
||||
modelslist.moveModel(model, previous_category, currentCategory);
|
||||
setCurrentModel(currentCategory->size()-1);
|
||||
}
|
||||
break;
|
||||
|
||||
case EVT_KEY_FIRST(KEY_PGDN):
|
||||
if (selectMode == MODE_SELECT_MODEL) {
|
||||
categoriesVerticalPosition += 1;
|
||||
if (categoriesVerticalPosition >= modelslist.categories.size())
|
||||
categoriesVerticalPosition = 0;
|
||||
setCurrentCategory(categoriesVerticalPosition);
|
||||
}
|
||||
else if (selectMode == MODE_MOVE_MODEL && categoriesVerticalPosition < modelslist.categories.size()-1) {
|
||||
ModelsCategory * previous_category = currentCategory;
|
||||
ModelCell * model = currentModel;
|
||||
categoriesVerticalPosition += 1;
|
||||
setCurrentCategory(categoriesVerticalPosition);
|
||||
modelslist.moveModel(model, previous_category, currentCategory);
|
||||
setCurrentModel(currentCategory->size()-1);
|
||||
}
|
||||
break;
|
||||
|
||||
case EVT_KEY_LONG(KEY_ENTER):
|
||||
if (selectMode == MODE_SELECT_MODEL) {
|
||||
killEvents(event);
|
||||
if (currentModel && currentModel != modelslist.currentModel) {
|
||||
POPUP_MENU_ADD_ITEM(STR_SELECT_MODEL);
|
||||
}
|
||||
POPUP_MENU_ADD_ITEM(STR_CREATE_MODEL);
|
||||
if (currentModel) {
|
||||
POPUP_MENU_ADD_ITEM(STR_DUPLICATE_MODEL);
|
||||
POPUP_MENU_ADD_ITEM(STR_MOVE_MODEL);
|
||||
}
|
||||
POPUP_MENU_ADD_ITEM(STR_CREATE_CATEGORY);
|
||||
POPUP_MENU_ADD_ITEM(STR_RENAME_CATEGORY);
|
||||
if (modelslist.categories.size() > 1) {
|
||||
POPUP_MENU_ADD_ITEM(STR_DELETE_CATEGORY);
|
||||
}
|
||||
// POPUP_MENU_ADD_SD_ITEM(STR_BACKUP_MODEL);
|
||||
if (currentModel && currentModel != modelslist.currentModel) {
|
||||
POPUP_MENU_ADD_ITEM(STR_DELETE_MODEL);
|
||||
}
|
||||
// POPUP_MENU_ADD_ITEM(STR_RESTORE_MODEL);
|
||||
POPUP_MENU_START(onModelSelectMenu);
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
lcdDrawSolidFilledRect(0, 0, LCD_W, LCD_H, TEXT_BGCOLOR);
|
||||
lcd->drawBitmap(0, 0, modelselIconBitmap);
|
||||
|
||||
// Categories
|
||||
int index = 0;
|
||||
coord_t y = 97;
|
||||
drawVerticalScrollbar(CATEGORIES_WIDTH-1, y-1, 5*(FH+7)-5, categoriesVerticalOffset, modelslist.categories.size(), 5);
|
||||
for (std::list<ModelsCategory *>::iterator it = modelslist.categories.begin(); it != modelslist.categories.end(); ++it, ++index) {
|
||||
if (index >= categoriesVerticalOffset && index < categoriesVerticalOffset+5) {
|
||||
if (index != categoriesVerticalOffset) {
|
||||
lcdDrawSolidHorizontalLine(5, y-4, CATEGORIES_WIDTH-10, LINE_COLOR);
|
||||
}
|
||||
if (selectMode == MODE_RENAME_CATEGORY && currentCategory == *it) {
|
||||
lcdDrawSolidFilledRect(0, y-INVERT_VERT_MARGIN+1, CATEGORIES_WIDTH-2, INVERT_LINE_HEIGHT, TEXT_BGCOLOR);
|
||||
editName(MENUS_MARGIN_LEFT, y, currentCategory->name, LEN_MODEL_FILENAME, event, 1, 0);
|
||||
if (s_editMode == 0 || event == EVT_KEY_BREAK(KEY_EXIT)) {
|
||||
modelslist.save();
|
||||
selectMode = MODE_SELECT_MODEL;
|
||||
putEvent(EVT_REFRESH);
|
||||
}
|
||||
}
|
||||
else {
|
||||
drawCategory(y, (*it)->name, currentCategory==*it);
|
||||
}
|
||||
y += FH+7;
|
||||
}
|
||||
}
|
||||
|
||||
// Models
|
||||
index = 0;
|
||||
y = 5;
|
||||
for (ModelsCategory::iterator it = currentCategory->begin(); it != currentCategory->end(); ++it, ++index) {
|
||||
if (index >= menuVerticalOffset*2 && index < (menuVerticalOffset+4)*2) {
|
||||
bool selected = ((selectMode==MODE_SELECT_MODEL || selectMode==MODE_MOVE_MODEL) && index==menuVerticalPosition*2+menuHorizontalPosition);
|
||||
bool current = !strncmp((*it)->name, g_eeGeneral.currModelFilename, LEN_MODEL_FILENAME);
|
||||
if (index & 1) {
|
||||
drawModel(MODELS_LEFT + MODELS_COLUMN_WIDTH, y, *it, current, selected);
|
||||
y += 66;
|
||||
}
|
||||
else {
|
||||
drawModel(MODELS_LEFT, y, *it, current, selected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Navigation
|
||||
if (currentModel) {
|
||||
if (selectMode == MODE_SELECT_MODEL) {
|
||||
if (navigate(event, index, 4, 2) != 0) {
|
||||
setCurrentModel(MODEL_INDEX());
|
||||
putEvent(EVT_REFRESH);
|
||||
}
|
||||
}
|
||||
else if (selectMode == MODE_MOVE_MODEL) {
|
||||
int8_t direction = navigate(event, index, 4, 2, false);
|
||||
if (direction) {
|
||||
modelslist.moveModel(currentCategory, currentModel, direction);
|
||||
setCurrentModel(MODEL_INDEX());
|
||||
putEvent(EVT_REFRESH);
|
||||
}
|
||||
}
|
||||
}
|
||||
drawVerticalScrollbar(DEFAULT_SCROLLBAR_X, 7, LCD_H - 15, menuVerticalOffset, (index + 1) / 2, 4);
|
||||
|
||||
// Footer
|
||||
lcd->drawBitmap(5, LCD_H-FH, modelselSdFreeBitmap);
|
||||
uint32_t size = sdGetSize() / 100;
|
||||
lcdDrawNumber(22, LCD_H-FH-1, size, PREC1, 0, NULL, "GB");
|
||||
lcd->drawBitmap(80, LCD_H-FH, modelselModelQtyBitmap);
|
||||
lcdDrawNumber(105, LCD_H-FH-1, modelslist.modelsCount);
|
||||
|
||||
return true;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,420 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "opentx.h"
|
||||
|
||||
#define MODEL_SPECIAL_FUNC_1ST_COLUMN 60
|
||||
#define MODEL_SPECIAL_FUNC_2ND_COLUMN 120
|
||||
#define MODEL_SPECIAL_FUNC_2ND_COLUMN_EXT (lcdNextPos + 5)
|
||||
#define MODEL_SPECIAL_FUNC_3RD_COLUMN 295
|
||||
#define MODEL_SPECIAL_FUNC_4TH_COLUMN 440
|
||||
#define MODEL_SPECIAL_FUNC_4TH_COLUMN_ONOFF 450
|
||||
|
||||
void onCustomFunctionsFileSelectionMenu(const char * result)
|
||||
{
|
||||
CustomFunctionData * cf;
|
||||
uint8_t eeFlags;
|
||||
|
||||
if (menuHandlers[menuLevel] == menuModelCustomFunctions) {
|
||||
cf = &g_model.customFn[menuVerticalPosition];
|
||||
eeFlags = EE_MODEL;
|
||||
}
|
||||
else {
|
||||
cf = &g_eeGeneral.customFn[menuVerticalPosition];
|
||||
eeFlags = EE_GENERAL;
|
||||
}
|
||||
|
||||
uint8_t func = CFN_FUNC(cf);
|
||||
|
||||
if (result == STR_UPDATE_LIST) {
|
||||
char directory[256];
|
||||
if (func == FUNC_PLAY_SCRIPT) {
|
||||
strcpy(directory, SCRIPTS_FUNCS_PATH);
|
||||
}
|
||||
else {
|
||||
strcpy(directory, SOUNDS_PATH);
|
||||
strncpy(directory+SOUNDS_PATH_LNG_OFS, currentLanguagePack->id, 2);
|
||||
}
|
||||
if (!sdListFiles(directory, func==FUNC_PLAY_SCRIPT ? SCRIPTS_EXT : SOUNDS_EXT, sizeof(cf->play.name), NULL)) {
|
||||
POPUP_WARNING(func==FUNC_PLAY_SCRIPT ? STR_NO_SCRIPTS_ON_SD : STR_NO_SOUNDS_ON_SD);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// The user choosed a file in the list
|
||||
memcpy(cf->play.name, result, sizeof(cf->play.name));
|
||||
storageDirty(eeFlags);
|
||||
if (func == FUNC_PLAY_SCRIPT) {
|
||||
LUA_LOAD_MODEL_SCRIPTS();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void onCustomFunctionsMenu(const char *result)
|
||||
{
|
||||
CustomFunctionData * cfn;
|
||||
uint8_t eeFlags;
|
||||
|
||||
if (menuHandlers[menuLevel] == menuModelCustomFunctions) {
|
||||
cfn = &g_model.customFn[menuVerticalPosition];
|
||||
eeFlags = EE_MODEL;
|
||||
}
|
||||
else {
|
||||
cfn = &g_eeGeneral.customFn[menuVerticalPosition];
|
||||
eeFlags = EE_GENERAL;
|
||||
}
|
||||
|
||||
if (result == STR_COPY) {
|
||||
clipboard.type = CLIPBOARD_TYPE_CUSTOM_FUNCTION;
|
||||
clipboard.data.cfn = *cfn;
|
||||
}
|
||||
else if (result == STR_PASTE) {
|
||||
*cfn = clipboard.data.cfn;
|
||||
storageDirty(eeFlags);
|
||||
}
|
||||
else if (result == STR_CLEAR) {
|
||||
memset(cfn, 0, sizeof(CustomFunctionData));
|
||||
storageDirty(eeFlags);
|
||||
}
|
||||
else if (result == STR_INSERT) {
|
||||
memmove(cfn+1, cfn, (NUM_CFN-menuVerticalPosition-1)*sizeof(CustomFunctionData));
|
||||
memset(cfn, 0, sizeof(CustomFunctionData));
|
||||
storageDirty(eeFlags);
|
||||
}
|
||||
else if (result == STR_DELETE) {
|
||||
memmove(cfn, cfn+1, (NUM_CFN-menuVerticalPosition-1)*sizeof(CustomFunctionData));
|
||||
memset(&g_model.customFn[NUM_CFN-1], 0, sizeof(CustomFunctionData));
|
||||
storageDirty(eeFlags);
|
||||
}
|
||||
}
|
||||
|
||||
void onAdjustGvarSourceLongEnterPress(const char * result)
|
||||
{
|
||||
CustomFunctionData * cfn = &g_model.customFn[menuVerticalPosition];
|
||||
|
||||
if (result == STR_CONSTANT) {
|
||||
CFN_GVAR_MODE(cfn) = FUNC_ADJUST_GVAR_CONSTANT;
|
||||
CFN_PARAM(cfn) = 0;
|
||||
storageDirty(EE_MODEL);
|
||||
}
|
||||
else if (result == STR_MIXSOURCE) {
|
||||
CFN_GVAR_MODE(cfn) = FUNC_ADJUST_GVAR_SOURCE;
|
||||
CFN_PARAM(cfn) = 0;
|
||||
storageDirty(EE_MODEL);
|
||||
}
|
||||
else if (result == STR_GLOBALVAR) {
|
||||
CFN_GVAR_MODE(cfn) = FUNC_ADJUST_GVAR_GVAR;
|
||||
CFN_PARAM(cfn) = 0;
|
||||
storageDirty(EE_MODEL);
|
||||
}
|
||||
else if (result == STR_INCDEC) {
|
||||
CFN_GVAR_MODE(cfn) = FUNC_ADJUST_GVAR_INCDEC;
|
||||
CFN_PARAM(cfn) = 0;
|
||||
storageDirty(EE_MODEL);
|
||||
}
|
||||
else {
|
||||
onSourceLongEnterPress(result);
|
||||
}
|
||||
}
|
||||
|
||||
enum SpecialFunctionsItems {
|
||||
ITEM_SPECIAL_FUNCTIONS_SWITCH,
|
||||
ITEM_SPECIAL_FUNCTIONS_FUNCTION,
|
||||
ITEM_SPECIAL_FUNCTIONS_PARAM,
|
||||
ITEM_SPECIAL_FUNCTIONS_VALUE,
|
||||
ITEM_SPECIAL_FUNCTIONS_ENABLE,
|
||||
ITEM_SPECIAL_FUNCTIONS_COUNT,
|
||||
ITEM_SPECIAL_FUNCTIONS_LAST = ITEM_SPECIAL_FUNCTIONS_COUNT-1
|
||||
};
|
||||
|
||||
bool menuCustomFunctions(evt_t event, CustomFunctionData * functions, CustomFunctionsContext & functionsContext)
|
||||
{
|
||||
uint8_t eeFlags = (functions == g_model.customFn) ? EE_MODEL : EE_GENERAL;
|
||||
|
||||
if (menuHorizontalPosition<0 && event==EVT_KEY_LONG(KEY_ENTER) && !READ_ONLY()) {
|
||||
killEvents(event);
|
||||
CustomFunctionData *cfn = &functions[menuVerticalPosition];
|
||||
if (!CFN_EMPTY(cfn))
|
||||
POPUP_MENU_ADD_ITEM(STR_COPY);
|
||||
if (clipboard.type == CLIPBOARD_TYPE_CUSTOM_FUNCTION)
|
||||
POPUP_MENU_ADD_ITEM(STR_PASTE);
|
||||
if (!CFN_EMPTY(cfn) && CFN_EMPTY(&functions[NUM_CFN-1]))
|
||||
POPUP_MENU_ADD_ITEM(STR_INSERT);
|
||||
if (!CFN_EMPTY(cfn))
|
||||
POPUP_MENU_ADD_ITEM(STR_CLEAR);
|
||||
for (int i=menuVerticalPosition+1; i<NUM_CFN; i++) {
|
||||
if (!CFN_EMPTY(&functions[i])) {
|
||||
POPUP_MENU_ADD_ITEM(STR_DELETE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
POPUP_MENU_START(onCustomFunctionsMenu);
|
||||
}
|
||||
|
||||
for (uint8_t i=0; i<NUM_BODY_LINES; i++) {
|
||||
coord_t y = MENU_CONTENT_TOP + i*FH;
|
||||
int k = i+menuVerticalOffset;
|
||||
|
||||
drawStringWithIndex(MENUS_MARGIN_LEFT, y, functions == g_model.customFn ? STR_SF : STR_GF, k+1, (menuVerticalPosition==k && menuHorizontalPosition<0) ? INVERS : 0);
|
||||
|
||||
CustomFunctionData * cfn = &functions[k];
|
||||
unsigned int func = CFN_FUNC(cfn);
|
||||
for (int j=0; j<ITEM_SPECIAL_FUNCTIONS_COUNT; j++) {
|
||||
LcdFlags attr = ((menuVerticalPosition==k && menuHorizontalPosition==j) ? ((s_editMode>0) ? BLINK|INVERS : INVERS) : 0);
|
||||
LcdFlags active = (attr && s_editMode>0);
|
||||
switch (j) {
|
||||
case ITEM_SPECIAL_FUNCTIONS_SWITCH:
|
||||
if (active || AUTOSWITCH_ENTER_LONG()) {
|
||||
CHECK_INCDEC_SWITCH(event, CFN_SWITCH(cfn), SWSRC_FIRST, SWSRC_LAST, eeFlags, isSwitchAvailableInCustomFunctions);
|
||||
}
|
||||
putsSwitches(MODEL_SPECIAL_FUNC_1ST_COLUMN, y, CFN_SWITCH(cfn), attr | ((functionsContext.activeSwitches & ((MASK_CFN_TYPE)1 << k)) ? BOLD : 0));
|
||||
break;
|
||||
|
||||
case ITEM_SPECIAL_FUNCTIONS_FUNCTION:
|
||||
if (CFN_SWITCH(cfn)) {
|
||||
if (active) {
|
||||
func = CFN_FUNC(cfn) = checkIncDec(event, CFN_FUNC(cfn), 0, FUNC_MAX-1, eeFlags, isAssignableFunctionAvailable);
|
||||
if (checkIncDec_Ret) CFN_RESET(cfn);
|
||||
}
|
||||
lcdDrawTextAtIndex(MODEL_SPECIAL_FUNC_2ND_COLUMN, y, STR_VFSWFUNC, func, attr);
|
||||
}
|
||||
else {
|
||||
j = ITEM_SPECIAL_FUNCTIONS_ENABLE; // skip other fields
|
||||
while (menuVerticalPosition==k && menuHorizontalPosition > 0) {
|
||||
REPEAT_LAST_CURSOR_MOVE(ITEM_SPECIAL_FUNCTIONS_LAST, true);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ITEM_SPECIAL_FUNCTIONS_PARAM:
|
||||
{
|
||||
int8_t maxParam = NUM_CHNOUT-1;
|
||||
#if defined(OVERRIDE_CHANNEL_FUNCTION)
|
||||
if (func == FUNC_OVERRIDE_CHANNEL) {
|
||||
putsChn(MODEL_SPECIAL_FUNC_2ND_COLUMN_EXT, y, CFN_CH_INDEX(cfn)+1, attr);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
if (func == FUNC_TRAINER) {
|
||||
maxParam = 4;
|
||||
putsMixerSource(MODEL_SPECIAL_FUNC_2ND_COLUMN_EXT, y, CFN_CH_INDEX(cfn)==0 ? 0 : MIXSRC_Rud+CFN_CH_INDEX(cfn)-1, attr);
|
||||
}
|
||||
#if defined(GVARS)
|
||||
else if (func == FUNC_ADJUST_GVAR) {
|
||||
maxParam = MAX_GVARS-1;
|
||||
drawStringWithIndex(MODEL_SPECIAL_FUNC_2ND_COLUMN_EXT, y, STR_GV, CFN_GVAR_INDEX(cfn)+1, attr);
|
||||
if (active) CFN_GVAR_INDEX(cfn) = checkIncDec(event, CFN_GVAR_INDEX(cfn), 0, maxParam, eeFlags);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
else if (func == FUNC_SET_TIMER) {
|
||||
maxParam = MAX_TIMERS-1;
|
||||
drawStringWithIndex(MODEL_SPECIAL_FUNC_2ND_COLUMN_EXT, y, STR_TIMER, CFN_TIMER_INDEX(cfn)+1, attr);
|
||||
if (active) CFN_TIMER_INDEX(cfn) = checkIncDec(event, CFN_TIMER_INDEX(cfn), 0, maxParam, eeFlags);
|
||||
break;
|
||||
}
|
||||
else if (attr) {
|
||||
REPEAT_LAST_CURSOR_MOVE(ITEM_SPECIAL_FUNCTIONS_LAST, true);
|
||||
}
|
||||
if (active) CHECK_INCDEC_MODELVAR_ZERO(event, CFN_CH_INDEX(cfn), maxParam);
|
||||
break;
|
||||
}
|
||||
|
||||
case ITEM_SPECIAL_FUNCTIONS_VALUE:
|
||||
{
|
||||
INCDEC_DECLARE_VARS(eeFlags);
|
||||
int16_t val_displayed = CFN_PARAM(cfn);
|
||||
int16_t val_min = 0;
|
||||
int16_t val_max = 255;
|
||||
if (func == FUNC_RESET) {
|
||||
val_max = FUNC_RESET_PARAM_LAST;
|
||||
lcdDrawTextAtIndex(MODEL_SPECIAL_FUNC_3RD_COLUMN, y, STR_VFSWRESET, CFN_PARAM(cfn), attr);
|
||||
}
|
||||
#if defined(OVERRIDE_CHANNEL_FUNCTION)
|
||||
else if (func == FUNC_OVERRIDE_CHANNEL) {
|
||||
val_min = -LIMIT_EXT_PERCENT; val_max = +LIMIT_EXT_PERCENT;
|
||||
lcdDrawNumber(MODEL_SPECIAL_FUNC_3RD_COLUMN, y, val_displayed, attr|LEFT);
|
||||
}
|
||||
#endif
|
||||
#if defined(DANGEROUS_MODULE_FUNCTIONS)
|
||||
else if (func >= FUNC_RANGECHECK && func <= FUNC_BIND) {
|
||||
val_max = NUM_MODULES-1;
|
||||
lcdDrawTextAtIndex(MODEL_SPECIAL_FUNC_3RD_COLUMN, y, "\004Int.Ext.", CFN_PARAM(cfn), attr);
|
||||
}
|
||||
#endif
|
||||
else if (func == FUNC_SET_TIMER) {
|
||||
val_max = 59*60+59;
|
||||
putsTimer(MODEL_SPECIAL_FUNC_3RD_COLUMN, y, val_displayed, attr|LEFT);
|
||||
}
|
||||
else if (func == FUNC_PLAY_SOUND) {
|
||||
val_max = AU_SPECIAL_SOUND_LAST-AU_SPECIAL_SOUND_FIRST-1;
|
||||
lcdDrawTextAtIndex(MODEL_SPECIAL_FUNC_3RD_COLUMN, y, STR_FUNCSOUNDS, val_displayed, attr);
|
||||
}
|
||||
#if defined(HAPTIC)
|
||||
else if (func == FUNC_HAPTIC) {
|
||||
val_max = 3;
|
||||
lcdDrawNumber(MODEL_SPECIAL_FUNC_3RD_COLUMN, y, val_displayed, attr|LEFT);
|
||||
}
|
||||
#endif
|
||||
#if defined(SDCARD)
|
||||
else if (func == FUNC_PLAY_TRACK || func == FUNC_BACKGND_MUSIC || func == FUNC_PLAY_SCRIPT) {
|
||||
coord_t x = MODEL_SPECIAL_FUNC_3RD_COLUMN;
|
||||
if (ZEXIST(cfn->play.name))
|
||||
lcdDrawSizedText(x, y, cfn->play.name, sizeof(cfn->play.name), attr);
|
||||
else
|
||||
lcdDrawTextAtIndex(x, y, STR_VCSWFUNC, 0, attr);
|
||||
if (attr) s_editMode = 0;
|
||||
if (attr && event==EVT_KEY_FIRST(KEY_ENTER)) {
|
||||
killEvents(KEY_ENTER);
|
||||
char directory[256];
|
||||
if (func==FUNC_PLAY_SCRIPT) {
|
||||
strcpy(directory, SCRIPTS_FUNCS_PATH);
|
||||
}
|
||||
else {
|
||||
strcpy(directory, SOUNDS_PATH);
|
||||
strncpy(directory+SOUNDS_PATH_LNG_OFS, currentLanguagePack->id, 2);
|
||||
}
|
||||
if (sdListFiles(directory, func==FUNC_PLAY_SCRIPT ? SCRIPTS_EXT : SOUNDS_EXT, sizeof(cfn->play.name), cfn->play.name)) {
|
||||
POPUP_MENU_START(onCustomFunctionsFileSelectionMenu);
|
||||
}
|
||||
else {
|
||||
POPUP_WARNING(func==FUNC_PLAY_SCRIPT ? STR_NO_SCRIPTS_ON_SD : STR_NO_SOUNDS_ON_SD);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
else if (func == FUNC_PLAY_VALUE) {
|
||||
val_max = MIXSRC_LAST_TELEM;
|
||||
putsMixerSource(MODEL_SPECIAL_FUNC_3RD_COLUMN, y, val_displayed, attr);
|
||||
if (active) {
|
||||
INCDEC_SET_FLAG(eeFlags | INCDEC_SOURCE);
|
||||
INCDEC_ENABLE_CHECK(functions == g_eeGeneral.customFn ? isSourceAvailableInGlobalFunctions : isSourceAvailable);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
else if (func == FUNC_VOLUME) {
|
||||
val_max = MIXSRC_LAST_CH;
|
||||
putsMixerSource(MODEL_SPECIAL_FUNC_3RD_COLUMN, y, val_displayed, attr);
|
||||
if (active) {
|
||||
INCDEC_SET_FLAG(eeFlags | INCDEC_SOURCE);
|
||||
INCDEC_ENABLE_CHECK(isSourceAvailable);
|
||||
}
|
||||
}
|
||||
else if (func == FUNC_LOGS) {
|
||||
if (val_displayed) {
|
||||
lcdDrawNumber(MODEL_SPECIAL_FUNC_3RD_COLUMN, y, val_displayed, attr|PREC1|LEFT, 0, NULL, "s");
|
||||
}
|
||||
else {
|
||||
lcdDrawTextAtIndex(MODEL_SPECIAL_FUNC_3RD_COLUMN, y, STR_MMMINV, 0, attr);
|
||||
}
|
||||
}
|
||||
#if defined(REVPLUS)
|
||||
else if (func == FUNC_BACKLIGHT) {
|
||||
drawSlider(MODEL_SPECIAL_FUNC_3RD_COLUMN, y, CFN_PARAM(cfn), 100, attr);
|
||||
INCDEC_SET_FLAG(eeFlags | NO_INCDEC_MARKS);
|
||||
val_min = 0;
|
||||
val_max = 100;
|
||||
}
|
||||
#endif
|
||||
#if defined(GVARS)
|
||||
else if (func == FUNC_ADJUST_GVAR) {
|
||||
switch (CFN_GVAR_MODE(cfn)) {
|
||||
case FUNC_ADJUST_GVAR_CONSTANT:
|
||||
val_displayed = (int16_t)CFN_PARAM(cfn);
|
||||
val_min = -CFN_GVAR_CST_MAX; val_max = +CFN_GVAR_CST_MAX;
|
||||
lcdDrawNumber(MODEL_SPECIAL_FUNC_3RD_COLUMN, y, val_displayed, attr|LEFT);
|
||||
break;
|
||||
case FUNC_ADJUST_GVAR_SOURCE:
|
||||
val_max = MIXSRC_LAST_CH;
|
||||
putsMixerSource(MODEL_SPECIAL_FUNC_3RD_COLUMN, y, val_displayed, attr);
|
||||
if (active) {
|
||||
INCDEC_SET_FLAG(eeFlags | INCDEC_SOURCE);
|
||||
INCDEC_ENABLE_CHECK(isSourceAvailable);
|
||||
}
|
||||
break;
|
||||
case FUNC_ADJUST_GVAR_GVAR:
|
||||
val_max = MAX_GVARS-1;
|
||||
drawStringWithIndex(MODEL_SPECIAL_FUNC_3RD_COLUMN, y, STR_GV, val_displayed+1, attr);
|
||||
break;
|
||||
default: // FUNC_ADJUST_GVAR_INC
|
||||
val_min = -100; val_max = +100;
|
||||
if (val_displayed < 0)
|
||||
lcdDrawText(MODEL_SPECIAL_FUNC_3RD_COLUMN, y, "-= ", attr);
|
||||
else
|
||||
lcdDrawText(MODEL_SPECIAL_FUNC_3RD_COLUMN, y, "+= ", attr);
|
||||
lcdDrawNumber(lcdNextPos, y, abs(val_displayed), attr|LEFT);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
else if (attr) {
|
||||
REPEAT_LAST_CURSOR_MOVE(ITEM_SPECIAL_FUNCTIONS_LAST, true);
|
||||
}
|
||||
|
||||
if (active || event==EVT_KEY_LONG(KEY_ENTER)) {
|
||||
CFN_PARAM(cfn) = CHECK_INCDEC_PARAM(event, val_displayed, val_min, val_max);
|
||||
if (func == FUNC_ADJUST_GVAR && attr && event==EVT_KEY_LONG(KEY_ENTER)) {
|
||||
killEvents(event);
|
||||
if (CFN_GVAR_MODE(cfn) != FUNC_ADJUST_GVAR_CONSTANT)
|
||||
POPUP_MENU_ADD_ITEM(STR_CONSTANT);
|
||||
if (CFN_GVAR_MODE(cfn) != FUNC_ADJUST_GVAR_SOURCE)
|
||||
POPUP_MENU_ADD_ITEM(STR_MIXSOURCE);
|
||||
if (CFN_GVAR_MODE(cfn) != FUNC_ADJUST_GVAR_GVAR)
|
||||
POPUP_MENU_ADD_ITEM(STR_GLOBALVAR);
|
||||
if (CFN_GVAR_MODE(cfn) != FUNC_ADJUST_GVAR_INCDEC)
|
||||
POPUP_MENU_ADD_ITEM(STR_INCDEC);
|
||||
POPUP_MENU_START(onAdjustGvarSourceLongEnterPress);
|
||||
s_editMode = EDIT_MODIFY_FIELD;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ITEM_SPECIAL_FUNCTIONS_ENABLE:
|
||||
if (HAS_ENABLE_PARAM(func)) {
|
||||
if (active) CFN_ACTIVE(cfn) = checkIncDec(event, CFN_ACTIVE(cfn), 0, 1, eeFlags);
|
||||
drawCheckBox(MODEL_SPECIAL_FUNC_4TH_COLUMN_ONOFF, y, CFN_ACTIVE(cfn), attr);
|
||||
}
|
||||
else if (HAS_REPEAT_PARAM(func)) {
|
||||
if (active) CFN_PLAY_REPEAT(cfn) = checkIncDec(event, CFN_PLAY_REPEAT(cfn)==CFN_PLAY_REPEAT_NOSTART?-1:CFN_PLAY_REPEAT(cfn), -1, 60/CFN_PLAY_REPEAT_MUL, eeFlags);
|
||||
if (CFN_PLAY_REPEAT(cfn) == 0) {
|
||||
lcdDrawText(MODEL_SPECIAL_FUNC_4TH_COLUMN+2, y, "1x", attr);
|
||||
}
|
||||
else if (CFN_PLAY_REPEAT(cfn) == CFN_PLAY_REPEAT_NOSTART) {
|
||||
lcdDrawText(MODEL_SPECIAL_FUNC_4TH_COLUMN-1, y, "!1x", attr);
|
||||
}
|
||||
else {
|
||||
lcdDrawNumber(MODEL_SPECIAL_FUNC_4TH_COLUMN+12, y, CFN_PLAY_REPEAT(cfn)*CFN_PLAY_REPEAT_MUL, attr|RIGHT, 0, NULL, "s");
|
||||
}
|
||||
}
|
||||
else if (attr) {
|
||||
REPEAT_LAST_CURSOR_MOVE(ITEM_SPECIAL_FUNCTIONS_LAST, true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool menuModelCustomFunctions(evt_t event)
|
||||
{
|
||||
MENU(STR_MENUCUSTOMFUNC, MODEL_ICONS, menuTabModel, e_CustomFunctions, NUM_CFN, { NAVIGATION_LINE_BY_LINE|4/*repeated*/ });
|
||||
return menuCustomFunctions(event, g_model.customFn, modelFunctionsContext);
|
||||
}
|
||||
@@ -0,0 +1,587 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "opentx.h"
|
||||
|
||||
enum menuModelTelemetryItems {
|
||||
ITEM_TELEMETRY_PROTOCOL_TYPE,
|
||||
ITEM_TELEMETRY_RSSI_LABEL,
|
||||
ITEM_TELEMETRY_RSSI_ALARM1,
|
||||
ITEM_TELEMETRY_RSSI_ALARM2,
|
||||
ITEM_TELEMETRY_SENSORS_LABEL,
|
||||
ITEM_TELEMETRY_SENSOR1,
|
||||
ITEM_TELEMETRY_SENSOR2,
|
||||
ITEM_TELEMETRY_SENSOR3,
|
||||
ITEM_TELEMETRY_SENSOR4,
|
||||
ITEM_TELEMETRY_SENSOR5,
|
||||
ITEM_TELEMETRY_SENSOR6,
|
||||
ITEM_TELEMETRY_SENSOR7,
|
||||
ITEM_TELEMETRY_SENSOR8,
|
||||
ITEM_TELEMETRY_SENSOR9,
|
||||
ITEM_TELEMETRY_SENSOR10,
|
||||
ITEM_TELEMETRY_SENSOR11,
|
||||
ITEM_TELEMETRY_SENSOR12,
|
||||
ITEM_TELEMETRY_SENSOR13,
|
||||
ITEM_TELEMETRY_SENSOR14,
|
||||
ITEM_TELEMETRY_SENSOR15,
|
||||
ITEM_TELEMETRY_SENSOR16,
|
||||
ITEM_TELEMETRY_SENSOR17,
|
||||
ITEM_TELEMETRY_SENSOR18,
|
||||
ITEM_TELEMETRY_SENSOR19,
|
||||
ITEM_TELEMETRY_SENSOR20,
|
||||
ITEM_TELEMETRY_SENSOR21,
|
||||
ITEM_TELEMETRY_SENSOR22,
|
||||
ITEM_TELEMETRY_SENSOR23,
|
||||
ITEM_TELEMETRY_SENSOR24,
|
||||
ITEM_TELEMETRY_SENSOR25,
|
||||
ITEM_TELEMETRY_SENSOR26,
|
||||
ITEM_TELEMETRY_SENSOR27,
|
||||
ITEM_TELEMETRY_SENSOR28,
|
||||
ITEM_TELEMETRY_SENSOR29,
|
||||
ITEM_TELEMETRY_SENSOR30,
|
||||
ITEM_TELEMETRY_SENSOR31,
|
||||
ITEM_TELEMETRY_SENSOR32,
|
||||
ITEM_TELEMETRY_DISCOVER_SENSORS,
|
||||
ITEM_TELEMETRY_NEW_SENSOR,
|
||||
ITEM_TELEMETRY_DELETE_ALL_SENSORS,
|
||||
ITEM_TELEMETRY_IGNORE_SENSOR_INSTANCE,
|
||||
#if defined(VARIO)
|
||||
ITEM_TELEMETRY_VARIO_LABEL,
|
||||
ITEM_TELEMETRY_VARIO_SOURCE,
|
||||
ITEM_TELEMETRY_VARIO_RANGE,
|
||||
ITEM_TELEMETRY_VARIO_CENTER,
|
||||
#endif
|
||||
ITEM_TELEMETRY_MAX
|
||||
};
|
||||
|
||||
#define TELEM_COL1 10
|
||||
#define TELEM_COL2 200
|
||||
#define TELEM_COL3 275
|
||||
#define TELEM_COL4 350
|
||||
|
||||
#define IF_FAS_OFFSET(x) x,
|
||||
#define IS_RANGE_DEFINED(k) (g_model.frsky.channels[k].ratio > 0)
|
||||
|
||||
#define SENSOR_ROWS(x) (isTelemetryFieldAvailable(x) ? (uint8_t)0 : HIDDEN_ROW)
|
||||
#define SENSORS_ROWS LABEL(Sensors), SENSOR_ROWS(0), SENSOR_ROWS(1), SENSOR_ROWS(2), SENSOR_ROWS(3), SENSOR_ROWS(4), SENSOR_ROWS(5), SENSOR_ROWS(6), SENSOR_ROWS(7), SENSOR_ROWS(8), SENSOR_ROWS(9), SENSOR_ROWS(10), SENSOR_ROWS(11), SENSOR_ROWS(12), SENSOR_ROWS(13), SENSOR_ROWS(14), SENSOR_ROWS(15), SENSOR_ROWS(16), SENSOR_ROWS(17), SENSOR_ROWS(18), SENSOR_ROWS(19), SENSOR_ROWS(20), SENSOR_ROWS(21), SENSOR_ROWS(22), SENSOR_ROWS(23), SENSOR_ROWS(24), SENSOR_ROWS(25), SENSOR_ROWS(26), SENSOR_ROWS(27), SENSOR_ROWS(28), SENSOR_ROWS(29), SENSOR_ROWS(30), SENSOR_ROWS(31), 0, 0, 0, 0,
|
||||
#if defined(VARIO)
|
||||
#define VARIO_ROWS LABEL(Vario), 0, 1, 2,
|
||||
#else
|
||||
#define VARIO_ROWS
|
||||
#endif
|
||||
#define RSSI_ROWS LABEL(RSSI), 0, 0,
|
||||
#define VARIO_RANGE_ROWS 3
|
||||
#define TELEMETRY_TYPE_ROWS (g_model.moduleData[INTERNAL_MODULE].rfProtocol == RF_PROTO_OFF && g_model.moduleData[EXTERNAL_MODULE].type == MODULE_TYPE_PPM) ? (uint8_t)0 : HIDDEN_ROW,
|
||||
|
||||
enum SensorFields {
|
||||
SENSOR_FIELD_NAME,
|
||||
SENSOR_FIELD_TYPE,
|
||||
SENSOR_FIELD_ID,
|
||||
SENSOR_FIELD_FORMULA=SENSOR_FIELD_ID,
|
||||
SENSOR_FIELD_UNIT,
|
||||
SENSOR_FIELD_PRECISION,
|
||||
SENSOR_FIELD_PARAM1,
|
||||
SENSOR_FIELD_PARAM2,
|
||||
SENSOR_FIELD_PARAM3,
|
||||
SENSOR_FIELD_PARAM4,
|
||||
SENSOR_FIELD_AUTOOFFSET,
|
||||
SENSOR_FIELD_ONLYPOSITIVE,
|
||||
SENSOR_FIELD_FILTER,
|
||||
SENSOR_FIELD_PERSISTENT,
|
||||
SENSOR_FIELD_LOGS,
|
||||
SENSOR_FIELD_MAX
|
||||
};
|
||||
|
||||
#define SENSOR_2ND_COLUMN 140
|
||||
#define SENSOR_3RD_COLUMN 280
|
||||
|
||||
#define SENSOR_UNIT_ROWS ((sensor->type == TELEM_TYPE_CALCULATED && (sensor->formula == TELEM_FORMULA_DIST)) || sensor->isConfigurable() ? (uint8_t)0 : HIDDEN_ROW)
|
||||
#define SENSOR_PREC_ROWS (sensor->isPrecConfigurable() && sensor->unit != UNIT_FAHRENHEIT ? (uint8_t)0 : HIDDEN_ROW)
|
||||
#define SENSOR_PARAM1_ROWS (sensor->unit >= UNIT_FIRST_VIRTUAL ? HIDDEN_ROW : (uint8_t)0)
|
||||
#define SENSOR_PARAM2_ROWS (sensor->unit == UNIT_GPS || sensor->unit == UNIT_DATETIME || sensor->unit == UNIT_CELLS || (sensor->type==TELEM_TYPE_CALCULATED && (sensor->formula==TELEM_FORMULA_CONSUMPTION || sensor->formula==TELEM_FORMULA_TOTALIZE)) ? HIDDEN_ROW : (uint8_t)0)
|
||||
#define SENSOR_PARAM3_ROWS (sensor->type == TELEM_TYPE_CALCULATED && sensor->formula < TELEM_FORMULA_MULTIPLY) ? (uint8_t)0 : HIDDEN_ROW
|
||||
#define SENSOR_PARAM4_ROWS (sensor->type == TELEM_TYPE_CALCULATED && sensor->formula < TELEM_FORMULA_MULTIPLY) ? (uint8_t)0 : HIDDEN_ROW
|
||||
#define SENSOR_AUTOOFFSET_ROWS (sensor->unit != UNIT_RPMS && sensor->isConfigurable() ? (uint8_t)0 : HIDDEN_ROW)
|
||||
#define SENSOR_ONLYPOS_ROWS (sensor->isConfigurable() ? (uint8_t)0 : HIDDEN_ROW)
|
||||
#define SENSOR_FILTER_ROWS (sensor->isConfigurable() ? (uint8_t)0 : HIDDEN_ROW)
|
||||
#define SENSOR_PERSISTENT_ROWS (sensor->type == TELEM_TYPE_CALCULATED ? (uint8_t)0 : HIDDEN_ROW)
|
||||
|
||||
bool menuModelSensor(evt_t event)
|
||||
{
|
||||
TelemetrySensor * sensor = &g_model.telemetrySensors[s_currIdx];
|
||||
|
||||
SUBMENU("SENSOR", ICON_MODEL_TELEMETRY, SENSOR_FIELD_MAX, { 0, 0, sensor->type == TELEM_TYPE_CALCULATED ? (uint8_t)0 : (uint8_t)1, SENSOR_UNIT_ROWS, SENSOR_PREC_ROWS, SENSOR_PARAM1_ROWS, SENSOR_PARAM2_ROWS, SENSOR_PARAM3_ROWS, SENSOR_PARAM4_ROWS, 0, 0, 0, 0, 0 });
|
||||
lcdDrawNumber(lcdNextPos, 3, s_currIdx+1, MENU_TITLE_COLOR|LEFT);
|
||||
putsTelemetryChannelValue(50, 3+FH, s_currIdx, getValue(MIXSRC_FIRST_TELEM+3*s_currIdx), MENU_TITLE_COLOR|LEFT);
|
||||
|
||||
for (unsigned int i=0; i<NUM_BODY_LINES+1; i++) {
|
||||
coord_t y = MENU_CONTENT_TOP - FH - 2 + i*FH;
|
||||
int k = i + menuVerticalOffset;
|
||||
|
||||
for (int j=0; j<k; j++) {
|
||||
if (mstate_tab[j+1] == HIDDEN_ROW) {
|
||||
if (++k >= (int)DIM(mstate_tab)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LcdFlags attr = (menuVerticalPosition==k ? (s_editMode>0 ? BLINK|INVERS : INVERS) : 0);
|
||||
|
||||
switch (k) {
|
||||
case SENSOR_FIELD_NAME:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_NAME);
|
||||
editName(SENSOR_2ND_COLUMN, y, sensor->label, TELEM_LABEL_LEN, event, attr);
|
||||
break;
|
||||
|
||||
case SENSOR_FIELD_TYPE:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, NO_INDENT(STR_TYPE));
|
||||
sensor->type = selectMenuItem(SENSOR_2ND_COLUMN, y, STR_VSENSORTYPES, sensor->type, 0, 1, attr, event);
|
||||
if (attr && checkIncDec_Ret) {
|
||||
sensor->instance = 0;
|
||||
if (sensor->type == TELEM_TYPE_CALCULATED) {
|
||||
sensor->param = 0;
|
||||
sensor->filter = 0;
|
||||
sensor->autoOffset = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SENSOR_FIELD_ID:
|
||||
if (sensor->type == TELEM_TYPE_CUSTOM) {
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_ID);
|
||||
lcdDrawHexNumber(SENSOR_2ND_COLUMN, y, sensor->id, LEFT|(menuHorizontalPosition==0 ? attr : 0));
|
||||
lcdDrawNumber(SENSOR_3RD_COLUMN, y, sensor->instance, LEFT|(menuHorizontalPosition==1 ? attr : 0));
|
||||
if (attr) {
|
||||
switch (menuHorizontalPosition) {
|
||||
case 0:
|
||||
sensor->id = checkIncDec(event, sensor->id, 0x0000, 0xffff, INCDEC_REP10|NO_INCDEC_MARKS);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
CHECK_INCDEC_MODELVAR_ZERO(event, sensor->instance, 0xff);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_FORMULA);
|
||||
sensor->formula = selectMenuItem(SENSOR_2ND_COLUMN, y, STR_VFORMULAS, sensor->formula, 0, TELEM_FORMULA_LAST, attr, event);
|
||||
if (attr && checkIncDec_Ret) {
|
||||
sensor->param = 0;
|
||||
if (sensor->formula == TELEM_FORMULA_CELL) {
|
||||
sensor->unit = UNIT_VOLTS;
|
||||
sensor->prec = 2;
|
||||
}
|
||||
else if (sensor->formula == TELEM_FORMULA_DIST) {
|
||||
sensor->unit = UNIT_DIST;
|
||||
sensor->prec = 0;
|
||||
}
|
||||
else if (sensor->formula == TELEM_FORMULA_CONSUMPTION) {
|
||||
sensor->unit = UNIT_MAH;
|
||||
sensor->prec = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SENSOR_FIELD_UNIT:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_UNIT);
|
||||
// TODO flash saving with selectMenuItem where I copied those 2 lines?
|
||||
lcdDrawTextAtIndex(SENSOR_2ND_COLUMN, y, STR_VTELEMUNIT, sensor->unit, attr);
|
||||
if (attr) {
|
||||
CHECK_INCDEC_MODELVAR_ZERO(event, sensor->unit, UNIT_MAX);
|
||||
if (checkIncDec_Ret) {
|
||||
if (sensor->unit == UNIT_FAHRENHEIT) {
|
||||
sensor->prec = 0;
|
||||
}
|
||||
telemetryItems[s_currIdx].clear();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SENSOR_FIELD_PRECISION:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_PRECISION);
|
||||
sensor->prec = selectMenuItem(SENSOR_2ND_COLUMN, y, STR_VPREC, sensor->prec, 0, 2, attr, event);
|
||||
if (attr && checkIncDec_Ret) {
|
||||
telemetryItems[s_currIdx].clear();
|
||||
}
|
||||
break;
|
||||
|
||||
case SENSOR_FIELD_PARAM1:
|
||||
if (sensor->type == TELEM_TYPE_CALCULATED) {
|
||||
if (sensor->formula == TELEM_FORMULA_CELL) {
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_CELLSENSOR);
|
||||
putsMixerSource(SENSOR_2ND_COLUMN, y, sensor->cell.source ? MIXSRC_FIRST_TELEM+3*(sensor->cell.source-1) : 0, attr);
|
||||
if (attr) {
|
||||
sensor->cell.source = checkIncDec(event, sensor->cell.source, 0, MAX_SENSORS, EE_MODEL|NO_INCDEC_MARKS, isCellsSensor);
|
||||
}
|
||||
break;
|
||||
}
|
||||
else if (sensor->formula == TELEM_FORMULA_DIST) {
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_GPSSENSOR);
|
||||
putsMixerSource(SENSOR_2ND_COLUMN, y, sensor->dist.gps ? MIXSRC_FIRST_TELEM+3*(sensor->dist.gps-1) : 0, attr);
|
||||
if (attr) {
|
||||
sensor->dist.gps = checkIncDec(event, sensor->dist.gps, 0, MAX_SENSORS, EE_MODEL|NO_INCDEC_MARKS, isGPSSensor);
|
||||
}
|
||||
break;
|
||||
}
|
||||
else if (sensor->formula == TELEM_FORMULA_CONSUMPTION) {
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_CURRENTSENSOR);
|
||||
putsMixerSource(SENSOR_2ND_COLUMN, y, sensor->consumption.source ? MIXSRC_FIRST_TELEM+3*(sensor->consumption.source-1) : 0, attr);
|
||||
if (attr) {
|
||||
sensor->consumption.source = checkIncDec(event, sensor->consumption.source, 0, MAX_SENSORS, EE_MODEL|NO_INCDEC_MARKS, isTelemetryFieldAvailable);
|
||||
}
|
||||
break;
|
||||
}
|
||||
else if (sensor->formula == TELEM_FORMULA_TOTALIZE) {
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, NO_INDENT(STR_SOURCE));
|
||||
putsMixerSource(SENSOR_2ND_COLUMN, y, sensor->consumption.source ? MIXSRC_FIRST_TELEM+3*(sensor->consumption.source-1) : 0, attr);
|
||||
if (attr) {
|
||||
sensor->consumption.source = checkIncDec(event, sensor->consumption.source, 0, MAX_SENSORS, EE_MODEL|NO_INCDEC_MARKS, isTelemetryFieldComparisonAvailable);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (sensor->unit == UNIT_RPMS) {
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, NO_INDENT(STR_BLADES));
|
||||
if (attr) sensor->custom.ratio = checkIncDec(event, sensor->custom.ratio, 1, 30000, EE_MODEL|NO_INCDEC_MARKS|INCDEC_REP10);
|
||||
lcdDrawNumber(SENSOR_2ND_COLUMN, y, sensor->custom.ratio, LEFT|attr);
|
||||
break;
|
||||
}
|
||||
else {
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_RATIO);
|
||||
if (attr) sensor->custom.ratio = checkIncDec(event, sensor->custom.ratio, 0, 30000, EE_MODEL|NO_INCDEC_MARKS|INCDEC_REP10);
|
||||
if (sensor->custom.ratio == 0)
|
||||
lcdDrawText(SENSOR_2ND_COLUMN, y, "-", attr);
|
||||
else
|
||||
lcdDrawNumber(SENSOR_2ND_COLUMN, y, sensor->custom.ratio, LEFT|attr|PREC1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// no break
|
||||
|
||||
case SENSOR_FIELD_PARAM2:
|
||||
if (sensor->type == TELEM_TYPE_CALCULATED) {
|
||||
if (sensor->formula == TELEM_FORMULA_CELL) {
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_CELLINDEX);
|
||||
sensor->cell.index = selectMenuItem(SENSOR_2ND_COLUMN, y, STR_VCELLINDEX, sensor->cell.index, 0, 8, attr, event);
|
||||
break;
|
||||
}
|
||||
else if (sensor->formula == TELEM_FORMULA_DIST) {
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_ALTSENSOR);
|
||||
putsMixerSource(SENSOR_2ND_COLUMN, y, sensor->dist.alt ? MIXSRC_FIRST_TELEM+3*(sensor->dist.alt-1) : 0, attr);
|
||||
if (attr) {
|
||||
sensor->dist.alt = checkIncDec(event, sensor->dist.alt, 0, MAX_SENSORS, EE_MODEL|NO_INCDEC_MARKS, isAltSensor);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (sensor->unit == UNIT_RPMS) {
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_MULTIPLIER);
|
||||
if (attr) sensor->custom.offset = checkIncDec(event, sensor->custom.offset, 1, 30000, EE_MODEL|NO_INCDEC_MARKS|INCDEC_REP10);
|
||||
lcdDrawNumber(SENSOR_2ND_COLUMN, y, sensor->custom.offset, LEFT|attr);
|
||||
break;
|
||||
}
|
||||
else {
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, NO_INDENT(STR_OFFSET));
|
||||
if (attr) sensor->custom.offset = checkIncDec(event, sensor->custom.offset, -30000, +30000, EE_MODEL|NO_INCDEC_MARKS|INCDEC_REP10);
|
||||
if (sensor->prec > 0) attr |= (sensor->prec == 2 ? PREC2 : PREC1);
|
||||
lcdDrawNumber(SENSOR_2ND_COLUMN, y, sensor->custom.offset, LEFT|attr);
|
||||
break;
|
||||
}
|
||||
// no break
|
||||
|
||||
case SENSOR_FIELD_PARAM3:
|
||||
// no break
|
||||
|
||||
case SENSOR_FIELD_PARAM4:
|
||||
{
|
||||
drawStringWithIndex(MENUS_MARGIN_LEFT, y, NO_INDENT(STR_SOURCE), k-SENSOR_FIELD_PARAM1+1);
|
||||
int8_t & source = sensor->calc.sources[k-SENSOR_FIELD_PARAM1];
|
||||
if (attr) {
|
||||
source = checkIncDec(event, source, -MAX_SENSORS, MAX_SENSORS, EE_MODEL|NO_INCDEC_MARKS, isSensorAvailable);
|
||||
}
|
||||
if (source < 0) {
|
||||
lcdDrawText(SENSOR_2ND_COLUMN, y, "-", attr);
|
||||
putsMixerSource(SENSOR_2ND_COLUMN+5, y, MIXSRC_FIRST_TELEM+3*(-1-source), attr);
|
||||
}
|
||||
else {
|
||||
putsMixerSource(SENSOR_2ND_COLUMN, y, source ? MIXSRC_FIRST_TELEM+3*(source-1) : 0, attr);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SENSOR_FIELD_AUTOOFFSET:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_AUTOOFFSET);
|
||||
sensor->autoOffset = editCheckBox(sensor->autoOffset, SENSOR_2ND_COLUMN, y, attr, event);
|
||||
break;
|
||||
|
||||
case SENSOR_FIELD_ONLYPOSITIVE:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_ONLYPOSITIVE);
|
||||
sensor->onlyPositive = editCheckBox(sensor->onlyPositive, SENSOR_2ND_COLUMN, y, attr, event);
|
||||
break;
|
||||
|
||||
case SENSOR_FIELD_FILTER:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_FILTER);
|
||||
sensor->filter = editCheckBox(sensor->filter, SENSOR_2ND_COLUMN, y, attr, event);
|
||||
break;
|
||||
|
||||
case SENSOR_FIELD_PERSISTENT:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, NO_INDENT(STR_PERSISTENT));
|
||||
sensor->persistent = editCheckBox(sensor->persistent, SENSOR_2ND_COLUMN, y, attr, event);
|
||||
break;
|
||||
|
||||
case SENSOR_FIELD_LOGS:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_LOGS);
|
||||
sensor->logs = editCheckBox(sensor->logs, SENSOR_2ND_COLUMN, y, attr, event);
|
||||
if (attr && checkIncDec_Ret) {
|
||||
closeLogs();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void onSensorMenu(const char *result)
|
||||
{
|
||||
int index = menuVerticalPosition - ITEM_TELEMETRY_SENSOR1;
|
||||
|
||||
if (index < MAX_SENSORS) {
|
||||
if (result == STR_EDIT) {
|
||||
pushMenu(menuModelSensor);
|
||||
}
|
||||
else if (result == STR_DELETE) {
|
||||
delTelemetryIndex(index);
|
||||
index += 1;
|
||||
if (index<MAX_SENSORS && isTelemetryFieldAvailable(index))
|
||||
menuVerticalPosition += 1;
|
||||
else
|
||||
menuVerticalPosition = ITEM_TELEMETRY_NEW_SENSOR;
|
||||
}
|
||||
else if (result == STR_COPY) {
|
||||
int newIndex = availableTelemetryIndex();
|
||||
|
||||
if (newIndex >= 0) {
|
||||
TelemetrySensor & sourceSensor = g_model.telemetrySensors[index];
|
||||
TelemetrySensor & newSensor = g_model.telemetrySensors[newIndex];
|
||||
newSensor = sourceSensor;
|
||||
TelemetryItem & sourceItem = telemetryItems[index];
|
||||
TelemetryItem & newItem = telemetryItems[newIndex];
|
||||
newItem = sourceItem;
|
||||
storageDirty(EE_MODEL);
|
||||
}
|
||||
else {
|
||||
POPUP_WARNING(STR_TELEMETRYFULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool menuModelTelemetry(evt_t event)
|
||||
{
|
||||
if (warningResult) {
|
||||
warningResult = 0;
|
||||
for (int i=0; i<MAX_SENSORS; i++) {
|
||||
delTelemetryIndex(i);
|
||||
}
|
||||
}
|
||||
|
||||
MENU(STR_MENUTELEMETRY, MODEL_ICONS, menuTabModel, e_Telemetry, ITEM_TELEMETRY_MAX, { TELEMETRY_TYPE_ROWS RSSI_ROWS SENSORS_ROWS VARIO_ROWS });
|
||||
|
||||
for (int i=0; i<NUM_BODY_LINES; i++) {
|
||||
coord_t y = MENU_CONTENT_TOP + i*FH;
|
||||
int k = i + menuVerticalOffset;
|
||||
for (int j=0; j<=k; j++) {
|
||||
if (mstate_tab[j] == HIDDEN_ROW)
|
||||
k++;
|
||||
}
|
||||
|
||||
LcdFlags blink = ((s_editMode>0) ? BLINK|INVERS : INVERS);
|
||||
LcdFlags attr = (menuVerticalPosition == k ? blink : 0);
|
||||
|
||||
if (k>=ITEM_TELEMETRY_SENSOR1 && k<ITEM_TELEMETRY_SENSOR1+MAX_SENSORS) {
|
||||
int index = k-ITEM_TELEMETRY_SENSOR1;
|
||||
lcdDrawNumber(MENUS_MARGIN_LEFT+INDENT_WIDTH, y, index+1, LEFT|attr, 0, NULL, ":");
|
||||
lcdDrawSizedText(60, y, g_model.telemetrySensors[index].label, TELEM_LABEL_LEN, ZCHAR);
|
||||
TelemetryItem & telemetryItem = telemetryItems[index];
|
||||
if (telemetryItem.isFresh()) {
|
||||
lcdDrawText(130, y, "*");
|
||||
}
|
||||
if (telemetryItem.isAvailable()) {
|
||||
LcdFlags color = telemetryItem.isOld() ? ALARM_COLOR : TEXT_COLOR;
|
||||
putsTelemetryChannelValue(TELEM_COL2, y, index, getValue(MIXSRC_FIRST_TELEM+3*index), LEFT|color);
|
||||
}
|
||||
else {
|
||||
lcdDrawText(TELEM_COL2, y, "---"); // TODO shortcut
|
||||
}
|
||||
TelemetrySensor * sensor = & g_model.telemetrySensors[index];
|
||||
#ifdef MULTIMODULE
|
||||
if (IS_SPEKTRUM_PROTOCOL()) {
|
||||
// Spektrum does not (yet?) really support multiple sensor of the same type. But a lot of
|
||||
// different sensor display the same information (e.g. voltage, capacity). Show the id
|
||||
// of the sensor in the overview to ease figuring out what sensors belong together
|
||||
lcdDrawHexNumber(TELEM_COL4, y, sensor->id, LEFT);
|
||||
} else
|
||||
#endif
|
||||
if (sensor->type == TELEM_TYPE_CUSTOM && !g_model.ignoreSensorIds) {
|
||||
lcdDrawNumber(TELEM_COL4, y, sensor->instance, LEFT);
|
||||
}
|
||||
if (attr) {
|
||||
s_editMode = 0;
|
||||
s_currIdx = index;
|
||||
if (event == EVT_KEY_LONG(KEY_ENTER)) {
|
||||
killEvents(event);
|
||||
POPUP_MENU_ADD_ITEM(STR_EDIT);
|
||||
POPUP_MENU_ADD_ITEM(STR_COPY);
|
||||
POPUP_MENU_ADD_ITEM(STR_DELETE);
|
||||
POPUP_MENU_START(onSensorMenu);
|
||||
}
|
||||
else if (event == EVT_KEY_BREAK(KEY_ENTER)) {
|
||||
pushMenu(menuModelSensor);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
switch (k) {
|
||||
case ITEM_TELEMETRY_PROTOCOL_TYPE:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_TELEMETRY_TYPE);
|
||||
g_model.telemetryProtocol = selectMenuItem(TELEM_COL2, y, "\017FrSky S.PORT\0 FrSky D\0 FrSky D (cable)", g_model.telemetryProtocol, PROTOCOL_TELEMETRY_FIRST, PROTOCOL_FRSKY_D, attr, event);
|
||||
break;
|
||||
|
||||
case ITEM_TELEMETRY_SENSORS_LABEL:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_TELEMETRY_SENSORS);
|
||||
lcdDrawText(TELEM_COL2, y, STR_VALUE, 0);
|
||||
if (!g_model.ignoreSensorIds && !IS_SPEKTRUM_PROTOCOL()) {
|
||||
lcdDrawText(TELEM_COL4, y, STR_ID, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case ITEM_TELEMETRY_DISCOVER_SENSORS:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT+INDENT_WIDTH, y, allowNewSensors ? NO_INDENT(STR_STOP_DISCOVER_SENSORS) : NO_INDENT(STR_DISCOVER_SENSORS), attr);
|
||||
if (attr && event==EVT_KEY_BREAK(KEY_ENTER)) {
|
||||
s_editMode = 0;
|
||||
allowNewSensors = !allowNewSensors;
|
||||
}
|
||||
break;
|
||||
|
||||
case ITEM_TELEMETRY_NEW_SENSOR:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT+INDENT_WIDTH, y, NO_INDENT(STR_TELEMETRY_NEWSENSOR), attr);
|
||||
if (attr && event==EVT_KEY_BREAK(KEY_ENTER)) {
|
||||
s_editMode = 0;
|
||||
int res = availableTelemetryIndex();
|
||||
if (res >= 0) {
|
||||
s_currIdx = res;
|
||||
pushMenu(menuModelSensor);
|
||||
}
|
||||
else {
|
||||
POPUP_WARNING(STR_TELEMETRYFULL);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ITEM_TELEMETRY_DELETE_ALL_SENSORS:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT+INDENT_WIDTH, y, NO_INDENT(STR_DELETE_ALL_SENSORS), attr);
|
||||
if (attr)
|
||||
s_editMode = 0;
|
||||
if (attr && event==EVT_KEY_LONG(KEY_ENTER)) {
|
||||
killEvents(KEY_ENTER);
|
||||
POPUP_CONFIRMATION(STR_CONFIRMDELETE);
|
||||
}
|
||||
break;
|
||||
|
||||
case ITEM_TELEMETRY_IGNORE_SENSOR_INSTANCE:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_IGNORE_INSTANCE);
|
||||
g_model.ignoreSensorIds = editCheckBox(g_model.ignoreSensorIds, TELEM_COL2, y, attr, event);
|
||||
break;
|
||||
|
||||
case ITEM_TELEMETRY_RSSI_LABEL:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, PSTR("RSSI"));
|
||||
break;
|
||||
|
||||
case ITEM_TELEMETRY_RSSI_ALARM1:
|
||||
case ITEM_TELEMETRY_RSSI_ALARM2: {
|
||||
uint8_t alarm = k-ITEM_TELEMETRY_RSSI_ALARM1;
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, (alarm==0 ? STR_LOWALARM : STR_CRITICALALARM));
|
||||
lcdDrawNumber(TELEM_COL2, y, getRssiAlarmValue(alarm), LEFT|attr, 3);
|
||||
if (attr && s_editMode>0) {
|
||||
CHECK_INCDEC_MODELVAR(event, g_model.frsky.rssiAlarms[alarm].value, -30, 30);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
#if defined(VARIO)
|
||||
case ITEM_TELEMETRY_VARIO_LABEL:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_VARIO);
|
||||
break;
|
||||
|
||||
case ITEM_TELEMETRY_VARIO_SOURCE:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_SOURCE);
|
||||
putsMixerSource(TELEM_COL2, y, g_model.frsky.varioSource ? MIXSRC_FIRST_TELEM+3*(g_model.frsky.varioSource-1) : 0, attr);
|
||||
if (attr) {
|
||||
g_model.frsky.varioSource = checkIncDec(event, g_model.frsky.varioSource, 0, MAX_SENSORS, EE_MODEL|NO_INCDEC_MARKS, isSensorAvailable);
|
||||
}
|
||||
break;
|
||||
|
||||
case ITEM_TELEMETRY_VARIO_RANGE:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_RANGE);
|
||||
lcdDrawNumber(TELEM_COL2, y, -10+g_model.frsky.varioMin, (menuHorizontalPosition==0 ? attr : 0)|LEFT);
|
||||
lcdDrawNumber(TELEM_COL3, y, 10+g_model.frsky.varioMax, (menuHorizontalPosition==1 ? attr : 0)|LEFT);
|
||||
if (attr && s_editMode>0) {
|
||||
switch (menuHorizontalPosition) {
|
||||
case 0:
|
||||
CHECK_INCDEC_MODELVAR(event, g_model.frsky.varioMin, -7, 7);
|
||||
break;
|
||||
case 1:
|
||||
CHECK_INCDEC_MODELVAR(event, g_model.frsky.varioMax, -7, 7);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ITEM_TELEMETRY_VARIO_CENTER:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_CENTER);
|
||||
lcdDrawNumber(TELEM_COL2, y, -5+g_model.frsky.varioCenterMin, (menuHorizontalPosition==0 ? attr : 0)|PREC1|LEFT);
|
||||
lcdDrawNumber(TELEM_COL3, y, 5+g_model.frsky.varioCenterMax, (menuHorizontalPosition==1 ? attr : 0)|PREC1|LEFT);
|
||||
lcdDrawTextAtIndex(TELEM_COL4, y, STR_VVARIOCENTER, g_model.frsky.varioCenterSilent, (menuHorizontalPosition==2 ? attr : 0));
|
||||
if (attr && s_editMode>0) {
|
||||
switch (menuHorizontalPosition) {
|
||||
case 0:
|
||||
CHECK_INCDEC_MODELVAR(event, g_model.frsky.varioCenterMin, -16, 5+min<int8_t>(10, g_model.frsky.varioCenterMax+5));
|
||||
break;
|
||||
case 1:
|
||||
CHECK_INCDEC_MODELVAR(event, g_model.frsky.varioCenterMax, -5+max<int8_t>(-10, g_model.frsky.varioCenterMin-5), +15);
|
||||
break;
|
||||
case 2:
|
||||
CHECK_INCDEC_MODELVAR_ZERO(event, g_model.frsky.varioCenterSilent, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,584 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "opentx.h"
|
||||
|
||||
int menuVerticalOffset;
|
||||
int menuVerticalPosition;
|
||||
int menuHorizontalPosition;
|
||||
int8_t s_editMode;
|
||||
uint8_t noHighlightCounter;
|
||||
uint8_t calibrationState; // TODO rename this variable
|
||||
int checkIncDecSelection = 0;
|
||||
|
||||
#if defined(AUTOSWITCH)
|
||||
swsrc_t checkIncDecMovedSwitch(swsrc_t val)
|
||||
{
|
||||
if (s_editMode>0) {
|
||||
swsrc_t swtch = getMovedSwitch();
|
||||
if (swtch) {
|
||||
div_t info = switchInfo(swtch);
|
||||
if (IS_TOGGLE(info.quot)) {
|
||||
if (info.rem != 0) {
|
||||
val = (val == swtch ? swtch-2 : swtch);
|
||||
}
|
||||
}
|
||||
else {
|
||||
val = swtch;
|
||||
}
|
||||
}
|
||||
}
|
||||
return val;
|
||||
}
|
||||
#endif
|
||||
|
||||
int8_t checkIncDec_Ret;
|
||||
|
||||
INIT_STOPS(stops100, 3, -100, 0, 100)
|
||||
INIT_STOPS(stops1000, 3, -1000, 0, 1000)
|
||||
INIT_STOPS(stopsSwitch, 15, SWSRC_FIRST, CATEGORY_END(-SWSRC_FIRST_LOGICAL_SWITCH), CATEGORY_END(-SWSRC_FIRST_TRIM), CATEGORY_END(-SWSRC_LAST_SWITCH+1), 0, CATEGORY_END(SWSRC_LAST_SWITCH), CATEGORY_END(SWSRC_FIRST_TRIM-1), CATEGORY_END(SWSRC_FIRST_LOGICAL_SWITCH-1), SWSRC_LAST)
|
||||
|
||||
void onSourceLongEnterPress(const char * result)
|
||||
{
|
||||
if (result == STR_MENU_INPUTS)
|
||||
checkIncDecSelection = getFirstAvailable(MIXSRC_FIRST_INPUT, MIXSRC_LAST_INPUT, isInputAvailable)+1;
|
||||
#if defined(LUA_MODEL_SCRIPTS)
|
||||
else if (result == STR_MENU_LUA)
|
||||
checkIncDecSelection = getFirstAvailable(MIXSRC_FIRST_LUA, MIXSRC_LAST_LUA, isSourceAvailable);
|
||||
#endif
|
||||
else if (result == STR_MENU_STICKS)
|
||||
checkIncDecSelection = MIXSRC_FIRST_STICK;
|
||||
else if (result == STR_MENU_POTS)
|
||||
checkIncDecSelection = MIXSRC_FIRST_POT;
|
||||
else if (result == STR_MENU_MAX)
|
||||
checkIncDecSelection = MIXSRC_MAX;
|
||||
else if (result == STR_MENU_HELI)
|
||||
checkIncDecSelection = MIXSRC_FIRST_HELI;
|
||||
else if (result == STR_MENU_TRIMS)
|
||||
checkIncDecSelection = MIXSRC_FIRST_TRIM;
|
||||
else if (result == STR_MENU_SWITCHES)
|
||||
checkIncDecSelection = MIXSRC_FIRST_SWITCH;
|
||||
else if (result == STR_MENU_TRAINER)
|
||||
checkIncDecSelection = MIXSRC_FIRST_TRAINER;
|
||||
else if (result == STR_MENU_CHANNELS)
|
||||
checkIncDecSelection = getFirstAvailable(MIXSRC_FIRST_CH, MIXSRC_LAST_CH, isSourceAvailable);
|
||||
else if (result == STR_MENU_GVARS)
|
||||
checkIncDecSelection = MIXSRC_FIRST_GVAR;
|
||||
else if (result == STR_MENU_TELEMETRY) {
|
||||
for (int i = 0; i < MAX_SENSORS; i++) {
|
||||
TelemetrySensor * sensor = & g_model.telemetrySensors[i];
|
||||
if (sensor->isAvailable()) {
|
||||
checkIncDecSelection = MIXSRC_FIRST_TELEM + 3*i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void onSwitchLongEnterPress(const char *result)
|
||||
{
|
||||
if (result == STR_MENU_SWITCHES)
|
||||
checkIncDecSelection = SWSRC_FIRST_SWITCH;
|
||||
else if (result == STR_MENU_TRIMS)
|
||||
checkIncDecSelection = SWSRC_FIRST_TRIM;
|
||||
else if (result == STR_MENU_LOGICAL_SWITCHES)
|
||||
checkIncDecSelection = SWSRC_FIRST_LOGICAL_SWITCH + getFirstAvailable(0, NUM_LOGICAL_SWITCH, isLogicalSwitchAvailable);
|
||||
else if (result == STR_MENU_OTHER)
|
||||
checkIncDecSelection = SWSRC_ON;
|
||||
else if (result == STR_MENU_INVERT)
|
||||
checkIncDecSelection = SWSRC_INVERT;
|
||||
}
|
||||
|
||||
int checkIncDec(evt_t event, int val, int i_min, int i_max, unsigned int i_flags, IsValueAvailable isValueAvailable, const CheckIncDecStops &stops)
|
||||
{
|
||||
int newval = val;
|
||||
|
||||
#if 0
|
||||
uint32_t in = KEYS_PRESSED();
|
||||
if (!(i_flags & NO_DBLKEYS) && (EVT_KEY_MASK(event))) {
|
||||
bool dblkey = true;
|
||||
if (DBLKEYS_PRESSED_RGT_LFT(in)) {
|
||||
if (!isValueAvailable || isValueAvailable(-val))
|
||||
newval = -val;
|
||||
}
|
||||
else if (DBLKEYS_PRESSED_RGT_UP(in)) {
|
||||
newval = (i_max > stops.max() ? stops.max() : i_max);
|
||||
while (isValueAvailable && !isValueAvailable(newval) && newval>i_min) {
|
||||
--newval;
|
||||
}
|
||||
}
|
||||
else if (DBLKEYS_PRESSED_LFT_DWN(in)) {
|
||||
newval = (i_min < stops.min() ? stops.min() : i_min);
|
||||
while (isValueAvailable && !isValueAvailable(newval) && newval<i_max) {
|
||||
++newval;
|
||||
}
|
||||
}
|
||||
else if (DBLKEYS_PRESSED_UP_DWN(in)) {
|
||||
newval = 0;
|
||||
}
|
||||
else {
|
||||
dblkey = false;
|
||||
}
|
||||
|
||||
if (dblkey) {
|
||||
killEvents(KEY_UP);
|
||||
killEvents(KEY_DOWN);
|
||||
killEvents(KEY_RIGHT);
|
||||
killEvents(KEY_LEFT);
|
||||
killEvents(KEY_ENTER);
|
||||
killEvents(KEY_EXIT);
|
||||
event = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (s_editMode>0 && event==EVT_ROTARY_RIGHT) {
|
||||
newval += min<int>(rotencSpeed, i_max-val);
|
||||
while (isValueAvailable && !isValueAvailable(newval) && newval<=i_max) {
|
||||
newval++;
|
||||
}
|
||||
if (newval > i_max) {
|
||||
newval = val;
|
||||
AUDIO_KEY_ERROR();
|
||||
}
|
||||
}
|
||||
else if (s_editMode>0 && event==EVT_ROTARY_LEFT) {
|
||||
newval -= min<int>(rotencSpeed, val-i_min);
|
||||
while (isValueAvailable && !isValueAvailable(newval) && newval>=i_min) {
|
||||
newval--;
|
||||
}
|
||||
if (newval < i_min) {
|
||||
newval = val;
|
||||
AUDIO_KEY_ERROR();
|
||||
}
|
||||
}
|
||||
|
||||
if (!READ_ONLY() && i_min==0 && i_max==1 && event==EVT_KEY_BREAK(KEY_ENTER)) {
|
||||
s_editMode = 0;
|
||||
newval = !val;
|
||||
}
|
||||
|
||||
#if defined(AUTOSWITCH)
|
||||
if (i_flags & INCDEC_SWITCH) {
|
||||
newval = checkIncDecMovedSwitch(newval);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(AUTOSOURCE)
|
||||
if (i_flags & INCDEC_SOURCE) {
|
||||
if (s_editMode>0) {
|
||||
int source = GET_MOVED_SOURCE(i_min, i_max);
|
||||
if (source) {
|
||||
newval = source;
|
||||
}
|
||||
#if defined(AUTOSWITCH)
|
||||
else {
|
||||
unsigned int swtch = abs(getMovedSwitch());
|
||||
if (swtch) {
|
||||
newval = switchToMix(swtch);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (newval != val) {
|
||||
storageDirty(i_flags & (EE_GENERAL|EE_MODEL));
|
||||
checkIncDec_Ret = (newval > val ? 1 : -1);
|
||||
AUDIO_KEY_PRESS();
|
||||
}
|
||||
else {
|
||||
checkIncDec_Ret = 0;
|
||||
}
|
||||
|
||||
if (i_flags & INCDEC_SOURCE) {
|
||||
if (event == EVT_KEY_LONG(KEY_ENTER)) {
|
||||
killEvents(event);
|
||||
checkIncDecSelection = MIXSRC_NONE;
|
||||
|
||||
if (i_min <= MIXSRC_FIRST_INPUT && i_max >= MIXSRC_FIRST_INPUT) {
|
||||
if (getFirstAvailable(MIXSRC_FIRST_INPUT, MIXSRC_LAST_INPUT, isInputAvailable) != MIXSRC_NONE) {
|
||||
POPUP_MENU_ADD_ITEM(STR_MENU_INPUTS);
|
||||
}
|
||||
}
|
||||
#if defined(LUA_MODEL_SCRIPTS)
|
||||
if (i_min <= MIXSRC_FIRST_LUA && i_max >= MIXSRC_FIRST_LUA) {
|
||||
if (getFirstAvailable(MIXSRC_FIRST_LUA, MIXSRC_LAST_LUA, isSourceAvailable) != MIXSRC_NONE) {
|
||||
POPUP_MENU_ADD_ITEM(STR_MENU_LUA);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (i_min <= MIXSRC_FIRST_STICK && i_max >= MIXSRC_FIRST_STICK) POPUP_MENU_ADD_ITEM(STR_MENU_STICKS);
|
||||
if (i_min <= MIXSRC_FIRST_POT && i_max >= MIXSRC_FIRST_POT) POPUP_MENU_ADD_ITEM(STR_MENU_POTS);
|
||||
if (i_min <= MIXSRC_MAX && i_max >= MIXSRC_MAX) POPUP_MENU_ADD_ITEM(STR_MENU_MAX);
|
||||
#if defined(HELI)
|
||||
if (i_min <= MIXSRC_FIRST_HELI && i_max >= MIXSRC_FIRST_HELI) POPUP_MENU_ADD_ITEM(STR_MENU_HELI);
|
||||
#endif
|
||||
if (i_min <= MIXSRC_FIRST_TRIM && i_max >= MIXSRC_FIRST_TRIM) POPUP_MENU_ADD_ITEM(STR_MENU_TRIMS);
|
||||
if (i_min <= MIXSRC_FIRST_SWITCH && i_max >= MIXSRC_FIRST_SWITCH) POPUP_MENU_ADD_ITEM(STR_MENU_SWITCHES);
|
||||
if (i_min <= MIXSRC_FIRST_TRAINER && i_max >= MIXSRC_FIRST_TRAINER) POPUP_MENU_ADD_ITEM(STR_MENU_TRAINER);
|
||||
if (i_min <= MIXSRC_FIRST_CH && i_max >= MIXSRC_FIRST_CH) POPUP_MENU_ADD_ITEM(STR_MENU_CHANNELS);
|
||||
if (i_min <= MIXSRC_FIRST_GVAR && i_max >= MIXSRC_FIRST_GVAR && isValueAvailable(MIXSRC_FIRST_GVAR)) {
|
||||
POPUP_MENU_ADD_ITEM(STR_MENU_GVARS);
|
||||
}
|
||||
|
||||
if (i_min <= MIXSRC_FIRST_TELEM && i_max >= MIXSRC_FIRST_TELEM) {
|
||||
for (int i = 0; i < MAX_SENSORS; i++) {
|
||||
TelemetrySensor * sensor = & g_model.telemetrySensors[i];
|
||||
if (sensor->isAvailable()) {
|
||||
POPUP_MENU_ADD_ITEM(STR_MENU_TELEMETRY);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
POPUP_MENU_START(onSourceLongEnterPress);
|
||||
}
|
||||
if (checkIncDecSelection != 0) {
|
||||
newval = checkIncDecSelection;
|
||||
if (checkIncDecSelection != MIXSRC_MAX)
|
||||
s_editMode = EDIT_MODIFY_FIELD;
|
||||
checkIncDecSelection = 0;
|
||||
}
|
||||
}
|
||||
else if (i_flags & INCDEC_SWITCH) {
|
||||
if (event == EVT_KEY_LONG(KEY_ENTER)) {
|
||||
killEvents(event);
|
||||
checkIncDecSelection = SWSRC_NONE;
|
||||
if (i_min <= SWSRC_FIRST_SWITCH && i_max >= SWSRC_LAST_SWITCH) POPUP_MENU_ADD_ITEM(STR_MENU_SWITCHES);
|
||||
if (i_min <= SWSRC_FIRST_TRIM && i_max >= SWSRC_LAST_TRIM) POPUP_MENU_ADD_ITEM(STR_MENU_TRIMS);
|
||||
if (i_min <= SWSRC_FIRST_LOGICAL_SWITCH && i_max >= SWSRC_LAST_LOGICAL_SWITCH) {
|
||||
for (int i = 0; i < NUM_LOGICAL_SWITCH; i++) {
|
||||
if (isValueAvailable && isValueAvailable(SWSRC_FIRST_LOGICAL_SWITCH+i)) {
|
||||
POPUP_MENU_ADD_ITEM(STR_MENU_LOGICAL_SWITCHES);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isValueAvailable && isValueAvailable(SWSRC_ON)) POPUP_MENU_ADD_ITEM(STR_MENU_OTHER);
|
||||
if (isValueAvailable && isValueAvailable(-newval)) POPUP_MENU_ADD_ITEM(STR_MENU_INVERT);
|
||||
POPUP_MENU_START(onSwitchLongEnterPress);
|
||||
s_editMode = EDIT_MODIFY_FIELD;
|
||||
}
|
||||
if (checkIncDecSelection != 0) {
|
||||
newval = (checkIncDecSelection == SWSRC_INVERT ? -newval : checkIncDecSelection);
|
||||
s_editMode = EDIT_MODIFY_FIELD;
|
||||
checkIncDecSelection = 0;
|
||||
}
|
||||
}
|
||||
return newval;
|
||||
}
|
||||
|
||||
#define CURSOR_NOT_ALLOWED_IN_ROW(row) ((int8_t)MAXCOL(row) < 0)
|
||||
#define HORTAB(row) (int8_t(row) > (int8_t)horTabMax ? horTab[horTabMax] : horTab[row])
|
||||
#define MAXCOL_RAW(row) (horTab ? HORTAB(row) : (const uint8_t)0)
|
||||
#define MAXCOL(row) (MAXCOL_RAW(row) >= HIDDEN_ROW ? MAXCOL_RAW(row) : (const uint8_t)(MAXCOL_RAW(row) & (~NAVIGATION_LINE_BY_LINE)))
|
||||
#define COLATTR(row) (MAXCOL_RAW(row) == (uint8_t)-1 ? (const uint8_t)0 : (const uint8_t)(MAXCOL_RAW(row) & NAVIGATION_LINE_BY_LINE))
|
||||
#define INC(val, min, max) if (val<max) {val++;} else if (max>min) {val=min;}
|
||||
#define DEC(val, min, max) if (val>min) {val--;} else {val=max;}
|
||||
|
||||
uint8_t menuPageIndex;
|
||||
uint8_t menuPageCount;
|
||||
uint16_t linesCount;
|
||||
|
||||
int8_t navigate(evt_t event, int count, int rows, int columns, bool loop)
|
||||
{
|
||||
int8_t result = 0;
|
||||
|
||||
int prevPosHorz = menuHorizontalPosition;
|
||||
int prevPosVert = menuVerticalPosition;
|
||||
|
||||
int maxrow = ((count+columns-1) / columns) - 1;
|
||||
int maxlastcol = count-maxrow*columns-1;
|
||||
int maxcol = (menuVerticalPosition != maxrow ? columns-1 : maxlastcol);
|
||||
|
||||
if (menuVerticalPosition > maxrow) {
|
||||
menuVerticalPosition = maxrow;
|
||||
menuHorizontalPosition = maxlastcol;
|
||||
}
|
||||
|
||||
switch (event) {
|
||||
case EVT_ROTARY_LEFT:
|
||||
result = -1;
|
||||
if (menuHorizontalPosition > 0) {
|
||||
menuHorizontalPosition--;
|
||||
}
|
||||
else if (loop) {
|
||||
DEC(menuVerticalPosition, 0, maxrow);
|
||||
menuHorizontalPosition = (menuVerticalPosition != maxrow ? columns-1 : maxlastcol);
|
||||
}
|
||||
else if (menuVerticalPosition > 0) {
|
||||
menuVerticalPosition -= 1;
|
||||
menuHorizontalPosition = columns-1;
|
||||
}
|
||||
break;
|
||||
|
||||
case EVT_ROTARY_RIGHT:
|
||||
result = +1;
|
||||
if (menuHorizontalPosition < maxcol) {
|
||||
menuHorizontalPosition++;
|
||||
}
|
||||
else if (loop) {
|
||||
INC(menuVerticalPosition, 0, maxrow);
|
||||
menuHorizontalPosition = 0;
|
||||
}
|
||||
else if (menuVerticalPosition < maxrow) {
|
||||
menuVerticalPosition += 1;
|
||||
menuHorizontalPosition = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (menuVerticalOffset > menuVerticalPosition) {
|
||||
menuVerticalOffset = menuVerticalPosition;
|
||||
}
|
||||
else if (menuVerticalOffset <= menuVerticalPosition - rows) {
|
||||
menuVerticalOffset = menuVerticalPosition - rows + 1;
|
||||
}
|
||||
|
||||
return (prevPosHorz != menuHorizontalPosition || prevPosVert != menuVerticalPosition) ? result : 0;
|
||||
}
|
||||
|
||||
bool check(check_event_t event, uint8_t curr, const MenuHandlerFunc * menuTab, uint8_t menuTabSize, const pm_uint8_t * horTab, uint8_t horTabMax, int rowcount, uint8_t flags)
|
||||
{
|
||||
uint8_t maxcol = MAXCOL(menuVerticalPosition);
|
||||
|
||||
if (menuTab == menuTabModel && event == EVT_KEY_FIRST(KEY_MODEL)) {
|
||||
killEvents(event);
|
||||
pushMenu(menuTabMonitors[lastMonitorPage]);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (menuTab && !calibrationState) {
|
||||
int cc = curr;
|
||||
switch (event) {
|
||||
case EVT_KEY_FIRST(KEY_PGDN):
|
||||
if (++cc == menuTabSize)
|
||||
cc = 0;
|
||||
break;
|
||||
|
||||
case EVT_KEY_FIRST(KEY_PGUP):
|
||||
if (cc-- == 0)
|
||||
cc = menuTabSize-1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (cc != curr) {
|
||||
chainMenu((MenuHandlerFunc)pgm_read_adr(&menuTab[cc]));
|
||||
return false;
|
||||
}
|
||||
|
||||
menuPageIndex = curr;
|
||||
menuPageCount = menuTabSize;
|
||||
}
|
||||
|
||||
switch(event) {
|
||||
case EVT_ENTRY:
|
||||
s_editMode = EDIT_MODE_INIT;
|
||||
menuVerticalPosition = MENU_FIRST_LINE_EDIT;
|
||||
menuHorizontalPosition = POS_HORZ_INIT(menuVerticalPosition);
|
||||
break;
|
||||
|
||||
case EVT_ENTRY_UP:
|
||||
s_editMode = 0;
|
||||
menuHorizontalPosition = POS_HORZ_INIT(menuVerticalPosition);
|
||||
break;
|
||||
|
||||
case EVT_ROTARY_BREAK:
|
||||
if (s_editMode > 1) break;
|
||||
if (menuHorizontalPosition < 0 && maxcol > 0 && READ_ONLY_UNLOCKED()) {
|
||||
menuHorizontalPosition = 0;
|
||||
AUDIO_KEY_PRESS();
|
||||
}
|
||||
else if (READ_ONLY_UNLOCKED() && rowcount > 0) {
|
||||
s_editMode = (s_editMode<=0);
|
||||
AUDIO_KEY_PRESS();
|
||||
}
|
||||
break;
|
||||
|
||||
case EVT_KEY_FIRST(KEY_EXIT):
|
||||
if (s_editMode > 0) {
|
||||
s_editMode = 0;
|
||||
AUDIO_KEY_PRESS();
|
||||
break;
|
||||
}
|
||||
|
||||
if (s_copyMode > 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (menuHorizontalPosition >= 0 && (COLATTR(menuVerticalPosition) & NAVIGATION_LINE_BY_LINE)) {
|
||||
menuHorizontalPosition = -1;
|
||||
AUDIO_KEY_PRESS();
|
||||
}
|
||||
else {
|
||||
uint8_t posVertInit = MENU_FIRST_LINE_EDIT;
|
||||
if (menuVerticalOffset != 0 || menuVerticalPosition != posVertInit) {
|
||||
menuVerticalOffset = 0;
|
||||
menuVerticalPosition = posVertInit;
|
||||
menuHorizontalPosition = POS_HORZ_INIT(menuVerticalPosition);
|
||||
AUDIO_KEY_PRESS();
|
||||
}
|
||||
else if (!calibrationState) {
|
||||
popMenu();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case EVT_KEY_FIRST(KEY_RIGHT):
|
||||
AUDIO_KEY_PRESS();
|
||||
// no break
|
||||
case EVT_KEY_REPT(KEY_RIGHT):
|
||||
if (s_editMode == 0) {
|
||||
INC(menuHorizontalPosition, 0, maxcol);
|
||||
}
|
||||
break;
|
||||
|
||||
case EVT_KEY_FIRST(KEY_LEFT):
|
||||
AUDIO_KEY_PRESS();
|
||||
// no break
|
||||
case EVT_KEY_REPT(KEY_LEFT):
|
||||
if (s_editMode == 0) {
|
||||
DEC(menuHorizontalPosition, 0, maxcol);
|
||||
}
|
||||
break;
|
||||
|
||||
case EVT_ROTARY_RIGHT:
|
||||
if (s_editMode != 0) break;
|
||||
AUDIO_KEY_PRESS();
|
||||
if ((COLATTR(menuVerticalPosition) & NAVIGATION_LINE_BY_LINE)) {
|
||||
if (menuHorizontalPosition >= 0) {
|
||||
INC(menuHorizontalPosition, 0, maxcol);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (menuHorizontalPosition < maxcol) {
|
||||
menuHorizontalPosition++;
|
||||
break;
|
||||
}
|
||||
do {
|
||||
INC(menuVerticalPosition, MENU_FIRST_LINE_EDIT, rowcount-1);
|
||||
} while (CURSOR_NOT_ALLOWED_IN_ROW(menuVerticalPosition));
|
||||
menuHorizontalPosition = POS_HORZ_INIT(menuVerticalPosition);
|
||||
|
||||
break;
|
||||
|
||||
case EVT_ROTARY_LEFT:
|
||||
if (s_editMode != 0) break;
|
||||
AUDIO_KEY_PRESS();
|
||||
if ((COLATTR(menuVerticalPosition) & NAVIGATION_LINE_BY_LINE)) {
|
||||
if (menuHorizontalPosition >= 0) {
|
||||
DEC(menuHorizontalPosition, 0, maxcol);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (menuHorizontalPosition > 0) {
|
||||
menuHorizontalPosition--;
|
||||
break;
|
||||
}
|
||||
do {
|
||||
DEC(menuVerticalPosition, MENU_FIRST_LINE_EDIT, rowcount-1);
|
||||
} while (CURSOR_NOT_ALLOWED_IN_ROW(menuVerticalPosition));
|
||||
if (COLATTR(menuVerticalPosition) & NAVIGATION_LINE_BY_LINE)
|
||||
menuHorizontalPosition = -1;
|
||||
else
|
||||
menuHorizontalPosition = MAXCOL(menuVerticalPosition);
|
||||
break;
|
||||
}
|
||||
|
||||
linesCount = rowcount;
|
||||
|
||||
if (menuVerticalPosition <= MENU_FIRST_LINE_EDIT) {
|
||||
menuVerticalOffset = 0;
|
||||
if (horTab) {
|
||||
linesCount = 0;
|
||||
for (int i=0; i<rowcount; i++) {
|
||||
if (HORTAB(i) != HIDDEN_ROW) {
|
||||
linesCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (horTab) {
|
||||
if (rowcount > linesDisplayed) {
|
||||
while (1) {
|
||||
int firstLine = 0;
|
||||
for (int numLines=0; firstLine<rowcount && numLines<menuVerticalOffset; firstLine++) {
|
||||
if (HORTAB(firstLine) != HIDDEN_ROW) {
|
||||
numLines++;
|
||||
}
|
||||
}
|
||||
if (menuVerticalPosition < firstLine) {
|
||||
menuVerticalOffset--;
|
||||
}
|
||||
else {
|
||||
int lastLine = firstLine;
|
||||
for (int numLines=0; lastLine<rowcount && numLines<linesDisplayed; lastLine++) {
|
||||
if (HORTAB(lastLine) != HIDDEN_ROW) {
|
||||
numLines++;
|
||||
}
|
||||
}
|
||||
if (menuVerticalPosition >= lastLine || HORTAB(firstLine) == ORPHAN_ROW) {
|
||||
menuVerticalOffset++;
|
||||
}
|
||||
else {
|
||||
linesCount = menuVerticalOffset + linesDisplayed;
|
||||
for (int i=lastLine; i<rowcount; i++) {
|
||||
if (HORTAB(i) != HIDDEN_ROW) {
|
||||
linesCount++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (menuVerticalPosition >= linesDisplayed + menuVerticalOffset) {
|
||||
menuVerticalOffset = menuVerticalPosition-linesDisplayed+1;
|
||||
}
|
||||
else if (menuVerticalPosition < menuVerticalOffset) {
|
||||
menuVerticalOffset = menuVerticalPosition;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool check_simple(check_event_t event, uint8_t curr, const MenuHandlerFunc *menuTab, uint8_t menuTabSize, int rowcount)
|
||||
{
|
||||
return check(event, curr, menuTab, menuTabSize, NULL, 0, rowcount);
|
||||
}
|
||||
|
||||
bool check_submenu_simple(check_event_t event, uint8_t rowcount)
|
||||
{
|
||||
return check_simple(event, 0, NULL, 0, rowcount);
|
||||
}
|
||||
|
||||
void repeatLastCursorMove(evt_t event)
|
||||
{
|
||||
if (CURSOR_MOVED_LEFT(event) || CURSOR_MOVED_RIGHT(event)) {
|
||||
putEvent(event);
|
||||
}
|
||||
else {
|
||||
menuHorizontalPosition = 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "opentx.h"
|
||||
|
||||
const char *warningText = NULL;
|
||||
const char *warningInfoText;
|
||||
uint8_t warningInfoLength;
|
||||
uint8_t warningType;
|
||||
uint8_t warningResult = 0;
|
||||
uint8_t warningInfoFlags = ZCHAR;
|
||||
int16_t warningInputValue;
|
||||
int16_t warningInputValueMin;
|
||||
int16_t warningInputValueMax;
|
||||
void (*popupFunc)(evt_t event) = NULL;
|
||||
const char *popupMenuItems[POPUP_MENU_MAX_LINES];
|
||||
uint8_t s_menu_item = 0;
|
||||
uint16_t popupMenuNoItems = 0;
|
||||
uint16_t popupMenuOffset = 0;
|
||||
uint8_t popupMenuOffsetType = MENU_OFFSET_INTERNAL;
|
||||
void (*popupMenuHandler)(const char * result);
|
||||
|
||||
void displayWarningBox()
|
||||
{
|
||||
// theme->drawMessageBox("", "", "", MESSAGEBOX_TYPE_WARNING);
|
||||
// lcdDrawSolidFilledRect(POPUP_X, POPUP_Y, POPUP_W, POPUP_H, TEXT_BGCOLOR);
|
||||
// lcdDrawSolidRect(POPUP_X, POPUP_Y, POPUP_W, POPUP_H, 2, ALARM_COLOR);
|
||||
// lcdDrawBitmap(POPUP_X+15, POPUP_Y+20, LBM_WARNING);
|
||||
}
|
||||
|
||||
void displayMessageBox()
|
||||
{
|
||||
// theme->drawMessageBox("", "", "", MESSAGEBOX_TYPE_INFO);
|
||||
// lcdDrawSolidFilledRect(POPUP_X, POPUP_Y, POPUP_W, POPUP_H, TEXT_BGCOLOR);
|
||||
// lcdDrawSolidRect(POPUP_X, POPUP_Y, POPUP_W, POPUP_H, 2, WARNING_COLOR);
|
||||
// lcdDrawBitmap(POPUP_X+15, POPUP_Y+20, LBM_MESSAGE);
|
||||
}
|
||||
|
||||
void drawAlertBox(const char * title, const char * text, const char * action)
|
||||
{
|
||||
theme->drawMessageBox(title, text, action, WARNING_TYPE_ALERT);
|
||||
}
|
||||
|
||||
void message(const pm_char * title, const pm_char * text, const char * action, uint8_t sound)
|
||||
{
|
||||
drawAlertBox(title, text, action);
|
||||
AUDIO_ERROR_MESSAGE(sound);
|
||||
lcdRefresh();
|
||||
lcdSetContrast();
|
||||
clearKeyEvents();
|
||||
}
|
||||
|
||||
void displayPopup(const char * title)
|
||||
{
|
||||
// displayMessageBox();
|
||||
lcdDrawSizedText(WARNING_LINE_X, WARNING_LINE_Y, title, WARNING_LINE_LEN, DBLSIZE|WARNING_COLOR);
|
||||
lcdRefresh();
|
||||
}
|
||||
|
||||
void displayWarning(evt_t event)
|
||||
{
|
||||
warningResult = false;
|
||||
|
||||
theme->drawMessageBox(warningText, warningInfoText, warningType == WARNING_TYPE_ASTERISK ? STR_EXIT : STR_POPUPS, warningType);
|
||||
|
||||
switch (event) {
|
||||
case EVT_KEY_BREAK(KEY_ENTER):
|
||||
if (warningType == WARNING_TYPE_ASTERISK)
|
||||
break;
|
||||
warningResult = true;
|
||||
// no break
|
||||
case EVT_KEY_BREAK(KEY_EXIT):
|
||||
warningText = NULL;
|
||||
warningType = WARNING_TYPE_ASTERISK;
|
||||
break;
|
||||
default:
|
||||
if (warningType != WARNING_TYPE_INPUT) break;
|
||||
s_editMode = EDIT_MODIFY_FIELD;
|
||||
warningInputValue = checkIncDec(event, warningInputValue, warningInputValueMin, warningInputValueMax);
|
||||
s_editMode = EDIT_SELECT_FIELD;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const char * displayPopupMenu(evt_t event)
|
||||
{
|
||||
const char * result = NULL;
|
||||
|
||||
uint8_t display_count = min<unsigned int>(popupMenuNoItems, MENU_MAX_DISPLAY_LINES);
|
||||
|
||||
switch (event) {
|
||||
case EVT_ROTARY_LEFT:
|
||||
if (s_menu_item > 0) {
|
||||
s_menu_item--;
|
||||
}
|
||||
else if (popupMenuOffset > 0) {
|
||||
popupMenuOffset--;
|
||||
result = STR_UPDATE_LIST;
|
||||
}
|
||||
else {
|
||||
s_menu_item = min<uint8_t>(display_count, MENU_MAX_DISPLAY_LINES) - 1;
|
||||
if (popupMenuNoItems > MENU_MAX_DISPLAY_LINES) {
|
||||
popupMenuOffset = popupMenuNoItems - MENU_MAX_DISPLAY_LINES;
|
||||
result = STR_UPDATE_LIST;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case EVT_ROTARY_RIGHT:
|
||||
if (s_menu_item < display_count - 1 && popupMenuOffset + s_menu_item + 1 < popupMenuNoItems) {
|
||||
s_menu_item++;
|
||||
}
|
||||
else if (popupMenuNoItems > popupMenuOffset + display_count) {
|
||||
popupMenuOffset++;
|
||||
result = STR_UPDATE_LIST;
|
||||
}
|
||||
else {
|
||||
s_menu_item = 0;
|
||||
if (popupMenuOffset) {
|
||||
popupMenuOffset = 0;
|
||||
result = STR_UPDATE_LIST;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EVT_KEY_BREAK(KEY_ENTER):
|
||||
result = popupMenuItems[s_menu_item + (popupMenuOffsetType == MENU_OFFSET_INTERNAL ? popupMenuOffset : 0)];
|
||||
// no break
|
||||
case EVT_KEY_BREAK(KEY_EXIT):
|
||||
popupMenuNoItems = 0;
|
||||
s_menu_item = 0;
|
||||
popupMenuOffset = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
int y = (LCD_H - (display_count*(FH+1))) / 2;
|
||||
|
||||
lcdDrawSolidFilledRect(MENU_X, y, MENU_W, display_count * (FH+1) + 1, TEXT_BGCOLOR);
|
||||
lcdDrawSolidRect(MENU_X, y, MENU_W, display_count * (FH+1) + 2, 1, ALARM_COLOR);
|
||||
|
||||
for (uint8_t i=0; i<display_count; i++) {
|
||||
if (i == s_menu_item) {
|
||||
lcdDrawSolidFilledRect(MENU_X+1, i*(FH+1) + y + 1, MENU_W-2, FH+1, TEXT_INVERTED_BGCOLOR);
|
||||
lcdDrawText(MENU_X+6, i*(FH+1) + y + 2, popupMenuItems[i+(popupMenuOffsetType == MENU_OFFSET_INTERNAL ? popupMenuOffset : 0)], TEXT_INVERTED_COLOR);
|
||||
}
|
||||
else {
|
||||
lcdDrawText(MENU_X+6, i*(FH+1) + y + 2, popupMenuItems[i+(popupMenuOffsetType == MENU_OFFSET_INTERNAL ? popupMenuOffset : 0)], 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (popupMenuNoItems > display_count) {
|
||||
drawVerticalScrollbar(MENU_X+MENU_W-1, y+1, MENU_MAX_DISPLAY_LINES * (FH+1), popupMenuOffset, popupMenuNoItems, MENU_MAX_DISPLAY_LINES);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,257 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "opentx.h"
|
||||
|
||||
#define XPOT_DELTA 10
|
||||
#define XPOT_DELAY 10 /* cycles */
|
||||
#define STICKS_WIDTH 90
|
||||
#define STICKS_Y 60
|
||||
#define STICK_LEFT_X 25
|
||||
#define STICK_RIGHT_X (LCD_W-STICK_LEFT_X-STICKS_WIDTH)
|
||||
|
||||
enum CalibrationState {
|
||||
CALIB_START = 0,
|
||||
CALIB_SET_MIDPOINT,
|
||||
CALIB_MOVE_STICKS,
|
||||
CALIB_STORE,
|
||||
CALIB_FINISHED
|
||||
};
|
||||
|
||||
#define STICK_PANEL_WIDTH 68
|
||||
|
||||
void drawStick(coord_t x, coord_t y, const BitmapBuffer * background, int16_t xval, int16_t yval)
|
||||
{
|
||||
static const BitmapBuffer * stick = BitmapBuffer::load(getThemePath("stick_pointer.png"));
|
||||
lcd->drawBitmap(x, y, background);
|
||||
lcd->drawBitmap(x + 2 + STICK_PANEL_WIDTH/2 + STICK_PANEL_WIDTH/2 * xval/RESX, y + 2 + STICK_PANEL_WIDTH/2 - STICK_PANEL_WIDTH/2 * yval/RESX, stick);
|
||||
}
|
||||
|
||||
void drawSticks()
|
||||
{
|
||||
static const BitmapBuffer * background = BitmapBuffer::load(getThemePath("stick_background.png"));
|
||||
int16_t calibStickVert = calibratedStick[CONVERT_MODE(1)];
|
||||
if (g_model.throttleReversed && CONVERT_MODE(1) == THR_STICK)
|
||||
calibStickVert = -calibStickVert;
|
||||
drawStick(STICK_LEFT_X, STICKS_Y, background, calibratedStick[CONVERT_MODE(0)], calibStickVert);
|
||||
|
||||
calibStickVert = calibratedStick[CONVERT_MODE(2)];
|
||||
if (g_model.throttleReversed && CONVERT_MODE(2) == THR_STICK)
|
||||
calibStickVert = -calibStickVert;
|
||||
drawStick(STICK_RIGHT_X, STICKS_Y, background, calibratedStick[CONVERT_MODE(3)], calibStickVert);
|
||||
}
|
||||
|
||||
void drawPots()
|
||||
{
|
||||
// The pots which are displayed in the main view
|
||||
extern void drawMainPots();
|
||||
drawMainPots();
|
||||
|
||||
// The 2 main front sliders
|
||||
drawVerticalSlider(125, 120, 120, calibratedStick[7], -RESX, RESX, 40, OPTION_SLIDER_TICKS | OPTION_SLIDER_BIG_TICKS |
|
||||
OPTION_SLIDER_SQUARE_BUTTON);
|
||||
drawVerticalSlider(LCD_W-125-12, 120, 120, calibratedStick[8], -RESX, RESX, 40, OPTION_SLIDER_TICKS | OPTION_SLIDER_BIG_TICKS |
|
||||
OPTION_SLIDER_SQUARE_BUTTON);
|
||||
}
|
||||
|
||||
void drawMouse()
|
||||
{
|
||||
static const BitmapBuffer * background = BitmapBuffer::load(getThemePath("trackp_background.png"));
|
||||
drawStick(STICK_LEFT_X, STICKS_Y+100, background, calibratedStick[11], calibratedStick[12]);
|
||||
}
|
||||
|
||||
bool menuCommonCalib(evt_t event)
|
||||
{
|
||||
drawMenuTemplate(NULL, ICON_RADIO_CALIBRATION, NULL, OPTION_MENU_NO_FOOTER);
|
||||
|
||||
for (uint8_t i=0; i<NUM_STICKS+NUM_POTS+NUM_MOUSE_ANALOGS; i++) { // get low and high vals for sticks and trims
|
||||
int16_t vt = i<NUM_STICKS+NUM_POTS ? anaIn(i) : anaIn(MOUSE1+i-NUM_STICKS-NUM_POTS);
|
||||
reusableBuffer.calib.loVals[i] = min(vt, reusableBuffer.calib.loVals[i]);
|
||||
reusableBuffer.calib.hiVals[i] = max(vt, reusableBuffer.calib.hiVals[i]);
|
||||
if (i >= POT1 && i <= POT_LAST) {
|
||||
if (IS_POT_WITHOUT_DETENT(i)) {
|
||||
reusableBuffer.calib.midVals[i] = (reusableBuffer.calib.hiVals[i] + reusableBuffer.calib.loVals[i]) / 2;
|
||||
}
|
||||
uint8_t idx = i - POT1;
|
||||
int count = reusableBuffer.calib.xpotsCalib[idx].stepsCount;
|
||||
if (IS_POT_MULTIPOS(i) && count <= XPOTS_MULTIPOS_COUNT) {
|
||||
// use raw analog value for multipos calibraton, anaIn() already has multipos decoded value
|
||||
vt = getAnalogValue(i) >> 1;
|
||||
if (reusableBuffer.calib.xpotsCalib[idx].lastCount == 0 || vt < reusableBuffer.calib.xpotsCalib[idx].lastPosition - XPOT_DELTA || vt > reusableBuffer.calib.xpotsCalib[idx].lastPosition + XPOT_DELTA) {
|
||||
reusableBuffer.calib.xpotsCalib[idx].lastPosition = vt;
|
||||
reusableBuffer.calib.xpotsCalib[idx].lastCount = 1;
|
||||
}
|
||||
else {
|
||||
if (reusableBuffer.calib.xpotsCalib[idx].lastCount < 255) reusableBuffer.calib.xpotsCalib[idx].lastCount++;
|
||||
}
|
||||
if (reusableBuffer.calib.xpotsCalib[idx].lastCount == XPOT_DELAY) {
|
||||
int16_t position = reusableBuffer.calib.xpotsCalib[idx].lastPosition;
|
||||
bool found = false;
|
||||
for (int j=0; j<count; j++) {
|
||||
int16_t step = reusableBuffer.calib.xpotsCalib[idx].steps[j];
|
||||
if (position >= step-XPOT_DELTA && position <= step+XPOT_DELTA) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
if (count < XPOTS_MULTIPOS_COUNT) {
|
||||
reusableBuffer.calib.xpotsCalib[idx].steps[count] = position;
|
||||
}
|
||||
reusableBuffer.calib.xpotsCalib[idx].stepsCount += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (event) {
|
||||
case EVT_ENTRY:
|
||||
calibrationState = CALIB_START;
|
||||
break;
|
||||
|
||||
case EVT_KEY_FIRST(KEY_EXIT):
|
||||
if (calibrationState == CALIB_START) {
|
||||
killEvents(KEY_EXIT);
|
||||
popMenu();
|
||||
}
|
||||
else {
|
||||
calibrationState = CALIB_START;
|
||||
}
|
||||
break;
|
||||
|
||||
case EVT_KEY_FIRST(KEY_ENTER):
|
||||
calibrationState++;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (calibrationState) {
|
||||
case CALIB_START:
|
||||
// START CALIBRATION
|
||||
if (!READ_ONLY()) {
|
||||
lcdDrawText(50, 3, STR_MENUCALIBRATION, MENU_TITLE_COLOR);
|
||||
lcdDrawText(50, 3+FH, STR_MENUTOSTART, MENU_TITLE_COLOR);
|
||||
}
|
||||
break;
|
||||
|
||||
case CALIB_SET_MIDPOINT:
|
||||
// SET MIDPOINT
|
||||
lcdDrawText(50, 3, STR_MENUCALIBRATION, MENU_TITLE_COLOR);
|
||||
lcdDrawText(50, 3+FH, STR_SETMIDPOINT, MENU_TITLE_COLOR);
|
||||
for (int i=0; i<NUM_STICKS+NUM_POTS+NUM_MOUSE_ANALOGS; i++) {
|
||||
reusableBuffer.calib.loVals[i] = 15000;
|
||||
reusableBuffer.calib.hiVals[i] = -15000;
|
||||
reusableBuffer.calib.midVals[i] = i<NUM_STICKS+NUM_POTS ? anaIn(i) : anaIn(MOUSE1+i-NUM_STICKS-NUM_POTS);
|
||||
if (i < NUM_XPOTS) {
|
||||
reusableBuffer.calib.xpotsCalib[i].stepsCount = 0;
|
||||
reusableBuffer.calib.xpotsCalib[i].lastCount = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case CALIB_MOVE_STICKS:
|
||||
// MOVE STICKS/POTS
|
||||
lcdDrawText(50, 3, STR_MENUCALIBRATION, MENU_TITLE_COLOR);
|
||||
lcdDrawText(50, 3+FH, STR_MOVESTICKSPOTS, MENU_TITLE_COLOR);
|
||||
for (uint8_t i=0; i<NUM_STICKS+NUM_POTS+NUM_MOUSE_ANALOGS; i++) {
|
||||
if (abs(reusableBuffer.calib.loVals[i]-reusableBuffer.calib.hiVals[i]) > 50) {
|
||||
g_eeGeneral.calib[i].mid = reusableBuffer.calib.midVals[i];
|
||||
int16_t v = reusableBuffer.calib.midVals[i] - reusableBuffer.calib.loVals[i];
|
||||
g_eeGeneral.calib[i].spanNeg = v - v/STICK_TOLERANCE;
|
||||
v = reusableBuffer.calib.hiVals[i] - reusableBuffer.calib.midVals[i];
|
||||
g_eeGeneral.calib[i].spanPos = v - v/STICK_TOLERANCE;
|
||||
}
|
||||
}
|
||||
for (int i=POT1; i<=POT_LAST; i++) {
|
||||
int idx = i - POT1;
|
||||
int count = reusableBuffer.calib.xpotsCalib[idx].stepsCount;
|
||||
if (IS_POT_MULTIPOS(i)) {
|
||||
if (count > 1 && count <= XPOTS_MULTIPOS_COUNT) {
|
||||
for (int j=0; j<count; j++) {
|
||||
for (int k=j+1; k<count; k++) {
|
||||
if (reusableBuffer.calib.xpotsCalib[idx].steps[k] < reusableBuffer.calib.xpotsCalib[idx].steps[j]) {
|
||||
SWAP(reusableBuffer.calib.xpotsCalib[idx].steps[j], reusableBuffer.calib.xpotsCalib[idx].steps[k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
StepsCalibData * calib = (StepsCalibData *) &g_eeGeneral.calib[i];
|
||||
calib->count = count - 1;
|
||||
for (int j=0; j<calib->count; j++) {
|
||||
calib->steps[j] = (reusableBuffer.calib.xpotsCalib[idx].steps[j+1] + reusableBuffer.calib.xpotsCalib[idx].steps[j]) >> 5;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// g_eeGeneral.potsConfig &= ~(0x03<<(2*idx));
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case CALIB_STORE:
|
||||
g_eeGeneral.chkSum = evalChkSum();
|
||||
storageDirty(EE_GENERAL);
|
||||
calibrationState = CALIB_FINISHED;
|
||||
break;
|
||||
|
||||
default:
|
||||
calibrationState = CALIB_START;
|
||||
break;
|
||||
}
|
||||
|
||||
static const BitmapBuffer * horus = BitmapBuffer::load(getThemePath("horus.bmp"));
|
||||
if (horus) {
|
||||
lcd->drawBitmap((LCD_W-horus->getWidth())/2, LCD_H-20-horus->getHeight(), horus);
|
||||
}
|
||||
|
||||
drawSticks();
|
||||
drawPots();
|
||||
drawMouse();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool menuGeneralCalib(evt_t event)
|
||||
{
|
||||
if (event == EVT_ENTRY || event == EVT_ENTRY_UP) TRACE("Menu %s displayed ...", STR_MENUCALIBRATION);
|
||||
if (calibrationState == CALIB_FINISHED) {
|
||||
calibrationState = CALIB_START;
|
||||
popMenu();
|
||||
return false;
|
||||
}
|
||||
if (!check_submenu_simple(event, 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
menuVerticalPosition = -1;
|
||||
|
||||
return menuCommonCalib(READ_ONLY() ? 0 : event);
|
||||
}
|
||||
|
||||
bool menuFirstCalib(evt_t event)
|
||||
{
|
||||
if (event == EVT_KEY_BREAK(KEY_EXIT) || calibrationState == CALIB_FINISHED) {
|
||||
calibrationState = CALIB_START;
|
||||
chainMenu(menuMainView);
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
return menuCommonCalib(event);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "opentx.h"
|
||||
|
||||
enum menuGeneralHwItems {
|
||||
ITEM_SETUP_HW_CALIBRATION,
|
||||
ITEM_SETUP_HW_LABEL_STICKS,
|
||||
ITEM_SETUP_HW_STICK1,
|
||||
ITEM_SETUP_HW_STICK2,
|
||||
ITEM_SETUP_HW_STICK3,
|
||||
ITEM_SETUP_HW_STICK4,
|
||||
ITEM_SETUP_HW_LABEL_POTS,
|
||||
ITEM_SETUP_HW_POT1,
|
||||
ITEM_SETUP_HW_POT2,
|
||||
ITEM_SETUP_HW_POT3,
|
||||
ITEM_SETUP_HW_LS,
|
||||
ITEM_SETUP_HW_RS,
|
||||
ITEM_SETUP_HW_LS2,
|
||||
ITEM_SETUP_HW_RS2,
|
||||
ITEM_SETUP_HW_LABEL_SWITCHES,
|
||||
ITEM_SETUP_HW_SA,
|
||||
ITEM_SETUP_HW_SB,
|
||||
ITEM_SETUP_HW_SC,
|
||||
ITEM_SETUP_HW_SD,
|
||||
ITEM_SETUP_HW_SE,
|
||||
ITEM_SETUP_HW_SF,
|
||||
ITEM_SETUP_HW_SG,
|
||||
ITEM_SETUP_HW_SH,
|
||||
ITEM_SETUP_HW_BLUETOOTH,
|
||||
// ITEM_SETUP_HW_UART3_MODE,
|
||||
ITEM_SETUP_HW_JITTER_FILTER,
|
||||
ITEM_SETUP_HW_BAT_CAL,
|
||||
ITEM_SETUP_HW_MAX
|
||||
};
|
||||
|
||||
#define HW_SETTINGS_COLUMN 150
|
||||
#define POTS_ROWS NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1, 0, 0, 0, 0
|
||||
#define SWITCHES_ROWS NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1
|
||||
#define BLUETOOTH_ROWS 1,
|
||||
#define SWITCH_TYPE_MAX(sw) ((MIXSRC_SF-MIXSRC_FIRST_SWITCH == sw || MIXSRC_SH-MIXSRC_FIRST_SWITCH == sw) ? SWITCH_2POS : SWITCH_3POS)
|
||||
|
||||
bool menuGeneralHardware(evt_t event)
|
||||
{
|
||||
MENU(STR_HARDWARE, RADIO_ICONS, menuTabGeneral, e_Hardware, ITEM_SETUP_HW_MAX, { 0, LABEL(Sticks), 0, 0, 0, 0, LABEL(Pots), POTS_ROWS, LABEL(Switches), SWITCHES_ROWS, BLUETOOTH_ROWS 0 });
|
||||
|
||||
uint8_t sub = menuVerticalPosition;
|
||||
|
||||
for (int i=0; i<NUM_BODY_LINES; ++i) {
|
||||
coord_t y = MENU_CONTENT_TOP + i*FH;
|
||||
int k = i + menuVerticalOffset;
|
||||
for (int j=0; j<=k; j++) {
|
||||
if (mstate_tab[j] == HIDDEN_ROW)
|
||||
k++;
|
||||
}
|
||||
LcdFlags attr = (sub == k ? ((s_editMode>0) ? BLINK|INVERS : INVERS) : 0);
|
||||
switch (k) {
|
||||
case ITEM_SETUP_HW_CALIBRATION:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, "Calibration", attr);
|
||||
if (attr && s_editMode>0) {
|
||||
pushMenu(menuGeneralCalib);
|
||||
}
|
||||
break;
|
||||
case ITEM_SETUP_HW_LABEL_STICKS:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_STICKS);
|
||||
break;
|
||||
case ITEM_SETUP_HW_STICK1:
|
||||
case ITEM_SETUP_HW_STICK2:
|
||||
case ITEM_SETUP_HW_STICK3:
|
||||
case ITEM_SETUP_HW_STICK4:
|
||||
{
|
||||
int idx = k - ITEM_SETUP_HW_STICK1;
|
||||
lcdDrawTextAtIndex(INDENT_WIDTH, y, STR_VSRCRAW, idx+1, 0);
|
||||
if (ZEXIST(g_eeGeneral.anaNames[idx]) || attr)
|
||||
editName(HW_SETTINGS_COLUMN, y, g_eeGeneral.anaNames[idx], LEN_ANA_NAME, event, attr);
|
||||
else
|
||||
lcdDrawTextAtIndex(HW_SETTINGS_COLUMN, y, STR_MMMINV, 0, 0);
|
||||
break;
|
||||
}
|
||||
case ITEM_SETUP_HW_LS:
|
||||
case ITEM_SETUP_HW_RS:
|
||||
case ITEM_SETUP_HW_LS2:
|
||||
case ITEM_SETUP_HW_RS2:
|
||||
{
|
||||
int idx = k - ITEM_SETUP_HW_LS;
|
||||
uint8_t mask = (0x01 << idx);
|
||||
lcdDrawTextAtIndex(INDENT_WIDTH, y, STR_VSRCRAW, NUM_STICKS+NUM_XPOTS+idx+1, menuHorizontalPosition < 0 ? attr : 0);
|
||||
if (ZEXIST(g_eeGeneral.anaNames[NUM_STICKS+NUM_XPOTS+2+idx]) || (attr && menuHorizontalPosition == 0))
|
||||
editName(HW_SETTINGS_COLUMN, y, g_eeGeneral.anaNames[NUM_STICKS+NUM_XPOTS+idx], LEN_ANA_NAME, event, attr && menuHorizontalPosition == 0);
|
||||
else
|
||||
lcdDrawTextAtIndex(HW_SETTINGS_COLUMN, y, STR_MMMINV, 0, 0);
|
||||
uint8_t potType = (g_eeGeneral.slidersConfig & mask) >> idx;
|
||||
potType = selectMenuItem(HW_SETTINGS_COLUMN+50, y, STR_SLIDERTYPES, potType, SLIDER_NONE, SLIDER_WITH_DETENT, menuHorizontalPosition == 1 ? attr : 0, event);
|
||||
g_eeGeneral.slidersConfig &= ~mask;
|
||||
g_eeGeneral.slidersConfig |= (potType << idx);
|
||||
break;
|
||||
}
|
||||
case ITEM_SETUP_HW_LABEL_POTS:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_POTS);
|
||||
break;
|
||||
case ITEM_SETUP_HW_POT1:
|
||||
case ITEM_SETUP_HW_POT2:
|
||||
case ITEM_SETUP_HW_POT3:
|
||||
{
|
||||
int idx = k - ITEM_SETUP_HW_POT1;
|
||||
uint8_t shift = (2*idx);
|
||||
uint8_t mask = (0x03 << shift);
|
||||
lcdDrawTextAtIndex(INDENT_WIDTH, y, STR_VSRCRAW, NUM_STICKS+idx+1, menuHorizontalPosition < 0 ? attr : 0);
|
||||
if (ZEXIST(g_eeGeneral.anaNames[NUM_STICKS+idx]) || (attr && menuHorizontalPosition == 0))
|
||||
editName(HW_SETTINGS_COLUMN, y, g_eeGeneral.anaNames[NUM_STICKS+idx], LEN_ANA_NAME, event, attr && menuHorizontalPosition == 0);
|
||||
else
|
||||
lcdDrawTextAtIndex(HW_SETTINGS_COLUMN, y, STR_MMMINV, 0, 0);
|
||||
uint8_t potType = (g_eeGeneral.potsConfig & mask) >> shift;
|
||||
potType = selectMenuItem(HW_SETTINGS_COLUMN+50, y, STR_POTTYPES, potType, POT_NONE, POT_WITHOUT_DETENT, menuHorizontalPosition == 1 ? attr : 0, event);
|
||||
g_eeGeneral.potsConfig &= ~mask;
|
||||
g_eeGeneral.potsConfig |= (potType << shift);
|
||||
break;
|
||||
}
|
||||
case ITEM_SETUP_HW_LABEL_SWITCHES:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_SWITCHES);
|
||||
break;
|
||||
case ITEM_SETUP_HW_SA:
|
||||
case ITEM_SETUP_HW_SB:
|
||||
case ITEM_SETUP_HW_SC:
|
||||
case ITEM_SETUP_HW_SD:
|
||||
case ITEM_SETUP_HW_SE:
|
||||
case ITEM_SETUP_HW_SF:
|
||||
case ITEM_SETUP_HW_SG:
|
||||
case ITEM_SETUP_HW_SH:
|
||||
{
|
||||
int index = k-ITEM_SETUP_HW_SA;
|
||||
int config = SWITCH_CONFIG(index);
|
||||
lcdDrawTextAtIndex(INDENT_WIDTH, y, STR_VSRCRAW, MIXSRC_FIRST_SWITCH-MIXSRC_Rud+index+1, menuHorizontalPosition < 0 ? attr : 0);
|
||||
if (ZEXIST(g_eeGeneral.switchNames[index]) || (attr && menuHorizontalPosition == 0))
|
||||
editName(HW_SETTINGS_COLUMN, y, g_eeGeneral.switchNames[index], LEN_SWITCH_NAME, event, menuHorizontalPosition == 0 ? attr : 0);
|
||||
else
|
||||
lcdDrawTextAtIndex(HW_SETTINGS_COLUMN, y, STR_MMMINV, 0, 0);
|
||||
config = selectMenuItem(HW_SETTINGS_COLUMN+50, y, STR_SWTYPES, config, SWITCH_NONE, SWITCH_TYPE_MAX(index), menuHorizontalPosition == 1 ? attr : 0, event);
|
||||
if (attr && checkIncDec_Ret) {
|
||||
swconfig_t mask = (swconfig_t)0x03 << (2*index);
|
||||
g_eeGeneral.switchConfig = (g_eeGeneral.switchConfig & ~mask) | ((swconfig_t(config) & 0x03) << (2*index));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ITEM_SETUP_HW_BLUETOOTH:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, "Bluetooth");
|
||||
drawCheckBox(HW_SETTINGS_COLUMN, y, g_eeGeneral.bluetoothEnable, menuHorizontalPosition == 0 ? attr : 0);
|
||||
if (attr && menuHorizontalPosition == 0) {
|
||||
g_eeGeneral.bluetoothEnable = checkIncDecGen(event, g_eeGeneral.bluetoothEnable, 0, 1);
|
||||
}
|
||||
editName(HW_SETTINGS_COLUMN+50, y, g_eeGeneral.bluetoothName, LEN_BLUETOOTH_NAME, event, menuHorizontalPosition == 1 ? attr : 0);
|
||||
break;
|
||||
|
||||
#if 0
|
||||
case ITEM_SETUP_HW_UART3_MODE:
|
||||
g_eeGeneral.serial2Mode = selectMenuItem(HW_SETTINGS_COLUMN, y, STR_UART3MODE, STR_UART3MODES, g_eeGeneral.serial2Mode, 0, UART_MODE_MAX, attr, event);
|
||||
if (attr && checkIncDec_Ret) {
|
||||
serial2Init(g_eeGeneral.serial2Mode, MODEL_TELEMETRY_PROTOCOL());
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
case ITEM_SETUP_HW_JITTER_FILTER:
|
||||
{
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_JITTER_FILTER);
|
||||
uint8_t b = 1-g_eeGeneral.jitterFilter;
|
||||
g_eeGeneral.jitterFilter = 1 - editCheckBox(b, HW_SETTINGS_COLUMN, y, attr, event);
|
||||
break;
|
||||
}
|
||||
|
||||
case ITEM_SETUP_HW_BAT_CAL:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_BATT_CALIB);
|
||||
lcdDrawNumber(HW_SETTINGS_COLUMN, y, getBatteryVoltage(), attr|LEFT|PREC2, 0, NULL, "V");
|
||||
if (attr) CHECK_INCDEC_GENVAR(event, g_eeGeneral.txVoltageCalibration, -127, 127);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,389 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "opentx.h"
|
||||
|
||||
#define REFRESH_FILES() do { reusableBuffer.sdmanager.offset = 65535; currentBitmapIndex = -1; } while (0)
|
||||
#define NODE_TYPE(fname) fname[SD_SCREEN_FILE_LENGTH+1]
|
||||
#define IS_DIRECTORY(fname) ((bool)(!NODE_TYPE(fname)))
|
||||
#define IS_FILE(fname) ((bool)(NODE_TYPE(fname)))
|
||||
|
||||
int currentBitmapIndex = 0;
|
||||
BitmapBuffer * currentBitmap = NULL;
|
||||
|
||||
bool menuGeneralSdManagerInfo(evt_t event)
|
||||
{
|
||||
SIMPLE_SUBMENU(STR_SD_INFO_TITLE, ICON_RADIO_SD_BROWSER, 1);
|
||||
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, 2*FH, STR_SD_TYPE);
|
||||
lcdDrawText(100, 2*FH, SD_IS_HC() ? STR_SDHC_CARD : STR_SD_CARD);
|
||||
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, 3*FH, STR_SD_SIZE);
|
||||
lcdDrawNumber(100, 3*FH, sdGetSize(), LEFT, 0, NULL, "M");
|
||||
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, 4*FH, STR_SD_SECTORS);
|
||||
#if defined(SD_GET_FREE_BLOCKNR)
|
||||
lcdDrawNumber(100, 4*FH, SD_GET_FREE_BLOCKNR()/1000, LEFT, 0, NULL, "/");
|
||||
lcdDrawNumber(150, 4*FH, sdGetNoSectors()/1000, LEFT);
|
||||
#else
|
||||
lcdDrawNumber(100, 4*FH, sdGetNoSectors()/1000, LEFT, 0, NULL, "k");
|
||||
#endif
|
||||
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, 5*FH, STR_SD_SPEED);
|
||||
lcdDrawNumber(100, 5*FH, SD_GET_SPEED()/1000, LEFT, 0, NULL, "kb/s");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool isFilenameGreater(bool isfile, const char * fn, const char * line)
|
||||
{
|
||||
return (isfile && IS_DIRECTORY(line)) || (isfile==IS_FILE(line) && strcasecmp(fn, line) > 0);
|
||||
}
|
||||
|
||||
inline bool isFilenameLower(bool isfile, const char * fn, const char * line)
|
||||
{
|
||||
return (!isfile && IS_FILE(line)) || (isfile==IS_FILE(line) && strcasecmp(fn, line) < 0);
|
||||
}
|
||||
|
||||
void getSelectionFullPath(char *lfn)
|
||||
{
|
||||
f_getcwd(lfn, _MAX_LFN);
|
||||
strcat(lfn, PSTR("/"));
|
||||
strcat(lfn, reusableBuffer.sdmanager.lines[menuVerticalPosition - menuVerticalOffset]);
|
||||
}
|
||||
|
||||
void onSdManagerMenu(const char * result)
|
||||
{
|
||||
TCHAR lfn[_MAX_LFN+1];
|
||||
|
||||
// TODO possible buffer overflows here!
|
||||
|
||||
uint8_t index = menuVerticalPosition-menuVerticalOffset;
|
||||
char *line = reusableBuffer.sdmanager.lines[index];
|
||||
|
||||
if (result == STR_SD_INFO) {
|
||||
pushMenu(menuGeneralSdManagerInfo);
|
||||
}
|
||||
else if (result == STR_SD_FORMAT) {
|
||||
POPUP_CONFIRMATION(STR_CONFIRM_FORMAT);
|
||||
}
|
||||
else if (result == STR_COPY_FILE) {
|
||||
clipboard.type = CLIPBOARD_TYPE_SD_FILE;
|
||||
f_getcwd(clipboard.data.sd.directory, CLIPBOARD_PATH_LEN);
|
||||
strncpy(clipboard.data.sd.filename, line, CLIPBOARD_PATH_LEN-1);
|
||||
}
|
||||
else if (result == STR_PASTE) {
|
||||
f_getcwd(lfn, _MAX_LFN);
|
||||
// if destination is dir, copy into that dir
|
||||
if (IS_DIRECTORY(line)) {
|
||||
strcat(lfn, PSTR("/"));
|
||||
strcat(lfn, line);
|
||||
}
|
||||
if (strcmp(clipboard.data.sd.directory, lfn)) { // prevent copying to the same directory
|
||||
POPUP_WARNING(sdCopyFile(clipboard.data.sd.filename, clipboard.data.sd.directory, clipboard.data.sd.filename, lfn));
|
||||
REFRESH_FILES();
|
||||
}
|
||||
}
|
||||
else if (result == STR_RENAME_FILE) {
|
||||
memcpy(reusableBuffer.sdmanager.originalName, line, sizeof(reusableBuffer.sdmanager.originalName));
|
||||
char * ext = getFileExtension(line, SD_SCREEN_FILE_LENGTH+1);
|
||||
if (ext) {
|
||||
// write spaces to allow a longer filename
|
||||
memset(ext, ' ', SD_SCREEN_FILE_LENGTH-LEN_FILE_EXTENSION-(ext-line));
|
||||
line[SD_SCREEN_FILE_LENGTH-LEN_FILE_EXTENSION] = '\0';
|
||||
}
|
||||
else {
|
||||
int len = strlen(line);
|
||||
memset(line + strlen(line), ' ', SD_SCREEN_FILE_LENGTH - len);
|
||||
line[SD_SCREEN_FILE_LENGTH] = '\0';
|
||||
}
|
||||
s_editMode = EDIT_MODIFY_STRING;
|
||||
editNameCursorPos = 0;
|
||||
}
|
||||
else if (result == STR_DELETE_FILE) {
|
||||
getSelectionFullPath(lfn);
|
||||
f_unlink(lfn);
|
||||
REFRESH_FILES();
|
||||
}
|
||||
else if (result == STR_PLAY_FILE) {
|
||||
getSelectionFullPath(lfn);
|
||||
audioQueue.stopAll();
|
||||
audioQueue.playFile(lfn, 0, ID_PLAY_FROM_SD_MANAGER);
|
||||
}
|
||||
#if 0
|
||||
else if (result == STR_ASSIGN_BITMAP) {
|
||||
strAppendFilename(g_model.header.bitmap, line, sizeof(g_model.header.bitmap));
|
||||
memcpy(modelHeaders[g_eeGeneral.currModel].bitmap, g_model.header.bitmap, sizeof(g_model.header.bitmap));
|
||||
storageDirty(EE_MODEL);
|
||||
}
|
||||
#endif
|
||||
else if (result == STR_VIEW_TEXT) {
|
||||
getSelectionFullPath(lfn);
|
||||
pushMenuTextView(lfn);
|
||||
}
|
||||
#if 0
|
||||
else if (result == STR_FLASH_BOOTLOADER) {
|
||||
getSelectionFullPath(lfn);
|
||||
flashBootloader(lfn);
|
||||
}
|
||||
#endif
|
||||
#if defined(LUA)
|
||||
else if (result == STR_EXECUTE_FILE) {
|
||||
getSelectionFullPath(lfn);
|
||||
luaExec(lfn);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool menuGeneralSdManager(evt_t _event)
|
||||
{
|
||||
if (warningResult) {
|
||||
warningResult = 0;
|
||||
displayPopup(STR_FORMATTING);
|
||||
closeLogs();
|
||||
audioQueue.stopSD();
|
||||
if (f_mkfs(0, 1, 0) == FR_OK) {
|
||||
f_chdir("/");
|
||||
REFRESH_FILES();
|
||||
}
|
||||
else {
|
||||
POPUP_WARNING(STR_SDCARD_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
evt_t event = (EVT_KEY_MASK(_event) == KEY_ENTER ? 0 : _event);
|
||||
SIMPLE_MENU(SD_IS_HC() ? STR_SDHC_CARD : STR_SD_CARD, RADIO_ICONS, menuTabGeneral, e_Sd, reusableBuffer.sdmanager.count);
|
||||
|
||||
int index = menuVerticalPosition-menuVerticalOffset;
|
||||
|
||||
switch(_event) {
|
||||
case EVT_ENTRY:
|
||||
f_chdir(ROOT_PATH);
|
||||
REFRESH_FILES();
|
||||
break;
|
||||
|
||||
#if 0
|
||||
case EVT_KEY_LONG(KEY_MENU):
|
||||
if (!READ_ONLY() && s_editMode == 0) {
|
||||
killEvents(_event);
|
||||
POPUP_MENU_ADD_ITEM(STR_SD_INFO);
|
||||
POPUP_MENU_ADD_ITEM(STR_SD_FORMAT);
|
||||
POPUP_MENU_START(onSdManagerMenu);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
case EVT_KEY_BREAK(KEY_EXIT):
|
||||
REFRESH_FILES();
|
||||
break;
|
||||
|
||||
case EVT_KEY_BREAK(KEY_ENTER):
|
||||
if (menuVerticalPosition < 0) {
|
||||
if (reusableBuffer.sdmanager.count > 0) {
|
||||
menuVerticalPosition = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
else if (s_editMode > 0) {
|
||||
break;
|
||||
}
|
||||
else {
|
||||
if (IS_DIRECTORY(reusableBuffer.sdmanager.lines[index])) {
|
||||
f_chdir(reusableBuffer.sdmanager.lines[index]);
|
||||
menuVerticalOffset = 0;
|
||||
menuVerticalPosition = 1;
|
||||
index = 1;
|
||||
REFRESH_FILES();
|
||||
killEvents(_event);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// no break
|
||||
|
||||
case EVT_KEY_LONG(KEY_ENTER):
|
||||
if (s_editMode == 0) {
|
||||
killEvents(_event);
|
||||
char * line = reusableBuffer.sdmanager.lines[index];
|
||||
char * ext = getFileExtension(line, SD_SCREEN_FILE_LENGTH+1);
|
||||
if (!strcmp(line, "..")) {
|
||||
break; // no menu for parent dir
|
||||
}
|
||||
if (ext) {
|
||||
if (!strcasecmp(ext, SOUNDS_EXT)) {
|
||||
POPUP_MENU_ADD_ITEM(STR_PLAY_FILE);
|
||||
}
|
||||
else if (!strcasecmp(ext, TEXT_EXT)) {
|
||||
POPUP_MENU_ADD_ITEM(STR_VIEW_TEXT);
|
||||
}
|
||||
#if defined(LUA)
|
||||
else if (!strcasecmp(ext, SCRIPTS_EXT)) {
|
||||
POPUP_MENU_ADD_ITEM(STR_EXECUTE_FILE);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
if (!READ_ONLY()) {
|
||||
if (IS_FILE(line))
|
||||
POPUP_MENU_ADD_ITEM(STR_COPY_FILE);
|
||||
if (clipboard.type == CLIPBOARD_TYPE_SD_FILE)
|
||||
POPUP_MENU_ADD_ITEM(STR_PASTE);
|
||||
POPUP_MENU_ADD_ITEM(STR_RENAME_FILE);
|
||||
if (IS_FILE(line))
|
||||
POPUP_MENU_ADD_ITEM(STR_DELETE_FILE);
|
||||
}
|
||||
POPUP_MENU_START(onSdManagerMenu);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (reusableBuffer.sdmanager.offset != menuVerticalOffset) {
|
||||
FILINFO fno;
|
||||
DIR dir;
|
||||
char *fn; /* This function is assuming non-Unicode cfg. */
|
||||
TCHAR lfn[_MAX_LFN + 1];
|
||||
fno.lfname = lfn;
|
||||
fno.lfsize = sizeof(lfn);
|
||||
|
||||
if (menuVerticalOffset == 0) {
|
||||
reusableBuffer.sdmanager.offset = 0;
|
||||
memset(reusableBuffer.sdmanager.lines, 0, sizeof(reusableBuffer.sdmanager.lines));
|
||||
}
|
||||
else if (menuVerticalOffset == reusableBuffer.sdmanager.count-NUM_BODY_LINES) {
|
||||
reusableBuffer.sdmanager.offset = menuVerticalOffset;
|
||||
memset(reusableBuffer.sdmanager.lines, 0, sizeof(reusableBuffer.sdmanager.lines));
|
||||
}
|
||||
else if (menuVerticalOffset > reusableBuffer.sdmanager.offset) {
|
||||
memmove(reusableBuffer.sdmanager.lines[0], reusableBuffer.sdmanager.lines[1], (NUM_BODY_LINES-1)*sizeof(reusableBuffer.sdmanager.lines[0]));
|
||||
memset(reusableBuffer.sdmanager.lines[NUM_BODY_LINES-1], 0xff, SD_SCREEN_FILE_LENGTH);
|
||||
NODE_TYPE(reusableBuffer.sdmanager.lines[NUM_BODY_LINES-1]) = 1;
|
||||
}
|
||||
else {
|
||||
memmove(reusableBuffer.sdmanager.lines[1], reusableBuffer.sdmanager.lines[0], (NUM_BODY_LINES-1)*sizeof(reusableBuffer.sdmanager.lines[0]));
|
||||
memset(reusableBuffer.sdmanager.lines[0], 0, sizeof(reusableBuffer.sdmanager.lines[0]));
|
||||
}
|
||||
|
||||
reusableBuffer.sdmanager.count = 0;
|
||||
|
||||
FRESULT res = f_opendir(&dir, "."); /* Open the directory */
|
||||
if (res == FR_OK) {
|
||||
for (;;) {
|
||||
res = f_readdir(&dir, &fno); /* Read a directory item */
|
||||
if (res != FR_OK || fno.fname[0] == 0) break; /* Break on error or end of dir */
|
||||
if (fno.fname[0] == '.' && fno.fname[1] == '\0') continue; /* Ignore dot entry */
|
||||
#if _USE_LFN
|
||||
fn = *fno.lfname ? fno.lfname : fno.fname;
|
||||
#else
|
||||
fn = fno.fname;
|
||||
#endif
|
||||
if (strlen(fn) > SD_SCREEN_FILE_LENGTH) continue;
|
||||
|
||||
reusableBuffer.sdmanager.count++;
|
||||
|
||||
bool isfile = !(fno.fattrib & AM_DIR);
|
||||
|
||||
if (menuVerticalOffset == 0) {
|
||||
for (int i=0; i<NUM_BODY_LINES; i++) {
|
||||
char *line = reusableBuffer.sdmanager.lines[i];
|
||||
if (line[0] == '\0' || isFilenameLower(isfile, fn, line)) {
|
||||
if (i < NUM_BODY_LINES-1) memmove(reusableBuffer.sdmanager.lines[i+1], line, sizeof(reusableBuffer.sdmanager.lines[i]) * (NUM_BODY_LINES-1-i));
|
||||
memset(line, 0, sizeof(reusableBuffer.sdmanager.lines[0]));
|
||||
strcpy(line, fn);
|
||||
NODE_TYPE(line) = isfile;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (reusableBuffer.sdmanager.offset == menuVerticalOffset) {
|
||||
for (int8_t i=NUM_BODY_LINES-1; i>=0; i--) {
|
||||
char *line = reusableBuffer.sdmanager.lines[i];
|
||||
if (line[0] == '\0' || isFilenameGreater(isfile, fn, line)) {
|
||||
if (i > 0) memmove(reusableBuffer.sdmanager.lines[0], reusableBuffer.sdmanager.lines[1], sizeof(reusableBuffer.sdmanager.lines[0]) * i);
|
||||
memset(line, 0, sizeof(reusableBuffer.sdmanager.lines[0]));
|
||||
strcpy(line, fn);
|
||||
NODE_TYPE(line) = isfile;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (menuVerticalOffset > reusableBuffer.sdmanager.offset) {
|
||||
if (isFilenameGreater(isfile, fn, reusableBuffer.sdmanager.lines[NUM_BODY_LINES-2]) && isFilenameLower(isfile, fn, reusableBuffer.sdmanager.lines[NUM_BODY_LINES-1])) {
|
||||
memset(reusableBuffer.sdmanager.lines[NUM_BODY_LINES-1], 0, sizeof(reusableBuffer.sdmanager.lines[0]));
|
||||
strcpy(reusableBuffer.sdmanager.lines[NUM_BODY_LINES-1], fn);
|
||||
NODE_TYPE(reusableBuffer.sdmanager.lines[NUM_BODY_LINES-1]) = isfile;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (isFilenameLower(isfile, fn, reusableBuffer.sdmanager.lines[1]) && isFilenameGreater(isfile, fn, reusableBuffer.sdmanager.lines[0])) {
|
||||
memset(reusableBuffer.sdmanager.lines[0], 0, sizeof(reusableBuffer.sdmanager.lines[0]));
|
||||
strcpy(reusableBuffer.sdmanager.lines[0], fn);
|
||||
NODE_TYPE(reusableBuffer.sdmanager.lines[0]) = isfile;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reusableBuffer.sdmanager.offset = menuVerticalOffset;
|
||||
|
||||
for (int i=0; i<NUM_BODY_LINES; i++) {
|
||||
coord_t y = MENU_CONTENT_TOP + i*FH;
|
||||
LcdFlags attr = (index == i ? INVERS : 0);
|
||||
if (reusableBuffer.sdmanager.lines[i][0]) {
|
||||
if (IS_DIRECTORY(reusableBuffer.sdmanager.lines[i])) {
|
||||
char s[sizeof(reusableBuffer.sdmanager.lines[0])+2];
|
||||
char * ptr = s;
|
||||
*ptr++ = '[';
|
||||
ptr = strAppend(ptr, reusableBuffer.sdmanager.lines[i]);
|
||||
*ptr++ = ']';
|
||||
*ptr = '\0';
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, s, attr);
|
||||
}
|
||||
else {
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, reusableBuffer.sdmanager.lines[i], attr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char * ext = getFileExtension(reusableBuffer.sdmanager.lines[index], SD_SCREEN_FILE_LENGTH+1);
|
||||
if (ext && (!strcasecmp(ext, BMP_EXT) || !strcasecmp(ext, PNG_EXT) || !strcasecmp(ext, JPG_EXT))) {
|
||||
if (currentBitmapIndex != menuVerticalPosition) {
|
||||
currentBitmapIndex = menuVerticalPosition;
|
||||
delete currentBitmap;
|
||||
currentBitmap = BitmapBuffer::load(reusableBuffer.sdmanager.lines[index]);
|
||||
}
|
||||
if (currentBitmap) {
|
||||
uint16_t height = currentBitmap->getHeight();
|
||||
uint16_t width = currentBitmap->getWidth();
|
||||
uint16_t startx = 0;
|
||||
uint16_t starty = 0;
|
||||
if (height > MENU_BODY_HEIGHT-10) {
|
||||
starty = (height - MENU_BODY_HEIGHT - 10) / 2;
|
||||
height = MENU_BODY_HEIGHT - 10;
|
||||
}
|
||||
if (width > LCD_W/2) {
|
||||
startx = (width - LCD_W/2) / 2;
|
||||
width = LCD_W/2;
|
||||
}
|
||||
lcd->drawBitmap(LCD_W / 2 - 20 + LCD_W/4 - width/2, MENU_BODY_TOP + MENU_BODY_HEIGHT/2 - height/2, currentBitmap, startx, starty, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,524 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define LANGUAGE_PACKS_DEFINITION
|
||||
|
||||
#include "opentx.h"
|
||||
|
||||
#define RADIO_SETUP_2ND_COLUMN 220
|
||||
|
||||
int8_t editSlider(coord_t x, coord_t y, evt_t event, int8_t value, int8_t min, int8_t max, LcdFlags attr)
|
||||
{
|
||||
drawHorizontalSlider(x, y, 100, value, min, max, 0, OPTION_SLIDER_DBL_COLOR|attr);
|
||||
return selectMenuItem(x, y, NULL, value, min, max, attr, event);
|
||||
}
|
||||
|
||||
#define SLIDER_5POS(y, val, event, attr) val = editSlider(RADIO_SETUP_2ND_COLUMN, y, event, val, -2, +2, attr)
|
||||
|
||||
#if defined(SPLASH) && !defined(FSPLASH)
|
||||
#define CASE_SPLASH_PARAM(x) x,
|
||||
#else
|
||||
#define CASE_SPLASH_PARAM(x)
|
||||
#endif
|
||||
|
||||
enum menuGeneralSetupItems {
|
||||
ITEM_SETUP_DATE,
|
||||
ITEM_SETUP_TIME,
|
||||
ITEM_SETUP_BATT_RANGE,
|
||||
ITEM_SETUP_SOUND_LABEL,
|
||||
ITEM_SETUP_BEEP_MODE,
|
||||
ITEM_SETUP_GENERAL_VOLUME,
|
||||
ITEM_SETUP_BEEP_VOLUME,
|
||||
ITEM_SETUP_BEEP_LENGTH,
|
||||
ITEM_SETUP_SPEAKER_PITCH,
|
||||
ITEM_SETUP_WAV_VOLUME,
|
||||
ITEM_SETUP_BACKGROUND_VOLUME,
|
||||
CASE_VARIO(ITEM_SETUP_VARIO_LABEL)
|
||||
CASE_VARIO(ITEM_SETUP_VARIO_VOLUME)
|
||||
CASE_VARIO(ITEM_SETUP_VARIO_PITCH)
|
||||
CASE_VARIO(ITEM_SETUP_VARIO_RANGE)
|
||||
CASE_VARIO(ITEM_SETUP_VARIO_REPEAT)
|
||||
CASE_HAPTIC(ITEM_SETUP_HAPTIC_LABEL)
|
||||
CASE_HAPTIC(ITEM_SETUP_HAPTIC_MODE)
|
||||
CASE_HAPTIC(ITEM_SETUP_HAPTIC_LENGTH)
|
||||
CASE_HAPTIC(ITEM_SETUP_HAPTIC_STRENGTH)
|
||||
// ITEM_SETUP_CONTRAST,
|
||||
ITEM_SETUP_ALARMS_LABEL,
|
||||
ITEM_SETUP_BATTERY_WARNING,
|
||||
ITEM_SETUP_INACTIVITY_ALARM,
|
||||
// ITEM_SETUP_MEMORY_WARNING,
|
||||
ITEM_SETUP_ALARM_WARNING,
|
||||
ITEM_SETUP_BACKLIGHT_LABEL,
|
||||
ITEM_SETUP_BACKLIGHT_MODE,
|
||||
ITEM_SETUP_BACKLIGHT_DELAY,
|
||||
ITEM_SETUP_BRIGHTNESS,
|
||||
ITEM_SETUP_DIM_LEVEL,
|
||||
ITEM_SETUP_FLASH_BEEP,
|
||||
// CASE_SPLASH_PARAM(ITEM_SETUP_DISABLE_SPLASH)
|
||||
CASE_GPS(ITEM_SETUP_LABEL_GPS)
|
||||
CASE_GPS(ITEM_SETUP_TIMEZONE)
|
||||
CASE_GPS(ITEM_SETUP_GPSFORMAT)
|
||||
CASE_PXX(ITEM_SETUP_COUNTRYCODE)
|
||||
ITEM_SETUP_LANGUAGE,
|
||||
ITEM_SETUP_IMPERIAL,
|
||||
// IF_FAI_CHOICE(ITEM_SETUP_FAI)
|
||||
// CASE_MAVLINK(ITEM_MAVLINK_BAUD)
|
||||
ITEM_SETUP_SWITCHES_DELAY,
|
||||
ITEM_SETUP_RX_CHANNEL_ORD,
|
||||
ITEM_SETUP_STICK_MODE,
|
||||
ITEM_SETUP_MAX
|
||||
};
|
||||
|
||||
bool menuGeneralSetup(evt_t event)
|
||||
{
|
||||
#if defined(RTCLOCK)
|
||||
struct gtm t;
|
||||
gettime(&t);
|
||||
|
||||
if ((menuVerticalPosition==ITEM_SETUP_DATE || menuVerticalPosition==ITEM_SETUP_TIME) &&
|
||||
(s_editMode>0) &&
|
||||
(event==EVT_KEY_FIRST(KEY_ENTER) || event==EVT_KEY_BREAK(KEY_ENTER) || event==EVT_KEY_LONG(KEY_ENTER) || event==EVT_KEY_FIRST(KEY_EXIT))) {
|
||||
// set the date and time into RTC chip
|
||||
rtcSetTime(&t);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(FAI_CHOICE)
|
||||
if (warningResult) {
|
||||
warningResult = 0;
|
||||
g_eeGeneral.fai = true;
|
||||
storageDirty(EE_GENERAL);
|
||||
}
|
||||
#endif
|
||||
|
||||
MENU(STR_MENURADIOSETUP, RADIO_ICONS, menuTabGeneral, e_Setup, ITEM_SETUP_MAX, {
|
||||
2|NAVIGATION_LINE_BY_LINE, 2|NAVIGATION_LINE_BY_LINE, 1|NAVIGATION_LINE_BY_LINE,
|
||||
LABEL(SOUND), 0, 0, 0, 0, 0, 0, 0,
|
||||
CASE_VARIO(LABEL(VARIO)) CASE_VARIO(0) CASE_VARIO(0) CASE_VARIO(0) CASE_VARIO(0)
|
||||
CASE_HAPTIC(LABEL(HAPTIC)) CASE_HAPTIC(0) CASE_HAPTIC(0) CASE_HAPTIC(0)
|
||||
LABEL(ALARMS), 0, 0, 0,
|
||||
LABEL(BACKLIGHT), 0, 0, 0, 0, 0,
|
||||
CASE_GPS(LABEL(GPS)) CASE_GPS(0) CASE_GPS(0) CASE_PXX(0) 0, 0, 0, 0, 0, 0, 1/*to force edit mode*/ });
|
||||
|
||||
if (event == EVT_ENTRY) {
|
||||
reusableBuffer.generalSettings.stickMode = g_eeGeneral.stickMode;
|
||||
}
|
||||
|
||||
int sub = menuVerticalPosition;
|
||||
|
||||
for (int i=0; i<NUM_BODY_LINES; i++) {
|
||||
coord_t y = MENU_CONTENT_TOP + i*FH;
|
||||
int k = i + menuVerticalOffset;
|
||||
LcdFlags blink = ((s_editMode>0) ? BLINK|INVERS : INVERS);
|
||||
LcdFlags attr = (sub == k ? blink : 0);
|
||||
|
||||
switch(k) {
|
||||
case ITEM_SETUP_DATE:
|
||||
{
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_DATE);
|
||||
LcdFlags flags = 0;
|
||||
if (attr && menuHorizontalPosition < 0) {
|
||||
flags |= INVERS;
|
||||
}
|
||||
for (uint8_t j=0; j<3; j++) {
|
||||
uint8_t rowattr = (menuHorizontalPosition==j ? attr : 0);
|
||||
switch (j) {
|
||||
case 0:
|
||||
lcdDrawNumber(RADIO_SETUP_2ND_COLUMN, y, t.tm_year+1900, flags|rowattr);
|
||||
if (rowattr && s_editMode>0) t.tm_year = checkIncDec(event, t.tm_year, 112, 200, 0);
|
||||
lcdDrawText(lcdNextPos+3, y, "-", flags);
|
||||
break;
|
||||
case 1:
|
||||
lcdDrawNumber(lcdNextPos+3, y, t.tm_mon+1, flags|rowattr|LEADING0, 2);
|
||||
if (rowattr && s_editMode>0) t.tm_mon = checkIncDec(event, t.tm_mon, 0, 11, 0);
|
||||
lcdDrawText(lcdNextPos+3, y, "-", flags);
|
||||
break;
|
||||
case 2:
|
||||
{
|
||||
int16_t year = 1900 + t.tm_year;
|
||||
int8_t dlim = (((((year%4==0) && (year%100!=0)) || (year%400==0)) && (t.tm_mon==1)) ? 1 : 0);
|
||||
static const pm_uint8_t dmon[] PROGMEM = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
|
||||
dlim += pgm_read_byte(&dmon[t.tm_mon]);
|
||||
lcdDrawNumber(lcdNextPos+3, y, t.tm_mday, flags|rowattr|LEADING0, 2);
|
||||
if (rowattr && s_editMode>0) t.tm_mday = checkIncDec(event, t.tm_mday, 1, dlim, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (attr && checkIncDec_Ret) {
|
||||
g_rtcTime = gmktime(&t); // update local timestamp and get wday calculated
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ITEM_SETUP_TIME:
|
||||
{
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_TIME);
|
||||
LcdFlags flags = 0;
|
||||
if (attr && menuHorizontalPosition < 0) {
|
||||
flags |= INVERS;
|
||||
}
|
||||
for (uint8_t j=0; j<3; j++) {
|
||||
uint8_t rowattr = (menuHorizontalPosition==j ? attr : 0);
|
||||
switch (j) {
|
||||
case 0:
|
||||
if (rowattr && s_editMode>0) t.tm_hour = checkIncDec(event, t.tm_hour, 0, 23, 0);
|
||||
lcdDrawNumber(RADIO_SETUP_2ND_COLUMN, y, t.tm_hour, flags|rowattr|LEADING0, 2);
|
||||
lcdDrawText(lcdNextPos+3, y, ":", flags);
|
||||
break;
|
||||
case 1:
|
||||
if (rowattr && s_editMode>0) t.tm_min = checkIncDec(event, t.tm_min, 0, 59, 0);
|
||||
lcdDrawNumber(lcdNextPos+3, y, t.tm_min, flags|rowattr|LEADING0, 2);
|
||||
lcdDrawText(lcdNextPos+3, y, ":", flags);
|
||||
break;
|
||||
case 2:
|
||||
if (rowattr && s_editMode>0) t.tm_sec = checkIncDec(event, t.tm_sec, 0, 59, 0);
|
||||
lcdDrawNumber(lcdNextPos+3, y, t.tm_sec, flags|rowattr|LEADING0, 2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (attr && checkIncDec_Ret)
|
||||
g_rtcTime = gmktime(&t); // update local timestamp and get wday calculated
|
||||
break;
|
||||
}
|
||||
|
||||
case ITEM_SETUP_BATT_RANGE:
|
||||
{
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_BATTERY_RANGE);
|
||||
LcdFlags flags = 0;
|
||||
if (attr && menuHorizontalPosition < 0) {
|
||||
flags |= INVERS;
|
||||
}
|
||||
lcdDrawNumber(RADIO_SETUP_2ND_COLUMN, y, 90+g_eeGeneral.vBatMin, flags|(menuHorizontalPosition==0 ? attr : 0)|PREC1|LEFT);
|
||||
lcdDrawText(lcdNextPos+3, y, "-", flags);
|
||||
lcdDrawNumber(lcdNextPos+3, y, 120+g_eeGeneral.vBatMax, flags|(menuHorizontalPosition>0 ? attr : 0)|PREC1|LEFT);
|
||||
lcdDrawText(lcdNextPos+1, y, "V", flags);
|
||||
if (attr && s_editMode>0) {
|
||||
if (menuHorizontalPosition==0)
|
||||
CHECK_INCDEC_GENVAR(event, g_eeGeneral.vBatMin, -50, g_eeGeneral.vBatMax+29); // min=4.0V
|
||||
else
|
||||
CHECK_INCDEC_GENVAR(event, g_eeGeneral.vBatMax, g_eeGeneral.vBatMin-29, +40); // max=16.0V
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ITEM_SETUP_SOUND_LABEL:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_SOUND_LABEL);
|
||||
break;
|
||||
|
||||
case ITEM_SETUP_BEEP_MODE:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_SPEAKER);
|
||||
g_eeGeneral.beepMode = selectMenuItem(RADIO_SETUP_2ND_COLUMN, y, STR_VBEEPMODE, g_eeGeneral.beepMode, -2, 1, attr, event);
|
||||
#if defined(FRSKY)
|
||||
if (attr && checkIncDec_Ret) frskySendAlarms();
|
||||
#endif
|
||||
break;
|
||||
|
||||
case ITEM_SETUP_GENERAL_VOLUME:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_SPEAKER_VOLUME);
|
||||
g_eeGeneral.speakerVolume = editSlider(RADIO_SETUP_2ND_COLUMN, y, event, g_eeGeneral.speakerVolume, -VOLUME_LEVEL_DEF, VOLUME_LEVEL_MAX-VOLUME_LEVEL_DEF, attr);
|
||||
break;
|
||||
|
||||
case ITEM_SETUP_BEEP_VOLUME:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_BEEP_VOLUME);
|
||||
g_eeGeneral.beepVolume = editSlider(RADIO_SETUP_2ND_COLUMN, y, event, g_eeGeneral.beepVolume, -2, +2, attr);
|
||||
break;
|
||||
case ITEM_SETUP_WAV_VOLUME:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_WAV_VOLUME);
|
||||
g_eeGeneral.wavVolume = editSlider(RADIO_SETUP_2ND_COLUMN, y, event, g_eeGeneral.wavVolume, -2, +2, attr);
|
||||
break;
|
||||
case ITEM_SETUP_BACKGROUND_VOLUME:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_BG_VOLUME);
|
||||
g_eeGeneral.backgroundVolume = editSlider(RADIO_SETUP_2ND_COLUMN, y, event, g_eeGeneral.backgroundVolume, -2, +2, attr);
|
||||
break;
|
||||
|
||||
case ITEM_SETUP_BEEP_LENGTH:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_BEEP_LENGTH);
|
||||
SLIDER_5POS(y, g_eeGeneral.beepLength, event, attr);
|
||||
break;
|
||||
|
||||
case ITEM_SETUP_SPEAKER_PITCH:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_SPKRPITCH);
|
||||
lcdDrawNumber(RADIO_SETUP_2ND_COLUMN, y, g_eeGeneral.speakerPitch*15, attr|LEFT, 0, "+", "Hz");
|
||||
if (attr) {
|
||||
CHECK_INCDEC_GENVAR(event, g_eeGeneral.speakerPitch, 0, 20);
|
||||
}
|
||||
break;
|
||||
|
||||
#if defined(VARIO)
|
||||
case ITEM_SETUP_VARIO_LABEL:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_VARIO);
|
||||
break;
|
||||
case ITEM_SETUP_VARIO_VOLUME:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, TR_SPEAKER_VOLUME);
|
||||
g_eeGeneral.varioVolume = editSlider(RADIO_SETUP_2ND_COLUMN, y, event, g_eeGeneral.varioVolume, -2, +2, attr);
|
||||
break;
|
||||
case ITEM_SETUP_VARIO_PITCH:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_PITCH_AT_ZERO);
|
||||
lcdDrawNumber(RADIO_SETUP_2ND_COLUMN, y, VARIO_FREQUENCY_ZERO+(g_eeGeneral.varioPitch*10), attr|LEFT, 0, NULL, "Hz");
|
||||
if (attr) CHECK_INCDEC_GENVAR(event, g_eeGeneral.varioPitch, -40, 40);
|
||||
break;
|
||||
case ITEM_SETUP_VARIO_RANGE:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_PITCH_AT_MAX);
|
||||
lcdDrawNumber(RADIO_SETUP_2ND_COLUMN, y, VARIO_FREQUENCY_ZERO+(g_eeGeneral.varioPitch*10)+VARIO_FREQUENCY_RANGE+(g_eeGeneral.varioRange*10), attr|LEFT, 0, NULL, "Hz");
|
||||
if (attr) CHECK_INCDEC_GENVAR(event, g_eeGeneral.varioRange, -80, 80);
|
||||
break;
|
||||
case ITEM_SETUP_VARIO_REPEAT:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_REPEAT_AT_ZERO);
|
||||
lcdDrawNumber(RADIO_SETUP_2ND_COLUMN, y, VARIO_REPEAT_ZERO+(g_eeGeneral.varioRepeat*10), attr|LEFT, 0, NULL, STR_MS);
|
||||
if (attr) CHECK_INCDEC_GENVAR(event, g_eeGeneral.varioRepeat, -30, 50);
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if defined(HAPTIC)
|
||||
case ITEM_SETUP_HAPTIC_LABEL:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_HAPTIC_LABEL);
|
||||
break;
|
||||
|
||||
case ITEM_SETUP_HAPTIC_MODE:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_MODE);
|
||||
g_eeGeneral.hapticMode = selectMenuItem(RADIO_SETUP_2ND_COLUMN, y, STR_VBEEPMODE, g_eeGeneral.hapticMode, -2, 1, attr, event);
|
||||
break;
|
||||
|
||||
case ITEM_SETUP_HAPTIC_LENGTH:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_LENGTH);
|
||||
SLIDER_5POS(y, g_eeGeneral.hapticLength, event, attr);
|
||||
break;
|
||||
|
||||
case ITEM_SETUP_HAPTIC_STRENGTH:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_HAPTICSTRENGTH);
|
||||
SLIDER_5POS(y, g_eeGeneral.hapticStrength, event, attr);
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
case ITEM_SETUP_CONTRAST:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_CONTRAST);
|
||||
lcdDrawNumber(RADIO_SETUP_2ND_COLUMN, y, g_eeGeneral.contrast, attr|LEFT);
|
||||
if (attr) {
|
||||
CHECK_INCDEC_GENVAR(event, g_eeGeneral.contrast, CONTRAST_MIN, CONTRAST_MAX);
|
||||
lcdSetContrast();
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
case ITEM_SETUP_ALARMS_LABEL:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_ALARMS_LABEL);
|
||||
break;
|
||||
|
||||
case ITEM_SETUP_BATTERY_WARNING:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_BATTERYWARNING);
|
||||
putsValueWithUnit(RADIO_SETUP_2ND_COLUMN, y, g_eeGeneral.vBatWarn, UNIT_VOLTS, attr|PREC1|LEFT);
|
||||
if (attr) CHECK_INCDEC_GENVAR(event, g_eeGeneral.vBatWarn, 40, 120); //4-12V
|
||||
break;
|
||||
|
||||
#if 0
|
||||
case ITEM_SETUP_MEMORY_WARNING:
|
||||
{
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_MEMORYWARNING);
|
||||
uint8_t b = 1-g_eeGeneral.disableMemoryWarning;
|
||||
g_eeGeneral.disableMemoryWarning = 1 - editCheckBox(b, RADIO_SETUP_2ND_COLUMN, y, attr, event);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
case ITEM_SETUP_ALARM_WARNING:
|
||||
{
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_ALARMWARNING);
|
||||
uint8_t b = 1-g_eeGeneral.disableAlarmWarning;
|
||||
g_eeGeneral.disableAlarmWarning = 1 - editCheckBox(b, RADIO_SETUP_2ND_COLUMN, y, attr, event);
|
||||
break;
|
||||
}
|
||||
|
||||
#if defined(PCBSKY9X)
|
||||
case ITEM_SETUP_CAPACITY_WARNING:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_CAPAWARNING);
|
||||
putsValueWithUnit(RADIO_SETUP_2ND_COLUMN, y, g_eeGeneral.mAhWarn*50, UNIT_MAH, attr|LEFT) ;
|
||||
if (attr) CHECK_INCDEC_GENVAR(event, g_eeGeneral.mAhWarn, 0, 100);
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if defined(PCBSKY9X)
|
||||
case ITEM_SETUP_TEMPERATURE_WARNING:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_TEMPWARNING);
|
||||
putsValueWithUnit(RADIO_SETUP_2ND_COLUMN, y, g_eeGeneral.temperatureWarn, UNIT_TEMPERATURE, attr|LEFT) ;
|
||||
if (attr) CHECK_INCDEC_GENVAR(event, g_eeGeneral.temperatureWarn, 0, 120); // 0 means no alarm
|
||||
break;
|
||||
#endif
|
||||
|
||||
case ITEM_SETUP_INACTIVITY_ALARM:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y,STR_INACTIVITYALARM);
|
||||
lcdDrawNumber(RADIO_SETUP_2ND_COLUMN, y, g_eeGeneral.inactivityTimer, attr|LEFT, 0, NULL, "m");
|
||||
if (attr) g_eeGeneral.inactivityTimer = checkIncDec(event, g_eeGeneral.inactivityTimer, 0, 250, EE_GENERAL); //0..250minutes
|
||||
break;
|
||||
|
||||
case ITEM_SETUP_BACKLIGHT_LABEL:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_BACKLIGHT_LABEL);
|
||||
break;
|
||||
|
||||
case ITEM_SETUP_BACKLIGHT_MODE:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_MODE);
|
||||
g_eeGeneral.backlightMode = selectMenuItem(RADIO_SETUP_2ND_COLUMN, y, STR_VBLMODE, g_eeGeneral.backlightMode, e_backlight_mode_off, e_backlight_mode_on, attr, event);
|
||||
break;
|
||||
|
||||
case ITEM_SETUP_FLASH_BEEP:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_ALARM);
|
||||
g_eeGeneral.alarmsFlash = editCheckBox(g_eeGeneral.alarmsFlash, RADIO_SETUP_2ND_COLUMN, y, attr, event ) ;
|
||||
break;
|
||||
|
||||
case ITEM_SETUP_BACKLIGHT_DELAY:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_BLDELAY);
|
||||
lcdDrawNumber(RADIO_SETUP_2ND_COLUMN, y, g_eeGeneral.lightAutoOff*5, attr|LEFT, 0, NULL, "s");
|
||||
if (attr) CHECK_INCDEC_GENVAR(event, g_eeGeneral.lightAutoOff, 0, 600/5);
|
||||
break;
|
||||
|
||||
case ITEM_SETUP_BRIGHTNESS:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_BLONBRIGHTNESS);
|
||||
lcdDrawNumber(RADIO_SETUP_2ND_COLUMN, y, 100-g_eeGeneral.backlightBright, attr|LEFT) ;
|
||||
if (attr) {
|
||||
uint8_t b = 100 - g_eeGeneral.backlightBright;
|
||||
CHECK_INCDEC_GENVAR(event, b, 5, 100);
|
||||
g_eeGeneral.backlightBright = 100 - b;
|
||||
}
|
||||
break;
|
||||
|
||||
case ITEM_SETUP_DIM_LEVEL:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_BLOFFBRIGHTNESS);
|
||||
lcdDrawNumber(RADIO_SETUP_2ND_COLUMN, y, g_eeGeneral.blOffBright, attr|LEFT) ;
|
||||
if (attr) CHECK_INCDEC_GENVAR(event, g_eeGeneral.blOffBright, 5, 100);
|
||||
break;
|
||||
|
||||
#if 0
|
||||
case ITEM_SETUP_DISABLE_SPLASH:
|
||||
{
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_SPLASHSCREEN);
|
||||
if (SPLASH_NEEDED()) {
|
||||
lcdDrawNumber(RADIO_SETUP_2ND_COLUMN, y, SPLASH_TIMEOUT/100, attr|LEFT, 0, NULL, "s");
|
||||
}
|
||||
else {
|
||||
lcdDrawTextAtIndex(RADIO_SETUP_2ND_COLUMN, y, STR_MMMINV, 0, attr); // TODO define
|
||||
}
|
||||
if (attr) g_eeGeneral.splashMode = -checkIncDecGen(event, -g_eeGeneral.splashMode, -3, 4);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
case ITEM_SETUP_LABEL_GPS:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_GPS);
|
||||
break;
|
||||
|
||||
case ITEM_SETUP_TIMEZONE:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_TIMEZONE);
|
||||
lcdDrawNumber(RADIO_SETUP_2ND_COLUMN, y, g_eeGeneral.timezone, attr|LEFT);
|
||||
if (attr) CHECK_INCDEC_GENVAR(event, g_eeGeneral.timezone, -12, 12);
|
||||
break;
|
||||
|
||||
case ITEM_SETUP_GPSFORMAT:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_GPSCOORD);
|
||||
g_eeGeneral.gpsFormat = selectMenuItem(RADIO_SETUP_2ND_COLUMN, y, STR_GPSFORMAT, g_eeGeneral.gpsFormat, 0, 1, attr, event);
|
||||
break;
|
||||
|
||||
case ITEM_SETUP_COUNTRYCODE:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_COUNTRYCODE);
|
||||
g_eeGeneral.countryCode = selectMenuItem(RADIO_SETUP_2ND_COLUMN, y, STR_COUNTRYCODES, g_eeGeneral.countryCode, 0, 2, attr, event);
|
||||
break;
|
||||
|
||||
case ITEM_SETUP_LANGUAGE:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_VOICELANG);
|
||||
lcdDrawText(RADIO_SETUP_2ND_COLUMN, y, currentLanguagePack->name, attr);
|
||||
if (attr) {
|
||||
currentLanguagePackIdx = checkIncDec(event, currentLanguagePackIdx, 0, DIM(languagePacks)-2, EE_GENERAL);
|
||||
if (checkIncDec_Ret) {
|
||||
currentLanguagePack = languagePacks[currentLanguagePackIdx];
|
||||
strncpy(g_eeGeneral.ttsLanguage, currentLanguagePack->id, 2);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ITEM_SETUP_IMPERIAL:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_UNITSSYSTEM);
|
||||
g_eeGeneral.imperial = selectMenuItem(RADIO_SETUP_2ND_COLUMN, y, STR_VUNITSSYSTEM, g_eeGeneral.imperial, 0, 1, attr, event);
|
||||
break;
|
||||
|
||||
#if 0
|
||||
case ITEM_SETUP_FAI:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, PSTR("FAI Mode"));
|
||||
g_eeGeneral.fai = editCheckBox(g_eeGeneral.fai, RADIO_SETUP_2ND_COLUMN, y, attr, event);
|
||||
if (attr && checkIncDec_Ret) {
|
||||
if (g_eeGeneral.fai)
|
||||
POPUP_WARNING(PSTR("FAI\001mode blocked!"));
|
||||
else
|
||||
POPUP_CONFIRMATION(PSTR("FAI mode?"));
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if defined(MAVLINK)
|
||||
case ITEM_MAVLINK_BAUD:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_MAVLINK_BAUD_LABEL);
|
||||
g_eeGeneral.mavbaud = selectMenuItem(RADIO_SETUP_2ND_COLUMN, y, STR_MAVLINK_BAUDS, g_eeGeneral.mavbaud, 0, 7, attr, event);
|
||||
break;
|
||||
#endif
|
||||
|
||||
case ITEM_SETUP_SWITCHES_DELAY:
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_SWITCHES_DELAY);
|
||||
lcdDrawNumber(RADIO_SETUP_2ND_COLUMN, y, 10*SWITCHES_DELAY(), attr|LEFT, 0, NULL, STR_MS);
|
||||
if (attr) CHECK_INCDEC_GENVAR(event, g_eeGeneral.switchesDelay, -15, +15);
|
||||
break;
|
||||
|
||||
case ITEM_SETUP_RX_CHANNEL_ORD:
|
||||
{
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, STR_RXCHANNELORD); // RAET->AETR
|
||||
char s[5];
|
||||
for (uint8_t i=0; i<4; i++) {
|
||||
s[i] = STR_RETA123[channel_order(i+1)];
|
||||
}
|
||||
s[4] = '\0';
|
||||
lcdDrawText(RADIO_SETUP_2ND_COLUMN, y, s, attr);
|
||||
if (attr) CHECK_INCDEC_GENVAR(event, g_eeGeneral.templateSetup, 0, 23);
|
||||
break;
|
||||
}
|
||||
|
||||
case ITEM_SETUP_STICK_MODE:
|
||||
{
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, NO_INDENT(STR_MODE));
|
||||
char s[2] = " ";
|
||||
s[0] = '1'+reusableBuffer.generalSettings.stickMode;
|
||||
lcdDrawText(RADIO_SETUP_2ND_COLUMN, y, s, attr);
|
||||
for (uint8_t i=0; i<4; i++) {
|
||||
putsMixerSource(RADIO_SETUP_2ND_COLUMN + 40 + 50*i, y, MIXSRC_Rud + pgm_read_byte(modn12x3 + 4*reusableBuffer.generalSettings.stickMode + i));
|
||||
}
|
||||
if (attr && s_editMode>0) {
|
||||
CHECK_INCDEC_GENVAR(event, reusableBuffer.generalSettings.stickMode, 0, 3);
|
||||
}
|
||||
else if (reusableBuffer.generalSettings.stickMode != g_eeGeneral.stickMode) {
|
||||
pausePulses();
|
||||
g_eeGeneral.stickMode = reusableBuffer.generalSettings.stickMode;
|
||||
checkTHR();
|
||||
resumePulses();
|
||||
clearKeyEvents();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "../../opentx.h"
|
||||
|
||||
#define TRAINER_COLUMN_WIDTH 60
|
||||
#define TRAINER_COLUMN_1 MENUS_MARGIN_LEFT+80
|
||||
#define TRAINER_COLUMN_2 TRAINER_COLUMN_1+TRAINER_COLUMN_WIDTH
|
||||
#define TRAINER_COLUMN_3 TRAINER_COLUMN_2+TRAINER_COLUMN_WIDTH
|
||||
|
||||
bool menuGeneralTrainer(evt_t event)
|
||||
{
|
||||
uint8_t y;
|
||||
bool slave = SLAVE_MODE();
|
||||
|
||||
MENU(STR_MENUTRAINER, RADIO_ICONS, menuTabGeneral, e_Trainer, (slave ? 0 : 6), { 2, 2, 2, 2, 0/*, 0*/ });
|
||||
|
||||
if (slave) {
|
||||
// TODO lcd_putsCenter(5*FH, STR_SLAVE, TEXT_COLOR);
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t attr;
|
||||
uint8_t blink = ((s_editMode>0) ? BLINK|INVERS : INVERS);
|
||||
|
||||
/* lcdDrawText(TRAINER_COLUMN_1, MENU_HEADER_HEIGHT+1, "Mode", HEADER_COLOR);
|
||||
lcdDrawText(TRAINER_COLUMN_2, MENU_HEADER_HEIGHT+1, "Weight", HEADER_COLOR);
|
||||
lcdDrawText(TRAINER_COLUMN_3, MENU_HEADER_HEIGHT+1, "Source", HEADER_COLOR);
|
||||
*/
|
||||
|
||||
y = MENU_CONTENT_TOP + FH;
|
||||
int sub = menuVerticalPosition + 1;
|
||||
|
||||
for (int i=1; i<=NUM_STICKS; i++) {
|
||||
uint8_t chan = channel_order(i);
|
||||
volatile TrainerMix *td = &g_eeGeneral.trainer.mix[chan-1];
|
||||
|
||||
putsMixerSource(MENUS_MARGIN_LEFT, y, MIXSRC_Rud-1+chan, ((sub==i && CURSOR_ON_LINE()) ? INVERS : 0));
|
||||
|
||||
for (int j=0; j<3; j++) {
|
||||
|
||||
attr = ((sub==i && menuHorizontalPosition==j) ? blink : 0);
|
||||
|
||||
switch(j) {
|
||||
case 0:
|
||||
lcdDrawTextAtIndex(TRAINER_COLUMN_1, y, STR_TRNMODE, td->mode, attr);
|
||||
if (attr&BLINK) CHECK_INCDEC_GENVAR(event, td->mode, 0, 2);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
lcdDrawNumber(TRAINER_COLUMN_2, y, td->studWeight, LEFT|attr, 0, NULL, "%");
|
||||
if (attr&BLINK) CHECK_INCDEC_GENVAR(event, td->studWeight, -125, 125);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
lcdDrawTextAtIndex(TRAINER_COLUMN_3, y, STR_TRNCHN, td->srcChn, attr);
|
||||
if (attr&BLINK) CHECK_INCDEC_GENVAR(event, td->srcChn, 0, 3);
|
||||
break;
|
||||
}
|
||||
}
|
||||
y += FH;
|
||||
}
|
||||
|
||||
attr = (sub==5) ? blink : 0;
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, MENU_CONTENT_TOP + 5*FH, STR_MULTIPLIER);
|
||||
lcdDrawNumber(TRAINER_COLUMN_1, MENU_CONTENT_TOP + 5*FH, g_eeGeneral.PPM_Multiplier+10, LEFT|attr|PREC1);
|
||||
if (attr) CHECK_INCDEC_GENVAR(event, g_eeGeneral.PPM_Multiplier, -10, 40);
|
||||
|
||||
attr = (sub==6) ? INVERS : 0;
|
||||
if (attr) s_editMode = 0;
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, MENU_CONTENT_TOP + 6*FH, STR_CAL, attr);
|
||||
for (int i=0; i<4; i++) {
|
||||
#if defined (PPM_UNIT_PERCENT_PREC1)
|
||||
lcdDrawNumber(TRAINER_COLUMN_1+i*TRAINER_COLUMN_WIDTH, MENU_CONTENT_TOP + 6*FH, (ppmInput[i]-g_eeGeneral.trainer.calib[i])*2, LEFT|PREC1);
|
||||
#else
|
||||
lcdDrawNumber(TRAINER_COLUMN_1+i*TRAINER_COLUMN_WIDTH, MENU_CONTENT_TOP + 6*FH, (ppmInput[i]-g_eeGeneral.trainer.calib[i])/5, LEFT);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (attr) {
|
||||
if (event==EVT_KEY_LONG(KEY_ENTER)){
|
||||
memcpy(g_eeGeneral.trainer.calib, ppmInput, sizeof(g_eeGeneral.trainer.calib));
|
||||
storageDirty(EE_GENERAL);
|
||||
AUDIO_WARNING1();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "opentx.h"
|
||||
|
||||
bool menuGeneralVersion(evt_t event)
|
||||
{
|
||||
DEBUG_TIMER_START(debugTimerVersion);
|
||||
DEBUG_TIMER_START(debugTimerSimpleMenu);
|
||||
SIMPLE_MENU(STR_MENUVERSION, RADIO_ICONS, menuTabGeneral, e_Vers, 0);
|
||||
DEBUG_TIMER_STOP(debugTimerSimpleMenu);
|
||||
|
||||
DEBUG_TIMER_START(debugTimerDrawText);
|
||||
DEBUG_TIMER_START(debugTimerDrawText1);
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, MENU_CONTENT_TOP + FH, vers_stamp);
|
||||
DEBUG_TIMER_STOP(debugTimerDrawText1);
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, MENU_CONTENT_TOP + 2*FH, date_stamp);
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, MENU_CONTENT_TOP + 3*FH, time_stamp);
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, MENU_CONTENT_TOP + 4*FH, eeprom_stamp);
|
||||
DEBUG_TIMER_STOP(debugTimerDrawText);
|
||||
|
||||
// TODO EEPROM erase + backup
|
||||
// lcd_putsCenter(MENU_HEADER_HEIGHT+6*FH, STR_EEBACKUP);
|
||||
// if (event == EVT_KEY_LONG(KEY_ENTER)) {
|
||||
// backupEeprom();
|
||||
// }
|
||||
DEBUG_TIMER_STOP(debugTimerVersion);
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,625 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "opentx.h"
|
||||
|
||||
uint8_t THEME_ICONS[] = {
|
||||
ICON_THEME,
|
||||
ICON_THEME_SETUP,
|
||||
ICON_THEME_VIEW1,
|
||||
ICON_THEME_VIEW2,
|
||||
ICON_THEME_VIEW3,
|
||||
ICON_THEME_VIEW4,
|
||||
ICON_THEME_VIEW5,
|
||||
ICON_THEME_ADD_VIEW,
|
||||
};
|
||||
|
||||
Layout * currentScreen;
|
||||
WidgetsContainerInterface * currentContainer;
|
||||
Widget * currentWidget;
|
||||
uint8_t currentZone;
|
||||
|
||||
#define SCREENS_SETUP_2ND_COLUMN 200
|
||||
|
||||
char fileSelection[LEN_ZONE_OPTION_STRING];
|
||||
uint8_t fileSelectionDone;
|
||||
|
||||
int updateMainviewsMenu();
|
||||
bool menuScreenAdd(evt_t event);
|
||||
void onScreenSetupMenu(const char * result);
|
||||
|
||||
void onZoneOptionFileSelectionMenu(const char * result)
|
||||
{
|
||||
if (result == STR_UPDATE_LIST) {
|
||||
if (!sdListFiles(BITMAPS_PATH, BITMAPS_EXT, LEN_ZONE_OPTION_STRING, NULL)) {
|
||||
POPUP_WARNING(STR_NO_BITMAPS_ON_SD);
|
||||
}
|
||||
}
|
||||
else {
|
||||
fileSelectionDone = true;
|
||||
memcpy(fileSelection, result, sizeof(fileSelection));
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t getZoneOptionColumns(const ZoneOption * option)
|
||||
{
|
||||
if (option->type == ZoneOption::Color) {
|
||||
return uint8_t(2 | NAVIGATION_LINE_BY_LINE);
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool editZoneOption(coord_t y, const ZoneOption * option, ZoneOptionValue * value, LcdFlags attr, uint32_t i_flags, evt_t event)
|
||||
{
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, option->name);
|
||||
|
||||
if (option->type == ZoneOption::Bool) {
|
||||
value->boolValue = editCheckBox(value->boolValue, SCREENS_SETUP_2ND_COLUMN, y, attr, event); // TODO always does storageDirty(EE_MODEL)
|
||||
}
|
||||
else if (option->type == ZoneOption::Integer) {
|
||||
lcdDrawNumber(SCREENS_SETUP_2ND_COLUMN, y, value->signedValue, attr | LEFT);
|
||||
if (attr) {
|
||||
CHECK_INCDEC_MODELVAR(event, value->signedValue, option->min.signedValue, option->max.signedValue); // TODO i_flags
|
||||
}
|
||||
}
|
||||
else if (option->type == ZoneOption::String) {
|
||||
editName(SCREENS_SETUP_2ND_COLUMN, y, value->stringValue, sizeof(value->stringValue), event, attr); // TODO i_flags?
|
||||
}
|
||||
else if (option->type == ZoneOption::File) {
|
||||
if (ZEXIST(value->stringValue))
|
||||
lcdDrawSizedText(SCREENS_SETUP_2ND_COLUMN, y, value->stringValue, sizeof(value->stringValue), attr);
|
||||
else
|
||||
lcdDrawTextAtIndex(SCREENS_SETUP_2ND_COLUMN, y, STR_VCSWFUNC, 0, attr); // TODO define
|
||||
if (attr) {
|
||||
if (event==EVT_KEY_FIRST(KEY_ENTER)) {
|
||||
s_editMode = 0;
|
||||
if (sdListFiles(BITMAPS_PATH, BITMAPS_EXT, sizeof(value->stringValue), value->stringValue, LIST_NONE_SD_FILE)) {
|
||||
fileSelectionDone = false;
|
||||
POPUP_MENU_START(onZoneOptionFileSelectionMenu);
|
||||
}
|
||||
else {
|
||||
POPUP_WARNING(STR_NO_BITMAPS_ON_SD);
|
||||
}
|
||||
}
|
||||
else if (fileSelectionDone) {
|
||||
memcpy(value->stringValue, fileSelection, sizeof(fileSelection));
|
||||
fileSelectionDone = false;
|
||||
storageDirty(i_flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (option->type == ZoneOption::TextSize) {
|
||||
lcdDrawTextAtIndex(SCREENS_SETUP_2ND_COLUMN, y, "\010StandardTiny\0 Small\0 Mid\0 Double", value->unsignedValue, attr);
|
||||
if (attr) {
|
||||
value->unsignedValue = checkIncDec(event, value->unsignedValue, 0, 4, i_flags);
|
||||
}
|
||||
}
|
||||
else if (option->type == ZoneOption::Timer) {
|
||||
drawStringWithIndex(SCREENS_SETUP_2ND_COLUMN, y, STR_TIMER, value->unsignedValue + 1, attr);
|
||||
if (attr) {
|
||||
value->unsignedValue = checkIncDec(event, value->unsignedValue, 0, MAX_TIMERS - 1, i_flags);
|
||||
}
|
||||
}
|
||||
else if (option->type == ZoneOption::Source) {
|
||||
putsMixerSource(SCREENS_SETUP_2ND_COLUMN, y, value->unsignedValue, attr);
|
||||
if (attr) {
|
||||
CHECK_INCDEC_MODELSOURCE(event, value->unsignedValue, 1, MIXSRC_LAST_TELEM);
|
||||
}
|
||||
}
|
||||
else if (option->type == ZoneOption::Color) {
|
||||
RGB_SPLIT(value->unsignedValue, r, g, b);
|
||||
|
||||
if (attr && menuHorizontalPosition < 0) {
|
||||
lcdDrawSolidFilledRect(SCREENS_SETUP_2ND_COLUMN-3, y-1, 230, FH+1, TEXT_INVERTED_BGCOLOR);
|
||||
}
|
||||
|
||||
lcdSetColor(value->unsignedValue);
|
||||
lcdDrawSolidFilledRect(SCREENS_SETUP_2ND_COLUMN-1, y+1, 42, 17, TEXT_COLOR);
|
||||
lcdDrawSolidFilledRect(SCREENS_SETUP_2ND_COLUMN, y+2, 40, 15, CUSTOM_COLOR);
|
||||
|
||||
lcdDrawText(SCREENS_SETUP_2ND_COLUMN + 50, y, "R:", menuHorizontalPosition < 0 ? TEXT_INVERTED_COLOR : TEXT_COLOR);
|
||||
lcdDrawNumber(SCREENS_SETUP_2ND_COLUMN + 70, y, r << 3, LEFT|TEXT_COLOR|((attr && menuHorizontalPosition <= 0) ? attr : 0));
|
||||
if (attr && menuHorizontalPosition == 0) {
|
||||
r = checkIncDec(event, r, 0, (1<<5)-1, i_flags);
|
||||
}
|
||||
lcdDrawText(SCREENS_SETUP_2ND_COLUMN + 110, y, "G:", menuHorizontalPosition < 0 ? TEXT_INVERTED_COLOR : TEXT_COLOR);
|
||||
lcdDrawNumber(SCREENS_SETUP_2ND_COLUMN + 130, y, g << 2, LEFT|TEXT_COLOR|((attr && (menuHorizontalPosition < 0 || menuHorizontalPosition == 1)) ? attr : 0));
|
||||
if (attr && menuHorizontalPosition == 1) {
|
||||
g = checkIncDec(event, g, 0, (1<<6)-1, i_flags);
|
||||
}
|
||||
lcdDrawText(SCREENS_SETUP_2ND_COLUMN + 170, y, "B:", menuHorizontalPosition < 0 ? TEXT_INVERTED_COLOR : TEXT_COLOR);
|
||||
lcdDrawNumber(SCREENS_SETUP_2ND_COLUMN + 190, y, b << 3, LEFT|TEXT_COLOR|((attr && (menuHorizontalPosition < 0 || menuHorizontalPosition == 2)) ? attr : 0));
|
||||
if (attr && menuHorizontalPosition == 2) {
|
||||
b = checkIncDec(event, b, 0, (1<<5)-1, i_flags);
|
||||
}
|
||||
if (attr && checkIncDec_Ret) {
|
||||
value->unsignedValue = RGB_JOIN(r, g, b);
|
||||
}
|
||||
}
|
||||
|
||||
return (attr && checkIncDec_Ret);
|
||||
}
|
||||
|
||||
int getOptionsCount(const ZoneOption * options)
|
||||
{
|
||||
if (options == NULL) {
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
int count = 0;
|
||||
for (const ZoneOption * option = options; option->name; option++) {
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool menuSettings(const char * title, const T * object, uint32_t i_flags, evt_t event)
|
||||
{
|
||||
const ZoneOption * options = object->getOptions();
|
||||
linesCount = getOptionsCount(options);
|
||||
uint8_t mstate_tab[MAX_WIDGET_OPTIONS];
|
||||
for (int i=0; i<linesCount; i++) {
|
||||
mstate_tab[i] = getZoneOptionColumns(&options[i]);
|
||||
}
|
||||
|
||||
CUSTOM_SUBMENU_WITH_OPTIONS(title, ICON_THEME, linesCount, OPTION_MENU_TITLE_BAR);
|
||||
|
||||
for (int i=0; i<NUM_BODY_LINES+1; i++) {
|
||||
coord_t y = MENU_CONTENT_TOP + i * FH;
|
||||
int k = i + menuVerticalOffset;
|
||||
LcdFlags blink = ((s_editMode>0) ? BLINK|INVERS : INVERS);
|
||||
LcdFlags attr = (menuVerticalPosition == k ? blink : 0);
|
||||
if (k < linesCount) {
|
||||
const ZoneOption * option = &options[k];
|
||||
ZoneOptionValue * value = object->getOptionValue(k);
|
||||
if (editZoneOption(y, option, value, attr, i_flags, event)) {
|
||||
object->update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool menuWidgetSettings(evt_t event)
|
||||
{
|
||||
return menuSettings<Widget>("Widget settings", currentWidget, EE_MODEL, event);
|
||||
}
|
||||
|
||||
bool menuWidgetChoice(evt_t event)
|
||||
{
|
||||
static Widget * previousWidget;
|
||||
static Widget::PersistentData tempData;
|
||||
|
||||
switch (event) {
|
||||
case EVT_ENTRY:
|
||||
{
|
||||
const WidgetFactory * factory;
|
||||
previousWidget = currentContainer->getWidget(currentZone);
|
||||
currentContainer->setWidget(currentZone, NULL);
|
||||
menuHorizontalPosition = 0;
|
||||
if (previousWidget) {
|
||||
factory = previousWidget->getFactory();
|
||||
for (unsigned int i=0; i<countRegisteredWidgets; i++) {
|
||||
if (factory->getName() == registeredWidgets[i]->getName()) {
|
||||
menuHorizontalPosition = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
factory = registeredWidgets[0];
|
||||
}
|
||||
currentWidget = factory->create(currentContainer->getZone(currentZone), &tempData);
|
||||
break;
|
||||
}
|
||||
|
||||
case EVT_KEY_FIRST(KEY_EXIT):
|
||||
delete currentWidget;
|
||||
currentContainer->setWidget(currentZone, previousWidget);
|
||||
popMenu();
|
||||
return false;
|
||||
|
||||
case EVT_KEY_FIRST(KEY_ENTER):
|
||||
delete previousWidget;
|
||||
currentContainer->createWidget(currentZone, registeredWidgets[menuHorizontalPosition]);
|
||||
storageDirty(EE_MODEL);
|
||||
popMenu();
|
||||
return false;
|
||||
|
||||
case EVT_ROTARY_RIGHT:
|
||||
if (menuHorizontalPosition < int(countRegisteredWidgets-1)) {
|
||||
delete currentWidget;
|
||||
currentWidget = registeredWidgets[++menuHorizontalPosition]->create(currentContainer->getZone(currentZone), &tempData);
|
||||
}
|
||||
break;
|
||||
|
||||
case EVT_ROTARY_LEFT:
|
||||
if (menuHorizontalPosition > 0) {
|
||||
delete currentWidget;
|
||||
currentWidget = registeredWidgets[--menuHorizontalPosition]->create(currentContainer->getZone(currentZone), &tempData);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
currentScreen->refresh();
|
||||
|
||||
Zone zone = currentContainer->getZone(currentZone);
|
||||
lcdDrawFilledRect(0, 0, zone.x-2, LCD_H, SOLID, OVERLAY_COLOR | (8<<24));
|
||||
lcdDrawFilledRect(zone.x+zone.w+2, 0, LCD_W-zone.x-zone.w-2, LCD_H, SOLID, OVERLAY_COLOR | (8<<24));
|
||||
lcdDrawFilledRect(zone.x-2, 0, zone.w+4, zone.y-2, SOLID, OVERLAY_COLOR | (8<<24));
|
||||
lcdDrawFilledRect(zone.x-2, zone.y+zone.h+2, zone.w+4, LCD_H-zone.y-zone.h-2, SOLID, OVERLAY_COLOR | (8<<24));
|
||||
|
||||
currentWidget->refresh();
|
||||
|
||||
lcdDrawBitmapPattern(zone.x-10, zone.y+zone.h/2-10, LBM_SWIPE_CIRCLE, TEXT_INVERTED_BGCOLOR);
|
||||
lcdDrawBitmapPattern(zone.x-10, zone.y+zone.h/2-10, LBM_SWIPE_LEFT, TEXT_INVERTED_COLOR);
|
||||
lcdDrawBitmapPattern(zone.x+zone.w-9, zone.y+zone.h/2-10, LBM_SWIPE_CIRCLE, TEXT_INVERTED_BGCOLOR);
|
||||
lcdDrawBitmapPattern(zone.x+zone.w-9, zone.y+zone.h/2-10, LBM_SWIPE_RIGHT, TEXT_INVERTED_COLOR);
|
||||
lcdDrawText(zone.x + zone.w, zone.y-1, currentWidget->getFactory()->getName(), RIGHT | TEXT_COLOR | SMLSIZE | INVERS);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void onZoneMenu(const char * result)
|
||||
{
|
||||
if (result == STR_SELECT_WIDGET) {
|
||||
pushMenu(menuWidgetChoice);
|
||||
}
|
||||
else if (result == STR_WIDGET_SETTINGS) {
|
||||
pushMenu(menuWidgetSettings);
|
||||
}
|
||||
else if (result == STR_REMOVE_WIDGET) {
|
||||
currentContainer->createWidget(currentZone, NULL);
|
||||
storageDirty(EE_MODEL);
|
||||
}
|
||||
}
|
||||
|
||||
bool menuWidgetsSetup(evt_t event)
|
||||
{
|
||||
switch (event) {
|
||||
case EVT_ENTRY:
|
||||
menuVerticalPosition = 0;
|
||||
break;
|
||||
case EVT_KEY_FIRST(KEY_EXIT):
|
||||
killEvents(KEY_EXIT);
|
||||
popMenu();
|
||||
return false;
|
||||
}
|
||||
|
||||
currentScreen->refresh();
|
||||
|
||||
for (int i=currentContainer->getZonesCount()-1; i>=0; i--) {
|
||||
Zone zone = currentContainer->getZone(i);
|
||||
LcdFlags color;
|
||||
int padding, thickness;
|
||||
if (currentContainer == topbar) {
|
||||
color = MENU_TITLE_COLOR;
|
||||
padding = 2;
|
||||
thickness = 1;
|
||||
}
|
||||
else {
|
||||
color = TEXT_INVERTED_BGCOLOR;
|
||||
padding = 4;
|
||||
thickness = 2;
|
||||
}
|
||||
if (menuVerticalPosition == i) {
|
||||
lcdDrawSolidRect(zone.x-padding, zone.y-padding, zone.w+2*padding, zone.h+2*padding, thickness, color);
|
||||
if (event == EVT_KEY_FIRST(KEY_ENTER)) {
|
||||
killEvents(KEY_ENTER);
|
||||
currentZone = menuVerticalPosition;
|
||||
currentWidget = currentContainer->getWidget(menuVerticalPosition);
|
||||
if (currentWidget) {
|
||||
POPUP_MENU_ADD_ITEM(STR_SELECT_WIDGET);
|
||||
if (currentWidget->getFactory()->getOptions())
|
||||
POPUP_MENU_ADD_ITEM(STR_WIDGET_SETTINGS);
|
||||
POPUP_MENU_ADD_ITEM(STR_REMOVE_WIDGET);
|
||||
POPUP_MENU_START(onZoneMenu);
|
||||
}
|
||||
else {
|
||||
onZoneMenu(STR_SELECT_WIDGET);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
lcdDrawRect(zone.x-padding, zone.y-padding, zone.w+2*padding, zone.h+2*padding, thickness, 0x3F, color);
|
||||
}
|
||||
}
|
||||
navigate(event, currentContainer->getZonesCount(), currentContainer->getZonesCount(), 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
T * editThemeChoice(coord_t x, coord_t y, T * array[], uint8_t count, T * current, bool needsOffsetCheck, LcdFlags attr, evt_t event)
|
||||
{
|
||||
static uint8_t menuHorizontalOffset;
|
||||
|
||||
int currentIndex = 0;
|
||||
for (unsigned int i=0; i<count; i++) {
|
||||
if (array[i] == current) {
|
||||
currentIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (event == EVT_ENTRY) {
|
||||
menuHorizontalOffset = 0;
|
||||
needsOffsetCheck = true;
|
||||
}
|
||||
|
||||
if (needsOffsetCheck) {
|
||||
if (currentIndex < menuHorizontalOffset) {
|
||||
menuHorizontalOffset = currentIndex;
|
||||
}
|
||||
else if (currentIndex > menuHorizontalOffset + 3) {
|
||||
menuHorizontalOffset = currentIndex - 3;
|
||||
}
|
||||
}
|
||||
if (attr) {
|
||||
if (menuHorizontalPosition < 0) {
|
||||
lcdDrawSolidFilledRect(x-3, y-1, min<uint8_t>(4, count)*56+1, 2*FH-5, TEXT_INVERTED_BGCOLOR);
|
||||
}
|
||||
else {
|
||||
if (needsOffsetCheck) {
|
||||
menuHorizontalPosition = currentIndex;
|
||||
}
|
||||
else if (menuHorizontalPosition < menuHorizontalOffset) {
|
||||
menuHorizontalOffset = menuHorizontalPosition;
|
||||
}
|
||||
else if (menuHorizontalPosition > menuHorizontalOffset + 3) {
|
||||
menuHorizontalOffset = menuHorizontalPosition - 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
unsigned int last = min<int>(menuHorizontalOffset + 4, count);
|
||||
for (unsigned int i=menuHorizontalOffset, pos=x; i<last; i++, pos += 56) {
|
||||
T * element = array[i];
|
||||
element->drawThumb(pos, y+1, current == element ? ((attr && menuHorizontalPosition < 0) ? TEXT_INVERTED_COLOR : TEXT_INVERTED_BGCOLOR) : LINE_COLOR);
|
||||
}
|
||||
if (count > 4) {
|
||||
lcdDrawBitmapPattern(x - 12, y+1, LBM_CARROUSSEL_LEFT, menuHorizontalOffset > 0 ? LINE_COLOR : CURVE_AXIS_COLOR);
|
||||
lcdDrawBitmapPattern(x + 4 * 56, y+1, LBM_CARROUSSEL_RIGHT, last < countRegisteredLayouts ? LINE_COLOR : CURVE_AXIS_COLOR);
|
||||
}
|
||||
if (attr && menuHorizontalPosition >= 0) {
|
||||
lcdDrawSolidRect(x + (menuHorizontalPosition - menuHorizontalOffset) * 56 - 3, y - 1, 57, 35, 1, TEXT_INVERTED_BGCOLOR);
|
||||
if (event == EVT_KEY_FIRST(KEY_ENTER)) {
|
||||
s_editMode = 0;
|
||||
return array[menuHorizontalPosition];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
enum menuScreensThemeItems {
|
||||
ITEM_SCREEN_SETUP_THEME,
|
||||
ITEM_SCREEN_SETUP_THEME_OPTION1 = ITEM_SCREEN_SETUP_THEME+2
|
||||
};
|
||||
|
||||
bool menuScreensTheme(evt_t event)
|
||||
{
|
||||
bool needsOffsetCheck = (menuVerticalPosition != 0 || menuHorizontalPosition < 0);
|
||||
const ZoneOption * options = theme->getOptions();
|
||||
int optionsCount = getOptionsCount(options);
|
||||
linesCount = ITEM_SCREEN_SETUP_THEME_OPTION1 + optionsCount + 1;
|
||||
|
||||
menuPageCount = updateMainviewsMenu();
|
||||
uint8_t mstate_tab[2 + MAX_THEME_OPTIONS + 1] = { uint8_t(NAVIGATION_LINE_BY_LINE|uint8_t(countRegisteredThemes-1)), ORPHAN_ROW };
|
||||
for (int i=0; i<optionsCount; i++) {
|
||||
mstate_tab[2+i] = getZoneOptionColumns(&options[i]);
|
||||
}
|
||||
mstate_tab[2+optionsCount] = 0; // The button for the Topbar setup
|
||||
CUSTOM_MENU_WITH_OPTIONS("User interface", THEME_ICONS, menuTabScreensSetup, menuPageCount, 0, linesCount);
|
||||
|
||||
for (int i=0; i<NUM_BODY_LINES; i++) {
|
||||
coord_t y = MENU_CONTENT_TOP + i * FH;
|
||||
int k = i + menuVerticalOffset;
|
||||
LcdFlags blink = ((s_editMode > 0) ? BLINK | INVERS : INVERS);
|
||||
LcdFlags attr = (menuVerticalPosition == k ? blink : 0);
|
||||
switch (k) {
|
||||
case ITEM_SCREEN_SETUP_THEME: {
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y + FH / 2, "Theme");
|
||||
Theme * new_theme = editThemeChoice<Theme>(SCREENS_SETUP_2ND_COLUMN, y, registeredThemes, countRegisteredThemes, theme, needsOffsetCheck, attr, event);
|
||||
if (new_theme) {
|
||||
new_theme->init();
|
||||
loadTheme(new_theme);
|
||||
strncpy(g_eeGeneral.themeName, new_theme->getName(), sizeof(g_eeGeneral.themeName));
|
||||
killEvents(KEY_ENTER);
|
||||
storageDirty(EE_GENERAL);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ITEM_SCREEN_SETUP_THEME+1:
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
uint8_t index = k - ITEM_SCREEN_SETUP_THEME_OPTION1;
|
||||
if (index < optionsCount) {
|
||||
const ZoneOption * option = &options[index];
|
||||
ZoneOptionValue * value = theme->getOptionValue(index);
|
||||
if (editZoneOption(y, option, value, attr, EE_GENERAL, event)) {
|
||||
theme->update();
|
||||
}
|
||||
}
|
||||
else if (index == optionsCount) {
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y, "Top bar");
|
||||
drawButton(SCREENS_SETUP_2ND_COLUMN, y, "Setup", attr);
|
||||
if (attr && event == EVT_KEY_FIRST(KEY_ENTER)) {
|
||||
currentScreen = customScreens[0];
|
||||
currentContainer = topbar;
|
||||
pushMenu(menuWidgetsSetup);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
enum MenuScreenSetupItems {
|
||||
ITEM_SCREEN_SETUP_LAYOUT,
|
||||
ITEM_SCREEN_SETUP_WIDGETS_SETUP = ITEM_SCREEN_SETUP_LAYOUT+2,
|
||||
ITEM_SCREEN_SETUP_LAYOUT_OPTION1,
|
||||
};
|
||||
|
||||
bool menuScreenSetup(int index, evt_t event)
|
||||
{
|
||||
if (customScreens[index] == NULL) {
|
||||
return menuScreenAdd(event);
|
||||
}
|
||||
|
||||
currentScreen = customScreens[index];
|
||||
currentContainer = currentScreen;
|
||||
bool needsOffsetCheck = (menuVerticalPosition != 0 || menuHorizontalPosition < 0);
|
||||
|
||||
char title[] = "Main view X";
|
||||
title[sizeof(title)-2] = '1' + index;
|
||||
menuPageCount = updateMainviewsMenu();
|
||||
|
||||
const ZoneOption * options = currentScreen->getFactory()->getOptions();
|
||||
int optionsCount = getOptionsCount(options);
|
||||
linesCount = ITEM_SCREEN_SETUP_LAYOUT_OPTION1 + optionsCount;
|
||||
if (menuPageCount > 3)
|
||||
++linesCount;
|
||||
|
||||
uint8_t mstate_tab[2 + MAX_LAYOUT_OPTIONS + 1] = { uint8_t(NAVIGATION_LINE_BY_LINE|uint8_t(countRegisteredLayouts-1)), ORPHAN_ROW };
|
||||
for (int i=0; i<optionsCount; i++) {
|
||||
mstate_tab[3+i] = getZoneOptionColumns(&options[i]);
|
||||
}
|
||||
mstate_tab[3+optionsCount] = 0; // The remove button
|
||||
|
||||
CUSTOM_MENU_WITH_OPTIONS(title, THEME_ICONS, menuTabScreensSetup, menuPageCount, index+1, linesCount);
|
||||
|
||||
for (int i=0; i<NUM_BODY_LINES; i++) {
|
||||
coord_t y = MENU_CONTENT_TOP + i * FH;
|
||||
int k = i + menuVerticalOffset;
|
||||
LcdFlags blink = ((s_editMode>0) ? BLINK|INVERS : INVERS);
|
||||
LcdFlags attr = (menuVerticalPosition == k ? blink : 0);
|
||||
switch(k) {
|
||||
case ITEM_SCREEN_SETUP_LAYOUT:
|
||||
{
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, y + FH / 2, "Layout");
|
||||
const LayoutFactory * factory = editThemeChoice<const LayoutFactory>(SCREENS_SETUP_2ND_COLUMN, y, registeredLayouts, countRegisteredLayouts, currentScreen->getFactory(), needsOffsetCheck, attr, event);
|
||||
if (factory) {
|
||||
delete customScreens[index];
|
||||
currentScreen = customScreens[index] = factory->create(&g_model.screenData[index].layoutData);
|
||||
strncpy(g_model.screenData[index].layoutName, factory->getName(), sizeof(g_model.screenData[index].layoutName));
|
||||
killEvents(KEY_ENTER);
|
||||
storageDirty(EE_MODEL);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ITEM_SCREEN_SETUP_LAYOUT+1:
|
||||
break;
|
||||
|
||||
case ITEM_SCREEN_SETUP_WIDGETS_SETUP:
|
||||
drawButton(SCREENS_SETUP_2ND_COLUMN, y, "Setup widgets", attr);
|
||||
if (attr && event == EVT_KEY_FIRST(KEY_ENTER)) {
|
||||
pushMenu(menuWidgetsSetup);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
uint8_t o = k - ITEM_SCREEN_SETUP_LAYOUT_OPTION1;
|
||||
if (o < optionsCount) {
|
||||
const ZoneOption * option = &options[o];
|
||||
ZoneOptionValue * value = currentScreen->getOptionValue(o);
|
||||
if (editZoneOption(y, option, value, attr, EE_MODEL, event)) {
|
||||
currentScreen->update();
|
||||
}
|
||||
}
|
||||
else if (menuPageCount > 3 && o == optionsCount) {
|
||||
drawButton(SCREENS_SETUP_2ND_COLUMN, y, STR_REMOVE_SCREEN, attr);
|
||||
if (attr && event == EVT_KEY_LONG(KEY_ENTER)) {
|
||||
delete currentScreen;
|
||||
if (index != MAX_CUSTOM_SCREENS-1) {
|
||||
memmove(&g_model.screenData[index], &g_model.screenData[index + 1], sizeof(CustomScreenData) * (MAX_CUSTOM_SCREENS - index - 1));
|
||||
memmove(&customScreens[index], &customScreens[index + 1], sizeof(Layout *) * (MAX_CUSTOM_SCREENS - index - 1));
|
||||
}
|
||||
memset(&g_model.screenData[MAX_CUSTOM_SCREENS-1], 0, sizeof(CustomScreenData));
|
||||
customScreens[MAX_CUSTOM_SCREENS-1] = NULL;
|
||||
killEvents(KEY_ENTER);
|
||||
chainMenu(menuTabScreensSetup[index > 0 ? index : 1]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<int N>
|
||||
bool menuCustomScreenSetup(evt_t event)
|
||||
{
|
||||
return menuScreenSetup(N, event);
|
||||
}
|
||||
|
||||
const MenuHandlerFunc menuTabScreensSetup[1+MAX_CUSTOM_SCREENS] = {
|
||||
menuScreensTheme,
|
||||
menuCustomScreenSetup<0>,
|
||||
menuCustomScreenSetup<1>,
|
||||
menuCustomScreenSetup<2>,
|
||||
menuCustomScreenSetup<3>,
|
||||
menuCustomScreenSetup<4>
|
||||
};
|
||||
|
||||
int updateMainviewsMenu()
|
||||
{
|
||||
for (int index=1; index<MAX_CUSTOM_SCREENS; index++) {
|
||||
if (customScreens[index]) {
|
||||
THEME_ICONS[2+index] = ICON_THEME_VIEW1+index;
|
||||
}
|
||||
else {
|
||||
THEME_ICONS[2+index] = ICON_THEME_ADD_VIEW;
|
||||
return 2+index;
|
||||
}
|
||||
}
|
||||
return 1+MAX_CUSTOM_SCREENS;
|
||||
}
|
||||
|
||||
bool menuScreenAdd(evt_t event)
|
||||
{
|
||||
menuPageCount = updateMainviewsMenu();
|
||||
|
||||
if (event == EVT_KEY_FIRST(KEY_ENTER)) {
|
||||
customScreens[menuPageCount-2] = registeredLayouts[0]->create(&g_model.screenData[menuPageCount-2].layoutData);
|
||||
strncpy(g_model.screenData[menuPageCount-2].layoutName, registeredLayouts[0]->getName(), sizeof(g_model.screenData[menuPageCount-2].layoutName));
|
||||
s_editMode = 0;
|
||||
menuHorizontalPosition = -1;
|
||||
killEvents(KEY_ENTER);
|
||||
storageDirty(EE_MODEL);
|
||||
return false;
|
||||
}
|
||||
|
||||
SIMPLE_MENU_WITH_OPTIONS("Add main view", THEME_ICONS, menuTabScreensSetup, menuPageCount, menuPageCount-1, 0);
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "opentx.h"
|
||||
#include "bmp_splash.lbm"
|
||||
|
||||
#if defined(SPLASH)
|
||||
void drawSplash()
|
||||
{
|
||||
lcd->clear();
|
||||
lcd->drawBitmap((LCD_W-BMP_SPLASH.getWidth())/2, (LCD_H-BMP_SPLASH.getHeight())/2, &BMP_SPLASH);
|
||||
|
||||
#if MENUS_LOCK == 1
|
||||
if (readonly == false) {
|
||||
lcdDrawSolidFilledRect((LCD_W-(sizeof(TR_UNLOCKED)-1)*FW)/2 - 9, 50, (sizeof(TR_UNLOCKED)-1)*FW+16, 11, SOLID, ROUND);
|
||||
lcdDrawText((LCD_W-(sizeof(TR_UNLOCKED)-1)*FW)/2 , 53, STR_UNLOCKED);
|
||||
}
|
||||
#endif
|
||||
|
||||
lcdRefresh();
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "opentx.h"
|
||||
|
||||
const BitmapBuffer * Theme::asterisk = NULL;
|
||||
const BitmapBuffer * Theme::question = NULL;
|
||||
const BitmapBuffer * Theme::busy = NULL;
|
||||
|
||||
void Theme::init() const
|
||||
{
|
||||
memset(&g_eeGeneral.themeData, 0, sizeof(Theme::PersistentData));
|
||||
if (options) {
|
||||
int i = 0;
|
||||
for (const ZoneOption * option = options; option->name; option++, i++) {
|
||||
// TODO compiler bug? The CPU freezes ... g_eeGeneral.themeData.options[i] = &option->deflt;
|
||||
memcpy(&g_eeGeneral.themeData.options[i], &option->deflt, sizeof(ZoneOptionValue));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Theme::load() const
|
||||
{
|
||||
if (!asterisk) asterisk = BitmapBuffer::load(getThemePath("asterisk.bmp"));
|
||||
if (!question) question = BitmapBuffer::load(getThemePath("question.bmp"));
|
||||
if (!busy) busy = BitmapBuffer::load(getThemePath("busy.bmp"));
|
||||
}
|
||||
|
||||
ZoneOptionValue * Theme::getOptionValue(unsigned int index) const
|
||||
{
|
||||
return &g_eeGeneral.themeData.options[index];
|
||||
}
|
||||
|
||||
const char * Theme::getFilePath(const char * filename) const
|
||||
{
|
||||
static char path[_MAX_LFN+1] = THEMES_PATH "/";
|
||||
strcpy(path + sizeof(THEMES_PATH), getName());
|
||||
int len = sizeof(THEMES_PATH) + strlen(path + sizeof(THEMES_PATH));
|
||||
path[len] = '/';
|
||||
strcpy(path+len+1, filename);
|
||||
return path;
|
||||
}
|
||||
|
||||
void Theme::drawThumb(uint16_t x, uint16_t y, uint32_t flags)
|
||||
{
|
||||
#define THUMB_WIDTH 51
|
||||
#define THUMB_HEIGHT 31
|
||||
if (!thumb) {
|
||||
thumb = BitmapBuffer::load(getFilePath("thumb.bmp"));
|
||||
}
|
||||
lcd->drawBitmap(x, y, thumb);
|
||||
if (flags == LINE_COLOR) {
|
||||
lcdDrawFilledRect(x, y, THUMB_WIDTH, THUMB_HEIGHT, SOLID, OVERLAY_COLOR | OPACITY(10));
|
||||
}
|
||||
}
|
||||
|
||||
void Theme::drawBackground() const
|
||||
{
|
||||
lcdDrawSolidFilledRect(0, 0, LCD_W, LCD_H, TEXT_BGCOLOR);
|
||||
}
|
||||
|
||||
void Theme::drawMessageBox(const char * title, const char * text, const char * action, uint32_t type) const
|
||||
{
|
||||
//if (flags & MESSAGEBOX_TYPE_ALERT) {
|
||||
drawBackground();
|
||||
lcdDrawFilledRect(0, POPUP_Y, LCD_W, POPUP_H, SOLID, TEXT_INVERTED_COLOR | OPACITY(8));
|
||||
//}
|
||||
|
||||
if (type == WARNING_TYPE_ALERT || type == WARNING_TYPE_ASTERISK)
|
||||
lcd->drawBitmap(POPUP_X-80, POPUP_Y+12, asterisk);
|
||||
else if (type == WARNING_TYPE_INFO)
|
||||
lcd->drawBitmap(POPUP_X-80, POPUP_Y+12, busy);
|
||||
else
|
||||
lcd->drawBitmap(POPUP_X-80, POPUP_Y+12, question);
|
||||
|
||||
if (type == WARNING_TYPE_ALERT) {
|
||||
#if defined(TRANSLATIONS_FR) || defined(TRANSLATIONS_IT) || defined(TRANSLATIONS_CZ)
|
||||
lcdDrawText(WARNING_LINE_X, WARNING_LINE_Y, STR_WARNING, ALARM_COLOR|DBLSIZE);
|
||||
lcdDrawText(WARNING_LINE_X, WARNING_LINE_Y+28, title, ALARM_COLOR|DBLSIZE);
|
||||
#else
|
||||
lcdDrawText(WARNING_LINE_X, WARNING_LINE_Y, title, ALARM_COLOR|DBLSIZE);
|
||||
lcdDrawText(WARNING_LINE_X, WARNING_LINE_Y+28, STR_WARNING, ALARM_COLOR|DBLSIZE);
|
||||
#endif
|
||||
}
|
||||
else if (title) {
|
||||
lcdDrawText(WARNING_LINE_X, WARNING_LINE_Y, title, ALARM_COLOR|DBLSIZE);
|
||||
}
|
||||
|
||||
if (text) {
|
||||
lcdDrawText(WARNING_LINE_X, WARNING_INFOLINE_Y, text);
|
||||
}
|
||||
|
||||
if (action) {
|
||||
lcdDrawText(WARNING_LINE_X, WARNING_INFOLINE_Y+24, action);
|
||||
}
|
||||
}
|
||||
|
||||
Theme * registeredThemes[MAX_REGISTERED_THEMES]; // TODO dynamic
|
||||
unsigned int countRegisteredThemes = 0;
|
||||
void registerTheme(Theme * theme)
|
||||
{
|
||||
if (countRegisteredThemes < MAX_REGISTERED_THEMES) {
|
||||
TRACE("register theme %s", theme->getName());
|
||||
registeredThemes[countRegisteredThemes++] = theme;
|
||||
}
|
||||
}
|
||||
|
||||
Theme * getTheme(const char * name)
|
||||
{
|
||||
for (unsigned int i=0; i<countRegisteredThemes; i++) {
|
||||
Theme * theme = registeredThemes[i];
|
||||
if (!strcmp(name, theme->getName())) {
|
||||
return theme;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void loadTheme(Theme * new_theme)
|
||||
{
|
||||
TRACE("load theme %s", new_theme->getName());
|
||||
theme = new_theme;
|
||||
theme->load();
|
||||
}
|
||||
|
||||
void loadTheme()
|
||||
{
|
||||
char name[sizeof(g_eeGeneral.themeName)+1];
|
||||
memset(name, 0, sizeof(name));
|
||||
strncpy(name, g_eeGeneral.themeName, sizeof(g_eeGeneral.themeName));
|
||||
Theme * new_theme = getTheme(name);
|
||||
if (new_theme) {
|
||||
loadTheme(new_theme);
|
||||
}
|
||||
else {
|
||||
theme->load();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _THEME_H_
|
||||
#define _THEME_H_
|
||||
|
||||
class BitmapBuffer;
|
||||
|
||||
#define MAX_THEME_OPTIONS 5
|
||||
|
||||
class Theme;
|
||||
void registerTheme(Theme * theme);
|
||||
|
||||
class Theme
|
||||
{
|
||||
public:
|
||||
struct PersistentData {
|
||||
ZoneOptionValue options[MAX_THEME_OPTIONS];
|
||||
};
|
||||
|
||||
Theme(const char * name, const ZoneOption * options=NULL):
|
||||
name(name),
|
||||
options(options),
|
||||
thumb(NULL)
|
||||
{
|
||||
registerTheme(this);
|
||||
}
|
||||
|
||||
inline const char * getName() const
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
const char * getFilePath(const char * filename) const;
|
||||
|
||||
void drawThumb(uint16_t x, uint16_t y, uint32_t flags);
|
||||
|
||||
inline const ZoneOption * getOptions() const
|
||||
{
|
||||
return options;
|
||||
}
|
||||
|
||||
void init() const;
|
||||
|
||||
virtual void update() const
|
||||
{
|
||||
}
|
||||
|
||||
ZoneOptionValue * getOptionValue(unsigned int index) const;
|
||||
|
||||
virtual void load() const;
|
||||
|
||||
virtual void drawBackground() const;
|
||||
|
||||
virtual void drawTopbarBackground(uint8_t icon) const = 0;
|
||||
|
||||
virtual void drawMenuIcon(uint8_t index, uint8_t position, bool selected) const { }
|
||||
|
||||
virtual void drawMessageBox(const char * title, const char * text, const char * action, uint32_t flags) const;
|
||||
|
||||
protected:
|
||||
const char * name;
|
||||
const ZoneOption * options;
|
||||
BitmapBuffer * thumb;
|
||||
static const BitmapBuffer * asterisk;
|
||||
static const BitmapBuffer * question;
|
||||
static const BitmapBuffer * busy;
|
||||
};
|
||||
|
||||
extern Theme * theme;
|
||||
|
||||
inline const char * getThemePath(const char * filename)
|
||||
{
|
||||
return theme->getFilePath(filename);
|
||||
}
|
||||
|
||||
#define MAX_REGISTERED_THEMES 10
|
||||
extern unsigned int countRegisteredThemes;
|
||||
void registerTheme(Theme * theme);
|
||||
extern Theme * registeredThemes[MAX_REGISTERED_THEMES]; // TODO dynamic
|
||||
|
||||
Theme * getTheme(const char * name);
|
||||
void loadTheme(Theme * theme);
|
||||
void loadTheme();
|
||||
|
||||
#endif // _THEME_H_
|
||||
@@ -0,0 +1,208 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "opentx.h"
|
||||
|
||||
class DarkblueTheme: public Theme
|
||||
{
|
||||
public:
|
||||
DarkblueTheme():
|
||||
Theme("Darkblue")
|
||||
{
|
||||
}
|
||||
|
||||
void loadColors() const
|
||||
{
|
||||
lcdColorTable[TEXT_COLOR_INDEX] = WHITE;
|
||||
lcdColorTable[TEXT_BGCOLOR_INDEX] = RGB(10, 78, 121);
|
||||
lcdColorTable[TEXT_INVERTED_COLOR_INDEX] = WHITE;
|
||||
lcdColorTable[TEXT_INVERTED_BGCOLOR_INDEX] = RGB(32, 34, 42);
|
||||
lcdColorTable[TEXT_STATUSBAR_COLOR_INDEX] = WHITE;
|
||||
lcdColorTable[LINE_COLOR_INDEX] = GREY;
|
||||
lcdColorTable[SCROLLBOX_COLOR_INDEX] = WHITE;
|
||||
lcdColorTable[MENU_TITLE_BGCOLOR_INDEX] = DARKGREY;
|
||||
lcdColorTable[MENU_TITLE_COLOR_INDEX] = WHITE;
|
||||
lcdColorTable[MENU_TITLE_DISABLE_COLOR_INDEX] = BLACK;
|
||||
lcdColorTable[HEADER_COLOR_INDEX] = RGB(32, 34, 42);
|
||||
lcdColorTable[ALARM_COLOR_INDEX] = RGB(32, 34, 42);
|
||||
lcdColorTable[WARNING_COLOR_INDEX] = YELLOW;
|
||||
lcdColorTable[TEXT_DISABLE_COLOR_INDEX] = GREY;
|
||||
lcdColorTable[CURVE_AXIS_COLOR_INDEX] = LIGHTGREY;
|
||||
lcdColorTable[CURVE_COLOR_INDEX] = RGB(32, 34, 42);
|
||||
lcdColorTable[CURVE_CURSOR_COLOR_INDEX] = RGB(32, 34, 42);
|
||||
lcdColorTable[TITLE_BGCOLOR_INDEX] = RGB(32, 34, 42);
|
||||
lcdColorTable[TRIM_BGCOLOR_INDEX] = RGB(32, 34, 42);
|
||||
lcdColorTable[TRIM_SHADOW_COLOR_INDEX] = RGB(100, 100, 100);
|
||||
lcdColorTable[MAINVIEW_PANES_COLOR_INDEX] = GREY;
|
||||
lcdColorTable[MAINVIEW_GRAPHICS_COLOR_INDEX] = WHITE;
|
||||
lcdColorTable[HEADER_BGCOLOR_INDEX] = BLACK;
|
||||
lcdColorTable[HEADER_ICON_BGCOLOR_INDEX] = BLACK;
|
||||
lcdColorTable[HEADER_CURRENT_BGCOLOR_INDEX] = RGB(10, 78, 121);
|
||||
lcdColorTable[OVERLAY_COLOR_INDEX] = BLACK;
|
||||
lcdColorTable[BARGRAPH1_COLOR_INDEX] = RGB(15, 150, 250);
|
||||
lcdColorTable[BARGRAPH2_COLOR_INDEX] = RGB(167, 167, 167);
|
||||
lcdColorTable[BARGRAPH_BGCOLOR_INDEX] = RGB(97, 97, 102);
|
||||
}
|
||||
|
||||
void loadMenuIcon(uint8_t index, const char * filename) const
|
||||
{
|
||||
BitmapBuffer * mask = BitmapBuffer::loadMask(getThemePath(filename));
|
||||
if (mask) {
|
||||
menuIconNormal[index] = new BitmapBuffer(BMP_RGB565, mask->getWidth(), mask->getHeight());
|
||||
menuIconNormal[index]->clear(HEADER_BGCOLOR);
|
||||
menuIconNormal[index]->drawMask(0, 0, mask, HEADER_CURRENT_BGCOLOR);
|
||||
menuIconSelected[index] = new BitmapBuffer(BMP_RGB565, mask->getWidth(), mask->getHeight());
|
||||
menuIconSelected[index]->clear(HEADER_BGCOLOR);
|
||||
menuIconSelected[index]->drawMask(0, 0, mask, MENU_TITLE_COLOR);
|
||||
}
|
||||
}
|
||||
|
||||
void loadMenusIcons() const
|
||||
{
|
||||
loadMenuIcon(ICON_OPENTX, "mask_opentx.png");
|
||||
loadMenuIcon(ICON_RADIO, "mask_menu_radio.png");
|
||||
loadMenuIcon(ICON_RADIO_SETUP, "mask_radio_setup.png");
|
||||
loadMenuIcon(ICON_RADIO_SD_BROWSER, "mask_radio_sd_browser.png");
|
||||
loadMenuIcon(ICON_RADIO_GLOBAL_FUNCTIONS, "mask_radio_global_functions.png");
|
||||
loadMenuIcon(ICON_RADIO_TRAINER, "mask_radio_trainer.png");
|
||||
loadMenuIcon(ICON_RADIO_HARDWARE, "mask_radio_hardware.png");
|
||||
loadMenuIcon(ICON_RADIO_VERSION, "mask_radio_version.png");
|
||||
loadMenuIcon(ICON_MODEL, "mask_menu_model.png");
|
||||
loadMenuIcon(ICON_MODEL_SETUP, "mask_model_setup.png");
|
||||
loadMenuIcon(ICON_MODEL_HELI, "mask_model_heli.png");
|
||||
loadMenuIcon(ICON_MODEL_FLIGHT_MODES, "mask_model_flight_modes.png");
|
||||
loadMenuIcon(ICON_MODEL_INPUTS, "mask_model_inputs.png");
|
||||
loadMenuIcon(ICON_MODEL_MIXER, "mask_model_mixer.png");
|
||||
loadMenuIcon(ICON_MODEL_OUTPUTS, "mask_model_outputs.png");
|
||||
loadMenuIcon(ICON_MODEL_CURVES, "mask_model_curves.png");
|
||||
loadMenuIcon(ICON_MODEL_GVARS, "mask_model_gvars.png");
|
||||
loadMenuIcon(ICON_MODEL_LOGICAL_SWITCHES, "mask_model_logical_switches.png");
|
||||
loadMenuIcon(ICON_MODEL_SPECIAL_FUNCTIONS, "mask_model_special_functions.png");
|
||||
loadMenuIcon(ICON_MODEL_LUA_SCRIPTS, "mask_model_lua_scripts.png");
|
||||
loadMenuIcon(ICON_MODEL_TELEMETRY, "mask_model_telemetry.png");
|
||||
loadMenuIcon(ICON_STATS, "mask_menu_stats.png");
|
||||
loadMenuIcon(ICON_STATS_THROTTLE_GRAPH, "mask_stats_throttle_graph.png");
|
||||
loadMenuIcon(ICON_STATS_TIMERS, "mask_stats_timers.png");
|
||||
loadMenuIcon(ICON_STATS_ANALOGS, "mask_stats_analogs.png");
|
||||
loadMenuIcon(ICON_STATS_DEBUG, "mask_stats_debug.png");
|
||||
loadMenuIcon(ICON_THEME, "mask_menu_theme.png");
|
||||
loadMenuIcon(ICON_THEME_SETUP, "mask_theme_setup.png");
|
||||
loadMenuIcon(ICON_THEME_VIEW1, "mask_theme_view1.png");
|
||||
loadMenuIcon(ICON_THEME_VIEW2, "mask_theme_view2.png");
|
||||
loadMenuIcon(ICON_THEME_VIEW3, "mask_theme_view3.png");
|
||||
loadMenuIcon(ICON_THEME_VIEW4, "mask_theme_view4.png");
|
||||
loadMenuIcon(ICON_THEME_VIEW5, "mask_theme_view5.png");
|
||||
loadMenuIcon(ICON_THEME_ADD_VIEW, "mask_theme_add_view.png");
|
||||
loadMenuIcon(ICON_MONITOR, "mask_monitor.png");
|
||||
loadMenuIcon(ICON_MONITOR_CHANNELS1, "mask_monitor_channels1.png");
|
||||
loadMenuIcon(ICON_MONITOR_CHANNELS2, "mask_monitor_channels2.png");
|
||||
loadMenuIcon(ICON_MONITOR_CHANNELS3, "mask_monitor_channels3.png");
|
||||
loadMenuIcon(ICON_MONITOR_CHANNELS4, "mask_monitor_channels4.png");
|
||||
loadMenuIcon(ICON_MONITOR_LOGICAL_SWITCHES, "/mask_monitor_logsw.png");
|
||||
}
|
||||
|
||||
void loadThemeBitmaps() const
|
||||
{
|
||||
// Channels monitor screen
|
||||
delete chanMonLockedBitmap;
|
||||
chanMonLockedBitmap = BitmapBuffer::loadMaskOnBackground("mask_monitor_lockch.png", TEXT_COLOR, TEXT_BGCOLOR);
|
||||
|
||||
delete chanMonInvertedBitmap;
|
||||
chanMonInvertedBitmap = BitmapBuffer::loadMaskOnBackground("mask_monitor_inver.png", TEXT_COLOR, TEXT_BGCOLOR);
|
||||
|
||||
// Mixer setup screen
|
||||
delete mixerSetupMixerBitmap;
|
||||
mixerSetupMixerBitmap = BitmapBuffer::loadMaskOnBackground("mask_sbar_mixer.png", MENU_TITLE_COLOR, HEADER_BGCOLOR);
|
||||
|
||||
delete mixerSetupToBitmap;
|
||||
mixerSetupToBitmap = BitmapBuffer::loadMaskOnBackground("mask_sbar_to.png", TEXT_BGCOLOR, HEADER_BGCOLOR);
|
||||
|
||||
delete mixerSetupOutputBitmap;
|
||||
mixerSetupOutputBitmap = BitmapBuffer::loadMaskOnBackground("mask_sbar_output.png", MENU_TITLE_COLOR, HEADER_BGCOLOR);
|
||||
|
||||
delete mixerSetupAddBitmap;
|
||||
mixerSetupAddBitmap = BitmapBuffer::loadMaskOnBackground("mask_mplex_add.png", TEXT_COLOR, TEXT_BGCOLOR);
|
||||
|
||||
delete mixerSetupMultiBitmap;
|
||||
mixerSetupMultiBitmap = BitmapBuffer::loadMaskOnBackground("mask_mplex_multi.png", TEXT_COLOR, TEXT_BGCOLOR);
|
||||
|
||||
delete mixerSetupReplaceBitmap;
|
||||
mixerSetupReplaceBitmap = BitmapBuffer::loadMaskOnBackground("mask_mplex_replace.png", TEXT_COLOR, TEXT_BGCOLOR);
|
||||
|
||||
delete mixerSetupLabelBitmap;
|
||||
mixerSetupLabelBitmap = BitmapBuffer::loadMaskOnBackground("mask_textline_label.png", TEXT_COLOR, TEXT_BGCOLOR);
|
||||
|
||||
delete mixerSetupCurveBitmap;
|
||||
mixerSetupCurveBitmap = BitmapBuffer::loadMaskOnBackground("mask_textline_curve.png", TEXT_COLOR, TEXT_BGCOLOR);
|
||||
|
||||
delete mixerSetupSwitchBitmap;
|
||||
mixerSetupSwitchBitmap = BitmapBuffer::loadMaskOnBackground("mask_textline_switch.png", TEXT_COLOR, TEXT_BGCOLOR);
|
||||
|
||||
delete mixerSetupSlowBitmap;
|
||||
mixerSetupSlowBitmap = BitmapBuffer::loadMaskOnBackground("mask_textline_slow.png", TEXT_COLOR, TEXT_BGCOLOR);
|
||||
|
||||
delete mixerSetupDelayBitmap;
|
||||
mixerSetupDelayBitmap = BitmapBuffer::loadMaskOnBackground("mask_textline_delay.png", TEXT_COLOR, TEXT_BGCOLOR);
|
||||
|
||||
delete mixerSetupDelaySlowBitmap;
|
||||
mixerSetupDelaySlowBitmap = BitmapBuffer::loadMaskOnBackground("mask_textline_delayslow.png", TEXT_COLOR, TEXT_BGCOLOR);
|
||||
|
||||
delete mixerSetupFlightmodeBitmap;
|
||||
mixerSetupFlightmodeBitmap = BitmapBuffer::loadMaskOnBackground("mask_textline_fm.png", TEXT_COLOR, TEXT_BGCOLOR);
|
||||
}
|
||||
|
||||
virtual void load() const
|
||||
{
|
||||
loadColors();
|
||||
loadMenusIcons();
|
||||
loadThemeBitmaps();
|
||||
loadFontCache();
|
||||
}
|
||||
|
||||
void drawTopbarBackground(uint8_t icon) const
|
||||
{
|
||||
lcdDrawSolidFilledRect(0, 0, LCD_W, MENU_HEADER_HEIGHT, HEADER_BGCOLOR);
|
||||
lcdDrawSolidFilledRect(0, 0, 41, MENU_HEADER_HEIGHT, HEADER_ICON_BGCOLOR);
|
||||
|
||||
if (icon == ICON_OPENTX) {
|
||||
static BitmapBuffer * thumb = BitmapBuffer::load(getFilePath("topmenu_opentx.bmp"));
|
||||
lcd->drawBitmap(5, 7, thumb);
|
||||
}
|
||||
else {
|
||||
lcd->drawBitmap(5, 7, menuIconSelected[icon]);
|
||||
}
|
||||
|
||||
drawTopbarDatetime();
|
||||
}
|
||||
|
||||
virtual void drawMenuIcon(uint8_t index, uint8_t position, bool selected) const
|
||||
{
|
||||
lcd->drawBitmap(50+position*MENU_ICONS_SPACING, 7, selected ? menuIconSelected[index] : menuIconNormal[index]);
|
||||
}
|
||||
|
||||
protected:
|
||||
static BitmapBuffer * menuIconNormal[MENUS_ICONS_COUNT];
|
||||
static BitmapBuffer * menuIconSelected[MENUS_ICONS_COUNT];
|
||||
};
|
||||
|
||||
BitmapBuffer * DarkblueTheme::menuIconNormal[MENUS_ICONS_COUNT] = { NULL };
|
||||
BitmapBuffer * DarkblueTheme::menuIconSelected[MENUS_ICONS_COUNT] = { NULL };
|
||||
|
||||
const DarkblueTheme darkblueTheme;
|
||||
@@ -0,0 +1,310 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "opentx.h"
|
||||
|
||||
const ZoneOption OPTIONS_THEME_DEFAULT[] = {
|
||||
{ "Background color", ZoneOption::Color, OPTION_VALUE_UNSIGNED(WHITE) },
|
||||
{ "Main color", ZoneOption::Color, OPTION_VALUE_UNSIGNED(RED) },
|
||||
{ NULL, ZoneOption::Bool }
|
||||
};
|
||||
|
||||
class DefaultTheme: public Theme
|
||||
{
|
||||
public:
|
||||
DefaultTheme():
|
||||
Theme("Default", OPTIONS_THEME_DEFAULT)
|
||||
{
|
||||
loadColors();
|
||||
}
|
||||
|
||||
void loadColors() const
|
||||
{
|
||||
lcdColorTable[TEXT_COLOR_INDEX] = BLACK;
|
||||
lcdColorTable[TEXT_BGCOLOR_INDEX] = WHITE;
|
||||
lcdColorTable[TEXT_INVERTED_COLOR_INDEX] = WHITE;
|
||||
lcdColorTable[TEXT_INVERTED_BGCOLOR_INDEX] = RED;
|
||||
lcdColorTable[TEXT_STATUSBAR_COLOR_INDEX] = WHITE;
|
||||
lcdColorTable[LINE_COLOR_INDEX] = GREY;
|
||||
lcdColorTable[SCROLLBOX_COLOR_INDEX] = RED;
|
||||
lcdColorTable[MENU_TITLE_BGCOLOR_INDEX] = DARKGREY;
|
||||
lcdColorTable[MENU_TITLE_COLOR_INDEX] = WHITE;
|
||||
lcdColorTable[MENU_TITLE_DISABLE_COLOR_INDEX] = RGB(130, 1, 5);
|
||||
lcdColorTable[HEADER_COLOR_INDEX] = DARKGREY;
|
||||
lcdColorTable[ALARM_COLOR_INDEX] = RED;
|
||||
lcdColorTable[WARNING_COLOR_INDEX] = YELLOW;
|
||||
lcdColorTable[TEXT_DISABLE_COLOR_INDEX] = GREY;
|
||||
lcdColorTable[CURVE_AXIS_COLOR_INDEX] = LIGHTGREY;
|
||||
lcdColorTable[CURVE_COLOR_INDEX] = RED;
|
||||
lcdColorTable[CURVE_CURSOR_COLOR_INDEX] = RED;
|
||||
lcdColorTable[TITLE_BGCOLOR_INDEX] = RED;
|
||||
lcdColorTable[TRIM_BGCOLOR_INDEX] = RED;
|
||||
lcdColorTable[TRIM_SHADOW_COLOR_INDEX] = BLACK;
|
||||
lcdColorTable[MAINVIEW_PANES_COLOR_INDEX] = WHITE;
|
||||
lcdColorTable[MAINVIEW_GRAPHICS_COLOR_INDEX] = RED;
|
||||
lcdColorTable[HEADER_BGCOLOR_INDEX] = DARKRED;
|
||||
lcdColorTable[HEADER_ICON_BGCOLOR_INDEX] = RED;
|
||||
lcdColorTable[HEADER_CURRENT_BGCOLOR_INDEX] = RED;
|
||||
lcdColorTable[OVERLAY_COLOR_INDEX] = BLACK;
|
||||
lcdColorTable[BARGRAPH1_COLOR_INDEX] = RED;
|
||||
lcdColorTable[BARGRAPH2_COLOR_INDEX] = RGB(167, 167, 167);
|
||||
lcdColorTable[BARGRAPH_BGCOLOR_INDEX] = RGB(222, 222, 222);
|
||||
}
|
||||
|
||||
void loadMenuIcon(uint8_t index, const char * filename, uint32_t color=MENU_TITLE_COLOR) const
|
||||
{
|
||||
BitmapBuffer * mask = BitmapBuffer::loadMask(getThemePath(filename));
|
||||
if (mask) {
|
||||
delete menuIconNormal[index];
|
||||
menuIconNormal[index] = new BitmapBuffer(BMP_RGB565, mask->getWidth(), mask->getHeight());
|
||||
menuIconNormal[index]->clear(HEADER_BGCOLOR);
|
||||
menuIconNormal[index]->drawMask(0, 0, mask, color);
|
||||
delete menuIconSelected[index];
|
||||
menuIconSelected[index] = new BitmapBuffer(BMP_RGB565, mask->getWidth(), mask->getHeight());
|
||||
menuIconSelected[index]->clear(HEADER_CURRENT_BGCOLOR);
|
||||
menuIconSelected[index]->drawMask(0, 0, mask, color);
|
||||
delete mask;
|
||||
}
|
||||
}
|
||||
|
||||
void loadIcons() const
|
||||
{
|
||||
#if defined(LOG_TELEMETRY) || defined(WATCHDOG_DISABLED)
|
||||
loadMenuIcon(ICON_OPENTX, "mask_opentx_testmode.png", TEXT_COLOR);
|
||||
#else
|
||||
loadMenuIcon(ICON_OPENTX, "mask_opentx.png");
|
||||
#endif
|
||||
loadMenuIcon(ICON_RADIO, "mask_menu_radio.png");
|
||||
loadMenuIcon(ICON_RADIO_SETUP, "mask_radio_setup.png");
|
||||
loadMenuIcon(ICON_RADIO_SD_BROWSER, "mask_radio_sd_browser.png");
|
||||
loadMenuIcon(ICON_RADIO_GLOBAL_FUNCTIONS, "mask_radio_global_functions.png");
|
||||
loadMenuIcon(ICON_RADIO_TRAINER, "mask_radio_trainer.png");
|
||||
loadMenuIcon(ICON_RADIO_HARDWARE, "mask_radio_hardware.png");
|
||||
loadMenuIcon(ICON_RADIO_CALIBRATION, "mask_radio_calibration.png");
|
||||
loadMenuIcon(ICON_RADIO_VERSION, "mask_radio_version.png");
|
||||
loadMenuIcon(ICON_MODEL, "mask_menu_model.png");
|
||||
loadMenuIcon(ICON_MODEL_SETUP, "mask_model_setup.png");
|
||||
loadMenuIcon(ICON_MODEL_HELI, "mask_model_heli.png");
|
||||
loadMenuIcon(ICON_MODEL_FLIGHT_MODES, "mask_model_flight_modes.png");
|
||||
loadMenuIcon(ICON_MODEL_INPUTS, "mask_model_inputs.png");
|
||||
loadMenuIcon(ICON_MODEL_MIXER, "mask_model_mixer.png");
|
||||
loadMenuIcon(ICON_MODEL_OUTPUTS, "mask_model_outputs.png");
|
||||
loadMenuIcon(ICON_MODEL_CURVES, "mask_model_curves.png");
|
||||
loadMenuIcon(ICON_MODEL_GVARS, "mask_model_gvars.png");
|
||||
loadMenuIcon(ICON_MODEL_LOGICAL_SWITCHES, "mask_model_logical_switches.png");
|
||||
loadMenuIcon(ICON_MODEL_SPECIAL_FUNCTIONS, "mask_model_special_functions.png");
|
||||
loadMenuIcon(ICON_MODEL_LUA_SCRIPTS, "mask_model_lua_scripts.png");
|
||||
loadMenuIcon(ICON_MODEL_TELEMETRY, "mask_model_telemetry.png");
|
||||
loadMenuIcon(ICON_STATS, "mask_menu_stats.png");
|
||||
loadMenuIcon(ICON_STATS_THROTTLE_GRAPH, "mask_stats_throttle_graph.png");
|
||||
loadMenuIcon(ICON_STATS_TIMERS, "mask_stats_timers.png");
|
||||
loadMenuIcon(ICON_STATS_ANALOGS, "mask_stats_analogs.png");
|
||||
loadMenuIcon(ICON_STATS_DEBUG, "mask_stats_debug.png");
|
||||
loadMenuIcon(ICON_THEME, "mask_menu_theme.png");
|
||||
loadMenuIcon(ICON_THEME_SETUP, "mask_theme_setup.png");
|
||||
loadMenuIcon(ICON_THEME_VIEW1, "mask_theme_view1.png");
|
||||
loadMenuIcon(ICON_THEME_VIEW2, "mask_theme_view2.png");
|
||||
loadMenuIcon(ICON_THEME_VIEW3, "mask_theme_view3.png");
|
||||
loadMenuIcon(ICON_THEME_VIEW4, "mask_theme_view4.png");
|
||||
loadMenuIcon(ICON_THEME_VIEW5, "mask_theme_view5.png");
|
||||
loadMenuIcon(ICON_THEME_ADD_VIEW, "mask_theme_add_view.png");
|
||||
loadMenuIcon(ICON_MONITOR, "mask_monitor.png");
|
||||
loadMenuIcon(ICON_MONITOR_CHANNELS1, "mask_monitor_channels1.png");
|
||||
loadMenuIcon(ICON_MONITOR_CHANNELS2, "mask_monitor_channels2.png");
|
||||
loadMenuIcon(ICON_MONITOR_CHANNELS3, "mask_monitor_channels3.png");
|
||||
loadMenuIcon(ICON_MONITOR_CHANNELS4, "mask_monitor_channels4.png");
|
||||
loadMenuIcon(ICON_MONITOR_LOGICAL_SWITCHES, "/mask_monitor_logsw.png");
|
||||
|
||||
BitmapBuffer * background = BitmapBuffer::loadMask(getThemePath("mask_currentmenu_bg.png"));
|
||||
BitmapBuffer * shadow = BitmapBuffer::loadMask(getThemePath("mask_currentmenu_shadow.png"));
|
||||
BitmapBuffer * dot = BitmapBuffer::loadMask(getThemePath("mask_currentmenu_dot.png"));
|
||||
|
||||
if (!currentMenuBackground) currentMenuBackground = new BitmapBuffer(BMP_RGB565, 36, 53);
|
||||
currentMenuBackground->drawSolidFilledRect(0, 0, currentMenuBackground->getWidth(), MENU_HEADER_HEIGHT, HEADER_BGCOLOR);
|
||||
currentMenuBackground->drawSolidFilledRect(0, MENU_HEADER_HEIGHT, currentMenuBackground->getWidth(), MENU_TITLE_TOP - MENU_HEADER_HEIGHT, TEXT_BGCOLOR);
|
||||
currentMenuBackground->drawSolidFilledRect(0, MENU_TITLE_TOP, currentMenuBackground->getWidth(), currentMenuBackground->getHeight() - MENU_TITLE_TOP, TITLE_BGCOLOR);
|
||||
currentMenuBackground->drawMask(0, 0, background, HEADER_CURRENT_BGCOLOR);
|
||||
currentMenuBackground->drawMask(0, 0, shadow, TRIM_SHADOW_COLOR);
|
||||
currentMenuBackground->drawMask(10, 39, dot, MENU_TITLE_COLOR);
|
||||
|
||||
delete topleftBitmap;
|
||||
topleftBitmap = BitmapBuffer::loadMaskOnBackground("topleft.png", TITLE_BGCOLOR, HEADER_BGCOLOR);
|
||||
|
||||
delete background;
|
||||
delete shadow;
|
||||
delete dot;
|
||||
}
|
||||
|
||||
void loadThemeBitmaps() const
|
||||
{
|
||||
// Model Selection screen
|
||||
delete modelselIconBitmap;
|
||||
modelselIconBitmap = BitmapBuffer::loadMaskOnBackground("modelsel/mask_iconback.png", TITLE_BGCOLOR, TEXT_BGCOLOR);
|
||||
if (modelselIconBitmap) {
|
||||
BitmapBuffer * bitmap = BitmapBuffer::load(getThemePath("modelsel/icon_default.png"));
|
||||
modelselIconBitmap->drawBitmap(25, 8, bitmap);
|
||||
delete bitmap;
|
||||
}
|
||||
|
||||
delete modelselSdFreeBitmap;
|
||||
modelselSdFreeBitmap = BitmapBuffer::loadMaskOnBackground("modelsel/mask_sdfree.png", TEXT_COLOR, TEXT_BGCOLOR);
|
||||
|
||||
delete modelselModelQtyBitmap;
|
||||
modelselModelQtyBitmap = BitmapBuffer::loadMaskOnBackground("modelsel/mask_modelqty.png", TEXT_COLOR, TEXT_BGCOLOR);
|
||||
|
||||
delete modelselModelMoveBackground;
|
||||
modelselModelMoveBackground = BitmapBuffer::loadMask(getThemePath("modelsel/mask_moveback.png"));
|
||||
|
||||
delete modelselModelMoveIcon;
|
||||
modelselModelMoveIcon = BitmapBuffer::loadMask(getThemePath("modelsel/mask_moveico.png"));
|
||||
|
||||
// Channels monitor screen
|
||||
delete chanMonLockedBitmap;
|
||||
chanMonLockedBitmap = BitmapBuffer::loadMaskOnBackground("mask_monitor_lockch.png", TEXT_COLOR, TEXT_BGCOLOR);
|
||||
|
||||
delete chanMonInvertedBitmap;
|
||||
chanMonInvertedBitmap = BitmapBuffer::loadMaskOnBackground("mask_monitor_inver.png", TEXT_COLOR, TEXT_BGCOLOR);
|
||||
|
||||
// Mixer setup screen
|
||||
delete mixerSetupMixerBitmap;
|
||||
mixerSetupMixerBitmap = BitmapBuffer::loadMaskOnBackground("mask_sbar_mixer.png", MENU_TITLE_COLOR, HEADER_BGCOLOR);
|
||||
|
||||
delete mixerSetupToBitmap;
|
||||
mixerSetupToBitmap = BitmapBuffer::loadMaskOnBackground("mask_sbar_to.png", MENU_TITLE_COLOR, HEADER_BGCOLOR);
|
||||
|
||||
delete mixerSetupOutputBitmap;
|
||||
mixerSetupOutputBitmap = BitmapBuffer::loadMaskOnBackground("mask_sbar_output.png", MENU_TITLE_COLOR, HEADER_BGCOLOR);
|
||||
|
||||
delete mixerSetupAddBitmap;
|
||||
mixerSetupAddBitmap = BitmapBuffer::loadMaskOnBackground("mask_mplex_add.png", TEXT_COLOR, TEXT_BGCOLOR);
|
||||
|
||||
delete mixerSetupMultiBitmap;
|
||||
mixerSetupMultiBitmap = BitmapBuffer::loadMaskOnBackground("mask_mplex_multi.png", TEXT_COLOR, TEXT_BGCOLOR);
|
||||
|
||||
delete mixerSetupReplaceBitmap;
|
||||
mixerSetupReplaceBitmap = BitmapBuffer::loadMaskOnBackground("mask_mplex_replace.png", TEXT_COLOR, TEXT_BGCOLOR);
|
||||
|
||||
delete mixerSetupLabelBitmap;
|
||||
mixerSetupLabelBitmap = BitmapBuffer::loadMaskOnBackground("mask_textline_label.png", TEXT_COLOR, TEXT_BGCOLOR);
|
||||
|
||||
delete mixerSetupCurveBitmap;
|
||||
mixerSetupCurveBitmap = BitmapBuffer::loadMaskOnBackground("mask_textline_curve.png", TEXT_COLOR, TEXT_BGCOLOR);
|
||||
|
||||
delete mixerSetupSwitchBitmap;
|
||||
mixerSetupSwitchBitmap = BitmapBuffer::loadMaskOnBackground("mask_textline_switch.png", TEXT_COLOR, TEXT_BGCOLOR);
|
||||
|
||||
delete mixerSetupFlightmodeBitmap;
|
||||
mixerSetupFlightmodeBitmap = BitmapBuffer::loadMaskOnBackground("mask_textline_fm.png", TEXT_COLOR, TEXT_BGCOLOR);
|
||||
|
||||
delete mixerSetupSlowBitmap;
|
||||
mixerSetupSlowBitmap = BitmapBuffer::loadMaskOnBackground("mask_textline_slow.png", TEXT_COLOR, TEXT_BGCOLOR);
|
||||
|
||||
delete mixerSetupDelayBitmap;
|
||||
mixerSetupDelayBitmap = BitmapBuffer::loadMaskOnBackground("mask_textline_delay.png", TEXT_COLOR, TEXT_BGCOLOR);
|
||||
|
||||
delete mixerSetupDelaySlowBitmap;
|
||||
mixerSetupDelaySlowBitmap = BitmapBuffer::loadMaskOnBackground("mask_textline_delayslow.png", TEXT_COLOR, TEXT_BGCOLOR);
|
||||
}
|
||||
|
||||
virtual void load() const
|
||||
{
|
||||
loadColors();
|
||||
Theme::load();
|
||||
if (!backgroundBitmap) {
|
||||
backgroundBitmap = BitmapBuffer::load(getThemePath("background.png"));
|
||||
}
|
||||
update();
|
||||
}
|
||||
|
||||
virtual void update() const
|
||||
{
|
||||
uint32_t color = g_eeGeneral.themeData.options[1].unsignedValue;
|
||||
lcdColorTable[TEXT_INVERTED_BGCOLOR_INDEX] = color;
|
||||
lcdColorTable[SCROLLBOX_COLOR_INDEX] = color;
|
||||
lcdColorTable[CURVE_COLOR_INDEX] = color;
|
||||
lcdColorTable[CURVE_CURSOR_COLOR_INDEX] = color;
|
||||
lcdColorTable[TITLE_BGCOLOR_INDEX] = color;
|
||||
lcdColorTable[TRIM_BGCOLOR_INDEX] = color;
|
||||
lcdColorTable[MAINVIEW_GRAPHICS_COLOR_INDEX] = color;
|
||||
lcdColorTable[HEADER_BGCOLOR_INDEX] = RGB(GET_RED(color)-69, GET_GREEN(color)-32, GET_BLUE(color)-24);
|
||||
lcdColorTable[HEADER_ICON_BGCOLOR_INDEX] = color;
|
||||
lcdColorTable[HEADER_CURRENT_BGCOLOR_INDEX] = color;
|
||||
loadIcons();
|
||||
loadThemeBitmaps();
|
||||
loadFontCache();
|
||||
}
|
||||
|
||||
virtual void drawBackground() const
|
||||
{
|
||||
if (backgroundBitmap) {
|
||||
lcd->drawBitmap(0, 0, backgroundBitmap);
|
||||
}
|
||||
else {
|
||||
lcdSetColor(g_eeGeneral.themeData.options[0].unsignedValue);
|
||||
lcdDrawSolidFilledRect(0, 0, LCD_W, LCD_H, CUSTOM_COLOR);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void drawTopbarBackground(uint8_t icon) const
|
||||
{
|
||||
if (topleftBitmap) {
|
||||
lcd->drawBitmap(0, 0, topleftBitmap);
|
||||
uint16_t width = topleftBitmap->getWidth();
|
||||
lcd->drawSolidFilledRect(width, 0, LCD_W-width, MENU_HEADER_HEIGHT, HEADER_BGCOLOR);
|
||||
}
|
||||
else {
|
||||
lcd->drawSolidFilledRect(0, 0, LCD_W, MENU_HEADER_HEIGHT, HEADER_BGCOLOR);
|
||||
}
|
||||
|
||||
if (icon == ICON_OPENTX)
|
||||
lcd->drawBitmap(4, 10, menuIconSelected[ICON_OPENTX]);
|
||||
else
|
||||
lcd->drawBitmap(5, 7, menuIconSelected[icon]);
|
||||
|
||||
drawTopbarDatetime();
|
||||
}
|
||||
|
||||
virtual void drawMenuIcon(uint8_t index, uint8_t position, bool selected) const
|
||||
{
|
||||
if (selected) {
|
||||
lcd->drawBitmap(58+position*MENU_ICONS_SPACING-10, 0, currentMenuBackground);
|
||||
lcd->drawBitmap(50+position*MENU_ICONS_SPACING, 7, menuIconSelected[index], MENU_TITLE_COLOR);
|
||||
}
|
||||
else {
|
||||
lcd->drawBitmap(50+position*MENU_ICONS_SPACING, 7, menuIconNormal[index], MENU_TITLE_COLOR);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
static const BitmapBuffer * backgroundBitmap;
|
||||
static BitmapBuffer * topleftBitmap;
|
||||
static BitmapBuffer * menuIconNormal[MENUS_ICONS_COUNT];
|
||||
static BitmapBuffer * menuIconSelected[MENUS_ICONS_COUNT];
|
||||
static BitmapBuffer * currentMenuBackground;
|
||||
};
|
||||
|
||||
const BitmapBuffer * DefaultTheme::backgroundBitmap = NULL;
|
||||
BitmapBuffer * DefaultTheme::topleftBitmap = NULL;
|
||||
BitmapBuffer * DefaultTheme::menuIconNormal[MENUS_ICONS_COUNT] = { NULL };
|
||||
BitmapBuffer * DefaultTheme::menuIconSelected[MENUS_ICONS_COUNT] = { NULL };
|
||||
BitmapBuffer * DefaultTheme::currentMenuBackground = NULL;
|
||||
|
||||
DefaultTheme defaultTheme;
|
||||
Theme * theme = &defaultTheme;
|
||||
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "opentx.h"
|
||||
|
||||
unsigned int Topbar::getZonesCount() const
|
||||
{
|
||||
return MAX_TOPBAR_ZONES;
|
||||
}
|
||||
|
||||
Zone Topbar::getZone(unsigned int index) const
|
||||
{
|
||||
Zone zone;
|
||||
zone.x = 49 + (TOPBAR_ZONE_WIDTH + 2*TOPBAR_ZONE_MARGIN) * index;
|
||||
zone.y = TOPBAR_ZONE_MARGIN;
|
||||
zone.w = TOPBAR_ZONE_WIDTH;
|
||||
zone.h = MENU_HEADER_HEIGHT - 2*TOPBAR_ZONE_MARGIN;
|
||||
return zone;
|
||||
}
|
||||
|
||||
const char * const STR_MONTHS[] = TR_MONTHS;
|
||||
|
||||
void drawTopbarDatetime()
|
||||
{
|
||||
lcd->drawSolidVerticalLine(DATETIME_SEPARATOR_X, 7, 31, TEXT_INVERTED_COLOR);
|
||||
|
||||
struct gtm t;
|
||||
gettime(&t);
|
||||
char str[10];
|
||||
sprintf(str, "%d %s", t.tm_mday, STR_MONTHS[t.tm_mon]);
|
||||
lcdDrawText(DATETIME_MIDDLE, DATETIME_LINE1, str, SMLSIZE|TEXT_INVERTED_COLOR|CENTERED);
|
||||
|
||||
getTimerString(str, getValue(MIXSRC_TX_TIME));
|
||||
lcdDrawText(DATETIME_MIDDLE, DATETIME_LINE2, str, SMLSIZE|TEXT_INVERTED_COLOR|CENTERED);
|
||||
}
|
||||
|
||||
void drawTopBar()
|
||||
{
|
||||
theme->drawTopbarBackground(0);
|
||||
|
||||
// USB icon
|
||||
if (usbPlugged()) {
|
||||
lcdDrawBitmapPattern(LCD_W-98, 8, LBM_TOPMENU_USB, MENU_TITLE_COLOR);
|
||||
}
|
||||
|
||||
// RSSI
|
||||
const uint8_t rssiBarsValue[] = {30, 40, 50, 60, 80};
|
||||
const uint8_t rssiBarsHeight[] = {5, 10, 15, 21, 31};
|
||||
for (unsigned int i = 0; i < DIM(rssiBarsHeight); i++) {
|
||||
uint8_t height = rssiBarsHeight[i];
|
||||
lcdDrawSolidFilledRect(LCD_W-90 + i * 6, 38 - height, 4, height, TELEMETRY_RSSI() >= rssiBarsValue[i] ? MENU_TITLE_COLOR : MENU_TITLE_DISABLE_COLOR);
|
||||
}
|
||||
|
||||
/* Audio volume */
|
||||
lcdDrawBitmapPattern(LCD_W-130, 4, LBM_TOPMENU_VOLUME_SCALE, MENU_TITLE_DISABLE_COLOR);
|
||||
if (requiredSpeakerVolume == 0 || g_eeGeneral.beepMode == e_mode_quiet)
|
||||
lcdDrawBitmapPattern(LCD_W-130, 4, LBM_TOPMENU_VOLUME_0, MENU_TITLE_COLOR);
|
||||
else if (requiredSpeakerVolume < 7)
|
||||
lcdDrawBitmapPattern(LCD_W-130, 4, LBM_TOPMENU_VOLUME_1, MENU_TITLE_COLOR);
|
||||
else if (requiredSpeakerVolume < 13)
|
||||
lcdDrawBitmapPattern(LCD_W-130, 4, LBM_TOPMENU_VOLUME_2, MENU_TITLE_COLOR);
|
||||
else if (requiredSpeakerVolume < 19)
|
||||
lcdDrawBitmapPattern(LCD_W-130, 4, LBM_TOPMENU_VOLUME_3, MENU_TITLE_COLOR);
|
||||
else
|
||||
lcdDrawBitmapPattern(LCD_W-130, 4, LBM_TOPMENU_VOLUME_4, MENU_TITLE_COLOR);
|
||||
|
||||
/* Tx battery */
|
||||
uint8_t bars = limit<int8_t>(0, 6 * (g_vbat100mV - g_eeGeneral.vBatMin - 90) / (30 + g_eeGeneral.vBatMax - g_eeGeneral.vBatMin), 5);
|
||||
lcdDrawBitmapPattern(LCD_W-130, 24, LBM_TOPMENU_TXBATT, MENU_TITLE_COLOR);
|
||||
for (unsigned int i = 0; i < 5; i++) {
|
||||
lcdDrawSolidFilledRect(LCD_W-122+4*i, 30, 2, 8, i >= bars ? MENU_TITLE_DISABLE_COLOR : MENU_TITLE_COLOR);
|
||||
}
|
||||
|
||||
topbar->refresh();
|
||||
|
||||
#if 0
|
||||
// Radio battery - TODO
|
||||
// putsValueWithUnit(370, 8, g_vbat100mV, UNIT_VOLTS, PREC1|SMLSIZE|MENU_TITLE_COLOR);
|
||||
// lcdDrawSolidRect(300, 3, 20, 50, MENU_TITLE_COLOR);
|
||||
// lcdDrawRect(batt_icon_x+FW, BAR_Y+1, 13, 7);
|
||||
// lcdDrawSolidVerticalLine(batt_icon_x+FW+13, BAR_Y+2, 5);
|
||||
|
||||
// Rx battery
|
||||
if (g_model.frsky.voltsSource) { // TODO should not be in frsky struct
|
||||
TelemetryItem & item = telemetryItems[g_model.frsky.voltsSource-1];
|
||||
if (item.isAvailable()) {
|
||||
int32_t value = item.value;
|
||||
TelemetrySensor & sensor = g_model.telemetrySensors[g_model.frsky.altitudeSource-1];
|
||||
LcdFlags att = 0;
|
||||
if (sensor.prec == 2) {
|
||||
att |= PREC1;
|
||||
value /= 10;
|
||||
}
|
||||
else if (sensor.prec == 1) {
|
||||
att |= PREC1;
|
||||
}
|
||||
att |= (item.isOld() ? ALARM_COLOR : TEXT_COLOR);
|
||||
lcdDrawSolidFilledRect(ALTITUDE_X, VOLTS_Y, ALTITUDE_W, ALTITUDE_H, TEXT_BGCOLOR);
|
||||
lcdDrawText(ALTITUDE_X+PADDING, VOLTS_Y+2, "Voltage", att);
|
||||
putsValueWithUnit(ALTITUDE_X+PADDING, VOLTS_Y+12, value, UNIT_VOLTS, DBLSIZE|LEFT|att);
|
||||
}
|
||||
}
|
||||
|
||||
// Model altitude
|
||||
if (g_model.frsky.altitudeSource) {
|
||||
TelemetryItem & item = telemetryItems[g_model.frsky.altitudeSource-1];
|
||||
if (item.isAvailable()) {
|
||||
int32_t value = item.value;
|
||||
TelemetrySensor & sensor = g_model.telemetrySensors[g_model.frsky.altitudeSource-1];
|
||||
if (sensor.prec) value /= sensor.prec == 2 ? 100 : 10;
|
||||
LcdFlags att = (item.isOld() ? ALARM_COLOR : TEXT_COLOR);
|
||||
lcdDrawSolidFilledRect(ALTITUDE_X, ALTITUDE_Y, ALTITUDE_W, ALTITUDE_H, TEXT_BGCOLOR);
|
||||
lcdDrawText(ALTITUDE_X+PADDING, ALTITUDE_Y+2, "Alt", att);
|
||||
putsValueWithUnit(ALTITUDE_X+PADDING, ALTITUDE_Y+12, value, UNIT_METERS, DBLSIZE|LEFT|att);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _TOPBAR_H_
|
||||
#define _TOPBAR_H_
|
||||
|
||||
#include "widgets_container.h"
|
||||
|
||||
#define MAX_TOPBAR_ZONES 4
|
||||
#define MAX_TOPBAR_OPTIONS 1 // just because of VC++ which doesn't like 0-size arrays :(
|
||||
#define TOPBAR_ZONE_WIDTH 70
|
||||
#define TOPBAR_ZONE_MARGIN 3
|
||||
|
||||
class Topbar: public WidgetsContainer<MAX_TOPBAR_ZONES, MAX_TOPBAR_OPTIONS>
|
||||
{
|
||||
public:
|
||||
explicit Topbar(PersistentData * persistentData):
|
||||
WidgetsContainer<MAX_TOPBAR_ZONES, MAX_TOPBAR_OPTIONS>(persistentData)
|
||||
{
|
||||
}
|
||||
|
||||
virtual unsigned int getZonesCount() const;
|
||||
|
||||
virtual Zone getZone(unsigned int index) const;
|
||||
};
|
||||
|
||||
#endif // _TOPBAR_H_
|
||||
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "../../opentx.h"
|
||||
|
||||
enum AboutScreens {
|
||||
ABOUT_OPENTX,
|
||||
ABOUT_BERTRAND,
|
||||
ABOUT_ANDRE,
|
||||
ABOUT_MIKE,
|
||||
ABOUT_KJELL,
|
||||
ABOUT_MARTIN,
|
||||
ABOUT_ROMOLO,
|
||||
ABOUT_ROB,
|
||||
ABOUT_HARDWARE,
|
||||
ABOUT_PARENTS,
|
||||
ABOUT_END,
|
||||
ABOUT_COUNT,
|
||||
};
|
||||
|
||||
#define ABOUT_X 62
|
||||
#define ABOUT_INDENT 16
|
||||
|
||||
bool menuAboutView(evt_t event)
|
||||
{
|
||||
static uint8_t screenIndex = 0;
|
||||
static uint8_t greyIndex = 0;
|
||||
|
||||
switch(event)
|
||||
{
|
||||
case EVT_ENTRY:
|
||||
screenIndex = 0;
|
||||
greyIndex = 0;
|
||||
break;
|
||||
case EVT_KEY_FIRST(KEY_RIGHT):
|
||||
screenIndex < ABOUT_PARENTS ? screenIndex++ : screenIndex = ABOUT_OPENTX;
|
||||
greyIndex = 0;
|
||||
break;
|
||||
case EVT_KEY_FIRST(KEY_LEFT):
|
||||
screenIndex > ABOUT_OPENTX ? screenIndex-- : screenIndex = ABOUT_PARENTS;
|
||||
greyIndex = 0;
|
||||
break;
|
||||
case EVT_KEY_FIRST(KEY_EXIT):
|
||||
chainMenu(menuMainView);
|
||||
break;
|
||||
}
|
||||
|
||||
theme->drawBackground();
|
||||
theme->drawTopbarBackground(0);
|
||||
|
||||
uint8_t screenDuration = 150;
|
||||
|
||||
switch (screenIndex) {
|
||||
case ABOUT_OPENTX:
|
||||
case ABOUT_END:
|
||||
lcdDrawText(ABOUT_X, MENU_CONTENT_TOP, STR_ABOUT_OPENTX_1);
|
||||
lcdDrawText(ABOUT_X, MENU_CONTENT_TOP + FH, STR_ABOUT_OPENTX_2);
|
||||
lcdDrawText(ABOUT_X, MENU_CONTENT_TOP + 2*FH, STR_ABOUT_OPENTX_3);
|
||||
lcdDrawText(ABOUT_X, MENU_CONTENT_TOP + 3*FH, STR_ABOUT_OPENTX_4);
|
||||
lcdDrawText(ABOUT_X, MENU_CONTENT_TOP + 4*FH, STR_ABOUT_OPENTX_5);
|
||||
screenDuration = 255;
|
||||
break;
|
||||
|
||||
case ABOUT_BERTRAND:
|
||||
lcdDrawText(ABOUT_X, MENU_CONTENT_TOP, STR_ABOUT_BERTRAND_1, INVERS);
|
||||
lcdDrawText(ABOUT_X+ABOUT_INDENT, MENU_CONTENT_TOP + FH, STR_ABOUT_BERTRAND_2);
|
||||
lcdDrawText(ABOUT_X+ABOUT_INDENT, MENU_CONTENT_TOP + 2*FH, STR_ABOUT_BERTRAND_3);
|
||||
break;
|
||||
|
||||
case ABOUT_ANDRE:
|
||||
lcdDrawText(ABOUT_X, MENU_CONTENT_TOP, STR_ABOUT_ANDRE_1, INVERS);
|
||||
lcdDrawText(ABOUT_X+ABOUT_INDENT, MENU_CONTENT_TOP + FH, STR_ABOUT_ANDRE_2);
|
||||
lcdDrawText(ABOUT_X+ABOUT_INDENT, MENU_CONTENT_TOP + 2*FH, STR_ABOUT_ANDRE_3);
|
||||
break;
|
||||
|
||||
case ABOUT_MIKE:
|
||||
lcdDrawText(ABOUT_X, MENU_CONTENT_TOP, STR_ABOUT_MIKE_1, INVERS);
|
||||
lcdDrawText(ABOUT_X+ABOUT_INDENT, MENU_CONTENT_TOP + FH, STR_ABOUT_MIKE_2);
|
||||
lcdDrawText(ABOUT_X+ABOUT_INDENT, MENU_CONTENT_TOP + 2*FH, STR_ABOUT_MIKE_3);
|
||||
lcdDrawText(ABOUT_X+ABOUT_INDENT, MENU_CONTENT_TOP + 3*FH, STR_ABOUT_MIKE_4);
|
||||
break;
|
||||
|
||||
case ABOUT_KJELL:
|
||||
lcdDrawText(ABOUT_X, MENU_CONTENT_TOP, STR_ABOUT_KJELL_1, INVERS);
|
||||
lcdDrawText(ABOUT_X+ABOUT_INDENT, MENU_CONTENT_TOP + FH, STR_ABOUT_KJELL_2);
|
||||
lcdDrawText(ABOUT_X+ABOUT_INDENT, MENU_CONTENT_TOP + 2*FH, STR_ABOUT_KJELL_3);
|
||||
lcdDrawText(ABOUT_X+ABOUT_INDENT, MENU_CONTENT_TOP + 3*FH, STR_ABOUT_KJELL_4);
|
||||
break;
|
||||
|
||||
case ABOUT_MARTIN:
|
||||
lcdDrawText(ABOUT_X, MENU_CONTENT_TOP, STR_ABOUT_MARTIN_1, INVERS);
|
||||
lcdDrawText(ABOUT_X+ABOUT_INDENT, MENU_CONTENT_TOP + FH, STR_ABOUT_MARTIN_2);
|
||||
break;
|
||||
|
||||
case ABOUT_ROMOLO:
|
||||
lcdDrawText(ABOUT_X, MENU_CONTENT_TOP, STR_ABOUT_ROMOLO_1, INVERS);
|
||||
lcdDrawText(ABOUT_X+ABOUT_INDENT, MENU_CONTENT_TOP + FH, STR_ABOUT_ROMOLO_2);
|
||||
lcdDrawText(ABOUT_X+ABOUT_INDENT, MENU_CONTENT_TOP + 2*FH, STR_ABOUT_ROMOLO_3);
|
||||
break;
|
||||
|
||||
case ABOUT_ROB:
|
||||
lcdDrawText(ABOUT_X, MENU_CONTENT_TOP, STR_ABOUT_ROB_1, INVERS);
|
||||
lcdDrawText(ABOUT_X+ABOUT_INDENT, MENU_CONTENT_TOP + FH, STR_ABOUT_ROB_2);
|
||||
break;
|
||||
|
||||
case ABOUT_HARDWARE:
|
||||
lcdDrawText(ABOUT_X, MENU_CONTENT_TOP, "FrSky", INVERS);
|
||||
lcdDrawText(ABOUT_X+ABOUT_INDENT, MENU_CONTENT_TOP + FH, STR_ABOUT_HARDWARE_2);
|
||||
lcdDrawText(ABOUT_X+ABOUT_INDENT, MENU_CONTENT_TOP + 2*FH, STR_ABOUT_HARDWARE_3);
|
||||
break;
|
||||
|
||||
case ABOUT_PARENTS:
|
||||
lcdDrawText(ABOUT_X, MENU_CONTENT_TOP, STR_ABOUT_PARENTS_1, INVERS);
|
||||
lcdDrawText(ABOUT_X+ABOUT_INDENT, MENU_CONTENT_TOP + FH, STR_ABOUT_PARENTS_2);
|
||||
lcdDrawText(ABOUT_X+ABOUT_INDENT, MENU_CONTENT_TOP + 2*FH, STR_ABOUT_PARENTS_3);
|
||||
lcdDrawText(ABOUT_X+ABOUT_INDENT, MENU_CONTENT_TOP + 3*FH, STR_ABOUT_PARENTS_4);
|
||||
screenDuration = 255;
|
||||
break;
|
||||
}
|
||||
|
||||
if (++greyIndex == screenDuration) {
|
||||
greyIndex = 0;
|
||||
if (++screenIndex == ABOUT_COUNT) {
|
||||
chainMenu(menuMainView);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "opentx.h"
|
||||
|
||||
#define RECT_OFFSET 80
|
||||
#define ROW_HEIGHT 42
|
||||
#define BAR_HEIGHT 13
|
||||
#define COLUMN_SIZE 200
|
||||
#define X_OFFSET 25
|
||||
#define Y_OFFSET 75
|
||||
#define Y_OUTBAR 15
|
||||
#define Y_MIXBAR 28
|
||||
#define LEG_COLORBOX 15
|
||||
|
||||
bool menuChannelsMonitor(evt_t event, uint8_t page);
|
||||
bool menuLogicalSwitches(evt_t);
|
||||
|
||||
template<int index>
|
||||
bool menuChannelsMonitor(evt_t event)
|
||||
{
|
||||
lastMonitorPage = e_MonChannelsFirst + index;
|
||||
MENU(STR_MONITOR_CHANNELS[index], MONITOR_ICONS, menuTabMonitors, lastMonitorPage, 0, { 0 });
|
||||
return menuChannelsMonitor(event, index);
|
||||
}
|
||||
|
||||
const MenuHandlerFunc menuTabMonitors[] PROGMEM = {
|
||||
menuChannelsMonitor<0>,
|
||||
menuChannelsMonitor<1>,
|
||||
menuChannelsMonitor<2>,
|
||||
menuChannelsMonitor<3>,
|
||||
menuLogicalSwitches
|
||||
};
|
||||
|
||||
uint8_t lastMonitorPage = 0;
|
||||
|
||||
uint16_t posOnBar(int16_t value_to100)
|
||||
{
|
||||
return divRoundClosest((value_to100 + (g_model.extendedLimits ? 150 : 100)) * COLUMN_SIZE, (g_model.extendedLimits ? 150 : 100) * 2);
|
||||
}
|
||||
|
||||
void drawOutputBarLimits(coord_t left, coord_t right, coord_t y)
|
||||
{
|
||||
lcd->drawSolidVerticalLine(left, y, BAR_HEIGHT, TEXT_COLOR);
|
||||
lcd->drawSolidHorizontalLine(left, y, 3, TEXT_COLOR);
|
||||
lcd->drawSolidHorizontalLine(left, y + BAR_HEIGHT - 1, 3, TEXT_COLOR);
|
||||
|
||||
lcd->drawSolidVerticalLine(--right, y, BAR_HEIGHT, TEXT_COLOR);
|
||||
lcd->drawSolidHorizontalLine(right - 3, y, 3, TEXT_COLOR);
|
||||
lcd->drawSolidHorizontalLine(right - 3, y + BAR_HEIGHT - 1, 3, TEXT_COLOR);
|
||||
}
|
||||
|
||||
void drawSingleMixerBar(coord_t x, coord_t y, coord_t w, coord_t h, uint8_t channel)
|
||||
{
|
||||
int16_t chanVal = calcRESXto100(ex_chans[channel]);
|
||||
int16_t displayVal = chanVal;
|
||||
chanVal = limit<int16_t>(-100, chanVal, 100);
|
||||
|
||||
lcdDrawSolidFilledRect(x, y, w, h, BARGRAPH_BGCOLOR);
|
||||
if (chanVal > 0) {
|
||||
lcdDrawSolidFilledRect(x + w / 2, y, divRoundClosest(chanVal * w, 200), h, BARGRAPH2_COLOR);
|
||||
lcdDrawNumber(x - 10 + w / 2, y - 2, displayVal, SMLSIZE | TEXT_COLOR | RIGHT, 0, NULL, "%");
|
||||
}
|
||||
else if (chanVal < 0) {
|
||||
uint16_t endpoint = x + w / 2;
|
||||
uint16_t size = divRoundClosest(-chanVal * w, 200);
|
||||
lcdDrawSolidFilledRect(endpoint - size, y, size, h, BARGRAPH2_COLOR);
|
||||
lcdDrawNumber(x + 10 + w / 2, y - 2, displayVal, SMLSIZE | TEXT_COLOR, 0, NULL, "%");
|
||||
}
|
||||
|
||||
lcd->drawSolidVerticalLine(x + w / 2, y, h, TEXT_COLOR);
|
||||
}
|
||||
|
||||
void drawSingleOutputBar(coord_t x, coord_t y, coord_t w, coord_t h, uint8_t channel)
|
||||
{
|
||||
int16_t chanVal = calcRESXto100(channelOutputs[channel]);
|
||||
int16_t displayVal = chanVal;
|
||||
|
||||
chanVal = limit<int16_t>(-100, chanVal, 100);
|
||||
|
||||
lcdDrawSolidFilledRect(x, y, w, h, BARGRAPH_BGCOLOR);
|
||||
if (chanVal > 0) {
|
||||
lcdDrawSolidFilledRect(x + w / 2, y, divRoundClosest(chanVal * w, 200), h, BARGRAPH1_COLOR);
|
||||
lcdDrawNumber(x - 10 + w / 2, y - 2, displayVal, SMLSIZE | TEXT_COLOR | RIGHT, 0, NULL, "%");
|
||||
}
|
||||
else if (chanVal < 0) {
|
||||
uint16_t endpoint = x + w / 2;
|
||||
uint16_t size = divRoundClosest(-chanVal * w, 200);
|
||||
lcdDrawSolidFilledRect(endpoint - size, y, size, h, BARGRAPH1_COLOR);
|
||||
lcdDrawNumber(x + 10 + w / 2, y - 2, displayVal, SMLSIZE | TEXT_COLOR, 0, NULL, "%");
|
||||
}
|
||||
|
||||
lcd->drawSolidVerticalLine(x + w / 2, y, h, TEXT_COLOR);
|
||||
}
|
||||
|
||||
void drawComboOutputBar(coord_t x, coord_t y, coord_t w, coord_t h, uint8_t channel)
|
||||
{
|
||||
char chanString[] = "Ch32 ";
|
||||
uint16_t limits = (g_model.extendedLimits ? 300 : 200);
|
||||
int16_t chanVal = calcRESXto100(channelOutputs[channel]);
|
||||
LimitData * ld = limitAddress(channel);
|
||||
|
||||
strAppendSigned(&chanString[2], channel + 1, 2);
|
||||
lcdDrawText(x, y, chanString, SMLSIZE | TEXT_COLOR | LEFT);
|
||||
|
||||
lcdDrawSizedText(x + 45, y, g_model.limitData[channel].name, sizeof(g_model.limitData[channel].name), SMLSIZE | TEXT_COLOR | LEFT | ZCHAR);
|
||||
int usValue = PPM_CH_CENTER(channel) + channelOutputs[channel] / 2;
|
||||
lcdDrawNumber(x + w, y, usValue, SMLSIZE | TEXT_COLOR | RIGHT, 0, NULL, STR_US);
|
||||
|
||||
lcdDrawSolidFilledRect(x, y + Y_OUTBAR, w, h, BARGRAPH_BGCOLOR);
|
||||
lcd->drawSolidVerticalLine(x + posOnBar(calcRESXto100(ld->offset)), y + Y_OUTBAR, h, MAINVIEW_GRAPHICS_COLOR);
|
||||
|
||||
if (chanVal > 0)
|
||||
lcdDrawNumber(x - 10 + w / 2, y + h, chanVal, SMLSIZE | TEXT_COLOR | RIGHT, 0, NULL, "%");
|
||||
else
|
||||
lcdDrawNumber(x + 10 + w / 2, y + h, chanVal, SMLSIZE | TEXT_COLOR, 0, NULL, "%");
|
||||
|
||||
chanVal = limit<int16_t>(-limits / 2, chanVal, limits / 2);
|
||||
if (posOnBar(chanVal) > posOnBar(calcRESXto100(ld->offset))) {
|
||||
lcdDrawSolidFilledRect(x + posOnBar(calcRESXto100(ld->offset)), y + Y_OUTBAR, posOnBar(chanVal) - posOnBar(calcRESXto100(ld->offset)), h, BARGRAPH1_COLOR);
|
||||
}
|
||||
else if (posOnBar(chanVal) < posOnBar(calcRESXto100(ld->offset))) {
|
||||
uint16_t endpoint = x + posOnBar(calcRESXto100(ld->offset));
|
||||
uint16_t size = posOnBar(calcRESXto100(ld->offset)) - posOnBar(chanVal);
|
||||
lcdDrawSolidFilledRect(endpoint - size, y + Y_OUTBAR, size, h, BARGRAPH1_COLOR);
|
||||
}
|
||||
|
||||
drawOutputBarLimits(x + posOnBar(-100 + ld->min / 10), x + posOnBar(100 + ld->max / 10), y + Y_OUTBAR);
|
||||
#if defined(OVERRIDE_CHANNEL_FUNCTION)
|
||||
if (safetyCh[channel] != OVERRIDE_CHANNEL_UNDEFINED) lcd->drawBitmap(x - X_OFFSET + 7, y + 7, chanMonLockedBitmap);
|
||||
#endif
|
||||
if (ld->revert) lcd->drawBitmap(x - X_OFFSET + 7, y + 25, chanMonInvertedBitmap);
|
||||
lcd->drawSolidVerticalLine(x + w / 2, y + Y_OUTBAR, h, TEXT_COLOR);
|
||||
}
|
||||
|
||||
coord_t drawChannelsMonitorLegend(coord_t x, const pm_char * s, int color)
|
||||
{
|
||||
lcdDrawSolidFilledRect(x, MENU_FOOTER_TOP + 2, LEG_COLORBOX + 2, LEG_COLORBOX + 2, BARGRAPH_BGCOLOR);
|
||||
lcdDrawSolidFilledRect(x + 1, MENU_FOOTER_TOP + 3, LEG_COLORBOX, LEG_COLORBOX, color);
|
||||
lcdDrawText(x + 20, MENU_FOOTER_TOP, s, TEXT_STATUSBAR_COLOR);
|
||||
return x + 25 + getTextWidth(s);
|
||||
}
|
||||
|
||||
bool menuChannelsMonitor(evt_t event, uint8_t page)
|
||||
{
|
||||
uint8_t channel = 8 * page;
|
||||
coord_t x, y = Y_OFFSET;
|
||||
|
||||
x = drawChannelsMonitorLegend(MENUS_MARGIN_LEFT, STR_MONITOR_OUTPUT_DESC, BARGRAPH1_COLOR);
|
||||
drawChannelsMonitorLegend(x, STR_MONITOR_MIXER_DESC, BARGRAPH2_COLOR);
|
||||
|
||||
x = X_OFFSET;
|
||||
for (uint8_t i = 0; i < 4; i++, channel++, y += ROW_HEIGHT) {
|
||||
drawComboOutputBar(x, y, COLUMN_SIZE, BAR_HEIGHT, channel);
|
||||
drawSingleMixerBar(x, y + Y_MIXBAR + 1, COLUMN_SIZE, BAR_HEIGHT, channel);
|
||||
}
|
||||
x = 1 + LCD_W / 2 + X_OFFSET;
|
||||
y = Y_OFFSET;
|
||||
for (uint8_t i = 0; i < 4; i++, channel++, y += ROW_HEIGHT) {
|
||||
drawComboOutputBar(x, y, COLUMN_SIZE, BAR_HEIGHT, channel);
|
||||
drawSingleMixerBar(x, y + Y_MIXBAR + 1, COLUMN_SIZE, BAR_HEIGHT, channel);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "opentx.h"
|
||||
|
||||
#define X_OFF 45
|
||||
#define Y_OFF 70
|
||||
#define HLINE_Y_OFF 215
|
||||
#define LS_COL_WIDTH 50
|
||||
#define LS_LINE_HEIGHT 17
|
||||
#define X_FUNC 50
|
||||
#define Y_FUNC 225
|
||||
|
||||
#define CSW_1ST_COLUMN 50
|
||||
#define CSW_2ND_COLUMN 120
|
||||
#define CSW_3RD_COLUMN 200
|
||||
#define CSW_4TH_COLUMN 280
|
||||
#define CSW_5TH_COLUMN 340
|
||||
#define CSW_6TH_COLUMN 390
|
||||
#define NUM_LOGICAL_SWITCH 64
|
||||
|
||||
extern void putsEdgeDelayParam(coord_t, coord_t, LogicalSwitchData *, uint8_t, uint8_t);
|
||||
|
||||
void displayLogicalSwitchedDetails(coord_t x, coord_t y, uint8_t idx)
|
||||
{
|
||||
LogicalSwitchData * cs = lswAddress(idx);
|
||||
lcdDrawTextAtIndex(x, y, STR_VCSWFUNC, cs->func, 0);
|
||||
// CSW params
|
||||
unsigned int cstate = lswFamily(cs->func);
|
||||
|
||||
if (cstate == LS_FAMILY_BOOL || cstate == LS_FAMILY_STICKY) {
|
||||
putsSwitches(CSW_2ND_COLUMN, y, cs->v1, 0);
|
||||
putsSwitches(CSW_3RD_COLUMN, y, cs->v2, 0);
|
||||
}
|
||||
else if (cstate == LS_FAMILY_EDGE) {
|
||||
putsSwitches(CSW_2ND_COLUMN, y, cs->v1, 0);
|
||||
putsEdgeDelayParam(CSW_3RD_COLUMN, y, cs, 0, 0);
|
||||
}
|
||||
else if (cstate == LS_FAMILY_COMP) {
|
||||
putsMixerSource(CSW_2ND_COLUMN, y, cs->v1, 0);
|
||||
putsMixerSource(CSW_3RD_COLUMN, y, cs->v2, 0);
|
||||
}
|
||||
else if (cstate == LS_FAMILY_TIMER) {
|
||||
lcdDrawNumber(CSW_2ND_COLUMN, y, lswTimerValue(cs->v1), LEFT | PREC1);
|
||||
lcdDrawNumber(CSW_3RD_COLUMN, y, lswTimerValue(cs->v2), LEFT | PREC1);
|
||||
}
|
||||
else {
|
||||
putsMixerSource(CSW_2ND_COLUMN, y, cs->v1, 0);
|
||||
putsChannelValue(CSW_3RD_COLUMN, y, cs->v1, cs->v1 <= MIXSRC_LAST_CH ? calc100toRESX(cs->v2) : cs->v2, LEFT);
|
||||
}
|
||||
|
||||
// CSW AND switch
|
||||
putsSwitches(CSW_4TH_COLUMN, y, cs->andsw, 0);
|
||||
|
||||
// CSW duration
|
||||
if (cs->duration > 0)
|
||||
lcdDrawNumber(CSW_5TH_COLUMN, y, cs->duration, PREC1 | LEFT);
|
||||
else
|
||||
lcdDrawTextAtIndex(CSW_5TH_COLUMN, y, STR_MMMINV, 0, 0);
|
||||
|
||||
// CSW delay
|
||||
if (cstate == LS_FAMILY_EDGE) {
|
||||
lcdDrawText(CSW_6TH_COLUMN, y, STR_NA);
|
||||
}
|
||||
else if (cs->delay > 0) {
|
||||
lcdDrawNumber(CSW_6TH_COLUMN, y, cs->delay, PREC1 | LEFT);
|
||||
}
|
||||
else {
|
||||
lcdDrawTextAtIndex(CSW_6TH_COLUMN, y, STR_MMMINV, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
bool menuLogicalSwitchesMonitor(evt_t event)
|
||||
{
|
||||
char lsString[] = "L64";
|
||||
lcdColorTable[CUSTOM_COLOR_INDEX] = RGB(160, 160, 160);
|
||||
for (uint8_t i = 0; i < NUM_LOGICAL_SWITCH; i++) {
|
||||
LcdFlags attr = (menuHorizontalPosition == i ? INVERS : 0) | LEFT;
|
||||
LogicalSwitchData * cs = lswAddress(i);
|
||||
strAppendSigned(&lsString[1], i + 1, 2);
|
||||
if (cs->func == LS_FUNC_NONE)
|
||||
attr += CUSTOM_COLOR;
|
||||
else if (getSwitch(SWSRC_SW1 + i))
|
||||
attr += BOLD;
|
||||
lcdDrawText(X_OFF + (i & 0x07) * LS_COL_WIDTH, Y_OFF + (i >> 3) * LS_LINE_HEIGHT, lsString, attr);
|
||||
}
|
||||
lcdDrawHorizontalLine(0, HLINE_Y_OFF, LCD_W, SOLID);
|
||||
if (lswAddress(menuHorizontalPosition)->func != LS_FUNC_NONE) {
|
||||
displayLogicalSwitchedDetails(X_FUNC, Y_FUNC, menuHorizontalPosition);
|
||||
}
|
||||
s_editMode = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool menuLogicalSwitches(evt_t event)
|
||||
{
|
||||
MENU(STR_MONITOR_SWITCHES, MONITOR_ICONS, menuTabMonitors, e_MonLogicalSwitches, 1, { NUM_LOGICAL_SWITCH - 1 });
|
||||
lastMonitorPage = e_MonLogicalSwitches;
|
||||
return menuLogicalSwitchesMonitor(event);
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "opentx.h"
|
||||
|
||||
#define TRIM_LH_X 10
|
||||
#define TRIM_LV_X 24
|
||||
#define TRIM_RV_X (LCD_W-35)
|
||||
#define TRIM_RH_X (LCD_W-175)
|
||||
#define TRIM_V_Y 55
|
||||
#define TRIM_H_Y (LCD_H-37)
|
||||
#define TRIM_LEN 80
|
||||
#define POTS_LINE_Y (LCD_H-20)
|
||||
|
||||
Layout * customScreens[MAX_CUSTOM_SCREENS] = { 0, 0, 0, 0, 0 };
|
||||
Topbar * topbar;
|
||||
|
||||
void drawMainPots()
|
||||
{
|
||||
// The 3 pots
|
||||
drawHorizontalSlider(TRIM_LH_X, POTS_LINE_Y, 160, calibratedStick[4], -RESX, RESX, 40, OPTION_SLIDER_TICKS | OPTION_SLIDER_BIG_TICKS | OPTION_SLIDER_SQUARE_BUTTON);
|
||||
drawHorizontalSlider(LCD_W/2-20, POTS_LINE_Y, XPOTS_MULTIPOS_COUNT*5, 1 + (potsPos[1] & 0x0f), 1, XPOTS_MULTIPOS_COUNT + 1, XPOTS_MULTIPOS_COUNT, OPTION_SLIDER_TICKS | OPTION_SLIDER_BIG_TICKS | OPTION_SLIDER_NUMBER_BUTTON);
|
||||
drawHorizontalSlider(TRIM_RH_X, POTS_LINE_Y, 160, calibratedStick[6], -RESX, RESX, 40, OPTION_SLIDER_TICKS | OPTION_SLIDER_BIG_TICKS | OPTION_SLIDER_SQUARE_BUTTON);
|
||||
|
||||
// The 2 rear sliders
|
||||
drawVerticalSlider(6, TRIM_V_Y, 160, calibratedStick[9], -RESX, RESX, 40, OPTION_SLIDER_TICKS | OPTION_SLIDER_BIG_TICKS | OPTION_SLIDER_SQUARE_BUTTON);
|
||||
drawVerticalSlider(LCD_W-18, TRIM_V_Y, 160, calibratedStick[10], -RESX, RESX, 40, OPTION_SLIDER_TICKS | OPTION_SLIDER_BIG_TICKS | OPTION_SLIDER_SQUARE_BUTTON);
|
||||
}
|
||||
|
||||
void drawTrims(uint8_t flightMode)
|
||||
{
|
||||
for (uint8_t i=0; i<4; i++) {
|
||||
static const coord_t x[4] = { TRIM_LH_X, TRIM_LV_X, TRIM_RV_X, TRIM_RH_X };
|
||||
static uint8_t vert[4] = {0, 1, 1, 0};
|
||||
unsigned int stickIndex = CONVERT_MODE(i);
|
||||
coord_t xm = x[stickIndex];
|
||||
int32_t trim = getTrimValue(flightMode, i);
|
||||
|
||||
if (vert[i]) {
|
||||
drawVerticalSlider(xm, TRIM_V_Y, 160, trim, -125, 125, 0, OPTION_SLIDER_EMPTY_BAR|OPTION_SLIDER_TRIM_BUTTON);
|
||||
if (g_model.displayTrims != DISPLAY_TRIMS_NEVER && trim != 0) {
|
||||
if (g_model.displayTrims == DISPLAY_TRIMS_ALWAYS || (trimsDisplayTimer > 0 && (trimsDisplayMask & (1<<i)))) {
|
||||
uint16_t y = TRIM_V_Y + TRIM_LEN + (trim<0 ? -TRIM_LEN/2 : TRIM_LEN/2);
|
||||
lcdDrawNumber(xm+2, y, trim, TINSIZE | CENTERED | VERTICAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
drawHorizontalSlider(xm, TRIM_H_Y, 160, trim, -125, 125, 0, OPTION_SLIDER_EMPTY_BAR|OPTION_SLIDER_TRIM_BUTTON);
|
||||
if (g_model.displayTrims != DISPLAY_TRIMS_NEVER && trim != 0) {
|
||||
if (g_model.displayTrims == DISPLAY_TRIMS_ALWAYS || (trimsDisplayTimer > 0 && (trimsDisplayMask & (1<<i)))) {
|
||||
uint16_t x = xm + TRIM_LEN + (trim>0 ? -TRIM_LEN/2 : TRIM_LEN/2);
|
||||
lcdDrawNumber(x, TRIM_H_Y+2, trim, TINSIZE | CENTERED);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void onMainViewMenu(const char *result)
|
||||
{
|
||||
if (result == STR_MODEL_SELECT) {
|
||||
chainMenu(menuModelSelect);
|
||||
}
|
||||
else if (result == STR_RESET_TIMER1) {
|
||||
timerReset(0);
|
||||
}
|
||||
else if (result == STR_RESET_TIMER2) {
|
||||
timerReset(1);
|
||||
}
|
||||
else if (result == STR_RESET_TIMER3) {
|
||||
timerReset(2);
|
||||
}
|
||||
else if (result == STR_VIEW_NOTES) {
|
||||
pushModelNotes();
|
||||
}
|
||||
else if (result == STR_RESET_SUBMENU) {
|
||||
POPUP_MENU_ADD_ITEM(STR_RESET_FLIGHT);
|
||||
POPUP_MENU_ADD_ITEM(STR_RESET_TIMER1);
|
||||
POPUP_MENU_ADD_ITEM(STR_RESET_TIMER2);
|
||||
POPUP_MENU_ADD_ITEM(STR_RESET_TIMER3);
|
||||
POPUP_MENU_ADD_ITEM(STR_RESET_TELEMETRY);
|
||||
}
|
||||
else if (result == STR_RESET_TELEMETRY) {
|
||||
telemetryReset();
|
||||
}
|
||||
else if (result == STR_RESET_FLIGHT) {
|
||||
flightReset();
|
||||
}
|
||||
else if (result == STR_STATISTICS) {
|
||||
pushMenu(menuTabStats[0]);
|
||||
}
|
||||
else if (result == STR_SETUP_SCREENS) {
|
||||
pushMenu(menuTabScreensSetup[1]);
|
||||
}
|
||||
else if (result == STR_ABOUT_US) {
|
||||
chainMenu(menuAboutView);
|
||||
}
|
||||
else if (result == STR_MONITOR_SCREENS) {
|
||||
pushMenu(menuTabMonitors[lastMonitorPage]);
|
||||
}
|
||||
}
|
||||
|
||||
int getMainViewsCount()
|
||||
{
|
||||
for (int index=1; index<MAX_CUSTOM_SCREENS; index++) {
|
||||
if (!customScreens[index]) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
return MAX_CUSTOM_SCREENS;
|
||||
}
|
||||
|
||||
bool menuMainView(evt_t event)
|
||||
{
|
||||
switch (event) {
|
||||
case EVT_ENTRY:
|
||||
killEvents(KEY_EXIT);
|
||||
killEvents(KEY_UP);
|
||||
killEvents(KEY_DOWN);
|
||||
break;
|
||||
|
||||
case EVT_KEY_LONG(KEY_ENTER):
|
||||
killEvents(event);
|
||||
POPUP_MENU_ADD_ITEM(STR_MODEL_SELECT);
|
||||
if (modelHasNotes()) {
|
||||
POPUP_MENU_ADD_ITEM(STR_VIEW_NOTES);
|
||||
}
|
||||
POPUP_MENU_ADD_ITEM(STR_MONITOR_SCREENS);
|
||||
POPUP_MENU_ADD_ITEM(STR_RESET_SUBMENU);
|
||||
POPUP_MENU_ADD_ITEM(STR_STATISTICS);
|
||||
POPUP_MENU_ADD_ITEM(STR_ABOUT_US);
|
||||
POPUP_MENU_START(onMainViewMenu);
|
||||
break;
|
||||
|
||||
case EVT_KEY_LONG(KEY_MODEL):
|
||||
killEvents(event);
|
||||
pushMenu(menuTabModel[0]);
|
||||
return false;
|
||||
|
||||
case EVT_KEY_LONG(KEY_RADIO):
|
||||
killEvents(event);
|
||||
pushMenu(menuTabGeneral[0]);
|
||||
return false;
|
||||
|
||||
case EVT_KEY_LONG(KEY_TELEM):
|
||||
killEvents(event);
|
||||
pushMenu(menuTabScreensSetup[1]);
|
||||
return false;
|
||||
|
||||
case EVT_KEY_FIRST(KEY_PGDN):
|
||||
storageDirty(EE_GENERAL);
|
||||
g_model.view = circularIncDec(g_model.view, +1, 0, getMainViewsCount()-1);
|
||||
break;
|
||||
|
||||
case EVT_KEY_FIRST(KEY_PGUP):
|
||||
killEvents(event);
|
||||
storageDirty(EE_GENERAL);
|
||||
g_model.view = circularIncDec(g_model.view, -1, 0, getMainViewsCount()-1);
|
||||
break;
|
||||
|
||||
case EVT_KEY_FIRST(KEY_EXIT):
|
||||
#if defined(GVARS)
|
||||
if (gvarDisplayTimer > 0) {
|
||||
gvarDisplayTimer = 0;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
if (g_model.view >= getMainViewsCount()) {
|
||||
g_model.view = 0;
|
||||
}
|
||||
|
||||
for (uint8_t i=0; i<MAX_CUSTOM_SCREENS; i++) {
|
||||
if (customScreens[i]) {
|
||||
if (i == g_model.view)
|
||||
customScreens[i]->refresh();
|
||||
else
|
||||
customScreens[i]->background();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#if 0
|
||||
bool menuMainViewChannelsMonitor(evt_t event)
|
||||
{
|
||||
switch (event) {
|
||||
case EVT_KEY_BREAK(KEY_EXIT):
|
||||
chainMenu(menuMainView);
|
||||
event = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
return menuChannelsView(event);
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,228 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "opentx.h"
|
||||
#include "stamp.h"
|
||||
|
||||
#define MENU_STATS_COLUMN1 (MENUS_MARGIN_LEFT + 120)
|
||||
#define MENU_STATS_COLUMN2 (LCD_W/2)
|
||||
#define MENU_STATS_COLUMN3 (LCD_W/2 + 120)
|
||||
|
||||
bool menuStatsGraph(evt_t event)
|
||||
{
|
||||
switch(event) {
|
||||
case EVT_KEY_LONG(KEY_ENTER):
|
||||
g_eeGeneral.globalTimer = 0;
|
||||
storageDirty(EE_GENERAL);
|
||||
sessionTimer = 0;
|
||||
killEvents(event);
|
||||
break;
|
||||
}
|
||||
|
||||
MENU("Statistics", STATS_ICONS, menuTabStats, e_StatsGraph, 0, { 0 });
|
||||
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, MENU_CONTENT_TOP, "Session");
|
||||
putsTimer(MENU_STATS_COLUMN1, MENU_CONTENT_TOP, sessionTimer, TIMEHOUR);
|
||||
lcdDrawText(MENU_STATS_COLUMN2, MENU_CONTENT_TOP, "Battery");
|
||||
putsTimer(MENU_STATS_COLUMN3, MENU_CONTENT_TOP, g_eeGeneral.globalTimer+sessionTimer, TIMEHOUR);
|
||||
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, MENU_CONTENT_TOP+FH, "Throttle");
|
||||
putsTimer(MENU_STATS_COLUMN1, MENU_CONTENT_TOP+FH, s_timeCumThr, TIMEHOUR);
|
||||
lcdDrawText(MENU_STATS_COLUMN2, MENU_CONTENT_TOP+FH, "Throttle %", TIMEHOUR);
|
||||
putsTimer(MENU_STATS_COLUMN3, MENU_CONTENT_TOP+FH, s_timeCum16ThrP/16, TIMEHOUR);
|
||||
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, MENU_CONTENT_TOP+2*FH, "Timers");
|
||||
lcdDrawText(MENU_STATS_COLUMN1, MENU_CONTENT_TOP+2*FH, "[1]", HEADER_COLOR);
|
||||
putsTimer(lcdNextPos+5, MENU_CONTENT_TOP+2*FH, timersStates[0].val, TIMEHOUR);
|
||||
lcdDrawText(MENU_STATS_COLUMN2, MENU_CONTENT_TOP+2*FH, "[2]", HEADER_COLOR);
|
||||
putsTimer(lcdNextPos+5, MENU_CONTENT_TOP+2*FH, timersStates[1].val, TIMEHOUR);
|
||||
#if TIMERS > 2
|
||||
lcdDrawText(MENU_STATS_COLUMN3, MENU_CONTENT_TOP+2*FH, "[3]", HEADER_COLOR);
|
||||
putsTimer(lcdNextPos+5, MENU_CONTENT_TOP+2*FH, timersStates[2].val, TIMEHOUR);
|
||||
#endif
|
||||
|
||||
const coord_t x = 10;
|
||||
const coord_t y = 240;
|
||||
lcdDrawHorizontalLine(x-3, y, MAXTRACE+3+3, SOLID, TEXT_COLOR);
|
||||
lcdDrawVerticalLine(x, y-96, 96+3, SOLID, TEXT_COLOR);
|
||||
for (coord_t i=0; i<MAXTRACE; i+=6) {
|
||||
lcdDrawVerticalLine(x+i, y-1, 3, SOLID, TEXT_COLOR);
|
||||
}
|
||||
|
||||
uint16_t traceRd = s_traceWr > MAXTRACE ? s_traceWr - MAXTRACE : 0;
|
||||
coord_t prev_yv = (coord_t)-1;
|
||||
for (coord_t i=1; i<=MAXTRACE && traceRd<s_traceWr; i++, traceRd++) {
|
||||
uint8_t h = s_traceBuf[traceRd % MAXTRACE];
|
||||
coord_t yv = y - 2 - 3*h;
|
||||
if (prev_yv != (coord_t)-1) {
|
||||
if (prev_yv < yv) {
|
||||
for (int y=prev_yv; y<=yv; y++) {
|
||||
lcdDrawBitmapPattern(x + i - 3, y, LBM_POINT, TEXT_COLOR);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int y=yv; y<=prev_yv; y++) {
|
||||
lcdDrawBitmapPattern(x + i - 3, y, LBM_POINT, TEXT_COLOR);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
lcdDrawBitmapPattern(x + i - 3, yv, LBM_POINT, TEXT_COLOR);
|
||||
}
|
||||
prev_yv = yv;
|
||||
}
|
||||
|
||||
lcdDrawText(LCD_W/2, MENU_FOOTER_TOP+2, STR_MENUTORESET, CENTERED);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool menuStatsDebug(evt_t event)
|
||||
{
|
||||
switch(event)
|
||||
{
|
||||
case EVT_KEY_FIRST(KEY_ENTER):
|
||||
maxMixerDuration = 0;
|
||||
#if defined(LUA)
|
||||
maxLuaInterval = 0;
|
||||
maxLuaDuration = 0;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
MENU("Debug", STATS_ICONS, menuTabStats, e_StatsDebug, 0, { 0 });
|
||||
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, MENU_CONTENT_TOP, "Free Mem");
|
||||
lcdDrawNumber(MENU_STATS_COLUMN1, MENU_CONTENT_TOP, availableMemory(), LEFT, 0, NULL, "b");
|
||||
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, MENU_CONTENT_TOP+FH, STR_TMIXMAXMS);
|
||||
lcdDrawNumber(MENU_STATS_COLUMN1, MENU_CONTENT_TOP+FH, DURATION_MS_PREC2(maxMixerDuration), PREC2|LEFT, 0, NULL, "ms");
|
||||
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, MENU_CONTENT_TOP+2*FH, STR_FREESTACKMINB);
|
||||
lcdDrawText(MENU_STATS_COLUMN1, MENU_CONTENT_TOP+2*FH+1, "[Menus]", HEADER_COLOR|SMLSIZE);
|
||||
lcdDrawNumber(lcdNextPos+5, MENU_CONTENT_TOP+2*FH, menusStack.available(), LEFT);
|
||||
lcdDrawText(lcdNextPos+20, MENU_CONTENT_TOP+2*FH+1, "[Mix]", HEADER_COLOR|SMLSIZE);
|
||||
lcdDrawNumber(lcdNextPos+5, MENU_CONTENT_TOP+2*FH, mixerStack.available(), LEFT);
|
||||
lcdDrawText(lcdNextPos+20, MENU_CONTENT_TOP+2*FH+1, "[Audio]", HEADER_COLOR|SMLSIZE);
|
||||
lcdDrawNumber(lcdNextPos+5, MENU_CONTENT_TOP+2*FH, audioStack.available(), LEFT);
|
||||
|
||||
int line = 3;
|
||||
|
||||
#if defined(DISK_CACHE)
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, MENU_CONTENT_TOP+line*FH, "SD cache hits");
|
||||
lcdDrawNumber(MENU_STATS_COLUMN1, MENU_CONTENT_TOP+line*FH, diskCache.getHitRate(), PREC1|LEFT, 0, NULL, "%");
|
||||
++line;
|
||||
#endif
|
||||
|
||||
#if defined(LUA)
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, MENU_CONTENT_TOP+line*FH, "Lua duration");
|
||||
lcdDrawNumber(MENU_STATS_COLUMN1, MENU_CONTENT_TOP+line*FH, 10*maxLuaDuration, LEFT, 0, NULL, "ms");
|
||||
++line;
|
||||
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, MENU_CONTENT_TOP+line*FH, "Lua interval");
|
||||
lcdDrawNumber(MENU_STATS_COLUMN1, MENU_CONTENT_TOP+line*FH, 10*maxLuaInterval, LEFT, 0, NULL, "ms");
|
||||
++line;
|
||||
#endif
|
||||
|
||||
lcdDrawText(LCD_W/2, MENU_FOOTER_TOP+2, STR_MENUTORESET, CENTERED);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool menuStatsAnalogs(evt_t event)
|
||||
{
|
||||
MENU("Analogs", STATS_ICONS, menuTabStats, e_StatsAnalogs, 0, { 0 });
|
||||
|
||||
for (uint8_t i=0; i<NUMBER_ANALOG; i++) {
|
||||
coord_t y = MENU_CONTENT_TOP + (i/2)*FH;
|
||||
coord_t x = MENUS_MARGIN_LEFT + (i & 1 ? LCD_W/2 : 0);
|
||||
lcdDrawNumber(x, y, i+1, LEADING0|LEFT, 2, NULL, ":");
|
||||
lcdDrawHexNumber(x+40, y, anaIn(i));
|
||||
#if defined(JITTER_MEASURE)
|
||||
lcdDrawNumber(x+100, y, rawJitter[i].get());
|
||||
lcdDrawNumber(x+140, y, avgJitter[i].get());
|
||||
lcdDrawNumber(x+180, y, (int16_t)calibratedStick[CONVERT_MODE(i)]*250/256, PREC1);
|
||||
#else
|
||||
if (i < NUM_STICKS+NUM_POTS)
|
||||
lcdDrawNumber(x+100, y, (int16_t)calibratedStick[CONVERT_MODE(i)]*25/256);
|
||||
else if (i >= MOUSE1)
|
||||
lcdDrawNumber(x+100, y, (int16_t)calibratedStick[NUM_STICKS+NUM_POTS+i-MOUSE1]*25/256);
|
||||
#endif
|
||||
}
|
||||
|
||||
// SWR
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, MENU_CONTENT_TOP+7*FH, "RAS");
|
||||
lcdDrawNumber(MENUS_MARGIN_LEFT+100, MENU_CONTENT_TOP+7*FH, telemetryData.swr.value);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
#if defined(DEBUG_TRACE_BUFFER)
|
||||
#define STATS_TRACES_INDEX_POS MENUS_MARGIN_LEFT
|
||||
#define STATS_TRACES_TIME_POS MENUS_MARGIN_LEFT + 4*10
|
||||
#define STATS_TRACES_EVENT_POS MENUS_MARGIN_LEFT + 14*10
|
||||
#define STATS_TRACES_DATA_POS MENUS_MARGIN_LEFT + 20*10
|
||||
|
||||
bool menuStatsTraces(evt_t event)
|
||||
{
|
||||
switch(event)
|
||||
{
|
||||
case EVT_KEY_LONG(KEY_ENTER):
|
||||
dumpTraceBuffer();
|
||||
killEvents(event);
|
||||
break;
|
||||
}
|
||||
|
||||
SIMPLE_MENU("", STATS_ICONS, menuTabStats, e_StatsTraces, TRACE_BUFFER_LEN);
|
||||
|
||||
uint8_t k = 0;
|
||||
int8_t sub = menuVerticalPosition;
|
||||
|
||||
lcdDrawChar(STATS_TRACES_INDEX_POS, MENU_TITLE_TOP+2, '#', MENU_TITLE_COLOR);
|
||||
lcdDrawText(STATS_TRACES_TIME_POS, MENU_TITLE_TOP+2, "Time", MENU_TITLE_COLOR);
|
||||
lcdDrawText(STATS_TRACES_EVENT_POS, MENU_TITLE_TOP+2, "Event", MENU_TITLE_COLOR);
|
||||
lcdDrawText(STATS_TRACES_DATA_POS, MENU_TITLE_TOP+2, "Data", MENU_TITLE_COLOR);
|
||||
|
||||
for (uint8_t i=0; i<NUM_BODY_LINES; i++) {
|
||||
coord_t y = MENU_CONTENT_TOP + i * FH;
|
||||
k = i+menuVerticalOffset;
|
||||
|
||||
// item
|
||||
lcdDrawNumber(STATS_TRACES_INDEX_POS, y, k, LEFT | (sub==k ? INVERS : 0));
|
||||
|
||||
const struct TraceElement * te = getTraceElement(k);
|
||||
if (te) {
|
||||
// time
|
||||
putstime_t tme = te->time % SECS_PER_DAY;
|
||||
putsTimer(STATS_TRACES_TIME_POS, y, tme, TIMEHOUR|LEFT);
|
||||
// event
|
||||
lcdDrawNumber(STATS_TRACES_EVENT_POS, y, te->event, LEADING0|LEFT, 3);
|
||||
// data
|
||||
lcdDrawSizedText(STATS_TRACES_DATA_POS, y, "0x", 2);
|
||||
lcdDrawHexNumber(STATS_TRACES_DATA_POS + 20, y, (uint16_t)(te->data >> 16));
|
||||
lcdDrawHexNumber(STATS_TRACES_DATA_POS+ 60, y, (uint16_t)(te->data & 0xFFFF));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif // defined(DEBUG_TRACE_BUFFER)
|
||||
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "opentx.h"
|
||||
|
||||
#define TEXT_FILE_MAXSIZE 2048
|
||||
|
||||
char s_text_file[TEXT_FILENAME_MAXLEN];
|
||||
char s_text_screen[NUM_BODY_LINES][LCD_COLS+1];
|
||||
|
||||
void readTextFile(int & lines_count)
|
||||
{
|
||||
FIL file;
|
||||
int result;
|
||||
char c;
|
||||
unsigned int sz;
|
||||
int line_length = 0;
|
||||
int escape = 0;
|
||||
char escape_chars[2];
|
||||
int current_line = 0;
|
||||
|
||||
memset(s_text_screen, 0, sizeof(s_text_screen));
|
||||
|
||||
result = f_open(&file, s_text_file, FA_OPEN_EXISTING | FA_READ);
|
||||
if (result == FR_OK) {
|
||||
for (int i=0; i<TEXT_FILE_MAXSIZE && f_read(&file, &c, 1, &sz)==FR_OK && sz==1 && (lines_count==0 || current_line-menuVerticalOffset<NUM_BODY_LINES); i++) {
|
||||
if (c == '\n') {
|
||||
++current_line;
|
||||
line_length = 0;
|
||||
escape = 0;
|
||||
}
|
||||
else if (c!='\r' && current_line>=menuVerticalOffset && current_line-menuVerticalOffset<NUM_BODY_LINES && line_length<LCD_COLS) {
|
||||
if (c=='\\' && escape==0) {
|
||||
escape = 1;
|
||||
continue;
|
||||
}
|
||||
else if (c!='\\' && escape>0 && escape<3) {
|
||||
escape_chars[escape-1] = c;
|
||||
if (escape == 2 && !strncmp(escape_chars, "up", 2)) {
|
||||
c = '\300';
|
||||
escape = 0;
|
||||
}
|
||||
else if (escape == 2 && !strncmp(escape_chars, "dn", 2)) {
|
||||
c = '\301';
|
||||
escape = 0;
|
||||
}
|
||||
else {
|
||||
escape++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (c=='~') {
|
||||
c = 'z'+1;
|
||||
}
|
||||
else if (c=='\t') {
|
||||
c = 0x1D; //tab
|
||||
}
|
||||
escape = 0;
|
||||
s_text_screen[current_line-menuVerticalOffset][line_length++] = c;
|
||||
}
|
||||
}
|
||||
if (c != '\n') {
|
||||
current_line += 1;
|
||||
}
|
||||
f_close(&file);
|
||||
}
|
||||
|
||||
if (lines_count == 0) {
|
||||
lines_count = current_line;
|
||||
}
|
||||
}
|
||||
|
||||
bool menuTextView(evt_t event)
|
||||
{
|
||||
static int lines_count;
|
||||
|
||||
drawMenuTemplate("TEXT VIEWER", ICON_OPENTX);
|
||||
|
||||
switch (event) {
|
||||
case EVT_ENTRY:
|
||||
menuVerticalOffset = 0;
|
||||
lines_count = 0;
|
||||
readTextFile(lines_count);
|
||||
break;
|
||||
|
||||
case EVT_ROTARY_LEFT:
|
||||
if (menuVerticalOffset == 0)
|
||||
break;
|
||||
else
|
||||
menuVerticalOffset--;
|
||||
readTextFile(lines_count);
|
||||
break;
|
||||
|
||||
case EVT_ROTARY_RIGHT:
|
||||
if (menuVerticalOffset+NUM_BODY_LINES >= lines_count)
|
||||
break;
|
||||
else
|
||||
++menuVerticalOffset;
|
||||
readTextFile(lines_count);
|
||||
break;
|
||||
|
||||
case EVT_KEY_FIRST(KEY_EXIT):
|
||||
popMenu();
|
||||
break;
|
||||
}
|
||||
|
||||
for (int i=0; i<NUM_BODY_LINES; i++) {
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, MENU_CONTENT_TOP + i*FH, s_text_screen[i]);
|
||||
}
|
||||
|
||||
char * title = s_text_file;
|
||||
#if defined(SIMU)
|
||||
if (!strncmp(title, "./", 2)) title += 2;
|
||||
#endif
|
||||
// lcd_putsCenter(MENU_FOOTER_TOP, title, HEADER_COLOR);
|
||||
|
||||
drawVerticalScrollbar(LCD_W-5, 50, 195, menuVerticalOffset, lines_count, NUM_BODY_LINES);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void pushMenuTextView(const char *filename)
|
||||
{
|
||||
if (strlen(filename) < TEXT_FILENAME_MAXLEN) {
|
||||
strcpy(s_text_file, filename);
|
||||
pushMenu(menuTextView);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "opentx.h"
|
||||
|
||||
const WidgetFactory * registeredWidgets[MAX_REGISTERED_WIDGETS]; // TODO dynamic
|
||||
unsigned int countRegisteredWidgets = 0;
|
||||
void registerWidget(const WidgetFactory * factory)
|
||||
{
|
||||
if (countRegisteredWidgets < MAX_REGISTERED_WIDGETS) {
|
||||
TRACE("register widget %s", factory->getName());
|
||||
registeredWidgets[countRegisteredWidgets++] = factory;
|
||||
}
|
||||
}
|
||||
|
||||
const WidgetFactory * getWidgetFactory(const char * name)
|
||||
{
|
||||
for (unsigned int i=0; i<countRegisteredWidgets; i++) {
|
||||
const WidgetFactory * factory = registeredWidgets[i];
|
||||
if (!strcmp(name, factory->getName())) {
|
||||
return factory;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Widget * loadWidget(const char * name, const Zone & zone, Widget::PersistentData * persistentData)
|
||||
{
|
||||
const WidgetFactory * factory = getWidgetFactory(name);
|
||||
if (factory) {
|
||||
return factory->create(zone, persistentData, false);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _WIDGET_H_
|
||||
#define _WIDGET_H_
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
|
||||
#define MAX_WIDGET_OPTIONS 5
|
||||
|
||||
struct Zone
|
||||
{
|
||||
uint16_t x, y, w, h;
|
||||
};
|
||||
|
||||
#define LEN_ZONE_OPTION_STRING 8
|
||||
union ZoneOptionValue
|
||||
{
|
||||
uint32_t unsignedValue;
|
||||
int32_t signedValue;
|
||||
bool boolValue;
|
||||
char stringValue[LEN_ZONE_OPTION_STRING];
|
||||
};
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#define OPTION_VALUE_UNSIGNED(x) uint32_t(x)
|
||||
#define OPTION_VALUE_SIGNED(x) uint32_t(x)
|
||||
#define OPTION_VALUE_BOOL(x) uint32_t(x)
|
||||
#define OPTION_VALUE_STRING(...) *(ZoneOptionValue *)(const char *)__VA_ARGS__
|
||||
#else
|
||||
#define OPTION_VALUE_UNSIGNED(x) { .unsignedValue = (x) }
|
||||
#define OPTION_VALUE_SIGNED(x) { .signedValue = (x) }
|
||||
#define OPTION_VALUE_BOOL(x) { .boolValue = (x) }
|
||||
#define OPTION_VALUE_STRING(...) *(ZoneOptionValue *)(const char *)__VA_ARGS__
|
||||
#endif
|
||||
|
||||
struct ZoneOption
|
||||
{
|
||||
enum Type {
|
||||
Integer,
|
||||
Source,
|
||||
Bool,
|
||||
String,
|
||||
File,
|
||||
TextSize,
|
||||
Timer,
|
||||
Switch,
|
||||
Color
|
||||
};
|
||||
|
||||
const char * name;
|
||||
Type type;
|
||||
ZoneOptionValue deflt;
|
||||
ZoneOptionValue min;
|
||||
ZoneOptionValue max;
|
||||
};
|
||||
|
||||
class WidgetFactory;
|
||||
class Widget
|
||||
{
|
||||
public:
|
||||
struct PersistentData {
|
||||
ZoneOptionValue options[MAX_WIDGET_OPTIONS];
|
||||
};
|
||||
|
||||
Widget(const WidgetFactory * factory, const Zone & zone, PersistentData * persistentData):
|
||||
factory(factory),
|
||||
zone(zone),
|
||||
persistentData(persistentData)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~Widget()
|
||||
{
|
||||
}
|
||||
|
||||
virtual void update() const
|
||||
{
|
||||
}
|
||||
|
||||
inline const WidgetFactory * getFactory() const
|
||||
{
|
||||
return factory;
|
||||
}
|
||||
|
||||
inline const ZoneOption * getOptions() const;
|
||||
|
||||
inline ZoneOptionValue * getOptionValue(unsigned int index) const
|
||||
{
|
||||
return &persistentData->options[index];
|
||||
}
|
||||
|
||||
virtual void refresh() = 0;
|
||||
|
||||
virtual void background()
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
const WidgetFactory * factory;
|
||||
Zone zone;
|
||||
PersistentData * persistentData;
|
||||
};
|
||||
|
||||
void registerWidget(const WidgetFactory * factory);
|
||||
|
||||
class WidgetFactory
|
||||
{
|
||||
public:
|
||||
WidgetFactory(const char * name, const ZoneOption * options=NULL):
|
||||
name(name),
|
||||
options(options)
|
||||
{
|
||||
registerWidget(this);
|
||||
}
|
||||
|
||||
inline const char * getName() const
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
inline const ZoneOption * getOptions() const
|
||||
{
|
||||
return options;
|
||||
}
|
||||
|
||||
void initPersistentData(Widget::PersistentData * persistentData) const
|
||||
{
|
||||
memset(persistentData, 0, sizeof(Widget::PersistentData));
|
||||
if (options) {
|
||||
int i = 0;
|
||||
for (const ZoneOption * option = options; option->name; option++) {
|
||||
// TODO compiler bug? The CPU freezes ... persistentData->options[i++] = option->deflt;
|
||||
memcpy(&persistentData->options[i++], &option->deflt, sizeof(ZoneOptionValue));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual Widget * create(const Zone & zone, Widget::PersistentData * persistentData, bool init=true) const = 0;
|
||||
|
||||
protected:
|
||||
const char * name;
|
||||
const ZoneOption * options;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
class BaseWidgetFactory: public WidgetFactory
|
||||
{
|
||||
public:
|
||||
BaseWidgetFactory(const char * name, const ZoneOption * options):
|
||||
WidgetFactory(name, options)
|
||||
{
|
||||
}
|
||||
|
||||
virtual Widget * create(const Zone & zone, Widget::PersistentData * persistentData, bool init=true) const
|
||||
{
|
||||
if (init) {
|
||||
initPersistentData(persistentData);
|
||||
}
|
||||
|
||||
return new T(this, zone, persistentData);
|
||||
}
|
||||
};
|
||||
|
||||
inline const ZoneOption * Widget::getOptions() const
|
||||
{
|
||||
return getFactory()->getOptions();
|
||||
}
|
||||
|
||||
#define MAX_REGISTERED_WIDGETS 10
|
||||
extern unsigned int countRegisteredWidgets;
|
||||
extern const WidgetFactory * registeredWidgets[MAX_REGISTERED_WIDGETS];
|
||||
Widget * loadWidget(const char * name, const Zone & zone, Widget::PersistentData * persistentData);
|
||||
|
||||
#endif // _WIDGET_H_
|
||||
@@ -0,0 +1,430 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "opentx.h"
|
||||
#include <stdio.h>
|
||||
|
||||
void drawStatusText(const char * text)
|
||||
{
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, MENU_FOOTER_TOP, text, TEXT_STATUSBAR_COLOR);
|
||||
}
|
||||
|
||||
void drawColumnHeader(const char * const * headers, const char * const * descriptions, uint8_t index)
|
||||
{
|
||||
lcdDrawText(LCD_W-MENUS_MARGIN_LEFT, MENU_TITLE_TOP + 2, headers[index], RIGHT | SMLSIZE | MENU_TITLE_COLOR);
|
||||
if (descriptions) {
|
||||
drawStatusText(descriptions[index]);
|
||||
}
|
||||
}
|
||||
|
||||
#include "alpha_button_on.lbm"
|
||||
#include "alpha_button_off.lbm"
|
||||
|
||||
void drawButton(coord_t x, coord_t y, const char * label, LcdFlags attr)
|
||||
{
|
||||
int width = getTextWidth(label, 0, 0);
|
||||
int padding = 0;
|
||||
if (attr & (BUTTON_OFF|BUTTON_ON)) {
|
||||
padding = 5;
|
||||
}
|
||||
if (attr & INVERS) {
|
||||
lcdDrawSolidFilledRect(x, y, padding+width+16+padding, 19, TEXT_INVERTED_BGCOLOR);
|
||||
lcdDrawText(x+padding+8, y, label, TEXT_INVERTED_COLOR);
|
||||
}
|
||||
else {
|
||||
lcdDrawText(x+padding+8, y, label, TEXT_COLOR);
|
||||
}
|
||||
lcdDrawSolidRect(x-1, y-1, padding+width+18+padding, 21, 1, TEXT_COLOR);
|
||||
if (attr & BUTTON_OFF)
|
||||
lcd->drawBitmap(x-6, y+3, &ALPHA_BUTTON_OFF);
|
||||
else if (attr & BUTTON_ON)
|
||||
lcd->drawBitmap(x-6, y+3, &ALPHA_BUTTON_ON);
|
||||
}
|
||||
|
||||
void drawCheckBox(coord_t x, coord_t y, uint8_t value, LcdFlags attr)
|
||||
{
|
||||
if (attr) {
|
||||
lcdDrawSolidFilledRect(x-1, y+2, 13, 13, TEXT_INVERTED_BGCOLOR);
|
||||
lcdDrawSolidFilledRect(x+1, y+4, 9, 9, TEXT_BGCOLOR);
|
||||
if (value) {
|
||||
lcdDrawSolidFilledRect(x+2, y+5, 7, 7, TEXT_INVERTED_BGCOLOR);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (value) {
|
||||
lcdDrawSolidFilledRect(x+2, y+5, 7, 7, SCROLLBOX_COLOR);
|
||||
lcdDrawSolidRect(x, y+3, 11, 11, 1, LINE_COLOR);
|
||||
}
|
||||
else {
|
||||
lcdDrawSolidRect(x, y+3, 11, 11, 1, LINE_COLOR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void drawVerticalScrollbar(coord_t x, coord_t y, coord_t h, uint16_t offset, uint16_t count, uint8_t visible)
|
||||
{
|
||||
if (visible < count) {
|
||||
lcdDrawSolidVerticalLine(x, y, h, LINE_COLOR);
|
||||
coord_t yofs = (h*offset + count/2) / count;
|
||||
coord_t yhgt = (h*visible + count/2) / count;
|
||||
if (yhgt + yofs > h)
|
||||
yhgt = h - yofs;
|
||||
lcdDrawSolidFilledRect(x-1, y + yofs, 3, yhgt, SCROLLBOX_COLOR);
|
||||
}
|
||||
}
|
||||
|
||||
void drawHorizontalScrollbar(coord_t x, coord_t y, coord_t w, uint16_t offset, uint16_t count, uint8_t visible)
|
||||
{
|
||||
if (visible < count) {
|
||||
lcdDrawSolidHorizontalLine(x, y, w, LINE_COLOR);
|
||||
coord_t xofs = (w*offset + count/2) / count;
|
||||
coord_t xhgt = (w*visible + count/2) / count;
|
||||
if (xhgt + xofs > w)
|
||||
xhgt = w - xofs;
|
||||
lcdDrawSolidFilledRect(x+xofs, y-1, xhgt, 3, SCROLLBOX_COLOR);
|
||||
}
|
||||
}
|
||||
|
||||
void drawProgressBar(const char *label)
|
||||
{
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, 4*FH, label);
|
||||
lcdDrawRect(3, 6*FH+4, 204, 7);
|
||||
lcdRefresh();
|
||||
}
|
||||
|
||||
void updateProgressBar(int num, int den)
|
||||
{
|
||||
if (num > 0 && den > 0) {
|
||||
int width = (200*num)/den;
|
||||
lcdDrawSolidFilledRect(5, 6*FH+6, width, 3, LINE_COLOR);
|
||||
lcdRefresh();
|
||||
}
|
||||
}
|
||||
|
||||
void drawShadow(coord_t x, coord_t y, coord_t w, coord_t h)
|
||||
{
|
||||
lcdDrawSolidVerticalLine(x+w, y+1, h, TEXT_COLOR);
|
||||
lcdDrawSolidHorizontalLine(x+1, y+h, w, TEXT_COLOR);
|
||||
lcdDrawSolidVerticalLine(x+w+1, y+2, h, LINE_COLOR);
|
||||
lcdDrawSolidHorizontalLine(x+2, y+h+1, w, LINE_COLOR);
|
||||
}
|
||||
|
||||
uint8_t linesDisplayed;
|
||||
|
||||
void drawMenuTemplate(const char * title, uint8_t icon, const uint8_t * icons, uint32_t options)
|
||||
{
|
||||
coord_t bodyTop, bodyBottom;
|
||||
|
||||
theme->drawTopbarBackground(icons ? icons[0] : icon);
|
||||
|
||||
// Menu title bar
|
||||
if (options & OPTION_MENU_TITLE_BAR) {
|
||||
linesDisplayed = NUM_BODY_LINES;
|
||||
bodyTop = MENU_BODY_TOP;
|
||||
lcdDrawSolidFilledRect(0, MENU_HEADER_HEIGHT, LCD_W, MENU_TITLE_TOP - MENU_HEADER_HEIGHT, TEXT_BGCOLOR);
|
||||
lcdDrawSolidFilledRect(0, MENU_TITLE_TOP, LCD_W, MENU_TITLE_HEIGHT, TITLE_BGCOLOR);
|
||||
if (title) {
|
||||
lcdDrawText(MENUS_MARGIN_LEFT, MENU_TITLE_TOP+1, title, MENU_TITLE_COLOR);
|
||||
}
|
||||
if (icons) {
|
||||
for (int i=0; i<menuPageCount; i++) {
|
||||
if (menuPageIndex != i) {
|
||||
theme->drawMenuIcon(icons[i+1], i, false);
|
||||
}
|
||||
}
|
||||
theme->drawMenuIcon(icons[menuPageIndex+1], menuPageIndex, true);
|
||||
}
|
||||
}
|
||||
else {
|
||||
linesDisplayed = NUM_BODY_LINES + 1;
|
||||
bodyTop = MENU_HEADER_HEIGHT;
|
||||
if (title) {
|
||||
lcdDrawText(50, 3, title, MENU_TITLE_COLOR);
|
||||
}
|
||||
}
|
||||
|
||||
// Footer
|
||||
if (options & OPTION_MENU_NO_FOOTER) {
|
||||
bodyBottom = LCD_H;
|
||||
}
|
||||
else {
|
||||
bodyBottom = MENU_FOOTER_TOP;
|
||||
lcdDrawSolidFilledRect(0, MENU_FOOTER_TOP, LCD_W, MENU_FOOTER_HEIGHT, HEADER_BGCOLOR);
|
||||
}
|
||||
|
||||
// Body
|
||||
lcdDrawSolidFilledRect(0, bodyTop, LCD_W, bodyBottom-bodyTop, TEXT_BGCOLOR);
|
||||
|
||||
// Scrollbar
|
||||
if (!(options & OPTION_MENU_NO_SCROLLBAR) && linesCount>linesDisplayed) {
|
||||
drawVerticalScrollbar(DEFAULT_SCROLLBAR_X, bodyTop+3, bodyBottom-bodyTop-6, menuVerticalOffset, linesCount, linesDisplayed);
|
||||
}
|
||||
}
|
||||
|
||||
select_menu_value_t selectMenuItem(coord_t x, coord_t y, const pm_char * values, select_menu_value_t value, select_menu_value_t min, select_menu_value_t max, LcdFlags attr, evt_t event)
|
||||
{
|
||||
if (attr) value = checkIncDec(event, value, min, max, (menuVerticalPositions[0] == 0) ? EE_MODEL : EE_GENERAL);
|
||||
if (values) lcdDrawTextAtIndex(x, y, values, value-min, attr);
|
||||
return value;
|
||||
}
|
||||
|
||||
uint8_t editCheckBox(uint8_t value, coord_t x, coord_t y, LcdFlags attr, evt_t event)
|
||||
{
|
||||
drawCheckBox(x, y, value, attr);
|
||||
return selectMenuItem(x, y, NULL, value, 0, 1, attr, event);
|
||||
}
|
||||
|
||||
swsrc_t switchMenuItem(coord_t x, coord_t y, swsrc_t value, LcdFlags attr, evt_t event)
|
||||
{
|
||||
if (attr) CHECK_INCDEC_MODELSWITCH(event, value, SWSRC_FIRST_IN_MIXES, SWSRC_LAST_IN_MIXES, isSwitchAvailableInMixes);
|
||||
putsSwitches(x, y, value, attr);
|
||||
return value;
|
||||
}
|
||||
|
||||
void drawTrimSquare(coord_t x, coord_t y)
|
||||
{
|
||||
lcdDrawSolidFilledRect(x-2, y, 15, 15, TRIM_BGCOLOR);
|
||||
lcdDrawBitmapPattern(x-2, y, LBM_TRIM_SHADOW, TRIM_SHADOW_COLOR);
|
||||
}
|
||||
|
||||
void drawHorizontalTrimPosition(coord_t x, coord_t y, int16_t dir)
|
||||
{
|
||||
drawTrimSquare(x, y);
|
||||
if (dir >= 0) {
|
||||
lcdDrawSolidVerticalLine(x+8, y+3, 9, TEXT_INVERTED_COLOR);
|
||||
}
|
||||
if (dir <= 0) {
|
||||
lcdDrawSolidVerticalLine(x+2, y+3, 9, TEXT_INVERTED_COLOR);
|
||||
}
|
||||
// if (exttrim) {
|
||||
// lcdDrawSolidVerticalLine(xm, ym, 9, TEXT_INVERTED_COLOR);
|
||||
// }
|
||||
}
|
||||
|
||||
void drawVerticalTrimPosition(coord_t x, coord_t y, int16_t dir)
|
||||
{
|
||||
drawTrimSquare(x, y);
|
||||
if (dir >= 0) {
|
||||
lcdDrawSolidHorizontalLine(x+1, y+4, 9, TEXT_INVERTED_COLOR);
|
||||
}
|
||||
if (dir <= 0) {
|
||||
lcdDrawSolidHorizontalLine(x+1, y+10, 9, TEXT_INVERTED_COLOR);
|
||||
}
|
||||
// if (exttrim) {
|
||||
// lcdDrawSolidHorizontalLine(xm-1, ym, 3, TEXT_INVERTED_COLOR);
|
||||
// }
|
||||
}
|
||||
|
||||
void drawVerticalSlider(coord_t x, coord_t y, int len, int val, int min, int max, uint8_t steps, uint32_t options)
|
||||
{
|
||||
if (steps) {
|
||||
int delta = len / steps;
|
||||
for (int i = 0; i <= len; i += delta) {
|
||||
if ((options & OPTION_SLIDER_BIG_TICKS) && (i == 0 || i == len / 2 || i == len))
|
||||
lcdDrawSolidHorizontalLine(x, y + i, 13, TEXT_COLOR);
|
||||
else
|
||||
lcdDrawSolidHorizontalLine(x + 2, y + i, 9, TEXT_COLOR);
|
||||
}
|
||||
}
|
||||
else {
|
||||
lcdDrawBitmapPattern(x + 1, y, LBM_VTRIM_FRAME, TEXT_COLOR);
|
||||
/* if (g_model.displayTrims != DISPLAY_TRIMS_NEVER && trim != 0) {
|
||||
if (g_model.displayTrims == DISPLAY_TRIMS_ALWAYS || (trimsDisplayTimer > 0 && (trimsDisplayMask & (1<<i)))) {
|
||||
lcdDrawNumber((stickIndex==0 ? TRIM_LH_X : TRIM_RH_X)+(trim>0 ? -20 : 50), ym+1, trim, TINSIZE);
|
||||
}
|
||||
} */
|
||||
}
|
||||
y += len - divRoundClosest(len * (val - min), max - min) - 5;
|
||||
if (options & OPTION_SLIDER_TRIM_BUTTON) {
|
||||
drawVerticalTrimPosition(x, y - 2, val);
|
||||
}
|
||||
else if (options & OPTION_SLIDER_NUMBER_BUTTON) {
|
||||
drawTrimSquare(x, y - 2);
|
||||
lcdDrawChar(x + 2, y - 1, '0' + val, SMLSIZE | TEXT_INVERTED_COLOR);
|
||||
}
|
||||
else {
|
||||
drawTrimSquare(x, y - 2);
|
||||
}
|
||||
}
|
||||
|
||||
void drawHorizontalSlider(coord_t x, coord_t y, int len, int val, int min, int max, uint8_t steps, uint32_t options)
|
||||
{
|
||||
int w = divRoundClosest(len * (val - min), max - min);
|
||||
if (options & OPTION_SLIDER_TICKS) {
|
||||
if (steps) {
|
||||
int delta = len / steps;
|
||||
for (int i = 0; i <= len; i += delta) {
|
||||
if ((options & OPTION_SLIDER_BIG_TICKS) && (i == 0 || i == len / 2 || i == len))
|
||||
lcdDrawSolidVerticalLine(x + i, y, 13, TEXT_COLOR);
|
||||
else
|
||||
lcdDrawSolidVerticalLine(x + i, y + 2, 9, TEXT_COLOR);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (options & OPTION_SLIDER_EMPTY_BAR) {
|
||||
lcdDrawBitmapPattern(x, y + 1, LBM_HTRIM_FRAME, TEXT_COLOR);
|
||||
}
|
||||
else if (options & OPTION_SLIDER_DBL_COLOR) {
|
||||
lcdDrawBitmapPattern(x, y + 8, LBM_SLIDER_BAR_LEFT, w <= 0 ? LINE_COLOR : TEXT_INVERTED_BGCOLOR);
|
||||
if (w > 4)
|
||||
lcdDrawSolidFilledRect(x + 4, y + 8, w - 4, 4, TEXT_INVERTED_BGCOLOR);
|
||||
if (w < len - 4)
|
||||
lcdDrawSolidFilledRect(x + w, y + 8, len - w - 4, 4, LINE_COLOR);
|
||||
lcdDrawBitmapPattern(x + len - 4, y + 8, LBM_SLIDER_BAR_RIGHT, w >= len ? TEXT_INVERTED_BGCOLOR : LINE_COLOR);
|
||||
}
|
||||
else {
|
||||
lcdDrawBitmapPattern(x, y + 8, LBM_SLIDER_BAR_LEFT, LINE_COLOR);
|
||||
lcdDrawSolidFilledRect(x + 4, y + 8, len - 8, 4, LINE_COLOR);
|
||||
lcdDrawBitmapPattern(x + len - 4, y + 8, LBM_SLIDER_BAR_RIGHT, LINE_COLOR);
|
||||
//
|
||||
/* if (g_model.displayTrims != DISPLAY_TRIMS_NEVER && trim != 0) {
|
||||
if (g_model.displayTrims == DISPLAY_TRIMS_ALWAYS || (trimsDisplayTimer > 0 && (trimsDisplayMask & (1<<i)))) {
|
||||
lcdDrawNumber((stickIndex==0 ? TRIM_LH_X : TRIM_RH_X)+(trim>0 ? -20 : 50), ym+1, trim, TINSIZE);
|
||||
}
|
||||
} */
|
||||
}
|
||||
x += w - 5;
|
||||
if (options & OPTION_SLIDER_TRIM_BUTTON) {
|
||||
drawHorizontalTrimPosition(x, y - 1, val);
|
||||
}
|
||||
else if (options & OPTION_SLIDER_NUMBER_BUTTON) {
|
||||
drawTrimSquare(x, y - 1);
|
||||
char text[] = { (char)('0' + val), '\0' };
|
||||
lcdDrawText(x + 5, y - 1, text, SMLSIZE | CENTERED | TEXT_INVERTED_COLOR);
|
||||
}
|
||||
else if (options & OPTION_SLIDER_SQUARE_BUTTON) {
|
||||
drawTrimSquare(x, y - 1);
|
||||
}
|
||||
else {
|
||||
lcdDrawBitmapPattern(x, y + 2, LBM_SLIDER_POINT_OUT, TEXT_COLOR);
|
||||
lcdDrawBitmapPattern(x, y + 2, LBM_SLIDER_POINT_MID, TEXT_BGCOLOR);
|
||||
if ((options & INVERS) && (!(options & BLINK) || !BLINK_ON_PHASE))
|
||||
lcdDrawBitmapPattern(x, y + 2, LBM_SLIDER_POINT_IN, TEXT_INVERTED_BGCOLOR);
|
||||
}
|
||||
}
|
||||
|
||||
void drawSlider(coord_t x, coord_t y, int len, int val, int min, int max, uint8_t steps, uint32_t options)
|
||||
{
|
||||
if (options & OPTION_SLIDER_VERTICAL)
|
||||
drawVerticalSlider(x, y, len, val, min, max, steps, options);
|
||||
else
|
||||
drawHorizontalSlider(x, y, len, val, min, max, steps, options);
|
||||
}
|
||||
|
||||
#if defined(GVARS)
|
||||
bool noZero(int val)
|
||||
{
|
||||
return val != 0;
|
||||
}
|
||||
|
||||
int16_t editGVarFieldValue(coord_t x, coord_t y, int16_t value, int16_t min, int16_t max, LcdFlags attr, uint8_t editflags, evt_t event)
|
||||
{
|
||||
uint16_t delta = GV_GET_GV1_VALUE(max);
|
||||
bool invers = (attr & INVERS);
|
||||
|
||||
// TRACE("editGVarFieldValue(val=%d min=%d max=%d)", value, min, max);
|
||||
|
||||
if (invers && event == EVT_KEY_LONG(KEY_ENTER)) {
|
||||
s_editMode = !s_editMode;
|
||||
if (attr & PREC1)
|
||||
value = (GV_IS_GV_VALUE(value, min, max) ? GET_GVAR(value, min, max, mixerCurrentFlightMode)*10 : delta);
|
||||
else
|
||||
value = (GV_IS_GV_VALUE(value, min, max) ? GET_GVAR(value, min, max, mixerCurrentFlightMode) : delta);
|
||||
storageDirty(EE_MODEL);
|
||||
}
|
||||
|
||||
if (GV_IS_GV_VALUE(value, min, max)) {
|
||||
attr &= ~PREC1;
|
||||
|
||||
int8_t idx = (int16_t) GV_INDEX_CALC_DELTA(value, delta);
|
||||
if (idx >= 0) ++idx; // transform form idx=0=GV1 to idx=1=GV1 in order to handle double keys invert
|
||||
if (invers) {
|
||||
CHECK_INCDEC_MODELVAR_CHECK(event, idx, -MAX_GVARS, MAX_GVARS, noZero);
|
||||
if (idx == 0) idx = 1; // handle reset to zero, map to GV1
|
||||
}
|
||||
if (idx < 0) {
|
||||
value = (int16_t) GV_CALC_VALUE_IDX_NEG(idx, delta);
|
||||
idx = -idx;
|
||||
drawStringWithIndex(x, y, STR_GV, idx, attr, "-");
|
||||
}
|
||||
else {
|
||||
drawStringWithIndex(x, y, STR_GV, idx, attr);
|
||||
value = (int16_t) GV_CALC_VALUE_IDX_POS(idx-1, delta);
|
||||
}
|
||||
}
|
||||
else {
|
||||
lcdDrawNumber(x, y, value, attr, 0, NULL, "%");
|
||||
if (invers) value = checkIncDec(event, value, min, max, EE_MODEL | editflags);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
#else
|
||||
int16_t editGVarFieldValue(coord_t x, coord_t y, int16_t value, int16_t min, int16_t max, LcdFlags attr, evt_t event)
|
||||
{
|
||||
if (attr & INVERS) value = checkIncDec(event, value, min, max, EE_MODEL);
|
||||
lcdDrawNumber(x, y, value, attr, 0, NULL, "%");
|
||||
return value;
|
||||
}
|
||||
#endif
|
||||
|
||||
void drawSleepBitmap()
|
||||
{
|
||||
lcd->clear();
|
||||
const BitmapBuffer * bitmap = BitmapBuffer::load(getThemePath("sleep.bmp"));
|
||||
if (bitmap) {
|
||||
lcd->drawBitmap((LCD_W-bitmap->getWidth())/2, (LCD_H-bitmap->getHeight())/2, bitmap);
|
||||
}
|
||||
lcdRefresh();
|
||||
}
|
||||
|
||||
#define SHUTDOWN_CIRCLE_DIAMETER 150
|
||||
void drawShutdownBitmap(uint32_t index)
|
||||
{
|
||||
static uint32_t last_index = 0xffffffff;
|
||||
static const BitmapBuffer * shutdown = BitmapBuffer::load(getThemePath("shutdown.bmp"));
|
||||
|
||||
if (shutdown) {
|
||||
if (index < last_index) {
|
||||
theme->drawBackground();
|
||||
lcd->drawBitmap((LCD_W-shutdown->getWidth())/2, (LCD_H-shutdown->getHeight())/2, shutdown);
|
||||
lcdStoreBackupBuffer();
|
||||
}
|
||||
else {
|
||||
lcdRestoreBackupBuffer();
|
||||
int quarter = index / (PWR_PRESS_SHUTDOWN / 5);
|
||||
if (quarter >= 1) lcdDrawBitmapPattern(LCD_W/2, (LCD_H-SHUTDOWN_CIRCLE_DIAMETER)/2, LBM_SHUTDOWN_CIRCLE, TEXT_COLOR, 0, SHUTDOWN_CIRCLE_DIAMETER/2);
|
||||
if (quarter >= 2) lcdDrawBitmapPattern(LCD_W/2, LCD_H/2, LBM_SHUTDOWN_CIRCLE, TEXT_COLOR, SHUTDOWN_CIRCLE_DIAMETER/2, SHUTDOWN_CIRCLE_DIAMETER/2);
|
||||
if (quarter >= 3) lcdDrawBitmapPattern((LCD_W-SHUTDOWN_CIRCLE_DIAMETER)/2, LCD_H/2, LBM_SHUTDOWN_CIRCLE, TEXT_COLOR, SHUTDOWN_CIRCLE_DIAMETER, SHUTDOWN_CIRCLE_DIAMETER/2);
|
||||
if (quarter >= 4) lcdDrawBitmapPattern((LCD_W-SHUTDOWN_CIRCLE_DIAMETER)/2, (LCD_H-SHUTDOWN_CIRCLE_DIAMETER)/2, LBM_SHUTDOWN_CIRCLE, TEXT_COLOR, SHUTDOWN_CIRCLE_DIAMETER*3/2, SHUTDOWN_CIRCLE_DIAMETER/2);
|
||||
}
|
||||
}
|
||||
else {
|
||||
lcd->clear();
|
||||
int quarter = index / (PWR_PRESS_SHUTDOWN / 5);
|
||||
for (int i=1; i<=4; i++) {
|
||||
if (quarter >= i) {
|
||||
lcd->drawSolidFilledRect(LCD_W / 2 - 70 + 24 * i, LCD_H / 2 - 10, 20, 20, TEXT_BGCOLOR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lcdRefresh();
|
||||
last_index = index;
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _WIDGETS_H_
|
||||
#define _WIDGETS_H_
|
||||
|
||||
#include "opentx.h"
|
||||
#include <stdio.h>
|
||||
|
||||
#define OPTION_MENU_NO_FOOTER 0x01
|
||||
#define OPTION_MENU_TITLE_BAR 0x02
|
||||
#define OPTION_MENU_NO_SCROLLBAR 0x04
|
||||
|
||||
#define OPTION_SLIDER_INVERS INVERS
|
||||
#define OPTION_SLIDER_BLINK BLINK
|
||||
#define OPTION_SLIDER_VERTICAL 0x04
|
||||
#define OPTION_SLIDER_EMPTY_BAR 0x08
|
||||
#define OPTION_SLIDER_DBL_COLOR 0x10
|
||||
#define OPTION_SLIDER_TICKS 0x20
|
||||
#define OPTION_SLIDER_BIG_TICKS 0x40
|
||||
#define OPTION_SLIDER_TRIM_BUTTON 0x80
|
||||
#define OPTION_SLIDER_NUMBER_BUTTON 0x100
|
||||
#define OPTION_SLIDER_SQUARE_BUTTON 0x200
|
||||
|
||||
void drawStatusText(const char * text);
|
||||
void drawColumnHeader(const char * const * headers, const char * const * descriptions, uint8_t index);
|
||||
void drawTopbarDatetime();
|
||||
|
||||
#define BUTTON_ON 0x10
|
||||
#define BUTTON_OFF 0x20
|
||||
void drawButton(coord_t x, coord_t y, const char * label, LcdFlags attr);
|
||||
void drawCheckBox(coord_t x, coord_t y, uint8_t value, LcdFlags attr);
|
||||
void drawVerticalScrollbar(coord_t x, coord_t y, coord_t h, uint16_t offset, uint16_t count, uint8_t visible);
|
||||
void drawHorizontalScrollbar(coord_t x, coord_t y, coord_t w, uint16_t offset, uint16_t count, uint8_t visible);
|
||||
void drawProgressBar(const char * label);
|
||||
void updateProgressBar(int num, int den);
|
||||
void drawShadow(coord_t x, coord_t y, coord_t w, coord_t h);
|
||||
void drawTrimSquare(coord_t x, coord_t y);
|
||||
void drawHorizontalTrimPosition(coord_t x, coord_t y, int16_t dir);
|
||||
void drawVerticalTrimPosition(coord_t x, coord_t y, int16_t dir);
|
||||
void drawVerticalSlider(coord_t x, coord_t y, int len, int val, int min, int max, uint8_t steps, uint32_t options);
|
||||
void drawHorizontalSlider(coord_t x, coord_t y, int len, int val, int min, int max, uint8_t steps, uint32_t options);
|
||||
void drawSlider(coord_t x, coord_t y, int len, int val, int min, int max, uint8_t steps, uint32_t options);
|
||||
#define drawStatusLine(...)
|
||||
|
||||
select_menu_value_t selectMenuItem(coord_t x, coord_t y, const pm_char * values, select_menu_value_t value, select_menu_value_t min, select_menu_value_t max, LcdFlags attr, evt_t event);
|
||||
uint8_t editCheckBox(uint8_t value, coord_t x, coord_t y, LcdFlags attr, evt_t event);
|
||||
swsrc_t switchMenuItem(coord_t x, coord_t y, swsrc_t value, LcdFlags attr, evt_t event);
|
||||
#if defined(GVARS)
|
||||
int16_t editGVarFieldValue(coord_t x, coord_t y, int16_t value, int16_t min, int16_t max, LcdFlags attr, uint8_t editflags, evt_t event);
|
||||
#else
|
||||
int16_t editGVarFieldValue(coord_t x, coord_t y, int16_t value, int16_t min, int16_t max, LcdFlags attr, evt_t event);
|
||||
#endif
|
||||
|
||||
// Screen templates
|
||||
void drawMenuTemplate(const char * title, uint8_t icon, const uint8_t * icons=NULL, uint32_t options=0);
|
||||
void drawSplash();
|
||||
void drawSleepBitmap();
|
||||
void drawShutdownBitmap(uint32_t index);
|
||||
|
||||
// Main view standard widgets
|
||||
void drawTopBar();
|
||||
void drawMainPots();
|
||||
void drawTrims(uint8_t flightMode);
|
||||
|
||||
extern uint8_t linesDisplayed;
|
||||
|
||||
#endif // _WIDGETS_H_
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "opentx.h"
|
||||
|
||||
class GaugeWidget: public Widget
|
||||
{
|
||||
public:
|
||||
GaugeWidget(const WidgetFactory * factory, const Zone & zone, Widget::PersistentData * persistentData):
|
||||
Widget(factory, zone, persistentData)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void refresh();
|
||||
|
||||
static const ZoneOption options[];
|
||||
};
|
||||
|
||||
const ZoneOption GaugeWidget::options[] = {
|
||||
{ "Source", ZoneOption::Source, OPTION_VALUE_UNSIGNED(1) },
|
||||
{ "Min", ZoneOption::Integer, OPTION_VALUE_SIGNED(-RESX), OPTION_VALUE_SIGNED(-RESX), OPTION_VALUE_SIGNED(RESX) },
|
||||
{ "Max", ZoneOption::Integer, OPTION_VALUE_SIGNED(RESX), OPTION_VALUE_SIGNED(-RESX), OPTION_VALUE_SIGNED(RESX) },
|
||||
{ "Color", ZoneOption::Color, OPTION_VALUE_UNSIGNED(RED) },
|
||||
{ NULL, ZoneOption::Bool }
|
||||
};
|
||||
|
||||
void GaugeWidget::refresh()
|
||||
{
|
||||
mixsrc_t index = persistentData->options[0].unsignedValue;
|
||||
int32_t min = persistentData->options[1].signedValue;
|
||||
int32_t max = persistentData->options[2].signedValue;
|
||||
uint16_t color = persistentData->options[3].unsignedValue;
|
||||
|
||||
int32_t value = getValue(index);
|
||||
int32_t value_in_range = value;
|
||||
if (value < min)
|
||||
value_in_range = min;
|
||||
else if (value > max)
|
||||
value_in_range = max;
|
||||
|
||||
int w = divRoundClosest(zone.w * (value_in_range - min), (max - min));
|
||||
int percent = divRoundClosest(100 * (value_in_range - min), (max - min));
|
||||
|
||||
// Gauge label
|
||||
putsMixerSource(zone.x, zone.y, index, SMLSIZE | TEXT_INVERTED_COLOR);
|
||||
|
||||
// Gauge
|
||||
lcdSetColor(color);
|
||||
lcdDrawSolidFilledRect(zone.x, zone.y + 16, zone.w, 16, TEXT_INVERTED_COLOR);
|
||||
lcdDrawNumber(zone.x+zone.w/2, zone.y + 17, percent, SMLSIZE | CUSTOM_COLOR | CENTERED, 0, NULL, "%");
|
||||
lcd->invertRect(zone.x + w, zone.y + 16, zone.w - w, 16, CUSTOM_COLOR);
|
||||
}
|
||||
|
||||
BaseWidgetFactory<GaugeWidget> gaugeWidget("Gauge", GaugeWidget::options);
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "opentx.h"
|
||||
|
||||
class ModelBitmapWidget: public Widget
|
||||
{
|
||||
public:
|
||||
ModelBitmapWidget(const WidgetFactory * factory, const Zone & zone, Widget::PersistentData * persistentData):
|
||||
Widget(factory, zone, persistentData),
|
||||
buffer(NULL)
|
||||
{
|
||||
memset(bitmapFilename, 255, sizeof(bitmapFilename));
|
||||
}
|
||||
|
||||
virtual ~ModelBitmapWidget()
|
||||
{
|
||||
delete buffer;
|
||||
}
|
||||
|
||||
void refreshBuffer()
|
||||
{
|
||||
delete buffer;
|
||||
buffer = new BitmapBuffer(BMP_RGB565, zone.w, zone.h);
|
||||
|
||||
if (buffer) {
|
||||
buffer->drawBitmap(0, 0, lcd, zone.x, zone.y, zone.w, zone.h);
|
||||
GET_FILENAME(filename, BITMAPS_PATH, g_model.header.bitmap, "");
|
||||
BitmapBuffer * bitmap = BitmapBuffer::load(filename);
|
||||
if (zone.h >= 96 && zone.w >= 120) {
|
||||
buffer->drawFilledRect(0, 0, zone.w, zone.h, SOLID, MAINVIEW_PANES_COLOR | OPACITY(5));
|
||||
static BitmapBuffer * icon = BitmapBuffer::loadMask(getThemePath("mask_menu_model.png"));
|
||||
buffer->drawMask(6, 4, icon, MAINVIEW_GRAPHICS_COLOR);
|
||||
buffer->drawSizedText(45, 10, g_model.header.name, LEN_MODEL_NAME, ZCHAR | SMLSIZE);
|
||||
buffer->drawSolidFilledRect(39, 27, zone.w - 48, 2, MAINVIEW_GRAPHICS_COLOR);
|
||||
if (bitmap) {
|
||||
buffer->drawScaledBitmap(bitmap, 0, 38, zone.w, zone.h - 38);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (bitmap) {
|
||||
buffer->drawScaledBitmap(bitmap, 0, 0, zone.w, zone.h);
|
||||
}
|
||||
}
|
||||
delete bitmap;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void refresh()
|
||||
{
|
||||
if (memcmp(bitmapFilename, g_model.header.bitmap, sizeof(g_model.header.bitmap)) != 0 ||
|
||||
memcmp(modelName, g_model.header.name, sizeof(g_model.header.name)) != 0) {
|
||||
refreshBuffer();
|
||||
memcpy(bitmapFilename, g_model.header.bitmap, sizeof(g_model.header.bitmap));
|
||||
memcpy(modelName, g_model.header.name, sizeof(g_model.header.name));
|
||||
}
|
||||
|
||||
if (buffer) {
|
||||
lcd->drawBitmap(zone.x, zone.y, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
char bitmapFilename[sizeof(g_model.header.bitmap)];
|
||||
char modelName[sizeof(g_model.header.name)];
|
||||
BitmapBuffer * buffer;
|
||||
};
|
||||
|
||||
BaseWidgetFactory<ModelBitmapWidget> modelBitmapWidget("ModelBmp", NULL);
|
||||
const WidgetFactory * defaultWidget = &modelBitmapWidget;
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "opentx.h"
|
||||
|
||||
#define RECT_OFFSET 80
|
||||
#define RECT_WIDTH (w - RECT_OFFSET)
|
||||
#define ROW_HEIGHT 17
|
||||
|
||||
class OutputsWidget: public Widget
|
||||
{
|
||||
public:
|
||||
OutputsWidget(const WidgetFactory * factory, const Zone & zone, Widget::PersistentData * persistentData):
|
||||
Widget(factory, zone, persistentData)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void refresh();
|
||||
|
||||
uint8_t drawChannels(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint8_t firstChan)
|
||||
{
|
||||
char chanString[] = "CH32";
|
||||
uint8_t numChan = h / ROW_HEIGHT;
|
||||
uint8_t row_height = (h - numChan * ROW_HEIGHT >= numChan ? ROW_HEIGHT + 1 : ROW_HEIGHT);
|
||||
uint8_t lastChan = firstChan + numChan;
|
||||
|
||||
for (uint8_t curChan = firstChan; curChan < lastChan && curChan < 33; curChan++) {
|
||||
int16_t chanVal = calcRESXto100(channelOutputs[curChan-1]);
|
||||
strAppend(chanString, "CH");
|
||||
strAppendSigned(&chanString[2], curChan, 2);
|
||||
lcdDrawText(x, y + (curChan - firstChan) * row_height + 1, chanString, SMLSIZE | TEXT_COLOR | LEFT);
|
||||
strAppendSigned(chanString, chanVal);
|
||||
lcdDrawText(x + RECT_OFFSET - 2, y + (curChan - firstChan) * row_height + 1, chanString, SMLSIZE | TEXT_COLOR | RIGHT);
|
||||
if (chanVal > 0) {
|
||||
lcdDrawSolidFilledRect(x + RECT_OFFSET + RECT_WIDTH / 2, y + (curChan -firstChan) * row_height, divRoundClosest(RECT_WIDTH * chanVal, 200), row_height, MAINVIEW_GRAPHICS_COLOR);
|
||||
}
|
||||
else if (chanVal < 0) {
|
||||
uint16_t startpoint = x + RECT_OFFSET;
|
||||
uint16_t endpoint = startpoint + RECT_WIDTH / 2;
|
||||
uint16_t size = divRoundClosest(- RECT_WIDTH * chanVal, 200);
|
||||
lcdDrawSolidFilledRect(endpoint - size, y + (curChan - firstChan) * row_height, size, row_height, MAINVIEW_GRAPHICS_COLOR);
|
||||
}
|
||||
lcdDrawRect(x + RECT_OFFSET, y + (curChan - firstChan) * row_height, RECT_WIDTH, row_height+1);
|
||||
lcd->drawSolidVerticalLine(x + RECT_OFFSET + RECT_WIDTH / 2, y + (curChan - firstChan) * row_height, row_height, MAINVIEW_GRAPHICS_COLOR);
|
||||
}
|
||||
return lastChan;
|
||||
}
|
||||
|
||||
void twoColumns()
|
||||
{
|
||||
uint8_t endColumn = drawChannels(zone.x, zone.y, zone.w / 2, zone.h, persistentData->options[0].unsignedValue);
|
||||
drawChannels(zone.x + zone.w / 2 + 2, zone.y, zone.w / 2, zone.h, endColumn + 1);
|
||||
}
|
||||
|
||||
void oneColumn()
|
||||
{
|
||||
drawChannels(zone.x, zone.y, zone.w, zone.h, persistentData->options[0].unsignedValue);
|
||||
}
|
||||
|
||||
static const ZoneOption options[];
|
||||
};
|
||||
|
||||
const ZoneOption OutputsWidget::options[] = {
|
||||
{ "First channel", ZoneOption::Integer, OPTION_VALUE_UNSIGNED(1), OPTION_VALUE_UNSIGNED(1), OPTION_VALUE_UNSIGNED(32) },
|
||||
{ NULL, ZoneOption::Bool }
|
||||
};
|
||||
|
||||
|
||||
void OutputsWidget::refresh()
|
||||
{
|
||||
if (zone.w > 300 && zone.h > 20)
|
||||
twoColumns();
|
||||
else if (zone.w > 150 && zone.h > 20)
|
||||
oneColumn();
|
||||
};
|
||||
|
||||
BaseWidgetFactory<OutputsWidget> outputsWidget("Outputs", OutputsWidget::options);
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "opentx.h"
|
||||
|
||||
class TextWidget: public Widget
|
||||
{
|
||||
public:
|
||||
TextWidget(const WidgetFactory * factory, const Zone & zone, Widget::PersistentData * persistentData):
|
||||
Widget(factory, zone, persistentData)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void refresh();
|
||||
|
||||
static const ZoneOption options[];
|
||||
};
|
||||
|
||||
const ZoneOption TextWidget::options[] = {
|
||||
{ "Text", ZoneOption::String, OPTION_VALUE_STRING("\015\347\0\14\377\376\373\364") },
|
||||
{ "Color", ZoneOption::Color, OPTION_VALUE_UNSIGNED(RED) },
|
||||
{ "Size", ZoneOption::TextSize, OPTION_VALUE_UNSIGNED(0) },
|
||||
{ NULL, ZoneOption::Bool }
|
||||
};
|
||||
|
||||
void TextWidget::refresh()
|
||||
{
|
||||
lcdSetColor(persistentData->options[1].unsignedValue);
|
||||
LcdFlags fontsize = FONTSIZE(persistentData->options[2].unsignedValue << 8);
|
||||
lcdDrawSizedText(zone.x, zone.y, persistentData->options[0].stringValue, sizeof(persistentData->options[0].stringValue), ZCHAR|fontsize|CUSTOM_COLOR);
|
||||
}
|
||||
|
||||
BaseWidgetFactory<TextWidget> textWidget("Text", TextWidget::options);
|
||||
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "opentx.h"
|
||||
|
||||
class TimerWidget: public Widget
|
||||
{
|
||||
public:
|
||||
TimerWidget(const WidgetFactory * factory, const Zone & zone, Widget::PersistentData * persistentData):
|
||||
Widget(factory, zone, persistentData)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void refresh();
|
||||
|
||||
static const ZoneOption options[];
|
||||
};
|
||||
|
||||
const ZoneOption TimerWidget::options[] = {
|
||||
{ "Timer source", ZoneOption::Timer, OPTION_VALUE_UNSIGNED(0) },
|
||||
{ NULL, ZoneOption::Bool }
|
||||
};
|
||||
|
||||
void TimerWidget::refresh()
|
||||
{
|
||||
uint32_t index = persistentData->options[0].unsignedValue;
|
||||
TimerData & timerData = g_model.timers[index];
|
||||
TimerState & timerState = timersStates[index];
|
||||
|
||||
if (zone.w >= 180 && zone.h >= 70) {
|
||||
if (timerState.val >= 0 || !(timerState.val % 2)) {
|
||||
lcdDrawBitmapPattern(zone.x, zone.y, LBM_TIMER_BACKGROUND, MAINVIEW_PANES_COLOR);
|
||||
}
|
||||
else {
|
||||
lcdDrawBitmapPattern(zone.x, zone.y, LBM_TIMER_BACKGROUND, HEADER_BGCOLOR);
|
||||
}
|
||||
if (timerData.start && timerState.val >= 0) {
|
||||
lcdDrawBitmapPatternPie(
|
||||
zone.x + 2,
|
||||
zone.y + 3, LBM_RSCALE, MAINVIEW_GRAPHICS_COLOR, 0,
|
||||
timerState.val <= 0 ? 360 : 360 * (timerData.start - timerState.val) / timerData.start);
|
||||
}
|
||||
else {
|
||||
lcdDrawBitmapPattern(zone.x + 3, zone.y + 4, LBM_TIMER, MAINVIEW_GRAPHICS_COLOR);
|
||||
}
|
||||
if (abs(timerState.val) >= 3600) {
|
||||
putsTimer(zone.x + 70, zone.y + 31, abs(timerState.val), TEXT_COLOR | MIDSIZE | LEFT | TIMEHOUR);
|
||||
}
|
||||
else {
|
||||
putsTimer(zone.x + 76, zone.y + 31, abs(timerState.val), TEXT_COLOR | DBLSIZE | LEFT);
|
||||
}
|
||||
if (ZLEN(timerData.name) > 0) {
|
||||
lcdDrawSizedText(zone.x + 78, zone.y + 20, timerData.name, LEN_TIMER_NAME, ZCHAR | SMLSIZE | TEXT_COLOR);
|
||||
}
|
||||
drawStringWithIndex(zone.x + 137, zone.y + 17, "TMR", index + 1, SMLSIZE | TEXT_COLOR);
|
||||
}
|
||||
else {
|
||||
if (timerState.val < 0 && timerState.val % 2) {
|
||||
lcdDrawSolidFilledRect(zone.x, zone.y, zone.w, zone.h, HEADER_ICON_BGCOLOR);
|
||||
}
|
||||
drawStringWithIndex(zone.x + 2, zone.y, "TMR", index + 1, SMLSIZE | TEXT_INVERTED_COLOR);
|
||||
if (zone.w > 100 && zone.h > 40) {
|
||||
if (abs(timerState.val) >= 3600) {
|
||||
putsTimer(zone.x + 3, zone.y + 16, abs(timerState.val), TEXT_INVERTED_COLOR | LEFT | TIMEHOUR);
|
||||
}
|
||||
else {
|
||||
putsTimer(zone.x + 3, zone.y + 16, abs(timerState.val), TEXT_INVERTED_COLOR | LEFT | MIDSIZE);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (abs(timerState.val) >= 3600) {
|
||||
putsTimer(zone.x + 3, zone.y + 14, abs(timerState.val), TEXT_INVERTED_COLOR | LEFT | SMLSIZE | TIMEHOUR);
|
||||
}
|
||||
else {
|
||||
putsTimer(zone.x + 3, zone.y + 14, abs(timerState.val), TEXT_INVERTED_COLOR | LEFT);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BaseWidgetFactory<TimerWidget> timerWidget("Timer", TimerWidget::options);
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "opentx.h"
|
||||
|
||||
class ValueWidget: public Widget
|
||||
{
|
||||
public:
|
||||
ValueWidget(const WidgetFactory * factory, const Zone & zone, Widget::PersistentData * persistentData):
|
||||
Widget(factory, zone, persistentData)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void refresh();
|
||||
|
||||
static const ZoneOption options[];
|
||||
};
|
||||
|
||||
const ZoneOption ValueWidget::options[] = {
|
||||
{ "Source", ZoneOption::Source, OPTION_VALUE_UNSIGNED(MIXSRC_Rud) },
|
||||
{ NULL, ZoneOption::Bool }
|
||||
};
|
||||
|
||||
void ValueWidget::refresh()
|
||||
{
|
||||
const int NUMBERS_PADDING = 4;
|
||||
|
||||
mixsrc_t field = persistentData->options[0].unsignedValue;
|
||||
|
||||
LcdFlags color = TEXT_INVERTED_COLOR;
|
||||
int x = zone.x;
|
||||
int y = zone.y;
|
||||
|
||||
// lcdDrawFilledRect(zone.x, zone.y, zone.w, zone.h, SOLID, MAINVIEW_PANES_COLOR | OPACITY(5));
|
||||
|
||||
int xValue, yValue, xLabel, yLabel;
|
||||
LcdFlags attrValue, attrLabel=0;
|
||||
if (zone.w < 120 && zone.h < 50) {
|
||||
xValue = x;
|
||||
yValue = y+14;
|
||||
xLabel = x;
|
||||
yLabel = y;
|
||||
attrValue = LEFT | NO_UNIT | MIDSIZE;
|
||||
attrLabel = SMLSIZE;
|
||||
}
|
||||
else if (zone.h < 50) {
|
||||
xValue = x+zone.w-NUMBERS_PADDING;
|
||||
yValue = y-2;
|
||||
xLabel = x+NUMBERS_PADDING;
|
||||
yLabel = y+2;
|
||||
attrValue = RIGHT | NO_UNIT | DBLSIZE;
|
||||
}
|
||||
else {
|
||||
xValue = x+NUMBERS_PADDING;
|
||||
yValue = y+16;
|
||||
xLabel = x+NUMBERS_PADDING;
|
||||
yLabel = y+2;
|
||||
attrValue = LEFT | DBLSIZE;
|
||||
}
|
||||
|
||||
if (field >= MIXSRC_FIRST_TIMER && field <= MIXSRC_LAST_TIMER) {
|
||||
TimerState & timerState = timersStates[field-MIXSRC_FIRST_TIMER];
|
||||
if (timerState.val < 0) {
|
||||
color = ALARM_COLOR;
|
||||
}
|
||||
putsMixerSource(x+NUMBERS_PADDING, y+2, field, color);
|
||||
putsTimer(xValue, yValue, abs(timerState.val), attrValue|DBLSIZE|color);
|
||||
return;
|
||||
}
|
||||
|
||||
if (field >= MIXSRC_FIRST_TELEM) {
|
||||
TelemetryItem & telemetryItem = telemetryItems[(field-MIXSRC_FIRST_TELEM)/3]; // TODO macro to convert a source to a telemetry index
|
||||
if (!telemetryItem.isAvailable() || telemetryItem.isOld()) {
|
||||
color = ALARM_COLOR;
|
||||
}
|
||||
}
|
||||
|
||||
putsMixerSource(xLabel, yLabel, field, attrLabel|color);
|
||||
putsChannel(xValue, yValue, field, attrValue|color);
|
||||
}
|
||||
|
||||
BaseWidgetFactory<ValueWidget> ValueWidget("Value", ValueWidget::options);
|
||||
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* Copyright (C) OpenTX
|
||||
*
|
||||
* Based on code named
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _WIDGETS_CONTAINER_H_
|
||||
#define _WIDGETS_CONTAINER_H_
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "widget.h"
|
||||
|
||||
class WidgetsContainerInterface
|
||||
{
|
||||
public:
|
||||
virtual unsigned int getZonesCount() const = 0;
|
||||
|
||||
virtual Zone getZone(unsigned int index) const = 0;
|
||||
|
||||
inline Widget * getWidget(unsigned int index)
|
||||
{
|
||||
return widgets[index];
|
||||
}
|
||||
|
||||
inline void setWidget(unsigned int index, Widget * widget)
|
||||
{
|
||||
widgets[index] = widget;
|
||||
}
|
||||
|
||||
virtual void createWidget(unsigned int index, const WidgetFactory * factory) = 0;
|
||||
|
||||
protected:
|
||||
Widget ** widgets;
|
||||
};
|
||||
|
||||
template<int N, int O>
|
||||
class WidgetsContainer: public WidgetsContainerInterface
|
||||
{
|
||||
public:
|
||||
struct ZonePersistentData {
|
||||
char widgetName[10];
|
||||
Widget::PersistentData widgetData;
|
||||
};
|
||||
|
||||
struct PersistentData {
|
||||
ZonePersistentData zones[N];
|
||||
ZoneOptionValue options[O];
|
||||
};
|
||||
|
||||
public:
|
||||
WidgetsContainer(PersistentData * persistentData):
|
||||
persistentData(persistentData)
|
||||
{
|
||||
widgets = (Widget **)calloc(N, sizeof(Widget *));
|
||||
}
|
||||
|
||||
virtual ~WidgetsContainer()
|
||||
{
|
||||
if (widgets) {
|
||||
for (uint8_t i=0; i<N; i++) {
|
||||
delete widgets[i];
|
||||
}
|
||||
free(widgets);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void createWidget(unsigned int index, const WidgetFactory * factory)
|
||||
{
|
||||
if (widgets) {
|
||||
memset(persistentData->zones[index].widgetName, 0, sizeof(persistentData->zones[index].widgetName));
|
||||
if (factory) {
|
||||
strncpy(persistentData->zones[index].widgetName, factory->getName(), sizeof(persistentData->zones[index].widgetName));
|
||||
widgets[index] = factory->create(getZone(index), &persistentData->zones[index].widgetData);
|
||||
}
|
||||
else {
|
||||
widgets[index] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual void create()
|
||||
{
|
||||
memset(persistentData, 0, sizeof(PersistentData));
|
||||
}
|
||||
|
||||
virtual void load()
|
||||
{
|
||||
if (widgets) {
|
||||
unsigned int count = getZonesCount();
|
||||
for (unsigned int i=0; i<count; i++) {
|
||||
delete widgets[i];
|
||||
if (persistentData->zones[i].widgetName[0]) {
|
||||
char name[sizeof(persistentData->zones[i].widgetName)+1];
|
||||
memset(name, 0, sizeof(name));
|
||||
strncpy(name, persistentData->zones[i].widgetName, sizeof(persistentData->zones[i].widgetName));
|
||||
widgets[i] = loadWidget(name, getZone(i), &persistentData->zones[i].widgetData);
|
||||
}
|
||||
else {
|
||||
widgets[i] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline ZoneOptionValue * getOptionValue(unsigned int index) const
|
||||
{
|
||||
return &persistentData->options[index];
|
||||
}
|
||||
|
||||
virtual unsigned int getZonesCount() const = 0;
|
||||
|
||||
virtual Zone getZone(unsigned int index) const = 0;
|
||||
|
||||
virtual void refresh()
|
||||
{
|
||||
if (widgets) {
|
||||
for (int i=0; i<N; i++) {
|
||||
if (widgets[i]) {
|
||||
widgets[i]->refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual void background()
|
||||
{
|
||||
if (widgets) {
|
||||
for (int i=0; i<N; i++) {
|
||||
if (widgets[i]) {
|
||||
widgets[i]->background();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
PersistentData * persistentData;
|
||||
};
|
||||
|
||||
#endif // _WIDGETS_CONTAINER_H_
|
||||
Reference in New Issue
Block a user