#if 1

    #include "project.h"

    // ----------------------------------------------------------------
    // every function in this source file can be marked as   static
    // except for the "constructor" function
    // ----------------------------------------------------------------

    static MAINTYPE RICHTEXTEDITId = SOB_TYPE_NO;

// ----------------------------------------------------------------
//  example of a custom  interception function     for a  subclassed   widget
// ----------------------------------------------------------------

WNDPROC    wpOrigRICHTEXTEDITProc ;

RICHTEXTEDITOBJECT* RICHTEXTEDITLastCreated;

static LRESULT CALLBACK RICHTEXTEDIT_SUBCLASS_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    // (demo) function causes the widget to change colour when the mouse hovers over the widget.
    // does not work for all widget types

    TRACKMOUSEEVENT tme = { sizeof(tme) };
    static myInt trackingFlag = 0;
    static COLORREF bgRGBenter = 0;

    union
    {
        SOBDATA* thisSob;
        RICHTEXTEDITOBJECT* thisObject;
    } promote = {};

    // be WARNED
    // try to find an OBJECT by the value of a (generic) property, in this case
    //  it's  HWND , we default to finding it by it scanning the SOBDATA.
    // If we assume, as in this function, that it was triggered by widget type XYZ
    // then we would need a widget specific search function.
    // So scan the SOBDATA and then "cast" using the union to the appropriate widgettype.

    promote.thisSob = sob_find_hwnd(hwnd);
    if (promote.thisSob)
    {
        switch (msg)
        {
            case WM_NCHITTEST:

                if (!trackingFlag)
                {
                    tme.dwFlags = TME_LEAVE; // TME_HOVER;
                    tme.hwndTrack = hwnd;
                    tme.dwHoverTime = 1000;
                    TrackMouseEvent(&tme);

                    trackingFlag = 1;
                    bgRGBenter = promote.thisObject->bgRGB;

                    RICHTEXTEDIT.SetRGB(promote.thisObject, RGB(0xc6, 0xe7, 0xff));
                }
                break;

            case WM_MOUSEHOVER:

                break;

            case WM_MOUSELEAVE:
                if (trackingFlag)
                {
                    trackingFlag = 0;
                    RICHTEXTEDIT.SetRGB(promote.thisObject, bgRGBenter);
                }
                break;

            default:
                break;
        }
    }
    return CallWindowProc(wpOrigRICHTEXTEDITProc, hwnd, msg, wParam, lParam);
}

// ----------------------------------------------------------------
//  example of a custom  ownerdrawn handler
// ----------------------------------------------------------------

// following line defines a functional prototype according to a
// predefined schema. Enforces strong typing when populating the
// OBJECT's private VTABLE

OWNDERDRAWNHANDLER RICHTEXTEDIT_OD_HANDLER;

void  RICHTEXTEDIT_OD_HANDLER(SOBDATA* sData, LPDRAWITEMSTRUCT lpDrawItemStruct)
{
    // the code here is common to BUTTONs and STATIC widgets
    // and is fairly involved.
    // So it has been made a common function located in the  ownderdraw_support files.

    bDrawButtonRGB(sData, lpDrawItemStruct);
}

// ----------------------------------------------------------------
//       custom functions / methods for this OBJECT
// ----------------------------------------------------------------

union
{
    SOBDATA* thisSob;
    RICHTEXTEDITOBJECT* thisObject;
} RICHTEXTEDIT_cast = {};

RICHTEXTEDITFNC_SHAKE   RICHTEXTEDIT_SetDefault;
static myUInt RICHTEXTEDIT_SetDefault(RICHTEXTEDITOBJECT* thisObject)
{
    RICHTEXTEDIT_cast.thisObject = thisObject;
    errorRichTextEdit = RICHTEXTEDIT_cast.thisSob;

    rtEditHWND = RICHTEXTEDIT_cast.thisSob->hWnd;
    return SOB_OK;

}

RICHTEXTEDITFNC_SETTEXT   RICHTEXTEDIT_TextRTF;
static myUInt RICHTEXTEDIT_TextRTF(RICHTEXTEDITOBJECT* thisWid,  TCHAR* txt)
{

    // example:
    // addTextRTF ( (TCHAR *) L"{\\rtf1\\ansi This is some \\b bold\\b0  text.}");
    // commands  such as  \b  \b0   MUST be followed a   SPACE or a \n
    DWORD WideLength = (DWORD)wcslen(txt) + 1;
    PSTR Utf8;
    DWORD Length;
    INT ReturnedLength;

    // A utf8 representation shouldn't be longer than 4 times the size
    // of the utf16 one.
    Length = WideLength * 4;
    Utf8 = (PSTR)malloc(Length);
    if (!Utf8) { return 1; }

    ReturnedLength = WideCharToMultiByte
    (CP_UTF8
    , 0
    , txt
    , WideLength - 1
    , Utf8
    , Length - 1
    , NULL
    , NULL
    );
    if (ReturnedLength)
    {
        // Need to zero terminate...
        Utf8[ReturnedLength] = 0;
    }
    else { /* TODO: handle failure */ }

    //  Pointer to the null-terminated text to insert. This text is an ANSI string, unless the code page is 1200 (Unicode),

    SETTEXTEX TextInfo = { 0 };

    TextInfo.flags = ST_SELECTION;
    TextInfo.codepage = CP_UTF8;

    SendMessage(thisWid->hWnd, EM_SETTEXTEX, (WPARAM)&TextInfo, (LPARAM)Utf8);

    free(Utf8);
    return 1;

}

void addTextBoldA(RICHTEXTEDITOBJECT* thisWid , TCHAR* txt)
{
    DWORD fromPos = 0, toPos = 0;;

    SendMessage(thisWid->hWnd, EM_SETSEL, -1, -1);
    SendMessage(thisWid->hWnd, EM_GETSEL, 0, (LPARAM)&fromPos);

    SendMessage(thisWid->hWnd, EM_REPLACESEL, 0, (LPARAM)txt);
    SendMessage(thisWid->hWnd, EM_SETSEL, fromPos, -1);

    SendMessage(thisWid->hWnd, EM_GETSEL, (WPARAM)&fromPos, (LPARAM)&toPos);
    SendMessage(thisWid->hWnd, EM_SETSEL, fromPos, toPos - 1);

    CHARFORMAT2 boldfont = {  };
    boldfont.cbSize = sizeof(CHARFORMAT2);
    boldfont.dwMask = CFM_BOLD BOR CFM_COLOR;
    boldfont.dwEffects = CFE_BOLD;
    boldfont.crTextColor = 0x0ff;
    SendMessage(thisWid->hWnd, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&boldfont);
}

RICHTEXTEDITFNC_SETTEXT   RICHTEXTEDIT_TextBold;
static myUInt RICHTEXTEDIT_TextBold(RICHTEXTEDITOBJECT* thisWid,  TCHAR* txt)
{

    DWORD fromPos = 0, toPos = 0;;

    addTextBoldA(thisWid , txt);

    CHARFORMAT2 boldfont = { };

    SendMessage(thisWid->hWnd, EM_SETSEL, -1, -1);
    SendMessage(thisWid->hWnd, EM_GETSEL, (WPARAM)&fromPos, (LPARAM)&toPos);
    SendMessage(thisWid->hWnd, EM_SETSEL, fromPos + 1, -1);

    SendMessage(thisWid->hWnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&boldfont);

    boldfont.cbSize = sizeof(CHARFORMAT2);
    boldfont.dwMask = CFM_BOLD BOR CFM_COLOR;
    boldfont.dwEffects = 0;
    boldfont.crTextColor = 0;
    SendMessage(thisWid->hWnd, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&boldfont);
    return 1;
}

RICHTEXTEDITFNC_SHAKE  RICHTEXTEDIT_Empty;
static  myUInt RICHTEXTEDIT_Empty(RICHTEXTEDITOBJECT* thisWid)
{
    return (myUInt)SendMessage(thisWid->hWnd, WM_SETTEXT, 0, (LPARAM)L"");
}

RICHTEXTEDITFNC_SETTEXT   RICHTEXTEDIT_TextAdd;
static myUInt RICHTEXTEDIT_TextAdd(RICHTEXTEDITOBJECT* thisWid, TCHAR* txt)
{

    SendMessage(thisWid->hWnd, EM_SETSEL, -1, -1);
    SendMessage(thisWid->hWnd, EM_REPLACESEL, 0, (LPARAM)txt);

    return 0;
}

// ---------------------------------
// following line defines a functional prototype according to a
// predefined schema. Enforces strong typing when populating the
// OBJECT's private VTABLE

RICHTEXTEDITFNC_SETVALUE RICHTEXTEDIT_SetRGB;
static  myUInt RICHTEXTEDIT_SetRGB(RICHTEXTEDITOBJECT* thisWid, myUInt rgbValue)
{
    return sob_SetRGB(thisWid->psSobData, (COLORREF)rgbValue);
}

// ---------------------------------
#if 0
    RICHTEXTEDITFNC_SETVALUE RICHTEXTEDIT_GetState;
static  myUInt RICHTEXTEDIT_GetState(RICHTEXTEDITOBJECT* thisObject)
{
    return ((myInt)SendMessage(thisObject->hWnd, BM_GETCHECK, 0, 0)
    EQU BST_CHECKED);
}
#endif
// ---------------------------------
#if 0
    RICHTEXTEDITFNC_SETVALUE  RICHTEXTEDIT_SetState;
static  myUInt RICHTEXTEDIT_SetState(RICHTEXTEDITOBJECT* thisObject, myUInt check)
{
    SOBDATA* pData = thisObject->ppSobData;

    SOBDATA* cData = sob_get_first_child(pData);
    while (cData)
    {
        if (cData->type EQU  RICHTEXTEDITId)
        {
            SendMessage
            (cData->hWnd
            , BM_SETCHECK
            , (WPARAM)BST_UNCHECKED
            , 0
            );
        }
        cData = sob_get_next_child(cData);
    }

    if (check) check = BST_CHECKED;
    else  check = BST_UNCHECKED;

    SendMessage
    (thisObject->hWnd
    , BM_SETCHECK
    , (WPARAM)check
    , 0
    );
    return (check EQU BST_CHECKED);
}
#endif
// ---------------------------------
#if 0
    RICHTEXTEDITFNC_SHAKE RICHTEXTEDIT_ToggleState;
static  myUInt RICHTEXTEDIT_ToggleState(RICHTEXTEDITOBJECT* thisObject)
{
    return  RICHTEXTEDIT.SetState(thisObject, (RICHTEXTEDIT.GetState(thisObject)  EQU 0));
}
#endif
// ---------------------------------

RICHTEXTEDITFNC_GETTEXT  RICHTEXTEDIT_GetTitle;
TCHAR* RICHTEXTEDIT_GetTitle(RICHTEXTEDITOBJECT* thisWid)
{
    return   fnGetTitleGeneric(thisWid->psSobData);
}

// ---------------------------------

RICHTEXTEDITFNC_SETEVENTHANDLER RICHTEXTEDIT_SetEventHandler;
static myUInt RICHTEXTEDIT_SetEventHandler(RICHTEXTEDITOBJECT* thisWid, EVENTHANDLER value)
{
    fnSetEventHandlerGeneric(thisWid->psSobData, value);

    return 1;
}

// ---------------------------------

RICHTEXTEDITFNC_SETTEXT  RICHTEXTEDIT_SetTitle;
myUInt RICHTEXTEDIT_SetTitle(RICHTEXTEDITOBJECT* thisWid,  TCHAR* txt)
{
    return fnSetTitleGeneric(thisWid->psSobData, txt );
}

// ---------------------------------

SOBFNC_GETTEXT RICHTEXTEDIT_SOB_GetType;
static  TCHAR* RICHTEXTEDIT_SOB_GetType(SOBDATA * thisObj)
{
    static TCHAR objType[] = TEXT("RICHTEXTEDIT");
    return  objType;
}// ---------------------------------

RICHTEXTEDITFNC_GETTEXT RICHTEXTEDIT_GetType;
static  TCHAR* RICHTEXTEDIT_GetType(RICHTEXTEDITOBJECT* thisWid)
{
    static TCHAR objType[] = TEXT("RICHTEXTEDIT");
    return  objType;
}

// ---------------------------------

RICHTEXTEDITFNC_SETVALUE RICHTEXTEDIT_SetShow;
static  myUInt RICHTEXTEDIT_SetShow(RICHTEXTEDITOBJECT* thisWid, myUInt value)
{
    // this will need customising if the widget is complex
    // i.e. is really a member of a set of  widgets that can not all appear at the same time
    // e.g. a radio button     or an  overlay set

    sobObj_SetShow(thisWid->psSobData, value);
    return  1;
}

// ----------------------------------------------------------------

// example of a EVENTHANDLER: in this case when the widget is clicked on with the mouse
// Careful this  function is only valid for widgets that receive
//   WM_COMMAND messages   carrying a  BN_CLICKED notification

static void RICHTEXTEDIT_object_clicked(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, SOBDATA * sData)
{
    RICHTEXTEDITOBJECT* thisObject = (RICHTEXTEDITOBJECT*)lParam;
    WORD NotificationCode = HIWORD(wParam);
    WORD identifier = LOWORD(wParam);

    switch (NotificationCode)
    {
        case BN_CLICKED:
            swprintf_s
            (txtBuffer, szTxtBuffer, L" RICHTEXTEDIT clicked 0x%04x  0x%04x  0x%04x\n"
            , thisObject->sIndex
            , NotificationCode
            , identifier);    addText(txtBuffer);

            //    RICHTEXTEDIT.ToggleState(thisObject);

            break;

        default:
            #if DebuggingAid
                swprintf_s
                (txtBuffer, szTxtBuffer, L" RICHTEXTEDIT unhandled notification 0x%04x  0x%04x  0x%04x\n"
                , thisObject->sIndex
                , NotificationCode
                , identifier);    addText(txtBuffer);
            #endif

            break;
    }
}

// ----------------------------------------------------------------
// the "destructor" function
// ----------------------------------------------------------------

RICHTEXTEDITFNC_SHAKE RICHTEXTEDIT_delete;
static  myUInt RICHTEXTEDIT_delete(RICHTEXTEDITOBJECT* thisWid)
{
    // this will need customising if the widget is complex
    // i.e. is really a set of widgets
    // e.g. an  overlay set

    sobObj_delete(thisWid->psSobData);
    return  1;
}

RICHTEXTEDITFNC_GETLAST RICHTEXTEDIT_LastWidget;
RICHTEXTEDITOBJECT* RICHTEXTEDIT_LastWidget( )
{
    return RICHTEXTEDITLastCreated;
}

// ----------------------------------------------------------------
// the "constructor" function
// ----------------------------------------------------------------

RICHTEXTEDITFNC_GETOBJECT RICHTEXTEDIT_create;
RICHTEXTEDITOBJECT* RICHTEXTEDIT_create(TCHAR* txt , int lines )
{
    union
    {
        SOBDATA* thisSob;
        RICHTEXTEDITOBJECT* thisObject;
    } promote = {};

    // ---------------------------------
    // use the FONT assigned to the PARENT as guidelines for text size

    SOBDATA* pData = sob_get_base(gpSobIndex);
    int nbrLines = lines;  // some CONSTRUCTOR functions want 2 input parameters,
    // typicaly the 2nd parameter is the height of the widget
    // give as the nbr of text lines

    int height = 0;
    int width = 0;

    int_sob_text_size(pData, txt, &width, &height);

    // But the HEIGHT only covers text, we need to increase it to allow multiple text
    // lines   AND for any SOB surround
    int objHeight = height * nbrLines + 8;
    int objWidth = width + 20;

    // ---------------------------------
    // A  Screen OBject ( SOB )  is really a structure
    //    the GENERIC structure
    //    ptr to the VTABLE
    // ---------------------------------
    // An OBJECT instance is really a structure
    //    the GENERIC structure
    //    ptr to the OBJECT's private functions
    // ---------------------------------
    // So a   SOB   and an    OBJECT     have identical structures
    // WHY:
    // This way we can create a SOB as a sort of generic OBJECT.
    // Some functions, predominantly concerned with
    //    screen size management
    //    screen position management
    //   are OBJECT independent.
    //
    // Then we can have an OBJECT specific "view" were each OBJECT type
    // can have its own strongly typed structure.
    // ---------------------------------

    if (!RICHTEXTEDITId)   RICHTEXTEDITId = int_next_maintype();

    promote.thisSob = sob_new_new(RICHTEXTEDITId, SOB_CONTAINER_NO, objWidth, objHeight);
    if (promote.thisSob EQU NULL)
    {
        int_return_maintype();  //  give the custom RICHTEXTEDITId back
        return NULL;
    }





    static HFONT hFont;



    hFont = CreateFont
    (
        24,                   /* cHeight */
        0,                    /* cWidth use default*/
        0,                    /* cEscapement */
        0,                    /* cOrientation */
        FW_NORMAL,            /* cWeight */
        FALSE,                /* bItalic */
        FALSE,                /* bUnderline */
        FALSE,                /* bStrikeOut */
        DEFAULT_CHARSET,      /* iCharSet */
        OUT_DEFAULT_PRECIS,   /* iOutPrecision */
        CLIP_DEFAULT_PRECIS,  /* iClipPrecision */
        DEFAULT_QUALITY,      /* iQuality */
        DEFAULT_PITCH,        /* iPitchAndFamily */
        (TCHAR*)L"Courier New"               /* pszFacename */
    );








    // ---------------------------------
    // if there are any special properties that need to be initialised
    // then do it here

    promote.thisSob->top = 0;
    promote.thisSob->left = 2;
    promote.thisSob->bottom = 0;
    promote.thisSob->right = 2;

    promote.thisSob->fncCustomGetTypeText = &RICHTEXTEDIT_SOB_GetType;

    // as an example:
    // promote.thisObject->txtRGB = RGB(0, 0, 0);
    // promote.thisObject->bgRGB = RGB(0xe0, 0, 0xff);

    // ---------------------------------
    // create an instance of the  RICHTEXTEDIT widget on the display

    TCHAR*  objClassName;

    HINSTANCE hLib;
    objClassName = (TCHAR*)_TEXT("RichEdit50W");
    hLib = LoadLibrary(_TEXT("MSFTEDIT.Dll"));
    if (hLib EQU  NULL)
    {
        objClassName = (TCHAR*)_TEXT("RichEdit20W");
        hLib = LoadLibrary((TCHAR*)_TEXT("RichEd20.Dll"));
        if (hLib EQU NULL)
        {
        }
    }

    DWORD objStyle = WS_CHILD | WS_VISIBLE |  WS_BORDER |
    WS_HSCROLL | WS_VSCROLL | ES_WANTRETURN |
    ES_MULTILINE | ES_LEFT |
    ES_NOHIDESEL;
    (void)sob_CreateWindowExNew(promote.thisSob, 0, objClassName, txt, objStyle);

    // ---------------------------------
    // if you need to subclass the widget, then insert the interception function here.
    wpOrigRICHTEXTEDITProc = (WNDPROC)SetWindowLongPtr(promote.thisObject->hWnd, GWLP_WNDPROC, (LONG_PTR)RICHTEXTEDIT_SUBCLASS_proc);

    // ---------------------------------
    // All OBJECTS are created with a prime/main  EVENTHANDLER
    // For this particular OBJECT the prime/main  EVENTHANDLER
    // is a left mouse click

    // Normally the  "user"  sets a, probably different, EVENTHANDLER for
    // each instantiated OBJECT.

    // If you want you can set a default EVENTHANDLER which the
    // "user" can overwrite for a specific OBJECT

    promote.thisObject->eventHandler = (EVENTHANDLER)RICHTEXTEDIT_object_clicked;

    // ---------------------------------

    SendMessage(promote.thisObject->hWnd, WM_SETTEXT, 0, (LPARAM)NULL);

    SendMessage(promote.thisObject->hWnd, EM_EXLIMITTEXT, -1, 0);
    SendMessage(promote.thisObject->hWnd, EM_SETLIMITTEXT, -1, 0);

    SendMessage(promote.thisObject->hWnd, EM_SETTEXTMODE, TM_RICHTEXT, (LPARAM)0);
    SendMessage(promote.thisObject->hWnd, EM_SETREADONLY, 1, 0);



    SendMessage(promote.thisObject->hWnd, WM_SETFONT, (WPARAM)hFont, TRUE);

    RICHTEXTEDITLastCreated = promote.thisObject;

    return   promote.thisObject;
}

// ----------------------------------------------------------------
// the external view
// ----------------------------------------------------------------

// BE WARNED: must be in the same sequence as the struc defn in the  header file
// if you create custom functions / methods for this OBJECT then they
// MUST be in the struct defn and initialised into this struct instance.

RICHTEXTEDITCVTBL  RICHTEXTEDIT =
{
    RICHTEXTEDIT_create,
    RICHTEXTEDIT_delete,

    RICHTEXTEDIT_SetShow,
    RICHTEXTEDIT_GetType,

    RICHTEXTEDIT_GetTitle ,
    RICHTEXTEDIT_SetTitle ,

    RICHTEXTEDIT_SetEventHandler ,

    RICHTEXTEDIT_SetRGB,

    RICHTEXTEDIT_TextAdd ,
    RICHTEXTEDIT_Empty
    , RICHTEXTEDIT_TextBold
    , RICHTEXTEDIT_TextRTF

    , RICHTEXTEDIT_SetDefault

    , RICHTEXTEDIT_LastWidget

    // RICHTEXTEDIT_SetState,
    // RICHTEXTEDIT_ToggleState,

    // RICHTEXTEDIT_GetState
};

#endif
