/*************************************************************************
**
**    OLE 2 Sample Code
**
**    outldata.c
**
**    This file contains LineList and NameTable functions
**    and related support functions.
**
**    (c) Copyright Microsoft Corp. 1992 - 1993 All Rights Reserved
**
*************************************************************************/


#include "outline.h"

OLEDBGDATA

extern LPOUTLINEAPP g_lpApp;

char ErrMsgListBox[] = "Can't create ListBox!";

static int g_iMapMode;

/* LineList_Init
 * -------------
 *
 *      Create and Initialize the LineList (owner-drawn listbox)
 */
BOOL LineList_Init(LPLINELIST lpLL, LPOUTLINEDOC lpOutlineDoc)
{
	LPOUTLINEAPP lpOutlineApp = (LPOUTLINEAPP)g_lpApp;

#if defined( INPLACE_CNTR )
	lpLL->m_hWndListBox = CreateWindow(
					"listbox",              /* Window class name           */
					NULL,                   /* Window's title              */

					/* OLE2NOTE: an in-place contanier MUST use
					**    WS_CLIPCHILDREN window style for the window
					**    that it uses as the parent for the server's
					**    in-place active window so that its
					**    painting does NOT interfere with the painting
					**    of the server's in-place active child window.
					*/

					WS_CLIPCHILDREN |
					WS_CHILDWINDOW |
					WS_VISIBLE |
					WS_VSCROLL |
					WS_HSCROLL |
					LBS_EXTENDEDSEL |
					LBS_NOTIFY |
					LBS_OWNERDRAWVARIABLE |
					LBS_NOINTEGRALHEIGHT |
					LBS_USETABSTOPS,
					0, 0,                   /* Use default X, Y            */
					0, 0,                   /* Use default X, Y            */
					lpOutlineDoc->m_hWndDoc,/* Parent window's handle      */
					(HMENU)IDC_LINELIST,    /* Child Window ID             */
					lpOutlineApp->m_hInst,  /* Instance of window          */
					NULL);                  /* Create struct for WM_CREATE */
#else
	lpLL->m_hWndListBox = CreateWindow(
					"listbox",              /* Window class name           */
					NULL,                   /* Window's title              */
					WS_CHILDWINDOW |
					WS_VISIBLE |
					WS_VSCROLL |
					WS_HSCROLL |
					LBS_EXTENDEDSEL |
					LBS_NOTIFY |
					LBS_OWNERDRAWVARIABLE |
					LBS_NOINTEGRALHEIGHT |
					LBS_USETABSTOPS,
					0, 0,                   /* Use default X, Y            */
					0, 0,                   /* Use default X, Y            */
					lpOutlineDoc->m_hWndDoc,/* Parent window's handle      */
					(HMENU)IDC_LINELIST,    /* Child Window ID             */
					lpOutlineApp->m_hInst,  /* Instance of window          */
					NULL);                  /* Create struct for WM_CREATE */

#endif


	if(! lpLL->m_hWndListBox) {
		OutlineApp_ErrorMessage(g_lpApp, ErrMsgListBox);
		return FALSE;
	}

	lpOutlineApp->m_ListBoxWndProc =
			(FARPROC) GetWindowLong ( lpLL->m_hWndListBox, GWL_WNDPROC );
	SetWindowLong (lpLL->m_hWndListBox, GWL_WNDPROC, (LONG) LineListWndProc);

#if defined ( USE_DRAGDROP )
	/* m_iDragOverLine saves index of line that has drag/drop target
	**    feedback. we currently use our focus rectangle feedback for
	**    this. it would be better to have a different visual feedback
	**    for potential target of the pending drop.
	*/
	lpLL->m_iDragOverLine = -1;
#endif

	lpLL->m_nNumLines = 0;
	lpLL->m_nMaxLineWidthInHimetric = 0;
	lpLL->m_lpDoc = lpOutlineDoc;
	_fmemset(&lpLL->m_lrSaveSel, 0, sizeof(LINERANGE));

	return TRUE;
}


/* LineList_Destroy
 * ----------------
 *
 *      Clear (delete) all Line objects from the list and free supporting
 *      memory (ListBox Window) used by the LineList object itself.
 */
void LineList_Destroy(LPLINELIST lpLL)
{
	int i;
	int linesTotal = lpLL->m_nNumLines;

	// Delete all Line objects
	for (i = 0; i < linesTotal; i++)
		LineList_DeleteLine(lpLL, 0);   // NOTE: always delete line 0

	// Remove all Lines from the ListBox
	SendMessage(lpLL->m_hWndListBox,LB_RESETCONTENT,0,0L);

	lpLL->m_nNumLines=0;
	DestroyWindow(lpLL->m_hWndListBox);
	lpLL->m_hWndListBox = NULL;
}


/* LineList_AddLine
 * ----------------
 *
 *      Add one line to the list box. The line is added following the
 * line with index "nIndex". If nIndex is larger than the number of lines
 * in the ListBox, then the line is appended to the end. The selection
 * is set to the newly added line.
 */
void LineList_AddLine(LPLINELIST lpLL, LPLINE lpLine, int nIndex)
{
	int nAddIndex = (lpLL->m_nNumLines == 0 ?
			0 :
			(nIndex >= lpLL->m_nNumLines ? lpLL->m_nNumLines : nIndex+1));
	LINERANGE lrSel;

#if defined( USE_HEADING )
	int nHeight = Line_GetHeightInHimetric(lpLine);

	nHeight = XformHeightInHimetricToPixels(NULL, nHeight);

	// Add a dummy string to the row heading
	Heading_RH_SendMessage(OutlineDoc_GetHeading(lpLL->m_lpDoc),
			LB_INSERTSTRING, (WPARAM)nAddIndex, MAKELPARAM(nHeight, 0));
#endif


	lrSel.m_nStartLine = nAddIndex;
	lrSel.m_nEndLine =   nAddIndex;

	if (!lpLine) {
		OutlineApp_ErrorMessage(g_lpApp, "Could not create line.");
		return;
	}

	SendMessage(lpLL->m_hWndListBox, LB_INSERTSTRING, (WPARAM)nAddIndex,
			(DWORD)lpLine);

	LineList_SetMaxLineWidthInHimetric(
			lpLL,
			Line_GetTotalWidthInHimetric(lpLine)
	);

	lpLL->m_nNumLines++;

	LineList_SetSel(lpLL, &lrSel);
}


/* LineList_DeleteLine
 * -------------------
 *
 *      Delete one line from listbox and memory
 */
void LineList_DeleteLine(LPLINELIST lpLL, int nIndex)
{
	LPLINE lpLine = LineList_GetLine(lpLL, nIndex);
	BOOL fResetSel;

	fResetSel = (BOOL)SendMessage(lpLL->m_hWndListBox, LB_GETSEL, (WPARAM)nIndex, 0L);

	if (lpLine)
		Line_Delete(lpLine);    // free memory of Line

	// Remove the Line from the ListBox
	SendMessage(lpLL->m_hWndListBox, LB_DELETESTRING, (WPARAM)nIndex, 0L);
	lpLL->m_nNumLines--;

	if (fResetSel) {
		if (nIndex > 0) {
#if defined( WIN32 )
			SendMessage(
					lpLL->m_hWndListBox,
					LB_SETSEL,
					(WPARAM)TRUE,
					(LPARAM)nIndex-1
			);
#else
			SendMessage(
					lpLL->m_hWndListBox,
					LB_SETSEL,
					(WPARAM)TRUE,
					MAKELPARAM(nIndex-1,0)
			);
#endif
		} else {
			if (lpLL->m_nNumLines > 0) {
#if defined( WIN32 )
				SendMessage(
						lpLL->m_hWndListBox,
						LB_SETSEL,
						(WPARAM)TRUE,
						(LPARAM)0
				);
#else
				SendMessage(
						lpLL->m_hWndListBox,
						LB_SETSEL,
						(WPARAM)TRUE,
						MAKELPARAM(0,0)
				);
#endif
			}
		}
	}

#if defined( USE_HEADING )
	// Remove the dummy string from the row heading
	Heading_RH_SendMessage(OutlineDoc_GetHeading(lpLL->m_lpDoc),
			LB_DELETESTRING, (WPARAM)nIndex, 0L);
#endif

}


/* LineList_ReplaceLine
 * --------------------
 *
 *      Replace the line at a given index in the list box with a new
 * line.
 */
void LineList_ReplaceLine(LPLINELIST lpLL, LPLINE lpLine, int nIndex)
{
	LPLINE lpOldLine = LineList_GetLine(lpLL, nIndex);

	if (lpOldLine)
		Line_Delete(lpOldLine);    // free memory of Line
	else
		return;     // if no previous line then invalid index

	SendMessage(
			lpLL->m_hWndListBox,
			LB_SETITEMDATA,
			(WPARAM)nIndex,
			(LPARAM)lpLine
	);
}


/* LineList_GetLineIndex
 * ---------------------
 *
 *      Return the index of the Line given a pointer to the line.
 *      Return -1 if the line is not found.
 */
int LineList_GetLineIndex(LPLINELIST lpLL, LPLINE lpLine)
{
	LRESULT lRet;

	if (! lpLine) return -1;

	lRet = SendMessage(
			lpLL->m_hWndListBox,
			LB_FINDSTRING,
			(WPARAM)-1,
			(LPARAM)(LPCSTR)lpLine
		);

	return ((lRet == LB_ERR) ? -1 : (int)lRet);
}


/* LineList_GetLine
 * ----------------
 *
 *      Retrieve the pointer to the Line given its index in the LineList
 */
LPLINE LineList_GetLine(LPLINELIST lpLL, int nIndex)
{
	DWORD dWord;
	LRESULT lRet;

	if (lpLL->m_nNumLines == 0 || nIndex > lpLL->m_nNumLines || nIndex < 0)
		return NULL;

	lRet = SendMessage(
			lpLL->m_hWndListBox,LB_GETTEXT,nIndex,(LPARAM)(LPCSTR)&dWord);

	return ((lRet == LB_ERR || lRet == 0) ? NULL : (LPLINE)dWord);
}


/* LineList_SetFocusLine
 * ---------------------
 *
 */

void LineList_SetFocusLine ( LPLINELIST lpLL, WORD wIndex )
{

	SendMessage(lpLL->m_hWndListBox, LB_SETCARETINDEX, (WPARAM)wIndex, 0L );

}


/* LineList_GetLineRect
 * --------------------
 *
 * Retrieve the rectangle of a Line given its index in the LineList
 */
BOOL LineList_GetLineRect(LPLINELIST lpLL, int nIndex, LPRECT lpRect)
{
	DWORD iReturn = (DWORD)LB_ERR;

	if ( !(lpLL->m_nNumLines == 0 || nIndex > lpLL->m_nNumLines || nIndex < 0) )
		iReturn = SendMessage(lpLL->m_hWndListBox,LB_GETITEMRECT,nIndex,(LPARAM)lpRect);

	return (iReturn == LB_ERR ? FALSE : TRUE );
}


/* LineList_GetFocusLineIndex
 * --------------------------
 *
 * Get the index of the line that currently has focus (the active line).
 */
int LineList_GetFocusLineIndex(LPLINELIST lpLL)
{
	return (int)SendMessage(lpLL->m_hWndListBox,LB_GETCARETINDEX,0,0L);
}


/* LineList_GetCount
 * -----------------
 *
 *      Return number of line objects
 */
int LineList_GetCount(LPLINELIST lpLL)
{
	if (lpLL)
		return lpLL->m_nNumLines;
	else {
		OleDbgAssert(lpLL!=NULL);
		return 0;
	}
}


/* LineList_SetMaxLineWidthInHimetric
 * ----------------------------------
 *
 *  Adjust the maximum line width for the listbox. The max line width is
 *  used to determine if a horizontal scroll bar is needed.
 *
 *  Parameters:
 *      nWidthInHimetric - if +ve, width of an additional line
 *                       - if -ve, reset Max to be the value
 *
 *  Returns:
 *      TRUE is max line width of LineList changed
 *      FALSE if no change
 */
BOOL LineList_SetMaxLineWidthInHimetric(LPLINELIST lpLL, int nWidthInHimetric)
{
	int nWidthInPix;
	BOOL fSizeChanged = FALSE;
	LPSCALEFACTOR lpscale;

	if (!lpLL)
		return FALSE;

	lpscale = OutlineDoc_GetScaleFactor(lpLL->m_lpDoc);

	if (nWidthInHimetric < 0) {
		lpLL->m_nMaxLineWidthInHimetric = -1;
		nWidthInHimetric *= -1;
	}

	if (nWidthInHimetric > lpLL->m_nMaxLineWidthInHimetric) {
		lpLL->m_nMaxLineWidthInHimetric = nWidthInHimetric;
		nWidthInPix = XformWidthInHimetricToPixels(NULL, nWidthInHimetric +
				LOWORD(OutlineDoc_GetMargin(lpLL->m_lpDoc)) +
				HIWORD(OutlineDoc_GetMargin(lpLL->m_lpDoc)));

		nWidthInPix = (int)(nWidthInPix * lpscale->dwSxN / lpscale->dwSxD);
		SendMessage(
				lpLL->m_hWndListBox,
				LB_SETHORIZONTALEXTENT,
				nWidthInPix,
				0L
		);
		fSizeChanged = TRUE;

#if defined( USE_HEADING )
		Heading_CH_SetHorizontalExtent(
				OutlineDoc_GetHeading(lpLL->m_lpDoc), lpLL->m_hWndListBox);
#endif

	}
	return fSizeChanged;
}


/* LineList_GetMaxLineWidthInHimetric
 * ----------------------------------
 *
 *      Return the width of the widest line
 */
int LineList_GetMaxLineWidthInHimetric(LPLINELIST lpLL)
{
	return lpLL->m_nMaxLineWidthInHimetric;
}


/* LineList_RecalcMaxLineWidthInHimetric
 * -------------------------------------
 *
 *  Recalculate the maximum line width in the entire list.
 *
 *  Parameters:
 *      nWidthInHimetric should be set to the width of line being removed.
 *      nWidthInHimetric == 0 forces list to recalculate in all cases.
 *      nWidthInHimetric == current max width => forces recalc.
 *
 *  Returns:
 *      TRUE is max line width of LineList changed
 *      FALSE if no change
 */
BOOL LineList_RecalcMaxLineWidthInHimetric(
		LPLINELIST          lpLL,
	int                 nWidthInHimetric
)
{
	int i;
	LPLINE lpLine;
	BOOL fSizeChanged = FALSE;
	int nOrgMaxLineWidthInHimetric = lpLL->m_nMaxLineWidthInHimetric;

	if (nWidthInHimetric == 0 ||
		nWidthInHimetric == lpLL->m_nMaxLineWidthInHimetric) {

		lpLL->m_nMaxLineWidthInHimetric = -1;

		LineList_SetMaxLineWidthInHimetric(lpLL, 0);

		for(i = 0; i < lpLL->m_nNumLines; i++) {
			lpLine=LineList_GetLine(lpLL, i);
			LineList_SetMaxLineWidthInHimetric(
					lpLL,
					Line_GetTotalWidthInHimetric(lpLine)
			);
		}
	}

	if (nOrgMaxLineWidthInHimetric != lpLL->m_nMaxLineWidthInHimetric)
		fSizeChanged = TRUE;

	return fSizeChanged;
}


/* LineList_CalcSelExtentInHimetric
 * --------------------------------
 *
 *      Calculate the extents (widht and height) of a selection of lines.
 *
 * if lplrSel == NULL, calculate extent of all lines.
 */
void LineList_CalcSelExtentInHimetric(
		LPLINELIST          lpLL,
		LPLINERANGE         lplrSel,
		LPSIZEL             lpsizel
)
{
	int i;
	int nEndLine;
	int nStartLine;
	LPLINE lpLine;
	long lWidth;

	if (lplrSel) {
		nEndLine = lplrSel->m_nEndLine;
		nStartLine = lplrSel->m_nStartLine;
	} else {
		nEndLine = LineList_GetCount(lpLL) - 1;
		nStartLine = 0;
	}

	lpsizel->cx = 0;
	lpsizel->cy = 0;

	for(i = nStartLine; i <= nEndLine; i++) {
		lpLine=LineList_GetLine(lpLL,i);
		if (lpLine) {
			lWidth = (long)Line_GetTotalWidthInHimetric(lpLine);
			lpsizel->cx = max(lpsizel->cx, lWidth);
			lpsizel->cy += lpLine->m_nHeightInHimetric;
		}
	}
}


/* LineList_GetWindow
 * ------------------
 *
 * Return handle of list box
 */
HWND LineList_GetWindow(LPLINELIST lpLL)
{
	return lpLL->m_hWndListBox;
}


/* LineList_GetDC
 * --------------
 *
 * Return DC handle of list box
 */
HDC LineList_GetDC(LPLINELIST lpLL)
{
	HFONT hfontOld;
	HDC hDC = GetDC(lpLL->m_hWndListBox);
	int     iXppli;     //* pixels per logical inch along width
	int     iYppli;     //* pixels per logical inch along height
	SIZE    size;

	// Setup a mapping mode for the DC which maps physical pixel
	// coordinates to HIMETRIC units. The standard MM_HIMETRIC mapping
	// mode does not work correctly because it does not take into
	// account that a logical inch on the display screen is drawn
	// physically larger than 1 inch. We will setup an anisotropic
	// mapping mode which will perform the transformation properly.

	g_iMapMode = SetMapMode(hDC, MM_ANISOTROPIC);
	iXppli = GetDeviceCaps (hDC, LOGPIXELSX);
	iYppli = GetDeviceCaps (hDC, LOGPIXELSY);
	SetViewportExtEx(hDC, iXppli, iYppli, &size);
	SetWindowExtEx(hDC, HIMETRIC_PER_INCH, HIMETRIC_PER_INCH, &size);

	// Set the default font size, and font face name
	hfontOld = SelectObject(hDC, OutlineApp_GetActiveFont(g_lpApp));

	return hDC;
}


/* LineList_ReleaseDC
 * ------------------
 *
 *      Release DC of list box returned from previous LineList_GetDC call.
 */
void LineList_ReleaseDC(LPLINELIST lpLL, HDC hDC)
{
	SetMapMode(hDC, g_iMapMode);
	ReleaseDC(lpLL->m_hWndListBox, hDC);
}


/* LineList_SetLineHeight
 * ----------------------
 *
 *      Set the height of a line in the LineList list box
 */
void LineList_SetLineHeight(LPLINELIST lpLL,int nIndex,int nHeightInHimetric)
{
	LPARAM          lParam;
	LPOUTLINEDOC    lpDoc;
	LPSCALEFACTOR   lpscale;
	UINT            uHeightInPix;
	LPHEADING       lphead;

	if (!lpLL)
		return;

	lpDoc = lpLL->m_lpDoc;
	lphead = OutlineDoc_GetHeading(lpDoc);
	lpscale = OutlineDoc_GetScaleFactor(lpDoc);

	uHeightInPix = XformHeightInHimetricToPixels(NULL, nHeightInHimetric);

	Heading_RH_SendMessage(lphead, LB_SETITEMDATA, (WPARAM)nIndex,
			MAKELPARAM(uHeightInPix, 0));

	uHeightInPix = (UINT)(uHeightInPix * lpscale->dwSyN / lpscale->dwSyD);

	if (uHeightInPix > LISTBOX_HEIGHT_LIMIT)
		uHeightInPix = LISTBOX_HEIGHT_LIMIT;


	lParam = MAKELPARAM(uHeightInPix, 0);
	SendMessage(lpLL->m_hWndListBox,LB_SETITEMHEIGHT,(WPARAM)nIndex, lParam);
	Heading_RH_SendMessage(lphead, LB_SETITEMHEIGHT, (WPARAM)nIndex, lParam);
	Heading_RH_ForceRedraw(lphead, TRUE);
}


/* LineList_ReScale
 * ----------------
 *
 *      Re-scale the LineList list box
 */
void LineList_ReScale(LPLINELIST lpLL, LPSCALEFACTOR lpscale)
{
	int nIndex;
	LPLINE lpLine;
	UINT uWidthInHim;

	if (!lpLL)
		return;

	for (nIndex = 0; nIndex < lpLL->m_nNumLines; nIndex++) {
		lpLine = LineList_GetLine(lpLL, nIndex);
		if (lpLine) {
			LineList_SetLineHeight(
					lpLL,
					nIndex,
					Line_GetHeightInHimetric(lpLine)
			);
		}
	}

	uWidthInHim = LineList_GetMaxLineWidthInHimetric(lpLL);
	LineList_SetMaxLineWidthInHimetric(lpLL, -(int)uWidthInHim);
}

/* LineList_SetSel
 * ---------------
 *
 *      Set the selection in list box
 */
void LineList_SetSel(LPLINELIST lpLL, LPLINERANGE lplrSel)
{
	DWORD dwSel;

	if (lpLL->m_nNumLines <= 0 || lplrSel->m_nStartLine < 0)
		return;     // no lines in list; can't set a selection

	dwSel = MAKELPARAM(lplrSel->m_nStartLine, lplrSel->m_nEndLine);

	lpLL->m_lrSaveSel = *lplrSel;

	/* remove previous selection */
#if defined( WIN32 )
	SendMessage(
			lpLL->m_hWndListBox,
			LB_SETSEL,
			(WPARAM)FALSE,
			(LPARAM)-1
	);
#else
	SendMessage(
			lpLL->m_hWndListBox,
			LB_SETSEL,
			(WPARAM)FALSE,
			MAKELPARAM(-1,0)
	);
#endif

	/* mark selection */
	SendMessage(lpLL->m_hWndListBox,LB_SELITEMRANGE, (WPARAM)TRUE, (LPARAM)dwSel);
	/* set focus line (caret) */
	LineList_SetFocusLine ( lpLL, (WORD)lplrSel->m_nStartLine );

}


/* LineList_GetSel
 * ---------------
 *
 * Get the selection in list box.
 *
 * Returns the count of items selected
 */
int LineList_GetSel(LPLINELIST lpLL, LPLINERANGE lplrSel)
{
	int nNumSel=(int)SendMessage(lpLL->m_hWndListBox,LB_GETSELCOUNT,0,0L);

	if (nNumSel) {
		SendMessage(lpLL->m_hWndListBox,LB_GETSELITEMS,
			(WPARAM)1,(LPARAM)(int FAR*)&(lplrSel->m_nStartLine));
		lplrSel->m_nEndLine = lplrSel->m_nStartLine + nNumSel - 1;
	} else {
		_fmemset(lplrSel, 0, sizeof(LINERANGE));
	}
	return nNumSel;
}


/* LineList_RemoveSel
 * ------------------
 *
 * Remove the selection in list box but save the selection state so that
 * it can be restored by calling LineList_RestoreSel
 * LineList_RemoveSel is called when the LineList window looses focus.
 */
void LineList_RemoveSel(LPLINELIST lpLL)
{
	LINERANGE lrSel;
	if (LineList_GetSel(lpLL, &lrSel) > 0) {
		lpLL->m_lrSaveSel = lrSel;
#if defined( WIN32 )
		SendMessage(
				lpLL->m_hWndListBox,
				LB_SETSEL,
				(WPARAM)FALSE,
				(LPARAM)-1
		);
#else
		SendMessage(
				lpLL->m_hWndListBox,
				LB_SETSEL,
				(WPARAM)FALSE,
				MAKELPARAM(-1,0)
		);
#endif
	}
}


/* LineList_RestoreSel
 * ------------------
 *
 * Restore the selection in list box that was previously saved by a call to
 * LineList_RemoveSel.
 * LineList_RestoreSel is called when the LineList window gains focus.
 */
void LineList_RestoreSel(LPLINELIST lpLL)
{
	LineList_SetSel(lpLL, &lpLL->m_lrSaveSel);
}


/* LineList_SetRedraw
 * ------------------
 *
 *      Enable/Disable the redraw of the linelist (listbox) on screen
 *
 *  fEnbaleDraw = TRUE      - enable redraw
 *                FALSE     - disable redraw
 */
void LineList_SetRedraw(LPLINELIST lpLL, BOOL fEnableDraw)
{
	SendMessage(lpLL->m_hWndListBox,WM_SETREDRAW,(WPARAM)fEnableDraw,0L);
}


/* LineList_ForceRedraw
 * --------------------
 *
 *      Force redraw of the linelist (listbox) on screen
 */
void LineList_ForceRedraw(LPLINELIST lpLL, BOOL fErase)
{
	InvalidateRect(lpLL->m_hWndListBox, NULL, fErase);
}


/* LineList_ForceLineRedraw
 * ------------------------
 *
 *      Force a particular line of the linelist (listbox) to redraw.
 */
void LineList_ForceLineRedraw(LPLINELIST lpLL, int nIndex, BOOL fErase)
{
	RECT   rect;

	LineList_GetLineRect( lpLL, nIndex, (LPRECT)&rect );
	InvalidateRect( lpLL->m_hWndListBox, (LPRECT)&rect, fErase );
}


/* LineList_ScrollLineIntoView
 * ---------------------------
 *  Make sure that the specified line is in view; if necessary scroll
 *      the listbox. if any portion of the line is visible, then no
 *      scrolling will occur.
 */
void LineList_ScrollLineIntoView(LPLINELIST lpLL, int nIndex)
{
	RECT rcWindow;
	RECT rcLine;
	RECT rcInt;

	if ( lpLL->m_nNumLines == 0 )
		return;

	if (! LineList_GetLineRect( lpLL, nIndex, (LPRECT)&rcLine ) )
		return;

	GetClientRect( lpLL->m_hWndListBox, (LPRECT) &rcWindow );

	if (! IntersectRect((LPRECT)&rcInt, (LPRECT)&rcWindow, (LPRECT)&rcLine))
		SendMessage(
				lpLL->m_hWndListBox,
				LB_SETTOPINDEX,
				(WPARAM)nIndex,
				(LPARAM)NULL
		);
}


/* LineList_CopySelToDoc
 * ---------------------
 *
 *      Copy the selection of the linelist to another document
 *
 *  RETURNS: number of lines copied.
 */
int LineList_CopySelToDoc(
		LPLINELIST              lpSrcLL,
		LPLINERANGE             lplrSel,
		LPOUTLINEDOC            lpDestDoc
)
{
	int             nEndLine;
	int             nStartLine;
	LPLINELIST      lpDestLL = &lpDestDoc->m_LineList;
	signed short    nDestIndex = LineList_GetFocusLineIndex(lpDestLL);
	LPLINE          lpSrcLine;
	int             nCopied = 0;
	int             i;

	if (lplrSel) {
		nEndLine = lplrSel->m_nEndLine;
		nStartLine = lplrSel->m_nStartLine;
	} else {
		nEndLine = LineList_GetCount(lpSrcLL) - 1;
		nStartLine = 0;
	}

	for(i = nStartLine; i <= nEndLine; i++) {
		lpSrcLine = LineList_GetLine(lpSrcLL, i);
		if (lpSrcLine && Line_CopyToDoc(lpSrcLine, lpDestDoc, nDestIndex)) {
			nDestIndex++;
			nCopied++;
		}
	}

	return nCopied;
}


/* LineList_SaveSelToStg
 * ---------------------
 *
 *      Save lines in selection into lpDestStg.
 *
 *      Return TRUE if ok, FALSE if error
 */
BOOL LineList_SaveSelToStg(
		LPLINELIST              lpLL,
		LPLINERANGE             lplrSel,
		UINT                    uFormat,
		LPSTORAGE               lpSrcStg,
		LPSTORAGE               lpDestStg,
		LPSTREAM                lpLLStm,
		BOOL                    fRemember
)
{
	int nEndLine;
	int nStartLine;
	int nNumLinesWritten = 0;
	HRESULT hrErr = NOERROR;
	ULONG nWritten;
	LPLINE lpLine;
	LINELISTHEADER llhRecord;
	int i;
	LARGE_INTEGER dlibSaveHeaderPos;
	LARGE_INTEGER dlibZeroOffset;
	LISet32( dlibZeroOffset, 0 );

	if (lplrSel) {
		nEndLine = lplrSel->m_nEndLine;
		nStartLine = lplrSel->m_nStartLine;
	} else {
		nEndLine = LineList_GetCount(lpLL) - 1;
		nStartLine = 0;
	}

	_fmemset((LPLINELISTHEADER)&llhRecord,0,sizeof(LINELISTHEADER));

	/* save seek position for LineList header record */
	hrErr = lpLLStm->lpVtbl->Seek(
			lpLLStm,
			dlibZeroOffset,
			STREAM_SEEK_CUR,
			(ULARGE_INTEGER FAR*)&dlibSaveHeaderPos
	);
	if (hrErr != NOERROR) goto error;

	/* write LineList header record */
	hrErr = lpLLStm->lpVtbl->Write(
			lpLLStm,
			(LPVOID)&llhRecord,
			sizeof(LINELISTHEADER),
			&nWritten
	);
	if (hrErr != NOERROR) {
		OleDbgOutHResult("Write LineList header returned", hrErr);
		goto error;
	}

	for(i = nStartLine; i <= nEndLine; i++) {
		lpLine = LineList_GetLine(lpLL, i);
		if(lpLine &&
			Line_SaveToStg(lpLine, uFormat, lpSrcStg, lpDestStg, lpLLStm,
																fRemember))
			llhRecord.m_nNumLines++;
	}

	/* retore seek position for LineList header record */
	hrErr = lpLLStm->lpVtbl->Seek(
			lpLLStm,
			dlibSaveHeaderPos,
			STREAM_SEEK_SET,
			NULL
	);
	if (hrErr != NOERROR) goto error;

	/* write LineList header record */
	hrErr = lpLLStm->lpVtbl->Write(
			lpLLStm,
			(LPVOID)&llhRecord,
			sizeof(LINELISTHEADER),
			&nWritten
	);
	if (hrErr != NOERROR) goto error;

	/* reset seek position to end of stream */
	hrErr = lpLLStm->lpVtbl->Seek(
			lpLLStm,
			dlibZeroOffset,
			STREAM_SEEK_END,
			NULL
	);
	if (hrErr != NOERROR) goto error;

	return TRUE;

error:
#if defined( _DEBUG )
	OleDbgAssertSz(
			hrErr == NOERROR,
			"Could not write LineList header to LineList stream"
	);
#endif
	return FALSE;
}


/* LineList_LoadFromStg
 * --------------------
 *
 *      Load lines into linelist from storage.
 *
 *      Return TRUE if ok, FALSE if error
 */
BOOL LineList_LoadFromStg(
		LPLINELIST              lpLL,
		LPSTORAGE               lpSrcStg,
		LPSTREAM                lpLLStm
)
{
	HRESULT hrErr;
	ULONG nRead;
	LPLINE lpLine;
	int i;
	int nNumLines;
	LINELISTHEADER llineRecord;

	/* write LineList header record */
	hrErr = lpLLStm->lpVtbl->Read(
			lpLLStm,
			(LPVOID)&llineRecord,
			sizeof(LINELISTHEADER),
			&nRead
	);

	if (hrErr != NOERROR) {
		OleDbgOutHResult("Read LineList header returned", hrErr);
		goto error;
	}

	nNumLines = llineRecord.m_nNumLines;

	for(i = 0; i < nNumLines; i++) {
		lpLine = Line_LoadFromStg(lpSrcStg, lpLLStm, lpLL->m_lpDoc);
		if (! lpLine)
			goto error;

		// Directly add lines to LineList without trying to update a NameTbl
		LineList_AddLine(lpLL, lpLine, i-1);
	}

	return TRUE;

error:
	// Delete any Line objects that were created
	if (lpLL->m_nNumLines > 0) {
		int nNumLines = lpLL->m_nNumLines;
		for (i = 0; i < nNumLines; i++)
			LineList_DeleteLine(lpLL, i);
	}

	return FALSE;
}


#if defined( USE_DRAGDROP )


/* LineList_SetFocusLineFromPointl
 * -------------------------------
 *
 */

void LineList_SetFocusLineFromPointl( LPLINELIST lpLL, POINTL pointl )
{
	int i = LineList_GetLineIndexFromPointl( lpLL, pointl );

	if ( i == (int)-1)
		return ;
	else
		LineList_SetFocusLine( lpLL, (WORD)i );
}


/* LineList_SetDragOverLineFromPointl
 * ----------------------------------
 *
 */

void LineList_SetDragOverLineFromPointl ( LPLINELIST lpLL, POINTL pointl )
{
	int    nIndex = LineList_GetLineIndexFromPointl( lpLL, pointl );
	LPLINE lpline = LineList_GetLine( lpLL, nIndex );

	if (!lpline)
		return;

	if (! lpline->m_fDragOverLine) {
		/* user has dragged over a new line. force new drop target line
		**    to repaint so that drop feedback will be drawn.
		*/
		lpline->m_fDragOverLine = TRUE;
		LineList_ForceLineRedraw( lpLL, nIndex, TRUE /*fErase*/);

		if (lpLL->m_iDragOverLine!= -1 && lpLL->m_iDragOverLine!=nIndex) {

			/* force previous drop target line to repaint so that drop
			**    feedback will be undrawn
			*/
			lpline = LineList_GetLine( lpLL, lpLL->m_iDragOverLine );
			if (lpline)
				lpline->m_fDragOverLine = FALSE;

			LineList_ForceLineRedraw(
					lpLL,lpLL->m_iDragOverLine,TRUE /*fErase*/);
		}

		lpLL->m_iDragOverLine = nIndex;

		// Force repaint immediately
		UpdateWindow(lpLL->m_hWndListBox);
	}
}


/* LineList_Scroll
 * ---------------
 *
 * Scroll the LineList list box in the desired direction by one line.
 *
 *      this function is called during a drag operation.
 */

void LineList_Scroll(LPLINELIST lpLL, DWORD dwScrollDir)
{
	switch (dwScrollDir) {
		case SCROLLDIR_UP:
			SendMessage( lpLL->m_hWndListBox, WM_VSCROLL, SB_LINEUP, 0L );
			break;

		case SCROLLDIR_DOWN:
			SendMessage( lpLL->m_hWndListBox, WM_VSCROLL, SB_LINEDOWN, 0L );
			break;
	}
}


/* LineList_GetLineIndexFromPointl
 * -------------------------------
 *   do hit test to get index of line corresponding to pointl
 */
int LineList_GetLineIndexFromPointl(LPLINELIST lpLL, POINTL pointl)
{
	RECT  rect;
	POINT point;
	DWORD i;

	point.x = (int)pointl.x;
	point.y = (int)pointl.y;

	ScreenToClient( lpLL->m_hWndListBox, &point);

	if ( lpLL->m_nNumLines == 0 )
		return -1;

	GetClientRect( lpLL->m_hWndListBox, (LPRECT) &rect );

	i = SendMessage( lpLL->m_hWndListBox, LB_GETTOPINDEX, (WPARAM)NULL, (LPARAM)NULL );

	for ( ;; i++){

		RECT rectItem;

		if (!LineList_GetLineRect( lpLL, (int)i, (LPRECT)&rectItem ) )
			return -1;

		if ( rectItem.top > rect.bottom )
			return -1;

		if ( rectItem.top <= point.y && point.y <= rectItem.bottom)
			return (int)i;

	}

}


/* LineList_RestoreDragFeedback
 * ----------------------------
 *
 * Retore the index of the line that currently has focus (the active line).
 */
void LineList_RestoreDragFeedback(LPLINELIST lpLL)
{
	LPLINE lpLine;

	if (lpLL->m_iDragOverLine < 0 )
	   return;

	lpLine = LineList_GetLine( lpLL, lpLL->m_iDragOverLine);

	if (lpLine) {

		lpLine->m_fDragOverLine = FALSE;
		LineList_ForceLineRedraw( lpLL,lpLL->m_iDragOverLine,TRUE /*fErase*/);

		// Force repaint immediately
		UpdateWindow(lpLL->m_hWndListBox);
	}

	lpLL->m_iDragOverLine = -1;

}

#endif
