535 lines
16 KiB
C++
535 lines
16 KiB
C++
//////////////////////////////////////////////////////////////////////////
|
|
// 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 <windowsx.h>
|
|
|
|
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 <strsafe.h>
|
|
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;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|