|
|
/////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 1993-1996 Microsoft Corporation. All Rights Reserved.
//
// MODULE: BtnBar.cpp
//
// PURPOSE: Implements a generic button bar.
//
#include "_apipch.h"
#define idtTrack 101
#define idcFolderList 102
#define HOTTRACK_TIMER 100
#define ID_HWNDBAR 2020
extern LPIMAGELIST_LOADIMAGE gpfnImageList_LoadImage;
//#define DEAD
void CBB_ConfigureRects(HWND hwnd); void CBB_DoHotTracking(HWND hwnd); void CBB_EndHotTracking(HWND hwnd); int CBB_HitTest(int x, int y); void CBB_SetSelBtn(int iSel,HWND hwnd);
//
// FUNCTION: CButtonBar::~CButtonBar()
//
// PURPOSE: Cleans up the resources we allocated during the life of the
// object.
//
void CBB_Cleanup(void) { LPPTGDATA lpPTGData=GetThreadStoragePointer();
// Free the GDI resources.
ImageList_Destroy(m_himlButtons); DeleteObject(m_hpalBkgnd); DeleteObject(m_hfButton); DeleteObject(m_hbmpBkgnd);
// Free the button array.
LocalFreeAndNull((LPVOID *)&m_rgButtons);
// NOTE - this is a comment from the original athena source code
//$REVIEW - we can't do this here, because it screws up
// when we have multiple instances of the CButtonBar
// with overlapping creates and destroys. we should
// probably unregister somewhere, but it isn't strictly
// necessary. (EricAn)
// Unregister our window class.
// UnregisterClass(c_szButtonBar, m_hInstance);
return; }
//
// FUNCTION: CButtonBar::Create()
//
// PURPOSE: Initializes the button bar and creates the button bar window.
//
// PARAMETERS:
// hwndParent - Handle of the window that will be the button bar parent.
// idHwnd - Child window ID for the button bar.
// idButtons - ID of the button icons bitmap.
// idHorzBackground - ID of the horizontal background bitmap.
// idVertBackground - ID of the vertical background bitmap.
// pBtnCreateParams - Pointer to the array of BTNCREATEPARAMS used to create the buttons.
// cParams - Number of buttons in pBtnCreateParams.
// uSide - Side of the parent window the bar should initially attach to.
//
// RETURN VALUE:
// Returns TRUE if successful, or FALSE otherwise.
//
HWND CBB_Create(HWND hwndParent, UINT idButtons, UINT idHorzBackground, PBTNCREATEPARAMS pBtnCreateParams, UINT cParams) {
LPPTGDATA lpPTGData=GetThreadStoragePointer();
int i; WNDCLASS wc; BITMAP bm; RECT rc; POINT ptL, ptR; ICONMETRICS im; HWND hwnd = NULL;
wc.style = CS_DBLCLKS; // Bug #15450
wc.lpfnWndProc = CBB_ButtonBarProc; wc.cbClsExtra = 0; wc.cbWndExtra = sizeof(LPVOID); wc.hInstance = hinstMapiX; wc.hIcon = 0; wc.hCursor = 0; wc.hbrBackground = 0; wc.lpszMenuName = 0; wc.lpszClassName = c_szButtonBar;
RegisterClass(&wc);
m_rgButtons = NULL; m_himlButtons = 0; m_hbmpBkgnd = 0; m_hpalBkgnd = 0; m_hfButton = 0; m_cButtons = 0; m_iSelect = -1; m_iOldSelect = -1;
// This is the information we'll need later to to draw the button bar etc.
// Stash it away for now.
m_cButtons = cParams;
m_rgButtons = LocalAlloc(LMEM_ZEROINIT, sizeof(BUTTON) * m_cButtons); if (!m_rgButtons) return FALSE;
for (i = 0; i < m_cButtons; i++) { m_rgButtons[i].id = pBtnCreateParams[i].id; m_rgButtons[i].iIcon = pBtnCreateParams[i].iIcon;
LoadString(hinstMapiX, pBtnCreateParams[i].idsLabel, m_rgButtons[i].szTitle, sizeof(m_rgButtons[i].szTitle)); }
// Load the bitmaps we'll need for drawing.
m_himlButtons = gpfnImageList_LoadImage(hinstMapiX, MAKEINTRESOURCE(idButtons), c_cxButtons, 0, c_crMask, IMAGE_BITMAP, 0); //LR_LOADMAP3DCOLORS);
if (!m_himlButtons) return (FALSE);
// Get the width of the bitmap we're going to use as the background so we
// know how wide to make the window.
if (!LoadBitmapAndPalette(idHorzBackground, &m_hbmpBkgnd, &m_hpalBkgnd)) return (FALSE);
if (!GetObject(m_hbmpBkgnd, sizeof(BITMAP), (LPVOID) &bm)) return (FALSE);
GetClientRect(hwndParent, &rc);
// Get the font we're going to use for the buttons
im.cbSize = sizeof(ICONMETRICS); SystemParametersInfo(SPI_GETICONMETRICS, 0, (LPVOID) &im, 0); m_hfButton = CreateFontIndirect(&(im.lfFont)); if (!m_hfButton) return (FALSE);
ptL.x = ptL.y=0; ptR.x = rc.right; ptR.y = bm.bmHeight;
hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, c_szButtonBar, c_szButtonBar, WS_CLIPSIBLINGS | WS_VISIBLE | WS_CHILD, ptL.x, ptL.y, ptR.x, ptR.y, hwndParent, (HMENU) ID_HWNDBAR, hinstMapiX, NULL);
CBB_ConfigureRects(hwnd);
return (hwnd); }
//
// FUNCTION: CButtonBar::ButtonBarProc()
//
// PURPOSE: Message handler for the button bar window.
//
LRESULT CALLBACK CBB_ButtonBarProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) { case WM_NCCREATE: SetWindowLong(hwnd, 0, (LONG) ((LPCREATESTRUCT) lParam)->lpCreateParams); return (TRUE); /***
case WM_CREATE: return 0; break;
case WM_SIZE: return 0; break;
case WM_LBUTTONDOWN: return 0; break;
case WM_COMMAND: return 0; break;
/***/ case WM_PAINT: CBB_OnPaint(hwnd); return 0; break;
case WM_MOUSEMOVE: CBB_OnMouseMove(hwnd, LOWORD(lParam), HIWORD(lParam), wParam); return 0; break;
case WM_LBUTTONUP: CBB_OnLButtonUp(hwnd, LOWORD(lParam), HIWORD(lParam), wParam); return 0; break;
case WM_TIMER: CBB_OnTimer(hwnd, wParam); return 0; break;
case WM_MOUSEACTIVATE: CBB_OnMouseActivate(hwnd, (HWND) wParam, (INT) LOWORD(lParam), (UINT) HIWORD(lParam)); return 0; break;
case WM_PALETTECHANGED: if ((HWND) wParam != hwnd) { LPPTGDATA lpPTGData=GetThreadStoragePointer(); HDC hdc = GetDC(hwnd); HPALETTE hPalOld = SelectPalette(hdc, m_hpalBkgnd, TRUE); RealizePalette(hdc); InvalidateRect(hwnd, NULL, TRUE); SelectPalette(hdc, hPalOld, TRUE); ReleaseDC(hwnd, hdc); } return 0; }
return (DefWindowProc(hwnd, uMsg, wParam, lParam)); }
void CBB_OnPaint(HWND hwnd) { LPPTGDATA lpPTGData=GetThreadStoragePointer();
HDC hdc; PAINTSTRUCT ps; BITMAP bm; HDC hdcMem; HBITMAP hbmMemOld, hbmMem; HPALETTE hpalOld; RECT rc; HFONT hf; COLORREF clrText, clrBk;
int cxIndent = 3; int cyIndent = 3; int nTop = 0; int nLeft = 0; int nButton = 0; int i=0;
if(!hwnd) goto out;
// Get the size of the background bitmap
GetObject(m_hbmpBkgnd, sizeof(BITMAP), (LPVOID) &bm); GetClientRect(hwnd, &rc);
hdc = BeginPaint(hwnd, &ps); hdcMem = CreateCompatibleDC(hdc);
// If we are displaying the buttons ...
{ // Draw the background bitmaps first.
hpalOld = SelectPalette(hdc, m_hpalBkgnd, TRUE); RealizePalette(hdc);
hbmMemOld = (HBITMAP) SelectObject(hdcMem, (HGDIOBJ) m_hbmpBkgnd);
// If the window is taller or wider than a single bitmap, we may have
// to loop and put a couple out there.
while (nLeft < rc.right) { BitBlt(hdc, nLeft, nTop, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY); nLeft += bm.bmWidth; }
// Now draw the buttons
nTop = 0;
hf = (HFONT) SelectObject(hdc, m_hfButton); SetBkMode(hdc, TRANSPARENT);
while (nButton < m_cButtons) { if (RectVisible(hdc, &(m_rgButtons[nButton].rcBound))) { ImageList_Draw(m_himlButtons, m_rgButtons[nButton].iIcon, hdc, m_rgButtons[nButton].rcIcon.left, m_rgButtons[nButton].rcIcon.top, ILD_TRANSPARENT);
// Draw the title of the button that the mouse is over with a
// different color.
if (nButton == m_iSelect) { clrBk = SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT)); clrText = SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT)); } else { SetBkColor(hdc, GetSysColor(COLOR_WINDOW)); SetTextColor(hdc, GetSysColor(COLOR_WINDOWTEXT)); }
SetTextAlign(hdc, TA_TOP /* | TA_CENTER */);
if (nButton == m_iSelect) { clrText = SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHT)); ExtTextOut( hdc, (m_rgButtons[nButton].rcTitle.right - m_rgButtons[nButton].rcTitle.left) / 2 + m_rgButtons[nButton].rcTitle.left, m_rgButtons[nButton].rcTitle.top, ETO_OPAQUE | ETO_CLIPPED, &(m_rgButtons[nButton].rcTitle), m_rgButtons[nButton].szTitle, lstrlen(m_rgButtons[nButton].szTitle), NULL); clrText = SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT)); DrawText(hdc, m_rgButtons[nButton].szTitle, lstrlen(m_rgButtons[nButton].szTitle), &m_rgButtons[nButton].rcTitle, DT_CENTER | DT_WORDBREAK); } else { DrawText(hdc, m_rgButtons[nButton].szTitle, lstrlen(m_rgButtons[nButton].szTitle), &m_rgButtons[nButton].rcTitle, DT_CENTER | DT_WORDBREAK); }
if (nButton == m_iSelect) { SetBkColor(hdc,clrBk); SetTextColor(hdc, clrText); } } nButton++; }
SelectObject(hdc, m_hfButton);
if (hpalOld != NULL) SelectPalette(hdc, hpalOld, TRUE);
SelectObject(hdcMem, hbmMemOld); }
DeleteObject(hbmMem); DeleteDC(hdcMem); EndPaint(hwnd, &ps);
out: return; }
//
// FUNCTION: CButtonBar::OnLButtonUp()
//
// PURPOSE: If we are dragging the button bar around, then the user has
// released the bar and we can clean up. If the user wasn't
// dragging, then they have clicked on a button and we send the
// appropriate command message to the parent window.
//
void CBB_OnLButtonUp(HWND hwnd, int x, int y, UINT keyFlags) { LPPTGDATA lpPTGData=GetThreadStoragePointer(); int iSel = 0;
if (-1 != (iSel = CBB_HitTest(x, y))) { // Move command handling from LButtonUp to LButtonDown to avoid
// duplicate messages being sent from double clicks - Nash Bug #15450
if (0 <= iSel) { SendMessage(GetParent(hwnd), WM_COMMAND, m_rgButtons[iSel].id, (LPARAM) hwnd); CBB_SetSelBtn(-1,hwnd); } }
return; }
//
// FUNCTION: CButtonBar::OnMouseMove()
//
// PURPOSE: If the user is dragging the bar around, we need to determine
// which side of the parent window the mouse is closest to and
// move the button bar to that edge.
//
// If the user is not dragging, then we need to decide if the
// mouse is over a button and if so highlight the text.
//
void CBB_OnMouseMove(HWND hwnd, int x, int y, UINT keyFlags) { POINT pt = {x, y}; int iSel; POINT ptScreen = {x, y};
// If we're not dragging the bar around, the just update the button
// selection.
iSel = CBB_HitTest(x, y); CBB_SetSelBtn(iSel,hwnd); if (iSel != -1) SetCursor(LoadCursor(hinstMapiX, MAKEINTRESOURCE(idcurPointedHand))); else SetCursor(LoadCursor(NULL, IDC_ARROW)); CBB_DoHotTracking(hwnd); return; }
int CBB_OnMouseActivate(HWND hwnd, HWND hwndTopLevel, UINT codeHitTest, UINT msg) { return (MA_ACTIVATE); }
//
// FUNCTION: CButtonBar::ConfigureRects()
//
// PURPOSE: Calculates the rectangles that are necessary for displaying
// the button bar based on the side of the parent window the
// bar is currently attached to.
//
void CBB_ConfigureRects(HWND hwnd) { LPPTGDATA lpPTGData=GetThreadStoragePointer();
// Need to gather some font information first. We need the height of the
// folder title font all the time and we need the width of the longest
// button title if we're displayed horizontally.
HDC hdc; int i; int cxMaxTitle; SIZE sizeString; SIZE sizeRect; int cyIconTitle; int cxCenter; int cyCenter; TEXTMETRIC tmTitle;
hdc = GetDC(hwnd);
SelectObject(hdc, m_hfButton); GetTextMetrics(hdc, &tmTitle);
// Button text width
cxMaxTitle = 0; for (i = 0; i < m_cButtons; i++) { GetTextExtentPoint32(hdc, m_rgButtons[i].szTitle, lstrlen(m_rgButtons[i].szTitle), &sizeString); if (sizeString.cx > cxMaxTitle) cxMaxTitle = sizeString.cx; }
// Add a little buffer here just to make it look nice.
cxMaxTitle += 10;
ReleaseDC(hwnd, hdc);
// Now calculate the button rectangles. Each button will have three rects
// associated with it. The first rectangle is the overall bounding rect
// which contains the image and title. The next rectangle is the rect for
// the image which is centered horizontally within the bounding rect and
// vertically when combined with the title. The final rect is the title.
// Calculate the initial bounding rectangle based on whether or not we're
// horizontal or vertical. sizeRect is the dimensions of each button's
// bounding rectangle.
{ RECT rcBound,rcWnd; int cyButton=0,cxButton=0;
ImageList_GetIconSize(m_himlButtons, &cxButton, &cyButton);
GetClientRect(hwnd,&rcWnd); sizeRect.cx = cxMaxTitle; sizeRect.cy = rcWnd.bottom - rcWnd.top; SetRect(&rcBound, 0, 0, sizeRect.cx, sizeRect.cy);
// Also calculate the offsets needed to center the image and text within
// the bound.
cyIconTitle = tmTitle.tmHeight + cyButton; cxCenter = ((rcBound.right - rcBound.left) - cxButton) / 2; cyCenter = ((rcBound.bottom - rcBound.top) - cyIconTitle) / 2;
// Now loop through all the buttons
for (i = 0; i < m_cButtons; i++) { m_rgButtons[i].rcBound = rcBound;
// Center the image horizontally within the bounding rect.
m_rgButtons[i].rcIcon.left = m_rgButtons[i].rcBound.left + cxCenter; m_rgButtons[i].rcIcon.top = m_rgButtons[i].rcBound.top + cyCenter; m_rgButtons[i].rcIcon.right = m_rgButtons[i].rcIcon.left + cxButton; m_rgButtons[i].rcIcon.bottom = m_rgButtons[i].rcIcon.top + cyButton;
// And the button title below the image
m_rgButtons[i].rcTitle.left = m_rgButtons[i].rcBound.left + 1; m_rgButtons[i].rcTitle.top = m_rgButtons[i].rcIcon.bottom; m_rgButtons[i].rcTitle.right = m_rgButtons[i].rcBound.right - 1; m_rgButtons[i].rcTitle.bottom = m_rgButtons[i].rcTitle.top + (tmTitle.tmHeight);// * 2);
// Offset the rcBound to the next button.
OffsetRect(&rcBound, sizeRect.cx, 0); } } }
//
// FUNCTION: CButtonBar::OnTimer()
//
// PURPOSE: When the timer fires we check to see if the mouse is still
// over the button bar window. If not we remove the selection
// from the active button.
//
void CBB_OnTimer(HWND hwnd, UINT id) { POINT pt; GetCursorPos(&pt); if (hwnd != WindowFromPoint(pt)) { CBB_SetSelBtn(-1,hwnd); }
CBB_EndHotTracking(hwnd); }
//
// FUNCTION: CButtonBar::DoHotTracking()
//
// PURPOSE: Starts a timer that allows the button bar to track the mouse
// in case it leaves the button bar window.
//
void CBB_DoHotTracking(HWND hwnd) { LPPTGDATA lpPTGData=GetThreadStoragePointer(); CBB_EndHotTracking(hwnd); m_fHotTrackTimer = SetTimer(hwnd, idtTrack, HOTTRACK_TIMER, NULL); }
//
// FUNCTION: CButtonBar::EndHotTracking()
//
// PURPOSE: If the timer was set to track the mouse, we kill it and reset
// our state.
//
void CBB_EndHotTracking(HWND hwnd) { LPPTGDATA lpPTGData=GetThreadStoragePointer(); if (m_fHotTrackTimer) { KillTimer(hwnd, idtTrack); m_fHotTrackTimer = FALSE; } }
//
// FUNCTION: CButtonBar::HitTest()
//
// PURPOSE: Returns the button number that the passed in position is
// over. If the mouse is over the menu button, it returns
// -2. Otherwise, if the mouse is not over a button the function
// returns -1.
//
// PARAMETERS:
// x, y - Position in client coordinates to check.
//
int CBB_HitTest(int x, int y) { LPPTGDATA lpPTGData=GetThreadStoragePointer();
POINT pt = {x, y}; int i;
// Walk through the different buttons and determine if the point is
// within either their image or title.
for (i = 0; i < m_cButtons; i++) { if (PtInRect(&m_rgButtons[i].rcBound, pt)) /* PtInRect(&m_rgButtons[i].rcIcon, pt) ||
PtInRect(&m_rgButtons[i].rcTitle, pt)) */ { return (i); } }
// If we're not over a button then return a default value.
return (-1); }
//
// FUNCTION: CButtonBar::CBB_SetSelBtn()
//
// PURPOSE: Changes the button selection to the specified button.
//
void CBB_SetSelBtn(int iSel,HWND hwnd) { LPPTGDATA lpPTGData=GetThreadStoragePointer();
if (m_iSelect != iSel) { HDC hdc = GetDC(hwnd);
// Remove the old selection
if (m_iSelect >= 0) InvalidateRect(hwnd, &m_rgButtons[m_iSelect].rcTitle, FALSE);
// Add the new selection
if (iSel >= 0) InvalidateRect(hwnd, &m_rgButtons[iSel].rcTitle, FALSE);
m_iOldSelect = m_iSelect;
// if (m_iOldSelect >= 0)
// DrawFocusRect(hdc, &m_rgButtons[m_iOldSelect].rcBound);
m_iSelect = iSel;
// if (m_iSelect >= 0)
// DrawFocusRect(hdc, &m_rgButtons[m_iSelect].rcBound);
ReleaseDC(hwnd, hdc); }
return; }
|