/******************************************************************************/
/*                                                                            */
/* Class Implementations in this file                                         */
/*      CFloatImgToolWnd                                                      */
/*      CImgToolWnd                                                           */
/*      CToolboxWnd                                                           */
/*      CTool                                                                 */
/*                                                                            */
/******************************************************************************/

#include "stdafx.h"
#include "global.h"
#include "pbrush.h"
#include "pbrusfrm.h"
#include "pbrusvw.h"
#include "ipframe.h"
#include "minifwnd.h"
#include "bmobject.h"
#include "imgsuprt.h"
#include "imgwnd.h"
#include "imgwell.h"
#include "imgtools.h"
#include "toolbox.h"
#include "imgcolor.h"
#include "docking.h"
#include "t_Text.h"

#define TRYANYTHING

#ifdef _DEBUG
#undef THIS_FILE
static CHAR BASED_CODE THIS_FILE[] = __FILE__;
#endif

IMPLEMENT_DYNAMIC(CToolboxWnd, CControlBar)

#include "memtrace.h"

CImgToolWnd* NEAR g_pImgToolWnd = NULL;

#define BPR(br, rop)        \
 { dc.SelectObject((br));   \
   dc.PatBlt(rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top, rop); }

#define iidmMac ( sizeof (rgidm) / sizeof (rgidm[0]) )

static UINT NEAR rgidm [] =
    {
    IDMB_PICKRGNTOOL,
    IDMB_PICKTOOL,

    IDMB_ERASERTOOL,
    IDMB_FILLTOOL,

    IDMY_PICKCOLOR,
    IDMB_ZOOMTOOL,

    IDMB_PENCILTOOL,
    IDMB_CBRUSHTOOL,

    IDMB_AIRBSHTOOL,
    IDMX_TEXTTOOL,

    IDMB_LINETOOL,
    IDMB_CURVETOOL,

    IDMB_RECTTOOL,
    IDMB_POLYGONTOOL,

    IDMB_ELLIPSETOOL,
    IDMB_RNDRECTTOOL
    };

/******************************************************************************/

BEGIN_MESSAGE_MAP(CImgToolWnd, CToolboxWnd)
        ON_WM_SYSCOLORCHANGE()
    ON_WM_ERASEBKGND()
    ON_WM_LBUTTONDOWN()
    ON_WM_LBUTTONDBLCLK()
    ON_WM_RBUTTONDOWN()
    ON_WM_PAINT()
    ON_WM_KEYDOWN()
    ON_WM_KEYUP()
    ON_WM_CHAR()
    ON_WM_NCHITTEST()
END_MESSAGE_MAP()


/******************************************************************************/

HTHEME SafeOpenThemeData(HWND hwnd, LPCWSTR pszClassList)
    {
    __try
        {
        return OpenThemeData(hwnd, pszClassList);
        }
    __except(EXCEPTION_EXECUTE_HANDLER)
        {
        return 0;
        }
    }

/******************************************************************************/

BOOL CImgToolWnd::Create(const TCHAR* pWindowName, DWORD dwStyle,
                         const RECT& rect, const POINT& btnSize, WORD wWide,
                         CWnd* pParentWnd, BOOL bDkRegister)
    {
    if (! CToolboxWnd::Create( pWindowName, dwStyle, rect,
                               btnSize, NUM_TOOLS_WIDE, pParentWnd, IDB_IMGTOOLS ))
        {
        return FALSE;
        }

    for (int iidm = 0; iidm < iidmMac; iidm += 1)
        {
        CTool* pTool = new CTool(this, (WORD)rgidm[iidm], iidm, TS_CMD | TS_STICKY,
                rgidm[iidm] == CImgTool::GetCurrentID() ? TF_DOWN : 0);

        if (pTool == NULL)
            {
            DestroyWindow();
            return FALSE;
            }
        AddTool(pTool);

        }


    m_nOffsetX = m_btnsize.x / 5;
    m_nOffsetY = m_btnsize.y / 5;

    CSize size = GetSize();

    MoveWindow( rect.left, rect.top, size.cx, size.cy );

    return TRUE;
    }


/******************************************************************************/
//

void CImgToolWnd::OnSysColorChange()
        {
        CToolboxWnd::OnSysColorChange();
        InvalidateRect(NULL, FALSE);
        }

/******************************************************************************/

int CImgToolWnd::OnToolHitTest(CPoint point, TOOLINFO* pTI) const
    {
    CRect rect;

    CTool* pTool = ToolFromPoint( &rect, &point );

	if (pTool != NULL)
	    {
		int nHit = pTool->m_wID;

		if (pTI != NULL)
		    {
			pTI->hwnd     = m_hWnd;
            pTI->uId      = nHit;
            pTI->rect     = rect;
			pTI->lpszText = LPSTR_TEXTCALLBACK;
		    }

		return nHit;
	    }

	return -1;  // not found
    }

/******************************************************************************/

void CImgToolWnd::OnUpdateCmdUI( CFrameWnd* pTarget, BOOL bDisableIfNoHndler )
    {
    }

/******************************************************************************/

CSize CImgToolWnd::CalcFixedLayout( BOOL bStretch, BOOL bHorz )
    {
#ifdef TRYANYTHING
        return GetSize();
#else
    CSize size = CControlBar::CalcFixedLayout( bStretch, bHorz );

    CSize sizeBar = GetSize();

    size.cx = sizeBar.cx;
    return size;
#endif
    }

/******************************************************************************/

UINT CImgToolWnd::OnNcHitTest(CPoint point)
    {
    return CToolboxWnd::OnNcHitTest(point);
    }

/******************************************************************************/

CSize CImgToolWnd::GetSize()
    {
    // Leave room in the toolbox for the brushes...
    CRect clientRect;
    CRect windowRect;
    CSize sizeDiff;

    GetWindowRect( &windowRect );
    GetClientRect( &clientRect );

    sizeDiff = windowRect.Size() - clientRect.Size();
    int nTools = GetToolCount();

    clientRect.right  = m_btnsize.x * NUM_TOOLS_WIDE + m_nOffsetX * 2;
    clientRect.bottom = (nTools / NUM_TOOLS_WIDE + (!!(nTools % NUM_TOOLS_WIDE)))
                                          * m_btnsize.y + m_nOffsetY * 2;

    m_rcTools.left   = m_nOffsetX;
    m_rcTools.top    = m_nOffsetY;
    m_rcTools.right  = clientRect.right - m_nOffsetX;
    m_rcTools.bottom = clientRect.bottom - m_nOffsetY;

    m_rcBrushes.left   = clientRect.left   + (4 + m_nOffsetX);
    m_rcBrushes.top    = clientRect.bottom;
    m_rcBrushes.right  = clientRect.right  - (4 + m_nOffsetX);
    m_rcBrushes.bottom = clientRect.bottom + 66;

    clientRect.bottom += m_rcBrushes.Height() + m_nOffsetY;

    return clientRect.Size() + sizeDiff;
    }

/******************************************************************************/

void CImgToolWnd::OnLButtonDown(UINT nFlags, CPoint pt)
    {
    BOOL bInBrushes = m_rcBrushes.PtInRect(pt);

    if (bInBrushes)
        {
        CRect optionsRect = m_rcBrushes;

        optionsRect.InflateRect(-1, -1);
        pt -= (CSize)optionsRect.TopLeft();

        CImgTool::GetCurrent()->OnClickOptions(this, optionsRect, pt);
        }
    else
        CToolboxWnd::OnLButtonDown(nFlags, pt);
    }

/******************************************************************************/

void CImgToolWnd::InvalidateOptions(BOOL bErase)
    {
    // NOTE: bErase is now ignored since we do drawing off-screen and
    // blt the whole thing...

    CRect optionsRect = m_rcBrushes;
    optionsRect.InflateRect(-1, -1);

    InvalidateRect(&optionsRect, FALSE);
    }

/******************************************************************************/

void CImgToolWnd::OnLButtonDblClk(UINT nFlags, CPoint pt)
    {
    CToolboxWnd::OnLButtonDblClk(nFlags, pt);
    }

/******************************************************************************/

void CImgToolWnd::OnRButtonDown(UINT nFlags, CPoint pt)
    {
    CToolboxWnd::OnRButtonDown(nFlags, pt);
    }

/******************************************************************************/

BOOL CImgToolWnd::OnEraseBkgnd( CDC* pDC )
    {
    CRect rect;
    GetClientRect( rect );
    pDC->FillRect( rect, GetSysBrush( COLOR_BTNFACE ) );

        return CControlBar::OnEraseBkgnd( pDC );
    }

/******************************************************************************/

void CImgToolWnd::OnPaint()
    {
    CPaintDC dc(this);

    if (dc.m_hDC == NULL)
        {
        theApp.SetGdiEmergency();
        return;
        }

    if (CImgWnd::c_pImgWndCur == NULL)
        {
        // Chances are, we're are going to be hidden soon, so don't
        // bother painting...
        return;
        }

    DrawButtons(dc, &dc.m_ps.rcPaint);

    ASSERT(CImgWnd::c_pImgWndCur->m_pImg != NULL);

    // Brush Shapes
    if (!(m_rcBrushes & dc.m_ps.rcPaint).IsRectEmpty())
        {
        Draw3dRect(dc.m_hDC, &m_rcBrushes );
        CRect optionsRect = m_rcBrushes;
        optionsRect.InflateRect(-1, -1);

        CRect rc(0, 0, optionsRect.Width(), optionsRect.Height());
        CDC memDC;
        CBitmap memBM;

        if (!memDC.CreateCompatibleDC(&dc) ||
            !memBM.CreateCompatibleBitmap(&dc, rc.right, rc.bottom))
            {
            theApp.SetGdiEmergency();
            return;
            }
        CBitmap* pOldBitmap = memDC.SelectObject(&memBM);

        CBrush* pOldBrush = memDC.SelectObject(GetSysBrush( COLOR_BTNFACE ));

        memDC.PatBlt(0, 0, rc.right, rc.bottom, PATCOPY);

        CRect rcPaint = dc.m_ps.rcPaint;
        rcPaint.OffsetRect(-optionsRect.left, -optionsRect.top);

        CImgTool::GetCurrent()->OnPaintOptions(&memDC, rcPaint, rc);

        dc.BitBlt(optionsRect.left, optionsRect.top, optionsRect.Width(),
                  optionsRect.Height(), &memDC, 0, 0, SRCCOPY);

        memDC.SelectObject(pOldBitmap);
        memDC.SelectObject(pOldBrush);
        }
    }

/******************************************************************************/

BOOL CImgToolWnd::PreTranslateMessage(MSG* pMsg)
    {
    switch (pMsg->message)
        {
        case WM_KEYDOWN:
        case WM_KEYUP:
        case WM_CHAR:
            if (CImgWnd::c_pImgWndCur != NULL)
                {
                pMsg->hwnd = CImgWnd::c_pImgWndCur->m_hWnd;
                return CImgWnd::c_pImgWndCur->PreTranslateMessage(pMsg);
                }
            return FALSE;
        }

        return CToolboxWnd::PreTranslateMessage(pMsg);
    }

/******************************************************************************/
// default button size

const POINT NEAR CToolboxWnd::ptDefButton = { 26, 26 };

/*DK*/
BEGIN_MESSAGE_MAP(CToolboxWnd, CControlBar)
        ON_WM_SYSCOLORCHANGE()
    ON_WM_PAINT()
    ON_WM_LBUTTONDOWN()
    ON_WM_RBUTTONDOWN()
    ON_WM_LBUTTONDBLCLK()
    ON_WM_MOUSEMOVE()
    ON_WM_LBUTTONUP()
    ON_WM_CLOSE()
    ON_WM_DESTROY()
    ON_WM_WININICHANGE()
    ON_WM_KEYDOWN()
    ON_MESSAGE(TM_TOOLDOWN, OnToolDown)
    ON_MESSAGE(TM_TOOLUP, OnToolUp)
    ON_MESSAGE(WM_THEMECHANGED, OnThemeChanged)
    /*DK*/
//  ON_MESSAGE(WM_HELPHITTEST, OnHelpHitTest)
END_MESSAGE_MAP()

/******************************************************************************/

CToolboxWnd::CToolboxWnd()
    {
    m_Tools    = new CObArray;
    m_bmStuck  = NULL;
    m_bmPushed = NULL;
    m_bmPopped = NULL;
    m_tCapture = NULL;
    m_bInside  = FALSE;
    m_nOffsetX = 0;
    m_nOffsetY = 0;
    m_pLastHot = 0;
    m_hTheme   = 0;
    }

/******************************************************************************/

CToolboxWnd::~CToolboxWnd()
    {
    if (m_bmStuck != NULL)
        delete m_bmStuck;

    if (m_bmPushed != NULL)
        delete m_bmPushed;

    if (m_bmPopped != NULL)
        delete m_bmPopped;

    if (m_Tools != NULL)
        {
        int nTools = (int)m_Tools->GetSize();

        for (int iTool = 0; iTool < nTools; iTool += 1)
            {
            CTool* pTool = (CTool*)m_Tools->GetAt(iTool);
            delete pTool;
            }

        delete m_Tools;
        }
    }

/******************************************************************************/

BOOL CToolboxWnd::Create(const TCHAR FAR* lpWindowName,
                         DWORD dwStyle, const RECT& rect,
                         const POINT& btnsize /* = ptDefButton */, WORD wWide /* = 1 */,
                         CWnd* pParentWnd /* = NULL */, int nImageWellID /* = 0 */)
    {
    // This routine is a lot more complicated than the typical Create, so
    // because (1) we aren't a built-in Windows window type, and (2) we
    // want to specify the client size with the btnsize and wWide parameters.
    // (We ignore the width, height of the rect parameter.)

    if (nImageWellID != 0 && !m_imageWell.Load(nImageWellID, CSize(16, 16)))
        return FALSE;

        // save the style
        m_dwStyle = (UINT)dwStyle | CBRS_TOOLTIPS | CBRS_FLYBY;

    DWORD dwS = (m_dwStyle & ~WS_VISIBLE);

    CRect t = rect;

    t.right  = t.left + 20;
    t.bottom = t.top  + 20;

    BOOL bRet = CControlBar::Create( NULL, lpWindowName, dwS, t, pParentWnd,
                                                         ID_VIEW_TOOL_BOX );
    if (! bRet)
        return FALSE;

    m_wWide   = wWide;
    m_btnsize = btnsize;

#ifdef TRYANYTHING
        SizeByButtons( -1, TRUE );
#else
    SizeByButtons( 0 );
#endif

    m_hTheme = SafeOpenThemeData(GetSafeHwnd(), L"toolbar");

    if (! DrawStockBitmaps())
        {
        DestroyWindow();
        return FALSE;
        }

    if (dwStyle & WS_VISIBLE)
        {
        ShowWindow(SW_SHOW);
        UpdateWindow();
        }

    return TRUE;
    }

/******************************************************************************/
// private DrawStockBitmaps:
// Draws the three states of button, given the desired button size of this
// CToolboxWnd.  These have no graphic on them; the buttons have bitmap
// glyphs to be added to these blank forms.
//
// The three states:
//   m_bmPopped  This is the normal look of a button.  Note that this is
//               also used as the basis of a grayed (disabled) button, by
//               changing how the button's glyph is drawn on it.
//   m_bmPushed  This is the button-down state for non-sticky tools (tools
//               that pop back out as soon as you let go.
//   m_bmStuck   This is the button-down state for sticky tools.  This has
//               a distinct look that is more easily visible, per UISG.
//

BOOL CToolboxWnd::DrawStockBitmaps()
    {
    CWindowDC wdc(this);
    if (wdc.m_hDC == NULL)
        {
        theApp.SetGdiEmergency(TRUE);
        return FALSE;
        }

    CBitmap* obm;
    CBrush* obr;
    CRect rc;
    CDC dc;

    if (!dc.CreateCompatibleDC(&wdc))
        {
        theApp.SetGdiEmergency(TRUE);
        return FALSE;
        }

    obr = dc.SelectObject(GetSysBrush(COLOR_WINDOWFRAME));

    // bmPopped:
    //
    if (m_bmPopped)
        delete m_bmPopped;
    m_bmPopped = new CBitmap;
    if (!m_bmPopped->CreateCompatibleBitmap(&wdc, m_btnsize.x, m_btnsize.y))
        {
        theApp.SetMemoryEmergency(TRUE);
        return FALSE;
        }
    obm = dc.SelectObject(m_bmPopped);
    rc = CRect(0, 0, m_btnsize.x, m_btnsize.y);
    BPR(GetSysBrush(COLOR_WINDOWFRAME), PATCOPY);
#ifdef OLDBUTTONS
    rc.right--; rc.bottom--;
    BPR(GetSysBrush(COLOR_BTNSHADOW), PATCOPY);
#endif
    rc.right--; rc.bottom--;
    BPR(GetSysBrush(COLOR_BTNHIGHLIGHT), PATCOPY);
    rc.left++; rc.top++;
    BPR(GetSysBrush(COLOR_BTNSHADOW), PATCOPY);
    rc.right--; rc.bottom--;
    BPR(GetSysBrush(COLOR_BTNFACE), PATCOPY);

    // bmPushed:
    //
    if (m_bmPushed)
        delete m_bmPushed;
    m_bmPushed = new CBitmap;
    if (!m_bmPushed->CreateCompatibleBitmap(&wdc, m_btnsize.x, m_btnsize.y))
        {
        theApp.SetMemoryEmergency(TRUE);
        return FALSE;
        }
    dc.SelectObject(m_bmPushed);
    rc = CRect(0, 0, m_btnsize.x, m_btnsize.y);
#ifndef OLDBUTTONS
    BPR(GetSysBrush(COLOR_BTNHIGHLIGHT), PATCOPY);
    rc.right--; rc.bottom--;
    BPR(GetSysBrush(COLOR_WINDOWFRAME), PATCOPY);
    rc.left++; rc.top++;
    BPR(GetSysBrush(COLOR_BTNFACE), PATCOPY);
    rc.right--; rc.bottom--;
    BPR(GetSysBrush(COLOR_BTNSHADOW), PATCOPY);
    rc.left++; rc.top++;
    BPR(GetSysBrush(COLOR_BTNFACE), PATCOPY);
#else
    BPR(GetSysBrush(COLOR_WINDOWFRAME), PATCOPY);
    rc.right--; rc.bottom--;
    BPR(GetSysBrush(COLOR_BTNSHADOW), PATCOPY);
    rc.left += 2; rc.top += 2;
    BPR(GetSysBrush(COLOR_BTNFACE), PATCOPY);
#endif

    // bmStuck:
    //
    if (m_bmStuck)
        delete m_bmStuck;
    m_bmStuck = new CBitmap;
    if (!m_bmStuck->CreateCompatibleBitmap(&wdc, m_btnsize.x, m_btnsize.y))
        {
        theApp.SetMemoryEmergency(TRUE);
        return FALSE;
        }
    dc.SelectObject(m_bmStuck);
    rc = CRect(0, 0, m_btnsize.x, m_btnsize.y);
#ifndef OLDBUTTONS
    BPR(GetSysBrush(COLOR_BTNHIGHLIGHT), PATCOPY);
    rc.right--; rc.bottom--;
    BPR(GetSysBrush(COLOR_WINDOWFRAME), PATCOPY);
    rc.left++; rc.top++;
    BPR(GetSysBrush(COLOR_BTNFACE), PATCOPY);
    rc.right--; rc.bottom--;
    BPR(GetSysBrush(COLOR_BTNSHADOW), PATCOPY);
    rc.left++; rc.top++;
#else
    BPR(GetSysBrush(COLOR_WINDOWFRAME), PATCOPY);
    rc.right--; rc.bottom--;
    BPR(GetSysBrush(COLOR_BTNSHADOW), PATCOPY);
    rc.left += 2; rc.top += 2;
#endif

    dc.SelectObject(GetHalftoneBrush());
#ifdef OLDBUTTONS
    dc.SetTextColor(RGB(255, 255, 255));
    dc.SetBkColor(RGB(192, 192, 192));
#else
        dc.SetTextColor(GetSysColor(COLOR_BTNFACE));
        dc.SetBkColor(GetSysColor(COLOR_BTNHIGHLIGHT));
#endif
    dc.PatBlt(rc.left, rc.top, rc.Width(), rc.Height(), PATCOPY);

    dc.SelectObject(obm);
    dc.SelectObject(obr);
    dc.DeleteDC();

    return TRUE;
    }

/******************************************************************************/
//

afx_msg void CToolboxWnd::OnSysColorChange()
        {
        CControlBar::OnSysColorChange();
        DrawStockBitmaps();
        InvalidateRect(NULL, FALSE);
        }

/******************************************************************************/
//
// SizeByButtons
//
// Sizes the window according to the current (or a specified) number of
// buttons.  If there are no buttons, the window makes room for one button.
// Unfilled button slots show through to the background.
//

void CToolboxWnd::SizeByButtons(int nButtons /* = -1 */,
                                BOOL bRepaint /* = FALSE */)
    {
    if (nButtons == -1)
        nButtons = (int)m_Tools->GetSize();
    if (nButtons == 0)
        nButtons = 1;

    // Can't use the hokey Windows' AdjustWindowRect() to work this out,
    // so we do it ourselves by adapting the window based on the difference
    // between GetWindowRect and ClientRect results.
    //
    CRect w, c;
    GetWindowRect(&w);
    w.right -= w.left;
    w.bottom -= w.top;
    GetClientRect(&c);

    if (bRepaint)
        Invalidate(TRUE);

    MoveWindow(w.left, w.top,
                m_btnsize.x * m_wWide + (w.right - c.right) - 1,
                m_btnsize.y * ((nButtons / m_wWide) + (!!(nButtons % m_wWide)))
                        + (w.bottom-c.bottom) - 1,
                bRepaint);
    }

/******************************************************************************/
// OnWinIniChange:
//
void CToolboxWnd::OnWinIniChange(LPCTSTR lpSection)

    {
        lpSection;
#ifdef TRYANYTHING
        CControlBar::OnWinIniChange( lpSection );
#endif
    DrawStockBitmaps();
    Invalidate(TRUE);
    }

/******************************************************************************/
//
// OnKeyDown
//
// Implements keyboard handling for the toolbox window.. this allows
// trapping of the ESC key, for moving the selected to back to the
// arrow.
//
void CToolboxWnd::OnKeyDown(UINT nKey, UINT nRepCnt, UINT nFlags)
    {
    if (nKey == VK_ESCAPE && m_tCapture)
        CancelDrag();
    else
        CControlBar::OnKeyDown(nKey, nRepCnt, nFlags);
    }

/******************************************************************************/

void CToolboxWnd::CancelDrag()
    {
#if 0
    // this is bogus as dragging is presently disabled
    if (m_tCapture != NULL)
        m_tCapture->m_wState |= TF_DRAG; // so select will cancel it
#endif

    m_bInside = FALSE;

#if 0
    // whoever tries to make drag/drop work will have to handle the fact
    // that our client does not get notified by SelectTool
    SelectTool( IDMB_ARROW );
#endif

    m_tCapture = NULL;
    ReleaseCapture();
    }

/******************************************************************************/
// AddTool:
//
void CToolboxWnd::AddTool(CTool* tool)
    {
    m_Tools->Add((CObject*)tool);

    if ((m_Tools->GetSize() + m_wWide - 1) / m_wWide > 11)  // only have 11 high if more increase the width
        m_wWide += 1;

    SizeByButtons(-1, TRUE);

    }


/******************************************************************************/
// RemoveTool:
//
void CToolboxWnd::RemoveTool(CTool* tool)
    {
    for (int nTool = GetToolCount() - 1; GetToolAt(nTool) != tool; nTool -= 1)
        ASSERT(nTool >= 0);

    m_Tools->RemoveAt(nTool);

    if ((m_Tools->GetSize() + m_wWide - 2) / (m_wWide - 1) <= 11 && m_wWide > 1)
        m_wWide -= 1;

    SizeByButtons(-1, TRUE);
    }


/******************************************************************************/
// private GetTool:
//
CTool* CToolboxWnd::GetTool(WORD wID)
    {
    int nTools = (int)m_Tools->GetSize();
    for (int i = 0; i < nTools; i++)
        {
        CTool* t = (CTool*)m_Tools->GetAt(i);
        if (t && t->m_wID == wID)
            return t;
        }

    return NULL;
    }

/******************************************************************************/
//
// SetToolState
//
// Used by the owner of a button to modify the state of the button.
// This does not notify the owner of the new state; presumably the
// owner knows what it's doing to its own buttons.  This allows the
// owner to use this API during a WM_TOOLDOWN, etc., without getting
// into a shouting match with the toolbox.
//
WORD CToolboxWnd::SetToolState(WORD wID, WORD wState)
    {
    CRect rect;
    CTool* t = GetTool(wID);
    if (t && !(t->m_wState & TF_NYI))
        {
        WORD w = t->m_wState;
        t->m_wState = wState;

        //
        // if state hasn't changed, return to avoid invalidate and
        // associated flicker.
        //

        if (w == wState)
            return w;

        //
        // Calculate the rectangle of the button whose state is changing,
        // and invalidate it.
        //
        // replaces ed's simplistic (and flickering) code:
        //
        //      Invalidate(FALSE)
        //

        for (int i = 0; (CTool*)m_Tools->GetAt(i) != t; i += 1)
            {
            ASSERT(i != m_Tools->GetSize());
            }

        rect.left   = (i % m_wWide) * m_btnsize.x + m_nOffsetX;
        rect.top    = (i / m_wWide) * m_btnsize.y + m_nOffsetY;
        rect.right  = rect.left + m_btnsize.x;
        rect.bottom = rect.top  + m_btnsize.y;

        InvalidateRect(&rect, TRUE);
        return w;
        }

    return 0;
    }

/******************************************************************************/
// SetToolStyle:
// Used by the owner of a button to modify the style of the button.
// This forces the state of the button to be enabled and released.
// This does not notify the owner of the new state; presumably the
// owner knows what it's doing to its own buttons.  This allows the
// owner to use this API during a WM_TOOLDOWN, etc., without getting
// into a shouting match with the toolbox.
//
WORD CToolboxWnd::SetToolStyle(WORD wID, WORD wStyle)
    {
    CTool* t = GetTool(wID);
    if (t)
        {
        WORD w = t->m_wStyle;
        t->m_wStyle = wStyle;
        t->m_wState = 0;
        Invalidate(FALSE);
        return w;
        }

    return 0;
    }


/******************************************************************************/
//
// SelectTool
//
// Selects a given tool and deselects all the other tools.      So, for instance,
// to select the arrow, call pToolbox->SelectTool(IDMB_ARROW);
//
void CToolboxWnd::SelectTool(WORD wID)
    {
    //
    // first clear all the tools except the one we want pressed, then
    // select the one we want.
    //
    for (int i = 0; i < m_Tools->GetSize(); i += 1)
        {
        CTool* pTool = (CTool*)m_Tools->GetAt(i);

        if (pTool->m_wID != wID)
            SetToolState(pTool->m_wID, 0);
        }

    SetToolState( wID, TF_SELECTED );
    }

/******************************************************************************/
/* CToolboxWnd::CurrentTool
 *
 * Returns the ID of the currently selected tool.
 */
WORD CToolboxWnd::CurrentToolID()
    {
    int nTools = (int)m_Tools->GetSize();
    for (int i = 0; i < nTools; i++)
        {
        CTool* t = (CTool*)m_Tools->GetAt(i);
        if (t && t->m_wState == TF_SELECTED)
            return t->m_wID;
        }
    return IDMB_ARROW;
    }

/******************************************************************************/

#define HITTYPE_SUCCESS         0               // hit an item in the control bar
#define HITTYPE_NOTHING         (-1)    // hit nothing, but hit the control bar itself
#define HITTYPE_OUTSIDE         (-2)    // hit a window outside of the control bar
#define HITTYPE_TRACKING        (-3)    // this app is has the focus (is tracking)
#define HITTYPE_INACTIVE        (-4)    // the app is not active
#define HITTYPE_DISABLED        (-5)    // the control bar is disabled
#define HITTYPE_FOCUS           (-6)    // the control bar has focus

int CToolboxWnd::HitTestToolTip( CPoint point, UINT* pHit )
    {
    if (pHit)
        *pHit = (UINT)-1;    // assume it won't hit anything

    int iReturn = HITTYPE_INACTIVE;

    // make sure this app is active
    if (theApp.m_bActiveApp)
        {
        // check for this application tracking (capture set)
        if (! m_tCapture)
            {
            // finally do the hit test on the items within the control bar
            ScreenToClient( &point );

            CRect  rect;
            CTool* pTool = ToolFromPoint( &rect, &point );

            if (pTool && rect.PtInRect( point ))
                {
                iReturn = HITTYPE_SUCCESS;

                if (pHit)
                    *pHit = pTool->m_wID;
                }
            else
                iReturn = HITTYPE_OUTSIDE;
            }
        else
            iReturn = HITTYPE_TRACKING;
                }

    #ifdef _DEBUG
    TRACE2( "HitTestToolType %d - %u\n", iReturn, pHit );
    #endif

    return iReturn;
    }

/******************************************************************************/

UINT CToolboxWnd::OnCmdHitTest( CPoint point, CPoint* pCenter )
    {
    ASSERT_VALID( this );

    // now hit test against CToolBar buttons
    CRect  rect;
    UINT   nHit  = (UINT)-1;
    CTool* pTool = ToolFromPoint( &rect, &point );

    if (pTool)
        nHit = pTool->m_wID;

    return nHit;
    }

/******************************************************************************/
// private ToolFromPoint:
// Given a CPoint in client coordinates, this function returns the tool
// associated with the button at that point, if any.  If a tool is found,
// the given CRect (if not NULL) is filled with the bounds of the tool's
// button.
//
CTool* CToolboxWnd::ToolFromPoint(CRect* rect, CPoint* pt) const
    {
    CRect  c = m_rcTools;
    CPoint p = *pt;

    if (p.x < c.left || p.x >= c.right
    ||  p.y < c.top  || p.y >= c.bottom)
        return NULL;

    int x = (p.x - m_nOffsetX) / m_btnsize.x;
    int y = (p.y - m_nOffsetY) / m_btnsize.y;
    int i = x + (y * m_wWide);

    if (i >= m_Tools->GetSize())
        return NULL;

    CTool* t = (CTool*)(m_Tools->GetAt( i ));

    if (t && rect)
        {
        rect->left   = m_btnsize.x * x + m_nOffsetX;
        rect->top    = m_btnsize.y * y + m_nOffsetY;
        rect->right  = rect->left + m_btnsize.x;
        rect->bottom = rect->top  + m_btnsize.y;
        }

    return t;
    }

/******************************************************************************/
// OnLButtonDown:

void CToolboxWnd::OnLButtonDown(UINT wFlags, CPoint point)
    {
        wFlags; // Avoid unused arg warnings
    m_tCapture = ToolFromPoint( &m_lasttool, &point );
    m_downpt   = point;

    if (m_tCapture)
        {
        CRect   rect = m_lasttool;
        CString strPrompt;

        m_bInside = FALSE;

        rect.InflateRect( -1, -1 );

        if (rect.PtInRect( point ))
            {
            if (m_tCapture->m_wID <= IDMB_USERBTN)
                GetOwner()->SendMessage( WM_SETMESSAGESTRING, (WPARAM)m_tCapture->m_wID );

            if (m_tCapture->m_wState & TF_DISABLED)
                m_tCapture = NULL;
            else
                m_bInside = TRUE;
            }
        else
            m_tCapture = NULL;
        }
    else
        {
                CControlBar::OnLButtonDown(wFlags,point);
        }

    if (m_tCapture )
        {
        SetCapture();

        if (m_tCapture)
            InvalidateRect( &m_lasttool, TRUE );
        }
    }

/******************************************************************************/

void CToolboxWnd::OnRButtonDown(UINT wFlags, CPoint point)
    {
        wFlags;
        point;
    if (GetCapture() == this)
        CancelDrag();
    }

/******************************************************************************/
/*DK*/
//  LRESULT CToolboxWnd::OnHelpHitTest(WPARAM wParam, LPARAM lParam)
//      {
//      CPoint pt(lParam);
//      CTool* t = ToolFromPoint(&m_lasttool, &pt);
//
//      if (t == NULL)
//          return CMiniFrmWnd::OnHelpHitTest(wParam, lParam);
//      else
//          {
//          ASSERT( t->m_wID );
//          return HID_BASE_BUTTON + t->m_wID;
//          }
//      }


// OnLButtonDblClk:  FUTURE: Maybe just not use CS_DBLCLKS?
//
void CToolboxWnd::OnLButtonDblClk(UINT wFlags, CPoint point)
    {
    OnLButtonDown(wFlags, point);
    }

/******************************************************************************/

BOOL CToolboxWnd::SetStatusText(int nHit)
    {
    if (nHit == -1 && m_pLastHot != 0)
        {
        m_pLastHot->m_wState &= ~TF_HOT;
        InvalidateRect(&m_rectLastHot, TRUE);
        m_pLastHot = 0;
        }

    return CControlBar::SetStatusText(nHit);
    }

/******************************************************************************/
// OnMouseMove:
//
void CToolboxWnd::OnMouseMove(UINT wFlags, CPoint point)
    {
    CTool* t = m_tCapture;

    if (! t || (t->m_wState & TF_DISABLED))
        {
        if (m_hTheme) 
            {
            CRect rectHot;

            CTool* pHot = ToolFromPoint(&rectHot, &point);

            if (m_pLastHot != pHot)
                {
                if (m_pLastHot)
                    {
                    m_pLastHot->m_wState &= ~TF_HOT;
                    InvalidateRect(&m_rectLastHot, TRUE);
                    }

                if (pHot)
                    {
                    pHot->m_wState |= TF_HOT;
                    InvalidateRect(&rectHot, TRUE);
                    }

                m_rectLastHot = rectHot;
                m_pLastHot = pHot;
                }
            }

        CControlBar::OnMouseMove( wFlags, point );
        return;
        }

    BOOL bWasInside = m_bInside;
    CRect rect = m_lasttool;

    rect.InflateRect( -1, -1 );

    m_bInside = ((! (t->m_wState & TF_DRAG)) && rect.PtInRect( point ));

    if (bWasInside != m_bInside)
        InvalidateRect( &m_lasttool, TRUE );

    if (t && !(t->m_wState & TF_DISABLED))
        {
        // if it's a mousemove and we're draggable, then see how far it
        // is from the original mousedown -- if it's a fair distance,
        // then drag it.
        if ((t->m_wStyle & TS_DRAG) &&
                    (((point.x - m_downpt.x) > 3) ||
                     ((point.y - m_downpt.y) > 3) ||
                     ((m_downpt.x - point.x) > 3) ||
                     ((m_downpt.y - point.y) > 3)))
            {
            t->m_wState |= TF_DRAG;

            if (t->m_wStyle & TS_STICKY)
                {
                if (!(t->m_wState & TF_SELECTED))
                    {
                    t->m_wState |= TF_SELECTED;

                    if (t->m_pOwner)
                        t->m_pOwner->SendMessage(TM_TOOLDOWN, t->m_wID);
                    }

                if (m_bInside)
                    InvalidateRect(&m_lasttool, TRUE);

                m_bInside = FALSE; // looks stuck immediately!
                }
            }
        if (t->m_pOwner && (t->m_wState & TF_DRAG))
            {
            CPoint spt = point;
            ClientToScreen(&spt);

            // if the drag and drop began ok, release the captured tool
//          if (t->m_pOwner->BeginDragDrop( t, spt ))
//              m_tCapture = NULL;
            }
        }
    }


/******************************************************************************/
// OnLButtonUp:

void CToolboxWnd::OnLButtonUp(UINT wFlags, CPoint point)
    {
    if (! m_tCapture )
        {
        CControlBar::OnLButtonUp( wFlags, point );
        return;
        }

    CTool* t = m_tCapture;

    if (t && ! (t->m_wState & TF_DISABLED))
        {
        m_bInside = (point.x >= m_lasttool.left
                  && point.x <  m_lasttool.right
                  && point.y >= m_lasttool.top
                  && point.y <  m_lasttool.bottom);

        if (m_bInside)
            {
            if (t->m_wStyle & TS_STICKY)
                {
                if (! (t->m_wState & TF_DRAG))
                    {
                    t->m_wState ^= TF_SELECTED;

                    InvalidateRect(&m_lasttool, TRUE);

                    if (t->m_pOwner)
                        t->m_pOwner->SendMessage( t->m_wState & TF_SELECTED?
                                         TM_TOOLDOWN : TM_TOOLUP, t->m_wID );
                    }
                }

            if (t->m_wStyle & TS_CMD)
                {
                if (t->m_pOwner)
                    AfxGetMainWnd()->SendMessage( WM_COMMAND, t->m_wID );
                }
            }
        }
    ReleaseCapture();
    m_tCapture = NULL;
    m_bInside  = FALSE;
    }

/******************************************************************************/

BOOL CToolboxWnd::OnCommand(UINT wParam, LONG lParam)
    {
    AfxGetMainWnd()->SendMessage(WM_COMMAND, wParam, lParam);
    return TRUE;
    }

/******************************************************************************/

void CToolboxWnd::DrawButtons(CDC& dc, RECT* rcPaint)
    {
    CRect rect;
    CRect updateRect;
    int i, n;

    if (rcPaint == NULL)
        {
        GetClientRect( &updateRect );
        rcPaint = &updateRect;
        }

    CDC memdc;
    memdc.CreateCompatibleDC(&dc);

    if (m_hTheme == 0)
        {
        // Force the buttons to be rebuilt here
        DrawStockBitmaps();
        }

    CBitmap* obm = memdc.SelectObject( m_bmPopped );
    CBrush*  obr = memdc.SelectObject( GetSysBrush( COLOR_BTNTEXT ) );

    n = (int)m_Tools->GetSize();

    BOOL bUsedImageWell = FALSE;

    for (i = 0; i < n; i++)
        {
        CTool* t = (CTool*)m_Tools->GetAt(i);

        if (! t)
            continue;

        rect.left   = (i % m_wWide) * m_btnsize.x + m_nOffsetX;
        rect.top    = (i / m_wWide) * m_btnsize.y + m_nOffsetY;

        rect.right  = rect.left + m_btnsize.x;
        rect.bottom = rect.top  + m_btnsize.y;

        CRect ir;

        if (! ir.IntersectRect( rcPaint, &rect ))
            continue;

        // Select the right stock bitmap, and remember to
        // shove the graphic if it's in a pushed state.
        //
        CBitmap* bmStock = m_bmPopped;
        int xshove = 0, yshove = 0;
        int iButtonStateId = TS_NORMAL;

        if (t->m_wState & (TF_SELECTED | TF_DRAG))
            {
            bmStock = m_bmStuck;
            xshove = 1; yshove = 1;
            iButtonStateId = t->m_wState & TF_HOT ? TS_HOTCHECKED : TS_CHECKED;
            }
        else if (t->m_wState & TF_HOT)
            {
            iButtonStateId = TS_HOT;
            }

        if ((t == m_tCapture && m_bInside) && !(t->m_wState & TF_DRAG))
            {
            bmStock = m_bmPushed;
            xshove = 2; yshove = 2;
            iButtonStateId = TS_PRESSED;
            }

        // Draw a blank button first...

        if (m_hTheme)     
            { 
            DrawThemeBackground(m_hTheme, dc, TP_BUTTON, iButtonStateId, &rect, 0);
            }
            else
            {
            ::DrawBitmap(&dc, bmStock, &rect, SRCCOPY, &memdc);
            }
         
        // Now draw the glyph on top...
        rect.OffsetRect( xshove, yshove );

        if (! bUsedImageWell)
            {
            if (! m_imageWell.Open())
                goto LReturn;

            bUsedImageWell = TRUE;

            if (! m_imageWell.CalculateMask())
                goto LReturn;
            }

        CPoint pt( rect.left + (rect.Width()  - 16) / 2,
                   rect.top  + (rect.Height() - 16) / 2 );

        m_imageWell.DrawImage( &dc, pt, t->m_nImage );
        }

LReturn:
    if (bUsedImageWell)
        m_imageWell.Close();

    memdc.SelectObject(obr);
    memdc.SelectObject(obm);
    memdc.DeleteDC();
    }

/******************************************************************************/
// OnPaint:
//

void CToolboxWnd::OnPaint()
    {
    CPaintDC dc(this);
    if (dc.m_hDC == NULL)
        {
        theApp.SetGdiEmergency();
        return;
        }
    DrawButtons(dc, &dc.m_ps.rcPaint);
    }

/******************************************************************************/
// OnClose
//
// A toolbox is usally created by the parent, and will be destroyed
// specifically by the parent upon leaving the app.  When the user closes
// the toolbox, it is simply hidden.  The parent can then reshow it without
// recreating it.
//
// This also changes the menu test to "show" rather than "hide"

void CToolboxWnd::OnClose()
    {
#ifdef TRYANYTHING
        CControlBar::OnClose();
#endif
//      ShowWindow(SW_HIDE);
    }

/******************************************************************************/
// OnDestroy
//
void CToolboxWnd::OnDestroy()
    {
    if (m_hTheme)
        {
        CloseThemeData(m_hTheme);
        m_hTheme = 0;
        }
    CControlBar::OnDestroy();
    }

/******************************************************************************/
// OnThemeChanged
//
LRESULT CToolboxWnd::OnThemeChanged(WPARAM, LPARAM)
    {
    if (m_hTheme)
        {
        CloseThemeData(m_hTheme);
        }
    m_hTheme = SafeOpenThemeData(GetSafeHwnd(), L"toolbar");
    InvalidateRect(0, TRUE);
    return TRUE;
    }

/******************************************************************************/
// OnToolDown:
//
LONG CToolboxWnd::OnToolDown(UINT wID, LONG /* lParam */)
    {
    for (int i = 0; i < m_Tools->GetSize(); i += 1)
        {
        CTool* pTool = (CTool*)m_Tools->GetAt(i);

        if (pTool->m_wID != wID)
            SetToolState(pTool->m_wID, 0);
        }

    return (LONG)TRUE;
    }

/******************************************************************************/
// OnToolUp:
//
LONG CToolboxWnd::OnToolUp(UINT /* wID */, LONG /* lParam */)
    {
    for (int i = 0; i < m_Tools->GetSize(); i += 1)
        {
        CTool* pTool = (CTool*)m_Tools->GetAt(i);
        SetToolState(pTool->m_wID, 0);
        }
    SetToolState(IDMB_ARROW, TF_SELECTED);

    return (LONG)TRUE;
    }
#ifdef XYZZYZ
/******************************************************************************/
// OnSwitch:
//
LONG CToolboxWnd::OnSwitch(UINT /* wID */, LONG /* point */)
    {
    return (LONG)TRUE;
    }

/******************************************************************************/
// OnQueryDrop:
//
BOOL CToolboxWnd::BeginDragDrop (CTool* /*pTool*/, CPoint /*pt*/)
    {
    return FALSE;
    }
#endif
/******************************************************************************/

CTool::CTool(CToolboxWnd* pOwner, WORD wID, int nImage,
        WORD wStyle /* = 0 */, WORD wState /* = 0 */)
    {
    m_pOwner = pOwner;
    m_wID    = wID;
    m_nImage = nImage;
    m_wStyle = wStyle;
    m_wState = wState;
    }

/******************************************************************************/



