Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

806 lines
20 KiB

/*++
Copyright (C) 1996-1999 Microsoft Corporation
Module Name:
intrvbar.cpp
Abstract:
Implementation of the interval bar control.
--*/
//==========================================================================//
// Includes //
//==========================================================================//
#include <windows.h>
#include <assert.h>
#include <limits.h>
#include "globals.h"
#include "winhelpr.h"
#include "utils.h"
#include "intrvbar.h"
//==========================================================================//
// Constants //
//==========================================================================//
#define dwILineClassStyle (CS_HREDRAW | CS_VREDRAW)
#define dwILineWindowStyle (WS_CHILD | WS_VISIBLE)
#define TO_THE_END 0x7FFFFFFFL
#define szILineClass TEXT("IntervalBar")
//==========================================================================//
// Macros //
//==========================================================================//
// Width of the start and stob grab bars
#define ILGrabWidth() \
(10)
#define ILGrabMinimumWidth() \
(6)
// A rectangle is "drawable" if it has both nonzero height and minimum width
#define PRectDrawable(lpRect) \
((lpRect->right - lpRect->left) >= ILGrabMinimumWidth()) && \
(lpRect->bottom - lpRect->top)
#define RectDrawable(Rect) \
((Rect.right - Rect.left) >= ILGrabMinimumWidth()) && \
(Rect.bottom - Rect.top)
//==========================================================================//
// Local Functions //
//==========================================================================//
void
CIntervalBar::NotifyChange (
void
)
{
HWND hWndParent ;
hWndParent = WindowParent (m_hWnd) ;
if (hWndParent)
SendMessage (hWndParent, WM_COMMAND,
(WPARAM) WindowID (m_hWnd),
(LPARAM) m_hWnd) ;
}
BOOL
CIntervalBar::GrabRect (
OUT LPRECT lpRect
)
{
switch (m_iMode) {
case ModeLeft:
*lpRect = m_rectLeftGrab ;
return (TRUE) ;
break ;
case ModeRight:
*lpRect = m_rectRightGrab ;
return (TRUE) ;
break ;
case ModeCenter:
*lpRect = m_rectCenterGrab ;
return (TRUE) ;
break ;
case ModeNone:
lpRect->left = 0 ;
lpRect->top = 0 ;
lpRect->right = 0 ;
lpRect->bottom = 0 ;
return (FALSE) ;
break ;
default:
return (FALSE);
}
}
void
CIntervalBar::DrawGrab (
HDC hDC,
LPRECT lpRectGrab,
BOOL bDown
)
{
if (!PRectDrawable(lpRectGrab))
return ;
Fill(hDC, GetSysColor(COLOR_3DFACE), lpRectGrab);
DrawEdge (hDC, lpRectGrab, (bDown ? EDGE_SUNKEN:EDGE_RAISED), BF_RECT);
}
INT
CIntervalBar::ValueToPixel (
INT iValue
)
{
INT xPixel ;
if (m_iEndValue > m_iBeginValue)
xPixel = MulDiv (iValue, m_rectBorder.right, (m_iEndValue - m_iBeginValue)) ;
else
xPixel = 0 ;
return (PinExclusive (xPixel, 0, m_rectBorder.right)) ;
}
INT
CIntervalBar::PixelToValue (
INT xPixel
)
{
INT iValue ;
if (m_rectBorder.right)
iValue = MulDiv (xPixel, (m_iEndValue - m_iBeginValue), m_rectBorder.right) ;
else
iValue = 0 ;
return (PinInclusive (iValue, m_iBeginValue, m_iEndValue)) ;
}
void
CIntervalBar::CalcPositions (
void
)
/*
Effect: Determine and set all of the physical rectangles of ILine,
based on the current size of the ILine window and the
current logical Start, Stop, Begin, and End values.
*/
{
INT xStart, xStop ;
INT yHeight ;
GetClientRect (m_hWnd, &m_rectBorder) ;
yHeight = m_rectBorder.bottom ;
xStart = ValueToPixel (m_iStartValue) ;
xStop = ValueToPixel (m_iStopValue) ;
m_rectLeftBk.left = 1 ;
m_rectLeftBk.top = 1 ;
m_rectLeftBk.right = xStart ;
m_rectLeftBk.bottom = yHeight - 1 ;
m_rectLeftGrab.left = xStart ;
m_rectLeftGrab.top = 1 ;
m_rectLeftGrab.right = xStart + ILGrabWidth () ;
m_rectLeftGrab.bottom = yHeight - 1 ;
m_rectRightBk.left = xStop ;
m_rectRightBk.top = 1 ;
m_rectRightBk.right = m_rectBorder.right - 1 ;
m_rectRightBk.bottom = yHeight - 1 ;
m_rectRightGrab.left = xStop - ILGrabWidth () ;
m_rectRightGrab.top = 1 ;
m_rectRightGrab.right = xStop ;
m_rectRightGrab.bottom = yHeight - 1 ;
m_rectCenterGrab.left = m_rectLeftGrab.right ;
m_rectCenterGrab.top = 1 ;
m_rectCenterGrab.right = m_rectRightGrab.left ;
m_rectCenterGrab.bottom = yHeight - 1 ;
if (m_rectLeftGrab.right > m_rectRightGrab.left) {
m_rectLeftGrab.right = m_rectLeftGrab.left + (xStop - xStart) / 2 ;
m_rectRightGrab.left = m_rectLeftGrab.right ;
m_rectCenterGrab.left = 0 ;
m_rectCenterGrab.right = 0 ;
// Ensure that at least one grab bar is visible when End > Begin and the total is
// wide enough. ILGrabMinimumWidth + 2 is the minimum.
// If on the left edge, make the Right grab visible.
// If on the right edge, make the Left grab visible.
// If in the middle, make them both visible.
if ( !RectDrawable(m_rectLeftGrab)
|| !RectDrawable(m_rectRightGrab) ) {
INT iWidth = ILGrabMinimumWidth();
if ( !RectDrawable(m_rectRightBk) ) {
// Make the Left grab visible.
m_rectRightGrab.left = m_rectRightGrab.right;
m_rectLeftGrab.right = m_rectRightGrab.right;
m_rectLeftGrab.left = m_rectLeftGrab.right - iWidth;
} else if (!RectDrawable(m_rectLeftBk) ) {
// Make the Right grab visible.
m_rectLeftGrab.right = m_rectLeftGrab.left;
m_rectRightGrab.left = m_rectLeftGrab.left;
m_rectRightGrab.right = m_rectRightGrab.left + iWidth;
} else {
// Make them both visible.
m_rectLeftGrab.left -= iWidth;
m_rectRightGrab.right += iWidth;
}
}
}
}
void
CIntervalBar::Draw (
HDC hDC,
LPRECT // lpRectUpdate
)
/*
Effect: Draw the image of pILine on hDC. Draw at least the
portions within rectUpdate.
Called By: OnPaint, OnMouseMove.
*/
{
if (IsWindowEnabled(m_hWnd)) {
FillRect (hDC, &m_rectLeftBk, m_hBrushBk) ;
FillRect (hDC, &m_rectRightBk, m_hBrushBk) ;
//DrawEdge (hDC, &m_rectBorder, BDR_SUNKENINNER, BF_RECT) ;
DrawEdge (hDC, &m_rectBorder, EDGE_SUNKEN, BF_RECT) ;
DrawGrab (hDC, &m_rectLeftGrab, m_iMode == ModeLeft) ;
DrawGrab (hDC, &m_rectRightGrab, m_iMode == ModeRight) ;
DrawGrab (hDC, &m_rectCenterGrab, m_iMode == ModeCenter) ;
}
else {
Fill(hDC, GetSysColor(COLOR_3DFACE), &m_rectBorder);
DrawEdge (hDC, &m_rectBorder, EDGE_SUNKEN, BF_RECT) ;
}
}
void
CIntervalBar::MoveLeftRight (
BOOL bStart,
BOOL bLeft,
INT iMoveAmt
)
{
INT iStart, iStop, iMove ;
iStart = m_iStartValue;
iStop = m_iStopValue;
iMove = iMoveAmt ;
if (bLeft)
iMove = -iMove ;
if (bStart)
{
if (iMoveAmt == TO_THE_END) {
iStart = m_iBeginValue ;
}
else {
iStart += iMove ;
if (iStart >= iStop) {
return;
}
}
SetStart (iStart) ;
}
else {
if (iMoveAmt == TO_THE_END) {
iStop = m_iEndValue ;
}
else {
iStop += iMove ;
if (iStart >= iStop) {
return;
}
}
SetStop (iStop) ;
}
NotifyChange () ;
}
BOOL
CIntervalBar::OnKeyDown (
WPARAM wParam
)
{
BOOL bHandle = TRUE ;
BOOL bStart ;
BOOL bLeftDirection ;
BOOL bShiftKeyDown ;
if (wParam == VK_LEFT || wParam == VK_RIGHT) {
bShiftKeyDown = (GetKeyState (VK_SHIFT) < 0) ;
if (!bShiftKeyDown) {
if (wParam == VK_LEFT) {
// Left Arrow --> move Start Edge Left
bStart = TRUE ;
bLeftDirection = TRUE ;
}
else {
// Right Arrow --> move Stop Edge Right
bStart = FALSE ;
bLeftDirection = FALSE ;
}
}
else {
if (wParam == VK_LEFT) {
// Shift Left Arrow --> move Stop Edge Left
bStart = FALSE ;
bLeftDirection = TRUE ;
}
else {
// Shift Right Arrow --> move Start Edge Right
bStart = TRUE ;
bLeftDirection = FALSE ;
}
}
MoveLeftRight (bStart, bLeftDirection, 1) ;
}
else if (wParam == VK_HOME) {
// move iStart all the way the Left
MoveLeftRight (TRUE, TRUE, TO_THE_END) ;
}
else if (wParam == VK_END) {
// move iStop all the way the right
MoveLeftRight (FALSE, FALSE, TO_THE_END) ;
}
else {
bHandle = FALSE ;
}
return (bHandle) ;
}
void
CIntervalBar::StartGrab (
void
)
{
RECT rectUpdate ;
SetCapture (m_hWnd) ;
GrabRect (&rectUpdate) ;
Update();
}
void
CIntervalBar::EndGrab (
void
)
/*
Internals: Set the mode to null after getting the grab rectangle
so ILGrabRect knows which grab bar to get.
*/
{
RECT rectUpdate ;
ReleaseCapture () ;
GrabRect (&rectUpdate) ;
m_iMode = ModeNone ;
Update();
}
//==========================================================================//
// Message Handlers //
//==========================================================================//
CIntervalBar::CIntervalBar (
void
)
{
m_hWnd = NULL;
m_iBeginValue = 0;
m_iEndValue = 100;
m_iStartValue = 0;
m_iStopValue = 100;
m_iMode = ModeNone;
m_hBrushBk = NULL;
}
CIntervalBar::~CIntervalBar (
void
)
{
if (m_hWnd)
DestroyWindow(m_hWnd);
if (m_hBrushBk)
DeleteBrush (m_hBrushBk);
}
BOOL
CIntervalBar::Init (
HWND hWndParent
)
{
#define dwIntervalBarClassStyle (CS_HREDRAW | CS_VREDRAW)
#define dwIntervalBarStyle (WS_CHILD | WS_VISIBLE)
#define szIntervalBarClass TEXT("IntervalBar")
// Register window class once
if (pstrRegisteredClasses[INTRVBAR_WNDCLASS] == NULL) {
WNDCLASS wc ;
wc.style = dwILineClassStyle ;
wc.lpfnWndProc = (WNDPROC)IntervalBarWndProc ;
wc.cbClsExtra = 0 ;
wc.cbWndExtra = sizeof(PCIntervalBar) ;
wc.hInstance = g_hInstance ;
wc.hIcon = NULL ;
wc.hCursor = LoadCursor (NULL, IDC_ARROW) ;
wc.hbrBackground = NULL ;
wc.lpszMenuName = NULL ;
wc.lpszClassName = szIntervalBarClass ;
if (RegisterClass (&wc)) {
pstrRegisteredClasses[INTRVBAR_WNDCLASS] = szIntervalBarClass;
}
else {
return FALSE;
}
}
// Create our window
m_hWnd = CreateWindow (szIntervalBarClass, // class
NULL, // caption
dwIntervalBarStyle, // window style
0, 0, // position
0, 0, // size
hWndParent, // parent window
NULL, // menu
g_hInstance, // program instance
(LPVOID) this ); // user-supplied data
if (m_hWnd == NULL) {
return FALSE;
}
m_hBrushBk = CreateSolidBrush (GetSysColor(COLOR_SCROLLBAR)) ;
CalcPositions () ;
return TRUE;
}
void
CIntervalBar::OnLButtonUp (
void
)
{
if (m_iMode == ModeNone)
return ;
EndGrab () ;
}
void
CIntervalBar::OnMouseMove (
POINTS ptsMouse
)
/*
Effect: Handle any actions needed when the mouse moves in the
ILine hWnd's client area or while the mouse is captured.
In particular, if we are tracking one of the grab bars,
determine if the mouse movement represents a logical value
change and move the grab bar accordingly.
Called By: ILineWndProc, in response to a WM_MOUSEMOVE message.
See Also: OnLButtonDown, OnLButtonUp.
Note: This function has multiple return points.
Note: Since we have captured the mouse, we receive mouse msgs
even when the mouse is outside our client area, but still
in client coordinates. Thus we can have negative mouse
coordinates. That is why we convert the lParam of the
mouse msg into a POINTS structure rather than 2 WORDS.
Internals: Remember that an IntervalLine can only take on integral
values in the user-supplied range. Therefore we do our
movement calculation in user values, not pixels. We
determine what the logical value would be for the previous
(last mouse move) and current mouse position. If these
LOGICAL values differ, we attempt an adjustment of the
grab bar by that logical amount. This way the grab
values assume on integral positions and the calculations
are simplified.
If we calculated by pixel movement, and then shifted the
bar into the nearest integal position, we would encounter
rounding problems. In particular, when tracking the center
grab bar, if we moved both start and stop by the same
amount of PIXELS, then converted to LOGICAL values, we
might find our center bar shrinking and growing while
the bar moves.
*/
{
INT iMousePrevious, iMouseCurrent ;
INT iMouseMove ;
// Are we tracking?
if (m_iMode == ModeNone)
return ;
// Calc LOGICAL mouse movement
assert ( USHRT_MAX >= m_rectBorder.left );
assert ( USHRT_MAX >= m_rectBorder.right );
ptsMouse.x = PinInclusive (ptsMouse.x,
(SHORT)m_rectBorder.left,
(SHORT)m_rectBorder.right) ;
iMousePrevious = PixelToValue (m_ptsMouse.x) ;
iMouseCurrent = PixelToValue (ptsMouse.x) ;
iMouseMove = iMouseCurrent - iMousePrevious ;
if (!iMouseMove)
return ;
// Move grab bar positions
switch (m_iMode) {
case ModeLeft:
m_iStartValue += iMouseMove ;
m_iStartValue = min (m_iStartValue, m_iStopValue - 1) ;
break ;
case ModeCenter:
// Before we slide the center grab bar we need to see if the
// desired movement amount would send either end out of bounds,
// and reduce the movement accordingly.
if (m_iStartValue + iMouseMove < m_iBeginValue)
iMouseMove = m_iBeginValue - m_iStartValue ;
if (m_iStopValue + iMouseMove > m_iEndValue)
iMouseMove = m_iEndValue - m_iStopValue ;
m_iStartValue += iMouseMove ;
m_iStopValue += iMouseMove ;
break ;
case ModeRight:
m_iStopValue += iMouseMove ;
m_iStopValue = max (m_iStartValue + 1, m_iStopValue) ;
break ;
}
m_iStartValue = PinInclusive (m_iStartValue, m_iBeginValue, m_iEndValue) ;
m_iStopValue = PinInclusive (m_iStopValue, m_iBeginValue, m_iEndValue) ;
Update();
m_ptsMouse = ptsMouse ;
NotifyChange () ;
}
void
CIntervalBar::OnLButtonDown (
POINTS ptsMouse
)
{
POINT ptMouse ;
m_ptsMouse = ptsMouse ;
ptMouse.x = ptsMouse.x ;
ptMouse.y = ptsMouse.y ;
if (PtInRect (&m_rectLeftGrab, ptMouse) ||
PtInRect (&m_rectLeftBk, ptMouse)) {
m_iMode = ModeLeft ;
}
else if (PtInRect (&m_rectRightGrab, ptMouse) ||
PtInRect (&m_rectRightBk, ptMouse)) {
m_iMode = ModeRight ;
}
else if (PtInRect (&m_rectCenterGrab, ptMouse)) {
m_iMode = ModeCenter ;
}
if (m_iMode != ModeNone)
StartGrab();
}
void
CIntervalBar::Update (
void
)
{
HDC hDC;
// Determine pixel pos, draw
CalcPositions () ;
hDC = GetDC (m_hWnd) ;
if ( NULL != hDC ) {
Draw (hDC, &m_rectBorder) ;
ReleaseDC (m_hWnd, hDC) ;
}
}
//==========================================================================//
// Exported Functions //
//==========================================================================//
LRESULT APIENTRY IntervalBarWndProc (
HWND hWnd,
UINT uiMsg,
WPARAM wParam,
LPARAM lParam
)
/*
Note: This function must be declared in the application's
linker-definition file, perfmon.def file.
*/
{
PCIntervalBar pIntrvBar;
BOOL bCallDefWindowProc ;
POINTS ptsMouse ;
LRESULT lrsltReturnValue ;
bCallDefWindowProc = FALSE ;
lrsltReturnValue = 0L ;
if (uiMsg == WM_CREATE) {
pIntrvBar = (PCIntervalBar)((CREATESTRUCT*)lParam)->lpCreateParams;
} else {
pIntrvBar = (PCIntervalBar)GetWindowLongPtr (hWnd, 0);
}
switch (uiMsg) {
case WM_CREATE:
SetWindowLongPtr(hWnd, 0, (INT_PTR)pIntrvBar);
break ;
case WM_LBUTTONDOWN:
// See the note in OnMouseMove for why we are using POINTS
SetFocus (hWnd) ;
ptsMouse = MAKEPOINTS (lParam) ;
pIntrvBar->OnLButtonDown (ptsMouse) ;
break ;
case WM_LBUTTONUP:
pIntrvBar->OnLButtonUp () ;
break ;
case WM_SETFOCUS:
case WM_KILLFOCUS:
pIntrvBar->NotifyChange () ;
return 0 ;
case WM_ENABLE:
pIntrvBar->Update();
break;
case WM_MOUSEMOVE:
// See the note in OnMouseMove for why we are using POINTS
ptsMouse = MAKEPOINTS (lParam) ;
pIntrvBar->OnMouseMove (ptsMouse) ;
break ;
case WM_KEYDOWN:
if (!pIntrvBar->OnKeyDown (wParam)) {
bCallDefWindowProc = TRUE ;
}
break ;
case WM_GETDLGCODE:
// We want to handle Arrow keys input. If we don't specify this
// the dialog will not pass arrow keys to us.
return (DLGC_WANTARROWS) ;
break ;
case WM_PAINT:
{
PAINTSTRUCT ps ;
HDC hDC;
hDC = BeginPaint (hWnd, &ps) ;
pIntrvBar->Draw (hDC, &ps.rcPaint) ;
EndPaint (hWnd, &ps) ;
}
break ;
case WM_SIZE:
pIntrvBar->CalcPositions () ;
break;
default:
bCallDefWindowProc = TRUE ;
}
if (bCallDefWindowProc)
lrsltReturnValue = DefWindowProc (hWnd, uiMsg, wParam, lParam) ;
return (lrsltReturnValue) ;
}
void
CIntervalBar::SetRange (
INT iBegin,
INT iEnd
)
{
m_iBeginValue = iBegin;
m_iEndValue = iEnd;
Update();
}
void
CIntervalBar::SetStart (
INT iStart
)
{
m_iStartValue = PinInclusive (iStart, m_iBeginValue, m_iEndValue) ;
Update();
}
void
CIntervalBar::SetStop (
INT iStop
)
{
m_iStopValue = PinInclusive (iStop, m_iBeginValue, m_iEndValue) ;
Update();
}