mirror of
https://github.com/clockworkpi/PicoCalc.git
synced 2025-12-12 18:28:53 +01:00
2426 lines
107 KiB
C
2426 lines
107 KiB
C
/***********************************************************************************************************************
|
|
PicoMite MMBasic
|
|
|
|
GUI.c
|
|
|
|
<COPYRIGHT HOLDERS> Geoff Graham, Peter Mather
|
|
Copyright (c) 2021, <COPYRIGHT HOLDERS> All rights reserved.
|
|
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
|
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
|
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
|
|
in the documentation and/or other materials provided with the distribution.
|
|
3. The name MMBasic be used when referring to the interpreter in any documentation and promotional material and the original copyright message be displayed
|
|
on the console at startup (additional copyright messages may be added).
|
|
4. All advertising materials mentioning features or use of this software must display the following acknowledgement: This product includes software developed
|
|
by the <copyright holder>.
|
|
5. Neither the name of the <copyright holder> nor the names of its contributors may be used to endorse or promote products derived from this software
|
|
without specific prior written permission.
|
|
THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDERS> AS IS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDERS> BE LIABLE FOR ANY DIRECT,
|
|
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
************************************************************************************************************************/
|
|
/**
|
|
* @file GUI.c
|
|
* @author Geoff Graham, Peter Mather
|
|
* @brief Source for GUI MMBasic commands and functions
|
|
*/
|
|
/**
|
|
* @cond
|
|
* The following section will be excluded from the documentation.
|
|
*/
|
|
|
|
|
|
#include "MMBasic_Includes.h"
|
|
#include "Hardware_Includes.h"
|
|
#include "float.h"
|
|
#define BTN_SIDE_BRIGHT -25
|
|
#define BTN_SIDE_DULL -50
|
|
#define BTN_SIDE_WIDTH 4
|
|
#define BTN_CAPTION_SHIFT 2
|
|
|
|
#define CLICK_DURATION 3 // the duration of a "click" in mSec
|
|
|
|
#define CTRL_NORMAL 0b0000000 // the control should be displayed as normal
|
|
#define CTRL_DISABLED 0b0000001 // the control is disabled and displayed in dull colours
|
|
#define CTRL_DISABLED2 0b0000010 // as above but only used when a keyboard is active
|
|
#define CTRL_HIDDEN 0b0000100 // the control is hidden
|
|
#define CTRL_HIDDEN2 0b0001000 // only used when setting a control to hidden
|
|
#define CTRL_SPINUP 0b0010000 // the spinbox up arrow is touched
|
|
#define CTRL_SPINDOWN 0b0100000 // ditto down
|
|
#define CTRL_SELECTED 0b1000000 // for a textbox or numberbox indicated that the box is selected
|
|
|
|
#define BTN_DISABLED -55
|
|
|
|
// define what the function DrawKeyboard() will do
|
|
#define KEY_OPEN 1
|
|
#define KEY_DRAW_ALL 2
|
|
#define KEY_KEY_DWN 3
|
|
#define KEY_KEY_UP 4
|
|
#define KEY_KEY_CANCEL 5
|
|
|
|
#define CTRL_BUTTON 1
|
|
#define CTRL_SWITCH 2
|
|
#define CTRL_RADIOBTN 3
|
|
#define CTRL_CHECKBOX 4
|
|
#define CTRL_LED 5
|
|
#define CTRL_SPINNER 6
|
|
#define CTRL_FRAME 7
|
|
#define CTRL_NBRBOX 8
|
|
#define CTRL_TEXTBOX 9
|
|
#define CTRL_FMTBOX 10
|
|
#define CTRL_DISPLAYBOX 11
|
|
#define CTRL_CAPTION 12
|
|
#define CTRL_AREA 13
|
|
#define CTRL_GAUGE 14
|
|
#define CTRL_BARGAUGE 15
|
|
|
|
#define MAX_PAGES 32 // the number of pages that can be specifies (this must not exceed 32)
|
|
extern void cmd_guiMX170(void);
|
|
|
|
|
|
// used by the gauge control to store extra data in the allocated string space
|
|
#define GAUGE_UNITS_SIZE 32
|
|
struct s_GaugeS {
|
|
char units[GAUGE_UNITS_SIZE];
|
|
MMFLOAT ta, tb, tc;
|
|
int c1, c2, c3, c4;
|
|
int laststrlen, cval, csaved, lastfc, lastbc;
|
|
};
|
|
|
|
|
|
int SetupPage;
|
|
unsigned int CurrentPages;
|
|
|
|
short gui_font_width, gui_font_height;
|
|
|
|
int display_backlight; // the brightness of the backlight (1 to 100)
|
|
|
|
int gui_click_pin = 0; // the sound pin
|
|
|
|
volatile int CursorTimer; // used to time the flashing cursor
|
|
volatile int ClickTimer = 0; // used to time the click when touch occurs
|
|
volatile int TouchTimer; // used to time the response to touch
|
|
int CheckGuiFlag = 0; // used to tell the mSec timer to call CheckGui()
|
|
|
|
short CurrentRef; // if the pen is down this is the control (or zero if not on a control)
|
|
short LastRef; // this is the last control touched
|
|
short LastX; // this is the x coord when the pen was lifted
|
|
short LastY; // ditto for y
|
|
|
|
MMFLOAT CtrlSavedVal; // a temporary place to save a control's value
|
|
|
|
int TouchX, TouchY;
|
|
volatile bool TouchDown = false;
|
|
volatile bool TouchUp = false;
|
|
volatile bool TouchState = false;
|
|
|
|
|
|
int last_x2, last_y2; // defaults used when creating controls
|
|
MMFLOAT last_inc, last_min, last_max;
|
|
int last_fcolour, last_bcolour;
|
|
MMFLOAT last_ta, last_tb, last_tc;
|
|
int last_c1 = -1, last_c2, last_c3, last_c4;
|
|
char last_units[32];
|
|
|
|
// used for keypads
|
|
int GUIKeyDown = 0; // true if a key is down
|
|
bool KeyAltShift = false; // true if in alt keypad layout
|
|
int InvokingCtrl = 0; // the number of the control that invoked the keypad
|
|
//int InCallback = 0; // true if we are running MM.KEYPRESS
|
|
//int InPause = 0; // true if we are inside a PAUSE command (used to suppress calling MM.KEYPRESS)
|
|
char CancelValue[256]; // save the value of the control in case the user cancels
|
|
|
|
bool gui_int_down=false; // true if the touch has triggered an interrupt
|
|
char *GuiIntDownVector = NULL; // address of the interrupt routine or NULL if no interrupt
|
|
bool gui_int_up=false; // true if the release of the touch has triggered an interrupt
|
|
char *GuiIntUpVector = NULL; // address of the interrupt routine or NULL if no interrupt
|
|
volatile bool DelayedDrawKeyboard = false; // a flag to indicate that the pop-up keyboard should be drawn AFTER the pen down interrupt
|
|
volatile bool DelayedDrawFmtBox = false; // a flag to indicate that the pop-up formatted keyboard should be drawn AFTER the pen down interrupt
|
|
|
|
//struct s_ctrl *Ctrl; // list of the controls
|
|
|
|
|
|
int ChangeBright(int c, int pct);
|
|
void SpecialWritePixel(int x, int y, unsigned int tc, int status);
|
|
void SpecialDrawLine(int x1, int y1, int x2, int y2, int w, int tc, int status);
|
|
void SpecialDrawBox(int x1, int y1, int x2, int y2, int w, int c, int fill, int status);
|
|
void SpecialDrawRBox(int x1, int y1, int x2, int y2, int radius, int c, int fill, int status);
|
|
void SpecialPrintString(int x, int y, int jh, int jv, int jo, int fc, int bc, char *str, int status);
|
|
void SpecialDrawCircle(int x, int y, int radius, int w, int c, int fill, MMFLOAT aspect, int status);
|
|
void SpecialDrawTriangle(int x0, int y0, int x1, int y1, int x2, int y2, int c, int fill, int status);
|
|
void DrawBorder(int x1, int y1, int x2, int y2, int w, int tc, int bc, int status);
|
|
void DrawBasicButton(int x1, int y1, int x2, int y2, int w, int up, int c, int status);
|
|
void DrawButton(int r);
|
|
void DrawSwitch(int r);
|
|
void DrawCheckBox(int r);
|
|
void DrawRadioBtn(int r);
|
|
void DrawLED(int r);
|
|
void DrawSpinner(int r);
|
|
void DrawFrame(int r);
|
|
void DrawCaption(int r);
|
|
void DrawGauge(int r);
|
|
void DrawBarGauge(int r);
|
|
char *GetCaption(int k, int is_alpha, int alt);
|
|
void GetSingleKeyCoord(int k, int is_alpha, int *x1, int *y1, int *x2, int *y2);
|
|
void DrawDisplayBox(int r);
|
|
void PopUpRedrawAll(int r, int disabled);
|
|
void KeyPadErase(int is_alpha);
|
|
void DrawSingleKey(int is_alpha, int x1, int y1, int x2, int y2, char *s, int fc, int bc);
|
|
void DrawKeyboard(int mode);
|
|
void DrawControl(int r);
|
|
void UpdateControl(int r);
|
|
void SetCtrlState(int r, int state, int err);
|
|
void DoCallback(int InvokingCtrl, char *key);
|
|
void DrawFmtBox(int mode);
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// The GUI command is the base of all the sophisticated GUI drawing features in the Micromite Plus
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
/*********************************************************************************************
|
|
Most controls have similar argument lists, so this function is a "universal" argument
|
|
collector for all controls.
|
|
It returns with the control's reference number in case the caller wants to make some
|
|
more adjustments.
|
|
********************************************************************************************/
|
|
int GetCtrlParams(int type, unsigned char *p) {
|
|
int r, a;
|
|
struct s_GaugeS *GaugeS; // in case we are dealing with a GAUGE control
|
|
if(!Option.MaxCtrls)error("No memory allocated for GUI controls");
|
|
getargs(&p, 40, (unsigned char *)",");
|
|
if((argc & 1) != 1) error("Argument count");
|
|
if(*argv[0] == '#') argv[0]++;
|
|
r = getint(argv[0], 1, Option.MaxCtrls - 1);
|
|
if(Ctrl[r].type) error("GUI reference number #% is in use", r);
|
|
if(argc < 5) error("Argument count");
|
|
a = 0;
|
|
|
|
// setup the defaults
|
|
Ctrl[r].x2 = last_x2; Ctrl[r].y2 = last_y2;
|
|
if(type == CTRL_CAPTION) {
|
|
Ctrl[r].fc = gui_fcolour; // a caption defaults to the system colours set by the COLOUR command
|
|
Ctrl[r].bc = gui_bcolour;
|
|
} else {
|
|
Ctrl[r].fc = last_fcolour; // the others take the last colours used in the last command
|
|
Ctrl[r].bc = last_bcolour;
|
|
}
|
|
Ctrl[r].inc = last_inc; Ctrl[r].min = last_min; Ctrl[r].max = last_max;
|
|
|
|
// save the current font, needed if we redraw the control
|
|
Ctrl[r].font = gui_font;
|
|
|
|
// get string space
|
|
Ctrl[r].s = GetMemory(MAXSTRLEN);
|
|
|
|
// and the caption if the control needs it
|
|
switch(type) { // these need a caption
|
|
case CTRL_CAPTION:
|
|
case CTRL_BUTTON:
|
|
case CTRL_LED:
|
|
case CTRL_SWITCH:
|
|
case CTRL_FRAME:
|
|
case CTRL_RADIOBTN:
|
|
case CTRL_CHECKBOX: strcpy((char *)Ctrl[r].s, (char *)getCstring(argv[a += 2]));
|
|
}
|
|
|
|
if(type == CTRL_FMTBOX) {
|
|
// get string space
|
|
a += 2;
|
|
if(*argv[a] == '"') {
|
|
Ctrl[r].fmt = GetMemory(MAXSTRLEN);
|
|
strcpy((char *)Ctrl[r].fmt, (char *)getCstring(argv[a]));
|
|
} else {
|
|
// Format String:
|
|
// xc where x is the maximum value digit and c is the ghost char
|
|
// these are concatenated to make a string of digits (eg, seconds = 5s9s)
|
|
// Special constructs: 1 to 32 = 3dDd 1 to 12 = 1mMm
|
|
// 1 to 24 = 2hHh 0 to 179 = 1dLd9d
|
|
// A separator is in brackets. Eg (:) or (' )
|
|
// Special chars: A = AM/PM N = N/S E = E/W
|
|
if(checkstring(argv[a], (unsigned char *)"DATE1")) Ctrl[r].fmt = (unsigned char *)"3dDd(/)1mMm(/)9y9y";
|
|
if(checkstring(argv[a], (unsigned char *)"DATE2")) Ctrl[r].fmt = (unsigned char *)"1mMm(/)3dDd(/)9y9y";
|
|
if(checkstring(argv[a], (unsigned char *)"DATE3")) Ctrl[r].fmt = (unsigned char *)"9y9y9y9y(/)1mMm(/)3dDd";
|
|
if(checkstring(argv[a], (unsigned char *)"TIME1")) Ctrl[r].fmt = (unsigned char *)"2hHh(:)5m9m";
|
|
if(checkstring(argv[a], (unsigned char *)"TIME2")) Ctrl[r].fmt = (unsigned char *)"2hHh(:)5m9m(:)5s9s";
|
|
if(checkstring(argv[a], (unsigned char *)"TIME3")) Ctrl[r].fmt = (unsigned char *)"1hMh(:)5m9m( )A";
|
|
if(checkstring(argv[a], (unsigned char *)"TIME4")) Ctrl[r].fmt = (unsigned char *)"1hMh(:)5m9m(:)5s9s( )A";
|
|
if(checkstring(argv[a], (unsigned char *)"DATETIME1")) Ctrl[r].fmt = (unsigned char *)"3dDd(/)1mMm(/)9y9y( )1hMh(:)5m9m( )A";
|
|
if(checkstring(argv[a], (unsigned char *)"DATETIME2")) Ctrl[r].fmt = (unsigned char *)"3dDd(/)1mMm(/)9y9y( )2hHh(:)5m9m";
|
|
if(checkstring(argv[a], (unsigned char *)"DATETIME3")) Ctrl[r].fmt = (unsigned char *)"1mMm(/)3dDd(/)9y9y( )1hMh(:)5m9m( )A";
|
|
if(checkstring(argv[a], (unsigned char *)"DATETIME4")) Ctrl[r].fmt = (unsigned char *)"1mMm(/)3dDd(/)9y9y( )2hHh(:)5m9m";
|
|
if(checkstring(argv[a], (unsigned char *)"LAT1")) Ctrl[r].fmt = (unsigned char *)"8d9d(` )5m9m(' )5s9s(\" )N";
|
|
if(checkstring(argv[a], (unsigned char *)"LAT2")) Ctrl[r].fmt = (unsigned char *)"8d9d(` )5m9m(' )5s9s(.)9s(\" )N";
|
|
if(checkstring(argv[a], (unsigned char *)"LONG1")) Ctrl[r].fmt = (unsigned char *)"1dLd9d(` )5m9m(' )5s9s(\" )E";
|
|
if(checkstring(argv[a], (unsigned char *)"LONG2")) Ctrl[r].fmt = (unsigned char *)"1dLd9d(` )5m9m(' )5s9s(.)9s(\" )E";
|
|
if(checkstring(argv[a], (unsigned char *)"ANGLE1")) Ctrl[r].fmt = (unsigned char *)"9d9d9d(` )5m9m(')";
|
|
}
|
|
}
|
|
|
|
|
|
// get x1 and y1 - all controls require these
|
|
Ctrl[r].x1 = getint(argv[a += 2], 0, HRes);
|
|
if(argc < a) error("Argument count");
|
|
Ctrl[r].y1 = getint(argv[a += 2], 0, VRes);
|
|
|
|
// the fourth argument in GUI CAPTION is the justification
|
|
if(type == CTRL_CAPTION) {
|
|
a += 2;
|
|
if(!(argc < a || *argv[a] == 0)) { // if justification is specified
|
|
int jh = 0, jv = 0, jo = 0;
|
|
if(!GetJustification((char *)argv[a], &jh, &jv, &jo))
|
|
if(!GetJustification((char *)getCstring(argv[a]), &jh, &jv, &jo))
|
|
error("Justification");
|
|
Ctrl[r].x2 = jh | jv << 2 | jo << 4; // stuff the justification parameters into the short int
|
|
}
|
|
else
|
|
Ctrl[r].x2 = 0;
|
|
} else {
|
|
// get the width - all controls except CAPTION need this
|
|
// for GAUGE, LED, etc this is the radius
|
|
if(argc > a + 2) if(*argv[a += 2] != 0) last_x2 = Ctrl[r].x2 = getint(argv[a], BTN_SIDE_WIDTH, HRes);
|
|
|
|
// now get or set the height
|
|
switch(type) {
|
|
case CTRL_NBRBOX: *Ctrl[r].s = '0'; // this needs to be set, then fall thru
|
|
case CTRL_BUTTON:
|
|
case CTRL_SWITCH:
|
|
case CTRL_FRAME:
|
|
case CTRL_TEXTBOX:
|
|
case CTRL_FMTBOX:
|
|
case CTRL_DISPLAYBOX:
|
|
case CTRL_SPINNER:
|
|
case CTRL_AREA:
|
|
case CTRL_BARGAUGE: // these all need the height in addition to the width
|
|
if(argc > a + 2) if(*argv[a += 2] != 0) last_y2 = Ctrl[r].y2 = getint(argv[a], BTN_SIDE_WIDTH, VRes);
|
|
if(type != CTRL_BARGAUGE) { Ctrl[r].x2 += Ctrl[r].x1; Ctrl[r].y2 += Ctrl[r].y1; }
|
|
break;
|
|
|
|
case CTRL_CHECKBOX: // the check box does not need the height and is a special case
|
|
// its touch sensitive area is different from its drawing parameters
|
|
// we have the width/height in x2 and it is saved in Ctrl[r].inc
|
|
Ctrl[r].inc = Ctrl[r].x2;
|
|
Ctrl[r].y2 = Ctrl[r].y1 + Ctrl[r].x2; // calculate the touch sensitive area
|
|
Ctrl[r].x2 = Ctrl[r].x1 + Ctrl[r].x2 + (gui_font_width * (strlen((char *)Ctrl[r].s) + 1)); // calculate the touch sensitive area
|
|
break;
|
|
|
|
case CTRL_LED:
|
|
case CTRL_RADIOBTN: // the LED and radio button also do not need the height and are a special case
|
|
// their touch sensitive area is stored in x1, y1, x2 and y2
|
|
// the radius is stored in Ctrl[r].max and the X and Y centre of the button is calculated when drawing the control
|
|
Ctrl[r].max = Ctrl[r].x2;
|
|
Ctrl[r].x2 = Ctrl[r].x1 + Ctrl[r].max + (gui_font_width * (strlen((char *)Ctrl[r].s) + 1)); // calculate the touch sensitive area
|
|
Ctrl[r].x1 -= Ctrl[r].max;
|
|
Ctrl[r].y2 = Ctrl[r].y1 + Ctrl[r].max;
|
|
Ctrl[r].y1 -= Ctrl[r].max;
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
// get the foreground colour - all controls except the area control need this
|
|
if(type != CTRL_AREA && argc > a + 2) if(*argv[a += 2] != 0) last_fcolour = Ctrl[r].fc = getint(argv[a], BLACK, WHITE);
|
|
|
|
switch(type) { // these all need the background colour
|
|
case CTRL_SPINNER:
|
|
case CTRL_BUTTON:
|
|
case CTRL_SWITCH:
|
|
case CTRL_TEXTBOX:
|
|
case CTRL_FMTBOX:
|
|
case CTRL_NBRBOX:
|
|
case CTRL_DISPLAYBOX: if(argc > a + 2) if(*argv[a += 2] != 0) last_bcolour = Ctrl[r].bc = getint(argv[a], 0, WHITE);
|
|
break;
|
|
case CTRL_CAPTION:
|
|
case CTRL_GAUGE:
|
|
case CTRL_BARGAUGE: if(argc > a + 2) if(*argv[a += 2] != 0) last_bcolour = Ctrl[r].bc = getint(argv[a], -1, WHITE);
|
|
break;
|
|
}
|
|
|
|
if(type == CTRL_SPINNER) {
|
|
if(argc > a + 2) if(*argv[a += 2] != 0) last_inc = Ctrl[r].inc = getnumber(argv[a]);
|
|
if(argc > a + 2) if(*argv[a += 2] != 0) last_min = Ctrl[r].min = getnumber(argv[a]);
|
|
if(argc > a + 2) last_max = Ctrl[r].max = getnumber(argv[a += 2]);
|
|
}
|
|
|
|
if(type == CTRL_GAUGE || type == CTRL_BARGAUGE) {
|
|
// special processing for a gauge
|
|
// Note that a GAUGE uses the allocated string memory (Ctrl[r].s) for also storing other data
|
|
if(argc > a + 2) if(*argv[a += 2] != 0) last_min = Ctrl[r].min = getnumber(argv[a]);
|
|
if(argc > a + 2) if(*argv[a += 2] != 0) last_max = Ctrl[r].max = getnumber(argv[a]);
|
|
if(type == CTRL_GAUGE) { if(argc > a + 2) if(*argv[a += 2] != 0) last_inc = Ctrl[r].inc = getnumber(argv[a]); }
|
|
|
|
strcpy((char *)Ctrl[r].s, last_units); // setup the default default
|
|
if(type == CTRL_GAUGE && argc > a + 2) {
|
|
if(*argv[a += 2] != 0) {
|
|
strcpy((char *)Ctrl[r].s, (char *)getCstring(argv[a])); // get the units caption
|
|
Ctrl[r].s[GAUGE_UNITS_SIZE - 1] = 0; // truncate to max size
|
|
strcpy(last_units, (char *)Ctrl[r].s); // save as default
|
|
} else
|
|
*last_units = *Ctrl[r].s = 0;
|
|
}
|
|
GaugeS = (void *)(Ctrl[r].s);
|
|
GaugeS->c1 = -1; GaugeS->c2 = GaugeS->c3 = GaugeS->c4 = gui_fcolour;
|
|
GaugeS->ta = GaugeS->tb = GaugeS->tc = Ctrl[r].max;
|
|
if(argc > a + 2) if(*argv[a += 2] != 0) GaugeS->c1 = getint(argv[a], 0, WHITE);
|
|
if(argc > a + 2) if(*argv[a += 2] != 0) GaugeS->ta = getnumber(argv[a]);
|
|
if(argc > a + 2) if(*argv[a += 2] != 0) GaugeS->c2 = getint(argv[a], 0, WHITE);
|
|
if(argc > a + 2) if(*argv[a += 2] != 0) GaugeS->tb = getnumber(argv[a]);
|
|
if(argc > a + 2) if(*argv[a += 2] != 0) GaugeS->c3 = getint(argv[a], 0, WHITE);
|
|
if(argc > a + 2) if(*argv[a += 2] != 0) GaugeS->tc = getnumber(argv[a]);
|
|
if(argc > a + 2) if(*argv[a += 2] != 0) GaugeS->c4 = getint(argv[a], 0, WHITE);
|
|
if(type == CTRL_GAUGE) Ctrl[r].y2 = -1; // on first use draw the full gauge
|
|
}
|
|
|
|
if(argc > a + 1) error("Argument count");
|
|
Ctrl[r].type = type;
|
|
if(type == CTRL_GAUGE || type == CTRL_BARGAUGE) {
|
|
Ctrl[r].value = Ctrl[r].min;
|
|
} else {
|
|
Ctrl[r].value = 0;
|
|
}
|
|
Ctrl[r].page = SetupPage;
|
|
Ctrl[r].fcc = gui_fcolour;
|
|
SetCtrlState(r, CTRL_NORMAL, false);
|
|
return r;
|
|
}
|
|
|
|
/* @endcond */
|
|
|
|
void cmd_gui(void) {
|
|
int r;
|
|
unsigned char *p;
|
|
|
|
if(!Option.DISPLAY_TYPE) error("Display not configured");
|
|
|
|
if((p = checkstring(cmdline, (unsigned char *)"PAGE"))) {
|
|
cmd_GUIpage(p);
|
|
return;
|
|
}
|
|
if((p = checkstring(cmdline, (unsigned char *)"BUTTON"))) {
|
|
r = GetCtrlParams(CTRL_BUTTON, p);
|
|
return;
|
|
}
|
|
|
|
|
|
if((p = checkstring(cmdline, (unsigned char *)"SWITCH"))) {
|
|
r = GetCtrlParams(CTRL_SWITCH, p);
|
|
return;
|
|
}
|
|
|
|
|
|
if((p = checkstring(cmdline, (unsigned char *)"CHECKBOX"))) {
|
|
r = GetCtrlParams(CTRL_CHECKBOX, p);
|
|
return;
|
|
}
|
|
|
|
|
|
if((p = checkstring(cmdline, (unsigned char *)"RADIO"))) {
|
|
r = GetCtrlParams(CTRL_RADIOBTN, p);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
if((p = checkstring(cmdline, (unsigned char *)"LED"))) {
|
|
r = GetCtrlParams(CTRL_LED, p);
|
|
Ctrl[r].inc = 0;
|
|
return;
|
|
}
|
|
|
|
|
|
if((p = checkstring(cmdline, (unsigned char *)"FRAME"))) {
|
|
r = GetCtrlParams(CTRL_FRAME, p);
|
|
return;
|
|
}
|
|
|
|
|
|
if((p = checkstring(cmdline, (unsigned char *)"NUMBERBOX"))) {
|
|
if((checkstring(p, (unsigned char *)"CANCEL"))) {
|
|
if(!Option.MaxCtrls)error("No memory allocated for GUI controls");
|
|
if(!InvokingCtrl) return;
|
|
DrawKeyboard(KEY_KEY_CANCEL);
|
|
} else
|
|
r = GetCtrlParams(CTRL_NBRBOX, p);
|
|
return;
|
|
}
|
|
|
|
|
|
if((p = checkstring(cmdline, (unsigned char *)"TEXTBOX"))) {
|
|
if((checkstring(p, (unsigned char *)"CANCEL"))) {
|
|
if(!Option.MaxCtrls)error("No memory allocated for GUI controls");
|
|
if(!InvokingCtrl) return;
|
|
DrawKeyboard(KEY_KEY_CANCEL);
|
|
|
|
} else
|
|
r = GetCtrlParams(CTRL_TEXTBOX, p);
|
|
return;
|
|
}
|
|
|
|
|
|
if((p = checkstring(cmdline, (unsigned char *)"FORMATBOX"))) {
|
|
if((checkstring(p, (unsigned char *)"CANCEL"))) {
|
|
if(!Option.MaxCtrls)error("No memory allocated for GUI controls");
|
|
if(!InvokingCtrl) return;
|
|
DrawFmtBox(KEY_KEY_CANCEL);
|
|
} else
|
|
r = GetCtrlParams(CTRL_FMTBOX, p);
|
|
return;
|
|
}
|
|
|
|
|
|
if((p = checkstring(cmdline, (unsigned char *)"SPINBOX"))) {
|
|
r = GetCtrlParams(CTRL_SPINNER, p);
|
|
Ctrl[r].value = 0;
|
|
if(Ctrl[r].value < Ctrl[r].min) Ctrl[r].value = Ctrl[r].min;
|
|
if(Ctrl[r].value > Ctrl[r].max) Ctrl[r].value = Ctrl[r].max;
|
|
FloatToStr((char *)Ctrl[r].s, Ctrl[r].value, 0, STR_AUTO_PRECISION, ' ');
|
|
UpdateControl(r); // update the displayed string
|
|
return;
|
|
}
|
|
|
|
|
|
if((p = checkstring(cmdline, (unsigned char *)"DISPLAYBOX"))) {
|
|
r = GetCtrlParams(CTRL_DISPLAYBOX, p);
|
|
return;
|
|
}
|
|
|
|
|
|
if((p = checkstring(cmdline, (unsigned char *)"CAPTION"))) {
|
|
r = GetCtrlParams(CTRL_CAPTION, p);
|
|
return;
|
|
}
|
|
|
|
|
|
if((p = checkstring(cmdline, (unsigned char *)"GAUGE"))) {
|
|
r = GetCtrlParams(CTRL_GAUGE, p);
|
|
return;
|
|
}
|
|
|
|
|
|
if((p = checkstring(cmdline, (unsigned char *)"BARGAUGE"))) {
|
|
r = GetCtrlParams(CTRL_BARGAUGE, p);
|
|
return;
|
|
}
|
|
|
|
|
|
if((p = checkstring(cmdline, (unsigned char *)"AREA"))) {
|
|
r = GetCtrlParams(CTRL_AREA, p);
|
|
return;
|
|
}
|
|
|
|
|
|
if((p = checkstring(cmdline, (unsigned char *)"DELETE"))) {
|
|
int i, r;
|
|
getargs(&p, MAX_ARG_COUNT, (unsigned char *)",");
|
|
if(!(argc & 1)) error("Argument count");
|
|
if(!Option.MaxCtrls)error("No memory allocated for GUI controls");
|
|
if(checkstring(argv[0], (unsigned char *)"ALL")) {
|
|
for(r = 1; r < Option.MaxCtrls; r++)
|
|
if(Ctrl[r].type != 0) {
|
|
SetCtrlState(r, CTRL_HIDDEN, true);
|
|
FreeMemorySafe((void **)&Ctrl[r].s);
|
|
if(Ctrl[r].fmt) FreeMemorySafe((void **)&Ctrl[r].fmt);
|
|
memset(&Ctrl[r],0,sizeof(struct s_ctrl));
|
|
}
|
|
return;
|
|
} else {
|
|
for(i = 0; i < argc; i += 2) {
|
|
if(*argv[i] == '#') argv[i]++;
|
|
r = getint(argv[i], 1, Option.MaxCtrls - 1);
|
|
SetCtrlState(r, CTRL_HIDDEN, true);
|
|
FreeMemorySafe((void **)&Ctrl[r].s);
|
|
if(Ctrl[r].fmt) FreeMemorySafe((void **)&Ctrl[r].fmt);
|
|
memset(&Ctrl[r],0,sizeof(struct s_ctrl));
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
if((p = checkstring(cmdline, (unsigned char *)"DISABLE"))) {
|
|
int i, r;
|
|
getargs(&p, MAX_ARG_COUNT, (unsigned char *)",");
|
|
if(!(argc & 1)) error("Argument count");
|
|
if(!Option.MaxCtrls)error("No memory allocated for GUI controls");
|
|
if(checkstring(argv[0], (unsigned char *)"ALL")) {
|
|
for(r = 1; r < Option.MaxCtrls; r++)
|
|
if(CurrentPages & (1 << Ctrl[r].page))
|
|
SetCtrlState(r, CTRL_DISABLED, false);
|
|
return;
|
|
}
|
|
for(i = 0; i < argc; i += 2) {
|
|
if(*argv[i] == '#') argv[i]++;
|
|
r = getint(argv[i], 1, Option.MaxCtrls - 1);
|
|
SetCtrlState(r, CTRL_DISABLED, true);
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
if((p = checkstring(cmdline, (unsigned char *)"HIDE"))) {
|
|
int i, r;
|
|
getargs(&p, MAX_ARG_COUNT, (unsigned char *)",");
|
|
if(!Option.MaxCtrls)error("No memory allocated for GUI controls");
|
|
if(!(argc & 1)) error("Argument count");
|
|
if(checkstring(argv[0], (unsigned char *)"ALL")) {
|
|
for(r = 1; r < Option.MaxCtrls; r++)
|
|
if(CurrentPages & (1 << Ctrl[r].page))
|
|
SetCtrlState(r, CTRL_HIDDEN, false);
|
|
return;
|
|
}
|
|
for(i = 0; i < argc; i += 2) {
|
|
if(*argv[i] == '#') argv[i]++;
|
|
r = getint(argv[i], 1, Option.MaxCtrls - 1);
|
|
SetCtrlState(r, CTRL_HIDDEN, true);
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
if((p = checkstring(cmdline, (unsigned char *)"ENABLE"))) {
|
|
int i, r;
|
|
getargs(&p, MAX_ARG_COUNT, (unsigned char *)",");
|
|
if(!(argc & 1)) error("Argument count");
|
|
if(!Option.MaxCtrls)error("No memory allocated for GUI controls");
|
|
if(checkstring(argv[0], (unsigned char *)"ALL")) {
|
|
for(r = 1; r < Option.MaxCtrls; r++) {
|
|
if(CurrentPages & (1 << Ctrl[r].page)) {
|
|
Ctrl[r].state &= ~(CTRL_DISABLED | CTRL_DISABLED2);
|
|
SetCtrlState(r, CTRL_NORMAL, false);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
for(i = 0; i < argc; i += 2) {
|
|
if(*argv[i] == '#') argv[i]++;
|
|
r = getint(argv[i], 1, Option.MaxCtrls - 1);
|
|
Ctrl[r].state &= ~(CTRL_DISABLED | CTRL_DISABLED2);
|
|
SetCtrlState(r, CTRL_NORMAL, true);
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
if((p = checkstring(cmdline, (unsigned char *)"SHOW"))) {
|
|
int i, r;
|
|
getargs(&p, MAX_ARG_COUNT, (unsigned char *)",");
|
|
if(!(argc & 1)) error("Argument count");
|
|
if(!Option.MaxCtrls)error("No memory allocated for GUI controls");
|
|
if(checkstring(argv[0], (unsigned char *)"ALL")) {
|
|
for(r = 1; r < Option.MaxCtrls; r++) {
|
|
if(CurrentPages & (1 << Ctrl[r].page)) {
|
|
Ctrl[r].state &= ~CTRL_HIDDEN;
|
|
SetCtrlState(r, CTRL_NORMAL, false);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
for(i = 0; i < argc; i += 2) {
|
|
if(*argv[i] == '#') argv[i]++;
|
|
r = getint(argv[i], 1, Option.MaxCtrls - 1);
|
|
Ctrl[r].state &= ~CTRL_HIDDEN;
|
|
SetCtrlState(r, CTRL_NORMAL, true);
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
if((p = checkstring(cmdline, (unsigned char *)"RESTORE"))) {
|
|
int i, r;
|
|
getargs(&p, MAX_ARG_COUNT, (unsigned char *)",");
|
|
if(!Option.MaxCtrls)error("No memory allocated for GUI controls");
|
|
if(!(argc & 1)) error("Argument count");
|
|
if(checkstring(argv[0], (unsigned char *)"ALL")) {
|
|
for(r = 1; r < Option.MaxCtrls; r++) {
|
|
if(CurrentPages & (1 << Ctrl[r].page)) {
|
|
Ctrl[r].state &= ~(CTRL_DISABLED | CTRL_DISABLED2 | CTRL_HIDDEN);
|
|
SetCtrlState(r, CTRL_NORMAL, false);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
for(i = 0; i < argc; i += 2) {
|
|
if(*argv[i] == '#') argv[i]++;
|
|
r = getint(argv[i], 1, Option.MaxCtrls - 1);
|
|
Ctrl[r].state &= ~(CTRL_DISABLED | CTRL_DISABLED2 | CTRL_HIDDEN);
|
|
SetCtrlState(r, CTRL_NORMAL, true);
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
if((p = checkstring(cmdline, (unsigned char *)"REDRAW"))) {
|
|
int i, r;
|
|
getargs(&p, MAX_ARG_COUNT, (unsigned char *)",");
|
|
if(!(argc & 1)) error("Argument count");
|
|
if(!Option.MaxCtrls)error("No memory allocated for GUI controls");
|
|
if(checkstring(argv[0], (unsigned char *)"ALL")) {
|
|
ClearScreen(gui_bcolour);
|
|
for(r = 1; r < Option.MaxCtrls; r++)
|
|
if(CurrentPages & (1 << Ctrl[r].page)) {
|
|
if(Ctrl[r].type == CTRL_GAUGE) Ctrl[r].y2 = -1; // this will force a full redraw of the gauge
|
|
UpdateControl(r);
|
|
}
|
|
return;
|
|
}
|
|
for(i = 0; i < argc; i += 2) {
|
|
if(*argv[i] == '#') argv[i]++;
|
|
r = getint(argv[i], 1, Option.MaxCtrls - 1);
|
|
if(Ctrl[r].type == CTRL_GAUGE) Ctrl[r].y2 = -1; // this will force a full redraw of the gauge
|
|
UpdateControl(r);
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
if((p = checkstring(cmdline, (unsigned char *)"FCOLOUR")) || (p = checkstring(cmdline, (unsigned char *)"BCOLOUR"))) {
|
|
int i, r, c;
|
|
getargs(&p, MAX_ARG_COUNT, (unsigned char *)",");
|
|
if(!Option.MaxCtrls)error("No memory allocated for GUI controls");
|
|
if(!(argc & 1) || argc < 3) error("Argument count");
|
|
c = getint(argv[0], BLACK, WHITE);
|
|
for(i = 2; i < argc; i += 2) {
|
|
if(*argv[i] == '#') argv[i]++;
|
|
r = getint(argv[i], 1, Option.MaxCtrls - 1);
|
|
if(Ctrl[r].type == 0) error("Control #% does not exist", r);
|
|
if(checkstring(cmdline, (unsigned char *)"FCOLOUR"))
|
|
Ctrl[r].fc = c;
|
|
else
|
|
Ctrl[r].bc = c;
|
|
UpdateControl(r);
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
/* if((p = checkstring(cmdline, "BEEP"))) {
|
|
if(Option.TOUCH_Click == 0) error("Click option not set");
|
|
ClickTimer = getint(p, 0, INT_MAX) + 1;
|
|
return;
|
|
}*/
|
|
|
|
|
|
if((p = checkstring(cmdline, (unsigned char *)"INTERRUPT"))) {
|
|
getargs(&p, 3, (unsigned char *)",");
|
|
if(Option.TOUCH_CS == 0) error("Touch option not set");
|
|
if(!Option.TOUCH_XZERO && !Option.TOUCH_YZERO) error("Touch not calibrated");
|
|
if(*argv[0] == '0' && !isdigit(*(argv[0]+1)))
|
|
GuiIntDownVector = GuiIntUpVector = NULL;
|
|
else {
|
|
GuiIntDownVector = (char *)GetIntAddress(argv[0]); // get a pointer to the down interrupt routine
|
|
if(argc == 3)
|
|
GuiIntUpVector = (char *)GetIntAddress(argv[2]); // and for the up routine
|
|
else
|
|
GuiIntUpVector = NULL;
|
|
InterruptUsed = true;
|
|
}
|
|
gui_int_down = gui_int_up = false;
|
|
return;
|
|
}
|
|
|
|
|
|
if((p = checkstring(cmdline, (unsigned char *)"SETUP"))) {
|
|
if(!Option.MaxCtrls)error("No memory allocated for GUI controls");
|
|
SetupPage = getint(p, 1, MAX_PAGES) - 1;
|
|
return;
|
|
}
|
|
|
|
// the final few commands are common to the MX170 and MX470 so execute them in Draw.c
|
|
cmd_guiMX170();
|
|
}
|
|
|
|
|
|
|
|
|
|
void cmd_GUIpage(unsigned char *p) {
|
|
int i, r, OldPages;
|
|
|
|
getargs(&p, MAX_ARG_COUNT, (unsigned char *)",");
|
|
if(!(argc & 1)) error("Argument count");
|
|
if(!Option.MaxCtrls)error("No memory allocated for GUI controls");
|
|
OldPages = CurrentPages;
|
|
CurrentPages = 0;
|
|
for(i = 0; i < argc; i += 2) { // get the new set of pages
|
|
if(*argv[i] == '#') argv[i]++;
|
|
CurrentPages |= (1 << (getint(argv[i], 1, MAX_PAGES) - 1));
|
|
}
|
|
|
|
// hide any that are showing but not on the new pages
|
|
for(r = 0; r < Option.MaxCtrls; r++) { // step thru the controls
|
|
if(Ctrl[r].type != 0) { // if the control is active
|
|
if((!(CurrentPages & (1 << Ctrl[r].page))) && (OldPages & (1 << Ctrl[r].page)) && (!(Ctrl[r].state & CTRL_HIDDEN))) {
|
|
Ctrl[r].state |= CTRL_HIDDEN;
|
|
if(r == CurrentRef) {
|
|
if(Ctrl[r].type == CTRL_BUTTON) Ctrl[r].value = 0;
|
|
if(Ctrl[r].type == CTRL_SPINNER) Ctrl[r].state &= ~(CTRL_SPINUP | CTRL_SPINDOWN);
|
|
LastRef = CurrentRef = 0;
|
|
gui_int_down = gui_int_up = false;
|
|
}
|
|
DrawControl(r);
|
|
Ctrl[r].state &= ~CTRL_HIDDEN;
|
|
}
|
|
}
|
|
}
|
|
|
|
// show any that are on the new pages but not currently showing and are not hidden
|
|
for(r = 0; r < Option.MaxCtrls; r++) { // step thru the controls
|
|
if(Ctrl[r].type != 0) { // if the control is active
|
|
if((!(OldPages & (1 << Ctrl[r].page))) && (CurrentPages & (1 << Ctrl[r].page)) && (!(Ctrl[r].state & CTRL_HIDDEN))) {
|
|
DrawControl(r);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* @cond
|
|
* The following section will be excluded from the documentation.
|
|
*/
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// This is the detailed implementation of the GUI controls in the Micromite Plus
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
int ChangeBright(int c, int pct) {
|
|
int r, g, b;
|
|
if(pct == 0 || c <= 0) return c;
|
|
r = (c >> 16) & 0xff;
|
|
g = (c >> 8) & 0xff;
|
|
b = c & 0xff;
|
|
r += (r * pct) / 100; if(r > 255) r = 255; if(r < 0) r = 0;
|
|
g += (g * pct) / 100; if(g > 255) g = 255; if(g < 0) g = 0;
|
|
b += (b * pct) / 100; if(b > 255) b = 255; if(b < 0) b = 0;
|
|
return RGB(r, g, b);
|
|
}
|
|
|
|
|
|
void SpecialWritePixel(int x, int y, unsigned int tc, int status) {
|
|
if(status & CTRL_HIDDEN) {
|
|
tc = gui_bcolour;
|
|
} else if(status & (CTRL_DISABLED | CTRL_DISABLED2)) {
|
|
tc = ChangeBright(tc, BTN_DISABLED);
|
|
}
|
|
DrawPixel(x, y, tc);
|
|
}
|
|
|
|
|
|
void SpecialDrawLine(int x1, int y1, int x2, int y2, int w, int tc, int status){
|
|
if(status & CTRL_HIDDEN) {
|
|
tc = gui_bcolour;
|
|
} else if(status & (CTRL_DISABLED | CTRL_DISABLED2)) {
|
|
tc = ChangeBright(tc, BTN_DISABLED);
|
|
}
|
|
DrawLine(x1, y1, x2, y2, w, tc);
|
|
}
|
|
|
|
|
|
void SpecialDrawBox(int x1, int y1, int x2, int y2, int w, int c, int fill, int status) {
|
|
if(status & CTRL_HIDDEN) {
|
|
c = gui_bcolour;
|
|
fill = gui_bcolour;
|
|
} else if(status & (CTRL_DISABLED | CTRL_DISABLED2)) {
|
|
c = ChangeBright(c, BTN_DISABLED);
|
|
if(fill != gui_bcolour) fill = ChangeBright(fill, BTN_DISABLED);
|
|
}
|
|
DrawBox(x1, y1, x2, y2, w, c, fill);
|
|
}
|
|
|
|
|
|
void SpecialDrawRBox(int x1, int y1, int x2, int y2, int radius, int c, int fill, int status) {
|
|
if(status & CTRL_HIDDEN) {
|
|
c = gui_bcolour;
|
|
fill = gui_bcolour;
|
|
} else if(status & (CTRL_DISABLED | CTRL_DISABLED2)) {
|
|
c = ChangeBright(c, BTN_DISABLED);
|
|
if(fill != gui_bcolour) fill = ChangeBright(fill, BTN_DISABLED);
|
|
}
|
|
DrawRBox(x1, y1, x2, y2, radius, c, fill);
|
|
}
|
|
|
|
|
|
void SpecialPrintString(int x, int y, int jh, int jv, int jo, int fc, int bc, char *str, int status) {
|
|
int lines, i, y2;
|
|
char t, *p, *idx[MAX_CAPTION_LINES + 1];
|
|
|
|
// first check if the string contains one or more line split chars ('~')
|
|
// while we are doing this save their addresses in idx[]
|
|
idx[0] = p = str;
|
|
lines = 1;
|
|
while((p = strchr((char *)p, '~')) != NULL && lines < MAX_CAPTION_LINES) {
|
|
idx[lines] = p++;
|
|
lines++;
|
|
}
|
|
|
|
if(lines > 1) {
|
|
// this is two or more lines
|
|
// recursively call this function to print each line
|
|
idx[lines] = str + strlen(str);
|
|
y2 = y - (gui_font_height / 2) * (lines - 1);
|
|
for(i = 0; i < lines; i++) {
|
|
t = *idx[i + 1];
|
|
*idx[i + 1] = 0;
|
|
SpecialPrintString(x, y2, jh, jv, jo, fc, bc, idx[i], status);
|
|
*idx[i + 1] = t;
|
|
if(t == '~') idx[i + 1]++;
|
|
y2 += gui_font_height;
|
|
}
|
|
} else {
|
|
// this is just a single line, so print it
|
|
if(status & CTRL_HIDDEN) {
|
|
fc = gui_bcolour;
|
|
bc = gui_bcolour;
|
|
} else if(status & (CTRL_DISABLED | CTRL_DISABLED2)) {
|
|
fc = ChangeBright(fc, BTN_DISABLED);
|
|
if(bc != gui_bcolour) bc = ChangeBright(bc, BTN_DISABLED);
|
|
}
|
|
GUIPrintString(x, y, gui_font, jh, jv, jo, fc, bc, str);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void SpecialDrawCircle(int x, int y, int radius, int w, int c, int fill, MMFLOAT aspect, int status) {
|
|
if(status & CTRL_HIDDEN) {
|
|
c = gui_bcolour;
|
|
fill = gui_bcolour;
|
|
} else if(status & (CTRL_DISABLED | CTRL_DISABLED2)) {
|
|
c = ChangeBright(c, BTN_DISABLED);
|
|
if(fill != gui_bcolour) fill = ChangeBright(fill, BTN_DISABLED);
|
|
}
|
|
DrawCircle(x, y, radius, w, c, fill, aspect);
|
|
}
|
|
|
|
|
|
|
|
void SpecialDrawTriangle(int x0, int y0, int x1, int y1, int x2, int y2, int c, int fill, int status) {
|
|
if(status & CTRL_HIDDEN) {
|
|
c = gui_bcolour;
|
|
fill = gui_bcolour;
|
|
} else if(status & (CTRL_DISABLED | CTRL_DISABLED2)) {
|
|
c = ChangeBright(c, BTN_DISABLED);
|
|
if(fill != gui_bcolour) fill = ChangeBright(fill, BTN_DISABLED);
|
|
}
|
|
DrawTriangle(x0, y0, x1, y1, x2, y2, c, fill);
|
|
DrawTriangle(x0, y0, x1, y1, x2, y2, c, -1);
|
|
}
|
|
|
|
|
|
|
|
void DrawBorder(int x1, int y1, int x2, int y2, int w, int tc, int bc, int status) {
|
|
int i;
|
|
for(i = 0; i < w; i++) {
|
|
SpecialDrawLine(x1 + i, y1 + i, x2 - i, y1 + i, 1, tc, status); // top border
|
|
SpecialDrawLine(x1 + i, y1 + i, x1 + i, y2 - i, 1, tc, status); // left border
|
|
SpecialDrawLine(x1 + i, y2 - i, x2 - i, y2 - i, 1, bc, status); // bottom
|
|
SpecialDrawLine(x2 - i, y1 + i, x2 - i, y2 - i, 1, bc, status); // and right
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void DrawBasicButton(int x1, int y1, int x2, int y2, int w, int up, int c, int status) {
|
|
DrawBorder(x1, y1, x2, y2, w, ChangeBright(c, up ? BTN_SIDE_BRIGHT:BTN_SIDE_DULL), ChangeBright(c, BTN_SIDE_DULL), status);
|
|
SpecialDrawBox(x1 + w, y1 + w, x2 - w, y2 - w, 0, 0, c, status); // fill in the face of the button
|
|
}
|
|
|
|
|
|
|
|
void DrawButton(int r) {
|
|
unsigned char *p, *pp, s[MAXSTRLEN];
|
|
int bs = 0;
|
|
|
|
DrawBasicButton(Ctrl[r].x1, Ctrl[r].y1, Ctrl[r].x2, Ctrl[r].y2, BTN_SIDE_WIDTH, (Ctrl[r].value == 0), Ctrl[r].bc, Ctrl[r].state);
|
|
|
|
// extract the up/down strings if necessary
|
|
for(p = Ctrl[r].s, pp = s; *p != '|';) {
|
|
if(*p == 0) {
|
|
p = s - 1;
|
|
bs = BTN_CAPTION_SHIFT;
|
|
break;
|
|
}
|
|
*pp++ = *p++;
|
|
}
|
|
p++;
|
|
*pp = 0;
|
|
|
|
// draw the caption
|
|
if(Ctrl[r].value == 0)
|
|
SpecialPrintString(Ctrl[r].x1 + (Ctrl[r].x2 - Ctrl[r].x1)/2, Ctrl[r].y1 + (Ctrl[r].y2 - Ctrl[r].y1)/2, JUSTIFY_CENTER, JUSTIFY_MIDDLE, ORIENT_NORMAL, Ctrl[r].fc, Ctrl[r].bc, (char *)s, Ctrl[r].state);
|
|
else
|
|
SpecialPrintString(Ctrl[r].x1 + (Ctrl[r].x2 - Ctrl[r].x1)/2 + bs, Ctrl[r].y1 + (Ctrl[r].y2 - Ctrl[r].y1)/2 + bs, JUSTIFY_CENTER, JUSTIFY_MIDDLE, ORIENT_NORMAL, Ctrl[r].fc, Ctrl[r].bc, (char *)p, Ctrl[r].state);
|
|
}
|
|
|
|
|
|
|
|
void DrawSwitch(int r) {
|
|
unsigned char *p, *pp, s[MAXSTRLEN];
|
|
int half, on, twobtn, shift;
|
|
|
|
// extract the up/down strings if necessary
|
|
twobtn = true;
|
|
for(p = Ctrl[r].s, pp = s; *p != '|';) {
|
|
if(*p == 0) {
|
|
p = s - 1;
|
|
twobtn = false;
|
|
break;
|
|
}
|
|
*pp++ = *p++;
|
|
}
|
|
p++;
|
|
*pp = 0;
|
|
|
|
on = (Ctrl[r].value == 0);
|
|
if(on) shift = 0; else shift = BTN_CAPTION_SHIFT;
|
|
if(twobtn) {
|
|
half = Ctrl[r].x1 + (Ctrl[r].x2 - Ctrl[r].x1)/2;
|
|
if(!(on)) { // use the min, max elements to store the active x area of the button
|
|
Ctrl[r].min = half;
|
|
Ctrl[r].max = Ctrl[r].x2;
|
|
} else {
|
|
Ctrl[r].min = Ctrl[r].x1;
|
|
Ctrl[r].max = half;
|
|
}
|
|
} else {
|
|
Ctrl[r].min = Ctrl[r].x1;
|
|
Ctrl[r].max = half = Ctrl[r].x2;
|
|
}
|
|
|
|
DrawBasicButton(Ctrl[r].x1, Ctrl[r].y1, half, Ctrl[r].y2, BTN_SIDE_WIDTH, on, ChangeBright(Ctrl[r].bc, on ? 0 : -25), Ctrl[r].state);
|
|
if(twobtn) DrawBasicButton(half, Ctrl[r].y1, Ctrl[r].x2, Ctrl[r].y2, BTN_SIDE_WIDTH, !on, ChangeBright(Ctrl[r].bc, on ? -25 : 0), Ctrl[r].state);
|
|
|
|
// draw the captions
|
|
if(on) shift = 0; else shift = BTN_CAPTION_SHIFT;
|
|
SpecialPrintString(Ctrl[r].x1 + (half - Ctrl[r].x1)/2 + shift, Ctrl[r].y1 + (Ctrl[r].y2 - Ctrl[r].y1)/2 + shift, JUSTIFY_CENTER, JUSTIFY_MIDDLE, ORIENT_NORMAL, ChangeBright(Ctrl[r].fc, (on || !twobtn) ? 0:-25), ChangeBright(Ctrl[r].bc, on ? 0:-25), (char *)s, Ctrl[r].state);
|
|
if(!on) shift = 0; else shift = BTN_CAPTION_SHIFT;
|
|
if(twobtn) SpecialPrintString(half + (Ctrl[r].x2 - half)/2 + shift, Ctrl[r].y1 + (Ctrl[r].y2 - Ctrl[r].y1)/2 + shift, JUSTIFY_CENTER, JUSTIFY_MIDDLE, ORIENT_NORMAL, ChangeBright(Ctrl[r].fc, on ? -25:0), ChangeBright(Ctrl[r].bc, on ? -25:0), (char *)p, Ctrl[r].state);
|
|
}
|
|
|
|
|
|
|
|
// for the checkbox the width/height is saved in Ctrl[r].inc
|
|
void DrawCheckBox(int r) {
|
|
int i, w;
|
|
|
|
SpecialDrawBox(Ctrl[r].x1, Ctrl[r].y1, Ctrl[r].x1 + Ctrl[r].inc, Ctrl[r].y1 + Ctrl[r].inc, BTN_SIDE_WIDTH, ChangeBright(Ctrl[r].fc, -30), gui_bcolour, Ctrl[r].state) ;
|
|
SpecialPrintString(Ctrl[r].x1 + Ctrl[r].inc + Ctrl[r].inc/2, Ctrl[r].y1 + Ctrl[r].inc/2, JUSTIFY_LEFT, JUSTIFY_MIDDLE, ORIENT_NORMAL, Ctrl[r].fcc, gui_bcolour, (char *)Ctrl[r].s, Ctrl[r].state);
|
|
|
|
// draw the tick
|
|
if(Ctrl[r].value != 0) {
|
|
w = (Ctrl[r].inc / 28) + 1; // vary the tick size according to the box size
|
|
for(i = -w; i <= w; i++) {
|
|
SpecialDrawLine(Ctrl[r].x1 + i + BTN_SIDE_WIDTH*2, Ctrl[r].y1 + BTN_SIDE_WIDTH*2, Ctrl[r].x1 + Ctrl[r].inc + i - BTN_SIDE_WIDTH*2, Ctrl[r].y2 - BTN_SIDE_WIDTH*2, 1, Ctrl[r].fc, Ctrl[r].state);
|
|
SpecialDrawLine(Ctrl[r].x1 + i + BTN_SIDE_WIDTH*2, Ctrl[r].y2 - BTN_SIDE_WIDTH*2, Ctrl[r].x1 + Ctrl[r].inc + i - BTN_SIDE_WIDTH*2, Ctrl[r].y1 + BTN_SIDE_WIDTH*2, 1, Ctrl[r].fc, Ctrl[r].state);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// the radio button is a special case, its touch sensitive area is different from its drawing parameters
|
|
// the touch sensitive area is stored in x1, y1 to x2, y2
|
|
// the buttons radius is stored in Ctrl[r].max and the X and Y centre of the button (xc and yc) are calculated using that
|
|
void DrawRadioBtn(int r) {
|
|
int i, frame;
|
|
int xc = Ctrl[r].x1 + Ctrl[r].max;
|
|
int yc = Ctrl[r].y1 + Ctrl[r].max;
|
|
|
|
SpecialDrawCircle(xc, yc, Ctrl[r].max, BTN_SIDE_WIDTH, ChangeBright(Ctrl[r].fc, -30), gui_bcolour, 1.0, Ctrl[r].state);
|
|
SpecialPrintString(xc + Ctrl[r].max + gui_font_width - (gui_font_width/4), yc, JUSTIFY_LEFT, JUSTIFY_MIDDLE, ORIENT_NORMAL, Ctrl[r].fcc, gui_bcolour, (char *)Ctrl[r].s, Ctrl[r].state);
|
|
|
|
if(Ctrl[r].value != 0) {
|
|
// draw the button if this control has been selected
|
|
SpecialDrawCircle(xc, yc, Ctrl[r].max - ((BTN_SIDE_WIDTH * 3) / 2), 0, 0, Ctrl[r].fc, 1.0, Ctrl[r].state);
|
|
|
|
// make sure that all the other radio buttons are in the up state
|
|
// first find the frame (if any) that the current button is in
|
|
for(frame = 1; frame < Option.MaxCtrls; frame++)
|
|
if(Ctrl[frame].type == CTRL_FRAME && Ctrl[r].page == Ctrl[frame].page && xc > Ctrl[frame].x1 && xc < Ctrl[frame].x2 && yc > Ctrl[frame].y1 && yc < Ctrl[frame].y2)
|
|
break;
|
|
// next look for any other radio buttons that are not the current one and is down and is in the same frame... and set it up
|
|
for(i = 1; i < Option.MaxCtrls; i++) {
|
|
if(Ctrl[i].type == CTRL_RADIOBTN && Ctrl[i].page == Ctrl[r].page && i != r && Ctrl[r].value != 0) {
|
|
// if frame == MAX_CTRL this means that there is no frame and the whole screen can be considered the frame
|
|
if(frame == Option.MaxCtrls || (Ctrl[i].x1 + Ctrl[i].max > Ctrl[frame].x1 && Ctrl[i].x1 + Ctrl[i].max < Ctrl[frame].x2 && Ctrl[i].y1 + Ctrl[i].max > Ctrl[frame].y1 && Ctrl[i].y1 + Ctrl[i].max < Ctrl[frame].y2)) {
|
|
Ctrl[i].value = 0;
|
|
UpdateControl(i);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// the LED is a special case, its touch sensitive area is different from its drawing parameters
|
|
// the touch sensitive area is stored in x1, y1 to x2, y2
|
|
// the LED's radius is stored in Ctrl[r].max and the X and Y centre of the button (xc and yc) are calculated using that
|
|
void DrawLED(int r) {
|
|
int xc = Ctrl[r].x1 + Ctrl[r].max;
|
|
int yc = Ctrl[r].y1 + Ctrl[r].max;
|
|
|
|
SpecialDrawCircle(xc, yc, Ctrl[r].max, 0, 0, RGB(160,160,160), 1.0, Ctrl[r].state);
|
|
SpecialDrawCircle(xc, yc, Ctrl[r].max - BTN_SIDE_WIDTH/2, 0, 0, ChangeBright(Ctrl[r].fc, (Ctrl[r].value == 0) ? -65:0), 1.0, Ctrl[r].state);
|
|
SpecialPrintString(xc + Ctrl[r].max + gui_font_width - (gui_font_width/4), yc, JUSTIFY_LEFT, JUSTIFY_MIDDLE, ORIENT_NORMAL, Ctrl[r].fcc, gui_bcolour, (char *)Ctrl[r].s, Ctrl[r].state);
|
|
}
|
|
|
|
|
|
void DrawSpinner(int r) {
|
|
int x1, x2, y1, y2;
|
|
int h, z;
|
|
|
|
h = (Ctrl[r].y2 - Ctrl[r].y1);
|
|
z = h/6;
|
|
x1 = Ctrl[r].x1; x2 = Ctrl[r].x2;
|
|
y1 = Ctrl[r].y1; y2 = Ctrl[r].y2;
|
|
SpecialDrawRBox(x1 + h, y1, x2 - h, y2, 10, Ctrl[r].fc, Ctrl[r].bc, Ctrl[r].state);
|
|
FloatToStr((char *)Ctrl[r].s, Ctrl[r].value, 0, STR_AUTO_PRECISION, ' '); // convert the value to a string for display
|
|
Ctrl[r].s[(x2 - x1 - h * 2) / gui_font_width] = 0; // truncate to the display width
|
|
SpecialPrintString(x1 + (x2 - x1)/2, y1 + h/2, JUSTIFY_CENTER, JUSTIFY_MIDDLE, ORIENT_NORMAL, Ctrl[r].fc, Ctrl[r].bc, (char *)Ctrl[r].s, Ctrl[r].state);
|
|
if(Ctrl[r].state & CTRL_SPINUP)
|
|
SpecialDrawTriangle(x2 - h/2, y1 + z, x2 - z, y1 + h - z, x2 - h + z, y1 + h - z, Ctrl[r].bc, Ctrl[r].fc, Ctrl[r].state); // touched up pointing spin button
|
|
else
|
|
SpecialDrawTriangle(x2 - h/2, y1 + z, x2 - z, y1 + h - z, x2 - h + z, y1 + h - z, Ctrl[r].fc, Ctrl[r].bc, Ctrl[r].state); // up pointing spin button
|
|
if(Ctrl[r].state & CTRL_SPINDOWN)
|
|
SpecialDrawTriangle(x1 + h/2, y1 + h - z, x1 + z, y1 + z, x1 + h - z, y1 + z, Ctrl[r].bc, Ctrl[r].fc, Ctrl[r].state); // touched down pointing spin button
|
|
else
|
|
SpecialDrawTriangle(x1 + h/2, y1 + h - z, x1 + z, y1 + z, x1 + h - z, y1 + z, Ctrl[r].fc, Ctrl[r].bc, Ctrl[r].state); // down pointing spin button
|
|
}
|
|
|
|
|
|
|
|
void DrawFrame(int r) {
|
|
SpecialDrawRBox(Ctrl[r].x1, Ctrl[r].y1, Ctrl[r].x2, Ctrl[r].y2, 10, Ctrl[r].fc, -1, Ctrl[r].state); // the border
|
|
SpecialPrintString(Ctrl[r].x1 + 15, Ctrl[r].y1, JUSTIFY_LEFT, JUSTIFY_MIDDLE, ORIENT_NORMAL, Ctrl[r].fc, gui_bcolour, (char *)Ctrl[r].s, Ctrl[r].state);
|
|
}
|
|
|
|
|
|
void DrawCaption(int r) {
|
|
SpecialPrintString(Ctrl[r].x1, Ctrl[r].y1, Ctrl[r].x2 & 0b0000011, (Ctrl[r].x2 >> 2) & 0b0000011, (Ctrl[r].x2 >> 4) & 0b0000111, Ctrl[r].fc, Ctrl[r].bc, (char *)Ctrl[r].s, Ctrl[r].state);
|
|
}
|
|
|
|
|
|
|
|
void DrawGauge(int r) {
|
|
int x, y, c;
|
|
MMFLOAT radius;
|
|
int xo1, yo1, xo2, yo2, xi1, yi1, xi2, yi2;
|
|
int i, v1, v2, v, ta, tb, tc;
|
|
char buf[GAUGE_UNITS_SIZE * 2];
|
|
struct s_GaugeS *GaugeS; // we store extra info in the string allocated to this control
|
|
|
|
|
|
// these define the span of the gauge in degrees
|
|
const int gstart = -225;
|
|
const int gend = 45;
|
|
|
|
// this defines the width of the gauge as a percentage of the radius
|
|
const MMFLOAT width = 0.25;
|
|
|
|
// this is the number of degrees resolution
|
|
// smaller nbr gives a cleaner graph but is slower
|
|
const int stepsize = 3;
|
|
|
|
x = Ctrl[r].x1; y = Ctrl[r].y1; radius = Ctrl[r].x2;
|
|
GaugeS = (void *)Ctrl[r].s;
|
|
c = Ctrl[r].fc;
|
|
|
|
// scale the value of the gauge and the thresholds to degrees within the span of the gauge
|
|
v = (((Ctrl[r].value - Ctrl[r].min) / (Ctrl[r].max - Ctrl[r].min)) * (gend - gstart)) + gstart;
|
|
ta = (((GaugeS->ta - Ctrl[r].min) / (Ctrl[r].max - Ctrl[r].min)) * (gend - gstart)) + gstart;
|
|
tb = (((GaugeS->tb - Ctrl[r].min) / (Ctrl[r].max - Ctrl[r].min)) * (gend - gstart)) + gstart;
|
|
tc = (((GaugeS->tc - Ctrl[r].min) / (Ctrl[r].max - Ctrl[r].min)) * (gend - gstart)) + gstart;
|
|
|
|
// make sure that the value and the thresholds are within the range
|
|
if(v < gstart) v = gstart;
|
|
if(v > gend) v = gend;
|
|
if(ta <= gstart) ta = gstart + 1;
|
|
if(tb <= ta + 1) tb = ta + 2;
|
|
if(tc <= tb + 1) tc = tb + 2;
|
|
if(ta > gend) ta = gend;
|
|
if(tb > gend) tb = gend;
|
|
if(tc > gend) tc = gend;
|
|
|
|
// set v1 and v2 so that we are drawing only the part that changed while making sure v1 < v2
|
|
if(v > GaugeS->cval) {
|
|
v1 = GaugeS->cval; v2 = v;
|
|
} else {
|
|
v2 = GaugeS->cval; v1 = v;
|
|
}
|
|
|
|
if(Ctrl[r].y2 != Ctrl[r].state || Ctrl[r].fc != GaugeS->lastfc || Ctrl[r].bc != GaugeS->lastbc ) {
|
|
// if the state of this control has changed we then need to redraw the complete gauge rather than just update the content of the gauge graph
|
|
v1 = gstart;
|
|
v2 = gend;
|
|
GaugeS->laststrlen = GAUGE_UNITS_SIZE;
|
|
Ctrl[r].y2 = Ctrl[r].state;
|
|
GaugeS->cval = gend + 1;
|
|
GaugeS->lastfc = Ctrl[r].fc; GaugeS->lastbc = Ctrl[r].bc;
|
|
}
|
|
|
|
if(v != GaugeS->cval) { // only draw the gauge part if something has changed
|
|
// draw the gauge
|
|
// this only draws the part changed since the previous call
|
|
for(i = v1; i <= v2; i = i+stepsize) {
|
|
xo1 = x + (cosf(Rad(i)) * radius);
|
|
yo1 = y + (sinf(Rad(i)) * radius);
|
|
xo2 = x + (cosf(Rad(i+stepsize)) * radius);
|
|
yo2 = y + (sinf(Rad(i+stepsize)) * radius);
|
|
xi1 = x + (cosf(Rad(i)) * (radius * (1 - width)));
|
|
yi1 = y + (sinf(Rad(i)) * (radius * (1 - width)));
|
|
xi2 = x + (cosf(Rad(i+stepsize)) * (radius * (1 - width)));
|
|
yi2 = y + (sinf(Rad(i+stepsize)) * (radius * (1 - width)));
|
|
|
|
c = Ctrl[r].fc;
|
|
if(GaugeS->c1 >= 0) c = GaugeS->c1;
|
|
if(i > ta) c = GaugeS->c2;
|
|
if(i > tb) c = GaugeS->c3;
|
|
if(i > tc) c = GaugeS->c4;
|
|
|
|
if(v > i || v == gend) {
|
|
// drawing a filled in part of the gauge
|
|
SpecialDrawTriangle(xo1, yo1, xo2, yo2, xi1, yi1, c, c, Ctrl[r].state);
|
|
SpecialDrawTriangle(xo2, yo2, xi1, yi1, xi2, yi2, c, c, Ctrl[r].state);
|
|
} else {
|
|
GaugeS->csaved = c;
|
|
// erasing part of the gauge
|
|
c = ChangeBright(c, -70);
|
|
if(GaugeS->c1 < 0 || ta >= gend) {
|
|
c = Ctrl[r].bc;
|
|
GaugeS->csaved = Ctrl[r].fc;
|
|
}
|
|
SpecialDrawTriangle(xo1, yo1, xo2, yo2, xi1, yi1, c, c, Ctrl[r].state);
|
|
SpecialDrawTriangle(xo2, yo2, xi1, yi1, xi2, yi2, c, c, Ctrl[r].state);
|
|
}
|
|
// draw the outside frame
|
|
SpecialDrawLine(xo1, yo1, xo2, yo2, 1, Ctrl[r].fc, Ctrl[r].state);
|
|
SpecialDrawLine(xi1, yi1, xi2, yi2, 1, Ctrl[r].fc, Ctrl[r].state);
|
|
|
|
// draw the end stops
|
|
if(i == gstart) SpecialDrawLine(xi1, yi1, xo1, yo1, 1, Ctrl[r].fc, Ctrl[r].state);
|
|
if(i == gend) SpecialDrawLine(xi2, yi2, xo2, yo2, 1, Ctrl[r].fc, Ctrl[r].state);
|
|
}
|
|
}
|
|
|
|
FloatToStr(buf, Ctrl[r].value, 0, Ctrl[r].inc, ' '); // convert the value to a string for display
|
|
if(strlen(buf) < GaugeS->laststrlen) { // erase the last text if the length of the text has shrunk
|
|
SpecialDrawCircle(x, y, (radius * 0.75) - 2, 0, Ctrl[r].bc, Ctrl[r].bc, 1.0, Ctrl[r].state) ;
|
|
}
|
|
GaugeS->laststrlen = strlen(buf);
|
|
|
|
// get the colour of the text for the current value
|
|
c = Ctrl[r].fc;
|
|
if(GaugeS->c1 >= 0) c = GaugeS->c1;
|
|
if(v > ta) c = GaugeS->c2;
|
|
if(v > tb) c = GaugeS->c3;
|
|
if(v > tc) c = GaugeS->c4;
|
|
|
|
// draw the text
|
|
if(*Ctrl[r].s) { strcat(buf, "~"); strcat(buf, (char *)Ctrl[r].s); } // append the units string
|
|
SpecialPrintString(x, y, JUSTIFY_CENTER, JUSTIFY_MIDDLE, ORIENT_NORMAL, c, Ctrl[r].bc, buf, Ctrl[r].state);
|
|
|
|
GaugeS->cval = v;
|
|
}
|
|
|
|
|
|
|
|
void DrawBarGauge(int r) {
|
|
int vert, x, y, x2 = 0, y2 = 0, start, end, len;
|
|
int v, ta, tb, tc;
|
|
struct s_GaugeS *GaugeS; // we store extra info in the string allocated to this control
|
|
|
|
if(Ctrl[r].x2 < Ctrl[r].y2) {
|
|
// vertical bar
|
|
// x, y refer to the bottom left of the bar (not the outline)
|
|
// x2 is the width of the bar and len the overall length
|
|
vert = true;
|
|
x = Ctrl[r].x1 + 1;
|
|
y = Ctrl[r].y1 + Ctrl[r].y2 - 1;
|
|
x2 = Ctrl[r].x2 + x - 2;
|
|
len = Ctrl[r].y2 - 2;
|
|
} else {
|
|
// horizontal bar
|
|
// x, y refer to the top left coord of the bar
|
|
// x2 is the width of the bar and len the overall length
|
|
vert = false;
|
|
x = Ctrl[r].x1 + 1;
|
|
y = Ctrl[r].y1 + 1;
|
|
y2 = Ctrl[r].y2 + y - 2;
|
|
len = Ctrl[r].x2 - 2;
|
|
}
|
|
|
|
GaugeS = (void *)Ctrl[r].s;
|
|
|
|
// scale the value of the gauge and the thresholds to pixels within the span of the gauge
|
|
v = ((Ctrl[r].value - Ctrl[r].min) / (Ctrl[r].max - Ctrl[r].min)) * (MMFLOAT)len;
|
|
ta = ((GaugeS->ta - Ctrl[r].min) / (Ctrl[r].max - Ctrl[r].min)) * (MMFLOAT)len;
|
|
tb = ((GaugeS->tb - Ctrl[r].min) / (Ctrl[r].max - Ctrl[r].min)) * (MMFLOAT)len;
|
|
tc = ((GaugeS->tc - Ctrl[r].min) / (Ctrl[r].max - Ctrl[r].min)) * (MMFLOAT)len;
|
|
|
|
// make sure that the value and the thresholds are within the range
|
|
if(v < 0) v = 0;
|
|
if(v > len) v = len;
|
|
if(ta <= 0) ta = 1;
|
|
if(tb <= ta + 1) tb = ta + 2;
|
|
if(tc <= tb + 1) tc = tb + 2;
|
|
if(ta > len) ta = len;
|
|
if(tb > len) tb = len;
|
|
if(tc > len) tc = len;
|
|
|
|
SpecialDrawLine(Ctrl[r].x1, Ctrl[r].y1, Ctrl[r].x1 + Ctrl[r].x2, Ctrl[r].y1, 1, Ctrl[r].fc, Ctrl[r].state); // draw the top of the outline
|
|
SpecialDrawLine(Ctrl[r].x1, Ctrl[r].y1, Ctrl[r].x1, Ctrl[r].y1 + Ctrl[r].y2, 1, Ctrl[r].fc, Ctrl[r].state); // draw the left side of the outline
|
|
SpecialDrawLine(Ctrl[r].x1, Ctrl[r].y1 + Ctrl[r].y2, Ctrl[r].x1 + Ctrl[r].x2, Ctrl[r].y1 + Ctrl[r].y2, 1, Ctrl[r].fc, Ctrl[r].state); // draw the botton of the outline
|
|
SpecialDrawLine(Ctrl[r].x1 + Ctrl[r].x2, Ctrl[r].y1, Ctrl[r].x1 + Ctrl[r].x2, Ctrl[r].y1 + Ctrl[r].y2, 1, Ctrl[r].fc, Ctrl[r].state); // draw the right side of the outline
|
|
|
|
if(vert) {
|
|
if(GaugeS->c1 < 0) GaugeS->c1 = Ctrl[r].fc;
|
|
// draw the first bar
|
|
if(v > ta) end = ta; else end = v;
|
|
if(end > 0) {
|
|
SpecialDrawBox(x, y, x2, y - end, 0, GaugeS->c1, GaugeS->c1, Ctrl[r].state);
|
|
// draw the second bar
|
|
start = end + 1; if(v > tb) end = tb; else end = v;
|
|
if(end > start) {
|
|
SpecialDrawBox(x, y - start, x2, y - end, 0, GaugeS->c2, GaugeS->c2, Ctrl[r].state);
|
|
// draw the third bar
|
|
start = end + 1; if(v > tc) end = tc; else end = v;
|
|
if(end > start) {
|
|
SpecialDrawBox(x, y - start, x2, y - end, 0, GaugeS->c3, GaugeS->c3, Ctrl[r].state);
|
|
// finally draw the fourth bar
|
|
start = end + 1; if(v > tc) end = v;
|
|
if(end > start) {
|
|
SpecialDrawBox(x, y - start, x2, y - end, 0, GaugeS->c4, GaugeS->c4, Ctrl[r].state);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// erase the remaining part of the bar
|
|
if(end < len) SpecialDrawBox(x, y - end, x2, y - len, 0, Ctrl[r].bc, Ctrl[r].bc, Ctrl[r].state);
|
|
} else {
|
|
if(GaugeS->c1 < 0) GaugeS->c1 = Ctrl[r].fc;
|
|
// draw the first bar
|
|
if(v > ta) end = ta; else end = v;
|
|
if(end > 0) {
|
|
SpecialDrawBox(x, y, x + end, y2, 0, GaugeS->c1, GaugeS->c1, Ctrl[r].state);
|
|
// draw the second bar
|
|
start = end + 1; if(v > tb) end = tb; else end = v;
|
|
if(end > start) {
|
|
SpecialDrawBox(x + start, y, x + end, y2, 0, GaugeS->c2, GaugeS->c2, Ctrl[r].state);
|
|
// draw the third bar
|
|
start = end + 1; if(v > tc) end = tc; else end = v;
|
|
if(end > start) {
|
|
SpecialDrawBox(x + start, y, x + end, y2, 0, GaugeS->c3, GaugeS->c3, Ctrl[r].state);
|
|
// finally draw the fourth bar
|
|
start = end + 1; if(v > tc) end = v;
|
|
if(end > start) {
|
|
SpecialDrawBox(x + start, y, x + end, y2, 0, GaugeS->c4, GaugeS->c4, Ctrl[r].state);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// erase the remaining part of the bar
|
|
if(end < len) SpecialDrawBox(x + end, y, x + len, y2, 0, Ctrl[r].bc, Ctrl[r].bc, Ctrl[r].state);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
char *KeypadCaption[12][2] = { {"Alt", "123"},
|
|
{"0", "Can"},
|
|
{"Ent", "Ent"},
|
|
{"1", "."},
|
|
{"2", ""},
|
|
{"3", ""},
|
|
{"4", "+"},
|
|
{"5", "-"},
|
|
{"6", "E"},
|
|
{"7", "Del"},
|
|
{"8", "CE"},
|
|
{"9", ""}
|
|
};
|
|
|
|
|
|
char *KeyboardCaption[33][4] = {
|
|
// this is the bottom row of keys
|
|
{"&12", "ABC", "&12", "ABC"},
|
|
{"Z", "+" , "z", "["},
|
|
{"X", "-", "x" , "]"},
|
|
{"C", "=", "c", "{" },
|
|
{"V", "?", "v", "}" },
|
|
{"B", ":", "b", "|" },
|
|
{"N", "/", "n", "\\" },
|
|
{"M", ",", "m", "," },
|
|
{".", ".", ".", "." },
|
|
{"[ ]", "[ ]", "[ ]", "[ ]" },
|
|
{"Ent", "Ent", "Ent", "Ent" },
|
|
|
|
// this is the middle row
|
|
{"Can", "Can", "Can", "Can" },
|
|
{"A", "!", "a", "_" },
|
|
{"S", "@", "s", "/" },
|
|
{"D", "#", "d", "<" },
|
|
{"F", "$", "f", ">" },
|
|
{"G", "%", "g", ":" },
|
|
{"H", "&", "h", ";" },
|
|
{"J", "*", "j", "^" },
|
|
{"K", "(", "k", "'" },
|
|
{"L", ")", "l", "-" },
|
|
{" ", " ", " ", " " },
|
|
|
|
// and this is the top row
|
|
{"Q", "1", "q", "1" },
|
|
{"W", "2", "w", "2" },
|
|
{"E", "3", "e", "3" },
|
|
{"R", "4", "r", "4" },
|
|
{"T", "5", "t", "5" },
|
|
{"Y", "6", "y", "6" },
|
|
{"U", "7", "u", "7" },
|
|
{"I", "8", "i", "8" },
|
|
{"O", "9", "o", "9" },
|
|
{"P", "0", "p", "0" },
|
|
{"Del", "Del", "Del", "Del" }
|
|
};
|
|
|
|
|
|
|
|
char *GetCaption(int k, int is_alpha, int alt) {
|
|
if(is_alpha)
|
|
return KeyboardCaption[k][alt];
|
|
else
|
|
return KeypadCaption[k][alt];
|
|
}
|
|
|
|
|
|
// returns the top left coord of a key (x1 & y1) and the bottom right (x2 & y2)
|
|
// these must be passed as pointers to ints
|
|
// k is the key number
|
|
// is_alpha is true if keypad is the text keyboard
|
|
void GetSingleKeyCoord(int k, int is_alpha, int *x1, int *y1, int *x2, int *y2) {
|
|
int horiznbr, width, height, keygap, BaseHoriz, BaseVert;
|
|
|
|
if(is_alpha) { // if this is the keyboard
|
|
horiznbr = 11; // number of horizontal buttons
|
|
if(HRes == 800) {
|
|
width = 69;
|
|
height = 77;
|
|
keygap = 4;
|
|
} else if(HRes == 480) {
|
|
width = 40;
|
|
height = 41;
|
|
keygap = 4;
|
|
} else {
|
|
keygap = 2;
|
|
width = (HRes - keygap*10)/11;
|
|
height = ((VRes/2) - keygap*2) / 3;
|
|
}
|
|
BaseVert = (Ctrl[InvokingCtrl].y2 > VRes/2) ? (height * 3) + (keygap * 3) : VRes + keygap; // what half of the screen?
|
|
BaseHoriz = HRes + keygap/2;
|
|
} else { // if this is the number pad
|
|
horiznbr = 3; // number of horizontal buttons
|
|
keygap = 5;
|
|
height = VRes < HRes ? VRes : HRes; // find the smallest resolution
|
|
height = width = ((height - keygap*3)/4 < 80) ? (height - keygap*3)/4 : 80; // scale the button size to the smallest resolution
|
|
if(Ctrl[InvokingCtrl].y1 + height/2 > VRes/2) {
|
|
BaseVert = (width * 4) + (keygap * 4); // should it be in the top part of the screen?
|
|
} else {
|
|
BaseVert = VRes + keygap; // or in the bottom
|
|
}
|
|
if(Ctrl[InvokingCtrl].x1 + width/2 > HRes/2) {
|
|
BaseHoriz = (width * 3) + (keygap * 3);
|
|
} else {
|
|
BaseHoriz = HRes + keygap;
|
|
}
|
|
}
|
|
|
|
*x1 = BaseHoriz - (horiznbr - (k % horiznbr)) * (width + keygap);
|
|
*y1 = BaseVert - ((k / horiznbr) + 1) * (height + keygap);
|
|
*x2 = *x1 + width;
|
|
*y2 = *y1 + height;
|
|
}
|
|
|
|
|
|
void DrawDisplayBox(int r) {
|
|
int fc, bc;
|
|
if(Ctrl[r].state & CTRL_SELECTED) {
|
|
fc = Ctrl[r].bc;
|
|
bc = Ctrl[r].fc;
|
|
} else {
|
|
fc = Ctrl[r].fc;
|
|
bc = Ctrl[r].bc;
|
|
}
|
|
SpecialDrawRBox(Ctrl[r].x1, Ctrl[r].y1, Ctrl[r].x2, Ctrl[r].y2, 10, fc, bc, Ctrl[r].state);
|
|
if(strlen((char *)Ctrl[r].s) > 2 && Ctrl[r].s[0] == '#' && Ctrl[r].s[1] == '#')
|
|
SpecialPrintString(Ctrl[r].x1 + (Ctrl[r].x2 - Ctrl[r].x1)/2, Ctrl[r].y1 + (Ctrl[r].y2 - Ctrl[r].y1)/2, JUSTIFY_CENTER, JUSTIFY_MIDDLE, ORIENT_NORMAL, ChangeBright(fc, BTN_DISABLED), bc, (char *)Ctrl[r].s + 2, Ctrl[r].state);
|
|
else
|
|
SpecialPrintString(Ctrl[r].x1 + (Ctrl[r].x2 - Ctrl[r].x1)/2, Ctrl[r].y1 + (Ctrl[r].y2 - Ctrl[r].y1)/2, JUSTIFY_CENTER, JUSTIFY_MIDDLE, ORIENT_NORMAL, fc, bc, (char *)Ctrl[r].s , Ctrl[r].state);
|
|
}
|
|
|
|
|
|
// this will set all controls (except the control r) to disabled or non disabled
|
|
// note: r must always be a reference to the text box (never the keypad)
|
|
void PopUpRedrawAll(int r, int disabled) {
|
|
int i;
|
|
for(i = 1; i < Option.MaxCtrls; i++) { // find all the other controls and set to disabled
|
|
if(Ctrl[i].type && i != r) {
|
|
if(disabled)
|
|
Ctrl[i].state |= CTRL_DISABLED2; // set it to disable
|
|
else
|
|
Ctrl[i].state &= ~CTRL_DISABLED2; // set it to not disabled
|
|
UpdateControl(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void KeyPadErase(int is_alpha) {
|
|
int x1, y1, x2, y2;
|
|
int j1, j2;
|
|
|
|
GetSingleKeyCoord(is_alpha ? 22:9, is_alpha, &x1, &y1, &j1, &j2);// get the top left coordinate of the key that is top left
|
|
GetSingleKeyCoord(is_alpha ? 10:2, is_alpha, &j1, &j2, &x2, &y2);// get the bottom right coordinate of the key which is bottom right
|
|
DrawBox(x1, y1, x2, y2, 0, 0, gui_bcolour); // erase the whole keyboard
|
|
|
|
GUIKeyDown = -1;
|
|
}
|
|
|
|
|
|
void DrawSingleKey(int is_alpha, int x1, int y1, int x2, int y2, char *s, int fc, int bc) {
|
|
int fnt, i;
|
|
fnt = gui_font;
|
|
if(is_alpha) {
|
|
if(HRes > 400)
|
|
SetFont(0x31); // set a readable font
|
|
else if(HRes > 400)
|
|
SetFont(0x11);
|
|
else
|
|
SetFont(0x01);
|
|
} else {
|
|
if(VRes > 240)
|
|
SetFont(0x31); // set a readable font
|
|
else if(VRes > 160)
|
|
SetFont(0x11);
|
|
else
|
|
SetFont(0x01);
|
|
}
|
|
SpecialDrawRBox(x1, y1, x2, y2, 10, fc, bc, 0); // the border
|
|
SpecialPrintString(x1 + (x2 - x1)/2, y1 + (y2 - y1)/2, JUSTIFY_CENTER, JUSTIFY_MIDDLE, ORIENT_NORMAL, fc, bc, s, 0);
|
|
if(is_alpha && *s == ' ') { // a space means that we should draw the shift symbol inside the button
|
|
for(i = 0; i < ((x2 - x1) / 3); i++)
|
|
SpecialDrawLine(x1 + (x2 - x1)/2 - i/2, y1 + i + (y2 - y1)/3, x1 + (x2 - x1)/2 + i/2, y1 + i + (y2 - y1)/3, 1, fc, 0);
|
|
}
|
|
SetFont(fnt);
|
|
}
|
|
|
|
|
|
// invoke the callback routine so that the programmer has the option of manipulating the result
|
|
//void DoCallback(int InvokingCtrl, char *key) {
|
|
// char callstr[32];
|
|
// char *nextstmtSaved = nextstmt;
|
|
// if(!InPause && FindSubFun("MM.KEYPRESS", 0) >= 0) {
|
|
// strcpy(callstr, "MM.KEYPRESS"); IntToStrPad(callstr + strlen(callstr), InvokingCtrl, ' ', 4, 10);
|
|
// strcat(callstr, ", \""); strcat(callstr, key); strcat(callstr, "\"");
|
|
// callstr[strlen(callstr)+1] = 0; // two NULL chars required to terminate the call
|
|
// g_LocalIndex++;
|
|
// InCallback = true;
|
|
// ExecuteProgram(callstr);
|
|
// InCallback = false;
|
|
// g_LocalIndex--;
|
|
// g_TempMemoryIsChanged = true; // signal that temporary memory should be checked
|
|
// nextstmt = nextstmtSaved;
|
|
// }
|
|
//}
|
|
|
|
|
|
void DrawKeyboard(int mode) {
|
|
int x1, y1, x2, y2;
|
|
int is_alpha, nbr_buttons;
|
|
int i;
|
|
unsigned char *p;
|
|
|
|
if(Ctrl[InvokingCtrl].type == CTRL_TEXTBOX) {
|
|
nbr_buttons = 33;
|
|
is_alpha = true;
|
|
} else {
|
|
nbr_buttons = 12;
|
|
is_alpha = false;
|
|
}
|
|
|
|
if(mode == KEY_OPEN) {
|
|
// DoCallback(InvokingCtrl, "Open");
|
|
KeyPadErase(Ctrl[InvokingCtrl].type == CTRL_TEXTBOX); // erase the background
|
|
mode = KEY_DRAW_ALL;
|
|
}
|
|
|
|
// special processing for KEY_KEY_UP when the numeric keypad is in alt mode
|
|
// this switches back to number mode for everything except for backspace when there are still chars in the buffer
|
|
if(mode == KEY_KEY_UP && !is_alpha && KeyAltShift && GUIKeyDown > 0 && (GUIKeyDown != 9 || *Ctrl[InvokingCtrl].s == 0)) {
|
|
KeyAltShift = false; // redraw all keys in number mode
|
|
mode = KEY_DRAW_ALL; // drop thru to redraw all
|
|
}
|
|
|
|
if(mode == KEY_DRAW_ALL) {
|
|
// just draw all the buttons
|
|
for(i = 0; i < nbr_buttons; i++) {
|
|
GetSingleKeyCoord(i, is_alpha, &x1, &y1, &x2, &y2);
|
|
DrawSingleKey(is_alpha, x1, y1, x2, y2, GetCaption(i, is_alpha, KeyAltShift), Ctrl[InvokingCtrl].fc, Ctrl[InvokingCtrl].bc);
|
|
}
|
|
GUIKeyDown = -1; // no key to be down
|
|
return;
|
|
}
|
|
|
|
// if a key has been released simply redraw it in normal mode and return immediately
|
|
if(mode == KEY_KEY_UP) {
|
|
if(GUIKeyDown >= 0) {
|
|
GetSingleKeyCoord(GUIKeyDown, is_alpha, &x1, &y1, &x2, &y2);
|
|
DrawSingleKey(is_alpha, x1, y1, x2, y2, GetCaption(GUIKeyDown, is_alpha, KeyAltShift), Ctrl[InvokingCtrl].fc, Ctrl[InvokingCtrl].bc);
|
|
}
|
|
GUIKeyDown = -1;
|
|
return;
|
|
}
|
|
|
|
if(mode != KEY_KEY_CANCEL) {
|
|
for(i = 0; i < nbr_buttons; i++) { // search through the keys
|
|
GetSingleKeyCoord(i, is_alpha, &x1, &y1, &x2, &y2); // find the coordinates for this key
|
|
if(TouchX > x1 && TouchX < x2 && TouchY > y1 && TouchY < y2) { // if the touch is on this key
|
|
if(!is_alpha && KeyAltShift) { // if this is the number keypad
|
|
// this ignores the invalid keys on the number pad when in the alt mode
|
|
switch(i) {
|
|
case 4:
|
|
case 5:
|
|
case 11: return;
|
|
case 3: if(*Ctrl[InvokingCtrl].s == 0) break; else if(strchr((char *)Ctrl[InvokingCtrl].s, '.') != NULL) return; break;
|
|
case 8: if(*Ctrl[InvokingCtrl].s == 0) break; else if(strchr((char *)Ctrl[InvokingCtrl].s, 'E') != NULL) return; break;
|
|
case 9:
|
|
case 10: if(*Ctrl[InvokingCtrl].s == 0) return;
|
|
}
|
|
}
|
|
GUIKeyDown = i; // record the key that has been touched
|
|
ClickTimer += CLICK_DURATION; // make a click
|
|
DrawSingleKey(is_alpha, x1, y1, x2, y2, GetCaption(i, is_alpha, KeyAltShift), Ctrl[InvokingCtrl].bc, Ctrl[InvokingCtrl].fc);
|
|
}
|
|
}
|
|
|
|
if(GUIKeyDown == -1) return;
|
|
|
|
if(GUIKeyDown == 0) { // this is the alt key
|
|
KeyAltShift = !(KeyAltShift & 1); // toggle alt/text bit, also resets shift to no shift
|
|
DrawKeyboard(KEY_DRAW_ALL); // redraw the keyboard/keypad
|
|
return;
|
|
}
|
|
|
|
if(GUIKeyDown == 21) { // if this is the shift key on a textbox
|
|
KeyAltShift = ((!(KeyAltShift & 0b10) << 1) | (KeyAltShift & 0b01)); // toggle the shift key
|
|
DrawKeyboard(KEY_DRAW_ALL); // redraw the keyboard/keypad
|
|
return;
|
|
}
|
|
}
|
|
|
|
if(mode == KEY_KEY_CANCEL || (is_alpha && GUIKeyDown == 11) || (!is_alpha && KeyAltShift && GUIKeyDown == 1)) { // the user has hit the cancel key
|
|
strcpy((char *)Ctrl[InvokingCtrl].s, CancelValue); // restore the current value
|
|
GUIKeyDown = (is_alpha ? 10 : 2); // fake the Enter key
|
|
}
|
|
|
|
if(GUIKeyDown == (is_alpha ? 10 : 2)) { // this is the enter key
|
|
// DoCallback(InvokingCtrl, GetCaption(GUIKeyDown, is_alpha, KeyAltShift));
|
|
if(!is_alpha) {
|
|
int t = STR_FLOAT_PRECISION;
|
|
// get the number of digits that have been entered after the decimal point and use that to determine the precision of the display
|
|
if(strchr((char *)Ctrl[InvokingCtrl].s, 'e') == NULL && strchr((char *)Ctrl[InvokingCtrl].s, 'E') == NULL && strchr((char *)Ctrl[InvokingCtrl].s, '.') != NULL) {
|
|
t = strlen(strchr((char *)Ctrl[InvokingCtrl].s, '.') + 1);
|
|
if(t < 1 || t > 16) t = STR_FLOAT_PRECISION;
|
|
}
|
|
FloatToStr((char *)Ctrl[InvokingCtrl].s, Ctrl[InvokingCtrl].value = atof((char *)Ctrl[InvokingCtrl].s), 1, t, ' '); // convert to a float and put back on the display
|
|
}
|
|
KeyPadErase(is_alpha); // hide the keypad
|
|
Ctrl[InvokingCtrl].state &= ~CTRL_SELECTED; // deselect the text box
|
|
PopUpRedrawAll(0, false); // redraw all the controls
|
|
LastRef = InvokingCtrl;
|
|
InvokingCtrl = 0;
|
|
return;
|
|
}
|
|
|
|
p = Ctrl[InvokingCtrl].s + strlen((char *)Ctrl[InvokingCtrl].s);
|
|
if(!is_alpha) {
|
|
// this is the number pad
|
|
if(KeyAltShift) {
|
|
// the keypad is in the alt mode
|
|
// process the special keys
|
|
switch(GUIKeyDown) {
|
|
case 3: *p++ = '.'; break;
|
|
case 6: *p++ = '+'; break;
|
|
case 7: *p++ = '-'; break;
|
|
case 8: *p++ = 'E'; break;
|
|
case 9: *(--p) = 0; break;
|
|
case 10: *Ctrl[InvokingCtrl].s = 0; break;
|
|
default: return;
|
|
}
|
|
} else {
|
|
*p++ = *GetCaption(GUIKeyDown, is_alpha, KeyAltShift); // otherwise use the caption
|
|
}
|
|
} else {
|
|
// this is the text keyboard
|
|
if(GUIKeyDown == 32) { // is this the delete key
|
|
if(strlen((char *)Ctrl[InvokingCtrl].s)) *(--p) = 0; // delete the char
|
|
} else if(GUIKeyDown == 9) { // if it is space
|
|
*p++ = ' '; // insert a space
|
|
} else {
|
|
*p++ = *GetCaption(GUIKeyDown, is_alpha, KeyAltShift); // otherwise use the caption
|
|
}
|
|
}
|
|
*p = 0;
|
|
|
|
// DoCallback(InvokingCtrl, GetCaption(GUIKeyDown, is_alpha, KeyAltShift));
|
|
DrawControl(InvokingCtrl);
|
|
}
|
|
|
|
|
|
char *FmtKeyCaption[12] = { "<<<", "0", "Can", "1", "2", "3", "4", "5", "6", "7", "8", "9" } ;
|
|
char FmtKeyEnabled[12];
|
|
|
|
|
|
void DrawFmtKeypad(char x, char prev, int GUIKeyDown) {
|
|
int i, dimfc, x1, y1, x2, y2;
|
|
// dp("1"); uSec(10000);
|
|
|
|
for(i = 0; i < 12; i++) FmtKeyEnabled[i] = false;
|
|
FmtKeyEnabled[0] = FmtKeyEnabled[2] = true;
|
|
FmtKeyCaption[7] = "5"; FmtKeyCaption[10] = "8";
|
|
|
|
if(!isdigit(x)) {
|
|
switch(x) {
|
|
case '+': FmtKeyCaption[10] = "+"; FmtKeyCaption[7] = "-"; x = 0; break;
|
|
case 'A': FmtKeyCaption[10] = "AM"; FmtKeyCaption[7] = "PM"; x = 0; break;
|
|
case 'N': FmtKeyCaption[10] = "N"; FmtKeyCaption[7] = "S"; x = 0; break;
|
|
case 'E': FmtKeyCaption[10] = "E"; FmtKeyCaption[7] = "W"; x = 0; break;
|
|
case 'M': if(prev == '0') {
|
|
x = '9';
|
|
} else {
|
|
x = '2';
|
|
FmtKeyEnabled[1] = true;
|
|
}
|
|
break;
|
|
case 'D': if(prev == '0') {
|
|
x = '9';
|
|
} else {
|
|
FmtKeyEnabled[1] = true;
|
|
if(prev == '3')
|
|
x = '1';
|
|
else
|
|
x = '9';
|
|
|
|
}
|
|
break;
|
|
case 'H': FmtKeyEnabled[1] = true;
|
|
if(prev == '0' || prev == '1')
|
|
x = '9';
|
|
else
|
|
x = '3';
|
|
break;
|
|
case 'L': FmtKeyEnabled[1] = true;
|
|
if(prev == '0')
|
|
x = '9';
|
|
else
|
|
x = '7';
|
|
break;
|
|
}
|
|
if(x == 0)
|
|
FmtKeyEnabled[10] = FmtKeyEnabled[7] = true;
|
|
else
|
|
for(i = 3 ; i < x - '0' + 3 ; i++)
|
|
FmtKeyEnabled[i] = true;
|
|
} else {
|
|
FmtKeyEnabled[1] = true;
|
|
for(i = 3 ; i < x - '0' + 3 ; i++)
|
|
FmtKeyEnabled[i] = true;
|
|
}
|
|
|
|
// dp("1"); uSec(10000);
|
|
dimfc = ChangeBright(Ctrl[InvokingCtrl].fc, BTN_DISABLED);
|
|
for(i = 0; i < 12; i++){
|
|
// dp("2"); uSec(10000);
|
|
GetSingleKeyCoord(i, false, &x1, &y1, &x2, &y2);
|
|
// dp("3"); uSec(10000);
|
|
if(i == GUIKeyDown)
|
|
DrawSingleKey(false, x1, y1, x2, y2, FmtKeyCaption[i], Ctrl[InvokingCtrl].bc, Ctrl[InvokingCtrl].fc);
|
|
else
|
|
DrawSingleKey(false, x1, y1, x2, y2, FmtKeyCaption[i], FmtKeyEnabled[i] ? Ctrl[InvokingCtrl].fc:dimfc, Ctrl[InvokingCtrl].bc);
|
|
}
|
|
}
|
|
|
|
|
|
void DrawFmtControl(int r, char *pfmt) {
|
|
unsigned char *p;
|
|
p = Ctrl[r].s + strlen((char *)Ctrl[r].s);
|
|
if(*pfmt) *p++ = 0xff; // marker to change the following text brightness to dim
|
|
while(*pfmt) {
|
|
if(*pfmt == '(') {
|
|
pfmt++;
|
|
while(*pfmt != ')') *p++ = *pfmt++;
|
|
pfmt++;
|
|
} else {
|
|
if(*(pfmt + 1) == 0)
|
|
*p++ = *pfmt++;
|
|
else {
|
|
*p++ = *(pfmt + 1);
|
|
pfmt += 2;
|
|
}
|
|
}
|
|
}
|
|
*p = 0;
|
|
DrawControl(r);
|
|
}
|
|
|
|
|
|
void DrawFmtBox(int mode) {
|
|
int i, x1, y1, x2, y2;
|
|
static unsigned char *pfmt, *p;
|
|
|
|
if(mode == KEY_OPEN) {
|
|
KeyPadErase(false); // erase the background
|
|
pfmt = Ctrl[InvokingCtrl].fmt;
|
|
p = Ctrl[InvokingCtrl].s;
|
|
DrawFmtKeypad(*pfmt, 0, 99); // just draw all the buttons
|
|
DrawFmtControl(InvokingCtrl, (char *)pfmt);
|
|
GUIKeyDown = -1; // no key to be down
|
|
return;
|
|
}
|
|
|
|
if(mode == KEY_KEY_UP) {
|
|
if(GUIKeyDown < 0) return;
|
|
DrawFmtKeypad(*pfmt, *(p - 1), 99); // just draw all the buttons to show that the key is up
|
|
return;
|
|
}
|
|
|
|
if(mode == KEY_KEY_DWN) {
|
|
// if we are here we have the simple case of a key down
|
|
for(i = 0; i < 12; i++) { // search through the keys
|
|
GetSingleKeyCoord(i, false, &x1, &y1, &x2, &y2); // find the coordinates for this key
|
|
if(TouchX > x1 && TouchX < x2 && TouchY > y1 && TouchY < y2) { // if the touch is on this key
|
|
// get the key and do something with it
|
|
if(FmtKeyEnabled[i] == false || (i == 0 && pfmt == Ctrl[InvokingCtrl].fmt)) return; // do nothing if key is disabled
|
|
DrawFmtKeypad((char)*pfmt, *(p - 1), i); // show that the key is down
|
|
ClickTimer += CLICK_DURATION; // make a click
|
|
|
|
if(i == 0) {
|
|
// it is the backspace key
|
|
if(*(pfmt - 1) == ')') {
|
|
pfmt -= 2;
|
|
while(*pfmt-- != '(') p--;
|
|
pfmt--; p--;
|
|
} else {
|
|
pfmt -= 2;
|
|
p--;
|
|
}
|
|
*p = 0;
|
|
} else if(i == 2) {
|
|
mode = KEY_KEY_CANCEL;
|
|
break;
|
|
} else {
|
|
// this is an ordinary key (eg, 0 to 9)
|
|
p += strlen(strcpy((char *)p, FmtKeyCaption[i])); // add they key caption to the display box string
|
|
pfmt++;
|
|
if(*pfmt) pfmt++;
|
|
if(*pfmt) {
|
|
if(*pfmt == '(') {
|
|
pfmt++;
|
|
while(*pfmt && *pfmt != ')') *p++ = *pfmt++;
|
|
*p = 0;
|
|
if(*pfmt) pfmt++;
|
|
}
|
|
}
|
|
if(*pfmt == 0) break; // *pfmt == 0 means that we have reached the end of the format string
|
|
}
|
|
GUIKeyDown = i;
|
|
DrawFmtControl(InvokingCtrl, (char *)pfmt);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if(*pfmt == 0 || mode == KEY_KEY_CANCEL) {
|
|
if(mode == KEY_KEY_CANCEL) strcpy((char *)Ctrl[InvokingCtrl].s, CancelValue); // if we are cancelling first restore the current value
|
|
KeyPadErase(false); // hide the keypad
|
|
DrawControl(InvokingCtrl);
|
|
Ctrl[InvokingCtrl].state &= ~CTRL_SELECTED; // deselect the text box
|
|
PopUpRedrawAll(0, false); // redraw all the controls
|
|
LastRef = InvokingCtrl;
|
|
InvokingCtrl = 0;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
// draw a control on the screen
|
|
void DrawControl(int r) {
|
|
int fnt;
|
|
fnt = gui_font;
|
|
SetFont(Ctrl[r].font);
|
|
switch(Ctrl[r].type) {
|
|
case CTRL_BUTTON: DrawButton(r); break;
|
|
case CTRL_SWITCH: DrawSwitch(r); break;
|
|
case CTRL_CHECKBOX: DrawCheckBox(r); break;
|
|
case CTRL_RADIOBTN: DrawRadioBtn(r); break;
|
|
case CTRL_LED: DrawLED(r); break;
|
|
case CTRL_SPINNER: DrawSpinner(r); break;
|
|
case CTRL_FRAME: DrawFrame(r); break;
|
|
case CTRL_TEXTBOX:
|
|
case CTRL_FMTBOX:
|
|
case CTRL_NBRBOX:
|
|
case CTRL_DISPLAYBOX: DrawDisplayBox(r); break;
|
|
case CTRL_CAPTION: DrawCaption(r); break;
|
|
case CTRL_GAUGE: DrawGauge(r); break;
|
|
case CTRL_BARGAUGE: DrawBarGauge(r); break;
|
|
default: break;
|
|
}
|
|
SetFont(fnt);
|
|
}
|
|
|
|
|
|
// similar to DrawControl() but it will only redraw the control if it is in the current
|
|
// set of pages for display and if it is not hidden
|
|
void UpdateControl(int r) {
|
|
if(Ctrl[r].type && (CurrentPages & (1 << Ctrl[r].page)) && !(Ctrl[r].state & CTRL_HIDDEN)) DrawControl(r);
|
|
}
|
|
|
|
|
|
// check if the pen has touched or been lifted and animate the GUI elements as required
|
|
// this is called after every command (from check_interrupt()), in the getchar() loop and repeatedly in a pause
|
|
// TouchDown and TouchUp are set in the Timer 4 interrupt
|
|
void ProcessTouch(void) {
|
|
static int repeat = 0;
|
|
static int waiting = false;
|
|
int r, spinup;
|
|
// if(!Option.MaxCtrls || !TOUCH_GETIRQTRIS)return;
|
|
if(repeat) {
|
|
if(TOUCH_DOWN)
|
|
if(TouchTimer < repeat)
|
|
return;
|
|
else
|
|
TouchDown = true;
|
|
else
|
|
repeat = 0;
|
|
} else {
|
|
if(waiting) {
|
|
if(TouchTimer < 50) // wait 50mS before a processing touch (allows the touch to settle)
|
|
return;
|
|
else
|
|
waiting = false;
|
|
} else {
|
|
waiting = true;
|
|
TouchTimer = 0; // TouchTimer is incremented every mS in the Timer 4 interrupt
|
|
}
|
|
}
|
|
|
|
if(!(TouchUp || TouchDown)) return; // quick exit if there is nothing to do
|
|
if(TouchDown) {
|
|
// touch has just occurred
|
|
TouchX = GetTouch(GET_X_AXIS);
|
|
TouchY = GetTouch(GET_Y_AXIS);
|
|
LastRef = CurrentRef = 0;
|
|
TouchUp = TouchDown = false;
|
|
if(TouchX == TOUCH_ERROR) return; // abort if the pen was lifted
|
|
if(InvokingCtrl) { // the keyboard/keypad takes complete control when activated
|
|
if(Ctrl[InvokingCtrl].type == CTRL_FMTBOX)
|
|
DrawFmtBox(KEY_KEY_DWN);
|
|
else
|
|
DrawKeyboard(KEY_KEY_DWN);
|
|
gui_int_down = false;
|
|
return;
|
|
}
|
|
|
|
gui_int_down = true; // signal that a MMBasic interrupt is valid
|
|
for(r = 1; r < Option.MaxCtrls; r++) {
|
|
if(Ctrl[r].type && TouchX >= Ctrl[r].x1 && TouchY >= Ctrl[r].y1 && TouchX <= Ctrl[r].x2 && TouchY <= Ctrl[r].y2) {
|
|
if(!(CurrentPages & (1 << Ctrl[r].page))) continue; // ignore if the page is not displayed
|
|
if(Ctrl[r].state & (CTRL_DISABLED | CTRL_DISABLED2 | CTRL_HIDDEN)) continue; // ignore if control is disabled
|
|
switch(Ctrl[r].type) {
|
|
case CTRL_SWITCH: if(!(TouchX >= Ctrl[r].min && TouchX <= Ctrl[r].max)) return; // skip if it is not the touch sensitive area (depends on the switch state)
|
|
Ctrl[r].value = !Ctrl[r].value;
|
|
break;
|
|
|
|
case CTRL_BUTTON:
|
|
case CTRL_RADIOBTN:;
|
|
case CTRL_AREA: Ctrl[r].value = 1;
|
|
break;
|
|
|
|
case CTRL_FRAME: continue; // a frame does not respond to touch but it might contain controls that do
|
|
|
|
case CTRL_CAPTION: continue; // a caption does not respond to touch but it might be overlayed by an AREA control
|
|
|
|
case CTRL_DISPLAYBOX:
|
|
case CTRL_LED: CurrentRef = r;
|
|
return; // these controls do not respond to touch
|
|
|
|
case CTRL_SPINNER: if(TouchX < (Ctrl[r].x1 + (Ctrl[r].y2 - Ctrl[r].y1))) // if it is in the left side
|
|
spinup = false; // it is spin down
|
|
else if(TouchX > (Ctrl[r].x2 - (Ctrl[r].y2 - Ctrl[r].y1))) // if it is in the right side
|
|
spinup = true; // it is spin up
|
|
else
|
|
return;
|
|
if(spinup) {
|
|
if(Ctrl[r].state & CTRL_SPINDOWN) return;
|
|
if(Ctrl[r].value + Ctrl[r].inc > Ctrl[r].max) { repeat = 0; break; }
|
|
Ctrl[r].state |= CTRL_SPINUP;
|
|
Ctrl[r].value += Ctrl[r].inc;
|
|
} else {
|
|
if(Ctrl[r].state & CTRL_SPINUP) return;
|
|
if(Ctrl[r].value - Ctrl[r].inc < Ctrl[r].min) { repeat = 0; break; }
|
|
Ctrl[r].state |= CTRL_SPINDOWN;
|
|
Ctrl[r].value -= Ctrl[r].inc;
|
|
}
|
|
if(Ctrl[r].value < Ctrl[r].inc && Ctrl[r].value > -Ctrl[r].inc) Ctrl[r].value = 0;
|
|
if(repeat == 0)
|
|
repeat = 500;
|
|
else
|
|
repeat = 100;
|
|
TouchTimer = 0;
|
|
break;
|
|
|
|
case CTRL_NBRBOX:
|
|
FloatToStr((char *)Ctrl[r].s, Ctrl[r].value, 0, STR_AUTO_PRECISION, ' '); // set the string value to be saved
|
|
// fall thru
|
|
|
|
case CTRL_TEXTBOX: strcpy(CancelValue, (char *)Ctrl[r].s); // save the current value in case the user cancels
|
|
*Ctrl[r].s = 0; // set it to an empty string
|
|
Ctrl[r].state |= CTRL_SELECTED; // select the number text/box
|
|
PopUpRedrawAll(r, true);
|
|
CurrentRef = InvokingCtrl = r; // tell the keypad what text/number box it is servicing
|
|
GUIKeyDown = -1;
|
|
KeyAltShift = 0;
|
|
DrawControl(r);
|
|
ClickTimer += CLICK_DURATION;
|
|
if(GuiIntDownVector == NULL)
|
|
DrawKeyboard(KEY_OPEN); // initial draw of the keypad
|
|
else
|
|
DelayedDrawKeyboard = true;
|
|
return;
|
|
|
|
case CTRL_FMTBOX: strcpy(CancelValue, (char *)Ctrl[r].s); // save the current value in case the user cancels
|
|
*Ctrl[r].s = 0; // set it to an empty string
|
|
Ctrl[r].state |= CTRL_SELECTED; // select the number text/box
|
|
PopUpRedrawAll(r, true);
|
|
CurrentRef = InvokingCtrl = r; // save the format box being serviced
|
|
GUIKeyDown = -1;
|
|
ClickTimer += CLICK_DURATION;
|
|
if(GuiIntDownVector == NULL)
|
|
DrawFmtBox(KEY_OPEN); // initial draw of the keypad
|
|
else
|
|
DelayedDrawFmtBox = true; // leave it until after the keyboard interrupt
|
|
return;
|
|
|
|
default: Ctrl[r].value = !Ctrl[r].value;
|
|
break;
|
|
}
|
|
CurrentRef = r;
|
|
DrawControl(r);
|
|
ClickTimer += CLICK_DURATION; // sound a "click"
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(TouchUp) {
|
|
// the touch has just been released
|
|
TouchUp = TouchDown = false;
|
|
LastX=TouchX;
|
|
LastY=TouchY;
|
|
if(InvokingCtrl) { // the keyboard/keypad takes complete control when activated
|
|
if(Ctrl[InvokingCtrl].type == CTRL_FMTBOX)
|
|
DrawFmtBox(KEY_KEY_UP);
|
|
else
|
|
DrawKeyboard(KEY_KEY_UP);
|
|
gui_int_down = false;
|
|
return;
|
|
}
|
|
|
|
gui_int_up = true;
|
|
if(CurrentRef) {
|
|
if(Ctrl[CurrentRef].type) {
|
|
if((CurrentPages & (1 << Ctrl[CurrentRef].page)) && !(Ctrl[CurrentRef].state & (CTRL_DISABLED | CTRL_DISABLED2 | CTRL_HIDDEN))) { // ignore if control is disabled or the page is not displayed
|
|
if(CurrentRef) LastRef = CurrentRef;
|
|
switch(Ctrl[CurrentRef].type) {
|
|
case CTRL_AREA:
|
|
case CTRL_BUTTON: Ctrl[CurrentRef].value = 0;
|
|
DrawControl(CurrentRef);
|
|
break;
|
|
|
|
case CTRL_SPINNER: Ctrl[CurrentRef].state &= ~(CTRL_SPINUP | CTRL_SPINDOWN);
|
|
DrawControl(CurrentRef);
|
|
break;
|
|
|
|
default: break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
CurrentRef = 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void SetCtrlState(int r, int state, int err) {
|
|
if(Ctrl[r].type == 0) {
|
|
if(err) error("Control #% does not exist", r);
|
|
} else {
|
|
if(r == CurrentRef) {
|
|
if(Ctrl[r].type == CTRL_BUTTON) {
|
|
Ctrl[r].value = 0;
|
|
UpdateControl(r);
|
|
}
|
|
if(Ctrl[r].type == CTRL_SPINNER) {
|
|
Ctrl[r].state &= ~(CTRL_SPINUP | CTRL_SPINDOWN);
|
|
UpdateControl(r);
|
|
}
|
|
LastRef = CurrentRef = 0;
|
|
gui_int_down = gui_int_up = false;
|
|
}
|
|
Ctrl[r].state |= state;
|
|
if(state & CTRL_HIDDEN)
|
|
DrawControl(r);
|
|
else
|
|
UpdateControl(r);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// this function is used by the CLS command
|
|
void HideAllControls(void) {
|
|
int r;
|
|
for(r = 1; r < Option.MaxCtrls; r++)
|
|
if((CurrentPages & (1 << Ctrl[r].page)) && !(Ctrl[r].state & CTRL_HIDDEN))
|
|
SetCtrlState(r, CTRL_HIDDEN, false);
|
|
}
|
|
|
|
|
|
// This will check if an interrupt is pending and will service if yes.
|
|
// Essentially this runs a short program which does nothing but it gives the
|
|
// interrupt checking routines in ExecuteProgram() a chance to do their job.
|
|
// This routine should be called repeatedly during long delays.
|
|
void ServiceInterrupts(void) {
|
|
char *ttp, *s, tcmdtoken;
|
|
char p[4]={0};
|
|
|
|
CheckAbort();
|
|
g_LocalIndex++; // preserve the current temporary string memory allocations
|
|
ttp = (char *)nextstmt; // save the globals used by commands
|
|
tcmdtoken = cmdtoken;
|
|
s = (char *)cmdline;
|
|
|
|
p[0] = (cmdEND_IF & 0x7f ) + C_BASETOKEN;
|
|
p[1] = (cmdEND_IF >> 7) + C_BASETOKEN; //tokens can be 14-bit
|
|
ExecuteProgram((unsigned char *)p); // execute the program's code
|
|
|
|
cmdline = (unsigned char *)s; // restore the globals
|
|
cmdtoken = tcmdtoken;
|
|
nextstmt = (unsigned char *)ttp;
|
|
g_LocalIndex--;
|
|
g_TempMemoryIsChanged = true; // signal that temporary memory should be checked
|
|
}
|
|
|
|
/* @endcond */
|
|
|
|
void fun_msgbox(void) {
|
|
int i, j, k;
|
|
int x, y, x1, y1, x2, y2, msgnbr, btnnbr, btnwidth;
|
|
char *p, *msg;
|
|
char *btn;
|
|
int btnx1[4], btny1, btnx2[4], btny2;
|
|
long long int timeout;
|
|
if(!Option.MaxCtrls)error("No memory allocated for GUI controls");
|
|
getargs(&ep, 9, (unsigned char *)",");
|
|
if(argc < 3) error("Argument count");
|
|
msg = GetMemory(MAX_CAPTION_LINES * MAXSTRLEN);
|
|
btn = GetMemory(4 * MAXSTRLEN);
|
|
|
|
p = (char *)getCstring(argv[0]);
|
|
i = msgnbr = 0;
|
|
while(*p) {
|
|
if(*p == '~') {
|
|
if(msgnbr + 1 >= MAX_CAPTION_LINES) break;
|
|
msg[msgnbr++ * MAXSTRLEN + i] = 0;
|
|
i = 0; p++;
|
|
} else {
|
|
msg[msgnbr * MAXSTRLEN + i++] = *p++;
|
|
}
|
|
}
|
|
msg[msgnbr++ * MAXSTRLEN + i]= 0;
|
|
|
|
btnnbr = ((argc - 2) / 2) + 1;
|
|
for(i = 0; i < btnnbr; i++)
|
|
strcpy(&btn[i * MAXSTRLEN], (char *)getCstring(argv[(i * 2) + 2]));
|
|
|
|
for(j = i = 0; i < msgnbr; i++)
|
|
if(strlen(&msg[i * MAXSTRLEN]) > j) j = strlen(&msg[i * MAXSTRLEN]);
|
|
j *= gui_font_width;
|
|
|
|
for(k = i = 0; i < btnnbr; i++)
|
|
if(strlen(&btn[i * MAXSTRLEN]) > k) k = strlen(&btn[i * MAXSTRLEN]);
|
|
btnwidth = (k + 2) * gui_font_width;
|
|
k = (btnwidth * btnnbr) + ((btnnbr - 1) * 2 * gui_font_width);
|
|
if(k > j) j = k;
|
|
j += 4 * gui_font_width;
|
|
|
|
i = (((5 + msgnbr) * gui_font_height) / 2) + gui_font_height / 4;
|
|
x1 = HRes/2 - j/2; y1 = VRes/2 - i; x2 = HRes/2 + j/2; y2 = VRes/2 + i;
|
|
|
|
// wait for any pen touch to be lifted
|
|
timeout = mSecTimer + 10000;
|
|
do { ServiceInterrupts();
|
|
} while(TouchState && mSecTimer < timeout) ;
|
|
|
|
PopUpRedrawAll(0, true);
|
|
SpecialDrawRBox(x1, y1, x2, y2, 25, gui_fcolour, gui_bcolour, CTRL_NORMAL);
|
|
|
|
for(i = 0; i < msgnbr; i++)
|
|
SpecialPrintString(HRes/2, y1 + gui_font_height + (gui_font_height * i), JUSTIFY_CENTER, JUSTIFY_TOP, ORIENT_NORMAL, gui_fcolour, gui_bcolour, &msg[i * MAXSTRLEN], CTRL_NORMAL);
|
|
|
|
btny1 = y2 - (gui_font_height * 3);
|
|
btny2 = y2 - gui_font_height;
|
|
for(i = 0; i < btnnbr; i++) {
|
|
btnx1[i] = HRes/2 - (k/2) + (btnwidth * i) + (i * 2 * gui_font_width); btnx2[i] = btnx1[i] + btnwidth;
|
|
DrawBasicButton(btnx1[i], btny1, btnx2[i], btny2, BTN_SIDE_WIDTH, 1, gui_fcolour, CTRL_NORMAL);
|
|
SpecialPrintString(btnx1[i] + btnwidth/2, btny1 + gui_font_height, JUSTIFY_CENTER, JUSTIFY_MIDDLE, ORIENT_NORMAL, gui_bcolour, gui_fcolour, &btn[i * MAXSTRLEN], CTRL_NORMAL);
|
|
}
|
|
|
|
while(1) {
|
|
ServiceInterrupts();
|
|
x = GetTouch(GET_X_AXIS); y = GetTouch(GET_Y_AXIS);
|
|
for(i = 0; i < btnnbr; i++)
|
|
if(x >= btnx1[i] && x <= btnx2[i] && y >= btny1 && y <= btny2)
|
|
break;
|
|
if(i < btnnbr) {
|
|
DrawBasicButton(btnx1[i], btny1, btnx2[i], btny2, BTN_SIDE_WIDTH, 0, gui_fcolour, CTRL_NORMAL);
|
|
SpecialPrintString(btnx1[i] + btnwidth/2 + BTN_CAPTION_SHIFT, btny1 + gui_font_height + BTN_CAPTION_SHIFT, JUSTIFY_CENTER, JUSTIFY_MIDDLE, ORIENT_NORMAL, gui_bcolour, gui_fcolour, &btn[i * MAXSTRLEN], CTRL_NORMAL);
|
|
ClickTimer += CLICK_DURATION; // sound a "click"
|
|
while(GetTouch(GET_X_AXIS) != TOUCH_ERROR) ServiceInterrupts();
|
|
SpecialDrawRBox(x1, y1, x2, y2, 25, gui_bcolour, gui_bcolour, CTRL_NORMAL);
|
|
PopUpRedrawAll(0, false);
|
|
iret = i + 1;
|
|
targ = T_INT;
|
|
FreeMemory((unsigned char *)msg); FreeMemory((unsigned char *)btn);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void fun_ctrlval(void) {
|
|
int r;
|
|
if(!Option.DISPLAY_TYPE) error("Display not configured");
|
|
if(!Option.MaxCtrls)error("No memory allocated for GUI controls");
|
|
if(*ep == '#') ep++;
|
|
r = getint(ep, 1, Option.MaxCtrls);
|
|
if(Ctrl[r].type == 0) error("Control #% does not exist", r);
|
|
if(Ctrl[r].type == CTRL_NBRBOX) {
|
|
if(r == InvokingCtrl){ // is the keypad for the number box being displayed?
|
|
fret = (MMFLOAT)atof(CancelValue); // if NOT in the call back just return the value saved in case of a cancel
|
|
}
|
|
else {
|
|
char a[16];
|
|
FloatToStr(a, Ctrl[r].value, 1, STR_FLOAT_PRECISION, ' '); // convert to a float and put back on the display
|
|
fret = atof(a);
|
|
}
|
|
targ = T_NBR;
|
|
} else if(Ctrl[r].type == CTRL_TEXTBOX || Ctrl[r].type == CTRL_FMTBOX) {
|
|
sret = GetTempMemory(STRINGSIZE); // this will last for the life of the command
|
|
if(r == InvokingCtrl) // is the keypad for the number box being displayed?
|
|
strcpy((char *)sret, CancelValue); // just return the value saved in case of a cancel
|
|
else
|
|
strcpy((char *)sret, (char *)Ctrl[r].s);
|
|
if(Ctrl[r].s[0] == '#' && Ctrl[r].s[1] == '#') *sret = 0; // return a zero length string if it is just "ghost text"
|
|
CtoM(sret); // convert to a MMBasic string
|
|
targ = T_STR;
|
|
} else if(Ctrl[r].type == CTRL_DISPLAYBOX || Ctrl[r].type == CTRL_CAPTION) {
|
|
sret = GetTempMemory(STRINGSIZE); // this will last for the life of the command
|
|
strcpy((char *)sret, (char *)Ctrl[r].s); // copy the string
|
|
CtoM(sret); // convert to a MMBasic string
|
|
targ = T_STR;
|
|
} else {
|
|
char a[16];
|
|
FloatToStr(a, Ctrl[r].value, 1, STR_FLOAT_PRECISION, ' '); // convert to a float and put back on the display
|
|
fret = atof(a);
|
|
targ = T_NBR;
|
|
}
|
|
}
|
|
|
|
|
|
void cmd_ctrlval(void) {
|
|
int r;
|
|
MMFLOAT v;
|
|
if(HRes == 0) error("LCD Panel not configured");
|
|
if(!Option.MaxCtrls)error("No memory allocated for GUI controls");
|
|
if(*cmdline == '#') cmdline++;
|
|
r = getint(cmdline, 1, Option.MaxCtrls);
|
|
if(Ctrl[r].type == 0) error("Control #% does not exist", r);
|
|
while(*cmdline && tokenfunction(*cmdline) != op_equal) cmdline++; // search for the = symbol
|
|
if(!*cmdline) error("Syntax");
|
|
++cmdline; // step over the = symbol
|
|
skipspace(cmdline);
|
|
if(!*cmdline || *cmdline == '\'') error("Syntax");
|
|
switch(Ctrl[r].type) {
|
|
case CTRL_BUTTON:
|
|
case CTRL_SWITCH:
|
|
case CTRL_CHECKBOX:
|
|
case CTRL_RADIOBTN: v = (getnumber(cmdline) != 0);
|
|
if(Ctrl[r].value == v) return; // don't update if no change
|
|
Ctrl[r].value = v;
|
|
break;
|
|
|
|
case CTRL_LED: v = getnumber(cmdline);
|
|
if(v > 1) {
|
|
Ctrl[r].inc = v;
|
|
Ctrl[r].value = 1;
|
|
CheckGuiFlag = true;
|
|
} else {
|
|
int i;
|
|
CheckGuiFlag = false;
|
|
Ctrl[r].inc = 0;
|
|
// scan through all controls to see if any others are in a flash
|
|
// if there is, set the CheckGuiFlag which indicates that flash timing should continue
|
|
for(i = 1; i < Option.MaxCtrls; i++) {
|
|
if(Ctrl[i].type == CTRL_LED && Ctrl[i].inc != 0) {
|
|
CheckGuiFlag = true;
|
|
break;
|
|
}
|
|
}
|
|
if(Ctrl[r].value == v) return; // don't update if no change
|
|
Ctrl[r].value = v;
|
|
}
|
|
break;
|
|
|
|
case CTRL_SPINNER:
|
|
case CTRL_GAUGE:
|
|
case CTRL_BARGAUGE: v = getnumber(cmdline);
|
|
if(v < Ctrl[r].min) v = Ctrl[r].min;
|
|
if(v > Ctrl[r].max) v = Ctrl[r].max;
|
|
if(Ctrl[r].value == v) return; // don't update if no change
|
|
Ctrl[r].value = v;
|
|
if(Ctrl[r].type == CTRL_SPINNER) FloatToStr((char *)Ctrl[r].s, v, 0, STR_AUTO_PRECISION, ' '); // update the displayed string
|
|
break;
|
|
|
|
case CTRL_NBRBOX: if(strlen((char *)cmdline) > 3 && cmdline[0] == '"' && cmdline[1] == '#' && cmdline[2] == '#') {
|
|
// the user wants ghost text
|
|
strcpy((char *)Ctrl[r].s, (char *)getCstring(cmdline));
|
|
Ctrl[r].value = 0;
|
|
} else {
|
|
// a normal number
|
|
v = getnumber(cmdline);
|
|
if(r != InvokingCtrl && Ctrl[r].value == v) return; // don't update if no change
|
|
Ctrl[r].value = v;
|
|
FloatToStr((char *)Ctrl[r].s, v, 0, STR_AUTO_PRECISION, ' '); // update the displayed string
|
|
}
|
|
break;
|
|
|
|
case CTRL_FRAME:
|
|
case CTRL_CAPTION:
|
|
memset(Ctrl[r].s, ' ', strlen((char *)Ctrl[r].s)); // set the caption to spaces
|
|
UpdateControl(r); // erase the existing text
|
|
// fall through
|
|
|
|
case CTRL_DISPLAYBOX:
|
|
case CTRL_TEXTBOX:
|
|
case CTRL_FMTBOX: strcpy((char *)Ctrl[r].s, (char *)getCstring(cmdline)); break;
|
|
}
|
|
|
|
if(!(Ctrl[r].state & CTRL_DISABLED2)) UpdateControl(r); // don't update if the gauge is disabled by a keypad (updating may mess they keypad)
|
|
}
|
|
/*
|
|
* @cond
|
|
* The following section will be excluded from the documentation.
|
|
*/
|
|
|
|
|
|
void fun_mmhpos(void) {
|
|
if(!Option.DISPLAY_TYPE) error("Display not configured");
|
|
iret = CurrentX;
|
|
targ = T_INT;
|
|
}
|
|
|
|
|
|
void fun_mmvpos(void) {
|
|
if(!Option.DISPLAY_TYPE) error("Display not configured");
|
|
iret = CurrentY;
|
|
targ = T_INT;
|
|
}
|
|
|
|
|
|
|
|
/*void cmd_backlight(void) {
|
|
if(HRes != 0 && Option.DISPLAY_TYPE > SSD_PANEL) error("SSD1963 display only");
|
|
SetBacklightSSD1963(getint(cmdline, 0, 100));
|
|
}*/
|
|
|
|
void ResetGUI(void) {
|
|
int i;
|
|
InvokingCtrl = CurrentRef = LastRef = 0;
|
|
LastX = LastY = TOUCH_ERROR;
|
|
gui_int_down = gui_int_up = false;
|
|
GuiIntDownVector = GuiIntUpVector = NULL;
|
|
GUIKeyDown = 0;
|
|
KeyAltShift = 0;
|
|
last_x2 = 100;
|
|
last_y2 = 100;
|
|
last_fcolour = gui_fcolour;
|
|
last_bcolour = gui_bcolour;
|
|
last_inc = 1;
|
|
last_min = -FLT_MAX;
|
|
last_max = FLT_MAX;
|
|
|
|
SetupPage = 0;
|
|
CurrentPages = 1;
|
|
for(i = 1; i < Option.MaxCtrls; i++) {
|
|
if(Ctrl[i].s) FreeMemorySafe((void **)&Ctrl[i].s);
|
|
if(Ctrl[i].fmt) FreeMemorySafe((void **)&Ctrl[i].fmt);
|
|
memset(&Ctrl[i],0,sizeof(struct s_ctrl));
|
|
}
|
|
}
|
|
|
|
|
|
// This is called by Timer.c every mSec
|
|
// it scans through all controls looking for a LED with a timeout set (in Ctrl[r].inc) and decrements it
|
|
void CheckGuiTimeouts(void) {
|
|
int r;
|
|
for(r = 1; r < Option.MaxCtrls; r++) {
|
|
if(Ctrl[r].type == CTRL_LED && Ctrl[r].inc > 1) {
|
|
Ctrl[r].inc--; // decrement the timeout
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// This implements a LED flash
|
|
// it is called by check_interrupt()
|
|
// it scans through all controls looking for a LED with a timeout set (in Ctrl[r].inc)
|
|
void CheckGui(void) {
|
|
int r;
|
|
CheckGuiFlag = false;
|
|
for(r = 1; r < Option.MaxCtrls; r++) {
|
|
if(Ctrl[r].type == CTRL_LED) {
|
|
if(Ctrl[r].inc > 1)
|
|
CheckGuiFlag = true; // keep calling this function while LEDs have timeouts
|
|
if(Ctrl[r].inc == 1) { // if this one has timed out
|
|
Ctrl[r].inc = 0;
|
|
Ctrl[r].value = 0; // turn off the LED
|
|
UpdateControl(r);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/* @endcond */
|