////////////////////////////////////////////////////////////////////////// // Slider.cpp: Custom slider control. // // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A // PARTICULAR PURPOSE. // // Copyright (c) Microsoft Corporation. All rights reserved. // ////////////////////////////////////////////////////////////////////////// #include "MFPlayer.h" #include namespace SliderControl { PCWSTR ClassName = L"SLIDER_CLASS"; PCWSTR InstanceData = L"SLIDER_PROP"; const LONG DEFAULT_MIN = 0; const LONG DEFAULT_MAX = 100; const LONG DEFAULT_THUMB = 0; struct Slider_Info { // Logical units LONG posMin; // minimum logical position LONG posMax; // maximum logical position LONG posThumb; // current logical position // Client area SIZE pxThumbSize; // real size of thumb bitmap (constant until bitmap changes RECT rcThumb; // client area of the thumb (changes with position) // state BOOL bThumbDown; // User is dragging the thumb? // GDI objects HBRUSH hBackground; HBITMAP hbmThumb; // Thumb bitmap }; LRESULT CALLBACK Slider_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); // Message handlers LRESULT OnCreate(HWND hwnd); LRESULT OnNcDestroy(HWND hwnd, Slider_Info *pInfo); LRESULT OnPaint(HWND hwnd, Slider_Info *pInfo); LRESULT OnLButtonDown(HWND hwnd, LONG x, LONG y, Slider_Info *pInfo); LRESULT OnLButtonUp(HWND hwnd, LONG x, LONG y, Slider_Info *pInfo); LRESULT OnMouseMove(HWND hwnd, LONG x, LONG y, Slider_Info *pInfo); LRESULT OnReleaseSlider(HWND hwnd, Slider_Info *pInfo); // Private message handlers LRESULT OnSetThumbBitmap(HWND hwnd, WORD nID, Slider_Info *pInfo); LRESULT OnSetBackground(HWND hwnd, HBRUSH hBrush, Slider_Info *pInfo); LRESULT OnSetMinMax(HWND hwnd, LONG posMin, LONG posMax, Slider_Info *pInfo); LRESULT OnSetPosition(HWND hwnd, LONG pos, Slider_Info *pInfo); LRESULT OnGetPosition(HWND hwnd, Slider_Info *pInfo); //-------------------------------------------------------------------------------------- // SetInfo // Description: Store the control's instance information. //-------------------------------------------------------------------------------------- inline BOOL SetInfo(HWND hwnd, Slider_Info *pInfo) { return SetProp(hwnd, InstanceData, pInfo); } //-------------------------------------------------------------------------------------- // GetInfo // Description: Get the control's instance information. //-------------------------------------------------------------------------------------- inline Slider_Info * GetInfo(HWND hwnd) { return (Slider_Info*)GetProp(hwnd, InstanceData); } //-------------------------------------------------------------------------------------- // GetThumbRect // Description: Get the current position of the thumb rectangle. // // The position is updated in the rcThumb member of pInfo. //-------------------------------------------------------------------------------------- void GetThumbRect(HWND hwnd, Slider_Info *pInfo) { // 1. Convert logical position to pixels: // logical width = max position - min position // pixel width = client area - thumb width // logical thumb position = current position - min position // pixel thumb position = (logical thumb position / logical width) * pixel width RECT rc; GetClientRect(hwnd, &rc); LONG logWidth = pInfo->posMax - pInfo->posMin; LONG pixWidth = Width(rc) - pInfo->pxThumbSize.cx; LONG logPosition = pInfo->posThumb - pInfo->posMin; LONG left = MulDiv(logPosition, pixWidth, logWidth); // 2. Center vertically LONG top = (Height(rc) - pInfo->pxThumbSize.cy) / 2; SetRect( &pInfo->rcThumb, left, top, left + pInfo->pxThumbSize.cx, top + pInfo->pxThumbSize.cy ); } //-------------------------------------------------------------------------------------- // PixelToLogical // Description: Convert a pixel position to a logical position. // // Returns the logical position. //-------------------------------------------------------------------------------------- LONG PixelToLogical(HWND hwnd, LONG x, Slider_Info *pInfo) { // % of pixel width * logical width, max = logical max // (x / pixel width) * logical width + logical min RECT rc; GetClientRect(hwnd, &rc); LONG pixWidth = Width(rc); LONG logWidth = pInfo->posMax - pInfo->posMin; LONG pos = MulDiv(x, logWidth, pixWidth) + pInfo->posMin; // clamp to slider min and max return max(pInfo->posMin, min(pos, pInfo->posMax)); } //-------------------------------------------------------------------------------------- // NotifyParent // Description: Send the parent window a WM_NOTIFY message with our current status. // // hwnd: Control window. // code: WM_NOTIFY code. (One of the SLIDER_NOTIFY_xxx constants.) // pInfo: Instance data. //-------------------------------------------------------------------------------------- void NotifyParent(HWND hwnd, UINT code, Slider_Info *pInfo) { HWND hParent = GetParent(hwnd); if (hParent) { NMSLIDER_INFO nminfo; nminfo.hdr.hwndFrom = hwnd; nminfo.hdr.idFrom = (UINT_PTR)GetMenu(hwnd); nminfo.hdr.code = code; nminfo.bSelected = pInfo->bThumbDown; nminfo.position = pInfo->posThumb; SendMessage(hParent, WM_NOTIFY, (WPARAM)nminfo.hdr.idFrom, (LPARAM)&nminfo); } } //-------------------------------------------------------------------------------------- // Slider_WndProc // Description: Window proc for the control. //-------------------------------------------------------------------------------------- #include LRESULT CALLBACK Slider_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { Slider_Info * const pInfo = GetInfo(hwnd); switch (uMsg) { case WM_CREATE: return OnCreate(hwnd); case WM_PAINT: return OnPaint(hwnd, pInfo); case WM_NCDESTROY: return OnNcDestroy(hwnd, pInfo); case WM_LBUTTONDOWN: return OnLButtonDown(hwnd, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), pInfo); case WM_LBUTTONUP: return OnLButtonUp(hwnd, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), pInfo); case WM_MOUSEMOVE: return OnMouseMove(hwnd, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), pInfo); case WM_ENABLE: if (wParam == FALSE) // Window is disabled. Stop tracking. { return OnReleaseSlider(hwnd, pInfo); } break; case WM_CAPTURECHANGED: // The window lost focus while the slider was tracking the mouse OR // the slider released the mouse capture itself. return OnReleaseSlider(hwnd, pInfo); // Custom messages case WM_SLIDER_SET_THUMB_BITMAP: return OnSetThumbBitmap(hwnd, (WORD)wParam, pInfo); case WM_SLIDER_SET_BACKGROUND: return OnSetBackground(hwnd, (HBRUSH)wParam, pInfo); case WM_SLIDER_SET_MIN_MAX: return OnSetMinMax(hwnd, (LONG)wParam, (LONG)lParam, pInfo); case WM_SLIDER_SET_POSITION: return OnSetPosition(hwnd, (LONG)wParam, pInfo); case WM_SLIDER_GET_POSITION: return OnGetPosition(hwnd, pInfo); default: return DefWindowProc(hwnd, uMsg, wParam, lParam); } return 0; }; LRESULT OnCreate(HWND hwnd) { Slider_Info *pInfo = new (std::nothrow) Slider_Info; if (!pInfo) { return (LRESULT)-1; } ZeroMemory(pInfo, sizeof(Slider_Info)); pInfo->posMin = DEFAULT_MIN; pInfo->posMax = DEFAULT_MAX; pInfo->posThumb = DEFAULT_THUMB; pInfo->bThumbDown = FALSE; pInfo->hBackground = CreateSolidBrush(RGB(0xFF, 0x80, 0x80)); if (SetInfo(hwnd, pInfo)) { return 0; } else { delete pInfo; return -1; } } LRESULT OnNcDestroy(HWND /*hwnd*/, Slider_Info *pInfo) { if (pInfo) { DeleteObject(pInfo->hBackground); DeleteObject(pInfo->hbmThumb); delete pInfo; } return 0; } LRESULT OnPaint(HWND hwnd, Slider_Info *pInfo) { PAINTSTRUCT ps; HDC hdc; hdc = BeginPaint(hwnd, &ps); // Draw the background if (pInfo->hBackground) { FillRect(hdc, &ps.rcPaint, pInfo->hBackground); } // Draw the thumb if (pInfo->hbmThumb) { HDC hdcCompat = CreateCompatibleDC(hdc); SelectObject(hdcCompat, pInfo->hbmThumb); (void)BitBlt( hdc, pInfo->rcThumb.left, pInfo->rcThumb.top, pInfo->pxThumbSize.cx, pInfo->pxThumbSize.cy, hdcCompat, 0, 0, SRCCOPY ); DeleteDC(hdcCompat); } EndPaint(hwnd, &ps); return 0; } void SetSliderPosition(HWND hwnd, LONG pos, Slider_Info *pInfo) { // Invalidate the old thumb rect InvalidateRect(hwnd, &pInfo->rcThumb, FALSE); pInfo->posThumb = pos; GetThumbRect(hwnd, pInfo); // Invalidate the new thumb rect InvalidateRect(hwnd, &pInfo->rcThumb, FALSE); } LRESULT OnLButtonDown(HWND hwnd, LONG x, LONG /*y*/, Slider_Info *pInfo) { // Move the slider to the mouse position. SetSliderPosition(hwnd, PixelToLogical(hwnd, x, pInfo), pInfo); // Set the thumb-down flag. pInfo->bThumbDown = TRUE; // Start capturing mouse moves so we can update the slider position. SetCapture(hwnd); // Notify the owner window that the control was selected. NotifyParent(hwnd, SLIDER_NOTIFY_SELECT, pInfo); return 0; } LRESULT OnLButtonUp(HWND hwnd, LONG /*x*/, LONG /*y*/, Slider_Info *pInfo) { return OnReleaseSlider(hwnd, pInfo); } LRESULT OnMouseMove(HWND hwnd, LONG x, LONG /*y*/, Slider_Info *pInfo) { // If the control is selected, update the slider position // and notify the owner window. if (pInfo->bThumbDown) { SetSliderPosition(hwnd, PixelToLogical(hwnd, x, pInfo), pInfo); NotifyParent(hwnd, SLIDER_NOTIFY_DRAG, pInfo); } return 0; } // OnReleaseSlider: Stop tracking the slider movement. LRESULT OnReleaseSlider(HWND hwnd, Slider_Info *pInfo) { if (pInfo->bThumbDown) { // Reset the thumb-down flag. pInfo->bThumbDown = FALSE; InvalidateRect(hwnd, &pInfo->rcThumb, FALSE); // Stop capturing mouse moves. ReleaseCapture(); // Notify the owner window that the control was deselected. NotifyParent(hwnd, SLIDER_NOTIFY_RELEASE, pInfo); } return 0; } //-------------------------------------------------------------------------------------- // OnSetThumbBitmap // Description: Sets the bitmap image for the slider thumb. // // Handler for WM_SLIDER_SET_THUMB_BITMAP message. //-------------------------------------------------------------------------------------- LRESULT OnSetThumbBitmap(HWND hwnd, WORD nID, Slider_Info *pInfo) { HBITMAP hbm = LoadBitmap(GetInstance(), MAKEINTRESOURCE(nID)); if (hbm == NULL) { return FALSE; } BITMAP bm; GetObject(hbm, sizeof(BITMAP), &bm); pInfo->pxThumbSize.cx = bm.bmWidth; pInfo->pxThumbSize.cy = bm.bmHeight; if (pInfo->hbmThumb) { DeleteObject(pInfo->hbmThumb); } pInfo->hbmThumb = hbm; GetThumbRect(hwnd, pInfo); return TRUE; } //-------------------------------------------------------------------------------------- // OnSetBackground // Description: Sets the background brush. // // Handler for WM_SLIDER_SET_BACKGROUND message. //-------------------------------------------------------------------------------------- LRESULT OnSetBackground(HWND /*hwnd*/, HBRUSH hBrush, Slider_Info *pInfo) { if (pInfo->hBackground) { DeleteObject(pInfo->hBackground); } pInfo->hBackground = hBrush; return TRUE; } //-------------------------------------------------------------------------------------- // OnSetMinMax // Description: Sets the slider range. // // Handler for WM_SLIDER_SET_MIN_MAX message. //-------------------------------------------------------------------------------------- LRESULT OnSetMinMax(HWND hwnd, LONG posMin, LONG posMax, Slider_Info *pInfo) { pInfo->posMin = posMin; pInfo->posMax = posMax; if (pInfo->posThumb < posMin) { SetSliderPosition(hwnd, posMin, pInfo); } else if (pInfo->posThumb > posMax) { SetSliderPosition(hwnd, posMax, pInfo); } return TRUE; } //-------------------------------------------------------------------------------------- // OnSetPosition // Description: Sets the slider range. // // Handler for WM_SLIDER_SET_POSITION message. //-------------------------------------------------------------------------------------- LRESULT OnSetPosition(HWND hwnd, LONG pos, Slider_Info *pInfo) { if (pos < pInfo->posMin || pos > pInfo->posMax) { return FALSE; } else { SetSliderPosition(hwnd, pos, pInfo); return TRUE; } } //-------------------------------------------------------------------------------------- // OnSetPosition // Description: Sets the slider range. // // Handler for WM_SLIDER_GET_POSITION message. //-------------------------------------------------------------------------------------- LRESULT OnGetPosition(HWND /*hwnd*/, Slider_Info *pInfo) { return pInfo->posThumb; } } // namespace SliderControl //-------------------------------------------------------------------------------------- // Slider_Init // Description: Initializes the slider window class. // // Call this function before using the slider control. //-------------------------------------------------------------------------------------- HRESULT Slider_Init() { WNDCLASSEX wce; ZeroMemory(&wce, sizeof(wce)); wce.cbSize = sizeof(WNDCLASSEX); wce.lpfnWndProc = SliderControl::Slider_WndProc; wce.hInstance = GetInstance(); wce.lpszClassName = SliderControl::ClassName; wce.cbWndExtra = sizeof(SliderControl::Slider_Info); // Reserve space for slider instance data ATOM a = RegisterClassEx(&wce); if (a == 0) { return __HRESULT_FROM_WIN32(GetLastError()); } else { return S_OK; } }