#include <precomp.h>

static const TCHAR szMainWndClass[] = TEXT("ImageSoftWndClass");

#define ID_MDI_FIRSTCHILD   50000
#define ID_MDI_WINDOWMENU   5
#define NUM_FLT_WND         3

/* toolbar buttons */
TBBUTTON StdButtons[] = {
/*   iBitmap,         idCommand,   fsState,         fsStyle,     bReserved[2], dwData, iString */
    {TBICON_NEW,      ID_NEW,      TBSTATE_ENABLED, BTNS_BUTTON, {0}, 0, 0},    /* new */
    {TBICON_OPEN,     ID_OPEN,     TBSTATE_ENABLED, BTNS_BUTTON, {0}, 0, 0},    /* open */
    {TBICON_SAVE,     ID_SAVE,     TBSTATE_ENABLED, BTNS_BUTTON, {0}, 0, 0},    /* save */

    {10, 0, TBSTATE_ENABLED, BTNS_SEP, {0}, 0, 0},                              /* separator */

    {TBICON_PRINT,    ID_PRINTPRE, TBSTATE_ENABLED, BTNS_BUTTON, {0}, 0, 0 },   /* print */
    {TBICON_PRINTPRE, ID_PRINT,    TBSTATE_ENABLED, BTNS_BUTTON, {0}, 0, 0 },   /* print preview */

    {10, 0, TBSTATE_ENABLED, BTNS_SEP, {0}, 0, 0},                              /* separator */

    {TBICON_CUT,      ID_CUT,      TBSTATE_ENABLED, BTNS_BUTTON, {0}, 0, 0 },   /* cut */
    {TBICON_COPY,     ID_COPY,     TBSTATE_ENABLED, BTNS_BUTTON, {0}, 0, 0 },   /* copy */
    {TBICON_PASTE,    ID_PASTE,    TBSTATE_ENABLED, BTNS_BUTTON, {0}, 0, 0 },   /* paste */

    {10, 0, TBSTATE_ENABLED, BTNS_SEP, {0}, 0, 0},                              /* separator */

    {TBICON_UNDO,     ID_UNDO,    TBSTATE_ENABLED, BTNS_BUTTON,  {0}, 0, 0 },   /* undo */
    {TBICON_REDO,     ID_REDO,    TBSTATE_ENABLED, BTNS_BUTTON,  {0}, 0, 0 },   /* redo */

    {10, 0, TBSTATE_ENABLED, BTNS_SEP, {0}, 0, 0},
};

TBBUTTON TextButtons[] = {
    {10, 0, TBSTATE_ENABLED, BTNS_SEP, {0}, 0, 0},                              /* separator */

    {TBICON_BOLD,     ID_BOLD,     TBSTATE_ENABLED, BTNS_BUTTON | TBSTYLE_CHECK, {0}, 0, 0},    /* bold */
    {TBICON_ITALIC,   ID_ITALIC,   TBSTATE_ENABLED, BTNS_BUTTON | TBSTYLE_CHECK, {0}, 0, 0},    /* italic */
    {TBICON_ULINE,    ID_ULINE,    TBSTATE_ENABLED, BTNS_BUTTON | TBSTYLE_CHECK, {0}, 0, 0},    /* underline */

    {10, 0, TBSTATE_ENABLED, BTNS_SEP, {0}, 0, 0},                              /* separator */

    {TBICON_TXTLEFT,  ID_TXTLEFT,  TBSTATE_ENABLED | TBSTATE_CHECKED, BTNS_BUTTON | TBSTYLE_GROUP | TBSTYLE_CHECK, {0}, 0, 0 },   /* left justified */
    {TBICON_TXTCENTER,ID_TXTCENTER,TBSTATE_ENABLED, BTNS_BUTTON | TBSTYLE_GROUP | TBSTYLE_CHECK, {0}, 0, 0 },   /* centered */
    {TBICON_TXTRIGHT, ID_TXTRIGHT, TBSTATE_ENABLED, BTNS_BUTTON | TBSTYLE_GROUP | TBSTYLE_CHECK, {0}, 0, 0 },   /* right justified */

    {10, 0, TBSTATE_ENABLED, BTNS_SEP, {0}, 0, 0},                              /* separator */
};



/* menu hints */
static const MENU_HINT MainMenuHintTable[] = {
    /* File Menu */
    {ID_BLANK,     IDS_HINT_BLANK},
    {ID_NEW,       IDS_HINT_NEW},
    {ID_OPEN,      IDS_HINT_OPEN},
    {ID_CLOSE,     IDS_HINT_CLOSE},
    {ID_CLOSEALL,  IDS_HINT_CLOSEALL},
    {ID_SAVE,      IDS_HINT_SAVE},
    {ID_SAVEAS,    IDS_HINT_SAVEAS},
    {ID_PRINTPRE,  IDS_HINT_PRINTPRE},
    {ID_PRINT,     IDS_HINT_PRINT},
    {ID_PROP,      IDS_HINT_PROP},
    {ID_EXIT,      IDS_HINT_EXIT},

    /* view menu */
    {ID_TOOLS,     IDS_HINT_TOOLS},
    {ID_COLOR,     IDS_HINT_COLORS},
    {ID_HISTORY,   IDS_HINT_HISTORY},
    {ID_STATUSBAR, IDS_HINT_STATUS},

    /* Window Menu */
    {ID_WINDOW_NEXT,      IDS_HINT_NEXT},
    {ID_WINDOW_CASCADE,   IDS_HINT_CASCADE},
    {ID_WINDOW_TILE_HORZ, IDS_HINT_TILE_HORZ},
    {ID_WINDOW_TILE_VERT, IDS_HINT_TILE_VERT},
    {ID_WINDOW_ARRANGE,   IDS_HINT_ARRANGE}
};

static const MENU_HINT SystemMenuHintTable[] = {
    {SC_RESTORE,    IDS_HINT_SYS_RESTORE},
    {SC_MOVE,       IDS_HINT_SYS_MOVE},
    {SC_SIZE,       IDS_HINT_SYS_SIZE},
    {SC_MINIMIZE,   IDS_HINT_SYS_MINIMIZE},
    {SC_MAXIMIZE,   IDS_HINT_SYS_MAXIMIZE},
    {SC_CLOSE,      IDS_HINT_CLOSE},
    {SC_NEXTWINDOW, IDS_HINT_NEXT},
};


/*  Toolbars */
#define ID_TOOLBAR_STANDARD 0
#define ID_TOOLBAR_TEXT     1
static const TCHAR szToolbarStandard[] = TEXT("STANDARD");
static const TCHAR szToolbarText[]     = TEXT("TEXT");


/* Test Toolbar */
#define ID_TOOLBAR_TEST 5
static const TCHAR szToolbarTest[] = TEXT("TEST");

/* Toolbars table */
static const DOCKBAR MainDockBars[] = {
    {ID_TOOLBAR_STANDARD, szToolbarStandard, IDS_TOOLBAR_STANDARD, TOP_DOCK},
    {ID_TOOLBAR_TEST, szToolbarTest, IDS_TOOLBAR_TEST, TOP_DOCK},
    {ID_TOOLBAR_TEXT, szToolbarText, IDS_TOOLBAR_TEXT, TOP_DOCK},
};


static BOOL CALLBACK
MainWndCreateToolbarClient(struct _TOOLBAR_DOCKS *TbDocks,
                           const DOCKBAR *Dockbar,
                           PVOID Context,
                           HWND hParent,
                           HWND *hwnd)
{
    const TBBUTTON *Buttons = NULL;
    UINT NumButtons = 0;
    UINT StartImageRes = 0;
    HWND hWndClient = NULL;

    UNREFERENCED_PARAMETER(Context);

    switch (Dockbar->BarId)
    {
        case ID_TOOLBAR_STANDARD:
        {
            Buttons = StdButtons;
            NumButtons = sizeof(StdButtons) / sizeof(StdButtons[0]);
            StartImageRes = IDB_MAINNEWICON;
            break;
        }

        case ID_TOOLBAR_TEXT:
        {
            Buttons = TextButtons;
            NumButtons = sizeof(TextButtons) / sizeof(TextButtons[0]);
            StartImageRes = IDB_TEXTBOLD;
            break;
        }

        case ID_TOOLBAR_TEST:
        {/*
            hWndClient = CreateWindowEx(WS_EX_TOOLWINDOW,
                                        TEXT("BUTTON"),
                                        TEXT("Test Button"),
                                        WS_CHILD | WS_VISIBLE,
                                        0,
                                        0,
                                        150,
                                        25,
                                        hParent,
                                        NULL,
                                        hInstance,
                                        NULL);*/
            break;
        }
    }

    if (Buttons != NULL)
    {
        hWndClient = CreateWindowEx(0,
                                    TOOLBARCLASSNAME,
                                    NULL,
                                    WS_CHILD | WS_CLIPSIBLINGS |
                                        CCS_NOPARENTALIGN | CCS_NOMOVEY | CCS_NORESIZE | CCS_NODIVIDER |
                                        TBSTYLE_FLAT | TBSTYLE_TOOLTIPS,
                                    0,
                                    0,
                                    0,
                                    0,
                                    hParent,
                                    NULL,
                                    hInstance,
                                    NULL);
        if (hWndClient != NULL)
        {
            HIMAGELIST hImageList;

            SendMessage(hWndClient,
                        TB_SETEXTENDEDSTYLE,
                        0,
                        TBSTYLE_EX_HIDECLIPPEDBUTTONS);

            SendMessage(hWndClient,
                        TB_BUTTONSTRUCTSIZE,
                        sizeof(Buttons[0]),
                        0);

            SendMessage(hWndClient,
                        TB_SETBITMAPSIZE,
                        0,
                        (LPARAM)MAKELONG(TB_BMP_WIDTH, TB_BMP_HEIGHT));

            hImageList = InitImageList(NumButtons,
                                       StartImageRes);

            ImageList_Destroy((HIMAGELIST)SendMessage(hWndClient,
                                                      TB_SETIMAGELIST,
                                                      0,
                                                      (LPARAM)hImageList));

            SendMessage(hWndClient,
                        TB_ADDBUTTONS,
                        NumButtons,
                        (LPARAM)Buttons);

        }
    }

    switch (Dockbar->BarId)
    {
        case ID_TOOLBAR_TEXT:
        {
            HWND hFontType;
            HWND hFontSize;

            /* font selection combo */
            hFontType = CreateWindowEx(0,
                                       WC_COMBOBOX,
                                       NULL,
                                       WS_CHILD | WS_VISIBLE | WS_VSCROLL |
                                        CBS_DROPDOWN | CBS_SORT | CBS_HASSTRINGS, //| CBS_OWNERDRAWFIXED,
                                       0, 0, 120, 0,
                                       hParent,
                                       NULL,
                                       hInstance,
                                       NULL);

            if (hFontType != NULL)
            {
                MakeFlatCombo(hFontType);

                SetParent(hFontType,
                          hWndClient);

                if (!ToolbarInsertSpaceForControl(hWndClient,
                                                  hFontType,
                                                  0,
                                                  ID_TXTFONTNAME,
                                                  TRUE))
                {
                    DestroyWindow(hFontType);
                }

                    /* Create the list of fonts */
                    FillFontStyleComboList(hFontType);
            }

            /* font size combo */
            hFontSize = CreateWindowEx(0,
                                       WC_COMBOBOX,
                                       NULL,
                                       WS_CHILD | WS_VISIBLE | CBS_DROPDOWN,
                                       0, 0, 50, 0,
                                       hParent,
                                       NULL,
                                       hInstance,
                                       NULL);
            if (hFontSize != NULL)
            {
                MakeFlatCombo(hFontSize);

                SetParent(hFontSize,
                          hWndClient);

                if (!ToolbarInsertSpaceForControl(hWndClient,
                                                  hFontSize,
                                                  1,
                                                  ID_TXTFONTSIZE,
                                                  TRUE))
                {
                    DestroyWindow(hFontSize);
                }

                /* Update the font-size-list */
                FillFontSizeComboList(hFontSize);
            }
            break;
        }
    }

    if (hWndClient != NULL)
    {
        *hwnd = hWndClient;
        return TRUE;
    }

    return FALSE;
}

static BOOL CALLBACK
MainWndDestroyToolbarClient(struct _TOOLBAR_DOCKS *TbDocks,
                            const DOCKBAR *Dockbar,
                            PVOID Context,
                            HWND hwnd)
{
    UNREFERENCED_PARAMETER(TbDocks);
    UNREFERENCED_PARAMETER(Dockbar);
    UNREFERENCED_PARAMETER(Context);

    DestroyWindow(hwnd);
    return TRUE;
}

static BOOL CALLBACK
MainWndToolbarInsertBand(struct _TOOLBAR_DOCKS *TbDocks,
                         const DOCKBAR *Dockbar,
                         PVOID Context,
                         UINT *Index,
                         LPREBARBANDINFO rbi)
{
    switch (rbi->wID)
    {
        case ID_TOOLBAR_TEXT:
        case ID_TOOLBAR_STANDARD:
        {
            SIZE Size;

            if (SendMessage(rbi->hwndChild,
                            TB_GETMAXSIZE,
                            0,
                            (LPARAM)&Size))
            {
                rbi->fStyle |= RBBS_USECHEVRON | RBBS_HIDETITLE;
                rbi->fMask |= RBBIM_SIZE | RBBIM_CHILDSIZE | RBBIM_IDEALSIZE;
                rbi->cx = rbi->cxIdeal = Size.cx;
                rbi->cxMinChild = 0;
                rbi->cyMinChild = Size.cy;
            }
            break;
        }

        case ID_TOOLBAR_TEST:
        {
            RECT rcBtn;

            if (GetWindowRect(rbi->hwndChild,
                              &rcBtn))
            {
                rbi->fStyle |= RBBS_HIDETITLE;
                rbi->fMask |= RBBIM_SIZE | RBBIM_CHILDSIZE;
                rbi->cx = rcBtn.right - rcBtn.left;
                rbi->cxMinChild = 0;
                rbi->cyMinChild = rcBtn.bottom - rcBtn.top;
            }
            break;
        }
    }
    return TRUE;
}

static VOID
TbCustomControlChange(HWND hWndToolbar,
                      HWND hWndControl,
                      BOOL Vert)
{
    /* the toolbar changed from horizontal to vertical or vice versa... */
    return;
}

static VOID CALLBACK
MainWndToolbarDockBand(struct _TOOLBAR_DOCKS *TbDocks,
                       const DOCKBAR *Dockbar,
                       PVOID Context,
                       DOCK_POSITION DockFrom,
                       DOCK_POSITION DockTo,
                       LPREBARBANDINFO rbi)
{
    if (rbi->fMask & RBBIM_CHILD && rbi->hwndChild != NULL)
    {
        switch (rbi->wID)
        {
            case ID_TOOLBAR_TEXT:
            case ID_TOOLBAR_STANDARD:
            {
                SIZE Size;
                BOOL Vert;
                DWORD dwStyle = (DWORD)SendMessage(rbi->hwndChild,
                                                   TB_GETSTYLE,
                                                   0,
                                                   0);
                switch (DockTo)
                {
                    case LEFT_DOCK:
                    case RIGHT_DOCK:
                        dwStyle |= CCS_VERT | TBSTYLE_WRAPABLE;
                        Vert = TRUE;
                        break;

                    default:
                        dwStyle &= ~(CCS_VERT | TBSTYLE_WRAPABLE);
                        Vert = FALSE;
                        break;
                }

                SendMessage(rbi->hwndChild,
                            TB_SETSTYLE,
                            0,
                            (LPARAM)dwStyle);

                ToolbarUpdateControlSpaces(rbi->hwndChild,
                                           TbCustomControlChange);

                if (SendMessage(rbi->hwndChild,
                                TB_GETMAXSIZE,
                                0,
                                (LPARAM)&Size))
                {
                    rbi->fMask |= RBBIM_SIZE | RBBIM_CHILDSIZE | RBBIM_IDEALSIZE;
                    rbi->cx = rbi->cxIdeal = (Vert ? Size.cy : Size.cx);
                    rbi->cxMinChild = 0;
                    rbi->cyMinChild = (Vert ? Size.cx : Size.cy);
                }
                break;
            }

            case ID_TOOLBAR_TEST:
            {
                if (DockTo == NO_DOCK)
                {
                    rbi->fMask |= RBBIM_SIZE | RBBIM_CHILDSIZE | RBBIM_IDEALSIZE;
                    rbi->cx = rbi->cxIdeal = 150;
                    rbi->cxMinChild = 0;
                    rbi->cyMinChild = 40;
                }
                break;
            }
        }
    }
}

static VOID CALLBACK
MainWndToolbarChevronPushed(struct _TOOLBAR_DOCKS *TbDocks,
                            const DOCKBAR *Dockbar,
                            PVOID Context,
                            HWND hwndChild,
                            LPNMREBARCHEVRON lpnm)
{
    switch (lpnm->wID)
    {
        case ID_TOOLBAR_STANDARD:
        {
            MapWindowPoints(lpnm->hdr.hwndFrom,
                            HWND_DESKTOP,
                            (LPPOINT)&lpnm->rc,
                            2);
            /* Create a popup menu for all toolbar icons hidden */
            break;
        }
    }
}

static VOID
MainWndMoveFloatingWindows(PMAIN_WND_INFO Info,
                           PRECT wndOldPos)
{
    RECT wndNewPos, TbRect;
    INT i, xMoved, yMoved;
    PFLT_WND WndArr[NUM_FLT_WND];

    if (GetWindowRect(Info->hSelf,
                      &wndNewPos))
    {

        xMoved = wndNewPos.left - wndOldPos->left;
        yMoved = wndNewPos.top - wndOldPos->top;

        /* store the pointers in an array */
        WndArr[0] = Info->fltTools;
        WndArr[1] = Info->fltColors;
        WndArr[2] = Info->fltHistory;

        for (i = 0; i < NUM_FLT_WND; i++)
        {
            GetWindowRect(WndArr[i]->hSelf,
                          &TbRect);

            WndArr[i]->x = TbRect.left + xMoved;
            WndArr[i]->y = TbRect.top + yMoved;

            MoveWindow(WndArr[i]->hSelf,
                       WndArr[i]->x,
                       WndArr[i]->y,
                       WndArr[i]->Width,
                       WndArr[i]->Height,
                       TRUE);
        }

        CopyMemory(wndOldPos,
                   &wndNewPos,
                   sizeof(RECT));
    }
}


static VOID
MainWndResetFloatingWindows(PMAIN_WND_INFO Info)
{
    RECT rect;

    if (GetWindowRect(Info->hMdiClient,
                      &rect))
    {

        /* tools datum */
        MoveWindow(Info->fltTools->hSelf,
                   rect.left + 5,
                   rect.top + 5,
                   Info->fltTools->Width,
                   Info->fltTools->Height,
                   TRUE);

        /* colors datum */
        MoveWindow(Info->fltColors->hSelf,
                   rect.left + 5,
                   rect.bottom - Info->fltColors->Height - 5,
                   Info->fltColors->Width,
                   Info->fltColors->Height,
                   TRUE);

        /* history datum */
        MoveWindow(Info->fltHistory->hSelf,
                   rect.right - Info->fltHistory->Width - 5,
                   rect.top + 5,
                   Info->fltHistory->Width,
                   Info->fltHistory->Height,
                   TRUE);
    }
}

static VOID
MainWndCreateFloatWindows(PMAIN_WND_INFO Info)
{
    RECT rect;
    HMENU hMenu;
    UINT Res;
    PFLT_WND WndArr[NUM_FLT_WND]; /* temp array for looping */
    INT i;

    Info->fltTools = HeapAlloc(ProcessHeap,
                               HEAP_ZERO_MEMORY,
                               sizeof(FLT_WND));
    Info->fltColors = HeapAlloc(ProcessHeap,
                                HEAP_ZERO_MEMORY,
                                sizeof(FLT_WND));
    Info->fltHistory = HeapAlloc(ProcessHeap,
                                 HEAP_ZERO_MEMORY,
                                 sizeof(FLT_WND));

    /* set window dimensions */
    Info->fltTools->Width    = 53;
    Info->fltTools->Height   = 300;
    Info->fltColors->Width   = 200;
    Info->fltColors->Height  = 200;
    Info->fltHistory->Width  = 150;
    Info->fltHistory->Height = 150;

    if (! GetWindowRect(Info->hMdiClient,
                        &rect))
    {
        return;
    }

    /* Set window datums */
    Info->fltTools->x = rect.left + 5;
    Info->fltTools->y = rect.top + 5;

    Info->fltColors->x = rect.left + 5;
    Info->fltColors->y = rect.bottom - Info->fltColors->Height - 5;

    Info->fltHistory->x = rect.right - Info->fltHistory->Width - 5;
    Info->fltHistory->y = rect.top + 5;

    /* save pointers into array incrementing within the loop*/
    WndArr[0] = Info->fltTools;
    WndArr[1] = Info->fltColors;
    WndArr[2] = Info->fltHistory;

    for (i = 0, Res = IDS_FLT_TOOLS; Res < IDS_FLT_TOOLS + NUM_FLT_WND; Res++, i++)
    {
        if (! AllocAndLoadString(&WndArr[i]->lpName,
                                 hInstance,
                                 Res))
        {
            WndArr[i]->lpName = NULL;
        }

        WndArr[i]->hSelf = CreateWindowEx(WS_EX_TOOLWINDOW,
                                          TEXT("ImageSoftFloatWndClass"),
                                          WndArr[i]->lpName,
                                          WS_POPUPWINDOW | WS_DLGFRAME | WS_VISIBLE,
                                          WndArr[i]->x,
                                          WndArr[i]->y,
                                          WndArr[i]->Width,
                                          WndArr[i]->Height,
                                          Info->hSelf,
                                          NULL,
                                          hInstance,
                                          WndArr[i]);
        ShowWindow(WndArr[i]->hSelf, SW_HIDE);
    }

    hMenu = GetMenu(Info->hSelf);

    if (Info->fltTools->hSelf != NULL)
    {
        if (FloatToolbarCreateToolsGui(Info))
        {
            CheckMenuItem(hMenu,
                          ID_TOOLS,
                          MF_CHECKED);

            ShowHideWindow(Info->fltTools->hSelf);
        }
    }

    if (Info->fltColors->hSelf != NULL)
    {
        if (FloatToolbarCreateColorsGui(Info))
        {

        }
    }

    if (Info->fltHistory->hSelf != NULL)
    {
        if (FloatToolbarCreateHistoryGui(Info))
        {

        }
    }

}

static VOID
MainWndDestroyFloatWindows(PMAIN_WND_INFO Info)
{
    if (Info->fltTools != NULL)
        HeapFree(ProcessHeap, 0, Info->fltTools);

    if (Info->fltColors != NULL)
        HeapFree(ProcessHeap, 0, Info->fltColors);

    if (Info->fltHistory != NULL)
        HeapFree(ProcessHeap, 0, Info->fltHistory);
}



static const DOCKBAR_ITEM_CALLBACKS MainWndDockBarCallbacks = {
    MainWndCreateToolbarClient,
    MainWndDestroyToolbarClient,
    MainWndToolbarInsertBand,
    MainWndToolbarDockBand,
    MainWndToolbarChevronPushed,
};

static VOID
CreateToolbars(PMAIN_WND_INFO Info)
{
    UINT i;

    for (i = 0; i < sizeof(MainDockBars) / sizeof(MainDockBars[0]); i++)
    {
        /* FIXME - lookup whether to display the toolbar */
        TbdAddToolbar(&Info->ToolDocks,
                      &MainDockBars[i],
                      Info,
                      &MainWndDockBarCallbacks);
    }

    MainWndCreateFloatWindows(Info);
}

static VOID CALLBACK
MainWndResize(PVOID Context,
              WORD cx,
              WORD cy)
{
    RECT rcClient = {0};
    RECT rcStatus = {0};
    HDWP dwp;
    INT DocksVisible;
    PMAIN_WND_INFO Info = (PMAIN_WND_INFO)Context;

    /* Calculate the MDI client rectangle */
    rcClient.right = cx;
    rcClient.bottom = cy;

    if (Info->hStatus != NULL)
    {
        GetWindowRect(Info->hStatus,
                      &rcStatus);
        rcClient.bottom -= (rcStatus.bottom - rcStatus.top);
    }

    /* Adjust the client rect if docked toolbars are visible */
    DocksVisible = TbdAdjustUpdateClientRect(&Info->ToolDocks,
                                             &rcClient);

    dwp = BeginDeferWindowPos(2 + DocksVisible);
    if (dwp != NULL)
    {
        /* Update the toolbar docks */
        if (DocksVisible != 0)
        {
            dwp = TbdDeferDocks(dwp,
                                &Info->ToolDocks);
            if (dwp == NULL)
                return;
        }

        /* Update the MDI client */
        if (Info->hMdiClient != NULL)
        {
            dwp = DeferWindowPos(dwp,
                                 Info->hMdiClient,
                                 NULL,
                                 rcClient.left,
                                 rcClient.top,
                                 rcClient.right - rcClient.left,
                                 rcClient.bottom - rcClient.top,
                                 SWP_NOZORDER);
            if (dwp == NULL)
                return;
        }

        /* Update the status bar */
        if (Info->hStatus != NULL)
        {
            dwp = DeferWindowPos(dwp,
                                 Info->hStatus,
                                 NULL,
                                 0,
                                 cy - (rcStatus.bottom - rcStatus.top),
                                 cx,
                                 rcStatus.bottom - rcStatus.top,
                                 SWP_NOZORDER);
            if (dwp == NULL)
                return;
        }

        EndDeferWindowPos(dwp);
    }
}

static VOID
InitMainWnd(PMAIN_WND_INFO Info)
{
    CLIENTCREATESTRUCT ccs;
    INT statwidths[] = {110, -1};

    /* FIXME - create controls and initialize the application */

    /* create the status bar */
    Info->hStatus = CreateWindowEx(0,
                                   STATUSCLASSNAME,
                                   NULL,
                                   WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS | CCS_NOPARENTALIGN | SBARS_SIZEGRIP,
                                   0,
                                   0,
                                   0,
                                   0,
                                   Info->hSelf,
                                   (HMENU)IDC_STATUSBAR,
                                   hInstance,
                                   NULL);

    if (Info->hStatus != NULL)
        SendMessage(Info->hStatus,
                    SB_SETPARTS,
                    sizeof(statwidths)/sizeof(int),
                    (LPARAM)statwidths);

    /* create the MDI client window */
    ccs.hWindowMenu = GetSubMenu(GetMenu(Info->hSelf),
                                 ID_MDI_WINDOWMENU);
    ccs.idFirstChild = ID_MDI_FIRSTCHILD;
    Info->hMdiClient = CreateWindowEx(WS_EX_ACCEPTFILES | WS_EX_CLIENTEDGE,
                                      TEXT("MDICLIENT"),
                                      NULL,
                                      WS_VISIBLE | WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_VSCROLL | WS_HSCROLL,
                                      0,
                                      0,
                                      0,
                                      0,
                                      Info->hSelf,
                                      NULL,
                                      hInstance,
                                      &ccs);

    TbdInitializeDocks(&Info->ToolDocks,
                       Info->hSelf,
                       Info,
                       MainWndResize);

    CreateToolbars(Info);

    /* initialize file open/save structure */
    FileInitialize(Info->hSelf);
}

static VOID
MainWndCommand(PMAIN_WND_INFO Info,
               WORD CmdId,
               HWND hControl)
{
    static TCHAR szFileName[MAX_PATH];
    static TCHAR szImageName[MAX_PATH];

    UNREFERENCED_PARAMETER(hControl);

    switch (CmdId)
    {
        case ID_NEW:
        {
            MessageBox(NULL, _T("Not yet implemented"), NULL, 0);
        }
        break;

        case ID_OPEN:
        {
            OPEN_IMAGE_EDIT_INFO OpenInfo;

            if (DoOpenFile(Info->hSelf,
                           szFileName,   /* full file path */
                           szImageName)) /* file name */
            {
                OpenInfo.CreateNew = FALSE;

                OpenInfo.Open.lpImagePath = szFileName;
                OpenInfo.lpImageName = szImageName;

                CreateImageEditWindow(Info,
                                      &OpenInfo);

                /* FIXME: move flt wnd's if scroll bars show
                MainWndResetFloatingWindows(Info->hMdiClient); */
            }

        }
        break;

        case ID_TOOLS:
        {
            HMENU hMenu = GetMenu(Info->hSelf);

            if (hMenu != NULL)
            {
                UINT uCheck = MF_CHECKED;

                if (ShowHideWindow(Info->fltTools->hSelf))
                    uCheck = MF_UNCHECKED;

                CheckMenuItem(hMenu,
                              ID_TOOLS,
                              uCheck);
            }
        }
        break;

        case ID_COLOR:
        {
            HMENU hMenu = GetMenu(Info->hSelf);

            if (hMenu != NULL)
            {
                UINT uCheck = MF_CHECKED;

                if (ShowHideWindow(Info->fltColors->hSelf))
                    uCheck = MF_UNCHECKED;

                CheckMenuItem(hMenu,
                              ID_COLOR,
                              uCheck);
            }
        }
        break;

        case ID_HISTORY:
        {
            HMENU hMenu = GetMenu(Info->hSelf);

            if (hMenu != NULL)
            {
                UINT uCheck = MF_CHECKED;

                if (ShowHideWindow(Info->fltHistory->hSelf))
                    uCheck = MF_UNCHECKED;

                CheckMenuItem(hMenu,
                              ID_HISTORY,
                              uCheck);
            }
        }
        break;

        case ID_BRIGHTNESS:
            DialogBoxParam(hInstance,
                           MAKEINTRESOURCE(IDD_BRIGHTNESS),
                           Info->hSelf,
                           BrightnessProc,
                           (LPARAM)Info);
        break;

        case ID_CONTRAST:
            DialogBoxParam(hInstance,
                           MAKEINTRESOURCE(IDD_BRIGHTNESS),
                           Info->hSelf,
                           ContrastProc,
                           (LPARAM)Info);
        break;

        case ID_BLACKANDWHITE:
        {
            if (Info->ImageEditors)
            {
                DisplayBlackAndWhite(Info->ImageEditors->hSelf,
                                     Info->ImageEditors->hDCMem,
                                     Info->ImageEditors->hBitmap);
            }
        }
        break;

        case ID_INVERTCOLORS:
        {
            if (Info->ImageEditors)
            {
                DisplayInvertedColors(Info->ImageEditors->hSelf,
                                      Info->ImageEditors->hDCMem,
                                      Info->ImageEditors->hBitmap);
            }
        }
        break;

        case ID_BLUR:
        {
            if (Info->ImageEditors)
            {
                DisplayBlur(Info->ImageEditors->hSelf,
                            Info->ImageEditors->hDCMem,
                            Info->ImageEditors->hBitmap);
            }
        }
        break;

        case ID_SHARPEN:
        {
            if (Info->ImageEditors)
            {
                DisplaySharpness(Info->ImageEditors->hSelf,
                                 Info->ImageEditors->hDCMem,
                                 Info->ImageEditors->hBitmap);
            }
        }
        break;

        case ID_EXIT:
            SendMessage(Info->hSelf,
                        WM_CLOSE,
                        0,
                        0);
            break;

        /* Window Menu */
        case ID_WINDOW_TILE_HORZ:
            SendMessage(Info->hMdiClient,
                        WM_MDITILE,
                        MDITILE_HORIZONTAL,
                        0);
            break;

        case ID_WINDOW_TILE_VERT:
            SendMessage(Info->hMdiClient,
                        WM_MDITILE,
                        MDITILE_VERTICAL,
                        0);
            break;

        case ID_WINDOW_CASCADE:
            SendMessage(Info->hMdiClient,
                        WM_MDICASCADE,
                        0,
                        0);
            break;

        case ID_WINDOW_ARRANGE:
            SendMessage(Info->hMdiClient,
                        WM_MDIICONARRANGE,
                        0,
                        0);
            break;

        case ID_WINDOW_NEXT:
            SendMessage(Info->hMdiClient,
                        WM_MDINEXT,
                        0,
                        0);
            break;

        /* Help Menu */
        case ID_ABOUT:
            DialogBox(hInstance,
                      MAKEINTRESOURCE(IDD_ABOUTBOX),
                      Info->hSelf,
                      AboutDialogProc);
            break;
    }
}

static VOID
DestroyMainWnd(PMAIN_WND_INFO Info)
{
    /* FIXME - cleanup allocated resources */

    MainWndDestroyFloatWindows(Info);
}


static VOID
UpdateMainStatusBar(PMAIN_WND_INFO Info)
{
    if (Info->hStatus != NULL)
    {
        SendMessage(Info->hStatus,
                    SB_SIMPLE,
                    (WPARAM)Info->InMenuLoop,
                    0);
    }
}

static BOOL
MainWndMenuHint(PMAIN_WND_INFO Info,
                WORD CmdId,
                const MENU_HINT *HintArray,
                DWORD HintsCount,
                UINT DefHintId)
{
    BOOL Found = FALSE;
    const MENU_HINT *LastHint;
    UINT HintId = DefHintId;

    LastHint = HintArray + HintsCount;
    while (HintArray != LastHint)
    {
        if (HintArray->CmdId == CmdId)
        {
            HintId = HintArray->HintId;
            Found = TRUE;
            break;
        }
        HintArray++;
    }

    StatusBarLoadString(Info->hStatus,
                        SB_SIMPLEID,
                        hInstance,
                        HintId);

    return Found;
}

static LRESULT CALLBACK
MainWndProc(HWND hwnd,
            UINT uMsg,
            WPARAM wParam,
            LPARAM lParam)
{
    PMAIN_WND_INFO Info;
    LRESULT Ret = 0;
    static BOOL bLBMouseDown = FALSE;
    static RECT wndOldPos;

    /* Get the window context */
    Info = (PMAIN_WND_INFO)GetWindowLongPtr(hwnd,
                                            GWLP_USERDATA);
    if (Info == NULL && uMsg != WM_CREATE)
    {
        goto HandleDefaultMessage;
    }

    switch (uMsg)
    {
        case WM_SIZE:
        {
            MainWndResize(Info,
                          LOWORD(lParam),
                          HIWORD(lParam));
            /* NOTE - do *not* forward this message to DefFrameProc! Otherwise the MDI client
                      will attempt to resize itself */

                /* reposition the floating toolbars */
                if ((wParam == SIZE_MAXIMIZED) || (wParam == SIZE_RESTORED))
                    MainWndResetFloatingWindows(Info);

            break;
        }

        case WM_NCLBUTTONDOWN:
            bLBMouseDown = TRUE;
            DefWindowProc(hwnd,
                          uMsg,
                          wParam,
                          lParam);
        break;

        case WM_NCLBUTTONUP:

            bLBMouseDown = FALSE;
            DefWindowProc(hwnd,
                          uMsg,
                          wParam,
                          lParam);
        break;

        case WM_MOVE:
        {
            /* if the main window is moved, move the toolbars too */
            if (bLBMouseDown)
                MainWndMoveFloatingWindows(Info, &wndOldPos);
        }
        break;

        case WM_NOTIFY:
        {
            UINT BarId;
            LPNMHDR pnmhdr = (LPNMHDR)lParam;
            if (!TbdHandleNotifications(&Info->ToolDocks,
                                        pnmhdr,
                                        &Ret))
            {
                if (TbdDockBarIdFromClientWindow(&Info->ToolDocks,
                                                 pnmhdr->hwndFrom,
                                                 &BarId))
                {
                    switch (BarId)
                    {
                        case ID_TOOLBAR_TEXT:
                            switch (pnmhdr->code)
                            {
                                case TBN_DELETINGBUTTON:
                                {
                                    LPNMTOOLBAR lpnmtb = (LPNMTOOLBAR)lParam;

                                    ToolbarDeleteControlSpace(pnmhdr->hwndFrom,
                                                              &lpnmtb->tbButton);
                                    break;
                                }
                            }
                            break;
                    }
                }
            }
            break;
        }

        case WM_COMMAND:
        {
            MainWndCommand(Info,
                           LOWORD(wParam),
                           (HWND)lParam);
            goto HandleDefaultMessage;
        }

        case WM_MENUSELECT:
        {
            if (Info->hStatus != NULL)
            {
                if (!MainWndMenuHint(Info,
                                     LOWORD(wParam),
                                     MainMenuHintTable,
                                     sizeof(MainMenuHintTable) / sizeof(MainMenuHintTable[0]),
                                     IDS_HINT_BLANK))
                {
                    MainWndMenuHint(Info,
                                    LOWORD(wParam),
                                    SystemMenuHintTable,
                                    sizeof(SystemMenuHintTable) / sizeof(SystemMenuHintTable[0]),
                                    IDS_HINT_BLANK);
                }
            }
            break;
        }

        case WM_ENTERMENULOOP:
        {
            Info->InMenuLoop = TRUE;
            UpdateMainStatusBar(Info);
            break;
        }

        case WM_EXITMENULOOP:
        {
            Info->InMenuLoop = FALSE;
            UpdateMainStatusBar(Info);
            break;
        }

        case WM_CLOSE:
        {
            DestroyWindow(hwnd);
            break;
        }

        case WM_ENABLE:
        {
            TbdHandleEnabling(&Info->ToolDocks,
                              hwnd,
                              (BOOL)wParam);
            goto HandleDefaultMessage;
        }

        case WM_NCACTIVATE:
        {
            TbdHandleActivation(&Info->ToolDocks,
                                hwnd,
                                &wParam,
                                &lParam);
            goto HandleDefaultMessage;
        }

        case WM_ACTIVATEAPP:
        {
            //TbdShowFloatingToolbars(&Info->ToolDocks,
            //                        (BOOL)wParam);
            goto HandleDefaultMessage;
        }

        case WM_CREATE:
        {
            Info = (PMAIN_WND_INFO)(((LPCREATESTRUCT)lParam)->lpCreateParams);

            /* Initialize the main window context */
            Info->hSelf = hwnd;

            SetWindowLongPtr(hwnd,
                             GWLP_USERDATA,
                             (LONG_PTR)Info);

            InitMainWnd(Info);

            /* Show the window */
            ShowWindow(hwnd,
                       Info->nCmdShow);
            /* get the windows position */
            GetWindowRect(hwnd,
                          &wndOldPos);

            break;
        }

        case WM_DESTROY:
        {
            DestroyMainWnd(Info);

            HeapFree(ProcessHeap,
                     0,
                     Info);
            SetWindowLongPtr(hwnd,
                             GWLP_USERDATA,
                             0);

            /* Break the message queue loop */
            PostQuitMessage(0);
            break;
        }

        default:
        {
HandleDefaultMessage:
            if (Info != NULL && Info->hMdiClient != NULL)
            {
                Ret = DefFrameProc(hwnd,
                                   Info->hMdiClient,
                                   uMsg,
                                   wParam,
                                   lParam);
            }
            else
            {
                Ret = DefWindowProc(hwnd,
                                    uMsg,
                                    wParam,
                                    lParam);
            }
            break;
        }
    }

    return Ret;
}

MDI_EDITOR_TYPE
MainWndGetCurrentEditor(PMAIN_WND_INFO MainWnd,
                        PVOID *Info)
{
    MDI_EDITOR_TYPE EditorType;

    if (MainWnd->ActiveEditor != NULL)
    {
        EditorType = *((PMDI_EDITOR_TYPE)MainWnd->ActiveEditor);
        *Info = MainWnd->ActiveEditor;
    }
    else
    {
        EditorType = metUnknown;
        *Info = NULL;
    }

    return EditorType;
}

VOID
MainWndSwitchEditorContext(PMAIN_WND_INFO Info,
                           HWND hDeactivate,
                           HWND hActivate)
{
    PMDI_EDITOR_TYPE EditorType;

    /* FIXME - optimize light weight switching
               when switching from and to an editor of same type */

    if (hDeactivate != NULL)
    {
        EditorType = (PMDI_EDITOR_TYPE)GetWindowLongPtr(hDeactivate,
                                                        GWLP_USERDATA);
        if (EditorType != NULL)
        {
            switch (*EditorType)
            {
                case metImageEditor:
                    SetImageEditorEnvironment((PEDIT_WND_INFO)EditorType,
                                              FALSE);
                    break;

                default:
                    break;
            }

            Info->ActiveEditor = NULL;
        }
    }

    if (hActivate != NULL)
    {
        EditorType = (PMDI_EDITOR_TYPE)GetWindowLongPtr(hActivate,
                                                        GWLP_USERDATA);
        if (EditorType != NULL)
        {
            Info->ActiveEditor = EditorType;

            switch (*EditorType)
            {
                case metImageEditor:
                    SetImageEditorEnvironment((PEDIT_WND_INFO)EditorType,
                                              TRUE);
                    break;

                default:
                    break;
            }
        }
    }
}

HWND
CreateMainWindow(LPCTSTR lpCaption,
                 int nCmdShow)
{
    PMAIN_WND_INFO Info;
    HWND hMainWnd = NULL;

    Info = HeapAlloc(ProcessHeap,
                     0,
                     sizeof(MAIN_WND_INFO));
    if (Info != NULL)
    {
        ZeroMemory(Info,
                   sizeof(MAIN_WND_INFO));
        Info->nCmdShow = nCmdShow;

        /* FIXME - load the window position from the registry */

        hMainWnd = CreateWindowEx(WS_EX_WINDOWEDGE,
                                  szMainWndClass,
                                  lpCaption,
                                  WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
                                  CW_USEDEFAULT,
                                  CW_USEDEFAULT,
                                  CW_USEDEFAULT,
                                  CW_USEDEFAULT,
                                  NULL,
                                  NULL,
                                  hInstance,
                                  Info);
        if (hMainWnd == NULL)
        {
            HeapFree(ProcessHeap,
                     0,
                     Info);
        }
    }

    return hMainWnd;
}

BOOL
MainWndTranslateMDISysAccel(HWND hwnd,
                            LPMSG lpMsg)
{
    PMAIN_WND_INFO Info;

    /* Get the window context */
    Info = (PMAIN_WND_INFO)GetWindowLongPtr(hwnd,
                                            GWLP_USERDATA);
    if (Info != NULL && Info->hMdiClient != NULL)
    {
        return TranslateMDISysAccel(Info->hMdiClient,
                                    lpMsg);
    }

    return FALSE;
}

BOOL
InitMainWindowImpl(VOID)
{
    WNDCLASSEX wc = {0};

    wc.cbSize = sizeof(WNDCLASSEX);
    wc.lpfnWndProc = MainWndProc;
    wc.hInstance = hInstance;
    wc.hIcon = LoadIcon(hInstance,
                        MAKEINTRESOURCE(IDI_IMAGESOFTICON));
    wc.hCursor = LoadCursor(NULL,
                            IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
    wc.lpszMenuName = MAKEINTRESOURCE(IDR_MAINMENU);
    wc.lpszClassName = szMainWndClass;
    wc.hIconSm = (HICON)LoadImage(hInstance,
                                  MAKEINTRESOURCE(IDI_IMAGESOFTICON),
                                  IMAGE_ICON,
                                  16,
                                  16,
                                  LR_SHARED);

    return RegisterClassEx(&wc) != (ATOM)0;
}

VOID
UninitMainWindowImpl(VOID)
{
    UnregisterClass(szMainWndClass,
                    hInstance);
}
