/***************************************************************************** * * MISC.C * * Copyright (C) Microsoft Corporation 1990. * All Rights reserved. * ****************************************************************************** * * Module Intent * * Contains helper routines for the main window procedures * ****************************************************************************** * * Prototypes * *****************************************************************************/ #include "help.h" #pragma hdrstop #include "inc\hwproc.h" #include "inc\sbutton.h" #include "inc\hinit.h" #include "inc\winclass.h" #include "resource.h" #include "inc\helpids.h" #ifdef _DEBUG #include "inc\navpriv.h" #endif #include #include // Totally unncessary #include since prototype is in funcs.h // Reverse when building under Chicago dev environment #ifdef CHIBUILD #include "..\..\..\core\inc\help.h" #else #include "inc\helpid.h" #endif #ifndef NO_PRAGMAS #pragma data_seg(".text", "CODE") #endif const char txtWindowsHlp[] = "windows.hlp"; const char txtDefaultTopic[] = "defaulttopic"; const char txtDefault[] = "default"; const char txtThunder[] = "Thunder"; #ifndef NO_PRAGMAS #pragma data_seg() #endif /***************************************************************************** * * Prototypes * *****************************************************************************/ // static void STDCALL SetFileExistsF(BOOL); BOOL EXPORT EnumHelpWindows(HWND, LONG); INLINE static BOOL STDCALL isTitleButtonPossible(HDE hde); INLINE static VOID STDCALL GetRectSizeHde(HDE hde, LPRECT qrct); INLINE WORD STDCALL GetDETypeHde(HDE); // Global variable from printset.h extern BOOL fSetupPrinterSetup; /***************************************************************************** * * Defines * *****************************************************************************/ // Commands for EnumHelpWindows() function #define ehDestroy 0L /* * This macro determines whether or not Help can respond to an API * message. It returns wERRS_NO if it can respond; otherwise, it returns * the error message that explains why help is not available. */ #define WerrsHelpAvailable() (fMultiPrinting ? wERRS_NOHELPPS : \ (fSetupPrinterSetup ? wERRS_NOHELPPS : \ (hdlgPrint != NULL ? wERRS_NOHELPPR : wERRS_NO))) #define HWND_THUNDER (HWND) -3 #define HELP_TAB 0x000f /***************************************************************************** * * Typedefs * *****************************************************************************/ /*------------------------------------------------------------*\ | This counts the APIs as they come in. This will matter to | some functions. Sometime in the future they might be in | another file, and we'll need to make this a global. \*------------------------------------------------------------*/ static int capi; /*----------------------------------------------------------------------------+ | TopicGoto(fWhich, qv) | | Purpose: | Execute a jump to a topic. | | Arguments: | fWhich The type of jump. | qv Pointer to jump arguments. | | Returns: | Nothing | | Method: | Calls Goto for the NSR and the SR. | | Notes: | Anything which causes a topic change comes here, including within-topic | jumps. This function sets the size of the NSR for the topic. +----------------------------------------------------------------------------*/ BOOL STDCALL TopicGoto(UINT fWhich, void* qv) { int dy; HDE hdeNSR; HDE hdeTopic; RECT rcParent; /* * WARNING! As part of a scheme to repaint efficiently, this function * always calls SizeWindows(TRUE), even at a time when no valid DEs may be * around. */ hdeTopic = HdeGetEnvHwnd(ahwnd[iCurWindow].hwndTopic); hdeNSR = HdeGetEnvHwnd(ahwnd[iCurWindow].hwndTitle); // H3.5 863: Reset the tabbing focus window to the NSR when we jump hwndFocusCur = ahwnd[iCurWindow].hwndTitle; // H3.5 871: And turn off CTRL-TAB hotspots ToggleHotspots(FALSE); if (hdeNSR) { POINT pt; RECT rectNSR; RECT rectTopic; /* * Set the initial NSR window size to NSR size + topic window size. * We will likely shrink it after we know how high its contents are. * (These rectangles are in client-coordinates, so "right" and * "bottom" denote relative width and height resp.) */ GetClientRect(ahwnd[iCurWindow].hwndTitle, &rectNSR); GetWindowRect(ahwnd[iCurWindow].hwndTopic, &rectTopic); pt.x = 0; pt.y = 0; ClientToScreen(ahwnd[iCurWindow].hwndTitle, &pt); rectNSR.bottom = rectTopic.bottom - pt.y + 1; SetSizeHdeQrct(hdeNSR, &rectNSR, FALSE); /* * Layout the NSR text. * NOTE: The NSR Goto is special. It does not invalidate or repaint * the NSR window, since the window size may change afterwards. */ if (!Goto(ahwnd[iCurWindow].hwndTitle, fWhich, qv)) /* * If we got an error in the NSR, then we will almost * certainly get the same error in the SR. So, we supress the * next error message until after we've dealt with the SR. */ cPostErrorMessages = 1; /* * Resize the NSR window to minimum required size, but do not * redraw yet. This size may be zero if there is no NSR. If the topic * has no scrolling region, we let the NSR get all the space. */ if (FTopicHasSR(hdeNSR)) dy = DyGetLayoutHeightHde(hdeNSR); else dy = rectNSR.bottom; SetWindowPos(ahwnd[iCurWindow].hwndTitle, NULL, 0, 0, rectNSR.right, dy, SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOREDRAW); } /* * Resize all windows: this will also set the proper DE vars and * repaint the windows. */ GetClientRect(ahwnd[iCurWindow].hwndParent, &rcParent); SizeWindows(ahwnd[iCurWindow].hwndParent, SIZENORMAL, MAKELONG(rcParent.right, rcParent.bottom), (hdeTopic && ahwnd[iCurWindow].fAutoSize && fWhich != fGOTO_TLP_RESIZEONLY) ? FALSE : TRUE, (fWhich == fGOTO_TLP_RESIZEONLY)); if (hdeTopic) { QDE qde = QdeFromGh(hdeTopic); if (!ahwnd[iCurWindow].fAutoSize || fWhich == fGOTO_TLP_RESIZEONLY) { if (!Goto(ahwnd[iCurWindow].hwndTopic, fWhich, qv)) { cPostErrorMessages = 0; return FALSE; } #ifdef _HILIGHT if (fFTSJump) { fFTSJump= FALSE; CreateHiliteInformation(qde); } else CheckForTopicChanges(qde); #endif } else { BOOL fResult; RECT rcClient; POINT pt; // Fake the layout into thinking this is a popup. // REVIEW: 26-Sep-1993 [ralphw] What happens with a topic that // goes off the screen? qde->deType = deNote; fResult = Goto(ahwnd[iCurWindow].hwndTopic, fWhich, qv); qde->deType = deAuto; cPostErrorMessages = 0; if (!fResult) return FALSE; #ifdef _HILIGHT if (fFTSJump) { fFTSJump= FALSE; CreateHiliteInformation(qde); } else CheckForTopicChanges(qde); #endif pt = PtGetLayoutSize(qde); // Add a bit of padding at the bottom -- spec'd by Florago pt.y += 15; GetClientRect(ahwnd[iCurWindow].hwndTopic, &rcClient); if (pt.y != rcClient.bottom) { int offset = pt.y - rcClient.bottom; GetWindowRect(ahwnd[iCurWindow].hwndParent, &rcParent); rcParent.bottom += offset; // Make certain we have the current screen resolution if (!cxScreen) GetScreenResolution(); if (rcParent.bottom > 0 && rcParent.bottom < RECT_HEIGHT(rcWorkArea) - 4) { // Fake the layout into thinking this is a popup. qde->deType = deNote; MoveRectWindow(ahwnd[iCurWindow].hwndParent, &rcParent, TRUE); qde->deType = deAuto; } else { rcParent.bottom = rcWorkArea.bottom - 5; // Do NOT fake a popup -- we need the scroll bar ahwnd[iCurWindow].fAutoSize = FALSE; qde->deType = deTopic; ASSERT(!qde->fVerScrollVis); qde->fVerScrollVis = TRUE; qde->rct.right -= GetSystemMetrics(SM_CXVSCROLL); MoveRectWindow(ahwnd[iCurWindow].hwndParent, &rcParent, TRUE); TopicGoto(fGOTO_TLP_RESIZEONLY, qv); ahwnd[iCurWindow].fAutoSize = TRUE; } } else { // Make certain NSR gets redrawn TopicGoto(fGOTO_TLP_RESIZEONLY, qv); } } if (fHelpAuthor && QDE_HHDR(qde).wVersionNo >= wVersion3_1) { char szBuf[256]; #ifdef _DEBUG wsprintf(szBuf, "%u %s", qde->top.mtop.lTopicNo + 1, ahwnd[iCurWindow].pszMemberName); if (ahwnd[iCurWindow].fsOnTop & ONTOP_AUTHOREDON) { strcat(szBuf, ", on-top"); if (cntFlags.fsOnTop == ONTOP_FORCEOFF || cntFlags.fsOnTop == ONTOP_FORCEON) strcat(szBuf, " (overridden)"); } if (ahwnd[iCurWindow].fAutoSize) strcat(szBuf, ", auto-size"); #else wsprintf(szBuf, "%u%s", qde->top.mtop.lTopicNo + 1, GetStringResource(sidHelpAuthorOn)); #endif SetWindowText(ahwnd[iCurWindow].hwndParent, szBuf); } } cPostErrorMessages = 0; #ifdef RAWHIDE /* gross ugly bug #1173 hack * We track layout of search hits to detect whether to disable the * next or prev buttons in the search results dialog. Here, before any * layout drawing has taken place, we note the first and last search hits * and by default mark the buttons enabled. */ if (hdeTopic) ResultsButtonsStart(hdeTopic); else if (hdeNSR) ResultsButtonsStart(hdeNSR); #endif return TRUE; } /****************** - - Name: Goto * * Purpose: Tells Nav to display ctx. * * Arguments: hwnd - window to display the text * fWhich - What kind of jump to take * fGOTO_CTX - context jump * fGOTO_ITO - index to topic offset jump * fGOTO_TO - Go to text offset * ... - The last argument will be a CTX for fGOTO_CTX, * an index for fGOTO_ITO, and a TO for fGOTO_TO * Returns: Nothing. * ******************/ BOOL STDCALL Goto(HWND hwnd, UINT fWhich, void* qv) { HDC hdc; HDE hde; POINT pt; BOOL fRet = TRUE; #ifdef _DEBUG QDE qde; #endif // REVIEW: this would probably be the place to deal with jumps out of // a popup when our class type is HELP_POPUP. hde = HdeGetEnvHwnd(hwnd); #ifdef _DEBUG qde = QdeFromGh(hde); #endif if (!hde) return FALSE; hdc = GetDC(hwnd); if (!hdc) { PostErrorMessage(wERRS_OOM); return FALSE; } SetHDC(hde, hdc); if (!fSequence) WaitCursor(); switch (fWhich) { case fGOTO_CTX: if (!JumpCtx(hde, *(CTX *)qv)) { fRet = FALSE; if (!cPostErrorMessages) { char szMsg[256]; wsprintf(szMsg, GetStringResource(wERRS_UNKNOWN_CTX), *(CTX *)qv); AuthorMsg(szMsg, hwnd); cPostErrorMessages = 2; } else cPostErrorMessages = 1; } break; case fGOTO_ITO: JumpITO(hde, (LONG)*(INT16 *)qv); break; case fGOTO_TLP_RESIZEONLY: /* (kevynct) * This forces a re-layout, not a jump, using the DE's current * TLP; and thus does not get added to the history list, etc. But * we still do all the other Goto things, like set the cursor. It * expects that the window sizes in the DE have been set. */ ASSERT(QDE_FM(QdeFromGh(hde))); ResizeLayout(QdeFromGh(hde)); break; case fGOTO_TLP: JumpTLP(hde, *(TLP *)qv); break; case fGOTO_LA: JumpQLA(hde, (QLA) qv); break; #ifdef RAWHIDE case fGOTO_RSS: JumpSS(hde, *(GH *)qv); break; #endif case fGOTO_HASH: fRet = JumpHash(hde, *(LONG *)qv); break; default: ASSERT(FALSE); fRet = FALSE; break; } /* (kevynct) * We do not want NSR windows updated here since they must still be * resized. A minor Hack: */ if (!fSequence) RemoveWaitCursor(); if (fRet) { POINTS pts; if (GetDETypeHde(hde) != deNSR) { InvalidateRect(hwnd, NULL, TRUE); if (fWhich != fGOTO_TLP_RESIZEONLY) SendTopicInfo(hde); } // REVIEW: do we want to do this just for a relayout? if (!hwndNote) // Popups don't allow selection KillSelection(QdeFromGh(hde), FALSE); GetCursorPos(&pt); /* Fix for bug 59 (kevynct 90/05/21) * * Pt used to be always relative to the topic window origin. * Now it is relative to the current environment's window origin. */ ScreenToClient(hwnd, &pt); pts.x = (SHORT) pt.x; pts.y = (SHORT) pt.y; MouseInFrame(hde, &pts, NAV_MOUSEMOVED, 0); if (fWhich != fGOTO_TLP_RESIZEONLY) EnableDisable(hde, FALSE, iCurWindow); } SetHDC(hde, NULL); ReleaseDC(hwnd, hdc); return fRet; } /****************** - - Name: CallDialog * * Purpose: Entry point for making all dialog calls. Takes care * of making and freeing the proc instance * Arguments: hIns - application instance handle * DlgId - id of the dialog to display * Returns: hwnd - window handle of the owndr * DlgProc - dialog box proc * ******************/ int STDCALL CallDialog(int DlgId, HWND hwnd, WHDLGPROC DlgProc) { int RetVal; if (fAutoClose) { KillOurTimers(); fAutoClose = FALSE; } /*------------------------------------------------------------*\ | Some dialogs (e.g. Search) should never have multiple | instances. This will prevent it, not just as firewall, but | to solve 3.1 #1323. \*------------------------------------------------------------*/ if (fInDialog) return IDCANCEL; else fInDialog = TRUE; /* * We disable the main help windows (and thus their descendants) * during this operation because the "other" (main versus secondary) * window would otherwise remain active, and potentially cause us to * recurse, or do other things we're just not set up to handle, like * changing the topic beneath an anotate dialog. */ #ifdef _DEBUG { QDE qde = QdeFromGh(HdeGetEnv()); if (qde) { ASSERT(!(qde->fSelectionFlags & CAPTURE_LOCKED)); ASSERT(!(qde->fSelectionFlags & MOUSE_CAPTURED)); } } #endif // _DEBUG DisableWindows(); RetVal = DialogBox(hInsNow, MAKEINTRESOURCE(DlgId), IsWindowVisible(hwnd) ? hwnd : NULL, DlgProc); // Re-enable all our windows. EnableWindows(); if (RetVal == -1 && DlgId != IDDLG_DUP_BUTTON) Error(wERRS_OOM, wERRA_RETURN); fInDialog = FALSE; fKeyDownSeen = FALSE; return RetVal; } /******************* - - Name: FReplaceCloneHde * * Purpose: Service routine that replaces or clones the current HDE with * a "clean" HDE for the current help file. It not only * does this, but also messes with the menus and buttons and may * resize the topic and NSR windows. * * * Arguments: pszMember = The member name of the window to operate in * fm = The file moniker of the help file to use. * It appears that this is always disposed. * hde = Non-nil if we have already created a DE to use * as the topic DE (as in the code for the API * keywordjump command). * fReplace : TRUE => replace DE, else clone it * * Returns: TRUE iff a new HDE is put in place. * ******************/ BOOL STDCALL FReplaceCloneHde(PCSTR pszMember, FM* pfm, HDE hde, BOOL fReplace) { HDE hdeOld; TLP tlp; BOOL fShowNSR; QDE qde; HWND hwndMember; HDE hdeCur; FM fmMain; // fm displayed in the main window FM fmTmp; BOOL fMainChanged; // TRUE => file in main win changed /* * We'll need to detect later if the file displayed in the main window * has been changed. We do this by capturing the fm referred to at the * begining and comparing it against what results later. */ fmMain = NULL; hdeCur = HdeGetEnvHwnd(ahwnd[MAIN_HWND].hwndTopic); if (hdeCur) fmMain = QDE_FM(QdeFromGh(hdeCur)); if (fReplace) /* * If we are replacing a DE, then we *must* either have a DE or an * FM from which to replace. Thus we set hdeOld to be the passed DE, * from which we might get an FM if we need it. */ hdeOld = hde; else { /* * On the other hand, if we are cloning a DE, as is the case * bringing up a secondary window, then we *might* not get a DE or an * FM at all. In that case, we'll use the FM given in the "current" * DE, from which we clone. */ hdeOld = HdeGetEnv(); if (!hdeOld) hdeOld = hde; } if (pfm) { /* * See if the requested member window is already up, and if so, get * the DE associated with it. If we can, then if that DE is for the * same file we're trying to switch to, all we need do is change focus * and nothing more */ hwndMember = HwndMemberNsz(pszMember); if (hwndMember) { /* * The named window is active. That means we need to see if * the same file is already there. */ hdeCur = HdeGetEnvHwnd(hwndMember); if (hdeCur) { if (FSameFile(hdeCur, *pfm)) { /* * If the fm passed that refers to the same file is not the * exact same fm, then we can dispose of it here. */ if (QDE_FM(QdeFromGh(hdeCur)) != *pfm) RemoveFM(pfm); // Even though the files are the same, we don't want to just change // focus if: // - we're cloning a DE, which is probably for another window // - we were passed a DE. If someone passes is a DE for the // current file, they've done so on purpose, and really want // to replace the current de completely. (Like for the keyword // API). if (fReplace && !hde) { /* * Note: we ignore FFocusSz's ability or inability * to actually change focus. This is on purpose right * now because GoToBookmark can place us here with a * null member name, and a currently null secondary * window name. In all other cases, if it every * actually happens (I can't think of a case), the * worst that would happen is that the topic in the * current window would get changed, rather than that * of the named member. */ FFocusSzHde(pszMember, hdeCur, FALSE); return TRUE; } } } } } else { // no fm was passed. That means we are to use the fm from the current // de. // If there's no HDE to get the filename from, complain about it. ASSERT(hdeOld || hde); if (!hdeOld && !hde) { PostErrorMessage(wERRS_INTERNAL_ERROR); CloseHelp(); // internal error -- shut down return FALSE; } qde = QdeFromGh(hde ? hde : hdeOld); fmTmp = FmCopyFm(QDE_FM(qde)); pfm = &fmTmp; } /* We have work do do (i.e. a DE needs creation or work). The code above * will return on either some failure, or if we are asked to replace an * HDE, and the designated window is up and already contains the requested * file. * * Thus reasons for getting this far include: * * 1) We're cloning. We'll need to create clone of the HDE passed. This * happens ONLY when we create a secondary window, and coincidentally * because we've recursed due to case #3. * 2) The designated window is up, but contains a different file. In this * case we're to change the current DE to reflect the desired file. * 3) The designated member is not up. There are two subcases: * 3a) The target window is up, but is currently configured for a * different member. In this case, the call to FFocusSzHde below * will simply reconfigure the existing window and return. NOTE * that this does not imply a file change. * 3b) The target window is not up. This also has two subcases: * 3b1) The target window is the main window. It is simply * configured and shown by FFocusSzHde. * 3b2) The target window is a secondary window. FFocusSzHde * will recurse here and cause a CLONE to occur. We replace * the cloned DE with the specifics for the designated file. * * This analysis is based on usage AND the code above...other cases could * exist but are not present in the product as of this writing. */ // create a new DE if we weren't supplied with one, or if we're being // asked to clone an existing one. if (!hde || !fReplace) { hde = HdeCreate(pfm, hde, deTopic); RemoveFM(pfm); } if (!hde) { // We could not create a new de. // Turn logo back on rather than display blank screen if (HdeGetEnv() == NULL && ahwnd[iCurWindow].hwndTopic) { InvalidateRect(ahwnd[iCurWindow].hwndTopic, NULL, FALSE); } return FALSE; } // If there is a window member specified, set that as focus, and then set // that as the DE's window. /* WARNING: This function call sets up both SR and NSR windows. * It also sets the value of ahwnd[iCurWindow].hwndTopic and ahwnd[iCurWindow].hwndTitle. * Any values in the window struct which need to be reset in * the respective DEs are done later in this routine. */ hwndMember = ahwnd[iCurWindow].hwndTopic; // Delay showing main window until we jump to a topic if ((lstrcmpi(pszMember, txtMain)) == 0) fDelayShow = TRUE; FFocusSzHde(pszMember, hde, TRUE); SetHdeHwnd(hde, ahwnd[iCurWindow].hwndTopic); /* At this point, if we're going to change the contents of a particular * window, we've done it. We need to know whether or not the file displayed * in the main window has changed, so we compare the fm we got earlier * against the one that is current. */ fMainChanged = fReplace && (ahwnd[iCurWindow].hwndTopic == ahwnd[MAIN_HWND].hwndTopic) && !FSameFile(hde, fmMain); // vvv Review: KLUDGE ALERT - HACK ALERT vvv // // Part I: // // This kludge is so that history has the old TLP when JumpTLP is // called. Given the current model, there does not seem to be a // right way to do this. // // We save the old TLP, but do not set the new TLP until the end // of this routine, in case any relayouts occur due to window sizing. // // See below for Part II. // { HDE hdeT = HdeGetEnv(); if (hdeT) tlp = TLPGetCurrentQde(QdeFromGh(hdeT)); else { tlp.va.dword = vaNil; tlp.lScroll = 0L; } } // ^^^ Review: KLUDGE ALERT - HACK ALERT ^^^ if (fReplace) { // We are replacing whatever DE was current. If it exists, destroy it. if (HwndGetEnv() == ahwnd[iCurWindow].hwndTopic) DestroyHde(HdeRemoveEnv()); } /* * WARNING: because of the way we deal with FM's, and the fact that * this routine can be called recursively (one level), I beleive that the * fm we were passed may be invalid after this point, having been copied * and disposed by the recursive call (secondary windows related). This * is a) pretty fragile, and b) pretty bogus. However it's also c) * pretty complicated, and would require a pretty major redesign of the * code to clean up. For now, use FmGetHde(hde) to get the correct * current fm past this point. 27-May-1991 LeoN */ FEnlistEnv(ahwnd[iCurWindow].hwndTopic, hde); { char rgchName[MAX_PATH]; lstrcpy(rgchName, PszFromGh(QDE_FM(QdeFromGh(hde)))); // NOTE: The DLL had better copy this information if it wants to keep it InformDLLs(DW_CHGFILE, (LONG) (void *) rgchName, (LONG) ((iCurWindow == MAIN_HWND) ? 0 : ahwnd[iCurWindow].hwndParent)); } // We need to know at this point whether there might be an NSR in the // topic. We hide or show the NSR window based on isTitleButtonPossible. fShowNSR = isTitleButtonPossible(hde); ASSERT (ahwnd[iCurWindow].hwndTitle); if (fShowNSR != (BOOL) SendMessage(ahwnd[iCurWindow].hwndTitle, TIWM_GETFSHOW, 0, 0L)) SendMessage(ahwnd[iCurWindow].hwndTitle, TIWM_SETFSHOW, fShowNSR, 0L); // De-enlist and destroy the previous NSR HDE if there was one. DestroyHde(HdeDefectEnv(ahwnd[iCurWindow].hwndTitle)); if (fShowNSR) { /* * Create and enlist a new non-scrolling region HDE based on the * new topic HDE. If we are not showing the NSR window in this file, * a DE will not be enlisted. Thus it is important to always check * the return value of FSetEnv if HdeGetEnv will subsequently be * called. */ HDE hdeNSR; hdeNSR = HdeCreate(NULL, hde, deNSR); SetHdeHwnd(hdeNSR, ahwnd[iCurWindow].hwndTitle); FEnlistEnv(ahwnd[iCurWindow].hwndTitle, hdeNSR); } else { RECT rcNSR; // If there is no NSR DE, shrink the NSR window into nothing GetClientRect(ahwnd[iCurWindow].hwndTitle, &rcNSR); SetWindowPos(ahwnd[iCurWindow].hwndTitle, NULL, 0, 0, rcNSR.right, 0, SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOREDRAW); } /* * Copy values from SR and NSR window structs to their DEs, now that * these have been determined. This used to be done in FFocusSzHde, * but we need to do it in a place which has access to all DEs and which * is at a known state. * Nothing before this point should rely on these DE fields being set. * Currently this only means the background colour. */ { DWORD dw; dw = (DWORD) GetWindowLong(ahwnd[iCurWindow].hwndTopic, GTWW_COBACK); if (dw != (DWORD) coNIL) SetHdeCoBack(HdeGetEnvHwnd (ahwnd[iCurWindow].hwndTopic), dw); ASSERT (ahwnd[iCurWindow].hwndTitle); dw = (DWORD) GetWindowLong(ahwnd[iCurWindow].hwndTitle, GNWW_COBACK); if (dw != (DWORD) coNIL) SetHdeCoBack(HdeGetEnvHwnd(ahwnd[iCurWindow].hwndTitle), dw); } FSetEnv(ahwnd[iCurWindow].hwndTopic); /*--------------------- START OF WIERD SECTION ---------------------- * vvvvvvvvvvvvvvvvvvvvv vvvvvvvvvvvvvvvvvvvvv * * At this point, we have a new topic DE and a new NSR DE enlisted. * We want to: * * - Delete existing buttons and menus * - Create the core buttons * - Maybe add the browse buttons * - Update the topic and its NSR sizes in their DEs * - force a repaint with the new state. * - Update button & menu state * * We should not do any repainting until we are done adding/deleting. * * Then we want to update everything at once. * Right now we execute a bunch of macros in HdeCreate when reading * the system file. Luckily for us, we are POSTmessaging stuff and things * happen After we are through this section. We should change things * to create an executable structure in HdeCreate, to be done later. * */ // Turn off repainting in icon win if (ahwnd[iCurWindow].hwndButtonBar) SendMessage(ahwnd[iCurWindow].hwndButtonBar, WM_SETREDRAW, 0, 0L); /* * Ignore any resize messages coming from button operations Do not * repaint buttons while arranging them */ fButtonsBusy = TRUE; /* * Delete the button and menu bindings of the previous file(s). Do this * only if the file in the main window has changed. */ if (fMainChanged) { ASSERT(ahwnd[MAIN_HWND].hwndButtonBar); SendMessage(ahwnd[MAIN_HWND].hwndButtonBar, IWM_UPDBTN, UB_REFRESH, 0); SendMessage(ahwnd[MAIN_HWND].hwndParent, MSG_CHANGEMENU, MNU_RESET, 0); /* * NOTE: The buttons get added in the correct order only because * we post messages in the HdeCreate macro stuff. We need to postpone * macro execution until after we get to the initial state we want. */ CreateCoreButtons(ahwnd[MAIN_HWND].hwndButtonBar, NULL); } // SetFileExistsF(TRUE); // Turn on repainting in icon win if (ahwnd[iCurWindow].hwndButtonBar) SendMessage(ahwnd[iCurWindow].hwndButtonBar, WM_SETREDRAW, 1, 0L); // Run the config macro's only if the file in the main window has changed. if (fMainChanged) ConfigMacrosHde(hde); // Ensure icon win will be repainted with new button arrangement { RECT rct; GetClientRect(ahwnd[iCurWindow].hwndParent, &rct); SizeWindows(ahwnd[iCurWindow].hwndParent, SIZENORMAL, MAKELONG(rct.right, rct.bottom), TRUE, TRUE); } fButtonsBusy = FALSE; /* ^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^*/ /*---------------- END OF WIERD SECTION ------------------*/ // NSR rect was added? /* * vvv NOTE: KLUDGE ALERT - HACK ALERT vvv * * (kevynct) * Gross Hack -- Part II * * Note that we do not set the NSR tlp here, since * this back/history hack is only used for topic DEs. * The NSR tlp will remain tlpNil. */ if (hde != NULL) TLPGetCurrentQde(QdeFromGh(hde)) = tlp; // ^^^ NOTE: KLUDGE ALERT - HACK ALERT ^^^ // If this is the main window, we show it here. Note that configuration // macro may have turned on the fNoShow flag. return TRUE; } /******************* - - Name: SetCaptionHde * * Purpose: Sets the caption for the window * * Arguments: hde - handle to display context * hwnd - handle to window to display the topic * fPrimary - primary (not secondary) window (for MDI) * * Returns: Nothing. * ******************/ void STDCALL SetCaptionHde(HDE hde, HWND hwnd, BOOL fPrimary) { QDE qde; char rgchBuffer[MAX_PATH]; int cchTitle; if (pszHelpTitle) { SetWindowText(hwnd, pszHelpTitle); return; } else if (!fPrimary) return; // We leave secondary windows blank if (!hde) { SetWindowText(hwnd, pszCaption); return; } // NOTE: Nothing in this module should access a QDE!!! qde = QdeFromGh(hde); // If this is a 3.0 Help file, then set the caption like we used to. // REVIEW: [ralphw] is this really necessary? if (QDE_HHDR(qde).wVersionNo <= wVersion3_0) { lstrcpy(rgchBuffer, QDE_RGCHTITLE(qde)); cchTitle = strlen(rgchBuffer); LoadString(hInsNow, sidHelp_, (LPSTR) &rgchBuffer[cchTitle], (128-cchTitle)); GetFmParts(QDE_FM(qde), rgchBuffer + strlen(rgchBuffer), PARTBASE | PARTEXT); SetWindowText(hwnd, rgchBuffer); } // Otherwise, do it the new way. else { if ((QDE_RGCHTITLE(qde)[0] == '\0')) SetWindowText(hwnd, pszCaption); else SetWindowText(hwnd, QDE_RGCHTITLE(qde)); } } /******************* - - Name: SizeWindows * * Purpose: Sizes the various windows on a WM_SIZE message * * Arguments: hwnd - handle to window that just changed size * wParam - type of size change i.e., iconic (from windows) * lParam - new width, height of client area (from windows) * fRedraw- TRUE=> force redraw of window * * Returns: Nothing. * * Notes: This used to tell the navigator that the sizes had changed * but it is now up to the caller to do that. * ******************/ void STDCALL SizeWindows( HWND hwnd, // window that just changed size WPARAM wParam, // type of size change i.e., iconic LPARAM lParam, // new width, height of client area BOOL fRedraw, /* Move windows if TRUE else just set * DE rects and other internal sizes. */ BOOL fResize) { int cxClient; // new width, height of client area int cyClient; RECT rcNSR; RECT rcTopic; UINT wButtonHeight; // Height of button bar int wNSRHeight; // height of the NSR (aka title bar) int iWindow = GetWindowIndex(hwnd); if (wParam == SIZEFULLSCREEN) InformDLLs(DW_MINMAX, 2L, 0L); ASSERT(ahwnd[iWindow].hwndTitle); GetClientRect(ahwnd[iWindow].hwndTitle, &rcNSR); wNSRHeight = ((rcNSR.bottom <= 0) ? 0 : rcNSR.bottom); /* * Resize the Icon, NSR, and Topic windows. The height of the Icon * window determines how much space is left for the remaining windows. * The NSR and Topic windows will only be redrawn if the fRedraw * parameter to this function is non-zero. */ cxClient = LOWORD(lParam); cyClient = HIWORD(lParam); if (ahwnd[iWindow].hwndButtonBar && HdeGetEnv()) wButtonHeight = LOWORD(SendMessage(ahwnd[iWindow].hwndButtonBar, (fResize && fRedraw) ? IWM_RESIZE : IWM_GETHEIGHT, cxClient, 0L)); else wButtonHeight = 0; rcNSR.top = wButtonHeight; rcNSR.bottom = min(rcNSR.top + wNSRHeight, cyClient + 1); rcNSR.left = 0; rcNSR.right = cxClient; rcTopic.top = (wNSRHeight > 0) ? rcNSR.bottom : wButtonHeight; rcTopic.bottom = max(rcTopic.top, cyClient); rcTopic.left = 0; rcTopic.right = cxClient; // If there is a NSR to be shown, ensure that the window is visible, else // ensure that it is not. if (!fNoShow) { ShowWindow(ahwnd[iWindow].hwndTitle, (rcNSR.bottom > rcNSR.top) ? SW_RESTORE : SW_HIDE); ShowWindow(ahwnd[iWindow].hwndTopic, (rcTopic.bottom > rcTopic.top) ? SW_RESTORE : SW_HIDE); } MoveRectWindow(ahwnd[iWindow].hwndTitle, &rcNSR, FALSE); MoveRectWindow(ahwnd[iWindow].hwndTopic, &rcTopic, FALSE); if (fRedraw) { /* * This bit of nonsense is to get the scrollbar to redraw * correctly. Invalidate rect doesn't do it, even if you invalidate * the parent's window. By hiding and showing the scroll bar, we * ensure a valid repaint. */ QDE qde = (QDE) HdeGetEnvHwnd(ahwnd[iWindow].hwndTopic); if (qde && qde->fVerScrollVis) { ShowScrollBar(qde->hwnd, SB_VERT, FALSE); ShowScrollBar(qde->hwnd, SB_VERT, TRUE); } InvalidateRect(ahwnd[iWindow].hwndTitle, NULL, TRUE); InvalidateRect(ahwnd[iWindow].hwndTopic, NULL, TRUE); } InformDLLs(DW_SIZE, lParam, (DWORD) ahwnd[iWindow].hwndParent); // WARNING! As part of a scheme to repaint efficiently, this function // also handles the case where no valid DEs may be around. // // Note also that these calls to SetSizeHdeQrct do not actually lay // anything out at this point in time. That's handled later. // REVIEW: [ralphw] -- Is the client rect really a different // size? GetClientRect(ahwnd[iWindow].hwndTitle, &rcNSR); SetSizeHdeQrct(HdeGetEnvHwnd(ahwnd[iWindow].hwndTitle), &rcNSR, FALSE); GetClientRect(ahwnd[iWindow].hwndTopic, &rcTopic); SetSizeHdeQrct(HdeGetEnvHwnd(ahwnd[iWindow].hwndTopic), &rcTopic, FALSE); } /*************************************************************************** FUNCTION: DispatchProc PURPOSE: Called from USER to process a WinHelp API function call PARAMETERS: hwnd -- window handle of caller hWinhlp -- data handle RETURNS: COMMENTS: Chicago's user thinks we're a 16-bit app, so they send us a 16-bit global memory handle, which we convert to 32-bits with the internal GlobalLock16() function. This should also work in NT if we replace winhelp.exe instead of winhlp32.exe, but it will probably die miserably if we try this stunt when called from a USER pass a 32-bit flat handle. The only hope would be if GlobalLock16 was/is smart enough to recognize that we're dealing with a flat handle instead of a segmented handle. MODIFICATION DATES: 22-Jul-1994 [ralphw] ***************************************************************************/ #define ptDst ((POINT *) (&qwinhlp + qwinhlp->offabData)) BOOL STDCALL DispatchProc(HWND hwnd, HGLOBAL hWinhlp) { LPWINHLP qwinhlp; GH ghHlp; QHLP qhlp; int i; RC rc; DWORD lTimestamp; HDE hde = HdeGetEnv(); QDE qde; HWND hwndTopics = GetTopicsDlgHwnd(); if (hde != NULL && !hwndTopics) { qde = QdeFromGh(HdeGetEnv()); rc = RcTimestampHfs(QDE_HFS(qde), &lTimestamp); if (rc != rcSuccess) { /* * Some FS error has occurred: it will not be handled here. */ } else if (lTimestamp != QDE_LTIMESTAMP(qde)) { /* * This file has changed since we lost focus. Put up an error * message and go away. The reason we don't attempt to stick * around and display the contents is that it's messy to get rid * of the old DE and create a new one. */ ErrorFileChanged(qde); return FALSE; } } capi++; if (!HIWORD(hWinhlp) && (!pGlobalLock16 || !pWOWGetVDMPointerFix)) { if (!LoadLockFunctions()) return FALSE; } /* * REVIEW: can we rely on the upper word being non-NULL if it is a * 16-bit memory handle? We certainly don't want to call this * under NT when handed a 32-bit memory handle. */ #ifdef _X86_ if (fIsThisChicago) qwinhlp = !HIWORD(hWinhlp) ? (LPWINHLP) pWOWGetVDMPointerFix(MAKELONG(0, hWinhlp), 0, TRUE) : (LPWINHLP) hWinhlp; else qwinhlp = !HIWORD(hWinhlp) ? (LPWINHLP) pGlobalLock16(hWinhlp) : (LPWINHLP) hWinhlp; #else qwinhlp = (LPWINHLP) hWinhlp; #endif // Save the current command in case it matters later. usCurrentCommand = qwinhlp->usCommand; /* * If the helpfile offset is at or past the end of the size of the data * struct, zap it, because there really is no helpfile. This avoids a bug * in windows where if the WinHelp API caller passes NULL for a filename, * the field is set to sizeof(HLP) anyway. */ if (qwinhlp->offszHelpFile >= qwinhlp->cbData) qwinhlp->offszHelpFile = 0; if (fHelp == TCARD_HELP) { if (!hwndTCApp) hwndTCApp = hwnd; else if (hwndTCApp != hwnd) { // we've got a new app asking for training cards. char szBuf[256]; wsprintf(szBuf, "W:%u %d %d\r\n", hwndTCApp, HELP_TCARD_OTHER_CALLER, 0); SendStringToParent(szBuf); SendMessage(hwndTCApp, WM_TCARD, HELP_TCARD_OTHER_CALLER, 0); } } /* * REVIEW: 27-Aug-1994 [ralphw] It would be nice to simply blast the * existing Topics dialog and put up a new one. However, under Chicago, * if we have the topics dialog up and call pGlobalLock16() we get * terminated. I don't know why, and this solution works -- notify the * user that they have to shut down existing help to get new help and * then switch the focus to the new help. */ if (hwndTopics && usCurrentCommand != HELP_QUIT) { fNoQuit = TRUE; SendMessage(hwndTopics, WM_COMMAND, IDCANCEL, 0); #if 0 if (!HIWORD(hWinhlp)) { if (fIsThisChicago) pWOWGetVDMPointerUnfix(MAKELONG(0, hWinhlp)); else pGlobalUnlock16(hWinhlp); } SetWindowPos(hwndTopics, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); ASSERT(!hwndAnimate); hwndAnimate = hwndTopics; // make ErrorHwnd() use hwndTopics Error(wERRS_HELP_RUNNING, wERRA_RETURN); hwndAnimate = NULL; SetWindowPos(hwndTopics, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); return FALSE; #endif } if ((fHelp == TCARD_HELP || fHelp == STANDARD_HELP) && qwinhlp->usCommand != HELP_QUIT) { if (hwnd != NULL) { char szClass[256]; HWND hwndTmp = hwnd; while ((hwndTmp = GetParent(hwndTmp))) hwnd = hwndTmp; GetClassName(hwnd, szClass, sizeof(szClass)); if (_strnicmp(szClass, txtThunder, strlen(txtThunder)) == 0) hwnd = HWND_THUNDER; // Find entry in table (if exists) for (iasCur = 0; iasCur < iasMax; iasCur++) if (aAppHwnd[iasCur] == hwnd) break; if (iasCur == iasMax) // Was entry found? iasCur = -1; /* * Insert in table if first time. Do not insert in table if we * are not going to be able to respond to this help request. */ if ((iasCur == -1) && (iasMax < MAX_APP) && WerrsHelpAvailable() == wERRS_NO) { aAppHwnd[iasMax] = hwnd; iasCur = iasMax++; } } } switch (qwinhlp->usCommand) { case HELP_QUIT: /* * -1 is An absolute way to kill help for CBTs. Don't bother checking * for anything else. * * 4.0: We also kill help if we're a training card. */ if (qwinhlp->ctx == -1L || fHelp == TCARD_HELP) fNoHide = TRUE; else { // fNoQuit means we shouldn't. Used during initialization. if (fNoQuit) break; // Remove from table (if found) if (iasMax > 0) { char szClass[256]; HWND hwndTmp = hwnd; while ((hwndTmp = GetParent(hwndTmp))) hwnd = hwndTmp; GetClassName(hwnd, szClass, sizeof(szClass)); if (_strnicmp(szClass, txtThunder, strlen(txtThunder)) == 0) hwnd = HWND_THUNDER; for (i = 0; i < iasMax; i++) { if (aAppHwnd[i] == hwnd || !IsWindow(aAppHwnd[i])) { if (i != iasMax - 1) { MoveMemory(&aAppHwnd[i], &aAppHwnd[i + 1], (iasMax - i - 1) * sizeof(HWND)); } iasMax--; break; } } } // Do not kill help if table of client apps is non-empty if (iasMax) { DBWIN("HELP_QUIT ignored") break; } } // fall through to post the message (and make sure printer is not up) case HELP_SETCONTENTS: /* * This call should always be accompanied with another API call. * Thus, if help is unavailable, we should only display one message to * that effect. */ if (WerrsHelpAvailable() != wERRS_NO) break; // else fall through default: /* * Ensure that dialogs are (coming) down before we post the * message. Basically, we cannot process messages with them up, * because that can cause recursion grief. */ if (FDestroyDialogsHwnd(ahwnd[MAIN_HWND].hwndParent, FALSE)) { if (qwinhlp->usCommand != HELP_QUIT && qwinhlp->usCommand != cmdTerminate) { /* * If we're getting a command from an app other than * the one most recently using us, close the secondary * window if it exists. */ if (hwnd != hwndLatest) DestroyAllSecondarys(); hwndLatest = hwnd; /* * If we are in fact enabled and visible, make sure * we're up and have the focus */ if (IsWindowEnabled(ahwnd[iCurWindow].hwndParent) && IsWindowVisible(ahwnd[iCurWindow].hwndParent)) { if (IsIconic(ahwnd[iCurWindow].hwndParent)) // This message simulates double-clicking on the icon. SendMessage(ahwnd[iCurWindow].hwndParent, WM_SYSCOMMAND, SC_RESTORE, 0); // We don't want to grab the focus on a JumpIDNoFocus() API. if (qwinhlp->usCommand != cmdIdNoFocus && qwinhlp->usCommand != cmdPWinNoFocus && hwndTCApp == NULL) { SetForegroundWindow(ahwnd[iCurWindow].hwndParent); SetFocus(ahwnd[iCurWindow].hwndParent); } } } ghHlp = GhAlloc(GPTR, sizeof(HLP) + ((LONG) qwinhlp->cbData) - sizeof(WINHLP)); if (ghHlp == NULL) { PostErrorMessage(wERRS_OOM); break; } qhlp = PtrFromGh(ghHlp); qhlp->hins = (HINSTANCE) ((hwnd != NULL) ? GetWindowLong(hwnd, GWL_HINSTANCE) : NULL); #ifdef _DEBUG { char szExecutableName[MAX_PATH]; DWORD pID; GetWindowThreadProcessId(hwnd, &pID); if (!GetModuleFileName((HMODULE)pID, szExecutableName, sizeof(szExecutableName))) GetLastError(); else GetLastError(); } #endif MoveMemory(&qhlp->winhlp, qwinhlp, (LONG) qwinhlp->cbData); // Close .GID if we're switching help files. if (qhlp->winhlp.offszHelpFile > 0) { #ifdef _DEBUG PSTR pszNewFile = (LPSTR) (&qhlp->winhlp) + qhlp->winhlp.offszHelpFile; #endif if (hfsGid && !IsCurrentFile((LPSTR) (&qhlp->winhlp) + qhlp->winhlp.offszHelpFile)) CloseGid(); } GenerateMessage(MSG_EXECAPI, 0, (LPARAM) ghHlp); } break; } #ifdef _X86_ if (!HIWORD(hWinhlp)) { if (fIsThisChicago) pWOWGetVDMPointerUnfix(MAKELONG(0, hWinhlp)); else pGlobalUnlock16(hWinhlp); } #endif return TRUE; } /******************* - - Name: ExecAPI * * Purpose: Dispatches commands sent by the application requesting * services from help. This routine handles the message after * it has been posted back to WinHelp by DispatchProc. * * Arguments: qhlp - far pointer to a help structure * * Returns: Nothing. * ******************/ BOOL STDCALL ExecAPI(QHLP qhlp) { HDE hde; FM fmTemp = NULL; FM fmCopy = NULL; LPSTR pszKey; char chBtreePrefix; HASH hash; char szWindowName[cchWindowMemberMax]; // member name parsed from request HWND hwndT; PWININFO pwininfo; PSTR pszTitle; PSTR pszMember; char szReplaceTitle[MAX_PATH]; // If we're quiting, we don't care about the filename if (qhlp->winhlp.usCommand == HELP_QUIT || qhlp->winhlp.usCommand == cmdTerminate) { SendStringToParent("HELP_QUIT\r\n"); /* * We assume that if no windows are being displayed, then we are in * the process of initializing and cannot be closed. */ if (!hwndAnimate && AreAnyWindowsVisible(0) >= 0) QuitHelp(); return TRUE; } KillOurTimers(); // Always assume the main window unless instructed otherwise strcpy(szWindowName, txtMain); // Save the current command in case it matters later. usCurrentCommand = qhlp->winhlp.usCommand; // First, we extract the filename and member name from qhlp, and put it in // the local variables fmTemp and szWindowName. if ( (qhlp->winhlp.usCommand != HELP_COMMAND || *((LPSTR) (&qhlp->winhlp) + qhlp->winhlp.offszHelpFile))&& qhlp->winhlp.usCommand != HELP_SETWINPOS && qhlp->winhlp.usCommand != cmdPWinNoFocus && qhlp->winhlp.usCommand != cmdFocusWin && qhlp->winhlp.usCommand != cmdCloseWin && qhlp->winhlp.offszHelpFile > 0) { pszTitle = (LPSTR) (&qhlp->winhlp) + qhlp->winhlp.offszHelpFile; if (_strnicmp(pszTitle, txtWinHelp, strlen(txtWinHelp)) == 0) { strcpy(szReplaceTitle, txtHelpOnHelp); pszTitle = szReplaceTitle; pszMember = NULL; } else { strcpy(szReplaceTitle, pszTitle); pszTitle = szReplaceTitle; pszMember = SzFromSzCh(pszTitle, WINDOWSEPARATOR); } if (pszMember) { lstrcpyn(szWindowName, pszMember + 1, cchWindowMemberMax); *pszMember = '\0'; } // Check for C:\\server\share -- remove drive portion if (strncmp(pszTitle + 1, ":\\\\", 3) == 0) { lstrcpy(pszTitle, pszTitle + 2); } // 4.0 -- if no extension, add .HLP if (*pszTitle && !StrChrDBCS(pszTitle, '.')) { strcpy(szReplaceTitle, pszTitle); ChangeExtension(szReplaceTitle, txtHlpExtension); pszTitle = szReplaceTitle; } // We check for the target file in the following directories... NoContext: fmTemp = NULL; hde = HdeGetEnv(); if (hde) { QDE qde; qde = QdeFromGh(hde); if (!*pszTitle) fmTemp = FmNew(QDE_FM(qde)); else fmTemp = FmNewSameDirFmSz(QDE_FM(qde), pszTitle); } if (!fmTemp || !FExistFm(fmTemp)) { DisposeFm(fmTemp); // Now try all of our standard locations fmTemp = FmNewExistSzDir(pszTitle, DIR_INI | DIR_CURRENT | DIR_PATH | DIR_SILENT_REG); } if ((!fmTemp || !FExistFm(fmTemp)) && (qhlp->hins != NULL)) { // Try to find the help file in the same directory as the // caller. char szExecutableName[MAX_PATH]; FM fmExecutable; DisposeFm(fmTemp); if (GetModuleFileName(qhlp->hins, szExecutableName, sizeof(szExecutableName)) || GetModuleFileName16(qhlp->hins, szExecutableName, sizeof(szExecutableName))) { char szFilePart[MAX_PATH]; fmExecutable = FmNewSzDir(szExecutableName, DIR_NIL); GetFmParts(pszTitle, szFilePart, PARTBASE | PARTEXT); fmTemp = FmNewSameDirFmSz(fmExecutable, szFilePart); DisposeFm(fmExecutable); } } // NOTE: The tutorial from dBase for Windows 5.0 passes in a null // helpfile name, so of course we won't find the help file. But, we // don't want to terminate yet-- the 16-bit winhelp still processed // the usCommand. if ((!fmTemp || !FExistFm(fmTemp)) && *pszTitle != '\0') { DisposeFm(fmTemp); fmTemp = FindThisFile(pszTitle, TRUE); // If we still can't find it, then die if (!fmTemp || !FExistFm(fmTemp)) { ErrorVarArgs(wERRS_FNF, wERRA_RETURN, pszTitle); if (!hde) // If we don't have a current help file, then die QuitHelp(); return FALSE; } } } // Carry out the given command. switch (qhlp->winhlp.usCommand) { case HELP_CONTEXT: // Show the passed topic id SendStringIdHelp("HELP_CONTEXT", qhlp->winhlp.ctx, fmTemp, szWindowName); if (FReplaceHde(szWindowName, &fmTemp, NULL)) { ASSERT(!fmTemp); fSupressErrorJump = TRUE; if (!TopicGoto(fGOTO_CTX, (LPVOID) &qhlp->winhlp.ctx)) { FlushMessageQueue(0); // so that the error message gets displayed QuitHelp(); return FALSE; } fSupressErrorJump = FALSE; } ASSERT(!fmTemp); break; case HELP_CONTEXTPOPUP: SendStringIdHelp("HELP_CONTEXTPOPUP", qhlp->winhlp.ctx, fmTemp, NULL); fSupressErrors = TRUE; if (!hwndNote && !ShowNote(fmTemp, NULL, qhlp->winhlp.ctx, fGOTO_CTX)) { FlushMessageQueue(0); // get rid of any error messages // Topic not found, so tell them there is no help for the control. // REVIEW: [ralphw] Should we just give an error? if (qhlp->winhlp.ctx != IDH_MISSING_CONTEXT) { pszTitle = (LPSTR) txtWindowsHlp; qhlp->winhlp.ctx = IDH_MISSING_CONTEXT; goto NoContext; } else { fSupressErrors = FALSE; Error(wERRS_NOTOPIC, wERRA_RETURN); fSupressErrors = TRUE; } QuitHelp(); return FALSE; } fSupressErrors = FALSE; break; case cmdTLP: // jump based on TLP if (FReplaceHde(szWindowName, &fmTemp, NULL)) #ifdef _X86_ TopicGoto(fGOTO_TLP, (LPVOID) &((TLPHELP *) ((QB) (&qhlp->winhlp) + qhlp->winhlp.offabData))->tlp); #else { TLP tlptmp; MoveMemory(&tlptmp, (LPVOID) &((TLPHELP *) ((QB) (&qhlp->winhlp) + qhlp->winhlp.offabData))->tlp,sizeof(TLP)); TopicGoto(fGOTO_TLP, &tlptmp); } #endif #ifdef _HILIGHT fFTSJump= FALSE; #endif ASSERT(!fmTemp); break; // Set index to other than default case HELP_SETCONTENTS: SendStringIdHelp("HELP_SETCONTENTS", qhlp->winhlp.ctx, fmTemp, NULL); /* Review: Should we make sure that the fm is correct, in case * an application is ill behaved? */ if ((hde = HdeGetEnv()) && FSameFile(hde, fmTemp)) SetIndex(hde, qhlp->winhlp.ctx); else // If we just started up, we are invisible. Let's go away. if (!IsWindowVisible(ahwnd[iCurWindow].hwndParent)) CloseHelp(); break; case HELP_FORCEFILE: SendStringHelp("HELP_FORCEFILE", fmTemp, szWindowName); hwndT = HwndMemberNsz(szWindowName); hde = HdeGetEnvHwnd(hwndT); if (hde && FSameFile(hde, fmTemp)) { DisposeFm(fmTemp); break; } goto ForceContents; case HELP_CONTENTS: SendStringHelp("HELP_CONTENTS", fmTemp, szWindowName); ForceContents: // REVIEW: [ralphw] Should show Contents tab if available if (FReplaceHde(szWindowName, &fmTemp, NULL)) { INT16 topic = 0; // MUST REMAIN 16 bits! TopicGoto(fGOTO_ITO, &topic); /* * Some apps like WinWord 6.0 think help has failed if * they can't find the help window after calling * HELP_FORCEFILE or perhaps even HELP_CONTENTS. So, we flush * out our message queue here before returning, thus * ensuring that a help window is actually showing before * returning. */ FlushMessageQueue(WM_USER); } ASSERT(!fmTemp); break; case cmdSrchSet: #if 0 if (FReplaceHde(szWindowName, &fmTemp, NULL)) TopicGoto(fGOTO_RSS, (LPVOID) &qhlp->winhlp.ctx); ASSERT(!fmTemp); #endif break; case cmdHash: #ifdef _DEBUG SendStringIdHelp("HELP_HASH", qhlp->winhlp.ctx, fmTemp, szWindowName); goto HashCommand; #endif case cmdHashPopup: #ifdef _DEBUG SendStringIdHelp("HELP_HASH_POPUP", qhlp->winhlp.ctx, fmTemp, szWindowName); goto HashCommand; #endif case cmdId: case cmdIdPopup: case cmdIdNoFocus: #ifdef _DEBUG HashCommand: #endif if ((qhlp->winhlp.usCommand == cmdId) || (qhlp->winhlp.usCommand == cmdIdPopup) || (qhlp->winhlp.usCommand == cmdIdNoFocus)) { pszKey = (LPSTR) (&qhlp->winhlp) + qhlp->winhlp.offabData; if (FValidContextSz(pszKey)) hash = HashFromSz(pszKey); else hash = 0L; } else hash = qhlp->winhlp.ctx; if ((qhlp->winhlp.usCommand == cmdId) || (qhlp->winhlp.usCommand == cmdHash) || (qhlp->winhlp.usCommand == cmdIdNoFocus)) { if (fHelp == POPUP_HELP) { WinHelp((iasMax != 1 && IsValidWindow(aAppHwnd[0])) ? aAppHwnd[0] : NULL, fmTemp, qhlp->winhlp.usCommand, (qhlp->winhlp.usCommand == cmdHash ? hash : (DWORD) pszKey)); RemoveFM(&fmTemp); QuitHelp(); } else if (FReplaceHde(szWindowName, &fmTemp, NULL)) TopicGoto(fGOTO_HASH, (LPVOID) &hash); ASSERT(!fmTemp); } else ShowNote(fmTemp, NULL, hash, fGOTO_HASH); #ifdef _HILIGHT fFTSJump= FALSE; #endif break; case HELP_FORCE_GID: // undocumented for 4.0, required by VBA CloseGid(); FindGidFile(fmTemp, FALSE, 0); if (!hfsGid) QuitHelp(); DisposeFm(fmTemp); break; case HELP_HELPONHELP: SendStringToParent("HELP_HELPONHELP\r\n"); /* * Do a back flip: call JumpIndex on our help on help file. * the selected help on help file. This will fly back to the * above case cmdIndex. Any OOM will be handled by the * appropriate routines. */ JumpHOH(HdeGetEnvHwnd(ahwnd[iCurWindow].hwndTopic)); break; case cmdFocus: ASSERT(ahwnd[iCurWindow].hwndParent != NULL); ShowWindow(ahwnd[iCurWindow].hwndParent, SW_SHOWNORMAL); break; // Show first topic for Keyword case HELP_KEY: SendStringHelp("HELP_KEY", fmTemp, szWindowName); goto CmdKey; case HELP_MULTIKEY: SendStringHelp("HELP_MULTIKEY", fmTemp, szWindowName); goto CmdKey; case HELP_PARTIALKEY: SendStringHelp("HELP_PARTIALKEY", fmTemp, szWindowName); CmdKey: /* cmdKey: * Locate Keyword & Goto first ocurrance * else put up message box. * * cmdMultiKey: * Locate Keyword & Goto first ocurrance * else look up "defaultkeyword" * else look up "default" * else put up message box. * * cmdPartialKey: * Locate Keyword * If one ocurrance: go to it * If >1 ocurrance, put up search dialog, with search completed * If no ocurrances, put up search dialog */ // Valid fm required here. if (!fmTemp) { if (pszTitle) ErrorVarArgs(wERRS_FNF, wERRA_RETURN, pszTitle); else PostErrorMessage(wERRS_NOHELP_FILE); break; } fmCopy = FmCopyFm(fmTemp); RemoveFM(&fmCaller); // remove any previous filename fmCaller = FmCopyFm(fmCopy); // save this filename if (IsWindowVisible(ahwnd[MAIN_HWND].hwndParent)) { hwndT = HwndMemberNsz(txtMain); hde = HdeGetEnvHwnd(hwndT); if (hde && !FSameFile(hde, fmTemp)) { ShowWindow(ahwnd[MAIN_HWND].hwndParent, SW_HIDE); } } fNoShow = TRUE; // don't allow main window to be shown hde = HdeGetEnv(); if (hfsGid && (!hde || !FSameFile(hde, fmTemp))) { SaveGidPositions(); CloseGid(); } FReplaceHde(txtMain, &fmTemp, NULL); fNoShow = FALSE; if (!hfsGid) FindGidFile(fmTemp, TRUE, 0); RemoveFM(&fmTemp); pszKey = (LPSTR) (&qhlp->winhlp) + qhlp->winhlp.offabData; if (qhlp->winhlp.usCommand == HELP_MULTIKEY) { /* * If the high word of the structure size is non-zero, * then we have been called by a 16-bit app, not a 32-bit * app. */ if (HIWORD(((LPMULTIKEYHELP) pszKey)->mkSize)) { chBtreePrefix = ((MULTIKEYHELP16*) pszKey) ->mkKeylist; pszKey = ((MULTIKEYHELP16*) pszKey)->szKeyphrase; } else { chBtreePrefix = ((LPMULTIKEYHELP) pszKey) ->mkKeylist; pszKey = ((LPMULTIKEYHELP) pszKey)->szKeyphrase; } chBtreePrefix = (char) CharUpper((LPSTR) chBtreePrefix); } else { PSTR pszSemi; chBtreePrefix = 'K'; strcpy(szSavedKeyword, pszKey); if ((pszSemi = StrChrDBCS(szSavedKeyword, ';'))) *pszSemi = '\0'; } SendStringToParent("\t\""); SendStringToParent(pszKey); SendStringToParent("\"\r\n"); if (!*pszKey || !doAlink(pszKey, AFLAG_JUMP_ON_SINGLE | AFLAG_NO_FAIL_CLOSE | (qhlp->winhlp.usCommand == HELP_PARTIALKEY || qhlp->winhlp.usCommand == HELP_MULTIKEY ? AFLAG_INDEX_ONLY : 0), 0, chBtreePrefix, pszMember)) { int result; if (qhlp->winhlp.usCommand == HELP_MULTIKEY) { PostErrorMessage(wERRS_BADKEYWORD); } else { /* * Avoid the temptation to use doTabSearch() as the * first parameter. cntFlags.fUseGlobalIndex can change in * the process of calling doTabSearch, and must, * therefore, be specified AFTER doTabSearch() is called. * We should not rely on order of evaluation, hence the * two lines. */ cntFlags.idOldTab = 1; // force the index tab result = doTabSearch(); CompleteSearch(result, (!hfsGid || !cntFlags.fUseGlobalIndex)); } } break; case HELP_COMMAND: SendStringHelp("HELP_COMMAND", fmTemp, szWindowName); SendStringToParent("\t"); SendStringToParent((LPSTR)(&qhlp->winhlp) + qhlp->winhlp.offabData); SendStringToParent(txtCR); fAniOwner = TRUE; /* * Resist the temptation to call FReplaceHde() here -- if you do, * Word 6.0 help will lock up. See Raid #19945. 12-Dec-1994 [ralphw] */ #if 0 if (!HdeGetEnv()) { fNoShow = TRUE; // don't allow main window to be shown FReplaceHde(szWindowName, &fmTemp, NULL); fNoShow = FALSE; } else #endif /* * Some macros absolutely must have a DE filled in. If we * were called without creating a window, then there is no * such DE. If it wasn't for Word 6.0 relying on there being * no window, we could just call FReplaceHde above. Instead, * any macro needing DE must create it from fmCaller if * the can't get one. */ if (_strnicmp("tab(", (LPSTR)(&qhlp->winhlp) + qhlp->winhlp.offabData, 4)==0) { qhlp->winhlp.ctx = (LONG) ((BYTE)*((LPSTR)&qhlp->winhlp + qhlp->winhlp.offabData + 4)) - 0x31; qhlp->winhlp.usCommand = HELP_TAB; goto DoFinderThing; } RemoveFM(&fmCaller); // remove any previous filename fmCaller = fmTemp; Execute((LPSTR)(&qhlp->winhlp) + qhlp->winhlp.offabData); break; /* * cmdPositionWin, cmdFocusWin, and cmdCloseWin take the data in * the struct at the end of the HLP block and repackage it into a * local WININFO handle. This data is then given to InformWindows() */ case HELP_SETWINPOS: case cmdPWinNoFocus: SendStringToParent("HELP_SETWINPOS\r\n"); pszKey = (LPSTR)(&qhlp->winhlp) + qhlp->winhlp.offabData; /* * If the high word of the structure size is non-zero, * then we have been called by a 16-bit app, not a 32-bit * app. */ { int cbMember; if (!HIWORD(((PHELPWININFOA) pszKey)->wStructSize)) cbMember = strlen(((PHELPWININFOA) pszKey)->rgchMember); else cbMember = strlen(((PWININFO) pszKey)->rgchMember); // sizeof(WININFO) includes +2 for the string pwininfo = lcMalloc(sizeof(WININFO) + cbMember); if (HIWORD(((PHELPWININFOA) pszKey)->wStructSize)) { CopyMemory(pwininfo, pszKey, ((PWININFO) pszKey)->wStructSize); } else { pwininfo->x = (INT16) ((PHELPWININFOA) pszKey)->x; pwininfo->y = (INT16) ((PHELPWININFOA) pszKey)->y; pwininfo->dx = (INT16) ((PHELPWININFOA) pszKey)->dx; pwininfo->dy = (INT16) ((PHELPWININFOA) pszKey)->dy; pwininfo->wMax = (INT16) ((PHELPWININFOA) pszKey)->wMax; strcpy(pwininfo->rgchMember, ((PHELPWININFOA) pszKey)->rgchMember); } { char szBuf[200]; wsprintf(szBuf, "\t%s> %u %u %u %u\r\n", pwininfo->rgchMember, pwininfo->x, pwininfo->y, pwininfo->dx, pwininfo->dy); SendStringToParent(szBuf); } InformWindow(IFMW_MOVE, pwininfo); } break; case cmdFocusWin: case cmdCloseWin: pszKey = (LPSTR)(&qhlp->winhlp) + qhlp->winhlp.offabData; // Note silent failure in OOM case if ((pwininfo = (PWININFO) LhAlloc(LMEM_FIXED, ((QWININFO) pszKey) ->wStructSize)) != NULL) { lstrcpy(pwininfo->rgchMember, pszKey); if (qhlp->winhlp.usCommand == cmdCloseWin) InformWindow(IFMW_CLOSE, pwininfo); else InformWindow(IFMW_FOCUS, pwininfo); } break; case HELP_TAB: goto DoFinderThing; case HELP_FINDER: SendStringHelp("HELP_FINDER", fmTemp, NULL); DoFinderThing: iCurWindow = MAIN_HWND; DestroyAllSecondarys(); if (ahwnd[MAIN_HWND].hwndParent) ShowWindow(ahwnd[MAIN_HWND].hwndParent, SW_HIDE); fNoShow = TRUE; // don't allow main window to be shown RemoveFM(&fmCaller); // remove any previous filename fmCaller = FmCopyFm(fmTemp); // save this filename if (FReplaceHde(txtZeroLength, &fmTemp, NULL)) { if (!hfsGid) { FindGidFile(fmCaller, FALSE, 0); FSetEnv(ahwnd[MAIN_HWND].hwndTopic); } fNoShow = FALSE; if (qhlp->winhlp.usCommand == HELP_TAB) cntFlags.idOldTab = 2 + ((IFW_TAB1 + qhlp->winhlp.ctx) - IFW_FIND); Finder(); } else QuitHelp(); ASSERT(!fmTemp); break; #ifndef HELP_SETPOPUP_POS #define HELP_SETPOPUP_POS 0x000d #endif case HELP_SETPOPUP_POS: ptPopup.x = LOWORD(qhlp->winhlp.ctx); ptPopup.y = HIWORD(qhlp->winhlp.ctx); { char szBuf[200]; wsprintf(szBuf, "HELP_SETPOPUP_POS: %u %u\r\n", ptPopup.x, ptPopup.y); SendStringToParent(szBuf); } break; default: #ifdef _DEBUG { char szMsg[256]; wsprintf(szMsg, "Unknown command: %d\n", qhlp->winhlp.usCommand); AuthorMsg(szMsg, NULL); } #endif break; } DisposeFm(fmCopy); return TRUE; } /******************* - - Name: FDestroyDialogsHwnd * * Purpose: Attempts to destroy all other popup windows that Help has * created. If the TRUE flag is set, then it also uniconizes * help, brings it to the front, and sets the focus to help. * * Arguments: hwnd - Help window to be activated. * fFocus - Set to true if the function is to uniconize and * set the focus to help. * * Returns: TRUE if successful, FALSE if help is not available. * * Notes: This function replaces the RegisterDialog() stuff * ******************/ BOOL STDCALL FDestroyDialogsHwnd(HWND hwnd, BOOL fFocus) { int werrs; if (fFocus && IsIconic(hwnd)) { // This message simulates double-clicking on the icon. SendMessage(hwnd, WM_SYSCOMMAND, SC_RESTORE, 0); SetFocus(hwnd); // may be redundant SetForegroundWindow(hwnd); return TRUE; } werrs = WerrsHelpAvailable(); if (werrs != wERRS_NO && !fMultiPrinting) { Error(werrs, wERRA_RETURN); return FALSE; } EnumTaskWindows(GetCurrentThreadId(), EnumHelpWindows, ehDestroy); if (fFocus) { SetFocus(hwnd); SetForegroundWindow(hwnd); } return TRUE; } /******************* - - Name: EnumHelpWindows * * Purpose: This is a windows call-back function for enumerating * all the windows in help, and performing the specified * task with them. Tasks are: * ehDestroy: Destroy all unnecessary windows by * sending a WM_COMMAND (IDCANCEL) message. * qHwnd: Returns the frontmost window in the * given pointer. * * Arguments: hwnd - window being enumerated. * ehCmd - Command to perform. * ******************/ BOOL EXPORT EnumHelpWindows(HWND hwnd, LONG ehCmd) { int i; if (ehCmd != ehDestroy) { *((HWND *) ehCmd) = hwnd; return FALSE; } for (i = 0; i < MAX_WINDOWS; i++) { if (hwnd == ahwnd[i].hwndParent) return TRUE; } if (hwnd == hwndNote || hwnd == hdlgPrint || hwnd == hwndHistory) return TRUE; // Check for hidden or bogus hwnd's if (!IsWindowVisible(hwnd)) return TRUE; SendMessage(hwnd, WM_COMMAND, IDCANCEL, 0L); return TRUE; } /******************* - - Name: EnableDisable * * Purpose: Enables/Disables all the menu items and icons based * on the state of the world. * * Arguments: hde - handle to display environment. nilHde forces all * all buttons to be refreshed * fForce - TRUE => ignore DE state information, and refresh * buttons anyway. This is required because some calls * are made which change button state without changing * DE state, such as macros which add buttons. * * Returns: Nothing. * * Note: Back and History aren't associated with the flags in * the HDE. Currently, we always make a function call * for each to set enable/disable state. Big deal. * ******************/ #define FIsNoteHde(hde) (GetDETypeHde(hde) == deNote) #define FIsNSRHde(hde) (GetDETypeHde(hde) == deNSR) void STDCALL EnableDisable(HDE hde, BOOL fForce, int iWindow) { STATE stateChange; STATE stateCur; #ifdef _DEBUG BOOL fNote = FIsNoteHde(hde); BOOL fNSR = FIsNSRHde(hde); #endif ASSERT(hde); /* * Don't change button state if this is a glossary (note), or a * non-scrolling region. Auto-sizing will make this look like * a note, even though it isn't, so if it looks like a note but * there is no note window, then process normally. */ if ((FIsNoteHde(hde) && hwndNote) || FIsNSRHde(hde)) return; if (FGetStateHde(hde, &stateChange, &stateCur) || fForce) { if (fForce) stateChange |= NAV_INDEX | NAV_SEARCHABLE | NAV_NEXTABLE | NAV_PREVABLE; if (stateChange & NAV_INDEX) EnableButton(ahwnd[iWindow].hwndButtonContents, stateCur & NAV_INDEX); if (stateChange & NAV_SEARCHABLE) EnableButton(ahwnd[iWindow].hwndButtonSearch, stateCur & NAV_SEARCHABLE); if (stateChange & NAV_NEXTABLE) EnableButton(ahwnd[iWindow].hwndButtonNext, stateCur & NAV_NEXTABLE); if (stateChange & NAV_PREVABLE) EnableButton(ahwnd[iWindow].hwndButtonPrev, stateCur & NAV_PREVABLE); } // Always force evaluation of back button EnableButton(ahwnd[iWindow].hwndButtonBack, FBackAvailable(iWindow)); } /******************* - - Name: LGetSmallTextExtent * * Purpose: Finds the extent of the given text in the small system font. * * Arguments: qszText * * Returns: The dimensions of the string, with the height in the high-order * word and the width in the low-order word. * ******************/ LONG STDCALL LGetSmallTextExtent(PSTR pszText) { HDC hdc; HFONT hfont; PSTR psz; hdc = CreateCompatibleDC(0); ASSERT(hdc != NULL); hfont = HfontGetSmallSysFont(); ASSERT(hfont != NULL); if (hdc && hfont) { POINT pt; HFONT hfontSave = SelectObject(hdc, hfont); LONG lReturn; int cch; /*-----------------------------------------------------------------*\ * Remove the ampersand before measuring the text size. \*-----------------------------------------------------------------*/ for (psz = pszText; *psz != '\0' && *psz != '&'; psz = CharNext(psz)) ; if (*psz == '&') { /*-----------------------------------------------------------------*\ * Note that this strlen includes the '&' but not the '\0'. \*-----------------------------------------------------------------*/ cch = strlen(psz); MoveMemory(psz, psz + 1, cch); } else psz = NULL; pt = GetTextSize(hdc, pszText, strlen(pszText)); lReturn = MAKELONG(pt.x, pt.y); if (psz) { MoveMemory(psz + 1, psz, cch); *psz = '&'; } SelectObject(hdc, hfontSave); DeleteDC(hdc); return lReturn; } else return 0; } /*************************************************************************** * - Name: GetCopyright - * Purpose: Gets the copyright text out of the DE. * * Arguments: LPSTR szCopyright - Place to put the copyright text * * Notes: Called by AboutDlg in hdlgfile.c, which doesn't know about DEs. * Review note: This assumes that szCopyright has enough space for * QDE_RGCHCOPYRIGHT(qde). * ***************************************************************************/ VOID STDCALL GetCopyright(LPSTR pszCopyRight) { HDE hde = HdeGetEnv();; if (hde) lstrcpy(pszCopyRight, QDE_RGCHCOPYRIGHT(QdeFromGh(hde))); else pszCopyRight[0] = '\0'; } /*************************************************************************** * - Name: LGetInfo - * Purpose: Gets global information from WinHelp. * * Globals Used: hInsNow, ahwnd[MAIN_HWND].hwndParent * * Notes: Called by HelpWndProc() in hwproc.c. If hwnd is NULL, then the * DE used to get the data will be the DE associated with the * window that currently has the focus. * ***************************************************************************/ LONG STDCALL LGetInfo(WORD cmd, HWND hwnd) { HDE hde; QDE qde; HANDLE h; switch (cmd) { case GI_INSTANCE: DBWIN("LGetInfo: GI_INSTANCE"); return (LONG) hInsNow; case GI_MAINHWND: DBWIN("LGetInfo: GI_MAINHWND"); return (LONG) ahwnd[MAIN_HWND].hwndParent; case GI_FFATAL: DBWIN("LGetInfo: GI_FFATAL"); return (LONG) fFatalExit; case GI_MACROSAFE: // We don't report on this since it gets called all the time return WerrsHelpAvailable() == wERRS_NO; case GI_LCID: DBWIN("LGetInfo: GI_LCID"); return lcid; } if (hwnd == NULL) hde = HdeGetEnvHwnd(hwndNote ? hwndNote : ahwnd[iCurWindow].hwndTopic); else { if ((hde = HdeGetEnvHwnd(hwnd)) == NULL) return 0; } qde = QdeFromGh(hde); switch(cmd) { case GI_CURRHWND: DBWIN("LGetInfo: GI_CURRHWND"); return (LONG) qde->hwnd; case GI_HFS: DBWIN("LGetInfo: GI_HFS"); return (LONG) QDE_HFS(qde); case GI_FGCOLOR: DBWIN("LGetInfo: GI_FGCOLOR"); return (LONG) qde->coFore; case GI_BKCOLOR: DBWIN("LGetInfo: GI_BKCOLOR"); return (LONG) qde->coBack; case GI_TOPICNO: DBWIN("LGetInfo: GI_TOPICNO"); return qde->top.mtop.lTopicNo; break; case GI_HPATH: DBWIN("LGetInfo: GI_HPATH"); // Must not use GhAlloc since this data will be shared with a DLL // BUGBUG: GMEM_SHARE not available for 32-bits if (!(h = GlobalAlloc(GMEM_SHARE, MAX_PATH))) { Error(wERRS_OOM, wERRA_RETURN); return 0; } strcpy(h, PszFromGh(QDE_FM(qde))); return (LONG) h; case GI_CURFM: DBWIN("LGetInfo: GI_CURFM"); return (qde ? (LONG)QDE_FM(qde) : NULL); } return 0; } /*************************************************************************** * - Name: PaletteChanged - * Purpose: Informs all child windows about palette changes * * Arguments: hwnd - window handle of the window that got the * WM_PALETTECHANGED message. * * Notes: Called by HelpWndProc() in hwproc.c. Will send the given the * check the title section of the current window, then the * topic section of the current window, then the title and topic * sections of the non-active window in that order. * ***************************************************************************/ void STDCALL BroadcastChildren(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { HWND hwndT; for (hwndT = GetWindow(hwnd , GW_CHILD); hwndT; hwndT = GetWindow(hwndT, GW_HWNDNEXT)) SendMessage(hwndT, msg, wParam, lParam); } /*************************************************************************** * - Name: HpalGet - * Purpose: Gets the palette to use * * Globals Used: ahwnd[iCurWindow].hwndTitle, hwndTitleMain, hwndTitle2nd, * ahwnd[iCurWindow].hwndTopic, hwndTopicMain, hwndTopic2nd * * Notes: Called by HelpWndProc() in hwproc.c. This routine will first * check the title section of the current window, then the * topic section of the current window, then the title and topic * sections of the non-active window in that order. * ***************************************************************************/ HPALETTE STDCALL HpalGet(VOID) { HPALETTE hpal; HDE hde; int i; // First try our current window if (ahwnd[iCurWindow].hwndTitle) { hde = HdeGetEnvHwnd(ahwnd[iCurWindow].hwndTitle); if (hde && (hpal = HpalGetBestPalette(hde))) return hpal; } if (ahwnd[iCurWindow].hwndTopic) { hde = HdeGetEnvHwnd(ahwnd[iCurWindow].hwndTopic); if (hde && (hpal = HpalGetBestPalette(hde))) return hpal; } // Nothing from current window, so try all secondary windows for (i = 0; i < MAX_WINDOWS; i++) { if (i == iCurWindow || ahwnd[i].hwndParent == NULL) continue; // we already tried this if (ahwnd[i].hwndTitle) { hde = HdeGetEnvHwnd(ahwnd[i].hwndTitle); if (hde && (hpal = HpalGetBestPalette(hde))) return hpal; } if (ahwnd[i].hwndTopic) { hde = HdeGetEnvHwnd(ahwnd[i].hwndTopic); if (hde && (hpal = HpalGetBestPalette(hde))) return hpal; } } return NULL; } /*************************************************************************** * - Name: JumpHOH - * Purpose: Function to jump to the index of the help on help file. * * Returns: nothing. * * Notes: This function makes the jump by using the WinHelp() call. * By using this call we are assured that the help system * (as opposed to WinDoc or WinHelp run standalone) will * be used to display a topic. * ***************************************************************************/ VOID STDCALL JumpHOH(HDE hde) { HWND hwnd; if (hwndNote) // no help on help while displaying a popup return; if (!fHelp) // If we are not help, we want hwnd = ahwnd[MAIN_HWND].hwndParent; // to act like any other app. else hwnd = NULL; WinHelp(hwnd, txtHelpOnHelp, HELP_FINDER, 0); } /*************************************************************************** * - Name: UpdateWinIniValues - * Purpose: If we have received a WM_WININICHANGE message, we should * call this function to make sure we act on any new information; * especially custom colours. * * Arguments: HDE hde The display environment -- may change. * LPSTR lpstr The section of win.ini that changed; NULL = ALL. * * Returns: Nothing. * * Notes: I'll start with just updating colours. Win.ini stuff is really * handled inconsistently (internationalization of this stuff would * be heinous). If I have time later, I'll fix all of it. I use * FLoadFontTablePdb() because InitSpecialColors() is private to * the font layer. * ***************************************************************************/ VOID STDCALL UpdateWinIniValues(HDE hde, LPCSTR lpstr) { // Pre-condition. ASSERT(hde); // If a specific section (not "Windows Help") changed, I don't care. if (!lpstr || (strcmp(lpstr, txtIniHelpSection) == 0)) { VERIFY(FLoadFontTablePdb(QDE_PDB(QdeFromGh(hde)))); } GetAuthorFlag(); } /*************** * - isTitleButtonPossible - * purpose * * Return TRUE if the file can have a non-scrolling region * and FALSE if it can't. * (3.0 files can't have non-scrolling regions.) * * arguments * hde Handle to Display Environment * * return value * TRUE iff there can be a non-scrolling region * * note * The reference to "titles" is an anachronism referring to the olden * days when we just had a title instead of an authorable region. * **************/ INLINE static BOOL STDCALL isTitleButtonPossible(HDE hde) { if (!hde) return FALSE; return QDE_HHDR(QdeFromGh(hde)).wVersionNo != wVersion3_0; } /*************************************************************************** * - Name: - * Purpose: * * Arguments: * * Returns: * * Globals Used: * * +++ * * Notes: * ***************************************************************************/ INLINE static VOID STDCALL GetRectSizeHde(HDE hde, LPRECT prc) { ASSERT(hde != NULL); if (prc != NULL) *prc = QdeFromGh(hde)->rct; } /*************** * * FNextTopicHde * * purpose * Retrieve address of next/prev topic * * arguments * HDE hde Handle to Display Environment * BOOL fnext TRUE to go to next topic, FALSE to go to previous topic * * notes * ASSERTs if bad handle is provided * Accessed with macros in nav.h * **************/ BOOL STDCALL FNextTopicHde(HDE hde, BOOL fNext, INT16* qito, QLA qla) { QDE qde; qde = QdeFromGh(hde); if (qde->top.fITO) { ASSERT(qito != NULL); if (fNext) *qito = (INT16) qde->top.mtop.next.ito; else *qito = (INT16) qde->top.mtop.prev.ito; } else { ASSERT(qla != NULL); CbReadMemQLA(qla, (QB) (fNext ? &qde->top.mtop.next.addr : &qde->top.mtop.prev.addr), QDE_HHDR(qde) .wVersionNo); } return (qde->top.fITO); } /*************** * - GetDETypeHde - * purpose * Gets the DE type from the passed HDE * * arguments * HDE hde - handle to the DE. * * return value * The DE type. * **************/ INLINE WORD STDCALL GetDETypeHde(HDE hde) { return (hde ? QdeFromGh(hde)->deType : deNone); } /*************** ** ** GH GhForceResize( GH gh, WORD wFlags, DWORD lcb ) ** ** purpose ** Resize an existing global block of memory ** Identical to GhResize, but dies in the event of an error ** ** arguments ** gh Handle to global memory block to be resized ** wFlags Memory allocation flags |'ed together ** lcb Number of bytes to allocate ** ** return value ** Possibly different handle to resized block ** ***************/ GH STDCALL GhForceResize(GH gh, UINT wFlags, DWORD lcb) { if ((gh = (GH) GhResize(gh, wFlags, lcb)) == NULL) OOM(); return gh; } void STDCALL ErrorFileChanged(QDE qde) { static BOOL fWarning = FALSE; if (!fWarning) { fWarning = TRUE; ErrorVarArgs(wERRS_FILECHANGE, wERRA_RETURN, PszFromGh(QDE_FM(qde))); fWarning = FALSE; QuitHelp(); } else SetForegroundWindow((hwndAnimate ? hwndAnimate : ahwnd[iCurWindow].hwndParent)); } /*************************************************************************** FUNCTION: EnableWindows PURPOSE: Enable any windows that were disabled while a dialog box was up. PARAMETERS: void RETURNS: COMMENTS: MODIFICATION DATES: 02-Sep-1993 [ralphw] ***************************************************************************/ void STDCALL EnableWindows(void) { int i; // Enable all parent windows that are visible for (i = 0; i < MAX_WINDOWS; i++) { if (ahwnd[i].hwndParent && IsWindowVisible(ahwnd[i].hwndParent)) EnableWindow(ahwnd[i].hwndParent, TRUE); } // Set the focus to our current window SetFocus(ahwnd[iCurWindow].hwndParent); } void STDCALL DisableWindows(void) { int i; // Disable all parent windows that are visible for (i = 0; i < MAX_WINDOWS; i++) { if (ahwnd[i].hwndParent && IsWindowVisible(ahwnd[i].hwndParent)) EnableWindow(ahwnd[i].hwndParent, FALSE); } } /*************************************************************************** FUNCTION: RemoveOnTop PURPOSE: Temporarily remove on-top state PARAMETERS: void RETURNS: COMMENTS: MODIFICATION DATES: 06-Sep-1993 [ralphw] ***************************************************************************/ void STDCALL RemoveOnTop(void) { int i; for (i = 0; i < MAX_WINDOWS; i++) { if (ahwnd[i].hwndParent && IsWindowVisible(ahwnd[i].hwndParent)) SetWindowPos(ahwnd[i].hwndParent, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); } } void STDCALL RestoreOnTop(void) { int i; if (cntFlags.fsOnTop == ONTOP_FORCEOFF) return; for (i = 0; i < MAX_WINDOWS; i++) { if (ahwnd[i].hwndParent && IsWindowVisible(ahwnd[i].hwndParent) && cntFlags.fsOnTop == ONTOP_FORCEON || ahwnd[i].fsOnTop == ONTOP_AUTHOREDON) SetWindowPos(ahwnd[i].hwndParent, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); } } void STDCALL ChangeOnTopState(void) { int i; for (i = 0; i < MAX_WINDOWS; i++) { if (ahwnd[i].hwndParent && IsWindowVisible(ahwnd[i].hwndParent)) SetWindowPos(ahwnd[i].hwndParent, (cntFlags.fsOnTop == ONTOP_FORCEON || (ahwnd[i].fsOnTop & ONTOP_AUTHOREDON && cntFlags.fsOnTop != ONTOP_FORCEOFF)) ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); } #ifdef _DEBUG if (fHelpAuthor) { QDE qde = (QDE) HdeGetEnvHwnd(ahwnd[iCurWindow].hwndTopic); char szBuf[256]; if (!qde || QDE_HHDR(qde).wVersionNo < wVersion3_1) return; wsprintf(szBuf, "%u %s", qde->top.mtop.lTopicNo + 1, ahwnd[iCurWindow].pszMemberName); if (ahwnd[iCurWindow].fsOnTop & ONTOP_AUTHOREDON) { strcat(szBuf, ", on-top"); if (cntFlags.fsOnTop == ONTOP_FORCEOFF || cntFlags.fsOnTop == ONTOP_FORCEON) strcat(szBuf, " (overridden)"); } if (ahwnd[iCurWindow].fAutoSize) strcat(szBuf, ", auto-size"); SetWindowText(ahwnd[iCurWindow].hwndParent, szBuf); } #endif } /*************************************************************************** FUNCTION: SetOnTopState PURPOSE: Set the on-top state for the window PARAMETERS: pos fOnTop RETURNS: COMMENTS: MODIFICATION DATES: 06-Sep-1993 [ralphw] ***************************************************************************/ void STDCALL SetOnTopState(UINT pos, UINT fsOnTop) { ahwnd[pos].fsOnTop = fsOnTop; if (cntFlags.fsOnTop == ONTOP_FORCEOFF || (fsOnTop == ONTOP_NOTSET && cntFlags.fsOnTop != ONTOP_FORCEON)) return; SetWindowPos(ahwnd[pos].hwndParent, (cntFlags.fsOnTop == ONTOP_FORCEON || (ahwnd[pos].fsOnTop & ONTOP_AUTHOREDON)) ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); } /*************************************************************************** FUNCTION: GetWindowIndex PURPOSE: Get the index into our array of windows based on the window handle PARAMETERS: hwnd RETURNS: COMMENTS: MODIFICATION DATES: 06-Sep-1993 [ralphw] ***************************************************************************/ int STDCALL GetWindowIndex(HWND hwnd) { int i; if (!hwnd) return MAIN_HWND; for (i = 0; i < MAX_WINDOWS; i++) { if (hwnd == ahwnd[i].hwndParent) return i; } // Okay, let's try for parents hwnd = GetParent(hwnd); for (i = 0; i < MAX_WINDOWS; i++) { if (hwnd == ahwnd[i].hwndParent) return i; } return 0; // default to the main window } /*************************************************************************** FUNCTION: GetStringResource PURPOSE: Load a string from the resource table into a static array PARAMETERS: idString RETURNS: COMMENTS: MODIFICATION DATES: 29-Sep-1993 [ralphw] ***************************************************************************/ static char szStringBuf[MAX_PATH]; PSTR STDCALL GetStringResource(DWORD idString) { if (LoadString(hInsNow, idString, szStringBuf, sizeof(szStringBuf)) == 0) { #if defined(_DEBUG) || defined(_PRIVATE) wsprintf(szStringBuf, "invalid string id #%u", idString); ErrorQch(szStringBuf); #endif szStringBuf[0] = '\0'; } return szStringBuf; } /*************************************************************************** FUNCTION: GetStringResource2 PURPOSE: Similar to GetStringResource, only it allocates a string if a buffer isn't supplied. PARAMETERS: idString pszDst RETURNS: COMMENTS: MODIFICATION DATES: 27-Sep-1994 [ralphw] ***************************************************************************/ PSTR STDCALL GetStringResource2(DWORD idString, PSTR pszDst) { if (!pszDst) pszDst = (PSTR) lcMalloc(MAX_PATH); if (LoadString(hInsNow, idString, pszDst, MAX_PATH) == 0) { #ifdef _DEBUG wsprintf(pszDst, "invalid string id #%u", idString); ErrorQch(pszDst); #endif pszDst[0] = '\0'; } return pszDst; } // 03-Oct-1993 [ralphw] Doesn't seem to do any good. /*************************************************************************** FUNCTION: RestoreFocusToAppCaller PURPOSE: This should ONLY be called when we were called as a popup, and the popup has been dismissed. We set the focus back to the caller, and throw away are app caller's window handle. PARAMETERS: void RETURNS: COMMENTS: MODIFICATION DATES: 03-Oct-1993 [ralphw] ***************************************************************************/ void STDCALL RestoreFocusToAppCaller(void) { ASSERT(fHelp == POPUP_HELP); if (iasMax != 1 || !IsValidWindow(aAppHwnd[0])) return; SetForegroundWindow(aAppHwnd[0]); iasMax--; } typedef HIMAGELIST (WINAPI* IMAGEPROC)(HINSTANCE, LPCSTR, int, int, COLORREF, UINT, UINT); typedef HIMAGELIST (WINAPI* DESTROYPROC)(HIMAGELIST); typedef void (WINAPI* INITCOMMONCONTROLS)(void); typedef HPROPSHEETPAGE (WINAPI* CREATEPROPERTYSHEETPAGE)(LPCPROPSHEETPAGE); typedef int (WINAPI* PROPERTYSHEET)(LPCPROPSHEETHEADER); extern HIMAGELIST (WINAPI *pImageList_LoadImage)(HINSTANCE, LPCSTR, int, int, COLORREF, UINT, UINT); extern HIMAGELIST (WINAPI *pImgLst_Destroy)(HIMAGELIST); extern void (WINAPI *pInitCommonControls)(void); extern HPROPSHEETPAGE (WINAPI *pCreatePropertySheetPage)(LPCPROPSHEETPAGE); extern int (WINAPI *pPropertySheet)(LPCPROPSHEETHEADER); #ifndef NO_PRAGMAS #pragma data_seg(".text", "CODE") #endif static const char txtImageLoadFunction[] = "ImageList_LoadImage"; static const char txtImageDestroyFunction[] = "ImageList_Destroy"; static const char txtCreatePropertySheetPage[] = "CreatePropertySheetPage"; static const char txtPropertySheet[] = "PropertySheet"; #ifndef NO_PRAGMAS #pragma data_seg() #endif BOOL STDCALL LoadShellApi(void) { HLIBMOD hmodule; if (pImageList_LoadImage) return TRUE; // already loaded if ((hmodule = HFindDLL(GetStringResource(sidCommCtrlDll), FALSE))) { pImageList_LoadImage = (IMAGEPROC) GetProcAddress(hmodule, txtImageLoadFunction); pImgLst_Destroy = (DESTROYPROC) GetProcAddress(hmodule, txtImageDestroyFunction); pCreatePropertySheetPage = (CREATEPROPERTYSHEETPAGE) GetProcAddress(hmodule, txtCreatePropertySheetPage); pPropertySheet = (PROPERTYSHEET) GetProcAddress(hmodule, txtPropertySheet); return TRUE; } else return FALSE; } typedef BOOL (WINAPI* CTL3DAUTOSUBCLASS)(HANDLE); typedef BOOL (WINAPI* CTL3DCOLORCHANGE)(void); typedef BOOL (WINAPI* CTL3DREGISTER)(HANDLE); typedef BOOL (WINAPI* CTL3DUNREGISTER)(HANDLE); BOOL (WINAPI* pCtl3dAutoSubclass)(HANDLE); BOOL (WINAPI* pCtl3dColorChange)(void); BOOL (WINAPI* pCtl3dRegister)(HANDLE); BOOL (WINAPI* pCtl3dUnregister)(HANDLE); #ifndef NO_PRAGMAS #pragma data_seg(".text", "CODE") #endif static const char txtCtl3dAutoSubclass[] = "Ctl3dAutoSubclass"; static const char txtCtl3dColorChange[] = "Ctl3dColorChange"; static const char txtCtl3dRegister[] = "Ctl3dRegister"; static const char txtCtl3dUnregister[] = "Ctl3dUnregister"; #ifndef NO_PRAGMAS #pragma data_seg() #endif BOOL STDCALL LoadCtl3d(void) { HLIBMOD hmodule; static BOOL fTried = FALSE; if (fTried) return FALSE; // already loaded fTried = TRUE; if ((hmodule = HFindDLL("NCTL3D.DLL", FALSE))) { pCtl3dAutoSubclass = (CTL3DAUTOSUBCLASS) GetProcAddress(hmodule, txtCtl3dAutoSubclass); pCtl3dColorChange = (CTL3DCOLORCHANGE) GetProcAddress(hmodule, txtCtl3dColorChange); pCtl3dRegister = (CTL3DREGISTER) GetProcAddress(hmodule, txtCtl3dRegister); pCtl3dUnregister = (CTL3DUNREGISTER) GetProcAddress(hmodule, txtCtl3dUnregister); if (!pCtl3dAutoSubclass || !pCtl3dColorChange || !pCtl3dRegister || !pCtl3dUnregister) return FALSE; if (!pCtl3dRegister(hInsNow)) { pCtl3dUnregister = NULL; pCtl3dColorChange = NULL; return FALSE; } pCtl3dAutoSubclass(hInsNow); return TRUE; } else return FALSE; } /*************************************************************************** FUNCTION: MoveClientWindow PURPOSE: Moves a child window using screen coordinates PARAMETERS: hwndParent hwndChild prc - rectangle containing coordinates fRedraw RETURNS: COMMENTS: This function is similar to MoveWindow, only it expects the coordinates to be in screen coordinates rather then client coordinates. This makes it possible to use functions like GetWindowRect() and use the values directly. MODIFICATION DATES: 25-Feb-1992 [ralphw] ***************************************************************************/ BOOL STDCALL MoveClientWindow(HWND hwndParent, HWND hwndChild, const RECT *prc, BOOL fRedraw) { POINT pt; pt.x = 0; pt.y = 0; ScreenToClient(hwndParent, &pt); return SetWindowPos(hwndChild, NULL, prc->left + pt.x, prc->top + pt.y, PRECT_WIDTH(prc), PRECT_HEIGHT(prc), (fRedraw ? (SWP_NOZORDER | SWP_NOACTIVATE) : (SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW))); } VOID STDCALL SafeDeleteObject(HGDIOBJ hobj) { if (hobj) DeleteObject(hobj); } /*************************************************************************** FUNCTION: MoveRectWindow PURPOSE: Move a window to the specified rectangle PARAMETERS: hwnd prc fRedraw RETURNS: COMMENTS: MODIFICATION DATES: 09-Dec-1993 [ralphw] ***************************************************************************/ void STDCALL MoveRectWindow(HWND hwnd, const RECT* prc, BOOL fRedraw) { MoveWindow(hwnd, prc->left, prc->top, PRECT_WIDTH(prc), PRECT_HEIGHT(prc), fRedraw); } /*************************************************************************** * - Name: FWsmagFromHrgwsmagNsz - * Purpose: * Returns the window information structure associated with a given window * class member name. * * Arguments: * hrgwsmag - handle to array of wsmags * szMember - pointer to member name * qswmagDest - pointer to place to put WSMAG information * * Returns: * 0 based id of the definition, else -1 if not found. * ***************************************************************************/ int STDCALL IWsmagFromHrgwsmagNsz(HDE hde, PCSTR nszMember, WSMAG * pwsmagDst) { int iRv; // return value int iwsmag; // number of smags seen QRGWSMAG qrgwsmag; // pointer to window smag array QWSMAG qwsmag; // pointer into window smag HRGWSMAG hrgwsmag; if (!hde) return -1; hrgwsmag = QDE_HRGWSMAG(QdeFromGh(hde)); if (!hrgwsmag) { #ifdef _DEBUG char szBuf[512]; if (*nszMember && lstrcmpi(nszMember, txtMain) != 0) { wsprintf(szBuf, "Requesting window %s, but no windows defined in the help file.", nszMember); ErrorQch(szBuf); ASSERT(!szBuf); } #endif goto NoSuchWindow; } ASSERT(nszMember); qrgwsmag = PtrFromGh(hrgwsmag); iwsmag = qrgwsmag->cWsmag; qwsmag = &qrgwsmag->rgwsmag[0]; /* * The help compiler converts window names into @num where num is an * index into the wsmag structure of window names. */ if (nszMember[0] == '@') { // direct index. Just grab the n'th element without any kind of // search. ASSERT((nszMember[1] - '0') < (char) iwsmag); iRv = atoi(nszMember + 1); ASSERT(iRv < iwsmag); if (iRv >= iwsmag) return -1; *pwsmagDst = qrgwsmag->rgwsmag[iRv]; return iRv; } else { while (iwsmag--) { // Currently must have both class and member names specified. ASSERT((qwsmag->grf & (fWindowClass | fWindowMember)) == (fWindowClass | fWindowMember)); if (lstrcmpi(qwsmag->rgchMember, nszMember) == 0) { // the membername matched what we sent in *pwsmagDst = *qwsmag; return qwsmag - (QWSMAG) &qrgwsmag->rgwsmag[0]; } qwsmag++; } } NoSuchWindow: if (!IsEmptyString(nszMember) && lstrcmpi(nszMember, txtMain) != 0) { char szBuf[512]; wsprintf(szBuf, GetStringResource(wERRS_BAD_WIN_NAME), nszMember); strcat(szBuf, txtCR); SendStringToParent(szBuf); } return -1; } void STDCALL OnContextMenu(WPARAM wParam, DWORD aKeywordIds[]) { WinHelp((HWND) wParam, txtHelpOnHelp, HELP_CONTEXTMENU, (DWORD) (LPVOID) aKeywordIds); } void STDCALL OnF1Help(LPARAM lParam, DWORD aKeywordIds[]) { #ifdef _DEBUG LPHELPINFO info = (LPHELPINFO) lParam; #endif WinHelp(((LPHELPINFO) lParam)->hItemHandle, txtHelpOnHelp, HELP_WM_HELP, (DWORD) (LPVOID) aKeywordIds); } /*************************************************************************** FUNCTION: Test PURPOSE: Turns on test mode notification PARAMETERS: flags -- reserved for future use RETURNS: COMMENTS: MODIFICATION DATES: 15-Feb-1994 [ralphw] ***************************************************************************/ void STDCALL Test(DWORD flags) { QDE qde = (QDE) GetMacroHde(); ASSERT(qde); if (QDE_HHDR(qde).wVersionNo == wVersion3_0) { OkMsgBox(GetStringResource(wERRS_NT_VERSION3)); return; } if (AreAnyWindowsVisible(0) < 0 && ahwnd[MAIN_HWND].hwndParent) ShowWindow(ahwnd[MAIN_HWND].hwndParent, SW_SHOW); switch (flags) { case 5: case 6: InitializeCntTest(flags); break; case 4: fSequence = 4; NextTopic(TRUE); FlushMessageQueue(0); dwSequence = 1; PostMessage(ahwnd[iCurWindow].hwndParent, MSG_NEXT_TOPIC, 0, 0); break; case 2: #ifdef _PRIVATE CreateTimeReport("Test(2) time:"); #endif fSequence = 1; NextTopic(TRUE); FlushMessageQueue(0); // deliberately fall through case 1: fSequence = 1; dwSequence = 1; PostMessage(ahwnd[iCurWindow].hwndParent, MSG_NEXT_TOPIC, 0, 0); break; case 3: fSequence = 2; // continue for ever NextTopic(TRUE); FlushMessageQueue(0); dwSequence = 1; PostMessage(ahwnd[iCurWindow].hwndParent, MSG_NEXT_TOPIC, 0, 0); break; case 7: Compare(GetCurFilename()); break; } } void STDCALL KillOurTimers(void) { if (fAutoClose) { KillTimer(ahwnd[MAIN_HWND].hwndParent, ID_AUTO_CLOSE); fAutoClose = FALSE; } } #ifndef NO_PRAGMAS #pragma data_seg(".text", "CODE") #endif const char txtPetra[] = "|Petra"; const char txtTopicId[] = "|TopicId"; #ifndef NO_PRAGMAS #pragma data_seg() #endif DLGRET TopicInfoDlg(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) { QDE qde; char szBuf[MAX_PATH + 10]; switch(msg) { case WM_INITDIALOG: qde = QdeFromGh(HdeGetEnv()); { HBT hbtSource = HbtOpenBtreeSz(txtPetra, QDE_HFS(qde), fFSOpenReadOnly); HBT hbtTopicId = HbtOpenBtreeSz(txtTopicId, QDE_HFS(qde), fFSOpenReadOnly); int id = qde->top.mtop.lTopicNo + 1; if (hbtSource && RcLookupByKey(hbtSource, (KEY) &id, NULL, szBuf) == rcSuccess) { SetWindowText(GetDlgItem(hwndDlg, IDC_SOURCE_FILE), szBuf); } else SetWindowText(GetDlgItem(hwndDlg, IDC_SOURCE_FILE), GetStringResource(sidUnAvailable)); if (hbtTopicId && RcLookupByKey(hbtTopicId, (KEY) &id, NULL, szBuf) == rcSuccess) { SetWindowText(GetDlgItem(hwndDlg, IDC_TOPIC_ID), szBuf); } else SetWindowText(GetDlgItem(hwndDlg, IDC_TOPIC_ID), GetStringResource(sidUnAvailable)); if (hbtSource) RcCloseBtreeHbt(hbtSource); if (hbtTopicId) RcCloseBtreeHbt(hbtTopicId); } if (qde->top.hTitle) wsprintf(szBuf, "%u: %s", qde->top.mtop.lTopicNo + 1, PszFromGh(qde->top.hTitle)); else wsprintf(szBuf, GetStringResource(sidUntitled), qde->top.mtop.lTopicNo + 1); SetWindowText(GetDlgItem(hwndDlg, IDC_TOPIC_TITLE), szBuf); SendMessage(GetDlgItem(hwndDlg, IDC_TOPIC_TITLE), WM_SETFONT, (WPARAM) hfontDefault, FALSE); if (qde->top.hEntryMacro != NULL) SetWindowText(GetDlgItem(hwndDlg, IDC_ENTRY_MACROS), PszFromGh(qde->top.hEntryMacro)); else SetWindowText(GetDlgItem(hwndDlg, IDC_ENTRY_MACROS), ""); SetWindowText(GetDlgItem(hwndDlg, IDC_WINDOW_NAME), ahwnd[iCurWindow].pszMemberName); { char chCompression; if (PDB_LPJPHRASE(QDE_PDB(qde))) chCompression = 'h'; else if (QDE_HPHR(qde)) chCompression = 'p'; else if (QDE_HHDR(qde).wFlags & fBLOCK_COMPRESSION) chCompression = 'z'; else chCompression = 'n'; wsprintf(szBuf, "%s [%s %c]", QDE_FM(qde), QDE_HHDR(qde).wVersionNo == wVersion3_1 ? "3.1" : QDE_HHDR(qde).wVersionNo == wVersion3_0 ? "3.0" : QDE_HHDR(qde).wVersionNo == wVersion40 ? "4.0" : "?.?", chCompression); SetWindowText(GetDlgItem(hwndDlg, IDC_HELP_FILE), szBuf); } SetFocus(GetDlgItem(hwndDlg, IDOK)); break; case WM_COMMAND: switch (LOWORD(wParam)) { case IDOK: case IDCANCEL: EndDialog(hwndDlg, FALSE); break; } break; } return FALSE; } void STDCALL JumpLinkedWinHelp(DWORD topic) { if (IsValidWindow(hwndSecondHelp)) { SendMessage(hwndSecondHelp, MSG_JUMP_TOPIC, 0, topic); } else { hwndSecondHelp = NULL; } } QDE STDCALL GetMacroHde(void) { HDE hde = HdeGetEnv(); if (!hde && fmCaller) { FM fmCopy = FmCopyFm(fmCaller); fNoShow = TRUE; // don't allow main window to be shown FReplaceHde(txtMain, &fmCopy, NULL); fNoShow = FALSE; hde = HdeGetEnv(); } return (hde ? QdeFromGh(hde) : NULL); } #ifndef NO_PRAGMAS #pragma data_seg(".text", "CODE") #endif const char txtSharedMem[] = "whshare"; #ifndef NO_PRAGMAS #pragma data_seg() #endif HANDLE hfShare; HWND hwndParent; static PSTR pszMap; static void STDCALL CreateSharedMemory(void); // These #defines must be the same for hcw #define WMP_WH_MSG (WM_USER + 1000) void STDCALL SendStringToParent(PCSTR pszString) { #if defined(_DEBUG) && defined(_PRIVATE) OutputDebugString(pszString); #endif if (!IsValidWindow(hwndParent)) { hwndParent = NULL; return; } if (!hfShare) CreateSharedMemory(); strcpy(pszMap, pszString); SendMessage(hwndParent, WMP_WH_MSG, 0, 0); } void STDCALL SendStringIdHelp(PCSTR pszString, UINT id, PCSTR pszHelpFile, PCSTR pszWindow) { char szBuf[512]; if (!hwndParent || !pszHelpFile) return; wsprintf(szBuf, "%s: %u -- %s", pszString, id, pszHelpFile); if (pszWindow && *pszWindow) { strcat(szBuf, ">"); strcat(szBuf, pszWindow); } strcat(szBuf, txtCR); SendStringToParent(szBuf); } void STDCALL SendStringHelp(PCSTR pszString, PCSTR pszHelpFile, PCSTR pszWindow) { char szBuf[512]; if (!hwndParent || !pszHelpFile) return; wsprintf(szBuf, "%s: %s", pszString, pszHelpFile); if (pszWindow && *pszWindow) { strcat(szBuf, ">"); strcat(szBuf, pszWindow); } strcat(szBuf, txtCR); SendStringToParent(szBuf); } static void STDCALL CreateSharedMemory(void) { if (!hfShare) { hfShare = CreateFileMapping((HANDLE) -1, NULL, PAGE_READWRITE, 0, 4096, txtSharedMem); if (!hfShare) { hwndParent = NULL; return; } pszMap = (PSTR) MapViewOfFile(hfShare, FILE_MAP_WRITE, 0, 0, 0); ASSERT(pszMap); } } static void STDCALL SendTopicInfo(HDE hde) { char szBuf[1024]; QDE qde = QdeFromGh(hde); if (!hwndParent || !qde) return; wsprintf(szBuf, "%u: %s -- %s>%s\r\n", qde->top.mtop.lTopicNo + 1, qde->top.hTitle ? PszFromGh(qde->top.hTitle) : GetStringResource(sidTitleUnknown), QDE_FM(qde), ahwnd[iCurWindow].pszMemberName); SendStringToParent(szBuf); }