/*********************************************************************************************************************** PicoMite MMBasic GUI.c Geoff Graham, Peter Mather Copyright (c) 2021, 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 . 5. Neither the name of the 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 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 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 */