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:
Bertrand Songis
2016-07-29 12:47:20 +02:00
committed by GitHub
parent e3e0d970a5
commit 83cdd67e6a
340 changed files with 3035 additions and 5126 deletions
+252
View File
@@ -0,0 +1,252 @@
/*
* 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 * lcdLoadBitmap(uint8_t * bmp, const char * filename, uint16_t width, uint16_t height)
{
FIL bmpFile;
UINT read;
uint8_t palette[16];
uint8_t bmpBuf[LCD_W]; /* maximum with LCD_W */
uint8_t * buf = &bmpBuf[0];
if (width > LCD_W) {
return NULL;
}
FRESULT result = f_open(&bmpFile, filename, FA_OPEN_EXISTING | FA_READ);
if (result != FR_OK) {
return NULL;
}
if (f_size(&bmpFile) < 14) {
f_close(&bmpFile);
return NULL;
}
result = f_read(&bmpFile, buf, 14, &read);
if (result != FR_OK || read != 14) {
f_close(&bmpFile);
return NULL;
}
if (buf[0] != 'B' || buf[1] != 'M') {
f_close(&bmpFile);
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(&bmpFile, buf, len, &read);
if (result != FR_OK || read != len) {
f_close(&bmpFile);
return NULL;
}
uint32_t ihsize = *((uint32_t *)&buf[0]); /* more header size */
/* invalid header size */
if (ihsize + 14 > hsize) {
f_close(&bmpFile);
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(&bmpFile) - 2;
/* declared file size less than header size */
if (fsize <= hsize) {
f_close(&bmpFile);
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(&bmpFile);
return NULL;
}
if (*((uint16_t *)&buf[0]) != 1) { /* planes */
f_close(&bmpFile);
return NULL;
}
if (w > width || h > height) {
f_close(&bmpFile);
return NULL;
}
uint16_t depth = *((uint16_t *)&buf[2]);
buf = &bmpBuf[0];
if (depth == 4) {
if (f_lseek(&bmpFile, hsize-64) != FR_OK || f_read(&bmpFile, buf, 64, &read) != FR_OK || read != 64) {
f_close(&bmpFile);
return NULL;
}
for (uint8_t i=0; i<16; i++) {
palette[i] = buf[4*i] >> 4;
}
}
else {
if (f_lseek(&bmpFile, hsize) != FR_OK) {
f_close(&bmpFile);
return NULL;
}
}
uint8_t * dest = bmp;
*dest++ = w;
*dest++ = h;
memset(dest, 0, BITMAP_BUFFER_SIZE(w, h) - 2);
uint32_t rowSize;
switch (depth) {
case 1:
rowSize = ((w+31)/32)*4;
for (uint32_t i=0; i<h; i+=2) {
result = f_read(&bmpFile, buf, rowSize*2, &read);
if (result != FR_OK || read != rowSize*2) {
f_close(&bmpFile);
return NULL;
}
for (uint32_t j=0; j<w; j++) {
uint8_t * dst = dest + (h-i-2)/2 * w + j;
if (!(buf[j/8] & (1<<(7-(j%8)))))
*dst |= 0xF0;
if (!(buf[rowSize+j/8] & (1<<(7-(j%8)))))
*dst |= 0x0F;
}
}
break;
case 4:
rowSize = ((4*w+31)/32)*4;
for (int32_t i=h-1; i>=0; i--) {
result = f_read(&bmpFile, buf, rowSize, &read);
if (result != FR_OK || read != rowSize) {
f_close(&bmpFile);
return NULL;
}
uint8_t * dst = dest + (i/2)*w;
for (uint32_t j=0; j<w; j++) {
uint8_t index = (buf[j/2] >> ((j & 1) ? 0 : 4)) & 0x0F;
uint8_t val = palette[index] << ((i & 1) ? 4 : 0);
*dst++ |= val ^ ((i & 1) ? 0xF0 : 0x0F);
}
}
break;
default:
f_close(&bmpFile);
return NULL;
}
f_close(&bmpFile);
return bmp;
}
const uint8_t bmpHeader[] = {
0x42, 0x4d, 0xF8, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00, 0x28, 0x00,
0x00, 0x00, 212, 0x00, 0x00, 0x00, 64, 0x00, 0x00, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00,
0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0xbc, 0x38, 0x00, 0x00, 0xbc, 0x38, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xee, 0xee, 0xee, 0x00, 0xdd, 0xdd,
0xdd, 0x00, 0xcc, 0xcc, 0xcc, 0x00, 0xbb, 0xbb, 0xbb, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x99, 0x99,
0x99, 0x00, 0x88, 0x88, 0x88, 0x00, 0x77, 0x77, 0x77, 0x00, 0x66, 0x66, 0x66, 0x00, 0x55, 0x55,
0x55, 0x00, 0x44, 0x44, 0x44, 0x00, 0x33, 0x33, 0x33, 0x00, 0x22, 0x22, 0x22, 0x00, 0x11, 0x11,
0x11, 0x00, 0x00, 0x00, 0x00, 0x00
};
inline display_t getPixel(unsigned int x, unsigned int y)
{
if (x>=LCD_W || y>=LCD_H)
return 0;
display_t * p = &displayBuf[y / 2 * LCD_W + x];
return (y & 1) ? (*p >> 4) : (*p & 0x0F);
}
const char * writeScreenshot()
{
FIL bmpFile;
UINT written;
char filename[42]; // /SCREENSHOTS/screen-2013-01-01-123540.bmp
// check and create folder here
strcpy_P(filename, SCREENSHOTS_PATH);
const char * error = sdCheckAndCreateDirectory(filename);
if (error) {
return error;
}
char * tmp = strAppend(&filename[sizeof(SCREENSHOTS_PATH)-1], "/screen");
tmp = strAppendDate(tmp, true);
strcpy(tmp, BMP_EXT);
FRESULT result = f_open(&bmpFile, filename, FA_CREATE_ALWAYS | FA_WRITE);
if (result != FR_OK) {
return SDCARD_ERROR(result);
}
result = f_write(&bmpFile, bmpHeader, sizeof(bmpHeader), &written);
if (result != FR_OK || written != sizeof(bmpHeader)) {
f_close(&bmpFile);
return SDCARD_ERROR(result);
}
for (int y=LCD_H-1; y>=0; y-=1) {
for (int x=0; x<8*((LCD_W+7)/8); x+=2) {
uint8_t byte = getPixel(x+1, y) + (getPixel(x, y) << 4);
f_write(&bmpFile, &byte, 1, &written);
if (result != FR_OK || written != 1) {
f_close(&bmpFile);
return SDCARD_ERROR(result);
}
}
}
f_close(&bmpFile);
return NULL;
}
+140
View File
@@ -0,0 +1,140 @@
/*
* 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 pm_uchar font_5x7[] PROGMEM = {
#include "font_05x07.lbm"
#if defined(TRANSLATIONS_DE)
#include "font_de_05x07.lbm"
#elif defined(TRANSLATIONS_CZ)
#include "font_cz_05x07.lbm"
#elif defined(TRANSLATIONS_ES)
#include "font_es_05x07.lbm"
#elif defined(TRANSLATIONS_FI)
#include "font_fi_05x07.lbm"
#elif defined(TRANSLATIONS_FR)
#include "font_fr_05x07.lbm"
#elif defined(TRANSLATIONS_IT)
#include "font_it_05x07.lbm"
#elif defined(TRANSLATIONS_PL)
#include "font_pl_05x07.lbm"
#elif defined(TRANSLATIONS_PT)
#include "font_pt_05x07.lbm"
#elif defined(TRANSLATIONS_SE)
#include "font_se_05x07.lbm"
#endif
};
const pm_uchar font_5x7_B[] PROGMEM = {
#include "font_05x07_B_compressed.lbm"
};
#if !defined(BOOT)
const pm_uchar font_10x14[] PROGMEM = {
#include "font_10x14_compressed.lbm"
#if defined(TRANSLATIONS_DE)
#include "font_de_10x14.lbm"
#elif defined(TRANSLATIONS_CZ)
#include "font_cz_10x14.lbm"
#elif defined(TRANSLATIONS_ES)
#include "font_es_10x14.lbm"
#elif defined(TRANSLATIONS_FI)
#include "font_fi_10x14.lbm"
#elif defined(TRANSLATIONS_FR)
#include "font_fr_10x14.lbm"
#elif defined(TRANSLATIONS_IT)
#include "font_it_10x14.lbm"
#elif defined(TRANSLATIONS_PL)
#include "font_pl_10x14.lbm"
#elif defined(TRANSLATIONS_PT)
#include "font_pt_10x14.lbm"
#elif defined(TRANSLATIONS_SE)
#include "font_se_10x14.lbm"
#endif
};
const pm_uchar font_3x5[] PROGMEM = {
#include "font_03x05.lbm"
};
const pm_uchar font_4x6[] PROGMEM = {
#include "font_04x06.lbm"
#if defined(TRANSLATIONS_DE)
#include "font_de_04x06.lbm"
#elif defined(TRANSLATIONS_CZ)
#include "font_cz_04x06.lbm"
#elif defined(TRANSLATIONS_ES)
#include "font_es_04x06.lbm"
#elif defined(TRANSLATIONS_FI)
#include "font_fi_04x06.lbm"
#elif defined(TRANSLATIONS_FR)
#include "font_fr_04x06.lbm"
#elif defined(TRANSLATIONS_IT)
#include "font_it_04x06.lbm"
#elif defined(TRANSLATIONS_PL)
#include "font_pl_04x06.lbm"
#elif defined(TRANSLATIONS_PT)
#include "font_pt_04x06.lbm"
#elif defined(TRANSLATIONS_SE)
#include "font_se_04x06.lbm"
#endif
};
const pm_uchar font_8x10[] PROGMEM = {
#include "font_08x10.lbm"
#if defined(TRANSLATIONS_DE)
#include "font_de_08x10.lbm"
#elif defined(TRANSLATIONS_CZ)
#include "font_cz_08x10.lbm"
#elif defined(TRANSLATIONS_ES)
#include "font_es_08x10.lbm"
#elif defined(TRANSLATIONS_FI)
#include "font_fi_08x10.lbm"
#elif defined(TRANSLATIONS_FR)
#include "font_fr_08x10.lbm"
#elif defined(TRANSLATIONS_IT)
#include "font_it_08x10.lbm"
#elif defined(TRANSLATIONS_PL)
#include "font_pl_08x10.lbm"
#elif defined(TRANSLATIONS_PT)
#include "font_pt_08x10.lbm"
#elif defined(TRANSLATIONS_SE)
#include "font_se_08x10.lbm"
#endif
};
const pm_uchar font_22x38_num[] PROGMEM = {
#include "font_22x38_num.lbm"
};
const pm_uchar font_4x6_extra[] PROGMEM = {
#include "font_04x06_extra.lbm"
};
const pm_uchar font_5x7_extra[] PROGMEM = {
#include "font_05x07_extra.lbm"
};
const pm_uchar font_10x14_extra[] PROGMEM = {
#include "font_10x14_extra.lbm"
};
#endif // !defined(BOOT)
+83
View File
@@ -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.
*/
#ifndef _GUI_H_
#define _GUI_H_
#include "gui_helpers.h"
#include "lcd.h"
#include "menus.h"
#define DEFAULT_SCROLLBAR_X (LCD_W-1)
#define NUM_BODY_LINES (LCD_LINES-1)
#define MENU_HEADER_HEIGHT FH
#define MENU_INIT_VPOS 0
#define BITMAP_BUFFER_SIZE(width, height) (2 + (width) * (((height)+7)/8)*4)
#define MODEL_BITMAP_WIDTH 64
#define MODEL_BITMAP_HEIGHT 32
#define WCHART (LCD_H/2)
#define X0 (LCD_W-WCHART-2)
#define Y0 (LCD_H/2)
#define MENUS_SCROLLBAR_WIDTH 2
#define MENU_COLUMN2_X (8 + LCD_W / 2)
#if MENU_COLUMNS < 2
#define MIXES_2ND_COLUMN (18*FW)
#else
#define MIXES_2ND_COLUMN (9*FW)
#endif
#define MODEL_BITMAP_SIZE BITMAP_BUFFER_SIZE(MODEL_BITMAP_WIDTH, MODEL_BITMAP_HEIGHT)
#define LOAD_MODEL_BITMAP() loadModelBitmap(g_model.header.bitmap, modelBitmap)
extern uint8_t modelBitmap[MODEL_BITMAP_SIZE];
bool loadModelBitmap(char * name, uint8_t * bitmap);
struct MenuItem {
const char * name;
const MenuHandlerFunc action;
};
void drawSplash();
void drawScreenIndex(uint8_t index, uint8_t count, uint8_t attr);
void drawVerticalScrollbar(coord_t x, coord_t y, coord_t h, uint16_t offset, uint16_t count, uint8_t visible);
void displayMenuBar(const MenuItem *menu, int index);
void drawProgressBar(const char *label);
void updateProgressBar(int num, int den);
void drawGauge(coord_t x, coord_t y, coord_t w, coord_t h, int32_t val, int32_t max);
void drawColumnHeader(const char * const * headers, uint8_t index);
void drawStick(coord_t centrex, int16_t xval, int16_t yval);
extern coord_t scrollbar_X;
#define SET_SCROLLBAR_X(x) scrollbar_X = (x);
extern const pm_uchar sticks[] PROGMEM;
#if defined(FLIGHT_MODES)
void displayFlightModes(coord_t x, coord_t y, FlightModesType value);
FlightModesType editFlightModes(coord_t x, coord_t y, uint8_t event, FlightModesType value, uint8_t attr);
#else
#define displayFlightModes(...)
#endif
#endif // _GUI_H_
File diff suppressed because it is too large Load Diff
+221
View File
@@ -0,0 +1,221 @@
/*
* 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_
#define LCD_W 212
#define LCD_H 64
#define BOX_WIDTH 31
#define coord_t int
#define scoord_t int
#define CENTER "\015"
#define CENTER_OFS (7*FW-FW/2)
#define CONTRAST_MIN 0
#define CONTRAST_MAX 45
#define lcdint_t int32_t
#define FW 6
#define FWNUM 5
#define FH 8
#define LCD_LINES (LCD_H/FH)
#define LCD_COLS (LCD_W/FW)
/* lcdDrawText flags */
#define BLINK 0x01
#define INVERS 0x02
#define BOLD 0x04
#define LEFT 0x00
#define RIGHT 0x08
#define FIXEDWIDTH 0x10
/* no 0x80 here because of "GV"1 which is aligned LEFT */
/* no 0x10 here because of "MODEL"01 which uses LEADING0 */
#define ZCHAR 0x80
/* lcdDrawNumber additional flags */
#define LEADING0 0x10
#define PREC1 0x20
#define PREC2 0x30
#define MODE(flags) ((((int8_t)(flags) & 0x30) - 0x10) >> 4)
/* line, rect, square flags */
#define FORCE 0x02
#define ERASE 0x04
#define ROUND 0x08
#define FILL_WHITE 0x10
/* telemetry flags */
#define NO_UNIT 0x40
#define FONTSIZE(x) ((x) & 0x0700)
#define TINSIZE 0x0100
#define SMLSIZE 0x0200
#define MIDSIZE 0x0300
#define DBLSIZE 0x0400
#define XXLSIZE 0x0500
#define ERASEBG 0x8000
#define VERTICAL 0x0800
#define TIMEBLINK 0x1000
#define TIMEHOUR 0x2000
#define STREXPANDED 0x4000
#define GREY(x) ((x) * 0x010000)
#define WHITE GREY(0xf)
#define GREY_DEFAULT GREY(11)
#define COLOUR_MASK(x) ((x) & 0x0F0000)
#define LcdFlags uint32_t
#define display_t uint8_t
#define DISPLAY_BUFFER_SIZE (LCD_W*LCD_H*4/8)
#if defined(REVPLUS) && defined(LCD_DUAL_BUFFER)
extern display_t displayBuf1[DISPLAY_BUFFER_SIZE];
extern display_t displayBuf2[DISPLAY_BUFFER_SIZE];
extern display_t * displayBuf;
#else
extern display_t displayBuf[DISPLAY_BUFFER_SIZE];
#endif
#if defined(REVPLUS) && !defined(LCD_DUAL_BUFFER) && !defined(SIMU)
void lcdRefreshWait();
#else
#define lcdRefreshWait()
#endif
extern coord_t lcdLastPos;
extern coord_t lcdNextPos;
#define DISPLAY_END (displayBuf + DISPLAY_BUFFER_SIZE)
#define ASSERT_IN_DISPLAY(p) assert((p) >= displayBuf && (p) < DISPLAY_END)
#if defined(BOOT)
// TODO quick & dirty :(
typedef const unsigned char pm_uchar;
typedef const char pm_char;
#endif
void lcdDrawChar(coord_t x, coord_t y, const unsigned char c);
void lcdDrawChar(coord_t x, coord_t y, const unsigned char c, LcdFlags mode);
void lcdDrawText(coord_t x, coord_t y, const pm_char * s, LcdFlags mode);
void lcdDrawTextAtIndex(coord_t x, coord_t y, const pm_char * s,uint8_t idx, LcdFlags mode);
void lcdDrawSizedText(coord_t x, coord_t y, const pm_char * s,unsigned char len, LcdFlags mode);
void lcdDrawText(coord_t x, coord_t y, const pm_char * s);
void lcdDrawSizedText(coord_t x, coord_t y, const pm_char * s, unsigned char len);
void lcd_putsLeft(coord_t y, const pm_char * s);
#define lcd_putsCenter(y, s) lcdDrawText((LCD_W-sizeof(TR_##s)*FW+FW+1)/2, y, STR_##s)
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 mode, uint8_t len);
void lcdDrawNumber(coord_t x, coord_t y, int32_t val, LcdFlags mode=0);
void drawStringWithIndex(coord_t x, coord_t y, const pm_char *str, uint8_t idx, LcdFlags att=0);
void putsModelName(coord_t x, coord_t y, char *name, uint8_t id, LcdFlags att);
void putsSwitches(coord_t x, coord_t y, int32_t swtch, LcdFlags att=0);
void putsStickName(coord_t x, coord_t y, uint8_t idx, LcdFlags att=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);
#if !defined(BOOT)
void putsCurveRef(coord_t x, coord_t y, CurveRef &curve, LcdFlags att);
#endif
void drawCurveName(coord_t x, coord_t y, int8_t idx, LcdFlags att=0);
void putsTimerMode(coord_t x, coord_t y, int32_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 putsVolts(coord_t x, coord_t y, uint16_t volts, LcdFlags att);
void putsVBat(coord_t x, coord_t y, LcdFlags att);
#if !defined(BOOT)
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);
#endif
#define putstime_t int32_t
void putsRtcTime(coord_t x, coord_t y, LcdFlags att);
void putsTimer(coord_t x, coord_t y, putstime_t tme, LcdFlags att, LcdFlags att2);
#define SOLID 0xff
#define DOTTED 0x55
void lcdDrawPoint(coord_t x, coord_t y, LcdFlags att=0);
void lcdMaskPoint(uint8_t *p, uint8_t mask, LcdFlags att=0);
void lcdDrawSolidHorizontalLine(coord_t x, coord_t y, coord_t w, LcdFlags att=0);
void lcdDrawHorizontalLine(coord_t x, coord_t y, coord_t w, uint8_t pat, LcdFlags att=0);
void lcdDrawSolidVerticalLine(coord_t x, scoord_t y, scoord_t h, LcdFlags att=0);
void lcdDrawVerticalLine(coord_t x, scoord_t y, scoord_t h, uint8_t pat, LcdFlags att=0);
void lcdDrawLine(coord_t x1, coord_t y1, coord_t x2, coord_t y2, uint8_t pat=SOLID, LcdFlags att=0);
void lcdDrawFilledRect(coord_t x, scoord_t y, coord_t w, coord_t h, uint8_t pat=SOLID, LcdFlags att=0);
inline void lcdDrawSolidFilledRect(coord_t x, scoord_t y, coord_t w, coord_t h, LcdFlags att=0)
{
lcdDrawFilledRect(x, y, w, h, SOLID, att);
}
void lcdDrawRect(coord_t x, coord_t y, coord_t w, coord_t h, uint8_t pat=SOLID, LcdFlags att=0);
inline void lcdDrawSquare(coord_t x, coord_t y, coord_t w, LcdFlags att=0)
{
lcdDrawRect(x, y, w, w, SOLID, att);
}
void lcdInvertLine(int8_t line);
#define lcdInvertLastLine() lcdInvertLine(LCD_LINES-1)
void drawShutdownBitmap(uint32_t index);
void drawSleepBitmap();
void lcdDrawTelemetryTopBar();
#define V_BAR(xx, yy, ll) \
lcdDrawSolidVerticalLine(xx-1,yy-ll,ll); \
lcdDrawSolidVerticalLine(xx ,yy-ll,ll); \
lcdDrawSolidVerticalLine(xx+1,yy-ll,ll)
void lcd_img(coord_t x, coord_t y, const pm_uchar * img, uint8_t idx, LcdFlags att=0);
void lcdDrawBitmap(coord_t x, coord_t y, const uint8_t * img, coord_t offset=0, coord_t width=0);
#define LCD_ICON(x, y, icon) lcdDrawBitmap(x, y, icons, icon)
void lcdSetRefVolt(unsigned char val);
void lcdClear();
void lcdSetContrast();
#if defined(REVPLUS) && !defined(SIMU)
void lcdRefresh(bool wait=true);
#else
void lcdRefresh();
#endif
uint8_t * lcdLoadBitmap(uint8_t * dest, const char * filename, uint16_t width, uint16_t height);
const char * writeScreenshot();
#if defined(BOOT)
#define BLINK_ON_PHASE (0)
#else
#define BLINK_ON_PHASE (g_blinkTmr10ms & (1<<6))
#endif
#endif // _LCD_H_
+168
View File
@@ -0,0 +1,168 @@
/*
* 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[] = {
menuModelSelect,
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
menuModelTelemetry,
CASE_MAVLINK(menuTelemetryMavlinkSetup)
menuModelDisplay
};
#if MENU_COLUMNS > 1
uint8_t editDelay(const coord_t x, const coord_t y, const uint8_t event, const uint8_t attr, const pm_char *str, uint8_t delay)
{
lcdDrawText(x, y, str);
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;
}
#else
uint8_t editDelay(const coord_t y, const uint8_t event, const uint8_t attr, const pm_char *str, uint8_t delay)
{
lcd_putsLeft(y, str);
lcdDrawNumber(MIXES_2ND_COLUMN, y, (10/DELAY_STEP)*delay, attr|PREC1|LEFT);
if (attr) CHECK_INCDEC_MODELVAR_ZERO(event, delay, DELAY_MAX);
return delay;
}
#endif
#define COPY_MODE 1
#define MOVE_MODE 2
uint8_t s_copyMode = 0;
int8_t s_copySrcRow;
int8_t s_copyTgtOfs;
uint8_t s_maxLines = 8;
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, uint8_t event, uint8_t active, uint8_t attr)
{
uint8_t mode = 0;
if (active) {
if (s_editMode <= 0)
mode = INVERS + FIXEDWIDTH;
else
mode = FIXEDWIDTH;
}
lcdDrawSizedText(x, y, name, size, attr | mode);
coord_t backupNextPos = lcdNextPos;
if (active) {
uint8_t cur = editNameCursorPos;
if (s_editMode > 0) {
int8_t c = name[cur];
int8_t v = c;
if (event==EVT_KEY_FIRST(KEY_DOWN) || event==EVT_KEY_FIRST(KEY_UP) || event==EVT_KEY_REPT(KEY_DOWN) || event==EVT_KEY_REPT(KEY_UP)) {
if (attr == ZCHAR) {
v = checkIncDec(event, abs(v), 0, ZCHAR_MAX, 0);
if (c <= 0) v = -v;
}
else {
v = checkIncDec(event, abs(v), '0', 'z', 0);
}
}
switch (event) {
case EVT_ROTARY_BREAK:
if (s_editMode == EDIT_MODIFY_FIELD) {
s_editMode = EDIT_MODIFY_STRING;
cur = 0;
}
else if (cur<size-1)
cur++;
else
s_editMode = 0;
break;
case EVT_ROTARY_LONG:
if (attr & ZCHAR) {
if (v == 0) {
s_editMode = 0;
killEvents(event);
}
else if (v>=-26 && v<=26) {
v = -v; // toggle case
}
}
else {
if (v == ' ') {
s_editMode = 0;
killEvents(event);
break;
}
else if (v>='A' && v<='Z') {
v = 'a'+v-'A'; // toggle case
}
else if (v>='a' && v<='z') {
v = 'A'+v-'a'; // toggle case
}
}
break;
}
if (c != v) {
name[cur] = v;
storageDirty(menuVerticalPositions[0] == 0 ? EE_MODEL : EE_GENERAL);
}
if (attr == ZCHAR) {
lcdDrawChar(x+editNameCursorPos*FW, y, idx2char(v), ERASEBG|INVERS|FIXEDWIDTH);
}
else {
lcdDrawChar(x+editNameCursorPos*FW, y, v, ERASEBG|INVERS|FIXEDWIDTH);
}
}
else {
cur = 0;
}
editNameCursorPos = cur;
lcdNextPos = backupNextPos;
}
}
void editSingleName(coord_t x, coord_t y, const pm_char *label, char *name, uint8_t size, uint8_t event, uint8_t active)
{
lcd_putsLeft(y, label);
editName(x, y, name, size, event, active);
}
uint8_t s_currIdx;
+39
View File
@@ -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"
const MenuHandlerFunc menuTabGeneral[] = {
menuGeneralSetup,
menuGeneralSdManager,
menuGeneralCustomFunctions,
menuGeneralTrainer,
menuGeneralVersion,
menuGeneralDiagKeys,
menuGeneralDiagAna,
menuGeneralHardware,
menuGeneralCalib
};
void menuGeneralCustomFunctions(uint8_t event)
{
MENU(STR_MENUGLOBALFUNCS, menuTabGeneral, e_GeneralCustomFunctions, NUM_CFN, { NAVIGATION_LINE_BY_LINE|4/*repeated*/ });
return menuCustomFunctions(event, g_eeGeneral.customFn, &globalFunctionsContext);
}
+83
View File
@@ -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];
uint8_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 == menuModelSelect)
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);
}
void menuModelNotes(uint8_t event)
{
if (event == EVT_ENTRY) {
strcpy(s_text_file, MODELS_PATH "/");
char *buf = strcat_modelname(&s_text_file[sizeof(MODELS_PATH)], g_eeGeneral.currModel);
strcpy(buf, TEXT_EXT);
}
menuTextView(event);
}
void pushModelNotes()
{
pushMenu(menuModelNotes);
}
+446
View File
@@ -0,0 +1,446 @@
/*
* 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_
#if defined(TRANSLATIONS_FR)
#define MENU_COLUMNS 1
#else
#define MENU_COLUMNS 2
#endif
#define lcd_putsColumnLeft(x, y, str) lcdDrawText((x > (LCD_W-10*FW-MENUS_SCROLLBAR_WIDTH)) ? MENU_COLUMN2_X : 0, y, str)
// Menus related stuff ...
typedef uint16_t vertpos_t;
typedef uint8_t & check_event_t;
#define horzpos_t int8_t
extern tmr10ms_t menuEntryTime;
extern vertpos_t menuVerticalPosition;
extern horzpos_t menuHorizontalPosition;
extern vertpos_t 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)
void drawCheckBox(coord_t x, coord_t y, uint8_t value, LcdFlags attr);
typedef void (*MenuHandlerFunc)(uint8_t event);
extern MenuHandlerFunc menuHandlers[5];
extern uint8_t menuVerticalPositions[4];
extern uint8_t menuLevel;
extern uint8_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();
void doMainScreenGraphics();
void menuFirstCalib(uint8_t event);
void onMainViewMenu(const char *result);
void menuMainViewChannelsMonitor(uint8_t event);
void menuChannelsView(uint8_t event);
void menuMainView(uint8_t event);
#if defined(FRSKY)
void menuTelemetryFrsky(uint8_t event);
#endif
void menuCustomFunctions(uint8_t event, CustomFunctionData * functions, CustomFunctionsContext * functionsContext);
void menuModelNotes(uint8_t event);
void menuStatisticsView(uint8_t event);
void menuStatisticsDebug(uint8_t event);
void menuAboutView(uint8_t event);
#if defined(DEBUG_TRACE_BUFFER)
void menuTraceBuffer(uint8_t event);
#endif
enum EnumTabRadio {
e_Setup,
e_Sd,
e_GeneralCustomFunctions,
e_Trainer,
e_Vers,
e_Keys,
e_Ana,
e_Hardware,
e_Calib,
e_TabRadioPagesCount
};
void menuGeneralSetup(uint8_t event);
void menuGeneralSdManager(uint8_t event);
void menuGeneralCustomFunctions(uint8_t event);
void menuGeneralTrainer(uint8_t event);
void menuGeneralVersion(uint8_t event);
void menuGeneralDiagKeys(uint8_t event);
void menuGeneralDiagAna(uint8_t event);
void menuGeneralHardware(uint8_t event);
void menuGeneralCalib(uint8_t event);
extern const MenuHandlerFunc menuTabGeneral[e_TabRadioPagesCount];
enum EnumTabModel {
e_ModelSelect,
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
e_Telemetry,
CASE_MAVLINK(e_MavSetup)
e_Display,
e_TabModelPagesCount
};
void menuModelSelect(uint8_t event);
void menuModelSetup(uint8_t event);
void menuModelHeli(uint8_t event);
void menuModelFlightModesAll(uint8_t event);
void menuModelExposAll(uint8_t event);
void menuModelMixAll(uint8_t event);
void menuModelLimits(uint8_t event);
void menuModelCurvesAll(uint8_t event);
void menuModelCurveOne(uint8_t event);
void menuModelGVars(uint8_t event);
void menuModelLogicalSwitches(uint8_t event);
void menuModelCustomFunctions(uint8_t event);
void menuModelCustomScripts(uint8_t event);
void menuModelTelemetry(uint8_t event);
void menuModelDisplay(uint8_t event);
void menuModelExpoOne(uint8_t event);
extern const MenuHandlerFunc menuTabModel[e_TabModelPagesCount];
void drawSlider(coord_t x, coord_t y, uint8_t value, uint8_t max, uint8_t attr);
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
#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)
// mawrow special values
#define TITLE_ROW ((uint8_t)-1)
#define HIDDEN_ROW ((uint8_t)-2)
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(unsigned int 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 NAVIGATION_LINE_BY_LINE 0x40
#define CURSOR_ON_LINE() (menuHorizontalPosition<0)
#define CHECK_FLAG_NO_SCREEN_INDEX 1
void check(const char *title, check_event_t event, uint8_t curr, const MenuHandlerFunc *menuTab, uint8_t menuTabSize, const pm_uint8_t *horTab, uint8_t horTabMax, vertpos_t maxrow, uint8_t flags=0);
void check_simple(const char *title, check_event_t event, uint8_t curr, const MenuHandlerFunc *menuTab, uint8_t menuTabSize, vertpos_t maxrow);
void check_submenu_simple(const char *title, check_event_t event, uint8_t maxrow);
void title(const pm_char * s);
#define TITLE(str) title(str)
#define MENU_TAB(...) const uint8_t mstate_tab[] = __VA_ARGS__
#define MENU_CHECK(title, tab, menu, lines_count) \
check(title, event, menu, tab, DIM(tab), mstate_tab, DIM(mstate_tab)-1, lines_count)
#define MENU_CHECK_FLAGS(title, tab, menu, flags, lines_count) \
check(title, event, menu, tab, DIM(tab), mstate_tab, DIM(mstate_tab)-1, lines_count, flags)
#define MENU(title, tab, menu, lines_count, ...) \
MENU_TAB(__VA_ARGS__); \
MENU_CHECK(title, tab, menu, lines_count)
#define MENU_FLAGS(title, tab, menu, flags, lines_count, ...) \
MENU_TAB(__VA_ARGS__); \
MENU_CHECK_FLAGS(title, tab, menu, flags, lines_count)
#define SIMPLE_MENU_FLAGS(title, tab, menu, flags, lines_count, ...) \
check(title, event, menu, tab, DIM(tab), NULL, 0, lines_count, flags)
#define SIMPLE_MENU(title, tab, menu, lines_count) \
check_simple(title, event, menu, tab, DIM(tab), lines_count)
#define SUBMENU_NOTITLE(lines_count, ...) { \
MENU_TAB(__VA_ARGS__); \
check(NULL, event, 0, NULL, 0, mstate_tab, DIM(mstate_tab)-1, lines_count); \
}
#define SUBMENU(title, lines_count, ...) \
MENU_TAB(__VA_ARGS__); \
check(title, event, 0, NULL, 0, mstate_tab, DIM(mstate_tab)-1, lines_count)
#define SIMPLE_SUBMENU_NOTITLE(lines_count) \
check_submenu_simple(NULL, event, lines_count);
#define SIMPLE_SUBMENU(title, lines_count) \
check_submenu_simple(title, event, lines_count)
typedef int select_menu_value_t;
select_menu_value_t selectMenuItem(coord_t x, coord_t y, const pm_char *label, const pm_char *values, select_menu_value_t value, select_menu_value_t min, select_menu_value_t max, LcdFlags attr, uint8_t event);
uint8_t editCheckBox(uint8_t value, coord_t x, coord_t y, const pm_char *label, LcdFlags attr, uint8_t event);
swsrc_t switchMenuItem(coord_t x, coord_t y, swsrc_t value, LcdFlags attr, uint8_t event);
#define ON_OFF_MENU_ITEM(value, x, y, label, attr, event) value = editCheckBox(value, x, y, label, attr, event)
#if defined(GVARS)
void drawGVarName(coord_t x, coord_t y, int8_t index, LcdFlags flags=0);
void drawGVarValue(coord_t x, coord_t y, uint8_t gvar, gvar_t value, LcdFlags flags=0);
int16_t editGVarFieldValue(coord_t x, coord_t y, int16_t value, int16_t min, int16_t max, LcdFlags attr, uint8_t editflags, uint8_t event);
#define GVAR_MENU_ITEM(x, y, v, min, max, lcdattr, editflags, event) editGVarFieldValue(x, y, v, min, max, lcdattr, editflags, event)
#define displayGVar(x, y, v, min, max) GVAR_MENU_ITEM(x, y, v, min, max, 0, 0, 0)
#else
#define GVAR_MENU_ITEM(x, y, v, min, max, lcdattr, editflags, event) editGVarFieldValue(x, y, v, min, max, lcdattr, event)
int16_t editGVarFieldValue(coord_t x, coord_t y, int16_t value, int16_t min, int16_t max, LcdFlags attr, uint8_t event);
#define displayGVar(x, y, v, min, max) lcdDrawNumber(x, y, v)
#endif
extern uint8_t s_curveChan;
void editCurveRef(coord_t x, coord_t y, CurveRef & curve, uint8_t event, uint8_t attr);
extern uint8_t editNameCursorPos;
void editName(coord_t x, coord_t y, char *name, uint8_t size, uint8_t event, uint8_t active, uint8_t attr=ZCHAR);
void editSingleName(coord_t x, coord_t y, const pm_char *label, char *name, uint8_t size, uint8_t event, uint8_t active);
#if MENU_COLUMNS > 1
uint8_t editDelay(const coord_t x, const coord_t y, const uint8_t event, const uint8_t attr, const pm_char *str, uint8_t delay);
#define EDIT_DELAY(x, y, event, attr, str, delay) editDelay(x, y, event, attr, str, delay)
#else
uint8_t editDelay(const coord_t y, const uint8_t event, const uint8_t attr, const pm_char *str, uint8_t delay);
#define EDIT_DELAY(x, y, event, attr, str, delay) editDelay(y, event, attr, str, delay)
#endif
#define WARNING_TYPE_ASTERISK 0
#define WARNING_TYPE_CONFIRM 1
#define WARNING_TYPE_INPUT 2
void copySelection(char * dst, const char * src, uint8_t size);
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 uint8_t s_maxLines;
extern uint8_t s_copySrcIdx;
extern uint8_t s_copySrcCh;
extern int8_t s_currCh;
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 30
#define MENU_Y 16
#define MENU_W LCD_W-(2*MENU_X)
#define WARNING_LINE_LEN 32
#define WARNING_LINE_X 16
#define WARNING_LINE_Y 3*FH
void displayBox(const char *title);
void displayPopup(const char *title);
void displayWarning(uint8_t event);
extern void (*popupFunc)(uint8_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) (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 6
#define POPUP_MENU_ADD_SD_ITEM(s) POPUP_MENU_ADD_ITEM(s)
#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(uint8_t event);
extern void (*popupMenuHandler)(const char *result);
#define STATUS_LINE_LENGTH 32
extern char statusLineMsg[STATUS_LINE_LENGTH];
void showStatusLine();
void drawStatusLine();
#define TEXT_FILENAME_MAXLEN 40
extern char s_text_file[TEXT_FILENAME_MAXLEN];
void menuTextView(uint8_t event);
void pushMenuTextView(const char *filename);
void pushModelNotes();
void menuChannelsView(uint8_t event);
#define LABEL(...) (uint8_t)-1
#if defined(REV9E) && !defined(SIMU)
#define KEY_UP KEY_MINUS
#define KEY_DOWN KEY_PLUS
#define KEY_RIGHT KEY_PLUS
#define KEY_LEFT KEY_MINUS
#define CURSOR_MOVED_LEFT(event) (EVT_KEY_MASK(event) == KEY_LEFT)
#define CURSOR_MOVED_RIGHT(event) (EVT_KEY_MASK(event) == KEY_RIGHT)
#else
#define KEY_UP KEY_PLUS
#define KEY_DOWN KEY_MINUS
#define KEY_RIGHT KEY_MINUS
#define KEY_LEFT KEY_PLUS
#define CURSOR_MOVED_LEFT(event) (EVT_KEY_MASK(event) == KEY_LEFT)
#define CURSOR_MOVED_RIGHT(event) (EVT_KEY_MASK(event) == KEY_RIGHT)
#endif
#define REPEAT_LAST_CURSOR_MOVE() { if (CURSOR_MOVED_LEFT(event) || CURSOR_MOVED_RIGHT(event)) putEvent(event); else menuHorizontalPosition = 0; }
#define MOVE_CURSOR_FROM_HERE() if (menuHorizontalPosition > 0) REPEAT_LAST_CURSOR_MOVE()
#define MENU_FIRST_LINE_EDIT (menuTab ? (MAXCOL((uint16_t)0) >= HIDDEN_ROW ? (MAXCOL((uint16_t)1) >= HIDDEN_ROW ? 2 : 1) : 0) : 0)
#define POS_HORZ_INIT(posVert) ((COLATTR(posVert) & NAVIGATION_LINE_BY_LINE) ? -1 : 0)
#define EDIT_MODE_INIT 0 // TODO enum
typedef int16_t (*FnFuncP) (int16_t x);
void drawFunction(FnFuncP fn, uint8_t offset=0);
void onSourceLongEnterPress(const char *result);
uint8_t switchToMix(uint8_t source);
#endif // _MENUS_H_
+322
View File
@@ -0,0 +1,322 @@
/*
* 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 s_curveChan;
int16_t curveFn(int16_t 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 = X0-1-WCHART+i*WCHART*2/(count-1);
result.y = (LCD_H-1) - (100 + points[i]) * (LCD_H-1) / 200;
if (custom && i>0 && i<count-1)
result.x = X0-1-WCHART + (100 + (100 + points[count+i-1]) * (2*WCHART)) / 200;
}
return result;
}
void DrawCurve(uint8_t offset=0)
{
drawFunction(curveFn, offset);
uint8_t i = 0;
do {
point_t point = getPoint(i);
i++;
if (point.x == 0) break;
lcdDrawFilledRect(point.x-offset, point.y-1, 3, 3, SOLID, FORCE); // do markup square
} while(1);
}
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(uint8_t event)
{
displayWarning(event);
lcdDrawNumber(WARNING_LINE_X+FW*7, WARNING_LINE_Y, 45*warningInputValue/4, LEFT|INVERS);
lcdDrawChar(lcdLastPos, WARNING_LINE_Y, '@', INVERS);
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);
}
}
}
void menuModelCurveOne(uint8_t event)
{
static uint8_t pointsOfs = 0;
CurveData & crv = g_model.curves[s_curveChan];
int8_t * points = curveAddress(s_curveChan);
lcdDrawText(11*FW+FW/2, 0, TR_PT "\002X\006Y");
drawStringWithIndex(PSIZE(TR_MENUCURVES)*FW+FW, 0, "CV", s_curveChan+1);
lcdDrawFilledRect(0, 0, LCD_W, FH, SOLID, FILL_WHITE|GREY_DEFAULT);
SIMPLE_SUBMENU(STR_MENUCURVES, 4 + 5+crv.points + (crv.type==CURVE_TYPE_CUSTOM ? 5+crv.points-2 : 0));
lcd_putsLeft(FH+1, STR_NAME);
editName(INDENT_WIDTH, 2*FH+1, crv.name, sizeof(crv.name), event, menuVerticalPosition==0);
uint8_t attr = (menuVerticalPosition==1 ? (s_editMode>0 ? INVERS|BLINK : INVERS) : 0);
lcd_putsLeft(3*FH+1, STR_TYPE);
lcdDrawTextAtIndex(INDENT_WIDTH, 4*FH+1, 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(getCurveX(5+crv.points, i)), 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;
}
}
attr = (menuVerticalPosition==2 ? (s_editMode>0 ? INVERS|BLINK : INVERS) : 0);
lcd_putsLeft(5*FH+1, STR_COUNT);
lcdDrawNumber(INDENT_WIDTH, 6*FH+1, 5+crv.points, LEFT|attr);
lcdDrawText(lcdLastPos, 6*FH+1, STR_PTS, attr);
if (attr) {
int8_t count = checkIncDecModel(event, crv.points, -3, 12); // 2pts - 17pts
if (checkIncDec_Ret) {
int8_t 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(getCurveX(5+count, i)), 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] = getCurveX(5+count, i);
}
crv.points = count;
}
}
lcd_putsLeft(7*FH+1, STR_SMOOTH);
drawCheckBox(7 * FW, 7 * FH + 1, crv.smooth, menuVerticalPosition == 3 ? INVERS : 0);
if (menuVerticalPosition==3) crv.smooth = checkIncDecModel(event, crv.smooth, 0, 1);
switch(event) {
case EVT_ENTRY:
pointsOfs = 0;
SET_SCROLLBAR_X(0);
break;
case EVT_KEY_LONG(KEY_ENTER):
if (menuVerticalPosition > 1) {
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;
case EVT_KEY_LONG(KEY_MENU):
pushMenu(menuChannelsView);
killEvents(event);
}
DrawCurve(FW);
uint8_t posY = FH+1;
attr = (s_editMode > 0 ? INVERS|BLINK : INVERS);
for (uint8_t i=0; i<5+crv.points; i++) {
point_t point = getPoint(i);
uint8_t selectionMode = 0;
if (crv.type==CURVE_TYPE_CUSTOM) {
if (menuVerticalPosition==4+2*i || (i==5+crv.points-1 && menuVerticalPosition==4+5+crv.points+5+crv.points-2-1))
selectionMode = 2;
else if (i>0 && menuVerticalPosition==3+2*i)
selectionMode = 1;
}
else if (menuVerticalPosition == 4+i) {
selectionMode = 2;
}
if (i>=pointsOfs && i<pointsOfs+7) {
int8_t x = getCurveX(5+crv.points, i);
if (crv.type==CURVE_TYPE_CUSTOM && i>0 && i<5+crv.points-1) x = points[5+crv.points+i-1];
lcdDrawNumber(6+10*FW+FW/2, posY, i+1, LEFT);
lcdDrawNumber(3+14*FW, posY, x, LEFT|(selectionMode==1?attr:0));
lcdDrawNumber(3+18*FW, posY, points[i], LEFT|(selectionMode==2?attr:0));
posY += FH;
}
if (selectionMode > 0) {
// do selection square
lcdDrawFilledRect(point.x-FW-1, point.y-2, 5, 5, SOLID, FORCE);
lcdDrawFilledRect(point.x-FW, point.y-1, 3, 3, SOLID);
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+6)
pointsOfs = i-6;
}
}
}
void editCurveRef(coord_t x, coord_t y, CurveRef & curve, uint8_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(x+5*FW+2, y, curve.value, -100, 100, menuHorizontalPosition==1 ? LEFT|attr : LEFT, 0, event);
break;
case CURVE_REF_FUNC:
lcdDrawTextAtIndex(x+5*FW+2, 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(x+5*FW+2, 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;
}
}
void menuModelCurvesAll(uint8_t event)
{
SIMPLE_MENU(STR_MENUCURVES, menuTabModel, e_CurvesAll, MAX_CURVES);
int sub = menuVerticalPosition;
switch (event) {
case EVT_KEY_BREAK(KEY_ENTER):
if (!READ_ONLY()) {
s_curveChan = sub;
pushMenu(menuModelCurveOne);
}
break;
}
for (int i=0; i<LCD_LINES-1; i++) {
coord_t y = MENU_HEADER_HEIGHT + 1 + i*FH;
int k = i + menuVerticalOffset;
LcdFlags attr = (sub == k ? INVERS : 0);
{
drawStringWithIndex(0, y, STR_CV, k+1, attr);
CurveData & crv = g_model.curves[k];
editName(4*FW, y, crv.name, sizeof(crv.name), 0, 0);
lcdDrawNumber(11*FW, y, 5+crv.points, LEFT);
lcdDrawText(lcdLastPos, y, STR_PTS, 0);
}
}
s_curveChan = sub;
DrawCurve(23);
}
@@ -0,0 +1,166 @@
/*
* 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 (12*FW)
#define SCRIPT_ONE_3RD_COLUMN_POS (23*FW)
void menuModelCustomScriptOne(uint8_t event)
{
ScriptData & sd = g_model.scriptsData[s_currIdx];
drawStringWithIndex(PSIZE(TR_MENUCUSTOMSCRIPTS)*FW+FW, 0, "LUA", s_currIdx+1, 0);
lcdDrawFilledRect(0, 0, LCD_W, FH, SOLID, FILL_WHITE|GREY_DEFAULT);
SUBMENU(STR_MENUCUSTOMSCRIPTS, 3+scriptInputsOutputs[s_currIdx].inputsCount, { 0, 0, LABEL(inputs), 0/*repeated*/ });
int8_t sub = menuVerticalPosition;
for (int k=0; k<LCD_LINES-1; k++) {
coord_t y = MENU_HEADER_HEIGHT + 1 + k*FH;
int i = k + menuVerticalOffset;
LcdFlags attr = (sub==i ? (s_editMode>0 ? BLINK|INVERS : INVERS) : 0);
if (i == ITEM_MODEL_CUSTOMSCRIPT_FILE) {
lcd_putsLeft(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 && event==EVT_KEY_BREAK(KEY_ENTER) && !READ_ONLY()) {
s_editMode = 0;
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) {
lcd_putsLeft(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) {
lcd_putsLeft(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, FH+1, LCD_H-FH-1);
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, FH+1+FH+i*FH, MIXSRC_FIRST_LUA+(s_currIdx*MAX_SCRIPT_OUTPUTS)+i, 0);
lcdDrawNumber(SCRIPT_ONE_3RD_COLUMN_POS+11*FW+3, FH+1+FH+i*FH, calcRESXto1000(scriptInputsOutputs[s_currIdx].outputs[i].value), PREC1|RIGHT);
}
}
}
void menuModelCustomScripts(uint8_t event)
{
lcdDrawNumber(19*FW, 0, luaGetMemUsed(), RIGHT);
lcdDrawText(19*FW+1, 0, STR_BYTES);
MENU(STR_MENUCUSTOMSCRIPTS, menuTabModel, e_CustomScripts, MAX_SCRIPTS, { NAVIGATION_LINE_BY_LINE|3/*repeated*/ });
coord_t y;
int8_t sub = menuVerticalPosition;
if (event == EVT_KEY_FIRST(KEY_ENTER)) {
s_currIdx = sub;
pushMenu(menuModelCustomScriptOne);
}
for (int i=0, scriptIndex=0; i<MAX_SCRIPTS; i++) {
y = 1 + (i+1)*FH;
ScriptData &sd = g_model.scriptsData[i];
// LUAx header
drawStringWithIndex(0, y, "LUA", i+1, sub==i ? INVERS : 0);
// LUA script
if (ZEXIST(sd.file)) {
lcdDrawSizedText(5*FW, y, sd.file, sizeof(sd.file), 0);
switch (scriptInternalData[scriptIndex].state) {
case SCRIPT_SYNTAX_ERROR:
lcdDrawText(30*FW+2, y, "(error)");
break;
case SCRIPT_KILLED:
lcdDrawText(29*FW+2, y, "(killed)");
break;
default:
lcdDrawNumber(34*FW, y, luaGetCpuUsed(scriptIndex), RIGHT);
lcdDrawChar(34*FW, y, '%');
break;
}
scriptIndex++;
}
else {
lcdDrawTextAtIndex(5*FW, y, STR_VCSWFUNC, 0, 0);
}
// Script name
lcdDrawSizedText(16*FW, y, sd.name, sizeof(sd.name), ZCHAR);
}
}
+257
View File
@@ -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"
enum menuModelDisplayItems {
ITEM_DISPLAY_TOP_BAR_LABEL,
ITEM_DISPLAY_TOP_BAR_VOLTAGE,
ITEM_DISPLAY_TOP_BAR_ALTITUDE,
ITEM_DISPLAY_SCREEN_LABEL1,
ITEM_DISPLAY_SCREEN_LINE1,
ITEM_DISPLAY_SCREEN_LINE2,
ITEM_DISPLAY_SCREEN_LINE3,
ITEM_DISPLAY_SCREEN_LINE4,
ITEM_DISPLAY_SCREEN_LABEL2,
ITEM_DISPLAY_SCREEN_LINE5,
ITEM_DISPLAY_SCREEN_LINE6,
ITEM_DISPLAY_SCREEN_LINE7,
ITEM_DISPLAY_SCREEN_LINE8,
ITEM_DISPLAY_SCREEN_LABEL3,
ITEM_DISPLAY_SCREEN_LINE9,
ITEM_DISPLAY_SCREEN_LINE10,
ITEM_DISPLAY_SCREEN_LINE11,
ITEM_DISPLAY_SCREEN_LINE12,
ITEM_DISPLAY_SCREEN_LABEL4,
ITEM_DISPLAY_SCREEN_LINE13,
ITEM_DISPLAY_SCREEN_LINE14,
ITEM_DISPLAY_SCREEN_LINE15,
ITEM_DISPLAY_SCREEN_LINE16,
ITEM_DISPLAY_MAX
};
#define DISPLAY_COL1 (1*FW)
#define DISPLAY_COL2 (16*FW)
#define DISPLAY_COL3 (28*FW+2)
#if defined(LUA)
#define SCREEN_TYPE_ROWS 1
#define DISPLAY_LINE_ROWS(x) ((TELEMETRY_SCREEN_TYPE(x) == TELEMETRY_SCREEN_TYPE_NONE || TELEMETRY_SCREEN_TYPE(x) == TELEMETRY_SCREEN_TYPE_SCRIPT) ? HIDDEN_ROW : (uint8_t)2)
#else
#define SCREEN_TYPE_ROWS 0
#define DISPLAY_LINE_ROWS(x) (TELEMETRY_SCREEN_TYPE(x) == TELEMETRY_SCREEN_TYPE_NONE ? HIDDEN_ROW : (uint8_t)2)
#endif
#define TELEMETRY_SCREEN_ROWS(x) SCREEN_TYPE_ROWS, DISPLAY_LINE_ROWS(x), DISPLAY_LINE_ROWS(x), DISPLAY_LINE_ROWS(x), DISPLAY_LINE_ROWS(x)
#define TELEMETRY_CURRENT_SCREEN(k) (k < ITEM_DISPLAY_SCREEN_LABEL2 ? 0 : (k < ITEM_DISPLAY_SCREEN_LABEL3 ? 1 : (k < ITEM_DISPLAY_SCREEN_LABEL4 ? 2 : 3)))
#if defined(LUA)
void onTelemetryScriptFileSelectionMenu(const char *result)
{
int screenIndex = TELEMETRY_CURRENT_SCREEN(menuVerticalPosition);
if (result == STR_UPDATE_LIST) {
if (!sdListFiles(SCRIPTS_TELEM_PATH, SCRIPTS_EXT, sizeof(g_model.frsky.screens[screenIndex].script.file), NULL)) {
POPUP_WARNING(STR_NO_SCRIPTS_ON_SD);
}
}
else {
// The user choosed a file in the list
memcpy(g_model.frsky.screens[screenIndex].script.file, result, sizeof(g_model.frsky.screens[screenIndex].script.file));
storageDirty(EE_MODEL);
LUA_LOAD_MODEL_SCRIPTS();
}
}
#endif
void menuModelDisplay(uint8_t event)
{
MENU(STR_MENU_DISPLAY, menuTabModel, e_Display, ITEM_DISPLAY_MAX, { LABEL(TopBar), 0, 0, TELEMETRY_SCREEN_ROWS(0), TELEMETRY_SCREEN_ROWS(1), TELEMETRY_SCREEN_ROWS(2), TELEMETRY_SCREEN_ROWS(3) });
for (int i=0; i<NUM_BODY_LINES; i++) {
coord_t y = MENU_HEADER_HEIGHT + 1 + 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);
switch (k) {
case ITEM_DISPLAY_TOP_BAR_LABEL:
lcd_putsLeft(y, STR_TOP_BAR);
break;
case ITEM_DISPLAY_TOP_BAR_VOLTAGE:
lcd_putsLeft(y, STR_VOLTAGE);
putsMixerSource(DISPLAY_COL2, y, g_model.frsky.voltsSource ? MIXSRC_FIRST_TELEM+3*(g_model.frsky.voltsSource-1) : 0, attr);
if (attr) {
g_model.frsky.voltsSource = checkIncDec(event, g_model.frsky.voltsSource, 0, MAX_SENSORS, EE_MODEL|NO_INCDEC_MARKS, isVoltsSensor);
}
break;
case ITEM_DISPLAY_TOP_BAR_ALTITUDE:
lcd_putsLeft(y, STR_ALTITUDE);
putsMixerSource(DISPLAY_COL2, y, g_model.frsky.altitudeSource ? MIXSRC_FIRST_TELEM+3*(g_model.frsky.altitudeSource-1) : 0, attr);
if (attr) {
g_model.frsky.altitudeSource = checkIncDec(event, g_model.frsky.altitudeSource, 0, MAX_SENSORS, EE_MODEL|NO_INCDEC_MARKS, isAltSensor);
}
break;
case ITEM_DISPLAY_SCREEN_LABEL1:
case ITEM_DISPLAY_SCREEN_LABEL2:
case ITEM_DISPLAY_SCREEN_LABEL3:
case ITEM_DISPLAY_SCREEN_LABEL4:
{
uint8_t screenIndex = TELEMETRY_CURRENT_SCREEN(k);
drawStringWithIndex(0*FW, y, STR_SCREEN, screenIndex+1);
TelemetryScreenType oldScreenType = TELEMETRY_SCREEN_TYPE(screenIndex);
TelemetryScreenType newScreenType = (TelemetryScreenType)selectMenuItem(DISPLAY_COL2, y, PSTR(""), STR_VTELEMSCREENTYPE, oldScreenType, 0, TELEMETRY_SCREEN_TYPE_MAX, (menuHorizontalPosition==0 ? attr : 0), event);
if (newScreenType != oldScreenType) {
g_model.frsky.screensType = (g_model.frsky.screensType & (~(0x03 << (2*screenIndex)))) | (newScreenType << (2*screenIndex));
memset(&g_model.frsky.screens[screenIndex], 0, sizeof(g_model.frsky.screens[screenIndex]));
}
#if defined(LUA)
if (newScreenType == TELEMETRY_SCREEN_TYPE_SCRIPT) {
TelemetryScriptData & scriptData = g_model.frsky.screens[screenIndex].script;
// TODO better function name for ---
// TODO function for these lines
if (ZEXIST(scriptData.file))
lcdDrawSizedText(DISPLAY_COL2+7*FW, y, scriptData.file, sizeof(scriptData.file), (menuHorizontalPosition==1 ? attr : 0));
else
lcdDrawTextAtIndex(DISPLAY_COL2+7*FW, y, STR_VCSWFUNC, 0, (menuHorizontalPosition==1 ? attr : 0));
if (menuHorizontalPosition==1 && attr && event==EVT_KEY_BREAK(KEY_ENTER) && READ_ONLY_UNLOCKED()) {
s_editMode = 0;
if (sdListFiles(SCRIPTS_TELEM_PATH, SCRIPTS_EXT, sizeof(g_model.frsky.screens[screenIndex].script.file), g_model.frsky.screens[screenIndex].script.file)) {
POPUP_MENU_START(onTelemetryScriptFileSelectionMenu);
}
else {
POPUP_WARNING(STR_NO_SCRIPTS_ON_SD);
}
}
}
else if (attr) {
MOVE_CURSOR_FROM_HERE();
}
#endif
break;
}
case ITEM_DISPLAY_SCREEN_LINE1:
case ITEM_DISPLAY_SCREEN_LINE2:
case ITEM_DISPLAY_SCREEN_LINE3:
case ITEM_DISPLAY_SCREEN_LINE4:
case ITEM_DISPLAY_SCREEN_LINE5:
case ITEM_DISPLAY_SCREEN_LINE6:
case ITEM_DISPLAY_SCREEN_LINE7:
case ITEM_DISPLAY_SCREEN_LINE8:
case ITEM_DISPLAY_SCREEN_LINE9:
case ITEM_DISPLAY_SCREEN_LINE10:
case ITEM_DISPLAY_SCREEN_LINE11:
case ITEM_DISPLAY_SCREEN_LINE12:
case ITEM_DISPLAY_SCREEN_LINE13:
case ITEM_DISPLAY_SCREEN_LINE14:
case ITEM_DISPLAY_SCREEN_LINE15:
case ITEM_DISPLAY_SCREEN_LINE16:
{
uint8_t screenIndex, lineIndex;
if (k < ITEM_DISPLAY_SCREEN_LABEL2) {
screenIndex = 0;
lineIndex = k-ITEM_DISPLAY_SCREEN_LINE1;
}
else if (k >= ITEM_DISPLAY_SCREEN_LABEL4) {
screenIndex = 3;
lineIndex = k-ITEM_DISPLAY_SCREEN_LINE13;
}
else if (k >= ITEM_DISPLAY_SCREEN_LABEL3) {
screenIndex = 2;
lineIndex = k-ITEM_DISPLAY_SCREEN_LINE9;
}
else {
screenIndex = 1;
lineIndex = k-ITEM_DISPLAY_SCREEN_LINE5;
}
if (IS_BARS_SCREEN(screenIndex)) {
FrSkyBarData & bar = g_model.frsky.screens[screenIndex].bars[lineIndex];
source_t barSource = bar.source;
putsMixerSource(DISPLAY_COL1, y, barSource, menuHorizontalPosition==0 ? attr : 0);
int barMax = getMaximumValue(barSource);
int barMin = -barMax;
if (barSource) {
if (barSource <= MIXSRC_LAST_CH) {
putsChannelValue(DISPLAY_COL2, y, barSource, calc100toRESX(bar.barMin), (menuHorizontalPosition==1 ? attr : 0) | LEFT);
putsChannelValue(DISPLAY_COL3, y, barSource, calc100toRESX(bar.barMax), (menuHorizontalPosition==2 ? attr : 0) | LEFT);
}
else {
putsChannelValue(DISPLAY_COL2, y, barSource, bar.barMin, (menuHorizontalPosition==1 ? attr : 0) | LEFT);
putsChannelValue(DISPLAY_COL3, y, barSource, bar.barMax, (menuHorizontalPosition==2 ? attr : 0) | LEFT);
}
}
else if (attr) {
MOVE_CURSOR_FROM_HERE();
}
if (attr && s_editMode>0) {
switch (menuHorizontalPosition) {
case 0:
bar.source = checkIncDec(event, barSource, 0, MIXSRC_LAST_TELEM, EE_MODEL|INCDEC_SOURCE|NO_INCDEC_MARKS, isSourceAvailable);
if (checkIncDec_Ret) {
if (barSource <= MIXSRC_LAST_CH) {
bar.barMin = -100;
bar.barMax = 100;
}
else {
bar.barMin = 0;
bar.barMax = 0;
}
}
break;
case 1:
bar.barMin = checkIncDec(event, bar.barMin, barMin, bar.barMax, EE_MODEL|NO_INCDEC_MARKS);
break;
case 2:
bar.barMax = checkIncDec(event, bar.barMax, bar.barMin, barMax, EE_MODEL|NO_INCDEC_MARKS);
break;
}
}
}
else {
for (int c=0; c<NUM_LINE_ITEMS; c++) {
LcdFlags cellAttr = (menuHorizontalPosition==c ? attr : 0);
source_t & value = g_model.frsky.screens[screenIndex].lines[lineIndex].sources[c];
const coord_t pos[] = {DISPLAY_COL1, DISPLAY_COL2, DISPLAY_COL3};
putsMixerSource(pos[c], y, value, cellAttr);
if (cellAttr && s_editMode>0) {
value = checkIncDec(event, value, 0, MIXSRC_LAST_TELEM, EE_MODEL|INCDEC_SOURCE|NO_INCDEC_MARKS, isSourceAvailable);
}
}
if (attr && menuHorizontalPosition == NUM_LINE_ITEMS) {
REPEAT_LAST_CURSOR_MOVE();
}
}
break;
}
}
}
}
+171
View File
@@ -0,0 +1,171 @@
/*
* 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 displayFlightModes(coord_t x, coord_t y, FlightModesType value)
{
lcdDrawText(x, y, STR_FP);
x = lcdNextPos + 1;
for (uint8_t p=0; p<MAX_FLIGHT_MODES; p++) {
lcdDrawChar(x, y, ((value & (1<<p)) ? '-' : '0'+p));
x += 5;
}
}
FlightModesType editFlightModes(coord_t x, coord_t y, uint8_t event, FlightModesType value, uint8_t attr)
{
lcd_putsColumnLeft(x, y, STR_FLMODE);
int posHorz = menuHorizontalPosition;
for (int p=0; p<MAX_FLIGHT_MODES; p++) {
LcdFlags flags = 0;
if (attr) {
flags |= INVERS;
if (posHorz==p) flags |= BLINK;
}
if (value & (1<<p))
lcdDrawChar(x, y, ' ', flags|FIXEDWIDTH);
else
lcdDrawChar(x, y, '0'+p, flags);
x += FW;
}
if (attr) {
if (s_editMode && event==EVT_KEY_BREAK(KEY_ENTER)) {
s_editMode = 0;
value ^= (1<<posHorz);
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_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);
}
void menuModelFlightModesAll(uint8_t event)
{
MENU(STR_MENUFLIGHTPHASES, menuTabModel, e_FlightModesAll, MAX_FLIGHT_MODES+1, { NAVIGATION_LINE_BY_LINE|(ITEM_FLIGHT_MODES_LAST-1), 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 });
int8_t sub = menuVerticalPosition;
horzpos_t posHorz = menuHorizontalPosition;
if (sub==0 && posHorz > 0) { posHorz += 1; }
if (sub<MAX_FLIGHT_MODES && posHorz>=0) {
drawColumnHeader(STR_PHASES_HEADERS, posHorz);
}
for (uint8_t i=0; i<LCD_LINES-1; i++) {
coord_t y = MENU_HEADER_HEIGHT + 1 + i*FH;
uint8_t k = i+menuVerticalOffset;
if (k==MAX_FLIGHT_MODES) {
// last line available - add the "check trims" line
lcd_putsLeft((LCD_LINES-1)*FH+1, STR_CHECKTRIMS);
putsFlightMode(OFS_CHECKTRIMS, (LCD_LINES-1)*FH+1, mixerCurrentFlightMode+1);
if (sub==MAX_FLIGHT_MODES) {
if (!trimsCheckTimer) {
if (event == EVT_KEY_FIRST(KEY_ENTER)) {
trimsCheckTimer = 200; // 2 seconds trims cancelled
s_editMode = 1;
killEvents(event);
}
else {
lcdInvertLastLine();
s_editMode = 0;
}
}
else {
if (event == EVT_KEY_FIRST(KEY_EXIT)) {
trimsCheckTimer = 0;
s_editMode = 0;
killEvents(event);
}
}
}
return;
}
FlightModeData *p = flightModeAddress(k);
putsFlightMode(0, y, k+1, (getFlightMode()==k ? BOLD : 0) | ((sub==k && menuHorizontalPosition<0) ? INVERS : 0));
for (uint8_t j=0; j<ITEM_FLIGHT_MODES_COUNT; j++) {
uint8_t attr = ((sub==k && posHorz==j) ? ((s_editMode>0) ? BLINK|INVERS : INVERS) : 0);
uint8_t active = (attr && s_editMode>0) ;
switch (j) {
case ITEM_FLIGHT_MODES_NAME:
editName(4*FW-1, y, p->name, sizeof(p->name), event, attr);
break;
case ITEM_FLIGHT_MODES_SWITCH:
if (k>0) {
putsSwitches((4+LEN_FLIGHT_MODE_NAME)*FW+FW/2, y, p->swtch, attr);
if (active) CHECK_INCDEC_MODELSWITCH(event, p->swtch, SWSRC_FIRST_IN_MIXES, SWSRC_LAST_IN_MIXES, isSwitchAvailableInMixes);
}
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:
{
uint8_t t = j-ITEM_FLIGHT_MODES_TRIM_RUD;
putsTrimMode((4+LEN_FLIGHT_MODE_NAME)*FW+j*(5*FW/2), y, k, t, attr);
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);
}
break;
}
case ITEM_FLIGHT_MODES_FADE_IN:
lcdDrawNumber(32*FW-2, y, (10/DELAY_STEP)*p->fadeIn, attr|PREC1|RIGHT);
if (active) p->fadeIn = checkIncDec(event, p->fadeIn, 0, DELAY_MAX, EE_MODEL|NO_INCDEC_MARKS);
break;
case ITEM_FLIGHT_MODES_FADE_OUT:
lcdDrawNumber(35*FW, y, (10/DELAY_STEP)*p->fadeOut, attr|PREC1|RIGHT);
if (active) p->fadeOut = checkIncDec(event, p->fadeOut, 0, DELAY_MAX, EE_MODEL|NO_INCDEC_MARKS);
break;
}
}
}
}
+187
View File
@@ -0,0 +1,187 @@
/*
* 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 editGVarValue(coord_t x, coord_t y, uint8_t event, uint8_t gvar, uint8_t flightMode, LcdFlags flags)
{
FlightModeData * fm = &g_model.flightModeData[flightMode];
gvar_t & v = fm->gvars[gvar];
int16_t vmin, vmax;
if (v > GVAR_MAX) {
uint8_t fm = v - GVAR_MAX - 1;
if (fm >= flightMode) fm++;
putsFlightMode(x, y, fm + 1, flags&(~LEFT));
vmin = GVAR_MAX + 1;
vmax = GVAR_MAX + MAX_FLIGHT_MODES - 1;
}
else {
drawGVarValue(x, y, gvar, v, flags);
vmin = GVAR_MIN + g_model.gvars[gvar].min;
vmax = GVAR_MAX - g_model.gvars[gvar].max;
}
if (flags & INVERS) {
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);
}
}
}
enum GVarFields {
GVAR_FIELD_NAME,
GVAR_FIELD_UNIT,
GVAR_FIELD_PREC,
GVAR_FIELD_MIN,
GVAR_FIELD_MAX,
GVAR_FIELD_POPUP,
GVAR_FIELD_FM0,
GVAR_FIELD_LAST=GVAR_FIELD_FM0+MAX_FLIGHT_MODES
};
#define GVAR_2ND_COLUMN (12*FW)
void menuModelGVarOne(uint8_t event)
{
GVarData * gvar = &g_model.gvars[s_currIdx];
drawStringWithIndex(PSIZE(TR_GVARS)*FW+FW, 0, STR_GV, s_currIdx+1, 0);
drawGVarValue(32*FW, 0, s_currIdx, getGVarValue(s_currIdx, getFlightMode()));
lcdDrawFilledRect(0, 0, LCD_W, FH, SOLID, FILL_WHITE|GREY_DEFAULT);
SIMPLE_SUBMENU(STR_GVARS, GVAR_FIELD_LAST);
for (int i=0; i<NUM_BODY_LINES; i++) {
coord_t y = MENU_HEADER_HEIGHT + 1 + i * FH;
int k = i + menuVerticalOffset;
LcdFlags attr = (menuVerticalPosition == k ? (s_editMode > 0 ? BLINK | INVERS : INVERS) : 0);
switch (k) {
case GVAR_FIELD_NAME:
editSingleName(GVAR_2ND_COLUMN, y, STR_NAME, gvar->name, LEN_GVAR_NAME, event, attr);
break;
case GVAR_FIELD_UNIT:
gvar->unit = selectMenuItem(GVAR_2ND_COLUMN, y, STR_UNIT, "\001-%", gvar->unit, 0, 1, attr, event);
break;
case GVAR_FIELD_PREC:
gvar->prec = selectMenuItem(GVAR_2ND_COLUMN, y, STR_PRECISION, STR_VPREC, gvar->prec, 0, 1, attr, event);
break;
case GVAR_FIELD_MIN:
lcdDrawText(0, y, STR_MIN);
drawGVarValue(GVAR_2ND_COLUMN, y, s_currIdx, GVAR_MIN+gvar->min, LEFT|attr);
if (attr) gvar->min = checkIncDec(event, GVAR_MIN+gvar->min, GVAR_MIN, GVAR_MAX-gvar->max, EE_MODEL) - GVAR_MIN;
break;
case GVAR_FIELD_MAX:
lcdDrawText(0, y, STR_MAX);
drawGVarValue(GVAR_2ND_COLUMN, y, s_currIdx, GVAR_MAX-gvar->max, LEFT|attr);
if (attr) gvar->max = GVAR_MAX - checkIncDec(event, GVAR_MAX-gvar->max, GVAR_MIN+gvar->min, GVAR_MAX, EE_MODEL);
break;
case GVAR_FIELD_POPUP:
ON_OFF_MENU_ITEM(gvar->popup, GVAR_2ND_COLUMN, y, STR_POPUP, attr, event);
break;
default:
drawStringWithIndex(0, y, STR_FP, k-GVAR_FIELD_FM0);
editGVarValue(GVAR_2ND_COLUMN, y, event, s_currIdx, k-GVAR_FIELD_FM0, LEFT|attr);
break;
}
}
}
void onGVARSMenu(const char *result)
{
int sub = menuVerticalPosition;
if (result == STR_EDIT) {
s_currIdx = sub;
pushMenu(menuModelGVarOne);
}
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_COLUMNS (NAVIGATION_LINE_BY_LINE|(MAX_FLIGHT_MODES-1))
#define GVARS_FM_COLUMN(p) (7*FW - 7 + (p)*20)
void menuModelGVars(uint8_t event)
{
tmr10ms_t tmr10ms = get_tmr10ms();
const char * menuTitle;
bool after2seconds = (tmr10ms - menuEntryTime > 200); /*2 seconds*/
if (after2seconds) {
menuTitle = STR_GVARS;
for (int i=0; i<MAX_GVARS; i++) {
drawStringWithIndex(GVARS_FM_COLUMN(i), 1, STR_FP, i, SMLSIZE|(getFlightMode()==i ? INVERS : 0));
}
}
else {
menuTitle = STR_MENUGLOBALVARS;
}
MENU_FLAGS(menuTitle, menuTabModel, e_GVars, after2seconds ? CHECK_FLAG_NO_SCREEN_INDEX : 0, MAX_GVARS, { GVARS_COLUMNS, GVARS_COLUMNS, GVARS_COLUMNS, GVARS_COLUMNS, GVARS_COLUMNS, GVARS_COLUMNS, GVARS_COLUMNS, GVARS_COLUMNS, GVARS_COLUMNS });
int sub = menuVerticalPosition;
for (int l=0; l<NUM_BODY_LINES; l++) {
int i = l + menuVerticalOffset;
coord_t y = MENU_HEADER_HEIGHT + 1 + l*FH;
drawGVarName(0, y, i, (sub==i && menuHorizontalPosition<0) ? INVERS : 0);
for (int j=0; j<MAX_FLIGHT_MODES; j++) {
FlightModeData * fm = &g_model.flightModeData[j];
gvar_t v = fm->gvars[i];
LcdFlags attr = ((sub == i && menuHorizontalPosition == j) ? (s_editMode > 0 ? BLINK | INVERS : INVERS) : 0);
coord_t x = GVARS_FM_COLUMN(j);
if (v > GVAR_MAX) {
attr |= SMLSIZE;
}
else if (g_model.gvars[i].prec > 0 || abs(v) >= 100) {
attr |= TINSIZE | NO_UNIT;
}
else {
attr |= SMLSIZE | NO_UNIT;
}
editGVarValue(x, y, event, i, j, attr);
}
}
if (menuHorizontalPosition<0 && event==EVT_KEY_LONG(KEY_ENTER)) {
killEvents(event);
POPUP_MENU_ADD_ITEM(STR_EDIT);
POPUP_MENU_ADD_ITEM(STR_CLEAR);
POPUP_MENU_START(onGVARSMenu);
}
}
+97
View File
@@ -0,0 +1,97 @@
/*
* 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 (LCD_W-17*FW-MENUS_SCROLLBAR_WIDTH)
void menuModelHeli(uint8_t event)
{
SIMPLE_MENU(STR_MENUHELISETUP, menuTabModel, e_Heli, ITEM_HELI_MAX);
int sub = menuVerticalPosition;
for (unsigned int i=0; i<NUM_BODY_LINES; i++) {
coord_t y = MENU_HEADER_HEIGHT + 1 + 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:
g_model.swashR.type = selectMenuItem(MODEL_HELI_2ND_COLUMN, y, STR_SWASHTYPE, STR_VSWASHTYPE, g_model.swashR.type, 0, SWASH_TYPE_MAX, attr, event);
break;
case ITEM_HELI_SWASHRING:
lcd_putsLeft(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:
lcd_putsLeft(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(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:
lcd_putsLeft(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(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:
lcd_putsLeft(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(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;
}
}
}
+574
View File
@@ -0,0 +1,574 @@
/*
* 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 EXPO_ONE_2ND_COLUMN (LCD_W-8*FW-90)
#define EXPO_ONE_FM_WIDTH (9*FW)
int16_t expoFn(int16_t 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];
}
void drawFunction(FnFuncP fn, uint8_t offset)
{
lcdDrawVerticalLine(X0-offset, 0/*TODO Y0-WCHART*/, WCHART*2, 0xee);
lcdDrawHorizontalLine(X0-WCHART-offset, Y0, WCHART*2, 0xee);
coord_t prev_yv = (coord_t)-1;
for (int xv=-WCHART; xv<=WCHART; xv++) {
coord_t yv = (LCD_H-1) - (((uint16_t)RESX + fn(xv * (RESX/WCHART))) / 2 * (LCD_H-1) / RESX);
if (prev_yv != (coord_t)-1) {
if (abs((int8_t)yv-prev_yv) <= 1) {
lcdDrawPoint(X0+xv-offset-1, prev_yv, FORCE);
}
else {
uint8_t tmp = (prev_yv < yv ? 0 : 1);
lcdDrawSolidVerticalLine(X0+xv-offset-1, yv+tmp, prev_yv-yv);
}
}
prev_yv = yv;
}
}
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_SCALE,
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
void menuModelExpoOne(uint8_t event)
{
if (event == EVT_KEY_LONG(KEY_MENU)) {
pushMenu(menuChannelsView);
killEvents(event);
}
ExpoData * ed = expoAddress(s_currIdx);
putsMixerSource(PSIZE(TR_MENUINPUTS)*FW+FW, 0, MIXSRC_FIRST_INPUT+ed->chn, 0);
lcdDrawFilledRect(0, 0, LCD_W, FH, SOLID, FILL_WHITE|GREY_DEFAULT);
SUBMENU(STR_MENUINPUTS, EXPO_FIELD_MAX, {0, 0, 0, ed->srcRaw >= MIXSRC_FIRST_TELEM ? (uint8_t)0 : (uint8_t)HIDDEN_ROW, 0, 0, CASE_CURVES(CURVE_ROWS) CASE_FLIGHT_MODES((MAX_FLIGHT_MODES-1) | NAVIGATION_LINE_BY_LINE) 0 /*, ...*/});
SET_SCROLLBAR_X(EXPO_ONE_2ND_COLUMN+10*FW);
int8_t sub = menuVerticalPosition;
coord_t y = MENU_HEADER_HEIGHT + 1;
for (unsigned int k=0; k<NUM_BODY_LINES; k++) {
int i = k + menuVerticalOffset;
for (int j=0; j<=i; ++j) {
if (j<(int)DIM(mstate_tab) && mstate_tab[j] == HIDDEN_ROW) {
++i;
}
}
LcdFlags attr = (sub==i ? (s_editMode>0 ? BLINK|INVERS : INVERS) : 0);
switch(i) {
case EXPO_FIELD_INPUT_NAME:
editSingleName(EXPO_ONE_2ND_COLUMN, y, STR_INPUTNAME, g_model.inputNames[ed->chn], sizeof(g_model.inputNames[ed->chn]), event, attr);
break;
case EXPO_FIELD_NAME:
editSingleName(EXPO_ONE_2ND_COLUMN, y, STR_EXPONAME, ed->name, sizeof(ed->name), event, attr);
break;
case EXPO_FIELD_SOURCE:
lcd_putsLeft(y, NO_INDENT(STR_SOURCE));
putsMixerSource(EXPO_ONE_2ND_COLUMN, y, ed->srcRaw, STREXPANDED|attr);
if (attr) ed->srcRaw = checkIncDec(event, ed->srcRaw, INPUTSRC_FIRST, INPUTSRC_LAST, EE_MODEL|INCDEC_SOURCE|NO_INCDEC_MARKS, isInputSourceAvailable);
break;
case EXPO_FIELD_SCALE:
lcd_putsLeft(y, STR_SCALE);
putsTelemetryChannelValue(EXPO_ONE_2ND_COLUMN, y, (ed->srcRaw - MIXSRC_FIRST_TELEM)/3, convertTelemValue(ed->srcRaw - MIXSRC_FIRST_TELEM + 1, ed->scale), LEFT|attr);
if (attr) ed->scale = checkIncDec(event, ed->scale, 0, maxTelemValue(ed->srcRaw - MIXSRC_FIRST_TELEM + 1), EE_MODEL);
break;
case EXPO_FIELD_WEIGHT:
lcd_putsLeft(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:
lcd_putsLeft(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:
lcd_putsLeft(y, STR_CURVE);
editCurveRef(EXPO_ONE_2ND_COLUMN, y, ed->curve, event, attr);
break;
#endif
#if defined(FLIGHT_MODES)
case EXPO_FIELD_FLIGHT_MODES:
ed->flightModes = editFlightModes(EXPO_ONE_2ND_COLUMN, y, event, ed->flightModes, attr);
break;
#endif
case EXPO_FIELD_SWITCH:
ed->swtch = switchMenuItem(EXPO_ONE_2ND_COLUMN, y, ed->swtch, attr, event);
break;
case EXPO_FIELD_SIDE:
ed->mode = 4 - selectMenuItem(EXPO_ONE_2ND_COLUMN, y, STR_SIDE, 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;
lcd_putsLeft(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;
}
drawFunction(expoFn);
int x512 = getValue(ed->srcRaw);
if (ed->srcRaw >= MIXSRC_FIRST_TELEM) {
putsTelemetryChannelValue(LCD_W-8, 6*FH, (ed->srcRaw - MIXSRC_FIRST_TELEM) / 3, x512, 0);
if (ed->scale > 0) x512 = (x512 * 1024) / convertTelemValue(ed->srcRaw - MIXSRC_FIRST_TELEM + 1, ed->scale);
}
else {
lcdDrawNumber(LCD_W-8, 6*FH, calcRESXto1000(x512), RIGHT | PREC1);
}
x512 = limit(-1024, x512, 1024);
int y512 = expoFn(x512);
y512 = limit(-1024, y512, 1024);
lcdDrawNumber(LCD_W-8-6*FW, 1*FH, calcRESXto1000(y512), RIGHT | PREC1);
x512 = X0+x512/(RESX/WCHART);
y512 = (LCD_H-1) - ((y512+RESX)/2) * (LCD_H-1) / RESX;
lcdDrawSolidVerticalLine(x512, y512-3, 3*2+1);
lcdDrawSolidHorizontalLine(x512-3, y512, 3*2+1);
}
#define _STR_MAX(x) PSTR("/" #x)
#define STR_MAX(x) _STR_MAX(x)
#define EXPO_LINE_WEIGHT_POS 8*FW+8
#define EXPO_LINE_SRC_POS 9*FW+3
#define EXPO_LINE_CURVE_POS 12*FW+11
#define EXPO_LINE_TRIM_POS 19*FW-4
#define EXPO_LINE_SWITCH_POS 20*FW-1
#define EXPO_LINE_SIDE_POS 25*FW
#define EXPO_LINE_FM_POS 12*FW+11
#define EXPO_LINE_SELECT_POS 5*FW+2
#define EXPO_LINE_NAME_POS LCD_W-LEN_EXPOMIX_NAME*FW-MENUS_SCROLLBAR_WIDTH
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, 0);
putsSwitches(EXPO_LINE_SWITCH_POS, y, ed->swtch, 0);
}
void displayExpoLine(coord_t y, ExpoData *ed)
{
putsMixerSource(EXPO_LINE_SRC_POS, y, ed->srcRaw, 0);
if (ed->carryTrim != TRIM_ON) {
lcdDrawChar(EXPO_LINE_TRIM_POS, y, ed->carryTrim > 0 ? '-' : STR_RETA123[-ed->carryTrim]);
}
if (!ed->flightModes || ((ed->curve.value || ed->swtch) && ((get_tmr10ms() / 200) & 1)))
displayExpoInfos(y, ed);
else
displayFlightModes(EXPO_LINE_FM_POS, y, ed->flightModes);
if (ed->name[0]) {
lcdDrawSizedText(EXPO_LINE_NAME_POS, y, ed->name, sizeof(ed->name), ZCHAR);
}
}
void menuModelExposAll(uint8_t event)
{
uint8_t sub = menuVerticalPosition;
if (s_editMode > 0) {
s_editMode = 0;
}
uint8_t chn = expoAddress(s_currIdx)->chn + 1;
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 {
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_KEY_LONG(KEY_LEFT):
case EVT_KEY_LONG(KEY_RIGHT):
if (s_copyMode && !s_copyTgtOfs) {
if (reachExposLimit()) break;
s_currCh = chn;
if (event == EVT_KEY_LONG(KEY_RIGHT)) { s_currIdx++; menuVerticalPosition++; }
insertExpo(s_currIdx);
pushMenu(menuModelExpoOne);
s_copyMode = 0;
killEvents(event);
}
break;
case EVT_KEY_FIRST(KEY_UP):
case EVT_KEY_REPT(KEY_UP):
case EVT_KEY_FIRST(KEY_DOWN):
case EVT_KEY_REPT(KEY_DOWN):
if (s_copyMode) {
uint8_t key = (event & 0x1f);
uint8_t next_ofs = (key==KEY_UP ? 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 (key==KEY_DOWN) s_currIdx++;
else if (sub-menuVerticalOffset >= 6) menuVerticalOffset++;
}
else if (next_ofs==0 && s_copyMode==COPY_MODE) {
// delete the mix
deleteExpo(s_currIdx);
if (key==KEY_UP) s_currIdx--;
}
else {
// only swap the mix with its neighbor
if (!swapExpos(s_currIdx, key==KEY_UP)) break;
storageDirty(EE_MODEL);
}
s_copyTgtOfs = next_ofs;
}
break;
}
lcdDrawNumber(FW*sizeof(TR_MENUINPUTS)+FW+FW/2, 0, getExposCount(), RIGHT);
lcdDrawText(FW*sizeof(TR_MENUINPUTS)+FW+FW/2, 0, STR_MAX(MAX_EXPOS));
// Value
uint8_t index = expoAddress(s_currIdx)->chn;
if (!s_currCh) {
lcdDrawNumber(127, 2, calcRESXto1000(anas[index]), PREC1|TINSIZE|RIGHT);
}
SIMPLE_MENU(STR_MENUINPUTS, menuTabModel, e_InputsAll, s_maxLines);
// Gauge
if (!s_currCh) {
drawGauge(127, 1, 58, 6, anas[index], 1024);
}
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_HEADER_HEIGHT+1+(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(0, y, ch, 0);
}
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))) {
lcdDrawRect(18, y-1, LCD_W-18, 9, DOTTED);
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) {
uint8_t 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 (ed->mode!=3) {
lcdDrawChar(EXPO_LINE_SIDE_POS, y, ed->mode == 2 ? 126 : 127);
}
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) */
lcdDrawRect(EXPO_LINE_SELECT_POS, y-1, LCD_W-EXPO_LINE_SELECT_POS, 9, s_copyMode == COPY_MODE ? SOLID : DOTTED);
}
if (cur == sub) {
/* invert the raw when it's the current one */
lcdDrawFilledRect(EXPO_LINE_SELECT_POS+1, y, LCD_W-EXPO_LINE_SELECT_POS-2, 7);
}
}
}
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))) {
lcdDrawRect(EXPO_LINE_SELECT_POS, y-1, LCD_W-EXPO_LINE_SELECT_POS, 9, DOTTED);
cur++;
}
}
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(0, y, ch, attr);
if (s_copyMode == MOVE_MODE && s_copySrcCh == ch) {
lcdDrawRect(EXPO_LINE_SELECT_POS, y-1, LCD_W-EXPO_LINE_SELECT_POS, 9, DOTTED);
}
}
cur++;
}
}
s_maxLines = cur;
if (sub >= s_maxLines-1) menuVerticalPosition = s_maxLines-1;
}
@@ -0,0 +1,261 @@
/*
* 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 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 (4*FW-3)
#define CSW_2ND_COLUMN (8*FW+2+FW/2)
#define CSW_3RD_COLUMN (14*FW+1+FW/2)
#define CSW_4TH_COLUMN (22*FW)
#define CSW_5TH_COLUMN (26*FW+3)
#define CSW_6TH_COLUMN (31*FW+1)
void putsEdgeDelayParam(coord_t x, coord_t y, LogicalSwitchData *cs, uint8_t lattr, uint8_t rattr)
{
lcdDrawChar(x-4, y, '[');
lcdDrawNumber(x, y, lswTimerValue(cs->v2), LEFT|PREC1|lattr);
lcdDrawChar(lcdLastPos, y, ':');
if (cs->v3 < 0)
lcdDrawText(lcdLastPos+3, y, "<<", rattr);
else if (cs->v3 == 0)
lcdDrawText(lcdLastPos+3, y, "--", rattr);
else
lcdDrawNumber(lcdLastPos+3, y, lswTimerValue(cs->v2+cs->v3), LEFT|PREC1|rattr);
lcdDrawChar(lcdLastPos, y, ']');
}
void onLogicalSwitchesMenu(const char *result)
{
int8_t sub = menuVerticalPosition;
LogicalSwitchData * cs = lswAddress(sub);
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);
}
}
void menuModelLogicalSwitches(uint8_t event)
{
INCDEC_DECLARE_VARS(EE_MODEL);
MENU(STR_MENULOGICALSWITCHES, menuTabModel, e_LogicalSwitches, NUM_LOGICAL_SWITCH, { NAVIGATION_LINE_BY_LINE|LS_FIELD_LAST/*repeated...*/ });
int k = 0;
int sub = menuVerticalPosition;
horzpos_t horz = menuHorizontalPosition;
if (horz>=0) {
drawColumnHeader(STR_LSW_HEADERS, horz);
}
if (horz<0 && event==EVT_KEY_LONG(KEY_ENTER) && !READ_ONLY()) {
killEvents(event);
LogicalSwitchData * cs = lswAddress(sub);
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_HEADER_HEIGHT + 1 + i*FH;
k = i+menuVerticalOffset;
LcdFlags attr = (sub==k ? ((s_editMode>0) ? BLINK|INVERS : INVERS) : 0);
LcdFlags attr1 = (horz==1 ? attr : 0);
LcdFlags attr2 = (horz==2 ? attr : 0);
LogicalSwitchData * cs = lswAddress(k);
// CSW name
unsigned int sw = SWSRC_SW1+k;
putsSwitches(0, y, sw, (getSwitch(sw) ? BOLD : 0) | ((sub==k && CURSOR_ON_LINE()) ? INVERS : 0));
// CSW func
lcdDrawTextAtIndex(CSW_1ST_COLUMN, y, STR_VCSWFUNC, cs->func, horz==0 ? attr : 0);
// CSW params
unsigned int 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, horz==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 (horz == 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 (horz == 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;
if (v1_val <= MIXSRC_LAST_CH) {
putsChannelValue(CSW_3RD_COLUMN, y, v1_val, calc100toRESX(cs->v2), LEFT|attr2);
}
else {
putsChannelValue(CSW_3RD_COLUMN, y, v1_val, cs->v2, LEFT|attr2);
}
}
// CSW AND switch
putsSwitches(CSW_4TH_COLUMN, y, cs->andsw, horz==LS_FIELD_ANDSW ? attr : 0);
// CSW duration
if (cs->duration > 0)
lcdDrawNumber(CSW_5TH_COLUMN, y, cs->duration, (horz==LS_FIELD_DURATION ? attr : 0)|PREC1|LEFT);
else
lcdDrawTextAtIndex(CSW_5TH_COLUMN, y, STR_MMMINV, 0, horz==LS_FIELD_DURATION ? attr : 0);
// CSW delay
if (cstate == LS_FAMILY_EDGE) {
lcdDrawText(CSW_6TH_COLUMN, y, STR_NA);
if (attr && horz == LS_FIELD_DELAY) {
REPEAT_LAST_CURSOR_MOVE();
}
}
else if (cs->delay > 0) {
lcdDrawNumber(CSW_6TH_COLUMN, y, cs->delay, (horz==LS_FIELD_DELAY ? attr : 0)|PREC1|LEFT);
}
else {
lcdDrawTextAtIndex(CSW_6TH_COLUMN, y, STR_MMMINV, 0, horz==LS_FIELD_DELAY ? attr : 0);
}
if (attr && horz == LS_FIELD_V3 && cstate != LS_FAMILY_EDGE) {
REPEAT_LAST_CURSOR_MOVE();
}
if (s_editMode>0 && attr) {
switch (horz) {
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:
if (v1_val >= MIXSRC_FIRST_TIMER) {
INCDEC_SET_FLAG(EE_MODEL | INCDEC_REP10 | NO_INCDEC_MARKS);
}
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;
}
}
}
}
+608
View File
@@ -0,0 +1,608 @@
/*
* 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 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_MODE)
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, uint8_t attr, uint8_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);
}
void drawOffsetBar(uint8_t x, uint8_t y, MixData * md)
{
const int gaugeWidth = 33;
const int gaugeHeight = 6;
int offset = GET_GVAR(MD_OFFSET(md), GV_RANGELARGE_NEG, GV_RANGELARGE, mixerCurrentFlightMode);
int weight = GET_GVAR(MD_WEIGHT(md), GV_RANGELARGE_NEG, GV_RANGELARGE, mixerCurrentFlightMode);
int barMin = offset - weight;
int barMax = offset + weight;
if (y > 15) {
lcdDrawNumber(x-((barMin >= 0) ? 2 : 3), y-6, barMin, TINSIZE|LEFT);
lcdDrawNumber(x+gaugeWidth+1, y-6, barMax, TINSIZE|RIGHT);
}
if (weight < 0) {
barMin = -barMin;
barMax = -barMax;
}
if (barMin < -101)
barMin = -101;
if (barMax > 101)
barMax = 101;
lcdDrawHorizontalLine(x-2, y, gaugeWidth+2, DOTTED);
lcdDrawHorizontalLine(x-2, y+gaugeHeight, gaugeWidth+2, DOTTED);
lcdDrawSolidVerticalLine(x-2, y+1, gaugeHeight-1);
lcdDrawSolidVerticalLine(x+gaugeWidth-1, y+1, gaugeHeight-1);
if (barMin <= barMax) {
int8_t right = (barMax * gaugeWidth) / 200;
int8_t left = ((barMin * gaugeWidth) / 200)-1;
lcdDrawFilledRect(x+gaugeWidth/2+left, y+2, right-left, gaugeHeight-3);
}
lcdDrawSolidVerticalLine(x+gaugeWidth/2-1, y, gaugeHeight+1);
if (barMin == -101) {
for (uint8_t i=0; i<3; ++i) {
lcdDrawPoint(x+i, y+4-i);
lcdDrawPoint(x+3+i, y+4-i);
}
}
if (barMax == 101) {
for (uint8_t i=0; i<3; ++i) {
lcdDrawPoint(x+gaugeWidth-8+i, y+4-i);
lcdDrawPoint(x+gaugeWidth-5+i, y+4-i);
}
}
}
void menuModelMixOne(uint8_t event)
{
if (event == EVT_KEY_LONG(KEY_MENU)) {
pushMenu(menuChannelsView);
killEvents(event);
}
MixData * md2 = mixAddress(s_currIdx) ;
putsChn(PSIZE(TR_MIXER)*FW+FW, 0, md2->destCh+1,0);
lcdDrawFilledRect(0, 0, LCD_W, FH, SOLID, FILL_WHITE|GREY_DEFAULT);
SUBMENU(STR_MIXER, MIX_FIELD_COUNT, {0, 0, 0, 0, 0, CASE_CURVES(1) CASE_FLIGHT_MODES((MAX_FLIGHT_MODES-1) | NAVIGATION_LINE_BY_LINE) 0, 0 /*, ...*/});
#if MENU_COLUMNS > 1
SET_SCROLLBAR_X(0);
#endif
int8_t sub = menuVerticalPosition;
int8_t editMode = s_editMode;
for (int k=0; k<MENU_COLUMNS*(LCD_LINES-1); k++) {
coord_t y;
if (k >= LCD_LINES-1)
y = 1 + (k-LCD_LINES+2)*FH;
else
y = 1 + (k+1)*FH;
int8_t i = k;
#if MENU_COLUMNS < 2
i = i + menuVerticalOffset;
#endif
LcdFlags attr = (sub==i ? (editMode>0 ? BLINK|INVERS : INVERS) : 0);
switch(i) {
case MIX_FIELD_NAME:
editSingleName(MIXES_2ND_COLUMN, y, STR_MIXNAME, md2->name, sizeof(md2->name), event, attr);
break;
case MIX_FIELD_SOURCE:
lcd_putsLeft(y, NO_INDENT(STR_SOURCE));
putsMixerSource(MIXES_2ND_COLUMN, y, md2->srcRaw, STREXPANDED|attr);
if (attr) CHECK_INCDEC_MODELSOURCE(event, md2->srcRaw, 1, MIXSRC_LAST);
break;
case MIX_FIELD_WEIGHT:
lcd_putsLeft(y, STR_WEIGHT);
gvarWeightItem(MIXES_2ND_COLUMN, y, md2, attr|LEFT, event);
break;
case MIX_FIELD_OFFSET:
{
lcd_putsLeft(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);
drawOffsetBar(MIXES_2ND_COLUMN+22, y, md2);
break;
}
case MIX_FIELD_TRIM:
lcd_putsLeft(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:
lcd_putsLeft(y, STR_CURVE);
editCurveRef(MIXES_2ND_COLUMN, y, md2->curve, event, attr);
break;
#endif
#if defined(FLIGHT_MODES)
case MIX_FIELD_FLIGHT_MODE:
md2->flightModes = editFlightModes(MIXES_2ND_COLUMN, y, event, md2->flightModes, attr);
break;
#endif
case MIX_FIELD_SWITCH:
md2->swtch = switchMenuItem(MENU_COLUMN2_X+MIXES_2ND_COLUMN, y, md2->swtch, attr, event);
break;
case MIX_FIELD_WARNING:
lcd_putsColumnLeft(MENU_COLUMN2_X+MIXES_2ND_COLUMN, y, STR_MIXWARNING);
if (md2->mixWarn)
lcdDrawNumber(MENU_COLUMN2_X+MIXES_2ND_COLUMN, y, md2->mixWarn, attr|LEFT);
else
lcdDrawText(MENU_COLUMN2_X+MIXES_2ND_COLUMN, y, STR_OFF, attr);
if (attr) CHECK_INCDEC_MODELVAR_ZERO(event, md2->mixWarn, 3);
break;
case MIX_FIELD_MLTPX:
md2->mltpx = selectMenuItem(MENU_COLUMN2_X+MIXES_2ND_COLUMN, y, STR_MULTPX, STR_VMLTPX, md2->mltpx, 0, 2, attr, event);
break;
case MIX_FIELD_DELAY_UP:
md2->delayUp = EDIT_DELAY(MENU_COLUMN2_X, y, event, attr, STR_DELAYUP, md2->delayUp);
break;
case MIX_FIELD_DELAY_DOWN:
md2->delayDown = EDIT_DELAY(MENU_COLUMN2_X, y, event, attr, STR_DELAYDOWN, md2->delayDown);
break;
case MIX_FIELD_SLOW_UP:
md2->speedUp = EDIT_DELAY(MENU_COLUMN2_X, y, event, attr, STR_SLOWUP, md2->speedUp);
break;
case MIX_FIELD_SLOW_DOWN:
md2->speedDown = EDIT_DELAY(MENU_COLUMN2_X, y, event, attr, STR_SLOWDOWN, md2->speedDown);
break;
}
}
}
#define _STR_MAX(x) PSTR("/" #x)
#define STR_MAX(x) _STR_MAX(x)
#define MIX_LINE_WEIGHT_POS 6*FW+10
#define MIX_LINE_SRC_POS 7*FW+5
#define MIX_LINE_CURVE_POS 13*FW+3
#define MIX_LINE_SWITCH_POS 19*FW+1
#define MIX_LINE_FM_POS 13*FW+3
#define MIX_LINE_DELAY_POS 24*FW+3
#define MIX_LINE_NAME_POS LCD_W-LEN_EXPOMIX_NAME*FW-MENUS_SCROLLBAR_WIDTH
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 displayHeaderChannelName(uint8_t ch)
{
uint8_t len = zlen(g_model.limitData[ch].name, sizeof(g_model.limitData[ch].name));
if (len) {
lcdDrawSizedText(80, 1, g_model.limitData[ch].name, len, ZCHAR|SMLSIZE);
}
}
void displayMixInfos(coord_t y, MixData *md)
{
putsCurveRef(MIX_LINE_CURVE_POS, y, md->curve, 0);
if (md->swtch) {
putsSwitches(MIX_LINE_SWITCH_POS, y, md->swtch);
}
}
void displayMixLine(coord_t y, MixData *md)
{
if (md->name[0])
lcdDrawSizedText(MIX_LINE_NAME_POS, y, md->name, sizeof(md->name), ZCHAR);
if (!md->flightModes || ((md->curve.value || md->swtch) && ((get_tmr10ms() / 200) & 1)))
displayMixInfos(y, md);
else
displayFlightModes(MIX_LINE_FM_POS, y, md->flightModes);
}
void menuModelMixAll(uint8_t event)
{
uint8_t sub = menuVerticalPosition;
if (s_editMode > 0) {
s_editMode = 0;
}
uint8_t chn = mixAddress(s_currIdx)->destCh + 1;
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;
}
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;
}
else {
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_KEY_LONG(KEY_LEFT):
case EVT_KEY_LONG(KEY_RIGHT):
if (s_copyMode && !s_copyTgtOfs) {
if (reachMixesLimit()) break;
s_currCh = chn;
if (event == EVT_KEY_LONG(KEY_RIGHT)) { s_currIdx++; menuVerticalPosition++; }
insertMix(s_currIdx);
pushMenu(menuModelMixOne);
s_copyMode = 0;
killEvents(event);
}
break;
case EVT_KEY_FIRST(KEY_UP):
case EVT_KEY_REPT(KEY_UP):
case EVT_KEY_FIRST(KEY_DOWN):
case EVT_KEY_REPT(KEY_DOWN):
if (s_copyMode) {
uint8_t key = (event & 0x1f);
uint8_t next_ofs = (key==KEY_UP ? 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 (key==KEY_DOWN) s_currIdx++;
else if (sub-menuVerticalOffset >= 6) menuVerticalOffset++;
}
else if (next_ofs==0 && s_copyMode==COPY_MODE) {
// delete the mix
deleteMix(s_currIdx);
if (key==KEY_UP) s_currIdx--;
}
else {
// only swap the mix with its neighbor
if (!swapMixes(s_currIdx, key==KEY_UP)) break;
storageDirty(EE_MODEL);
}
s_copyTgtOfs = next_ofs;
}
break;
}
lcdDrawNumber(FW*sizeof(TR_MIXER)+FW+FW/2, 0, getMixesCount(), RIGHT);
lcdDrawText(FW*sizeof(TR_MIXER)+FW+FW/2, 0, STR_MAX(MAX_MIXERS));
// Value
uint8_t index = mixAddress(s_currIdx)->destCh;
if (!s_currCh) {
displayHeaderChannelName(index);
lcdDrawNumber(127, 2, calcRESXto1000(ex_chans[index]), PREC1|TINSIZE|RIGHT);
}
SIMPLE_MENU(STR_MIXER, menuTabModel, e_MixAll, s_maxLines);
// Gauge
if (!s_currCh) {
drawGauge(127, 1, 58, 6, ex_chans[index], 1024);
}
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_HEADER_HEIGHT+1+(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(0, 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))) {
lcdDrawRect(22, y-1, LCD_W-22, 9, DOTTED);
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) {
uint8_t attr = ((s_copyMode || sub != cur) ? 0 : INVERS);
if (mixCnt > 0) lcdDrawTextAtIndex(FW, y, STR_VMLTPX2, md->mltpx, 0);
putsMixerSource(MIX_LINE_SRC_POS, y, md->srcRaw, 0);
gvarWeightItem(MIX_LINE_WEIGHT_POS, y, md, RIGHT | attr | (isMixActive(i) ? BOLD : 0), 0);
displayMixLine(y, md);
char cs = ' ';
if (md->speedDown || md->speedUp)
cs = 'S';
if (md->delayUp || md->delayDown)
cs = (cs =='S' ? '*' : 'D');
lcdDrawChar(MIX_LINE_DELAY_POS, y, cs);
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) */
lcdDrawRect(22, y-1, LCD_W-22, 9, s_copyMode == COPY_MODE ? SOLID : DOTTED);
}
if (cur == sub) {
/* invert the raw when it's the current one */
lcdDrawFilledRect(23, y, LCD_W-24, 7);
}
}
}
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))) {
lcdDrawRect(22, y-1, LCD_W-22, 9, DOTTED);
cur++;
}
}
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(0, y, ch, attr); // show CHx
if (s_copyMode == MOVE_MODE && s_copySrcCh == ch) {
lcdDrawRect(22, y-1, LCD_W-22, 9, DOTTED);
}
}
cur++;
}
}
s_maxLines = cur;
if (sub >= s_maxLines-1) menuVerticalPosition = s_maxLines-1;
}
+265
View File
@@ -0,0 +1,265 @@
/*
* 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 (int 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 4*FW
#define LIMITS_OFFSET_POS 14*FW+4
#define LIMITS_MIN_POS 20*FW-3
#if defined(PPM_CENTER_ADJUSTABLE)
#define LIMITS_DIRECTION_POS 20*FW-3
#define LIMITS_MAX_POS 24*FW+2
#define LIMITS_REVERT_POS 25*FW-1
#define LIMITS_CURVE_POS 27*FW-1
#define LIMITS_PPM_CENTER_POS 34*FW
#else
#define LIMITS_DIRECTION_POS 21*FW
#define LIMITS_MAX_POS 26*FW
#define LIMITS_REVERT_POS 27*FW
#define LIMITS_CURVE_POS 32*FW-3
#endif
#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)
{
int ch = menuVerticalPosition;
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);
}
}
void menuModelLimits(uint8_t event)
{
int sub = menuVerticalPosition;
if (sub < NUM_CHNOUT) {
#if defined(PPM_CENTER_ADJUSTABLE) || defined(PPM_UNIT_US)
lcdDrawNumber(13*FW, 0, PPM_CH_CENTER(sub)+channelOutputs[sub]/2, RIGHT);
lcdDrawText(13*FW, 0, STR_US);
#else
lcdDrawNumber(13*FW, 0, calcRESXto1000(channelOutputs[sub]), PREC1|RIGHT);
#endif
}
MENU(STR_MENULIMITS, 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 });
if (sub<NUM_CHNOUT && menuHorizontalPosition>=0) {
drawColumnHeader(STR_LIMITS_HEADERS, 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_HEADER_HEIGHT + 1 + 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;
lcdDrawText(CENTER_OFS, 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;
}
LimitData *ld = limitAddress(k);
int16_t v = (ld->revert) ? -LIMIT_OFS(ld) : LIMIT_OFS(ld);
char swVal = '-'; // '-', '<', '>'
if ((channelOutputs[k] - v) > 50) swVal = (ld->revert ? 127 : 126); // Switch to raw inputs? - remove trim!
if ((channelOutputs[k] - v) < -50) swVal = (ld->revert ? 126 : 127);
lcdDrawChar(LIMITS_DIRECTION_POS, y, swVal);
int limit = (g_model.extendedLimits ? LIMIT_EXT_MAX : 1000);
putsChn(0, 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 (int j=0; j<ITEM_LIMITS_COUNT; j++) {
LcdFlags 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, 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);
}
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;
#if defined(PPM_CENTER_ADJUSTABLE)
lcdDrawChar(LIMITS_REVERT_POS, y, revert ? 127 : 126, attr);
#else
lcdDrawTextAtIndex(LIMITS_REVERT_POS, y, STR_MMMINV, revert, attr);
#endif
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:
lcdDrawChar(LCD_W-FW-MENUS_SCROLLBAR_WIDTH, y, ld->symetrical ? '=' : '\306', attr);
if (active) {
CHECK_INCDEC_MODELVAR_ZERO(event, ld->symetrical, 1);
}
break;
#endif
}
}
}
}
+274
View File
@@ -0,0 +1,274 @@
/*
* 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 MODELSIZE_POS_X 170
#define MODELSEL_W 133
void onModelSelectMenu(const char *result)
{
int8_t sub = menuVerticalPosition;
if (result == STR_SELECT_MODEL || result == STR_CREATE_MODEL) {
selectModel(sub);
}
else if (result == STR_COPY_MODEL) {
s_copyMode = COPY_MODE;
s_copyTgtOfs = 0;
s_copySrcRow = -1;
}
else if (result == STR_MOVE_MODEL) {
s_copyMode = MOVE_MODE;
s_copyTgtOfs = 0;
s_copySrcRow = -1;
}
else if (result == STR_BACKUP_MODEL) {
storageCheck(true); // force writing of current model data before this is changed
POPUP_WARNING(eeBackupModel(sub));
}
else if (result == STR_RESTORE_MODEL || result == STR_UPDATE_LIST) {
if (!sdListFiles(MODELS_PATH, MODELS_EXT, MENU_LINE_LENGTH-1, NULL)) {
POPUP_WARNING(STR_NO_MODELS_ON_SD);
}
}
else if (result == STR_DELETE_MODEL) {
POPUP_CONFIRMATION(STR_DELETEMODEL);
SET_WARNING_INFO(modelHeaders[sub].name, sizeof(g_model.header.name), ZCHAR);
}
else {
// The user choosed a file on SD to restore
storageCheck(true);
POPUP_WARNING(eeRestoreModel(sub, (char *)result));
if (!warningText && g_eeGeneral.currModel == sub) {
eeLoadModel(sub);
}
}
}
void menuModelSelect(uint8_t event)
{
if (warningResult) {
warningResult = 0;
storageCheck(true);
eeDeleteModel(menuVerticalPosition); // delete file
s_copyMode = 0;
event = EVT_ENTRY_UP;
}
uint8_t _event_ = ((event==EVT_KEY_BREAK(KEY_ENTER) || event==EVT_KEY_LONG(KEY_ENTER)) ? 0 : event);
if ((s_copyMode && EVT_KEY_MASK(event) == KEY_EXIT) || event == EVT_KEY_BREAK(KEY_EXIT)) {
_event_ -= KEY_EXIT;
}
int8_t oldSub = menuVerticalPosition;
check_submenu_simple(NULL, _event_, MAX_MODELS);
if (s_editMode > 0) s_editMode = 0;
int sub = menuVerticalPosition;
switch (event)
{
case EVT_ENTRY:
menuVerticalPosition = sub = g_eeGeneral.currModel;
if (sub >= NUM_BODY_LINES) menuVerticalOffset = sub-(NUM_BODY_LINES-1);
s_copyMode = 0;
s_editMode = EDIT_MODE_INIT;
break;
case EVT_KEY_LONG(KEY_EXIT):
if (s_copyMode && s_copyTgtOfs == 0 && g_eeGeneral.currModel != sub && eeModelExists(sub)) {
POPUP_CONFIRMATION(STR_DELETEMODEL);
SET_WARNING_INFO(modelHeaders[sub].name, sizeof(g_model.header.name), ZCHAR);
killEvents(event);
break;
}
// no break
case EVT_KEY_BREAK(KEY_EXIT):
if (s_copyMode) {
sub = menuVerticalPosition = (s_copyMode == MOVE_MODE || s_copySrcRow<0) ? (MAX_MODELS+sub+s_copyTgtOfs) % MAX_MODELS : s_copySrcRow;
s_copyMode = 0;
}
else {
if (menuVerticalPosition != g_eeGeneral.currModel) {
sub = menuVerticalPosition = g_eeGeneral.currModel;
menuVerticalOffset = 0;
}
else if (event != EVT_KEY_LONG(KEY_EXIT)) {
popMenu();
}
}
break;
case EVT_KEY_LONG(KEY_ENTER):
case EVT_KEY_BREAK(KEY_ENTER):
s_editMode = 0;
if (READ_ONLY()) {
if (g_eeGeneral.currModel != sub && eeModelExists(sub)) {
selectModel(sub);
}
}
else if (s_copyMode && (s_copyTgtOfs || s_copySrcRow>=0)) {
displayPopup(s_copyMode==COPY_MODE ? STR_COPYINGMODEL : STR_MOVINGMODEL);
storageCheck(true); // force writing of current model data before this is changed
uint8_t cur = (MAX_MODELS + sub + s_copyTgtOfs) % MAX_MODELS;
if (s_copyMode == COPY_MODE) {
if (!eeCopyModel(cur, s_copySrcRow))
cur = sub;
}
s_copySrcRow = g_eeGeneral.currModel; // to update the currModel value
while (sub != cur) {
uint8_t src = cur;
cur = (s_copyTgtOfs > 0 ? cur+MAX_MODELS-1 : cur+1) % MAX_MODELS;
eeSwapModels(src, cur);
if (src == s_copySrcRow)
s_copySrcRow = cur;
else if (cur == s_copySrcRow)
s_copySrcRow = src;
}
if (s_copySrcRow != g_eeGeneral.currModel) {
g_eeGeneral.currModel = s_copySrcRow;
storageDirty(EE_GENERAL);
}
s_copyMode = 0;
event = EVT_ENTRY_UP;
}
else if (event == EVT_KEY_LONG(KEY_ENTER)) {
s_copyMode = 0;
killEvents(event);
if (g_eeGeneral.currModel != sub) {
if (eeModelExists(sub)) {
POPUP_MENU_ADD_ITEM(STR_SELECT_MODEL);
POPUP_MENU_ADD_SD_ITEM(STR_BACKUP_MODEL);
POPUP_MENU_ADD_ITEM(STR_COPY_MODEL);
POPUP_MENU_ADD_ITEM(STR_MOVE_MODEL);
POPUP_MENU_ADD_ITEM(STR_DELETE_MODEL);
}
else {
POPUP_MENU_ADD_ITEM(STR_CREATE_MODEL);
POPUP_MENU_ADD_ITEM(STR_RESTORE_MODEL);
}
}
else {
POPUP_MENU_ADD_SD_ITEM(STR_BACKUP_MODEL);
POPUP_MENU_ADD_ITEM(STR_COPY_MODEL);
POPUP_MENU_ADD_ITEM(STR_MOVE_MODEL);
}
POPUP_MENU_START(onModelSelectMenu);
}
else if (eeModelExists(sub)) {
s_copyMode = (s_copyMode == COPY_MODE ? MOVE_MODE : COPY_MODE);
s_copyTgtOfs = 0;
s_copySrcRow = -1;
}
break;
case EVT_KEY_BREAK(KEY_PAGE):
case EVT_KEY_LONG(KEY_PAGE):
chainMenu(event == EVT_KEY_BREAK(KEY_PAGE) ? menuModelSetup : menuTabModel[DIM(menuTabModel)-1]);
killEvents(event);
break;
case EVT_KEY_FIRST(KEY_UP):
case EVT_KEY_REPT(KEY_UP):
case EVT_KEY_FIRST(KEY_DOWN):
case EVT_KEY_REPT(KEY_DOWN):
if (s_copyMode) {
int8_t next_ofs = s_copyTgtOfs + oldSub - menuVerticalPosition;
if (next_ofs == MAX_MODELS || next_ofs == -MAX_MODELS)
next_ofs = 0;
if (s_copySrcRow < 0 && s_copyMode==COPY_MODE) {
s_copySrcRow = oldSub;
// find a hole (in the first empty slot above / below)
sub = eeFindEmptyModel(s_copySrcRow, event==EVT_KEY_FIRST(KEY_DOWN) || event==EVT_KEY_REPT(KEY_DOWN));
if (sub < 0) {
// no free room for duplicating the model
AUDIO_ERROR();
sub = oldSub;
s_copyMode = 0;
}
next_ofs = 0;
menuVerticalPosition = sub;
}
s_copyTgtOfs = next_ofs;
}
break;
}
lcdDrawNumber(19*FW, 0, EeFsGetFree(), RIGHT);
lcdDrawText(19*FW + 3, 0, STR_BYTES);
lcdDrawText(lcdLastPos + 3, 0, STR_FREE);
drawScreenIndex(e_ModelSelect, DIM(menuTabModel), 0);
lcdDrawFilledRect(0, 0, LCD_W, FH, SOLID, FILL_WHITE|GREY_DEFAULT);
TITLE(STR_MENUMODELSEL);
for (uint8_t i=0; i<NUM_BODY_LINES; i++) {
coord_t y = MENU_HEADER_HEIGHT + 1 + i*FH;
uint8_t k = i+menuVerticalOffset;
lcdDrawNumber(3*FW+2, y, k+1, RIGHT|LEADING0|((!s_copyMode && sub==k) ? INVERS : 0), 2);
if (s_copyMode == MOVE_MODE || (s_copyMode == COPY_MODE && s_copySrcRow >= 0)) {
if (k == sub) {
if (s_copyMode == COPY_MODE) {
k = s_copySrcRow;
lcdDrawChar(MODELSEL_W-FW, y, '+');
}
else {
k = sub + s_copyTgtOfs;
}
}
else if (s_copyTgtOfs < 0 && ((k < sub && k >= sub+s_copyTgtOfs) || (k-MAX_MODELS < sub && k-MAX_MODELS >= sub+s_copyTgtOfs)))
k += 1;
else if (s_copyTgtOfs > 0 && ((k > sub && k <= sub+s_copyTgtOfs) || (k+MAX_MODELS > sub && k+MAX_MODELS <= sub+s_copyTgtOfs)))
k += MAX_MODELS-1;
}
k %= MAX_MODELS;
if (eeModelExists(k)) {
putsModelName(4*FW, y, modelHeaders[k].name, k, 0);
lcdDrawNumber(20*FW, y, eeModelSize(k), RIGHT);
if (k==g_eeGeneral.currModel && (s_copyMode!=COPY_MODE || s_copySrcRow<0 || i+menuVerticalOffset!=(vertpos_t)sub))
lcdDrawChar(1, y, '*');
}
if (s_copyMode && (vertpos_t)sub==i+menuVerticalOffset) {
lcdDrawFilledRect(9, y, MODELSEL_W-1-9, 7);
lcdDrawRect(8, y-1, MODELSEL_W-1-7, 9, s_copyMode == COPY_MODE ? SOLID : DOTTED);
}
}
if (event == EVT_ENTRY || sub != oldSub) {
loadModelBitmap(modelHeaders[sub].bitmap, modelBitmap);
}
lcdDrawBitmap(22*FW+2, 2*FH+FH/2, modelBitmap);
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,428 @@
/*
* 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 (4*FW+2)
#define MODEL_SPECIAL_FUNC_2ND_COLUMN (8*FW+2)
#define MODEL_SPECIAL_FUNC_3RD_COLUMN (21*FW)
#define MODEL_SPECIAL_FUNC_4TH_COLUMN (33*FW-3)
#define MODEL_SPECIAL_FUNC_4TH_COLUMN_ONOFF (34*FW-3)
void onCustomFunctionsFileSelectionMenu(const char * result)
{
int sub = menuVerticalPosition;
CustomFunctionData * cfn;
uint8_t eeFlags;
if (menuHandlers[menuLevel] == menuModelCustomFunctions) {
cfn = &g_model.customFn[sub];
eeFlags = EE_MODEL;
}
else {
cfn = &g_eeGeneral.customFn[sub];
eeFlags = EE_GENERAL;
}
uint8_t func = CFN_FUNC(cfn);
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(cfn->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(cfn->play.name, result, sizeof(cfn->play.name));
storageDirty(eeFlags);
if (func == FUNC_PLAY_SCRIPT) {
LUA_LOAD_MODEL_SCRIPTS();
}
}
}
void onCustomFunctionsMenu(const char * result)
{
int sub = menuVerticalPosition;
CustomFunctionData * cfn;
uint8_t eeFlags;
if (menuHandlers[menuLevel] == menuModelCustomFunctions) {
cfn = &g_model.customFn[sub];
eeFlags = EE_MODEL;
}
else {
cfn = &g_eeGeneral.customFn[sub];
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-sub-1)*sizeof(CustomFunctionData));
memset(cfn, 0, sizeof(CustomFunctionData));
storageDirty(eeFlags);
}
else if (result == STR_DELETE) {
memmove(cfn, cfn+1, (NUM_CFN-sub-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 CustomFunctionsItems {
ITEM_CUSTOM_FUNCTIONS_SWITCH,
ITEM_CUSTOM_FUNCTIONS_FUNCTION,
ITEM_CUSTOM_FUNCTIONS_PARAM1,
ITEM_CUSTOM_FUNCTIONS_PARAM2,
ITEM_CUSTOM_FUNCTIONS_REPEAT,
ITEM_CUSTOM_FUNCTIONS_COUNT,
ITEM_CUSTOM_FUNCTIONS_LAST = ITEM_CUSTOM_FUNCTIONS_COUNT-1
};
void menuCustomFunctions(uint8_t event, CustomFunctionData * functions, CustomFunctionsContext * functionsContext)
{
int sub = menuVerticalPosition;
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[sub];
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=sub+1; i<NUM_CFN; i++) {
if (!CFN_EMPTY(&functions[i])) {
POPUP_MENU_ADD_ITEM(STR_DELETE);
break;
}
}
POPUP_MENU_START(onCustomFunctionsMenu);
}
for (int i=0; i<NUM_BODY_LINES; i++) {
coord_t y = MENU_HEADER_HEIGHT + 1 + i*FH;
int k = i+menuVerticalOffset;
drawStringWithIndex(0, y, functions == g_model.customFn ? STR_SF : STR_GF, k+1, (sub==k && menuHorizontalPosition<0) ? INVERS : 0);
CustomFunctionData *cfn = &functions[k];
uint8_t func = CFN_FUNC(cfn);
for (uint8_t j=0; j<5; j++) {
uint8_t attr = ((sub==k && menuHorizontalPosition==j) ? ((s_editMode>0) ? BLINK|INVERS : INVERS) : 0);
uint8_t active = (attr && s_editMode>0);
switch (j) {
case ITEM_CUSTOM_FUNCTIONS_SWITCH:
putsSwitches(MODEL_SPECIAL_FUNC_1ST_COLUMN, y, CFN_SWITCH(cfn), attr | ((functionsContext->activeSwitches & ((MASK_CFN_TYPE)1 << k)) ? BOLD : 0));
if (active || AUTOSWITCH_ENTER_LONG()) CHECK_INCDEC_SWITCH(event, CFN_SWITCH(cfn), SWSRC_FIRST, SWSRC_LAST, eeFlags, isSwitchAvailableInCustomFunctions);
if (func == FUNC_OVERRIDE_CHANNEL && functions != g_model.customFn) {
func = CFN_FUNC(cfn) = func+1;
}
break;
case ITEM_CUSTOM_FUNCTIONS_FUNCTION:
if (CFN_SWITCH(cfn)) {
lcdDrawTextAtIndex(MODEL_SPECIAL_FUNC_2ND_COLUMN, y, STR_VFSWFUNC, func, attr);
if (active) {
func = CFN_FUNC(cfn) = checkIncDec(event, CFN_FUNC(cfn), 0, FUNC_MAX-1, eeFlags, isAssignableFunctionAvailable);
if (checkIncDec_Ret) CFN_RESET(cfn);
}
}
else {
j = ITEM_CUSTOM_FUNCTIONS_LAST; // skip other fields
if (sub==k && menuHorizontalPosition > 0) {
REPEAT_LAST_CURSOR_MOVE();
}
}
break;
case ITEM_CUSTOM_FUNCTIONS_PARAM1:
{
int8_t maxParam = NUM_CHNOUT-1;
#if defined(OVERRIDE_CHANNEL_FUNCTION)
if (func == FUNC_OVERRIDE_CHANNEL) {
putsChn(lcdNextPos, y, CFN_CH_INDEX(cfn)+1, attr);
}
else
#endif
if (func == FUNC_TRAINER) {
maxParam = NUM_STICKS;
putsMixerSource(lcdNextPos, 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(lcdNextPos, 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 = TIMERS-1;
drawStringWithIndex(lcdNextPos, 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();
}
if (active) CHECK_INCDEC_MODELVAR_ZERO(event, CFN_CH_INDEX(cfn), maxParam);
break;
}
case ITEM_CUSTOM_FUNCTIONS_PARAM2:
{
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_FIRST_TELEM+lastUsedTelemetryIndex();
int param = CFN_PARAM(cfn);
if (param < FUNC_RESET_PARAM_FIRST_TELEM) {
lcdDrawTextAtIndex(MODEL_SPECIAL_FUNC_3RD_COLUMN, y, STR_VFSWRESET, param, attr);
}
else {
TelemetrySensor * sensor = & g_model.telemetrySensors[param-FUNC_RESET_PARAM_FIRST_TELEM];
lcdDrawSizedText(MODEL_SPECIAL_FUNC_3RD_COLUMN, y, sensor->label, TELEM_LABEL_LEN, attr|ZCHAR);
}
if (active) INCDEC_ENABLE_CHECK(functionsContext == &globalFunctionsContext ? isSourceAvailableInGlobalResetSpecialFunction : isSourceAvailableInResetSpecialFunction);
}
#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
else if (func >= FUNC_SET_FAILSAFE && func <= FUNC_BIND) {
val_max = NUM_MODULES-1;
lcdDrawTextAtIndex(MODEL_SPECIAL_FUNC_3RD_COLUMN, y, "\004Int.Ext.", CFN_PARAM(cfn), attr);
}
else if (func == FUNC_SET_TIMER) {
val_max = 539*60+59; //8:59:59
putsTimer(MODEL_SPECIAL_FUNC_3RD_COLUMN, y, val_displayed, attr|LEFT|TIMEHOUR, attr);
}
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 (active && event==EVT_KEY_BREAK(KEY_ENTER)) {
s_editMode = 0;
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(functionsContext == &globalFunctionsContext ? 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);
lcdDrawChar(lcdLastPos, y, '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);
if (active) 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_MIN; 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);
drawGVarValue(lcdNextPos, y, CFN_GVAR_INDEX(cfn), abs(val_displayed), attr|LEFT);
}
}
#endif
else if (attr) {
REPEAT_LAST_CURSOR_MOVE();
}
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_CUSTOM_FUNCTIONS_REPEAT:
if (HAS_ENABLE_PARAM(func)) {
drawCheckBox(MODEL_SPECIAL_FUNC_4TH_COLUMN_ONOFF, y, CFN_ACTIVE(cfn), attr);
if (active) CFN_ACTIVE(cfn) = checkIncDec(event, CFN_ACTIVE(cfn), 0, 1, eeFlags);
}
else if (HAS_REPEAT_PARAM(func)) {
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) {
lcdDrawChar(MODEL_SPECIAL_FUNC_4TH_COLUMN-1, y, '!', attr);
lcdDrawText(MODEL_SPECIAL_FUNC_4TH_COLUMN+2, y, "1x", attr);
}
else {
lcdDrawNumber(MODEL_SPECIAL_FUNC_4TH_COLUMN+2+FW, y, CFN_PLAY_REPEAT(cfn)*CFN_PLAY_REPEAT_MUL, attr|RIGHT);
lcdDrawChar(MODEL_SPECIAL_FUNC_4TH_COLUMN+2+FW, y, 's', attr);
}
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);
}
else if (attr) {
REPEAT_LAST_CURSOR_MOVE();
}
break;
}
}
}
}
void menuModelCustomFunctions(uint8_t event)
{
MENU(STR_MENUCUSTOMFUNC, menuTabModel, e_CustomFunctions, NUM_CFN, { NAVIGATION_LINE_BY_LINE|4/*repeated*/ });
return menuCustomFunctions(event, g_model.customFn, &modelFunctionsContext);
}
+580
View File
@@ -0,0 +1,580 @@
/*
* 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 (1*FW)
#define TELEM_COL2 (16*FW)
#define TELEM_COL3 (28*FW+2)
#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 (12*FW)
#define SENSOR_3RD_COLUMN (18*FW)
#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)
void menuModelSensor(uint8_t event)
{
TelemetrySensor * sensor = &g_model.telemetrySensors[s_currIdx];
drawStringWithIndex(PSIZE(TR_MENUSENSOR)*FW+FW, 0, STR_SENSOR, s_currIdx+1);
putsTelemetryChannelValue(25*FW, 0, s_currIdx, getValue(MIXSRC_FIRST_TELEM+3*s_currIdx));
lcdDrawFilledRect(0, 0, LCD_W, FH, SOLID, FILL_WHITE|GREY_DEFAULT);
SUBMENU(STR_MENUSENSOR, 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, SENSOR_AUTOOFFSET_ROWS, SENSOR_ONLYPOS_ROWS, SENSOR_FILTER_ROWS, SENSOR_PERSISTENT_ROWS, 0 });
for (int i=0; i<NUM_BODY_LINES; i++) {
coord_t y = MENU_HEADER_HEIGHT + 1 + 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;
}
}
}
LcdFlags attr = (menuVerticalPosition==k ? (s_editMode>0 ? BLINK|INVERS : INVERS) : 0);
switch (k) {
case SENSOR_FIELD_NAME:
editSingleName(SENSOR_2ND_COLUMN, y, STR_NAME, sensor->label, TELEM_LABEL_LEN, event, attr);
break;
case SENSOR_FIELD_TYPE:
sensor->type = selectMenuItem(SENSOR_2ND_COLUMN, y, NO_INDENT(STR_TYPE), 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) {
lcd_putsLeft(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 {
sensor->formula = selectMenuItem(SENSOR_2ND_COLUMN, y, STR_FORMULA, 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:
lcd_putsLeft(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:
sensor->prec = selectMenuItem(SENSOR_2ND_COLUMN, y, STR_PRECISION, 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) {
lcd_putsLeft(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) {
lcd_putsLeft(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) {
lcd_putsLeft(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, isSensorAvailable);
}
break;
}
else if (sensor->formula == TELEM_FORMULA_TOTALIZE) {
lcd_putsLeft(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, isSensorAvailable);
}
break;
}
}
else {
if (sensor->unit == UNIT_RPMS) {
lcd_putsLeft(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 {
lcd_putsLeft(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)
lcdDrawChar(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) {
sensor->cell.index = selectMenuItem(SENSOR_2ND_COLUMN, y, STR_CELLINDEX, STR_VCELLINDEX, sensor->cell.index, 0, 8, attr, event);
break;
}
else if (sensor->formula == TELEM_FORMULA_DIST) {
lcd_putsLeft(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) {
lcd_putsLeft(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 {
lcd_putsLeft(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(0, 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) {
lcdDrawChar(SENSOR_2ND_COLUMN, y, '-', attr);
putsMixerSource(lcdNextPos, 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:
ON_OFF_MENU_ITEM(sensor->autoOffset, SENSOR_2ND_COLUMN, y, STR_AUTOOFFSET, attr, event);
break;
case SENSOR_FIELD_ONLYPOSITIVE:
ON_OFF_MENU_ITEM(sensor->onlyPositive, SENSOR_2ND_COLUMN, y, STR_ONLYPOSITIVE, attr, event);
break;
case SENSOR_FIELD_FILTER:
ON_OFF_MENU_ITEM(sensor->filter, SENSOR_2ND_COLUMN, y, STR_FILTER, attr, event);
break;
case SENSOR_FIELD_PERSISTENT:
ON_OFF_MENU_ITEM(sensor->persistent, SENSOR_2ND_COLUMN, y, NO_INDENT(STR_PERSISTENT), attr, event);
if (checkIncDec_Ret && !sensor->persistent) {
sensor->persistentValue = 0;
}
break;
case SENSOR_FIELD_LOGS:
ON_OFF_MENU_ITEM(sensor->logs, SENSOR_2ND_COLUMN, y, STR_LOGS, attr, event);
if (attr && checkIncDec_Ret) {
closeLogs();
}
break;
}
}
}
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);
}
}
}
}
void menuModelTelemetry(uint8_t event)
{
if (warningResult) {
warningResult = 0;
for (int i=0; i<MAX_SENSORS; i++) {
delTelemetryIndex(i);
}
}
MENU(STR_MENUTELEMETRY, 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_HEADER_HEIGHT + 1 + 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(INDENT_WIDTH, y, index+1, LEFT|attr);
lcdDrawChar(lcdLastPos, y, ':', attr);
lcdDrawSizedText(3*FW, y, g_model.telemetrySensors[index].label, TELEM_LABEL_LEN, ZCHAR);
if (telemetryItems[index].isFresh()) {
lcdDrawChar(10*FW, y, '*');
}
TelemetryItem & telemetryItem = telemetryItems[index];
if (telemetryItem.isAvailable()) {
bool isOld = telemetryItem.isOld();
lcdNextPos = TELEM_COL2;
if (isOld) lcdDrawChar(lcdNextPos, y, '[');
putsTelemetryChannelValue(lcdNextPos, y, index, getValue(MIXSRC_FIRST_TELEM+3*index), LEFT);
if (isOld) lcdDrawChar(lcdLastPos, y, ']');
}
else {
lcdDrawText(TELEM_COL2, y, "---", 0); // 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_COL3, y, sensor->id, LEFT);
} else
#endif
if (sensor->type == TELEM_TYPE_CUSTOM && !g_model.ignoreSensorIds) {
lcdDrawNumber(TELEM_COL3, 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:
g_model.telemetryProtocol = selectMenuItem(TELEM_COL2, y, STR_TELEMETRY_TYPE, "\017FrSky S.PORT\0 FrSky D\0 FrSky D (cable)", g_model.telemetryProtocol, PROTOCOL_TELEMETRY_FIRST, g_eeGeneral.serial2Mode==UART_MODE_TELEMETRY ? PROTOCOL_FRSKY_D_SECONDARY : PROTOCOL_FRSKY_D, attr, event);
break;
case ITEM_TELEMETRY_SENSORS_LABEL:
lcd_putsLeft(y, STR_TELEMETRY_SENSORS);
lcdDrawText(TELEM_COL2, y, STR_VALUE, 0);
if (!g_model.ignoreSensorIds && !IS_SPEKTRUM_PROTOCOL()) {
lcdDrawText(TELEM_COL3, y, STR_ID, 0);
}
break;
case ITEM_TELEMETRY_DISCOVER_SENSORS:
lcdDrawText(0, y, allowNewSensors ? STR_STOP_DISCOVER_SENSORS : STR_DISCOVER_SENSORS, attr);
if (attr && event==EVT_KEY_BREAK(KEY_ENTER)) {
s_editMode = 0;
allowNewSensors = !allowNewSensors;
}
break;
case ITEM_TELEMETRY_NEW_SENSOR:
lcdDrawText(0, y, 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(0, y, 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:
ON_OFF_MENU_ITEM(g_model.ignoreSensorIds, TELEM_COL2, y, STR_IGNORE_INSTANCE, attr, event);
break;
case ITEM_TELEMETRY_RSSI_LABEL:
lcd_putsLeft(y, PSTR("RSSI"));
break;
case ITEM_TELEMETRY_RSSI_ALARM1:
case ITEM_TELEMETRY_RSSI_ALARM2: {
uint8_t alarm = k-ITEM_TELEMETRY_RSSI_ALARM1;
lcd_putsLeft(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:
lcd_putsLeft(y, STR_VARIO);
break;
case ITEM_TELEMETRY_VARIO_SOURCE:
lcd_putsLeft(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:
lcd_putsLeft(y, STR_RANGE);
lcdDrawNumber(TELEM_COL2, y, -10+g_model.frsky.varioMin, (menuHorizontalPosition==0 ? attr : 0)|LEFT);
lcdDrawNumber(TELEM_COL2+7*FW, 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:
lcd_putsLeft(y, STR_CENTER);
lcdDrawNumber(TELEM_COL2, y, -5+g_model.frsky.varioCenterMin, (menuHorizontalPosition==0 ? attr : 0)|PREC1|LEFT);
lcdDrawNumber(TELEM_COL2+7*FW, y, 5+g_model.frsky.varioCenterMax, (menuHorizontalPosition==1 ? attr : 0)|PREC1|LEFT);
lcdDrawTextAtIndex(TELEM_COL3, 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
}
}
}
+600
View File
@@ -0,0 +1,600 @@
/*
* 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"
vertpos_t menuVerticalOffset;
vertpos_t menuVerticalPosition;
horzpos_t menuHorizontalPosition;
int8_t s_editMode;
uint8_t noHighlightCounter;
uint8_t calibrationState;
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;
}
#define DBLKEYS_PRESSED_RGT_LFT(in) ((in & ((1<<KEY_PLUS) + (1<<KEY_MINUS))) == ((1<<KEY_PLUS) + (1<<KEY_MINUS)))
#define DBLKEYS_PRESSED_UP_DWN(in) ((in & ((1<<KEY_MENU) + (1<<KEY_PAGE))) == ((1<<KEY_MENU) + (1<<KEY_PAGE)))
#define DBLKEYS_PRESSED_RGT_UP(in) ((in & ((1<<KEY_ENTER) + (1<<KEY_MINUS))) == ((1<<KEY_ENTER) + (1<<KEY_MINUS)))
#define DBLKEYS_PRESSED_LFT_DWN(in) ((in & ((1<<KEY_PAGE) + (1<<KEY_EXIT))) == ((1<<KEY_PAGE) + (1<<KEY_EXIT)))
int checkIncDec(unsigned int event, int val, int i_min, int i_max, unsigned int i_flags, IsValueAvailable isValueAvailable, const CheckIncDecStops &stops)
{
int newval = val;
#if defined(DBLKEYS)
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_PAGE);
killEvents(KEY_MENU);
killEvents(KEY_ENTER);
killEvents(KEY_EXIT);
event = 0;
}
}
#endif
if (s_editMode>0 && (event==EVT_KEY_FIRST(KEY_PLUS) || event==EVT_KEY_REPT(KEY_PLUS))) {
do {
if (IS_KEY_REPT(event) && (i_flags & INCDEC_REP10)) {
newval += min(10, i_max-val);
}
else {
newval++;
}
} while (isValueAvailable && !isValueAvailable(newval) && newval<=i_max);
if (newval > i_max) {
newval = val;
killEvents(event);
AUDIO_KEY_ERROR();
}
}
else if (s_editMode>0 && (event==EVT_KEY_FIRST(KEY_MINUS) || event==EVT_KEY_REPT(KEY_MINUS))) {
do {
if (IS_KEY_REPT(event) && (i_flags & INCDEC_REP10)) {
newval -= min(10, val-i_min);
}
else {
newval--;
}
} while (isValueAvailable && !isValueAvailable(newval) && newval>=i_min);
if (newval < i_min) {
newval = val;
killEvents(event);
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) {
if (!(i_flags & NO_INCDEC_MARKS) && (newval != i_max) && (newval != i_min) && stops.contains(newval)) {
bool pause = (newval > val ? !stops.contains(newval+1) : !stops.contains(newval-1));
if (pause) {
pauseEvents(event); // delay before auto-repeat continues
}
}
storageDirty(i_flags & (EE_GENERAL|EE_MODEL));
checkIncDec_Ret = (newval > val ? 1 : -1);
if (!IS_KEY_REPT(event)) {
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 MAXCOL_RAW(row) (horTab ? pgm_read_byte(horTab+min(row, (vertpos_t)horTabMax)) : (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 {val=min;}
#define DEC(val, min, max) if (val>min) {val--;} else {val=max;}
coord_t scrollbar_X = DEFAULT_SCROLLBAR_X;
void onLongMenuPress(const char *result)
{
if (result == STR_VIEW_CHANNELS) {
pushMenu(menuChannelsView);
}
else if (result == STR_VIEW_NOTES) {
pushModelNotes();
}
}
tmr10ms_t menuEntryTime;
void check(const char * name, check_event_t event, uint8_t curr, const MenuHandlerFunc *menuTab, uint8_t menuTabSize, const pm_uint8_t *horTab, uint8_t horTabMax, vertpos_t rowcount, uint8_t flags)
{
vertpos_t l_posVert = menuVerticalPosition;
horzpos_t l_posHorz = menuHorizontalPosition;
uint8_t maxcol = MAXCOL(l_posVert);
if (menuTab) {
int cc = curr;
switch (event) {
case EVT_KEY_LONG(KEY_MENU):
if (menuTab == menuTabModel) {
killEvents(event);
if (modelHasNotes()) {
POPUP_MENU_ADD_SD_ITEM(STR_VIEW_CHANNELS);
POPUP_MENU_ADD_ITEM(STR_VIEW_NOTES);
POPUP_MENU_START(onLongMenuPress);
}
else {
pushMenu(menuChannelsView);
}
}
break;
case EVT_KEY_LONG(KEY_PAGE):
if (curr > 0)
cc = curr - 1;
else
cc = menuTabSize-1;
killEvents(event);
break;
case EVT_KEY_BREAK(KEY_PAGE):
if (curr < (menuTabSize-1))
cc = curr + 1;
else
cc = 0;
break;
}
if (!calibrationState && cc != curr) {
chainMenu((MenuHandlerFunc)pgm_read_adr(&menuTab[cc]));
}
if (!(flags&CHECK_FLAG_NO_SCREEN_INDEX)) {
drawScreenIndex(curr, menuTabSize, 0);
}
lcdDrawFilledRect(0, 0, LCD_W, MENU_HEADER_HEIGHT, SOLID, FILL_WHITE|GREY_DEFAULT);
}
DISPLAY_PROGRESS_BAR(menuTab ? lcdLastPos-2*FW-((curr+1)/10*FWNUM)-2 : 20*FW+1);
switch(event)
{
case EVT_ENTRY:
menuEntryTime = get_tmr10ms();
s_editMode = EDIT_MODE_INIT;
l_posVert = MENU_FIRST_LINE_EDIT;
l_posHorz = POS_HORZ_INIT(l_posVert);
SET_SCROLLBAR_X(LCD_W-1);
break;
case EVT_ENTRY_UP:
menuEntryTime = get_tmr10ms();
s_editMode = 0;
l_posHorz = POS_HORZ_INIT(l_posVert);
SET_SCROLLBAR_X(LCD_W-1);
break;
case EVT_ROTARY_BREAK:
if (s_editMode > 1) break;
if (menuHorizontalPosition < 0 && maxcol > 0 && READ_ONLY_UNLOCKED()) {
l_posHorz = 0;
AUDIO_KEY_PRESS();
}
else if (READ_ONLY_UNLOCKED()) {
s_editMode = (s_editMode<=0);
AUDIO_KEY_PRESS();
}
break;
case EVT_KEY_LONG(KEY_EXIT):
s_editMode = 0; // TODO needed? we call ENTRY_UP after which does the same
popMenu();
break;
case EVT_KEY_BREAK(KEY_EXIT):
if (s_editMode > 0) {
s_editMode = 0;
AUDIO_KEY_PRESS();
break;
}
if (l_posHorz >= 0 && (COLATTR(l_posVert) & NAVIGATION_LINE_BY_LINE)) {
l_posHorz = -1;
AUDIO_KEY_PRESS();
}
else {
uint8_t posVertInit = MENU_FIRST_LINE_EDIT;
if (menuVerticalOffset != 0 || l_posVert != posVertInit) {
menuVerticalOffset = 0;
l_posVert = posVertInit;
l_posHorz = POS_HORZ_INIT(l_posVert);
AUDIO_KEY_PRESS();
}
else {
popMenu();
}
}
break;
case EVT_KEY_FIRST(KEY_RIGHT):
AUDIO_KEY_PRESS();
// no break
case EVT_KEY_REPT(KEY_RIGHT):
if (s_editMode != 0) break;
if ((COLATTR(l_posVert) & NAVIGATION_LINE_BY_LINE)) {
if (l_posHorz >= 0) {
INC(l_posHorz, 0, maxcol);
break;
}
}
else {
if (l_posHorz < maxcol) {
l_posHorz++;
break;
}
else {
l_posHorz = 0;
}
}
do {
INC(l_posVert, MENU_FIRST_LINE_EDIT, rowcount-1);
} while (CURSOR_NOT_ALLOWED_IN_ROW(l_posVert));
s_editMode = 0; // if we go down, we must be in this mode
l_posHorz = POS_HORZ_INIT(l_posVert);
break;
case EVT_KEY_FIRST(KEY_LEFT):
AUDIO_KEY_PRESS();
// no break
case EVT_KEY_REPT(KEY_LEFT):
if (s_editMode != 0) break;
if ((COLATTR(l_posVert) & NAVIGATION_LINE_BY_LINE)) {
if (l_posHorz >= 0) {
DEC(l_posHorz, 0, maxcol);
break;
}
}
else if (l_posHorz > 0) {
l_posHorz--;
break;
}
else {
l_posHorz = 0xff;
}
do {
DEC(l_posVert, MENU_FIRST_LINE_EDIT, rowcount-1);
} while (CURSOR_NOT_ALLOWED_IN_ROW(l_posVert));
s_editMode = 0; // if we go up, we must be in this mode
if ((COLATTR(l_posVert) & NAVIGATION_LINE_BY_LINE))
l_posHorz = -1;
else
l_posHorz = min((uint8_t)l_posHorz, MAXCOL(l_posVert));
break;
}
int linesCount = rowcount;
if (l_posVert == 0 || (l_posVert==1 && MAXCOL(vertpos_t(0)) >= HIDDEN_ROW) || (l_posVert==2 && MAXCOL(vertpos_t(0)) >= HIDDEN_ROW && MAXCOL(vertpos_t(1)) >= HIDDEN_ROW)) {
menuVerticalOffset = 0;
if (horTab) {
linesCount = 0;
for (int i=0; i<rowcount; i++) {
if (i>horTabMax || horTab[i] != HIDDEN_ROW) {
linesCount++;
}
}
}
}
else if (horTab) {
if (rowcount > NUM_BODY_LINES) {
while (1) {
vertpos_t firstLine = 0;
for (int numLines=0; firstLine<rowcount && numLines<menuVerticalOffset; firstLine++) {
if (firstLine>=horTabMax || horTab[firstLine] != HIDDEN_ROW) {
numLines++;
}
}
if (l_posVert < firstLine) {
menuVerticalOffset--;
}
else {
vertpos_t lastLine = firstLine;
for (int numLines=0; lastLine<rowcount && numLines<NUM_BODY_LINES; lastLine++) {
if (lastLine>=horTabMax || horTab[lastLine] != HIDDEN_ROW) {
numLines++;
}
}
if (l_posVert >= lastLine) {
menuVerticalOffset++;
}
else {
linesCount = menuVerticalOffset + NUM_BODY_LINES;
for (int i=lastLine; i<rowcount; i++) {
if (i>horTabMax || horTab[i] != HIDDEN_ROW) {
linesCount++;
}
}
break;
}
}
}
}
}
else {
if (l_posVert>=NUM_BODY_LINES+menuVerticalOffset) {
menuVerticalOffset = l_posVert-NUM_BODY_LINES+1;
}
else if (l_posVert<menuVerticalOffset) {
menuVerticalOffset = l_posVert;
}
}
if (scrollbar_X && linesCount > NUM_BODY_LINES) {
drawVerticalScrollbar(scrollbar_X, MENU_HEADER_HEIGHT, LCD_H-MENU_HEADER_HEIGHT, menuVerticalOffset, linesCount, NUM_BODY_LINES);
}
if (name) {
title(name);
}
menuVerticalPosition = l_posVert;
menuHorizontalPosition = l_posHorz;
}
void check_simple(const char * name, check_event_t event, uint8_t curr, const MenuHandlerFunc *menuTab, uint8_t menuTabSize, vertpos_t rowcount)
{
check(name, event, curr, menuTab, menuTabSize, 0, 0, rowcount);
}
void check_submenu_simple(const char * name, check_event_t event, uint8_t rowcount)
{
check_simple(name, event, 0, 0, 0, rowcount);
}
void repeatLastCursorMove(uint8_t event)
{
if (CURSOR_MOVED_LEFT(event) || CURSOR_MOVED_RIGHT(event)) {
putEvent(event);
}
else {
menuHorizontalPosition = 0;
}
}
+187
View File
@@ -0,0 +1,187 @@
/*
* 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)(uint8_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 displayBox(const char * title)
{
lcdDrawFilledRect(10, 16, LCD_W-20, 40, SOLID, ERASE);
lcdDrawRect(10, 16, LCD_W-20, 40);
lcdDrawSizedText(WARNING_LINE_X, WARNING_LINE_Y, title, WARNING_LINE_LEN);
// could be a place for a warningInfoText
}
void displayPopup(const char * title)
{
displayBox(title);
lcdRefresh();
}
const pm_uchar asterisk_lbm[] PROGMEM = {
#include "asterisk.lbm"
};
void drawAlertBox(const char * title, const char * text, const char * action)
{
lcdClear();
lcdDrawBitmap(0, 0, asterisk_lbm);
#define MESSAGE_LCD_OFFSET 60
#if defined(TRANSLATIONS_FR) || defined(TRANSLATIONS_IT) || defined(TRANSLATIONS_CZ)
lcdDrawText(MESSAGE_LCD_OFFSET, 0, STR_WARNING, DBLSIZE);
lcdDrawText(MESSAGE_LCD_OFFSET, 2*FH, title, DBLSIZE);
#else
lcdDrawText(MESSAGE_LCD_OFFSET, 0, title, DBLSIZE);
lcdDrawText(MESSAGE_LCD_OFFSET, 2*FH, STR_WARNING, DBLSIZE);
#endif
lcdDrawFilledRect(MESSAGE_LCD_OFFSET, 0, LCD_W-MESSAGE_LCD_OFFSET, 32);
if (text) {
lcdDrawText(MESSAGE_LCD_OFFSET, 5*FH, text);
}
if (action) {
lcdDrawText(MESSAGE_LCD_OFFSET, 7*FH, action);
}
#undef MESSAGE_LCD_OFFSET
}
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 displayWarning(uint8_t event)
{
warningResult = false;
displayBox(warningText);
if (warningInfoText) {
lcdDrawSizedText(WARNING_LINE_X, WARNING_LINE_Y+FH, warningInfoText, warningInfoLength, WARNING_INFO_FLAGS);
}
lcdDrawText(WARNING_LINE_X, WARNING_LINE_Y+2*FH, warningType == WARNING_TYPE_ASTERISK ? STR_EXIT : STR_POPUPS);
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(uint8_t event)
{
const char * result = NULL;
uint8_t display_count = min<unsigned int>(popupMenuNoItems, MENU_MAX_DISPLAY_LINES);
uint8_t y = (display_count >= 5 ? MENU_Y - FH - 1 : MENU_Y);
lcdDrawFilledRect(MENU_X, y, MENU_W, display_count * (FH+1) + 2, SOLID, ERASE);
lcdDrawRect(MENU_X, y, MENU_W, display_count * (FH+1) + 2);
for (uint8_t i=0; i<display_count; i++) {
lcdDrawText(MENU_X+6, i*(FH+1) + y + 2, popupMenuItems[i+(popupMenuOffsetType == MENU_OFFSET_INTERNAL ? popupMenuOffset : 0)], 0);
if (i == s_menu_item) lcdDrawFilledRect(MENU_X+1, i*(FH+1) + y + 1, MENU_W-2, 9);
}
if (popupMenuNoItems > display_count) {
drawVerticalScrollbar(MENU_X+MENU_W-1, y+1, MENU_MAX_DISPLAY_LINES * (FH+1), popupMenuOffset, popupMenuNoItems, display_count);
}
switch (event) {
case EVT_KEY_FIRST(KEY_UP):
case EVT_KEY_REPT(KEY_UP):
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_KEY_FIRST(KEY_DOWN):
case EVT_KEY_REPT(KEY_DOWN):
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;
}
return result;
}
+220
View File
@@ -0,0 +1,220 @@
/*
* 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 BAR_SPACING 12
#define BAR_HEIGHT 22
enum CalibrationState {
CALIB_START = 0,
CALIB_SET_MIDPOINT,
CALIB_MOVE_STICKS,
CALIB_STORE,
CALIB_FINISHED
};
void drawPotsBars()
{
// Optimization by Mike Blandford
uint8_t x, i, len ; // declare temporary variables
for (x=LCD_W/2-(NUM_POTS/2)*BAR_SPACING+BAR_SPACING/2, i=NUM_STICKS; i<NUM_STICKS+NUM_POTS; x+=BAR_SPACING, i++) {
if (IS_POT_AVAILABLE(i)) {
len = ((calibratedStick[i]+RESX)*BAR_HEIGHT/(RESX*2))+1l; // calculate once per loop
V_BAR(x, LCD_H-8, len);
putsStickName(x-2, LCD_H-6, i, TINSIZE);
}
}
}
void menuCommonCalib(uint8_t event)
{
for (uint8_t i=0; i<NUM_STICKS+NUM_POTS; i++) { // get low and high vals for sticks and trims
int16_t vt = anaIn(i);
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;
}
}
}
}
}
calibrationState = reusableBuffer.calib.state; // make sure we don't scroll while calibrating
switch (event) {
case EVT_ENTRY:
case EVT_KEY_BREAK(KEY_EXIT):
reusableBuffer.calib.state = CALIB_START;
break;
case EVT_KEY_BREAK(KEY_ENTER):
reusableBuffer.calib.state++;
break;
}
switch (reusableBuffer.calib.state) {
case CALIB_START:
// START CALIBRATION
if (!READ_ONLY()) {
lcd_putsLeft(MENU_HEADER_HEIGHT+2*FH, STR_MENUTOSTART);
}
break;
case CALIB_SET_MIDPOINT:
// SET MIDPOINT
lcdDrawText(0*FW, MENU_HEADER_HEIGHT+FH, STR_SETMIDPOINT, INVERS);
lcd_putsLeft(MENU_HEADER_HEIGHT+2*FH, STR_MENUWHENDONE);
for (uint8_t i=0; i<NUM_STICKS+NUM_POTS; i++) {
reusableBuffer.calib.loVals[i] = 15000;
reusableBuffer.calib.hiVals[i] = -15000;
reusableBuffer.calib.midVals[i] = getAnalogValue(i) >> 1;
if (i<NUM_XPOTS) {
reusableBuffer.calib.xpotsCalib[i].stepsCount = 0;
reusableBuffer.calib.xpotsCalib[i].lastCount = 0;
}
}
break;
case CALIB_MOVE_STICKS:
// MOVE STICKS/POTS
STICK_SCROLL_DISABLE();
lcdDrawText(0*FW, MENU_HEADER_HEIGHT+FH, STR_MOVESTICKSPOTS, INVERS);
lcd_putsLeft(MENU_HEADER_HEIGHT+2*FH, STR_MENUWHENDONE);
for (uint8_t i=0; i<NUM_STICKS+NUM_POTS; 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;
}
}
break;
case CALIB_STORE:
for (uint8_t 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));
}
}
}
g_eeGeneral.chkSum = evalChkSum();
storageDirty(EE_GENERAL);
reusableBuffer.calib.state = CALIB_FINISHED;
break;
default:
reusableBuffer.calib.state = CALIB_START;
break;
}
doMainScreenGraphics();
drawPotsBars();
#if 0
for (int i=POT1; i<=POT_LAST; i++) {
uint8_t steps = 0;
if (reusableBuffer.calib.state == CALIB_MOVE_STICKS) {
steps = reusableBuffer.calib.xpotsCalib[i-POT1].stepsCount;
}
else if (IS_POT_MULTIPOS(i)) {
StepsCalibData * calib = (StepsCalibData *) &g_eeGeneral.calib[i];
steps = calib->count + 1;
}
if (steps > 0 && steps <= XPOTS_MULTIPOS_COUNT) {
lcdDrawNumber(LCD_W/2-2+(i-POT1)*5, LCD_H-6, steps, TINSIZE|RIGHT);
}
}
#endif
}
void menuGeneralCalib(uint8_t event)
{
check_simple(STR_MENUCALIBRATION, event, e_Calib, menuTabGeneral, DIM(menuTabGeneral), 0);
menuCommonCalib(READ_ONLY() ? 0 : event);
if (menuEvent) {
calibrationState = CALIB_START;
}
}
void menuFirstCalib(uint8_t event)
{
if (event == EVT_KEY_BREAK(KEY_EXIT) || reusableBuffer.calib.state == CALIB_FINISHED) {
calibrationState = CALIB_START;
chainMenu(menuMainView);
}
else {
lcd_putsCenter(0*FH, MENUCALIBRATION);
lcdInvertLine(0);
menuCommonCalib(event);
}
}
+47
View File
@@ -0,0 +1,47 @@
/*
* 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 menuGeneralDiagAna(uint8_t event)
{
SIMPLE_MENU(STR_MENUANA, menuTabGeneral, e_Ana, 0);
STICK_SCROLL_DISABLE();
for (int i=0; i<NUM_STICKS+NUM_POTS; i++) {
coord_t y = MENU_HEADER_HEIGHT + 1 + (i/2)*FH;
uint8_t x = i&1 ? LCD_W/2 + FW : 0;
lcdDrawNumber(x, y, i+1, LEADING0|LEFT, 2);
lcdDrawChar(x+2*FW-2, y, ':');
lcdDrawHexNumber(x+3*FW-1, y, anaIn(i));
#if defined(JITTER_MEASURE)
lcdDrawNumber(x+10*FW-1, y, rawJitter[i].get(), RIGHT);
lcdDrawNumber(x+13*FW-1, y, avgJitter[i].get(), RIGHT);
lcdDrawNumber(x+17*FW-1, y, (int16_t)calibratedStick[CONVERT_MODE(i)]*25/256, RIGHT);
#else
lcdDrawNumber(x+10*FW-1, y, (int16_t)calibratedStick[CONVERT_MODE(i)]*25/256, RIGHT);
#endif
}
// SWR
lcd_putsLeft(MENU_HEADER_HEIGHT+6*FH, "RAS");
lcdDrawNumber(10*FW-1, MENU_HEADER_HEIGHT+6*FH, telemetryData.swr.value, RIGHT);
}
+50
View File
@@ -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"
void displayKeyState(uint8_t x, uint8_t y, EnumKeys key)
{
uint8_t t = switchState(key);
lcdDrawChar(x, y, t+'0', t ? INVERS : 0);
}
void menuGeneralDiagKeys(uint8_t event)
{
SIMPLE_MENU(STR_MENUDIAG, menuTabGeneral, e_Keys, 1);
lcdDrawText(14*FW, MENU_HEADER_HEIGHT+2*FH, STR_VTRIM);
for(uint8_t i=0; i<9; i++) {
coord_t y;
if (i<8) {
y = MENU_HEADER_HEIGHT + FH*3 + FH*(i/2);
if (i&1) lcd_img(14*FW, y, sticks, i/2, 0);
displayKeyState(i&1? 20*FW : 18*FW, y, (EnumKeys)(TRM_BASE+i));
}
if (i<6) {
y = i*FH+MENU_HEADER_HEIGHT+FH;
lcdDrawTextAtIndex(0, y, STR_VKEYS, (5-i), 0);
displayKeyState(5*FW+2, y, (EnumKeys)(KEY_MENU+(5-i)));
}
}
}
+223
View File
@@ -0,0 +1,223 @@
/*
* 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_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,
CASE_REVPLUS(ITEM_SETUP_HW_POT3)
CASE_REV9E(ITEM_SETUP_HW_POT4)
ITEM_SETUP_HW_LS,
ITEM_SETUP_HW_RS,
CASE_REV9E(ITEM_SETUP_HW_LS2)
CASE_REV9E(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,
CASE_REV9E(ITEM_SETUP_HW_SI)
CASE_REV9E(ITEM_SETUP_HW_SJ)
CASE_REV9E(ITEM_SETUP_HW_SK)
CASE_REV9E(ITEM_SETUP_HW_SL)
CASE_REV9E(ITEM_SETUP_HW_SM)
CASE_REV9E(ITEM_SETUP_HW_SN)
CASE_REV9E(ITEM_SETUP_HW_SO)
CASE_REV9E(ITEM_SETUP_HW_SP)
CASE_REV9E(ITEM_SETUP_HW_SQ)
CASE_REV9E(ITEM_SETUP_HW_SR)
CASE_REV9E(ITEM_SETUP_HW_BLUETOOTH)
ITEM_SETUP_HW_UART3_MODE,
ITEM_SETUP_HW_JITTER_FILTER,
ITEM_SETUP_HW_MAX
};
#define HW_SETTINGS_COLUMN 15*FW
#if defined(REV9E)
#define POTS_ROWS NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1, 0, 0, NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1
#elif defined(REVPLUS)
#define POTS_ROWS NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1, 0, 0
#else
#define POTS_ROWS NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1, 0, 0
#endif
#if defined(REV9E)
#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, 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, NAVIGATION_LINE_BY_LINE|1, NAVIGATION_LINE_BY_LINE|1
#define BLUETOOTH_ROWS 1,
#else
#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
#endif
#define SWITCH_TYPE_MAX(sw) ((MIXSRC_SF-MIXSRC_FIRST_SWITCH == sw || MIXSRC_SH-MIXSRC_FIRST_SWITCH == sw) ? SWITCH_2POS : SWITCH_3POS)
void menuGeneralHardware(uint8_t event)
{
MENU(STR_HARDWARE, menuTabGeneral, e_Hardware, ITEM_SETUP_HW_MAX, { 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_HEADER_HEIGHT + 1 + 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_LABEL_STICKS:
lcd_putsLeft(y, STR_STICKS);
break;
case ITEM_SETUP_HW_STICK1:
case ITEM_SETUP_HW_STICK2:
case ITEM_SETUP_HW_STICK3:
case ITEM_SETUP_HW_STICK4:
case ITEM_SETUP_HW_LS:
case ITEM_SETUP_HW_RS:
{
int idx = (k<=ITEM_SETUP_HW_STICK4 ? k-ITEM_SETUP_HW_STICK1 : k-ITEM_SETUP_HW_LS+MIXSRC_SLIDER1-MIXSRC_Rud);
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;
}
#if defined(REV9E)
case ITEM_SETUP_HW_LS2:
case ITEM_SETUP_HW_RS2:
{
int idx = k - ITEM_SETUP_HW_LS2;
uint8_t mask = (0x01 << idx);
lcdDrawTextAtIndex(INDENT_WIDTH, y, STR_VSRCRAW, NUM_STICKS+NUM_XPOTS+2+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+2+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+5*FW, y, "", STR_SLIDERTYPES, potType, SLIDER_NONE, SLIDER_WITH_DETENT, menuHorizontalPosition == 1 ? attr : 0, event);
g_eeGeneral.slidersConfig &= ~mask;
g_eeGeneral.slidersConfig |= (potType << idx);
break;
}
#endif
case ITEM_SETUP_HW_LABEL_POTS:
lcd_putsLeft(y, STR_POTS);
break;
case ITEM_SETUP_HW_POT1:
case ITEM_SETUP_HW_POT2:
#if defined(REVPLUS)
case ITEM_SETUP_HW_POT3:
#endif
#if defined(REV9E)
case ITEM_SETUP_HW_POT4:
#endif
{
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+5*FW, 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:
lcd_putsLeft(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:
#if defined(REV9E)
case ITEM_SETUP_HW_SI:
case ITEM_SETUP_HW_SJ:
case ITEM_SETUP_HW_SK:
case ITEM_SETUP_HW_SL:
case ITEM_SETUP_HW_SM:
case ITEM_SETUP_HW_SN:
case ITEM_SETUP_HW_SO:
case ITEM_SETUP_HW_SP:
case ITEM_SETUP_HW_SQ:
case ITEM_SETUP_HW_SR:
#endif
{
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+5*FW, 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;
}
#if defined(REV9E)
case ITEM_SETUP_HW_BLUETOOTH:
lcd_putsLeft(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+5*FW, y, g_eeGeneral.bluetoothName, LEN_BLUETOOTH_NAME, event, menuHorizontalPosition == 1 ? attr : 0);
break;
#endif
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;
case ITEM_SETUP_HW_JITTER_FILTER:
{
uint8_t b = 1-g_eeGeneral.jitterFilter;
g_eeGeneral.jitterFilter = 1 - editCheckBox(b, HW_SETTINGS_COLUMN, y, STR_JITTER_FILTER, attr, event);
break;
}
}
}
}
+467
View File
@@ -0,0 +1,467 @@
/*
* 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 REFRESH_FILES() do { reusableBuffer.sdmanager.offset = 65535; menuVerticalPosition = 0; } 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)))
void menuGeneralSdManagerInfo(evt_t event)
{
SIMPLE_SUBMENU(STR_SD_INFO_TITLE, 1);
lcd_putsLeft(2*FH, STR_SD_TYPE);
lcdDrawText(10*FW, 2*FH, SD_IS_HC() ? STR_SDHC_CARD : STR_SD_CARD);
lcd_putsLeft(3*FH, STR_SD_SIZE);
lcdDrawNumber(10*FW, 3*FH, sdGetSize(), LEFT);
lcdDrawChar(lcdLastPos, 3*FH, 'M');
lcd_putsLeft(4*FH, STR_SD_SECTORS);
#if defined(SD_GET_FREE_BLOCKNR)
lcdDrawNumber(10*FW, 4*FH, SD_GET_FREE_BLOCKNR()/1000, LEFT);
lcdDrawChar(lcdLastPos, 4*FH, '/');
lcdDrawNumber(lcdLastPos+FW, 4*FH, sdGetNoSectors()/1000, LEFT);
#else
lcdDrawNumber(10*FW, 4*FH, sdGetNoSectors()/1000, LEFT);
#endif
lcdDrawChar(lcdLastPos, 4*FH, 'k');
lcd_putsLeft(5*FH, STR_SD_SPEED);
lcdDrawNumber(10*FW, 5*FH, SD_GET_SPEED()/1000, LEFT);
lcdDrawText(lcdLastPos, 5*FH, "kb/s");
}
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);
}
bool isBootloader(const char * filename)
{
FIL file;
f_open(&file, filename, FA_READ);
uint8_t buffer[1024];
UINT count;
if (f_read(&file, buffer, 1024, &count) != FR_OK || count != 1024) {
return false;
}
return isBootloaderStart((uint32_t *)buffer);
}
void flashBootloader(const char * filename)
{
FIL file;
f_open(&file, filename, FA_READ);
uint8_t buffer[1024];
UINT count;
lcdClear();
drawProgressBar(STR_WRITING);
static uint8_t unlocked = 0;
if (!unlocked) {
unlocked = 1;
unlockFlash();
}
for (int i=0; i<BOOTLOADER_SIZE; i+=1024) {
watchdogSetTimeout(100/*1s*/);
if (f_read(&file, buffer, 1024, &count) != FR_OK || count != 1024) {
POPUP_WARNING(STR_SDCARD_ERROR);
break;
}
if (i==0 && !isBootloaderStart((uint32_t *)buffer)) {
POPUP_WARNING(STR_INCOMPATIBLE);
break;
}
for (int j=0; j<1024; j+=FLASH_PAGESIZE) {
writeFlash(CONVERT_UINT_PTR(FIRMWARE_ADDRESS+i+j), (uint32_t *)(buffer+j));
}
updateProgressBar(i, BOOTLOADER_SIZE);
SIMU_SLEEP(30/*ms*/);
}
if (unlocked) {
lockFlash();
unlocked = 0;
}
f_close(&file);
}
void flashSportDevice(ModuleIndex module, const char *filename)
{
pausePulses();
watchdogSetTimeout(60*60*100/*1h*/);
lcdClear();
drawProgressBar(STR_WRITING);
sportFirmwareUpdate(module, filename);
watchdogSetTimeout(100/*1s*/);
resumePulses();
}
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);
strncpy(statusLineMsg, line, 13);
strcpy_P(statusLineMsg+min((uint8_t)strlen(statusLineMsg), (uint8_t)13), STR_REMOVED);
showStatusLine();
REFRESH_FILES();
}
else if (result == STR_PLAY_FILE) {
getSelectionFullPath(lfn);
audioQueue.stopAll();
audioQueue.playFile(lfn, 0, ID_PLAY_FROM_SD_MANAGER);
}
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);
}
else if (result == STR_VIEW_TEXT) {
getSelectionFullPath(lfn);
pushMenuTextView(lfn);
}
else if (result == STR_FLASH_BOOTLOADER) {
getSelectionFullPath(lfn);
flashBootloader(lfn);
}
else if (result == STR_FLASH_INTERNAL_MODULE) {
getSelectionFullPath(lfn);
flashSportDevice(INTERNAL_MODULE, lfn);
}
else if (result == STR_FLASH_EXTERNAL_DEVICE) {
getSelectionFullPath(lfn);
flashSportDevice(EXTERNAL_MODULE, lfn);
}
#if defined(LUA)
else if (result == STR_EXECUTE_FILE) {
getSelectionFullPath(lfn);
luaExec(lfn);
}
#endif
}
void 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);
}
}
int lastPos = menuVerticalPosition;
evt_t event = (EVT_KEY_MASK(_event) == KEY_ENTER ? 0 : _event);
SIMPLE_MENU(SD_IS_HC() ? STR_SDHC_CARD : STR_SD_CARD, menuTabGeneral, e_Sd, reusableBuffer.sdmanager.count);
int index = menuVerticalPosition-menuVerticalOffset;
switch(_event) {
case EVT_ENTRY:
f_chdir(ROOT_PATH);
REFRESH_FILES();
lastPos = -1;
break;
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;
case EVT_KEY_BREAK(KEY_EXIT):
REFRESH_FILES();
break;
case EVT_KEY_BREAK(KEY_ENTER):
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;
}
}
// 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, BITMAPS_EXT)) {
if (!READ_ONLY() && (ext-line) <= (int)sizeof(g_model.header.bitmap)) {
POPUP_MENU_ADD_ITEM(STR_ASSIGN_BITMAP);
}
}
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
else if (!READ_ONLY() && !strcasecmp(ext, FIRMWARE_EXT)) {
TCHAR lfn[_MAX_LFN + 1];
getSelectionFullPath(lfn);
if (isBootloader(lfn)) {
POPUP_MENU_ADD_ITEM(STR_FLASH_BOOTLOADER);
}
}
else if (!READ_ONLY() && !strcasecmp(ext, SPORT_FIRMWARE_EXT)) {
POPUP_MENU_ADD_ITEM(STR_FLASH_EXTERNAL_DEVICE);
POPUP_MENU_ADD_ITEM(STR_FLASH_INTERNAL_MODULE);
}
}
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_HEADER_HEIGHT + 1 + i*FH;
lcdNextPos = 0;
LcdFlags attr = (index == i ? INVERS : 0);
if (reusableBuffer.sdmanager.lines[i][0]) {
if (IS_DIRECTORY(reusableBuffer.sdmanager.lines[i])) { lcdDrawChar(0, y, '[', s_editMode == EDIT_MODIFY_STRING ? 0 : attr); }
if (s_editMode == EDIT_MODIFY_STRING && attr) {
editName(lcdNextPos, y, reusableBuffer.sdmanager.lines[i], SD_SCREEN_FILE_LENGTH-4, _event, attr, 0);
if (s_editMode == 0) {
unsigned int len = effectiveLen(reusableBuffer.sdmanager.lines[i], SD_SCREEN_FILE_LENGTH-LEN_FILE_EXTENSION);
char * ext = getFileExtension(reusableBuffer.sdmanager.originalName, sizeof(reusableBuffer.sdmanager.originalName));
if (ext) {
strAppend(&reusableBuffer.sdmanager.lines[i][len], ext);
}
else {
reusableBuffer.sdmanager.lines[i][len] = 0;
}
f_rename(reusableBuffer.sdmanager.originalName, reusableBuffer.sdmanager.lines[i]);
REFRESH_FILES();
}
}
else {
lcdDrawText(lcdNextPos, y, reusableBuffer.sdmanager.lines[i], attr);
}
if (IS_DIRECTORY(reusableBuffer.sdmanager.lines[i])) { lcdDrawChar(lcdNextPos, y, ']', s_editMode == EDIT_MODIFY_STRING ? 0 : attr); }
}
}
char * ext = getFileExtension(reusableBuffer.sdmanager.lines[index], SD_SCREEN_FILE_LENGTH+1);
if (ext && !strcasecmp(ext, BITMAPS_EXT)) {
if (lastPos != menuVerticalPosition) {
if (!lcdLoadBitmap(modelBitmap, reusableBuffer.sdmanager.lines[index], MODEL_BITMAP_WIDTH, MODEL_BITMAP_HEIGHT)) {
memcpy(modelBitmap, logo_taranis, MODEL_BITMAP_SIZE);
}
}
lcdDrawBitmap(22*FW+2, 2*FH+FH/2, modelBitmap);
}
}
+505
View File
@@ -0,0 +1,505 @@
/*
* 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"
const pm_uchar sticks[] PROGMEM = {
#include "sticks.lbm"
};
#define RADIO_SETUP_2ND_COLUMN (LCD_W-10*FW-MENUS_SCROLLBAR_WIDTH)
#define RADIO_SETUP_DATE_COLUMN RADIO_SETUP_2ND_COLUMN + 4*FWNUM
#define RADIO_SETUP_TIME_COLUMN RADIO_SETUP_2ND_COLUMN + 2*FWNUM
#define SLIDER_5POS(y, value, label, event, attr) { \
int8_t tmp = value; \
drawSlider(RADIO_SETUP_2ND_COLUMN, y, 2+tmp, 4, attr); \
value = selectMenuItem(RADIO_SETUP_2ND_COLUMN, y, label, NULL, tmp, -2, +2, attr, event); \
}
#if defined(SPLASH) && !defined(FSPLASH)
#define CASE_SPLASH_PARAM(x) x,
#else
#define CASE_SPLASH_PARAM(x)
#endif
enum menuGeneralSetupItems {
CASE_RTCLOCK(ITEM_SETUP_DATE)
CASE_RTCLOCK(ITEM_SETUP_TIME)
ITEM_SETUP_BATTERY_CALIB,
ITEM_SETUP_BATTERY_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,
CASE_REVPLUS(ITEM_SETUP_BACKLIGHT_COLOR)
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_ADJUST_RTC)
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_LABELS,
ITEM_SETUP_STICK_MODE,
ITEM_SETUP_MAX
};
void menuGeneralSetup(uint8_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, menuTabGeneral, e_Setup, ITEM_SETUP_MAX, { 2, 2, 0, 1, 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) 0, LABEL(ALARMS), 0, 0, 0, 0, IF_ROTARY_ENCODERS(0) LABEL(BACKLIGHT), 0, 0, 0, CASE_REVPLUS(0) CASE_PWM_BACKLIGHT(0) CASE_PWM_BACKLIGHT(0) 0, CASE_SPLASH_PARAM(0) CASE_GPS(LABEL(GPS)) CASE_GPS(0) CASE_GPS(0) CASE_GPS(0) CASE_PXX(0) 0, 0, IF_FAI_CHOICE(0) CASE_MAVLINK(0) 0, 0, LABEL(TX_MODE), 0, 1/*to force edit mode*/ });
if (event == EVT_ENTRY) {
reusableBuffer.generalSettings.stickMode = g_eeGeneral.stickMode;
}
int sub = menuVerticalPosition;
for (unsigned int i=0; i<NUM_BODY_LINES; i++) {
coord_t y = MENU_HEADER_HEIGHT + 1 + i*FH;
uint8_t k = i+menuVerticalOffset;
uint8_t blink = ((s_editMode>0) ? BLINK|INVERS : INVERS);
uint8_t attr = (sub == k ? blink : 0);
switch(k) {
case ITEM_SETUP_DATE:
lcd_putsLeft(y, STR_DATE);
lcdDrawChar(RADIO_SETUP_DATE_COLUMN, y, '-'); lcdDrawChar(RADIO_SETUP_DATE_COLUMN+3*FW-2, y, '-');
for (uint8_t j=0; j<3; j++) {
uint8_t rowattr = (menuHorizontalPosition==j ? attr : 0);
switch (j) {
case 0:
lcdDrawNumber(RADIO_SETUP_DATE_COLUMN, y, t.tm_year+1900, rowattr|RIGHT);
if (rowattr && s_editMode>0) t.tm_year = checkIncDec(event, t.tm_year, 112, 200, 0);
break;
case 1:
lcdDrawNumber(RADIO_SETUP_DATE_COLUMN+3*FW-2, y, t.tm_mon+1, rowattr|LEADING0|RIGHT, 2);
if (rowattr && s_editMode>0) t.tm_mon = checkIncDec(event, t.tm_mon, 0, 11, 0);
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(RADIO_SETUP_DATE_COLUMN+6*FW-4, y, t.tm_mday, rowattr|LEADING0|RIGHT, 2);
if (rowattr && s_editMode>0) t.tm_mday = checkIncDec(event, t.tm_mday, 1, dlim, 0);
break;
}
}
}
if (attr && menuHorizontalPosition < 0) lcdDrawFilledRect(RADIO_SETUP_2ND_COLUMN, y, LCD_W-RADIO_SETUP_2ND_COLUMN-MENUS_SCROLLBAR_WIDTH, 8);
if (attr && checkIncDec_Ret) {
g_rtcTime = gmktime(&t); // update local timestamp and get wday calculated
}
break;
case ITEM_SETUP_TIME:
lcd_putsLeft(y, STR_TIME);
lcdDrawChar(RADIO_SETUP_TIME_COLUMN+1, y, ':'); lcdDrawChar(RADIO_SETUP_TIME_COLUMN+3*FW-2, y, ':');
for (uint8_t j=0; j<3; j++) {
uint8_t rowattr = (menuHorizontalPosition==j ? attr : 0);
switch (j) {
case 0:
lcdDrawNumber(RADIO_SETUP_TIME_COLUMN, y, t.tm_hour, rowattr|LEADING0|RIGHT, 2);
if (rowattr && s_editMode>0) t.tm_hour = checkIncDec(event, t.tm_hour, 0, 23, 0);
break;
case 1:
lcdDrawNumber(RADIO_SETUP_TIME_COLUMN+3*FWNUM, y, t.tm_min, rowattr|LEADING0|RIGHT, 2);
if (rowattr && s_editMode>0) t.tm_min = checkIncDec(event, t.tm_min, 0, 59, 0);
break;
case 2:
lcdDrawNumber(RADIO_SETUP_TIME_COLUMN+6*FWNUM, y, t.tm_sec, rowattr|LEADING0|RIGHT, 2);
if (rowattr && s_editMode>0) t.tm_sec = checkIncDec(event, t.tm_sec, 0, 59, 0);
break;
}
}
if (attr && menuHorizontalPosition < 0) lcdDrawFilledRect(RADIO_SETUP_2ND_COLUMN, y, LCD_W-RADIO_SETUP_2ND_COLUMN-MENUS_SCROLLBAR_WIDTH, 8);
if (attr && checkIncDec_Ret)
g_rtcTime = gmktime(&t); // update local timestamp and get wday calculated
break;
case ITEM_SETUP_BATTERY_CALIB:
lcd_putsLeft(y, STR_BATT_CALIB);
putsVolts(RADIO_SETUP_2ND_COLUMN, y, getBatteryVoltage(), attr | PREC2 | LEFT);
if (attr && s_editMode > 0) {
CHECK_INCDEC_GENVAR(event, g_eeGeneral.txVoltageCalibration, -127, 127);
}
break;
case ITEM_SETUP_BATTERY_RANGE:
lcd_putsLeft(y, STR_BATTERY_RANGE);
putsVolts(RADIO_SETUP_2ND_COLUMN, y, 90+g_eeGeneral.vBatMin, (menuHorizontalPosition==0 ? attr : 0)|LEFT|NO_UNIT);
lcdDrawChar(lcdLastPos, y, '-');
putsVolts(lcdLastPos+FW, y, 120+g_eeGeneral.vBatMax, (menuHorizontalPosition>0 ? attr : 0)|LEFT|NO_UNIT);
if (attr && menuHorizontalPosition < 0) lcdDrawFilledRect(RADIO_SETUP_2ND_COLUMN, y, LCD_W-RADIO_SETUP_2ND_COLUMN-MENUS_SCROLLBAR_WIDTH, 8);
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:
lcd_putsLeft(y, STR_SOUND_LABEL);
break;
case ITEM_SETUP_BEEP_MODE:
g_eeGeneral.beepMode = selectMenuItem(RADIO_SETUP_2ND_COLUMN, y, STR_SPEAKER, STR_VBEEPMODE, g_eeGeneral.beepMode, -2, 1, attr, event);
#if defined(FRSKY)
if (attr && checkIncDec_Ret) frskySendAlarms();
#endif
break;
case ITEM_SETUP_GENERAL_VOLUME:
{
lcd_putsLeft(y, STR_SPEAKER_VOLUME);
uint8_t b = g_eeGeneral.speakerVolume+VOLUME_LEVEL_DEF;
drawSlider(RADIO_SETUP_2ND_COLUMN, y, b, VOLUME_LEVEL_MAX, attr);
if (attr) {
CHECK_INCDEC_GENVAR(event, b, 0, VOLUME_LEVEL_MAX);
if (checkIncDec_Ret) {
g_eeGeneral.speakerVolume = (int8_t)b-VOLUME_LEVEL_DEF;
}
}
break;
}
case ITEM_SETUP_BEEP_VOLUME:
SLIDER_5POS(y, g_eeGeneral.beepVolume, STR_BEEP_VOLUME, event, attr);
break;
case ITEM_SETUP_WAV_VOLUME:
SLIDER_5POS(y, g_eeGeneral.wavVolume, STR_WAV_VOLUME, event, attr);
break;
case ITEM_SETUP_BACKGROUND_VOLUME:
SLIDER_5POS(y, g_eeGeneral.backgroundVolume, STR_BG_VOLUME, event, attr);
break;
case ITEM_SETUP_BEEP_LENGTH:
SLIDER_5POS(y, g_eeGeneral.beepLength, STR_BEEP_LENGTH, event, attr);
break;
case ITEM_SETUP_SPEAKER_PITCH:
lcd_putsLeft( y, STR_SPKRPITCH);
lcdDrawChar(RADIO_SETUP_2ND_COLUMN, y, '+', attr);
lcdDrawNumber(RADIO_SETUP_2ND_COLUMN+FW, y, g_eeGeneral.speakerPitch*15, attr|LEFT);
lcdDrawText(lcdLastPos, y, "Hz", attr);
if (attr) {
CHECK_INCDEC_GENVAR(event, g_eeGeneral.speakerPitch, 0, 20);
}
break;
#if defined(VARIO)
case ITEM_SETUP_VARIO_LABEL:
lcd_putsLeft(y, STR_VARIO);
break;
case ITEM_SETUP_VARIO_VOLUME:
SLIDER_5POS(y, g_eeGeneral.varioVolume, TR_SPEAKER_VOLUME, event, attr);
break;
case ITEM_SETUP_VARIO_PITCH:
lcd_putsLeft(y, STR_PITCH_AT_ZERO);
lcdDrawNumber(RADIO_SETUP_2ND_COLUMN, y, VARIO_FREQUENCY_ZERO+(g_eeGeneral.varioPitch*10), attr|LEFT);
lcdDrawText(lcdLastPos, y, "Hz", attr);
if (attr) CHECK_INCDEC_GENVAR(event, g_eeGeneral.varioPitch, -40, 40);
break;
case ITEM_SETUP_VARIO_RANGE:
lcd_putsLeft(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);
lcdDrawText(lcdLastPos, y, "Hz", attr);
if (attr) CHECK_INCDEC_GENVAR(event, g_eeGeneral.varioRange, -80, 80);
break;
case ITEM_SETUP_VARIO_REPEAT:
lcd_putsLeft(y, STR_REPEAT_AT_ZERO);
lcdDrawNumber(RADIO_SETUP_2ND_COLUMN, y, VARIO_REPEAT_ZERO+(g_eeGeneral.varioRepeat*10), attr|LEFT);
lcdDrawText(lcdLastPos, y, STR_MS, attr);
if (attr) CHECK_INCDEC_GENVAR(event, g_eeGeneral.varioRepeat, -30, 50);
break;
#endif
#if defined(HAPTIC)
case ITEM_SETUP_HAPTIC_LABEL:
lcd_putsLeft(y, STR_HAPTIC_LABEL);
break;
case ITEM_SETUP_HAPTIC_MODE:
g_eeGeneral.hapticMode = selectMenuItem(RADIO_SETUP_2ND_COLUMN, y, STR_MODE, STR_VBEEPMODE, g_eeGeneral.hapticMode, -2, 1, attr, event);
break;
case ITEM_SETUP_HAPTIC_LENGTH:
SLIDER_5POS(y, g_eeGeneral.hapticLength, STR_LENGTH, event, attr);
break;
case ITEM_SETUP_HAPTIC_STRENGTH:
SLIDER_5POS(y, g_eeGeneral.hapticStrength, STR_HAPTICSTRENGTH, event, attr);
break;
#endif
case ITEM_SETUP_CONTRAST:
lcd_putsLeft(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;
case ITEM_SETUP_ALARMS_LABEL:
lcd_putsLeft(y, STR_ALARMS_LABEL);
break;
case ITEM_SETUP_BATTERY_WARNING:
lcd_putsLeft(y, STR_BATTERYWARNING);
putsVolts(RADIO_SETUP_2ND_COLUMN, y, g_eeGeneral.vBatWarn, attr|LEFT);
if (attr) CHECK_INCDEC_GENVAR(event, g_eeGeneral.vBatWarn, 40, 120); // 4-12V
break;
case ITEM_SETUP_MEMORY_WARNING:
{
uint8_t b = 1-g_eeGeneral.disableMemoryWarning;
g_eeGeneral.disableMemoryWarning = 1 - editCheckBox(b, RADIO_SETUP_2ND_COLUMN, y, STR_MEMORYWARNING, attr, event);
break;
}
case ITEM_SETUP_ALARM_WARNING:
{
uint8_t b = 1-g_eeGeneral.disableAlarmWarning;
g_eeGeneral.disableAlarmWarning = 1 - editCheckBox(b, RADIO_SETUP_2ND_COLUMN, y, STR_ALARMWARNING, attr, event);
break;
}
case ITEM_SETUP_INACTIVITY_ALARM:
lcd_putsLeft(y, STR_INACTIVITYALARM);
lcdDrawNumber(RADIO_SETUP_2ND_COLUMN, y, g_eeGeneral.inactivityTimer, attr|LEFT);
lcdDrawChar(lcdLastPos, y, 'm');
if(attr) g_eeGeneral.inactivityTimer = checkIncDec(event, g_eeGeneral.inactivityTimer, 0, 250, EE_GENERAL); //0..250minutes
break;
case ITEM_SETUP_BACKLIGHT_LABEL:
lcd_putsLeft(y, STR_BACKLIGHT_LABEL);
break;
case ITEM_SETUP_BACKLIGHT_MODE:
g_eeGeneral.backlightMode = selectMenuItem(RADIO_SETUP_2ND_COLUMN, y, STR_MODE, STR_VBLMODE, g_eeGeneral.backlightMode, e_backlight_mode_off, e_backlight_mode_on, attr, event);
break;
case ITEM_SETUP_FLASH_BEEP:
g_eeGeneral.alarmsFlash = editCheckBox(g_eeGeneral.alarmsFlash, RADIO_SETUP_2ND_COLUMN, y, STR_ALARM, attr, event ) ;
break;
case ITEM_SETUP_BACKLIGHT_DELAY:
lcd_putsLeft(y, STR_BLDELAY);
lcdDrawNumber(RADIO_SETUP_2ND_COLUMN, y, g_eeGeneral.lightAutoOff*5, attr|LEFT);
lcdDrawChar(lcdLastPos, y, 's');
if (attr) CHECK_INCDEC_GENVAR(event, g_eeGeneral.lightAutoOff, 0, 600/5);
break;
case ITEM_SETUP_BRIGHTNESS:
lcd_putsLeft(y, STR_BRIGHTNESS);
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, 0, 100);
g_eeGeneral.backlightBright = 100 - b;
}
break;
#if defined(REVPLUS)
case ITEM_SETUP_BACKLIGHT_COLOR:
lcd_putsLeft(y, STR_BLCOLOR);
drawSlider(RADIO_SETUP_2ND_COLUMN, y, g_eeGeneral.backlightColor, 20, attr);
if (attr) g_eeGeneral.backlightColor = checkIncDec(event, g_eeGeneral.backlightColor, 0, 20, EE_GENERAL | NO_INCDEC_MARKS);
break;
#endif
#if defined(SPLASH) && !defined(FSPLASH)
case ITEM_SETUP_DISABLE_SPLASH:
{
lcd_putsLeft(y, STR_SPLASHSCREEN);
if (SPLASH_NEEDED()) {
lcdDrawNumber(RADIO_SETUP_2ND_COLUMN, y, SPLASH_TIMEOUT/100, attr|LEFT);
lcdDrawChar(lcdLastPos, y, '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
#if defined(FRSKY) && defined(FRSKY_HUB) && defined(GPS)
case ITEM_SETUP_LABEL_GPS:
lcd_putsLeft(y, STR_GPS);
break;
case ITEM_SETUP_TIMEZONE:
lcd_putsLeft(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_ADJUST_RTC:
g_eeGeneral.adjustRTC = editCheckBox(g_eeGeneral.adjustRTC, RADIO_SETUP_2ND_COLUMN, y, STR_ADJUST_RTC, attr, event);
break;
case ITEM_SETUP_GPSFORMAT:
g_eeGeneral.gpsFormat = selectMenuItem(RADIO_SETUP_2ND_COLUMN, y, STR_GPSCOORD, STR_GPSFORMAT, g_eeGeneral.gpsFormat, 0, 1, attr, event);
break;
#endif
#if defined(PXX)
case ITEM_SETUP_COUNTRYCODE:
g_eeGeneral.countryCode = selectMenuItem(RADIO_SETUP_2ND_COLUMN, y, STR_COUNTRYCODE, STR_COUNTRYCODES, g_eeGeneral.countryCode, 0, 2, attr, event);
break;
#endif
case ITEM_SETUP_LANGUAGE:
lcd_putsLeft(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:
g_eeGeneral.imperial = selectMenuItem(RADIO_SETUP_2ND_COLUMN, y, STR_UNITSSYSTEM, STR_VUNITSSYSTEM, g_eeGeneral.imperial, 0, 1, attr, event);
break;
#if defined(FAI_CHOICE)
case ITEM_SETUP_FAI:
editCheckBox(g_eeGeneral.fai, RADIO_SETUP_2ND_COLUMN, y, PSTR("FAI Mode"), 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:
g_eeGeneral.mavbaud = selectMenuItem(RADIO_SETUP_2ND_COLUMN, y, STR_MAVLINK_BAUD_LABEL, STR_MAVLINK_BAUDS, g_eeGeneral.mavbaud, 0, 7, attr, event);
break;
#endif
case ITEM_SETUP_SWITCHES_DELAY:
lcd_putsLeft(y, STR_SWITCHES_DELAY);
lcdDrawNumber(RADIO_SETUP_2ND_COLUMN, y, 10*SWITCHES_DELAY(), attr|LEFT);
lcdDrawText(lcdLastPos, y, STR_MS, attr);
if (attr) CHECK_INCDEC_GENVAR(event, g_eeGeneral.switchesDelay, -15, 100-15);
break;
case ITEM_SETUP_RX_CHANNEL_ORD:
lcd_putsLeft(y, STR_RXCHANNELORD); // RAET->AETR
for (uint8_t i=1; i<=4; i++) {
putsChnLetter(RADIO_SETUP_2ND_COLUMN - FW + i*FW, y, channel_order(i), attr);
}
if (attr) CHECK_INCDEC_GENVAR(event, g_eeGeneral.templateSetup, 0, 23);
break;
case ITEM_SETUP_STICK_MODE_LABELS:
lcd_putsLeft(y, NO_INDENT(STR_MODE));
for (uint8_t i=0; i<4; i++) {
lcd_img((6+4*i)*FW, y, sticks, i, 0);
}
break;
case ITEM_SETUP_STICK_MODE:
lcdDrawChar(2*FW, y, '1'+reusableBuffer.generalSettings.stickMode, attr);
for (uint8_t i=0; i<4; i++) {
putsStickName((6+4*i)*FW, y, pgm_read_byte(modn12x3 + 4*reusableBuffer.generalSettings.stickMode + i), 0);
}
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;
}
}
}
+99
View File
@@ -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"
#define TRAINER_CALIB_POS 12
void menuGeneralTrainer(uint8_t event)
{
uint8_t y;
bool slave = SLAVE_MODE();
MENU(STR_MENUTRAINER, menuTabGeneral, e_Trainer, (slave ? 0 : 6), { 2, 2, 2, 2, 0/*, 0*/ });
if (slave) {
lcdDrawText(7*FW, 4*FH, STR_SLAVE);
return;
}
LcdFlags attr;
LcdFlags blink = ((s_editMode>0) ? BLINK|INVERS : INVERS);
lcdDrawText(3*FW, MENU_HEADER_HEIGHT+1, STR_MODESRC);
y = MENU_HEADER_HEIGHT + 1 + 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];
putsStickName(0, y, chan-1, (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(4*FW, y, STR_TRNMODE, td->mode, attr);
if (attr&BLINK) CHECK_INCDEC_GENVAR(event, td->mode, 0, 2);
break;
case 1:
lcdDrawNumber(11*FW, y, td->studWeight, attr|RIGHT);
if (attr&BLINK) CHECK_INCDEC_GENVAR(event, td->studWeight, -125, 125);
break;
case 2:
lcdDrawTextAtIndex(12*FW, 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;
lcd_putsLeft(MENU_HEADER_HEIGHT+1+5*FH, STR_MULTIPLIER);
lcdDrawNumber(LEN_MULTIPLIER*FW+3*FW, MENU_HEADER_HEIGHT+1+5*FH, g_eeGeneral.PPM_Multiplier+10, attr|PREC1|RIGHT);
if (attr) CHECK_INCDEC_GENVAR(event, g_eeGeneral.PPM_Multiplier, -10, 40);
attr = (sub==6) ? INVERS : 0;
if (attr) s_editMode = 0;
lcdDrawText(0*FW, MENU_HEADER_HEIGHT+1+6*FH, STR_CAL, attr);
for (int i=0; i<4; i++) {
uint8_t x = (i*TRAINER_CALIB_POS+16)*FW/2;
#if defined (PPM_UNIT_PERCENT_PREC1)
lcdDrawNumber(x, MENU_HEADER_HEIGHT+1+6*FH, (ppmInput[i]-g_eeGeneral.trainer.calib[i])*2, PREC1|RIGHT);
#else
lcdDrawNumber(x, MENU_HEADER_HEIGHT+1+6*FH, (ppmInput[i]-g_eeGeneral.trainer.calib[i])/5, RIGHT);
#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();
}
}
}
+101
View File
@@ -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"
void backupEeprom()
{
char filename[60];
uint8_t buffer[1024];
FIL file;
lcdClear();
drawProgressBar(STR_WRITING);
// reset unexpectedShutdown to prevent warning when user restores EEPROM backup
g_eeGeneral.unexpectedShutdown = 0;
storageDirty(EE_GENERAL);
storageCheck(true);
// create the directory if needed...
const char * error = sdCheckAndCreateDirectory(EEPROMS_PATH);
if (error) {
POPUP_WARNING(error);
return;
}
// prepare the filename...
char * tmp = strAppend(filename, EEPROMS_PATH "/eeprom");
tmp = strAppendDate(tmp, true);
strAppend(tmp, EEPROM_EXT);
// open the file for writing...
f_open(&file, filename, FA_WRITE | FA_CREATE_ALWAYS);
for (int i=0; i<EESIZE; i+=1024) {
UINT count;
eepromReadBlock(buffer, i, 1024);
f_write(&file, buffer, 1024, &count);
updateProgressBar(i, EESIZE);
SIMU_SLEEP(100/*ms*/);
}
f_close(&file);
//set back unexpectedShutdown
g_eeGeneral.unexpectedShutdown = 1;
storageDirty(EE_GENERAL);
storageCheck(true);
}
void menuGeneralVersion(uint8_t event)
{
if (warningResult) {
warningResult = 0;
displayPopup(STR_STORAGE_FORMAT);
storageEraseAll(false);
#if !defined(SIMU)
NVIC_SystemReset();
#else
exit(0);
#endif
}
if (event == EVT_ENTRY) {
getCPUUniqueID(reusableBuffer.version.id);
}
SIMPLE_MENU(STR_MENUVERSION, menuTabGeneral, e_Vers, 1);
lcd_putsLeft(MENU_HEADER_HEIGHT+1, vers_stamp);
lcd_putsLeft(MENU_HEADER_HEIGHT+4*FH+1, "UID\037\033:");
lcdDrawText(5*FW+3, MENU_HEADER_HEIGHT+4*FH+1, reusableBuffer.version.id);
lcd_putsLeft(MENU_HEADER_HEIGHT+5*FH+1, STR_EEBACKUP);
lcd_putsLeft(MENU_HEADER_HEIGHT+6*FH+1, STR_FACTORYRESET);
lcdDrawFilledRect(0, MENU_HEADER_HEIGHT+5*FH, LCD_W, 2*FH+1, SOLID);
if (event == EVT_KEY_LONG(KEY_ENTER)) {
backupEeprom();
}
else if (event == EVT_KEY_LONG(KEY_MENU)) {
POPUP_CONFIRMATION(STR_CONFIRMRESET);
}
}
+44
View File
@@ -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.
*/
#include "../../opentx.h"
#if defined(SPLASH)
const pm_uchar splashdata[] PROGMEM = {
'S','P','S',0,
#include "bitmaps/212x64/splash.lbm"
'S','P','E',0 };
const pm_uchar * const splash_lbm = splashdata+4;
void drawSplash()
{
lcdClear();
lcdDrawBitmap(0, 0, splash_lbm);
#if MENUS_LOCK == 1
if (readonly == false) {
lcdDrawFilledRect((LCD_W-(sizeof(TR_UNLOCKED)-1)*FW)/2 - 9, 50, (sizeof(TR_UNLOCKED)-1)*FW+16, 11, SOLID, ERASE|ROUND);
lcdDrawText((LCD_W-(sizeof(TR_UNLOCKED)-1)*FW)/2 , 53, STR_UNLOCKED);
}
#endif
lcdRefresh();
}
#endif
+151
View File
@@ -0,0 +1,151 @@
/*
* 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 pm_uchar about_bmp[] PROGMEM = {
#include "about.lbm"
};
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 6
void menuAboutView(uint8_t event)
{
static uint8_t screenIndex;
static uint8_t greyIndex;
switch(event)
{
case EVT_ENTRY:
screenIndex = 0;
greyIndex = 0;
break;
case EVT_KEY_FIRST(KEY_DOWN):
screenIndex < ABOUT_PARENTS ? screenIndex++ : screenIndex = ABOUT_OPENTX;
greyIndex = 0;
break;
case EVT_KEY_FIRST(KEY_UP):
screenIndex > ABOUT_OPENTX ? screenIndex-- : screenIndex = ABOUT_PARENTS;
greyIndex = 0;
break;
case EVT_KEY_FIRST(KEY_EXIT):
chainMenu(menuMainView);
break;
}
lcdDrawBitmap(0, 0, about_bmp);
lcdDrawText(64, 0, STR_ABOUTUS, DBLSIZE);
lcdDrawSolidHorizontalLine(ABOUT_X, 18, 120);
lcdDrawSolidHorizontalLine(ABOUT_X, 19, 130, GREY_DEFAULT);
LcdFlags att = GREY(max(0, 15-greyIndex/2));
uint8_t screenDuration = 150;
switch (screenIndex) {
case ABOUT_OPENTX:
case ABOUT_END:
lcdDrawText(ABOUT_X, 22, STR_ABOUT_OPENTX_1, SMLSIZE);
lcdDrawText(ABOUT_X, 30, STR_ABOUT_OPENTX_2, SMLSIZE);
lcdDrawText(ABOUT_X, 38, STR_ABOUT_OPENTX_3, SMLSIZE);
lcdDrawText(ABOUT_X, 46, STR_ABOUT_OPENTX_4, SMLSIZE);
lcdDrawText(ABOUT_X, 54, STR_ABOUT_OPENTX_5, SMLSIZE);
screenDuration = 255;
break;
case ABOUT_BERTRAND:
lcdDrawText(ABOUT_X, 24, STR_ABOUT_BERTRAND_1, att);
lcdDrawText(ABOUT_X+ABOUT_INDENT, 36, STR_ABOUT_BERTRAND_2, att|SMLSIZE);
lcdDrawText(ABOUT_X+ABOUT_INDENT, 44, STR_ABOUT_BERTRAND_3, att|SMLSIZE);
break;
case ABOUT_ANDRE:
lcdDrawText(ABOUT_X, 24, STR_ABOUT_ANDRE_1, att);
lcdDrawText(ABOUT_X+ABOUT_INDENT, 36, STR_ABOUT_ANDRE_2, att|SMLSIZE);
lcdDrawText(ABOUT_X+ABOUT_INDENT, 44, STR_ABOUT_ANDRE_3, att|SMLSIZE);
break;
case ABOUT_MIKE:
lcdDrawText(ABOUT_X, 24, STR_ABOUT_MIKE_1, att);
lcdDrawText(ABOUT_X+ABOUT_INDENT, 36, STR_ABOUT_MIKE_2, att|SMLSIZE);
lcdDrawText(ABOUT_X+ABOUT_INDENT, 44, STR_ABOUT_MIKE_3, att|SMLSIZE);
lcdDrawText(ABOUT_X+ABOUT_INDENT, 52, STR_ABOUT_MIKE_4, att|SMLSIZE);
break;
case ABOUT_KJELL:
lcdDrawText(ABOUT_X, 24, STR_ABOUT_KJELL_1, att);
lcdDrawText(ABOUT_X+ABOUT_INDENT, 36, STR_ABOUT_KJELL_2, att|SMLSIZE);
lcdDrawText(ABOUT_X+ABOUT_INDENT, 44, STR_ABOUT_KJELL_3, att|SMLSIZE);
lcdDrawText(ABOUT_X+ABOUT_INDENT, 52, STR_ABOUT_KJELL_4, att|SMLSIZE);
break;
case ABOUT_MARTIN:
lcdDrawText(ABOUT_X, 24, STR_ABOUT_MARTIN_1, att);
lcdDrawText(ABOUT_X+ABOUT_INDENT, 36, STR_ABOUT_MARTIN_2, att|SMLSIZE);
break;
case ABOUT_ROMOLO:
lcdDrawText(ABOUT_X, 24, STR_ABOUT_ROMOLO_1, att);
lcdDrawText(ABOUT_X+ABOUT_INDENT, 36, STR_ABOUT_ROMOLO_2, att|SMLSIZE);
lcdDrawText(ABOUT_X+ABOUT_INDENT, 44, STR_ABOUT_ROMOLO_3, att|SMLSIZE);
break;
case ABOUT_ROB:
lcdDrawText(ABOUT_X, 24, STR_ABOUT_ROB_1, att);
lcdDrawText(ABOUT_X+ABOUT_INDENT, 36, STR_ABOUT_ROB_2, att|SMLSIZE);
break;
case ABOUT_HARDWARE:
lcdDrawText(ABOUT_X, 24, STR_ABOUT_HARDWARE_1, att);
lcdDrawText(ABOUT_X+ABOUT_INDENT, 36, STR_ABOUT_HARDWARE_2, att|SMLSIZE);
lcdDrawText(ABOUT_X+ABOUT_INDENT, 44, STR_ABOUT_HARDWARE_3, att|SMLSIZE);
break;
case ABOUT_PARENTS:
lcdDrawText(ABOUT_X, 24, STR_ABOUT_PARENTS_1, att);
lcdDrawText(ABOUT_X+ABOUT_INDENT, 36, STR_ABOUT_PARENTS_2, att|SMLSIZE);
lcdDrawText(ABOUT_X+ABOUT_INDENT, 44, STR_ABOUT_PARENTS_3, att|SMLSIZE);
lcdDrawText(ABOUT_X+ABOUT_INDENT, 52, STR_ABOUT_PARENTS_4, att|SMLSIZE);
screenDuration = 255;
break;
}
if (++greyIndex == screenDuration) {
greyIndex = 0;
if (++screenIndex == ABOUT_COUNT) {
chainMenu(menuMainView);
}
}
}
+116
View File
@@ -0,0 +1,116 @@
/*
* 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 menuChannelsView(uint8_t event)
{
static bool longNames = false;
bool newLongNames = false;
static bool secondPage = false;
#ifdef MIXERS_MONITOR
static bool mixersView = false;
#endif
uint8_t ch;
switch(event)
{
case EVT_KEY_BREAK(KEY_EXIT):
popMenu();
break;
case EVT_KEY_FIRST(KEY_RIGHT):
case EVT_KEY_FIRST(KEY_LEFT):
secondPage = !secondPage;
break;
#ifdef MIXERS_MONITOR
case EVT_KEY_FIRST(KEY_ENTER):
mixersView = !mixersView;
break;
#endif
}
if (secondPage)
ch = 16;
else
ch = 0;
#ifdef MIXERS_MONITOR
if (mixersView)
lcd_putsCenter(0*FH, MIXERS_MONITOR);
else
#endif
lcd_putsCenter(0*FH, CHANNELS_MONITOR);
lcdInvertLine(0);
// Column separator
lcdDrawSolidVerticalLine(LCD_W/2, FH, LCD_H-FH);
for (uint8_t col=0; col<2; col++) {
uint8_t x = col*LCD_W/2+1;
// Channels
for (uint8_t line=0; line<8; line++) {
uint8_t y = 9+line*7;
#ifdef MIXERS_MONITOR
int32_t val = (mixersView) ? ex_chans[ch] : channelOutputs[ch];
#else
int32_t val = channelOutputs[ch];
#endif
uint8_t ofs = (col ? 0 : 1);
// Channel name if present, number if not
uint8_t lenLabel = ZLEN(g_model.limitData[ch].name);
if (lenLabel > 4) {
newLongNames = longNames = true;
}
if (lenLabel > 0)
lcdDrawSizedText(x+1-ofs, y, g_model.limitData[ch].name, sizeof(g_model.limitData[ch].name), ZCHAR | SMLSIZE);
else
putsChn(x+1-ofs, y, ch+1, SMLSIZE);
// Value
#if defined(PPM_UNIT_US)
uint8_t wbar = (longNames ? 54 : 64);
lcdDrawNumber(x+LCD_W/2-3-wbar-ofs, y+1, PPM_CH_CENTER(ch)+val/2, TINSIZE|RIGHT);
#elif defined(PPM_UNIT_PERCENT_PREC1)
uint8_t wbar = (longNames ? 48 : 58);
lcdDrawNumber(x+LCD_W/2-3-wbar-ofs, y+1, calcRESXto1000(val), PREC1|TINSIZE|RIGHT);
#else
uint8_t wbar = (longNames ? 54 : 64);
lcdDrawNumber(x+LCD_W/2-3-wbar-ofs, y+1, calcRESXto1000(val)/10, TINSIZE|RIGHT);
#endif
// Gauge
#ifdef MIXERS_MONITOR
uint16_t lim = mixersView ? 512*2*2 : (g_model.extendedLimits ? 640*2 : 512*2);
#else
uint16_t lim = g_model.extendedLimits ? 640*2 : 512*2;
#endif
drawGauge(x+LCD_W/2-3-wbar-ofs, y, wbar, 6, val, lim);
ch++;
}
}
longNames = newLongNames;
}
+591
View File
@@ -0,0 +1,591 @@
/*
* 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 BIGSIZE MIDSIZE
#define LBOX_CENTERX (BOX_WIDTH/2 + 16)
#define RBOX_CENTERX (LCD_W-LBOX_CENTERX-1)
#define MODELNAME_X (15)
#define MODELNAME_Y (11)
#define VBATT_X (MODELNAME_X+26)
#define VBATT_Y (FH+3)
#define VBATTUNIT_X (VBATT_X-2)
#define VBATTUNIT_Y VBATT_Y
#define BITMAP_X ((LCD_W-64)/2)
#define BITMAP_Y (LCD_H/2)
#define PHASE_X BITMAP_X
#define PHASE_Y (3*FH)
#define PHASE_FLAGS (0)
#define TIMERS_X 145
#define TIMERS_Y 20
#define TIMERS_H 25
#define TIMERS_R 193
#define REBOOT_X (LCD_W-FW)
#define VSWITCH_X(i) (((i>=NUM_LOGICAL_SWITCH*3/4) ? BITMAP_X+28 : ((i>=NUM_LOGICAL_SWITCH/2) ? BITMAP_X+25 : ((i>=NUM_LOGICAL_SWITCH/4) ? 21 : 18))) + 3*i)
#define VSWITCH_Y (LCD_H-9)
#define TRIM_LH_X (32+9)
#define TRIM_LV_X 10
#define TRIM_RV_X (LCD_W-11)
#define TRIM_RH_X (LCD_W-32-9)
#define TRIM_LEN 27
#define MARKER_WIDTH 5
const pm_uchar logo_taranis[] PROGMEM = {
#include "../../bitmaps/212x64/logo.lbm"
};
const pm_uchar icons[] PROGMEM = {
#include "../../bitmaps/212x64/icons.lbm"
};
#define ICON_RSSI 0, 9
#define ICON_SPEAKER0 9, 8
#define ICON_SPEAKER1 17, 8
#define ICON_SPEAKER2 25, 8
#define ICON_SPEAKER3 33, 8
#define ICON_SD 41, 11
#define ICON_LOGS 51, 11
#define ICON_TRAINER 61, 11
#define ICON_TRAINEE 71, 11
#define ICON_USB 81, 11
#define ICON_REBOOT 91, 11
#define ICON_ALTITUDE 102, 9
void doMainScreenGraphics()
{
int16_t calibStickVert = calibratedStick[CONVERT_MODE(1)];
if (g_model.throttleReversed && CONVERT_MODE(1) == THR_STICK)
calibStickVert = -calibStickVert;
drawStick(LBOX_CENTERX, calibratedStick[CONVERT_MODE(0)], calibStickVert);
calibStickVert = calibratedStick[CONVERT_MODE(2)];
if (g_model.throttleReversed && CONVERT_MODE(2) == THR_STICK)
calibStickVert = -calibStickVert;
drawStick(RBOX_CENTERX, calibratedStick[CONVERT_MODE(3)], calibStickVert);
}
void displayTrims(uint8_t phase)
{
for (unsigned int i=0; i<NUM_STICKS; i++) {
coord_t x[4] = { TRIM_LH_X, TRIM_LV_X, TRIM_RV_X, TRIM_RH_X };
uint8_t vert[4] = { 0, 1, 1, 0 };
coord_t xm, ym;
unsigned int stickIndex = CONVERT_MODE(i);
xm = x[stickIndex];
uint32_t att = ROUND;
int32_t trim = getTrimValue(phase, i);
int32_t val = trim;
bool exttrim = false;
if (val < TRIM_MIN || val > TRIM_MAX) {
exttrim = true;
}
if (val < -(TRIM_LEN+1)*4) {
val = -(TRIM_LEN+1);
}
else if (val > (TRIM_LEN+1)*4) {
val = TRIM_LEN+1;
}
else {
val /= 4;
}
if (vert[i]) {
ym = 31;
lcdDrawSolidVerticalLine(xm, ym-TRIM_LEN, TRIM_LEN*2);
if (i!=2 || !g_model.thrTrim) {
lcdDrawSolidVerticalLine(xm-1, ym-1, 3);
lcdDrawSolidVerticalLine(xm+1, ym-1, 3);
}
ym -= val;
lcdDrawFilledRect(xm-3, ym-3, 7, 7, SOLID, att|ERASE);
if (trim >= 0) {
lcdDrawSolidHorizontalLine(xm-1, ym-1, 3);
}
if (trim <= 0) {
lcdDrawSolidHorizontalLine(xm-1, ym+1, 3);
}
if (exttrim) {
lcdDrawSolidHorizontalLine(xm-1, ym, 3);
}
if (g_model.displayTrims != DISPLAY_TRIMS_NEVER && trim != 0) {
if (g_model.displayTrims == DISPLAY_TRIMS_ALWAYS || (trimsDisplayTimer > 0 && (trimsDisplayMask & (1<<i)))) {
lcdDrawNumber(trim>0 ? 22 : 54, xm-2, -abs(trim), RIGHT|TINSIZE|VERTICAL);
}
}
}
else {
ym = 60;
lcdDrawSolidHorizontalLine(xm-TRIM_LEN, ym, TRIM_LEN*2);
lcdDrawSolidHorizontalLine(xm-1, ym-1, 3);
lcdDrawSolidHorizontalLine(xm-1, ym+1, 3);
xm += val;
lcdDrawFilledRect(xm-3, ym-3, 7, 7, SOLID, att|ERASE);
if (trim >= 0) {
lcdDrawSolidVerticalLine(xm+1, ym-1, 3);
}
if (trim <= 0) {
lcdDrawSolidVerticalLine(xm-1, ym-1, 3);
}
if (exttrim) {
lcdDrawSolidVerticalLine(xm, ym-1, 3);
}
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 ? -11 : 20), ym-2, -abs(trim), RIGHT|TINSIZE);
}
}
}
lcdDrawSquare(xm-3, ym-3, 7, att);
}
}
void drawSliders()
{
for (uint8_t i=NUM_STICKS; i<NUM_STICKS+NUM_POTS; i++) {
#if defined(REV9E)
if (i < SLIDER1) continue; // TODO change and display more values
coord_t x = ((i==SLIDER1 || i==SLIDER3) ? 3 : LCD_W-5);
int8_t y = (i<SLIDER3 ? LCD_H/2+1 : 1);
#else
if (i == POT3) continue;
coord_t x = ((i==POT1 || i==SLIDER1) ? 3 : LCD_W-5);
int8_t y = (i>=SLIDER1 ? LCD_H/2+1 : 1);
#endif
lcdDrawSolidVerticalLine(x, y, LCD_H/2-2);
lcdDrawSolidVerticalLine(x+1, y, LCD_H/2-2);
y += LCD_H/2-4;
y -= ((calibratedStick[i]+RESX)*(LCD_H/2-4)/(RESX*2)); // calculate once per loop
lcdDrawSolidVerticalLine(x-1, y, 2);
lcdDrawSolidVerticalLine(x+2, y, 2);
}
}
#define BAR_X 14
#define BAR_Y 1
#define BAR_W 184
#define BAR_H 9
#define BAR_NOTIFS_X BAR_X+133
#define BAR_VOLUME_X BAR_X+147
#define BAR_TIME_X BAR_X+159
void displayTopBarGauge(coord_t x, int count, bool blinking=false)
{
if (!blinking || BLINK_ON_PHASE)
lcdDrawFilledRect(x+1, BAR_Y+2, 11, 5, SOLID, ERASE);
for (int i=0; i<count; i+=2)
lcdDrawSolidVerticalLine(x+2+i, BAR_Y+3, 3);
}
#define LCD_NOTIF_ICON(x, icon) \
lcdDrawBitmap(x, BAR_Y, icons, icon); \
lcdDrawSolidHorizontalLine(x, BAR_Y+8, 11)
void displayTopBar()
{
uint8_t batt_icon_x;
uint8_t altitude_icon_x;
/* Tx voltage */
putsVBat(BAR_X+2, BAR_Y+1, LEFT);
batt_icon_x = lcdLastPos;
lcdDrawRect(batt_icon_x+FW, BAR_Y+1, 13, 7);
lcdDrawSolidVerticalLine(batt_icon_x+FW+13, BAR_Y+2, 5);
if (TELEMETRY_STREAMING()) {
/* RSSI */
LCD_ICON(batt_icon_x+3*FW+3, BAR_Y, ICON_RSSI);
lcdDrawRect(batt_icon_x+5*FW, BAR_Y+1, 13, 7);
/* Rx voltage */
altitude_icon_x = batt_icon_x+7*FW+3;
if (g_model.frsky.voltsSource) {
uint8_t item = g_model.frsky.voltsSource-1;
if (item < MAX_SENSORS) {
TelemetryItem & voltsItem = telemetryItems[item];
if (voltsItem.isAvailable()) {
putsTelemetryChannelValue(batt_icon_x+7*FW+2, BAR_Y+1, item, voltsItem.value, LEFT);
altitude_icon_x = lcdLastPos+1;
}
}
}
/* Altitude */
if (g_model.frsky.altitudeSource) {
uint8_t item = g_model.frsky.altitudeSource-1;
if (item < MAX_SENSORS) {
TelemetryItem & altitudeItem = telemetryItems[item];
if (altitudeItem.isAvailable()) {
LCD_ICON(altitude_icon_x, BAR_Y, ICON_ALTITUDE);
int32_t value = altitudeItem.value / g_model.telemetrySensors[item].getPrecDivisor();
putsValueWithUnit(altitude_icon_x+2*FW-1, BAR_Y+1, value, g_model.telemetrySensors[item].unit, LEFT);
}
}
}
}
/* Notifs icons */
coord_t x = BAR_NOTIFS_X;
#if defined(LOG_TELEMETRY) || defined(WATCHDOG_DISABLED)
LCD_NOTIF_ICON(x, ICON_REBOOT);
x -= 12;
#else
if (unexpectedShutdown) {
LCD_NOTIF_ICON(x, ICON_REBOOT);
x -= 12;
}
#endif
if (usbPlugged()) {
LCD_NOTIF_ICON(x, ICON_USB);
x -= 12;
}
if (TRAINER_CONNECTED()) {
if (SLAVE_MODE()) {
LCD_NOTIF_ICON(x, ICON_TRAINEE);
x -= 12;
}
else if (IS_TRAINER_INPUT_VALID()) {
LCD_NOTIF_ICON(x, ICON_TRAINER);
x -= 12;
}
}
if (isFunctionActive(FUNCTION_LOGS)) {
LCD_NOTIF_ICON(x, ICON_LOGS);
x -= 12;
}
/* Audio volume */
if (requiredSpeakerVolume == 0 || g_eeGeneral.beepMode == e_mode_quiet)
LCD_ICON(BAR_VOLUME_X, BAR_Y, ICON_SPEAKER0);
else if (requiredSpeakerVolume <= 6)
LCD_ICON(BAR_VOLUME_X, BAR_Y, ICON_SPEAKER1);
else if (requiredSpeakerVolume <= 12)
LCD_ICON(BAR_VOLUME_X, BAR_Y, ICON_SPEAKER2);
else if (requiredSpeakerVolume <= 18)
LCD_ICON(BAR_VOLUME_X, BAR_Y, ICON_SPEAKER2);
else
LCD_ICON(BAR_VOLUME_X, BAR_Y, ICON_SPEAKER3);
/* RTC time */
putsRtcTime(BAR_TIME_X, BAR_Y+1, LEFT|TIMEBLINK);
/* The background */
lcdDrawFilledRect(BAR_X, BAR_Y, BAR_W, BAR_H, SOLID, FILL_WHITE|GREY(12)|ROUND);
/* The inside of the Batt gauge */
displayTopBarGauge(batt_icon_x+FW, GET_TXBATT_BARS(), IS_TXBATT_WARNING());
/* The inside of the RSSI gauge */
if (TELEMETRY_RSSI() > 0) {
displayTopBarGauge(batt_icon_x+5*FW, TELEMETRY_RSSI() / 10, TELEMETRY_RSSI() < getRssiAlarmValue(0));
}
}
void displayTimers()
{
// Main and Second timer
for (unsigned int i=0; i<2; i++) {
if (g_model.timers[i].mode) {
TimerState & timerState = timersStates[i];
TimerData & timerData = g_model.timers[i];
uint8_t y = TIMERS_Y + i*TIMERS_H;
if (ZLEN(timerData.name) > 0) {
lcdDrawSizedText(TIMERS_X, y-7, timerData.name, LEN_TIMER_NAME, ZCHAR|SMLSIZE);
}
else {
putsTimerMode(TIMERS_X, y-7, timerData.mode, SMLSIZE);
}
putsTimer(TIMERS_X, y, timerState.val, TIMEHOUR|MIDSIZE|LEFT, TIMEHOUR|MIDSIZE|LEFT);
if (timerData.persistent) {
lcdDrawChar(TIMERS_R, y+1, 'P', SMLSIZE);
}
if (timerState.val < 0) {
if (BLINK_ON_PHASE) {
lcdDrawFilledRect(TIMERS_X-7, y-8, 60, 20);
}
}
}
}
}
void menuMainViewChannelsMonitor(uint8_t event)
{
switch(event) {
case EVT_KEY_BREAK(KEY_PAGE):
case EVT_KEY_BREAK(KEY_EXIT):
chainMenu(menuMainView);
event = 0;
break;
}
return menuChannelsView(event);
}
void onMainViewMenu(const char *result)
{
if (result == STR_RESET_TIMER1) {
timerReset(0);
}
else if (result == STR_RESET_TIMER2) {
timerReset(1);
}
#if TIMERS > 2
else if (result == STR_RESET_TIMER3) {
timerReset(2);
}
#endif
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) {
chainMenu(menuStatisticsView);
}
else if (result == STR_ABOUT_US) {
chainMenu(menuAboutView);
}
}
void displaySwitch(coord_t x, coord_t y, int width, unsigned int index)
{
if (SWITCH_EXISTS(index)) {
int val = getValue(MIXSRC_FIRST_SWITCH+index);
if (val >= 0) {
lcdDrawSolidHorizontalLine(x, y, width);
lcdDrawSolidHorizontalLine(x, y+2, width);
y += 4;
if (val > 0) {
lcdDrawSolidHorizontalLine(x, y, width);
lcdDrawSolidHorizontalLine(x, y+2, width);
y += 4;
}
}
lcdDrawChar(width==5 ? x+1 : x, y, 'A'+index, TINSIZE);
y += 6;
if (val <= 0) {
lcdDrawSolidHorizontalLine(x, y, width);
lcdDrawSolidHorizontalLine(x, y+2, width);
if (val < 0) {
lcdDrawSolidHorizontalLine(x, y+4, width);
lcdDrawSolidHorizontalLine(x, y+6, width);
}
}
}
}
const MenuItem MAIN_MENU[] = {
{ "RADIO SETTINGS", menuGeneralSetup },
{ "MODEL SELECT", menuModelSelect },
{ "MODEL SETTINGS", menuModelSetup },
{ "CHECKLIST", menuModelNotes },
{ "SD MANAGER", menuGeneralSdManager },
{ "VERSION", menuGeneralVersion }
};
bool isMenuAvailable(int index)
{
if (index == 4) {
return modelHasNotes();
}
else {
return true;
}
}
int getSwitchCount()
{
int count = 0;
for (int i=0; i<NUM_SWITCHES; ++i) {
if (SWITCH_EXISTS(i)) {
++count;
}
}
return count;
}
void menuMainView(uint8_t event)
{
STICK_SCROLL_DISABLE();
switch(event) {
case EVT_ENTRY:
killEvents(KEY_EXIT);
killEvents(KEY_UP);
killEvents(KEY_DOWN);
// no break
case EVT_ENTRY_UP:
LOAD_MODEL_BITMAP();
break;
case EVT_KEY_LONG(KEY_ENTER):
killEvents(event);
if (modelHasNotes()) {
POPUP_MENU_ADD_ITEM(STR_VIEW_NOTES);
}
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;
#if MENUS_LOCK != 2/*no menus*/
case EVT_KEY_BREAK(KEY_MENU):
pushMenu(menuModelSelect);
break;
case EVT_KEY_LONG(KEY_MENU):
pushMenu(menuGeneralSetup);
killEvents(event);
break;
#endif
case EVT_KEY_BREAK(KEY_PAGE):
storageDirty(EE_GENERAL);
g_model.view += 1;
if (g_model.view >= VIEW_COUNT) {
g_model.view = 0;
chainMenu(menuMainViewChannelsMonitor);
}
break;
case EVT_KEY_LONG(KEY_PAGE):
if (!IS_FAI_ENABLED())
chainMenu(menuTelemetryFrsky);
killEvents(event);
break;
case EVT_KEY_FIRST(KEY_EXIT):
#if defined(GVARS)
if (gvarDisplayTimer > 0) {
gvarDisplayTimer = 0;
}
#endif
break;
}
// Flight Mode Name
int mode = mixerCurrentFlightMode;
lcdDrawSizedText(PHASE_X, PHASE_Y, g_model.flightModeData[mode].name, sizeof(g_model.flightModeData[mode].name), ZCHAR|PHASE_FLAGS);
// Model Name
putsModelName(MODELNAME_X, MODELNAME_Y, g_model.header.name, g_eeGeneral.currModel, BIGSIZE);
// Trims sliders
displayTrims(mode);
// Top bar
displayTopBar();
// Sliders (Pots / Sliders)
drawSliders();
lcdDrawBitmap(BITMAP_X, BITMAP_Y, modelBitmap);
// Switches
if (getSwitchCount() > 8) {
for (int i=0; i<NUM_SWITCHES; ++i) {
div_t qr = div(i, 9);
if (g_model.view == VIEW_INPUTS) {
div_t qr2 = div(qr.rem, 5);
if (i >= 14) qr2.rem += 1;
const coord_t x[4] = { 50, 142 };
const coord_t y[4] = { 25, 42, 25, 42 };
displaySwitch(x[qr.quot]+qr2.rem*4, y[qr2.quot], 3, i);
}
else {
displaySwitch(17+qr.rem*6, 25+qr.quot*17, 5, i);
}
}
}
else {
int index = 0;
for (int i=0; i<NUM_SWITCHES; ++i) {
if (SWITCH_EXISTS(i)) {
getvalue_t val = getValue(MIXSRC_FIRST_SWITCH+i);
getvalue_t sw = ((val < 0) ? 3*i+1 : ((val == 0) ? 3*i+2 : 3*i+3));
putsSwitches((g_model.view == VIEW_INPUTS) ? (index<4 ? 8*FW+1 : 23*FW+2) : (index<4 ? 3*FW+1 : 8*FW-2), (index%4)*FH+3*FH, sw, 0);
index++;
}
}
}
if (g_model.view == VIEW_TIMERS) {
displayTimers();
}
else if (g_model.view == VIEW_INPUTS) {
// Sticks
doMainScreenGraphics();
}
else {
// Logical Switches
lcdDrawText(TRIM_RH_X - TRIM_LEN/2 + 5, 6*FH-1, "LS 1-32");
for (int sw=0; sw<32; ++sw) {
div_t qr = div(sw, 10);
uint8_t y = 13 + 11 * qr.quot;
uint8_t x = TRIM_RH_X - TRIM_LEN + qr.rem*5 + (qr.rem >= 5 ? 3 : 0);
LogicalSwitchData * cs = lswAddress(sw);
if (cs->func == LS_FUNC_NONE) {
lcdDrawSolidHorizontalLine(x, y+6, 4);
lcdDrawSolidHorizontalLine(x, y+7, 4);
}
else if (getSwitch(SWSRC_SW1+sw)) {
lcdDrawFilledRect(x, y, 4, 8);
}
else {
lcdDrawRect(x, y, 4, 8);
}
}
}
#if defined(GVARS)
if (gvarDisplayTimer > 0) {
gvarDisplayTimer--;
lcdDrawFilledRect(BITMAP_X, BITMAP_Y, 64, 32, SOLID, ERASE);
lcdDrawRect(BITMAP_X, BITMAP_Y, 64, 32);
drawStringWithIndex(BITMAP_X+FW, BITMAP_Y+FH-1, STR_GV, gvarLastChanged+1);
lcdDrawSizedText(BITMAP_X+4*FW+FW/2, BITMAP_Y+FH-1, g_model.gvars[gvarLastChanged].name, LEN_GVAR_NAME, ZCHAR);
lcdDrawText(BITMAP_X+FW, BITMAP_Y+2*FH+3, PSTR("["), BOLD);
drawGVarValue(BITMAP_X+2*FW, BITMAP_Y+2*FH+3, gvarLastChanged, GVAR_VALUE(gvarLastChanged, getGVarFlightMode(mixerCurrentFlightMode, gvarLastChanged)), LEFT|BOLD);
lcdDrawText(lcdLastPos, BITMAP_Y+2*FH+3, PSTR("]"), BOLD);
}
#endif
}
+244
View File
@@ -0,0 +1,244 @@
/*
* 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 STATS_1ST_COLUMN FW/2
#define STATS_2ND_COLUMN 12*FW+FW/2
#define STATS_3RD_COLUMN 24*FW+FW/2
#define STATS_LABEL_WIDTH 4*FW
void menuStatisticsView(uint8_t event)
{
TITLE(STR_MENUSTAT);
switch(event)
{
case EVT_KEY_FIRST(KEY_UP):
chainMenu(menuStatisticsDebug);
break;
case EVT_KEY_LONG(KEY_MENU):
g_eeGeneral.globalTimer = 0;
storageDirty(EE_GENERAL);
sessionTimer = 0;
break;
case EVT_KEY_FIRST(KEY_EXIT):
chainMenu(menuMainView);
break;
}
// Session and Total timers
lcdDrawText(STATS_1ST_COLUMN, FH*1+1, "SES", BOLD);
putsTimer(STATS_1ST_COLUMN + STATS_LABEL_WIDTH, FH*1+1, sessionTimer, 0, 0);
lcdDrawText(STATS_1ST_COLUMN, FH*2+1, "TOT", BOLD);
putsTimer(STATS_1ST_COLUMN + STATS_LABEL_WIDTH, FH*2+1, g_eeGeneral.globalTimer + sessionTimer, TIMEHOUR, 0);
// Throttle special timers
lcdDrawText(STATS_2ND_COLUMN, FH*0+1, "THR", BOLD);
putsTimer(STATS_2ND_COLUMN + STATS_LABEL_WIDTH, FH*0+1, s_timeCumThr, 0, 0);
lcdDrawText(STATS_2ND_COLUMN, FH*1+1, "TH%", BOLD);
putsTimer(STATS_2ND_COLUMN + STATS_LABEL_WIDTH, FH*1+1, s_timeCum16ThrP/16, 0, 0);
// Timers
for (int i=0; i<TIMERS; i++) {
drawStringWithIndex(STATS_3RD_COLUMN, FH*i+1, "TM", i+1, BOLD);
if (timersStates[i].val > 3600)
putsTimer(STATS_3RD_COLUMN + STATS_LABEL_WIDTH, FH*i+1, timersStates[i].val, TIMEHOUR, 0);
else
putsTimer(STATS_3RD_COLUMN + STATS_LABEL_WIDTH, FH*i+1, timersStates[i].val, 0, 0);
}
#if defined(THRTRACE)
const coord_t x = 5;
const coord_t y = 60;
lcdDrawSolidHorizontalLine(x-3, y, MAXTRACE+3+3);
lcdDrawSolidVerticalLine(x, y-32, 32+3);
for (coord_t i=0; i<MAXTRACE; i+=6) {
lcdDrawSolidVerticalLine(x+i+6, y-1, 3);
}
uint16_t traceRd = s_traceWr > MAXTRACE ? s_traceWr - MAXTRACE : 0;
for (coord_t i=1; i<=MAXTRACE && traceRd<s_traceWr; i++, traceRd++) {
uint8_t h = s_traceBuf[traceRd % MAXTRACE];
lcdDrawSolidVerticalLine(x+i, y-h, h);
}
#endif
}
#define MENU_DEBUG_COL1_OFS (11*FW-2)
#define MENU_DEBUG_Y_MIXMAX (2*FH-3)
#define MENU_DEBUG_Y_LUA (3*FH-2)
#define MENU_DEBUG_Y_FREE_RAM (4*FH-1)
#define MENU_DEBUG_Y_USB (5*FH)
#define MENU_DEBUG_Y_RTOS (6*FH)
#if defined(USB_SERIAL)
extern uint16_t usbWraps;
extern uint16_t charsWritten;
extern "C" volatile uint32_t APP_Rx_ptr_in;
#endif
void menuStatisticsDebug(uint8_t event)
{
TITLE(STR_MENUDEBUG);
#if defined(WATCHDOG_TEST)
if (warningResult) {
warningResult = 0;
// do a user requested watchdog test
TRACE("Performing watchdog test");
pausePulses();
}
#endif
switch(event)
{
case EVT_KEY_LONG(KEY_ENTER):
g_eeGeneral.globalTimer = 0;
storageDirty(EE_GENERAL);
sessionTimer = 0;
killEvents(event);
break;
case EVT_KEY_FIRST(KEY_ENTER):
#if defined(LUA)
maxLuaInterval = 0;
maxLuaDuration = 0;
#endif
maxMixerDuration = 0;
break;
#if defined(DEBUG_TRACE_BUFFER)
case EVT_KEY_FIRST(KEY_UP):
pushMenu(menuTraceBuffer);
return;
#endif
case EVT_KEY_FIRST(KEY_DOWN):
chainMenu(menuStatisticsView);
break;
case EVT_KEY_FIRST(KEY_EXIT):
chainMenu(menuMainView);
break;
#if defined(WATCHDOG_TEST)
case EVT_KEY_LONG(KEY_MENU):
{
POPUP_CONFIRMATION("Test the watchdog?");
const char * w = "The radio will reset!";
SET_WARNING_INFO(w, strlen(w), 0);
}
break;
#endif
}
lcd_putsLeft(MENU_DEBUG_Y_FREE_RAM, "Free Mem");
lcdDrawNumber(MENU_DEBUG_COL1_OFS, MENU_DEBUG_Y_FREE_RAM, availableMemory(), LEFT);
lcdDrawText(lcdLastPos, MENU_DEBUG_Y_FREE_RAM, "b");
#if defined(LUA)
lcd_putsLeft(MENU_DEBUG_Y_LUA, "Lua scripts");
lcdDrawText(MENU_DEBUG_COL1_OFS, MENU_DEBUG_Y_LUA+1, "[Duration]", SMLSIZE);
lcdDrawNumber(lcdLastPos, MENU_DEBUG_Y_LUA, 10*maxLuaDuration, LEFT);
lcdDrawText(lcdLastPos+2, MENU_DEBUG_Y_LUA+1, "[Interval]", SMLSIZE);
lcdDrawNumber(lcdLastPos, MENU_DEBUG_Y_LUA, 10*maxLuaInterval, LEFT);
#endif
lcd_putsLeft(MENU_DEBUG_Y_MIXMAX, STR_TMIXMAXMS);
lcdDrawNumber(MENU_DEBUG_COL1_OFS, MENU_DEBUG_Y_MIXMAX, DURATION_MS_PREC2(maxMixerDuration), PREC2|LEFT);
lcdDrawText(lcdLastPos, MENU_DEBUG_Y_MIXMAX, "ms");
#if !defined(SIMU) && defined(USB_SERIAL)
lcd_putsLeft(MENU_DEBUG_Y_USB, "Usb");
lcdDrawNumber(MENU_DEBUG_COL1_OFS, MENU_DEBUG_Y_USB, charsWritten, LEFT);
lcdDrawText(lcdLastPos, MENU_DEBUG_Y_USB, " ");
lcdDrawNumber(lcdLastPos, MENU_DEBUG_Y_USB, APP_Rx_ptr_in, LEFT);
lcdDrawText(lcdLastPos, MENU_DEBUG_Y_USB, " ");
lcdDrawNumber(lcdLastPos, MENU_DEBUG_Y_USB, usbWraps, LEFT);
#endif
lcd_putsLeft(MENU_DEBUG_Y_RTOS, STR_FREESTACKMINB);
lcdDrawText(MENU_DEBUG_COL1_OFS, MENU_DEBUG_Y_RTOS+1, "[M]", SMLSIZE);
lcdDrawNumber(lcdLastPos, MENU_DEBUG_Y_RTOS, menusStack.available(), LEFT);
lcdDrawText(lcdLastPos+2, MENU_DEBUG_Y_RTOS+1, "[X]", SMLSIZE);
lcdDrawNumber(lcdLastPos, MENU_DEBUG_Y_RTOS, mixerStack.available(), LEFT);
lcdDrawText(lcdLastPos+2, MENU_DEBUG_Y_RTOS+1, "[A]", SMLSIZE);
lcdDrawNumber(lcdLastPos, MENU_DEBUG_Y_RTOS, audioStack.available(), LEFT);
lcdDrawText(lcdLastPos+2, MENU_DEBUG_Y_RTOS+1, "[I]", SMLSIZE);
lcdDrawNumber(lcdLastPos, MENU_DEBUG_Y_RTOS, stackAvailable(), LEFT);
lcdDrawText(3*FW, 7*FH+1, STR_MENUTORESET);
lcdInvertLastLine();
}
#if defined(DEBUG_TRACE_BUFFER)
#include "stamp-opentx.h"
void menuTraceBuffer(uint8_t event)
{
switch(event)
{
case EVT_KEY_LONG(KEY_ENTER):
dumpTraceBuffer();
killEvents(event);
break;
}
SIMPLE_SUBMENU("Trace Buffer " VERSION, TRACE_BUFFER_LEN);
/* RTC time */
struct gtm t;
gettime(&t);
putsTime(LCD_W+1, 0, t, TIMEBLINK);
uint8_t y = 0;
uint8_t k = 0;
int8_t sub = menuVerticalPosition;
lcdDrawChar(0, FH, '#');
lcdDrawText(4*FW, FH, "Time");
lcdDrawText(14*FW, FH, "Event");
lcdDrawText(20*FW, FH, "Data");
for (uint8_t i=0; i<LCD_LINES-2; i++) {
y = 1 + (i+2)*FH;
k = i+menuVerticalOffset;
//item
lcdDrawNumber(0, 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(4*FW, y, tme, TIMEHOUR|LEFT, TIMEHOUR|LEFT);
//event
lcdDrawNumber(14*FW, y, te->event, LEADING0|LEFT, 3);
//data
lcdDrawSizedText (20*FW, y, "0x", 2);
lcdDrawHexNumber(22*FW-2, y, (uint16_t)(te->data >> 16));
lcdDrawHexNumber(25*FW, y, (uint16_t)(te->data & 0xFFFF));
}
}
}
#endif //#if defined(DEBUG_TRACE_BUFFER)
+259
View File
@@ -0,0 +1,259 @@
/*
* 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 STATUS_BAR_Y (7*FH+1)
#define TELEM_2ND_COLUMN (11*FW)
uint8_t s_frsky_view = 0;
#define BAR_LEFT 30
#define BAR_WIDTH 152
void displayRssiLine()
{
if (TELEMETRY_STREAMING()) {
lcdDrawSolidHorizontalLine(0, 55, 212, 0); // separator
uint8_t rssi = min((uint8_t)99, TELEMETRY_RSSI());
lcdDrawSizedText(0, STATUS_BAR_Y, STR_RX, 2);
lcdDrawNumber(4*FW, STATUS_BAR_Y, rssi, LEADING0|RIGHT, 2);
lcdDrawRect(BAR_LEFT, 57, 78, 7);
lcdDrawFilledRect(BAR_LEFT+1, 58, 19*rssi/25, 5, (rssi < getRssiAlarmValue(0)) ? DOTTED : SOLID);
}
else {
lcdDrawText(7*FW, STATUS_BAR_Y, STR_NODATA, BLINK);
lcdInvertLastLine();
}
}
int barCoord(int value, int min, int max)
{
if (value <= min)
return 0;
else if (value >= max)
return BAR_WIDTH-1;
else
return ((BAR_WIDTH-1) * (value - min)) / (max - min);
}
void displayGaugesTelemetryScreen(FrSkyScreenData & screen)
{
// Custom Screen with gauges
int barHeight = 5;
for (int i=3; i>=0; i--) {
FrSkyBarData & bar = screen.bars[i];
source_t source = bar.source;
getvalue_t barMin = bar.barMin;
getvalue_t barMax = bar.barMax;
if (source <= MIXSRC_LAST_CH) {
barMin = calc100toRESX(barMin);
barMax = calc100toRESX(barMax);
}
if (source && barMax > barMin) {
int y = barHeight+6+i*(barHeight+6);
putsMixerSource(0, y+barHeight-5, source, 0);
lcdDrawRect(BAR_LEFT, y, BAR_WIDTH+1, barHeight+2);
getvalue_t value = getValue(source);
putsChannel(BAR_LEFT+2+BAR_WIDTH, y+barHeight-5, source, LEFT);
uint8_t thresholdX = 0;
int width = barCoord(value, barMin, barMax);
uint8_t barShade = SOLID;
lcdDrawFilledRect(BAR_LEFT+1, y+1, width, barHeight, barShade);
for (uint8_t j=24; j<99; j+=25) {
if (j>thresholdX || j>width) {
lcdDrawSolidVerticalLine(j*BAR_WIDTH/100+BAR_LEFT+1, y+1, barHeight);
}
}
if (thresholdX) {
lcdDrawVerticalLine(BAR_LEFT+1+thresholdX, y-2, barHeight+3, DOTTED);
lcdDrawSolidHorizontalLine(BAR_LEFT+thresholdX, y-2, 3);
}
}
else {
barHeight += 2;
}
}
displayRssiLine();
}
bool displayNumbersTelemetryScreen(FrSkyScreenData & screen)
{
// Custom Screen with numbers
const coord_t pos[] = {0, 71, 143, 214};
uint8_t fields_count = 0;
for (uint8_t i=0; i<4; i++) {
for (uint8_t j=0; j<NUM_LINE_ITEMS; j++) {
source_t field = screen.lines[i].sources[j];
if (field > 0) {
fields_count++;
}
if (i==3) {
lcdDrawSolidVerticalLine(69, 8, 48);
lcdDrawSolidVerticalLine(141, 8, 48);
if (!TELEMETRY_STREAMING()) {
displayRssiLine();
return fields_count;
}
}
if (field) {
coord_t x = pos[j+1]-2;
coord_t y = (i==3 ? 1+FH+2*FH*i:FH+2*FH*i);
LcdFlags att = RIGHT | (i==3 ? NO_UNIT : DBLSIZE|NO_UNIT);
if (field >= MIXSRC_FIRST_TIMER && field <= MIXSRC_LAST_TIMER && i!=3) {
// there is not enough space on LCD for displaying "Tmr1" or "Tmr2" and still see the - sign, we write "T1" or "T2" instead
drawStringWithIndex(pos[j], 1+FH+2*FH*i, "T", field-MIXSRC_FIRST_TIMER+1, 0);
if (timersStates[field-MIXSRC_FIRST_TIMER].val > 3600) {
att += TIMEHOUR - DBLSIZE;
x -= 3*FW;
y += FH/2;
}
}
else if (field >= MIXSRC_FIRST_TELEM && isGPSSensor(1+(field-MIXSRC_FIRST_TELEM)/3) && telemetryItems[(field-MIXSRC_FIRST_TELEM)/3].isAvailable()) {
// we don't display GPS name, no space for it
}
else {
putsMixerSource(pos[j], 1+FH+2*FH*i, field, 0);
}
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()) {
continue;
}
else if (telemetryItem.isOld()) {
att |= INVERS|BLINK;
}
}
putsChannel(x, y, field, att);
}
}
}
lcdInvertLastLine();
return fields_count;
}
void displayCustomTelemetryScreen(uint8_t index)
{
FrSkyScreenData & screen = g_model.frsky.screens[index];
if (IS_BARS_SCREEN(s_frsky_view)) {
return displayGaugesTelemetryScreen(screen);
}
displayNumbersTelemetryScreen(screen);
}
bool displayTelemetryScreen()
{
#if defined(LUA)
if (TELEMETRY_SCREEN_TYPE(s_frsky_view) == TELEMETRY_SCREEN_TYPE_SCRIPT) {
uint8_t state = isTelemetryScriptAvailable(s_frsky_view);
switch (state) {
case SCRIPT_OK:
return true; // contents will be drawed by Lua Task
case SCRIPT_NOFILE:
return false; // requested lua telemetry screen not available
case SCRIPT_SYNTAX_ERROR:
case SCRIPT_PANIC:
case SCRIPT_KILLED:
luaError(state, false);
return true;
}
return false;
}
#endif
if (TELEMETRY_SCREEN_TYPE(s_frsky_view) == TELEMETRY_SCREEN_TYPE_NONE) {
return false;
}
lcdDrawTelemetryTopBar();
if (s_frsky_view < MAX_TELEMETRY_SCREENS) {
displayCustomTelemetryScreen(s_frsky_view);
}
return true;
}
enum NavigationDirection {
none,
up,
down
};
#define decrTelemetryScreen() direction = up
#define incrTelemetryScreen() direction = down
void menuTelemetryFrsky(uint8_t event)
{
enum NavigationDirection direction = none;
switch (event) {
case EVT_KEY_FIRST(KEY_EXIT):
case EVT_KEY_LONG(KEY_EXIT):
killEvents(event);
chainMenu(menuMainView);
break;
case EVT_KEY_LONG(KEY_PAGE):
killEvents(event);
// no break;
case EVT_KEY_FIRST(KEY_UP):
decrTelemetryScreen();
break;
case EVT_KEY_BREAK(KEY_PAGE):
case EVT_KEY_FIRST(KEY_DOWN):
incrTelemetryScreen();
break;
case EVT_KEY_LONG(KEY_ENTER):
killEvents(event);
POPUP_MENU_ADD_ITEM(STR_RESET_TELEMETRY);
POPUP_MENU_ADD_ITEM(STR_RESET_FLIGHT);
POPUP_MENU_START(onMainViewMenu);
break;
}
for (int i=0; i<=TELEMETRY_SCREEN_TYPE_MAX; i++) {
if (direction == up) {
if (s_frsky_view-- == 0)
s_frsky_view = TELEMETRY_VIEW_MAX;
}
else if (direction == down) {
if (s_frsky_view++ == TELEMETRY_VIEW_MAX)
s_frsky_view = 0;
}
else {
direction = down;
}
if (displayTelemetryScreen()) {
return;
}
}
lcdDrawTelemetryTopBar();
lcdDrawText(8*FW, 3*FH, "No Telemetry Screens");
displayRssiLine();
}
+153
View File
@@ -0,0 +1,153 @@
/*
* 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[LCD_LINES-1][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[4] = {0};
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<LCD_LINES-1); i++) {
if (c == '\n') {
++current_line;
line_length = 0;
escape = 0;
}
else if (c!='\r' && current_line>=menuVerticalOffset && current_line-menuVerticalOffset<LCD_LINES-1 && line_length<LCD_COLS) {
if (c=='\\' && escape==0) {
escape = 1;
continue;
}
else if (c!='\\' && escape>0 && escape<4) {
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 if (escape == 3) {
int val = atoi(escape_chars);
if (val >= 200 && val < 225) {
c = '\200' + val-200;
}
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;
}
}
void menuTextView(uint8_t event)
{
static int lines_count;
switch (event) {
case EVT_ENTRY:
menuVerticalOffset = 0;
lines_count = 0;
readTextFile(lines_count);
break;
case EVT_KEY_FIRST(KEY_UP):
if (menuVerticalOffset == 0)
break;
else
menuVerticalOffset--;
readTextFile(lines_count);
break;
case EVT_KEY_FIRST(KEY_DOWN):
if (menuVerticalOffset+LCD_LINES-1 >= lines_count)
break;
else
++menuVerticalOffset;
readTextFile(lines_count);
break;
case EVT_KEY_BREAK(KEY_EXIT):
popMenu();
break;
}
for (int i=0; i<LCD_LINES-1; i++) {
lcdDrawText(0, i*FH+FH+1, s_text_screen[i], FIXEDWIDTH);
}
char *title = s_text_file;
#if defined(SIMU)
if (!strncmp(title, "./", 2)) title += 2;
#else
// TODO?
#endif
lcdDrawText(LCD_W/2-strlen(title)*FW/2, 0, title);
lcdInvertLine(0);
if (lines_count > LCD_LINES-1) {
drawVerticalScrollbar(LCD_W-1, FH, LCD_H-FH, menuVerticalOffset, lines_count, LCD_LINES-1);
}
}
void pushMenuTextView(const char *filename)
{
if (strlen(filename) < TEXT_FILENAME_MAXLEN) {
strcpy(s_text_file, filename);
pushMenu(menuTextView);
}
}
+267
View File
@@ -0,0 +1,267 @@
/*
* 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 pm_uchar bmp_sleep[] PROGMEM = {
#include "../../bitmaps/212x64/sleep.lbm"
};
#define SLEEP_BITMAP_WIDTH 60
#define SLEEP_BITMAP_HEIGHT 60
void drawSleepBitmap()
{
lcdClear();
lcdDrawBitmap((LCD_W-SLEEP_BITMAP_WIDTH)/2, (LCD_H-SLEEP_BITMAP_HEIGHT)/2, bmp_sleep, 0, SLEEP_BITMAP_WIDTH);
lcdRefresh();
}
#if defined(PWR_BUTTON_DELAY)
const pm_uchar bmp_shutdown[] PROGMEM = {
#include "../../bitmaps/212x64/shutdown.lbm"
};
#define SHUTDOWN_BITMAP_WIDTH 60
#define SHUTDOWN_BITMAP_HEIGHT 60
void drawShutdownBitmap(uint32_t index)
{
index /= (PWR_PRESS_SHUTDOWN / 4);
lcdRefreshWait();
lcdClear();
lcdDrawBitmap((LCD_W-SHUTDOWN_BITMAP_WIDTH)/2, (LCD_H-SHUTDOWN_BITMAP_HEIGHT)/2, bmp_shutdown, index * SHUTDOWN_BITMAP_WIDTH, SHUTDOWN_BITMAP_WIDTH);
lcdRefresh();
}
#endif
void drawStick(coord_t centrex, int16_t xval, int16_t yval)
{
#define BOX_CENTERY (LCD_H-BOX_WIDTH/2-10)
#define MARKER_WIDTH 5
lcdDrawSquare(centrex-BOX_WIDTH/2, BOX_CENTERY-BOX_WIDTH/2, BOX_WIDTH);
lcdDrawSolidVerticalLine(centrex, BOX_CENTERY-1, 3);
lcdDrawSolidHorizontalLine(centrex-1, BOX_CENTERY, 3);
lcdDrawSquare(centrex + (xval/((2*RESX)/(BOX_WIDTH-MARKER_WIDTH))) - MARKER_WIDTH/2, BOX_CENTERY - (yval/((2*RESX)/(BOX_WIDTH-MARKER_WIDTH))) - MARKER_WIDTH/2, MARKER_WIDTH, ROUND);
#undef BOX_CENTERY
#undef MARKER_WIDTH
}
void drawColumnHeader(const char * const *headers, uint8_t index)
{
lcdDrawText(17*FW, 0, headers[index], 0);
}
void drawCheckBox(coord_t x, coord_t y, uint8_t value, LcdFlags attr)
{
if (value)
lcdDrawChar(x+1, y, '#');
if (attr)
lcdDrawFilledRect(x, y, 7, 7);
else
lcdDrawSquare(x, y, 7);
}
void drawScreenIndex(uint8_t index, uint8_t count, uint8_t attr)
{
lcdDrawNumber(LCD_W, 0, count, RIGHT | attr);
coord_t x = 1+LCD_W-FW*(count>9 ? 3 : 2);
lcdDrawChar(x, 0, '/', attr);
lcdDrawNumber(x, 0, index+1, RIGHT | attr);
}
void drawVerticalScrollbar(coord_t x, coord_t y, coord_t h, uint16_t offset, uint16_t count, uint8_t visible)
{
lcdDrawVerticalLine(x, y, h, DOTTED);
coord_t yofs = (h * offset) / count;
coord_t yhgt = (h * visible) / count;
if (yhgt + yofs > h)
yhgt = h - yofs;
lcdDrawVerticalLine(x, y + yofs, yhgt, SOLID, FORCE);
}
void drawProgressBar(const char *label)
{
lcd_putsLeft(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;
lcdDrawSolidHorizontalLine(5, 6*FH+6, width, FORCE);
lcdDrawSolidHorizontalLine(5, 6*FH+7, width, FORCE);
lcdDrawSolidHorizontalLine(5, 6*FH+8, width, FORCE);
lcdRefresh();
}
}
void drawGauge(coord_t x, coord_t y, coord_t w, coord_t h, int32_t val, int32_t max)
{
lcdDrawRect(x, y, w+1, h);
lcdDrawFilledRect(x+1, y+1, w-1, 4, SOLID, ERASE);
coord_t len = limit((uint8_t)1, uint8_t((abs(val) * w/2 + max/2) / max), uint8_t(w/2));
coord_t x0 = (val>0) ? x+w/2 : x+1+w/2-len;
for (coord_t i=h-2; i>0; i--) {
lcdDrawSolidHorizontalLine(x0, y+i, len);
}
}
void title(const pm_char * s)
{
lcdDrawText(0, 0, s, INVERS);
}
select_menu_value_t selectMenuItem(coord_t x, coord_t y, const pm_char *label, const pm_char *values, select_menu_value_t value, select_menu_value_t min, select_menu_value_t max, LcdFlags attr, uint8_t event)
{
lcd_putsColumnLeft(x, y, label);
if (values) lcdDrawTextAtIndex(x, y, values, value-min, attr);
if (attr) value = checkIncDec(event, value, min, max, (menuVerticalPositions[0] == 0) ? EE_MODEL : EE_GENERAL);
return value;
}
uint8_t editCheckBox(uint8_t value, coord_t x, coord_t y, const pm_char *label, LcdFlags attr, uint8_t event )
{
drawCheckBox(x, y, value, attr);
return selectMenuItem(x, y, label, NULL, value, 0, 1, attr, event);
}
swsrc_t switchMenuItem(coord_t x, coord_t y, swsrc_t value, LcdFlags attr, uint8_t event)
{
lcd_putsColumnLeft(x, y, STR_SWITCH);
putsSwitches(x, y, value, attr);
if (attr) CHECK_INCDEC_MODELSWITCH(event, value, SWSRC_FIRST_IN_MIXES, SWSRC_LAST_IN_MIXES, isSwitchAvailableInMixes);
return value;
}
void drawSlider(coord_t x, coord_t y, uint8_t value, uint8_t max, uint8_t attr)
{
lcdDrawChar(x+(value*4*FW)/max, y, '$');
lcdDrawSolidHorizontalLine(x, y+3, 5*FW-1, FORCE);
if (attr && (!(attr & BLINK) || !BLINK_ON_PHASE)) lcdDrawFilledRect(x, y, 5*FW-1, FH-1);
}
#if defined(GVARS)
bool noZero(int val)
{
return val != 0;
}
void drawGVarName(coord_t x, coord_t y, int8_t index, LcdFlags flags)
{
if (ZEXIST(g_model.gvars[index].name))
lcdDrawSizedText(x, y, g_model.gvars[index].name, LEN_GVAR_NAME, ZCHAR|flags);
else
drawStringWithIndex(x, y, STR_GV, index+1, flags);
}
void drawGVarValue(coord_t x, coord_t y, uint8_t gvar, gvar_t value, LcdFlags flags)
{
uint8_t prec = g_model.gvars[gvar].prec;
if (prec > 0) {
flags |= (prec == 1 ? PREC1 : PREC2);
}
putsValueWithUnit(x, y, value, g_model.gvars[gvar].unit ? UNIT_PERCENT : UNIT_RAW, flags);
}
int16_t editGVarFieldValue(coord_t x, coord_t y, int16_t value, int16_t min, int16_t max, LcdFlags attr, uint8_t editflags, uint8_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)) {
if (attr & RIGHT)
x -= 2*FW+FWNUM;
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;
lcdDrawChar(x-6, y, '-', attr);
}
else {
value = (int16_t) GV_CALC_VALUE_IDX_POS(idx-1, delta);
}
drawGVarName(x, y, idx-1, attr);
}
else {
lcdDrawNumber(x, y, value, attr);
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, uint8_t event)
{
lcdDrawNumber(x, y, value, attr);
if (attr&INVERS) value = checkIncDec(event, value, min, max, EE_MODEL);
return value;
}
#endif
#if defined(SDCARD)
char statusLineMsg[STATUS_LINE_LENGTH];
tmr10ms_t statusLineTime = 0;
uint8_t statusLineHeight = 0;
void showStatusLine()
{
statusLineTime = get_tmr10ms();
}
#define STATUS_LINE_DELAY (3 * 100) /* 3s */
void drawStatusLine()
{
if (statusLineTime) {
if ((tmr10ms_t)(get_tmr10ms() - statusLineTime) <= (tmr10ms_t)STATUS_LINE_DELAY) {
if (statusLineHeight < FH) statusLineHeight++;
}
else if (statusLineHeight) {
statusLineHeight--;
}
else {
statusLineTime = 0;
}
lcdDrawFilledRect(0, LCD_H-statusLineHeight, LCD_W, FH, SOLID, ERASE);
lcdDrawText(5, LCD_H+1-statusLineHeight, statusLineMsg, 0);
lcdDrawFilledRect(0, LCD_H-statusLineHeight, LCD_W, FH, SOLID);
}
}
#endif