/*****************************************************************************
*
*  frfc.c
*  Copyright (C) Microsoft Corporation 1990.
*  All Rights reserved.
*
******************************************************************************
*
* This file contains code for handling FCs in the layout manager.
*
* FCs (Full Context Points) are one of the key concepts of the layout
* manager. An FC is a piece of layout material which contains all of the
* information needed to display it. A display topic is nothing more than a
* series of FCs. FCs are never side by side- they are always stacked.
*
* FCs are laid out in a logical coordinate space where 0,0 corresponds to
* the upper-left-hand corner of the layout area. 0,0 in FC coordinates,
* therefore, corresponds to de.rct.left,de.rct.top in display device
* coordinates. All coordinates passed into frfc.c are in FC coordinates.
*
* FCs are stored in an MRD.
* The data structures in the FCM are as follows:
*	HANDLE hfr; 		handle to array of FRs.  Always exists, even when
*						   there are no frames (the size is padded by 1).
*	INT fExport;		used for text export?
*	HFC hfc;			HFC containing raw layout data for this FC
*	FCID fcid;			FCID of this FC
*	INT xPos;			position of the FC in FC space
*	INT yPos;
*	INT dxSize; 		size of the FC
*	INT dySize;
*	INT cfr;			total number of frames in the FC.  This may be 0.
*	INT wStyle; 		current text style for this FC.  Only for layout.
*	INT imhiFirst;		hotspot manager info
*	INT imhiLast;
*
******************************************************************************
*
*  Testing Notes
*
******************************************************************************
*
*  Current Owner:
*
******************************************************************************
*
*  Released by Development:
*
******************************************************************************
*
*  Revision History:
* 15-Aug-1989 MattB 	Created
* 24-Oct-1990 LeoN		JumpButton takes a pointer to its arg. Minor clenaup
* 04-Nov-1990 Tomsn 	Use new VA address type (enabling zeck compression).
* 30-Jul-1991 LeoN		HELP31 #1244: remove fHiliteMatches from DE. Add
*						FSet/GetMatchState
*
*****************************************************************************/

#include "help.h"

#include "inc\frstuff.h"

/*-------------------------------------------------------------------------
| IfcmLayout(qde, hfc, yPos, fFirst, fExport)							  |
|																		  |
| Purpose:	This lays out a new FC corresponding to the passed HFC, and   |
|			returns its IFCM.											  |
| Params:	qde 	   qde to use										  |
|			hfc 	   hfc containing raw layout data					  |
|			yPos	   vertical position of this FCM					  |
|			fFirst	   TRUE if FCM is first in layout chain 			 |
|			fExport    TRUE if FCM is being used for text export.		 |
| Method:	Each FC contains a single layout object, so all we do is call |
|			LayoutObject() to lay it out.  During layout, all frames are  |
|			placed in temporary storage provided by de.mrfr.  After 	  |
|			layout, we increase the size of hfcm, and append the frames   |
|			after the fcm structure.									  |
-------------------------------------------------------------------------*/

IFCM STDCALL IfcmLayout(QDE qde, HFC hfc, int yPos, BOOL fFirst, BOOL fExport)
{
	IFCM ifcm;
	QFCM qfcm;
	QB qbObj, qb;
	MOBJ mobj;
	LPSTR qchText;
	int cfr;
	OLR olr;

	if (fFirst)
		ifcm = IFooInsertFooMRD(((QMRD)&qde->mrdFCM), sizeof(FCM), FOO_NIL);
	else
		ifcm = IFooInsertFooMRD(((QMRD)&qde->mrdFCM), sizeof(FCM), IFooLastMRD(((QMRD)&qde->mrdFCM)));
	qfcm = (QFCM)QFooInMRD(((QMRD)&qde->mrdFCM), sizeof(FCM), ifcm);
	qfcm->fExport = fExport;
	qfcm->hfc = hfc;
	qfcm->va =	 VaFromHfc(hfc);
	qfcm->xPos = xLeftFCMargin;
	qfcm->yPos = yPos;
	qfcm->cfr = 0;
	qfcm->wStyle = wStyleNil;
	ClearMR((QMR)&qde->mrFr);

	qbObj = (QB) QobjLockHfc(hfc);
#ifdef _X86_
	qchText = qbObj + CbUnpackMOBJ((QMOBJ)&mobj, qbObj);
#else
	qchText = qbObj + CbUnpackMOBJ((QMOBJ)&mobj, qbObj, QDE_ISDFFTOPIC(qde));
#endif
	qchText += mobj.lcbSize;

	qfcm->cobjrg = (COBJRG)mobj.wObjInfo;
#ifdef RAWHIDE
	qfcm->cobjrgP = CobjrgFromHfc(hfc);
#endif

	olr.xPos = olr.yPos = 0;
	olr.ifrFirst = 0;
	olr.objrgFirst = 0;
	olr.objrgFront = objrgNil;

	AccessMR((QMR)&qde->mrFr);
	LayoutObject(qde, qfcm, qbObj, qchText, qde->rct.right - qde->rct.left
		- xLeftFCMargin, &olr);

	cfr = qfcm->cfr = olr.ifrMax;

	/*
	 * REVIEW: This is pretty gross. cfr can be 0, but we always want to
	 * allocate an hfr, so that we don't have to check for it everywhere.
	 */

	qfcm->hfr = GhForceAlloc(0, cfr * sizeof(FR) + 1);
	qb = PtrFromGh(qfcm->hfr);
	MoveMemory((QB)qb, (QB)QFooInMR((QMR)&qde->mrFr, sizeof(FR), 0),
		cfr * sizeof(FR));
	DeAccessMR((QMR)&qde->mrFr);

	qfcm->dxSize = olr.dxSize;
	qfcm->dySize = olr.dySize;
	if (!fExport) {
		RegisterHotspots(qde, ifcm, fFirst);
#ifdef RAWHIDE
		RegisterSearchHits(qde, ifcm, qchText);
#endif
	}

	return ifcm;
}

/*-------------------------------------------------------------------------
| DrawIfcm(qde, ifcm, pt, qrct, ifrFirst, ifrMax)						  |
|																		  |
| Purpose:	Draws a specified set of frames in an FCM.					  |
| Params:	qde 		qde to use										  |
|			pt			offset between FC space and display space		  |
|			qrct		rectangle containing the area we want to draw.	  |
|						This is for efficiency only- we don't handle      |
|						clipping.  If qrct == NULL, it's ignored.         |
|			ifrFirst	First frame to draw 							  |
|			ifrMax		Max of frames to draw (ie, ifrMax isn't drawn)    |
-------------------------------------------------------------------------*/

void STDCALL DrawIfcm(QDE qde, IFCM ifcm, POINT pt, LPRECT qrct, int ifrFirst,
	int ifrMax, BOOL fErase)
{
  QFCM qfcm;
  QB qbObj;
  MOBJ mobj;
  LPSTR qchText;
  QFR qfr;
  QFR qfrStart;
  int ifr;

  qfcm = (QFCM)QFooInMRD(((QMRD)&qde->mrdFCM), sizeof(FCM), ifcm);
  ASSERT(!qfcm->fExport);
  pt.x += qfcm->xPos;
  pt.y += qfcm->yPos;
  if (qrct != NULL && (pt.y > qrct->bottom || pt.y + qfcm->dySize <= qrct->top))
	return;

  qbObj = (QB) QobjLockHfc(qfcm->hfc);
#ifdef _X86_
  qchText = qbObj + CbUnpackMOBJ((QMOBJ)&mobj, qbObj);
#else
  qchText = qbObj + CbUnpackMOBJ((QMOBJ)&mobj, qbObj, QDE_ISDFFTOPIC(qde));
#endif
  qchText += mobj.lcbSize;

  qfrStart = qfr = (QFR)PtrFromGh(qfcm->hfr);

  for (ifr = ifrFirst, qfr += ifr; ifr < ifrMax; ifr++, qfr++)
	{
	if (qrct != NULL && (qfr->yPos + pt.y > qrct->bottom
	  || qfr->yPos + qfr->dySize + pt.y <= qrct->top))
	  continue;
	switch(qfr->bType) {
	  case bFrTypeText:
		DrawTextFrame(qde, qchText, qfr, pt, fErase);
		break;

	  case bFrTypeAnno:
		DrawAnnoFrame(qde, qfr, pt);
		break;

	  case bFrTypeBitmap:
		DrawBitmapFrame(qde, qfr, pt, fErase);
		break;

	  case bFrTypeHotspot:
		DrawHotspotFrame(qde, qfr, pt, fErase);
		break;

	  case bFrTypeBox:
		DrawBoxFrame(qde, qfr, pt);
		break;

	  case bFrTypeWindow:
		DrawWindowFrame(qde, qfr, pt);
		break;

	  case bFrTypeColdspot:
		/* Currently never drawn */
		/* DrawColdspot(qde, qfr, pt); */
		break;
	}
#ifdef _DEBUG
#define coYELLOW		RGB(255, 255,	0)
	  if (fDebugState & fDEBUGFRAME)
		{
		HSGC  hsgc;

		hsgc = (HSGC) HsgcFromQde(qde);
		FSetPen(hsgc, 1, coDEFAULT, coYELLOW, wTRANSPARENT, roXOR, wPenSolid);
		Rectangle(hsgc, pt.x + qfr->xPos, pt.y + qfr->yPos,
		 pt.x + qfr->xPos + qfr->dxSize, pt.y + qfr->yPos + qfr->dySize);
		FreeHsgc(hsgc);
		}
#endif
	}

#ifdef _DEBUG
#define coMAGENTA RGB(255, 0, 255)
	  if (fDebugState & fDEBUGFRAME)
		{
		HSGC  hsgc;

		hsgc = (HSGC) HsgcFromQde(qde);
		FSetPen(hsgc, 1, coDEFAULT, coMAGENTA, wTRANSPARENT, roCOPY, wPenSolid);
		Rectangle(hsgc, pt.x, pt.y, pt.x + qfcm->dxSize, pt.y + qfcm->dySize);
		FreeHsgc(hsgc);
		}
#endif /* DEBUG */

#ifdef RAWHIDE
	if (FGetMatchState()) {
		/*
		 * If printing, don't show search hilites. On HP printers, prints
		 * out black squares instead of text where the search matches are.
		 */

		if (qde->deType != dePrint)
			DrawMatchesIfcm(qde, ifcm, pt, qrct, ifrFirst, ifrMax, fErase);
	}
#endif
}

/***************************************************************************

	FUNCTION:	ClickFC

	PURPOSE:	Handles a mouse click in an FC

	PARAMETERS:
		qde
		ifcm
		pt		Offset between FC space and display space

	RETURNS:	TRUE if point is in a hotspot

	COMMENTS:

	MODIFICATION DATES:
		13-Mar-1995 [ralphw]

***************************************************************************/

BOOL STDCALL ClickFC(QDE qde, IFCM ifcm, POINT pt)
{
	QFCM qfcm;
	QFR qfr;
	int xNew, yNew, ifr;

	qfcm = (QFCM)QFooInMRD(((QMRD)&qde->mrdFCM), sizeof(FCM), ifcm);
	ASSERT(!qfcm->fExport);
	qfr = (QFR) PtrFromGh(qfcm->hfr);
	xNew = pt.x - qfcm->xPos;
	yNew = pt.y - qfcm->yPos;
	for (ifr = 0; ifr < qfcm->cfr; ifr++, qfr++) {
		if (qfr->rgf.fHot
				&& xNew >= qfr->xPos && xNew <= qfr->xPos + qfr->dxSize
				&& yNew >= qfr->yPos && yNew <= qfr->yPos + qfr->dySize) {
			FSelectHotspot(qde, FOO_NIL);
			ClickFrame(qde, ifcm, ifr);
			return TRUE;
		}
	}
	return FALSE;
}

/*-------------------------------------------------------------------------
| DiscardIfcm(qde, ifcm)												  |
|																		  |
| Purpose:	Discards all memory associated with an FC					  |
-------------------------------------------------------------------------*/

static int count;

void STDCALL DiscardIfcm(QDE qde, int ifcm)
{
	QFCM qfcm;
	QFR qfr;

	qfcm = (QFCM)QFooInMRD(((QMRD)&qde->mrdFCM), sizeof(FCM), ifcm);
	qfr = (QFR)PtrFromGh(qfcm->hfr);
	if (!qfcm->fExport) {
		ReleaseHotspots(qde, ifcm);
#ifdef RAWHIDE
		ReleaseSearchHits(qde, ifcm);
#endif
	}
	count++;
	DiscardFrames(qde, qfr, qfr + qfcm->cfr);

	if (qfcm->hfc != NULL)
		lcClearFree(&qfcm->hfc);

	if (qfcm->hfr)
		lcClearFree(&qfcm->hfr);
	DeleteFooMRD(((QMRD)&qde->mrdFCM), sizeof(FCM), ifcm);
}

/*-------------------------------------------------------------------------
| DiscardFrames(qde, qfrFirst, qfrMax)									  |
|																		  |
| Purpose:	Discards all memory associated with a given set of frames.	  |
|			Currently, only bitmap, window and possibly hotspot frames	  |
|			allocate memory.											  |
-------------------------------------------------------------------------*/
void STDCALL DiscardFrames(QDE qde, QFR qfrFirst, QFR qfrMax)
{
	QFR qfr;

	for (qfr = qfrFirst; qfr < qfrMax; qfr++) {
		switch (qfr->bType) {
			case bFrTypeText:
			case bFrTypeAnno:
				break;

			case bFrTypeBitmap:
				DiscardBitmapFrame(qfr);
				break;

			case bFrTypeHotspot:
				DiscardHotspotFrame(qfr);
				break;

			case bFrTypeWindow:
				DiscardWindowFrame(qde, qfr);
				break;
		}
	}
}

 /***************
 **
 ** GH	GhForceAlloc(WORD wFlags, DWORD lcb)
 **
 ** purpose
 **   Create a handle to relocatable block
 **   Identical to GhAlloc, but dies in the event of an error
 **
 ** arguments
 **   wFlags  Memory allocation flags |'ed together
 **   lcb	  Number of bytes to allocate
 **
 ** return value
 **   Handle to allocated block of memory, or NULL otherwise
 **
 ***************/

GH STDCALL GhForceAlloc(UINT wFlags, DWORD lcb)
{
	GH gh;

	if ((gh = GhAlloc(wFlags, lcb)) == NULL)
		OOM();

	return gh;
}