/////////////////////////////////////////////////////////////////////////////////////////////////////////// // // MBUTTON.CPP // // Multimedia Button Control class; helper functions // // Copyright (c) Microsoft Corporation 1997 // // 12/14/97 David Stewart / dstewart // /////////////////////////////////////////////////////////////////////////////////////////////////////////// #include "mbutton.h" #include //for GetWindowFont #include //for TrackMouseEvent #include //for WM_MOUSELEAVE #include #include "resource.h" #include "dib.h" #include "mmfw.h" //file-local default values for buttons #define NUM_STATES 3 #define STATE_UP 0 #define STATE_DN 1 #define STATE_HI 2 #define BOFF_STANDARDLEFT 0 #define BOFF_TOGGLELEFT 1 #define BOFF_STANDARDRIGHT 2 #define BOFF_DROPRIGHT 3 #define BOFF_TOGGLERIGHT 4 #define BOFF_MIDDLE 5 #define BOFF_SYSTEM 6 #define BOFF_MINIMIZE 7 #define BOFF_RESTORE 8 #define BOFF_MAXIMIZE 9 #define BOFF_CLOSE 10 #define BOFF_MUTE 11 #define BOFF_END 12 #define BUTTON_BITMAP_HEIGHT 19 #define BUTTON_FONT_SIZE 8 #define BUTTON_DBCS_FONT_SIZE 9 #define BUTTON_FONT_WEIGHT FW_BOLD #define MBUTTON_TEXT_COLOR RGB(0xFF,0xFF,0xFF) HFONT hFont = NULL; HANDLE hbmpButtonToolkit = NULL; int nStateOffset = 0; int nButtonOffsets[BOFF_END+1]; extern HPALETTE hpalMain; extern int g_nColorMode; CMButton* pButtonFocus = NULL; CMButton* pButtonMouse = NULL; BOOL fAllowFocus = TRUE; //for this to work on a Win95 machine, we need to make TrackMouseEvent //a dynamically loaded thing ... but then you get NO HOVER-OVER EFFECT typedef BOOL (PASCAL *TRACKPROC)(LPTRACKMOUSEEVENT); TRACKPROC procTrackMouseEvent = NULL; BOOL InitMButtons(HINSTANCE hInst, HWND hwnd) { BOOL fReturn = TRUE; //if TrackMouseEvent exists, use it HMODULE hUser = GetModuleHandle(TEXT("USER32")); if (hUser) { procTrackMouseEvent = (TRACKPROC)GetProcAddress(hUser,"TrackMouseEvent"); } //create font, named store in IDS_MBUTTON_FONT in string table LOGFONT lf; ZeroMemory( &lf, sizeof(lf) ); HFONT hTempFont = GetWindowFont( hwnd ); if (hTempFont == NULL) { hTempFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT); } GetObject(hTempFont,sizeof(lf),&lf); lf.lfHeight = (-BUTTON_FONT_SIZE * STANDARD_PIXELS_PER_INCH) / 72; if (lf.lfCharSet == ANSI_CHARSET) { lf.lfWeight = BUTTON_FONT_WEIGHT; } else if (IS_DBCS_CHARSET(lf.lfCharSet)) { lf.lfHeight = (-BUTTON_DBCS_FONT_SIZE * STANDARD_PIXELS_PER_INCH) / 72; } lf.lfOutPrecision = OUT_DEFAULT_PRECIS; lf.lfClipPrecision = CLIP_DEFAULT_PRECIS; lf.lfQuality = PROOF_QUALITY; lf.lfPitchAndFamily = DEFAULT_PITCH | FF_SWISS; LoadString(hInst,IDS_MBUTTON_FONT,lf.lfFaceName,LF_FACESIZE-1); hFont = CreateFontIndirect(&lf); //create bitmap, has all states for all buttons int nBitmap = IDB_BUTTON_TOOLKIT; switch (g_nColorMode) { case COLOR_16 : nBitmap = IDB_BUTTON_TOOLKIT_16; break; case COLOR_HICONTRAST : nBitmap = IDB_BUTTON_TOOLKIT_HI; break; } HBITMAP hbmpTemp = (HBITMAP)LoadImage(hInst,MAKEINTRESOURCE(nBitmap),IMAGE_BITMAP,0,0,LR_CREATEDIBSECTION); hbmpButtonToolkit = DibFromBitmap((HBITMAP)hbmpTemp,0,0,NULL,0); BITMAP bm; GetObject(hbmpTemp,sizeof(bm),&bm); DeleteObject(hbmpTemp); nStateOffset = bm.bmWidth / NUM_STATES; //offsets within bitmap nButtonOffsets[BOFF_STANDARDLEFT] = 0; nButtonOffsets[BOFF_TOGGLELEFT] = 9; nButtonOffsets[BOFF_STANDARDRIGHT] = 11; nButtonOffsets[BOFF_DROPRIGHT] = 20; nButtonOffsets[BOFF_TOGGLERIGHT] = 42; nButtonOffsets[BOFF_MIDDLE] = 44; nButtonOffsets[BOFF_SYSTEM] = 52; nButtonOffsets[BOFF_MINIMIZE] = 64; nButtonOffsets[BOFF_RESTORE] = 78; nButtonOffsets[BOFF_MAXIMIZE] = 92; nButtonOffsets[BOFF_CLOSE] = 106; nButtonOffsets[BOFF_MUTE] = 121; nButtonOffsets[BOFF_END] = nStateOffset; //SetDibUsage(hbmpButtonToolkit,hpalMain,DIB_RGB_COLORS); return (fReturn); } void UninitMButtons() { GlobalFree(hbmpButtonToolkit); DeleteObject(hFont); } //Given a parent window and a control ID, return the CMButton object CMButton* GetMButtonFromID(HWND hwndParent, int nID) { HWND hwnd = GetDlgItem(hwndParent, nID); return (GetMButtonFromHWND(hwnd)); } //Given the window handle of the button, return the CMButton object CMButton* GetMButtonFromHWND(HWND hwnd) { CMButton* pButton = (CMButton*)GetWindowLongPtr(hwnd, GWLP_USERDATA); return (pButton); } CMButton* CreateMButton(TCHAR* szCaption, int nIconID, DWORD dwWindowStyle, DWORD dwMButtonStyle, int x, int y, int width, int height, HWND hwndParentOrSub, BOOL fSubExisting, int nID, int nToolTipID, HINSTANCE hInst) { CMButton* pButton = new CMButton; //ensure the button is a child, pushbutton, owner-draw //caller should specify WS_VISIBLE|WS_TABSTOP if desired dwWindowStyle = dwWindowStyle|WS_CHILD|BS_PUSHBUTTON|BS_OWNERDRAW; HWND hwnd; if (fSubExisting) { //user already created button, is probably calling from WM_INITDIALOG hwnd = hwndParentOrSub; } else { //need to create the button ourselves hwnd = CreateWindow(TEXT("BUTTON"), szCaption, dwWindowStyle, x, y, width, height, hwndParentOrSub, (HMENU)IntToPtr(nID), hInst, NULL); } if (hwnd == NULL) { //if we can't create the window, nuke it and fail delete pButton; return NULL; } pButton->m_hInst = hInst; pButton->m_fRedraw = FALSE; pButton->m_dwStyle = dwMButtonStyle; pButton->m_hwnd = hwnd; pButton->SetIcon(nIconID); pButton->SetFont(hFont); pButton->m_fRedraw = TRUE; pButton->m_nID = nID; pButton->SetToolTipID(nToolTipID); pButton->PreDrawUpstate(width,height); //subclass the button; allows tracking of mouse events pButton->m_fnOldButton = (WNDPROC)SetWindowLongPtr(hwnd,GWLP_WNDPROC,(LONG_PTR)CMButton::ButtonProc); //put the button's pointer into the window's user bytes SetWindowLongPtr(hwnd,GWLP_USERDATA,(LONG_PTR)pButton); return (pButton); } void CMButton::PreDrawUpstate(int width, int height) { //pre-draw the up states of the buttons for a faster-seeming first blit if (m_hbmpUp) DeleteObject(m_hbmpUp); if (m_hbmpDn) DeleteObject(m_hbmpDn); if (m_hbmpHi) DeleteObject(m_hbmpHi); m_hbmpUp = NULL; m_hbmpDn = NULL; m_hbmpHi = NULL; DRAWITEMSTRUCT drawItem; drawItem.rcItem.left = 0; drawItem.rcItem.top = 0; drawItem.rcItem.right = width; drawItem.rcItem.bottom = height; drawItem.itemState = 0; drawItem.hDC = GetDC(m_hwnd); HPALETTE hpalOld = SelectPalette(drawItem.hDC,hpalMain,FALSE); RealizePalette(drawItem.hDC); DrawButtonBitmap(&drawItem,FALSE,NULL); SelectPalette(drawItem.hDC,hpalOld,TRUE); RealizePalette(drawItem.hDC); ReleaseDC(m_hwnd,drawItem.hDC); } LRESULT CALLBACK CMButton::ButtonProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam) { CMButton* pButton = (CMButton*)GetWindowLongPtr(hwnd, GWLP_USERDATA); if (pButton == NULL) { return (0); } switch (iMsg) { case WM_MOUSEMOVE : { if (!pButton->m_fMouseInButton) { if (procTrackMouseEvent) { //only do this if the trackmouseevent function exists, //otherwise, the button color will never turn off pButton->m_fMouseInButton = TRUE; TRACKMOUSEEVENT tme; tme.cbSize = sizeof(tme); tme.dwFlags = TME_LEAVE; tme.dwHoverTime = HOVER_DEFAULT; tme.hwndTrack = hwnd; procTrackMouseEvent(&tme); InvalidateRect(hwnd,NULL,FALSE); } //end proctrackmouseevent is valid } } break; case WM_KEYUP : { //for the close, min, and view buttons, we want to shunt //any keyboard movement to the knob ... although tempting, //don't do this on the setfocus, or the mouse won't work right if ( (pButton->m_nID == IDB_CLOSE) || (pButton->m_nID == IDB_MINIMIZE) || (pButton->m_nID == IDB_SET_TINY_MODE) || (pButton->m_nID == IDB_SET_NORMAL_MODE) ) { HWND hwndFocus = GetDlgItem(GetParent(hwnd),IDB_VOLUME); if (IsWindowVisible(hwndFocus)) { SetFocus(hwndFocus); } else { hwndFocus = GetDlgItem(GetParent(hwnd),IDB_OPTIONS); SetFocus(hwndFocus); } } //end if } break; case WM_SETFOCUS : { SendMessage(GetParent(pButton->m_hwnd),DM_SETDEFID,pButton->m_nID,0); } break; case WM_MOUSELEAVE : { pButton->m_fMouseInButton = FALSE; InvalidateRect(hwnd,NULL,FALSE); } break; case WM_ERASEBKGND : { DRAWITEMSTRUCT drawItem; drawItem.hDC = (HDC)wParam; GetClientRect(hwnd,&(drawItem.rcItem)); drawItem.itemState = pButton->m_LastState; pButton->Draw(&drawItem); return TRUE; } break; } //end switch LRESULT lResult = CallWindowProc((WNDPROC)pButton->m_fnOldButton,hwnd,iMsg,wParam,lParam); if ((iMsg == WM_DESTROY) && ((pButton->m_dwStyle & MBS_NOAUTODELETE) == 0)) { //auto-delete the button class SetWindowLongPtr(hwnd,GWLP_USERDATA,0); delete pButton; pButton = NULL; } return (lResult); } CMButton::CMButton() { //init all data values m_fMouseInButton = FALSE; m_dwStyle = MBS_STANDARDLEFT | MBS_STANDARDRIGHT; m_hFont = hFont; m_nID = 0; m_IconID = 0; m_hwnd = NULL; m_hbmpUp = NULL; m_hbmpDn = NULL; m_hbmpHi = NULL; m_fRedraw = FALSE; m_fMenu = FALSE; m_fMenuingOff = FALSE; m_LastState = 0; } CMButton::~CMButton() { if (m_hbmpUp) DeleteObject(m_hbmpUp); if (m_hbmpDn) DeleteObject(m_hbmpDn); if (m_hbmpHi) DeleteObject(m_hbmpHi); } void CMButton::SetText(TCHAR* szCaption) { SetWindowText(m_hwnd,szCaption); if (m_fRedraw) { InvalidateRect(m_hwnd,NULL,FALSE); } } void CMButton::SetIcon(int nIconID) { //assuming caller is responsible for cleaning up m_IconID = nIconID; if (m_fRedraw) { InvalidateRect(m_hwnd,NULL,FALSE); } } void CMButton::SetFont(HFONT hFont) { //assume caller is responsible for cleaning up m_hFont = hFont; } void CMButton::DrawButtonBitmap(LPDRAWITEMSTRUCT lpdis, BOOL fDrawToScreen, RECT* pMidRect) { HANDLE hTemp = m_hbmpUp; int nState = STATE_UP; if (m_fMouseInButton) { pButtonMouse = this; hTemp = m_hbmpHi; nState = STATE_HI; if (pButtonFocus!=NULL) { fAllowFocus = FALSE; if (this != pButtonFocus) { InvalidateRect(pButtonFocus->GetHWND(),NULL,FALSE); UpdateWindow(pButtonFocus->GetHWND()); } } } else { if (lpdis->itemState & ODS_FOCUS) { if (this == pButtonFocus) { if (fAllowFocus) { hTemp = m_hbmpHi; nState = STATE_HI; } else { hTemp = m_hbmpUp; nState = STATE_UP; } } else { pButtonFocus = this; hTemp = m_hbmpHi; nState = STATE_HI; fAllowFocus = TRUE; if ((pButtonMouse!=NULL) && (pButtonMouse!=this)) { pButtonMouse->m_fMouseInButton = FALSE; InvalidateRect(pButtonMouse->GetHWND(),NULL,FALSE); UpdateWindow(pButtonMouse->GetHWND()); pButtonMouse = NULL; } //end if removing mouse highlight } } } //end if mouse in or out of button if ((lpdis->itemState & ODS_SELECTED) || (m_fMenu)) { hTemp = m_hbmpDn; nState = STATE_DN; } int offLeft, widLeft; int offRight, widRight; int offMid, widMid; offLeft = nButtonOffsets[BOFF_STANDARDLEFT] + (nState * nStateOffset); widLeft = nButtonOffsets[BOFF_STANDARDLEFT+1] - nButtonOffsets[BOFF_STANDARDLEFT]; if ((m_dwStyle & MBS_TOGGLELEFT) == MBS_TOGGLELEFT) { offLeft = nButtonOffsets[BOFF_TOGGLELEFT] + (nState * nStateOffset); widLeft = nButtonOffsets[BOFF_TOGGLELEFT+1] - nButtonOffsets[BOFF_TOGGLELEFT]; } if ((m_dwStyle & MBS_SYSTEMTYPE) == MBS_SYSTEMTYPE) { switch (m_IconID) { case IDB_CLOSE : { offLeft = nButtonOffsets[BOFF_CLOSE] + (nState * nStateOffset); widLeft = nButtonOffsets[BOFF_CLOSE+1] - nButtonOffsets[BOFF_CLOSE]; } break; case IDB_MINIMIZE : { offLeft = nButtonOffsets[BOFF_MINIMIZE] + (nState * nStateOffset); widLeft = nButtonOffsets[BOFF_MINIMIZE+1] - nButtonOffsets[BOFF_MINIMIZE]; } break; case IDB_SET_TINY_MODE : { offLeft = nButtonOffsets[BOFF_RESTORE] + (nState * nStateOffset); widLeft = nButtonOffsets[BOFF_RESTORE+1] - nButtonOffsets[BOFF_RESTORE]; } break; case IDB_SET_NORMAL_MODE : { offLeft = nButtonOffsets[BOFF_MAXIMIZE] + (nState * nStateOffset); widLeft = nButtonOffsets[BOFF_MAXIMIZE+1] - nButtonOffsets[BOFF_MAXIMIZE]; } break; case IDB_MUTE : { offLeft = nButtonOffsets[BOFF_MUTE] + (nState * nStateOffset); widLeft = nButtonOffsets[BOFF_MUTE+1] - nButtonOffsets[BOFF_MUTE]; } } } offRight = nButtonOffsets[BOFF_STANDARDRIGHT] + (nState * nStateOffset); widRight = nButtonOffsets[BOFF_STANDARDRIGHT+1] - nButtonOffsets[BOFF_STANDARDRIGHT]; if ((m_dwStyle & MBS_TOGGLERIGHT) == MBS_TOGGLERIGHT) { offRight = nButtonOffsets[BOFF_TOGGLERIGHT] + (nState * nStateOffset); widRight = nButtonOffsets[BOFF_TOGGLERIGHT+1] - nButtonOffsets[BOFF_TOGGLERIGHT]; } if ((m_dwStyle & MBS_DROPRIGHT) == MBS_DROPRIGHT) { offRight = nButtonOffsets[BOFF_DROPRIGHT] + (nState * nStateOffset); widRight = nButtonOffsets[BOFF_DROPRIGHT+1] - nButtonOffsets[BOFF_DROPRIGHT]; } offMid = nButtonOffsets[BOFF_MIDDLE] + (nState * nStateOffset); widMid = (lpdis->rcItem.right - lpdis->rcItem.left) - widLeft - widRight; if (pMidRect) { if (m_dwStyle & MBS_DROPRIGHT) { //set rect to just the middle of the button part SetRect(pMidRect, lpdis->rcItem.left, lpdis->rcItem.top, lpdis->rcItem.left + widLeft + widMid, lpdis->rcItem.top + BUTTON_BITMAP_HEIGHT); } else { //set rect to whole button SetRect(pMidRect, lpdis->rcItem.left, lpdis->rcItem.top, lpdis->rcItem.right, lpdis->rcItem.bottom); } } if (hTemp == NULL) { //draw and save bumps HDC memDC = CreateCompatibleDC(lpdis->hDC); HPALETTE hpalOld = SelectPalette(memDC, hpalMain, FALSE); HBITMAP holdbmp; switch (nState) { case STATE_UP : { m_hbmpUp = CreateCompatibleBitmap(lpdis->hDC, lpdis->rcItem.right - lpdis->rcItem.left, BUTTON_BITMAP_HEIGHT); holdbmp = (HBITMAP)SelectObject(memDC,m_hbmpUp); } break; case STATE_DN : { m_hbmpDn = CreateCompatibleBitmap(lpdis->hDC, lpdis->rcItem.right - lpdis->rcItem.left, BUTTON_BITMAP_HEIGHT); holdbmp = (HBITMAP)SelectObject(memDC,m_hbmpDn); } break; case STATE_HI : { m_hbmpHi = CreateCompatibleBitmap(lpdis->hDC, lpdis->rcItem.right - lpdis->rcItem.left, BUTTON_BITMAP_HEIGHT); holdbmp = (HBITMAP)SelectObject(memDC,m_hbmpHi); } break; } //draw left DibBlt(memDC, lpdis->rcItem.left, lpdis->rcItem.top, -1, -1, hbmpButtonToolkit, offLeft,0, SRCCOPY,0); if ((m_dwStyle & MBS_SYSTEMTYPE) != MBS_SYSTEMTYPE) { //draw middle StretchDibBlt(memDC, lpdis->rcItem.left + widLeft, lpdis->rcItem.top, widMid, BUTTON_BITMAP_HEIGHT, hbmpButtonToolkit, offMid,0, nButtonOffsets[BOFF_MIDDLE+1] - nButtonOffsets[BOFF_MIDDLE], BUTTON_BITMAP_HEIGHT, SRCCOPY,0); //draw right DibBlt(memDC, lpdis->rcItem.right - widRight, lpdis->rcItem.top, -1, -1, hbmpButtonToolkit, offRight,0, SRCCOPY,0); } SelectObject(memDC,holdbmp); SelectPalette(memDC, hpalOld, TRUE); DeleteDC(memDC); } //end not already drawn if (fDrawToScreen) { //should have bumps ready to go now hTemp = m_hbmpUp; if (m_fMouseInButton) { hTemp = m_hbmpHi; } if (lpdis->itemState & ODS_FOCUS) { if (fAllowFocus) { hTemp = m_hbmpHi; } } if ((lpdis->itemState & ODS_SELECTED) || (m_fMenu)) { hTemp = m_hbmpDn; } if (!(lpdis->itemState & ODS_SELECTED) && (m_fMenuingOff)) { m_fMenu = FALSE; m_fMenuingOff = FALSE; hTemp = m_hbmpUp; } HDC memDC = CreateCompatibleDC(lpdis->hDC); HBITMAP holdbmp = (HBITMAP)SelectObject(memDC,hTemp); BitBlt(lpdis->hDC, lpdis->rcItem.left, lpdis->rcItem.top, lpdis->rcItem.right - lpdis->rcItem.left, lpdis->rcItem.bottom - lpdis->rcItem.top, memDC, 0,0, SRCCOPY); SelectObject(memDC,holdbmp); DeleteDC(memDC); } } void CMButton::Draw(LPDRAWITEMSTRUCT lpdis) { HPALETTE hpalOld = SelectPalette(lpdis->hDC,hpalMain,FALSE); RealizePalette(lpdis->hDC); SetTextColor(lpdis->hDC, MBUTTON_TEXT_COLOR); m_LastState = lpdis->itemState; if (lpdis->itemState & ODS_DISABLED) { SetTextColor(lpdis->hDC, GetSysColor(COLOR_GRAYTEXT)); } RECT midRect; DrawButtonBitmap(lpdis,TRUE,&midRect); if ((m_dwStyle & MBS_SYSTEMTYPE) == MBS_SYSTEMTYPE) { SelectPalette(lpdis->hDC,hpalOld,TRUE); RealizePalette(lpdis->hDC); return; } SetBkMode(lpdis->hDC,TRANSPARENT); if (m_IconID == 0) { HFONT hOldFont = (HFONT)SelectObject(lpdis->hDC,m_hFont); TCHAR szCaption[MAX_PATH]; GetWindowText(m_hwnd,szCaption,sizeof(szCaption)/sizeof(TCHAR)); SIZE size; GetTextExtentPoint32( lpdis->hDC, szCaption, _tcslen(szCaption), &size ); //center text int x = (((midRect.right - midRect.left) - size.cx) / 2) + midRect.left; int y = (((lpdis->rcItem.bottom - lpdis->rcItem.top) - size.cy) / 2) + lpdis->rcItem.top; //simulate a press if ((lpdis->itemState & ODS_SELECTED) || (m_fMenu)) { x++; y++; } ExtTextOut( lpdis->hDC, x, y, 0, NULL, szCaption, _tcslen(szCaption), NULL ); SelectObject(lpdis->hDC,hOldFont); } //end if no icon else { //center icon int x = (((midRect.right - midRect.left) - 32) / 2) + midRect.left; int y = (((lpdis->rcItem.bottom - lpdis->rcItem.top) - 32) / 2) + lpdis->rcItem.top; //simulate a press if ((lpdis->itemState & ODS_SELECTED) || (m_fMenu)) { x++; y++; } HICON hIcon = (HICON)LoadImage(m_hInst, MAKEINTRESOURCE(m_IconID), IMAGE_ICON, 32, 32, LR_MONOCHROME); DrawIcon(lpdis->hDC,x,y,hIcon); DestroyIcon(hIcon); } SelectPalette(lpdis->hDC,hpalOld,TRUE); RealizePalette(lpdis->hDC); } void CMButton::SetMenuingState(BOOL fMenuOn) { if (fMenuOn) { m_fMenu = fMenuOn; m_fMenuingOff = FALSE; } else { m_fMenuingOff = TRUE; InvalidateRect(m_hwnd,NULL,FALSE); UpdateWindow(m_hwnd); } } void CMButton::SetToolTipID(int nID) { m_nToolTipID = nID; //for buttons with icons, set the window text to equal the tooltip text. //this helps for accessibility aids, so they can read the button's function if (m_IconID != 0) { TCHAR szCaption[MAX_PATH]; LoadString(m_hInst,nID,szCaption,sizeof(szCaption)/sizeof(TCHAR)); SetWindowText(m_hwnd,szCaption); } }