OUTLLIST.C

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


#include "outline.h"

OLEDBGDATA

extern LPOUTLINEAPP g_lpApp;

OLECHAR ErrMsgListBox[] = OLESTR("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 */

/* NOTE: 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, OLESTR("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_ONDISK 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(&llhRecord,0,sizeof(llhRecord));

/* 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(llhRecord),
&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(llhRecord),
&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_ONDISK llineRecord;

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

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

nNumLines = (int) 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