NT4/private/windows/win4help/winhelp/nav.c
2020-09-30 17:12:29 +02:00

799 lines
18 KiB
C

/*****************************************************************************
*
* NAV.C
*
* Copyright (C) Microsoft Corporation 1990.
* All Rights reserved.
*
******************************************************************************
*
* Module Intent: Provide services and processes messages in an environment
* independent way.
*
*****************************************************************************/
#include "help.h"
#include "inc\cursor.h"
#include "inc\navpriv.h"
_subsystem(NAV);
#define SCROLL_YAMOUNT 15 // One line scroll amount
#define SCROLL_XAMOUNT 15
#define MAX_SCROLL 0x7FFF
static void STDCALL InvalidateLayoutRect(const QDE qde);
static VOID STDCALL SetScrollPosQde(QDE qde, int i, int wWhich);
static BOOL STDCALL FSetColors(QDE qde);
INLINE static void STDCALL ScrollLayoutRect(const QDE qde, PT pt);
INLINE VOID STDCALL SetScrollPosHwnd(QDE qde, int i, int wWhich);
VOID FASTCALL SetPainting(HDE hde, BOOL f)
{
ASSERT(hde);
if (f) {
QdeFromGh(hde)->fSelectionFlags |= REPAINTING;
}
else {
QdeFromGh(hde)->fSelectionFlags &= ~REPAINTING;
}
}
/***************
*
- MouseInFrame
-
* purpose
* Called when any mouse event occurs in the topic window
*
* arguments
* HDE hde Handle to Display Environment
* LPPOINT qpt Pointer to PT structure containing local coords of
* mousedown INT wtype Type of mouse event: NAV_MOUSEDOWN, NAV_MOUSEUP,
* NAV_MOUSEMOVED
*
* notes:
* With mouse moved events, the cursor may change. It might be nice
* to remember the previous cursor state to avoid flicker, but (1)
* we're not sure if flicker will occur, and (2) some other application
* might change the cursor, invalidating our state information.
* Mouse downs are relayed to Frame Mgr, and mouse ups are ignored.
* (Will this work?)
*
**************/
VOID STDCALL MouseInFrame(HDE hde, LPPOINTS qpt, int wtype, UINT fwKeys)
{
QDE qde;
int icurs;
DWORD err;
BOOL flags;
POINT pt;
WRECT rc;
if (hde == NULL)
return;
qde = QdeFromGh(hde);
POINTSTOPOINT(pt, *qpt);
switch (wtype) {
case WM_TIMER:
{
SCRLAMT scrlamt;
WORD scrlMul;
if (fwKeys != ID_DRAG_SCROLL)
break;
GetWindowWRect(qde->hwnd, &rc);
InflateRect((RECT*) &rc, -qde->tm.tmAveCharWidth,
-qde->tm.tmHeight);
rc.top = 0;
rc.left = 0;
if (pt.x >= rc.cx)
{
scrlamt= SCROLL_LINEDN;
scrlMul= (pt.x + SCROLL_DISTANCE_FACTOR - 1 - rc.cx)
/ SCROLL_DISTANCE_FACTOR;
}
else
if (pt.x < rc.left)
{
scrlamt= SCROLL_LINEUP;
scrlMul= (rc.left + SCROLL_DISTANCE_FACTOR - 1 - pt.x)
/ SCROLL_DISTANCE_FACTOR;
}
else scrlamt= 0;
if (scrlamt)
FScrollHde(hde, scrlamt, SCROLL_HORZ, scrlMul);
if (pt.y >= rc.cy)
{
scrlamt= SCROLL_LINEDN;
scrlMul= (pt.y + SCROLL_DISTANCE_FACTOR - 1 - rc.cy)
/ SCROLL_DISTANCE_FACTOR;
}
else
if (pt.y < rc.top)
{
scrlamt= SCROLL_LINEUP;
scrlMul= (rc.top + SCROLL_DISTANCE_FACTOR - 1 - pt.y)
/ SCROLL_DISTANCE_FACTOR;
}
else scrlamt= 0;
if (scrlamt)
FScrollHde(hde, scrlamt, SCROLL_VERT, scrlMul);
if (qde->fSelectionFlags & WORD_SELECT)
vSelectWord (qde, pt, TRUE, &err);
else
vSelectPoint(qde, pt, TRUE, &err);
UpdateWindow(qde->hwnd);
break;
}
case WM_MOUSEMOVE:
if (qde->fSelectionFlags & BUTTON_DOWN && qde->deType != deNote)
{
FSetCursor(icurIBEAM);
GetWindowWRect(qde->hwnd, &rc);
InflateRect((RECT*) &rc, -qde->tm.tmAveCharWidth,
-qde->tm.tmHeight);
rc.top = 0;
rc.left = 0;
POINTSTOPOINT(pt,*qpt);
//if (qde->deType != deNSR && !PtInRect(&rc, *qpt))
if (qde->deType != deNSR && !PtInRect((PRECT) &rc, pt))
if (qde->fSelectionFlags & SCROLL_TIMER_ON)
return;
else
{
if (SetTimer(qde->hwnd, ID_DRAG_SCROLL,
GetProfileInt("Windows", "KeyboardSpeed",
DEFAULT_SCROLL_SPEED
),
NULL
)
)
qde->fSelectionFlags |= SCROLL_TIMER_ON;
MouseInFrame(hde, qpt, WM_TIMER, ID_DRAG_SCROLL);
break;
}
else
if (qde->fSelectionFlags & SCROLL_TIMER_ON)
{
KillTimer(qde->hwnd, ID_DRAG_SCROLL);
qde->fSelectionFlags &= ~SCROLL_TIMER_ON;
}
if (qde->fSelectionFlags & WORD_SELECT)
vSelectWord (qde, pt, TRUE, &err);
else
vSelectPoint(qde, pt, TRUE, &err);
UpdateWindow(qde->hwnd);
}
else
{
if (IsSelected(qde) && fPointInSelection(qde, pt))
{
FSetCursor(icurARROW);
break;
}
icurs = IcursTrackLayout( qde, pt );
/* Fix for bug 59 (kevynct 90/05/23)
*
* The point pt is relative to the client area
* of the DE being passed. Any mouse action outside
* the DE will cause IcursTrackLayout to return icurNil.
* This means: do not change the current cursor.
* So we are assuming that the correct cursor has been set
* upon display of the DE which has captured the mouse.
*
* See also the comments in ShowNote (hmessage.c) for how
* the cursor is set when creating a note.
*/
if (icurs == icurARROW && qde->deType != deNote)
{
HWND hwndCur;
hwndCur= ChildWindowFromPoint(qde->hwnd, pt);
if (!hwndCur || hwndCur == qde->hwnd)
icurs= icurIBEAM;
}
FSetCursor(icurs);
break;
}
break;
case WM_RBUTTONDOWN:
case WM_LBUTTONDOWN:
case WM_RBUTTONDBLCLK:
case WM_LBUTTONDBLCLK:
if (qde->fSelectionFlags & BUTTON_DOWN)
break;
// Ignore hotspots in selected areas
if (IsSelected(qde) && fPointInSelection(qde, pt))
{
if (qde->fSelectionFlags & CAPTURE_LOCKED)
{
qde->fSelectionFlags &= ~CAPTURE_LOCKED;
ReleaseCapture();
RelHDC(qde->hwnd, hde, qde->hdc);
}
if (wtype == WM_LBUTTONDOWN || wtype == WM_LBUTTONDBLCLK)
DragSelection(qde);
break;
}
else if ((wtype == WM_LBUTTONDOWN || wtype == WM_LBUTTONDBLCLK) &&
ClickLayout(qde, pt)) {
break; // hotspot was clicked, don't start a selection
}
icurs= IcursTrackLayout(qde, pt);
if (qde->deType != deNote && (icurs == icurARROW ||
((icurs == icurNil) && (qde->fSelectionFlags & CAPTURE_LOCKED)))) {
BOOL fExtendSelection = fwKeys & MK_SHIFT && IsSelected(qde);
KillSelection(qde, fExtendSelection);
switch (wtype) {
case WM_RBUTTONDOWN:
qde->fSelectionFlags |= MOUSE_CAPTURED | RIGHT_BUTTON_DOWN;
break;
case WM_LBUTTONDOWN:
qde->fSelectionFlags |= MOUSE_CAPTURED | LEFT_BUTTON_DOWN;
break;
case WM_RBUTTONDBLCLK:
qde->fSelectionFlags |=
MOUSE_CAPTURED | RIGHT_BUTTON_DOWN | WORD_SELECT;
break;
case WM_LBUTTONDBLCLK:
qde->fSelectionFlags |=
MOUSE_CAPTURED | LEFT_BUTTON_DOWN | WORD_SELECT;
break;
}
if (!(qde->fSelectionFlags & CAPTURE_LOCKED))
SetCapture(qde->hwnd);
FSetCursor(icurIBEAM);
if (qde->fSelectionFlags & WORD_SELECT)
vSelectWord (qde, pt, fExtendSelection, &err);
else
vSelectPoint(qde, pt, fExtendSelection, &err);
UpdateWindow(qde->hwnd);
}
break;
case WM_RBUTTONUP:
case WM_LBUTTONUP:
if (( (qde->fSelectionFlags & BUTTON_DOWN)
== ((wtype==WM_LBUTTONUP)? LEFT_BUTTON_DOWN : RIGHT_BUTTON_DOWN)
)
)
{
if (qde->fSelectionFlags & SCROLL_TIMER_ON)
{
KillTimer(qde->hwnd, ID_DRAG_SCROLL);
qde->fSelectionFlags &= ~SCROLL_TIMER_ON;
}
flags= qde->fSelectionFlags;
if ((flags & BUTTON_DOWN) != ((wtype == WM_LBUTTONUP)?
LEFT_BUTTON_DOWN : RIGHT_BUTTON_DOWN))
break;
qde->fSelectionFlags &=
~(MOUSE_CAPTURED | WORD_SELECT | BUTTON_DOWN);
if (qde->fSelectionFlags & WORD_SELECT)
vSelectWord (qde, pt, TRUE, &err);
else
vSelectPoint(qde, pt, TRUE, &err);
UpdateWindow(qde->hwnd);
if (!(qde->fSelectionFlags & CAPTURE_LOCKED))
ReleaseCapture();
if (flags & RIGHT_BUTTON_DOWN) {
POINT pt;
pt.x = -1;
DisplayFloatingMenu(pt);
}
}
else if (wtype == WM_RBUTTONUP) {
POINT pt;
pt.x = -1;
DisplayFloatingMenu(pt);
}
break;
default:
break;
}
return;
} // MouseInFrame()
void STDCALL ReleaseCaptureLock(HDE hde)
{
QDE qde;
qde = QdeFromGh(hde);
ASSERT((qde->fSelectionFlags & (BUTTON_DOWN | WORD_SELECT | CAPTURE_LOCKED)) == CAPTURE_LOCKED);
qde->fSelectionFlags &= ~CAPTURE_LOCKED;
ReleaseCapture();
}
/***************
*
- FScrollHde
-
* purpose
* Scroll topic window
*
* arguments
* HDE hde Handle to Display Environment
* SCRLAMT amount amount to scroll by. See 'navcnst.h' for values
* SCRLDIR dir direction in which to scroll. See 'navcnst.h'
* WORD wMult Amout to multiply scroll by (i.e. two lines or 3 lines)
*
* return value
* Returns TRUE if it actually scrolled the amount requested, and
* FALSE otherwise (generally due to reaching the end of the document.)
*
* notes
* This WILL NOT update the topic window, or the scroll bar.
* The new scroll bar position will be communicated to the Applet
* somehow. (TBD: Will Applet ask for it?)
* Expose/refresh events will be generated from (the graphics layer?)
* (Under Win, this is done automatically by Scroll function. On
* Mac, the blitting operation (which calls Toolbox Scroll fcn) will
* itself post expose events! This implies that Nav's RefreshHde()
* will be called.
* Scrolling down in the helpfile means that what's on the screen
* goes UP!
*
**************/
BOOL STDCALL FScrollHde(HDE hde, SCRLAMT scrlamt, SCRLDIR scrldir, int wMult)
{
QDE qde;
POINT dpt; // amount in pixels to scroll
POINT dptActual; // Amount actually scrolled
BOOL fSucceed; // Return result
int amtGross; // amount of gross movement
if (hde == NULL)
return FALSE;
qde = QdeFromGh(hde);
// Don't try to scroll a popup topic
if (qde->deType == deNote)
return FALSE;
amtGross = 0;
switch (scrlamt) {
case SCROLL_END:
amtGross = MAX_SCROLL;
// Deliberately fall through
case SCROLL_HOME: // Equivalent to placing thumb at beginning
if ((scrldir & SCROLL_VERT)
&& (!QDE_TOPIC(qde) || qde->fVerScrollVis) ) {
MoveLayoutToThumb(qde, amtGross, SCROLL_VERT);
InvalidateLayoutRect(qde);
}
if ((scrldir & SCROLL_HORZ)
&& (!QDE_TOPIC(qde) || qde->fHorScrollVis)) {
MoveLayoutToThumb(qde, amtGross, SCROLL_HORZ);
InvalidateLayoutRect(qde);
}
fSucceed = TRUE;
break;
default:
// All of these involve a scroll command to Frame Manager
dpt.x = dpt.y = 0;
switch (scrlamt) { // Check for other scroll amounts
case SCROLL_LINEDN:
if (scrldir & SCROLL_VERT)
dpt.y = -SCROLL_YAMOUNT * wMult;
if (scrldir & SCROLL_HORZ)
dpt.x = -SCROLL_YAMOUNT * wMult;
break;
case SCROLL_LINEUP:
if (scrldir & SCROLL_VERT)
dpt.y = SCROLL_YAMOUNT * wMult;
if (scrldir & SCROLL_HORZ)
dpt.x = SCROLL_YAMOUNT * wMult;
break;
case SCROLL_PAGEUP:
if (scrldir & SCROLL_VERT) {
dpt.y = qde->rct.bottom - qde->rct.top;
if (dpt.y >= 2 * SCROLL_YAMOUNT)
dpt.y -= SCROLL_YAMOUNT;
}
if (scrldir & SCROLL_HORZ) {
dpt.x = qde->rct.right - qde->rct.left;
if (dpt.x >= 2 * SCROLL_YAMOUNT)
dpt.x -= SCROLL_XAMOUNT;
}
break;
case SCROLL_PAGEDN:
if (scrldir & SCROLL_VERT) {
dpt.y = qde->rct.top - qde->rct.bottom;
if (dpt.y <= 2 * -SCROLL_YAMOUNT)
dpt.y += SCROLL_YAMOUNT;
}
if (scrldir & SCROLL_HORZ) {
dpt.x = qde->rct.left - qde->rct.right;
if (dpt.x <= 2 * -SCROLL_XAMOUNT)
dpt.x += SCROLL_XAMOUNT;
}
break;
default:
ASSERT(FALSE); // Bad scroll amount!
}
// Check to see what scrolling is allowed for this de.
if (QDE_TOPIC(qde) && !qde->fHorScrollVis)
dpt.x = 0;
if (QDE_TOPIC(qde) && !qde->fVerScrollVis)
dpt.y = 0;
// dpt contains the amount we *want* to scroll by!
dptActual = DptScrollLayout(qde, dpt);
ScrollLayoutRect(qde, dptActual);
fSucceed = (dpt.x == dptActual.x && dpt.y == dptActual.y);
break;
}
return fSucceed;
}
/***************
*
- VOID STDCALL MoveToThumbHde(hde, scrlpos, scrldir)
-
* purpose
* Requests an update of the window to match the thumb
*
* arguments
* HDE hde Handle to Display Environment
* WORD scrlpos New position of scroll bar
* SCRLDIR scrldir SCROLL_HORZ or SCROLL_VERT
*
**************/
VOID STDCALL MoveToThumbHde(HDE hde, int scrlpos, SCRLDIR scrldir)
{
ASSERT(hde);
InvalidateLayoutRect(QdeFromGh(hde));
MoveLayoutToThumb(QdeFromGh(hde), scrlpos, scrldir);
}
/***************
*
- SetScrollQde
-
* purpose
* Set the position of the scroll bar thumb
*
* arguments
* QDE qde Pointer to Display Environment
* WORD scrlpos New position of scroll bar
* SCRLDIR scrldir SCROLL_HORZ or SCROLL_VERT
*
**************/
VOID STDCALL SetScrollQde(QDE qde, int scrlpos, SCRLDIR scrldir)
{
if (scrldir & SCROLL_VERT)
SetScrollPosQde(qde, scrlpos, SB_VERT);
else
SetScrollPosQde(qde, scrlpos, SB_HORZ);
}
/***************
*
- RefreshHde
-
* purpose
* Refresh part or all of topic window
*
* arguments
* HDE Hde Handle to Display Environment
* LPRECT qrct Rectangle to be updated
*
* return value
* None. Will assert if bad handle is passed down.
*
* notes
* Should this default to the whole region if qrct is NULL?
*
**************/
void STDCALL RefreshHde(HDE hde, LPRECT qrct)
{
QDE qde;
if (hde == NULL)
return;
qde = QdeFromGh(hde);
// Set up the default foreground and background colors.
FSetColors(qde);
DrawLayout(qde, qrct);
InvertSelection(qde);
}
/***************
*
- SetHDC
-
* purpose
* Set the Display Surface field of DE
*
* argumnts
* HDE hde Handle to Display Environment
* HDS hds Handle to Display Surface, or (HDS)NULL to clear it
*
* notes
* Under Windows (where HDS's are HDC's), HDS's are a scarce resource.
* The Applet is responsible for setting the HDS associated with a
* DE using this call, and for releasing it appropriately (unnecessary
* on Mac, where HDS's are grafports). This means that Applet
* (the Windows applet in particular) needs to know which Nav fcns
* might try to do some drawing. See the other headers for this info.
*
**************/
void STDCALL SetHDC(HDE hde, HDC hdc)
{
QDE qde;
if (hde == NULL)
return;
qde = QdeFromGh(hde);
if (qde->hdc)
SelectObject(qde->hdc, GetStockObject(SYSTEM_FONT));
qde->hdc = hdc;
FSetColors(qde);
if (hdc) {
qde->wXAspectMul = GetDeviceCaps(qde->hdc, LOGPIXELSX);
qde->wYAspectMul = GetDeviceCaps(qde->hdc, LOGPIXELSY);
}
return;
}
/*******************
-
- Name: InvalidateLayoutRect
*
* Purpose: Erases the whole layout area and generates an update event
* for it.
*
*******************/
static void STDCALL InvalidateLayoutRect(const QDE qde)
{
if (qde->hwnd == NULL) // Don't invalidate the entire screen!
return;
InvalidateRect(qde->hwnd, &qde->rct, TRUE);
}
/*
* Ugly global used to get around Windows scrollbar bug. This global is
* defined here and SET when the addition of a horizontal scrollbar is done
* in the presence of a vertical scrollbar. It is RESET only in the
* WM_VSCROLL handler of the TopicWndProc. There should be no problem with
* multiple DEs using this flag given how it can be set.
*/
BOOL fHorzBarPending;
#define MAX_RANGE 32767
/***************************************************************************\
*
- Function: SetScrollPosHwnd
-
* Purpose: Sets the position of the specified scroll bar
*
* ASSUMES
* args IN: hwnd - window handle
* i - position to set to
* wWhich - which scroll bar to set
*
* PROMISES
* returns: Position of thumb on scrollbar
*
* Side Effects:
* scroll bar updated
*
\***************************************************************************/
INLINE VOID STDCALL SetScrollPosHwnd(QDE qde, int i, int wWhich)
{
#ifdef LATER
// All of this is very cool, only the page size is wrong. We should make
// SCROLLINFO part of the de structure.
if (wWhich == SB_HORZ) {
SCROLLINFO si;
RECT rc;
GetClientRect(qde-hwnd, &rc);
si.nMin = 0;
si.nMax = qde->xScrollMax;
si.cbSize = sizeof(si);
si.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
si.nPos = qde->xScrolled;
si.nPage = ???
SetScrollInfo(qde->hwnd, SB_HORZ, &si, TRUE);
return;
}
else
#endif
SetScrollRange(qde->hwnd, wWhich, 0, MAX_RANGE, FALSE);
if (i != GetScrollPos(qde->hwnd, wWhich))
SetScrollPos(qde->hwnd, wWhich, i, TRUE);
}
/*******************
**
** Name: SetScrollPosQde
**
** Purpose: Gets the position of the specified scroll bar.
**
** Arguments: qde - far pointer to a DE
** wWhich - which scroll (SCROLL_VERT or SCROLL_HORZ)
**
** Returns: Position of thumb on scrollbar.
**
*******************/
static VOID STDCALL SetScrollPosQde(QDE qde, int i, int wWhich)
{
ASSERT((wWhich == SB_VERT) || (wWhich == SB_HORZ));
if (wWhich == SB_VERT && !qde->fVerScrollVis)
return;
if (wWhich == SB_HORZ && !qde->fHorScrollVis)
return;
SetScrollPosHwnd(qde, i, wWhich);
}
/*******************
**
** Name: FSetColors
**
** Purpose: Sets the foreground and background colors for a DS,
** using the defaults in the DE.
**
** Arguments: qde
** coFore
** coBack
**
** Returns: Success
**
*******************/
static BOOL STDCALL FSetColors(QDE qde)
{
if (qde->hdc) {
SetTextColor(qde->hdc, qde->coFore);
SetBkColor(qde->hdc, qde->coBack);
return TRUE;
}
else
return FALSE;
}
/*******************
-
- Name: ScrollLayoutRect
*
* Purpose: Scrolls the layout rectangle, and generates an update
* event for the newly exposed area.
*
* Notes: This routine should do nothing if the qde is for printing,
* because ScrollDC will crash if qde->hdc is a printer HDC.
* Currently, we detect a printer qde by checking if qde->hwnd
* is nil. If we add an fPrinting flag to the qde, we should
* check against that instead.
*
******************/
INLINE static void STDCALL ScrollLayoutRect(const QDE qde, PT pt)
{
RECT rc;
if (qde->hwnd == NULL)
return;
GetClientRect(qde->hwnd, &rc);
ScrollWindow(qde->hwnd, pt.x, pt.y, NULL, &rc);
UpdateWindow(qde->hwnd);
}