//********************************************************************************
//* File       : AnsiCmdDef.hpp                                                  *
//* Author     : Mahlon R. Smith                                                 *
//*              Copyright (c) 2022-2026 Mahlon R. Smith, The Software Samurai   *
//*                 GNU GPL copyright notice below                               *
//* Date       : 31-Mar-2026                                                     *
//* Version    : (see AnsiCmdVersion string in AnsiCmd.cpp)                      *
//*                                                                              *
//* Description: General definitions for the AnsiCmd class.                      *
//*    - ANSI escape sequences defined in the standard, listed as the "aeSeq"    *
//*      (Ansi Escape SEQuence) enumerated type.                                 *
//*    - Color-attribute bitfields listed as the "acAttr" (AnsiCmd ATTRibutes)   *
//*      enumerated type.                                                        *
//*    - The acaExpand class handles manipulation of the color-attribute         *
//*      bitfield data.                                                          *
//*    - Structured parameters for setting RGB (Red/Green/Blue) color attributes *
//*      defined as the "WebSafeRGB" enumerated type.                            *
//*    - The skField and skForm classes which define text input fields and other *
//*      user control types within the ACWin window class.                       *
//*    - The WinPos class defines the mechanism for cursor positioning within    *
//*      the terminal window.                                                    *
//*    - Special character definitions (e.g. line drawing characters) and        *
//*      keycode definitions (e.g. cursor control keys).                         *
//*    - The AC_Box class and AC_Line class define structured access to          *
//*      line-drawing operations in the terminal window.                         *
//*    - Miscellaneous defined constants.                                        *
//*                                                                              *
//********************************************************************************

using namespace std ;      //* C++ Scope quailfier

//*****************
//* Include Files *
//*****************
#include <unistd.h>           //* UNIX system interface
#include <cstdlib>            //* Misc. functionality
#include <cstdint>            //* [u]int*_t definitions (C++13)
#include <climits>            //* C++ version of limits.h
#include <sys/ioctl.h>        //* Get low-level terminal info (e.g. struct winsize)
#include <termios.h>          //* Terminal attributes (i.e. /usr/include/asm-generic/termios.h)
#include <signal.h>           //* Signal capture (CTRL+C capture)
#include <limits>             //* Define max size of input stream
#include <fcntl.h>            //* For fcntl() function (non-blocking read)
#include <fstream>            //* File I/O streams
#include <filesystem>         //* filesystem (C++17)
#include <sys/stat.h>         //* file stats (statx function, etc.)
#include "TFMan.hpp"          //* manage temporary files
#include "gString.hpp"        //* Text analysis and formatting

//***************
//* Definitions *
//***************
//* Convert declaration of C structure for terminal dimensions to C++ format.  *
typedef struct winsize WinSize ;
//* Convert declaration of C structure for terminal I/O settings to C++ format.*
typedef struct termios TermIos ;
//* Convert declaration of C structure for clock access to C++ format.*
typedef struct timespec TimeSpec ;

//* General constant values *
const wchar_t NULLCHAR  = L'\0' ;   // ASCII null character  U+0000
const wchar_t BACKSPACE = L'\b' ;   // ASCII backspace       U+0008
const wchar_t hTAB      = L'\t' ;   // ASCII horizontal tab  U+0009
const wchar_t NEWLINE   = L'\n' ;   // ASCII linefeed        U+000A
const wchar_t CR        = L'\r' ;   // ASCII carriage return U+000D (Windoze uses this)
const wchar_t SPACE     = L' ' ;    // ASCII space           U+0020
const wchar_t DASH      = L'-' ;    // ASCII hyphen/minus    U+002D
const wchar_t DOT       = L'.' ;    // ASCII full stop       U+002E
const wchar_t fSLASH    = L'/' ;    // ASCII forward slash   U+002F
const wchar_t CARET     = L'^' ;    // ASCII caret           U+005E
const wchar_t sDASH     = 0x00AD ;  // Unicode &shy;         U+00AD (soft hyphen)
const wchar_t uMINUS    = 0x2212 ;  // Unicode &minus;       U+2012
const wchar_t nDASH     = 0x2013 ;  // Unicode &ndash;       U+2013
const wchar_t mDASH     = 0x2014 ;  // Unicode &mdash;       U+2014
const wchar_t CJKSPACE  = 0x3000 ;  // CJK space             U+3000 (two-column character)
const wchar_t CJKCOMMA  = 0x3001 ;  // CJK comma             U+3001 (two-column character)
const wchar_t CJKSTOP   = 0x3002 ;  // CJK full stop         U+3002 (two-column character)
const wchar_t wcsENTER  = NEWLINE ; // another name for the newline character
const short   ZERO      = (0) ;     // numeric zero

//* For Radiobuttons, default "selected" character. *
//* small black diamond (♦ single-column character) *
const wchar_t wcsSDIAMOND = 0x002666 ;

//* For captured escape sequences, control codes and combination keys,  *
//* the captured data are converted to ordinary printing characters.    *
//* The converted keycodes fall generally within the Unicode range which*
//* includes the "general punctuation", "arrow key" and "mathematical   *
//* operators" groups defined as: U+2000 through U+2290.                *
//*    (see isPrint() for additional information)                       *
const wchar_t RESKEY_MIN = 0x002000 ; // corresponds to wcsF01
const wchar_t RESKEY_MAX = 0x002290 ; // corresponds to wcsALT_RB

//* Cursor Shapes (style)                           *
//* These are VT520 i.e. XTerm definitions.         *
//* Additional styles and colors may be added later.*
enum CurStyle: short
{
   csBblock = 0,  // Full-character cell, blinking block
   csDflt   = 1,  // Cursor type defined in the terminal settings (usually csBblock)
   csSblock = 2,  // Full-character cell, steady (non-blinking) block
   csBuline = 3,  // Blinking underline
   csSuline = 4,  // Steady underline
   csBvbar  = 5,  // Blinking vertical bar
   csSvbar  = 6,  // Steady vertical bar
   csHide   = 7,  // Hide cursor (make it invisible)
   csShow   = 8,  // Show cursor (make it visible)

} ;

//* Cursor positioning and reporting. (Adapted from author's NcDialog API) *
class WinPos
{
   public:
   //* Default constructor.                           *
   //* Terminal window positions are 1-based offsets) *
   //* API Window positions are 0-based offsets)      *
   WinPos ( void ) { row = 0 ; col = 0 ; }
   //* Initialization constructors *
   WinPos( const short& rval, const short& cval ) : row(rval), col(cval) {}
   WinPos( const WinPos& wpA, const WinPos& wpB )
   { this->row = wpA.row + wpB.row ; this->col = wpA.col + wpB.col ; }
   WinPos( const WinPos& wpA, const WinPos& wpB, const WinPos& wpC )
   { this->row = wpA.row + wpB.row + wpC.row ; this->col = wpA.col + wpB.col+ wpC.col ; }

   void reset ( void ) { this->row = this->col = 0 ; }
   bool operator == ( const WinPos& wp )
   {
      bool eq = false ;
      if ( (wp.row == this->row) && (wp.col == this->col) ) { eq = true ; }
      return eq ;
   }
   bool operator != ( const WinPos& wp )
   {
      bool eq = false ;
      if ( (wp.row != this->row) || (wp.col != this->col) ) { eq = true ; }
      return eq ;
   }
   void operator += ( const WinPos& wpA )
   { this->row += wpA.row ; this->col += wpA.col ; }

   //* Public data members *
   short row ;                // Row number    (offset)
   short col ;                // Column number (offset)
} ;   // WinPos

//* Line-drawing codes for drawing lines and window borders *
enum LineType : short 
{ 
   ltHORIZ,  ltVERT, 
   ltSINGLE, ltSINGLEBOLD, ltDUAL,
   ltDASH2,  ltDASH2BOLD, 
   ltDASH3,  ltDASH3BOLD,
   ltDASH4,  ltDASH4BOLD
} ;

//******************************************************
//** All ANSI escape sequences ("aesXXX") and control **
//** codes defined in ECMA-48, ISO/IEC 6429 standards.**
//** Note that not all of these options are supported **
//** by all operating systems and environments.       **
//******************************************************
enum aeSeq : short
{
   //** Text Display Modification Options. **
   //** (supported by most Linux systems   **
   //**  and terminal emulator software)   **
   aesRESET = 0,              // reset all attributes
   aesBOLD,                   // bold (increase intensity)
   aesFAINT,                  // faint/dim (decrease intensity)
   aesITALIC,                 // italic (or inverse)
   aesUNDERLINE,              // underline
   aesBLINK_SLOW,             // slow blink
   aesBLINK_FAST,             // fast blink
   aesREVERSE,                // reverse foreground and background colors
   aesCONCEAL,                // hide text
   aesXOUT,                   // crossed out text (marked for deletion)
   aesFRACTUR,                // a variety of German calligraphy
   aesDBL_UNDERLINE,          // double-underline (or bold off)
   aesFRAMED,                 // "frame" the text
   aesENCIRCLE,               // "encircle" the text
   aesOVERLINE,               // overlined text
   aesBOLD_OFF,               // normal intensity (neither bold nor faint)
   aesITALIC_OFF,             // italic off (and Fractur off)
   aesUNDERLINE_OFF,          // underline off
   aesBLINK_OFF,              // blinking off
   aesREVERSE_OFF,            // reverse off
   aesCONCEAL_OFF,            // conceal off
   aesXOUT_OFF,               // not crossed out
   aesFRM_ENC_OFF,            // not framed, not encircled
   aesOVERLINE_OFF,           // overline off

   //** Foreground Color Attributes (3-bit and 4-bit color selection) **
   aesFG_BLACK,               // set foreground color to black (i==24)
   aesFG_RED,                 // set foreground color to red
   aesFG_GREEN,               // set foreground color to green
   aesFG_BROWN,               // set foreground color to brown
   aesFG_BLUE,                // set foreground color to blue
   aesFG_MAGENTA,             // set foreground color to magenta
   aesFG_CYAN,                // set foreground color to cyan
   aesFG_GREY,                // set foreground color to grey (dull white)
   aesFGb_BLACK,              // set foreground color to bright black (grey)
   aesFGb_RED,                // set foreground color to bright red
   aesFGb_GREEN,              // set foreground color to bright green
   aesFGb_BROWN,              // set foreground color to yellow (bright brown)
   aesFGb_BLUE,               // set foreground color to bright blue
   aesFGb_MAGENTA,            // set foreground color to bright magenta
   aesFGb_CYAN,               // set foreground color to bright cyan
   aesFGb_GREY,               // set foreground color to white (bright grey)
   aesFG_DFLT,                // set foreground color to terminal default

   //** Background Color Attributes (3-bit and 4-bit color selection) **
   aesBG_BLACK,               // set background color to black (i==41)
   aesBG_RED,                 // set background color to red
   aesBG_GREEN,               // set background color to green
   aesBG_BROWN,               // set background color to brown
   aesBG_BLUE,                // set background color to blue
   aesBG_MAGENTA,             // set background color to magenta
   aesBG_CYAN,                // set background color to cyan
   aesBG_GREY,                // set background color to grey (dull white)
   aesBGb_BLACK,              // set background color to bright black (grey)
   aesBGb_RED,                // set background color to bright red
   aesBGb_GREEN,              // set background color to bright green
   aesBGb_BROWN,              // set background color to yellow (bright brown)
   aesBGb_BLUE,               // set background color to bright blue
   aesBGb_MAGENTA,            // set background color to bright magenta
   aesBGb_CYAN,               // set background color to bright cyan
   aesBGb_GREY,               // set background color to bright white
   aesBG_DFLT,                // set background color to terminal default

   //** Indexed Color Attribute Options (8-bit color and greyscale) **
   //*  0 - 7     basic standard color indices
   //*  8 - 15    basic intense colors indices
   //*  16 - 231  216 8-bit color indices
   //*  232 - 255 greyscale color indices (24 steps)
   aesFG_INDEX,               // set foreground color by lookup table index (0-255)
   aesBG_INDEX,               // set background color by lookup table index (0-255)

   //** Red/Green/Blue Color Attribute Options (24-bit color) **
   //*  Encoding. Fg: \x1B[38;2;(r);(g);(b)m  Bg: \x1B[48;2;(r);(g);(b)m
   aesFG_RGB,                 // set foreground color by r/g/b register number
   aesBG_RGB,                 // set background color by r/g/b register number

   //** Cursor-positioning Options **
   aesCUR_HOME,               // set cursor at 1,1
   aesCUR_ABSPOS,             // set (absolute) cursor at specified row;column position
   aesCUR_ROWSUP,             // move cursor upward by specified number of rows
   aesCUR_ROWSDN,             // move cursor downward by specified number of rows
   aesCUR_COLSRT,             // move cursor right by specified number of columns
   aesCUR_COLSLT,             // move cursor left by specified number of columns
   aesCUR_NEXTROW,            // move cursor to beginning of row below (or downward row count?)
   aesCUR_PREVROW,            // move cursor to beginning of row above (or upward row count?)
   aesCUR_ABSCOL,             // move cursor to specified column on current line
   aesCUR_REPORT,             // request current cursor position
   aesCUR_ROWDEL,             // delete the current row (scrolling the lower rows upward)
   aesCUR_SAVE_DEC,           // save current cursor position (DEC)
   aesCUR_RESTORE_DEC,        // restore cursor to last saved position (DEC)
   aesCUR_SAVE_SCO,           // save current cursor position (SCO, unreliable)
   aesCUR_RESTORE_SCO,        // restore cursor to last saved position (SCO, unreliable)

   //** Text Erasure Options **
   aesERASE_BOW,              // erase from cursor to bottom of window
   aesERASE_TOW,              // erase from cursor to top of window
   aesERASE_WIN,              // erase entire window ('clear')
   aesERASE_SAVED,            // erase "saved" lines
   aesERASE_EOL,              // erase from cursor to end of line
   aesERASE_BOL,              // erase from cursor to beginning of line
   aesERASE_LINE,             // erase entire line

   //** Alternate Font group **
   aesPRIMARY_FONT,           // primary font      (font 0)
   aesALTERNATE_FONT,         // alternate fonts (font 1 through font 9)

   //** ASCII Control Codes. **
   aesBELL,                   // '\a' terminal "bell" (beep, ping, etc.)
   aesBKSP,                   // '\b' backspace
   aesHTAB,                   // '\t' horizontal tab
   aesLINEFEED,               // '\n' linefeed (end-of-line)
   aesVTAB,                   // '\v' vertical tab
   aesFORMFEED,               // '\f' formfeed (new page)
   aesRETURN,                 // '\r' carriage return (used by Windoze)
   aesESC,                    // '\e' escape character ( ^[ or \x1B[ )
   aesDEL,                    // delete character under cursor

   //** Ideogram (logogram) group of options **
   aesIDEO_UNDER,             // ideogram underlined
   aesIDEO_UDOUBLE,           // ideogram double-underlined
   aesIDEO_OVER,              // ideogram overlined
   aesIDEO_ODOUBLE,           // ideogram double-overlined
   aesIDEO_STRESS,            // ideogram stress marking
   aesIDEO_OFF,               // all ideogram attributes off

   //* NOTE: The Aixterm-specific bright fg/bg codes are not  *
   //*       part of the ANSI standard, but may be supported. *
   aesAIXFGb_BLACK,           // set foreground color to bright black (grey)
   aesAIXFGb_RED,             // set foreground color to bright red
   aesAIXFGb_GREEN,           // set foreground color to bright green
   aesAIXFGb_BROWN,           // set foreground color to yellow (bright brown)
   aesAIXFGb_BLUE,            // set foreground color to bright blue
   aesAIXFGb_MAGENTA,         // set foreground color to bright magenta
   aesAIXFGb_CYAN,            // set foreground color to bright cyan
   aesAIXFGb_GREY,            // set foreground color to white (bright grey)

   aesAIXBGb_BLACK,           // set background color to bright black (grey)
   aesAIXBGb_RED,             // set background color to bright red
   aesAIXBGb_GREEN,           // set background color to bright green
   aesAIXBGb_BROWN,           // set background color to yellow (bright brown)
   aesAIXBGb_BLUE,            // set background color to bright blue
   aesAIXBGb_MAGENTA,         // set background color to bright magenta
   aesAIXBGb_CYAN,            // set background color to bright cyan
   aesAIXBGb_GREY,            // set background color to white (bright grey)

   aesCOUNT                   // number of items in enumerated type list
} ;   // enum aeSeq

//********************************************************************
//* acAttr (AnsiCmd Attributes) provides application-level color-    *
//* attribute and text-modifier configuration for Linux terminal     *
//* applications.                                                    *
//* Specify color attributes and text modifiers in a consistent      *
//* format which encapsulates the primitive ANSI escape sequences    *
//* defined for terminal-window text formatting. (see enum aeSeq)    *
//*                                                                  *
//* Within the AnsiCmd-class methods, these values are mapped to     *
//* their corresponding ANSI escape sequences (or group of           *
//* sequences). This allows the application to focus on algorithms   *
//* and user interaction rather than reading and writing binary data *
//* within the terminal window.                                      *
//*                                                                  *
//* acAttr is defined as an enumerated type, but is constructed      *
//* as a group of macros defining bitfields. This allows the macros  *
//* to be identified as a coherent group while also allowing         *
//* bitwise OR operations for the desired foreground and background  *
//* attributes including text-modifier attributes.                   *
//* For example, combine foreground, background attributes PLUS the  *
//* "bold" and "italic" text attributes, then pass the value as an   *
//* argument to one of the application-level methods.                *
//* acAttr txtAttr =                                                 *
//*        acAttr(acaFG_GREEN | acaBG_GREY | acaBOLD | acaITALIC) ;  *
//* acSetAttributes ( txtAttr ) ;                                    *
//*                                                                  *
//* -- Technical Note: The compiler will complain when performing    *
//*    mathematical operations on members of an enumerated type;     *
//*    therefore, be sure to cast the OR'd value back to the         *
//*    acAttr type. For instance, the following would cause a        *
//*    compiler warning: acAttr a;  a |= acaBOLD;                    *
//*    Instead, use the following syntax: a = acAttr(a | acaBOLD);   *
//*                                                                  *
//* See also the notes in the header of acaExpand-class 'decode()'   *
//* method for more details.                                         *
//********************************************************************
enum acAttr : uint32_t
{
   //** Foreground and Background color index fields **
   acaFG_INDEX    = 0x000000FF,  // Foreground index
   acaBG_INDEX    = 0x0000FF00,  // Background index

   //** Text modifier flags **
   acaPLAINTXT    = 0x00000000,  // plain text, no modifier flags set
   acaBOLD        = 0x00010000,  // bold-text modifier
   acaITALIC      = 0x00020000,  // italic-text modifier
   acaUNDERLINE   = 0x00040000,  // underline-text modifier
   acaOVERLINE    = 0x00080000,  // overline-text modifier
   acaXOUT        = 0x00100000,  // x-out (strikethrough) text modifier
   acaBLINK       = 0x00200000,  // blinking-text modifier
   acaCONCEAL     = 0x00400000,  // concealed (invisible) text
   acaREVERSE     = 0x00800000,  // reversed foreground/background color attributes

   //** Other flags **
   acaCLEAR_MODS  = 0x01000000,  // clear (reset) specified text modifiers (or all modifiers)
   acaFG_DFLT     = 0x02000000,  // Terminal default-foreground color attribute
                                 // (value in acaFG_INDEX field will be ignored)
   acaFG_RGB      = 0x04000000,  // if set, acaFG_INDEX contains the index of 
                                 // "web-safe" RGB color attribute for foreground
   acaBG_DFLT     = 0x08000000,  // Terminal default-foreground color attribute
                                 // (value in acaBG_INDEX field will be ignored)
   acaBG_RGB      = 0x10000000,  // if set, acaBG_INDEX contains the index of 
                                 // "web-safe" RGB color attribute for background
   acaRES1        = 0x20000000,  // reserved bit 1
   acaRES2        = 0x40000000,  // reserved bit 2
   acaUSEDFLT     = 0x80000000,  // placeholder for optional parameters (ignore the value)

   //** Foreground color attributes **
   acaFG_BLACK    = 0x00000000,  // foreground Black
   acaFG_RED      = 0x00000001,  // foreground Red
   acaFG_GREEN    = 0x00000002,  // foreground Green
   acaFG_BROWN    = 0x00000003,  // foreground Brown
   acaFG_BLUE     = 0x00000004,  // foreground Blue
   acaFG_MAGENTA  = 0x00000005,  // foreground Magenta
   acaFG_CYAN     = 0x00000006,  // foreground Cyan
   acaFG_GREY     = 0x00000007,  // foreground Grey
   acaFGb_BLACK   = 0x00000008,  // bold foreground Black
   acaFGb_RED     = 0x00000009,  // bold foreground Red
   acaFGb_GREEN   = 0x0000000A,  // bold foreground Green
   acaFGb_BROWN   = 0x0000000B,  // bold foreground Brown
   acaFGb_BLUE    = 0x0000000C,  // bold foreground Blue
   acaFGb_MAGENTA = 0x0000000D,  // bold foreground Magenta
   acaFGb_CYAN    = 0x0000000E,  // bold foreground Cyan
   acaFGb_GREY    = 0x0000000F,  // bold foreground Grey

   //** Background color attributes **
   acaBG_BLACK    = 0x00000000,  // background Black
   acaBG_RED      = 0x00000100,  // background Red
   acaBG_GREEN    = 0x00000200,  // background Green
   acaBG_BROWN    = 0x00000300,  // background Brown
   acaBG_BLUE     = 0x00000400,  // background Blue
   acaBG_MAGENTA  = 0x00000500,  // background Magenta
   acaBG_CYAN     = 0x00000600,  // background Cyan
   acaBG_GREY     = 0x00000700,  // background Grey
   acaBGb_BLACK   = 0x00000800,  // bold background Black
   acaBGb_RED     = 0x00000900,  // bold background Red
   acaBGb_GREEN   = 0x00000A00,  // bold background Green
   acaBGb_BROWN   = 0x00000B00,  // bold background Brown
   acaBGb_BLUE    = 0x00000C00,  // bold background Blue
   acaBGb_MAGENTA = 0x00000D00,  // bold background Magenta
   acaBGb_CYAN    = 0x00000E00,  // bold background Cyan
   acaBGb_GREY    = 0x00000F00,  // bold background Grey

   acaFG_MASK     = (acaFG_INDEX | acaFG_DFLT | acaFG_RGB), // Foreground bitmask
   acaBG_MASK     = (acaBG_INDEX | acaBG_DFLT | acaBG_RGB), // Background bitmask
   acaATTR_DFLT   = (acaFG_DFLT  | acaBG_DFLT), // Default fgnd OR'd with default bgnd
   acaFLAG_MASK   = 0xFFFF0000,  // All binary flags
   acaMOD_MASK    = 0x00FF0000,  // Text-modifier flags only

} ;   // enum acAttr

//********************************************************************
//* Definition of parameters for setting "web-safe" RGB foreground   *
//* and background. They are used for calls to the public methods:   *
//*      acSetWebFg( WebSafeRGB hue, uint8_t shade );                *
//*      acSetWebBg( WebSafeRGB hue, uint8_t shade );                *
//* and the private method:                                          *
//*      setRgbWeb( bool bgnd, WebSafeRGB hue, uint8_t shade );      *
//********************************************************************
enum WebSafeRGB : uint8_t
{  //* These two values are used for internal calculations. *
   wsrgbSHADES  = 36,            // number of shades in each web-safe color group
   wsrgbSTEP    = 6,             // "web-safe" color attribute step value

   //* These values define the range of the "shade" parameter for    *
   //* setting the RGB attributes via calls to the above RGB methods.*
   wsrgbSHADE_MIN = 0,               // minimum shade within a color block
   wsrgbSHADE_MAX = wsrgbSHADES - 1, // maximum shade within a color block
   wsrgbSHADE_MED = wsrgbSHADES / 2, // medium  shade within a color block

   //* These values designate the "hue" parameter (color block) for  *
   //* setting the RGB attributes via calls to the above RGB methods.*
   wsrgbMIN     = 0,                            // minimum web-safe color index
   wsrgbBLACK   = wsrgbMIN,                     // Rgb (black)
   wsrgbRED     = wsrgbBLACK + 1,               // base color index (red block)
   wsrgbGREEN   = wsrgbRED + wsrgbSHADES,       // base color index (green block)
   wsrgbBLUE    = wsrgbGREEN + wsrgbSHADES,     // base color index (blue block)
   wsrgbBROWN   = wsrgbBLUE + wsrgbSHADES,      // base color index (brown block)
   wsrgbMAGENTA = wsrgbBROWN + wsrgbSHADES,     // base color index (magenta block)
   wsrgbCYAN    = wsrgbMAGENTA + wsrgbSHADES,   // base color index (cyan block)
   wsrgbGREY    = wsrgbCYAN + wsrgbSHADES,      // base color index (grey block)
   wsrgbMAX     = wsrgbGREY + wsrgbSHADES - 1,  // maximum web-safe color index
} ;   // enum WebSafeRGB

//***********************************************************
//* Decode an acAttr bitfield into its component parts.     *
//* As a convenience during development, all data members   *
//* are declared public; HOWEVER, all data members except   *
//* 'acaVal' should be considered as read-only.             *
//***********************************************************
class acaExpand
{
   public:
   ~acaExpand ( void ) { /* nothing to do */ }     // destructor
   acaExpand ( void ) ;                            // default constructor
   //* Constructor: Decode the specified bitfield value.*
   acaExpand ( acAttr bits ) ;

   //* The operator= overload decodes the specified value.*
   void operator= ( acAttr acaValue ) { this->decode ( acaValue ) ; }
   //* Decode the specified bitfield value.*
   void decode ( acAttr bits ) ;
   //* Add (or remove) one or more elements to (from) the bitfield value.*
   acAttr modify ( acAttr acaValue, bool add = true ) ;
   //* Reset (clear) all data members. Optionally, reset text-mod flags only.*
   void reset ( bool modsOnly = false ) ;
   //* Update attribute tracking data using a member of enum aeSeq.*
   void update ( aeSeq aesValue, uint8_t colorIndx = ZERO ) ;
   //* Construct an acAttr attribute bitmap from component values. *
   acAttr compose ( void ) ;
   //* Swap foreground and background attributes *
   bool  swap ( void ) ;
   //* Convert between acAttr (bitfield) and aeSeq (ANSI escape sequence name) *
   aeSeq bits2aeseq ( acAttr acaBits, uint8_t byteIndx, bool isFgnd, bool isRgb = false ) ;
   //* Convert between aeSeq (ANSI escape sequence name) and acAttr (bitfield) *
   acAttr aeseq2bits ( aeSeq aesCmd, bool& setFlag ) ;
   //* Convert web-safe RGB index into RGB component values *
   bool  web2regs ( uint8_t webrgb, bool fgnd ) ;

   //* Construct an acAttr bitfield from web-safe RGB color indices *
   acAttr buildRgb ( bool fg, WebSafeRGB fgVal, 
                     bool bg = false, WebSafeRGB bgVal = wsrgbMIN ) const ;
   //* Construct an acAttr bitfield from 8-bit color indices *
   acAttr build8Bit ( bool fg, uint8_t fgVal, 
                      bool bg = false, uint8_t bgVal = ZERO ) const ;

   //*************************
   //** Public Data Members **
   //*************************
   acAttr     acaVal ;     // full, encoded acAttr value
   uint32_t   fgBits ;     // nine bits defining foreground: index byte + fg default flag
   uint32_t   bgBits ;     // nine bits defining background: index byte + bg default flag
   uint32_t   allFlags ;   // all binary flags (excludes fg/bg indices)
   uint32_t   modFlags ;   // text-modification flags
   acAttr     acaFgnd ;    // the isolated 8 bits that define the foreground index
   acAttr     acaBgnd ;    // the isolated 8 bits that define the background index
   aeSeq      aesFgType ;  // Fgnd attr type: aesFG_DFLT, aesFG_INDEX, aesFG_RGB
   aeSeq      aesBgType ;  // Bgnd attr type: aesBG_DFLT, aesBG_INDEX, aesBG_RGB
   aeSeq      aesFgnd ;    // 4-bit foreground (member of enum aeSeq)
   aeSeq      aesBgnd ;    // 4-bit background (member of enum aeSeq)
   uint8_t    fgIndex ;    // foreground byte index
   uint8_t    bgIndex ;    // background byte index
   WebSafeRGB fgHue ;      // if RGB foreground, color group (black,red...)
   uint8_t    fgShade ;    // if RGB foreground, color shade (wsrgbSHADE_MIN-wsrgbSHADE_MAX)
   WebSafeRGB bgHue ;      // if RGB background, color group (black,red...)
   uint8_t    bgShade ;    // if RGB background, color shade (wsrgbSHADE_MIN-wsrgbSHADE_MAX)
   uint8_t    rgbFgR ;     // if RGB foreground, Red register value
   uint8_t    rgbFgG ;     // if RGB foreground, Green register value
   uint8_t    rgbFgB ;     // if RGB foreground, Blue register value
   uint8_t    rgbBgR ;     // if RGB background, Red register value
   uint8_t    rgbBgG ;     // if RGB background, Green register value
   uint8_t    rgbBgB ;     // if RGB background, Blue register value
   bool       fgDflt ;     // if set, terminal default foreground (ignore fg index bits)
   bool       bgDflt ;     // if set, terminal default background (ignore bg index bits)
   bool       boldMod ;    // if set, Bold (intense) text
   bool       italicMod ;  // if set, Italic text
   bool       ulineMod ;   // if set, Underlined text
   bool       olineMod ;   // if set, Overlined text
   bool       xoutMod ;    // if set, X-out (strikethrough) text
   bool       blinkMod ;   // if set, Blinking text
   bool       invisMod ;   // if set, Invisible (concealed) text
   bool       revMod ;     // if set, Reversed foreground and background
   bool       clrMods ;    // if set, Clear existing mods before setting new mods
   bool       fgRgb ;      // if set, rgbRed, rgbGreen,rgbBlue contain the foreground color
   bool       bgRgb ;      // if set, rgbRed, rgbGreen,rgbBlue contain the background color
} ;   // acaExpand

//***********************************************************
//* User input field definition. This defines the position, *
//* dimensions and type of the field as well as the text    *
//* and cursor position within the field.                   *
//***********************************************************
#define DEBUG_SPINNER (0)

enum FldType : short // Field Type for fields defined by the skField class
{
   ftPB,          // PushButton (select)
   ftRB,          // Radio Button (on/off)
   ftTB,          // Text Box (edit text)
   ftSB,          // ScrollBox (view and scroll text)
   ftMB,          // Menu Box
   #if DEBUG_SPINNER != 0
   ftSP,          // SPinner (numeric-range selection)
   #endif   // DEBUG_SPINNER
//   ftSL,          // SLider (magnitude selection)
//   ftDD,          // DropDown menu
//   ftBB,          // BillBoard (auto-scrolling text)
//   ftSE,          // Scroll box External data
} ;

enum FloType : short // Automatic line-break option for text in skField class.
{
   floToken,      // Automatic line breaks by token (token delimited by whitespace)
   floHyphen,     // Same as floToken, but also allow break at hyphens.
   floChar,       // Automatic line break by character (fast-but-ugly)
                  // (Use this option if text is pre-formatted.)
   floShift,      // Shift the display rows to bring the insertion-point (IP)
                  // into visible space. This is text-editor mode.
   floDflt,       // Placeholder: Indicates that current setting should be used.
} ;

const CurStyle istyleDFLT = csBblock,     // default cursor style Insert mode
               ostyleDFLT = csBuline ;    // default cursor style Overstrike mode

const wchar_t noGROUP = NULLCHAR ;        // default field-group character

//** For parsing of skField text data and scrolling within the data. **
class MRParse
{
   public:
   ~MRParse ( void ) { if ( this->roff != NULL ) { delete [] this->roff ; } }
   MRParse ( void )
   {
      //* Note: 'roff' initially allocated with one element (single-row field).
      this->roff = new int[this->rCnt = 1] ; this->roff[ZERO] = ZERO ;
      this->rtop = this->rcur = this->rbot = this->rlst = 
      this->bor  = this->eor  = this->bov = this->eov  = this->ni = ZERO ;
      this->ovr  = false ;
   }

   void operator= ( const MRParse& newMrp )
   {
      if ( (this->rCnt != newMrp.rCnt) || (this->roff == NULL) )
      {
         if ( this->roff != NULL ) { delete [] this->roff ; }
         this->roff = new int[newMrp.rCnt] ;
      }
      this->rCnt = newMrp.rCnt ;
      for ( int i = ZERO ; i < this->rCnt ; ++i )
         this->roff[i] = newMrp.roff[i] ;
      this->rtop = newMrp.rtop ;
      this->rcur = newMrp.rcur ;
      this->rbot = newMrp.rbot ;
      this->rlst = newMrp.rlst ;
      this->bor  = newMrp.bor ;
      this->eor  = newMrp.eor ;
      this->bov  = newMrp.bov ;
      this->eov  = newMrp.eov ;
      this->ni   = newMrp.ni ;
      this->ovr  = newMrp.ovr ;
   }

   int* roff ;             // array of indices for each text row
   int  rCnt ;             // number of text rows (visible and invisible)
   int  rtop ;             // index of top visible row
   int  rcur ;             // index of row containing IP
   int  rbot ;             // index of last visible row
   int  rlst ;             // index of last text row (visible or invisible)
   int  bor ;              // index character at beginning of current row
   int  eor ;              // index character at end of current row
   int  bov ;              // index of first visible char on current row (floShift formatting)
   int  eov ;              // index of last  visible char on current row (floShift formatting)
   int  ni ;               // index of null terminator
   bool ovr ;              // set if IP references a character not in visible space
} ;      // MRParse

const short labelLEN = 64 ;   // Maximum length of a field label
const short LABPAD   = 12 ;   // (space for escape sequenc(s))
const short MIN_ACNT = 2 ;    // minimum size of skField 'iAttr' array

//*******************
//** skField Class **
//*******************
class skField
{
   public:
   ~skField ( void )                // destructor
   {
      if ( this->iAttr != NULL )    // release the dynamic allocation
      { delete [] this->iAttr ; this->iAttr = NULL ; }
   }

   skField ( void )                 // default constructor
   {
      this->reset () ;              // initialize to default values
   }

   //* Initialization Constructor.                    *
   //* The first five(5) arguments are required.      *
   //* The remaining arguments are optional and       *
   //* if not specified, will receive default values. *
   skField ( FldType  fldType,
             short    fldHeight,
             short    fldWidth,
             short    fldOffsetY,
             short    fldOffsetX,
             //* Optional Arguments *
             const char* fldText = NULL,
             acAttr   fldAttr    = acaUSEDFLT,
             FloType  fldFlow    = floChar,
             int      insPoint   = ZERO,
             bool     readOnly   = false,
             bool     fldBorder  = false,
             bool     fldHidden  = false,
             bool     fldLocked  = true,
             bool     binState   = false,
             wchar_t  hotKey     = NULLCHAR,
             wchar_t  groupId    = noGROUP
           ) ;

   //* Copy data from another skField object. *
   //*     (see also skForm operator=)        *
   void operator= ( const skField& skf )
   {
      //* Public data members *
      this->txt   = skf.txt ;
      this->orig  = skf.orig ;
      this->aca   = skf.aca ;
      this->ip    = skf.ip ;
      this->hgt   = skf.hgt ;
      this->wid   = skf.wid ;
      this->type  = skf.type ;
      this->flow  = skf.flow ;
      this->hkey  = skf.hkey ;
      this->grp   = skf.grp ;
      this->state = skf.state ;
      this->bdr   = skf.bdr ;
      this->hide  = skf.hide ;
      this->lock  = skf.lock ;
      this->ro    = skf.ro ;
      //* "Protected" data members.                  *
      //* Because these are protected members, they  *
      //* _should_ contain valid data, but beware.   *
      //* If an object is copied at the application  *
      //* level, these members will not be accessible*
      this->mrp   = skf.mrp ;
      for ( short i = ZERO ; i < (labelLEN + LABPAD) ; ++i )
      { if ( (this->lab[i] = skf.lab[i]) == NULLCHAR ) { break ; } }
      this->lab[labelLEN + LABPAD] = NULLCHAR ;
      this->lpos  = skf.lpos ;
      this->lattr = skf.lattr ;
      this->battr = skf.battr ;
      this->cur   = skf.cur ;
      this->btype = skf.btype ;
      this->sitem = skf.sitem ;
      this->rbch  = skf.rbch ;
      this->off   = skf.off ;
      this->shft  = skf.shft ;
      this->sela  = skf.sela ;
      this->selb  = skf.selb ;
      if ( skf.iAttr == NULL )
      {
         if ( this->iAttr != NULL ) { delete [] this->iAttr ; }
         this->iAttr = NULL ;
      }
      else
      {
         // Programmer's Note: It is theoretically possible that the source array 
         // is not terminated by acaUSEDFLT; however, this is unlikely because the 
         // data initialization is controlled within colorConfig().
         short scnt = ZERO ;              // count items in source array
         while ( skf.iAttr[scnt++] != acaUSEDFLT ) ;
         this->iAlloc ( scnt ) ;
         for ( short r = ZERO ; r < scnt ; ++r ) // copy source to target
            this->iAttr[r] = skf.iAttr[r] ;
      }
   }

   //* Extract a copy the data of the protected 'mrp' member *
   void getMrp ( MRParse& Mrp ) const
   {
      Mrp.rCnt = this->mrp.rCnt ;
      Mrp.rtop = this->mrp.rtop ;
      Mrp.rcur = this->mrp.rcur ;
      Mrp.rbot = this->mrp.rbot ;
      Mrp.rlst = this->mrp.rlst ;
      Mrp.bor  = this->mrp.bor ;
      Mrp.eor  = this->mrp.eor ;
      Mrp.eov  = this->mrp.eov ;
      Mrp.ni   = this->mrp.ni ;
      Mrp.ovr  = this->mrp.ovr ;
   }

   //* Get a copy of the protected 'shft' member *
   //* Used for manual calculation of horizontal *
   //* positioning.                              *
   int get_shft ( void )
   { return ( this->shft ) ; }

   //* For Menus (ftMB) and Scrollbox controls (ftSB),   *
   //* Get the "item" i.e. the row containing 'ip'.      *
   //* For ftMB, 'iNum' is the 1-based menu item.        *
   //* For ftSB, 'iNum' is the 0-based row index.        *
   bool getItem ( gString& iTxt, short& iNum, bool selnum = false ) const ;

   //* For Textbox (ftTB) controls, set the text-modifier*
   //* attribute to be used for highlighted substrings.  *
   bool setHiliteMod ( acAttr txtMod ) ;

   //* For Textbox (ftTB) controls, return data on the   *
   //* highlighted (or 'selected') substring.            *
   bool getHilite ( int& begin, int& len, gString& gsTxt, acAttr* hmod = NULL ) ;

   //* Set the line type and attribute for the field outline. *
   bool outlineConfig ( LineType lType, acAttr lAttr = acaUSEDFLT ) ;

   //* Specify label text, color and position. *
   bool labelConfig ( const char* labText, 
                      acAttr labAttr = acaUSEDFLT, bool center = false ) ;
   bool labelConfig ( acAttr labAttr, bool lableft = false ) ;

   //* Specify individual color attributes for each display row. *
   bool colorConfig ( const acAttr* rowAttr, short rowCnt ) ;

   public:
   gString txt ;                    // text displayed within the field
   WinPos  orig ;                   // field origin (upper left corner)
   acaExpand aca ;                  // decoded color attributes for field
   int     ip ;                     // insertion point (index into text array)
   short   hgt,                     // field height (rows)
           wid ;                    // field width (columns)
   FldType type ;                   // type of field, member of enum FldType
   FloType flow ;                   // formatting option for multi-row fields
   wchar_t hkey ;                   // hotkey keycode
   wchar_t grp ;                    // character indicating group membership (ftRB,ftMB)
   const   MRParse* mrptr ;         // pointer to protected parsing data
   bool    state ;                  // binary state: set or reset (ftRB)
   bool    bdr ;                    // field border specified
   bool    hide ;                   // if set, field is hidden and inaccessible
   bool    lock ;                   // if set, control is always visible
   bool    ro ;                     // if set, field is read-only (cannot receive focus)

   protected:
   //* Set all data members to defaults *
   void reset ( void )
   {
      this->orig   = { 1, 1 } ;     // field origin
      this->hgt    = 1,             // initial height (rows)
      this->wid    = 5 ;            // initial width (columns)
      this->ip     = ZERO ;         // reference text origin
      this->cur    = { ZERO, ZERO };// cursor offset from origin
      this->off    = ZERO ;         // scroll offset (index first visible character)
      this->shft   = ZERO ;         // horizontal shift
      this->sela   = ZERO ;         // no text selected
      this->selb   = ZERO ;
      this->shft   = ZERO ;         // no left-shift
      this->lab[0] = NULLCHAR ;     // label text not yet specified
      this->lattr  = acaUSEDFLT ;   // color attributes for label text
      this->lpos   = { 0, 0 } ;     // label position
      this->type   = ftTB ;         // textbox by default
      this->flow   = floChar ;      // simple auto line wrap by default
      this->rbch   = wcsSDIAMOND ;  // default radiobutton 'set' character
      this->grp    = noGROUP ;      // not a group member
      this->mrptr  = &this->mrp ;   // const pointer to protected data
      this->hkey   = NULLCHAR ;     // hotkey unknown
      this->battr  = acaUSEDFLT ;   // field border color attributes
      this->btype  = ltSINGLE ;     // field border line type
      this->sitem  = -1 ;           // no menu item selected
      this->iAttr  = NULL ;         // no per-item attributes
      this->lock   = true ;         // lock is set by default
      this->state  = false ;        // state == reset
      this->bdr    = false ;        // no field border
      this->hide   = false ;        // field is visible and accessible
      this->ro     = false ;        // field can accept user input
      //* 'txt' is initially an empty string.                     *
      //* 'aca' is initialized to acaATTR_DFLT when acaExpand     *
      //*       object is instantiated, and is fully initialized  *
      //*       by the ACWin constructor.                         *
      //* 'mrp' members get defaults when object is instantiated  *
      //*       and then initialized by ACWin/ACDlg constructor.  *
   }

   //* Allocate an array of color attribute values. This array is *
   //* used for per-row attributes and text highlighting. It is   *
   //* assumed that caller is not a moron, (always dangerous).    *
   void iAlloc ( short items )
   {
      if ( items < MIN_ACNT ) { items = MIN_ACNT ; }
      if ( this->iAttr != NULL ) { delete [] this->iAttr ; }
      this->iAttr = new acAttr[items] ;
   }

   //** Protected Data Members **
   MRParse mrp ;                    // calculated position of each text row
   wchar_t lab[labelLEN + LABPAD] ; // buffer to contain optional label text
   WinPos  lpos ;                   // label position (for fields with external labels)
   acAttr  lattr ;                  // color attributes for label text
   acAttr  battr ;                  // field border color attributes
   WinPos  cur ;                    // current cursor offset
   LineType btype ;                 // field border line type
   short   sitem ;                  // ftMB/ftSB only: 'selected' menu item (0-based)
   wchar_t rbch ;                   // character indicating that ftRB is 'set'
   acAttr* iAttr ;                  // pointer to array of attributes, one for each item
   int     off,                     // offset from top of text to first displayed char
           shft,                    // left shift for floShift formatted output
           sela,                    // selection delimiters
           selb ;

   friend class ACWin ;
   friend class skForm ;
   #if DEBUG_ANSICMD != 0
   friend class AnsiCmd ;  // this allows test methods to report protected members
   #endif   // DEBUG_ANSICMD

   // Notes On Public Data Members:
   // -----------------------------
   // 'txt'  : Text displayed in the field.
   // 'orig' : This is the 0-based offset from the skForm's 'base' coordinates,
   //          which in turn is the 1-based offset from the upper-left corner 
   //          of the terminal window.
   //          Note that within an ACWin object, 'skForm.base' is the same as 
   //          the 'txtBase' member of the ACWin object. 
   // 'aca'  : Color attributes for the field.
   // 'hgt'  : Height: number of vertical rows.
   // 'wid'  : Width : number horizontal columns.
   // 'ip'   : text insertion point: index into 'txt.gstr()'
   // 'type' : Specifies the type of field. This defines how the user interacts
   //          with the field. See notes for the individual types.
   // 'flow' : layout type for display of text in multi-row fields. See enum FloType.
   //          Single-row fields always use the 'floShift' option.
   //          Programmer's Note: Internally, positioning of line breaks between 
   //          tokens is calculated by the gString class.
   // 'grp'  : For Radiobuttons, Menus and other control types which can be grouped.
   //          All group members share the character. (default: noGROUP i.e. nullchar)
   // 'hkey' : Hotkey assigned to the control (default: NULLCHAR)
   //          Valid values are the GUI keys (wcsALT_A, etc.) and function keys 
   //          (wcsF01, etc.). For ftPB and ftRB, any initial value is ignored.
   //          While direct specification of hotkeys is valid for all other 
   //          control types, this is recommended _only_ for Menubox controls 
   //          because Menubox controls have no label. For all other control types 
   //          it is recommended that if a hotkey is desired, it should be embedded 
   //          in the field label. See SetFieldHotkey method for details.
   // 'mrptr': This is a public const pointer to the protected MRParse object
   //          defined in this class. Because it is a "const MRParse*", this means 
   //          that the application layer may VIEW BUT NOT MODIFY the text-parsing 
   //          data. See the MRParse class for a description of these data.
   // 'state': For Radiobuttons and other binary-state controls, current state
   //          (set or reset).
   // 'bdr'  : If the field has had a border drawn around it, this flag is set.
   //          Note that 'bdr' flag is public and may therefore be specified 
   //          during instantiation; however 'btype' (border line type) and 'battr'
   //          (border line color) are protected, so if a non-default border is 
   //          needed, it is set after opening window. See FieldOutline method.
   // 'hide' : If the field is set to hidden, it is not visible, it will become 
   //          visible and available to the user only if the associated hotkey 
   //          (if any) is pressed; or if the application re-enables the field.
   // 'lock' : The 'lock' flag is primarily used to control visibility and accessibility 
   //          of menu controls when they receive and lose the input focus.
   //          When the 'lock' flag is set, it indicates that the control is always visible 
   //          in the window regardless of whether it has focus (unless is obscured by 
   //          another control). When 'lock' is reset, the menu field will be set as 
   //          invisible and inactive when it loses focus (see 'hide' flag).
   //          Exception: Menubar stays open if any member of its group has focus.
   //          This flag may also be used in combination with the 'hide' flag to 
   //          de-activate other types of fields which are not currently in use. 
   //          This prevents wasted time refreshing fields which are fully obscured 
   //          by other fields.
   // 'ro'   : If the field is set to read-only, then that field is visible, but 
   //          cannot receive the input focus, and the contents of the field cannot 
   //          be modified under user control.

   // Notes On Protected Data Members:
   // 'mrp'  : For ftTB fields, parsing and indexing data used to format data for display.
   // 'lab'  : For controls with seperate field and label data, this contains the 
   //          label text with optional escape sequences to indicate hotkeys.
   // 'lpos' : Label position (window-relative) for fields with external labels.
   // 'lattr': Attributes for field label text
   // 'battr': Attributes for field outline
   // 'rbch' : RadioButton CHaracter - character which indicates when the control is "set".
   //          Example - set: "[◆] label text"   reset: "[ ] label text"
   //          If a non-default 'set' character is specified within the Radiobutton 
   //          text, it is extracted and stored here. Else, the default 'set'
   //          character (wcsSDIAMOND) is used.
   // 'iAttr': Array of attributes, one for each item within the control.
   //          'iAttr' is supported for view-only data; thus only Menu (ftMW) and
   //          Scrollbox (ftSB) controls support per-row attributes.
   // 'btype': Line type (ltSINGLE || ltDUAL) for field outline
   // 'sitem': For menus (ftMB) and scrollbox controls (ftSB) only, index of the 
   //          'selected' item (0-based). Note that "selection" applies only to 
   //          scrollbox controls and to context menus which are locked in the 
   //          visible state. Internally, this allows the selected item to be 
   //          displayed using an alternate color attribute (or indicator).
   //          This value may be retrieved from the application layer via the
   //          getItem() method, (see above).
   //          If no item has been selected, contains the default: -1.
   // 'cur'  : This is the 0-based offset from 'orig' coordinates which defines
   //          the position of the visible cursor. Note that the value is
   //          dynamically calculated from the 'ip' member.
   // 'off'  : This is the character offset from the head of the text to the first
   //          displayed character. It fascilitates scrolling of the text 
   //          within the field.
   // 'shft' : For text display when the floShift formatting option is specified:
   //          indicates the left-shift for display of each row.
   // 'sela' : index of first "selected" character (if sela==selb, then no selection)
   // 'selb' : index of last "selected" character

} ;   // skField

//***********************************************************
//* skForm Class                                            *
//* -- The skForm class is integrated into the ACWin class  *
//*    and the ACDlg class, and defines the array of        *
//*    skField objects within the window.                   *
//***********************************************************
class skForm
{
   public:
   //* Public Methods *
   ~skForm ( void )                       // destructor
   {
      if  ( (this->fCnt > ZERO) && (this->fld != NULL) )
      { delete [] this->fld ; this->fld = NULL ; }
   }

   //* Initialization Constructor:                    *
   //* Range-checking: The number of controls is      *
   //* conceptually unlimited; however, it would be   *
   //* rather insane to have as many as 100 controls. *
   skForm ( short fieldCount )
   {
      if ( (fieldCount < 1) || (fieldCount > 99) ) { fieldCount = 99 ; }
      this->reset () ;              // set data members to defaults
      this->fCnt = fieldCount ;     // create the specified field(s)
      this->seli = this->fCnt ;     // no selected text
      this->fld = new skField[this->fCnt] ;
   }

   //* Initialization Constructor:                    *
   //* a) Specify initial skField count.              *
   //* b) Attach an array of skField constructors.    *
   //* c) Specify optional arguments as desired.      *
   skForm ( short fldCount,               // number of objecdts in 'fldArray'
           skField* fldArray,             // array of skField objects defining the form
           //* Optional arguments *
           short fldIndex = ZERO,         // index of initialially-active field
           acAttr fldAttr = acaUSEDFLT,   // default color attributes for control with focus
           bool  noisy = true,            // beep on unhandled keycode received
           bool  ioFlag = true,           // initial state of Insert/Overstrike flag
           bool  ioToggle = true,         // user access to Insert/Overstrike mode
           bool  ioCursor = false,        // enable cursor shape change for Insert/Overstrike
           CurStyle insCursor = csBblock, // cursor shape when Insert mode
           CurStyle ovrCursor = csBuline // cursor shape when Overstrike mode
          ) ;

   //* Copy data from another skForm object *
   void operator= ( const skForm *skfp )
   {
      //* If the assignment operator is used correctly, then      *
      //* source and target array lengths should always be equal, *
      //* but if not, release and re-allocate the target array.   *
      if ( (skfp->fCnt > this->fCnt) && (this->fld != NULL) )
      {
         delete [] this->fld ;
         this->fld = new skField[skfp->fCnt] ;
      }
      this->fCnt    = skfp->fCnt ;
      this->fi      = skfp->fi ;
      this->base    = skfp->base ;
      this->faca    = skfp->faca ;
      this->pKey    = skfp->pKey ;
      this->seli    = skfp->seli ;
      this->icur    = skfp->icur ;
      this->ocur    = skfp->ocur ;
      this->ins     = skfp->ins ;
      this->insc    = skfp->insc ;
      this->togg    = skfp->togg ;
      this->beep    = skfp->beep ;
      this->foot    = skfp->foot ;
      for ( short f = ZERO ; f < this->fCnt ; ++f )
         this->fld[f] = skfp->fld[f] ;
   }

   //* Debugging Method: Report the current *
   //* values for protected data members.   *
   bool skfProtectedMembers ( WinPos& p_base, wchar_t& p_pKey, 
                              short& p_seli, bool& p_foot, bool& p_dlg ) const
   {
      bool enabled = false ;
      p_base = this->base ;
      p_pKey = this->pKey ;
      p_seli = this->seli ;
      p_foot = this->foot ;
      p_dlg  = this->dlg ;
      enabled = true ;
      return enabled ;
   }

   private:
   //* Default constructor is private to prevent user embarrassment.*
   skForm ( void )            // default constructor
   {
      this->reset () ;        // set data members to defaults
      this->fCnt = ZERO ;     // no fields defined
      this->fld = NULL ;
   }

   void reset ( void )        // called only by constructors
   {
      this->pKey  = NULLCHAR ;   // no previous keycode
      this->fi    = ZERO ;       // index first field
      this->base  = { 0, 0 } ;   // origin of display area
      this->faca.decode( acaUSEDFLT ) ; // no attributes specified
      this->seli  = this->fCnt ; // No current text selection
      this->icur  = csBblock ;   // Insert Mode - default cursor style
      this->ocur  = csBuline ;   // Overstrike Mode - default cursor style
      this->ins   = true ;       // Insert mode
      this->insc  = false ;      // disable cursor style change
      this->togg  = true ;       // enable toggle of insert/overstrike
      this->beep  = true ;       // noisy mode
      this->foot  = false ;      // footnotes disabled
      this->dlg   = false ;      // assume a parent ACWin object
   }

   //* Clear text in field with focus, or all fields.*
   //* This method does not update the display.      *
   void clear ( bool all )
   {
      if ( all )
      {
         for ( short f = ZERO ; f < this->fCnt ; ++f )
            this->fld[f].txt.clear() ;
      }
      else
         this->fld[this->fi].txt.clear() ;
   }

   //* Public Data Members *
   public:
   skField *fld ;             // array of skField objects
   short   fCnt,              // number of objects in 'fld' array
           fi ;               // index of currently-active field
   acaExpand faca ;           // decoded color attributes for control with focus
   CurStyle icur ;            // cursor style when in Insert mode
   CurStyle ocur ;            // cursor style when in Overstrike mode
   bool    ins ;              // insert/overstrike flag
                              // 'true'==insert, 'false'==overstrike
   bool    insc ;             // cursor shape changes with insert/overstrike toggle
                              // 'true'  == enable cursor shape change
                              // 'false' == disable cursor shape change
   bool    togg ;             // enable/disable the insert/overstrike toggle
                              // 'true'  == enable toggle
                              // 'false' == disable toggle
   bool    beep ;             // 'true' audible alert if keycode unprocessed
                              // 'false' silent

   //* Protected Data Members *
   protected:
   WinPos  base ;             // offset for upper left corner of parent window
   wchar_t pKey ;             // most recent special keycode processed
   short   seli ;             // index of field with selected text (if any)
   bool    foot ;             // 'true' display diagnostic footnotes
                              // 'false' no diagnostic message
   bool    dlg ;              // internal use only: 'true' == ACDlg, 'false' == ACWin

   friend class ACWin ;
   // Notes On Public Data Members:
   // -----------------------------
   // 'fld'    An array of one or more skField objects which are allocated 
   //          by the initialization constructor. The fields are individually 
   //          initialized by the application, then the application's values 
   //          are range-checked and adjusted as necessary when application's
   //          copies of the object are copied into the ACWin-class skForm object.
   // 'fCnt'   This value is set during instantiation of the skForm object, and 
   //          may not be modified.
   // 'fi'     This is the index into the fld[] array which indicates the field 
   //          which currently has the input focus.
   // 'faca'   Color attribute for field (control) with the input focus.
   //          Applies to controls for which the cursor is not visible.
   // 'icur'   Cursor style for Insert mode.
   // 'ocur'   Cursor style for Overstrike mode.
   // 'ins'    This is the current state of the Insert/Overstrike flag for user 
   //          input into the fields.
   // 'togg'   If set, this locks the current value of the 'ins' member, 
   //          preventing the user from toggling the flag.
   // 'insc'   If set, then change the shape of the visible cursor when the 
   //          Insert key is pressed. This is a visual cue that insert/overstrike 
   //          mode has been toggled.
   // 'beep'   If set this flag activates an audible alert for invalid user 
   //          input or attempted overrun of the target field.

   // Notes On Protected Data Members:
   // --------------------------------
   // 'base'   Set to offset 0,0 from the window's 'textBase' member 
   //          (upper-left corner of window interior).                                                        *
   // 'pKey'   The most recent "special key" keycode processed.
   //          (used to track text selection)
   // 'seli'   Index of field containing "selected" text.
   //          If no field contains a selection, then seli==fCnt.
   // 'foot'   If set this flag activates "footnotes" i.e. diagnostic messages 
   //          displayed in the lower border of the window containing the form.
   // 'dlg'    If set, this flag indicates that the skForm object lives within 
   //          an ACDlg sub-window. If reset, object lives within an ACWin window.
} ;   // skForm

//********************************************************************
//* The AC_Box class defines a mechanism for drawing a window-like   *
//* rectangle within the terminal window.                            *
//* This is a greatly-simplified version of the ACWin-class object.  *
//* Visually, it is identical; however, it has a much smaller        *
//* footprint and uses the ANSI escape sequence primitives directly. *
//* Therefore, it is the application's responsibility to ensure that *
//* the data are displayed in the intended format. Minimal range     *
//* checking and error correction are performed.                     *
//*                                                                  *
//* All data members except height and width and position values are *
//* public, so the object can be modified and redrawn as needed.     *
//*                    However:                                      *
//* "With great power comes great responsibility" - Old Uncle Ben    *
//* (Not all programmers are as skilled as you are, dear reader.)    *
//*                                                                  *
//* Specific AnsiCmd-class methods are available for the basic       *
//* AC_Box operations. These methods are located in AnsiCmdApi.cpp.  *
//* For examples, see the Test_Box() method in AnsiCmdTest.cpp.      *
//*                                                                  *
//*  acBoxDraw()     Draw the box in the terminal window.            *
//*  acBoxErase()    Erase the box from the terminal window.         *
//*  acBoxClear()    Erase the text from the interior of the box.    *
//*                                                                  *
//* For more advanced operations, use the standard AnsiCmd methods,  *
//* e.g. acWrite(), ttyWrite(), acSetCursor(), etc.                  *
//*                                                                  *
//* Two initialization constructors are defined. One uses terminal   *
//* default color attributes and default border style (ltSINGLE).    *
//* The other allows for direct specification of:                    *
//*  1) border and interior attributes,                              *
//*  2) line style for the border of either ltSINGLE or ltDUAL,      *
//*  3) text of title to be written in the top border                *
//*     (null pointer or empty string ("") indicates no title).      *
//*                                                                  *
//* (Note that the default constructor is pretty useless.)           *
//*                                                                  *
//********************************************************************
class AC_Box
{
   public:
   AC_Box ( void )               // default constructor
   {
      this->reset() ;                        // reset data members
   }

   //* Initialization constructor (basic) *
   AC_Box ( WinPos& base, short height, short width )
   {
      this->reset() ;                        // reset data members
      this->range ( base, height, width ) ;  // range check parameters
      this->orig   = base ;
      this->hgt    = height ;
      this->wid    = width ;
   }

   //* Initialization constructor (full) *
   AC_Box ( WinPos& base, short height, short width, LineType typ, 
            aeSeq bfg, aeSeq bbg, aeSeq tfg, aeSeq tbg, const char* tit )
   {
      this->reset() ;                        // reset data members
      this->range ( base, height, width ) ;  // range check parameters
      this->orig   = base ;
      this->hgt    = height ;
      this->wid    = width ;
      this->lnType = typ ;
      this->bFgnd  = bfg ;
      this->bBgnd  = bbg ;
      this->tFgnd  = tfg ;
      this->tBgnd  = tbg ;
      if ( tit != NULL && tit[0] != '\0' )
      {
         gString gs( tit ) ;
         gs.copy( this->title, gsALLOCMIN ) ;
      }
   }

   ~AC_Box ( void )              // destructor
   { /* nothing to do */ }

   //* Private Methods and Data *
   private:
   void reset ( void )           // data initialization
   {
      *this->title = '\0' ;
      this->orig = { 1, 1 } ;
      this->hgt    = 3 ;
      this->wid    = 3 ;
      this->lnType = ltSINGLE ;
      this->bFgnd  = aesFG_DFLT ;
      this->bBgnd  = aesBG_DFLT ;
      this->tFgnd  = aesFG_DFLT ;
      this->tBgnd  = aesBG_DFLT ;
   }
   void range ( WinPos& base, short& hgt, short& wid )
   {
      if ( base.row < 1 )  base.row = 1 ;
      if ( base.col < 1 )  base.col = 1 ;
      if ( hgt < 3 )       hgt = 3 ;
      if ( wid < 3 )       wid = 3 ;
   }

   WinPos orig ;                 // Box origin (terminal-relative)
   short hgt ;                   // Height of object (rows)
   short wid ;                   // Width of object (columns)

   //* Public Data Members *
   public:
   wchar_t title[gsALLOCMIN] ;   // Title to be displayed in top row of box
   LineType lnType ;             // line type for border
   aeSeq bFgnd ;                 // Border Foreground color attribute
   aeSeq bBgnd ;                 // Border Background color attribute
   aeSeq tFgnd ;                 // Interior Foreground color attribute
   aeSeq tBgnd ;                 // Interior Background color attribute

   friend class AnsiCmd ;  // AnsiCmd class has access to protected members

   // Notes:
   // 'orig'   The 1-based offset from the upper-left corner of the terminal
   //          window to the upper-left corner of the box.
   // 'hgt'    Height of the box in rows.
   // 'wid'    Width of the box in columns.
   // 'title'  Title text (truncated if necessary to fit box width).
   // 'lnType' Member of enun LineType (limited to ltSINGLE or ltDUAL)
   // 'bFgnd'  Border foreground attribute
   // 'bBgnd'  Border background attribute
   // 'tFgnd'  Text foreground attribute
   // 'tBgnd'  Text background attribute
} ;   // AC_Box class

#if 0    // Currently unused
// Future enhancement: Port line-drawing class and methods from NcDialog LineDef class.
//* Line drawing definition as parameter for DrawLine().                       *
//* Note that lines are drawn left-to-right and top-to-bottom.                 *
//* By default, as new lines are drawn, they will form a visual connection     *
//* with existing lines. To prevent this behavior, set the appropriate flag to *
//* 'false'.
// Because AnsiCmd windows are not memory blocks, intersects can't be automatic, 
// so the intersect row/column(s) must be specified explicitly.
// Convert enumerated types and definitions to match AnsiCmd.
// Rename the members to conform to AnsiCmd style.
// Create a new DrawLine() method.
class AC_Line
{
   public:
   LineDef( ncLineType t, ncLineType s, short y, short x, short l, attr_t c ) :
         type(t), style(s), startY(y), startX(x), length(l), color(c)
         {
            cInsect  = true ;
            cLeft    = true ;
            cRight   = true ;
            cTop     = true ;
            cBot     = true ;
         }
   ncLineType  type ;      // line type (ncltHORIZ or ncltVERT)
   ncLineType  style ;     // line style: member of enum ncLineType
   short       startY ;    // start position Y
   short       startX ;    // start position X
   short       length ;    // line length in columns (horiz) or rows (vert)
   attr_t      color ;     // color attribute
   bool        cInsect ;   // 'true' create an intersection with an existing line 
                           //       (other than at new line's endpoints)
   bool        cLeft ;     // 'true' connect on left with an existing line
   bool        cRight ;    // 'true' connect on right with an existing line
   bool        cTop ;      // 'true' connect on top with an existing line
   bool        cBot ;      // 'true' connect on bottom with an existing line
} ;   // AC_Line class
#endif   // Currently unused

//*******************************************************************
//* ASCII Control Codes:                                            *
//* The ASCII control codes are non-printing characters (bytes)     *
//* which were designed to control Teletype machines and terminal   *
//* hardware. Most of these are unused in the modern era of         *
//* terminal emulators, which frees them for use as command codes   *
//* in applications.                                                *
//* -- These keycodes _are not_ case sensitive; that is, Ctrl+d and *
//*    Ctrl+Shift+d generate the same code (0x04).                  *
//* -- Some of these keycodes may be configured as shortcuts within *
//*    the terminal setup, and therefore unavailable to the         *
//*    application. Those shortcuts may be disabled in the terminal *
//*    settings in order to make them available to the application. *
//* -- Certain control codes are universally recognized as having   *
//*    a specific function or character meaning. These include:     *
//*    0x00                   - the Null character                  *
//*    0x1B Ctrl+[            - the Escape character                *
//*    0x08 Ctrl+H            - Backspace                           *
//*    0x09 Ctrl+I            - Horizontal Tab                      *
//*    0x0A Ctrl+J and Ctrl+M - Newline/Enter (under Linux)         *
//*    0x0D Ctrl+M            - The Windoze morons include          *
//*                             carriage return as a seperate code. *
//* --------------------------------------------------------------- *
//* By default, a few of the ASCII control codes are captured by    *
//* the terminal itself (or more accurately by the shell program):  *
//*    Ctrl+C SIGINT signal                                         *
//*    Ctrl+Q Resume stdout stream (after Ctrl+S)                   *
//*    Ctrl+S Stop (pause) stdout stream                            *
//*    Ctrl+Z SIGTSTP signal - suspend the active process           *
//*                            (use the 'fg' utility re-activate)   *
//*    Ctrl+4 SIGQUIT signal (same as Ctrl+\)                       *
//*    Ctrl+8 ASCII DEL key (usually assigned to Backspace)         *
//* The AnsiCmd library captures these signals when practical,      *
//* _especially_ Ctrl+C which can then be used as copy-to-clipboard.*
//*******************************************************************
const wchar_t wcsCTL_A = 0x00223B ;       // Ctrl+A
const wchar_t wcsCTL_B = 0x00223C ;       // Ctrl+B
const wchar_t wcsCTL_C = 0x00223D ;       // Ctrl+C   (Break, SIGINT signal, captured)
const wchar_t wcsCTL_D = 0x00223E ;       // Ctrl+D
const wchar_t wcsCTL_E = 0x00223F ;       // Ctrl+E
const wchar_t wcsCTL_F = 0x002240 ;       // Ctrl+F
const wchar_t wcsCTL_G = 0x002241 ;       // Ctrl+G
const wchar_t wcsCTL_H = 0x002242 ;       // Ctrl+H
const wchar_t wcsCTL_I = 0x002243 ;       // Ctrl+I
const wchar_t wcsCTL_J = 0x002244 ;       // Ctrl+J
const wchar_t wcsCTL_K = 0x002245 ;       // Ctrl+K
const wchar_t wcsCTL_L = 0x002246 ;       // Ctrl+L
const wchar_t wcsCTL_M = 0x002247 ;       // Ctrl+M
const wchar_t wcsCTL_N = 0x002248 ;       // Ctrl+N
const wchar_t wcsCTL_O = 0x002249 ;       // Ctrl+O
const wchar_t wcsCTL_P = 0x00224A ;       // Ctrl+P
const wchar_t wcsCTL_Q = 0x00224B ;       // Ctrl+Q   (Resume output signal, disabled)
const wchar_t wcsCTL_R = 0x00224C ;       // Ctrl+R
const wchar_t wcsCTL_S = 0x00224D ;       // Ctrl+S   (Pause output signal, disabled)
const wchar_t wcsCTL_T = 0x00224E ;       // Ctrl+T
const wchar_t wcsCTL_U = 0x00224F ;       // Ctrl+U
const wchar_t wcsCTL_V = 0x002250 ;       // Ctrl+V
const wchar_t wcsCTL_W = 0x002251 ;       // Ctrl+W
const wchar_t wcsCTL_X = 0x002252 ;       // Ctrl+X
const wchar_t wcsCTL_Y = 0x002253 ;       // Ctrl+Y
const wchar_t wcsCTL_Z = 0x002254 ;       // Ctrl+Z   (Stop, TSTP signal, captured)
const wchar_t wcsCTL_4 = 0x002255 ;       // Ctrl+4   (Quit, SIGQUIT) (same as Ctrl+\)
const wchar_t wcsCTL_5 = 0x002256 ;       // Ctrl+5
const wchar_t wcsCTL_6 = 0x002257 ;       // Ctrl+6
const wchar_t wcsCTL_7 = 0x002258 ;       // Ctrl+7
const wchar_t wcsCUP    = 0x0021BF ;      // Ctrl + up arrow     '↿' 1B 5B 31 3B 35 41
const wchar_t wcsCDOWN  = 0x0021C3 ;      // Ctrl + down arrow   '⇃' 1B 5B 31 3B 25 42
const wchar_t wcsCRIGHT = 0x0021C1 ;      // Ctrl + right arrow  '⇁' 1B 5B 31 3B 35 43
const wchar_t wcsCLEFT  = 0x0021BD ;      // Ctrl + left arrow   '↽' 1B 5B 31 3B 35 44
const wchar_t wcsCHOME  = 0x0021C8 ;      // Ctrl + Home key     '⇈' 1B 5B 31 3B 35 48
const wchar_t wcsCEND   = 0x0021CA ;      // Ctrl + End key      '⇊' 1B 5B 31 3B 35 46

//*******************************************************************
//* Group of "Special Keycodes": These keycodes are translated from *
//* escape sequences or ASCII control codes and are used for cursor *
//* movement and text editing during user input.                    *
//*******************************************************************
const wchar_t wcsUARROW  = 0x002191 ;     // arrow up             '↑'
const wchar_t wcsDARROW  = 0x002193 ;     // arrow down           '↓'
const wchar_t wcsRARROW  = 0x002192 ;     // arrow right          '→'
const wchar_t wcsLARROW  = 0x002190 ;     // arrow left           '←'
const wchar_t wcsHOME    = 0x002196 ;     // Home key             '↖'
const wchar_t wcsEND     = 0x002198 ;     // End key              '↘'
const wchar_t wcsPGUP    = 0x002197 ;     // PageUp key           '↗'
const wchar_t wcsPGDN    = 0x002199 ;     // PageDown key         '↙'
const wchar_t wcsBKSP    = 0x0021B0 ;     // Backspace key        '↰'
const wchar_t wcsTAB     = 0x0021B1 ;     // Tab key              '↱'
const wchar_t wcsSTAB    = 0x0021B2 ;     // Shift + Tab key      '↲'
const wchar_t wcsDELETE  = 0x0021B3 ;     // Delete key           '↳'
const wchar_t wcsINSERT  = 0x0021A5 ;     // Insert key           '↥'
const wchar_t wcsAENTER  = 0x0021A7 ;     // Alt   + Enter key    '↧'
const wchar_t wcsSUP     = 0x0021BE ;     // Shift + up arrow     '↾'
const wchar_t wcsSDOWN   = 0x0021C2 ;     // Shift + down arrow   '⇂'
const wchar_t wcsSRIGHT  = 0x0021C0 ;     // Shift + right arrow  '⇀'
const wchar_t wcsSLEFT   = 0x0021BC ;     // Shift + left arrow   '↼'
const wchar_t wcsSHOME   = 0x0021C7 ;     // Shift + Home key     '⇇'
const wchar_t wcsSEND    = 0x0021C9 ;     // Shift + End key      '⇉'
const wchar_t wcsESCAPE  = 0x0021BB ;     // Stand-alone Escape   '↻'
const wchar_t wcsCOPY    = wcsCTL_C ;     // Ctrl  + C (copy)
const wchar_t wcsCUT     = wcsCTL_X ;     // Ctrl  + X (cut)
const wchar_t wcsPASTE   = wcsCTL_V ;     // Ctrl  + V (paste)
const wchar_t wcsUNDO    = wcsCTL_Z ;     // Ctrl  + Z (undo)
const wchar_t wcsSELECT  = wcsCTL_A ;     // Ctrl  + A (select all)
//const wchar_t wcsX       = 0x0021B4 ;     // (reserved)           '↴'
//const wchar_t wcsX       = 0x0021B5 ;     // (reserved)           '↵'
//const wchar_t wcsX       = 0x0021B6 ;     // (reserved)           '↶'
//const wchar_t wcsX       = 0x0021B7 ;     // (reserved)           '↷'
//const wchar_t wcsX       = 0x0021B8 ;     // (reserved)           '↸'
//const wchar_t wcsX       = 0x0021B9 ;     // (reserved)           '↹'
//const wchar_t wcsX       = 0x0021BA ;     // (reserved)           '↺'
// ----- Additional Keycodes Available -----
//const wchar_t wcsX       = 0x0021A0 ;     // (reserved)           '↠'
//const wchar_t wcsX       = 0x0021A1 ;     // (reserved)           '↡'
//const wchar_t wcsX       = 0x0021A4 ;     // (reserved)           '↤'
//const wchar_t wcsX       = 0x0021A6 ;     // (reserved)           '↦'
//const wchar_t wcsX       = 0x0021A8 ;     // (reserved)           '↨'
//const wchar_t wcsX       = 0x0021A9 ;     // (reserved)           '↩'
//const wchar_t wcsX       = 0x0021AA ;     // (reserved)           '↪'
//const wchar_t wcsX       = 0x0021AB ;     // (reserved)           '↫'
//const wchar_t wcsX       = 0x0021AC ;     // (reserved)           '↬'
//const wchar_t wcsX       = 0x0021AD ;     // (reserved)           '↭'
//const wchar_t wcsX       = 0x0021AE ;     // (reserved)           '↮'
//const wchar_t wcsX       = 0x0021AF ;     // (reserved)           '↯'

//*******************************************************************
//* Alt Key Combinations: (commonly known as the GUI keys)          *
//* The Left Alt key in combination with ordinary alphabetic and    *
//* punctuation keys are reported to terminal applications as       *
//* escape sequences. These escape sequences are captured and       *
//* converted to the the Unicode codepoint values listed here.      *
//* Although these keycodes are converted to ordiary graphing       *
//* (printing) characters, these definitions allow the application  *
//* to interpret them as application-specific command codes.        *
//* Example: Alt+H commonly invokes a help menu.                    *
//*******************************************************************
const wchar_t wcsALT_A  = 0x002259 ;      // Alt + [a | A]
const wchar_t wcsALT_B  = 0x00225A ;      // Alt + [b | B]
const wchar_t wcsALT_C  = 0x00225B ;      // Alt + [c | C]
const wchar_t wcsALT_D  = 0x00225C ;      // Alt + [d | D]
const wchar_t wcsALT_E  = 0x00225D ;      // Alt + [e | E]
const wchar_t wcsALT_F  = 0x00225E ;      // Alt + [f | F]
const wchar_t wcsALT_G  = 0x00225F ;      // Alt + [g | G]
const wchar_t wcsALT_H  = 0x002260 ;      // Alt + [h | H]
const wchar_t wcsALT_I  = 0x002261 ;      // Alt + [i | I]
const wchar_t wcsALT_J  = 0x002262 ;      // Alt + [j | J]
const wchar_t wcsALT_K  = 0x002263 ;      // Alt + [k | K]
const wchar_t wcsALT_L  = 0x002264 ;      // Alt + [l | L]
const wchar_t wcsALT_M  = 0x002265 ;      // Alt + [m | M]
const wchar_t wcsALT_N  = 0x002266 ;      // Alt + [n | N]
const wchar_t wcsALT_O  = 0x002267 ;      // Alt + [o | O]
const wchar_t wcsALT_P  = 0x002268 ;      // Alt + [p | P]
const wchar_t wcsALT_Q  = 0x002269 ;      // Alt + [q | Q]
const wchar_t wcsALT_R  = 0x00226A ;      // Alt + [r | R]
const wchar_t wcsALT_S  = 0x00226B ;      // Alt + [s | S]
const wchar_t wcsALT_T  = 0x00226C ;      // Alt + [t | T]
const wchar_t wcsALT_U  = 0x00226D ;      // Alt + [u | U]
const wchar_t wcsALT_V  = 0x00226E ;      // Alt + [v | V]
const wchar_t wcsALT_W  = 0x00226F ;      // Alt + [w | W]
const wchar_t wcsALT_X  = 0x002270 ;      // Alt + [x | X]
const wchar_t wcsALT_Y  = 0x002271 ;      // Alt + [y | Y]
const wchar_t wcsALT_Z  = 0x002272 ;      // Alt + [z | Z]
const wchar_t wcsALT_0  = 0x002273 ;      // Alt + [0 | )]
const wchar_t wcsALT_1  = 0x002274 ;      // Alt + [1 | !]
const wchar_t wcsALT_2  = 0x002275 ;      // Alt + [2 | @]
const wchar_t wcsALT_3  = 0x002276 ;      // Alt + [3 | #]
const wchar_t wcsALT_4  = 0x002277 ;      // Alt + [4 | $]
const wchar_t wcsALT_5  = 0x002278 ;      // Alt + [5 | %]
const wchar_t wcsALT_6  = 0x002279 ;      // Alt + [6 | ^]
const wchar_t wcsALT_7  = 0x00227A ;      // Alt + [7 | &]
const wchar_t wcsALT_8  = 0x00227B ;      // Alt + [8 | *]
const wchar_t wcsALT_9  = 0x00227C ;      // Alt + [9 | (]
const wchar_t wcsALT_DQ = 0x00227D ;      // Alt + "  (Double Quote)
const wchar_t wcsALT_SQ = 0x00227E ;      // Alt + '  (Single Quote)
const wchar_t wcsALT_PL = 0x00227F ;      // Alt + +  (PLus)
const wchar_t wcsALT_CO = 0x002280 ;      // Alt + ,  (COmma)
const wchar_t wcsALT_MI = 0x002281 ;      // Alt + -  (MInus)
const wchar_t wcsALT_ST = 0x002282 ;      // Alt + .  (STop)
const wchar_t wcsALT_FS = 0x002283 ;      // Alt + /  (Forward Stroke)
const wchar_t wcsALT_CN = 0x002284 ;      // Alt + :  (ColoN)
const wchar_t wcsALT_SC = 0x002285 ;      // Alt + ;  (SemiColon)
const wchar_t wcsALT_LT = 0x002286 ;      // Alt + <  (Less Than)
const wchar_t wcsALT_EQ = 0x002287 ;      // Alt + =  (EQual)
const wchar_t wcsALT_GT = 0x002288 ;      // Alt + >  (Greater Than)
const wchar_t wcsALT_QU = 0x002289 ;      // Alt + ?  (QUestion)
const wchar_t wcsALT_LS = 0x00228A ;      // Alt + [  (Left Square) [may be unavailable]
const wchar_t wcsALT_BS = 0x00228B ;      // Alt + \  (Back Stroke)
const wchar_t wcsALT_RS = 0x00228C ;      // Alt + ]  (Right Square)
const wchar_t wcsALT_US = 0x00228D ;      // Alt + _  (UnderScore)
const wchar_t wcsALT_LB = 0x00228E ;      // Alt + {  (Left Brace)
const wchar_t wcsALT_VB = 0x00228F ;      // Alt + |  (Vertical Bar)
const wchar_t wcsALT_RB = 0x002290 ;      // Alt + }  (Right Brace)
const wchar_t wcsAUP    = 0x002194 ;      // Alt + UpArrow      '↔'
const wchar_t wcsADOWN  = 0x002195 ;      // Alt + DownArrow    '↕'
const wchar_t wcsARIGHT = 0x00219A ;      // Alt + RightArrow   '↚'
const wchar_t wcsALEFT  = 0x00219B ;      // Alt + LeftArrow    '↛'
const wchar_t wcsAEND   = 0x00219C ;      // Alt + End          '↜'
const wchar_t wcsAHOME  = 0x00219D ;      // Alt + Home         '↝'
const wchar_t wcsAPGUP  = 0x00219E ;      // Alt + PageUp key   '↞'
const wchar_t wcsAPGDN  = 0x00219F ;      // Alt + PageDown key '↟'

//************************************************
//** Function Keys                              **
//************************************************
//********************************************************************************
//* Notes:                                                                       *
//* There are 48 (12 * 4) Function key combinations available:                   *
//*  a) F01-12, b) SF01-12, c) CF01-12, d) CSF01-12                              *
//* Only the first three groups are supported;(36 sequential codepoints).        *
//* The Ctrl+Shift+Fnn group are silently converted to Ctrl+Fnn equivalents.     *
//*  Ctrl+Shift+F01 1B 5B 31 3B 36 50      Ctrl+Shift+F07 1B 5B 31 38 3B 36 7E   *
//*  Ctrl+Shift+F02 1B 5B 31 3B 36 51      Ctrl+Shift+F08 1B 5B 31 39 3B 36 7E   *
//*  Ctrl+Shift+F03 1B 5B 31 3B 36 52      Ctrl+Shift+F09 1B 5B 32 30 3B 36 7E   *
//*  Ctrl+Shift+F04 1B 5B 31 3B 36 53      Ctrl+Shift+F10 1B 5B 32 31 3B 36 7E   *
//*  Ctrl+Shift+F05 1B 5B 31 35 3B 36 7E   Ctrl+Shift+F11 1B 5B 32 33 3B 36 7E   *
//*  Ctrl+Shift+F06 1B 5B 31 37 3B 36 7E   Ctrl+Shift+F12 1B 5B 32 34 3B 36 7E   *
//  Values 0x002200 - 0x00222C (44 sequential keycodes) are available.           *
//  - Note that a few of these codepoints are common Unicode symbols, but are not*
//    conveniently entered on the keyboard. e.g. 0x2212(minus), 0x22125(divide), *
//    0x2217(asterisk), 0x221A(square root), 0x221E(infinity)                    *
//********************************************************************************
const wchar_t wcsF01  = 0x002000 ;        // F01       '∀' 1B 4F 50
const wchar_t wcsF02  = 0x002001 ;        // F02       '∁' 1B 4F 51
const wchar_t wcsF03  = 0x002002 ;        // F03       '∂' 1B 4F 52
const wchar_t wcsF04  = 0x002003 ;        // F04       '∃' 1B 4F 53
const wchar_t wcsF05  = 0x002004 ;        // F05       '∄' 1B 5B 31 35 7E
const wchar_t wcsF06  = 0x002005 ;        // F06       '∅' 1B 5B 31 37 7E
const wchar_t wcsF07  = 0x002006 ;        // F07       '∆' 1B 5B 31 38 7E
const wchar_t wcsF08  = 0x002007 ;        // F08       '∇' 1B 5B 31 39 7E
const wchar_t wcsF09  = 0x002008 ;        // F09       '∈' 1B 5B 32 30 7E
const wchar_t wcsF10  = 0x002009 ;        // F10       '∉' 1B 5B 32 31 7E
const wchar_t wcsF11  = 0x00200A ;        // F11       '∊' 1B 5B 32 33 7E
const wchar_t wcsF12  = 0x00200B ;        // F12       '∋' 1B 5B 32 34 7E
                     // --------
const wchar_t wcsSF01 = 0x00200C ;        // Shift+F01 '∌' 1B 5B 31 3B 32 50
const wchar_t wcsSF02 = 0x00200D ;        // Shift+F02 '∍' 1B 5B 31 3B 32 51
const wchar_t wcsSF03 = 0x00200E ;        // Shift+F03 '∎' 1B 5B 31 3B 32 52
const wchar_t wcsSF04 = 0x00200F ;        // Shift+F04 '∏' 1B 5B 31 3B 32 53
const wchar_t wcsSF05 = 0x002010 ;        // Shift+F05 '∐' 1B 5B 31 35 3B 32 7E
const wchar_t wcsSF06 = 0x002011 ;        // Shift+F06 '∑' 1B 5B 31 37 3B 32 7E
const wchar_t wcsSF07 = 0x002012 ;        // Shift+F07 '−' 1B 5B 31 38 3B 32 7E
const wchar_t wcsSF08 = 0x002013 ;        // Shift+F08 '∓' 1B 5B 31 39 3B 32 7E
const wchar_t wcsSF09 = 0x002014 ;        // Shift+F09 '∔' 1B 5B 32 30 3B 32 7E
const wchar_t wcsSF10 = 0x002015 ;        // Shift+F10 '∕' 1B 5B 32 31 3B 32 7E
const wchar_t wcsSF11 = 0x002016 ;        // Shift+F11 '∖' 1B 5B 32 33 3B 32 7E
const wchar_t wcsSF12 = 0x002017 ;        // Shift+F12 '∗' 1B 5B 32 34 3B 32 7E
                     // --------
const wchar_t wcsCF01 = 0x002018 ;        // Ctrl+F01  '∘' 1B 5B 31 3B 35 50
const wchar_t wcsCF02 = 0x002019 ;        // Ctrl+F02  '∙' 1B 5B 31 3B 35 51
const wchar_t wcsCF03 = 0x00201A ;        // Ctrl+F03  '√' 1B 5B 31 3B 35 52
const wchar_t wcsCF04 = 0x00201B ;        // Ctrl+F04  '∛' 1B 5B 31 3B 35 53
const wchar_t wcsCF05 = 0x00201C ;        // Ctrl+F05  '∜' 1B 5B 31 35 3B 35 7E
const wchar_t wcsCF06 = 0x00201D ;        // Ctrl+F06  '∝' 1B 5B 31 37 3B 35 7E
const wchar_t wcsCF07 = 0x00201E ;        // Ctrl+F07  '∞' 1B 5B 31 38 3B 35 7E
const wchar_t wcsCF08 = 0x00201F ;        // Ctrl+F08  '∟' 1B 5B 31 39 3B 35 7E
const wchar_t wcsCF09 = 0x002020 ;        // Ctrl+F09  '∠' 1B 5B 32 30 3B 35 7E
const wchar_t wcsCF10 = 0x002021 ;        // Ctrl+F10  '∡' 1B 5B 32 31 3B 35 7E
const wchar_t wcsCF11 = 0x002022 ;        // Ctrl+F11  '∢' 1B 5B 32 33 3B 35 7E
const wchar_t wcsCF12 = 0x002023 ;        // Ctrl+F12  '∣' 1B 5B 32 34 3B 35 7E

//************************************************
//** Miscellaneous Conditioned key combinations **
//************************************************
const wchar_t wcsSPGUP  = 0x00222D ;      // Shift+PgUp         '∭' 1B 5B 35 3B 32 7E
const wchar_t wcsSPGDN  = 0x00222E ;      // Shift+PgDn         '∮' 1B 5B 36 3B 32 7E
const wchar_t wcsASPGUP = 0x00222F ;      // Alt+Shift+PgUp     '∯' 1B 5B 35 3B 34 7E
const wchar_t wcsASPGDN = 0x002230 ;      // Alt+Shift+PgDn     '∰' 1B 5B 36 3B 34 7E
const wchar_t wcsCPGUP  = 0x0021A2 ;      // Ctrl+PageUp        '↢' 1B 5B 35 3B 35 7E
const wchar_t wcsCPGDN  = 0x0021A3 ;      // Ctrl+PageDown      '↣' 1B 5B 36 3B 35 7E
const wchar_t wcsCSPGUP = 0x002231 ;      // Ctrl+Shift+PgUp    '∱' 1B 5B 35 3B 36 7E
const wchar_t wcsCSPGDN = 0x002232 ;      // Ctrl+Shift+PgDn    '∲' 1B 5B 36 3B 36 7E
const wchar_t wcsCAPGUP = 0x002233 ;      // Ctrl+Alt+PgUp      '∳' 1B 5B 35 3B 37 7E
const wchar_t wcsCAPGDN = 0x002234 ;      // Ctrl+Alt+PgDn      '∴' 1B 5B 36 3B 37 7E
const wchar_t wcsSDEL   = 0x002235 ;      // Shift+ Delete      '∵' 1B 5B 33 3B 32 7E
const wchar_t wcsADEL   = 0x002236 ;      // Alt  + Delete      '∶' 1B 5B 33 3B 33 7E
const wchar_t wcsASDEL  = 0x002237 ;      // Alt  + Shift+Del   '∷' 1B 5B 33 3B 34 7E
const wchar_t wcsCDEL   = 0x002238 ;      // Ctrl + Delete      '∸' 1B 5B 33 3B 35 7E
const wchar_t wcsCSDEL  = 0x002239 ;      // Ctrl + Shift+Del   '∹' 1B 5B 33 3B 36 7E
const wchar_t wcsCASDEL = 0x00223A ;      // Ctrl+Alt+Shift+Del '∺' 1B 5B 33 3B 38 7E

//*******************************************************************
//* Line drawing characters and miscellaneous block characters.     *
//* These definitions are taken directly from the author's NcDialog *
//* API (NCurses.hpp).                                              *
//* --------------------------------------------------------------- *
//* The wcsXXXX group of character definitions are the wchar_t      *
//* character codes that correspond to the UTF-8 character encoding *
//* for line-drawing characters and other miscellaneous characters  *
//* found in the Linux/UNIX character set.                          *
//*******************************************************************

const wchar_t wcsSPACE   = 0x000020 ;     // space (blank)
const wchar_t wcsBLOCK   = 0x0025AE ;     // solid block
const wchar_t wcsDIAMOND = 0x0025C6 ;     // diamond (black)
const wchar_t wcsPATTERN = 0x002592 ;     // patterned block
const wchar_t wcsDEGREE  = 0x0000B0 ;     // degree symbol
const wchar_t wcsPLMINUS = 0x0000B1 ;     // plus-and-minus symbol
const wchar_t wcsBOARD   = 0x002592 ;     // pattern of squares
const wchar_t wcsLANTERN = 0x002603 ;     // light bulb
const wchar_t wcsLR      = 0x002518 ;     // drawing character - lower right corner
const wchar_t wcsUR      = 0x002510 ;     // drawing character - upper right corner
const wchar_t wcsUL      = 0x00250C ;     // drawing character - upper left corner
const wchar_t wcsLL      = 0x002514 ;     // drawing character - lower left corner
const wchar_t wcsINSECT  = 0x00253C ;     // drawing character - line intersection
const wchar_t wcsSCAN1   = 0x0023BA ;     // horizontal scanline #1
const wchar_t wcsSCAN3   = 0x0023BB ;     // horizontal scanline #3
const wchar_t wcsHORIZ   = 0x002500 ;     // drawing character - horizontal line
const wchar_t wcsSCAN7   = 0x0023BC ;     // horizontal scanline #7
const wchar_t wcsSCAN9   = 0x0023BD ;     // horizontal scanline #9
const wchar_t wcsLTEE    = 0x00251C ;     // drawing character - T for left-side connect
const wchar_t wcsRTEE    = 0x002524 ;     // drawing character - T for right-edge connect
const wchar_t wcsBTEE    = 0x002534 ;     // drawing character - T for bottom-edge connect
const wchar_t wcsTTEE    = 0x00252C ;     // drawing character - T for top-edge connect
const wchar_t wcsVERT    = 0x002502 ;     // drawing character - vertical line
const wchar_t wcsLEQUAL  = 0x002264 ;     // less-than-or-equal symbol    [same as wcsALT_L keycode]
const wchar_t wcsGEQUAL  = 0x002265 ;     // greater-than-or equal symbol [same as wcsALT_M keycode]
const wchar_t wcsPI      = 0x0003C0 ;     // greek letter Pi
const wchar_t wcsNEQUAL  = 0x002260 ;     // not-equal-to symbol          [same as wcsALT_H keycode]
const wchar_t wcsSTRLING = 0x0000A3 ;     // U.K Pound Sterling symbol
const wchar_t wcsBULLET  = 0x0000B7 ;     // bullet mark (centered dot)
//* Extra line-drawing characters defined in the Unicode character set.        *
const wchar_t wcsHORIZs  = wcsHORIZ ;  // single-line norm - horizontal line
const wchar_t wcsHORIZb  = 0x002501 ;  // single-line bold - horizontal line
const wchar_t wcsHORIZd  = 0x002550 ;  // dual-line        - horizontal line
const wchar_t wcsVERTs   = wcsVERT  ;  // single-line norm - vertical line
const wchar_t wcsVERTb   = 0x002503 ;  // single-line bold - vertical line
const wchar_t wcsVERTd   = 0x002551 ;  // dual-line        - vertical line
const wchar_t wcsULs     = wcsUL ;     // single-line norm - upper left corner 
const wchar_t wcsULb     = 0x00250F ;  // single-line bold - upper left corner
const wchar_t wcsULd     = 0x002554 ;  // dual-line        - upper left corner
const wchar_t wcsURs     = wcsUR ;     // single-line norm - upper right corner 
const wchar_t wcsURb     = 0x002513 ;  // single-line bold - upper right corner
const wchar_t wcsURd     = 0x002557 ;  // dual-line        - upper right corner
const wchar_t wcsLLs     = wcsLL ;     // single-line norm - lower left corner 
const wchar_t wcsLLb     = 0x002517 ;  // single-line bold - lower left corner
const wchar_t wcsLLd     = 0x00255A ;  // dual-line        - lower left corner
const wchar_t wcsLRs     = wcsLR ;     // single-line norm - lower right corner 
const wchar_t wcsLRb     = 0x00251B ;  // single-line bold - lower right corner
const wchar_t wcsLRd     = 0x00255D ;  // dual-line        - lower right corner
const wchar_t wcsLTEEs   = wcsLTEE  ;  // single-line norm - T for left-edge connect
const wchar_t wcsLTEEb   = 0x002523 ;  // single-line bold - T for left-edge connect
const wchar_t wcsLTEEd   = 0x002560 ;  // dual-line        - T for left-edge connect
const wchar_t wcsRTEEs   = wcsRTEE  ;  // single-line norm - T for right-edge connect
const wchar_t wcsRTEEb   = 0x00252B ;  // single-line bold - T for right-edge connect
const wchar_t wcsRTEEd   = 0x002563 ;  // dual-line        - T for right-edge connect
const wchar_t wcsBTEEs   = wcsBTEE  ;  // single-line norm - T for bottom-edge connect
const wchar_t wcsBTEEb   = 0x00253B ;  // single-line bold - T for bottom-edge connect
const wchar_t wcsBTEEd   = 0x002569 ;  // dual-line        - T for bottom-edge connect
const wchar_t wcsTTEEs   = wcsTTEE  ;  // single-line norm - T for top-edge connect
const wchar_t wcsTTEEb   = 0x002533 ;  // single-line bold - T for top-edge connect
const wchar_t wcsTTEEd   = 0x002566 ;  // dual-line        - T for top-edge connect
const wchar_t wcsINSECTs = wcsINSECT;  // single-line norm - line intersection
const wchar_t wcsINSECTb = 0x00254B ;  // single-line bold - line intersection
const wchar_t wcsINSECTd = 0x00256C ;  // dual-line        - line intersection
//* Dash (dotted) lines *
const wchar_t wcsHDASH2s = 0x00254C ;  // norm - 2-dash horizontal line
const wchar_t wcsHDASH2b = 0x00254D ;  // bold - 2-dash horizontal line
const wchar_t wcsVDASH2s = 0x00254E ;  // norm - 2-dash vertical line
const wchar_t wcsVDASH2b = 0x00254F ;  // bold - 2-dash vertical line
const wchar_t wcsHDASH3s = 0x002504 ;  // norm - 3-dash horizontal line
const wchar_t wcsHDASH3b = 0x002505 ;  // bold - 3-dash horizontal line
const wchar_t wcsVDASH3s = 0x002506 ;  // norm - 3-dash vertical line
const wchar_t wcsVDASH3b = 0x002507 ;  // bold - 3-dash vertical line
const wchar_t wcsHDASH4s = 0x002508 ;  // norm - 4-dash horizontal line
const wchar_t wcsHDASH4b = 0x002509 ;  // bold - 4-dash horizontal line
const wchar_t wcsVDASH4s = 0x00250A ;  // norm - 4-dash vertical line
const wchar_t wcsVDASH4b = 0x00250B ;  // bold - 4-dash vertical line

//* Miscellaneous special characters defined in the Unicode character set.     *
//* There are many additional text-mode drawing characters defined by the      *
//* Unicode standard. We define only a subset of them here. For a complete list*
//* visit the Unicode Consortium at  http://www.unicode.org - specifically the *
//* listings for 0x2500-0x257f and 0x2580-0x259F.                              *
const wchar_t wcsLTEEbv  = 0x002520 ;  // T for left-edge connect - bold vert. norm hori.
const wchar_t wcsLTEEbh  = 0x00251D ;  // T for left-edge connect - bold horiz. norm vert.
const wchar_t wcsLTEEdv  = 0x00255F ;  // T for left-edge connect - dual vert. single horiz.
const wchar_t wcsLTEEdh  = 0x00255E ;  // T for left-edge connect - dual horiz. single vert.

const wchar_t wcsRTEEbv  = 0x002528 ;  // T for right-edge connect - bold vert. norm hori.
const wchar_t wcsRTEEbh  = 0x002525 ;  // T for right-edge connect - bold horiz. norm vert.
const wchar_t wcsRTEEdv  = 0x002562 ;  // T for right-edge connect - dual vert. single horiz.
const wchar_t wcsRTEEdh  = 0x002561 ;  // T for right-edge connect - single vert. dual horiz.

const wchar_t wcsBTEEbv  = 0x002538 ;  // T for bottom-edge connect - bold vert. norm hori.
const wchar_t wcsBTEEbh  = 0x002537 ;  // T for bottom-edge connect - bold horiz. norm vert.
const wchar_t wcsBTEEdv  = 0x002568 ;  // T for bottom-edge connect - dual vert. single horiz.
const wchar_t wcsBTEEdh  = 0x002567 ;  // T for bottom-edge connect - single vert. dual horiz.

const wchar_t wcsTTEEbv  = 0x002530 ;  // T for top-edge connect - bold vert. norm hori.
const wchar_t wcsTTEEbh  = 0x00252F ;  // T for top-edge connect - bold horiz. norm vert.
const wchar_t wcsTTEEdv  = 0x002565 ;  // T for top-edge connect - dual vert. single horiz.
const wchar_t wcsTTEEdh  = 0x002564 ;  // T for top-edge connect - single vert. dual horiz.

const wchar_t wcsINSECTbv= 0x002542 ;  // line intersection - bold vert. norm horiz. 
const wchar_t wcsINSECTbh= 0x00253F ;  // line intersection - bold horiz. norm vert.
const wchar_t wcsINSECTdv= 0x00256B ;  // line intersection - dual vert. single horiz.
const wchar_t wcsINSECTdh= 0x00256A ;  // line intersection - single vert. dual horiz.
const wchar_t wcsBLOCKl  = 0x002591 ;  // block character - light pattern
const wchar_t wcsBLOCKm  = 0x002592 ;  // block character - medium pattern
const wchar_t wcsBLOCKd  = 0x002593 ;  // block character - dark pattern
const wchar_t wcsBLOCKs  = 0x002588 ;  // block character - solid black
const wchar_t wcsWCIRCLE = 0x0025CB ;  // white circle  ( ○ )
const wchar_t wcsBCIRCLE = 0x0025CF ;  // black circle  ( ● )
const wchar_t wcsFISHEYE = 0x0025C9 ;  // black circle within a white circle  ( ◉ )
const wchar_t wcsBULLSEYE= 0x0025CE ;  // small circle within a larger circle ( ◎ )
const wchar_t wcsWDIAMOND= 0x0025C7 ;  // white diamond ( ◇ see also wcsDIAMOND above)
const wchar_t wcsDIAMONDS= 0x0025C8 ;  // black diamond within a white diamond  ( ◈ )
const wchar_t wcsCHECK1  = 0x002713 ;  // checkmark (standard) ( ✓ )
const wchar_t wcsCHECK2  = 0x002714 ;  // checkmark (heavy)    ( ✔ )
const wchar_t wcsCROSS1  = 0x002715 ;  // cross (standard)     ( ✕ )
const wchar_t wcsCROSS2  = 0x002716 ;  // cross (heavy)        ( ✖ )

