/***************************************************************************** * * HDESET.C * * Copyright (C) Microsoft Corporation 1989-1994. * All Rights reserved. * ****************************************************************************** * * Module Intent * * This module contains routines that manipulate an HDE. This includes * creation, destruction, and the changing of any fields within the HDE. * *****************************************************************************/ #include "help.h" #pragma hdrstop #include "inc\navpriv.h" #include "inc\compress.h" INLINE static void STDCALL InitScrollQde(QDE qde); static BOOL STDCALL FDeallocPdb (PDB pdb); static PDB STDCALL pdbAllocFm (FM* pfm, UINT *pwErr); /*************************************************************************** * - Name: HdeCreate - * Purpose: * Create a new handle to Display Environment * * Arguments: * fm - A file moniker for the file containing the help topic. * NOTE: this FM will be disposed of appropriately by this * routine or subsequent actions. * hdeSource - Topic DE to copy information from, as necessary. * deType - Type of DE being created (deTopic, deNote, dePrint, deCopy) * * Returns: * Handle to DE if successful, NULL otherwise. * * +++ * * Notes: * The hdeTopic is used as a "prototype" DE to copy certain fields rather * than initializing them twice. If used, by passing nil values for fm, we * will use values from hdeTopic instead. * * If an error occurs, a message box is displayed (by virtue of a message * being posted to ourselves). * ***************************************************************************/ FM fmCreating = NULL; // used in mastkey and fm.c when no files specified HDE STDCALL HdeCreate(FM* pfm, HDE hdeSource, int deType) { FM fmPrevCreating; HDE hde; // handle to the de we're creating QDE qde = NULL; // pointer to the de we're creating QDE qdeSource = NULL; // pointer to the template de UINT wError = wERRS_OOM; // error return from called funcs FM fmTmp; // It's possible for Winhelp to reenter HdeCreate as a result of the // WinHelp() API being called by a helper DLL or the help file itself. fmPrevCreating = fmCreating; // Allocate the memory for the de and bomb if not possible. hde = GhAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizeof(DE)); if (!hde) goto HdeErrorMess; qde = QdeFromGh(hde); // Note that many of the fields of the DE MUST be initialized to NULL // We depend on the fact that 1) the memory allocation zeros out the // block and 2) the nil value for many of the fields is 0. If a // nil value changes, then initialization must occur. qde->ifnt = ifntNil; QDE_PREVSTATE(qde) = NAV_UNINITIALIZED; if (hdeSource) { qdeSource = QdeFromGh(hdeSource); if (!pfm || !*pfm) { fmTmp = FmCopyFm(QDE_FM(qdeSource)); pfm = &fmTmp; } } /* * If we got here without an fm, it's because the FmCopyFm above * failed. However, this is a nicer, safer place to put the check just in * case there is ever a path around that conditional that results in * fm==fmNil */ if (!*pfm) goto HdeErrorMess; qde->deType = deType; fmCreating = *pfm; // in case we need it for processing .CNT file // get information into the database structure for the file. QDE_PDB(qde) = pdbAllocFm(pfm, &wError); if (!QDE_PDB(qde)) goto HdeErrorMess; // Init the font cache if (!FInitFntInfoQde(qde)) goto HdeErrorMess; if (qdeSource) { // This DE is to receive a bunch of information from another DE. // Copy over the appropriate stuff, and/or initialize other fields // appropriately. qde->coFore = qdeSource->coFore; qde->coBack = qdeSource->coBack; } else { // This DE is being created from scatch. Initialize fields otherwise // copied. qde->coFore = GetSysColor(COLOR_WINDOWTEXT); qde->coBack = GetSysColor(COLOR_WINDOW); } // Initialize or override some fields based on the DE type. if (QDE_TOPIC(qde)) { qde->FFlags |= FBROWSEABLE | FINDEX; if (FAccessHfs(QDE_HFS(qde), (LPSTR) txtKEYWORDBTREE) || (hfsGid && cntFlags.fUseGlobalIndex == TRUE)) qde->FFlags |= FSEARCHABLE; } // REVIEW: won't this prevent color printers from working? if (deType == dePrint) { // printing DE. Override colors with black and white. qde->coFore = coBLACK; qde->coBack = coWHITE; } // The following Frame Mgr call fills frame manager fields of the DE FInitLayout(qde); // For deCopy and dePrint, we want to already be at a topic, rather than // jump to it. Therefore, we copy over the fields that determine the help // topic. The top.hTitle handle must be duplicated, as it will get freed // in DestroyHde, and also in layout code. Also, as a minor hack, to be // able to distinguish NSR Copy from SR Copy, copy and print DEs use // top.mtop.vaTopic to hold the address of the first FC in the layout. if (deType == deCopy || deType == dePrint) { ASSERT (qdeSource); qde->tlp = qdeSource->tlp; qde->top = qdeSource->top; if (qdeSource->top.hTitle) qde->top.hTitle = GhDupGh(qdeSource->top.hTitle); if (qdeSource->top.hEntryMacro) qde->top.hEntryMacro = GhDupGh (qdeSource->top.hEntryMacro); qde->rct = qdeSource->rct; } // Printing de's need a new bitmap cache, because they use a different // display device. if (QDE_TOPIC(qde) || deType == dePrint || qdeSource == NULL) QDE_HTBMI(qde) = HtbmiAlloc(qde); else { ASSERT(qdeSource); QDE_HTBMI(qde) = QDE_HTBMI(qdeSource); qde->FFlags |= FCOPYBMCACHE; } // normal exit handling fmCreating = fmPrevCreating; return hde; // Abnormal exit handling HdeErrorMess: PostErrorMessage(wError); if (hde) FreeHde(hde); fmCreating = fmPrevCreating; return NULL; } // HdeCreate() /*************************************************************************** * - Name: DestroyHde - * Purpose: * Destory a handle to Display Environment * * Arguments: * hde - Hde to be destroyed. * * Returns: * nothing * * +++ * * Notes: * This will be performed once for each Help Window, when it is closed. * Asserts if given a bad handle. Might want to make this thing * ensure that hds has been cleared, and assert if not...probably not worth * ***************************************************************************/ VOID STDCALL DestroyHde(HDE hde) { if (hde) { QDE qde = QdeFromGh(hde); // dealloc all the file related information FDeallocPdb(QDE_PDB(qde)); // deallocate cached font information DestroyFntInfoQde(qde); // Fix for bug 81 (kevynct) // // If we die for any reason, do not attempt to free potentially // inconsistent layout structure. This is a HACK which must live until // our general error scheme is improved. GI_FFatal is set to FALSE in // FInitialize, and set TRUE in Error() in the case that a DIE is // received if (!fFatalExit) DiscardLayout(qde); /* * The handles stored in the TOP structure are initially NULL when * the DE is created, allocated in HfcNear (a.k.a. HfcFindPrevFc), * and freed either on the next call to HfcNear with the same DE, or * here when the DE is destroyed. (Or when the macro is executed.) * This is a fragile scheme but seems to work. */ if (qde->top.hEntryMacro != NULL) { FreeGh(qde->top.hEntryMacro); qde->top.hEntryMacro = NULL; } if (qde->top.hTitle != NULL) { FreeGh(qde->top.hTitle); qde->top.hTitle = NULL; } if (!(qde->FFlags & FCOPYBMCACHE)) DestroyHtbmi(QDE_HTBMI(qde)); // Destroy bitmaps if (qde->hss != NULL) FreeGh(qde->hss); // Free the keyword search set hit list FreeHde(hde); } } // DestroyHde /*************************************************************************** * - Name: SetHdeHwnd - * Purpose: * Set the hwnd field of the Hde to a particular value * * Arguments: * hde = hde to update * hwnd = hwnd to place there * * Returns: * nothing * ***************************************************************************/ VOID STDCALL SetHdeHwnd ( HDE hde, HWND hwnd ) { QDE qde; // pointer to locked DE if (hde) { qde = QdeFromGh(hde); qde->hwnd = hwnd; if (QDE_TOPIC(qde)) // Once we've actually set an hwnd to associate with this DE, it turns // out that this is also the appropriate time to initialize the scroll // bars. // InitScrollQde(qde); } } /* SetHdeHwnd */ /*************************************************************************** * - Name: SetHdeCoBack - * Purpose: * Sets the HDE's background color field * * Arguments: * hde - handle to de * color - color to put in it * * Returns: * nothing. * ***************************************************************************/ void STDCALL SetHdeCoBack(HDE hde, DWORD coBack) { if (hde) QdeFromGh(hde)->coBack = coBack; } /*************************************************************************** * - Name: SetSizeHdeQrct - * Purpose: * Set/Change size of client window * * Arguments: * hde - Handle to Display Environment * qrct - Pointer to rect structure * fLayout - Forces a new layout iff TRUE * * Returns: * void for now. Asserts if Applet accidentally gives a bad HDE. * ***************************************************************************/ VOID STDCALL SetSizeHdeQrct(HDE hde, LPRECT qrct, BOOL fLayout) { if (hde) { QDE qde = QdeFromGh(hde); qde->rct = *qrct; // This routine can be called before there is a valid FM, and I presume // the layout manager may read the file, thus we must make sure that we // have a good one before proceding. if (QDE_FM(qde) && fLayout) ResizeLayout(qde); } } /* SetSizeHdeQrct() */ /*************************************************************************** * - Name: StateGet - * Purpose: * Get current state * * Arguments: * hde - handle to de from which to get state * * Returns: * The current setting of the state flags for the passed hde. If * HDE is NULL, then 0 is returned. * ***************************************************************************/ STATE STDCALL StateGetHde(HDE hde) { return hde ? QDE_THISSTATE(QdeFromGh(hde)) : 0; } /*************************************************************************** * - Name: SetIndex - * Purpose: * Set a context to use as an index other than the default index. * * Arguments: * hde - handle to display environment * ctx - ctx to set * * Returns: * Nothing * ***************************************************************************/ VOID STDCALL SetIndex ( HDE hde, CTX ctx ) { QDE qde; // Pointer to locked DE to work on if (hde) { qde = QdeFromGh(hde); QDE_CTXINDEX(qde) = ctx; } } /* SetIndex */ /*************************************************************************** * - Name: FGetStateHde - * Purpose: * Get current state and changed information * * Arguments: * hde - handle to display environment * qstatechanged - Where to put flags that have changed since * previous call, or NULL * qstatecurrent - Where to put current state, or NULL * * Returns: * TRUE if something's changed (statechanged != 0) * * Notes: * Asserts if bad handle is passed. * This is a distructive read in that the previous state structure * is updated on calling this routine. * ***************************************************************************/ BOOL STDCALL FGetStateHde(HDE hde, QSTATE qstatechanged, QSTATE qstatecurrent) { BOOL fChanged; // TRUE => state changed if (hde) { QDE qde = QdeFromGh(hde); fChanged = QDE_PREVSTATE(qde) != QDE_THISSTATE(qde); if (qstatechanged) // If we aren't initialized then consider EVERYTHING to be changed, // otherwise, XOR past and current states! How clever! *qstatechanged = (QDE_PREVSTATE(qde) == NAV_UNINITIALIZED) ? NAV_ALLFLAGS : QDE_PREVSTATE(qde) ^ QDE_THISSTATE(qde); if (qstatecurrent) *qstatecurrent = QDE_THISSTATE(qde); // Finally, update the previous state QDE_PREVSTATE(qde) = QDE_THISSTATE(qde); } else { // default: all flags are changed to off when there's no de's! if (qstatechanged) *qstatechanged = NAV_TOPICFLAGS; if (qstatecurrent) *qstatecurrent = (STATE) ~NAV_TOPICFLAGS; return FALSE; } return fChanged; } /*************************************************************************** * - Name: GhDupGh - * Purpose: * Duplicates a block of memory referenced by a particular handle * * Arguments: * ghOrg - handle of memory to be copied * fSystemObject - TRUE => we cannot do debug checks on it. * * Returns: * Handle to a copy of the object, or NULL on failure * * Notes: * because this routine is used on objects which can be passed to the * system, it cannot take advantage of our debug stuff. * ***************************************************************************/ GH STDCALL GhDupGh(GH ghOrg) { DWORD cbObject; GH ghNew; if (!ghOrg) return NULL; cbObject = GhSize(ghOrg); ghNew = GhAlloc(LMEM_FIXED, cbObject); if (!ghNew) OOM(); CopyMemory((void*) ghNew, (void*) ghOrg, cbObject); return ghNew; } /******************* ** ** Name: InitScrollQde ** ** Purpose: Initializes the horizontal and vertical scroll bar. ** ** Arguments: qde - far pointer to a DE ** ** Returns: Nothing. ** *******************/ extern BOOL fHorzBarPending; INLINE static void STDCALL InitScrollQde(QDE qde) { if (QDE_TOPIC(qde)) ShowScrollBar(qde->hwnd, SB_BOTH, FALSE); qde->fHorScrollVis = FALSE; qde->fVerScrollVis = FALSE; qde->dxVerScrollWidth = GetSystemMetrics(SM_CXVSCROLL); qde->dyHorScrollHeight = GetSystemMetrics(SM_CYHSCROLL); fHorzBarPending = FALSE; } extern BOOL fTableLoaded; static PDB pdbList; // linked list of dbs INLINE WORD STDCALL wMapFSErrorW(UINT16 wErrorDefault); /*************************************************************************** * - Name: pdbAllocFm - * Purpose: * Given an fm, returns a pdb structure full of information on that file. * May open and read the file as required, or may return a pointer to an * already open pdb. * * Arguments: * fm - fm of the file to be referenced * NOTE: this FM will be disposed of appropriately by this * routine or subsequent actions. * pwErr - pointer to location to place error code on failure * * Returns: * pdb - pointer to the DB structure for the file. Returns NULL * on error, and *pwErr updated. * * * Globals Used: * * +++ * * Notes: * Every pdbAllocFm MUST have an accompanying FDeallocPdb call. * ***************************************************************************/ static PDB STDCALL pdbAllocFm(FM* pfm, UINT *pwErr) { PDB pdb; ASSERT(pfm); /* * First walk the list of known DB's, and see if we already have one. * If so, all we need to do is return that one. */ for (pdb = pdbList; pdb; pdb = PDB_PDBNEXT(pdb)) { if (FSameFmFm(PDB_FM(pdb), *pfm)) { if (PDB_FM(pdb) != *pfm) RemoveFM(pfm); PDB_CREF(pdb)++; lcid = pdb->lcid; MoveMemory(&kwlcid, &pdb->kwlcid, sizeof(KEYWORD_LOCALE)); return pdb; } } // DB not found. Allocate memory for a new one. pdb = (PDB) LhAlloc(LMEM_FIXED | LMEM_ZEROINIT, sizeof(DB)); if (!pdb) { *pwErr = wERRS_OOM; return NULL; } PDB_CREF(pdb) = 1; // open the physical file PDB_FM(pdb) = FmCopyFm(*pfm); PDB_HFS(pdb) = HfsOpenFm(*pfm, fFSOpenReadOnly); if (!PDB_HFS(pdb)) { goto FSError; } // Get the timestamp of the newly opened FS. if (rcSuccess != RcTimestampHfs(PDB_HFS(pdb), &PDB_LTIMESTAMP(pdb))) PDB_LTIMESTAMP(pdb) = 0; // don't fail just because we can't read the time // Load up the fields contained in the system file. if (!FReadSystemFile(PDB_HFS(pdb), pdb, pwErr, FALSE)) goto GenError; // Load HALL COMPRESSION phrase table PDB_LPJPHRASE(pdb) = LoadJohnTables(pdb); // Load Phrase Table if (!PDB_LPJPHRASE(pdb)) { PDB_HPHR(pdb) = HphrLoadTableHfs(PDB_HFS(pdb), PDB_HHDR(pdb).wVersionNo); if (PDB_HPHR(pdb) == hphrOOM) { PDB_HPHR(pdb) = NULL; *pwErr = wERRS_OOM; goto GenError; } } // Open the "topic" file for the FC Manager. PDB_HFTOPIC(pdb) = HfOpenHfs (PDB_HFS(pdb), txtTopicFs, fFSOpenReadOnly); if (!PDB_HFTOPIC(pdb)) goto FSError; #ifndef _X86_ PDB_ISDFFTOPIC(pdb) = ISdffFileIdHf(PDB_HFTOPIC(pdb)); #endif /* * Open the topic map and/or Context hash btree files for the Navigator * If BOTH of these are nil, it's an error */ PDB_HFMAP(pdb) = HfOpenHfs(PDB_HFS(pdb), "|TOMAP", fFSOpenReadOnly); PDB_HBTCONTEXT(pdb) = HbtOpenBtreeSz("|CONTEXT", PDB_HFS(pdb), fFSOpenReadOnly); if (!PDB_HFMAP(pdb) && !PDB_HBTCONTEXT(pdb)) { *pwErr = wERRS_BADFILE; goto BadFile; } // REVIEW: we should delay attaching annotations for popup windows // attach any annotations. if (fHelp != POPUP_HELP) InitAnnoPdb(pdb); else PDB_HADS(pdb) = NULL; // Load the font information from the file. if (!FLoadFontTablePdb(pdb)) { *pwErr = wERRS_OOM; goto GenError; } #ifdef RAWHIDE // Load the full text search engine and index as appropriate. if (!fTableLoaded || fHelp == POPUP_HELP) { if (pdb) PDB_HRHFT(pdb) = NULL; } else FLoadFtIndexPdb(pdb); #endif // We're done. Insert the pdb at the head of the list. PDB_PDBNEXT(pdb) = pdbList; pdbList = pdb; // REVIEW: delay for popup windows if (fHelp != POPUP_HELP) { FindGidFile(*pfm, FALSE, 0); // REVIEW: we should update window positions if gid changed or is new } return pdb; FSError: /* * An error occurred in a file system operation. Return the mapped * error code. */ *pwErr = wMapFSErrorW(0); BadFile: ASSERT(*pwErr != wERRS_BADFILE); ASSERT(*pwErr != wERRS_OLDFILE); ASSERT(*pwErr != wERRS_ADVISOR_FILE); if (*pwErr == wERRS_BADFILE || *pwErr == wERRS_OLDFILE || *pwErr == wERRS_ADVISOR_FILE) { char szName[MAX_PATH]; lstrcpy(szName, PszFromGh(PDB_FM(pdb))); ErrorVarArgs(*pwErr, wERRA_RETURN, szName); } GenError: // Generic error. *pwErr has already been set. Discard the pdb and return. FDeallocPdb(pdb); return NULL; } /*************************************************************************** * - Name: wMapFSErrorW - * Purpose: * Maps a file system error to one of our own. Calls RcGetFSError to get * the current file system error. * * Arguments: * wDefault - error to be used as a default when all else fails. * * Returns: * error code * ***************************************************************************/ INLINE WORD STDCALL wMapFSErrorW(UINT16 wErrorDefault) { switch (RcGetFSError()) { case rcOutOfMemory: return wERRS_OOM; case rcInvalid: return wERRS_BADFILE; case rcBadVersion: return wERRS_OLDFILE; case rcAdvisorFile: return wERRS_ADVISOR_FILE; default: // REVIEW: the wERRS_NOHELP_FILE is bogus -- we need to add more // error messages for things like "out of file handles", etc. return wErrorDefault ? wErrorDefault : wERRS_NOHELP_FILE; } } /*************************************************************************** * - Name: FDeallocPdb - * Purpose: * Removes a reference to the pdb passed. If no more references exist, it * is destroyed. * * Arguments: * pdb - pdb to be unreferenced * * Returns: * TRUE if the pdb remains, FALSE if it was deleted * ***************************************************************************/ static BOOL STDCALL FDeallocPdb (PDB pdb) { PDB pdbWalk; // for walking the PDB list // Dec the reference count, and if non zero, just return. We're done. if (--PDB_CREF(pdb)) return TRUE; /* * Destroy various data structures as contained in the DB. Data * structures in the pdb not dealt with here are static items that can be * deleted without side effect. */ // REVIEW: PDB_FM(pdb) is invalidated by the close of the file system? // Close the Full-text search session for this file #ifdef RAWHIDE if (PDB_HRHFT(pdb)) UnloadFtIndexPdb(pdb); #endif if (PDB_BMK(pdb)) FreeGh(PDB_BMK(pdb)); FiniAnnoPdb(pdb); if (PDB_HBTCONTEXT(pdb)) RcCloseBtreeHbt(PDB_HBTCONTEXT(pdb)); if (PDB_HFMAP(pdb)) RcCloseHf(PDB_HFMAP(pdb)); if (PDB_HCITATION(pdb)) FreeGh(PDB_HCITATION(pdb)); if (PDB_HHELPON(pdb)) FreeGh(PDB_HHELPON(pdb)); DestroyFontTablePdb(pdb); if (PDB_HFTOPIC(pdb)) RcCloseHf(PDB_HFTOPIC(pdb)); DestroyJPhrase(PDB_LPJPHRASE(pdb)); DestroyHphr(PDB_HPHR(pdb)); if (PDB_HRGWSMAG(pdb)) FreeGh(PDB_HRGWSMAG(pdb)); if (PDB_HFS(pdb)) RcCloseHfs(PDB_HFS(pdb)); if (PDB_LLMACROS(pdb)) DestroyLL(PDB_LLMACROS(pdb)); RemoveFM(&PDB_FM(pdb)); /* * Flush the 4K-block |TOPIC file cache since we're switching to * another file: */ FlushCache(); // Finally, remove the pdb from the list of dbs, and free it. if (pdbList == pdb) pdbList = PDB_PDBNEXT(pdb); else for (pdbWalk = pdbList; pdbWalk; pdbWalk = PDB_PDBNEXT(pdbWalk)) { if (PDB_PDBNEXT(pdbWalk) == pdb) { PDB_PDBNEXT(pdbWalk) = PDB_PDBNEXT(pdb); break; } } // pdb was allocated with LhAlloc because it is fixed FreeLh((HLOCAL) pdb); return FALSE; }