INTRLINE.C

/* 
==============================================================================

Application:

Microsoft Windows NT (TM) Performance Monitor

File:
intrline.c -- IntervalLine custom control.

Copyright 1992 - 1998 Microsoft Corporation. All Rights Reserved.
==============================================================================
*/


/*


Basic Information
-----------------

An ILine (Interval Line) control is a horizontally-sliding device; the user
can slide the start position and end position independently by dragging
either "grab" bar, or move them simultaneously by dragging the center grab
bar. An ILine control is used in the Input Log dialog of Perfmon, but could
be used anywhere. The control allows the user to specify the start, end and
duration of playback within the range of previously-logged data.


ILine Parts
-----------

iBeginValue iEndValue
| iStartValue iStopValue |
| | | |
v v v v
+------+-+---------------------------------------+-+-----------+
| |X| |X| |
| |X| |X| |
+------+-+---------------------------------------+-+-----------+
^ ^ ^
Start grab bar Center grab bar Stop grab bar


ILine Terminology
-----------------

All external routines are designated by the prefix ILine-.
Internal routines are designated by the prefix IL-.

The suffix -Value represents an integer in the user-supplied domain.
The suffix -Pixel represents an integer location on the screen.

Note that the range of the IntervalLine is represented by "Begin" and
"End", while the the currently selected interval is represented by
"Start" and "Stop".


Implementation Notes
--------------------

ILine is a custom control, but does not have all the messages
normally associated with a full-fledged control type like "button".
In particular, it doesn't have a keyboard interface or ask it's parent
for color choices. It also doesn't have the functions needed for interfacing
with the dialog editor custom control menu.

An ILine control is designed to work with an integer range of values
specified by the user of the control. These values should have meaning to
the caller. The caller can specify the minimum value of the range
(iBeginValue), the maximum value of the range (iEndValue), and the current
starting and stopping values (iStartValue and iStopValue).

These values are set with a function-based interface. (ILSetXXX).

The code distinguishes between the *values* for the begin, end, start, and
stop, and the *pixels* which represent locations on the control for these
values.

Various bits of the control style allow for changing the control's
behavior.

To allow for multiple ILine controls, all data used by each ILine
instance is allocated from the heap and associated with the control.
The allocation is done in OnCreate and the freeing in OnDestroy. The
instance data is contained in the ILINE structure. Each message handler
retrieves this instance data with the ILData function.

*/


//==========================================================================//
// Includes //
//==========================================================================//


#include "perfmon.h"
#include "utils.h"
#include "intrline.h"
#include "pmemory.h" // for MemoryXXX (mallloc-type) routines

BOOL IntrLineFocus ;

//==========================================================================//
// Constants //
//==========================================================================//


#define dwILineClassStyle (CS_HREDRAW | CS_VREDRAW)
#define iILineClassExtra (0)
#define iILineWindowExtra (sizeof (PILINE))
#define dwILineWindowStyle (WS_CHILD | WS_VISIBLE)


#define ILModeNone 0
#define ILModeLeft 1
#define ILModeCenter 2
#define ILModeRight 3

#define TO_THE_END 0x7FFFFFFFL

//==========================================================================//
// Typedefs //
//==========================================================================//


// The instance data for each IL window. One of these is allocated for each
// window in the window's OnCreate processing and free'd in OnDestroy.
typedef struct ILINESTRUCT
{ // ILINE
int iBeginValue ; // user-supplied lowest range
int iEndValue ; // user-supplied highest range
int iStartValue ; // current start of selected interval
int iStopValue ; // current end of selected interval
int iGranularityValue ;
int iMinimumRangeValue ;

RECT rectBorder ;
RECT rectLeftBk ;
RECT rectLeftGrab ;
RECT rectCenterGrab ;
RECT rectRightGrab ;
RECT rectRightBk ;

HBRUSH hBrushBk ;

POINTS ptsMouse ;
int iMode ; // who is being tracked?
} ILINE ;

typedef ILINE *PILINE ;


//==========================================================================//
// Macros //
//==========================================================================//


// Width of the start and stob grab bars
#define ILGrabWidth() \
(xScrollThumbWidth)


// A rectangle is "drawable" if it has both nonzero width and height
#define RectDrawable(lpRect) \
((lpRect->right - lpRect->left) && \
(lpRect->bottom - lpRect->top))


//======================================//
// ILine Accessor Macros //
//======================================//

// These macros reference fields in the ILine structure and should be
// used in place of direct reference to the fields. This makes the code
// easier to read and allows modification of the underlying structure.

// You can use these macros to both get and set the fields.

#define ILBegin(pILine) \
(pILine->iBeginValue)

#define ILEnd(pILine) \
(pILine->iEndValue)

#define ILStart(pILine) \
(pILine->iStartValue)

#define ILStop(pILine) \
(pILine->iStopValue)

#define ILMode(pILine) \
(pILine->iMode)


//======================================//
// ILine Pseudo-Accessor Macros //
//======================================//

// These macros are used to get values which don't correspond to a single
// field. You can get the value with these macros but can't set it.

#define ILWidth(pILine) \
(pILine->rectBorder.right)

#define ILRange(pILine) \
(ILEnd (pILine) - ILBegin (pILine))

#define ILStartPixel(pILine) \
(pILine->rectLeftGrab.left)

#define ILStopPixel(pILine) \
(pILine->rectRightGrab.right)


//==========================================================================//
// Local Functions //
//==========================================================================//


void static ILNotifyChange (HWND hWnd,
PILINE pILine)
{ // ILNotifyChange
HWND hWndParent ;

hWndParent = WindowParent (hWnd) ;

if (hWndParent)
SendMessage (hWndParent, WM_COMMAND,
(WPARAM) WindowID (hWnd),
(LPARAM) hWnd) ;
} // ILNotifyChange


BOOL ILGrabRect (IN PILINE pILine,
OUT LPRECT lpRect)
/*
Effect: Return in lpRect the rectangle representing the position
of the grab bar currently being tracked.
*/
{ // ILGrabRect
switch (ILMode (pILine))
{ // switch
case ILModeLeft:
*lpRect = pILine->rectLeftGrab ;
return (TRUE) ;
break ;

case ILModeRight:
*lpRect = pILine->rectRightGrab ;
return (TRUE) ;
break ;

case ILModeCenter:
*lpRect = pILine->rectCenterGrab ;
return (TRUE) ;
break ;

case ILModeNone:
lpRect->left = 0 ;
lpRect->top = 0 ;
lpRect->right = 0 ;
lpRect->bottom = 0 ;
return (FALSE) ;
break ;
} // switch
} // ILGrabRect


PILINE ILData (HWND hWndIL)
{
return ((PILINE) GetWindowLong (hWndIL, 0)) ;
}


PILINE AllocateILData (HWND hWndIL)
{
PILINE pILine ;

pILine = MemoryAllocate (sizeof (ILINE)) ;
SetWindowLong (hWndIL, 0, (LONG) pILine) ;

return (pILine) ;
}


void static DrawGrab (HDC hDC,
LPRECT lpRectGrab,
BOOL bDown)
{ // DrawGrab
if (!RectDrawable (lpRectGrab))
return ;

if (bDown)
ThreeDConcave1 (hDC,
lpRectGrab->left,
lpRectGrab->top,
lpRectGrab->right,
lpRectGrab->bottom) ;
else
ThreeDConvex1 (hDC,
lpRectGrab->left,
lpRectGrab->top,
lpRectGrab->right,
lpRectGrab->bottom) ;
} // DrawGrab


int ILValueToPixel (PILINE pILine,
int iValue)
{
int xPixel ;

if (ILRange (pILine))
xPixel = MulDiv (iValue, ILWidth (pILine), ILRange (pILine)) ;
else
xPixel = 0 ;

return (PinExclusive (xPixel, 0, pILine->rectBorder.right)) ;
}


int ILPixelToValue (PILINE pILine,
int xPixel)
{
int iValue ;

if (ILWidth (pILine))
iValue = MulDiv (xPixel, ILRange (pILine), ILWidth (pILine)) ;
else
iValue = 0 ;

return (PinInclusive (iValue, ILBegin (pILine), ILEnd (pILine))) ;
}


void static ILCalcPositions (HWND hWnd,
PILINE pILine)
/*
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.
*/
{ // ILCalcPositions
int xStart, xStop ;
int yHeight ;

GetClientRect (hWnd, &pILine->rectBorder) ;
yHeight = pILine->rectBorder.bottom ;

xStart = ILValueToPixel (pILine, ILStart (pILine)) ;
xStop = ILValueToPixel (pILine, ILStop (pILine)) ;

pILine->rectLeftBk.left = 1 ;
pILine->rectLeftBk.top = 1 ;
pILine->rectLeftBk.right = xStart ;
pILine->rectLeftBk.bottom = yHeight - 1 ;

pILine->rectLeftGrab.left = xStart ;
pILine->rectLeftGrab.top = 1 ;
pILine->rectLeftGrab.right = xStart + ILGrabWidth () ;
pILine->rectLeftGrab.bottom = yHeight - 1 ;

pILine->rectRightBk.left = xStop ;
pILine->rectRightBk.top = 1 ;
pILine->rectRightBk.right = pILine->rectBorder.right - 1 ;
pILine->rectRightBk.bottom = yHeight - 1 ;

pILine->rectRightGrab.left = xStop - ILGrabWidth () ;
pILine->rectRightGrab.top = 1 ;
pILine->rectRightGrab.right = xStop ;
pILine->rectRightGrab.bottom = yHeight - 1 ;

pILine->rectCenterGrab.left = pILine->rectLeftGrab.right ;
pILine->rectCenterGrab.top = 1 ;
pILine->rectCenterGrab.right = pILine->rectRightGrab.left ;
pILine->rectCenterGrab.bottom = yHeight - 1 ;

if (pILine->rectLeftGrab.right > pILine->rectRightGrab.left)
{
pILine->rectLeftGrab.right = pILine->rectLeftGrab.left +
(xStop - xStart) / 2 ;
pILine->rectRightGrab.left = pILine->rectLeftGrab.right ;
pILine->rectCenterGrab.left = 0 ;
pILine->rectCenterGrab.right = 0 ;
}
} // ILCalcPositions


void static ILDraw (HDC hDC,
PILINE pILine,
LPRECT lpRectUpdate)
/*
Effect: Draw the image of pILine on hDC. Draw at least the
portions within rectUpdate.

Called By: OnPaint, OnMouseMove.
*/
{ // ILDraw

//=============================//
// Draw Border //
//=============================//

FrameRect (hDC, &pILine->rectBorder, GetStockObject (BLACK_BRUSH)) ;

//=============================//
// Draw Background //
//=============================//

FillRect (hDC, &pILine->rectLeftBk, pILine->hBrushBk) ;
FillRect (hDC, &pILine->rectRightBk, pILine->hBrushBk) ;

//=============================//
// Draw Range Grabs //
//=============================//

DrawGrab (hDC, &pILine->rectLeftGrab,
ILMode (pILine) == ILModeLeft) ;
DrawGrab (hDC, &pILine->rectRightGrab,
ILMode (pILine) == ILModeRight) ;

//=============================//
// Draw Center Grab //
//=============================//

DrawGrab (hDC, &pILine->rectCenterGrab,
ILMode (pILine) == ILModeCenter) ;
} // ILDraw

#if 0
void ILPageLeftRight (HWND hWnd,
PILINE pILine,
BOOL bLeft)
{ // ILPageLeftRight
int iStart, iStop, iMove ;
HDC hDC ;

iStart = ILStart (pILine) ;
iStop = ILStop (pILine) ;

if (ILRange (pILine) >= 10)
iMove = ILRange (pILine) / 10 ;
else
iMove = 1 ;

if (bLeft)
iMove = -iMove ;

iStart += iMove ;
iStop += iMove ;

ILineSetStart (hWnd, iStart) ;
ILineSetStop (hWnd, iStop) ;

hDC = GetDC (hWnd) ;
ILDraw (hDC, pILine, &pILine->rectBorder) ;
ReleaseDC (hWnd, hDC) ;

ILNotifyChange (hWnd, pILine) ;
} // ILPageLeftRight
#endif

void ILMoveLeftRight (HWND hWnd,
BOOL bStart,
BOOL bLeft,
int MoveAmt)
{ // ILPageLeftRight
int iStart, iStop, iMove ;
int iBegin, iEnd ;
HDC hDC ;
PILINE pILine ;

pILine = ILData (hWnd) ;
iStart = ILStart (pILine) ;
iStop = ILStop (pILine) ;
iBegin = ILBegin (pILine) ;
iEnd = ILEnd (pILine) ;

iMove = MoveAmt ;
if (bLeft)
iMove = -iMove ;

if (bStart)
{
if (MoveAmt == TO_THE_END)
{
iStart = iBegin ;
}
else
{
iStart += iMove ;
if (iStart >= iStop)
{
return;
}
}

ILineSetStart (hWnd, iStart) ;
}
else
{
if (MoveAmt == TO_THE_END)
{
iStop = iEnd ;
}
else
{
iStop += iMove ;
if (iStart >= iStop)
{
return;
}
}

ILineSetStop (hWnd, iStop) ;
}

hDC = GetDC (hWnd) ;
ILDraw (hDC, pILine, &pILine->rectBorder) ;
ReleaseDC (hWnd, hDC) ;

ILNotifyChange (hWnd, pILine) ;
} // ILMoveLeftRight

static BOOL OnKeyDown (HWND hWnd,
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 ;
}
}
ILMoveLeftRight (hWnd, bStart, bLeftDirection, 1) ;
}
else if (wParam == VK_HOME)
{
// move iStart all the way the Left
ILMoveLeftRight (hWnd, TRUE, TRUE, TO_THE_END) ;
}
else if (wParam == VK_END)
{
// move iStop all the way the right
ILMoveLeftRight (hWnd, FALSE, FALSE, TO_THE_END) ;
}
else
{
bHandle = FALSE ;
}
return (bHandle) ;

} // OnKeyDown

void StartGrab (HWND hWnd,
PILINE pILine)
{ // StartGrab
RECT rectUpdate ;
HDC hDC ;

SetCapture (hWnd) ;
ILGrabRect (pILine, &rectUpdate) ;

hDC = GetDC (hWnd) ;
ILDraw (hDC, pILine, &rectUpdate) ;
ReleaseDC (hWnd, hDC) ;
} // StartGrab


void EndGrab (HWND hWnd,
PILINE pILine)
/*
Internals: Set the mode to null after getting the grab rectangle
so ILGrabRect knows which grab bar to get.
*/
{
RECT rectUpdate ;
HDC hDC ;

ReleaseCapture () ;

ILGrabRect (pILine, &rectUpdate) ;
ILMode (pILine) = ILModeNone ;

hDC = GetDC (hWnd) ;
ILDraw (hDC, pILine, &rectUpdate) ;
ReleaseDC (hWnd, hDC) ;
}


//==========================================================================//
// Message Handlers //
//==========================================================================//


void static OnPaint (HWND hWnd)
{
PAINTSTRUCT ps ;
HDC hDC ;
PILINE pILine ;

pILine = ILData (hWnd) ;
hDC = BeginPaint (hWnd, &ps) ;

ILDraw (hDC, pILine, &ps.rcPaint) ;

EndPaint (hWnd, &ps) ;
}


void static OnCreate (HWND hWnd)
{
PILINE pILine ;

pILine = AllocateILData (hWnd) ;
ILBegin (pILine) = 0 ;
ILEnd (pILine) = 100 ;
ILStart (pILine) = 0 ;
ILStop (pILine) = 100 ;
ILMode (pILine) = ILModeNone ;

pILine->hBrushBk = CreateSolidBrush (crBlue) ;
ILCalcPositions (hWnd, pILine) ;
}


void static OnDestroy (HWND hWnd)
{
PILINE pILine ;

pILine = ILData (hWnd) ;

IntrLineFocus = FALSE ;
DeleteBrush (pILine->hBrushBk) ;
MemoryFree (pILine) ;
}


void static OnLButtonUp (HWND hWnd)
{ // OnLButtonUp
PILINE pILine ;

pILine = ILData (hWnd) ;

if (ILMode (pILine) == ILModeNone)
return ;

EndGrab (hWnd, pILine) ;
} // OnLButtonUp


void static OnMouseMove (HWND hWnd,
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 ;
PILINE pILine ;
HDC hDC ;

//=============================//
// Are we tracking? //
//=============================//

pILine = ILData (hWnd) ;
if (ILMode (pILine) == ILModeNone)
return ;

//=============================//
// Calc LOGICAL mouse movement //
//=============================//

ptsMouse.x = PinInclusive ((INT)ptsMouse.x,
(INT)pILine->rectBorder.left,
(INT)pILine->rectBorder.right) ;

iMousePrevious = ILPixelToValue (pILine, pILine->ptsMouse.x) ;
iMouseCurrent = ILPixelToValue (pILine, ptsMouse.x) ;

iMouseMove = iMouseCurrent - iMousePrevious ;
if (!iMouseMove)
return ;

//=============================//
// Move grab bar positions //
//=============================//

switch (ILMode (pILine))
{ // switch
case ILModeLeft:
ILStart (pILine) += iMouseMove ;
ILStart (pILine) = min (ILStart (pILine), ILStop (pILine) - 1) ;
break ;

case ILModeCenter:
// 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 (ILStart (pILine) + iMouseMove < ILBegin (pILine))
iMouseMove = ILBegin (pILine) - ILStart (pILine) ;
if (ILStop (pILine) + iMouseMove > ILEnd (pILine))
iMouseMove = ILEnd (pILine) - ILStop (pILine) ;

ILStart (pILine) += iMouseMove ;
ILStop (pILine) += iMouseMove ;
break ;

case ILModeRight:
ILStop (pILine) += iMouseMove ;
ILStop (pILine) = max (ILStart (pILine) + 1, ILStop (pILine)) ;
break ;
} // switch


//=============================//
// Keep grab bars in range //
//=============================//


ILStart (pILine) = PinInclusive (ILStart (pILine),
ILBegin (pILine), ILEnd (pILine)) ;
ILStop (pILine) = PinInclusive (ILStop (pILine),
ILBegin (pILine), ILEnd (pILine)) ;

//=============================//
// Determine pixel pos, draw //
//=============================//

ILCalcPositions (hWnd, pILine) ;

hDC = GetDC (hWnd) ;
ILDraw (hDC, pILine, &pILine->rectBorder) ;
ReleaseDC (hWnd, hDC) ;

pILine->ptsMouse = ptsMouse ;
ILNotifyChange (hWnd, pILine) ;
} // OnMouseMove


void static OnLButtonDown (HWND hWnd,
POINTS ptsMouse)
{
PILINE pILine ;
POINT ptMouse ;

pILine = ILData (hWnd) ;

pILine->ptsMouse = ptsMouse ;
ptMouse.x = ptsMouse.x ;
ptMouse.y = ptsMouse.y ;

#if 0
// Russ said this is bad, so don't do it
if (PtInRect (&pILine->rectLeftBk, ptMouse))
ILPageLeftRight (hWnd, pILine, TRUE) ;

else if (PtInRect (&pILine->rectRightBk, ptMouse))
ILPageLeftRight (hWnd, pILine, FALSE) ;

else if (PtInRect (&pILine->rectLeftGrab, ptMouse))
#endif
if (PtInRect (&pILine->rectLeftGrab, ptMouse) ||
PtInRect (&pILine->rectLeftBk, ptMouse))
{
ILMode (pILine) = ILModeLeft ;
StartGrab (hWnd, pILine) ;
}

else if (PtInRect (&pILine->rectRightGrab, ptMouse) ||
PtInRect (&pILine->rectRightBk, ptMouse))
{
ILMode (pILine) = ILModeRight ;
StartGrab (hWnd, pILine) ;
}

else if (PtInRect (&pILine->rectCenterGrab, ptMouse))
{
ILMode (pILine) = ILModeCenter ;
StartGrab (hWnd, pILine) ;
}
}


void static OnSize (HWND hWnd, WORD xWidth, WORD yHeight)
{ // OnSize
PILINE pILine ;

pILine = ILData (hWnd) ;

ILCalcPositions (hWnd, pILine) ;
} // OnSize


//==========================================================================//
// Exported Functions //
//==========================================================================//


LONG FAR PASCAL ILineWndProc (HWND hWnd,
unsigned msg,
WPARAM wParam,
LONG lParam)
/*
Note: This function must be declared in the application's
linker-definition file, perfmon.def file.
*/
{ // ILineWndProc
BOOL bCallDefWindowProc ;
POINTS ptsMouse ;
LONG lReturnValue ;

bCallDefWindowProc = FALSE ;
lReturnValue = 0L ;

switch (msg)
{ // switch
case WM_CREATE:
OnCreate (hWnd) ;
break ;

case WM_DESTROY:
OnDestroy (hWnd) ;
break ;

case WM_LBUTTONDOWN:
// See the note in OnMouseMove for why we are using POINTS
SetFocus (hWnd) ;
ptsMouse = MAKEPOINTS (lParam) ;
OnLButtonDown (hWnd, ptsMouse) ;
break ;

case WM_LBUTTONUP:
OnLButtonUp (hWnd) ;
break ;

case WM_SETFOCUS:
case WM_KILLFOCUS:
{
PILINE pILine ;

IntrLineFocus = (msg == WM_SETFOCUS) ;

pILine = ILData (hWnd) ;
ILNotifyChange (hWnd, pILine) ;
}
return 0 ;


case WM_MOUSEMOVE:
// See the note in OnMouseMove for why we are using POINTS
ptsMouse = MAKEPOINTS (lParam) ;
OnMouseMove (hWnd, ptsMouse) ;
break ;

case WM_KEYDOWN:
if (!OnKeyDown (hWnd, 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:
OnPaint (hWnd) ;
break ;

case WM_SIZE:
OnSize (hWnd, LOWORD (lParam), HIWORD (lParam)) ;

default:
bCallDefWindowProc = TRUE ;
} // switch

if (bCallDefWindowProc)
lReturnValue = DefWindowProc (hWnd, msg, wParam, lParam) ;

return (lReturnValue) ;
} // ILineWndProc



BOOL ILineInitializeApplication (void)
/*
Effect: Perform all initializations required before an application
can create an IntervalLine. In particular, register the
IntervalLine window class.

Called By: The application, in its InitializeApplication routine.

Returns: Whether the class could be registered.
*/
{ // ILineInitializeApplication
WNDCLASS wc ;

wc.style = dwILineClassStyle ;
wc.lpfnWndProc = ILineWndProc ;
wc.cbClsExtra = iILineClassExtra ;
wc.cbWndExtra = iILineWindowExtra ;
wc.hInstance = hInstance ;
wc.hIcon = NULL ;
wc.hCursor = LoadCursor (NULL, IDC_ARROW) ;
wc.hbrBackground = NULL ;
wc.lpszMenuName = NULL ;
wc.lpszClassName = szILineClass ;

return (RegisterClass (&wc)) ;
} // ILineInitializeApplication


void ILineSetRange (HWND hWnd, int iBegin, int iEnd)

{  // ILineSetRange 
PILINE pILine ;

pILine = ILData (hWnd) ;

ILBegin (pILine) = iBegin ;
ILEnd (pILine) = iEnd ;

ILCalcPositions (hWnd, pILine) ;
} // ILineSetRange


void ILineSetStart (HWND hWnd, int iStart)
{ // ILineSetStart
PILINE pILine ;

pILine = ILData (hWnd) ;

iStart = PinInclusive (iStart, ILBegin (pILine), ILEnd (pILine)) ;
ILStart (pILine) = iStart ;

ILCalcPositions (hWnd, pILine) ;
} // ILineSetStart



void ILineSetStop (HWND hWnd, int iStop)
{ // ILineSetStop
PILINE pILine ;

pILine = ILData (hWnd) ;

iStop = PinInclusive (iStop, ILBegin (pILine), ILEnd (pILine)) ;
ILStop (pILine) = iStop ;

ILCalcPositions (hWnd, pILine) ;
} // ILineSetStop


int ILineXStart (HWND hWnd)
{
PILINE pILine ;

pILine = ILData (hWnd) ;

return (pILine->rectLeftGrab.left) ;
}


int ILineXStop (HWND hWnd)
{
PILINE pILine ;

pILine = ILData (hWnd) ;

return (pILine->rectRightGrab.right) ;
}


int ILineStart (HWND hWnd)
{
PILINE pILine ;

pILine = ILData (hWnd) ;

return (ILStart (pILine)) ;
}


int ILineStop (HWND hWnd)
{
PILINE pILine ;

pILine = ILData (hWnd) ;

return (ILStop (pILine)) ;
}