TOOLBOX.C


/******************************************************************************\
* This is a part of the Microsoft Source Code Samples.
* Copyright 1993 - 1998 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;
}