
/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples. 
*       Copyright (C) 1993-1996 Microsoft Corporation.
*       All rights reserved. 
*       This source code is only intended as a supplement to 
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the 
*       Microsoft samples programs.
\******************************************************************************/

/****************************** Module Header *******************************
* Module Name: toolbox.c
*
* Contains routines that handle the toolbox.
*
* Functions:
*
*    ToolboxShow()
*    ToolboxOnTop()
*    ToolboxWndProc()
*    ToolBtnWndProc()
*    ToolboxSelectTool()
*    ToolboxCreate()
*    ToolboxDrawBitmap()
*
* Comments:
*
****************************************************************************/

#include "dlgedit.h"
#include "dlgfuncs.h"
#include "dlgextrn.h"

#include "dialogs.h"


#define TOOLBOXMARGIN   2       // Pixels around the buttons in the Toolbox.
#define TOOLBOXCOLUMNS  2       // Columns in the Toolbox.

/*
 * Style of the toolbox window.
 */
#define TOOLBOXSTYLE    (WS_POPUP | WS_CLIPSIBLINGS | WS_CAPTION | WS_SYSMENU)

STATICFN VOID ToolboxCreate(VOID);
STATICFN VOID ToolboxDrawBitmap(HDC hDC, INT type);

/*
 * Dimensions of a tool button bitmap.
 */
static INT cxToolBtn;
static INT cyToolBtn;



/****************************************************************************
* ToolboxShow
*
* This function shows or hides the toolbox window.  It will create
* the Toolbox if necessary.
*
* Arguments:
*   BOOL fShow - whether to show or hide the toolbox window.
*
****************************************************************************/

VOID ToolboxShow(
    BOOL fShow)
{
    if (fShow) {
        /*
         * Don't allow a toolbox to be shown in Translate mode.
         */
        if (gfTranslateMode)
            return;

        /*
         * Create it if it doesn't exist yet.
         */
        if (!ghwndToolbox)
            ToolboxCreate();

        if (ghwndToolbox)
            ShowWindow(ghwndToolbox, SW_SHOWNA);
    }
    else {
        if (ghwndToolbox)
            ShowWindow(ghwndToolbox, SW_HIDE);
    }
}



/****************************************************************************
* ToolboxOnTop
*
* This function positions the toolbox window on top.  It needs to be
* called any time that a new dialog window is created to be sure the
* dialog does not cover the toolbox.
*
* It can be called even if the toolbox is not created yet (it will
* be a noop in that case).
*
****************************************************************************/

VOID ToolboxOnTop(VOID)
{
   if (ghwndToolbox) {
       SetWindowPos(ghwndToolbox, NULL, 0, 0, 0, 0,
               SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
   }
}



/****************************************************************************
* ToolboxCreate
*
* This function creates the toolbox window.
*
****************************************************************************/

STATICFN VOID ToolboxCreate(VOID)
{
    BITMAP bmp;
    INT i;
    INT x;
    INT y;
    INT cx;
    INT cy;
    INT cxDummy;
    INT cyDummy;
    RECT rc;
    RECT rcSubClient;
    BOOL fMaximized;

    /*
     * Load the bitmaps.
     */
    if (!(ghbmPointerToolUp = LoadBitmap(ghInst,
            MAKEINTRESOURCE(IDBM_TUPOINTR))) ||
            !(ghbmPointerToolDown = LoadBitmap(ghInst,
            MAKEINTRESOURCE(IDBM_TDPOINTR))))
        return;

    for (i = 0; i < CCONTROLS; i++) {
        if (!(awcd[i].hbmToolBtnUp = LoadBitmap(ghInst,
                MAKEINTRESOURCE(awcd[i].idbmToolBtnUp))))
            return;

        if (!(awcd[i].hbmToolBtnDown = LoadBitmap(ghInst,
                MAKEINTRESOURCE(awcd[i].idbmToolBtnDown))))
            return;
    }

    /*
     * Get the dimensions of the tool button bitmaps.
     */
    GetObject(awcd[0].hbmToolBtnUp, sizeof(BITMAP), &bmp);
    cxToolBtn = bmp.bmWidth;
    cyToolBtn = bmp.bmHeight;

    /*
     * Calculate the required window size for the client area
     * size we want.  The size leaves room for a margin, and
     * assumes that adjacent buttons overlap their borders by
     * one pixel.
     */
    rc.left = 0;
    rc.top = 0;
    rc.right = TOOLBOXMARGIN + ((cxToolBtn - 1) * 2) + 1 + TOOLBOXMARGIN;
    rc.bottom = TOOLBOXMARGIN + ((cyToolBtn - 1) *
            ((CCONTROLS / 2) + 1)) + 1 + TOOLBOXMARGIN;
    AdjustWindowRect(&rc, TOOLBOXSTYLE, FALSE);
    cx = rc.right - rc.left;
    cy = rc.bottom - rc.top;

    /*
     * Get the saved position of the Toolbox.  Note that we throw away
     * the size fields, because we just calculated the required size.
     */
    if (!ReadWindowPos(szTBPos, &x, &y, &cxDummy, &cyDummy, &fMaximized)) {
        /*
         * The previous position of the Toolbox  couldn't be found.
         * Position the toolbox to the upper right corner of the
         * "client" area of the editor, but make sure it is completely
         * visible.
         */
        GetWindowRect(ghwndSubClient, &rcSubClient);
        x = rcSubClient.right - cx - (2 * TOOLBOXMARGIN);
        y = rcSubClient.top + (2 * TOOLBOXMARGIN);
        SetRect(&rc, x, y, x + cx, y + cy);
        FitRectToScreen(&rc);
        x = rc.left;
        y = rc.top;
    }

    /*
     * Create the toolbox window.
     */
    if (!(ghwndToolbox = CreateWindow(szToolboxClass, NULL, TOOLBOXSTYLE,
            x, y, cx, cy, ghwndMain, NULL, ghInst, NULL)))
        return;

    /*
     * Create the Pointer (W_NOTHING) button.
     */
    CreateWindow(szToolBtnClass, NULL,
            WS_CHILD | WS_VISIBLE,
            TOOLBOXMARGIN, TOOLBOXMARGIN, (cxToolBtn * 2) - 1, cyToolBtn,
            ghwndToolbox, (HMENU)W_NOTHING, ghInst, NULL);

    /*
     * Create the other buttons.
     */
    x = TOOLBOXMARGIN;
    y = TOOLBOXMARGIN + cyToolBtn - 1;
    for (i = 0; i < CCONTROLS; i++) {
        CreateWindow(szToolBtnClass, NULL,
                WS_CHILD | WS_VISIBLE,
                x, y, cxToolBtn, cyToolBtn,
                ghwndToolbox, (HMENU)i, ghInst, NULL);

        if (x == TOOLBOXMARGIN) {
            x += cxToolBtn - 1;
        }
        else {
            x = TOOLBOXMARGIN;
            y += cyToolBtn - 1;
        }
    }
}



/****************************************************************************
* ToolboxWndProc
*
* This is the window procedure for the toolbox window.
*
****************************************************************************/

WINDOWPROC ToolboxWndProc(
    HWND hwnd,
    UINT msg,
    WPARAM wParam,
    LPARAM lParam)
{
    switch (msg) {
        case WM_CREATE:
            {
                HMENU hmenu = GetSystemMenu(hwnd, FALSE);

                RemoveMenu(hmenu, 7, MF_BYPOSITION);    // Second separator.
                RemoveMenu(hmenu, 5, MF_BYPOSITION);    // First separator.

                RemoveMenu(hmenu, SC_RESTORE, MF_BYCOMMAND);
                RemoveMenu(hmenu, SC_SIZE, MF_BYCOMMAND);
                RemoveMenu(hmenu, SC_MINIMIZE, MF_BYCOMMAND);
                RemoveMenu(hmenu, SC_MAXIMIZE, MF_BYCOMMAND);
                RemoveMenu(hmenu, SC_TASKLIST, MF_BYCOMMAND);
            }

            return 0;

        case WM_KEYDOWN:
            {
                INT iToolNext;

                switch (wParam) {
                    case VK_UP:
                        if ((GetKeyState(VK_SHIFT) & 0x8000) ||
                                (GetKeyState(VK_CONTROL) & 0x8000))
                            break;

                        /*
                         * Go up a row, but don't go beyond the top.
                         */
                        iToolNext = gCurTool - TOOLBOXCOLUMNS;
                        if (iToolNext < 0)
                            iToolNext = W_NOTHING;

                        ToolboxSelectTool(iToolNext, FALSE);

                        break;

                    case VK_DOWN:
                        if ((GetKeyState(VK_SHIFT) & 0x8000) ||
                                (GetKeyState(VK_CONTROL) & 0x8000))
                            break;

                        if (gCurTool == W_NOTHING) {
                            iToolNext = 0;
                        }
                        else {
                            /*
                             * Go down a row, but don't go beyond the bottom.
                             */
                            iToolNext = gCurTool + TOOLBOXCOLUMNS;
                            if (iToolNext >= CCONTROLS)
                                break;
                        }

                        ToolboxSelectTool(iToolNext, FALSE);

                        break;

                    case VK_LEFT:
                        if ((GetKeyState(VK_SHIFT) & 0x8000) ||
                                (GetKeyState(VK_CONTROL) & 0x8000))
                            break;

                        if (gCurTool == W_NOTHING ||
                                !(gCurTool % TOOLBOXCOLUMNS))
                            break;

                        /*
                         * Go left a column.
                         */
                        ToolboxSelectTool(gCurTool - 1, FALSE);

                        break;

                    case VK_RIGHT:
                        if ((GetKeyState(VK_SHIFT) & 0x8000) ||
                                (GetKeyState(VK_CONTROL) & 0x8000))
                            break;

                        if (gCurTool == W_NOTHING ||
                                (gCurTool % TOOLBOXCOLUMNS) ==
                                TOOLBOXCOLUMNS - 1)
                            break;

                        /*
                         * Go right a column.
                         */
                        ToolboxSelectTool(gCurTool + 1, FALSE);

                        break;

                    case VK_TAB:
                        if (GetKeyState(VK_CONTROL) & 0x8000)
                            break;

                        /*
                         * Is the shift key pressed also?
                         */
                        if (GetKeyState(VK_SHIFT) & 0x8000) {
                            if (gCurTool == W_NOTHING)
                                iToolNext = CCONTROLS - 1;
                            else if (gCurTool == 0)
                                iToolNext = W_NOTHING;
                            else
                                iToolNext = gCurTool - 1;
                        }
                        else {
                            if (gCurTool == W_NOTHING)
                                iToolNext = 0;
                            else if (gCurTool == CCONTROLS - 1)
                                iToolNext = W_NOTHING;
                            else
                                iToolNext = gCurTool + 1;
                        }

                        ToolboxSelectTool(iToolNext, FALSE);

                        break;

                    case VK_END:
                        if ((GetKeyState(VK_SHIFT) & 0x8000) ||
                                (GetKeyState(VK_CONTROL) & 0x8000))
                            break;

                        ToolboxSelectTool(CCONTROLS - 1, FALSE);

                        break;

                    case VK_HOME:
                    case VK_ESCAPE:
                        if ((GetKeyState(VK_SHIFT) & 0x8000) ||
                                (GetKeyState(VK_CONTROL) & 0x8000))
                            break;

                        ToolboxSelectTool(W_NOTHING, FALSE);

                        break;
                }
            }

            break;

        case WM_ACTIVATE:
            if (LOWORD(wParam))
                gidCurrentDlg = DID_TOOLBOX;

            break;

        case WM_CLOSE:
            /*
             * The user closed the toolbox from the system menu.
             * Hide the toolbox (we don't actually destroy it so
             * that it will appear in the same spot when they show
             * it again).
             */
            ToolboxShow(FALSE);
            gfShowToolbox = FALSE;
            break;

        case WM_DESTROY:
            {
                INT i;
                RECT rc;

                DeleteObject(ghbmPointerToolUp);
                ghbmPointerToolUp = NULL;
                DeleteObject(ghbmPointerToolDown);
                ghbmPointerToolDown = NULL;

                for (i = 0; i < CCONTROLS; i++) {
                    DeleteObject(awcd[i].hbmToolBtnUp);
                    awcd[i].hbmToolBtnUp = NULL;
                    DeleteObject(awcd[i].hbmToolBtnDown);
                    awcd[i].hbmToolBtnDown = NULL;
                }

                /*
                 * Save the position of the toolbox.
                 */
                GetWindowRect(hwnd, &rc);
                WriteWindowPos(&rc, FALSE, szTBPos);

                /*
                 * Null out the global window handle for the toolbox
                 * for safety's sake.
                 */
                ghwndToolbox = NULL;
            }

            break;

        default:
            return DefWindowProc(hwnd, msg, wParam, lParam);
    }

    return 0;
}



/****************************************************************************
* ToolBtnWndProc
*
* This is the window procedure for the buttons in the toolbox window.
*
****************************************************************************/

WINDOWPROC ToolBtnWndProc(
    HWND hwnd,
    UINT msg,
    WPARAM wParam,
    LPARAM lParam)
{
    switch (msg) {
        case WM_LBUTTONDOWN:
            /*
             * Be sure any outstanding changes get applied
             * without errors.
             */
            if (!StatusApplyChanges())
                return TRUE;

            /*
             * Select the tool that was clicked on.  If the Ctrl
             * key is down, lock the tool also.
             */
            ToolboxSelectTool((UINT)GetWindowLong((hwnd), GWL_ID),
                    (GetKeyState(VK_CONTROL) & 0x8000) ? TRUE : FALSE);

            break;

        case WM_PAINT:
            {
                HDC hDC;
                PAINTSTRUCT ps;

                hDC = BeginPaint(hwnd, &ps);
                ToolboxDrawBitmap(hDC, (UINT)GetWindowLong((hwnd), GWL_ID));
                EndPaint(hwnd, &ps);
            }

            break;

        default:
            return DefWindowProc(hwnd, msg, wParam, lParam);
    }

    return 0;
}



/****************************************************************************
* ToolboxDrawBitmap
*
* Draws the current tool bitmap.
*
* Arguments:
*   HDC hDC - handle to the DC for the toolbox window.
*   INR type - type of bitmap to draw.
*
****************************************************************************/

STATICFN VOID ToolboxDrawBitmap(
    HDC hDC,
    INT type)
{
    HDC hMemDC;
    HBITMAP hbm;
    HBITMAP hbmOld;
    INT cxBitmap;

    if (type == W_NOTHING) {
        hbm = (type == gCurTool) ? ghbmPointerToolDown : ghbmPointerToolUp;

        /*
         * Note that the size of the Pointer tool is twice the width
         * of the other bitmaps, but less one pixel.  This is because
         * the other tools overlap their adjacent borders.
         */
        cxBitmap = (cxToolBtn * 2) - 1;
    }
    else {
        hbm = (type == gCurTool) ?
                awcd[type].hbmToolBtnDown : awcd[type].hbmToolBtnUp;
        cxBitmap = cxToolBtn;
    }

    /*
     * Draw the image.
     */
    hMemDC = CreateCompatibleDC(hDC);
    hbmOld = SelectObject(hMemDC, hbm);
    BitBlt(hDC, 0, 0, cxBitmap, cyToolBtn, hMemDC, 0, 0, SRCCOPY);
    SelectObject(hMemDC, hbmOld);
    DeleteDC(hMemDC);
}



/****************************************************************************
* ToolboxSelectTool
*
* This function selects a tool to be the current tool.
*
* Arguments:
*   INT type   - Type of control (one of the W_* defines).
*   BOOL fLock - TRUE if the tool should be locked down.
*
****************************************************************************/

VOID ToolboxSelectTool(
    INT type,
    BOOL fLock)
{
    PWINDOWCLASSDESC pwcd;

    if (gCurTool != type) {
        /*
         * Set the current wcd global for the current tool type.
         * This will point to the WINDOWCLASSDESC structure of the
         * current tool.  If the Custom tool was selected, the user
         * is asked which of the installed custom controls that they
         * really want.
         */
        if (type == W_CUSTOM) {
            /*
             * There are no custom controls installed.  Beep and
             * return without doing anything.
             */
            if (!gpclHead) {
                MessageBeep(0);
                return;
            }

            /*
             * If there are multiple custom controls installed,
             * ask the user which one they want.  Note that they
             * can press Cancel and return NULL!
             */
            if (gpclHead->pclNext) {
                if (!(pwcd = SelCustDialog()))
                    return;

                gpwcdCurTool = pwcd;
            }
            else {
                /*
                 * Since there is only one type of custom control
                 * installed, there is no need to ask the user
                 * which one they want.
                 */
                gpwcdCurTool = gpclHead->pwcd;
            }
        }
        else {
            gpwcdCurTool = (type == W_NOTHING) ? NULL : &awcd[type];
        }

        /*
         * Force the previous and current buttons to repaint.
         */
        if (ghwndToolbox) {
            InvalidateRect(GetDlgItem(ghwndToolbox, gCurTool), NULL, FALSE);
            InvalidateRect(GetDlgItem(ghwndToolbox, type), NULL, FALSE);
        }

        /*
         * Set the current tool type global.  This will be W_CUSTOM for
         * all custom controls.
         */
        gCurTool = type;
    }

    gfToolLocked = fLock;
}
