#if 1

    #include "project.h"

    CONTYPE GRIDId = SOB_CONTAINER_NO;

SOBDATA* GRIDLastCreated;

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

static myUInt  stretchGRID(SOBDATA* conData)
{

    int ColOrRow = conData->ColOrRow;
    int Dimension = conData->dimension;
    int cnt = conData->nbrChildren;
    int remain = 0;
    int defaultWidth = 0, defaultHeight = 0;

    if (!cnt)
    {

    }
    else     if (ColOrRow)
    {

        defaultWidth = conData->displayWidth - 2 * (conData->left + conData->right);

        defaultWidth -= (Dimension - 1) * (sobBnd.left + sobBnd.right);
        defaultWidth = defaultWidth / Dimension;

        defaultHeight = conData->displayHeight - 2 * (conData->top + conData->bottom);
        remain = (cnt + Dimension - 1) / Dimension;
        defaultHeight -= (remain - 1) * (sobBnd.bottom + sobBnd.top);
        defaultHeight = defaultHeight / remain;
    }
    else
    {
        defaultWidth = conData->displayWidth - 2 * (conData->left + conData->right);
        remain = (cnt + Dimension - 1) / Dimension;
        defaultWidth -= (remain - 1) * (sobBnd.left + sobBnd.right);
        defaultWidth = defaultWidth / remain;

        defaultHeight = conData->displayHeight - 2 * (conData->top + conData->bottom);
        defaultHeight -= (Dimension - 1) * (sobBnd.bottom + sobBnd.top);
        defaultHeight = defaultHeight / Dimension;
    }

    // ---------------------------------
    // now set childrens display WIDTH/HEIGHT
    // Ignore any STRETCH effects.
    // Ignore any surround/boundaries.
    // Just set  the width/height of the area that the object's pixels occupy

    SOBDATA* childObject = GRID.FirstChild(conData);

    while (childObject)
    {
        childObject->displayHeight = defaultHeight;
        childObject->displayWidth = defaultWidth;
        childObject = SOB.GetNextChild(childObject);
    }

    // ---------------------------------
    // now set childrens display POSITIONS
    // of the object in the CLIENT area of the host.
    // So take account of the  sobBnd  values,
    // and  IGNORE  STRETCHING

    int Xpos = conData->left;
    int Ypos = conData->top;

    cnt = 0;

    childObject = GRID.FirstChild(conData);
    while (childObject)
    {
        // ---------------------------------
        //   child positioning
        // ---------------------------------

        childObject->displayX = Xpos;
        childObject->displayY = Ypos;

        if (ColOrRow)
        Xpos += childObject->displayWidth + sobBnd.left + sobBnd.right;
        else
        Ypos += childObject->displayHeight + sobBnd.bottom + sobBnd.top;

        cnt++;
        if (cnt EQU Dimension)
        {
            cnt = 0;

            if (ColOrRow)
            {
                Xpos = conData->left;
                Ypos += childObject->displayHeight + sobBnd.bottom + sobBnd.top;
            }
            else
            {
                Ypos = conData->top;
                Xpos += childObject->displayWidth + sobBnd.left + sobBnd.right;
            }
        }
        childObject = SOB.GetNextChild(childObject);
    }
    return SOB_OK;
}

static  myUInt ReSize_GRID(SOBDATA* conData)
{   // conData   = address of the SOBDATA of the GRID CONTAINER

    // ---------------------------------
    // this container template creates a matrix/grid of
    // children and sets them all to have the
    // the Width of the widest child
    // and Height of the hichest child
    //
    // Additionally this container will position the children
    // in a uniform grid
    //
    // You must specify 2 parameters:
    //   ColOrRow  =0   in ROWS
    //             else in COLUMNS
    //   DIMENSION = max nbr of rows if ColRow = 0
    //             else max nbr of COLUMNS
    //

    int ColOrRow  = conData->ColOrRow;
    int Dimension = conData->dimension;

    // ---------------------------------
    // CUSTOMISE THIS     the initialisation value of   setDefault
    // 0    no width/height management
    // 1         manage HEIGHT  if   ColOrRow   selects ROW = 0
    //      or   manage WIDTH  if   ColOrRow   selects COL = 1
    // ---------------------------------

    int useDefault = 0;

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

    int WidestChild = 0, HighestChild = 0;
    conData->minWidth = 0;
    conData->minHeight = 0;

    int cnt = 0;

    // ---------------------------------
    SOBDATA* childObject = GRID.FirstChild(conData);
    while (childObject)
    {
        cnt++;

        if (childObject->minWidth > WidestChild)     WidestChild = childObject->minWidth;
        if (childObject->minHeight > HighestChild)   HighestChild = childObject->minHeight;

        conData->minHeight += childObject->minHeight;
        conData->minWidth += childObject->minWidth;

        childObject = SOB.GetNextChild(childObject);
    }

    conData->nbrChildren = cnt;

    // ---------------------------------
    int otherDim = ( cnt + Dimension - 1) / Dimension;
    if (cnt)
    {
        if (ColOrRow)
        {
            conData->minWidth  = Dimension * WidestChild;
            conData->minWidth += (Dimension - 1) * (sobBnd.left + sobBnd.right);
            conData->minWidth += 2 * (conData->left + conData->right);

            conData->minHeight = otherDim * HighestChild;
            conData->minHeight += (otherDim - 1) * (sobBnd.top + sobBnd.bottom);
            conData->minHeight += 2 * (conData->top + conData->bottom);
        }
        else
        {
            conData->minWidth = otherDim * WidestChild;
            conData->minWidth += (otherDim - 1) * (sobBnd.left + sobBnd.right);
            conData->minWidth += 2 * (conData->left + conData->right);

            conData->minHeight = Dimension * HighestChild;
            conData->minHeight += (Dimension - 1) * (sobBnd.top + sobBnd.bottom);
            conData->minHeight += 2 * (conData->top + conData->bottom);
        }
    }

    conData->effectiveWidth = conData->minWidth;
    conData->effectiveHeight = conData->minHeight;

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

    int defaultWidth = WidestChild, defaultHeight = HighestChild;

    // ---------------------------------
    // now set childrens display WIDTH/HEIGHT
    // Ignore any STRETCH effects.
    // Ignore any surround/boundaries.
    // Just set  the width/height of the area that the object's pixels occupy

    childObject = GRID.FirstChild(conData);
    while (childObject)
    {
        if (defaultHeight)  childObject->displayHeight = defaultHeight;
        else childObject->displayHeight = childObject->minHeight;

        if (defaultWidth)  childObject->displayWidth = defaultWidth;
        else childObject->displayWidth = childObject->minWidth;

        childObject = SOB.GetNextChild(childObject);
    }

    // ---------------------------------
    // now set childrens display POSITIONS
    // of the object in the CLIENT area of the host.
    // So take account of the  sobBnd  values,
    // and  IGNORE  STRETCHING

    int Xpos = conData->left;
    int Ypos = conData->top;

    cnt = 0;

    childObject = GRID.FirstChild(conData);
    while (childObject)
    {
        // ---------------------------------
        //   child positioning
        // ---------------------------------

        childObject->displayX = Xpos;
        childObject->displayY = Ypos;

        if (ColOrRow)
        Xpos += childObject->displayWidth + sobBnd.left + sobBnd.right;
        else
        Ypos += childObject->displayHeight + sobBnd.bottom + sobBnd.top;

        cnt++;
        if (cnt EQU Dimension)
        {
            cnt = 0;

            if (ColOrRow)
            {
                Xpos = conData->left;
                Ypos += childObject->displayHeight + sobBnd.bottom + sobBnd.top;
            }
            else
            {
                Ypos = conData->top;
                Xpos += childObject->displayWidth + sobBnd.left + sobBnd.right;
            }
        }

        childObject = SOB.GetNextChild(childObject);
    }

    return SOB_OK;
}

// ---------------------------------
CONTAINERINFORM Display_GRID;
static  myUInt Display_GRID(SOBDATA* conData)
{   // conData   = & SOBDATA for the CONTAINER   being displayed

    // typically called by a parent container
    // we should display this container and its SOB children, and trigger any child containers to do the same!

    if (fnInvalidSobGeneric(conData))
    {
        swprintf_s(txtBuffer, szTxtBuffer, _TEXT("GRID.Display given an invalid SOBOBJ\n"));  ReportError(txtBuffer);
        return NULLV;
    }
    if (conData->subType NEQU GRIDId)
    {
        swprintf_s(txtBuffer, szTxtBuffer, _TEXT("GRID.Display given a non GRID object\n"));  ReportError(txtBuffer);
        SOB.Report(conData);
        return SOB_NOK;
    }

    // swprintf_s(txtBuffer, szTxtBuffer, _TEXT("GRID.Display\n"));  ReportError(txtBuffer);

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

    stretchGRID(conData);
    //     ReSize_GRID(conData);

    return SOB_OK;
}
// ---------------------------------

CONTAINERCREATE LastWidget_GRID;
static SOBDATA* LastWidget_GRID()
{
    return  GRIDLastCreated;
}

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

SOBFNC_GETTEXT GRID_SOB_GetTypeText;
static TCHAR* GRID_SOB_GetTypeText(SOBDATA* thisSob)
{
    static TCHAR objType[] = TEXT("GRID");
    return  objType;
}

CONTAINERGETTEXT GetTypeText_GRID;
static  TCHAR* GetTypeText_GRID(SOBDATA* conData)
{

    if (fnInvalidSobGeneric(conData))
    {
        swprintf_s(txtBuffer, szTxtBuffer, _TEXT("GRID.GetTypeText given an invalid SOBOBJ\n"));  ReportError(txtBuffer);
        return NULL;
    }

    if (conData->subType NEQU GRIDId)
    {
        swprintf_s(txtBuffer, szTxtBuffer, _TEXT("GRID.GetTypeText given a non GRID object\n"));  ReportError(txtBuffer);
        SOB.Report(conData);
        return NULL;
    }

    //  swprintf_s(txtBuffer, szTxtBuffer, _TEXT("GRID.GetTypeText\n"));  ReportError(txtBuffer);

    return   GRID_SOB_GetTypeText(conData);
}

CONTAINERINFORM  UpDateChildren_GRID;

static  myUInt UpDateChildren_GRID(SOBDATA* conData)
{   // conData   = address of the SOBDATA of the GRID CONTAINER   that is UPDATING it's children

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

    if (fnInvalidSobGeneric(conData))
    {
        swprintf_s(txtBuffer, szTxtBuffer, _TEXT("GRID.UpDateChildren given an invalid SOBOBJ\n"));  ReportError(txtBuffer);
        return SOB_NOK;
    }

    if (conData->subType NEQU GRIDId)
    {
        swprintf_s(txtBuffer, szTxtBuffer, _TEXT("GRID.UpDateChildren given a non GRID object\n"));  ReportError(txtBuffer);
        return SOB_NOK;
    }

    //    swprintf_s(txtBuffer, szTxtBuffer, _TEXT("GRID.UpDateChildren %04i\n"), conData->sIndex);  ReportError(txtBuffer);

    stretchGRID(conData);
    //    ReSize_GRID(conData);

    if (
    (conData->effectiveWidth EQU  conData->displayWidth)
    LAND
    (conData->effectiveHeight EQU  conData->displayHeight)
    )
    {
        //           swprintf_s(txtBuffer, szTxtBuffer, _TEXT("NOT DOWNWARDS\n"));  ReportError(txtBuffer);

        //        return SOB_OK;
    }

    SOBDATA* childObject = NULL;
/*

    swprintf_s(txtBuffer, szTxtBuffer, _TEXT("GRID.UpDateChildren  DOWNWARDS %04i\n%0xi %0xi\n%0xi %0xi\n" )
    ,conData->sIndex
    , conData->effectiveWidth, conData->effectiveHeight
    , conData->displayWidth, conData->displayHeight

    );  ReportError(txtBuffer);
*/
    childObject = GRID.FirstChild(conData);
    while (childObject)
    {
        if ((childObject->type EQU SOB_TYPE_CONTAINER) LAND(childObject->pVtbl)   LAND(childObject->pVtbl->UpDateChildren))
        CONTAINER.UpDateChildren(childObject);
        childObject = SOB.GetNextChild(childObject);
    }

    return SOB_OK;
}

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

CONTAINERINFORM  Open_GRID;
static  myUInt Open_GRID(SOBDATA* conData)
{   // conData   = address of the SOBDATA of the GRID CONTAINER   being Opened
    // i.e. made the parent of container of subsequent children

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

    if (fnInvalidSobGeneric(conData))
    {
        swprintf_s(txtBuffer, szTxtBuffer, _TEXT("GRID.Open given an invalid SOBOBJ\n"));  ReportError(txtBuffer);
        return SOB_NOK;
    }

    if (conData->subType NEQU GRIDId)
    {
        swprintf_s(txtBuffer, szTxtBuffer, _TEXT("GRID.Open given a non GRID object\n"));  ReportError(txtBuffer);
        return SOB_NOK;
    }

    // swprintf_s(txtBuffer, szTxtBuffer, _TEXT("GRID.Open\n"));  ReportError(txtBuffer);

    sob_set_pSobObj(conData);

    return SOB_OK;
}

CONTAINERINFORM  Update_GRID;

static  myUInt Update_GRID(SOBDATA* conData)
{   // conData   = address of the SOBDATA of the GRID CONTAINER   being UPDATED

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

    if (fnInvalidSobGeneric(conData))
    {
        swprintf_s(txtBuffer, szTxtBuffer, _TEXT("GRID.Update given an invalid SOBOBJ\n"));  ReportError(txtBuffer);
        return SOB_NOK;
    }

    if (conData->subType NEQU GRIDId)
    {
        swprintf_s(txtBuffer, szTxtBuffer, _TEXT("GRID.Update given a non GRID object\n"));  ReportError(txtBuffer);
        return SOB_NOK;
    }

    // swprintf_s(txtBuffer, szTxtBuffer, _TEXT("GRID.Update\n"));  ReportError(txtBuffer);

    ReSize_GRID(conData);
    CONTAINER.Update(conData->ppSobData);

    return SOB_OK;
}

// ---------------------------------
CONTAINERINFORM   UpDateParent_GRID;

static  myUInt UpDateParent_GRID(SOBDATA* childObject)
{   // childObject   = address of the SOBDATA of the CHILD object   being added

    // ---------------------------------
    // This is only invoked in the generic function    sob_new_new
    // ---------------------------------

    #if DeveloperMode

        if (fnInvalidSobGeneric(childObject))
        {
            swprintf_s(txtBuffer, szTxtBuffer, _TEXT("GRID.UpDateParent given an invalid SOBOBJ\n"));  ReportError(txtBuffer);
            return SOB_NOK;
        }

        if (childObject->ppSobData->subType NEQU GRIDId)
        {
            swprintf_s(txtBuffer, szTxtBuffer, _TEXT("GRID.UpDateParent given a CHILD object belonging to a different container\n"));  ReportError(txtBuffer);
            SOB.Report(childObject);
            return SOB_NOK;
        }

        //  swprintf_s(txtBuffer, szTxtBuffer, _TEXT("GRID.UpDateParent\n"));  ReportError(txtBuffer);
    #endif

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

    (void)Update_GRID(childObject->ppSobData);

    return SOB_OK;
}
// ---------------------------------
CONTAINERINFORM   Delete_GRID;

static  myUInt Delete_GRID(SOBDATA* conData)
{   // conData   = address of the SOBDATA of the GRID CONTAINER   being DELETED

    // you MUST do checks on the validity of the input
    // Must protect against a "user" trying to delete an already deleted SOB

    if (fnInvalidSobGeneric(conData))
    {
        swprintf_s(txtBuffer, szTxtBuffer, _TEXT("GRID.Delete given an invalid SOBOBJ\n"));  ReportError(txtBuffer);
        return SOB_NOK;
    }

    if (conData->subType NEQU GRIDId)
    {
        swprintf_s(txtBuffer, szTxtBuffer, _TEXT("GRID.Delete given a non GRID object\n"));  ReportError(txtBuffer);
        SOB.Report(conData);
        return SOB_NOK;
    }

    //   swprintf_s(txtBuffer, szTxtBuffer, _TEXT("GRID.Delete\n"));  ReportError(txtBuffer);

    // ---------------------------------
    // trigger a delete on each child of the container

    SOBDATA* childObject = GRID.FirstChild(conData);
    while (childObject)
    {
        if (childObject->type EQU SOB_TYPE_CONTAINER)  CONTAINER.Delete(childObject);  // this "child" is itself a container
        else sobObj_delete(childObject);  //  delete the child's  SOBOBJ

        childObject = SOB.GetNextChild(conData);  // yes FIRST, since we just delete the original FIRST
    }

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

    SOBDATA* pDate = conData->ppSobData;  // get the PARENT of this contaner

    sobObj_delete(conData);  //  delete the container's  SOBOBJ

    CONTAINER.Update(pDate); // trigger an update of the PARENT of the container we have deleted

    // ---------------------------------
    return SOB_OK;
}

CONTAINERINFORM   DeleteChildren_GRID;

static  myUInt DeleteChildren_GRID(SOBDATA* conData)
{   // conData   = address of the SOBDATA of the   CONTAINER  whose children are to be DELETED

    return CONTAINER.DeleteChildren(conData);
}

// ---------------------------------
CONTAINERSETVALUE Stretch_GRID;
static myUInt Stretch_GRID(SOBDATA* conData, myUInt value)
{   // conData   = address of the SOBDATA of the GRID CONTAINER

    // you MUST do checks on the validity of the input
    // Must protect against a "user" trying to do an action on  deleted SOB

    if (fnInvalidSobGeneric(conData))
    {
        swprintf_s(txtBuffer, szTxtBuffer, _TEXT("GRID.Stretch given an invalid SOBOBJ\n"));  ReportError(txtBuffer);
        return SOB_NOK;
    }

    if (conData->subType NEQU GRIDId)
    {
        swprintf_s(txtBuffer, szTxtBuffer, _TEXT("GRID.Stretch given a non GRID object\n"));  ReportError(txtBuffer);
        SOB.Report(conData);
        return SOB_NOK;
    }
    return sob_set_stretch(conData, value);
}

CONTAINERGETSOBDATA FirstChild_GRID;
static  SOBDATA* FirstChild_GRID(SOBDATA* conData)
{
    if (fnInvalidSobGeneric(conData))
    {
        swprintf_s(txtBuffer, szTxtBuffer, _TEXT("GRID.FirstChild given an invalid SOBOBJ\n"));  ReportError(txtBuffer);
        return NULL;
    }

    if (conData->subType NEQU GRIDId)
    {
        swprintf_s(txtBuffer, szTxtBuffer, _TEXT("GRID.FirstChild given a non MANAGER object\n"));  ReportError(txtBuffer);
        SOB.Report(conData);
        return NULL;
    }

    return  conData->headLink;
}

// ---------------------------------
CONTAINERCREATE2D Create_GRID;

static  PVTBL2D * GRID_get_container_vtbl()
{
    static PVTBL2D conTable;
    static int  conTableFlag = 0;

    if (!conTableFlag)
    {
        // only need to initialise this table once since it is common to all containers of type   GRID

        conTable.AddChild = UpDateParent_GRID;
        conTable.Update = Update_GRID;
        conTable.Delete = Delete_GRID;
        conTable.Display = Display_GRID;
        conTable.TypeText = GetTypeText_GRID;
        conTable.Stretch = Stretch_GRID;
        conTable.UpDateChildren = UpDateChildren_GRID;
        conTable.ReSize = ReSize_GRID;
        conTable.Open = Open_GRID;

        conTable.Create = Create_GRID;
        conTable.LastWidget = LastWidget_GRID;

        conTable.FirstChild = FirstChild_GRID;
        conTable.DeleteChildren = DeleteChildren_GRID;

        conTableFlag = 1;
    }
    return &conTable;
}

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

static SOBDATA* internalCreate_GRID(myUInt ColOrRow, myUInt dimension)
{
    if (!dimension) return NULL;
    if (!GRIDId)   GRIDId = int_next_contype();

    int height = 5;
    int width = 5;

    SOBDATA* sData = sob_new_new(SOB_TYPE_CONTAINER, GRIDId, width, height);
    if (sData EQU NULL) return NULL;

    // ---------------------------------
    //  special properties that are GRID dependent   and   need to be initialised in the instances data

    // sData->fncCustomGetTypeText = GRID_SOB_GetTypeText;
    sData->ColOrRow = (short) ColOrRow;
    sData->dimension = (short) dimension;

    // sData->fncCustomDelete;    
    // sData->fncCustomGetTypeText;
    sData->fncCustomGetSubTypeText = GRID_SOB_GetTypeText;
    // sData->fncCustomShow;

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

    sData->pVtbl = (MNGR_METHOD_TABLE_GENERIC*) GRID_get_container_vtbl();
    // sData->cVtbl = get_container_ctbl_generic();

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

    DWORD objStyle = (WS_CHILD | WS_VISIBLE | WS_BORDER) BAND ~(WS_HSCROLL | WS_VSCROLL);

    (void)sob_CreateWindowExNew(sData, 0, szWindowClass, _TEXT("  "), objStyle);

    WINDOWINFO myWI = {0};
    myWI.cbSize = sizeof(WINDOWINFO);
    GetWindowInfo(sData->hWnd, &myWI);
    sData->right = sData->bottom = 0;

    sData->left = (short int)abs(myWI.rcClient.left - myWI.rcWindow.left);
    sData->top = (short int)abs(myWI.rcWindow.top - myWI.rcClient.top);

    if (sData->left) sData->right = 1 * sData->left;
    if (sData->top) sData->bottom = 1 * sData->top;

    GRIDLastCreated = sData;
    return sData;
}

static  SOBDATA* Create_GRID( myUInt ColOrRow , myUInt dimension)
{
    return internalCreate_GRID(ColOrRow , dimension);
}
static  SOBDATA* Create_ROWTILE()
{
    return internalCreate_GRID(0, 1);
}
static  SOBDATA* Create_COLUMNTILE()
{
    return internalCreate_GRID(1, 1);
}
// ---------------------------------

PVTBL2D  GRID =
{
    UpDateParent_GRID
    , Update_GRID
    , Delete_GRID
    , Display_GRID
    , GetTypeText_GRID
    , Stretch_GRID

    , ReSize_GRID
    , UpDateChildren_GRID
    , Open_GRID
    , LastWidget_GRID

    , FirstChild_GRID

    , DeleteChildren_GRID

    , Create_GRID
};

PVTBL0T ROWTILE =
{
    UpDateParent_GRID
    , Update_GRID
    , Delete_GRID
    , Display_GRID
    , GetTypeText_GRID
    , Stretch_GRID

    , ReSize_GRID
    , UpDateChildren_GRID
    , Open_GRID
    , LastWidget_GRID

    , FirstChild_GRID

    , DeleteChildren_GRID

    , Create_ROWTILE
};

PVTBL0T COLUMNTILE =
{
    UpDateParent_GRID
    , Update_GRID
    , Delete_GRID
    , Display_GRID
    , GetTypeText_GRID
    , Stretch_GRID

    , ReSize_GRID
    , UpDateChildren_GRID
    , Open_GRID
    , LastWidget_GRID

    , FirstChild_GRID

    , DeleteChildren_GRID

    , Create_COLUMNTILE
};

#endif
