/***************************************************************************** * * secndary.c * * Copyright (C) Microsoft Corporation 1990 - 1994. * All Rights reserved. * ****************************************************************************** * * Module Intent * * This file contains code unique to displaying secondary windows. * ****************************************************************************** * * Current Owner: LeoN * *****************************************************************************/ #include "help.h" #pragma hdrstop #include "inc\tmp.h" // until it gets added to windows.h #include "inc\hinit.h" #include "inc\hwproc.h" #include "inc\winclass.h" INLINE static int STDCALL FCreate2ndHwnd(WRECT* prcw, const WSMAG* pwsmag); static void STDCALL ExecuteConfigMacros(int idxWindow, HDE hde); static BOOL fMainSized; // TRUE => main window is sized static int iWin2ndMember = -1; #ifndef NO_PRAGMAS #pragma data_seg(".text", "CODE") #endif static const char txtSecondary[] = "SECONDARY"; #ifndef NO_PRAGMAS #pragma data_seg() #endif /*************************************************************************** * - Name: FFocusSzHde - * Purpose: * Changes focus to the named window. Secondary windows are created if a * window of their class does not already exist. * * Arguments: * nszMember = class member name of the window to switch to. (NULL or null * string implies no change) * hde = hde of topic to be displayed there * fResizeMain = TRUE means resize the main window if this is a main * window operation, and there is sizing information * * Returns: * FALSE on error in member name or GetDC failure. Error posted for * member name failure. * ***************************************************************************/ BOOL STDCALL FFocusSzHde(PCSTR pszMember, HDE hde, BOOL fResizeMain) { BOOL fRv; // return value int iWin; // id of secondary window def WRECT rcw; // rectangle to size window to WSMAG wsmag; // window information int i; #ifdef _DEBUG QDE qde = QdeFromGh(hde); #endif LPSTR psz; if (fAutoClose) { KillOurTimers(); fAutoClose = FALSE; } if (hde) psz = PszFromGh(QDE_FM(QdeFromGh(hde))); ASSERT(pszMember && hde); // assume the worst, and that there is no wsmag info. /* * DANGER! Nothing in this routine should call HdeGetEnvHwnd, since * the enlistments may not be done yet. */ fRv = FALSE; /* * If no member is specified.... choose a default name based on the * current window. */ if (IsEmptyString(pszMember)) pszMember = (PCSTR) ahwnd[iCurWindow].pszMemberName; /* * Make sure we know the size of the screen for proper scaling of * window size requests */ if (!cxScreen) GetScreenResolution(); /* * Set up default values for the window size, in case we need to create * a window without a specified size. */ // REVIEW: these values should come from the .GID file. rcw.left = 660 * RECT_WIDTH(rcWorkArea) / dxVirtScreen; rcw.top = 10 * RECT_HEIGHT(rcWorkArea) / dyVirtScreen; rcw.cx = 360 * RECT_WIDTH(rcWorkArea) / dxVirtScreen; rcw.cy = 600 * RECT_HEIGHT(rcWorkArea) / dyVirtScreen; // ensure that this is a valid member name for the file. iWin = IWsmagFromHrgwsmagNsz(hde, pszMember, &wsmag); if (iWin == -1 && *pszMember && _strcmpi(pszMember, txtMain) != 0) { char szMsg[256]; wsprintf(szMsg, GetStringResource(wERRS_BAD_WIN_NAME), pszMember); SendStringToParent(szMsg); if (fHelpAuthor) ErrorVarArgs(wERRS_BAD_WIN_NAME, wERRA_RETURN, pszMember); return FFocusSzHde(txtMain, hde, fResizeMain); } if (iWin != -1) { /* * if there's a window size specified, then scale that into our rcw * structure for use when we create or resize the window. */ // 4.0: the FWSMAG_ABSOLUTE can be used to set an absolute position // 4.0: we set only the values actually specified. if (wsmag.grf & fWindowX) rcw.left = (wsmag.grf & FWSMAG_ABSOLUTE) ? wsmag.x : wsmag.x * cxScreen / dxVirtScreen; if (wsmag.grf & fWindowY) rcw.top = (wsmag.grf & FWSMAG_ABSOLUTE) ? wsmag.y : wsmag.y * cyScreen / dyVirtScreen; if (wsmag.grf & fWindowDX) rcw.cx = (wsmag.grf & FWSMAG_ABSOLUTE) ? wsmag.dx : wsmag.dx * cxScreen / dxVirtScreen; if (wsmag.grf & fWindowDY) rcw.cy = (wsmag.grf & FWSMAG_ABSOLUTE) ? wsmag.dy : wsmag.dy * cyScreen / dyVirtScreen; // Adjust position, but not height or width CheckWindowPosition(&rcw, FALSE); /* * See if this new member name is that of either the current primary * or secondary window. If so, then just set focus to that window & * we're done. MULTIPLE: search all known windows */ // REVIEW: what if it is an interfile jump, and the new file has // different properties for the secondary window? for (i = 0; i < MAX_WINDOWS; i++) { if (ahwnd[i].pszMemberName && _strcmpi(wsmag.rgchMember, ahwnd[i].pszMemberName) == 0) { static BOOL fReplacing = FALSE; HDE hdeCur = HdeGetEnvHwnd(ahwnd[i].hwndTitle); if (!fReplacing && hdeCur && !FSameFile(hde, QDE_FM((QDE) hdeCur))) { FM fmTemp = FmCopyFm(QDE_FM((QDE) hde)); fReplacing = TRUE; FReplaceHde(ahwnd[i].pszMemberName, &fmTemp, NULL); ASSERT(!fmTemp); fReplacing = FALSE; } if (fHelp != TCARD_HELP) { SetFocusHwnd(ahwnd[i].hwndParent); } fRv = TRUE; break; } } /* * if this is a request for the MAIN class, we just accept it and set * focus to it. */ if (i == MAIN_HWND) { if (fMainSized && !fResizeMain) // 4.0: add all the flags, not just the first one. wsmag.grf &= ~(fWindowX | fWindowY | fWindowDX | fWindowDY | fWindowMaximize); fMainSized = TRUE; SetFocusHwnd(ahwnd[MAIN_HWND].hwndParent); } else { /* * If the member refers to a secondary window, and it does not * exist, create it. */ if (i == MAX_WINDOWS) { // we didn't find the window // Only 4.0 files can have multiple secondary windows open if ((QDE_HHDR((QDE) hde).wVersionNo < wVersion40)) { iCurWindow = MAIN_HWND; DestroyAllSecondarys(); } if ((i = FCreate2ndHwnd(&rcw, &wsmag)) > 0) { // If this is being created for "normal" reasons, if (!fNoShow) { ShowWindow(ahwnd[i].hwndParent, SW_SHOWNA); } FCloneHde(txtZeroLength, NULL, hde); // REVIEW: right place? buttons must be created first ExecuteConfigMacros(iWin, hde); // 4.0: add all flags SetWindowLong(ahwnd[i].hwndParent, GHWL_HICON, (LONG) GhDupGh((HGLOBAL) GetWindowLong(ahwnd[MAIN_HWND].hwndParent, GHWL_HICON))); } else return FALSE; } if (fHelp != TCARD_HELP) SetFocusHwnd(ahwnd[i].hwndParent); fRv = TRUE; iWin2ndMember = iWin; } if (fRv) { iCurWindow = i; /* * A new window member, main or secondary, was switched to. Check * for changing those characteristics we change ONLY when we switch * to a new member. */ if (wsmag.grf & FWSMAG_ON_TOP) SetOnTopState(iCurWindow, ONTOP_AUTHOREDON); else SetOnTopState(iCurWindow, ONTOP_NOTSET); /* * 4.0: The 3.1 version of WinHelp used the fWindowMaximize * flag to determine whether wMax should override the user's * settings for a maximized or normal window. We maintain this * for backwards compatibility, but add the ability to make the * maximize flag in wMax one of 16 possible flags instead of * taking up and entire word just to set this one flag. * 06-Feb-1994 [ralphw] */ if (wsmag.grf & fWindowMaximize) { if (wsmag.wMax & FWSMAG_WMAX_MAXIMIZE) { if (!IsZoomed(ahwnd[iCurWindow].hwndParent)) { ShowWindow(ahwnd[iCurWindow].hwndParent, SW_SHOWMAXIMIZED); if (iCurWindow == MAIN_HWND) cntFlags.fMainMax = TRUE; } } } if (IsIconic(ahwnd[iCurWindow].hwndParent)) ShowWindow(ahwnd[iCurWindow].hwndParent, SW_SHOWNORMAL); // if a window size was specified, set it. if (!ahwnd[iCurWindow].fAutoSize && // 4.0: use all flags (wsmag.grf & (fWindowX | fWindowY | fWindowDX | fWindowDY)) && !IsZoomed(ahwnd[iCurWindow].hwndParent)) { if (iCurWindow == MAIN_HWND) CopyRect((RECT*) &rctHelp, (RECT*) &rcw); SetWindowPos(ahwnd[iCurWindow].hwndParent, NULL, rcw.left, rcw.top, rcw.cx, rcw.cy, SWP_NOACTIVATE | SWP_NOZORDER | SWP_DRAWFRAME); } } } else wsmag.grf = 0; /* * if the member name was not found in the help file, we must support * MAIN as a member regardless. */ if (!fRv && _strcmpi(pszMember, txtMain) == 0) { SetFocusHwnd(ahwnd[MAIN_HWND].hwndParent); fRv = TRUE; } if (fRv) { // Set background colors SetWindowLong(ahwnd[iCurWindow].hwndTopic, GTWW_COBACK, ((wsmag.grf & fWindowRgbMain) && !cntFlags.fOverColor && !fDisableAuthorColors) ? wsmag.rgbMain : coNIL); SetWindowLong(ahwnd[iCurWindow].hwndTitle, GNWW_COBACK, ((wsmag.grf & fWindowRgbNSR) && !cntFlags.fOverColor && !fDisableAuthorColors) ? wsmag.rgbNSR : coNIL); } /* Finally, change the active window focus to the window that we just * activated internally. */ /*------------------------------------------------------------*\ | OK, this is a little hack. If we believe that the current | command is the IdNoFocus command, there is not point in | getting the focus now, is there. \*------------------------------------------------------------*/ if (usCurrentCommand != cmdIdNoFocus && usCurrentCommand != cmdPWinNoFocus && hwndTCApp == NULL && !fNoShow) SetFocus(ahwnd[iCurWindow].hwndParent); ShowCurrentWindow(hde); /*------------------------------------------------------------*\ | The window caption may be a little screwed up at this time. \*------------------------------------------------------------*/ if (fRv && (wsmag.grf & fWindowCaption) && strlen(wsmag.rgchCaption)) // If a caption was specified, set it. SetWindowText(ahwnd[iCurWindow].hwndParent, wsmag.rgchCaption); else SetCaptionHde(hde, ahwnd[iCurWindow].hwndParent, (ahwnd[iCurWindow].hwndParent == ahwnd[MAIN_HWND].hwndParent)); if (IsIconic(ahwnd[iCurWindow].hwndParent)) // Similarly, if the active window is iconic, it probably should not be. ShowWindow(ahwnd[iCurWindow].hwndParent, SW_SHOWNORMAL); // BUGBUG: 12-Apr-1994 [ralphw] We shouldn't get here with this error. if (!fRv && *pszMember && fHelpAuthor) { PostErrorMessage(wERRS_WINCLASS); } return fRv; } /*************************************************************************** * - Name: FCreate2nd - * Purpose: * Creates a secondary window of the named class * * Arguments: * prc - pointer to rcw describing the initial size * fOnTop - TRUE -> Help On Top * * Returns: * hwnd on success, else null. * ***************************************************************************/ #ifndef WS_EX_CLIENTEDGE #define WS_EX_WINDOWEDGE 0x00000100L #define WS_EX_CLIENTEDGE 0x00000200L #endif INLINE static int STDCALL FCreate2ndHwnd(WRECT* prc, const WSMAG* pwsmag) { PCSTR pszClass; int iWindow; BOOL fOnTop = cntFlags.fsOnTop == ONTOP_FORCEON || pwsmag->grf & FWSMAG_ON_TOP; if (cntFlags.fsOnTop == ONTOP_FORCEOFF) fOnTop = FALSE; pszClass = rgWndClsInfo[IWNDCLS2ND].szClassName; // Find an empty slot for our new window for (iWindow = MAIN_HWND + 1; iWindow < MAX_WINDOWS; iWindow++) { if (!ahwnd[iWindow].hwndParent) break; } if (iWindow == MAX_WINDOWS) { Error(wERRS_TOOMANY_WINDOWS, wERRA_RETURN); // Force to an already open window return MAIN_HWND + 1; } // Save the member name ahwnd[iWindow].pszMemberName = LocalStrDup(pwsmag->rgchMember); if (!ahwnd[iWindow].pszMemberName) BOOM(wERRS_OOM_OUT_OF_LOCAL); if (!fOnTop) CheckWindowPosition(prc, TRUE); ahwnd[iWindow].hwndParent = CreateWindowEx((fOnTop ? WS_EX_TOPMOST : 0) | WS_EX_CLIENTEDGE, pszClass, NULL, WS_THICKFRAME | ((fHelp == TCARD_HELP) ? (WS_OVERLAPPED | WS_SYSMENU | WS_CLIPCHILDREN) : WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX), prc->left, prc->top, prc->cx, prc->cy, NULL, NULL, hInsNow, NULL); ASSERT(ahwnd[iWindow].hwndParent); if (!ahwnd[iWindow].hwndParent) return -1; #if 0 // we don't support icons in the help file any more { HICON hiconLocal = (HICON) GetWindowLong(ahwnd[MAIN_HWND].hwndParent, GHWL_HICON); SetClassLong(ahwnd[iWindow].hwndParent, GCL_HICON, (int) (hiconLocal ? hiconLocal : hIconDefault)); } #endif if (!CreateChildWindows(iWindow, pwsmag, TRUE)) { if (ahwnd[iWindow].hwndParent) { DestroyWindow(ahwnd[iWindow].hwndParent); ahwnd[iWindow].hwndParent = NULL; } FreeLh(ahwnd[iWindow].pszMemberName); ahwnd[iWindow].pszMemberName = NULL; return -1; } if (pwsmag->grf & FWSMAG_AUTO_SIZE) ahwnd[iWindow].fAutoSize = TRUE; #if defined(BIDI_MULT) // jgross - determine if vert scroll bars go on // the left or right { extern BOOL IsSetup, RtoL; if (IsSetup) EnableMenuItem(GetSystemMenu(ahwnd[iWindow].hwndParent, FALSE), 8, MF_BYPOSITION | MF_GRAYED); MakeScrollBarsRtoL(ahwnd[iWindow].hwndTopic, RtoL, TRUE); } #endif return iWindow; } /*************************************************************************** * - Name: SetFocusHwnd - * Purpose: * Changes the topic focus to the passed hwnd. This changes the >internal< * focus, and does not affect the actual focus as maintained by windows * and/or as seen by the user. * * Arguments: * hwndCur - main level window getting focus (ahwnd[MAIN_HWND].hwndParent or * * Returns: * nothing * * Globals Used: * updates ahwnd[iCurWindow].hwndTopic * * +++ * * Notes: * ***************************************************************************/ void STDCALL SetFocusHwnd(HWND hwndCur) { int i; if (!hwndCur || hwndCur == ahwnd[iCurWindow].hwndParent) return; for (i = 0; i < MAX_WINDOWS; i++) { if (i == iCurWindow) continue; // we already checked if (hwndCur == ahwnd[i].hwndParent) { FSetEnv(ahwnd[iCurWindow = i].hwndTopic); return; } } } /*************************************************************************** FUNCTION: HwndMemberNsz PURPOSE: Given a window name, returns the handle to the parent if that window actuall exists. PARAMETERS: pszMember -- window name. May be NULL or "" for main window RETURNS: Parent window handle or NULL COMMENTS: MODIFICATION DATES: 13-Jun-1994 [ralphw] Return hwndParent not hwndTopic ***************************************************************************/ HWND STDCALL HwndMemberNsz(PCSTR pszMember) { if (!pszMember || !*pszMember) return ahwnd[iCurWindow].hwndTopic; else if (!_strcmpi(pszMember, txtMain)) return ahwnd[MAIN_HWND].hwndTopic; else { int i; for (i = MAIN_HWND + 1; i < MAX_WINDOWS; i++) { if (ahwnd[i].hwndParent && _strcmpi(pszMember, ahwnd[i].pszMemberName) == 0) return ahwnd[i].hwndTopic; } } /* * A member name starting with an '@' is a window name converted * by the help compiler into a number. If it is not followed by * a number, then the secondary window is implied (doesn't matter * which secondary window). */ if (*pszMember == '@') return HwndMemberNsz(ConvertToWindowName(atoi(pszMember + 1), QdeFromGh(HdeGetEnv()))); return NULL; } /*************************************************************************** * - Name: InformWindow - * Purpose: Function to postion, close, or set the focus to a help * window. * * wAction - the action to take place. May be any of * IWA_CLOSE - close the window * IWA_FOCUS - give the window the focus * IWA_MOVE - move the given window. * lh - local handle to a WININFO structure (see * button.h). * * Returns: nothing. * * Notes: REVIEW: This function makes no attempts to assure that * the help window stays on the screen. If it should end up * off the screen, it may be lost for a single session. At * the start of the next session, the code that gets the position * from WIN.INI will force it back on the screen. * ***************************************************************************/ void STDCALL InformWindow(int wAction, PWININFO pwininfo) { HWND hwnd; // handle of window to muck with. HWND hwndFocusOrg; // window with startng focus ASSERT(pwininfo); ASSERT(wAction < 4); hwnd = HwndMemberNsz(pwininfo->rgchMember); if (wAction != IFMW_MOVE) FreeLh(pwininfo); if (!hwnd) return; hwnd = GetParent(hwnd); // Get the parent window handle switch (wAction) { case IFMW_CLOSE: SendMessage(hwnd, WM_CLOSE, 0, 0L); return; case IFMW_FOCUS: if (fHelp != TCARD_HELP) SetFocus(hwnd); return; case IFMW_MOVE: hwndFocusOrg = ahwnd[iCurWindow].hwndParent; ASSERT (pwininfo->wMax <= SW_MAX); if (!fNoShow) ShowWindow(hwnd, pwininfo->wMax); // wMax is simply a SW_ parameter // New to 4.0 -- ignore if pwininfo->dx == 0 and negative numbers // mean absolute if (!IsIconic(hwnd) && !IsZoomed(hwnd) && pwininfo->dx) { if (!cxScreen) { GetScreenResolution(); } // if (pwininfo->x < 0, then use absolute values if (pwininfo->x >= 0) MoveWindow(hwnd, pwininfo->x * cxScreen / dxVirtScreen, pwininfo->y * cyScreen / dyVirtScreen, pwininfo->dx * cxScreen / dxVirtScreen, pwininfo->dy * cyScreen / dyVirtScreen, TRUE); else MoveWindow(hwnd, -pwininfo->x, -pwininfo->y, -pwininfo->dx, -pwininfo->dy, TRUE); } FreeLh(pwininfo); /* * Ensure that focus remains with the window that was in focus * when we started. */ SetFocusHwnd(hwndFocusOrg); return; default: return; } } /*************************************************************************** FUNCTION: DestroyAllSecondarys PURPOSE: Destroy all secondarys except the current one PARAMETERS: void RETURNS: COMMENTS: MODIFICATION DATES: 26-Jul-1994 [ralphw] ***************************************************************************/ void STDCALL DestroyAllSecondarys(void) { static BOOL fDestroying = FALSE; int i, iSavCur = iCurWindow; // Prevent recursion if (fDestroying) return; else fDestroying = TRUE; for (i = MAIN_HWND + 1; i < MAX_WINDOWS; i++) { if (ahwnd[i].hwndParent && i != iSavCur) { ASSERT(IsValidWindow(ahwnd[i].hwndParent)); DestroyWindow(ahwnd[i].hwndParent); ahwnd[i].hwndParent = NULL; } } SetFocusHwnd(ahwnd[iSavCur].hwndParent); fDestroying = FALSE; } void STDCALL ShowCurrentWindow(HDE hde) { if (!IsWindowVisible(ahwnd[iCurWindow].hwndParent) && !fNoShow) { if (!fDelayShow) { ShowWindow(ahwnd[iCurWindow].hwndParent, (iCurWindow == MAIN_HWND && cntFlags.fMainMax && !IsIconic(ahwnd[iCurWindow].hwndParent)) ? SW_SHOWMAXIMIZED : SW_SHOW); ShowWindow(ahwnd[iCurWindow].hwndTopic, SW_SHOW); ShowWindow(ahwnd[iCurWindow].hwndTitle, SW_SHOW); SetForegroundWindow(ahwnd[iCurWindow].hwndParent); /* * If we're bringing the window to the forground for the first * time, there's a fair chance it may not have a DE associated * with it yet. Make sure that there is one. */ if (!HdeGetEnvHwnd(ahwnd[iCurWindow].hwndTopic)) FCloneHde(txtZeroLength, NULL, hde); } } } /*************************************************************************** FUNCTION: ExecuteConfigMacros PURPOSE: Execute the config section for a secondary window PARAMETERS: idxWindow -- index of the window. Index numbers should be in the same order as the wsmag structure RETURNS: nothing COMMENTS: returns immediately without an error if there is no config section for the window. config macros are stored as a single string (semi-colon separated) and each stored in its own file with the prefix |CFn where n is the index of the secondary window. MODIFICATION DATES: 22-Jun-1994 [ralphw] ***************************************************************************/ static char szConfigName[] = "|CF "; // 3 trailing spaces required static void STDCALL ExecuteConfigMacros(int idxWindow, HDE hde) { HF hf; int cb; LPSTR psz; ASSERT(idxWindow < 256 && idxWindow >= 0); wsprintf(szConfigName + 3, txtFormatUnsigned, idxWindow); if ((hf = HfOpenHfs(QDE_HFS(QdeFromGh(hde)), szConfigName, fFSOpenReadOnly)) == NULL) return; // there was no config section for this window cb = LcbSizeHf(hf); psz = (PSTR) GhAlloc(GMEM_FIXED, cb + 1); if (!psz) { BOOM(wERRS_OOM_CONFIG_MACRO); // complain and die return; } if (LcbReadHf(hf, psz, (LONG) cb) <= 0) { if (RcGetFSError() == rcOutOfMemory) BOOM(wERRS_OOM_CONFIG_MACRO); // complain and die // REVIEW: now what? The file is probably corrupted RcCloseHf(hf); FreeGh((GH) psz); return; } RcCloseHf(hf); Execute(psz); FreeGh((GH) psz); } /*************************************************************************** FUNCTION: ConvertToWindowName PURPOSE: Given an index into our smag structures, return a pointer to the window name PARAMETERS: i RETURNS: Window name or "MAIN" if the number isn't valid COMMENTS: MODIFICATION DATES: 06-Nov-1994 [ralphw] ***************************************************************************/ PCSTR STDCALL ConvertToWindowName(int i, QDE qde) { QRGWSMAG qrgwsmag; ASSERT(qde); qrgwsmag = (QRGWSMAG) QDE_HRGWSMAG(qde); ASSERT(qrgwsmag); // Theoretically impossible -- means no windows defined if (!qrgwsmag) return txtMain; ASSERT(i < qrgwsmag->cWsmag); if (i >= qrgwsmag->cWsmag || i < 0) return txtMain; return qrgwsmag->rgwsmag[i].rgchMember; } #ifdef _DEBUG #undef ShowWindow #undef MoveWindow #undef IsWindowVisible // We use these debug variations so that all calls will go through a single // function. Makes it a LOT easier to debug who's calling them when. BOOL STDCALL DebugShowWindow(HWND hwnd, int nCmdShow) { return ShowWindow(hwnd, nCmdShow); } BOOL STDCALL DebugMoveWindow(HWND hwnd, int x, int y, int cx, int cy, BOOL bRepaint) { BOOL fZoomed = IsZoomed(hwnd); BOOL fIconic = IsIconic(hwnd); ASSERT(!IsZoomed(hwnd)); ASSERT(!IsIconic(hwnd)) return MoveWindow(hwnd, x, y, cx, cy, bRepaint); } BOOL STDCALL DebugIsWindowVisible(HWND hwnd) { // ASSERT(IsValidWindow(hwnd)); return IsWindowVisible(hwnd); } #endif