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

881 lines
21 KiB
C

/*****************************************************************************
* *
* NAVSUP.C *
* *
* Copyright (C) Microsoft Corporation 1990. *
* All Rights reserved. *
* *
******************************************************************************
* *
* Module Intent: Provides services and processes messages in an environment *
* independent way. These routines are not used much as *
* the routines in NAV.C. *
*
*****************************************************************************/
#include "help.h"
#include "inc\navpriv.h"
#include "inc\annopriv.h"
#include "inc\cmdobj.h"
_subsystem(NAV);
/*****************************************************************************
* *
* Typedefs *
* *
*****************************************************************************/
/*****************************************************************************
* *
* Prototypes *
* *
*****************************************************************************/
static VOID STDCALL AddrGetTopicTitle(HASH, LPSTR);
typedef struct {
HASH hash;
char rgchFile[1];
} IF, * QIF; // Interfile jump or popup structure*/
#ifndef _X86_
/* Must be funcs to deal with alignment */
WORD WFromQv(QV qv)
{
WORD wRet;
MoveMemory( &wRet, qv, sizeof( WORD ) );
return( wRet );
}
DWORD ULFromQv(QV qv)
{
DWORD dwRet;
MoveMemory( &dwRet, qv, sizeof( DWORD ) );
return( dwRet );
}
#endif // !_X86_
/***************
*
- GetCurrentTitleQde
-
* purpose
* Places upto cb-1 characters of the current title in a buffer.
* The string is then null terminated.
*
* arguments
* QDE qde - far pointer to the DE.
* LPSTR qch - pointer to buffer for title
* INT cb - size of the buffer.
*
* return value
* Nothing.
*
**************/
VOID STDCALL GetCurrentTitleQde(QDE qde, PSTR pszTitle, int cb)
{
int cbT;
ASSERT(cb > 1);
if (qde->top.hTitle == NULL) { // NULL means no topic title
if (!pszUntitled)
pszUntitled = LocalStrDup(GetStringResource(sidUntitled));
wsprintf(pszTitle, pszUntitled,
QDE_HHDR(qde).wVersionNo == wVersion3_0 ? 0 :
qde->top.mtop.lTopicNo);
return;
}
cbT = min(qde->top.cbTitle, cb - 1);
MoveMemory(pszTitle, PtrFromGh(qde->top.hTitle), cbT);
pszTitle[cbT] = '\0';
}
/***************
*
- FDisplayAnnoHde
-
* purpose
* Calls the annotation manager to display an annotation for the
* current TO.
*
* arguments
* HDE hde - handle to diaplay environment
*
* return value
* TRUE if annotation was processed, FALSE if we attempted to recurse
* on ourselves.
*
**************/
static fDoingIt; // Recursion protection
BOOL STDCALL FDisplayAnnoHde(HDE hde)
{
QDE qde;
qde = QdeFromGh(hde);
// We allow only one annotation up at a time.
if (!fDoingIt) {
VA vaFirst;
fDoingIt = TRUE;
vaFirst = VaFirstQde(qde);
/*
* (kevynct) Fix for H3.5 bug 750 If there is no scrolling
* region, place the annotation in the non-scrolling region.
*/
if (vaFirst.dword == vaNil && qde->deType == deTopic)
vaFirst = qde->top.mtop.vaNSR;
ASSERT(vaFirst.dword != vaNil);
if (FProcessAnnoQde(qde, vaFirst))
GenerateMessage(MSG_REPAINT, 0, 0L);
fDoingIt = FALSE;
}
/*------------------------------------------------------------*\
| This was possibly set, if annotation was called by macro
\*------------------------------------------------------------*/
ClearMacroFlag();
return !fDoingIt;
}
/***************
*
- WNavMsgHde
-
* purpose
* Dispatches keyboard messages passed to the navigator.
* (Currently these only drive hotspot actions.)
*
* arguments
* HDE hde - handle to display environment
* WORD wMsg - message to dispatch
*
* return value
* wReturn - See nav.h for wNav return codes
*
**************/
int STDCALL WNavMsgHde(HDE hde, UINT wMsg)
{
if (hde == NULL)
return wNavFailure;
switch (wMsg) {
case NAV_NEXTHS:
case NAV_PREVHS:
return FHiliteNextHotspot(QdeFromGh(hde), (wMsg == NAV_NEXTHS))
? wNavSuccess : wNavNoMoreHotspots;
case NAV_HITHS:
if (FHitCurrentHotspot(QdeFromGh(hde)))
return wNavSuccess;
else
return wNavNoMoreHotspots;
case NAV_TOTALHILITEOFF:
case NAV_TOTALHILITEON:
if (FHiliteVisibleHotspots(QdeFromGh(hde), wMsg == NAV_TOTALHILITEON))
return wNavSuccess;
else
return wNavFailure;
}
return wNavFailure;
}
/***************************************************************************\
*
- Function: FSameFile( hde, fm )
-
* Purpose: Determine whether fm specifies the same file as the one
* associated with hde.
*
* ASSUMES
*
* args IN: hde - HDE: the display environment to check against
* fm - any old fm
*
* PROMISES
*
* returns: TRUE if same file; else FALSE
*
\***************************************************************************/
BOOL STDCALL FSameFile(HDE hde, FM fm)
{
#ifdef _DEBUG
QDE qde =QdeFromGh(hde);
#endif
if (!hde)
return FALSE;
return FSameFmFm(QDE_FM(QdeFromGh(hde)), fm);
}
/***************
*
- FActivateHelp
-
* purpose
* Perform whatever actions are appropriate when Help is
* activated or deactivated (i.e. gets or loses "focus" to
* another application).
*
* arguments
*
* hde
* fActivate - TRUE if Help is getting activation,
* FALSE if Help is losing activation
*
* return value
* TRUE if it worked.
*
**************/
int STDCALL DyGetLayoutHeightHde(HDE hde)
{
QDE qde;
int dy;
qde = QdeFromGh(hde);
if ((qde->deType == deNSR && qde->top.mtop.vaNSR.dword == vaNil)
|| (QDE_TOPIC(qde) && qde->top.mtop.vaSR.dword == vaNil))
dy = 0;
else {
POINT pt = PtGetLayoutSize(qde);
dy = pt.y;
}
/*
* This is kind of gross but account for the line at the bottom of the
* NSR that separates the NSR from the SR. it's drawn at the bottom of the
* NSR client rect after the NSR has been painted.
*/
if (dy != 0)
dy += 1;
return dy;
}
/***************************************************************************
*
- Name:
-
* Purpose:
*
* Arguments:
*
* Returns:
*
* Globals Used:
*
* +++
*
* Notes:
*
***************************************************************************/
PUBLIC BOOL STDCALL FGetTLPStartInfo(HDE hde, QTLP qtlp, BOOL fWantNSRStuff)
{
QDE qde;
VA va;
qde = QdeFromGh(hde);
va = (fWantNSRStuff) ? qde->top.mtop.vaNSR : qde->top.mtop.vaSR;
if (qtlp != NULL) {
qtlp->va = va;
qtlp->lScroll = 0;
}
return (va.dword != vaNil);
}
/***************************************************************************
*
- Name: VaLayoutBoundsQde
-
* Purpose: Return the VA which marks the first FC in either the current
* layout or the next one.
*
* Arguments qde
* fTopMark - TRUE if we want the first FC in the current layout.
* FALSE if we want the first FC in the next layout.
*
* Returns The requested VA.
*
* Notes: This talks to HfcNextPrevFc.
* HACK ALERT! To be able to distinguish the print NSR from
* the print SR, we use top.vaCurr. We rely on the fact that
* when we print or copy-to-clipboard, we jump to the start
* of each layout that we want to print/copy.
*
***************************************************************************/
VA STDCALL VaLayoutBoundsQde(QDE qde, BOOL fTopMark)
{
VA vaBogus;
VA vaCurr;
vaBogus.dword = vaNil;
ASSERT(qde != NULL);
if (qde->top.mtop.vaNSR.dword == vaNil
|| qde->top.mtop.vaSR.dword == vaNil)
return vaBogus;
switch (qde->deType) {
case deNSR:
vaCurr = qde->top.mtop.vaNSR;
break;
case deAuto:
case deTopic:
vaCurr = qde->top.mtop.vaSR;
default:
vaCurr = qde->top.vaCurr;
}
if (qde->top.mtop.vaNSR.dword < qde->top.mtop.vaSR.dword
&& vaCurr.dword >= qde->top.mtop.vaSR.dword)
return (fTopMark ? qde->top.mtop.vaSR : vaBogus);
else
return (fTopMark ? vaBogus : qde->top.mtop.vaSR);
}
/***************************************************************************
*
- Name: JumpButton
-
* Purpose:
* Posts a Jump message to the application queue
*
* Arguments:
* qv - pointer to the additional data based on the jump type
* wHotspotType - button/jump type. One of the hotspot command values.
* wDEType - The type of DE (Topic or NSR)
*
* Returns:
* Nothing
*
***************************************************************************/
static const char txtMainWindow[] = ">MAIN";
void STDCALL JumpButton(LPVOID qv, WORD wHotspotType, QDE qde)
{
BOOL fPopup; // TRUE => Popup long jump
char rgchFileMember[MAX_PATH]; // string including filename & mem
char rgchMember[cchWindowMemberMax]; // string holding member
PSTR pszFilename; // pointer to the filename
JD jdType; // Type of jump: Originator NSR/SR, Jump/Note
#ifndef _X86_
JI ji;
QV qvEndJi;
#endif
#define qji ((QJI)qv) // param treated as QJI
#define ql ((QL)qv) // param treated as QL
#ifdef _DEBUG
if (!FAskFirst(qv, wHotspotType))
return;
#else // DEBUG
if (fHelpAuthor && !FAskFirst(qv, wHotspotType))
return;
#endif // else DEBUG
// Check for bad jump. (-1 placed by compiler?)
if (qv == (QV)-1L) {
Error(wERRS_NOTOPIC, wERRA_RETURN);
return;
}
fPopup = FALSE;
jdType.bf.fNote = FALSE;
jdType.bf.fFromNSR = (qde->deType == deNSR);
switch (wHotspotType) {
// Short Jumps. These include only a long word of information,
// either the ITO, or the hash value of some string
case bShortItoNote:
jdType.bf.fNote = TRUE;
// Deliberately fall through
case bShortItoJump:
#ifdef _X86_
GenerateMessage(MSG_JUMPITO, jdType.word, *ql);
#else
GenerateMessage(MSG_JUMPITO, jdType.word, (LONG)ULFromQv(qv));
#endif
break;
case bShortHashNote:
case bShortInvHashNote:
jdType.bf.fNote = TRUE;
// Fall through
case bShortHashJump:
case bShortInvHashJump:
#ifdef _X86_
GenerateMessage(MSG_JUMPHASH, jdType.word, *ql);
#else
GenerateMessage(MSG_JUMPHASH, jdType.word, (LONG)ULFromQv(qv));
#endif
break;
// Long Jumps. These include a structure containing various optional
// fields possible including hash value, membername, member index,
// and/or filename.
//
case bLongHashNote:
case bLongInvHashNote:
fPopup = TRUE;
// fall through
case bLongHashJump:
case bLongInvHashJump:
// REVIEW: this is pretty bogus. Here the compiler went through all this
// REVIEW: work to parse the filename from the member, and what are we
// REVIEW: going to do? cat them back together again. The reason is the
// REVIEW: fixed bandwidth of the interfaces we're going through to
// REVIEW: finally display this stuff. The _correct_ approach would be
// REVIEW: to create an alternate version of FWinHelp which takes the
// REVIEW: various member name parameters, and an additional API (?)
// REVIEW: that includes a structure big enough to hold it. Either that
// REVIEW: or bypass the entire message posting process.
#ifdef _X86_
pszFilename = qji->uf.szFileOnly;
rgchMember [0] = 0;
rgchFileMember[0] = 0;
if (qji->bFlags & fSzMember) {
if (fPopup) {
if (fHelpAuthor)
PostErrorMessage(wERRS_IG_WINDOW);
}
else {
rgchMember[0] = '>';
strcpy(rgchMember + 1, qji->uf.szMemberOnly);
pszFilename = SzEnd(qji->uf.szMemberOnly) + 1;
}
}
else if (qji->bFlags & fIMember) {
if (fPopup) {
if (fHelpAuthor)
PostErrorMessage(wERRS_IG_WINDOW);
}
else if (qji->uf.iMember == 0xff)
strcpy(rgchMember, txtMainWindow);
else {
wsprintf(rgchMember, ">@%u", qji->uf.iMember);
pszFilename += sizeof (qji->uf.iMember);
}
}
/*
* Get the filename for the jump. For this form of jump, a
* filename MUST be supplied. Thus we either get the filename as
* passed in as part of the jump, OR we get the current filename
* from the WinHelp.
*/
if (qji->bFlags & fSzFile)
lstrcpy(rgchFileMember, pszFilename);
else {
FM fmCur = (FM) LGetInfo(GI_CURFM, NULL);
ASSERT(fmCur);
lstrcpy(rgchFileMember, PszFromGh(fmCur));
}
strcat(rgchFileMember, rgchMember[0] ? rgchMember : txtMainWindow);
if (fPopup)
FPopupHash(rgchFileMember, qji->hash);
else
FJumpHash(rgchFileMember, qji->hash);
#else
qvEndJi = pszFilename = (QB)qv + LcbMapSDFF(QDE_ISDFFTOPIC(qde), SE_JI, &ji, qv);
ji.uf.iMember = *(QB)qvEndJi;
/* convert the hash back because of btree */
ji.hash = LQuickMapSDFF(QDE_ISDFFTOPIC(qde), TE_LONG, &ji.hash);
/*szFilename = qji->uf.szFileOnly;*/
rgchMember [0] = 0;
rgchFileMember[0] = 0;
if (ji.bFlags & fSzMember) {
if (fPopup)
PostErrorMessage(wERRS_IG_WINDOW);
else {
rgchMember [0] = '>';
lstrcpy (rgchMember+1, (PCH)qvEndJi);
pszFilename = SzEnd((PCH)qvEndJi) + 1;
}
}
else if (ji.bFlags & fIMember) {
if (fPopup)
PostErrorMessage(wERRS_IG_WINDOW);
else if (ji.uf.iMember == 0xff)
strcpy(rgchMember, txtMainWindow);
else {
wsprintf(rgchMember, ">@%u", ji.uf.iMember);
pszFilename += sizeof(ji.uf.iMember);
}
}
/* Get the filename for the jump. For this form of jump, a filename */
/* MUST be supplied. Thus we either get the filename as passed in as */
/* part of the jump, OR we get the current filename from the app. */
if (ji.bFlags & fSzFile)
lstrcpy (rgchFileMember, pszFilename);
else
{
FM fmCur;
fmCur = (FM)LGetInfo (GI_CURFM, NULL);
ASSERT (fmCur);
lstrcpy(rgchFileMember, PszFromGh(fmCur));
}
lstrcat(rgchFileMember, rgchMember[0] ? rgchMember : txtMainWindow);
if (fPopup)
FPopupHash(rgchFileMember, ji.hash);
else
FJumpHash(rgchFileMember, ji.hash);
#endif
break;
case bAnnoHotspot:
GenerateMessage(MSG_ANNO, 0, 0L);
break;
case bLongMacro:
case bLongMacroInv:
GenerateMessage(MSG_NEW_MACRO, 0, (LPARAM) qv);
break;
default:
NotReached();
break;
}
}
/***************************************************************************
*
- Name: FAskFirst
-
* Purpose:
* The fDebugFlag fDEBUGASKFIRST is set, this function will query the
* user (with information about the hotspot) before the action is
* actually take.
*
* Arguments:
* qv - pointer to the additional data based on the jump type
* wHotspotType - button/jump type. One of the hotspot command values.
* wDEType - The type of DE (Topic or NSR)
*
* Returns: TRUE iff the action is to be taken.
*
* Method: This routine is mostly a copy of JumpButton where I have
* inserted queries instead of actions for all the requests.
*
*
***************************************************************************/
BOOL STDCALL FAskFirst(QV qv, WORD wHotspotType)
{
BOOL fPopup = FALSE; // TRUE => Popup long jump
CHAR rgchFileMember[MAX_PATH + 20]; // string including filename & mem
CHAR rgchBuffer[512];
CHAR rgchTitle[256];
CHAR rgchMember[cchWindowMemberMax]; // string holding member
LPSTR pszFilename; // pointer to the filename
#ifndef _X86_
QDE qde = QdeFromGh(HdeGetEnv());
#endif
#define qji ((QJI)qv) // param treated as QJI
#define ql ((QL)qv) // param treated as QL
if (!hwndParent && !(fDebugState & fDEBUGASKFIRST))
return TRUE;
// Check for bad jump. (-1 placed by compiler?)
if (qv == (QV) -1L)
return TRUE;
/* Else assumed to be SR: Hopefully later we can abandon
* this scheme altogether.
*/
switch (wHotspotType) {
/*
* Short Jumps. These include only a long word of information,
* either the ITO, or the hash value of some string
*/
#ifdef _DEBUG
case bShortItoJump:
wsprintf(rgchBuffer, "Jump to: 0x%x", *ql);
return FAskBox(rgchBuffer);
break;
case bShortItoNote:
wsprintf(rgchBuffer, "Popup to: 0x%x", *ql);
return FAskBox(rgchBuffer);
break;
#endif
case bShortHashJump:
case bShortInvHashJump:
case bShortHashNote:
case bShortInvHashNote:
#ifdef _X86_
AddrGetTopicTitle(*ql, rgchTitle);
#else
AddrGetTopicTitle((LONG)ULFromQv((QV)ql), rgchTitle);
#endif
wsprintf(rgchBuffer, GetStringResource(sidAskJump), rgchTitle);
return FAskBox(rgchBuffer);
break;
// Long Jumps. These include a structure containing various optional
// fields possible including hash value, membername, member index,
// and/or filename.
case bLongHashNote:
case bLongInvHashNote:
fPopup = TRUE;
// fall through
case bLongHashJump:
case bLongInvHashJump:
pszFilename = qji->uf.szFileOnly;
rgchMember[0] = 0;
rgchFileMember[0] = 0;
if (qji->bFlags & fSzMember) {
if (!fPopup) {
rgchMember [0] = '>';
lstrcpy (rgchMember+1, qji->uf.szMemberOnly);
pszFilename = SzEnd(qji->uf.szMemberOnly) + 1;
}
}
else if (qji->bFlags & fIMember) {
if (!fPopup) {
rgchMember [0] = '>';
if (qji->uf.iMember == 0xff)
strcpy(rgchMember + 1, txtMain);
else {
HDE hde = HdeGetEnv();
QRGWSMAG qrgwsmag = (QRGWSMAG) QDE_HRGWSMAG(QdeFromGh(hde));
lstrcpy(rgchMember + 1, qrgwsmag->rgwsmag[qji->uf.iMember].rgchMember);
pszFilename += sizeof (qji->uf.iMember);
}
}
}
/*
* Get the filename for the jump. For this form of jump, a
* filename MUST be supplied. Thus we either get the filename
* as passed in as part of the jump, OR we get the current
* filename from the app.
*/
if (qji->bFlags & fSzFile)
lstrcpy(rgchFileMember, pszFilename);
lstrcat(rgchFileMember, rgchMember);
switch(wHotspotType) {
case bLongHashNote:
case bLongInvHashNote:
case bLongHashJump:
case bLongInvHashJump:
if (FSameFile(HdeGetEnv(), pszFilename))
AddrGetTopicTitle(qji->hash, rgchTitle);
else
strcpy(rgchTitle, GetStringResource(sidTitleUnknown));
wsprintf(rgchBuffer, GetStringResource(sidAskWinJump), rgchFileMember, rgchTitle);
return FAskBox(rgchBuffer);
}
break;
case bLongMacro:
case bLongMacroInv:
{
BOOL fReturn;
PSTR psz;
if (!(fDebugState & fDEBUGASKFIRST))
return TRUE;
psz = lcMalloc(lstrlen((PSTR) qv) + 50);
wsprintf(psz, GetStringResource(sidAskMacro), (LPSTR) qv);
fReturn = FAskBox(psz);
lcFree(psz);
return fReturn;
}
default:
// NotReached();
// Annotation?
break;
}
}
/***************************************************************************
*
- Name: FAskBox
-
* Purpose: Puts up a "YES/NO" message box with the specified string.
*
* Arguments: nsz - near string to the message to be displayed.
*
* Returns: TRUE if the user selects YES.
*
***************************************************************************/
BOOL STDCALL FAskBox(PCSTR pszMsg)
{
if (!(fDebugState & fDEBUGASKFIRST)) {
SendStringToParent("\t");
SendStringToParent(pszMsg);
SendStringToParent("\r\n");
return TRUE;
}
else
return MessageBox((hwndAnimate ?
hwndAnimate : ahwnd[iCurWindow].hwndParent), pszMsg, pszCaption,
MB_YESNO) == IDYES;
}
BOOL STDCALL OkMsgBox(PCSTR pszMsg)
{
int result;
if (fAutoClose)
KillTimer(ahwnd[MAIN_HWND].hwndParent, ID_AUTO_CLOSE);
#if defined(_DEBUG)
{
int answer;
PSTR pszNewMsg = lcMalloc(strlen(pszMsg) + 100);
strcpy(pszNewMsg, pszMsg);
strcat(pszNewMsg, "\r\n\r\nClick YES to break into the debugger.");
answer = MessageBox((hwndAnimate ? hwndAnimate : ahwnd[iCurWindow].hwndParent),
pszNewMsg, pszCaption,
MB_YESNO | MB_DEFBUTTON2);
switch (answer) {
case IDYES:
DebugBreak();
// deliberately fall through
default:
lcFree(pszNewMsg);
return TRUE;
}
}
#endif
result = MessageBox((hwndAnimate ? hwndAnimate : ahwnd[iCurWindow].hwndParent),
pszMsg, pszCaption, MB_OK | MB_ICONINFORMATION);
if (fAutoClose)
SetTimer(ahwnd[MAIN_HWND].hwndParent, ID_AUTO_CLOSE, NOTE_TIMEOUT,
NULL);
return result;
}
/***************************************************************************
*
- Name: AddrGetTopicTitle
-
* Purpose: Gets the title associated with a particular hash value
* (help 3.5 files only).
*
* Arguments: hash - hash of the topic to find title for
* qch - buffer to place title in
*
* Returns: nothing; qch will be an empty string if the title cannot
* be found or an error occurs.
*
***************************************************************************/
// Title b-tree file name (see srch.h)
static void STDCALL AddrGetTopicTitle(HASH hash, LPSTR pszTitle)
{
ADDR addr;
HBT hbtTitle;
RC rc;
QDE qde;
HDE hde = HdeGetEnvHwnd(ahwnd[iCurWindow].hwndTopic);
if (!hde)
return;
addr = addrNil;
qde = QdeFromGh(hde);
// Get physical address from hash
RcLookupByKey(QDE_HBTCONTEXT(qde), (KEY) (LPVOID) &hash, NULL, &addr);
if (addr == addrNil) { // Address not found.
*pszTitle = '\0';
return;
}
// Get title from title B-Tree.
if (hbtTitle = HbtOpenBtreeSz(txtTTLBTREENAME, QDE_HFS(qde),
fFSOpenReadOnly)) {
rc = RcLookupByKey(hbtTitle, (KEY) (LPVOID) &addr, NULL, pszTitle);
}
else
rc = rcSuccess + 1; // Set rc to some failure value.
RcCloseBtreeHbt(hbtTitle);
if (rc != rcSuccess || !*pszTitle) {
strcpy(pszTitle, GetStringResource(sidTitleUnknown));
}
}